Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
arm: boot: simple loader for xnu kernelcache
  • Loading branch information
zhuowei committed Jul 7, 2018
1 parent 43a4739 commit 81816d6
Showing 1 changed file with 88 additions and 0 deletions.
88 changes: 88 additions & 0 deletions hw/arm/boot.c
Expand Up @@ -25,6 +25,8 @@
#include "qemu/option.h"
#include "exec/address-spaces.h"

#include <mach-o/loader.h>

/* Kernel boot protocol is specified in the kernel docs
* Documentation/arm/Booting and Documentation/arm64/booting.txt
* They have different preferred image load offsets from system RAM base.
Expand Down Expand Up @@ -919,12 +921,90 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
return size;
}

static void macho_highest_lowest(struct mach_header_64* mh, uint64_t *lowaddr, uint64_t *highaddr) {
struct load_command* cmd = (struct load_command*)((uint8_t*)mh + sizeof(struct mach_header_64));
// iterate through all the segments once to find highest and lowest addresses
uint64_t low_addr_temp = ~0;
uint64_t high_addr_temp = 0;
for (unsigned int index = 0; index < mh->ncmds; index++) {
switch (cmd->cmd) {
case LC_SEGMENT_64: {
struct segment_command_64* segCmd = (struct segment_command_64*)cmd;
if (segCmd->vmaddr < low_addr_temp) {
low_addr_temp = segCmd->vmaddr;
}
if (segCmd->vmaddr + segCmd->vmsize > high_addr_temp) {
high_addr_temp = segCmd->vmaddr + segCmd->vmsize;
}
break;
}
}
cmd = (struct load_command*)((char*)cmd + cmd->cmdsize);
}
*lowaddr = low_addr_temp;
*highaddr = high_addr_temp;
}

static uint64_t arm_load_macho(struct arm_boot_info *info, uint64_t *pentry, AddressSpace *as)
{
hwaddr kernel_load_offset = 0x00000000;
hwaddr mem_base = info->loader_start;

uint8_t *data = NULL;
gsize len;
bool ret = false;
uint8_t* rom_buf = NULL;
if (!g_file_get_contents(info->kernel_filename, (char**) &data, &len, NULL)) {
goto out;
}
struct mach_header_64* mh = (struct mach_header_64*)data;
struct load_command* cmd = (struct load_command*)(data + sizeof(struct mach_header_64));
// iterate through all the segments once to find highest and lowest addresses
uint64_t pc = 0;
uint64_t low_addr_temp;
uint64_t high_addr_temp;
macho_highest_lowest(mh, &low_addr_temp, &high_addr_temp);
uint64_t rom_buf_size = high_addr_temp - low_addr_temp;
rom_buf = g_malloc0(rom_buf_size);
for (unsigned int index = 0; index < mh->ncmds; index++) {
switch (cmd->cmd) {
case LC_SEGMENT_64: {
struct segment_command_64* segCmd = (struct segment_command_64*)cmd;
memcpy(rom_buf + (segCmd->vmaddr - low_addr_temp), data + segCmd->fileoff, segCmd->filesize);
break;
}
case LC_UNIXTHREAD: {
// grab just the entry point PC
uint64_t* ptrPc = (uint64_t*)((char*)cmd + 0x110); // for arm64 only.
pc = (*ptrPc & 0x3fffffff) + mem_base + kernel_load_offset;
break;
}
}
cmd = (struct load_command*)((char*)cmd + cmd->cmdsize);
}
hwaddr rom_base = (low_addr_temp & 0x3fffffff) + mem_base + kernel_load_offset;
rom_add_blob_fixed_as("macho", rom_buf, rom_buf_size, rom_base, as);
*pentry = pc;
ret = true;
fprintf(stderr, "%llx %llx %llx\n", low_addr_temp, high_addr_temp, pc);
fprintf(stderr, "%llx\n", *(uint64_t*)rom_buf);
out:
if (data) {
g_free(data);
}
if (rom_buf) {
g_free(rom_buf);
}
return ret? high_addr_temp - low_addr_temp : -1;
}

void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
{
CPUState *cs;
int kernel_size;
int initrd_size;
int is_linux = 0;
int is_xnu = 0;
uint64_t elf_entry, elf_low_addr, elf_high_addr;
int elf_machine;
hwaddr entry;
Expand Down Expand Up @@ -1048,6 +1128,14 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
}
}
entry = elf_entry;
if (kernel_size < 0) {
// try a MachO kernel.
kernel_size = arm_load_macho(info, &entry, as);
if (kernel_size >= 0) {
fprintf(stderr, "xnu\n");
is_xnu = 1;
}
}
if (kernel_size < 0) {
kernel_size = load_uimage_as(info->kernel_filename, &entry, NULL,
&is_linux, NULL, NULL, as);
Expand Down

0 comments on commit 81816d6

Please sign in to comment.