From 32a2e699a479ad3003e819cee0ee86106eb33d59 Mon Sep 17 00:00:00 2001 From: Chris Kearney <303316+chriskearney@users.noreply.github.com> Date: Thu, 5 Nov 2026 07:36:28 -0800 Subject: [PATCH 01/10] drm/asahi: add a measured J514M T6034 GPU profile The 14-core M3 Max reports T6034, with four ten-slot G15C clusters and enabled masks yielding 8, 8, 7 and 7 cores. Add an exact J514M/T6034 OF profile, the measured identity pair and B1 silicon revision, and a six-element memory-cache IOMapping with repacked following VAs. Preserve the shared four-cluster command shape and explicitly warn about the inherited, unqualified J516C power reference words. Propagate the SoC identity into the hardware record and require the separately published T6034 native display before admission. No enable parameter is needed. The firmware build list is intentionally empty until the normal kernel path measures the canonical loaded TEXT digest; empty profiles cannot use the generic prefix escape hatch and cannot start firmware. Host validation compiles the actual profiles and firmware admission, checks record identity and six-channel mapping, and exercises display ordering and wrong board/SoC refusals with compiled mutants. Existing full T6031 firmware identities remain unchanged. Hardware startup and full ARM64 package validation remain for the coordinated lab run. Assisted-by: Codex:GPT-6 Signed-off-by: Scott Jones --- drivers/gpu/drm/asahi/g15/board.rs | 1 + drivers/gpu/drm/asahi/g15/board/record.rs | 34 +++++- drivers/gpu/drm/asahi/g15/boot.rs | 15 ++- drivers/gpu/drm/asahi/g15/glue.c | 30 ++++-- drivers/gpu/drm/asahi/g15/hw.rs | 2 + drivers/gpu/drm/asahi/g15/hw/t6034.rs | 85 +++++++++++++++ drivers/gpu/drm/asahi/g15/identity.rs | 9 +- drivers/gpu/drm/asahi/g15/mod.rs | 7 +- tools/asahi/g15-display-order-tests.py | 82 +++++++++++--- tools/asahi/g15-t6034-profile-tests.py | 124 ++++++++++++++++++++++ tools/asahi/g15-zero-words-tests.py | 8 +- 11 files changed, 367 insertions(+), 30 deletions(-) create mode 100644 drivers/gpu/drm/asahi/g15/hw/t6034.rs create mode 100644 tools/asahi/g15-t6034-profile-tests.py diff --git a/drivers/gpu/drm/asahi/g15/board.rs b/drivers/gpu/drm/asahi/g15/board.rs index f9682b2ead10c2..5bb672fce6c495 100644 --- a/drivers/gpu/drm/asahi/g15/board.rs +++ b/drivers/gpu/drm/asahi/g15/board.rs @@ -158,6 +158,7 @@ pub(crate) fn hardware_record( }; } let inputs = record::Inputs { + chip_id: cfg.chip_id, props, zones: &zones, main: &main, diff --git a/drivers/gpu/drm/asahi/g15/board/record.rs b/drivers/gpu/drm/asahi/g15/board/record.rs index 119a6920643914..e20116f513a16e 100644 --- a/drivers/gpu/drm/asahi/g15/board/record.rs +++ b/drivers/gpu/drm/asahi/g15/board/record.rs @@ -183,6 +183,8 @@ pub(crate) struct IoSlot { /// Everything the record is built from. pub(crate) struct Inputs<'a> { + /// SoC identity of this G15C record. + pub(crate) chip_id: u32, pub(crate) props: [u32; PROPS.len()], /// `apple,power-zones` cells (triples). pub(crate) zones: &'a [u32], @@ -458,7 +460,7 @@ fn hwdata_b(out: &mut [u8], i: &Inputs<'_>, t: &Tables) -> R<()> { put32(out, IOMAPS + 32 * slot as usize + 0x18, 1)?; } // Identity and flags. - put32(out, 0xa28, 0x6031)?; + put32(out, 0xa28, i.chip_id)?; put32(out, 0xa2c, 1)?; put32(out, 0xa30, 2)?; for at in [0xa38, 0xa3c, 0xa40, 0xa50, 0xa58, 0xa60] { @@ -848,6 +850,9 @@ pub(crate) fn cross_check(built: &[u8], file: &[u8]) -> Option<(usize, u32, u32) /// Builds the record into `out` (zeroed first) from the inputs. pub(crate) fn build(out: &mut [u8], i: &Inputs<'_>) -> R<()> { + if !matches!(i.chip_id, 0x6031 | 0x6034) { + return Err("unsupported G15C record SoC"); + } if out.len() != SIZE { return Err("record of another size"); } @@ -950,6 +955,7 @@ mod tests { fn inputs<'a>(main: &'a [Opp], cs: &'a [Opp], slots: &'a [IoSlot]) -> Inputs<'a> { Inputs { + chip_id: 0x6031, props: props(), zones: &[], main, @@ -966,6 +972,31 @@ mod tests { } } + #[test] + fn t6034_identity_and_six_memory_channels() { + let main = main_opps(); + let cs = [opp(24, [635; CLUSTERS])]; + let slots = [IoSlot { + slot: 11, + pa: 0x220000000, + va: 0xfffffc2068000000, + total: 6 * 0xd8000, + element: 0xd8000, + writable: true, + }]; + let mut input = inputs(&main, &cs, &slots); + input.chip_id = 0x6034; + let mut out = vec![0u8; SIZE]; + build(&mut out, &input).unwrap(); + assert_eq!(get(&out, 0xa28), 0x6034); + assert_eq!(get(&out, IOMAPS + 32 * 11 + 0x10), 6 * 0xd8000); + assert_eq!(get(&out, IOMAPS + 32 * 11 + 0x14), 0xd8000); + // The fused 30-core part still has four ten-slot physical clusters. + assert_eq!(get(&out, 0xb54), 40); + input.chip_id = 0x6030; + assert_eq!(build(&mut out, &input), Err("unsupported G15C record SoC")); + } + #[test] fn factors() { assert_eq!(F_1, 1.0f32.to_bits()); @@ -1500,6 +1531,7 @@ mod tests { }) .collect(); let i = Inputs { + chip_id: 0x6031, props, zones: &zones, main: &main, diff --git a/drivers/gpu/drm/asahi/g15/boot.rs b/drivers/gpu/drm/asahi/g15/boot.rs index 3b7daf647211a4..6a7c1f0aec0480 100644 --- a/drivers/gpu/drm/asahi/g15/boot.rs +++ b/drivers/gpu/drm/asahi/g15/boot.rs @@ -469,13 +469,17 @@ pub(crate) fn admit( let pmp = cfg.platform == hw::Platform::AdtWithPmp; // J514C uses the same-die T6031 path on a normal boot. This does not qualify // the inherited J516C reference words. Keep exact board AND SoC checks; - // T6034 boards must never inherit this four-cluster T6031 configuration. + // T6034 boards use their own profile and never inherit T6031 admission. // The remaining display, DT/ADT, full firmware identity, hardware identity // and record-builder admission below is unchanged. let j514c_board = cfg.chip_id == 0x6031 && cfg.platform == hw::Platform::DeviceTree && glue::machine(kernel::c_str!("apple,j514c")) && glue::machine(kernel::c_str!("apple,t6031")); + let j514m_board = cfg.chip_id == 0x6034 + && cfg.platform == hw::Platform::DeviceTree + && glue::machine(kernel::c_str!("apple,j514m")) + && glue::machine(kernel::c_str!("apple,t6034")); if pmp { if !glue::board() { return Err(ENODEV); @@ -490,7 +494,9 @@ pub(crate) fn admit( } return Err(err); } - } else if !glue::machine(cfg.machine) && !j514c_board { + } else if (cfg.chip_id == 0x6034 && !j514m_board) + || (!glue::machine(cfg.machine) && !j514c_board) + { dev_err!(dev, "G15: not a {} machine\n", cfg.name); return Err(ENODEV); } else { @@ -505,11 +511,12 @@ pub(crate) fn admit( return Err(err); } } - if j514c_board { + if j514c_board || j514m_board { if let hw::Board::DeviceTree(reference) = cfg.board { dev_warn!( dev, - "J514C bring-up: unqualified J516C reference: max_power_mw={} relative_power={:?} fast_die_mask={:#018x} alarm_mask={:#018x}; OPP powers are boot-loader stand-ins\n", + "{} bring-up: unqualified J516C reference: max_power_mw={} relative_power={:?} fast_die_mask={:#018x} alarm_mask={:#018x}; OPP powers are boot-loader stand-ins\n", + cfg.name, reference.max_power_mw, reference.relative_power, reference.fast_die_mask, diff --git a/drivers/gpu/drm/asahi/g15/glue.c b/drivers/gpu/drm/asahi/g15/glue.c index 8e1767ea61912c..4a3dac630520df 100644 --- a/drivers/gpu/drm/asahi/g15/glue.c +++ b/drivers/gpu/drm/asahi/g15/glue.c @@ -54,6 +54,7 @@ void asahi_g15_report_loader_gpu(void) const char *compatible; } socs[] = { { "asahi,t6031-gpu", "apple,agx-t6031" }, + { "asahi,t6034-gpu", "apple,agx-t6034" }, { "asahi,t6030-gpu", "apple,agx-t6030" }, }; struct device_node *chosen, *np; @@ -144,7 +145,7 @@ int asahi_g15_pmp_prepare(struct device *dev) } /* - * T6031 has no PMP vote, but its first firmware start follows the native + * T6031/T6034 have no PMP vote, but its first firmware start follows the native * display as T6030's does: defer until that display is up. The display can * be off for this boot (no handoff, or not requested), so say once why. An * enabled display that is not published yet is said once too: if it never @@ -154,18 +155,35 @@ int asahi_g15_pmp_prepare(struct device *dev) int asahi_g15_display_prepare(struct device *dev) { struct device_node *np; + const char *compatible, *name; int ret; - if (!dev->of_node || !of_device_is_compatible(dev->of_node, "apple,agx-t6031")) + if (!dev->of_node) return -ENODEV; - ret = apple_drm_t6031_link_device(dev); + if (of_device_is_compatible(dev->of_node, "apple,agx-t6031")) { + compatible = "apple,t6031-display-subsystem"; + name = "T6031"; + ret = apple_drm_t6031_link_device(dev); + } else if (of_device_is_compatible(dev->of_node, "apple,agx-t6034")) { + compatible = "apple,t6034-display-subsystem"; + name = "T6034"; + ret = apple_drm_t6034_link_device(dev); + } else { + return -ENODEV; + } if (ret != -EPROBE_DEFER) return ret; - np = of_find_compatible_node(NULL, NULL, "apple,t6031-display-subsystem"); + + np = of_find_compatible_node(NULL, NULL, compatible); if (!np || !of_device_is_available(np)) - dev_info_once(dev, "G15: waiting for the T6031 native display, which this boot did not enable (it needs the boot loader's display handoff, and an admitted native-display board)\n"); + dev_info_once(dev, + "G15: waiting for the %s native display, which this boot did not enable (it needs the boot loader's display handoff, and an admitted native-display board)\n", + name); else - dev_info_once(dev, "G15: waiting for the T6031 native display to come up: the GPU firmware starts only after the display is published\n"); + dev_info_once(dev, + "G15: waiting for the %s native display to come up: the GPU firmware starts only after the display is published\n", + name); + of_node_put(np); return ret; } diff --git a/drivers/gpu/drm/asahi/g15/hw.rs b/drivers/gpu/drm/asahi/g15/hw.rs index 88a402fe19551d..847ab6685444ad 100644 --- a/drivers/gpu/drm/asahi/g15/hw.rs +++ b/drivers/gpu/drm/asahi/g15/hw.rs @@ -9,6 +9,8 @@ use core::ops::Range; #[cfg(CONFIG_DRM_ASAHI_G15)] pub(crate) mod t6031; +#[cfg(CONFIG_DRM_ASAHI_G15)] +pub(crate) mod t6034; /// Identification register values of a qualified G15S. pub(crate) struct HwId { diff --git a/drivers/gpu/drm/asahi/g15/hw/t6034.rs b/drivers/gpu/drm/asahi/g15/hw/t6034.rs new file mode 100644 index 00000000000000..750b75f9e03ae0 --- /dev/null +++ b/drivers/gpu/drm/asahi/g15/hw/t6034.rs @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT + +//! J514M/T6034 (M3 Max, G15C) measurement profile. +//! +//! The powered identity read on this J514M found four ten-slot clusters with +//! enabled masks [0x1fd, 0x1fd, 0x1ed, 0x1ed], or 30 enabled cores. Its ADT has +//! four voltage tables and six memory channels. The four-cluster G15C object +//! and command layouts below are inherited from T6031; they are not a native +//! macOS capture of this machine. The J516C power/reference words remain +//! unqualified for this board and are identified at admission. +//! +//! No loaded firmware digest has been measured by the kernel yet. An empty +//! build list permits the normal admission path to report that identity and +//! refuses firmware startup, including the generic prefix escape hatch. + +use super::super::Config; +use super::{HwId, IoMap, Layout}; + +/// Six memory-cache elements, rather than the T6031 reference's eight. +const MCC_BYTES: u64 = 6 * 0xd_8000; +const IOMAP_GAP: u64 = 0x4000; + +/// Keep the shared G15C physical apertures and pack firmware VAs after the +/// smaller memory-cache aperture. No seventh or eighth element is mapped. +const fn iomaps() -> [IoMap; 17] { + let mut out = super::t6031::IOMAPS; + let mut va = out[0].va; + let mut k = 0; + while k < out.len() { + if out[k].slot == 11 { + out[k].map_bytes = MCC_BYTES; + } + out[k].va = va; + va += out[k].map_bytes + IOMAP_GAP; + k += 1; + } + out +} + +pub(crate) const IOMAPS: [IoMap; 17] = iomaps(); + +const fn record_slots() -> [(u32, u32, bool); 17] { + let mut out = [(0, 0, false); 17]; + let mut k = 0; + while k < out.len() { + out[k] = super::t6031::LAYOUT.record_slots[k]; + if IOMAPS[k].slot == 11 { + out[k].0 = MCC_BYTES as u32; + } + k += 1; + } + out +} + +const RECORD_SLOTS: [(u32, u32, bool); 17] = record_slots(); + +pub(crate) const LAYOUT: Layout = Layout { + iomaps: &IOMAPS, + record_slots: &RECORD_SLOTS, + ..super::t6031::LAYOUT +}; + +pub(crate) const CONFIG: Config = Config { + name: "T6034 G15C", + chip_id: 0x6034, + gpu_variant: crate::hw::GpuVariant::C, + // ID_VERSION's silicon revision byte: (0x07041100 >> 8) & 0xff. + gpu_revision: 0x11, + uat_ias: 42, + uat_oas: 42, + num_clusters: 4, + num_cores_per_cluster: 10, + hw_ids: &[HwId { + version: 0x07041100, + counts: 0x0021040a, + }], + firmware: &[], + machine: kernel::c_str!("apple,j514m"), + platform: super::Platform::DeviceTree, + layout: &LAYOUT, + board: super::Board::DeviceTree(&super::super::board::record::J516C), + // Binning changes enabled masks, not the four physical clusters or the + // ten slots per cluster used by these G15C command words. + compute: super::t6031::CONFIG.compute, +}; diff --git a/drivers/gpu/drm/asahi/g15/identity.rs b/drivers/gpu/drm/asahi/g15/identity.rs index 2bf95c8ad98dbe..c9d8b264c8491a 100644 --- a/drivers/gpu/drm/asahi/g15/identity.rs +++ b/drivers/gpu/drm/asahi/g15/identity.rs @@ -97,7 +97,14 @@ pub(crate) fn firmware( let qualified = builds .iter() .map(|b| (Measured::from(b), b.machine.map_or(true, glue::machine))); - match lookup::verdict(&m, qualified, super::params::accept_text_prefix()) { + // An incomplete SoC profile is measurement-only, even if a generic + // bring-up prefix remains on the command line. It must first pin a build. + let accept_prefix = if builds.is_empty() { + 0 + } else { + super::params::accept_text_prefix() + }; + match lookup::verdict(&m, qualified, accept_prefix) { Verdict::Pinned(i) => { let build = builds.get(i).ok_or(EINVAL)?; dev_info!(dev, "GPU firmware build: {}\n", build.name); diff --git a/drivers/gpu/drm/asahi/g15/mod.rs b/drivers/gpu/drm/asahi/g15/mod.rs index 5228c145bb87cc..f8afd4fc7d2acd 100644 --- a/drivers/gpu/drm/asahi/g15/mod.rs +++ b/drivers/gpu/drm/asahi/g15/mod.rs @@ -140,7 +140,7 @@ impl Config { } /// OF match table of the G15 platform driver. -const OF_TABLE: kernel::device_id::IdArray = +const OF_TABLE: kernel::device_id::IdArray = kernel::device_id::IdArray::new([ ( of::DeviceId::new(c_str!("apple,agx-t6030")), @@ -150,6 +150,10 @@ const OF_TABLE: kernel::device_id::IdArray = of::DeviceId::new(c_str!("apple,agx-t6031")), &hw::t6031::CONFIG, ), + ( + of::DeviceId::new(c_str!("apple,agx-t6034")), + &hw::t6034::CONFIG, + ), ]); // Probe admission requires the native display before firmware starts (T6030 @@ -161,6 +165,7 @@ kernel::of_device_table!( [ (of::DeviceId::new(c_str!("apple,agx-t6030")), ()), (of::DeviceId::new(c_str!("apple,agx-t6031")), ()), + (of::DeviceId::new(c_str!("apple,agx-t6034")), ()), ] ); diff --git a/tools/asahi/g15-display-order-tests.py b/tools/asahi/g15-display-order-tests.py index 202a63e59f1e54..18f7ea4da41db5 100644 --- a/tools/asahi/g15-display-order-tests.py +++ b/tools/asahi/g15-display-order-tests.py @@ -39,8 +39,9 @@ def c_source(root): state = [line for line in apple.splitlines() if line in ("static DEFINE_MUTEX(apple_g15_display_lock);", "static struct device *apple_t6030_display;", - "static struct device *apple_t6031_display;")] - assert len(state) == 3, f"publication state not found: {state}" + "static struct device *apple_t6031_display;", + "static struct device *apple_t6034_display;")] + assert len(state) == 4, f"publication state not found: {state}" bodies = [item(apple, marker) for marker in ( "\nstatic struct device **apple_g15_display_slot(struct device *dev)\n{", "\nstatic int apple_g15_display_publish(struct device *dev)\n{", @@ -48,6 +49,7 @@ def c_source(root): "\nstatic int apple_g15_link_device(", "\nint apple_drm_t6030_link_device(struct device *consumer)\n{", "\nint apple_drm_t6031_link_device(struct device *consumer)\n{", + "\nint apple_drm_t6034_link_device(struct device *consumer)\n{", )] bodies.append(item(glue, "\nint asahi_g15_display_prepare(struct device *dev)\n{")) return "\n".join(state) + "\n\n" + "\n\n".join(bodies) + "\n" @@ -73,7 +75,7 @@ def c_source(root): struct mutex { bool held; }; static const char *machine_compat[2]; -static struct device_node *nodes[4]; +static struct device_node *nodes[6]; static int links_made, link_fail, infos; static struct device_link last_link; @@ -92,7 +94,7 @@ def c_source(root): const char *compat) { (void)from; (void)type; - for (int i = 0; i < 4; i++) + for (int i = 0; i < 6; i++) if (nodes[i] && !strcmp(nodes[i]->compatible, compat)) { nodes[i]->refs++; return nodes[i]; @@ -112,7 +114,7 @@ def c_source(root): last_link = (struct device_link){ consumer, supplier, flags }; return &last_link; } -#define dev_info_once(dev, ...) do { static bool once_; (void)(dev); if (!once_) { once_ = true; infos++; } } while (0) +#define dev_info_once(dev, ...) do { static bool once_; (void)(dev); if (false) fprintf(stderr, __VA_ARGS__); if (!once_) { once_ = true; infos++; } } while (0) static void unlock_(struct mutex **m) { if (*m) { assert((*m)->held); (*m)->held = false; } } static struct mutex *lock_(struct mutex *m) { assert(!m->held); m->held = true; return m; } @@ -128,6 +130,9 @@ def c_source(root): static struct device_node gpu31 = { "apple,agx-t6031", true }, gpu30 = { "apple,agx-t6030", true }; static struct device_node disp31 = { "apple,t6031-display-subsystem", false }; static struct device_node disp30 = { "apple,t6030-display-subsystem", true }; +static struct device_node gpu34 = { "apple,agx-t6034", true }; +static struct device_node disp34 = { "apple,t6034-display-subsystem", true }; +static struct device g34 = { &gpu34 }, d34 = { &disp34 }; static struct device_node other = { "apple,display-subsystem", true }; static struct device g31 = { &gpu31 }, g30 = { &gpu30 }, none = { NULL }; static struct device d31 = { &disp31 }, d31b = { &disp31 }, d30 = { &disp30 }, dother = { &other }; @@ -140,7 +145,7 @@ def c_source(root): static void clean(void) { CHECK(!apple_g15_display_lock.held); - for (int i = 0; i < 4; i++) + for (int i = 0; i < 6; i++) CHECK(!nodes[i] || nodes[i]->refs == 0); } static int prepare(struct device *dev) { int ret = asahi_g15_display_prepare(dev); clean(); return ret; } @@ -157,6 +162,8 @@ def c_source(root): nodes[1] = &disp31; nodes[2] = &disp30; nodes[3] = &other; + nodes[4] = &gpu34; + nodes[5] = &disp34; if (!strcmp(scenario, "disabled")) { /* The gate left the display disabled: no start, no success, said once. */ CHECK(prepare(&g31) == -EPROBE_DEFER && infos == 1); @@ -206,6 +213,20 @@ def c_source(root): withdraw(&d30); CHECK(d30.refs == 0 && link30(&g30) == -EPROBE_DEFER); CHECK(link30(&g31) == -ENODEV); + } else if (!strcmp(scenario, "t6034")) { + /* A published T6031 display never admits the separate T6034 GPU. */ + CHECK(publish(&d31) == 0); + machine("apple,t6034", "apple,j514m"); + CHECK(prepare(&g34) == -EPROBE_DEFER && links_made == 0); + CHECK(publish(&d34) == 0 && d34.refs == 1); + CHECK(prepare(&g34) == 0 && links_made == 1 && last_link.supplier == &d34); + machine("apple,t6034", "apple,j516m"); + CHECK(prepare(&g34) == -ENODEV && links_made == 1); + machine("apple,t6031", "apple,j514m"); + CHECK(prepare(&g34) == -ENODEV && links_made == 1); + machine("apple,t6034", "apple,j514m"); + withdraw(&d34); + CHECK(!d34.refs && prepare(&g34) == -EPROBE_DEFER); } else { CHECK(!"unknown scenario"); } @@ -213,13 +234,16 @@ def c_source(root): } ''' -C_SCENARIOS = ["disabled", "absent", "pending", "ready", "isolation", "foreign", "t6030"] +C_SCENARIOS = ["disabled", "absent", "pending", "ready", "isolation", "foreign", "t6030", "t6034"] C_MUTANTS = { "unpublished-succeeds": ("if (!*display)\n\t\treturn -EPROBE_DEFER;", "if (!*display)\n\t\treturn 0;"), "disabled-display-skipped": (re.compile(r"\t\tdev_info_once\(dev,[^;]*;\n"), "\t\t{ of_node_put(np); return 0; }\n"), "pending-display-silent": (re.compile(r"\telse\n\t\tdev_info_once\(dev,[^;]*;\n"), ""), "t6031-reads-t6030-slot": ("&apple_t6031_display);", "&apple_t6030_display);"), + "t6034-reads-t6031-slot": ("&apple_t6034_display);", "&apple_t6031_display);"), + "t6034-published-as-t6031": ("return &apple_t6034_display;", "return &apple_t6031_display;"), + "t6034-no-board-check": ('if (!of_machine_is_compatible("apple,j514m"))', 'if (false)'), "t6031-published-as-t6030": ("return &apple_t6031_display;", "return &apple_t6030_display;"), "no-machine-check": ("!of_machine_is_compatible(machine) ||", ""), "withdraw-keeps-display": ("*slot = NULL;", ";"), @@ -243,10 +267,10 @@ def c_source(root): macro_rules! dev_err { ($($args:tt)*) => {{}}; } macro_rules! dev_info { ($($args:tt)*) => {{}}; } macro_rules! dev_warn { ($dev:expr, $($args:tt)*) => {{ warned(format!($($args)*)); }}; } -struct State { board: bool, machine: bool, pmp: i32, display: i32, sleep_hold: bool, asc_fail: bool, j514c: bool, t6031: bool } +struct State { board: bool, machine: bool, pmp: i32, display: i32, sleep_hold: bool, asc_fail: bool, j514c: bool, t6031: bool, j514m: bool, t6034: bool } thread_local! { static CALLS: RefCell> = const { RefCell::new(Vec::new()) }; - static STATE: RefCell = const { RefCell::new(State { board: true, machine: true, pmp: 0, display: 0, sleep_hold: true, asc_fail: false, j514c: false, t6031: false }) }; + static STATE: RefCell = const { RefCell::new(State { board: true, machine: true, pmp: 0, display: 0, sleep_hold: true, asc_fail: false, j514c: false, t6031: false, j514m: false, t6034: false }) }; static HOLD: RefCell> = const { RefCell::new(None) }; static WARNINGS: RefCell = const { RefCell::new(0) }; static WARNING_TEXT: RefCell = const { RefCell::new(String::new()) }; @@ -301,7 +325,7 @@ def c_source(root): mod glue { use super::{call, STATE}; pub fn board() -> bool { call("board"); STATE.with(|s| s.borrow().board) } - pub fn machine(name: &str) -> bool { if name != "apple,j514c" && name != "apple,t6031" { call("machine"); } STATE.with(|s| { let s=s.borrow(); match name { "apple,j514c" => s.j514c, "apple,t6031" => s.t6031, _ => s.machine } }) } + pub fn machine(name: &str) -> bool { if !["apple,j514c", "apple,t6031", "apple,j514m", "apple,t6034"].contains(&name) { call("machine"); } STATE.with(|s| { let s=s.borrow(); match name { "apple,j514c" => s.j514c, "apple,t6031" => s.t6031, "apple,j514m" => s.j514m, "apple,t6034" => s.t6034, _ => s.machine } }) } pub unsafe fn asahi_g15_pmp_prepare(_: *mut core::ffi::c_void) -> core::ffi::c_int { call("pmp"); STATE.with(|s| s.borrow().pmp) } pub unsafe fn asahi_g15_display_prepare(_: *mut core::ffi::c_void) -> core::ffi::c_int { call("display"); STATE.with(|s| s.borrow().display) } pub fn dma_coherent(_: &super::device::Device) -> bool { call("coherent"); false } @@ -316,7 +340,7 @@ def c_source(root): const TAIL: [&str; 6] = ["adt", "identity", "coherent", "board_images", "regs", "hold"]; fn run(cfg: &'static Config, machine: bool, pmp: i32, display: i32) -> (Result<()>, Vec<&'static str>) { CALLS.with(|c| c.borrow_mut().clear()); - STATE.with(|s| *s.borrow_mut() = State { board: true, machine, pmp, display, sleep_hold: true, asc_fail: false, j514c: false, t6031: false }); + STATE.with(|s| *s.borrow_mut() = State { board: true, machine, pmp, display, sleep_hold: true, asc_fail: false, j514c: false, t6031: false, j514m: false, t6034: false }); HOLD.with(|h| *h.borrow_mut() = None); WARNINGS.with(|w| *w.borrow_mut() = 0); let pdev = platform::Device(device::Device, Core(PhantomData)); @@ -327,7 +351,7 @@ def c_source(root): @SLEEP_HOLD_RUN@ fn candidate_for(cfg: &'static Config, board: bool, soc: bool, display: i32) -> (Result<()>, Vec<&'static str>, u32) { CALLS.with(|c| c.borrow_mut().clear()); - STATE.with(|s| *s.borrow_mut() = State { board: true, machine: false, pmp: 0, display, sleep_hold: true, asc_fail: false, j514c: board, t6031: soc }); + STATE.with(|s| *s.borrow_mut() = State { board: true, machine: false, pmp: 0, display, sleep_hold: true, asc_fail: false, j514c: board, t6031: soc, j514m: false, t6034: false }); WARNINGS.with(|w| *w.borrow_mut() = 0); let pdev = platform::Device(device::Device, Core(PhantomData)); let result = admit(&pdev, &AsahiDevice, cfg).map(|_| ()); @@ -337,7 +361,34 @@ def c_source(root): candidate_for(&T6031, board, soc, display) } static T6034: Config = Config { chip_id: 0x6034, platform: hw::Platform::DeviceTree, machine: "apple,j514m", name: "T6034", firmware: (), board: hw::Board::DeviceTree(&board::record::J516C) }; +fn candidate34(board: bool, soc: bool, display: i32) -> (Result<()>, Vec<&'static str>, u32) { + CALLS.with(|c| c.borrow_mut().clear()); + STATE.with(|s| *s.borrow_mut() = State { board: true, machine: false, pmp: 0, display, + sleep_hold: true, asc_fail: false, j514c: false, t6031: false, j514m: board, t6034: soc }); + WARNINGS.with(|w| *w.borrow_mut() = 0); + let pdev = platform::Device(device::Device, Core(PhantomData)); + let result = admit(&pdev, &AsahiDevice, &T6034).map(|_| ()); + (result, CALLS.with(|c| c.borrow().clone()), WARNINGS.with(|w| *w.borrow())) +} +static FOREIGN: Config = Config { chip_id: 0x6030, platform: hw::Platform::DeviceTree, machine: "apple,j516c", name: "foreign", firmware: (), board: hw::Board::DeviceTree(&board::record::J516C) }; fn main() { + let (result, calls, _) = candidate_for(&FOREIGN, true, true, 0); + assert_eq!(result, Err(ENODEV), "J514C must not override another Config's chip"); + assert!(!calls.contains(&"display") && !calls.contains(&"adt")); + for (board, soc) in [(false, true), (true, false), (false, false)] { + let (result, calls, _) = candidate34(board, soc, 0); + assert_eq!(result, Err(ENODEV), "T6034 needs its exact board and SoC"); + assert!(!calls.contains(&"display") && !calls.contains(&"adt")); + } + let (result, calls, warnings) = candidate34(true, true, 0); + assert_eq!(result, Ok(())); + assert!(calls.ends_with(&TAIL)); + assert_eq!(warnings, 1, "T6034 must identify inherited unqualified power words"); + for code in [-517, -19] { + let (result, calls, _) = candidate34(true, true, code); + assert_eq!(result, Err(Error(code))); + assert!(!calls.contains(&"adt") && !calls.contains(&"hold")); + } let (result, calls, _) = candidate_for(&T6034, true, true, 0); assert_eq!(result, Err(ENODEV), "wrong Config cannot inherit this candidate"); assert!(!calls.contains(&"display") && !calls.contains(&"adt")); @@ -379,7 +430,7 @@ def c_source(root): SLEEP_HOLD_RUN = r''' fn run_hold(cfg: &'static Config, sleep_hold: bool, asc_fail: bool) -> (Result<()>, Vec<&'static str>, Option, u32) { CALLS.with(|c| c.borrow_mut().clear()); - STATE.with(|s| *s.borrow_mut() = State { board: true, machine: true, pmp: 0, display: 0, sleep_hold, asc_fail, j514c: false, t6031: false }); + STATE.with(|s| *s.borrow_mut() = State { board: true, machine: true, pmp: 0, display: 0, sleep_hold, asc_fail, j514c: false, t6031: false, j514m: false, t6034: false }); HOLD.with(|h| *h.borrow_mut() = None); WARNINGS.with(|w| *w.borrow_mut() = 0); let pdev = platform::Device(device::Device, Core(PhantomData)); @@ -406,6 +457,7 @@ def c_source(root): } RS_MUTANTS = { + "t6034-ignores-soc": ('&& glue::machine(kernel::c_str!("apple,t6034"))', "&& true"), "candidate-ignores-config-chip": ("cfg.chip_id == 0x6031", "true"), "candidate-ignores-board": ('&& glue::machine(kernel::c_str!("apple,j514c"))', "&& true"), "candidate-ignores-soc": ('&& glue::machine(kernel::c_str!("apple,t6031"))', "&& true"), @@ -471,8 +523,8 @@ def static_checks(root): autoload = module[start:module.index("\n);", start)] start = module.index("const OF_TABLE:") matched = module[start:module.index("]);", start)] - assert compat.findall(autoload) == ["apple,agx-t6030", "apple,agx-t6031"], "autoload table" - assert compat.findall(matched) == ["apple,agx-t6030", "apple,agx-t6031"], "match table" + assert compat.findall(autoload) == ["apple,agx-t6030", "apple,agx-t6031", "apple,agx-t6034"], "autoload table" + assert compat.findall(matched) == ["apple,agx-t6030", "apple,agx-t6031", "apple,agx-t6034"], "match table" init = item(apple, "\nstatic int apple_drm_init(struct device *dev)\n{") order = [init.index(s) for s in ("drm_dev_register(", "drm_client_setup_with_fourcc(", "apple_g15_display_publish(dev)")] assert order == sorted(order), "publication must follow DRM registration and client setup" diff --git a/tools/asahi/g15-t6034-profile-tests.py b/tools/asahi/g15-t6034-profile-tests.py new file mode 100644 index 00000000000000..ea0386af80e050 --- /dev/null +++ b/tools/asahi/g15-t6034-profile-tests.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only OR MIT +"""Compile the actual G15C profiles and test T6034 shape and measurement-only admission.""" +from pathlib import Path +import subprocess +import tempfile +ROOT = Path(__file__).resolve().parents[2] +G15 = ROOT / 'drivers/gpu/drm/asahi/g15' + +def item(text, marker): + a=text.index(marker); at=text.index('{',a)+1; depth=1 + while depth: + depth+=(text[at]=='{')-(text[at]=='}');at+=1 + return text[a:at] + +hw=(G15/'hw.rs').read_text();record=(G15/'board/record.rs').read_text() +config=item((G15/'mod.rs').read_text(),'pub(crate) struct Config {') +reference=item(record,'pub(crate) struct Reference {') +value=item(record,'pub(crate) const J516C: Reference = Reference {')+';' +types='\n'.join(item(hw,name) for name in ['pub(crate) struct HwId {','pub(crate) struct FirmwareBuild {','pub(crate) struct Layout {','pub(crate) struct ComputeRegs {']) +types+='\n#[derive(Copy, Clone)]\n'+item(hw,'pub(crate) struct IoMap {') +types+='\n#[derive(Copy, Clone, PartialEq, Eq)]\n'+item(hw,'pub(crate) enum Platform {') +types+='\n#[derive(Copy, Clone, PartialEq, Eq)]\n'+item(hw,'pub(crate) enum Board {') +source=r''' +#![allow(dead_code)] +extern crate self as kernel; +pub mod str { pub type CStr = core::ffi::CStr; } +#[macro_export] macro_rules! c_str { ($s:literal) => { $crate::c_str_const(concat!($s,"\0").as_bytes()) }; } +const fn c_str_const(s: &[u8])->&core::ffi::CStr { unsafe { core::ffi::CStr::from_bytes_with_nul_unchecked(s) } } +mod hw { pub enum GpuVariant { C } } +mod g15 { + use kernel::str::CStr; + @CONFIG@ + pub mod board { pub mod record { + const RELATIVE_STATES: usize = 7; + #[derive(Copy,Clone,Debug,PartialEq,Eq)] + @REFERENCE@ + @VALUE@ + } } + pub mod hw { + @TYPES@ + #[path="@T6031@"] pub(crate) mod t6031; + #[path="@T6034@"] pub(crate) mod t6034; + } +} +fn main() { + use g15::hw::{t6031,t6034}; + let c=&t6034::CONFIG; + assert_eq!(c.chip_id,0x6034); + assert_eq!(c.machine.to_bytes(),b"apple,j514m"); + assert_eq!((c.num_clusters,c.num_cores_per_cluster),(4,10)); + assert_eq!(c.gpu_revision,0x11); + assert_eq!((c.hw_ids[0].version,c.hw_ids[0].counts),(0x07041100,0x0021040a)); + assert!(c.firmware.is_empty()); + assert_eq!((c.compute.tail,c.compute.ptc,c.compute.dupm),(0x2980,0x6000400020,0x3c000000140)); + assert_eq!((c.compute.tail,c.compute.ptc,c.compute.dupm),(t6031::CONFIG.compute.tail,t6031::CONFIG.compute.ptc,t6031::CONFIG.compute.dupm)); + let mask=0x7b5ed7f5fdu64; + let counts:Vec<_>=(0..4).map(|i|((mask>>(10*i))&1023).count_ones()).collect(); + assert_eq!(counts,[8,8,7,7]); + let maps=c.layout.iomaps;let records=c.layout.record_slots; + assert_eq!(maps.len(),17);assert_eq!(records.len(),17); + for (i,map) in maps.iter().enumerate() { + if map.slot==11 { + assert_eq!((map.pa,map.map_bytes),(0x220000000,6*0xd8000)); + assert_eq!(records[i],(6*0xd8000,0xd8000,true)); + } else { + assert_eq!((map.pa,map.map_bytes,map.delta),(t6031::IOMAPS[i].pa,t6031::IOMAPS[i].map_bytes,t6031::IOMAPS[i].delta)); + } + if i>0 { assert_eq!(map.va,maps[i-1].va+maps[i-1].map_bytes+0x4000); } + } + assert_eq!(t6031::IOMAPS.iter().find(|m|m.slot==11).unwrap().map_bytes,8*0xd8000); +} +''' +for token,replacement in {'@CONFIG@':config,'@REFERENCE@':reference,'@VALUE@':value,'@TYPES@':types,'@T6031@':str(G15/'hw/t6031.rs'),'@T6034@':str(G15/'hw/t6034.rs')}.items():source=source.replace(token,replacement) +identity=(G15/'identity.rs').read_text() +# Run the production firmware admission against synthetic public identity bytes. +firmware=item(identity,'pub(crate) fn firmware(') +identity_source=r''' +#![allow(dead_code,unused_variables)] +use std::fmt; +use std::sync::atomic::{AtomicU32,Ordering}; +#[derive(Debug)] struct Error; +impl From for Error { fn from(_:std::num::TryFromIntError)->Self { Self } } +type Result = std::result::Result; +type KVVec = Vec; +const EINVAL:Error=Error; +mod device { pub struct Device; } +struct FirmwareBuild { text_size:u64,data_size:u64,marker:u32,text_sha256:[u8;32],machine:Option<&'static str>,name:&'static str } +mod adt { pub struct GpuAdt { pub text:std::ops::Range,pub data:std::ops::Range } } +mod params { pub fn accept_text_prefix()->u64 { super::PREFIX_READS.fetch_add(1,super::Ordering::Relaxed); 0x1111111111111111 } } +static PREFIX_READS:AtomicU32=AtomicU32::new(0); +mod glue { pub fn sha256(_: &[u8])->[u8;32] {[0x11;32]} pub fn machine(_: &str)->bool {true} } +#[path="MATCHING"] mod matching; +#[path="LOOKUP"] mod lookup; +use lookup::{Measured,Verdict}; +struct Hex<'a>(&'a [u8]); +impl fmt::Display for Hex<'_> {fn fmt(&self,f:&mut fmt::Formatter<'_>)->fmt::Result {for b in self.0 {write!(f,"{:02x}",b)?;}Ok(())}} +impl From<&FirmwareBuild> for Measured { fn from(b:&FirmwareBuild)->Self {Self {text_size:b.text_size,data_size:b.data_size,marker:b.marker,sha256:b.text_sha256}} } +macro_rules! dev_err { ($d:expr,$($x:tt)*) => {{ eprintln!($($x)*); }} } +macro_rules! dev_info { ($d:expr,$($x:tt)*) => {{}} } +macro_rules! dev_warn { ($d:expr,$($x:tt)*) => {{}} } +fn copy_physical(_:u64,size:usize)->Result> { + let mut b=vec![0u8;size];b[16..24].copy_from_slice(b"GKTS\x08\0\0\0");b[24..32].fill(0xa5);Ok(b) +} +mod identity { + use super::*; + FIRMWARE +} +static OTHER:[FirmwareBuild;1]=[FirmwareBuild {text_size:64,data_size:64,marker:16,text_sha256:[0x22;32],machine:None,name:"other"}]; +fn main() { + let a=adt::GpuAdt{text:0x1000..0x1040,data:0x2000..0x2040}; + assert!(identity::firmware(&device::Device,&a,&[]).is_err()); + assert_eq!(PREFIX_READS.load(Ordering::Relaxed),0,"empty profile must not consult prefix escape hatch"); + assert!(identity::firmware(&device::Device,&a,&OTHER).unwrap().is_none(),"existing explicitly requested prefix behavior remains"); + assert_eq!(PREFIX_READS.load(Ordering::Relaxed),1); +} +''' +for token,replacement in {'FIRMWARE':firmware,'MATCHING':str(G15/'identity/matching.rs'),'LOOKUP':str(G15/'identity/lookup.rs')}.items():identity_source=identity_source.replace(token,replacement) +with tempfile.TemporaryDirectory(prefix='t6034-profile-') as temp: + for name,code in [('profile',source),('measurement-refusal',identity_source)]: + p=Path(temp)/name;p.with_suffix('.rs').write_text(code) + subprocess.run(['rustc','--edition=2021','--cfg','CONFIG_DRM_ASAHI_G15','-o',str(p),str(p.with_suffix('.rs'))],check=True) + subprocess.run([str(p)],check=True) + print(name+': PASS') diff --git a/tools/asahi/g15-zero-words-tests.py b/tools/asahi/g15-zero-words-tests.py index 78e62a6e921c20..d443597fd1ca41 100644 --- a/tools/asahi/g15-zero-words-tests.py +++ b/tools/asahi/g15-zero-words-tests.py @@ -124,7 +124,7 @@ def function(source, marker): } fn inputs<'a>(main: &'a [Opp], cs: &'a [Opp], props: [u32; PROPS.len()]) -> Inputs<'a> { - Inputs {props, zones:&[], main, cs, afr:cs, slots:&[], empty_writable:&[], + Inputs {chip_id:0x6031, props, zones:&[], main, cs, afr:cs, slots:&[], empty_writable:&[], ttbat:0x100000, usc_base:0x1000000000, user_end:0x2000000000, timestamps:0xfffffc2ffc000000, sensor_rows: rows(), reference: &REF} } @@ -218,7 +218,11 @@ def main(): for name in ("record.rs", "words.rs", "template.rs", "data.rs")} golden = {} for name, expected in GOLDEN_FILES.items(): - actual = sha((root / G15 / name).read_bytes()) + data = (root / G15 / name).read_bytes() + if name == 'hw.rs': + # New SoC module wiring does not alter the golden T6030 profile. + data = data.replace(b'#[cfg(CONFIG_DRM_ASAHI_G15)]\npub(crate) mod t6034;\n', b'') + actual = sha(data) if actual != expected: raise SystemExit("T6030 golden file changed: " + name) golden[name] = actual From 049dccd16ae7c88eaedcec423135e32c79ac83c8 Mon Sep 17 00:00:00 2001 From: Chris Kearney <303316+chriskearney@users.noreply.github.com> Date: Thu, 5 Nov 2026 07:37:19 -0800 Subject: [PATCH 02/10] dt-bindings: display: describe the T6034 internal display path Add the distinct T6034 DCP, display-subsystem and PMP compatibles used by the J514M handoff. The DCP register layout has eight named regions, including the fifth display window, before its IOP VBAR region. Assisted-by: Codex:GPT-6 Signed-off-by: Scott Jones --- Documentation/devicetree/bindings/display/apple,dcp.yaml | 4 +++- .../devicetree/bindings/display/apple,display-subsystem.yaml | 1 + Documentation/devicetree/bindings/soc/apple/apple,pmp.yaml | 4 +++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/display/apple,dcp.yaml b/Documentation/devicetree/bindings/display/apple,dcp.yaml index a7c74d948e6e9c..ec3251e7c4fcaa 100644 --- a/Documentation/devicetree/bindings/display/apple,dcp.yaml +++ b/Documentation/devicetree/bindings/display/apple,dcp.yaml @@ -17,6 +17,7 @@ description: properties: compatible: oneOf: + - const: apple,t6034-dcp - items: - enum: - apple,t8103-dcp @@ -39,7 +40,7 @@ properties: reg: minItems: 2 - maxItems: 7 + maxItems: 8 reg-names: minItems: 2 @@ -50,6 +51,7 @@ properties: - enum: [disp-2, iop-vbar] - enum: [disp-3, iop-vbar] - enum: [disp-4, iop-vbar] + - enum: [disp-5, iop-vbar] - const: iop-vbar mboxes: diff --git a/Documentation/devicetree/bindings/display/apple,display-subsystem.yaml b/Documentation/devicetree/bindings/display/apple,display-subsystem.yaml index 14bcb1f275c716..7b06e33dc29f96 100644 --- a/Documentation/devicetree/bindings/display/apple,display-subsystem.yaml +++ b/Documentation/devicetree/bindings/display/apple,display-subsystem.yaml @@ -18,6 +18,7 @@ properties: enum: - apple,display-subsystem - apple,t8122-display-subsystem + - apple,t6034-display-subsystem iommus: minItems: 1 diff --git a/Documentation/devicetree/bindings/soc/apple/apple,pmp.yaml b/Documentation/devicetree/bindings/soc/apple/apple,pmp.yaml index 997761ed5ad4de..6451b60d6b8302 100644 --- a/Documentation/devicetree/bindings/soc/apple/apple,pmp.yaml +++ b/Documentation/devicetree/bindings/soc/apple/apple,pmp.yaml @@ -20,7 +20,9 @@ properties: oneOf: - const: apple,t6000-pmp-v2 - items: - - const: apple,t8140-pmp-v2 + - enum: + - apple,t8140-pmp-v2 + - apple,t6034-pmp-v2 - const: apple,t6000-pmp-v2 reg: From ad7ab7b785ee54d65fc77a7df9de6aa0403cfd7f Mon Sep 17 00:00:00 2001 From: Chris Kearney <303316+chriskearney@users.noreply.github.com> Date: Thu, 5 Nov 2026 07:38:10 -0800 Subject: [PATCH 03/10] drm/apple: admit J514M native display on normal boots Give the measured J514M and T6034 pair a distinct native-display handoff, exact DCP image UUID and PMP namespace. Keep the checked inherited DART mappings, PMP image and power-state topology before enabling consumers. The native display starts by default only on that board and SoC pair; an explicit owner disable still keeps the framebuffer and GPU firmware off. Publish and retain its component separately from T6031 so GPU startup cannot follow the wrong display. The IOMFB record requires both the J514M board and T6034 root compatible, and uses boot-framebuffer geometry while logging the first native timings. Unmeasured panel controls stay off. Host checks run the production default/request helpers and board/image selection, including wrong boards and SoCs, unknown firmware, owner disable, and three failing mutants. Existing DCP panel and J514C default checks pass. Native hardware boot qualification is performed separately by the controller. Assisted-by: Codex:GPT-6 Signed-off-by: Scott Jones --- drivers/gpu/drm/apple/apple_drv.c | 13 ++++ drivers/gpu/drm/apple/dcp.c | 1 + drivers/gpu/drm/apple/iomfb_v14_7.c | 17 ++++- drivers/gpu/drm/apple/iomfb_v14_7_board.h | 2 + drivers/gpu/drm/apple/v14-board-test.c | 22 +++++++ drivers/soc/apple/Kconfig | 5 ++ drivers/soc/apple/t6030-display-gate.c | 61 ++++++++++++++++++ include/linux/soc/apple/display.h | 1 + tools/asahi/t6034-display-tests.py | 78 +++++++++++++++++++++++ 9 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 tools/asahi/t6034-display-tests.py diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 2324a8acd4c72a..a54f473e9e86cb 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -60,6 +60,7 @@ struct apple_drm_private { static DEFINE_MUTEX(apple_g15_display_lock); static struct device *apple_t6030_display; static struct device *apple_t6031_display; +static struct device *apple_t6034_display; /* The publication slot of a G15 SoC's internal display subsystem, or NULL. */ static struct device **apple_g15_display_slot(struct device *dev) @@ -68,6 +69,8 @@ static struct device **apple_g15_display_slot(struct device *dev) return &apple_t6030_display; if (of_device_is_compatible(dev->of_node, "apple,t6031-display-subsystem")) return &apple_t6031_display; + if (of_device_is_compatible(dev->of_node, "apple,t6034-display-subsystem")) + return &apple_t6034_display; return NULL; } @@ -134,6 +137,15 @@ int apple_drm_t6031_link_device(struct device *consumer) } EXPORT_SYMBOL_GPL(apple_drm_t6031_link_device); +int apple_drm_t6034_link_device(struct device *consumer) +{ + if (!of_machine_is_compatible("apple,j514m")) + return -ENODEV; + return apple_g15_link_device(consumer, "apple,t6034", "apple,agx-t6034", + &apple_t6034_display); +} +EXPORT_SYMBOL_GPL(apple_drm_t6034_link_device); + DEFINE_DRM_GEM_DMA_FOPS(apple_fops); #define DART_PAGE_SIZE 16384 @@ -1237,6 +1249,7 @@ static const struct of_device_id of_match[] = { { .compatible = "apple,t6030-display-subsystem" }, { .compatible = "apple,t8122-display-subsystem" }, { .compatible = "apple,t6031-display-subsystem" }, + { .compatible = "apple,t6034-display-subsystem" }, {} }; MODULE_DEVICE_TABLE(of, of_match); diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index a8571b5446f5c4..8cb040d16d552c 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -5182,6 +5182,7 @@ static const struct of_device_id of_match[] = { { .compatible = "apple,t6030-dcpext", .data = &apple_dcp_hw_t6030_dcpext, }, { .compatible = "apple,t8122-dcp", .data = &apple_dcp_hw_t8122, }, { .compatible = "apple,t6031-dcp", .data = &apple_dcp_hw_t6031, }, + { .compatible = "apple,t6034-dcp", .data = &apple_dcp_hw_t6031, }, { .compatible = "apple,t8122-dcpext", .data = &apple_dcp_hw_t8122_dcpext, }, { .compatible = "apple,dcp", .data = &apple_dcp_hw_dcp, }, { .compatible = "apple,dcpext", .data = &apple_dcp_hw_dcpext, }, diff --git a/drivers/gpu/drm/apple/iomfb_v14_7.c b/drivers/gpu/drm/apple/iomfb_v14_7.c index e540ba355f8298..4f381ab769f315 100644 --- a/drivers/gpu/drm/apple/iomfb_v14_7.c +++ b/drivers/gpu/drm/apple/iomfb_v14_7.c @@ -190,12 +190,26 @@ static const struct dcp_v14_board dcp_v14_board_j514c = { .log_bringup = true, }; +/* J514M's 14.7 DCP image and panel geometry; the root SoC is required too. */ +static const struct dcp_v14_board dcp_v14_board_j514m = { + .name = "J514M", + .debugfs = "dcp-j514m", + .dcp_compatible = "apple,t6034-dcp", + .machine = "apple,j514m", + .soc = "apple,t6034", + .firmware_uuid = DCP_V14_J516C_FIRMWARE_UUID, + .handoff = "apple,t6034-handoff", + .promotion = true, + .log_bringup = true, +}; + static const struct dcp_v14_board *const dcp_v14_boards[] = { &dcp_v14_board_t6030, &dcp_v14_board_j613, &dcp_v14_board_j615, &dcp_v14_board_j516c, &dcp_v14_board_j514c, + &dcp_v14_board_j514m, }; struct dcp_v14_property { @@ -1655,7 +1669,8 @@ const struct dcp_v14_board *dcp_v14_board_select(dcp_v14_compat_fn dcp_is, if (!dcp_is(ctx, board->dcp_compatible)) continue; *dcp_known = true; - if (board->machine && !machine_is(ctx, board->machine)) + if ((board->machine && !machine_is(ctx, board->machine)) || + (board->soc && !machine_is(ctx, board->soc))) continue; return board; } diff --git a/drivers/gpu/drm/apple/iomfb_v14_7_board.h b/drivers/gpu/drm/apple/iomfb_v14_7_board.h index fed617e7d12031..b5986420643a72 100644 --- a/drivers/gpu/drm/apple/iomfb_v14_7_board.h +++ b/drivers/gpu/drm/apple/iomfb_v14_7_board.h @@ -19,6 +19,8 @@ struct dcp_v14_board { const char *dcp_compatible; /* The machine compatible; NULL for any machine with this DCP. */ const char *machine; + /* Optional additional root SoC restriction. */ + const char *soc; /* The firmware image whose layouts were tested on this board. */ const char *firmware_uuid; /* Set to <1> by the boot loader on the DCP, display and PIODMA nodes. */ diff --git a/drivers/gpu/drm/apple/v14-board-test.c b/drivers/gpu/drm/apple/v14-board-test.c index 98df7dfab36ea8..23453f1368ee14 100644 --- a/drivers/gpu/drm/apple/v14-board-test.c +++ b/drivers/gpu/drm/apple/v14-board-test.c @@ -51,6 +51,8 @@ static const struct dcp_v14_board *board_for(const char *dcp, const char *const static const char *const j516c[] = { "apple,j516c", "apple,t6031", "apple,arm-platform", NULL }; static const char *const j514c[] = { "apple,j514c", "apple,t6031", "apple,arm-platform", NULL }; +static const char *const j514m[] = { "apple,j514m", "apple,t6034", "apple,arm-platform", NULL }; +static const char *const j514m_wrong_soc[] = { "apple,j514m", "apple,t6031", NULL }; static const char *const j516m[] = { "apple,j516m", "apple,t6034", "apple,arm-platform", NULL }; static const char *const j516s[] = { "apple,j516s", "apple,t6030", "apple,arm-platform", NULL }; static const char *const j613[] = { "apple,j613", "apple,t8122", "apple,arm-platform", NULL }; @@ -108,6 +110,25 @@ static void v14_board_j514c(struct kunit *test) KUNIT_EXPECT_PTR_NE(test, b, board_for("apple,t6031-dcp", j516c, &known)); } +static void v14_board_j514m(struct kunit *test) +{ + const struct dcp_v14_board *b; + bool known; + + b = board_for("apple,t6034-dcp", j514m, &known); + KUNIT_ASSERT_NOT_NULL(test, b); + KUNIT_EXPECT_TRUE(test, known); + KUNIT_EXPECT_STREQ(test, b->machine, "apple,j514m"); + KUNIT_EXPECT_STREQ(test, b->soc, "apple,t6034"); + KUNIT_EXPECT_STREQ(test, b->handoff, "apple,t6034-handoff"); + KUNIT_EXPECT_TRUE(test, dcp_v14_board_admits(b, J516C_DCP_IMAGE)); + KUNIT_EXPECT_FALSE(test, dcp_v14_board_admits(b, T6030_DCP_IMAGE)); + KUNIT_EXPECT_FALSE(test, b->panel_nits); + KUNIT_EXPECT_NULL(test, board_for("apple,t6034-dcp", j514m_wrong_soc, &known)); + KUNIT_EXPECT_NULL(test, board_for("apple,t6034-dcp", j516m, &known)); + KUNIT_EXPECT_NULL(test, board_for("apple,t6031-dcp", j514m, &known)); +} + /* A T6031 DCP on a T6034 has no record: refused. */ static void v14_board_t6031_other_boards(struct kunit *test) { @@ -255,6 +276,7 @@ static void v14_swap_timestamps_encoding(struct kunit *test) static struct kunit_case v14_board_cases[] = { KUNIT_CASE(v14_board_j516c), KUNIT_CASE(v14_board_j514c), + KUNIT_CASE(v14_board_j514m), KUNIT_CASE(v14_board_t6031_other_boards), KUNIT_CASE(v14_board_j516c_image), KUNIT_CASE(v14_board_unchanged), diff --git a/drivers/soc/apple/Kconfig b/drivers/soc/apple/Kconfig index f00718c130dc43..d04f84bdc088f8 100644 --- a/drivers/soc/apple/Kconfig +++ b/drivers/soc/apple/Kconfig @@ -237,6 +237,11 @@ config APPLE_T6030_DISPLAY_GATE not come up, also keeps the GPU off for that boot. It is not a display-only switch. + J514M/T6034 uses an independent apple,t6034-handoff profile and the + same checked PMP firmware layout. It starts on normal boots of that + exact board and SoC pair; apple_t6034_display.enable=0 retains the + boot framebuffer and keeps its GPU off. Other T6034 boards stay off. + Say 'y' if you want the native display on M3 Pro MacBook Pros. config APPLE_T8122_GPU_PMP_LINK diff --git a/drivers/soc/apple/t6030-display-gate.c b/drivers/soc/apple/t6030-display-gate.c index 83960cf5b1697f..016abbcf6cf96f 100644 --- a/drivers/soc/apple/t6030-display-gate.c +++ b/drivers/soc/apple/t6030-display-gate.c @@ -183,6 +183,21 @@ static bool __init gate_t6031_wanted(int sw, bool default_board) return sw < 0 ? default_board : sw; } +/* T6034 has an independent owner disable and never admits an unmeasured board. */ +static int gate_t6034_switch __initdata = -1; +static bool gate_t6034_requested __initdata; + +static bool __init gate_t6034_default_board(void) +{ + return of_machine_is_compatible("apple,t6034") && + of_machine_is_compatible("apple,j514m"); +} + +static bool __init gate_t6034_wanted(int sw) +{ + return gate_t6034_default_board() && gate_t6031_wanted(sw, true); +} + /* One SoC's internal display handoff, as the gate checks and completes it */ struct gate_soc { const char *machine; /* root compatible */ @@ -307,10 +322,35 @@ static const struct gate_soc gate_t6031 __initconst = { .fw_uuids = "asahi,t6031-fw-uuids", }; +/* Fresh J514M ADT: shared PMP image/layout, separate root and handoff. */ +static const struct gate_soc gate_t6034 __initconst = { + .machine = "apple,t6034", + .prefix = "apple-t6034-display: ", + .name = "T6034", + .dcp_compat = "apple,t6034-dcp", + .display_compat = "apple,t6034-display-subsystem", + .marker = "apple,t6034-handoff", + .pwrstate_compat = "apple,t6031-pmgr-pwrstate", + .dcp_full_name = "dcp@386c00000", + .pmp_values = "/chosen/asahi,t6034-pmp", + .pmp_uuid = gate_t6031_pmp_uuid, + .report_compat = "apple,t6034-pmp-v2-report", + .disp_entry = "report@10", + .reuse_pmp = true, + .pmp_compat = "apple,t6034-pmp-v2", + .ans_entry = "report@21", + .requested = &gate_t6034_requested, + .ps_chain = { PMP_PS_DISP_FE, PMP_PS_DISP_CPU, PMP_PS_DISP_SYS }, + .pmp_ps_any_pmgr = true, + .describe = true, + .fw_uuids = "asahi,t6034-fw-uuids", +}; + static const struct gate_soc *const gate_socs[] __initconst = { &gate_t6030, &gate_t8122, &gate_t6031, + &gate_t6034, }; /* The SoC this boot runs on, if the gate serves it. */ @@ -383,6 +423,17 @@ static int __init gate_setup_t6031(char *arg) } early_param("apple_t6031_display.enable", gate_setup_t6031); +static int __init gate_setup_t6034(char *arg) +{ + bool on; + int ret = kstrtobool(arg, &on); + + if (!ret) + gate_t6034_switch = on; + return ret; +} +early_param("apple_t6034_display.enable", gate_setup_t6034); + /* Returns the only node compatible with @compat, or NULL if there are none or several. */ static struct device_node *__init gate_find_one(const char *compat) { @@ -2002,6 +2053,16 @@ static void __init gate_run(void) board); } + if (gate_soc == &gate_t6034) { + if (!gate_t6034_default_board()) { + gate_info("unsupported board; native display stays disabled\n"); + return; + } + gate_t6034_requested = gate_t6034_wanted(gate_t6034_switch); + if (gate_t6034_switch < 0) + gate_info("native display on by default on J514M; apple_t6034_display.enable=0 keeps the boot framebuffer\n"); + } + if (!*gate_soc->requested) { if (gate_soc->opt_in && gate_t6031_switch < 0) gate_info("native display not requested (%s is not on the command line), display stays on the boot framebuffer\n", diff --git a/include/linux/soc/apple/display.h b/include/linux/soc/apple/display.h index eb71ce91e09193..ce602031e56fd7 100644 --- a/include/linux/soc/apple/display.h +++ b/include/linux/soc/apple/display.h @@ -7,5 +7,6 @@ struct device; /* Initial native display admission, before starting retained G15 firmware. */ int apple_drm_t6030_link_device(struct device *consumer); int apple_drm_t6031_link_device(struct device *consumer); +int apple_drm_t6034_link_device(struct device *consumer); #endif diff --git a/tools/asahi/t6034-display-tests.py b/tools/asahi/t6034-display-tests.py new file mode 100644 index 00000000000000..4d1142b733cdd8 --- /dev/null +++ b/tools/asahi/t6034-display-tests.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only OR MIT +"""Run production J514M display selection and opt-out policy on the host.""" +from pathlib import Path +import subprocess +import tempfile +ROOT = Path(__file__).resolve().parents[2] +gate = (ROOT / 'drivers/soc/apple/t6030-display-gate.c').read_text() +driver = (ROOT / 'drivers/gpu/drm/apple/iomfb_v14_7.c').read_text() +header = (ROOT / 'drivers/gpu/drm/apple/iomfb_v14_7_board.h').read_text() +def item(s, marker): + a=s.index(marker);b=s.index('\n}',a)+2 + return s[a:b] +prefix = r''' +#include +#include +#include +#include +#include +#define __init +#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) +#define A(n) (n) +typedef uint32_t u32; +static const char *board, *soc, *dcp; +static bool of_machine_is_compatible(const char *c) +{ return (board && !strcmp(board,c)) || (soc && !strcmp(soc,c)); } +static bool machine_is(const void *ctx,const char *c) { return of_machine_is_compatible(c); } +static bool dcp_is(const void *ctx,const char *c) { return dcp && !strcmp(dcp,c); } +''' +types=header[header.index('struct dcp_v14_board {'):header.index('#endif')] +defines='\n'.join(l for l in driver.splitlines() if l.startswith('#define DCP_V14_') and 'FIRMWARE_UUID' in l) +records=driver[driver.index('static const struct dcp_v14_board dcp_v14_board_t6030'):driver.index('struct dcp_v14_property {')] +functions='\n'.join([item(gate,'static bool __init gate_t6031_wanted('), + item(gate,'static bool __init gate_t6034_default_board('),item(gate,'static bool __init gate_t6034_wanted('), + item(driver,'const struct dcp_v14_board *dcp_v14_board_select('),item(driver,'bool dcp_v14_board_admits(')]) +main = r''' +int main(void) +{ + const char *boards[]={"apple,j514m","apple,j516m","apple,j514c","apple,j999"}; + const char *socs[]={"apple,t6034","apple,t6031","apple,t6030",NULL}; + dcp="apple,t6034-dcp"; + for(unsigned i=0;ihandoff,"apple,t6034-handoff")); + assert(b->promotion && b->log_bringup && !b->ctm_set && !b->ctm_get && !b->panel_nits); + assert(dcp_v14_board_admits(b,"F493F03E-CC40-3D89-9A08-5F259BA2B246")); + assert(!dcp_v14_board_admits(b,"F493F03E-CC40-3D89-9A08-5F259BA2B247")); + assert(!dcp_v14_board_admits(b,"f493f03e-cc40-3d89-9a08-5f259ba2b246")); + assert(!dcp_v14_board_admits(b,NULL)); + } + board="apple,j514m";soc="apple,t6034";dcp="apple,t6031-dcp";bool known; + assert(!dcp_v14_board_select(dcp_is,machine_is,NULL,&known)); + return 0; +} +''' +source="\n".join([prefix,types,defines,records,functions,main]) +mutants={ + 'control':source, + 'lost-board-soc':source.replace('.soc = "apple,t6034",','.soc = NULL,'), + 'lost-owner-optout':source.replace('return sw < 0 ? default_board : sw;','return sw < 0 ? default_board : true;'), + 'lost-firmware-identity':source.replace('return uuid && !strcmp(uuid, board->firmware_uuid);','return uuid != NULL;'), +} +with tempfile.TemporaryDirectory(prefix='t6034-display-') as directory: + for name,c in mutants.items(): + exe=Path(directory)/name + subprocess.run(['cc','-std=gnu11','-Wall','-Wextra','-Werror','-Wno-unused-parameter','-x','c','-','-o',str(exe)],input=c,text=True,check=True) + result=subprocess.run([str(exe)],capture_output=True) + assert (result.returncode==0)==(name=='control'),(name,result.stderr) + print(name+': '+('pass' if name=='control' else 'caught')) +assert 'gate_t6034_requested = gate_t6034_wanted(gate_t6034_switch);' in gate +print('T6034 normal display defaults, owner disable, exact root pair and firmware identity: PASS') From 4e590fa4335a995c3c36fcbc390c5aa19f5ad310 Mon Sep 17 00:00:00 2001 From: Chris Kearney <303316+chriskearney@users.noreply.github.com> Date: Thu, 5 Nov 2026 07:38:10 -0800 Subject: [PATCH 04/10] arm64: dts: apple: describe the J514M internal display The fresh J514M ADT identifies the T6031-layout internal DCP, its display DARTs, and the checked PMP firmware, with a 400 MHz disp0 clock. Describe those resources only on J514M, behind distinct T6034 handoff and component compatibles. Keep all native display consumers disabled until the loader and kernel gates admit them. The device tree preprocesses and compiles. Readback confirms the T6034 DCP, display subsystem and PMP compatibles, the measured 400 MHz clock, and the disabled DCP status. Assisted-by: Codex:GPT-6 Signed-off-by: Scott Jones --- arch/arm64/boot/dts/apple/t6034-dcp.dtsi | 28 +++++++++++++++++++++++ arch/arm64/boot/dts/apple/t6034-j514m.dts | 12 ++++++++++ 2 files changed, 40 insertions(+) create mode 100644 arch/arm64/boot/dts/apple/t6034-dcp.dtsi diff --git a/arch/arm64/boot/dts/apple/t6034-dcp.dtsi b/arch/arm64/boot/dts/apple/t6034-dcp.dtsi new file mode 100644 index 00000000000000..824549363791bc --- /dev/null +++ b/arch/arm64/boot/dts/apple/t6034-dcp.dtsi @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * J514M/T6034 internal display. Fresh ADT metadata identifies the same + * DCP/DART/PMP register layout and 14.7 firmware images as T6031, with a + * 400 MHz disp0 clock. Keep a separate handoff and component compatible; + * every consumer remains disabled until the T6034 gate validates it. + */ +#include "t6031-dcp.dtsi" + +&clk_disp0 { + clock-frequency = <400000000>; +}; + +&display { + compatible = "apple,t6034-display-subsystem"; +}; + +&dcp { + compatible = "apple,t6034-dcp"; +}; + +&pmp { + compatible = "apple,t6034-pmp-v2", "apple,t6000-pmp-v2"; +}; + +&pmp_report { + compatible = "apple,t6034-pmp-v2-report", "apple,t6020-pmp-v2-report"; +}; diff --git a/arch/arm64/boot/dts/apple/t6034-j514m.dts b/arch/arm64/boot/dts/apple/t6034-j514m.dts index f9c2b6241c9551..790319c2db3f21 100644 --- a/arch/arm64/boot/dts/apple/t6034-j514m.dts +++ b/arch/arm64/boot/dts/apple/t6034-j514m.dts @@ -10,6 +10,7 @@ /dts-v1/; #include "t6034.dtsi" +#include "t6034-dcp.dtsi" #include "t603x-j514-j516.dtsi" / { @@ -29,6 +30,17 @@ brcm,board-type = "apple,texa"; }; +/* J514 panel dimensions, as on the other 14-inch M3 MacBook Pros. */ +&dcp { + panel { + compatible = "apple,panel-j514", "apple,panel-mini-led", "apple,panel"; + width-mm = <302>; + height-mm = <196>; + adj-height-mm = <189>; + apple,max-brightness = <500>; + }; +}; + &aop_audio { apple,chassis-name = "J514"; apple,machine-kind = "MacBook Pro"; From 0cd7199e2b274b0e65518c28c245b92bb1286eaf Mon Sep 17 00:00:00 2001 From: Chris Kearney <303316+chriskearney@users.noreply.github.com> Date: Thu, 5 Nov 2026 07:41:04 -0800 Subject: [PATCH 05/10] ci: register the T6034 graphics admission host checks Run the new measured-profile and native-display policy suites in the maintained host-test runner. Pin the Rust profile suite to 1.93.1 and retain the C display suite in the normal tools group. Update the shared display-order description to include T6034. The manifest discovery and validation checks pass. The T6034 profile and display suites pass with the actual production bodies, including empty-firmware refusal and the display failure mutants. Assisted-by: Codex:GPT-6 Signed-off-by: Scott Jones --- tools/aurora-ci/host-tests.json | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tools/aurora-ci/host-tests.json b/tools/aurora-ci/host-tests.json index b73ea9977e5dd9..b004f8c32d18c0 100644 --- a/tools/aurora-ci/host-tests.json +++ b/tools/aurora-ci/host-tests.json @@ -2197,7 +2197,7 @@ "tools/asahi/g15-display-order-tests.py" ], "rust": "1.93.1", - "note": "Actual display publication, T6030/T6031 link and admit() bodies on OF/device-link and admission-order models; C and admit() mutants must build and fail. No hardware or private fixtures." + "note": "Actual display publication, T6030/T6031/T6034 links and admit() bodies on OF/device-link and admission-order models; C and admit() mutants must build and fail. No hardware or private fixtures." }, { "name": "tools/asahi/g15-j514c-display-default-tests.py", @@ -2218,6 +2218,25 @@ "rust": "1.93.1", "note": "Production full firmware lookup against T6031 rows; exact J514C identity and other-board refusal. Public synthetic inputs only; no firmware images." }, + { + "name": "tools/asahi/g15-t6034-profile-tests.py", + "group": "tools", + "argv": [ + "python3", + "tools/asahi/g15-t6034-profile-tests.py" + ], + "rust": "1.93.1", + "note": "Production G15C profiles, exact fused T6034 topology, six memory channels and empty-firmware refusal without the prefix escape hatch; public synthetic identity inputs only." + }, + { + "name": "tools/asahi/t6034-display-tests.py", + "group": "tools", + "argv": [ + "python3", + "tools/asahi/t6034-display-tests.py" + ], + "note": "Production J514M/T6034 display default and board/image selection; compiled wrong-SoC, lost-disable and firmware-identity mutants. No hardware or private fixtures." + }, { "name": "tools/testing/selftests/drivers/gpu/asahi-g15/run.py", "group": "tools", From 1718cc55a0508aeb35756800d871cad17cedd8a1 Mon Sep 17 00:00:00 2001 From: Chris Kearney <303316+chriskearney@users.noreply.github.com> Date: Thu, 5 Nov 2026 08:23:40 -0800 Subject: [PATCH 06/10] pmdomain: apple: start the handed-off T6034 PMP before DCP The first J514M native-display diagnostic reached Linux with the validated DCP/PMP handoff, but DCP returned -ENODEV before starting its RTKit session. The T6034 report node matched only its T6020 fallback. That observer-only record does not create a halted-PMP startup owner, so wait_ready() correctly refused the display request rather than running DCP without an ACK. Add the exact T6034 report match and a separate startup record. Selected read-only ADT metadata confirms status index 1, request index 0x200 and ACK index 0x208, with all 35 soc-device IDs in record order. DISP is ID 17 with ACK and ANS is ID 34 without ACK. Keep the real firmware, SRAM, PMGR-floor, PTD-layout and request-ACK checks intact. The PMGR nodes retain the existing T6031-specific tag on T6034; use that tag for their lookup. Report explicit disable parameters for both normal-default T6031 and T6034 fallback paths. Compile the production layout records and OF match table in the registered T6034 host test, with missing-match, missing-startup-owner and wrong-ACK-aperture mutants. The old table reproduces the missing owner; the corrected table and all mutants pass. ARM64 LLVM 22 compiles the PMP object and the KUnit-enabled display gate. The GPU firmware table remains empty for the next digest-only diagnostic; no firmware startup is admitted. Fixes: a8dd7fcd2f40 ("arm64: dts: apple: describe the J514M internal display") Assisted-by: Codex:GPT-6 Signed-off-by: Scott Jones --- drivers/pmdomain/apple/pmp-report.c | 20 ++++++++++++ tools/asahi/t6034-display-tests.py | 49 +++++++++++++++++++++++++++++ tools/aurora-ci/host-tests.json | 2 +- 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/drivers/pmdomain/apple/pmp-report.c b/drivers/pmdomain/apple/pmp-report.c index 56e8ab67ae6970..fac35afc4e6cb2 100644 --- a/drivers/pmdomain/apple/pmp-report.c +++ b/drivers/pmdomain/apple/pmp-report.c @@ -1222,6 +1222,25 @@ static const struct apple_pmp_report_offsets apple_pmp_offsets_t6031 = { .devices_ordered = true, }; +/* + * J514M/T6034's selected ADT ptd-range has the same request (index 0x200), + * acknowledge (0x208) and status (0x1) apertures. Its 35 soc-device records + * are in ID order; DISP is ID 17 with ACK and ANS is ID 34 without ACK. + * The exact T6034 display gate supplies the halted PMP and validated values. + * The generic T6020 fallback only observes an already-running PMP. + */ +static const struct apple_pmp_report_offsets apple_pmp_offsets_t6034 = { + .tgt_read = 0x2000, + .tgt_write = 0x11000, + .actual = 0x2080, + .status = 0x10, + .starts_pmp = true, + .name = "T6034", + .fallback = "apple_t6034_display.enable=0", + .pwrstate = "apple,t6031-pmgr-pwrstate", + .devices_ordered = true, +}; + /* The fast die-temperature report is the low two bytes of entry 0x1c1. */ static const struct apple_pmp_report_offsets apple_pmp_offsets_t8140 = { .tgt_read = 0x1880, @@ -1238,6 +1257,7 @@ static const struct of_device_id apple_pmp_report_of_match[] = { { .compatible = "apple,t6020-pmp-v2-report", .data = &apple_pmp_offsets_t602x }, { .compatible = "apple,t6030-pmp-v2-report", .data = &apple_pmp_offsets_t6030 }, { .compatible = "apple,t6031-pmp-v2-report", .data = &apple_pmp_offsets_t6031 }, + { .compatible = "apple,t6034-pmp-v2-report", .data = &apple_pmp_offsets_t6034 }, { .compatible = "apple,t8112-pmp-v2-report", .data = &apple_pmp_offsets_t8112 }, { .compatible = "apple,t8122-pmp-v2-report", .data = &apple_pmp_offsets_t8122 }, { .compatible = "apple,t8140-pmp-v2-report", .data = &apple_pmp_offsets_t8140 }, diff --git a/tools/asahi/t6034-display-tests.py b/tools/asahi/t6034-display-tests.py index 4d1142b733cdd8..9e44577ecfec68 100644 --- a/tools/asahi/t6034-display-tests.py +++ b/tools/asahi/t6034-display-tests.py @@ -76,3 +76,52 @@ def item(s, marker): print(name+': '+('pass' if name=='control' else 'caught')) assert 'gate_t6034_requested = gate_t6034_wanted(gate_t6034_switch);' in gate print('T6034 normal display defaults, owner disable, exact root pair and firmware identity: PASS') + +# Compile the production report layout records and OF match table. Matching +# T6034 through its generic T6020 fallback binds a report that never creates +# the halted-PMP startup owner; wait_ready() then correctly returns -ENODEV. +pmp = (ROOT / 'drivers/pmdomain/apple/pmp-report.c').read_text() +pmp_type = item(pmp, 'struct apple_pmp_report_offsets {') + ';' +pmp_tables = pmp[pmp.index('static const struct apple_pmp_report_offsets apple_pmp_offsets_t600x = {'):pmp.index('static struct platform_driver apple_pmp_report_driver = {')] +pmp_main = r''' +static const struct apple_pmp_report_offsets *report(const char *specific) +{ + const char *compatibles[]={specific,"apple,t6020-pmp-v2-report"}; + for(unsigned i=0;istarts_pmp); + assert(!strcmp(o->name,"T6034")); + assert(!strcmp(o->fallback,"apple_t6034_display.enable=0")); + assert(!strcmp(o->pwrstate,"apple,t6031-pmgr-pwrstate")); + assert(o->tgt_read==0x2000 && o->tgt_write==0x11000 && o->actual==0x2080 && o->status==0x10); + assert(o->devices_ordered); + o=report("apple,t6031-pmp-v2-report"); + assert(o && o->starts_pmp && !strcmp(o->fallback,"apple_t6031_display.enable=0")); + o=report("apple,t6020-pmp-v2-report"); + assert(o && !o->starts_pmp); + return 0; +} +''' +pmp_source='\n'.join([prefix,'#define IS_ENABLED(x) 0',pmp_type, + 'struct of_device_id {const char *compatible; const struct apple_pmp_report_offsets *data;};',pmp_tables,pmp_main]) +pmp_mutants={ + 'control':pmp_source, + 'missing-t6034-match':pmp_source.replace('.compatible = "apple,t6034-pmp-v2-report"','.compatible = "apple,missing-t6034-report"'), + 'lost-halted-pmp-owner':pmp_source.replace('.starts_pmp = true,\n\t.name = "T6034"','.starts_pmp = false,\n\t.name = "T6034"'), + 'wrong-ack-aperture':pmp_source.replace('.actual = 0x2080,','.actual = 0x2088,'), +} +with tempfile.TemporaryDirectory(prefix='t6034-pmp-report-') as directory: + for name,c in pmp_mutants.items(): + exe=Path(directory)/name + subprocess.run(['cc','-std=gnu11','-Wall','-Wextra','-Werror','-Wno-unused-parameter','-Wno-unused-function','-Wno-unused-variable','-x','c','-','-o',str(exe)],input=c,text=True,check=True) + result=subprocess.run([str(exe)],capture_output=True) + assert (result.returncode==0)==(name=='control'),(name,result.stderr) + print('PMP '+name+': '+('pass' if name=='control' else 'caught')) +print('T6034 exact PMP report binding, halted owner, offsets and explicit fallback: PASS') diff --git a/tools/aurora-ci/host-tests.json b/tools/aurora-ci/host-tests.json index b004f8c32d18c0..31e774562621ef 100644 --- a/tools/aurora-ci/host-tests.json +++ b/tools/aurora-ci/host-tests.json @@ -2235,7 +2235,7 @@ "python3", "tools/asahi/t6034-display-tests.py" ], - "note": "Production J514M/T6034 display default and board/image selection; compiled wrong-SoC, lost-disable and firmware-identity mutants. No hardware or private fixtures." + "note": "Production J514M/T6034 display default, board/image selection and PMP report OF table; compiled wrong-SoC, lost-disable, firmware-identity, missing-report-match, halted-owner and ACK-aperture mutants. No hardware or private fixtures." }, { "name": "tools/testing/selftests/drivers/gpu/asahi-g15/run.py", From 8148b2718da0f42498a4fcb60e364751c255cd14 Mon Sep 17 00:00:00 2001 From: Chris Kearney <303316+chriskearney@users.noreply.github.com> Date: Thu, 5 Nov 2026 08:23:40 -0800 Subject: [PATCH 07/10] drm/asahi: record the measured J514M enabled-core masks The selected powered-identity metadata reports core-mask-0 0x5fc7f5fd and core-mask-1 0x7b. Splitting that value into four ten-slot clusters gives [0x1fd, 0x1fd, 0x1fc, 0x1ed], with [8, 8, 7, 7] enabled cores. Correct the profile comment and use those actual masks in the host fixture instead of the same-count reference masks. The production profile still admits the measured hardware IDs and geometry; it does not require an exact fuse mask. The production-profile host harness passes with the corrected fixture. Assisted-by: Codex:GPT-6 Signed-off-by: Scott Jones --- drivers/gpu/drm/asahi/g15/hw/t6034.rs | 2 +- tools/asahi/g15-t6034-profile-tests.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/asahi/g15/hw/t6034.rs b/drivers/gpu/drm/asahi/g15/hw/t6034.rs index 750b75f9e03ae0..66652d2e29cd57 100644 --- a/drivers/gpu/drm/asahi/g15/hw/t6034.rs +++ b/drivers/gpu/drm/asahi/g15/hw/t6034.rs @@ -3,7 +3,7 @@ //! J514M/T6034 (M3 Max, G15C) measurement profile. //! //! The powered identity read on this J514M found four ten-slot clusters with -//! enabled masks [0x1fd, 0x1fd, 0x1ed, 0x1ed], or 30 enabled cores. Its ADT has +//! enabled masks [0x1fd, 0x1fd, 0x1fc, 0x1ed], or 30 enabled cores. Its ADT has //! four voltage tables and six memory channels. The four-cluster G15C object //! and command layouts below are inherited from T6031; they are not a native //! macOS capture of this machine. The J516C power/reference words remain diff --git a/tools/asahi/g15-t6034-profile-tests.py b/tools/asahi/g15-t6034-profile-tests.py index ea0386af80e050..c1820bb29f992d 100644 --- a/tools/asahi/g15-t6034-profile-tests.py +++ b/tools/asahi/g15-t6034-profile-tests.py @@ -54,7 +54,9 @@ def item(text, marker): assert!(c.firmware.is_empty()); assert_eq!((c.compute.tail,c.compute.ptc,c.compute.dupm),(0x2980,0x6000400020,0x3c000000140)); assert_eq!((c.compute.tail,c.compute.ptc,c.compute.dupm),(t6031::CONFIG.compute.tail,t6031::CONFIG.compute.ptc,t6031::CONFIG.compute.dupm)); - let mask=0x7b5ed7f5fdu64; + let mask=0x7b5fc7f5fdu64; + let masks:Vec<_>=(0..4).map(|i|(mask>>(10*i))&1023).collect(); + assert_eq!(masks,[0x1fd,0x1fd,0x1fc,0x1ed]); let counts:Vec<_>=(0..4).map(|i|((mask>>(10*i))&1023).count_ones()).collect(); assert_eq!(counts,[8,8,7,7]); let maps=c.layout.iomaps;let records=c.layout.record_slots; From 5498a67a39b69048e61079e31b9dd463d57c7a51 Mon Sep 17 00:00:00 2001 From: Chris Kearney <303316+chriskearney@users.noreply.github.com> Date: Thu, 5 Nov 2026 08:49:06 -0800 Subject: [PATCH 08/10] drm/apple: enumerate the T6034 DCP as a display component The second J514M diagnostic reached the PMP display-request ACK, and the T6034 DCP provider probed successfully. Native DRM still did not publish, so GPU admission correctly kept firmware stopped. The display master and DCP provider had their T6034 OF matches, but apple_dcp_id_tbl omitted the T6034 internal DCP. add_dcp_components() therefore found no component and returned -ENODEV before master binding. The same table also controls DCP initialization after binding. Add the internal T6034 compatible to that shared enumeration table. Keep availability, board/firmware, PMP ACK, boot-display readiness and GPU publication ordering checks unchanged. No external display compatible is added. The frozen J514M DTB has one internal DCP with its direct panel and no external endpoints; its provider, mailbox, DART, PMGR and PMP wiring was reviewed alongside all T6031-only selectors in the affected consumers. Compile the production component table, master/provider OF tables, add_dcp_components(), apple_dcp_usable() and apple_platform_probe() in the registered host suite. The old table reproduces the missing component; the fixed control passes and missing-enumeration, missing-master/provider binding and lost-availability-filter mutants fail. ARM64 LLVM 22 compiles apple_drv.o, and all five maintained admission/display host suites pass. The GPU firmware table remains empty for the next digest-only diagnostic. Fixes: a8dd7fcd2f40 ("arm64: dts: apple: describe the J514M internal display") Assisted-by: Codex:GPT-6 Signed-off-by: Scott Jones --- drivers/gpu/drm/apple/apple_drv.c | 1 + tools/asahi/t6034-display-tests.py | 89 ++++++++++++++++++++++++++++++ tools/aurora-ci/host-tests.json | 2 +- 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index a54f473e9e86cb..b6a5be040ef9c8 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -901,6 +901,7 @@ static const struct of_device_id apple_dcp_id_tbl[] = { { .compatible = "apple,t6030-dcp" }, { .compatible = "apple,t8122-dcp" }, { .compatible = "apple,t6031-dcp" }, + { .compatible = "apple,t6034-dcp" }, {}, }; diff --git a/tools/asahi/t6034-display-tests.py b/tools/asahi/t6034-display-tests.py index 9e44577ecfec68..ddd1cf54cc4c39 100644 --- a/tools/asahi/t6034-display-tests.py +++ b/tools/asahi/t6034-display-tests.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: GPL-2.0-only OR MIT """Run production J514M display selection and opt-out policy on the host.""" from pathlib import Path +import re import subprocess import tempfile ROOT = Path(__file__).resolve().parents[2] @@ -125,3 +126,91 @@ def item(s, marker): assert (result.returncode==0)==(name=='control'),(name,result.stderr) print('PMP '+name+': '+('pass' if name=='control' else 'caught')) print('T6034 exact PMP report binding, halted owner, offsets and explicit fallback: PASS') + +# The platform drivers can both match while the master sees no component. +# Exercise its production enumeration and platform probe, plus both OF tables. +master = (ROOT / 'drivers/gpu/drm/apple/apple_drv.c').read_text() +dcp_driver = (ROOT / 'drivers/gpu/drm/apple/dcp.c').read_text() +component_table = item(master, 'static const struct of_device_id apple_dcp_id_tbl[] = {') + ';' +master_table = (item(master, 'static const struct of_device_id of_match[] = {') + ';').replace('of_match[]', 'master_of_match[]') +dcp_table = (item(dcp_driver, 'static const struct of_device_id of_match[] = {') + ';').replace('of_match[]', 'dcp_of_match[]') +data_names = sorted(set(re.findall(r'\.data = &(\w+)', dcp_table))) +component_prefix = r''' +#include +#include +#include +#include +#define IS_ENABLED(x) 0 +#define ENODEV 19 +struct device_node { const char *compatible; bool available; }; +struct device { struct device_node *of_node; }; +struct platform_device { struct device dev; }; +struct component_match { struct device_node *nodes[4]; int count; }; +struct of_device_id { const char *compatible; const void *data; }; +static struct device_node *nodes[4]; +static struct component_match matches; +static int masters; +static bool of_device_is_available(const struct device_node *n) {return n && n->available;} +static bool of_device_is_compatible(const struct device_node *n,const char *s) {return n && !strcmp(n->compatible,s);} +static bool dcp_t6030_ext_native(struct device_node *n) {(void)n;return false;} +static bool dcp_h17p_external_supported(struct device_node *n) {(void)n;return false;} +static bool table_has(const struct of_device_id *table,const char *compatible) +{for(;table->compatible;table++)if(!strcmp(table->compatible,compatible))return true;return false;} +static struct device_node *next_node(struct device_node *previous,const struct of_device_id *table) +{ + unsigned i=0;if(previous){while(nodes[i] && nodes[i]!=previous)i++;if(nodes[i])i++;} + for(;nodes[i];i++)if(table_has(table,nodes[i]->compatible))return nodes[i]; + return NULL; +} +#define for_each_matching_node(np,table) for(np=next_node(NULL,table);np;np=next_node(np,table)) +#define for_each_endpoint_of_node(np,ep) for(ep=NULL;ep;ep=NULL) +static struct device_node *of_graph_get_remote_port_parent(struct device_node *n) {(void)n;return NULL;} +static void of_node_put(struct device_node *n) {(void)n;} +static int component_compare_of(struct device *d,void *p) {(void)d;(void)p;return 1;} +static void drm_of_component_match_add(struct device *d,struct component_match **m,int (*compare)(struct device *,void *),struct device_node *n) +{(void)d;(void)compare;*m=&matches;assert(matches.count<4);matches.nodes[matches.count++]=n;} +static int apple_drm_ops; +static int component_master_add_with_match(struct device *d,int *ops,struct component_match *m) +{(void)d;assert(ops==&apple_drm_ops && m && m->count>0);masters++;return 0;} +''' +component_functions='\n'.join(item(master, marker) for marker in [ + 'static bool apple_dcp_usable(struct device_node *np)', + 'static int add_dcp_components(struct device *dev,', + 'static int apple_platform_probe(struct platform_device *pdev)']) +component_main=r''' +int main(void) +{ + struct device_node dcp={"apple,t6034-dcp",true}; + struct device_node ext={"apple,t6034-dcpext",true}; + struct device_node display={"apple,t6034-display-subsystem",true}; + struct platform_device platform={{&display}}; + assert(table_has(master_of_match,display.compatible)); + assert(table_has(dcp_of_match,dcp.compatible)); + nodes[0]=&dcp;nodes[1]=&ext; + assert(apple_platform_probe(&platform)==0 && masters==1); + assert(matches.count==1 && matches.nodes[0]==&dcp); + matches.count=0;masters=0;dcp.available=false; + assert(apple_platform_probe(&platform)==-ENODEV && !masters && !matches.count); + dcp.available=true;dcp.compatible="apple,t6031-dcp"; + assert(apple_platform_probe(&platform)==0 && matches.count==1); + matches.count=0;masters=0;dcp.compatible="apple,t6034-dcpext";nodes[1]=NULL; + assert(apple_platform_probe(&platform)==-ENODEV && !masters && !matches.count); + return 0; +} +''' +component_source='\n'.join([component_prefix,'\n'.join('static const char '+n+';' for n in data_names),component_table,master_table,dcp_table,component_functions,component_main]) +component_mutants={ + 'control':component_source, + 'missing-master-dcp':component_source.replace(component_table,component_table.replace('"apple,t6034-dcp"','"apple,missing-t6034-dcp"')), + 'missing-master-binding':component_source.replace(master_table,master_table.replace('"apple,t6034-display-subsystem"','"apple,missing-t6034-display-subsystem"')), + 'missing-dcp-binding':component_source.replace(dcp_table,dcp_table.replace('"apple,t6034-dcp"','"apple,missing-t6034-dcp"')), + 'lost-disabled-filter':component_source.replace('return of_device_is_available(np) &&','return true &&'), +} +with tempfile.TemporaryDirectory(prefix='t6034-components-') as directory: + for name,c in component_mutants.items(): + exe=Path(directory)/name + subprocess.run(['cc','-std=gnu11','-Wall','-Wextra','-Werror','-x','c','-','-o',str(exe)],input=c,text=True,check=True) + result=subprocess.run([str(exe)],capture_output=True) + assert (result.returncode==0)==(name=='control'),(name,result.stderr) + print('components '+name+': '+('pass' if name=='control' else 'caught')) +print('T6034 DCP/platform matches, production component enumeration and disabled filtering: PASS') diff --git a/tools/aurora-ci/host-tests.json b/tools/aurora-ci/host-tests.json index 31e774562621ef..006e6d14b37506 100644 --- a/tools/aurora-ci/host-tests.json +++ b/tools/aurora-ci/host-tests.json @@ -2235,7 +2235,7 @@ "python3", "tools/asahi/t6034-display-tests.py" ], - "note": "Production J514M/T6034 display default, board/image selection and PMP report OF table; compiled wrong-SoC, lost-disable, firmware-identity, missing-report-match, halted-owner and ACK-aperture mutants. No hardware or private fixtures." + "note": "Production J514M/T6034 display admission, PMP report OF table, DCP/platform matching and component enumeration; compiled board/image, report-owner/aperture, missing-component/binding and disabled-filter mutants. No hardware or private fixtures." }, { "name": "tools/testing/selftests/drivers/gpu/asahi-g15/run.py", From a2ca52344980ec5734f1e0615445bb8aed7eb4ae Mon Sep 17 00:00:00 2001 From: Chris Kearney <303316+chriskearney@users.noreply.github.com> Date: Thu, 5 Nov 2026 09:08:24 -0800 Subject: [PATCH 09/10] tools/asahi: include T6034 in the display admission fixture The shared production display-slot selector gained T6034 support, but the T6030 pthread/sanitizer harness declared only the T6030 and T6031 slots. Importing the current production body therefore fails to compile every variant, including the control, on apple_t6034_display. This caused the host-tools failure in full CI run 42066154106 at 9056262c3010 and reproduces on the subsequent diagnostic source; the new T6034 suites passed. Declare the third slot in the host fixture and include it in reset and non-provider checks. Publish T6034 alongside T6031, check that neither can satisfy T6030 admission, and verify that withdrawing T6034 preserves the T6031 owner and both reference counts. Add missing-slot and shared-slot mutants while retaining all existing compile and runtime requirements. The sanitizer/pthread control passes 17 boundaries, all ten mutants compile and fail at runtime, and the maintained runner passes all six related host suites without skips. Manifest checks pass and checkpatch reports no errors or warnings. This changes only the host tests; the frozen 1.60343 diagnostic kernel, configuration, device trees and firmware policy are unchanged. Fixes: 8a1c538b2d50 ("drm/apple: admit J514M native display on normal boots") Assisted-by: Codex:GPT-6 Signed-off-by: Scott Jones --- tools/asahi/t6030-display-admission-tests.c | 24 ++++++++++++++++---- tools/asahi/t6030-display-admission-tests.py | 2 ++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/tools/asahi/t6030-display-admission-tests.c b/tools/asahi/t6030-display-admission-tests.c index ebf3e2e868324f..3f281d8150bc24 100644 --- a/tools/asahi/t6030-display-admission-tests.c +++ b/tools/asahi/t6030-display-admission-tests.c @@ -25,11 +25,13 @@ struct device_link { static pthread_mutex_t apple_g15_display_lock = PTHREAD_MUTEX_INITIALIZER; static struct device *apple_t6030_display; static struct device *apple_t6031_display; +static struct device *apple_t6034_display; static struct device_node display_node = { "apple,t6030-display-subsystem" }, gpu_node = { "apple,agx-t6030" }, other_node = { "apple,display-subsystem" }, - t6031_display_node = { "apple,t6031-display-subsystem" }; -static struct device display, duplicate, gpu, t6031_display; + t6031_display_node = { "apple,t6031-display-subsystem" }, + t6034_display_node = { "apple,t6034-display-subsystem" }; +static struct device display, duplicate, gpu, t6031_display, t6034_display; static const char *machine = "apple,t6030"; static int pmp_calls, link_calls, pmp_result; static bool link_ok = true; @@ -108,7 +110,8 @@ static int apple_pmp_link_device(struct device *dev) #include "production.h" static void reset(void) { - assert(!apple_t6030_display && !apple_t6031_display && !locked); + assert(!apple_t6030_display && !apple_t6031_display && + !apple_t6034_display && !locked); display = (struct device){ .of_node = &display_node, .refs = 1 }; duplicate = (struct device){ .of_node = &display_node, .refs = 1 }; gpu = (struct device){ .of_node = &gpu_node, .refs = 1 }; @@ -179,7 +182,7 @@ int main(void) cases++; display.of_node = &other_node; assert(apple_g15_display_publish(&display) == 0 && - !apple_t6030_display && !apple_t6031_display && + !apple_t6030_display && !apple_t6031_display && !apple_t6034_display && atomic_load(&display.refs) == 1); cases++; /* The T6031 display has its own slot: it never satisfies T6030 admission. */ @@ -188,6 +191,19 @@ int main(void) apple_t6031_display == &t6031_display && !apple_t6030_display && atomic_load(&t6031_display.refs) == 2); assert(asahi_g15_pmp_prepare(&gpu) == -EPROBE_DEFER && pmp_calls == 2); + /* T6034 can coexist with T6031 without satisfying T6030 admission. */ + t6034_display = (struct device){ .of_node = &t6034_display_node, .refs = 1 }; + assert(apple_g15_display_publish(&t6034_display) == 0 && + apple_t6034_display == &t6034_display && + apple_t6031_display == &t6031_display && !apple_t6030_display && + atomic_load(&t6034_display.refs) == 2); + assert(asahi_g15_pmp_prepare(&gpu) == -EPROBE_DEFER && pmp_calls == 2); + cases++; + apple_g15_display_withdraw(&t6034_display); + assert(!apple_t6034_display && apple_t6031_display == &t6031_display && + atomic_load(&t6034_display.refs) == 1 && + atomic_load(&t6031_display.refs) == 2); + cases++; apple_g15_display_withdraw(&t6031_display); assert(!apple_t6031_display && atomic_load(&t6031_display.refs) == 1); cases++; diff --git a/tools/asahi/t6030-display-admission-tests.py b/tools/asahi/t6030-display-admission-tests.py index 479c11b00fed01..2aa4ecae03464f 100644 --- a/tools/asahi/t6030-display-admission-tests.py +++ b/tools/asahi/t6030-display-admission-tests.py @@ -48,6 +48,8 @@ def main(): ('wrong-gpu', '!of_device_is_compatible(consumer->of_node, gpu)', '((void)gpu, false)'), ('withdraw-wrong-provider', 'if (*slot == dev)', 'if (((void)dev, true))'), ('shared-display-slot', 'return &apple_t6031_display;', 'return &apple_t6030_display;'), + ('missing-t6034-slot', 'return &apple_t6034_display;', 'return NULL;'), + ('t6034-shared-display-slot', 'return &apple_t6034_display;', 'return &apple_t6031_display;'), ]: assert code.count(old) == 1, (name, code.count(old)) variants.append((name, code.replace(old, new))) From e585531e0ba61a26d7e5e844ac938db310034435 Mon Sep 17 00:00:00 2001 From: Chris Kearney <303316+chriskearney@users.noreply.github.com> Date: Thu, 5 Nov 2026 09:21:56 -0800 Subject: [PATCH 10/10] drm/asahi: pin the measured J514M GPU firmware identity The 1.60343 diagnostic on J514M reached native apple-drm publication and reported one canonical loaded TEXT identity twice, then refused GPU admission with EINVAL before firmware startup. Pin only that exact J514M row: TEXT 0x64000, DATA 0x140000, GKTS 0x5cf28 and canonical SHA-256 4ca24b658f8ebe7c27375fdf38e41e5daf8632515077b537ca19e6d0dc7704b7. The raw collector result remains FAILED because its unanchored accepted- build regex also matched the unsupported-build refusal; the independently reviewed log and source control flow establish the expected refusal. Make the internal identity helper's prefix policy explicit. T6034 never consults g15_accept_text_prefix, including with a nonempty build table; older profiles keep their existing explicitly requested override behavior. The exact board/SoC, measured hardware IDs, B1 revision, four-cluster/six-MCC layout, normal defaults, owner opt-outs and display-before-GPU checks stay. Inherited J516C power/reference words remain explicitly unqualified. Compile the actual profile and production lookup against its public row, rejecting every other board and each changed identity field. Exercise the production identity helper with a nonempty strict table and verify that it never reads a matching override prefix. Production admit() mutations catch both enabling a T6034 prefix and changing the older profiles' policy. All six related maintained host suites and manifest checks pass; checkpatch reports no errors or warnings. No firmware images were read or exported. This is a candidate for a bounded idle/single low-load native pixel probe, not hardware or power/thermal qualification. R4's new frequency-reporting warning remains unresolved; the DCP clock rate and timing are unchanged. No additional hardware action or package build accompanies this commit. Assisted-by: Codex:GPT-6 Signed-off-by: Scott Jones --- drivers/gpu/drm/asahi/g15/boot.rs | 4 ++- drivers/gpu/drm/asahi/g15/hw/t6034.rs | 23 ++++++++---- drivers/gpu/drm/asahi/g15/identity.rs | 8 +++-- tools/asahi/g15-display-order-tests.py | 7 +++- tools/asahi/g15-t6034-profile-tests.py | 48 +++++++++++++++++++++++--- tools/aurora-ci/host-tests.json | 2 +- 6 files changed, 75 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/asahi/g15/boot.rs b/drivers/gpu/drm/asahi/g15/boot.rs index 6a7c1f0aec0480..b0ea7bfc44aedf 100644 --- a/drivers/gpu/drm/asahi/g15/boot.rs +++ b/drivers/gpu/drm/asahi/g15/boot.rs @@ -526,7 +526,9 @@ pub(crate) fn admit( } let adt = adt::read(dev, cfg) .inspect_err(|e| dev_err!(dev, "GPU regions not admitted: {:?}\n", e))?; - identity::firmware(dev, &adt, cfg.firmware) + // T6034 requires complete identities; the older profiles keep their + // existing explicitly requested bring-up prefix policy. + identity::firmware(dev, &adt, cfg.firmware, cfg.chip_id != 0x6034) .inspect_err(|e| dev_err!(dev, "loaded GFX TEXT identity mismatch: {:?}\n", e))?; dev_info!(dev, "M3: firmware identity accepted\n"); if cfg.board == hw::Board::None { diff --git a/drivers/gpu/drm/asahi/g15/hw/t6034.rs b/drivers/gpu/drm/asahi/g15/hw/t6034.rs index 66652d2e29cd57..1c0ec130cc177e 100644 --- a/drivers/gpu/drm/asahi/g15/hw/t6034.rs +++ b/drivers/gpu/drm/asahi/g15/hw/t6034.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT -//! J514M/T6034 (M3 Max, G15C) measurement profile. +//! J514M/T6034 (M3 Max, G15C) measured boot-chain profile. //! //! The powered identity read on this J514M found four ten-slot clusters with //! enabled masks [0x1fd, 0x1fd, 0x1fc, 0x1ed], or 30 enabled cores. Its ADT has @@ -9,12 +9,12 @@ //! macOS capture of this machine. The J516C power/reference words remain //! unqualified for this board and are identified at admission. //! -//! No loaded firmware digest has been measured by the kernel yet. An empty -//! build list permits the normal admission path to report that identity and -//! refuses firmware startup, including the generic prefix escape hatch. +//! The loaded firmware identity below was measured after native display +//! publication on J514M. Runtime and power/thermal qualification remain +//! pending; this profile never permits the generic digest-prefix override. use super::super::Config; -use super::{HwId, IoMap, Layout}; +use super::{FirmwareBuild, HwId, IoMap, Layout}; /// Six memory-cache elements, rather than the T6031 reference's eight. const MCC_BYTES: u64 = 6 * 0xd_8000; @@ -74,7 +74,18 @@ pub(crate) const CONFIG: Config = Config { version: 0x07041100, counts: 0x0021040a, }], - firmware: &[], + firmware: &[FirmwareBuild { + name: "J514M RTKit 2419.140.12 (runtime qualification pending)", + machine: Some(kernel::c_str!("apple,j514m")), + text_size: 0x64000, + data_size: 0x140000, + marker: 0x5cf28, + text_sha256: [ + 0x4c, 0xa2, 0x4b, 0x65, 0x8f, 0x8e, 0xbe, 0x7c, 0x27, 0x37, 0x5f, 0xdf, 0x38, + 0xe4, 0x1e, 0x5d, 0xaf, 0x86, 0x32, 0x51, 0x50, 0x77, 0xb5, 0x37, 0xca, 0x19, + 0xe6, 0xd0, 0xdc, 0x77, 0x04, 0xb7, + ], + }], machine: kernel::c_str!("apple,j514m"), platform: super::Platform::DeviceTree, layout: &LAYOUT, diff --git a/drivers/gpu/drm/asahi/g15/identity.rs b/drivers/gpu/drm/asahi/g15/identity.rs index c9d8b264c8491a..4193a3cba90780 100644 --- a/drivers/gpu/drm/asahi/g15/identity.rs +++ b/drivers/gpu/drm/asahi/g15/identity.rs @@ -70,10 +70,12 @@ impl From<&FirmwareBuild> for Measured { /// Identifies the loaded firmware build: the pinned build, or `None` for a build admitted by /// its digest prefix (`g15_accept_text_prefix`). +/// With `allow_prefix` false, only a complete pinned identity may be admitted. pub(crate) fn firmware( dev: &device::Device, adt: &super::adt::GpuAdt, builds: &'static [FirmwareBuild], + allow_prefix: bool, ) -> Result> { let size = usize::try_from(adt.text.end - adt.text.start)?; let data_size = adt.data.end - adt.data.start; @@ -97,9 +99,9 @@ pub(crate) fn firmware( let qualified = builds .iter() .map(|b| (Measured::from(b), b.machine.map_or(true, glue::machine))); - // An incomplete SoC profile is measurement-only, even if a generic - // bring-up prefix remains on the command line. It must first pin a build. - let accept_prefix = if builds.is_empty() { + // A profile may require exact identities even after its first build is + // pinned. Empty profiles remain measurement-only in either policy. + let accept_prefix = if !allow_prefix || builds.is_empty() { 0 } else { super::params::accept_text_prefix() diff --git a/tools/asahi/g15-display-order-tests.py b/tools/asahi/g15-display-order-tests.py index 18f7ea4da41db5..6ff26493965c60 100644 --- a/tools/asahi/g15-display-order-tests.py +++ b/tools/asahi/g15-display-order-tests.py @@ -306,7 +306,10 @@ def c_source(root): pub fn read(_: &super::device::Device, _: &super::Config) -> super::Result { super::call("adt"); Ok(GpuAdt) } } mod identity { - pub fn firmware(_: &super::device::Device, _: &super::adt::GpuAdt, _: ()) -> super::Result { super::call("identity"); Ok(()) } + pub fn firmware(_: &super::device::Device, _: &super::adt::GpuAdt, _: (), allow_prefix: bool) -> super::Result { + super::STATE.with(|s| assert_eq!(allow_prefix, !s.borrow().t6034)); + super::call("identity"); Ok(()) + } } mod initdata { pub fn prepare_board(_: &super::device::Device, _: &super::Config, _: &super::adt::GpuAdt) -> super::Result { @@ -457,6 +460,8 @@ def c_source(root): } RS_MUTANTS = { + "t6034-prefix-enabled": ('cfg.chip_id != 0x6034', 'true'), + "other-prefix-policy-lost": ('cfg.chip_id != 0x6034', 'false'), "t6034-ignores-soc": ('&& glue::machine(kernel::c_str!("apple,t6034"))', "&& true"), "candidate-ignores-config-chip": ("cfg.chip_id == 0x6031", "true"), "candidate-ignores-board": ('&& glue::machine(kernel::c_str!("apple,j514c"))', "&& true"), diff --git a/tools/asahi/g15-t6034-profile-tests.py b/tools/asahi/g15-t6034-profile-tests.py index c1820bb29f992d..4f73b5172aee76 100644 --- a/tools/asahi/g15-t6034-profile-tests.py +++ b/tools/asahi/g15-t6034-profile-tests.py @@ -1,6 +1,10 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0-only OR MIT -"""Compile the actual G15C profiles and test T6034 shape and measurement-only admission.""" +"""Compile actual G15C profiles and test T6034 shape and exact firmware admission. + +The diagnostic profile has no firmware row. Once a measured row is present, +exercise its complete public identity and J514M binding without reading an image. +""" from pathlib import Path import subprocess import tempfile @@ -28,6 +32,8 @@ def item(text, marker): #[macro_export] macro_rules! c_str { ($s:literal) => { $crate::c_str_const(concat!($s,"\0").as_bytes()) }; } const fn c_str_const(s: &[u8])->&core::ffi::CStr { unsafe { core::ffi::CStr::from_bytes_with_nul_unchecked(s) } } mod hw { pub enum GpuVariant { C } } +#[path="@MATCHING@"] mod matching; +#[path="@LOOKUP@"] mod lookup; mod g15 { use kernel::str::CStr; @CONFIG@ @@ -51,7 +57,35 @@ def item(text, marker): assert_eq!((c.num_clusters,c.num_cores_per_cluster),(4,10)); assert_eq!(c.gpu_revision,0x11); assert_eq!((c.hw_ids[0].version,c.hw_ids[0].counts),(0x07041100,0x0021040a)); - assert!(c.firmware.is_empty()); + // An empty table is the first diagnostic build. A completed profile may + // contain only the measured row for this exact board. + assert_eq!(c.firmware.len(),1); + if !c.firmware.is_empty() { + use lookup::{Measured,Verdict}; + assert_eq!(c.firmware.len(),1); + let b=&c.firmware[0]; + assert_eq!(b.machine.map(|m|m.to_bytes()),Some(b"apple,j514m".as_slice())); + let exact=Measured {text_size:b.text_size,data_size:b.data_size,marker:b.marker,sha256:b.text_sha256}; + let verdict=|measured:&Measured,board:&[u8]| { + let qualified=c.firmware.iter().map(|b| { + (Measured {text_size:b.text_size,data_size:b.data_size,marker:b.marker,sha256:b.text_sha256}, + b.machine.is_some_and(|m|m.to_bytes()==board)) + }); + lookup::verdict(measured,qualified,0) + }; + assert_eq!(verdict(&exact,b"apple,j514m"),Verdict::Pinned(0)); + for board in [b"apple,j514c".as_slice(),b"apple,j516c",b"apple,j516m",b"apple,j514s",b"apple,j999"] { + assert_eq!(verdict(&exact,board),Verdict::Refused); + } + let mut changed=exact;changed.sha256[31]^=1; + assert_eq!(verdict(&changed,b"apple,j514m"),Verdict::Refused); + let mut changed=exact;changed.text_size+=0x4000; + assert_eq!(verdict(&changed,b"apple,j514m"),Verdict::Refused); + let mut changed=exact;changed.data_size+=0x4000; + assert_eq!(verdict(&changed,b"apple,j514m"),Verdict::Refused); + let mut changed=exact;changed.marker+=8; + assert_eq!(verdict(&changed,b"apple,j514m"),Verdict::Refused); + } assert_eq!((c.compute.tail,c.compute.ptc,c.compute.dupm),(0x2980,0x6000400020,0x3c000000140)); assert_eq!((c.compute.tail,c.compute.ptc,c.compute.dupm),(t6031::CONFIG.compute.tail,t6031::CONFIG.compute.ptc,t6031::CONFIG.compute.dupm)); let mask=0x7b5fc7f5fdu64; @@ -73,7 +107,7 @@ def item(text, marker): assert_eq!(t6031::IOMAPS.iter().find(|m|m.slot==11).unwrap().map_bytes,8*0xd8000); } ''' -for token,replacement in {'@CONFIG@':config,'@REFERENCE@':reference,'@VALUE@':value,'@TYPES@':types,'@T6031@':str(G15/'hw/t6031.rs'),'@T6034@':str(G15/'hw/t6034.rs')}.items():source=source.replace(token,replacement) +for token,replacement in {'@CONFIG@':config,'@REFERENCE@':reference,'@VALUE@':value,'@TYPES@':types,'@T6031@':str(G15/'hw/t6031.rs'),'@T6034@':str(G15/'hw/t6034.rs'),'@MATCHING@':str(G15/'identity/matching.rs'),'@LOOKUP@':str(G15/'identity/lookup.rs')}.items():source=source.replace(token,replacement) identity=(G15/'identity.rs').read_text() # Run the production firmware admission against synthetic public identity bytes. firmware=item(identity,'pub(crate) fn firmware(') @@ -108,12 +142,16 @@ def item(text, marker): use super::*; FIRMWARE } +static PINNED:[FirmwareBuild;1]=[FirmwareBuild {text_size:64,data_size:64,marker:16,text_sha256:[0x11;32],machine:Some("apple,j514m"),name:"synthetic pinned identity"}]; static OTHER:[FirmwareBuild;1]=[FirmwareBuild {text_size:64,data_size:64,marker:16,text_sha256:[0x22;32],machine:None,name:"other"}]; fn main() { let a=adt::GpuAdt{text:0x1000..0x1040,data:0x2000..0x2040}; - assert!(identity::firmware(&device::Device,&a,&[]).is_err()); + assert!(identity::firmware(&device::Device,&a,&[],true).is_err()); assert_eq!(PREFIX_READS.load(Ordering::Relaxed),0,"empty profile must not consult prefix escape hatch"); - assert!(identity::firmware(&device::Device,&a,&OTHER).unwrap().is_none(),"existing explicitly requested prefix behavior remains"); + assert!(identity::firmware(&device::Device,&a,&PINNED,false).unwrap().is_some(),"pinned exact identity remains admitted with prefix disabled"); + assert!(identity::firmware(&device::Device,&a,&OTHER,false).is_err(),"nonempty strict profile must reject a matching override prefix"); + assert_eq!(PREFIX_READS.load(Ordering::Relaxed),0,"strict profiles never consult the prefix escape hatch"); + assert!(identity::firmware(&device::Device,&a,&OTHER,true).unwrap().is_none(),"existing explicitly requested prefix behavior remains"); assert_eq!(PREFIX_READS.load(Ordering::Relaxed),1); } ''' diff --git a/tools/aurora-ci/host-tests.json b/tools/aurora-ci/host-tests.json index 006e6d14b37506..fb9cdbdd66fc69 100644 --- a/tools/aurora-ci/host-tests.json +++ b/tools/aurora-ci/host-tests.json @@ -2226,7 +2226,7 @@ "tools/asahi/g15-t6034-profile-tests.py" ], "rust": "1.93.1", - "note": "Production G15C profiles, exact fused T6034 topology, six memory channels and empty-firmware refusal without the prefix escape hatch; public synthetic identity inputs only." + "note": "Production G15C profiles, exact T6034 topology/six memory channels, full J514M firmware tuple and wrong-board/field refusal; strict nonempty and empty profiles never consult prefix overrides, while older policy remains unchanged. Public identity inputs only." }, { "name": "tools/asahi/t6034-display-tests.py",