From 9c8e6d6ccbd200aa837dc608d9a7c58acf6a4bff Mon Sep 17 00:00:00 2001 From: Hisham Alkhudrawi Date: Thu, 29 Oct 2026 22:32:03 -0400 Subject: [PATCH 01/10] pmgr_adt2dt: resolve no_ps parents to their nearest real ancestors Assisted-By: LLM Signed-off-by: Hisham Alkhudrawi --- proxyclient/tools/pmgr_adt2dt.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/proxyclient/tools/pmgr_adt2dt.py b/proxyclient/tools/pmgr_adt2dt.py index 44d78429..8fbffffd 100755 --- a/proxyclient/tools/pmgr_adt2dt.py +++ b/proxyclient/tools/pmgr_adt2dt.py @@ -81,8 +81,20 @@ def die_label(s): if dev.flags.critical: print("\t\tapple,always-on;") - if any(dt.pmgr_dev_get_parents(dev)): - domains = [f"<&{die_node('ps_'+dev_by_id[idx].name.lower())}>" for idx in dt.pmgr_dev_get_parents(dev) if idx] + # no_ps parents (fabric nodes like SIO/AFISOCNI0 on t8142) have no node; use their parents + def ps_parents(d): + for idx in dt.pmgr_dev_get_parents(d): + if not idx: + continue + p = dev_by_id[idx] + if p.flags.no_ps: + yield from ps_parents(p) + else: + yield p + + parents = list(dict.fromkeys(p.name for p in ps_parents(dev))) + if parents: + domains = [f"<&{die_node('ps_'+name.lower())}>" for name in parents] print(f"\t\tpower-domains = {', '.join(domains)};") print( "\t};") From 384f73de23d154e2307088e5bd658fdad0859456 Mon Sep 17 00:00:00 2001 From: Hisham Alkhudrawi Date: Thu, 29 Oct 2026 22:32:03 -0400 Subject: [PATCH 02/10] kboot: match DT CPU nodes to running CPUs by MPIDR Instead of assuming the DT lists CPUs in the same order as the ADT, look up the live CPU with the node's MPIDR. Same result for in-order trees; lets a bring-up DT reorder CPUs (used to boot one specific E-core early on t8142). Assisted-By: LLM Signed-off-by: Hisham Alkhudrawi --- src/kboot.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/kboot.c b/src/kboot.c index eec8fa20..6187cf0a 100644 --- a/src/kboot.c +++ b/src/kboot.c @@ -584,8 +584,17 @@ static int dt_set_cpus(void) if (dt_mpidr == (mrs(MPIDR_EL1) & 0xFFFFFF)) goto next_cpu; - if (!smp_is_alive(cpu)) { - printf("FDT: CPU %d is not alive, disabling...\n", cpu); + /* Match DT CPU nodes to running CPUs by MPIDR, so the DT order doesn't matter */ + int idx = -1; + for (int i = 0; i < MAX_CPUS; i++) { + if (smp_is_alive(i) && smp_get_mpidr(i) == dt_mpidr) { + idx = i; + break; + } + } + + if (idx < 0) { + printf("FDT: CPU %d (MPIDR 0x%lx) is not alive, disabling...\n", cpu, dt_mpidr); pruned_phandles[pruned++] = fdt_get_phandle(dt, node); int next = fdt_next_subnode(dt, node); @@ -603,16 +612,11 @@ static int dt_set_cpus(void) } } - u64 mpidr = smp_get_mpidr(cpu); - - if (dt_mpidr != mpidr) - bail_cleanup("FDT: DT CPU %d MPIDR mismatch: 0x%lx != 0x%lx\n", cpu, dt_mpidr, mpidr); - - u64 release_addr = smp_get_release_addr(cpu); + u64 release_addr = smp_get_release_addr(idx); if (fdt_setprop_inplace_u64(dt, node, "cpu-release-addr", release_addr)) bail_cleanup("FDT: couldn't set cpu-release-addr property\n"); - printf("FDT: CPU %d MPIDR=0x%lx release-addr=0x%lx\n", cpu, mpidr, release_addr); + printf("FDT: CPU %d MPIDR=0x%lx release-addr=0x%lx\n", idx, dt_mpidr, release_addr); next_cpu: cpu++; From 5568fefc1c3bdadfed3c5034b0ed9a1c0f918740 Mon Sep 17 00:00:00 2001 From: Hisham Alkhudrawi Date: Thu, 29 Oct 2026 22:44:32 -0400 Subject: [PATCH 03/10] kboot: reserve the second CTRR range on t8142 (M5) t8142 has a second CTRR instance, covering the DeviceTree/TrustCache area below the bootloader. iBoot leaves it enabled on the secondary cores, so Linux crashes when it hands those pages to a secondary CPU and writes to them, the same way it does for the first instance. The first instance is already reserved for t8142 through the apple_sysregs_unlocked feature check. Name the second instance's two registers (encodings 3, 0, 11, 0, 2/3, next to the existing pair) and reserve that range too, gated on t8142 and skipped when the instance reads back zero, so the M4 targets are untouched. Assisted-By: LLM Signed-off-by: Hisham Alkhudrawi --- src/cpu_regs.h | 2 ++ src/kboot.c | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/cpu_regs.h b/src/cpu_regs.h index a7f48619..24b87598 100644 --- a/src/cpu_regs.h +++ b/src/cpu_regs.h @@ -822,3 +822,5 @@ /* 48kb read only region given to us by iBoot */ #define SYS_IMP_APL_CTRR_LOWER_EL1 sys_reg(3, 0, 11, 0, 0) #define SYS_IMP_APL_CTRR_UPPER_EL1 sys_reg(3, 0, 11, 0, 1) +#define SYS_IMP_APL_CTRR_D_LOWER_EL1 sys_reg(3, 0, 11, 0, 2) +#define SYS_IMP_APL_CTRR_D_UPPER_EL1 sys_reg(3, 0, 11, 0, 3) diff --git a/src/kboot.c b/src/kboot.c index 6187cf0a..de5d8fc5 100644 --- a/src/kboot.c +++ b/src/kboot.c @@ -388,6 +388,26 @@ static int dt_set_memory(void) bail("FDT: Could not reserve CTRR range\n"); } + // On t8142 a second CTRR instance covers the DeviceTree/TrustCache area below m1n1, and Linux + // faults the same way if it hands those pages to a secondary CPU. Only reserve it where it is + // known to be populated; the instance reads back zero when it is not in use. + if (chip_id == T8142) { + u64 d_lower = mrs(SYS_IMP_APL_CTRR_D_LOWER_EL1); + u64 d_upper = mrs(SYS_IMP_APL_CTRR_D_UPPER_EL1); + + if (d_lower && d_upper >= d_lower) { + u64 d_start = ALIGN_DOWN(d_lower, get_page_size()); + u64 d_end = ALIGN_UP(d_upper + SZ_4K, get_page_size()); + + if (d_start < dram_min || d_end <= d_start || d_end > dram_max) + bail("FDT: Invalid CTRR D range 0x%lx..0x%lx\n", d_start, d_end); + + printf("FDT: Reserving CTRR D range 0x%lx..0x%lx\n", d_start, d_end); + if (fdt_add_mem_rsv(dt, d_start, d_end - d_start)) + bail("FDT: Could not reserve CTRR D range\n"); + } + } + printf("FDT: DRAM at 0x%lx size 0x%lx\n", dram_base, dram_size); printf("FDT: Usable memory is 0x%lx..0x%lx (0x%lx)\n", dram_min, dram_max, dram_max - dram_min); From 68bafd982685aba27454e056540a98a708caed56 Mon Sep 17 00:00:00 2001 From: Hisham Alkhudrawi Date: Thu, 29 Oct 2026 22:32:03 -0400 Subject: [PATCH 04/10] pcie: support t8142 (M5) Register layout and PHY init from macOS AppleT8142PCIe: 8 shared reg entries, the PHY-PHY block at reg[2] + 0x20000, the 100 MHz reference clock poll, the Gen4 PHY IP bit (_initializeGen4Phy) and PHY_CTRL bit 27 (_apcieGPInit). No fuse block. Assisted-By: LLM Signed-off-by: Hisham Alkhudrawi --- src/pcie.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/pcie.c b/src/pcie.c index f4f21fcd..976db536 100644 --- a/src/pcie.c +++ b/src/pcie.c @@ -141,6 +141,7 @@ enum apcie_type { APCIE_T8122 = 2, APCIE_T6031 = 3, APCIE_T8132 = 4, + APCIE_T8142 = 5, }; struct reg_info { @@ -215,6 +216,25 @@ static const struct reg_info regs_t8132 = { .fuse_idx = 5, }; +/* + * M5: macOS (AppleT8142PCIe::start) maps 8 shared reg entries: 1 RC, 2/3/4 PHY / PHY-PHY / PHY-IP, + * 5 AXI2AF, 6 PCIECLKGEN, 7 AUS5 AON top; then 6 per port (config, LTSSM, PHY glue, INTR2AXI, 2 debug). + * No fuse block: fuse_idx only has to be a valid index. + */ +static const struct reg_info regs_t8142 = { + .type = APCIE_T8142, + .compat = APCIE_T8122, + .shared_reg_count = 8, + .config_idx = 0, + .rc_idx = 1, + .phy_common_idx = 2, + .phy_idx = 2, + .phy_ctrl_reset = APCIE_PHY_CTRL_RESET_T8132, + .phy_ip_idx = 4, + .axi_idx = 5, + .fuse_idx = 6, +}; + static const struct reg_info regs_t6031 = { .type = APCIE_T6031, .compat = APCIE_T8122, @@ -308,6 +328,10 @@ static int pcie_init_controller(int controller, const char *path) fuse_bits = NULL; state->pcie_regs = ®s_t8132; printf("pcie: Initializing t8132 PCIe controller\n"); + } else if (adt_is_compatible(adt, adt_offset, "apcie,t8142")) { + fuse_bits = NULL; + state->pcie_regs = ®s_t8142; + printf("pcie: Initializing t8142 PCIe controller\n"); } else if (adt_is_compatible(adt, adt_offset, "apcie,t6040")) { fuse_bits = NULL; state->pcie_regs = ®s_t8132; @@ -390,7 +414,8 @@ static int pcie_init_controller(int controller, const char *path) if (state->pcie_regs->compat == APCIE_T8122) { // The T8122 init seems very similar to the T602X, with different offsets, // and with reg[2], [3] and [4] coalesced to reg[2] in the ADT. - state->phy_base[0] = state->phy_base[0] + 0x8000; + // T8142: the PHY-PHY block is reg[3] = reg[2] + 0x20000 (its PHY tunables target that offset) + state->phy_base[0] += state->pcie_regs->type == APCIE_T8142 ? 0x20000 : 0x8000; state->phy_common_base += 0x4000; } @@ -455,7 +480,8 @@ static int pcie_init_controller(int controller, const char *path) return -1; } - if (state->pcie_regs->type == APCIE_T602X || state->pcie_regs->type == APCIE_T6031) { + if (state->pcie_regs->type == APCIE_T602X || state->pcie_regs->type == APCIE_T6031 || + state->pcie_regs->type == APCIE_T8142) { if (poll32(state->phy_common_base + APCIE_PHYCMN_CLK, APCIE_PHYCMN_CLK_100MHZ, APCIE_PHYCMN_CLK_100MHZ, 250000)) { printf("pcie: Reference clock not available\n"); @@ -520,6 +546,10 @@ static int pcie_init_controller(int controller, const char *path) return -1; } + /* AppleT8142PCIe::_initializeGen4Phy */ + if (state->pcie_regs->type == APCIE_T8142) + clear32(state->phy_ip_base[phy] + 0x90, BIT(16)); + if (state->pcie_regs->type == APCIE_T602X || state->pcie_regs->compat == APCIE_T8122) { set32(state->phy_base[phy] + 4, 0x10); } @@ -538,6 +568,8 @@ static int pcie_init_controller(int controller, const char *path) for (int phy = 0; phy < state->num_phys; phy++) { if (state->pcie_regs->type == APCIE_T602X) { set32(state->phy_base[phy] + APCIE_PHY_CTRL, 0x300); + } else if (state->pcie_regs->type == APCIE_T8142) { + set32(state->phy_base[phy] + APCIE_PHY_CTRL, BIT(27)); /* APCIECoreRCGen4::_apcieGPInit */ } else if (state->pcie_regs->compat == APCIE_T8122) { set32(state->phy_base[phy] + APCIE_PHY_CTRL, 0x200); } From e72da0e7bd74243fe1b17865d01a34e1a8ce4e84 Mon Sep 17 00:00:00 2001 From: Hisham Alkhudrawi Date: Thu, 29 Oct 2026 22:32:03 -0400 Subject: [PATCH 05/10] kboot: j704: raise WLAN_REG_ON (SMC gP13) before PCIe init Built only with -DJ704_WLAN_PWR. Linux has no SMC driver for t8142 yet, so m1n1 sets SMC GPIO gP13 = 0x01000001 (CMD_OUTPUT | 1, as macsmc-gpio does) before pcie_init so the BCM4388 enumerates. Adds smc_read_u32 to log the old value. Assisted-By: LLM Signed-off-by: Hisham Alkhudrawi --- src/kboot.c | 29 +++++++++++++++++++++++++++++ src/smc.c | 12 ++++++++++++ src/smc.h | 1 + 3 files changed, 42 insertions(+) diff --git a/src/kboot.c b/src/kboot.c index de5d8fc5..74f5fd15 100644 --- a/src/kboot.c +++ b/src/kboot.c @@ -18,6 +18,7 @@ #include "mcc.h" #include "memory.h" #include "pcie.h" +#include "smc.h" #include "pmgr.h" #include "sep.h" #include "sio.h" @@ -2899,6 +2900,31 @@ int kboot_prepare_dt(void *fdt) return 0; } +#ifdef J704_WLAN_PWR +/* + * j704: WLAN_REG_ON is SMC GPIO 0x13 (ADT /arm-io/amfm function-reg_on = pKW4('gP13', ...)). Linux has no + * SMC driver for t8142 yet, so raise it here, before pcie_init/Linux release PERST#. The value is what + * Asahi's macsmc-gpio writes to set an output pin (CMD_OUTPUT | 1). + */ +static void j704_wlan_power_on(void) +{ + smc_dev_t *smc = smc_init(); + if (!smc) { + printf("j704: SMC init failed, Wi-Fi stays off\n"); + return; + } + u32 old = 0; + int ret = smc_read_u32(smc, 0x67503133 /* 'gP13' */, &old); + printf("j704: gP13 = 0x%x (read: %d)\n", old, ret); + if (!ret) { + ret = smc_write_u32(smc, 0x67503133, 0x01000001); + printf("j704: gP13 <- 0x01000001 (WLAN_REG_ON): %d\n", ret); + } + smc_shutdown(smc); + mdelay(10); +} +#endif + int kboot_boot(void *kernel) { mcc_enable_cache(); @@ -2906,6 +2932,9 @@ int kboot_boot(void *kernel) clk_init(); usb_init(); +#ifdef J704_WLAN_PWR + j704_wlan_power_on(); +#endif pcie_init(); /* HACK: DAPF init raises an SError on T8132. */ if (chip_id != T8132) diff --git a/src/smc.c b/src/smc.c index 142ce670..cd50313f 100644 --- a/src/smc.c +++ b/src/smc.c @@ -170,6 +170,18 @@ smc_dev_t *smc_init(void) return NULL; } +int smc_read_u32(smc_dev_t *smc, u32 key, u32 *value) +{ + u64 msg = FIELD_PREP(SMC_MSG_TYPE, SMC_READ_KEY); + msg |= FIELD_PREP(SMC_WRITE_KEY_SIZE, sizeof(*value)); + msg |= FIELD_PREP(SMC_WRITE_KEY_KEY, key); + + int ret = smc_cmd(smc, msg); + if (!ret) // values up to 4 bytes come back in the result message + *value = FIELD_GET(SMC_RESULT_VALUE, smc->ret[(smc->msgid - 1) & 0xF]); + return ret; +} + int smc_write_u32(smc_dev_t *smc, u32 key, u32 value) { memcpy(smc->shmem, &value, sizeof(value)); diff --git a/src/smc.h b/src/smc.h index 4b95794b..ed25c4a5 100644 --- a/src/smc.h +++ b/src/smc.h @@ -9,6 +9,7 @@ typedef struct smc_dev smc_dev_t; +int smc_read_u32(smc_dev_t *smc, u32 key, u32 *value); int smc_write_u32(smc_dev_t *smc, u32 key, u32 value); smc_dev_t *smc_init(void); From b58ac9e882f2c794a6f2269dc0e58f1d9928f080 Mon Sep 17 00:00:00 2001 From: Hisham Alkhudrawi Date: Thu, 29 Oct 2026 22:32:03 -0400 Subject: [PATCH 06/10] dapf: t8142: find the DAPF reg by its ADT instance name On t8142 the DAPF reg index used on earlier SoCs lands on a page shared with PMGR, and touching it raises an SError. The reg this SoC wants is identified by the ADT instance name instead, so look it up that way and keep the index path for the SoCs that use it. Assisted-By: LLM Signed-off-by: Hisham Alkhudrawi --- src/dapf.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/dapf.c b/src/dapf.c index f5c2b055..6c7a1d04 100644 --- a/src/dapf.c +++ b/src/dapf.c @@ -6,6 +6,7 @@ #include "malloc.h" #include "memory.h" #include "pmgr.h" +#include "soc.h" #include "string.h" #include "utils.h" @@ -188,6 +189,29 @@ struct entry dapf_entries[] = { {"/arm-io/dart-ave", 3}, {NULL, -1}, }; +/* + * T8142: the DAPF reg index differs per DART (dart-mtp: reg[1]; dart-aop/pmp have none, their reg[1] is a + * shared PSWRDIS page that SErrors on write). The ADT "instance" list names each reg in order as + * {fourcc, index, name[8]}, so find the entry named DAPF; -1 if there is none. + */ +static int dapf_reg_index(const char *path) +{ + const struct { + u32 type; + u32 idx; + char name[8]; + } PACKED *inst; + u32 len; + int node = adt_path_offset(adt, path); + + if (node < 0 || !(inst = adt_getprop(adt, node, "instance", &len))) + return -1; + for (u32 i = 0; i < len / sizeof(*inst); i++) + if (!strncmp(inst[i].name, "DAPF", sizeof(inst[i].name))) + return i; + return -1; +} + int dapf_init_all(void) { int ret = 0; @@ -199,7 +223,13 @@ int dapf_init_all(void) entry++; continue; } - if (dapf_init(entry->path, entry->index) < 0) { + int index = chip_id == T8142 ? dapf_reg_index(entry->path) : entry->index; + if (index < 0) { + printf("dapf: %s has no DAPF instance, skipping\n", entry->path); + entry++; + continue; + } + if (dapf_init(entry->path, index) < 0) { ret = -1; } entry++; From 322c093a31302ad8a315ba0bb7fe43c78b651285 Mon Sep 17 00:00:00 2001 From: Hisham Alkhudrawi Date: Thu, 29 Oct 2026 22:32:03 -0400 Subject: [PATCH 07/10] kboot: j704: reserve the DCP carveouts on t8142 (dcp only) Same region ids as t8112, all mapped in dart-dcp stream 23 on the M5. No disp0/piodma nodes yet; a no-op unless the DT has a "dcp" alias. Assisted-By: LLM Signed-off-by: Hisham Alkhudrawi --- src/kboot.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/kboot.c b/src/kboot.c index 74f5fd15..135b596a 100644 --- a/src/kboot.c +++ b/src/kboot.c @@ -2027,6 +2027,16 @@ static int dt_set_display(void) ARRAY_SIZE(disp_reserved_regions_t602x)); if (ret) return ret; + } else if (!fdt_node_check_compatible(dt, 0, "apple,t8142")) { + /* + * j704: same carveout ids as t8112 (49/50/57/94/95 + vram), all mapped in dart-dcp stream 23 + * (proxy dump 2026-10-19). DCP only for now: no disp0 / piodma nodes in the DT yet. + */ + ret = dt_carveout_reserved_regions("dcp", NULL, NULL, disp_reserved_regions_t8112, + ARRAY_SIZE(disp_reserved_regions_t8112)); + if (ret) + return ret; + return dt_vram_reserved_region("dcp", NULL); } else if (!fdt_node_check_compatible(dt, 0, "apple,t6022")) { /* noop */ } else { From 4afef1462c89c4b8ecf76fbb29919957ad3f22ea Mon Sep 17 00:00:00 2001 From: Hisham Alkhudrawi Date: Thu, 29 Oct 2026 22:32:03 -0400 Subject: [PATCH 08/10] kboot: j704: split DCP mapping runs, reserve unlisted iBoot DCP mappings, skip iommu-addresses without phandle Assisted-By: LLM Signed-off-by: Hisham Alkhudrawi --- src/kboot.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 126 insertions(+), 6 deletions(-) diff --git a/src/kboot.c b/src/kboot.c index 135b596a..d06c5cc8 100644 --- a/src/kboot.c +++ b/src/kboot.c @@ -1435,6 +1435,12 @@ static int dt_device_set_reserved_mem(int node, const char *name, uint32_t phand { int ret; + // j704: phandle 0 matches no device, so Linux would map nothing and wipe a locked DART's boot tables + if (!phandle) { + printf("FDT: '%s': device has no phandle, skipping iommu-addresses\n", name); + return 0; + } + ret = fdt_appendprop_u32(dt, node, "iommu-addresses", phandle); if (ret != 0) bail("FDT: could not append phandle to '%s.iommu-addresses' property: %d\n", name, ret); @@ -1454,12 +1460,41 @@ static int dt_device_set_reserved_mem_from_dart(int node, dart_dev_t *dart, cons uint32_t phandle, u64 paddr, u64 size) { u64 iova = dart_get_mapping(dart, name, paddr, size); - if (DART_IS_ERR(iova)) { - printf("ADT: no mapping found for '%s' (0x%012lx iova:0x%08lx)\n", name, paddr, iova); - return 0; - } + if (!DART_IS_ERR(iova)) + return dt_device_set_reserved_mem(node, name, phandle, iova, size); - return dt_device_set_reserved_mem(node, name, phandle, iova, size); + /* + * j704: iBoot maps some regions in several IOVA runs (t8142 dcp_data). Describe each run, since + * Linux replaces a locked DART's tables with what iommu-addresses lists. + */ + for (u64 done = 0; done < size;) { + iova = dart_search(dart, (void *)(paddr + done)); + if (DART_IS_ERR(iova)) { + /* + * With nothing described yet, keep the historical behaviour: the region stays in + * /reserved-memory without an iommu-addresses entry and the boot continues. Once a + * run has been appended, stopping here would leave Linux a truncated list and it + * would drop the rest of a locked DART's mappings, so fail instead. + */ + if (done) + bail("FDT: '%s' is mapped only up to +0x%lx of 0x%lx, refusing to describe a " + "partial region\n", + name, done, size); + printf("ADT: no mapping found for '%s' (0x%012lx)\n", name, paddr); + return 0; + } + u64 len = SZ_16K; + while (done + len < size && + (u64)dart_translate_silent(dart, iova + len) == paddr + done + len) + len += SZ_16K; + printf("ADT: %s run 0x%lx..0x%lx at iova 0x%lx\n", name, paddr + done, paddr + done + len, + iova); + int ret = dt_device_set_reserved_mem(node, name, phandle, iova, len); + if (ret) + return ret; + done += len; + } + return 0; } static int dt_get_or_add_reserved_mem(const char *node_name, const char *compat, bool nomap, @@ -1787,6 +1822,87 @@ static int dt_vram_reserved_region(const char *dcp_alias, const char *disp_alias disp_reserved_regions_vram, ®ion, 1); } +/* + * j704: reserve the iBoot DCP mappings that no carveout in the table covers (on 26.6.2: 0x20000 at + * 0x103df7f0000, the DCP crashlog/coredump area just below dcp_data, iova 0x8cd4000). Linux replaces the + * locked dart-dcp L1 with its own table, so anything not in iommu-addresses vanishes under the running DCP. + */ +static int dt_reserve_dcp_unlisted(const char *dcp_alias, struct disp_mapping *maps, u32 num_maps) +{ + struct mem_region known[MAX_DISP_MAPPINGS + 1]; + u32 num_known = 0; + int adt_path[4]; + + int node = adt_path_offset(adt, "/chosen/carveout-memory-map"); + if (node < 0) + return 0; + for (u32 i = 0; i < num_maps; i++) { + u64 phys_map[2]; + if (ADT_GETPROP_ARRAY(adt, node, maps[i].region_adt, phys_map) == sizeof(phys_map)) { + known[num_known].paddr = phys_map[0]; + known[num_known++].size = phys_map[1]; + } + } + node = adt_path_offset_trace(adt, "/vram", adt_path); + if (node >= 0) { + int pp = 0; + while (adt_path[pp]) + pp++; + adt_path[pp + 1] = 0; + if (adt_get_reg(adt, adt_path, "reg", 0, &known[num_known].paddr, + &known[num_known].size) >= 0) + num_known++; + } + + int dcp_node = fdt_path_offset(dt, dcp_alias); + if (dcp_node < 0) + return 0; + uint32_t dcp_phandle = fdt_get_phandle(dt, dcp_node); + dart_dev_t *dart = dt_init_dart_by_node(dcp_node, 0); + if (!dart) + bail("FDT: failed to init DART for '%s'\n", dcp_alias); + + int ret = 0; + // iBoot maps the DCP below 0x9000000 on 26.6.2; scan 256 MiB, widen if that grows + for (u64 iova = 0; iova < 0x10000000;) { + u64 pa = (u64)dart_translate_silent(dart, iova); + if (!pa) { + iova += SZ_16K; + continue; + } + u64 len = SZ_16K; + while ((u64)dart_translate_silent(dart, iova + len) == pa + len) + len += SZ_16K; + + bool covered = false; + for (u32 i = 0; i < num_known; i++) + if (pa >= known[i].paddr && pa + len <= known[i].paddr + known[i].size) + covered = true; + + if (!covered) { + char name[64]; + snprintf(name, sizeof(name), "dcp-iboot@%lx", pa); + int mem_node = dt_get_or_add_reserved_mem(name, "apple,asc-mem", true, pa, len); + if (mem_node < 0) { + ret = -1; + break; + } + uint32_t mem_phandle = fdt_get_phandle(dt, mem_node); + ret = dt_device_set_reserved_mem(mem_node, name, dcp_phandle, iova, len); + if (!ret) + ret = dt_device_add_mem_region(dcp_alias, mem_phandle, "dcp-iboot"); + if (ret < 0) + break; + printf("FDT: %s: kept unlisted iBoot mapping 0x%lx (0x%lx) at iova 0x%lx\n", dcp_alias, + pa, len, iova); + } + iova += len; + } + + dart_shutdown(dart); + return ret; +} + static int dt_reserve_asc_firmware(const char *adt_path, const char *adt_path_alt, const char *fdt_path, bool remap, u64 base) { @@ -2036,7 +2152,11 @@ static int dt_set_display(void) ARRAY_SIZE(disp_reserved_regions_t8112)); if (ret) return ret; - return dt_vram_reserved_region("dcp", NULL); + ret = dt_vram_reserved_region("dcp", NULL); + if (ret || !fdt_get_alias(dt, "dcp")) + return ret; + return dt_reserve_dcp_unlisted("dcp", disp_reserved_regions_t8112, + ARRAY_SIZE(disp_reserved_regions_t8112)); } else if (!fdt_node_check_compatible(dt, 0, "apple,t6022")) { /* noop */ } else { From 1e5b7f078126ee8b284f153b2c3ef91b84a7fcca Mon Sep 17 00:00:00 2001 From: Hisham Alkhudrawi Date: Thu, 29 Oct 2026 22:32:03 -0400 Subject: [PATCH 09/10] kboot_atc: copy the atc-phy tunables on t8142 t8142's atc-phy nodes carry the same tunable layout as t8122/t8132, so list the compatible in the fuse table (reverse-chronological, ahead of t8132) and select the t8122 tunable set for it. Assisted-By: LLM Signed-off-by: Hisham Alkhudrawi --- src/kboot_atc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/kboot_atc.c b/src/kboot_atc.c index 3cc2165a..b27b8eb8 100644 --- a/src/kboot_atc.c +++ b/src/kboot_atc.c @@ -162,6 +162,7 @@ static const struct atc_fuse_info atc_fuses_t8112_port1[] = { // Order "atc-phy" compatibles in reverse chronologically order to deal with mutliple compatible // strings in ADT atc-phy nodes. static const struct atc_fuse_hw atc_fuses[] = { + {"atc-phy,t8142", -1, NULL, 0}, {"atc-phy,t8132", -1, NULL, 0}, {"atc-phy,t8122", -1, NULL, 0}, {"atc-phy,t6020", -1, NULL, 0}, @@ -301,7 +302,8 @@ static void dt_copy_atc_tunables(void *dt, const char *adt_path, const char *dt_ } if (adt_is_compatible_at(adt, adt_node, "atc-phy,t8132", 0) || - adt_is_compatible_at(adt, adt_node, "atc-phy,t8122", 0)) { + adt_is_compatible_at(adt, adt_node, "atc-phy,t8122", 0) || + adt_is_compatible_at(adt, adt_node, "atc-phy,t8142", 0)) { tunables = &atc_tunables_t8122[0]; tunable_count = atc_tunables_t8122_count; } else { From 571ee4fe691ef5a124add2534079385605e48808 Mon Sep 17 00:00:00 2001 From: Hisham Alkhudrawi Date: Thu, 29 Oct 2026 22:32:03 -0400 Subject: [PATCH 10/10] smp: log once when a call target hasn't taken a call after 1 s A secondary that never takes a cross-CPU call leaves smp_call4() spinning with no output at all, which looks exactly like a dead board. Print the CPU, the call and the target state once after a second, flush the console, and then keep waiting as before, so the behaviour is unchanged apart from the message. This was written to chase a first-secondary wedge on J704 (M5) that shows up roughly one boot in three. Assisted-By: LLM Signed-off-by: Hisham Alkhudrawi --- src/smp.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/smp.c b/src/smp.c index 15fe4e29..2f4c93cc 100644 --- a/src/smp.c +++ b/src/smp.c @@ -5,6 +5,7 @@ #include "aic.h" #include "aic_regs.h" #include "cpu_regs.h" +#include "iodev.h" #include "malloc.h" #include "memory.h" #include "pmgr.h" @@ -470,8 +471,19 @@ void smp_call4(int cpu, void *func, u64 arg0, u64 arg1, u64 arg2, u64 arg3) else smp_send_ipi(cpu); - while (target->flag == flag) + // Debug instrument for the J704 first-secondary wedge (a target that never takes the call + // hangs the caller here silently); say so once after 1 s, then keep waiting exactly as before. + u64 start = get_ticks(); + bool reported = false; + while (target->flag == flag) { sysop("dmb sy"); + if (!reported && ticks_to_msecs(get_ticks() - start) >= 1000) { + reported = true; + printf("smp: CPU %d has not taken call %p after 1 s (flag %ld, target %lx, mpidr %lx)\n", cpu, + func, target->flag, target->target, target->mpidr); + iodev_console_flush(); + } + } } u64 smp_wait(int cpu)