historical/m0-applesillicon.git/xnu-qemu-arm64-5.1.0/hw/arm/xnu.c
2024-01-16 11:20:27 -06:00

343 lines
12 KiB
C

/*
*
* Copyright (c) 2019 Jonathan Afek <jonyafek@me.com>
*
* 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 "sysemu/sysemu.h"
#include "qemu/error-report.h"
#include "hw/arm/xnu.h"
#include "hw/loader.h"
static void allocate_and_copy(MemoryRegion *mem, AddressSpace *as,
const char *name, hwaddr pa, hwaddr size,
void *buf)
{
if (mem) {
allocate_ram(mem, name, pa, align_64k_high(size));
}
address_space_rw(as, pa, MEMTXATTRS_UNSPECIFIED, (uint8_t *)buf, size, 1);
}
void macho_load_dtb(char *filename, AddressSpace *as, MemoryRegion *mem,
const char *name, hwaddr dtb_pa, uint64_t *size,
hwaddr ramdisk_addr, hwaddr ramdisk_size,
hwaddr *uart_mmio_pa)
{
uint8_t *file_data = NULL;
unsigned long fsize;
if (g_file_get_contents(filename, (char **)&file_data, &fsize, NULL)) {
DTBNode *root = load_dtb(file_data);
//first fetch the uart mmio address
DTBNode *child = get_dtb_child_node_by_name(root, "arm-io");
if (NULL == child) {
abort();
}
DTBProp *prop = get_dtb_prop(child, "ranges");
if (NULL == prop) {
abort();
}
hwaddr *ranges = (hwaddr *)prop->value;
hwaddr soc_base_pa = ranges[1];
child = get_dtb_child_node_by_name(child, "uart0");
if (NULL == child) {
abort();
}
//make sure this node has the boot-console prop
prop = get_dtb_prop(child, "boot-console");
if (NULL == prop) {
abort();
}
prop = get_dtb_prop(child, "reg");
if (NULL == prop) {
abort();
}
hwaddr *uart_offset = (hwaddr *)prop->value;
if (NULL != uart_mmio_pa) {
*uart_mmio_pa = soc_base_pa + uart_offset[0];
}
child = get_dtb_child_node_by_name(root, "chosen");
child = get_dtb_child_node_by_name(child, "memory-map");
if (NULL == child) {
abort();
}
uint64_t memmap[2] = {0};
if ((0 != ramdisk_addr) && (0 != ramdisk_size)) {
memmap[0] = ramdisk_addr;
memmap[1] = ramdisk_size;
add_dtb_prop(child, "RAMDisk", sizeof(memmap),
(uint8_t *)&memmap[0]);
}
uint64_t size_n = get_dtb_node_buffer_size(root);
uint8_t *buf = g_malloc0(size_n);
save_dtb(buf, root);
allocate_and_copy(mem, as, name, dtb_pa, size_n, buf);
g_free(file_data);
delete_dtb_node(root);
g_free(buf);
*size = size_n;
} else {
abort();
}
}
void macho_map_raw_file(const char *filename, AddressSpace *as, MemoryRegion *mem,
const char *name, hwaddr file_pa, uint64_t *size)
{
Error *err = NULL;
MemoryRegion *mr = NULL;
struct stat file_info;
if (stat(filename, &file_info)) {
fprintf(stderr, "Couldn't get file size for mmapping. Loading into RAM.\n");
goto load_fallback;
}
mr = g_new(MemoryRegion, 1);
*size = file_info.st_size;
memory_region_init_ram_from_file(mr, NULL, name, *size & (~0xffffUL), 0, 0, filename, &err);
if (err) {
error_report_err(err);
fprintf(stderr, "Couldn't mmap file. Loading into RAM.\n");
goto load_fallback;
}
memory_region_add_subregion(mem, file_pa, mr);
return;
load_fallback:
if (mr) {
g_free(mr);
}
macho_load_raw_file(filename, as, mem, name, file_pa, size);
}
void macho_load_raw_file(const char *filename, AddressSpace *as, MemoryRegion *mem,
const char *name, hwaddr file_pa, uint64_t *size)
{
uint8_t* file_data = NULL;
unsigned long sizef;
if (g_file_get_contents(filename, (char **)&file_data, &sizef, NULL)) {
*size = sizef;
allocate_and_copy(mem, as, name, file_pa, *size, file_data);
g_free(file_data);
} else {
abort();
}
}
void macho_tz_setup_bootargs(const char *name, AddressSpace *as,
MemoryRegion *mem, hwaddr bootargs_addr,
hwaddr virt_base, hwaddr phys_base,
hwaddr mem_size, hwaddr kern_args,
hwaddr kern_entry, hwaddr kern_phys_base)
{
struct xnu_arm64_monitor_boot_args boot_args;
memset(&boot_args, 0, sizeof(boot_args));
boot_args.version = xnu_arm64_kBootArgsVersion2;
boot_args.virtBase = virt_base;
boot_args.physBase = phys_base;
boot_args.memSize = mem_size;
boot_args.kernArgs = kern_args;
boot_args.kernEntry = kern_entry;
boot_args.kernPhysBase = kern_phys_base;
boot_args.kernPhysSlide = 0;
boot_args.kernVirtSlide = 0;
allocate_and_copy(mem, as, name, bootargs_addr, sizeof(boot_args),
&boot_args);
}
void macho_setup_bootargs(const char *name, AddressSpace *as,
MemoryRegion *mem, hwaddr bootargs_pa,
hwaddr virt_base, hwaddr phys_base, hwaddr mem_size,
hwaddr top_of_kernel_data_pa, hwaddr dtb_va,
hwaddr dtb_size, video_boot_args v_bootargs,
char *kern_args)
{
struct xnu_arm64_boot_args boot_args;
memset(&boot_args, 0, sizeof(boot_args));
boot_args.Revision = xnu_arm64_kBootArgsRevision2;
boot_args.Version = xnu_arm64_kBootArgsVersion2;
boot_args.virtBase = virt_base;
boot_args.physBase = phys_base;
boot_args.memSize = mem_size;
boot_args.Video.v_baseAddr = v_bootargs.v_baseAddr;
boot_args.Video.v_depth = v_bootargs.v_depth;
boot_args.Video.v_display = v_bootargs.v_display;
boot_args.Video.v_height = v_bootargs.v_height;
boot_args.Video.v_rowBytes = v_bootargs.v_rowBytes;
boot_args.Video.v_width = v_bootargs.v_width;
boot_args.topOfKernelData = top_of_kernel_data_pa;
boot_args.deviceTreeP = dtb_va;
boot_args.deviceTreeLength = dtb_size;
boot_args.memSizeActual = 0;
if (kern_args) {
g_strlcpy(boot_args.CommandLine, kern_args,
sizeof(boot_args.CommandLine));
}
allocate_and_copy(mem, as, name, bootargs_pa, sizeof(boot_args),
&boot_args);
}
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 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 void macho_file_highest_lowest(const char *filename, hwaddr *lowest,
hwaddr *highest)
{
gsize len;
uint8_t *data = NULL;
if (!g_file_get_contents(filename, (char **)&data, &len, NULL)) {
abort();
}
struct mach_header_64* mh = (struct mach_header_64*)data;
macho_highest_lowest(mh, lowest, highest);
g_free(data);
}
void macho_file_highest_lowest_base(const char *filename, hwaddr phys_base,
hwaddr *virt_base, hwaddr *lowest,
hwaddr *highest)
{
uint8_t high_Low_dif_bit_index;
uint8_t phys_base_non_zero_bit_index;
hwaddr bit_mask_for_index;
macho_file_highest_lowest(filename, lowest, highest);
high_Low_dif_bit_index =
get_highest_different_bit_index(align_64k_high(*highest),
align_64k_low(*lowest));
if (phys_base) {
phys_base_non_zero_bit_index =
get_lowest_non_zero_bit_index(phys_base);
//make sure we have enough zero bits to have all the diffrent kernel
//image addresses have the same non static bits in physical and in
//virtual memory.
if (high_Low_dif_bit_index > phys_base_non_zero_bit_index) {
abort();
}
bit_mask_for_index =
get_low_bits_mask_for_bit_index(phys_base_non_zero_bit_index);
*virt_base = align_64k_low(*lowest) & (~bit_mask_for_index);
}
}
void arm_load_macho(char *filename, AddressSpace *as, MemoryRegion *mem,
const char *name, hwaddr phys_base, hwaddr virt_base,
hwaddr low_virt_addr, hwaddr high_virt_addr, hwaddr *pc,
char *darwin_ver)
{
uint8_t *data = NULL;
gsize len;
uint8_t* rom_buf = NULL;
if (!g_file_get_contents(filename, (char **)&data, &len, NULL)) {
abort();
}
const char *darwin_str = "Darwin Kernel Version";
if (darwin_ver) {
char *res = memmem((char *)data, len, darwin_str,
sizeof(darwin_str));
if (!res)
abort();
strncpy(darwin_ver, res, strnlen(res, 1024) + 1);
}
struct mach_header_64* mh = (struct mach_header_64*)data;
struct load_command* cmd = (struct load_command*)(data +
sizeof(struct mach_header_64));
uint64_t rom_buf_size = align_64k_high(high_virt_addr) - low_virt_addr;
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_virt_addr),
data + segCmd->fileoff, segCmd->filesize);
break;
}
case LC_UNIXTHREAD: {
// grab just the entry point PC
uint64_t* ptrPc = (uint64_t*)((char*)cmd + 0x110);
// 0x110 for arm64 only.
*pc = vtop_bases(*ptrPc, phys_base, virt_base);
break;
}
}
cmd = (struct load_command*)((char*)cmd + cmd->cmdsize);
}
hwaddr low_phys_addr = vtop_bases(low_virt_addr, phys_base, virt_base);
allocate_and_copy(mem, as, name, low_phys_addr, rom_buf_size, rom_buf);
if (data) {
g_free(data);
}
if (rom_buf) {
g_free(rom_buf);
}
}