Skip to content

Commit 81816d6

Browse files
committed
arm: boot: simple loader for xnu kernelcache
1 parent 43a4739 commit 81816d6

1 file changed

Lines changed: 88 additions & 0 deletions

File tree

‎hw/arm/boot.c‎

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include "qemu/option.h"
2626
#include "exec/address-spaces.h"
2727

28+
#include <mach-o/loader.h>
29+
2830
/* Kernel boot protocol is specified in the kernel docs
2931
* Documentation/arm/Booting and Documentation/arm64/booting.txt
3032
* 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,
919921
return size;
920922
}
921923

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+
9221001
void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
9231002
{
9241003
CPUState *cs;
9251004
int kernel_size;
9261005
int initrd_size;
9271006
int is_linux = 0;
1007+
int is_xnu = 0;
9281008
uint64_t elf_entry, elf_low_addr, elf_high_addr;
9291009
int elf_machine;
9301010
hwaddr entry;
@@ -1048,6 +1128,14 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
10481128
}
10491129
}
10501130
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+
}
10511139
if (kernel_size < 0) {
10521140
kernel_size = load_uimage_as(info->kernel_filename, &entry, NULL,
10531141
&is_linux, NULL, NULL, as);

0 commit comments

Comments
 (0)