From d927a327d525853c2ca8791f7dc7287286beed75 Mon Sep 17 00:00:00 2001 From: TripleU <182648541+TripleU613@users.noreply.github.com> Date: Thu, 12 Nov 2026 20:18:30 -0500 Subject: [PATCH] accel/ane: T6050: admit the macOS 27.3 beta 1 firmware On macOS 27.3 beta 1 (26C5035r, iBoot 20457.60.65) the ANE firmware that iBoot preloads on T6050 has a different TEXT/DATA split. Apple moved 0x2c000 bytes from DATA into TEXT; both layouts end at the same DVA, so the firmware span (heap tail, pool, IPC) does not move: 27.0: TEXT 0xc0000 @ 0x10000000000, DATA 0x2ac000 @ 0x100000c0000 27.3: TEXT 0xec000 @ 0x10000000000, DATA 0x280000 @ 0x100000ec000 The hand-off check knows only the 27.0 layout and pin, so the driver leaves the engine alone ("preload layout"). Once started, the 27.3 firmware also asks for a bigger IPC area: it reports 0x10440 (IPC size minus one) where 27.0 reports 0xc440, and the boot path refused anything other than 0xc440 and mapped only 64 KiB. - Give a platform a table of admitted preload builds. The handed-off segment layout selects the build, and the build supplies the TEXT sha256 pin. T6050 admits 27.0 (its existing pin) and 27.3. Platforms without a table keep their single layout and pin. - Map the IPC window up to the heap (0x14000 bytes) and accept any IPC size the firmware requests that holds the channel table and fits that window, then pass that size back in the init block instead of the fixed 0xc441. The H16 boot path already checks the request this way. - Log the selected build and the measured TEXT digest, and name the layout when no admitted build matches it. - KUnit: the layout selects the build and its pin, and a mix of the two layouts matches neither. The H17 boot path is shared with T8140. Its firmware's 0xc440 request still passes, and the IPC mapping grows from 64 KiB to the 80 KiB window below the heap, which nothing else maps. Tested on a J714s (M5 Pro) on 27.3 beta 1, booted through Aura with the preloaded ANE handed over, on integration/aurora f1b79c4beaef and later bases with this change (plus the 27.3 GPU and DCP changes that are still out of tree): H17 ANE: loader hand-off verified (27.3 build, TEXT sha256 115575864d4c8d02d48d83077170e7e16079aaf70dadd608c71910ac66dc6066); starting the engine H17 ANE: READY; IPC 0x10441 bytes requested The engine came up at bind and /dev/accel/accel0 was present on each test boot where it was checked, on several kernels from these bases, with no SError or Oops. No inference workload was run. The 27.0 firmware and T8140 were not re-tested with this change. Signed-off-by: TripleU <182648541+TripleU613@users.noreply.github.com> Assisted-by: LLM (Claude Code) Co-Authored-By: Claude Opus 5.5 --- drivers/accel/ane/ane_fw_cold.c | 54 ++++++++++++++------ drivers/accel/ane/ane_fw_cold.h | 3 ++ drivers/accel/ane/ane_fw_platform.c | 29 +++++++++++ drivers/accel/ane/ane_fw_platform.h | 63 ++++++++++++++++++++++-- drivers/accel/ane/ane_fw_platform_test.c | 35 +++++++++++++ 5 files changed, 166 insertions(+), 18 deletions(-) diff --git a/drivers/accel/ane/ane_fw_cold.c b/drivers/accel/ane/ane_fw_cold.c index 6f447afa769e2..858caae4635ad 100644 --- a/drivers/accel/ane/ane_fw_cold.c +++ b/drivers/accel/ane/ane_fw_cold.c @@ -74,6 +74,8 @@ struct ane_fw_backend { struct ane_fw_surface preload[2], heap_tail; u64 preload_phys[2]; u8 data_digest[SHA256_DIGEST_SIZE]; + /* The TEXT digest of the preloaded build, chosen by its layout. */ + const u8 *text_pin; /* Powered down for system sleep; a (cold or warm) boot is due. */ bool slept; /* @@ -719,7 +721,7 @@ static int ane_fw_reload_segment(struct ane_fw_backend *b, unsigned int i) memunmap(source); dma_wmb(); sha256(s->cpu, s->size, digest); - return memcmp(digest, i ? b->data_digest : b->soc->text_pin, sizeof(digest)) ? + return memcmp(digest, i ? b->data_digest : b->text_pin, sizeof(digest)) ? -EKEYREJECTED : 0; } @@ -1552,7 +1554,8 @@ static int ane_fw_preload_metadata(struct ane_fw_backend *b, u64 physical[2], if (ret) return ret; } - if (!ane_fw_preload_ranges_valid(b->soc, physical, size, dva)) + b->text_pin = ane_fw_preload_text_pin(b->soc, physical, size, dva); + if (!b->text_pin) return -EINVAL; return 0; } @@ -1591,7 +1594,7 @@ static int ane_fw_preload(struct ane_fw_backend *b) sha256_init(&hash); sha256_update(&hash, snapshots[i].cpu, size[i]); sha256_final(&hash, digest); - if (memcmp(digest, i ? data_pin : b->soc->text_pin, sizeof(digest))) + if (memcmp(digest, i ? data_pin : b->text_pin, sizeof(digest))) return -EKEYREJECTED; } /* Preserve every personalized DATA byte, including the iBoot TLV table. */ @@ -1670,7 +1673,11 @@ static int ane_fw_prepare(struct ane_fw_backend *b) IOMMU_READ | IOMMU_WRITE, &b->pool); if (ret) return ret; - ret = ane_fw_alloc_at(b, ANE_FW_IPC_DVA, SZ_64K, + /* + * The whole window up to the heap: the firmware names its IPC size at + * boot (0xc441 bytes on 27.0, 0x10441 on T6050 27.3). + */ + ret = ane_fw_alloc_at(b, ANE_FW_IPC_DVA, ANE_FW_HEAP_DVA - ANE_FW_IPC_DVA, IOMMU_READ | IOMMU_WRITE, &b->ipc); if (ret) return ret; @@ -1691,7 +1698,7 @@ static int ane_fw_boot(struct ane_fw_backend *b) { void __iomem *scratch = b->engine + b->soc->scratch; u8 *init = b->pool.cpu + 0x180; - u64 table; + u64 table, ipc_bytes; unsigned int i; int ret; @@ -1707,15 +1714,22 @@ static int ane_fw_boot(struct ane_fw_backend *b) if (ret) return ret; dev_dbg(&b->pdev->dev, "ANE boot: READY\n"); - if (ane_rd32(b, scratch) != 8 || ane_rd32(b, scratch + 4) != 0xc440 || + /* + * The firmware's requests: 8 channels, its IPC size minus one, the heap + * and the mode. The IPC must fit the window mapped for it. + */ + ipc_bytes = (u64)ane_rd32(b, scratch + 4) + 1; + if (ane_rd32(b, scratch) != 8 || ipc_bytes < ANE_FW_TABLE_SIZE || + ipc_bytes > b->ipc.size || ane_rd32(b, scratch + 12) != 0xf00000 || ane_rd32(b, scratch + 16)) return -EPROTO; + dev_info(&b->pdev->dev, "H17 ANE: READY; IPC %#llx bytes requested\n", ipc_bytes); put_unaligned_le64(b->ipc.dva, init + 8); put_unaligned_le64(ANE_FW_SPAN, init + 0x10); put_unaligned_le64(0xfc80000, init + 0x18); put_unaligned_le64(b->heap.dva, init + 0x20); put_unaligned_le64(b->heap.size, init + 0x28); - put_unaligned_le64(0xc441, init + 0x50); + put_unaligned_le64(ipc_bytes, init + 0x50); put_unaligned_le64(b->pool.dva, init + 0x58); put_unaligned_le64(b->pool.size, init + 0x60); put_unaligned_le32(64, init + 0x68); @@ -2125,7 +2139,7 @@ static bool ane_fw_handoff_present(struct platform_device *pdev, struct device_node *np = pdev->dev.of_node; struct device_node *chosen __free(device_node) = of_find_node_by_path("/chosen"); struct ane_fw_handoff h = {}; - u8 digest[SHA256_DIGEST_SIZE] = {}; + const u8 *pin = NULL; unsigned int i; if (chosen) @@ -2142,13 +2156,16 @@ static bool ane_fw_handoff_present(struct platform_device *pdev, !strcmp(name, i ? "firmware-data" : "firmware-text") && !ane_fw_preload_range(np, i, &h.phys[i], &h.size[i]); } - if (h.regions_ok && ane_fw_preload_ranges_valid(soc, h.phys, h.size, h.dva)) { + if (h.regions_ok) + pin = ane_fw_preload_text_pin(soc, h.phys, h.size, h.dva); + if (pin) { void *text = memremap(h.phys[0], h.size[0], MEMREMAP_WB); + h.build = ane_fw_preload_build(soc, h.phys, h.size, h.dva); if (text) { - sha256(text, h.size[0], digest); + sha256(text, h.size[0], h.text_digest); memunmap(text); - h.text_ok = !memcmp(digest, soc->text_pin, sizeof(digest)); + h.text_ok = !memcmp(h.text_digest, pin, sizeof(h.text_digest)); } } *hp = h; @@ -2157,10 +2174,14 @@ static bool ane_fw_handoff_present(struct platform_device *pdev, dev_info(&pdev->dev, "H17 ANE: no loader hand-off (%s); leaving the engine alone\n", !h.marker || strcmp(h.marker, ANE_FW_COLD_BOOT_EVIDENCE) ? "raw-boot marker" : !h.preloaded || !h.regions_ok ? "preload reservations" : - !h.text_ok ? "firmware TEXT pin" : "preload layout"); - if (h.regions_ok && !h.text_ok) + !pin ? "preload layout" : !h.text_ok ? "firmware TEXT pin" : "preload layout"); + if (h.regions_ok && !pin) + dev_info(&pdev->dev, + "H17 ANE: no admitted build has TEXT %#llx@%#llx DATA %#llx@%#llx\n", + h.size[0], h.dva[0], h.size[1], h.dva[1]); + else if (h.regions_ok && !h.text_ok) dev_info(&pdev->dev, "H17 ANE: preloaded TEXT sha256 %*phN\n", - (int)sizeof(digest), digest); + (int)sizeof(h.text_digest), h.text_digest); return false; } @@ -2354,7 +2375,10 @@ int ane_fw_staged_init(struct platform_device *pdev, const struct ane_fw_platfor ane_backend = NULL; goto fail; } - dev_info(&pdev->dev, "H17 ANE: loader hand-off verified; starting the engine\n"); + dev_info(&pdev->dev, + "H17 ANE: loader hand-off verified (%s build, TEXT sha256 %*phN); starting the engine\n", + handoff.build ? handoff.build->name : "pinned", (int)sizeof(handoff.text_digest), + handoff.text_digest); INIT_WORK(&b->start, ane_fw_start_work); return 0; fail: diff --git a/drivers/accel/ane/ane_fw_cold.h b/drivers/accel/ane/ane_fw_cold.h index a9bea3958790c..d3497f50ec53d 100644 --- a/drivers/accel/ane/ane_fw_cold.h +++ b/drivers/accel/ane/ane_fw_cold.h @@ -153,6 +153,9 @@ struct ane_fw_handoff { bool regions_ok; u64 phys[2], size[2], dva[2]; bool text_ok; + /* The admitted build the layout names and the measured TEXT digest. */ + const struct ane_fw_preload_build *build; + u8 text_digest[32]; const struct ane_fw_cold_state *pmgr; bool crash_kernel; }; diff --git a/drivers/accel/ane/ane_fw_platform.c b/drivers/accel/ane/ane_fw_platform.c index 5639f340d0a61..c17656c17e47e 100644 --- a/drivers/accel/ane/ane_fw_platform.c +++ b/drivers/accel/ane/ane_fw_platform.c @@ -49,6 +49,33 @@ static const u8 ane_t6050_text_pin_27_0[] = { 0xd1, 0xa2, 0x20, 0xd0, 0xee, 0x43, 0x8b, 0x8f, }; +/* The macOS 27.3 build (26C5035r), as iBoot 20457.60.65 preloads it on a J714s. */ +static const u8 ane_t6050_text_pin_27_3[] = { + 0x11, 0x55, 0x75, 0x86, 0x4d, 0x4c, 0x8d, 0x02, + 0xd4, 0x8d, 0x83, 0x07, 0x71, 0x70, 0xe7, 0xe1, + 0x60, 0x79, 0xaa, 0xf7, 0x0d, 0xad, 0xd6, 0x08, + 0xc7, 0x19, 0x10, 0xac, 0x66, 0xdc, 0x60, 0x66, +}; + +/* + * T6050 builds, by their ADT ane0 segment-ranges. 27.3 moved 0x2c000 bytes + * from DATA into TEXT; the firmware span (heap tail, pool, IPC) is unchanged. + */ +static const struct ane_fw_preload_build ane_t6050_builds[] = { + { + .name = "27.0", + .dva = { 0x10000000000, 0x100000c0000 }, + .size = { 0xc0000, 0x2ac000 }, + .text_pin = ane_t6050_text_pin_27_0, + }, + { + .name = "27.3", + .dva = { 0x10000000000, 0x100000ec000 }, + .size = { 0xec000, 0x280000 }, + .text_pin = ane_t6050_text_pin_27_3, + }, +}; + const struct ane_fw_platform ane_h16_platform = { .h16_profiles = ane_h16_profiles, .h16_profile_count = ARRAY_SIZE(ane_h16_profiles), @@ -175,6 +202,8 @@ const struct ane_fw_platform ane_t6050_platform = { .dart_revision = 0x204, .dapf_count = 5, .text_pin = ane_t6050_text_pin_27_0, + .preload_builds = ane_t6050_builds, + .preload_build_count = ARRAY_SIZE(ane_t6050_builds), }; /* diff --git a/drivers/accel/ane/ane_fw_platform.h b/drivers/accel/ane/ane_fw_platform.h index e4afe348b30c1..a2b11206ee522 100644 --- a/drivers/accel/ane/ane_fw_platform.h +++ b/drivers/accel/ane/ane_fw_platform.h @@ -24,6 +24,20 @@ enum ane_fw_irq_role { ANE_FW_IRQ_CHANNELS, }; +/* + * One firmware build the loader may preload: the device addresses and sizes + * of its TEXT and DATA segments and the digest of its TEXT. Apple moves the + * TEXT/DATA split between macOS releases (T6050: 27.0 TEXT 0xc0000 and DATA + * 0x2ac000, 27.3 TEXT 0xec000 and DATA 0x280000, both ending at 0x36c000), + * so the handed-off layout selects the build and the build its TEXT pin. + */ +struct ane_fw_preload_build { + const char *name; + u64 dva[2]; + u64 size[2]; + const u8 *text_pin; +}; + /* Later firmware generations must never enter the H14 staging path. */ struct ane_fw_platform { const struct ane_h16_profile *h16_profiles; @@ -99,11 +113,17 @@ struct ane_fw_platform { u16 dart_revision; u8 dapf_count; const u8 *text_pin; + /* + * The preloaded builds this SoC admits. When set, they replace + * preload_dva, preload_size and text_pin for the hand-off checks. + */ + const struct ane_fw_preload_build *preload_builds; + u8 preload_build_count; }; /* Pure range check: the boot loader supplies the physical addresses. */ static inline bool -ane_fw_preload_ranges_valid(const struct ane_fw_platform *soc, const u64 phys[2], +ane_fw_preload_layout_match(const u64 want_dva[2], const u64 want_size[2], const u64 phys[2], const u64 size[2], const u64 dva[2]) { u64 end[2]; @@ -111,14 +131,51 @@ ane_fw_preload_ranges_valid(const struct ane_fw_platform *soc, const u64 phys[2] for (i = 0; i < 2; i++) { if (!phys[i] || !IS_ALIGNED(phys[i], SZ_16K) || - size[i] != soc->preload_size[i] || - dva[i] != soc->preload_dva[i] || + size[i] != want_size[i] || + dva[i] != want_dva[i] || check_add_overflow(phys[i], size[i], &end[i])) return false; } return end[0] <= phys[1] || end[1] <= phys[0]; } +/* The admitted build whose layout the loader handed off, or NULL. */ +static inline const struct ane_fw_preload_build * +ane_fw_preload_build(const struct ane_fw_platform *soc, const u64 phys[2], + const u64 size[2], const u64 dva[2]) +{ + unsigned int i; + + for (i = 0; i < soc->preload_build_count; i++) + if (ane_fw_preload_layout_match(soc->preload_builds[i].dva, + soc->preload_builds[i].size, phys, size, dva)) + return &soc->preload_builds[i]; + return NULL; +} + +static inline bool +ane_fw_preload_ranges_valid(const struct ane_fw_platform *soc, const u64 phys[2], + const u64 size[2], const u64 dva[2]) +{ + if (soc->preload_build_count) + return ane_fw_preload_build(soc, phys, size, dva); + return ane_fw_preload_layout_match(soc->preload_dva, soc->preload_size, phys, size, dva); +} + +/* The TEXT digest the handed-off layout must carry, or NULL for none. */ +static inline const u8 * +ane_fw_preload_text_pin(const struct ane_fw_platform *soc, const u64 phys[2], + const u64 size[2], const u64 dva[2]) +{ + const struct ane_fw_preload_build *build; + + if (!soc->preload_build_count) + return ane_fw_preload_layout_match(soc->preload_dva, soc->preload_size, phys, + size, dva) ? soc->text_pin : NULL; + build = ane_fw_preload_build(soc, phys, size, dva); + return build ? build->text_pin : NULL; +} + /* * The system sleep path of a device the ane_t6021 driver bound. Devices of * the shared firmware platform (H16, H17) carry the backend's drvdata and diff --git a/drivers/accel/ane/ane_fw_platform_test.c b/drivers/accel/ane/ane_fw_platform_test.c index 7fa82abf63faf..d3c55993f4756 100644 --- a/drivers/accel/ane/ane_fw_platform_test.c +++ b/drivers/accel/ane/ane_fw_platform_test.c @@ -37,6 +37,40 @@ static void ane_preload_ranges_test(struct kunit *test) KUNIT_EXPECT_FALSE(test, ane_fw_preload_ranges_valid(&soc, phys, size, dva)); } +/* + * A SoC with several admitted builds: the handed-off layout picks the build + * and its TEXT pin; a mix of two layouts matches neither. + */ +static void ane_preload_builds_test(struct kunit *test) +{ + static const u8 pin_a[32] = { 0xa }, pin_b[32] = { 0xb }; + static const struct ane_fw_preload_build builds[] = { + { "a", { 0x10000000000, 0x100000c0000 }, { 0xc0000, 0x2ac000 }, pin_a }, + { "b", { 0x10000000000, 0x100000ec000 }, { 0xec000, 0x280000 }, pin_b }, + }; + const struct ane_fw_platform soc = { + .preload_dva = { 0x10000000000, 0x100000c0000 }, + .preload_size = { 0xc0000, 0x2ac000 }, + .text_pin = pin_a, + .preload_builds = builds, + .preload_build_count = ARRAY_SIZE(builds), + }; + u64 phys[] = { 0x100000000, 0x200000000 }; + u64 size[] = { 0xec000, 0x280000 }; + u64 dva[] = { 0x10000000000, 0x100000ec000 }; + + KUNIT_EXPECT_TRUE(test, ane_fw_preload_ranges_valid(&soc, phys, size, dva)); + KUNIT_EXPECT_PTR_EQ(test, ane_fw_preload_build(&soc, phys, size, dva), &builds[1]); + KUNIT_EXPECT_PTR_EQ(test, ane_fw_preload_text_pin(&soc, phys, size, dva), &pin_b[0]); + size[0] = 0xc0000; + size[1] = 0x2ac000; + dva[1] = 0x100000c0000; + KUNIT_EXPECT_PTR_EQ(test, ane_fw_preload_text_pin(&soc, phys, size, dva), &pin_a[0]); + size[0] = 0xec000; + KUNIT_EXPECT_FALSE(test, ane_fw_preload_ranges_valid(&soc, phys, size, dva)); + KUNIT_EXPECT_NULL(test, ane_fw_preload_text_pin(&soc, phys, size, dva)); +} + /* * H16 and H17 devices (any firmware platform) take the backend's sleep path, * never the rtclient one; only the H13/H14 rtclient (no platform) does. @@ -70,6 +104,7 @@ static struct kunit_case ane_fw_platform_cases[] = { KUNIT_CASE(ane_platform_staged_bind_test), KUNIT_CASE(ane_platform_sleep_path_test), KUNIT_CASE(ane_preload_ranges_test), + KUNIT_CASE(ane_preload_builds_test), {} };