|
25 | 25 | #include "qemu/option.h" |
26 | 26 | #include "exec/address-spaces.h" |
27 | 27 |
|
| 28 | +#include <mach-o/loader.h> |
| 29 | + |
28 | 30 | /* Kernel boot protocol is specified in the kernel docs |
29 | 31 | * Documentation/arm/Booting and Documentation/arm64/booting.txt |
30 | 32 | * They have different preferred image load offsets from system RAM base. |
@@ -919,12 +921,90 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base, |
919 | 921 | return size; |
920 | 922 | } |
921 | 923 |
|
| 924 | +static void macho_highest_lowest(struct mach_header_64* mh, uint64_t *lowaddr, uint64_t *highaddr) { |
| 925 | + struct load_command* cmd = (struct load_command*)((uint8_t*)mh + sizeof(struct mach_header_64)); |
| 926 | + // iterate through all the segments once to find highest and lowest addresses |
| 927 | + uint64_t low_addr_temp = ~0; |
| 928 | + uint64_t high_addr_temp = 0; |
| 929 | + for (unsigned int index = 0; index < mh->ncmds; index++) { |
| 930 | + switch (cmd->cmd) { |
| 931 | + case LC_SEGMENT_64: { |
| 932 | + struct segment_command_64* segCmd = (struct segment_command_64*)cmd; |
| 933 | + if (segCmd->vmaddr < low_addr_temp) { |
| 934 | + low_addr_temp = segCmd->vmaddr; |
| 935 | + } |
| 936 | + if (segCmd->vmaddr + segCmd->vmsize > high_addr_temp) { |
| 937 | + high_addr_temp = segCmd->vmaddr + segCmd->vmsize; |
| 938 | + } |
| 939 | + break; |
| 940 | + } |
| 941 | + } |
| 942 | + cmd = (struct load_command*)((char*)cmd + cmd->cmdsize); |
| 943 | + } |
| 944 | + *lowaddr = low_addr_temp; |
| 945 | + *highaddr = high_addr_temp; |
| 946 | +} |
| 947 | + |
| 948 | +static uint64_t arm_load_macho(struct arm_boot_info *info, uint64_t *pentry, AddressSpace *as) |
| 949 | +{ |
| 950 | + hwaddr kernel_load_offset = 0x00000000; |
| 951 | + hwaddr mem_base = info->loader_start; |
| 952 | + |
| 953 | + uint8_t *data = NULL; |
| 954 | + gsize len; |
| 955 | + bool ret = false; |
| 956 | + uint8_t* rom_buf = NULL; |
| 957 | + if (!g_file_get_contents(info->kernel_filename, (char**) &data, &len, NULL)) { |
| 958 | + goto out; |
| 959 | + } |
| 960 | + struct mach_header_64* mh = (struct mach_header_64*)data; |
| 961 | + struct load_command* cmd = (struct load_command*)(data + sizeof(struct mach_header_64)); |
| 962 | + // iterate through all the segments once to find highest and lowest addresses |
| 963 | + uint64_t pc = 0; |
| 964 | + uint64_t low_addr_temp; |
| 965 | + uint64_t high_addr_temp; |
| 966 | + macho_highest_lowest(mh, &low_addr_temp, &high_addr_temp); |
| 967 | + uint64_t rom_buf_size = high_addr_temp - low_addr_temp; |
| 968 | + rom_buf = g_malloc0(rom_buf_size); |
| 969 | + for (unsigned int index = 0; index < mh->ncmds; index++) { |
| 970 | + switch (cmd->cmd) { |
| 971 | + case LC_SEGMENT_64: { |
| 972 | + struct segment_command_64* segCmd = (struct segment_command_64*)cmd; |
| 973 | + memcpy(rom_buf + (segCmd->vmaddr - low_addr_temp), data + segCmd->fileoff, segCmd->filesize); |
| 974 | + break; |
| 975 | + } |
| 976 | + case LC_UNIXTHREAD: { |
| 977 | + // grab just the entry point PC |
| 978 | + uint64_t* ptrPc = (uint64_t*)((char*)cmd + 0x110); // for arm64 only. |
| 979 | + pc = (*ptrPc & 0x3fffffff) + mem_base + kernel_load_offset; |
| 980 | + break; |
| 981 | + } |
| 982 | + } |
| 983 | + cmd = (struct load_command*)((char*)cmd + cmd->cmdsize); |
| 984 | + } |
| 985 | + hwaddr rom_base = (low_addr_temp & 0x3fffffff) + mem_base + kernel_load_offset; |
| 986 | + rom_add_blob_fixed_as("macho", rom_buf, rom_buf_size, rom_base, as); |
| 987 | + *pentry = pc; |
| 988 | + ret = true; |
| 989 | + fprintf(stderr, "%llx %llx %llx\n", low_addr_temp, high_addr_temp, pc); |
| 990 | + fprintf(stderr, "%llx\n", *(uint64_t*)rom_buf); |
| 991 | + out: |
| 992 | + if (data) { |
| 993 | + g_free(data); |
| 994 | + } |
| 995 | + if (rom_buf) { |
| 996 | + g_free(rom_buf); |
| 997 | + } |
| 998 | + return ret? high_addr_temp - low_addr_temp : -1; |
| 999 | +} |
| 1000 | + |
922 | 1001 | void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) |
923 | 1002 | { |
924 | 1003 | CPUState *cs; |
925 | 1004 | int kernel_size; |
926 | 1005 | int initrd_size; |
927 | 1006 | int is_linux = 0; |
| 1007 | + int is_xnu = 0; |
928 | 1008 | uint64_t elf_entry, elf_low_addr, elf_high_addr; |
929 | 1009 | int elf_machine; |
930 | 1010 | hwaddr entry; |
@@ -1048,6 +1128,14 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) |
1048 | 1128 | } |
1049 | 1129 | } |
1050 | 1130 | entry = elf_entry; |
| 1131 | + if (kernel_size < 0) { |
| 1132 | + // try a MachO kernel. |
| 1133 | + kernel_size = arm_load_macho(info, &entry, as); |
| 1134 | + if (kernel_size >= 0) { |
| 1135 | + fprintf(stderr, "xnu\n"); |
| 1136 | + is_xnu = 1; |
| 1137 | + } |
| 1138 | + } |
1051 | 1139 | if (kernel_size < 0) { |
1052 | 1140 | kernel_size = load_uimage_as(info->kernel_filename, &entry, NULL, |
1053 | 1141 | &is_linux, NULL, NULL, as); |
|
0 commit comments