Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 39 additions & 15 deletions drivers/accel/ane/ane_fw_cold.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
/*
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand All @@ -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;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions drivers/accel/ane/ane_fw_cold.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
29 changes: 29 additions & 0 deletions drivers/accel/ane/ane_fw_platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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),
};

/*
Expand Down
63 changes: 60 additions & 3 deletions drivers/accel/ane/ane_fw_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -99,26 +113,69 @@ 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];
unsigned int i;

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
Expand Down
35 changes: 35 additions & 0 deletions drivers/accel/ane/ane_fw_platform_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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),
{}
};

Expand Down
Loading