summaryrefslogtreecommitdiff
path: root/tools/src/kboot.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/src/kboot.c')
-rw-r--r--tools/src/kboot.c1937
1 files changed, 1937 insertions, 0 deletions
diff --git a/tools/src/kboot.c b/tools/src/kboot.c
new file mode 100644
index 0000000..c56c0e7
--- /dev/null
+++ b/tools/src/kboot.c
@@ -0,0 +1,1937 @@
+/* SPDX-License-Identifier: MIT */
+
+#include <stdint.h>
+
+#include "kboot.h"
+#include "adt.h"
+#include "assert.h"
+#include "dapf.h"
+#include "devicetree.h"
+#include "exception.h"
+#include "firmware.h"
+#include "malloc.h"
+#include "memory.h"
+#include "pcie.h"
+#include "pmgr.h"
+#include "sep.h"
+#include "smp.h"
+#include "types.h"
+#include "usb.h"
+#include "utils.h"
+#include "xnuboot.h"
+
+#include "libfdt/libfdt.h"
+
+#define MAX_CHOSEN_PARAMS 16
+
+#define MAX_ATC_DEVS 8
+#define MAX_CIO_DEVS 8
+
+#define MAX_DISP_MAPPINGS 8
+
+static void *dt = NULL;
+static int dt_bufsize = 0;
+static void *initrd_start = NULL;
+static size_t initrd_size = 0;
+static char *chosen_params[MAX_CHOSEN_PARAMS][2];
+
+extern const char *const m1n1_version;
+
+int dt_set_gpu(void *dt);
+
+#define DT_ALIGN 16384
+
+#define bail(...) \
+ do { \
+ printf(__VA_ARGS__); \
+ return -1; \
+ } while (0)
+
+#define bail_cleanup(...) \
+ do { \
+ printf(__VA_ARGS__); \
+ ret = -1; \
+ goto err; \
+ } while (0)
+
+void get_notchless_fb(u64 *fb_base, u64 *fb_height)
+{
+ *fb_base = cur_boot_args.video.base;
+ *fb_height = cur_boot_args.video.height;
+
+ int node = adt_path_offset(adt, "/product");
+
+ if (node < 0) {
+ printf("FDT: /product node not found\n");
+ return;
+ }
+
+ u32 val;
+
+ if (ADT_GETPROP(adt, node, "partially-occluded-display", &val) < 0 || !val) {
+ printf("FDT: No notch detected\n");
+ return;
+ }
+
+ u64 hfrac = cur_boot_args.video.height * 16 / cur_boot_args.video.width;
+ u64 new_height = cur_boot_args.video.width * hfrac / 16;
+
+ if (new_height == cur_boot_args.video.height) {
+ printf("FDT: Notch detected, but display aspect is already 16:%lu?\n", hfrac);
+ return;
+ }
+
+ u64 offset = cur_boot_args.video.height - new_height;
+
+ printf("display: Hiding notch, %lux%lu -> %lux%lu (+%lu, 16:%lu)\n", cur_boot_args.video.width,
+ cur_boot_args.video.height, cur_boot_args.video.width, new_height, offset, hfrac);
+
+ *fb_base += cur_boot_args.video.stride * offset;
+ *fb_height = new_height;
+}
+
+static int dt_set_rng_seed_sep(int node)
+{
+ u64 kaslr_seed;
+ uint8_t rng_seed[128]; // same size used by Linux for kexec
+
+ if (sep_get_random(&kaslr_seed, sizeof(kaslr_seed)) != sizeof(kaslr_seed))
+ bail("SEP: couldn't get enough random bytes for KASLR seed");
+ if (sep_get_random(rng_seed, sizeof(rng_seed)) != sizeof(rng_seed))
+ bail("SEP: couldn't get enough random bytes for RNG seed");
+
+ if (fdt_setprop_u64(dt, node, "kaslr-seed", kaslr_seed))
+ bail("FDT: couldn't set kaslr-seed\n");
+ if (fdt_setprop(dt, node, "rng-seed", rng_seed, sizeof(rng_seed)))
+ bail("FDT: couldn't set rng-seed\n");
+
+ printf("FDT: Passing %ld bytes of KASLR seed and %ld bytes of random seed\n",
+ sizeof(kaslr_seed), sizeof(rng_seed));
+
+ return 0;
+}
+
+static int dt_set_rng_seed_adt(int node)
+{
+ int anode = adt_path_offset(adt, "/chosen");
+
+ if (anode < 0)
+ bail("ADT: /chosen not found\n");
+
+ const uint8_t *random_seed;
+ u32 seed_length;
+
+ random_seed = adt_getprop(adt, anode, "random-seed", &seed_length);
+ if (random_seed) {
+ printf("ADT: %d bytes of random seed available\n", seed_length);
+
+ if (seed_length >= sizeof(u64)) {
+ u64 kaslr_seed;
+
+ memcpy(&kaslr_seed, random_seed, sizeof(kaslr_seed));
+
+ // Ideally we would throw away the kaslr_seed part of random_seed
+ // and avoid reusing it. However, Linux wants 64 bytes of bootloader
+ // random seed to consider its CRNG initialized, which is exactly
+ // how much iBoot gives us. This probably doesn't matter, since
+ // that entropy is going to get shuffled together and Linux makes
+ // sure to clear the FDT randomness after using it anyway, but just
+ // in case let's mix in a few bits from our own KASLR base to make
+ // kaslr_seed unique.
+
+ kaslr_seed ^= (u64)cur_boot_args.virt_base;
+
+ if (fdt_setprop_u64(dt, node, "kaslr-seed", kaslr_seed))
+ bail("FDT: couldn't set kaslr-seed\n");
+
+ printf("FDT: KASLR seed initialized\n");
+ } else {
+ printf("ADT: not enough random data for kaslr-seed\n");
+ }
+
+ if (seed_length) {
+ if (fdt_setprop(dt, node, "rng-seed", random_seed, seed_length))
+ bail("FDT: couldn't set rng-seed\n");
+
+ printf("FDT: Passing %d bytes of random seed\n", seed_length);
+ }
+ } else {
+ printf("ADT: no random-seed available!\n");
+ }
+
+ return 0;
+}
+
+static int dt_set_chosen(void)
+{
+
+ int node = fdt_path_offset(dt, "/chosen");
+ if (node < 0)
+ bail("FDT: /chosen node not found in devtree\n");
+
+ for (int i = 0; i < MAX_CHOSEN_PARAMS; i++) {
+ if (!chosen_params[i][0])
+ break;
+
+ const char *name = chosen_params[i][0];
+ const char *value = chosen_params[i][1];
+ if (fdt_setprop(dt, node, name, value, strlen(value) + 1) < 0)
+ bail("FDT: couldn't set chosen.%s property\n", name);
+ printf("FDT: %s = '%s'\n", name, value);
+ }
+
+ if (initrd_start && initrd_size) {
+ if (fdt_setprop_u64(dt, node, "linux,initrd-start", (u64)initrd_start))
+ bail("FDT: couldn't set chosen.linux,initrd-start property\n");
+
+ u64 end = ((u64)initrd_start) + initrd_size;
+ if (fdt_setprop_u64(dt, node, "linux,initrd-end", end))
+ bail("FDT: couldn't set chosen.linux,initrd-end property\n");
+
+ if (fdt_add_mem_rsv(dt, (u64)initrd_start, initrd_size))
+ bail("FDT: couldn't add reservation for the initrd\n");
+
+ printf("FDT: initrd at %p size 0x%lx\n", initrd_start, initrd_size);
+ }
+
+ if (cur_boot_args.video.base) {
+ int fb = fdt_path_offset(dt, "/chosen/framebuffer");
+ if (fb < 0)
+ bail("FDT: /chosen node not found in devtree\n");
+
+ u64 fb_base, fb_height;
+ get_notchless_fb(&fb_base, &fb_height);
+ u64 fb_size = cur_boot_args.video.stride * fb_height;
+ u64 fbreg[2] = {cpu_to_fdt64(fb_base), cpu_to_fdt64(fb_size)};
+ char fbname[32];
+
+ snprintf(fbname, sizeof(fbname), "framebuffer@%lx", fb_base);
+
+ if (fdt_setprop(dt, fb, "reg", fbreg, sizeof(fbreg)))
+ bail("FDT: couldn't set framebuffer.reg property\n");
+
+ if (fdt_set_name(dt, fb, fbname))
+ bail("FDT: couldn't set framebuffer name\n");
+
+ if (fdt_setprop_u32(dt, fb, "width", cur_boot_args.video.width))
+ bail("FDT: couldn't set framebuffer width\n");
+
+ if (fdt_setprop_u32(dt, fb, "height", fb_height))
+ bail("FDT: couldn't set framebuffer height\n");
+
+ if (fdt_setprop_u32(dt, fb, "stride", cur_boot_args.video.stride))
+ bail("FDT: couldn't set framebuffer stride\n");
+
+ const char *format = NULL;
+
+ switch (cur_boot_args.video.depth & 0xff) {
+ case 32:
+ format = "x8r8g8b8";
+ break;
+ case 30:
+ format = "x2r10g10b10";
+ break;
+ case 16:
+ format = "r5g6b5";
+ break;
+ default:
+ printf("FDT: unsupported fb depth %lu, not enabling\n", cur_boot_args.video.depth);
+ return 0; // Do not error out, but don't set the FB
+ }
+
+ if (fdt_setprop_string(dt, fb, "format", format))
+ bail("FDT: couldn't set framebuffer format\n");
+
+ fdt_delprop(dt, fb, "status"); // may fail if it does not exist
+
+ printf("FDT: %s base 0x%lx size 0x%lx\n", fbname, fb_base, fb_size);
+
+ // We do not need to reserve the framebuffer, as it will be excluded from the usable RAM
+ // range already.
+
+ // save notch height in the dcp node if present
+ if (cur_boot_args.video.height - fb_height) {
+ int dcp = fdt_path_offset(dt, "dcp");
+ if (dcp >= 0)
+ if (fdt_appendprop_u32(dt, dcp, "apple,notch-height",
+ cur_boot_args.video.height - fb_height))
+ printf("FDT: couldn't set apple,notch-height\n");
+ }
+ }
+ node = fdt_path_offset(dt, "/chosen");
+ if (node < 0)
+ bail("FDT: /chosen node not found in devtree\n");
+
+ int ipd = adt_path_offset(adt, "/arm-io/spi3/ipd");
+ if (ipd < 0)
+ ipd = adt_path_offset(adt, "/arm-io/dockchannel-mtp/mtp-transport/keyboard");
+
+ if (ipd < 0) {
+ printf("ADT: no keyboard found\n");
+ } else {
+ u32 len;
+ const u8 *kblang = adt_getprop(adt, ipd, "kblang-calibration", &len);
+ if (kblang && len >= 2) {
+ if (fdt_setprop_u32(dt, node, "asahi,kblang-code", kblang[1]))
+ bail("FDT: couldn't set asahi,kblang-code");
+ } else {
+ printf("ADT: kblang-calibration not found, no keyboard layout\n");
+ }
+ }
+
+ if (fdt_setprop(dt, node, "asahi,iboot1-version", system_firmware.iboot,
+ strlen(system_firmware.iboot) + 1))
+ bail("FDT: couldn't set asahi,iboot1-version");
+
+ if (fdt_setprop(dt, node, "asahi,system-fw-version", system_firmware.string,
+ strlen(system_firmware.string) + 1))
+ bail("FDT: couldn't set asahi,system-fw-version");
+
+ if (fdt_setprop(dt, node, "asahi,iboot2-version", os_firmware.iboot,
+ strlen(os_firmware.iboot) + 1))
+ bail("FDT: couldn't set asahi,iboot2-version");
+
+ if (fdt_setprop(dt, node, "asahi,os-fw-version", os_firmware.string,
+ strlen(os_firmware.string) + 1))
+ bail("FDT: couldn't set asahi,os-fw-version");
+
+ if (fdt_setprop(dt, node, "asahi,m1n1-stage2-version", m1n1_version, strlen(m1n1_version) + 1))
+ bail("FDT: couldn't set asahi,m1n1-stage2-version");
+
+ if (dt_set_rng_seed_sep(node))
+ return dt_set_rng_seed_adt(node);
+
+ return 0;
+}
+
+static int dt_set_memory(void)
+{
+ int anode = adt_path_offset(adt, "/chosen");
+
+ if (anode < 0)
+ bail("ADT: /chosen not found\n");
+
+ u64 dram_base, dram_size;
+
+ if (ADT_GETPROP(adt, anode, "dram-base", &dram_base) < 0)
+ bail("ADT: Failed to get dram-base\n");
+ if (ADT_GETPROP(adt, anode, "dram-size", &dram_size) < 0)
+ bail("ADT: Failed to get dram-size\n");
+
+ // Tell the kernel our usable memory range. We cannot declare all of DRAM, and just reserve the
+ // bottom and top, because the kernel would still map it (and just not use it), which breaks
+ // ioremap (e.g. simplefb).
+
+ u64 dram_min = cur_boot_args.phys_base;
+ u64 dram_max = cur_boot_args.phys_base + cur_boot_args.mem_size;
+
+ 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);
+
+ u64 memreg[2] = {cpu_to_fdt64(dram_min), cpu_to_fdt64(dram_max - dram_min)};
+
+ int node = fdt_path_offset(dt, "/memory");
+ if (node < 0)
+ bail("FDT: /memory node not found in devtree\n");
+
+ if (fdt_setprop(dt, node, "reg", memreg, sizeof(memreg)))
+ bail("FDT: couldn't set memory.reg property\n");
+
+ return 0;
+}
+
+static int dt_set_serial_number(void)
+{
+
+ int fdt_root = fdt_path_offset(dt, "/");
+ int adt_root = adt_path_offset(adt, "/");
+
+ if (fdt_root < 0)
+ bail("FDT: could not open a handle to FDT root.\n");
+ if (adt_root < 0)
+ bail("ADT: could not open a handle to ADT root.\n");
+
+ u32 sn_len;
+ const char *serial_number = adt_getprop(adt, adt_root, "serial-number", &sn_len);
+ if (fdt_setprop_string(dt, fdt_root, "serial-number", serial_number))
+ bail("FDT: unable to set device serial number!\n");
+ printf("FDT: reporting device serial number: %s\n", serial_number);
+
+ return 0;
+}
+
+static int dt_set_cpus(void)
+{
+ int ret = 0;
+
+ int cpus = fdt_path_offset(dt, "/cpus");
+ if (cpus < 0)
+ bail("FDT: /cpus node not found in devtree\n");
+
+ uint32_t *pruned_phandles = malloc(MAX_CPUS * sizeof(uint32_t));
+ size_t pruned = 0;
+ if (!pruned_phandles)
+ bail("FDT: out of memory\n");
+
+ /* Prune CPU nodes */
+ int node, cpu = 0;
+ for (node = fdt_first_subnode(dt, cpus); node >= 0;) {
+ const char *name = fdt_get_name(dt, node, NULL);
+ if (strncmp(name, "cpu@", 4))
+ goto next_node;
+
+ if (cpu > MAX_CPUS)
+ bail_cleanup("Maximum number of CPUs exceeded, consider increasing MAX_CPUS\n");
+
+ const fdt64_t *prop = fdt_getprop(dt, node, "reg", NULL);
+ if (!prop)
+ bail_cleanup("FDT: failed to get reg property of CPU\n");
+
+ u64 dt_mpidr = fdt64_ld(prop);
+
+ 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);
+ pruned_phandles[pruned++] = fdt_get_phandle(dt, node);
+
+ int next = fdt_next_subnode(dt, node);
+ fdt_nop_node(dt, node);
+ cpu++;
+ node = next;
+ continue;
+ }
+
+ 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);
+ 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);
+
+ next_cpu:
+ cpu++;
+ next_node:
+ node = fdt_next_subnode(dt, node);
+ }
+
+ if ((node < 0) && (node != -FDT_ERR_NOTFOUND)) {
+ bail_cleanup("FDT: error iterating through CPUs\n");
+ }
+
+ /* Prune AIC PMU affinities */
+ int aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic");
+ if (aic == -FDT_ERR_NOTFOUND)
+ aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic2");
+ if (aic < 0)
+ bail_cleanup("FDT: Failed to find AIC node\n");
+
+ int affinities = fdt_subnode_offset(dt, aic, "affinities");
+ if (affinities < 0) {
+ printf("FDT: Failed to find AIC affinities node, ignoring...\n");
+ } else {
+ int node;
+ for (node = fdt_first_subnode(dt, affinities); node >= 0;
+ node = fdt_next_subnode(dt, node)) {
+ int len;
+ const fdt32_t *phs = fdt_getprop(dt, node, "cpus", &len);
+ if (!phs)
+ bail_cleanup("FDT: Failed to find cpus property under AIC affinity\n");
+
+ fdt32_t *new_phs = malloc(len);
+ size_t index = 0;
+ size_t count = len / sizeof(fdt32_t);
+
+ for (size_t i = 0; i < count; i++) {
+ uint32_t phandle = fdt32_ld(&phs[i]);
+ bool prune = false;
+
+ for (size_t j = 0; j < pruned; j++) {
+ if (pruned_phandles[j] == phandle) {
+ prune = true;
+ break;
+ }
+ }
+ if (!prune)
+ new_phs[index++] = phs[i];
+ }
+
+ ret = fdt_setprop(dt, node, "cpus", new_phs, sizeof(fdt32_t) * index);
+ free(new_phs);
+
+ if (ret < 0)
+ bail_cleanup("FDT: Failed to set cpus property under AIC affinity\n");
+
+ const char *name = fdt_get_name(dt, node, NULL);
+ printf("FDT: Pruned %ld/%ld CPU references in [AIC]/affinities/%s\n", count - index,
+ count, name);
+ }
+
+ if ((node < 0) && (node != -FDT_ERR_NOTFOUND))
+ bail_cleanup("FDT: Error iterating through affinity nodes\n");
+ }
+
+ /* Prune CPU-map */
+ int cpu_map = fdt_path_offset(dt, "/cpus/cpu-map");
+ if (cpu_map < 0) {
+ printf("FDT: /cpus/cpu-map node not found in devtree, ignoring...\n");
+ free(pruned_phandles);
+ return 0;
+ }
+
+ int cluster_idx = 0;
+ int cluster_node;
+ for (cluster_node = fdt_first_subnode(dt, cpu_map); cluster_node >= 0;) {
+ const char *name = fdt_get_name(dt, cluster_node, NULL);
+ int cpu_idx = 0;
+
+ if (strncmp(name, "cluster", 7))
+ goto next_cluster;
+
+ int cpu_node;
+ for (cpu_node = fdt_first_subnode(dt, cluster_node); cpu_node >= 0;) {
+ const char *cpu_name = fdt_get_name(dt, cpu_node, NULL);
+
+ if (strncmp(cpu_name, "core", 4))
+ goto next_map_cpu;
+
+ int len;
+ const fdt32_t *cpu_ph = fdt_getprop(dt, cpu_node, "cpu", &len);
+
+ if (!cpu_ph || len != sizeof(*cpu_ph))
+ bail_cleanup("FDT: Failed to get cpu prop for /cpus/cpu-map/%s/%s\n", name,
+ cpu_name);
+
+ uint32_t phandle = fdt32_ld(cpu_ph);
+ bool prune = false;
+ for (size_t i = 0; i < pruned; i++) {
+ if (pruned_phandles[i] == phandle) {
+ prune = true;
+ break;
+ }
+ }
+
+ if (prune) {
+ printf("FDT: Pruning /cpus/cpu-map/%s/%s\n", name, cpu_name);
+
+ int next = fdt_next_subnode(dt, cpu_node);
+ fdt_nop_node(dt, cpu_node);
+ cpu_node = next;
+ continue;
+ } else {
+ char new_name[16];
+
+ snprintf(new_name, 16, "core%d", cpu_idx++);
+ fdt_set_name(dt, cpu_node, new_name);
+ }
+ next_map_cpu:
+ cpu_node = fdt_next_subnode(dt, cpu_node);
+ }
+
+ if ((cpu_node < 0) && (cpu_node != -FDT_ERR_NOTFOUND))
+ bail_cleanup("FDT: Error iterating through CPU nodes\n");
+
+ if (cpu_idx == 0) {
+ printf("FDT: Pruning /cpus/cpu-map/%s\n", name);
+
+ int next = fdt_next_subnode(dt, cluster_node);
+ fdt_nop_node(dt, cluster_node);
+ cluster_node = next;
+ continue;
+ } else {
+ char new_name[16];
+
+ snprintf(new_name, 16, "cluster%d", cluster_idx++);
+ fdt_set_name(dt, cluster_node, new_name);
+ }
+ next_cluster:
+ cluster_node = fdt_next_subnode(dt, cluster_node);
+ }
+
+ if ((cluster_node < 0) && (cluster_node != -FDT_ERR_NOTFOUND))
+ bail_cleanup("FDT: Error iterating through CPU clusters\n");
+
+ return 0;
+
+err:
+ free(pruned_phandles);
+ return ret;
+}
+
+static struct {
+ const char *alias;
+ const char *fdt_property;
+ bool swap;
+} mac_address_devices[] = {
+ {
+ .alias = "bluetooth0",
+ .fdt_property = "local-bd-address",
+ .swap = true,
+ },
+ {
+ .alias = "ethernet0",
+ .fdt_property = "local-mac-address",
+ },
+ {
+ .alias = "wifi0",
+ .fdt_property = "local-mac-address",
+ },
+};
+
+static int dt_set_mac_addresses(void)
+{
+ int anode = adt_path_offset(adt, "/chosen");
+
+ if (anode < 0)
+ bail("ADT: /chosen not found\n");
+
+ for (size_t i = 0; i < sizeof(mac_address_devices) / sizeof(*mac_address_devices); i++) {
+ char propname[32];
+ snprintf(propname, sizeof(propname), "mac-address-%s", mac_address_devices[i].alias);
+
+ uint8_t addr[6];
+ if (ADT_GETPROP_ARRAY(adt, anode, propname, addr) < 0)
+ continue;
+
+ if (mac_address_devices[i].swap) {
+ for (size_t i = 0; i < sizeof(addr) / 2; ++i) {
+ uint8_t tmp = addr[i];
+ addr[i] = addr[sizeof(addr) - i - 1];
+ addr[sizeof(addr) - i - 1] = tmp;
+ }
+ }
+
+ const char *path = fdt_get_alias(dt, mac_address_devices[i].alias);
+ if (path == NULL)
+ continue;
+
+ int node = fdt_path_offset(dt, path);
+ if (node < 0)
+ continue;
+
+ fdt_setprop(dt, node, mac_address_devices[i].fdt_property, addr, sizeof(addr));
+ }
+
+ return 0;
+}
+
+static int dt_set_bluetooth_cal(int anode, int node, const char *adt_name, const char *fdt_name)
+{
+ u32 len;
+ const u8 *cal_blob = adt_getprop(adt, anode, adt_name, &len);
+
+ if (!cal_blob || !len)
+ bail("ADT: Failed to get %s", adt_name);
+
+ fdt_setprop(dt, node, fdt_name, cal_blob, len);
+ return 0;
+}
+
+static int dt_set_bluetooth(void)
+{
+ int ret;
+ int anode = adt_path_offset(adt, "/arm-io/bluetooth");
+
+ if (anode < 0)
+ bail("ADT: /arm-io/bluetooth not found\n");
+
+ const char *path = fdt_get_alias(dt, "bluetooth0");
+ if (path == NULL)
+ return 0;
+
+ int node = fdt_path_offset(dt, path);
+ if (node < 0)
+ return 0;
+
+ ret = dt_set_bluetooth_cal(anode, node, "bluetooth-taurus-calibration-bf",
+ "brcm,taurus-bf-cal-blob");
+ if (ret)
+ return ret;
+
+ ret = dt_set_bluetooth_cal(anode, node, "bluetooth-taurus-calibration", "brcm,taurus-cal-blob");
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int dt_set_multitouch(void)
+{
+ const char *path = fdt_get_alias(dt, "touchbar0");
+ if (path == NULL)
+ return 0;
+
+ int node = fdt_path_offset(dt, path);
+ if (node < 0)
+ bail("FDT: alias points at nonexistent node");
+
+ int anode = adt_path_offset(adt, "/arm-io/spi0/multi-touch");
+ if (anode < 0)
+ bail("ADT /arm-io/spi0/multi-touch not found\n");
+
+ u32 len;
+ const u8 *cal_blob = adt_getprop(adt, anode, "multi-touch-calibration", &len);
+ if (!cal_blob || !len)
+ bail("ADT: Failed to get multi-touch-calibration");
+
+ fdt_setprop(dt, node, "apple,z2-cal-blob", cal_blob, len);
+ return 0;
+}
+
+static int dt_set_wifi(void)
+{
+ int anode = adt_path_offset(adt, "/arm-io/wlan");
+
+ if (anode < 0)
+ bail("ADT: /arm-io/wlan not found\n");
+
+ uint8_t info[16];
+ if (ADT_GETPROP_ARRAY(adt, anode, "wifi-antenna-sku-info", info) < 0)
+ bail("ADT: Failed to get wifi-antenna-sku-info");
+
+ const char *path = fdt_get_alias(dt, "wifi0");
+ if (path == NULL)
+ return 0;
+
+ int node = fdt_path_offset(dt, path);
+ if (node < 0)
+ return 0;
+
+ char antenna[8];
+ memcpy(antenna, &info[8], sizeof(antenna));
+ fdt_setprop_string(dt, node, "apple,antenna-sku", antenna);
+
+ u32 len;
+ const u8 *cal_blob = adt_getprop(adt, anode, "wifi-calibration-msf", &len);
+
+ if (!cal_blob || !len)
+ bail("ADT: Failed to get wifi-calibration-msf");
+
+ fdt_setprop(dt, node, "brcm,cal-blob", cal_blob, len);
+
+ return 0;
+}
+
+static void dt_set_uboot_dm_preloc(int node)
+{
+ // Tell U-Boot to bind this node early
+ fdt_setprop_empty(dt, node, "u-boot,dm-pre-reloc");
+ fdt_setprop_empty(dt, node, "bootph-all");
+
+ // Make sure the power domains are bound early as well
+ int pds_size;
+ const fdt32_t *pds = fdt_getprop(dt, node, "power-domains", &pds_size);
+ if (!pds)
+ return;
+
+ fdt32_t *phandles = malloc(pds_size);
+ if (!phandles) {
+ printf("FDT: out of memory\n");
+ return;
+ }
+ memcpy(phandles, pds, pds_size);
+
+ for (int i = 0; i < pds_size / 4; i++) {
+ node = fdt_node_offset_by_phandle(dt, fdt32_ld(&phandles[i]));
+ if (node < 0)
+ continue;
+ dt_set_uboot_dm_preloc(node);
+
+ // restore node offset after DT update
+ node = fdt_node_offset_by_phandle(dt, fdt32_ld(&phandles[i]));
+ if (node < 0)
+ continue;
+
+ // And make sure the PMGR node is bound early too
+ node = fdt_parent_offset(dt, node);
+ if (node < 0)
+ continue;
+ dt_set_uboot_dm_preloc(node);
+ }
+
+ free(phandles);
+}
+
+static int dt_set_uboot(void)
+{
+ // Make sure that U-Boot can initialize the serial port in its
+ // pre-relocation phase by marking its node and the nodes of the
+ // power domains it depends on with a "u-boot,dm-pre-reloc"
+ // property.
+
+ const char *path = fdt_get_alias(dt, "serial0");
+ if (path == NULL)
+ return 0;
+
+ int node = fdt_path_offset(dt, path);
+ if (node < 0)
+ return 0;
+
+ dt_set_uboot_dm_preloc(node);
+ return 0;
+}
+
+struct atc_tunable {
+ u32 offset : 24;
+ u32 size : 8;
+ u32 mask;
+ u32 value;
+} PACKED;
+static_assert(sizeof(struct atc_tunable) == 12, "Invalid atc_tunable size");
+
+struct adt_tunable_info {
+ const char *adt_name;
+ const char *fdt_name;
+ size_t reg_offset;
+ size_t reg_size;
+ bool required;
+};
+
+static const struct adt_tunable_info atc_tunables[] = {
+ /* global tunables applied after power on or reset */
+ {"tunable_ATC0AXI2AF", "apple,tunable-axi2af", 0x0, 0x4000, true},
+ {"tunable_ATC_FABRIC", "apple,tunable-common", 0x45000, 0x4000, true},
+ {"tunable_AUS_CMN_TOP", "apple,tunable-common", 0x800, 0x4000, true},
+ {"tunable_AUS_CMN_SHM", "apple,tunable-common", 0xa00, 0x4000, true},
+ {"tunable_AUSPLL_CORE", "apple,tunable-common", 0x2200, 0x4000, true},
+ {"tunable_AUSPLL_TOP", "apple,tunable-common", 0x2000, 0x4000, true},
+ {"tunable_CIO3PLL_CORE", "apple,tunable-common", 0x2a00, 0x4000, true},
+ {"tunable_CIO3PLL_TOP", "apple,tunable-common", 0x2800, 0x4000, true},
+ {"tunable_CIO_CIO3PLL_TOP", "apple,tunable-common", 0x2800, 0x4000, false},
+ {"tunable_USB_ACIOPHY_TOP", "apple,tunable-common", 0x0, 0x4000, true},
+ /* lane-specific tunables applied after a cable is connected */
+ {"tunable_DP_LN0_AUSPMA_TX_TOP", "apple,tunable-lane0-dp", 0xc000, 0x1000, true},
+ {"tunable_DP_LN1_AUSPMA_TX_TOP", "apple,tunable-lane1-dp", 0x13000, 0x1000, true},
+ {"tunable_USB_LN0_AUSPMA_RX_TOP", "apple,tunable-lane0-usb", 0x9000, 0x1000, true},
+ {"tunable_USB_LN0_AUSPMA_RX_EQ", "apple,tunable-lane0-usb", 0xa000, 0x1000, true},
+ {"tunable_USB_LN0_AUSPMA_RX_SHM", "apple,tunable-lane0-usb", 0xb000, 0x1000, true},
+ {"tunable_USB_LN0_AUSPMA_TX_TOP", "apple,tunable-lane0-usb", 0xc000, 0x1000, true},
+ {"tunable_USB_LN1_AUSPMA_RX_TOP", "apple,tunable-lane1-usb", 0x10000, 0x1000, true},
+ {"tunable_USB_LN1_AUSPMA_RX_EQ", "apple,tunable-lane1-usb", 0x11000, 0x1000, true},
+ {"tunable_USB_LN1_AUSPMA_RX_SHM", "apple,tunable-lane1-usb", 0x12000, 0x1000, true},
+ {"tunable_USB_LN1_AUSPMA_TX_TOP", "apple,tunable-lane1-usb", 0x13000, 0x1000, true},
+ {"tunable_CIO_LN0_AUSPMA_RX_TOP", "apple,tunable-lane0-cio", 0x9000, 0x1000, true},
+ {"tunable_CIO_LN0_AUSPMA_RX_EQ", "apple,tunable-lane0-cio", 0xa000, 0x1000, true},
+ {"tunable_CIO_LN0_AUSPMA_RX_SHM", "apple,tunable-lane0-cio", 0xb000, 0x1000, true},
+ {"tunable_CIO_LN0_AUSPMA_TX_TOP", "apple,tunable-lane0-cio", 0xc000, 0x1000, true},
+ {"tunable_CIO_LN1_AUSPMA_RX_TOP", "apple,tunable-lane1-cio", 0x10000, 0x1000, true},
+ {"tunable_CIO_LN1_AUSPMA_RX_EQ", "apple,tunable-lane1-cio", 0x11000, 0x1000, true},
+ {"tunable_CIO_LN1_AUSPMA_RX_SHM", "apple,tunable-lane1-cio", 0x12000, 0x1000, true},
+ {"tunable_CIO_LN1_AUSPMA_TX_TOP", "apple,tunable-lane1-cio", 0x13000, 0x1000, true},
+};
+
+static int dt_append_atc_tunable(int adt_node, int fdt_node,
+ const struct adt_tunable_info *tunable_info)
+{
+ u32 tunables_len;
+ const struct atc_tunable *tunable_adt =
+ adt_getprop(adt, adt_node, tunable_info->adt_name, &tunables_len);
+
+ if (!tunable_adt) {
+ printf("ADT: tunable %s not found\n", tunable_info->adt_name);
+
+ if (tunable_info->required)
+ return -1;
+ else
+ return 0;
+ }
+
+ if (tunables_len % sizeof(*tunable_adt)) {
+ printf("ADT: tunable %s with invalid length %d\n", tunable_info->adt_name, tunables_len);
+ return -1;
+ }
+
+ u32 n_tunables = tunables_len / sizeof(*tunable_adt);
+ for (size_t j = 0; j < n_tunables; j++) {
+ const struct atc_tunable *tunable = &tunable_adt[j];
+
+ if (tunable->size != 32) {
+ printf("kboot: ATC tunable has invalid size %d\n", tunable->size);
+ return -1;
+ }
+
+ if (tunable->offset % (tunable->size / 8)) {
+ printf("kboot: ATC tunable has unaligned offset %x\n", tunable->offset);
+ return -1;
+ }
+
+ if (tunable->offset + (tunable->size / 8) > tunable_info->reg_size) {
+ printf("kboot: ATC tunable has invalid offset %x\n", tunable->offset);
+ return -1;
+ }
+
+ if (fdt_appendprop_u32(dt, fdt_node, tunable_info->fdt_name,
+ tunable->offset + tunable_info->reg_offset) < 0)
+ return -1;
+ if (fdt_appendprop_u32(dt, fdt_node, tunable_info->fdt_name, tunable->mask) < 0)
+ return -1;
+ if (fdt_appendprop_u32(dt, fdt_node, tunable_info->fdt_name, tunable->value) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static void dt_copy_atc_tunables(const char *adt_path, const char *dt_alias)
+{
+ int ret;
+
+ int adt_node = adt_path_offset(adt, adt_path);
+ if (adt_node < 0)
+ return;
+
+ const char *fdt_path = fdt_get_alias(dt, dt_alias);
+ if (fdt_path == NULL) {
+ printf("FDT: Unable to find alias %s\n", dt_alias);
+ return;
+ }
+
+ int fdt_node = fdt_path_offset(dt, fdt_path);
+ if (fdt_node < 0) {
+ printf("FDT: Unable to find path %s for alias %s\n", fdt_path, dt_alias);
+ return;
+ }
+
+ for (size_t i = 0; i < sizeof(atc_tunables) / sizeof(*atc_tunables); ++i) {
+ ret = dt_append_atc_tunable(adt_node, fdt_node, &atc_tunables[i]);
+ if (ret)
+ goto cleanup;
+ }
+
+ return;
+
+cleanup:
+ /*
+ * USB3 and Thunderbolt won't work if something went wrong. Clean up to make
+ * sure we don't leave half-filled properties around so that we can at least
+ * try to boot with USB2 support only.
+ */
+ for (size_t i = 0; i < sizeof(atc_tunables) / sizeof(*atc_tunables); ++i)
+ fdt_delprop(dt, fdt_node, atc_tunables[i].fdt_name);
+
+ printf("FDT: Unable to setup ATC tunables for %s - USB3/Thunderbolt will not work\n", adt_path);
+}
+
+static int dt_set_atc_tunables(void)
+{
+ char adt_path[32];
+ char fdt_alias[32];
+
+ for (int i = 0; i < MAX_ATC_DEVS; ++i) {
+ memset(adt_path, 0, sizeof(adt_path));
+ snprintf(adt_path, sizeof(adt_path), "/arm-io/atc-phy%d", i);
+
+ memset(fdt_alias, 0, sizeof(adt_path));
+ snprintf(fdt_alias, sizeof(fdt_alias), "atcphy%d", i);
+
+ dt_copy_atc_tunables(adt_path, fdt_alias);
+ }
+
+ return 0;
+}
+
+static const struct adt_tunable_info acio_tunables[] = {
+ /* NHI tunables */
+ {"hi_up_tx_desc_fabric_tunables", "apple,tunable-nhi", 0xf0000, 0x4000, true},
+ {"hi_up_tx_data_fabric_tunables", "apple,tunable-nhi", 0xec000, 0x4000, true},
+ {"hi_up_rx_desc_fabric_tunables", "apple,tunable-nhi", 0xe8000, 0x4000, true},
+ {"hi_up_wr_fabric_tunables", "apple,tunable-nhi", 0xf4000, 0x4000, true},
+ {"hi_up_merge_fabric_tunables", "apple,tunable-nhi", 0xf8000, 0x4000, true},
+ {"hi_dn_merge_fabric_tunables", "apple,tunable-nhi", 0xfc000, 0x4000, true},
+ {"fw_int_ctl_management_tunables", "apple,tunable-nhi", 0x4000, 0x4000, true},
+ /* M3 tunables */
+ {"top_tunables", "apple,tunable-m3", 0x0, 0x4000, true},
+ {"hbw_fabric_tunables", "apple,tunable-m3", 0x4000, 0x4000, true},
+ {"lbw_fabric_tunables", "apple,tunable-m3", 0x8000, 0x4000, true},
+ /* PCIe adapter tunables */
+ {"pcie_adapter_regs_tunables", "apple,tunable-pcie-adapter", 0x0, 0x4000, true},
+};
+
+struct acio_tunable {
+ u32 offset;
+ u32 size;
+ u64 mask;
+ u64 value;
+} PACKED;
+static_assert(sizeof(struct acio_tunable) == 24, "Invalid acio_tunable size");
+
+/*
+ * This is *almost* identical to dt_append_atc_tunable except for the different
+ * tunable struct and that tunable->size is in bytes instead of bits.
+ * If only C had generics that aren't macros :-(
+ */
+static int dt_append_acio_tunable(int adt_node, int fdt_node,
+ const struct adt_tunable_info *tunable_info)
+{
+ u32 tunables_len;
+ const struct acio_tunable *tunable_adt =
+ adt_getprop(adt, adt_node, tunable_info->adt_name, &tunables_len);
+
+ if (!tunable_adt) {
+ printf("ADT: tunable %s not found\n", tunable_info->adt_name);
+
+ if (tunable_info->required)
+ return -1;
+ else
+ return 0;
+ }
+
+ if (tunables_len % sizeof(*tunable_adt)) {
+ printf("ADT: tunable %s with invalid length %d\n", tunable_info->adt_name, tunables_len);
+ return -1;
+ }
+
+ u32 n_tunables = tunables_len / sizeof(*tunable_adt);
+ for (size_t j = 0; j < n_tunables; j++) {
+ const struct acio_tunable *tunable = &tunable_adt[j];
+
+ if (tunable->size != 4) {
+ printf("kboot: ACIO tunable has invalid size %d\n", tunable->size);
+ return -1;
+ }
+
+ if (tunable->offset % tunable->size) {
+ printf("kboot: ACIO tunable has unaligned offset %x\n", tunable->offset);
+ return -1;
+ }
+
+ if (tunable->offset + tunable->size > tunable_info->reg_size) {
+ printf("kboot: ACIO tunable has invalid offset %x\n", tunable->offset);
+ return -1;
+ }
+
+ if (fdt_appendprop_u32(dt, fdt_node, tunable_info->fdt_name,
+ tunable->offset + tunable_info->reg_offset) < 0)
+ return -1;
+ if (fdt_appendprop_u32(dt, fdt_node, tunable_info->fdt_name, tunable->mask) < 0)
+ return -1;
+ if (fdt_appendprop_u32(dt, fdt_node, tunable_info->fdt_name, tunable->value) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int dt_copy_acio_tunables(const char *adt_path, const char *dt_alias)
+{
+ int ret;
+ int adt_node = adt_path_offset(adt, adt_path);
+ if (adt_node < 0)
+ return -1;
+
+ const char *fdt_path = fdt_get_alias(dt, dt_alias);
+ if (fdt_path == NULL)
+ bail("FDT: Unable to find alias %s\n", dt_alias);
+
+ int fdt_node = fdt_path_offset(dt, fdt_path);
+ if (fdt_node < 0)
+ bail("FDT: Unable to find path %s for alias %s\n", fdt_path, dt_alias);
+
+ u32 drom_len;
+ const u8 *drom_blob = adt_getprop(adt, adt_node, "thunderbolt-drom", &drom_len);
+ if (!drom_blob || !drom_len)
+ bail("ADT: Failed to get thunderbolt-drom");
+
+ fdt_setprop(dt, fdt_node, "apple,thunderbolt-drom", drom_blob, drom_len);
+ for (size_t i = 0; i < sizeof(acio_tunables) / sizeof(*acio_tunables); ++i) {
+ ret = dt_append_acio_tunable(adt_node, fdt_node, &acio_tunables[i]);
+ if (ret)
+ bail_cleanup("ADT: unable to convert '%s' tunable", acio_tunables[i].adt_name);
+ }
+
+ return 0;
+
+err:
+ fdt_delprop(dt, fdt_node, "apple,thunderbolt-drom");
+ fdt_delprop(dt, fdt_node, "apple,tunable-nhi");
+ fdt_delprop(dt, fdt_node, "apple,tunable-m3");
+ fdt_delprop(dt, fdt_node, "apple,tunable-pcie-adapter");
+
+ return -1;
+}
+
+static int dt_set_acio_tunables(void)
+{
+ char adt_path[32];
+ char fdt_alias[32];
+
+ for (int i = 0; i < MAX_CIO_DEVS; ++i) {
+ memset(adt_path, 0, sizeof(adt_path));
+ snprintf(adt_path, sizeof(adt_path), "/arm-io/acio%d", i);
+
+ memset(fdt_alias, 0, sizeof(adt_path));
+ snprintf(fdt_alias, sizeof(fdt_alias), "acio%d", i);
+
+ dt_copy_acio_tunables(adt_path, fdt_alias);
+ }
+
+ return 0;
+}
+
+static int dt_get_iommu_node(int node, u32 num)
+{
+ int len;
+ assert(num < 32);
+ const void *prop = fdt_getprop(dt, node, "iommus", &len);
+ if (!prop || len < 0 || (u32)len < 8 * (num + 1)) {
+ printf("FDT: unexpected 'iommus' prop / len %d\n", len);
+ return -FDT_ERR_NOTFOUND;
+ }
+
+ const fdt32_t *iommus = prop;
+ uint32_t phandle = fdt32_ld(&iommus[num * 2]);
+
+ return fdt_node_offset_by_phandle(dt, phandle);
+}
+
+static dart_dev_t *dt_init_dart_by_node(int node, u32 num)
+{
+ int len;
+ assert(num < 32);
+ const void *prop = fdt_getprop(dt, node, "iommus", &len);
+ if (!prop || len < 0 || (u32)len < 8 * (num + 1)) {
+ printf("FDT: unexpected 'iommus' prop / len %d\n", len);
+ return NULL;
+ }
+
+ const fdt32_t *iommus = prop;
+ u32 iommu_phandle = fdt32_ld(&iommus[num * 2]);
+ u32 iommu_stream = fdt32_ld(&iommus[num * 2 + 1]);
+
+ printf("FDT: iommu phande:%u stream:%u\n", iommu_phandle, iommu_stream);
+
+ return dart_init_fdt(dt, iommu_phandle, iommu_stream, true);
+}
+
+static u64 dart_get_mapping(dart_dev_t *dart, const char *path, u64 paddr, size_t size)
+{
+ u64 iova = dart_search(dart, (void *)paddr);
+ if (DART_IS_ERR(iova)) {
+ printf("ADT: %s paddr: 0x%lx is not mapped\n", path, paddr);
+ return iova;
+ }
+
+ u64 pend = (u64)dart_translate(dart, iova + size - 1);
+ if (pend != (paddr + size - 1)) {
+ printf("ADT: %s is not continuously mapped: 0x%lx\n", path, pend);
+ return DART_PTR_ERR;
+ }
+
+ return iova;
+}
+
+static int dt_device_set_reserved_mem(int node, dart_dev_t *dart, const char *name,
+ uint32_t phandle, u64 paddr, u64 size)
+{
+ int ret;
+
+ u64 iova = dart_get_mapping(dart, name, paddr, size);
+ if (DART_IS_ERR(iova))
+ bail("ADT: no mapping found for '%s' 0x%012lx iova:0x%08lx)\n", name, paddr, iova);
+
+ ret = fdt_appendprop_u32(dt, node, "iommu-addresses", phandle);
+ if (ret != 0)
+ bail("DT: could not append phandle '%s.compatible' property: %d\n", name, ret);
+
+ ret = fdt_appendprop_u64(dt, node, "iommu-addresses", iova);
+ if (ret != 0)
+ bail("DT: could not append iova to '%s.iommu-addresses' property: %d\n", name, ret);
+
+ ret = fdt_appendprop_u64(dt, node, "iommu-addresses", size);
+ if (ret != 0)
+ bail("DT: could not append size to '%s.iommu-addresses' property: %d\n", name, ret);
+
+ return 0;
+}
+
+static int dt_get_or_add_reserved_mem(const char *node_name, const char *compat, u64 paddr,
+ size_t size)
+{
+ int ret;
+ int resv_node = fdt_path_offset(dt, "/reserved-memory");
+ if (resv_node < 0)
+ bail("DT: '/reserved-memory' not found\n");
+
+ int node = fdt_subnode_offset(dt, resv_node, node_name);
+ if (node >= 0)
+ return node;
+
+ node = fdt_add_subnode(dt, resv_node, node_name);
+ if (node < 0)
+ bail("DT: failed to add node '%s' to '/reserved-memory'\n", node_name);
+
+ uint32_t phandle;
+ ret = fdt_generate_phandle(dt, &phandle);
+ if (ret)
+ bail("DT: failed to generate phandle: %d\n", ret);
+
+ ret = fdt_setprop_u32(dt, node, "phandle", phandle);
+ if (ret != 0)
+ bail("DT: couldn't set '%s.phandle' property: %d\n", node_name, ret);
+
+ u64 reg[2] = {cpu_to_fdt64(paddr), cpu_to_fdt64(size)};
+ ret = fdt_setprop(dt, node, "reg", reg, sizeof(reg));
+ if (ret != 0)
+ bail("DT: couldn't set '%s.reg' property: %d\n", node_name, ret);
+
+ ret = fdt_setprop_string(dt, node, "compatible", compat);
+ if (ret != 0)
+ bail("DT: couldn't set '%s.compatible' property: %d\n", node_name, ret);
+
+ ret = fdt_setprop_empty(dt, node, "no-map");
+ if (ret != 0)
+ bail("DT: couldn't set '%s.no-map' property: %d\n", node_name, ret);
+
+ return node;
+}
+
+static int dt_device_add_mem_region(const char *alias, uint32_t phandle, const char *name)
+{
+ int ret;
+ int dev_node = fdt_path_offset(dt, alias);
+ if (dev_node < 0)
+ bail("DT: failed to get node for alias '%s'\n", alias);
+
+ ret = fdt_appendprop_u32(dt, dev_node, "memory-region", phandle);
+ if (ret != 0)
+ bail("DT: failed to append to 'memory-region' property\n");
+
+ dev_node = fdt_path_offset(dt, alias);
+ if (dev_node < 0)
+ bail("DT: failed to update node for alias '%s'\n", alias);
+
+ ret = fdt_appendprop_string(dt, dev_node, "memory-region-names", name);
+ if (ret != 0)
+ bail("DT: failed to append to 'memory-region-names' property\n");
+
+ return 0;
+}
+
+static int dt_set_dcp_firmware(const char *alias)
+{
+ const char *path = fdt_get_alias(dt, alias);
+
+ if (!path)
+ return 0;
+
+ int node = fdt_path_offset(dt, path);
+ if (node < 0)
+ return 0;
+
+ if (firmware_set_fdt(dt, node, "apple,firmware-version", &os_firmware) < 0)
+ bail("FDT: Could not set apple,firmware-version for %s\n", path);
+
+ const struct fw_version_info *compat;
+
+ switch (os_firmware.version) {
+ case V12_3_1:
+ case V12_4:
+ compat = &fw_versions[V12_3];
+ break;
+ default:
+ compat = &os_firmware;
+ break;
+ }
+
+ if (firmware_set_fdt(dt, node, "apple,firmware-compat", compat) < 0)
+ bail("FDT: Could not set apple,firmware-compat for %s\n", path);
+
+ return 0;
+}
+
+struct disp_mapping {
+ char region_adt[24];
+ char mem_fdt[24];
+ bool map_dcp;
+ bool map_disp;
+ bool map_piodma;
+};
+
+struct mem_region {
+ u64 paddr;
+ u64 size;
+};
+
+static int dt_add_reserved_regions(const char *dcp_alias, const char *disp_alias,
+ const char *piodma_alias, const char *compat,
+ struct disp_mapping *maps, struct mem_region *region,
+ u32 num_maps)
+{
+ int ret = 0;
+ dart_dev_t *dart_dcp = NULL, *dart_disp = NULL, *dart_piodma = NULL;
+ uint32_t dcp_phandle = 0, disp_phandle = 0, piodma_phandle = 0;
+
+ /* Check for display device aliases, if one is missing assume it is an old DT
+ * without display nodes and return without error.
+ * Otherwise init each dart and retrieve the node's phandle.
+ */
+ if (dcp_alias) {
+ int dcp_node = fdt_path_offset(dt, dcp_alias);
+ if (dcp_node < 0) {
+ printf("DT: could not resolve '%s' alias\n", dcp_alias);
+ goto err; // cleanup
+ }
+ dart_dcp = dt_init_dart_by_node(dcp_node, 0);
+ if (!dart_dcp)
+ bail_cleanup("DT: failed to init DART for '%s'\n", dcp_alias);
+ dcp_phandle = fdt_get_phandle(dt, dcp_node);
+ }
+
+ if (disp_alias) {
+ int disp_node = fdt_path_offset(dt, disp_alias);
+ if (disp_node < 0) {
+ printf("DT: could not resolve '%s' alias\n", disp_alias);
+ goto err; // cleanup
+ }
+ dart_disp = dt_init_dart_by_node(disp_node, 0);
+ if (!dart_disp)
+ bail_cleanup("DT: failed to init DART for '%s'\n", disp_alias);
+ disp_phandle = fdt_get_phandle(dt, disp_node);
+ }
+
+ if (piodma_alias) {
+ int piodma_node = fdt_path_offset(dt, piodma_alias);
+ if (piodma_node < 0) {
+ printf("DT: could not resolve '%s' alias\n", piodma_alias);
+ goto err; // cleanup
+ }
+
+ dart_piodma = dt_init_dart_by_node(piodma_node, 0);
+ if (!dart_piodma)
+ bail_cleanup("DT: failed to init DART for '%s'\n", piodma_alias);
+ piodma_phandle = fdt_get_phandle(dt, piodma_node);
+ }
+
+ for (unsigned i = 0; i < num_maps; i++) {
+ const char *name = maps[i].mem_fdt;
+ char node_name[64];
+
+ snprintf(node_name, sizeof(node_name), "%s@%lx", name, region[i].paddr);
+ int mem_node =
+ dt_get_or_add_reserved_mem(node_name, compat, region[i].paddr, region[i].size);
+ if (mem_node < 0)
+ goto err;
+
+ uint32_t mem_phandle = fdt_get_phandle(dt, mem_node);
+
+ if (maps[i].map_dcp && dart_dcp) {
+ ret = dt_device_set_reserved_mem(mem_node, dart_dcp, node_name, dcp_phandle,
+ region[i].paddr, region[i].size);
+ if (ret != 0)
+ goto err;
+ }
+ if (maps[i].map_disp && dart_disp) {
+ ret = dt_device_set_reserved_mem(mem_node, dart_disp, node_name, disp_phandle,
+ region[i].paddr, region[i].size);
+ if (ret != 0)
+ goto err;
+ }
+ if (maps[i].map_piodma && dart_piodma) {
+ ret = dt_device_set_reserved_mem(mem_node, dart_piodma, node_name, piodma_phandle,
+ region[i].paddr, region[i].size);
+ if (ret != 0)
+ goto err;
+ }
+
+ /* modify device nodes after filling /reserved-memory to avoid
+ * reloading mem_node's offset */
+ if (maps[i].map_dcp && dcp_alias) {
+ ret = dt_device_add_mem_region(dcp_alias, mem_phandle, maps[i].mem_fdt);
+ if (ret < 0)
+ goto err;
+ }
+ if (maps[i].map_disp && disp_alias) {
+ ret = dt_device_add_mem_region(disp_alias, mem_phandle, maps[i].mem_fdt);
+ if (ret < 0)
+ goto err;
+ }
+ if (maps[i].map_piodma && piodma_alias) {
+ ret = dt_device_add_mem_region(piodma_alias, mem_phandle, maps[i].mem_fdt);
+ if (ret < 0)
+ goto err;
+ }
+ }
+
+ /* enable dart-disp0, it is disabled in device tree to avoid resetting
+ * it and breaking display scanout when booting with old m1n1 which
+ * does not lock dart-disp0.
+ */
+ if (disp_alias) {
+ int disp_node = fdt_path_offset(dt, disp_alias);
+
+ int dart_disp0 = dt_get_iommu_node(disp_node, 0);
+ if (dart_disp0 < 0)
+ bail_cleanup("DT: failed to find 'dart-disp0'\n");
+
+ if (fdt_setprop_string(dt, dart_disp0, "status", "okay") < 0)
+ bail_cleanup("DT: failed to enable 'dart-disp0'\n");
+ }
+err:
+ if (dart_dcp)
+ dart_shutdown(dart_dcp);
+ if (dart_disp)
+ dart_shutdown(dart_disp);
+ if (dart_piodma)
+ dart_shutdown(dart_piodma);
+
+ return ret;
+}
+
+static int dt_carveout_reserved_regions(const char *dcp_alias, const char *disp_alias,
+ const char *piodma_alias, struct disp_mapping *maps,
+ u32 num_maps)
+{
+ int ret = 0;
+
+ struct mem_region region[MAX_DISP_MAPPINGS];
+
+ assert(num_maps <= MAX_DISP_MAPPINGS);
+
+ // return early if dcp_alias does not exists
+ if (!fdt_get_alias(dt, dcp_alias))
+ return 0;
+
+ ret = dt_set_dcp_firmware(dcp_alias);
+ if (ret)
+ return ret;
+
+ int node = adt_path_offset(adt, "/chosen/carveout-memory-map");
+ if (node < 0)
+ bail("ADT: '/chosen/carveout-memory-map' not found\n");
+
+ /* read physical addresses of reserved memory regions */
+ /* do this up front to avoid errors after modifying the DT */
+ for (unsigned i = 0; i < num_maps; i++) {
+
+ int ret;
+ u64 phys_map[2];
+ struct disp_mapping *map = &maps[i];
+ const char *name = map->region_adt;
+
+ ret = ADT_GETPROP_ARRAY(adt, node, name, phys_map);
+ if (ret != sizeof(phys_map))
+ bail("ADT: could not get carveout memory '%s'\n", name);
+ if (!phys_map[0] || !phys_map[1])
+ bail("ADT: carveout memory '%s'\n", name);
+
+ region[i].paddr = phys_map[0];
+ region[i].size = phys_map[1];
+ }
+
+ return dt_add_reserved_regions(dcp_alias, disp_alias, piodma_alias, "apple,asc-mem", maps,
+ region, num_maps);
+}
+
+static struct disp_mapping disp_reserved_regions_vram[] = {
+ // boot framebuffer, mapped to dart-disp0 sid 0 and dart-dcp sid 0/5
+ {"vram", "framebuffer", true, true, false},
+};
+
+static int dt_vram_reserved_region(const char *dcp_alias, const char *disp_alias)
+{
+ int ret = 0;
+ int adt_path[4];
+ struct mem_region region;
+
+ // return early if dcp_alias does not exists
+ if (!fdt_get_alias(dt, dcp_alias))
+ return 0;
+
+ int node = adt_path_offset_trace(adt, "/vram", adt_path);
+
+ if (node < 0)
+ bail("ADT: '/vram' not found\n");
+
+ int pp = 0;
+ while (adt_path[pp])
+ pp++;
+ adt_path[pp + 1] = 0;
+
+ ret = adt_get_reg(adt, adt_path, "reg", 0, &region.paddr, &region.size);
+ if (ret < 0)
+ bail("ADT: failed to read /vram/reg\n");
+
+ return dt_add_reserved_regions(dcp_alias, disp_alias, NULL, "framebuffer",
+ disp_reserved_regions_vram, &region, 1);
+}
+
+static struct disp_mapping disp_reserved_regions_t8103[] = {
+ {"region-id-50", "dcp_data", true, false, false},
+ {"region-id-57", "region57", true, false, false},
+ // The 2 following regions are mapped in dart-dcp sid 0 and dart-disp0 sid 0 and 4
+ {"region-id-94", "region94", true, true, false},
+ {"region-id-95", "region95", true, false, true},
+};
+
+static struct disp_mapping dcpext_reserved_regions_t8103[] = {
+ {"region-id-73", "dcpext_data", true, false, false},
+ {"region-id-74", "region74", true, false, false},
+};
+
+static struct disp_mapping disp_reserved_regions_t8112[] = {
+ {"region-id-49", "dcp_txt", true, false, false},
+ {"region-id-50", "dcp_data", true, false, false},
+ {"region-id-57", "region57", true, false, false},
+ // The 2 following regions are mapped in dart-dcp sid 5 and dart-disp0 sid 0 and 4
+ {"region-id-94", "region94", true, true, false},
+ {"region-id-95", "region95", true, false, true},
+};
+
+static struct disp_mapping dcpext_reserved_regions_t8112[] = {
+ {"region-id-49", "dcp_txt", true, false, false},
+ {"region-id-73", "dcpext_data", true, false, false},
+ {"region-id-74", "region74", true, false, false},
+};
+
+static struct disp_mapping disp_reserved_regions_t600x[] = {
+ {"region-id-50", "dcp_data", true, false, false},
+ {"region-id-57", "region57", true, false, false},
+ // The 2 following regions are mapped in dart-dcp sid 0 and dart-disp0 sid 0 and 4
+ {"region-id-94", "region94", true, true, false},
+ {"region-id-95", "region95", true, false, true},
+ // used on M1 Pro/Max/Ultra, mapped to dcp and disp0
+ {"region-id-157", "region157", true, true, false},
+};
+
+#define MAX_DCPEXT 8
+
+static struct disp_mapping dcpext_reserved_regions_t600x[MAX_DCPEXT][2] = {
+ {
+ {"region-id-73", "dcpext0_data", true, false, false},
+ {"region-id-74", "", true, false, false},
+ },
+ {
+ {"region-id-88", "dcpext1_data", true, false, false},
+ {"region-id-89", "region89", true, false, false},
+ },
+ {
+ {"region-id-111", "dcpext2_data", true, false, false},
+ {"region-id-112", "region112", true, false, false},
+ },
+ {
+ {"region-id-119", "dcpext3_data", true, false, false},
+ {"region-id-120", "region120", true, false, false},
+ },
+ {
+ {"region-id-127", "dcpext4_data", true, false, false},
+ {"region-id-128", "region128", true, false, false},
+ },
+ {
+ {"region-id-135", "dcpext5_data", true, false, false},
+ {"region-id-136", "region136", true, false, false},
+ },
+ {
+ {"region-id-143", "dcpext6_data", true, false, false},
+ {"region-id-144", "region144", true, false, false},
+ },
+ {
+ {"region-id-151", "dcpext7_data", true, false, false},
+ {"region-id-152", "region152", true, false, false},
+ },
+};
+
+#define ARRAY_SIZE(s) (sizeof(s) / sizeof((s)[0]))
+
+static int dt_set_display(void)
+{
+ /* lock dart-disp0 to prevent old software from resetting it */
+ dart_lock_adt("/arm-io/dart-disp0", 0);
+
+ /* Add "/reserved-memory" nodes with iommu mapping and link them to their
+ * devices. The memory is already excluded from useable RAM so these nodes
+ * are only required to inform the OS about the existing mappings.
+ * Required for disp0, dcp and all dcpext.
+ * Checks for dcp* / disp*_piodma / disp* aliases and fails silently if
+ * they are missing. */
+
+ int ret = 0;
+
+ if (!fdt_node_check_compatible(dt, 0, "apple,t8103")) {
+ ret = dt_carveout_reserved_regions("dcp", "disp0", "disp0_piodma",
+ disp_reserved_regions_t8103,
+ ARRAY_SIZE(disp_reserved_regions_t8103));
+ if (ret)
+ return ret;
+
+ ret = dt_carveout_reserved_regions("dcpext", NULL, NULL, dcpext_reserved_regions_t8103,
+ ARRAY_SIZE(dcpext_reserved_regions_t8103));
+ } else if (!fdt_node_check_compatible(dt, 0, "apple,t8112")) {
+ ret = dt_carveout_reserved_regions("dcp", "disp0", "disp0_piodma",
+ disp_reserved_regions_t8112,
+ ARRAY_SIZE(disp_reserved_regions_t8112));
+ if (ret)
+ return ret;
+
+ ret = dt_carveout_reserved_regions("dcpext", NULL, NULL, dcpext_reserved_regions_t8112,
+ ARRAY_SIZE(dcpext_reserved_regions_t8112));
+ } else if (!fdt_node_check_compatible(dt, 0, "apple,t6000") ||
+ !fdt_node_check_compatible(dt, 0, "apple,t6001") ||
+ !fdt_node_check_compatible(dt, 0, "apple,t6002")) {
+ ret = dt_carveout_reserved_regions("dcp", "disp0", "disp0_piodma",
+ disp_reserved_regions_t600x,
+ ARRAY_SIZE(disp_reserved_regions_t600x));
+ if (ret)
+ return ret;
+
+ for (int n = 0; n < MAX_DCPEXT && ret == 0; n++) {
+ char dcpext_alias[16];
+
+ snprintf(dcpext_alias, sizeof(dcpext_alias), "dcpext%d", n);
+ ret = dt_carveout_reserved_regions(dcpext_alias, NULL, NULL,
+ dcpext_reserved_regions_t600x[n],
+ ARRAY_SIZE(dcpext_reserved_regions_t600x[n]));
+ }
+ } else {
+ printf("DT: unknown compatible, skip display reserved-memory setup\n");
+ return 0;
+ }
+ if (ret)
+ return ret;
+
+ return dt_vram_reserved_region("dcp", "disp0");
+}
+
+static int dt_disable_missing_devs(const char *adt_prefix, const char *dt_prefix, int max_devs)
+{
+ int ret = -1;
+ int adt_prefix_len = strlen(adt_prefix);
+ int dt_prefix_len = strlen(dt_prefix);
+
+ int acnt = 0, phcnt = 0;
+ u64 *addrs = malloc(max_devs * sizeof(u64));
+ u32 *phandles = malloc(max_devs * sizeof(u32) * 4); // Allow up to 4 extra nodes per device
+ if (!addrs || !phandles)
+ bail_cleanup("FDT: out of memory\n");
+
+ int path[8];
+ int node = adt_path_offset_trace(adt, "/arm-io", path);
+ if (node < 0)
+ bail_cleanup("ADT: /arm-io not found\n");
+
+ int pp = 0;
+ while (path[pp])
+ pp++;
+ path[pp + 1] = 0;
+
+ u32 die_count;
+ if (ADT_GETPROP(adt, node, "die-count", &die_count) < 0) {
+ die_count = 1;
+ }
+ if (die_count > 8) {
+ printf("ADT: limiting die-count from %u to 8\n", die_count);
+ die_count = 8;
+ }
+
+ /* Find ADT registers */
+ ADT_FOREACH_CHILD(adt, node)
+ {
+ const char *name = adt_get_name(adt, node);
+ if (strncmp(name, adt_prefix, adt_prefix_len))
+ continue;
+
+ path[pp] = node;
+ if (adt_get_reg(adt, path, "reg", 0, &addrs[acnt++], NULL) < 0)
+ bail_cleanup("Error getting /arm-io/%s regs\n", name);
+ }
+
+ for (u32 die = 0; die < die_count; ++die) {
+ char path[32] = "/soc";
+
+ if (die_count > 1) {
+ // pre-linux submission multi-die path
+ // can probably removed the next time someone read this comment.
+ snprintf(path, sizeof(path), "/soc/die%u", die);
+ int die_node = fdt_path_offset(dt, path);
+ if (die_node < 0) {
+ /* this should use aliases for the soc nodes */
+ u64 die_unit_addr = die * PMGR_DIE_OFFSET + 0x200000000;
+ snprintf(path, sizeof(path), "/soc@%lx", die_unit_addr);
+ }
+ }
+
+ int soc = fdt_path_offset(dt, path);
+ if (soc < 0)
+ bail("FDT: %s node not found in devtree\n", path);
+
+ // parse ranges for address translation
+ struct dt_ranges_tbl ranges[DT_MAX_RANGES] = {0};
+ dt_parse_ranges(dt, soc, ranges);
+
+ /* Disable primary devices */
+ fdt_for_each_subnode(node, dt, soc)
+ {
+ const char *name = fdt_get_name(dt, node, NULL);
+ if (strncmp(name, dt_prefix, dt_prefix_len))
+ continue;
+
+ const fdt64_t *reg = fdt_getprop(dt, node, "reg", NULL);
+ if (!reg)
+ bail_cleanup("FDT: failed to get reg property of %s\n", name);
+
+ u64 addr = dt_translate(ranges, reg);
+
+ int i;
+ for (i = 0; i < acnt; i++)
+ if (addrs[i] == addr)
+ break;
+ if (i < acnt)
+ continue;
+
+ int iommus_size;
+ const fdt32_t *iommus = fdt_getprop(dt, node, "iommus", &iommus_size);
+ if (iommus) {
+ if (iommus_size & 7 || iommus_size > 4 * 8) {
+ printf("FDT: bad iommus property for %s/%s\n", path, name);
+ } else {
+ for (int i = 0; i < iommus_size / 8; i++)
+ phandles[phcnt++] = fdt32_ld(&iommus[i * 2]);
+ }
+ }
+
+ int phys_size;
+ const fdt32_t *phys = fdt_getprop(dt, node, "phys", &phys_size);
+ if (phys) {
+ if (phys_size & 7 || phys_size > 4 * 8) {
+ printf("FDT: bad phys property for %s/%s\n", path, name);
+ } else {
+ for (int i = 0; i < phys_size / 8; i++)
+ phandles[phcnt++] = fdt32_ld(&phys[i * 2]);
+ }
+ }
+
+ const char *status = fdt_getprop(dt, node, "status", NULL);
+ if (!status || strcmp(status, "disabled")) {
+ printf("FDT: Disabling missing device %s/%s\n", path, name);
+
+ if (fdt_setprop_string(dt, node, "status", "disabled") < 0)
+ bail_cleanup("FDT: failed to set status property of %s/%s\n", path, name);
+ }
+ }
+
+ /* Disable secondary devices */
+ fdt_for_each_subnode(node, dt, soc)
+ {
+ const char *name = fdt_get_name(dt, node, NULL);
+ u32 phandle = fdt_get_phandle(dt, node);
+
+ for (int i = 0; i < phcnt; i++) {
+ if (phandles[i] != phandle)
+ continue;
+
+ const char *status = fdt_getprop(dt, node, "status", NULL);
+ if (status && !strcmp(status, "disabled"))
+ continue;
+
+ printf("FDT: Disabling secondary device %s/%s\n", path, name);
+
+ if (fdt_setprop_string(dt, node, "status", "disabled") < 0)
+ bail_cleanup("FDT: failed to set status property of %s/%s\n", path, name);
+ break;
+ }
+ }
+ }
+
+ ret = 0;
+err:
+ free(phandles);
+ free(addrs);
+
+ return ret;
+}
+
+static int dt_transfer_virtios(void)
+{
+ int path[3];
+ path[0] = adt_path_offset(adt, "/arm-io/");
+ if (path[0] < 0)
+ bail("ADT: /arm-io not found\n");
+
+ int aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic");
+ if (aic == -FDT_ERR_NOTFOUND)
+ aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic2");
+ if (aic < 0)
+ bail("FDT: failed to find AIC node\n");
+
+ u32 aic_phandle = fdt_get_phandle(dt, aic);
+ const fdt32_t *ic_prop = fdt_getprop(dt, aic, "#interrupt-cells", NULL);
+ u32 intcells = 0;
+ if (ic_prop)
+ intcells = fdt32_ld(ic_prop);
+ if (intcells < 3 || intcells > 4)
+ bail("FDT: bad '#interrupt-cells' on AIC node (%d)\n", intcells);
+
+ for (u32 i = 0; i < 16; i++) {
+ char name[16], fullname[32];
+ snprintf(name, sizeof(name) - 1, "virtio%d", i);
+
+ path[1] = adt_subnode_offset(adt, path[0], name);
+ if (path[1] < 0)
+ break;
+ path[2] = 0;
+
+ u64 addr, size;
+ if (adt_get_reg(adt, path, "reg", 0, &addr, &size) < 0)
+ bail("ADT: error getting /arm-io/%s regs\n", name);
+
+ u32 irq;
+ ADT_GETPROP(adt, path[1], "interrupts", &irq);
+
+ snprintf(fullname, sizeof(fullname) - 1, "virtio@%lx", addr);
+ printf("FDT: Adding %s found in ADT\n", name);
+
+ int fnode = fdt_add_subnode(dt, 0, fullname);
+ if (fnode < 0)
+ bail("FDT: failed to create %s\n", fullname);
+
+ if (fdt_setprop_string(dt, fnode, "compatible", "virtio,mmio"))
+ bail("FDT: couldn't set %s.compatible\n", fullname);
+
+ fdt64_t reg[2];
+ fdt64_st(reg + 0, addr);
+ fdt64_st(reg + 1, size);
+ if (fdt_setprop(dt, fnode, "reg", reg, sizeof(reg)))
+ bail("FDT: couldn't set %s.reg\n", fullname);
+
+ if (fdt_setprop_u32(dt, fnode, "interrupt-parent", aic_phandle))
+ bail("FDT: couldn't set %s.interrupt-parent\n", fullname);
+
+ fdt32_t intprop[4];
+ fdt32_st(intprop + 0, 0); // AIC_IRQ
+ fdt32_st(intprop + 1, 0);
+ fdt32_st(intprop + intcells - 2, irq);
+ fdt32_st(intprop + intcells - 1, 4); // IRQ_TYPE_LEVEL_HIGH
+ if (fdt_setprop(dt, fnode, "interrupts", intprop, 4 * intcells))
+ bail("FDT: couldn't set %s.interrupts\n", fullname);
+ }
+
+ return 0;
+}
+
+void kboot_set_initrd(void *start, size_t size)
+{
+ initrd_start = start;
+ initrd_size = size;
+}
+
+int kboot_set_chosen(const char *name, const char *value)
+{
+ int i = 0;
+
+ if (!name)
+ return -1;
+
+ for (i = 0; i < MAX_CHOSEN_PARAMS; i++) {
+ if (!chosen_params[i][0]) {
+ chosen_params[i][0] = malloc(strlen(name) + 1);
+ strcpy(chosen_params[i][0], name);
+ break;
+ }
+
+ if (!strcmp(name, chosen_params[i][0])) {
+ free(chosen_params[i][1]);
+ chosen_params[i][1] = NULL;
+ break;
+ }
+ }
+
+ if (i >= MAX_CHOSEN_PARAMS)
+ return -1;
+
+ if (value) {
+ chosen_params[i][1] = malloc(strlen(value) + 1);
+ strcpy(chosen_params[i][1], value);
+ }
+
+ return i;
+}
+
+int kboot_prepare_dt(void *fdt)
+{
+ if (dt) {
+ free(dt);
+ dt = NULL;
+ }
+
+ dt_bufsize = fdt_totalsize(fdt);
+ assert(dt_bufsize);
+
+ dt_bufsize += 64 * 1024; // Add 64K of buffer for modifications
+ dt = memalign(DT_ALIGN, dt_bufsize);
+
+ if (fdt_open_into(fdt, dt, dt_bufsize) < 0)
+ bail("FDT: fdt_open_into() failed\n");
+
+ if (fdt_add_mem_rsv(dt, (u64)dt, dt_bufsize))
+ bail("FDT: couldn't add reservation for the devtree\n");
+
+ if (fdt_add_mem_rsv(dt, (u64)_base, ((u64)_end) - ((u64)_base)))
+ bail("FDT: couldn't add reservation for m1n1\n");
+
+ if (dt_set_chosen())
+ return -1;
+ if (dt_set_serial_number())
+ return -1;
+ if (dt_set_memory())
+ return -1;
+ if (dt_set_cpus())
+ return -1;
+ if (dt_set_mac_addresses())
+ return -1;
+ if (dt_set_wifi())
+ return -1;
+ if (dt_set_bluetooth())
+ return -1;
+ if (dt_set_uboot())
+ return -1;
+ if (dt_set_atc_tunables())
+ return -1;
+ if (dt_set_acio_tunables())
+ return -1;
+ if (dt_set_display())
+ return -1;
+ if (dt_set_gpu(dt))
+ return -1;
+ if (dt_set_multitouch())
+ return -1;
+ if (dt_disable_missing_devs("usb-drd", "usb@", 8))
+ return -1;
+ if (dt_disable_missing_devs("i2c", "i2c@", 8))
+ return -1;
+#ifndef RELEASE
+ if (dt_transfer_virtios())
+ return 1;
+#endif
+
+ if (fdt_pack(dt))
+ bail("FDT: fdt_pack() failed\n");
+
+ printf("FDT prepared at %p\n", dt);
+
+ return 0;
+}
+
+int kboot_boot(void *kernel)
+{
+ usb_init();
+ pcie_init();
+ dapf_init_all();
+
+ printf("Setting SMP mode to WFE...\n");
+ smp_set_wfe_mode(true);
+ printf("Preparing to boot kernel at %p with fdt at %p\n", kernel, dt);
+
+ next_stage.entry = kernel;
+ next_stage.args[0] = (u64)dt;
+ next_stage.args[1] = 0;
+ next_stage.args[2] = 0;
+ next_stage.args[3] = 0;
+ next_stage.args[4] = 0;
+ next_stage.restore_logo = false;
+
+ return 0;
+}