/* * macOS 11 Big Sur - j273 - A12Z * * Copyright (c) 2019 Jonathan Afek * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu-common.h" #include "hw/arm/boot.h" #include "exec/address-spaces.h" #include "hw/misc/unimp.h" #include "sysemu/sysemu.h" #include "sysemu/reset.h" #include "qemu/error-report.h" #include "hw/platform-bus.h" #include "hw/arm/j273_macos11.h" #include "hw/arm/exynos4210.h" #include "hw/arm/guest-services/general.h" #define J273_SECURE_RAM_SIZE (0x100000) #define J273_PHYS_BASE (0x40000000) //compiled nop instruction: mov x0, x0 #define NOP_INST (0xaa0003e0) #define RET_INST (0xd65f03c0) // *NEW* #define MOV_W0_01_INST (0x52800020) #define CMP_X9_x9_INST (0xeb09013f) //compiled instruction: mov w7, #0 #define W7_ZERO_INST (0x52800007) #define W10_ZERO_INST (0x5280000a) #define W23_ZERO_INST (0x52800017) #define ORR_X0_2_INST (0xb27f0000) // *NEW* //hook the kernel to execute our "driver" code in this function //after things are already running in the kernel but the root mount is not //yet mounted. //We chose this place in the beginning of ubc_init() inlined in bsd_init() //because enough things are up and running for our driver to properly setup, //This means that global IOKIT locks and dictionaries are already initialized //and in general, the IOKIT system is already initialized. //We are now able to initialize our driver and attach it to an existing //IOReg object. //On the other hand, no mounting of any FS happened yet so we have a chance //for our block device driver to present a new block device that will be //mounted on the root mount. //We need to choose the hook location carefully. //We need 3 instructions in a row that we overwrite that are not location //dependant (such as adr, adrp and branching) as we are going to execute //them elsewhere. //We also need a register to use as a scratch register that its value is //disregarded right after the hook and does not affect anything. #define UBC_INIT_VADDR_16B92 (0xfffffff0073dec10) #define J273_CPREG_FUNCS(name) \ static uint64_t j273_cpreg_read_##name(CPUARMState *env, \ const ARMCPRegInfo *ri) \ { \ J273MachineState *nms = (J273MachineState *)ri->opaque; \ return nms->J273_CPREG_VAR_NAME(name); \ } \ static void j273_cpreg_write_##name(CPUARMState *env, const ARMCPRegInfo *ri, \ uint64_t value) \ { \ J273MachineState *nms = (J273MachineState *)ri->opaque; \ nms->J273_CPREG_VAR_NAME(name) = value; \ } #define J273_CPREG_DEF(p_name, p_op0, p_op1, p_crn, p_crm, p_op2, p_access) \ { .cp = CP_REG_ARM64_SYSREG_CP, \ .name = #p_name, .opc0 = p_op0, .crn = p_crn, .crm = p_crm, \ .opc1 = p_op1, .opc2 = p_op2, .access = p_access, .type = ARM_CP_IO, \ .state = ARM_CP_STATE_AA64, .readfn = j273_cpreg_read_##p_name, \ .writefn = j273_cpreg_write_##p_name } #define ENABLE_EL2_REGS J273_CPREG_FUNCS(ARM64_REG_EHID1) J273_CPREG_FUNCS(ARM64_REG_EHID10) J273_CPREG_FUNCS(ARM64_REG_EHID4) J273_CPREG_FUNCS(ARM64_REG_HID11) J273_CPREG_FUNCS(ARM64_REG_HID3) J273_CPREG_FUNCS(ARM64_REG_HID5) J273_CPREG_FUNCS(ARM64_REG_HID4) J273_CPREG_FUNCS(ARM64_REG_HID8) J273_CPREG_FUNCS(ARM64_REG_HID7) J273_CPREG_FUNCS(ARM64_REG_LSU_ERR_STS) J273_CPREG_FUNCS(PMC0) J273_CPREG_FUNCS(PMC1) J273_CPREG_FUNCS(PMCR1) J273_CPREG_FUNCS(PMSR) J273_CPREG_FUNCS(L2ACTLR_EL1) #ifdef ENABLE_EL2_REGS J273_CPREG_FUNCS(ARM64_REG_MIGSTS_EL1); J273_CPREG_FUNCS(ARM64_REG_KERNELKEYLO_EL1); J273_CPREG_FUNCS(ARM64_REG_KERNELKEYHI_EL1); J273_CPREG_FUNCS(ARM64_REG_VMSA_LOCK_EL1); J273_CPREG_FUNCS(APRR_EL0); J273_CPREG_FUNCS(APRR_EL1); J273_CPREG_FUNCS(CTRR_LOCK); J273_CPREG_FUNCS(CTRR_A_LWR_EL1); J273_CPREG_FUNCS(CTRR_A_UPR_EL1); J273_CPREG_FUNCS(CTRR_CTL_EL1); J273_CPREG_FUNCS(APRR_MASK_EN_EL1); J273_CPREG_FUNCS(APRR_MASK_EL0); J273_CPREG_FUNCS(ACC_CTRR_A_LWR_EL2); J273_CPREG_FUNCS(ACC_CTRR_A_UPR_EL2); J273_CPREG_FUNCS(ACC_CTRR_CTL_EL2); J273_CPREG_FUNCS(ACC_CTRR_LOCK_EL2); J273_CPREG_FUNCS(ARM64_REG_CYC_CFG); J273_CPREG_FUNCS(ARM64_REG_CYC_OVRD); J273_CPREG_FUNCS(IPI_SR); J273_CPREG_FUNCS(UPMCR0); J273_CPREG_FUNCS(UPMPCM); #endif static const ARMCPRegInfo j273_cp_reginfo_kvm[] = { // Apple-specific registers J273_CPREG_DEF(ARM64_REG_EHID1, 3, 0, 15, 3, 1, PL1_RW), J273_CPREG_DEF(ARM64_REG_EHID10, 3, 0, 15, 10, 1, PL1_RW), J273_CPREG_DEF(ARM64_REG_EHID4, 3, 0, 15, 4, 1, PL1_RW), J273_CPREG_DEF(ARM64_REG_HID11, 3, 0, 15, 13, 0, PL1_RW), J273_CPREG_DEF(ARM64_REG_HID3, 3, 0, 15, 3, 0, PL1_RW), J273_CPREG_DEF(ARM64_REG_HID4, 3, 0, 15, 4, 0, PL1_RW), J273_CPREG_DEF(ARM64_REG_HID5, 3, 0, 15, 5, 0, PL1_RW), J273_CPREG_DEF(ARM64_REG_HID7, 3, 0, 15, 7, 0, PL1_RW), J273_CPREG_DEF(ARM64_REG_HID8, 3, 0, 15, 8, 0, PL1_RW), J273_CPREG_DEF(ARM64_REG_LSU_ERR_STS, 3, 3, 15, 0, 0, PL1_RW), J273_CPREG_DEF(PMC0, 3, 2, 15, 0, 0, PL1_RW), J273_CPREG_DEF(PMC1, 3, 2, 15, 1, 0, PL1_RW), J273_CPREG_DEF(PMCR1, 3, 1, 15, 1, 0, PL1_RW), J273_CPREG_DEF(PMSR, 3, 1, 15, 13, 0, PL1_RW), J273_CPREG_DEF(L2ACTLR_EL1, 3, 1, 15, 0, 0, PL1_RW), #ifdef ENABLE_EL2_REGS J273_CPREG_DEF(ARM64_REG_MIGSTS_EL1, 3, 4, 15, 0, 4, PL1_RW), J273_CPREG_DEF(ARM64_REG_KERNELKEYLO_EL1, 3, 4, 15, 1, 0, PL1_RW), J273_CPREG_DEF(ARM64_REG_KERNELKEYHI_EL1, 3, 4, 15, 1, 1, PL1_RW), J273_CPREG_DEF(ARM64_REG_VMSA_LOCK_EL1, 3, 4, 15, 1, 2, PL1_RW), J273_CPREG_DEF(APRR_EL0, 3, 4, 15, 2, 0, PL1_RW), J273_CPREG_DEF(APRR_EL1, 3, 4, 15, 2, 1, PL1_RW), J273_CPREG_DEF(CTRR_LOCK, 3, 4, 15, 2, 2, PL1_RW), J273_CPREG_DEF(CTRR_A_LWR_EL1, 3, 4, 15, 2, 3, PL1_RW), J273_CPREG_DEF(CTRR_A_UPR_EL1, 3, 4, 15, 2, 4, PL1_RW), J273_CPREG_DEF(CTRR_CTL_EL1, 3, 4, 15, 2, 5, PL1_RW), J273_CPREG_DEF(APRR_MASK_EN_EL1, 3, 4, 15, 2, 6, PL1_RW), J273_CPREG_DEF(APRR_MASK_EL0, 3, 4, 15, 2, 7, PL1_RW), J273_CPREG_DEF(ACC_CTRR_A_LWR_EL2, 3, 4, 15, 11, 0, PL1_RW), J273_CPREG_DEF(ACC_CTRR_A_UPR_EL2, 3, 4, 15, 11, 1, PL1_RW), J273_CPREG_DEF(ACC_CTRR_CTL_EL2, 3, 4, 15, 11, 4, PL1_RW), J273_CPREG_DEF(ACC_CTRR_LOCK_EL2, 3, 4, 15, 11, 5, PL1_RW), J273_CPREG_DEF(ARM64_REG_CYC_CFG, 3, 5, 15, 4, 0, PL1_RW), J273_CPREG_DEF(ARM64_REG_CYC_OVRD, 3, 5, 15, 5, 0, PL1_RW), J273_CPREG_DEF(IPI_SR, 3, 5, 15, 1, 1, PL1_RW), J273_CPREG_DEF(UPMCR0, 3, 7, 15, 0, 4, PL1_RW), J273_CPREG_DEF(UPMPCM, 3, 7, 15, 5, 4, PL1_RW), #endif // Aleph-specific registers for communicating with QEMU // REG_QEMU_CALL: { .cp = CP_REG_ARM64_SYSREG_CP, .name = "REG_QEMU_CALL", .opc0 = 3, .opc1 = 3, .crn = 15, .crm = 15, .opc2 = 0, .access = PL0_RW, .type = ARM_CP_IO, .state = ARM_CP_STATE_AA64, .readfn = qemu_call_status, .writefn = qemu_call }, REGINFO_SENTINEL, }; // This is the same as the array for kvm, but without // the L2ACTLR_EL1, which is already defined in TCG. // Duplicating this list isn't a perfect solution, // but it's quick and reliable. static const ARMCPRegInfo j273_cp_reginfo_tcg[] = { // Apple-specific registers J273_CPREG_DEF(ARM64_REG_EHID1, 3, 0, 15, 3, 1, PL1_RW), J273_CPREG_DEF(ARM64_REG_EHID10, 3, 0, 15, 10, 1, PL1_RW), J273_CPREG_DEF(ARM64_REG_EHID4, 3, 0, 15, 4, 1, PL1_RW), J273_CPREG_DEF(ARM64_REG_HID11, 3, 0, 15, 13, 0, PL1_RW), J273_CPREG_DEF(ARM64_REG_HID3, 3, 0, 15, 3, 0, PL1_RW), J273_CPREG_DEF(ARM64_REG_HID5, 3, 0, 15, 5, 0, PL1_RW), J273_CPREG_DEF(ARM64_REG_HID4, 3, 0, 15, 4, 0, PL1_RW), J273_CPREG_DEF(ARM64_REG_HID8, 3, 0, 15, 8, 0, PL1_RW), J273_CPREG_DEF(ARM64_REG_HID7, 3, 0, 15, 7, 0, PL1_RW), J273_CPREG_DEF(ARM64_REG_LSU_ERR_STS, 3, 3, 15, 0, 0, PL1_RW), J273_CPREG_DEF(PMC0, 3, 2, 15, 0, 0, PL1_RW), J273_CPREG_DEF(PMC1, 3, 2, 15, 1, 0, PL1_RW), J273_CPREG_DEF(PMCR1, 3, 1, 15, 1, 0, PL1_RW), J273_CPREG_DEF(PMSR, 3, 1, 15, 13, 0, PL1_RW), #ifdef ENABLE_EL2_REGS J273_CPREG_DEF(ARM64_REG_MIGSTS_EL1, 3, 4, 15, 0, 4, PL1_RW), J273_CPREG_DEF(ARM64_REG_KERNELKEYLO_EL1, 3, 4, 15, 1, 0, PL1_RW), J273_CPREG_DEF(ARM64_REG_KERNELKEYHI_EL1, 3, 4, 15, 1, 1, PL1_RW), J273_CPREG_DEF(ARM64_REG_VMSA_LOCK_EL1, 3, 4, 15, 1, 2, PL1_RW), J273_CPREG_DEF(APRR_EL0, 3, 4, 15, 2, 0, PL1_RW), J273_CPREG_DEF(APRR_EL1, 3, 4, 15, 2, 1, PL1_RW), J273_CPREG_DEF(CTRR_LOCK, 3, 4, 15, 2, 2, PL1_RW), J273_CPREG_DEF(CTRR_A_LWR_EL1, 3, 4, 15, 2, 3, PL1_RW), J273_CPREG_DEF(CTRR_A_UPR_EL1, 3, 4, 15, 2, 4, PL1_RW), J273_CPREG_DEF(CTRR_CTL_EL1, 3, 4, 15, 2, 5, PL1_RW), J273_CPREG_DEF(APRR_MASK_EN_EL1, 3, 4, 15, 2, 6, PL1_RW), J273_CPREG_DEF(APRR_MASK_EL0, 3, 4, 15, 2, 7, PL1_RW), J273_CPREG_DEF(ACC_CTRR_A_LWR_EL2, 3, 4, 15, 11, 0, PL1_RW), J273_CPREG_DEF(ACC_CTRR_A_UPR_EL2, 3, 4, 15, 11, 1, PL1_RW), J273_CPREG_DEF(ACC_CTRR_CTL_EL2, 3, 4, 15, 11, 4, PL1_RW), J273_CPREG_DEF(ACC_CTRR_LOCK_EL2, 3, 4, 15, 11, 5, PL1_RW), J273_CPREG_DEF(ARM64_REG_CYC_CFG, 3, 5, 15, 4, 0, PL1_RW), J273_CPREG_DEF(ARM64_REG_CYC_OVRD, 3, 5, 15, 5, 0, PL1_RW), J273_CPREG_DEF(IPI_SR, 3, 5, 15, 1, 1, PL1_RW), J273_CPREG_DEF(UPMCR0, 3, 7, 15, 0, 4, PL1_RW), J273_CPREG_DEF(UPMPCM, 3, 7, 15, 5, 4, PL1_RW), #endif // Aleph-specific registers for communicating with QEMU // REG_QEMU_CALL: { .cp = CP_REG_ARM64_SYSREG_CP, .name = "REG_QEMU_CALL", .opc0 = 3, .opc1 = 3, .crn = 15, .crm = 15, .opc2 = 0, .access = PL0_RW, .type = ARM_CP_IO, .state = ARM_CP_STATE_AA64, .readfn = qemu_call_status, .writefn = qemu_call }, REGINFO_SENTINEL, }; static uint32_t g_nop_inst = NOP_INST; static uint32_t g_ret_inst = RET_INST; static uint32_t g_mov_w0_01_inst = MOV_W0_01_INST; static uint32_t g_compare_true_inst = CMP_X9_x9_INST; static uint32_t g_w7_zero_inst = W7_ZERO_INST; static uint32_t g_w10_zero_inst = W10_ZERO_INST; static uint32_t g_w23_zero_inst = W23_ZERO_INST; static uint32_t g_orr_x0_2_inst = ORR_X0_2_INST; static uint32_t g_set_cpacr_and_branch_inst[] = { // 91400c21 add x1, x1, 3, lsl 12 # x1 = x1 + 0x3000 // d378dc21 lsl x1, x1, 8 # x1 = x1 * 0x100 (x1 = 0x300000) // d5181041 msr cpacr_el1, x1 # cpacr_el1 = x1 (enable FP) // d2800041 mov x1, #2 // d51cf081 mov apctl_el1, x1 // aa1f03e1 mov x1, xzr # x1 = 0 // 14000eb5 b 0x1fc0 # branch to regular start 0x91400c21, 0xd378dc21, 0xd5181041, 0xd2800041, 0xd51cf081, 0xaa1f03e1, 0x14000eb5 }; static uint32_t g_bzero_branch_unconditionally_inst = 0x14000039; static uint32_t g_qemu_call = 0xd51bff1f; typedef struct darwin_patch { uint64_t addr; uint32_t *inst; uint32_t len; } darwin_patch; typedef struct darwin_kernel_patch { const char *darwin_str; uint32_t num_patches; struct darwin_patch patches[]; } darwin_kernel_patch; // Patch is a single instruction #define DARWIN_PATCH(offset, instruction) \ { .addr = offset, .inst = &instruction, .len = sizeof(instruction) } // Patch is an array of instructions #define DARWIN_PATCH_A(offset, instruction) \ { .addr = offset, .inst = instruction, .len = sizeof(instruction) } struct darwin_kernel_patch darwin_patches_20A5364e = { .darwin_str = "Darwin Kernel Version 20.0.0: Sun Jun 14 21:36:36 PDT 2020; " "root:Bridge_xnu-7090.111.5.2~1/RELEASE_ARM64_T8020", .num_patches = 6, .patches = { DARWIN_PATCH_A(0xfffffe00079f0580, g_set_cpacr_and_branch_inst), // initial branch DARWIN_PATCH(0xfffffe00079e49fc, g_bzero_branch_unconditionally_inst), // bzero conditional branch DARWIN_PATCH(0xfffffe0007f8330c, g_w23_zero_inst), // parse_machfile slide set instruction DARWIN_PATCH(0xfffffe0007a5b47c, g_qemu_call), // notify kernel task pointer DARWIN_PATCH(0xfffffe0008af5e3c, g_mov_w0_01_inst), // core trust check DARWIN_PATCH(0xfffffe0007f83108, g_nop_inst), // load_machfile: disable IMGPF_NOJOP } }; struct darwin_kernel_patch darwin_patches_20B5012d = { .darwin_str = "Darwin Kernel Version 20.1.0: Sat Oct 24 21:20:41 PDT 2020; " "root:xnu-7195.50.3.201.1~1/RELEASE_ARM64_T8020", .num_patches = 6, .patches = { DARWIN_PATCH_A(0xfffffe0007ab0580, g_set_cpacr_and_branch_inst), // initial branch DARWIN_PATCH(0xfffffe0007aa49fc, g_bzero_branch_unconditionally_inst), // bzero conditional branch DARWIN_PATCH(0xfffffe0008056168, g_w10_zero_inst), // parse_machfile slide set instruction DARWIN_PATCH(0xfffffe0007b1f4d8, g_qemu_call), // notify kernel task pointer DARWIN_PATCH(0xfffffe0008c96538, g_mov_w0_01_inst), // core trust check DARWIN_PATCH(0xfffffe0008055f64, g_nop_inst), // load_machfile: disable IMGPF_NOJOP } }; struct darwin_kernel_patch darwin_patches_20C69 = { .darwin_str = "Darwin Kernel Version 20.2.0: Wed Dec 2 20:40:22 PST 2020; " "root:xnu-7195.60.75~1/RELEASE_ARM64_T8020", .num_patches = 5, .patches = { DARWIN_PATCH_A(0xfffffe0007ac4580, g_set_cpacr_and_branch_inst), // initial branch DARWIN_PATCH(0xfffffe0007ab8a3c, g_bzero_branch_unconditionally_inst), // bzero conditional branch DARWIN_PATCH(0xfffffe000806b438, g_w10_zero_inst), // parse_machfile slide set instruction DARWIN_PATCH(0xfffffe0008cb6538, g_mov_w0_01_inst), // core trust check DARWIN_PATCH(0xfffffe000806b234, g_nop_inst), // load_machfile: disable IMGPF_NOJOP } }; struct darwin_kernel_patch *darwin_patches[] = { &darwin_patches_20A5364e, &darwin_patches_20B5012d, &darwin_patches_20C69, }; static void j273_add_cpregs(J273MachineState *nms) { ARMCPU *cpu = nms->cpu; nms->J273_CPREG_VAR_NAME(ARM64_REG_EHID1) = 0; nms->J273_CPREG_VAR_NAME(ARM64_REG_EHID10) = 0; nms->J273_CPREG_VAR_NAME(ARM64_REG_EHID4) = 0; nms->J273_CPREG_VAR_NAME(ARM64_REG_HID11) = 0; nms->J273_CPREG_VAR_NAME(ARM64_REG_HID3) = 0; nms->J273_CPREG_VAR_NAME(ARM64_REG_HID5) = 0; nms->J273_CPREG_VAR_NAME(ARM64_REG_HID8) = 0; nms->J273_CPREG_VAR_NAME(ARM64_REG_HID7) = 0; nms->J273_CPREG_VAR_NAME(ARM64_REG_LSU_ERR_STS) = 0; nms->J273_CPREG_VAR_NAME(PMC0) = 0; nms->J273_CPREG_VAR_NAME(PMC1) = 0; nms->J273_CPREG_VAR_NAME(PMCR1) = 0; nms->J273_CPREG_VAR_NAME(PMSR) = 0; nms->J273_CPREG_VAR_NAME(L2ACTLR_EL1) = 0; #ifdef ENABLE_EL2_REGS nms->J273_CPREG_VAR_NAME(ARM64_REG_MIGSTS_EL1) = 0; nms->J273_CPREG_VAR_NAME(ARM64_REG_KERNELKEYLO_EL1) = 0; nms->J273_CPREG_VAR_NAME(ARM64_REG_KERNELKEYHI_EL1) = 0; nms->J273_CPREG_VAR_NAME(ARM64_REG_VMSA_LOCK_EL1) = 0; nms->J273_CPREG_VAR_NAME(APRR_EL0) = 0; nms->J273_CPREG_VAR_NAME(APRR_EL1) = 0; nms->J273_CPREG_VAR_NAME(CTRR_LOCK) = 0; nms->J273_CPREG_VAR_NAME(CTRR_A_LWR_EL1) = 0; nms->J273_CPREG_VAR_NAME(CTRR_A_UPR_EL1) = 0; nms->J273_CPREG_VAR_NAME(CTRR_CTL_EL1) = 0; nms->J273_CPREG_VAR_NAME(APRR_MASK_EN_EL1) = 0; nms->J273_CPREG_VAR_NAME(APRR_MASK_EL0) = 0; nms->J273_CPREG_VAR_NAME(ACC_CTRR_A_LWR_EL2) = 0; nms->J273_CPREG_VAR_NAME(ACC_CTRR_A_UPR_EL2) = 0; nms->J273_CPREG_VAR_NAME(ACC_CTRR_CTL_EL2) = 0; nms->J273_CPREG_VAR_NAME(ACC_CTRR_LOCK_EL2) = 0; nms->J273_CPREG_VAR_NAME(ARM64_REG_CYC_CFG) = 0; nms->J273_CPREG_VAR_NAME(ARM64_REG_CYC_OVRD) = 0; nms->J273_CPREG_VAR_NAME(UPMCR0) = 0; nms->J273_CPREG_VAR_NAME(UPMPCM) = 0; #endif if (kvm_enabled()) { define_arm_cp_regs_with_opaque(cpu, j273_cp_reginfo_kvm, nms); } else { define_arm_cp_regs_with_opaque(cpu, j273_cp_reginfo_tcg, nms); } } static void j273_create_s3c_uart(const J273MachineState *nms, Chardev *chr) { qemu_irq irq; DeviceState *d; SysBusDevice *s; hwaddr base = nms->uart_mmio_pa; //hack for now. create a device that is not used just to have a dummy //unused interrupt d = qdev_new(TYPE_PLATFORM_BUS_DEVICE); s = SYS_BUS_DEVICE(d); sysbus_init_irq(s, &irq); //pass a dummy irq as we don't need nor want interrupts for this UART DeviceState *dev = exynos4210_uart_create(base, 256, 0, chr, irq); if (!dev) { abort(); } } static void j273_patch_kernel(AddressSpace *nsas, char *darwin_ver) { bool found = false; darwin_patch *patch; darwin_kernel_patch *kernel_patch; for (int i = 0; i < sizeof(darwin_patches) / sizeof(uint64_t); i++) { kernel_patch = darwin_patches[i]; if (!strncmp(darwin_ver, kernel_patch->darwin_str, 1024)) { for (int a = 0; a < kernel_patch->num_patches; a++) { patch = &kernel_patch->patches[a]; address_space_rw(nsas, vtop_static(patch->addr), MEMTXATTRS_UNSPECIFIED, (uint8_t *)patch->inst, patch->len, 1); } found = true; } } if (found == false) { printf("No support for %s\n", darwin_ver); abort(); } } static void j273_ns_memory_setup(MachineState *machine, MemoryRegion *sysmem, AddressSpace *nsas) { uint64_t used_ram_for_blobs = 0; hwaddr kernel_low; hwaddr kernel_high; hwaddr virt_base; hwaddr dtb_va; uint64_t dtb_size; hwaddr kbootargs_pa; hwaddr top_of_kernel_data_pa; hwaddr mem_size; hwaddr remaining_mem_size; hwaddr allocated_ram_pa; hwaddr phys_ptr; hwaddr phys_pc; hwaddr ramfb_pa = 0; video_boot_args v_bootargs = {0}; J273MachineState *nms = J273_MACHINE(machine); char darwin_ver[1024]; //setup the memory layout: //At the beginning of the non-secure ram we have the raw kernel file. //After that we have the static trust cache. //After that we have all the kernel sections. //After that we have ramdosk //After that we have the device tree //After that we have the kernel boot args //After that we have the rest of the RAM macho_file_highest_lowest_base(nms->kernel_filename, J273_PHYS_BASE, &virt_base, &kernel_low, &kernel_high); g_virt_base = virt_base; g_phys_base = J273_PHYS_BASE; phys_ptr = J273_PHYS_BASE; //now account for the loaded kernel arm_load_macho(nms->kernel_filename, nsas, sysmem, "kernel.j273", J273_PHYS_BASE, virt_base, kernel_low, kernel_high, &phys_pc, darwin_ver); nms->kpc_pa = phys_pc; used_ram_for_blobs += (align_64k_high(kernel_high) - kernel_low); j273_patch_kernel(nsas, darwin_ver); phys_ptr = align_64k_high(vtop_static(kernel_high)); //now account for the ramdisk nms->ramdisk_file_dev.pa = 0; hwaddr ramdisk_size = 0; if (0 != nms->ramdisk_filename[0]) { nms->ramdisk_file_dev.pa = phys_ptr; macho_map_raw_file(nms->ramdisk_filename, nsas, sysmem, "ramdisk_raw_file.j273", nms->ramdisk_file_dev.pa, &nms->ramdisk_file_dev.size); ramdisk_size = nms->ramdisk_file_dev.size; phys_ptr += align_64k_high(nms->ramdisk_file_dev.size); } //now account for device tree macho_load_dtb(nms->dtb_filename, nsas, sysmem, "dtb.j273", phys_ptr, &dtb_size, nms->ramdisk_file_dev.pa, ramdisk_size, &nms->uart_mmio_pa); dtb_va = ptov_static(phys_ptr); phys_ptr += align_64k_high(dtb_size); used_ram_for_blobs += align_64k_high(dtb_size); //now account for kernel boot args used_ram_for_blobs += align_64k_high(sizeof(struct xnu_arm64_boot_args)); kbootargs_pa = phys_ptr; nms->kbootargs_pa = kbootargs_pa; phys_ptr += align_64k_high(sizeof(struct xnu_arm64_boot_args)); nms->extra_data_pa = phys_ptr; allocated_ram_pa = phys_ptr; if (nms->use_ramfb){ ramfb_pa = ((hwaddr)&((AllocatedData *)nms->extra_data_pa)->ramfb[0]); xnu_define_ramfb_device(nsas,ramfb_pa); xnu_get_video_bootargs(&v_bootargs, ramfb_pa); } phys_ptr += align_64k_high(sizeof(AllocatedData)); top_of_kernel_data_pa = phys_ptr; remaining_mem_size = machine->ram_size - used_ram_for_blobs; mem_size = allocated_ram_pa - J273_PHYS_BASE + remaining_mem_size; macho_setup_bootargs("k_bootargs.j273", nsas, sysmem, kbootargs_pa, virt_base, J273_PHYS_BASE, mem_size, top_of_kernel_data_pa, dtb_va, dtb_size, v_bootargs, nms->kern_args); allocate_ram(sysmem, "j273.ram", allocated_ram_pa, remaining_mem_size); } static void j273_memory_setup(MachineState *machine, MemoryRegion *sysmem, MemoryRegion *secure_sysmem, AddressSpace *nsas) { j273_ns_memory_setup(machine, sysmem, nsas); } static void j273_cpu_setup(MachineState *machine, MemoryRegion **sysmem, MemoryRegion **secure_sysmem, ARMCPU **cpu, AddressSpace **nsas) { Object *cpuobj = object_new(machine->cpu_type); *cpu = ARM_CPU(cpuobj); CPUState *cs = CPU(*cpu); *sysmem = get_system_memory(); object_property_set_link(cpuobj, "memory", OBJECT(*sysmem), &error_abort); //set secure monitor to false object_property_set_bool(cpuobj, "has_el3", false, NULL); object_property_set_bool(cpuobj, "has_el2", false, NULL); object_property_set_bool(cpuobj, "realized", true, &error_fatal); *nsas = cpu_get_address_space(cs, ARMASIdx_NS); object_unref(cpuobj); //currently support only a single CPU and thus //use no interrupt controller and wire IRQs from devices directly to the CPU } static void j273_bootargs_setup(MachineState *machine) { J273MachineState *nms = J273_MACHINE(machine); nms->bootinfo.firmware_loaded = true; } static void j273_cpu_reset(void *opaque) { J273MachineState *nms = J273_MACHINE((MachineState *)opaque); ARMCPU *cpu = nms->cpu; CPUState *cs = CPU(cpu); CPUARMState *env = &cpu->env; cpu_reset(cs); env->xregs[0] = nms->kbootargs_pa; env->pc = nms->kpc_pa; } //hooks arg is expected like this: //"hookfilepath@va@scratch_reg#hookfilepath@va@scratch_reg#..." static void j273_machine_init_hook_funcs(J273MachineState *nms, AddressSpace *nsas) { AllocatedData *allocated_data = (AllocatedData *)nms->extra_data_pa; uint64_t i = 0; char *orig_pos = NULL; size_t orig_len = 0; char *pos = NULL; char *next_pos = NULL; size_t len = 0; char *elem = NULL; char *next_elem = NULL; size_t elem_len = 0; char *end; //ugly solution but a simple one for now, use this memory which is fixed at //(pa: 0x0000000049BF4C00 va: 0xFFFFFFF009BF4C00) for globals to be common //between drivers/hooks. Please adjust address if anything changes in //the layout of the memory the "boot loader" sets up uint64_t zero_var = 0; address_space_rw(nsas, (hwaddr)&allocated_data->hook_globals[0], MEMTXATTRS_UNSPECIFIED, (uint8_t *)&zero_var, sizeof(zero_var), 1); nms->hook_funcs_count = 0; pos = &nms->hook_funcs_cfg[0]; if ((NULL == pos) || (0 == strlen(pos))) { //fprintf(stderr, "no function hooks configured\n"); return; } orig_pos = pos; orig_len = strlen(pos); do { next_pos = memchr(pos, '#', strlen(pos)); if (NULL != next_pos) { len = next_pos - pos; } else { len = strlen(pos); } elem = pos; next_elem = memchr(elem, '@', len); if (NULL == next_elem) { fprintf(stderr, "hook[%lu] failed to find '@' in %s\n", i, elem); abort(); } elem_len = next_elem - elem; elem[elem_len] = 0; uint8_t *code = NULL; size_t size = 0; if (!g_file_get_contents(elem, (char **)&code, &size, NULL)) { fprintf(stderr, "hook[%lu] failed to read filepath: %s\n", i, elem); abort(); } elem += elem_len + 1; next_elem = memchr(elem, '@', len); if (NULL == next_elem) { fprintf(stderr, "hook[%lu] failed to find '@' in %s\n", i, elem); abort(); } elem_len = next_elem - elem; elem[elem_len] = 0; nms->hook_funcs[i].va = strtoull(elem, &end, 16); nms->hook_funcs[i].pa = vtop_static(nms->hook_funcs[i].va); nms->hook_funcs[i].buf_va = ptov_static((hwaddr)&allocated_data->hook_funcs_code[i][0]); nms->hook_funcs[i].buf_pa = (hwaddr)&allocated_data->hook_funcs_code[i][0]; nms->hook_funcs[i].buf_size = HOOK_CODE_ALLOC_SIZE; nms->hook_funcs[i].code = (uint8_t *)code; nms->hook_funcs[i].code_size = size; elem += elem_len + 1; if (NULL != next_pos) { elem_len = next_pos - elem; } else { elem_len = strlen(elem); } elem[elem_len] = 0; nms->hook_funcs[i].scratch_reg = (uint8_t)strtoul(elem, &end, 10); i++; pos += len + 1; } while ((NULL != pos) && (pos < (orig_pos + orig_len))); nms->hook_funcs_count = i; } static void j273_machine_init(MachineState *machine) { J273MachineState *nms = J273_MACHINE(machine); MemoryRegion *sysmem; MemoryRegion *secure_sysmem; AddressSpace *nsas; ARMCPU *cpu; CPUState *cs; DeviceState *cpudev; j273_cpu_setup(machine, &sysmem, &secure_sysmem, &cpu, &nsas); nms->cpu = cpu; j273_memory_setup(machine, sysmem, secure_sysmem, nsas); cpudev = DEVICE(cpu); cs = CPU(cpu); AllocatedData *allocated_data = (AllocatedData *)nms->extra_data_pa; if (0 != nms->driver_filename[0]) { xnu_hook_tr_setup(nsas, cpu); uint8_t *code = NULL; unsigned long size; if (!g_file_get_contents(nms->driver_filename, (char **)&code, &size, NULL)) { abort(); } nms->hook.va = UBC_INIT_VADDR_16B92; nms->hook.pa = vtop_static(UBC_INIT_VADDR_16B92); nms->hook.buf_va = ptov_static((hwaddr)&allocated_data->hook_code[0]); nms->hook.buf_pa = (hwaddr)&allocated_data->hook_code[0]; nms->hook.buf_size = HOOK_CODE_ALLOC_SIZE; nms->hook.code = (uint8_t *)code; nms->hook.code_size = size; nms->hook.scratch_reg = 2; } if (0 != nms->qc_file_0_filename[0]) { qc_file_open(0, &nms->qc_file_0_filename[0]); } if (0 != nms->qc_file_1_filename[0]) { qc_file_open(1, &nms->qc_file_1_filename[0]); } if (0 != nms->qc_file_log_filename[0]) { qc_file_open(2, &nms->qc_file_log_filename[0]); } j273_machine_init_hook_funcs(nms, nsas); j273_add_cpregs(nms); j273_create_s3c_uart(nms, serial_hd(0)); //wire timer to FIQ as expected by Apple's SoCs qdev_connect_gpio_out(cpudev, GTIMER_VIRT, qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); j273_bootargs_setup(machine); qemu_register_reset(j273_cpu_reset, nms); } static void j273_set_ramdisk_filename(Object *obj, const char *value, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); g_strlcpy(nms->ramdisk_filename, value, sizeof(nms->ramdisk_filename)); } static char *j273_get_ramdisk_filename(Object *obj, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); return g_strdup(nms->ramdisk_filename); } static void j273_set_kernel_filename(Object *obj, const char *value, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); g_strlcpy(nms->kernel_filename, value, sizeof(nms->kernel_filename)); } static char *j273_get_kernel_filename(Object *obj, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); return g_strdup(nms->kernel_filename); } static void j273_set_dtb_filename(Object *obj, const char *value, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); g_strlcpy(nms->dtb_filename, value, sizeof(nms->dtb_filename)); } static char *j273_get_dtb_filename(Object *obj, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); return g_strdup(nms->dtb_filename); } static void j273_set_kern_args(Object *obj, const char *value, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); g_strlcpy(nms->kern_args, value, sizeof(nms->kern_args)); } static char *j273_get_kern_args(Object *obj, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); return g_strdup(nms->kern_args); } static void j273_set_tunnel_port(Object *obj, const char *value, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); nms->tunnel_port = atoi(value); } static char *j273_get_tunnel_port(Object *obj, Error **errp) { char buf[128]; J273MachineState *nms = J273_MACHINE(obj); snprintf(buf, 128, "%d", nms->tunnel_port); return g_strdup(buf); } static void j273_set_hook_funcs(Object *obj, const char *value, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); g_strlcpy(nms->hook_funcs_cfg, value, sizeof(nms->hook_funcs_cfg)); } static char *j273_get_hook_funcs(Object *obj, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); return g_strdup(nms->hook_funcs_cfg); } static void j273_set_driver_filename(Object *obj, const char *value, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); g_strlcpy(nms->driver_filename, value, sizeof(nms->driver_filename)); } static char *j273_get_driver_filename(Object *obj, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); return g_strdup(nms->driver_filename); } static void j273_set_qc_file_0_filename(Object *obj, const char *value, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); g_strlcpy(nms->qc_file_0_filename, value, sizeof(nms->qc_file_0_filename)); } static char *j273_get_qc_file_0_filename(Object *obj, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); return g_strdup(nms->qc_file_0_filename); } static void j273_set_qc_file_1_filename(Object *obj, const char *value, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); g_strlcpy(nms->qc_file_1_filename, value, sizeof(nms->qc_file_1_filename)); } static char *j273_get_qc_file_1_filename(Object *obj, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); return g_strdup(nms->qc_file_1_filename); } static void j273_set_qc_file_log_filename(Object *obj, const char *value, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); g_strlcpy(nms->qc_file_log_filename, value, sizeof(nms->qc_file_log_filename)); } static char *j273_get_qc_file_log_filename(Object *obj, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); return g_strdup(nms->qc_file_log_filename); } static void j273_set_xnu_ramfb(Object *obj, const char *value, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); if (strcmp(value,"on") == 0) nms->use_ramfb = true; else { if (strcmp(value,"off") != 0) fprintf(stderr,"NOTE: the value of xnu-ramfb is not valid,\ the framebuffer will be disabled.\n"); nms->use_ramfb = false; } } static char* j273_get_xnu_ramfb(Object *obj, Error **errp) { J273MachineState *nms = J273_MACHINE(obj); if (nms->use_ramfb) return g_strdup("on"); else return g_strdup("off"); } static void j273_instance_init(Object *obj) { object_property_add_str(obj, "ramdisk-filename", j273_get_ramdisk_filename, j273_set_ramdisk_filename); object_property_set_description(obj, "ramdisk-filename", "Set the ramdisk filename to be loaded"); object_property_add_str(obj, "kernel-filename", j273_get_kernel_filename, j273_set_kernel_filename); object_property_set_description(obj, "kernel-filename", "Set the kernel filename to be loaded"); object_property_add_str(obj, "dtb-filename", j273_get_dtb_filename, j273_set_dtb_filename); object_property_set_description(obj, "dtb-filename", "Set the dev tree filename to be loaded"); object_property_add_str(obj, "kern-cmd-args", j273_get_kern_args, j273_set_kern_args); object_property_set_description(obj, "kern-cmd-args", "Set the XNU kernel cmd args"); object_property_add_str(obj, "tunnel-port", j273_get_tunnel_port, j273_set_tunnel_port); object_property_set_description(obj, "tunnel-port", "Set the port for the tunnel connection"); object_property_add_str(obj, "hook-funcs", j273_get_hook_funcs, j273_set_hook_funcs); object_property_set_description(obj, "hook-funcs", "Set the hook funcs to be loaded"); object_property_add_str(obj, "driver-filename", j273_get_driver_filename, j273_set_driver_filename); object_property_set_description(obj, "driver-filename", "Set the driver filename to be loaded"); object_property_add_str(obj, "qc-file-0-filename", j273_get_qc_file_0_filename, j273_set_qc_file_0_filename); object_property_set_description(obj, "qc-file-0-filename", "Set the qc file 0 filename to be loaded"); object_property_add_str(obj, "qc-file-1-filename", j273_get_qc_file_1_filename, j273_set_qc_file_1_filename); object_property_set_description(obj, "qc-file-1-filename", "Set the qc file 1 filename to be loaded"); object_property_add_str(obj, "qc-file-log-filename", j273_get_qc_file_log_filename, j273_set_qc_file_log_filename); object_property_set_description(obj, "qc-file-log-filename", "Set the qc file log filename to be loaded"); object_property_add_str(obj, "xnu-ramfb", j273_get_xnu_ramfb, j273_set_xnu_ramfb); object_property_set_description(obj, "xnu-ramfb", "Turn on the display framebuffer"); } static void j273_machine_class_init(ObjectClass *klass, void *data) { MachineClass *mc = MACHINE_CLASS(klass); mc->desc = "macOS Big Sur Beta 6 (j273 - A12Z)"; mc->init = j273_machine_init; mc->max_cpus = 1; //this disables the error message "Failed to query for block devices!" //when starting qemu - must keep at least one device //mc->no_sdcard = 1; mc->no_floppy = 1; mc->no_cdrom = 1; mc->no_parallel = 1; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a57"); mc->minimum_page_bits = 12; } static const TypeInfo j273_machine_info = { .name = TYPE_J273_MACHINE, .parent = TYPE_MACHINE, .instance_size = sizeof(J273MachineState), .class_size = sizeof(J273MachineClass), .class_init = j273_machine_class_init, .instance_init = j273_instance_init, }; static void j273_machine_types(void) { type_register_static(&j273_machine_info); } type_init(j273_machine_types)