diff options
Diffstat (limited to 'common/main.c')
| -rw-r--r-- | common/main.c | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/common/main.c b/common/main.c new file mode 100644 index 0000000..94f7195 --- /dev/null +++ b/common/main.c @@ -0,0 +1,164 @@ +/* + * main.c - the C entry. console up first, then the board from the dtb, + * then find a payload and jump. called from start.S with x0 = dtb. + * + * Copyright (c) 2026 Bradley Morgan <brads@mainlining.org> + * + * 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 <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <stdint.h> +#include <libfdt.h> +#include <debug.h> +#include <semihosting.h> +#include <boot.h> + +#define TB_VERSION "0.1" + +extern int tb_console_init(const void *dtb); +extern void __NO_RETURN tb_boot_linux(uintptr_t ep, const void *dtb); + +/* + * staging area for the payload. past the bootloader at the bottom of + * RAM, the image header decides its final resting place. + */ +#define TB_LOAD_ADDR 0x40200000 + +static int tb_read_memory(const void *dtb, uintptr_t *base, uint32 *size) +{ + const fdt32_t *prop; + int len; + int node; + + node = fdt_path_offset(dtb, "/memory@40000000"); + if (node < 0) + node = fdt_path_offset(dtb, "/memory"); + if (node < 0) + return -1; + + prop = fdt_getprop(dtb, node, "reg", &len); + if (!prop || len < 12) + return -1; + + *base = ((uintptr_t)fdt32_to_cpu(prop[0]) << 32) | + fdt32_to_cpu(prop[1]); + *size = fdt32_to_cpu(prop[2]); + return 0; +} + +/* + * bootargs from chosen. passed straight through, tashaboot does not + * rewrite the kernel command line. + */ +static const char *tb_get_cmdline(const void *dtb) +{ + const char *cmdline; + int len; + int node; + + node = fdt_path_offset(dtb, "/chosen"); + if (node < 0) + return NULL; + + cmdline = fdt_getprop(dtb, node, "bootargs", &len); + if (!cmdline || len <= 0) + return NULL; + return cmdline; +} + +/* + * tashaboot,bootfile in chosen overrides the default image name. the + * host file that semihosting serves is what gets loaded. + */ +static const char *tb_get_bootfile(const void *dtb) +{ + const char *bootfile; + int len; + int node; + + node = fdt_path_offset(dtb, "/chosen"); + if (node < 0) + return NULL; + + bootfile = fdt_getprop(dtb, node, "tashaboot,bootfile", &len); + if (!bootfile || len <= 0) + return NULL; + return bootfile; +} + +void tashaboot_main(const void *dtb) +{ + struct tb_image img; + uintptr_t mem_base = 0; + uint32 mem_size = 0; + const char *cmdline; + const char *bootfile; + int ret; + + if (tb_console_init(dtb)) + return; + + dprintf(ALWAYS, "tashaboot " TB_VERSION "\n"); + dprintf(ALWAYS, "dtb at %p\n", dtb); + + if (fdt_check_header(dtb)) { + dprintf(ALWAYS, "bad dtb header, halting\n"); + platform_halt(); + } + + if (tb_read_memory(dtb, &mem_base, &mem_size)) { + dprintf(ALWAYS, "no usable memory node, halting\n"); + platform_halt(); + } + dprintf(ALWAYS, "memory %lx-%lx\n", mem_base, mem_base + mem_size); + + cmdline = tb_get_cmdline(dtb); + if (cmdline) + dprintf(ALWAYS, "cmdline: %s\n", cmdline); + + /* + * semihosting first, it is the qemu development path. a real board + * boots from flash or block storage, those loaders come later and + * slot in right here. + */ + if (!smh_probe()) { + dprintf(ALWAYS, "no semihosting host, halting\n"); + platform_halt(); + } + + bootfile = tb_get_bootfile(dtb); + if (!bootfile) + bootfile = "Image"; + + ret = tb_load_semihosting(bootfile, TB_LOAD_ADDR, mem_base, mem_size, &img); + if (ret) { + dprintf(ALWAYS, "load failed (%d), halting\n", ret); + platform_halt(); + } + + dprintf(ALWAYS, "loaded %llu bytes at %lx, entry %lx\n", + (unsigned long long)img.size, img.load, img.ep); + dprintf(ALWAYS, "jumping\n"); + + tb_boot_linux(img.ep, dtb); +} |
