summaryrefslogtreecommitdiff
path: root/common/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/main.c')
-rw-r--r--common/main.c95
1 files changed, 86 insertions, 9 deletions
diff --git a/common/main.c b/common/main.c
index cd234bb..a0237af 100644
--- a/common/main.c
+++ b/common/main.c
@@ -43,10 +43,6 @@
extern int tb_console_init(void);
/*
- * fixed load address, the osdev way. past the bootloader at the
- * bottom of RAM, the image header decides its final resting place.
- */
-/*
* the payload goes 16MB clear of wherever this bootloader is
* actually running, ADR knows the runtime base and qemu is free
* to place us anywhere. hardcoding 0x40200000 smashed our own
@@ -59,6 +55,12 @@ extern char __image_copy_end[];
#define TB_BOOTFILE "Image"
extern void __NO_RETURN tb_boot_linux(uintptr_t ep, uintptr_t fw_arg);
+extern uintptr_t tb_dtb_relocate(uintptr_t dtb, void *scratch,
+ size_t scratch_size, size_t grow);
+extern int tb_gic_init(uintptr_t gicd, uintptr_t gicc);
+extern int tb_dtb_add_chosen_prop(uintptr_t dtb, const char *name,
+ const void *val, size_t len);
+static size_t initrd_size;
void tashaboot_main(uintptr_t fw_arg)
{
@@ -141,22 +143,97 @@ void tashaboot_main(uintptr_t fw_arg)
}
/*
+ * firmware owns the devicetree it hands the kernel. ours
+ * relocates into scratch with grow room, then the chosen
+ * properties are added there and the kernel gets the new
+ * address, the same flow libfdt firmware uses.
+ */
+ {
+ /*
+ * the scratch lives at a fixed free address, clear of
+ * our image, the payload, and the kernel relocation
+ * zone. a bss array would sit inside 0x40080000+ and
+ * the kernel overwrites it while copying itself.
+ */
+ uint8_t *dtb_scratch = (uint8_t *)0x45000000ULL;
+ uintptr_t newdtb;
+
+ newdtb = tb_dtb_relocate(fw_arg, dtb_scratch,
+ 0x10000, 0x200);
+ if (!newdtb) {
+ dprintf(ALWAYS, "dtb: relocate failed\n");
+ platform_halt();
+ }
+
+ fw_arg = newdtb;
+
+ /*
+ * the interrupt controller the machine told us
+ * about, found by name, the reg pair read with the
+ * root cell counts. the gic goes into the defined
+ * off state before the kernel brings its own irq
+ * handling up.
+ */
+ {
+ extern int tb_dtb_find_reg0(uintptr_t dtb,
+ const char *name,
+ uintptr_t *addr,
+ size_t *size);
+ uintptr_t gicd = 0;
+ uintptr_t gicc = 0;
+ size_t sz = 0;
+
+ if (tb_dtb_find_reg0(fw_arg, "intc", &gicd, &sz) == 0 &&
+ sz >= 0x10000) {
+ gicc = gicd + 0x10000;
+ tb_gic_init(gicd, gicc);
+ dprintf(ALWAYS, "gic: %lx off\n",
+ (unsigned long)gicd);
+ }
+ }
+ }
+
+ /*
* the initrd rides after the kernel, the dtb /chosen carries
* linux,initrd-start and -end, both already patched in place
* with this layout.
*/
{
- extern int tb_load_raw(const char *fname,
- uintptr_t load_addr);
- int r = tb_load_raw(TB_INITRD_FILE, TB_INITRD_ADDR);
+ int r = tb_load_raw(TB_INITRD_FILE, TB_INITRD_ADDR,
+ &initrd_size);
if (r == 0)
- dprintf(ALWAYS, "initrd at %lx\n",
- (unsigned long)TB_INITRD_ADDR);
+ dprintf(ALWAYS, "initrd at %lx, %lx bytes\n",
+ (unsigned long)TB_INITRD_ADDR,
+ (unsigned long)initrd_size);
else
dprintf(ALWAYS, "no initrd (%d)\n", r);
}
+ /*
+ * the chosen properties, written now that the initrd size
+ * is known. the cells are big endian, the fdt is a big
+ * endian format end to end.
+ */
+ {
+ uint8_t start_cells[8], end_cells[8];
+ uint64_t start = TB_INITRD_ADDR;
+ uint64_t end = TB_INITRD_ADDR + initrd_size;
+ int a, b;
+
+ for (int i = 0; i < 8; i++) {
+ start_cells[i] = (uint8_t)(start >> (56 - 8 * i));
+ end_cells[i] = (uint8_t)(end >> (56 - 8 * i));
+ }
+ a = tb_dtb_add_chosen_prop(fw_arg, "linux,initrd-start",
+ start_cells, 8);
+ b = tb_dtb_add_chosen_prop(fw_arg, "linux,initrd-end",
+ end_cells, 8);
+ dprintf(ALWAYS, "dtb: initrd props %d %d\n", a, b);
+ }
+
+
+
dprintf(ALWAYS, "loaded %llu bytes at %lx, entry %lx\n",
(unsigned long long)img.size, img.load, img.ep);
dprintf(ALWAYS, "jumping\n");