2021-01-29 16:36:45 +00:00
|
|
|
/* SPDX-License-Identifier: MIT */
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include "adt.h"
|
2021-01-30 08:02:51 +00:00
|
|
|
#include "exception.h"
|
2021-01-29 16:36:45 +00:00
|
|
|
#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();
|
2021-01-30 08:02:51 +00:00
|
|
|
exception_shutdown();
|
2021-01-29 16:36:45 +00:00
|
|
|
|
|
|
|
printf("Booting kernel at %p with fdt at %p\n", kernel, dt);
|
|
|
|
|
|
|
|
((kernel_entry)kernel)(dt, 0, 0, 0);
|
|
|
|
|
|
|
|
panic("kernel returned\n");
|
|
|
|
}
|