diff --git a/arch/arm64/boot/dts/apple/t8142-j813-gpu.dtsi b/arch/arm64/boot/dts/apple/t8142-j813-gpu.dtsi index 763125bfe4ec7..abc977fab1898 100644 --- a/arch/arm64/boot/dts/apple/t8142-j813-gpu.dtsi +++ b/arch/arm64/boot/dts/apple/t8142-j813-gpu.dtsi @@ -33,6 +33,18 @@ reg = <0x100 0x02444000 0x0 0x184000>; }; + /* + * Both roles' __TEXT, contiguous (ADT gfx-asc and gfx1-asc segment-ranges), + * which the driver identifies before it starts the firmware. Fixed at the + * macOS 27.0 address like fw-data, until the bootloader fills it in; under + * 27.2 (26B123) iBoot loads it at 0x100_00e5_4000 and the DATA at + * 0x100_0245_8000. + */ + gpu_fw_text: fw-text { + reg = <0x100 0x00e44000 0x0 0x9c000>; + no-map; + }; + uat_handoff: uat-handoff { reg = <0 0 0 0>; }; @@ -105,9 +117,10 @@ power-domains = <&ps_gfx>; memory-region = <&uat_ttbs>, <&uat_pagetables>, <&uat_handoff>, <&uat_l2_pagetables>, <&gfx_data>, <&gfx1_data>, - <&gpu_fw_data>; + <&gpu_fw_data>, <&gpu_fw_text>; memory-region-names = "ttbs", "pagetables", "handoff", - "shared-l2", "gfx-data", "gfx1-data", "fw-data"; + "shared-l2", "gfx-data", "gfx1-data", "fw-data", + "fw-text"; operating-points-v2 = <&gpu_opp>; /* Holds the DCS and fabric floor while GPU work is in flight. */ apple,clpc = <&clpc>; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 61bef7cdc5782..c23dea8ed47fe 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -5260,11 +5260,12 @@ static enum dcp_firmware_version dcp_check_firmware_version(struct device *dev) * T8142 (J813) on macOS 27.0: the H17P interface written for its 26.x * firmware makes the 27.0 DCP firmware panic right after the first * hotplug ("ap_link: callback failed with: 0x5"). It runs the T6050's - * firmware-27 generation. + * firmware-27 generation, and so does 27.2, with the 27.2 method table. */ if (of_device_is_compatible(dev->of_node, "apple,t8142-dcp")) { ret = dcp_read_fw_version(dev, "apple,firmware-compat", compat_str); - if (ret >= 0 && !strcmp(compat_str, "27.0.0")) + if (ret >= 0 && (!strcmp(compat_str, "27.0.0") || + !strcmp(compat_str, "27.2.0"))) return DCP_FIRMWARE_V_27_0; } diff --git a/drivers/gpu/drm/apple/iomfb_v27.c b/drivers/gpu/drm/apple/iomfb_v27.c index 2b527f2f2bf2f..b8b9406ccb6a8 100644 --- a/drivers/gpu/drm/apple/iomfb_v27.c +++ b/drivers/gpu/drm/apple/iomfb_v27.c @@ -119,9 +119,9 @@ static const struct dcp_method_entry dcp_methods_27_0[dcpep_num_methods] = { * A400/A401, so IOMobileFramebufferAP moved up by 50 (start_signal A451, * get_color_remap_mode A477), and set_create_DFB and vi_set_temperature_hint * up by one. The UPPipe A0xx and A1xx IDs are unchanged. The firmware rejects - * an unknown tag with a panic ("link_rpc_lookup callback not found"), so - * dfb_ready_query, whose 27.2 tag has not been seen in a call, has no entry - * here and is not sent to this build (boot_5()). + * an unknown tag with a panic ("link_rpc_lookup callback not found"). + * dfb_ready_query is A385, one above its 27.0 ID: the 27.3 trace sends it + * there, and the J813's DCP answers it at A385 on 27.2. */ static const struct dcp_method_entry dcp_methods_27_2[dcpep_num_methods] = { IOMFB_METHOD("A000", dcpep_late_init_signal), @@ -147,6 +147,7 @@ static const struct dcp_method_entry dcp_methods_27_2[dcpep_num_methods] = { IOMFB_METHOD("A104", dcpep_set_dfb_dimensions), IOMFB_METHOD("A105", dcpep_commit_dfb_info), IOMFB_METHOD("A035", dcpep_dfb_query), + IOMFB_METHOD("A385", dcpep_dfb_ready_query), IOMFB_METHOD("A353", dcpep_pipe_query_353), IOMFB_METHOD("A352", dcpep_pipe_set_352), IOMFB_METHOD("A465", dcpep_pipe_cfg_415), diff --git a/drivers/gpu/drm/asahi/fwid.rs b/drivers/gpu/drm/asahi/fwid.rs index d646bff94cf1f..258ebafa04f09 100644 --- a/drivers/gpu/drm/asahi/fwid.rs +++ b/drivers/gpu/drm/asahi/fwid.rs @@ -585,4 +585,32 @@ mod tests { assert!(admit(t, d, &e[..1], h).is_none()); } } + + #[test] + fn t8142_identifies_each_build() { + use crate::g17::fw::m5::initdata::RootProfile; + let crate::g17::fw::variant::Firmware::Identified(builds) = crate::hw::t8142::CONFIG.firmware + else { + panic!("the J813 must identify its loaded firmware"); + }; + assert_eq!(builds.len(), 2); + // Same TEXT and DATA sizes for both builds, so each is told apart by its digest at its + // own record offsets. + t6050_admits_only(builds); + let [a428, b123] = [&builds[0].images[0], &builds[1].images[0]]; + for (image, other) in [(a428, builds[1].images), (b123, builds[0].images)] { + assert!(lookup(other, image.text_size, image.data_size, |_| { + Some(image.text_sha256) + }) + .is_none()); + } + // 26A428 runs the board as probed; 26B123 runs it on the 26B123 graph. + assert!(builds[0].config.is_none()); + let config = builds[1].config.unwrap(); + assert_eq!(config.m5.unwrap().graph.profile, RootProfile::J873G18G26B123); + assert_eq!( + crate::hw::t8142::CONFIG.m5.unwrap().graph.profile, + RootProfile::J873G18G26A428 + ); + } } diff --git a/drivers/gpu/drm/asahi/g17/fw/m5/initdata.rs b/drivers/gpu/drm/asahi/g17/fw/m5/initdata.rs index e4de37f639579..dda570401f0a7 100644 --- a/drivers/gpu/drm/asahi/g17/fw/m5/initdata.rs +++ b/drivers/gpu/drm/asahi/g17/fw/m5/initdata.rs @@ -36,6 +36,11 @@ pub(crate) enum RootProfile { J873G18G26A428, J714G17S26A428, + + /// The common interface of macOS 27.2 (26B123, RTKit 3514.41.1): the + /// [`Self::J873G18G26A428`] graph with a longer policy and status config + /// ([`COMMON26B123_LAYOUTS`]). The J813's G17G build runs it as the J873's G18G one does. + J873G18G26B123, } impl RootProfile { @@ -44,13 +49,28 @@ impl RootProfile { Self::J700G17P => INITDATA_VERSION_WORD, Self::J813G17G25G76 => 0x0c8a_a322_039e_04c0, Self::J873G18G26A428 => COMMON26_INITDATA_VERSION_WORD, + Self::J873G18G26B123 => COMMON26B123_INITDATA_VERSION_WORD, Self::J714G17S26A428 => M5_INITDATA_VERSION_WORD, } } + /// Either common-interface build, 26A428 or 26B123. + pub(crate) const fn is_common26(self) -> bool { + matches!(self, Self::J873G18G26A428 | Self::J873G18G26B123) + } + + /// The shared, private and per-object layouts of a common-interface build. + pub(crate) const fn common26_layouts(self) -> Option<&'static Common26Layouts> { + match self { + Self::J873G18G26A428 => Some(&COMMON26_LAYOUTS), + Self::J873G18G26B123 => Some(&COMMON26B123_LAYOUTS), + Self::J700G17P | Self::J813G17G25G76 | Self::J714G17S26A428 => None, + } + } + pub(crate) const fn root_size(self, role: InstanceRole) -> usize { match self { - Self::J873G18G26A428 => COMMON26_ROOT_SIZE, + Self::J873G18G26A428 | Self::J873G18G26B123 => COMMON26_ROOT_SIZE, Self::J714G17S26A428 => M5_ROOT_SIZE, Self::J700G17P | Self::J813G17G25G76 => root_size(role), } @@ -58,10 +78,10 @@ impl RootProfile { pub(crate) const fn control_opening_opcode(self, role: InstanceRole) -> u32 { match (self, role) { - (Self::J873G18G26A428, InstanceRole::Primary) => { + (Self::J873G18G26A428 | Self::J873G18G26B123, InstanceRole::Primary) => { COMMON26_CONTROL_OPENING_OPCODE_PRIMARY } - (Self::J873G18G26A428, InstanceRole::Secondary) => { + (Self::J873G18G26A428 | Self::J873G18G26B123, InstanceRole::Secondary) => { COMMON26_CONTROL_OPENING_OPCODE_SECONDARY } (Self::J714G17S26A428, InstanceRole::Primary) => M5_CONTROL_OPENING_OPCODE_PRIMARY, @@ -75,7 +95,7 @@ impl RootProfile { pub(crate) const fn control_register_opcode(self) -> Option { match self { - Self::J873G18G26A428 | Self::J714G17S26A428 => None, + Self::J873G18G26A428 | Self::J873G18G26B123 | Self::J714G17S26A428 => None, Self::J700G17P | Self::J813G17G25G76 => Some(CONTROL_OPENING_PRIMARY_REGISTER_OPCODE), } } @@ -83,6 +103,7 @@ impl RootProfile { pub(crate) const fn bundle_view_offsets(self) -> [usize; 5] { match self { Self::J873G18G26A428 => COMMON26_BUNDLE_VIEW_OFFSETS, + Self::J873G18G26B123 => COMMON26B123_BUNDLE_VIEW_OFFSETS, Self::J714G17S26A428 => M5_BUNDLE_VIEW_OFFSETS, Self::J700G17P | Self::J813G17G25G76 => BUNDLE_VIEW_OFFSETS, } @@ -90,7 +111,8 @@ impl RootProfile { pub(crate) const fn bundle_view_extents(self) -> [usize; 5] { match self { - Self::J873G18G26A428 => COMMON26_BUNDLE_VIEW_EXTENTS, + // 26B123 grows only the policy and the status config; the five views keep their sizes. + Self::J873G18G26A428 | Self::J873G18G26B123 => COMMON26_BUNDLE_VIEW_EXTENTS, Self::J714G17S26A428 => M5_BUNDLE_VIEW_EXTENTS, Self::J700G17P | Self::J813G17G25G76 => BUNDLE_VIEW_EXTENTS, } @@ -99,7 +121,7 @@ impl RootProfile { /// The GPU-visible aliases of the firmware's own tables the main config names. pub(crate) const fn region_view_low_addrs(self) -> [u64; 2] { match self { - Self::J873G18G26A428 => COMMON26_REGION_VIEW_LOW_ADDRS, + Self::J873G18G26A428 | Self::J873G18G26B123 => COMMON26_REGION_VIEW_LOW_ADDRS, Self::J714G17S26A428 | Self::J700G17P | Self::J813G17G25G76 => REGION_VIEW_LOW_ADDRS, } } @@ -107,6 +129,7 @@ impl RootProfile { pub(crate) const fn repeated_region_bundle_offset(self) -> usize { match self { Self::J873G18G26A428 => COMMON26_SHARED_LAYOUT.block_88, + Self::J873G18G26B123 => COMMON26B123_SHARED_LAYOUT.block_88, Self::J714G17S26A428 => M5_SHARED_LAYOUT.block_88, Self::J700G17P | Self::J813G17G25G76 => MAIN_REPEATED_REGION_BUNDLE_OFFSET, } @@ -115,6 +138,7 @@ impl RootProfile { pub(crate) const fn work_ring_bundle_offsets(self) -> [usize; WORK_CHANNEL_COUNT] { match self { Self::J873G18G26A428 => COMMON26_WORK_RING_BUNDLE_OFFSETS, + Self::J873G18G26B123 => COMMON26B123_WORK_RING_BUNDLE_OFFSETS, Self::J714G17S26A428 => M5_WORK_RING_BUNDLE_OFFSETS, Self::J700G17P | Self::J813G17G25G76 => WORK_RING_BUNDLE_OFFSETS, } @@ -876,18 +900,17 @@ pub(crate) fn encode_main_config_for_profile( RootProfile::J700G17P => MAIN_SECONDARY_KIND_VALUE, RootProfile::J813G17G25G76 => 0xc, - RootProfile::J873G18G26A428 | RootProfile::J714G17S26A428 => { - COMMON26_MAIN_SECONDARY_KIND - } + RootProfile::J873G18G26A428 | RootProfile::J873G18G26B123 + | RootProfile::J714G17S26A428 => COMMON26_MAIN_SECONDARY_KIND, }); if profile == RootProfile::J714G17S26A428 { put_u64(out, COMMON26_MAIN_SECONDARY_POLICY_PTR, bundle + M5_SHARED_LAYOUT.policy as u64); - } else if profile == RootProfile::J873G18G26A428 { + } else if let Some(layouts) = profile.common26_layouts() { put_u64(out, COMMON26_MAIN_SECONDARY_POLICY_PTR, - bundle + COMMON26_SHARED_LAYOUT.policy as u64); + bundle + layouts.shared.policy as u64); } else { let extra = bundle @@ -909,7 +932,7 @@ pub(crate) fn encode_region_c_for_profile( if out.len() != REGION_C_SIZE { return Err(InitdataError::BufferSize); } - if profile == RootProfile::J873G18G26A428 { + if profile.is_common26() { return encode_common26_region_c(out); } @@ -976,7 +999,7 @@ pub(crate) fn encode_secondary_root_extra_1_for_profile( out.fill(0); write_runs(out, SECONDARY_ROOT_EXTRA_1_RUNS); - if matches!(profile, RootProfile::J813G17G25G76 | RootProfile::J873G18G26A428) { + if profile == RootProfile::J813G17G25G76 || profile.is_common26() { put_u32(out, 0x10, 2); } Ok(()) @@ -1904,16 +1927,213 @@ const _: () = { <= COMMON26_STATUS_B_SPAN); }; +// --- The common interface of macOS 27.2 (26B123, RTKit 3514.41.1) ------------------------------- +// +// Against 26A428, the same changes as the J873's G18G build of this release: +// +// - Both roles expect the InitData root version word with bit 45 set. +// - The policy grows by 0x60 (0x7168 -> 0x71c8 for G17G; 0x7800 -> 0x7860 in the common table) +// and the status config by 8 (0x6d0 -> 0x6d8); the other 22 allocations keep their sizes. +// - Policy and status-config fields move as the bands below describe. The new words carry a new +// accelerator power-controller field (accel +0x1122c), which the generic power and performance +// setup sets to 5000 and the G17G one leaves alone. +// - Every other InitData object keeps its field offsets. + +/// FW_PROVEN 26B123: u16 {0x04c0, 0x83a0, 0x6339, 0x0c8b}. +pub(crate) const COMMON26B123_INITDATA_VERSION_WORD: u64 = 0x0c8b_6339_83a0_04c0; + +/// The common 26A428 table with the 26B123 policy and status config. +pub(crate) const ABI_ALLOCATION_SIZES_26B123: [usize; 24] = [ + 0x2848, 0x0c18, 0x1048, 0x0e10, 0x7860, 0x0068, 0x0800, 0x0088, 0x1800, 0x04c0, 0x4000, 0x0030, + 0xe730, 0x06d8, 0x0020, 0x01b0, 0x4800, 0x28800, 0x79800, 0x9000, 0x0060, 0x0008, 0x0e80, 0x00e4, +]; + +/// Everything after the policy in the shared cluster moves by 0x80 (64-byte packing); the page +/// total is unchanged. +pub(crate) const COMMON26B123_SHARED_LAYOUT: SharedClusterLayout = + shared_cluster_layout(&ABI_ALLOCATION_SIZES_26B123); +/// 0x6d8 packs to the 0x700 that 0x6d0 took: the private cluster is the 26A428 one. +pub(crate) const COMMON26B123_PRIVATE_LAYOUT: PrivateClusterLayout = + private_cluster_layout(&ABI_ALLOCATION_SIZES_26B123); + +pub(crate) const COMMON26B123_BUNDLE_VIEW_OFFSETS: [usize; 5] = [ + COMMON26B123_SHARED_LAYOUT.block_c18, + COMMON26B123_SHARED_LAYOUT.block_1048, + COMMON26B123_SHARED_LAYOUT.block_e10, + COMMON26B123_SHARED_LAYOUT.block_68, + COMMON26B123_SHARED_LAYOUT.block_800, +]; + +pub(crate) const COMMON26B123_WORK_RING_BUNDLE_OFFSETS: [usize; WORK_CHANNEL_COUNT] = + work_ring_offsets(&COMMON26B123_SHARED_LAYOUT); + +/// Policy 26A428 -> 26B123. +/// +/// - +0x2f08 stays; the accelerator's power-controller words at +0x2f0c..+0x2f17 move by 0x10 +/// and the new field is the u32 at +0x2f28. +/// - +0x2f18..+0x2fb3 holds nothing the host writes in either build. +/// - From +0x2fb4 to the last 26A428 store (+0x6d98, u64) everything moves by 0x18, among them +/// the policy state pointer, the AON PTD pointer and the boot timestamp. The host writes +/// nothing past +0x6da0. +const COMMON26B123_POLICY_BANDS: &[LayoutBand] = &[ + band(0x0000, 0x2f0c, 0, Evidence::ProducerChecked), + band(0x2f0c, 0x2f18, 0x10, Evidence::ProducerChecked), + band(0x2fb4, 0x6da0, 0x18, Evidence::ProducerChecked), +]; +const COMMON26B123_POLICY_NEW_WORDS: &[(usize, u32)] = &[(0x2f28, 5000)]; + +/// Status config 26A428 -> 26B123: +0x59c..+0x5b3 move by 4 and +0x5b4 to the end by 8 (the +/// link word and pointer among them). The new +0x59c stays zero (only the zeroing of +/// +0x590..+0x59f covers it); +0x5b8 is the new accelerator field. +const COMMON26B123_STATUS_CONFIG_BANDS: &[LayoutBand] = &[ + band(0x000, 0x59c, 0, Evidence::ProducerChecked), + band(0x59c, 0x5b4, 4, Evidence::ProducerChecked), + band(0x5b4, 0x6d0, 8, Evidence::ProducerChecked), +]; +const COMMON26B123_STATUS_CONFIG_NEW_WORDS: &[(usize, u32)] = &[(0x5b8, 5000)]; + +/// How a later build moves the blocks the 26A428 encoders produce. +pub(crate) struct Common26Relayout { + policy_bands: &'static [LayoutBand], + policy_new_words: &'static [(usize, u32)], + status_config_bands: &'static [LayoutBand], + status_config_new_words: &'static [(usize, u32)], +} + +/// The layouts of one common-interface build. +pub(crate) struct Common26Layouts { + pub(crate) shared: SharedClusterLayout, + pub(crate) private: PrivateClusterLayout, + pub(crate) policy_size: usize, + pub(crate) status_config_size: usize, + /// None for 26A428, which the encoders produce directly. + relayout: Option, +} + +pub(crate) const COMMON26_LAYOUTS: Common26Layouts = Common26Layouts { + shared: COMMON26_SHARED_LAYOUT, + private: COMMON26_PRIVATE_LAYOUT, + policy_size: COMMON26_POLICY_SIZE, + status_config_size: COMMON26_STATUS_CONFIG_SIZE, + relayout: None, +}; + +pub(crate) const COMMON26B123_LAYOUTS: Common26Layouts = Common26Layouts { + shared: COMMON26B123_SHARED_LAYOUT, + private: COMMON26B123_PRIVATE_LAYOUT, + policy_size: ABI_ALLOCATION_SIZES_26B123[ALLOC_POLICY], + status_config_size: ABI_ALLOCATION_SIZES_26B123[ALLOC_STATUS_CONFIG], + relayout: Some(Common26Relayout { + policy_bands: COMMON26B123_POLICY_BANDS, + policy_new_words: COMMON26B123_POLICY_NEW_WORDS, + status_config_bands: COMMON26B123_STATUS_CONFIG_BANDS, + status_config_new_words: COMMON26B123_STATUS_CONFIG_NEW_WORDS, + }), +}; + +/// The common 26B123 build (macOS 27.2; the J813). +pub(crate) const COMMON26B123_GRAPH: Graph = Graph { + profile: RootProfile::J873G18G26B123, + private: COMMON26B123_PRIVATE_LAYOUT, + shared: COMMON26B123_SHARED_LAYOUT, + policy_size: COMMON26B123_LAYOUTS.policy_size, + bundle_view_offsets: COMMON26B123_BUNDLE_VIEW_OFFSETS, + bundle_view_extents: COMMON26_BUNDLE_VIEW_EXTENTS, + work_ring_offsets: COMMON26B123_WORK_RING_BUNDLE_OFFSETS, + state_grids: state_grids(&COMMON26B123_PRIVATE_LAYOUT), + power_assert_tally: COMMON26_GRAPH.power_assert_tally, +}; + +const _: () = { + let (old, new) = (COMMON26_SHARED_LAYOUT, COMMON26B123_SHARED_LAYOUT); + assert!(new.total == old.total && new.policy == old.policy); + assert!(new.block_68 == old.block_68 + 0x80 && new.main_config[0] == old.main_config[0] + 0x80); + assert!(COMMON26B123_PRIVATE_LAYOUT.total == COMMON26_PRIVATE_LAYOUT.total); + assert!(COMMON26B123_PRIVATE_LAYOUT.status_config == COMMON26_PRIVATE_LAYOUT.status_config); + assert!(COMMON26B123_PRIVATE_LAYOUT.roles[0].status_a == COMMON26_PRIVATE_LAYOUT.roles[0].status_a); + assert!(COMMON26B123_PRIVATE_LAYOUT.policy_state == COMMON26_PRIVATE_LAYOUT.policy_state); + assert!(COMMON26_STATUS_CONFIG_IN_STATUS_B + COMMON26B123_LAYOUTS.status_config_size + <= COMMON26_STATUS_B_SPAN); + assert!(bands_fit(COMMON26B123_POLICY_BANDS, COMMON26_POLICY_SIZE, + COMMON26B123_LAYOUTS.policy_size)); + assert!(bands_fit(COMMON26B123_STATUS_CONFIG_BANDS, COMMON26_STATUS_CONFIG_SIZE, + COMMON26B123_LAYOUTS.status_config_size)); +}; + +/// Whether `bands` are ascending, move no byte down, keep their order once moved and land +/// inside `new_size`, so [`relayout_by_bands`] can move them in place. +const fn bands_fit(bands: &[LayoutBand], old_size: usize, new_size: usize) -> bool { + let mut index = 0; + while index < bands.len() { + let band = bands[index]; + if band.delta < 0 || band.start >= band.end || band.end > old_size + || band.end + band.delta as usize > new_size { + return false; + } + if index > 0 { + let previous = bands[index - 1]; + if previous.end > band.start + || previous.end + previous.delta as usize > band.start + band.delta as usize { + return false; + } + } + index += 1; + } + true +} + +/// Move a block encoded in the 26A428 layout (the first `old_size` bytes of `out`) to the layout +/// `bands` describe, in place, and zero what the bands leave behind. A nonzero byte no band +/// carries has no place in the new layout and is refused. +fn relayout_by_bands(bands: &[LayoutBand], old_size: usize, out: &mut [u8]) + -> Result<(), InitdataError> { + let old = out.get(..old_size).ok_or(InitdataError::BufferSize)?; + if old.iter().enumerate().any(|(offset, byte)| { + *byte != 0 && relocate_by_bands(bands, offset).is_none() + }) { + return Err(InitdataError::InterfaceMismatch); + } + for band in bands.iter().rev() { + let delta = band.delta as usize; + if delta == 0 { + continue; + } + let target = band.start + delta; + if target + (band.end - band.start) > out.len() { + return Err(InitdataError::BufferSize); + } + out.copy_within(band.start..band.end, target); + out[band.start..band.end.min(target)].fill(0); + } + Ok(()) +} + +/// Bring a block the 26A428 encoder produced to the build's layout and set the words the build +/// added. +fn relayout_common26( + bands: &[LayoutBand], + new_words: &[(usize, u32)], + old_size: usize, + out: &mut [u8], +) -> Result<(), InitdataError> { + relayout_by_bands(bands, old_size, out)?; + for &(offset, value) in new_words { + put_u32(out, offset, value); + } + Ok(()) +} + impl Graph { pub(crate) fn is_common26(&self) -> bool { - self.profile == RootProfile::J873G18G26A428 + self.profile.is_common26() } } -/// Encode the common 26A428 primary status-B span: status B, status config and the gap to -/// status A. The status config carries the board's 25G76 words relocated, the fields 26A428 -/// added and its new cross-link to status B. +/// Encode the common primary status-B span: status B, status config and the gap to status A. The +/// status config carries the board's 25G76 words relocated, the fields 26A428 added and its new +/// cross-link to status B, moved to the layout of the build `layouts` describes. fn encode_common26_primary_status_b( + layouts: &Common26Layouts, fwctl_state: u64, fwctl_ring: u64, status_b: u64, @@ -1926,15 +2146,24 @@ fn encode_common26_primary_status_b( required_address(fwctl_ring)?; required_address(status_b)?; let config = COMMON26_STATUS_CONFIG_IN_STATUS_B; + // Where the build keeps the status config's half of the cross-link. + let link_word = match &layouts.relayout { + Some(relayout) => relocate_by_bands(relayout.status_config_bands, + COMMON26_STATUS_CONFIG_LINK_WORD) + .ok_or(InitdataError::InterfaceMismatch)?, + None => COMMON26_STATUS_CONFIG_LINK_WORD, + }; out.fill(0); encode_status_block(&mut out[..STATUS_BLOCK_SIZE])?; put_u64(out, COMMON26_STATUS_B_FWCTL_STATE, fwctl_state); put_u64(out, COMMON26_STATUS_B_FWCTL_RING, fwctl_ring); put_u32(out, COMMON26_STATUS_B_CONFIG_HEADER, 1); put_u32(out, COMMON26_STATUS_B_LINK_WORD, 0); - put_u64(out, COMMON26_STATUS_B_LINK_PTR, - status_b + (config + COMMON26_STATUS_CONFIG_LINK_WORD) as u64); - let status_config = &mut out[config..config + COMMON26_STATUS_CONFIG_SIZE]; + put_u64(out, COMMON26_STATUS_B_LINK_PTR, status_b + (config + link_word) as u64); + // The 26A428 encoding fills the first COMMON26_STATUS_CONFIG_SIZE bytes; a later build's + // layout is made from it below. + let status_config = out.get_mut(config..config + layouts.status_config_size) + .ok_or(InitdataError::BufferSize)?; write_relocated_words(M5_25G76_STATUS_CONFIG_WORDS, 0, COMMON26_STATUS_CONFIG_BANDS, status_config)?; for (offset, value) in COMMON26_STATUS_CONFIG_NEW_WORDS { @@ -1964,18 +2193,24 @@ fn encode_common26_primary_status_b( put_u32(status_config, COMMON26_STATUS_CONFIG_LINK_WORD, 1); put_u64(status_config, COMMON26_STATUS_CONFIG_LINK_PTR, status_b + COMMON26_STATUS_B_LINK_WORD as u64); + if let Some(relayout) = &layouts.relayout { + relayout_common26(relayout.status_config_bands, relayout.status_config_new_words, + COMMON26_STATUS_CONFIG_SIZE, status_config)?; + } Ok(()) } -/// Encode the common 26A428 private cluster mapped at `private_base`: the status-B span, both -/// status A blocks, the secondary root's 0x60 object and one opening-producer entry per control -/// channel. +/// Encode the private cluster of the common build `profile` mapped at `private_base`: the +/// status-B span, both status A blocks, the secondary root's 0x60 object and one opening-producer +/// entry per control channel. pub(crate) fn encode_common26_private_cluster( + profile: RootProfile, private_base: u64, fwctl_state: u64, out: &mut [u8], ) -> Result<(), InitdataError> { - let layout = COMMON26_PRIVATE_LAYOUT; + let layouts = profile.common26_layouts().ok_or(InitdataError::InterfaceMismatch)?; + let layout = layouts.private; if out.len() != layout.total { return Err(InitdataError::BufferSize); } @@ -1995,13 +2230,14 @@ pub(crate) fn encode_common26_private_cluster( out.fill(0); let status_b = layout.status_b; let status_a = layout.roles[0].status_a; - encode_common26_primary_status_b(fwctl_state, fwctl_ring, private_base + status_b as u64, + encode_common26_primary_status_b(layouts, fwctl_state, fwctl_ring, + private_base + status_b as u64, &mut out[status_b..status_a])?; for role in layout.roles { encode_status_block(&mut out[role.status_a..role.status_a + STATUS_BLOCK_SIZE])?; } let extra = layout.secondary_extra_1; - encode_secondary_root_extra_1_for_profile(RootProfile::J873G18G26A428, + encode_secondary_root_extra_1_for_profile(profile, &mut out[extra..extra + STATUS_BLOCK_SIZE])?; put_u32(out, layout.control_state + CHANNEL_STATE_PRODUCER * CHANNEL_STATE_SPACING, CONTROL_OPENING_PRIMARY_PRODUCER); @@ -2010,15 +2246,18 @@ pub(crate) fn encode_common26_private_cluster( Ok(()) } -/// Encode the six common 26A428 blocks after the HW data (0xc18, 0x1048, 0xe10, perf/policy, -/// 0x68, 0x800): the board's 25G76 policy words relocated, the fields 26A428 added and the owned -/// pointers. Preserves the HW data and everything from the BRN table on. +/// Encode the six common blocks after the HW data (0xc18, 0x1048, 0xe10, perf/policy, 0x68, +/// 0x800): the board's 25G76 policy words relocated, the fields 26A428 added and the owned +/// pointers, in the layout of the board's build. Preserves the HW data and everything from the +/// BRN table on. pub(crate) fn encode_common26_bundle_policy( board: &Board, bindings: M5PolicyBindings, out: &mut [u8], ) -> Result<(), InitdataError> { - let layout = COMMON26_SHARED_LAYOUT; + let layouts = board.graph.profile.common26_layouts() + .ok_or(InitdataError::InterfaceMismatch)?; + let layout = layouts.shared; if out.len() < layout.brn_table { return Err(InitdataError::BufferSize); } @@ -2043,7 +2282,10 @@ pub(crate) fn encode_common26_bundle_policy( return Err(InitdataError::BufferSize); } } - let policy = &mut out[layout.policy..layout.policy + COMMON26_POLICY_SIZE]; + // The 26A428 encoding fills the first COMMON26_POLICY_SIZE bytes; a later build's layout is + // made from it below. + let policy = out.get_mut(layout.policy..layout.policy + layouts.policy_size) + .ok_or(InitdataError::BufferSize)?; for &(offset, value) in M5_25G76_POLICY_WORDS { if (m5.policy..m5_policy_end).contains(&offset) { write_relocated_words(&[(offset, value)], m5.policy, COMMON26_POLICY_BANDS, policy)?; @@ -2067,6 +2309,10 @@ pub(crate) fn encode_common26_bundle_policy( put_u64(policy, COMMON26_POLICY_STATE_PTR, bindings.state); put_u64(policy, COMMON26_POLICY_AON_PTD_PTR, bindings.aon_ptd); put_u64(policy, COMMON26_POLICY_BOOT_TIMESTAMP, bindings.boot_timestamp); + if let Some(relayout) = &layouts.relayout { + relayout_common26(relayout.policy_bands, relayout.policy_new_words, COMMON26_POLICY_SIZE, + policy)?; + } Ok(()) } @@ -2311,6 +2557,12 @@ pub(crate) const J813: Board = Board { completion_inputs: false, }; +/// The J813 on its macOS 27.2 (26B123) firmware: the same board on the common 26B123 graph. +pub(crate) const J813_26B123: Board = Board { + graph: &COMMON26B123_GRAPH, + ..J813 +}; + const _: () = { use super::target::min_parameter_buffer_pages; assert!(J714.min_parameter_buffer as u64 == min_parameter_buffer_pages(J714.clusters) << 15); diff --git a/drivers/gpu/drm/asahi/g17/initdata/m5.rs b/drivers/gpu/drm/asahi/g17/initdata/m5.rs index 5c3200fefce19..9352e5ec32f3e 100644 --- a/drivers/gpu/drm/asahi/g17/initdata/m5.rs +++ b/drivers/gpu/drm/asahi/g17/initdata/m5.rs @@ -205,7 +205,7 @@ impl InitData { m::encode_common26_board_region_c( &mut self.object_mut(self.region_c)?.bytes_mut()[..m::REGION_C_SIZE]) .map_err(|_| EINVAL)?; - m::encode_common26_private_cluster(PRIVATE, FWCTL, + m::encode_common26_private_cluster(board.graph.profile, PRIVATE, FWCTL, &mut self.object_mut(self.cluster)?.bytes_mut()[..private.total]) .map_err(|_| EINVAL)?; } else { diff --git a/drivers/gpu/drm/asahi/hw/t8142.rs b/drivers/gpu/drm/asahi/hw/t8142.rs index 26fe2e49abcdb..eae2960d74fab 100644 --- a/drivers/gpu/drm/asahi/hw/t8142.rs +++ b/drivers/gpu/drm/asahi/hw/t8142.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT -//! M5 (T8142, MacBook Air J813) on the macOS 27.0 (26A428) GPU firmware. +//! M5 (T8142, MacBook Air J813) on the macOS 27.0 (26A428) and 27.2 (26B123) GPU firmware. //! //! Bring-up only: the M5 graph ([`crate::g17::fw::m5::initdata`]) has no J813 board yet, so //! [`PROBE`] stops at the identity check, after `get_g17_id` has logged the ID registers and @@ -57,8 +57,15 @@ pub(crate) const PROBE: Config = Config { }, }; -/// The J813 on the M5 graph: [`PROBE`] with the GPU's identity and the J813 board. +/// The J813 on the M5 graph: [`PROBE`] with the GPU's identity and the J813 board. It runs +/// either firmware build of [`BUILDS`]; 26B123 runs [`CONFIG_26B123`]. pub(crate) const CONFIG: Config = Config { + firmware: crate::g17::fw::variant::Firmware::Identified(&BUILDS), + ..BASE +}; + +/// The J813 board and identity, before a firmware build is chosen. +const BASE: Config = Config { identity: Identity { family: 0xb, variant: 2, @@ -79,6 +86,72 @@ pub(crate) const CONFIG: Config = Config { ..PROBE }; +/// The J813 on the 27.2 firmware: the board on the common 26B123 graph. +pub(crate) const CONFIG_26B123: Config = Config { + m5: Some(&crate::g17::fw::m5::initdata::J813_26B123), + firmware: crate::g17::fw::variant::Firmware::Identified(&[BUILD_26B123]), + ..BASE +}; + +/// The firmware in `fw-text` must be one of these builds. iBoot loads the G17G c000 image of the +/// primary (TEXT 0x50000) and the c010 image of the secondary (0x4c000) back to back. The digest +/// is that of both TEXTs with the entropy records and the ranges iBoot fills zeroed (the image +/// header's load fields at +0x224 and +0x23c, the 0x740 bytes of boot arguments before each +/// role's GKTS record and the 0x231 bytes of records from it); it is that of the stock images +/// in the IPSW with the same ranges zeroed, and was measured on two 27.0 boots and one 27.2 boot. +const BUILDS: [crate::g17::fw::variant::Build; 2] = [ + BUILD_26A428, + crate::g17::fw::variant::Build { + config: Some(&CONFIG_26B123), + ..BUILD_26B123 + }, +]; + +const BUILD_26A428: crate::g17::fw::variant::Build = crate::g17::fw::variant::Build { + os_version: [27, 0, 0], + name: "27.0 (26A428), G17G", + images: &[crate::fwid::FirmwareBuild { + name: "26A428 for T8142 G17G, primary then secondary", + text_size: 0x9c000, + data_size: 0x184000, + entropy: &[[0x49d10, 0x49df3], [0x997d0, 0x998b3]], + boot_filled: &[ + (0x224, 4), (0x23c, 8), (0x495d0, 0x740), (0x49d10, 0x231), + (0x50224, 4), (0x5023c, 8), (0x99090, 0x740), (0x997d0, 0x231), + ], + text_sha256: [ + 0xec, 0xff, 0x40, 0xe3, 0xad, 0xb8, 0x9c, 0x90, 0x62, 0xe9, 0x1d, 0xb6, 0x3f, + 0x31, 0xba, 0xaf, 0x75, 0xeb, 0x5d, 0x3f, 0x97, 0x22, 0xe3, 0x06, 0x95, 0x45, + 0x3f, 0xad, 0x02, 0xfc, 0xf5, 0x0c, + ], + }], + variant: &crate::g17::fw::variant::V26A428_M5, + config: None, +}; + +/// RTKit 3514.41.1: the 26A428 protocol on the 26B123 InitData graph. +const BUILD_26B123: crate::g17::fw::variant::Build = crate::g17::fw::variant::Build { + os_version: [27, 2, 0], + name: "27.2 (26B123), G17G", + images: &[crate::fwid::FirmwareBuild { + name: "26B123 for T8142 G17G, primary then secondary", + text_size: 0x9c000, + data_size: 0x184000, + entropy: &[[0x49d54, 0x49e37], [0x997e4, 0x998c7]], + boot_filled: &[ + (0x224, 4), (0x23c, 8), (0x49614, 0x740), (0x49d54, 0x231), + (0x50224, 4), (0x5023c, 8), (0x990a4, 0x740), (0x997e4, 0x231), + ], + text_sha256: [ + 0xe3, 0xab, 0x32, 0x7b, 0x73, 0x1b, 0xf2, 0xf1, 0x74, 0x9b, 0x04, 0x44, 0x1f, + 0x5c, 0xb5, 0xab, 0xfc, 0x23, 0xf3, 0x41, 0xc3, 0x99, 0x72, 0x8e, 0x2c, 0xb4, + 0x6f, 0x68, 0xb1, 0x7a, 0x39, 0xb9, + ], + }], + variant: &crate::g17::fw::variant::V26A428_M5, + config: None, +}; + // Each compute operand bank holds 6 KiB per core slot of the board. const _: () = assert!( CONFIG.core_layout.operand_state.bank_bytes()