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};") 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/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++; diff --git a/src/kboot.c b/src/kboot.c index eec8fa20..d06c5cc8 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" @@ -388,6 +389,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); @@ -584,8 +605,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 +633,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++; @@ -1410,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); @@ -1429,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, @@ -1762,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) { @@ -2002,6 +2143,20 @@ 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; + 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 { @@ -2875,6 +3030,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(); @@ -2882,6 +3062,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/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 { 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); } 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); 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)