m1n1/src/kboot.c

216 lines
6.3 KiB
C
Raw Normal View History

/* SPDX-License-Identifier: MIT */
#include <assert.h>
#include "adt.h"
#include "exception.h"
#include "kboot.h"
#include "libfdt/libfdt.h"
#include "malloc.h"
#include "memory.h"
#include "types.h"
#include "utils.h"
#include "xnuboot.h"
static void *dt = NULL;
static int dt_bufsize = 0;
static char *bootargs = NULL;
static void *initrd_start = NULL;
static size_t initrd_size = 0;
#define DT_ALIGN 16384
#define bail(...) \
do { \
printf(__VA_ARGS__); \
return -1; \
} while (0)
typedef void (*kernel_entry)(void *devtree, u64 rsv1, u64 rsv2, u64 rsv3);
static int dt_set_chosen(void)
{
int node = fdt_path_offset(dt, "/chosen");
if (node < 0)
bail("FDT: /chosen node not found in devtree\n");
if (bootargs) {
if (fdt_setprop(dt, node, "bootargs", bootargs, strlen(bootargs) + 1) < 0)
bail("FDT: couldn't set chosen.bootargs property\n");
printf("FDT: bootargs = '%s'\n", bootargs);
}
if (initrd_start && initrd_size) {
if (fdt_setprop_u64(dt, node, "linux,initrd-start", (u64)initrd_start))
bail("FDT: couldn't set chosen.linux,initrd-start property\n");
u64 end = ((u64)initrd_start) + initrd_size;
if (fdt_setprop_u64(dt, node, "linux,initrd-end", end))
bail("FDT: couldn't set chosen.linux,initrd-end property\n");
if (fdt_add_mem_rsv(dt, (u64)initrd_start, initrd_size))
bail("FDT: couldn't add reservation for the initrd\n");
printf("FDT: initrd at %p size 0x%lx\n", initrd_start, initrd_size);
}
if (cur_boot_args.video.base) {
int fb = fdt_path_offset(dt, "/chosen/framebuffer");
if (fb < 0)
bail("FDT: /chosen node not found in devtree\n");
u64 fb_base = cur_boot_args.video.base;
u64 fb_size = cur_boot_args.video.stride * cur_boot_args.video.height;
u64 fbreg[2] = {cpu_to_fdt64(fb_base), cpu_to_fdt64(fb_size)};
char fbname[32];
sprintf(fbname, "framebuffer@%lx", fb_base);
if (fdt_setprop(dt, fb, "reg", fbreg, sizeof(fbreg)))
bail("FDT: couldn't set framebuffer.reg property\n");
if (fdt_set_name(dt, fb, fbname))
bail("FDT: couldn't set framebuffer name\n");
if (fdt_setprop_u32(dt, fb, "width", cur_boot_args.video.width))
bail("FDT: couldn't set framebuffer width\n");
if (fdt_setprop_u32(dt, fb, "height", cur_boot_args.video.height))
bail("FDT: couldn't set framebuffer height\n");
if (fdt_setprop_u32(dt, fb, "stride", cur_boot_args.video.stride))
bail("FDT: couldn't set framebuffer stride\n");
const char *format = NULL;
switch (cur_boot_args.video.depth) {
case 32:
format = "x8r8g8b8";
break;
case 30:
format = "x2r10g10b10";
break;
case 16:
format = "r5g6b5";
break;
default:
printf("FDT: unsupported fb depth %d, not enabling\n", cur_boot_args.video.depth);
return 0; // Do not error out, but don't set the FB
}
if (fdt_setprop_string(dt, fb, "format", format))
bail("FDT: couldn't set framebuffer format\n");
fdt_delprop(dt, fb, "status"); // may fail if it does not exist
printf("FDT: %s size 0x%lx\n", fbname, fb_base, fb_size);
// We do not need to reserve the framebuffer, as it will be excluded from the usable RAM
// range already.
}
return 0;
}
static int dt_set_memory(void)
{
int anode = adt_path_offset(adt, "/chosen");
if (anode < 0)
bail("ADT: /chosen not found\n");
u64 dram_base, dram_size;
if (ADT_GETPROP(adt, anode, "dram-base", &dram_base) < 0)
bail("ADT: Failed to get dram-base\n");
if (ADT_GETPROP(adt, anode, "dram-size", &dram_size) < 0)
bail("ADT: Failed to get dram-size\n");
// Tell the kernel our usable memory range. We cannot declare all of DRAM, and just reserve the
// bottom and top, because the kernel would still map it (and just not use it), which breaks
// ioremap (e.g. simplefb).
u64 dram_min = cur_boot_args.phys_base;
u64 dram_max = cur_boot_args.phys_base + cur_boot_args.mem_size;
printf("FDT: DRAM at 0x%lx size 0x%lx\n", dram_base, dram_size);
printf("FDT: Usable memory is 0x%lx..0x%lx (0x%lx)\n", dram_min, dram_max, dram_max - dram_min);
u64 memreg[2] = {cpu_to_fdt64(dram_min), cpu_to_fdt64(dram_max - dram_min)};
int node = fdt_path_offset(dt, "/memory");
if (node < 0)
bail("FDT: /memory node not found in devtree\n");
if (fdt_setprop(dt, node, "reg", memreg, sizeof(memreg)))
bail("FDT: couldn't set memory.reg property\n");
return 0;
}
void kboot_set_initrd(void *start, size_t size)
{
initrd_start = start;
initrd_size = size;
}
void kboot_set_bootargs(const char *ba)
{
if (bootargs)
free(bootargs);
if (!ba) {
bootargs = NULL;
return;
}
bootargs = malloc(strlen(ba) + 1);
strcpy(bootargs, ba);
}
int kboot_prepare_dt(void *fdt)
{
if (dt) {
free(dt);
dt = NULL;
}
dt_bufsize = fdt_totalsize(fdt);
assert(dt_bufsize);
dt_bufsize += 64 * 1024; // Add 64K of buffer for modifications
dt = memalign(DT_ALIGN, dt_bufsize);
if (fdt_open_into(fdt, dt, dt_bufsize) < 0)
bail("FDT: fdt_open_into() failed\n");
if (fdt_add_mem_rsv(dt, (u64)dt, dt_bufsize))
bail("FDT: couldn't add reservation for the devtree\n");
if (dt_set_chosen())
return -1;
if (dt_set_memory())
return -1;
if (fdt_pack(dt))
bail("FDT: fdt_pack() failed\n");
printf("FDT prepared at %p\n", dt);
return 0;
}
int kboot_boot(void *kernel)
{
mmu_shutdown();
exception_shutdown();
printf("Booting kernel at %p with fdt at %p\n", kernel, dt);
((kernel_entry)kernel)(dt, 0, 0, 0);
panic("kernel returned\n");
}