/* SPDX-License-Identifier: MIT */ #include "../build/build_cfg.h" #include "chainload.h" #include "adt.h" #include "malloc.h" #include "memory.h" #include "nvme.h" #include "string.h" #include "types.h" #include "utils.h" #include "xnuboot.h" #ifdef CHAINLOADING int rust_load_image(const char *spec, void **image, size_t *size); #endif extern u8 _chainload_stub_start[]; extern u8 _chainload_stub_end[]; int chainload_image(void *image, size_t size, char **vars, size_t var_cnt) { u64 new_base = (u64)_base; size_t image_size = size; printf("chainload: Preparing image...\n"); // m1n1 variables for (size_t i = 0; i < var_cnt; i++) image_size += strlen(vars[i]) + 1; // pad to end payload image_size += 4; image_size = ALIGN_UP(image_size, SZ_16K); // SEPFW size_t sepfw_off = image_size; int anode = adt_path_offset(adt, "/chosen/memory-map"); if (anode < 0) { printf("chainload: /chosen/memory-map not found\n"); return -1; } u64 sepfw[2]; if (ADT_GETPROP_ARRAY(adt, anode, "SEPFW", sepfw) < 0) { printf("chainload: Failed to find SEPFW\n"); return -1; } image_size += sepfw[1]; image_size = ALIGN_UP(image_size, SZ_16K); // Bootargs size_t bootargs_off = image_size; const size_t bootargs_size = SZ_16K; image_size += bootargs_size; printf("chainload: Total image size: 0x%lx\n", image_size); size_t stub_size = _chainload_stub_end - _chainload_stub_start; void *new_image = malloc(image_size + stub_size); // Copy m1n1 memcpy(new_image, image, size); // Add vars u8 *p = new_image + size; for (size_t i = 0; i < var_cnt; i++) { size_t len = strlen(vars[i]); memcpy(p, vars[i], len); p[len] = '\n'; p += len + 1; } // Add end padding memset(p, 0, 4); // Copy SEPFW memcpy(new_image + sepfw_off, (void *)sepfw[0], sepfw[1]); // Adjust ADT SEPFW address sepfw[0] = new_base + sepfw_off; if (adt_setprop(adt, anode, "SEPFW", &sepfw, sizeof(sepfw)) < 0) { printf("chainload: Failed to set SEPFW prop\n"); free(new_image); return -1; } // Copy bootargs struct boot_args *new_boot_args = new_image + bootargs_off; *new_boot_args = cur_boot_args; new_boot_args->top_of_kernel_data = new_base + image_size; // Copy chainload stub void *stub = new_image + image_size; memcpy(stub, _chainload_stub_start, stub_size); dc_cvau_range(stub, stub_size); ic_ivau_range(stub, stub_size); // Set up next stage next_stage.entry = stub; next_stage.args[0] = new_base + bootargs_off; next_stage.args[1] = (u64)new_image; next_stage.args[2] = new_base; next_stage.args[3] = image_size; next_stage.args[4] = new_base + 0x800; // m1n1 entrypoint next_stage.restore_logo = false; return 0; } #ifdef CHAINLOADING int chainload_load(const char *spec, char **vars, size_t var_cnt) { void *image; size_t size; int ret; if (!nvme_init()) { printf("chainload: NVME init failed\n"); return -1; } ret = rust_load_image(spec, &image, &size); nvme_shutdown(); if (ret < 0) return ret; return chainload_image(image, size, vars, var_cnt); } #else int chainload_load(const char *spec, char **vars, size_t var_cnt) { UNUSED(spec); UNUSED(vars); UNUSED(var_cnt); printf("Chainloading files not supported in this build!\n"); return -1; } #endif