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: 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"; diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 2324a8acd4c72a..b6a5be040ef9c8 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 @@ -889,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" }, {}, }; @@ -1237,6 +1250,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/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..b0ea7bfc44aedf 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, @@ -519,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/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..1c0ec130cc177e --- /dev/null +++ b/drivers/gpu/drm/asahi/g15/hw/t6034.rs @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT + +//! 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 +//! 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. +//! +//! 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::{FirmwareBuild, 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: &[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, + 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..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,7 +99,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()) { + // 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() + }; + 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/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/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/g15-display-order-tests.py b/tools/asahi/g15-display-order-tests.py index 202a63e59f1e54..6ff26493965c60 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()) }; @@ -282,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 { @@ -301,7 +328,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 +343,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 +354,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 +364,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 +433,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 +460,9 @@ 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"), "candidate-ignores-soc": ('&& glue::machine(kernel::c_str!("apple,t6031"))', "&& true"), @@ -471,8 +528,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..4f73b5172aee76 --- /dev/null +++ b/tools/asahi/g15-t6034-profile-tests.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only OR MIT +"""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 +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 } } +#[path="@MATCHING@"] mod matching; +#[path="@LOOKUP@"] mod lookup; +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)); + // 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; + 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; + 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'),'@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(') +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 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,&[],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,&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); +} +''' +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 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))) diff --git a/tools/asahi/t6034-display-tests.py b/tools/asahi/t6034-display-tests.py new file mode 100644 index 00000000000000..ddd1cf54cc4c39 --- /dev/null +++ b/tools/asahi/t6034-display-tests.py @@ -0,0 +1,216 @@ +#!/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 re +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') + +# 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') + +# 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 b73ea9977e5dd9..fb9cdbdd66fc69 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 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", + "group": "tools", + "argv": [ + "python3", + "tools/asahi/t6034-display-tests.py" + ], + "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", "group": "tools",