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), {} };