summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/console.c74
-rw-r--r--common/image.c77
-rw-r--r--common/load.c57
-rw-r--r--common/main.c75
4 files changed, 283 insertions, 0 deletions
diff --git a/common/console.c b/common/console.c
new file mode 100644
index 0000000..a7108ba
--- /dev/null
+++ b/common/console.c
@@ -0,0 +1,74 @@
+/*
+ * console.c - the console dprintf writes to. semihosting SYS_WRITE0,
+ * the firmware service on the qemu dev path, the arm64 stand-in for
+ * the bios teletype the osdev loaders use. on real hardware this is
+ * the one file that changes.
+ *
+ * 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 <sys/types.h>
+#include <debug.h>
+#include <semihosting.h>
+
+/*
+ * lk's _dprintf sink. printf buffers a line here then hands it to the
+ * host, semihosting wants zero terminated strings not counts.
+ */
+#define TB_CONSOLE_MAX 256
+
+static char console_buf[TB_CONSOLE_MAX];
+static size_t console_len;
+
+static void console_flush(void)
+{
+ if (console_len == 0)
+ return;
+ console_buf[console_len] = '\0';
+ smh_write0(console_buf);
+ console_len = 0;
+}
+
+void _putchar(char c)
+{
+ if (console_len >= TB_CONSOLE_MAX - 1)
+ console_flush();
+ if (c == '\n') {
+ /* the host terminal wants cr lf, not lf alone */
+ console_buf[console_len++] = '\r';
+ }
+ console_buf[console_len++] = c;
+ if (c == '\n')
+ console_flush();
+}
+
+int tb_console_init(void)
+{
+ /*
+ * the probe is one harmless call: SYS_GET_ERRNO with no file
+ * handle open. a host answers, bare metal ignores the trap.
+ */
+ if (!smh_probe())
+ return -1;
+ console_len = 0;
+ return 0;
+}
diff --git a/common/image.c b/common/image.c
new file mode 100644
index 0000000..640eab4
--- /dev/null
+++ b/common/image.c
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * image.c - arm64 kernel Image validation and placement.
+ *
+ * The relocation rules are the ones from the kernel boot protocol,
+ * including the pre a2c1d73b94ed quirks, same math u-boot's
+ * booti_setup() runs.
+ *
+ * Copyright (C) 2026 Bradley Morgan <brads@mainlining.org>
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <endian.h>
+#include <boot.h>
+
+#define LINUX_ARM64_IMAGE_MAGIC 0x644d5241 /* "ARM\x64" */
+
+#define SZ_16M 0x01000000
+#define SZ_2M 0x00200000
+
+/* the 64 byte header from Documentation/arch/arm64/booting.rst */
+struct Image_header {
+ uint32 code0; /* executable */
+ uint32 code1; /* unused */
+ uint64 text_offset; /* load offset, LE */
+ uint64 image_size; /* size, LE */
+ uint64 flags; /* bit 3: relocatable */
+ uint64 res1;
+ uint64 res2;
+ uint64 res3;
+ uint32 magic; /* "ARM\x64" */
+ uint32 res4;
+};
+
+int tb_image_setup(uintptr_t image, struct tb_image *img)
+{
+ const struct Image_header *ih = (const struct Image_header *)image;
+ uint64_t image_size, text_offset;
+
+ if (le32_to_cpu(ih->magic) != LINUX_ARM64_IMAGE_MAGIC)
+ return -1;
+
+ if (le64_to_cpu(ih->image_size) == 0) {
+ /* ancient image, no size field, assume the old defaults */
+ image_size = SZ_16M;
+ text_offset = 0x80000;
+ } else {
+ image_size = le64_to_cpu(ih->image_size);
+ text_offset = le64_to_cpu(ih->text_offset);
+ }
+
+ /*
+ * flag bit 3 says the image can live anywhere, honour where it
+ * already is. otherwise the base must be 2MB aligned, the
+ * physical offset from there is text_offset.
+ */
+ if (le64_to_cpu(ih->flags) & (1ULL << 3)) {
+ uintptr_t base = image - text_offset;
+
+ img->load = ((base + SZ_2M - 1) & ~(uintptr_t)(SZ_2M - 1)) +
+ text_offset;
+ } else {
+ /*
+ * no relocate flag: the image must sit text_offset from a
+ * 2MB aligned base. it is already staged at the fixed
+ * address, treat its own position as the answer.
+ */
+ img->load = image;
+ }
+
+ /* the whole image, header included, lives at load, entry is code0 */
+ img->ep = img->load;
+ img->size = image_size;
+
+ return 0;
+}
diff --git a/common/load.c b/common/load.c
new file mode 100644
index 0000000..cb3b41d
--- /dev/null
+++ b/common/load.c
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * load.c - pull the payload into RAM. semihosting is the whole story
+ * for now, the bios INT 13h of this loader: the host serves the file,
+ * we read it at the fixed address and let the image header place it.
+ *
+ * Copyright (C) 2026 Bradley Morgan <brads@mainlining.org>
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <debug.h>
+#include <semihosting.h>
+#include <boot.h>
+
+int tb_load_semihosting(const char *fname, uintptr_t load_addr,
+ struct tb_image *img)
+{
+ long fd, len, ret;
+
+ fd = smh_open(fname, MODE_READ | MODE_BINARY);
+ if (fd < 0)
+ return fd;
+
+ len = smh_flen(fd);
+ if (len < 0) {
+ smh_close(fd);
+ return len;
+ }
+
+ /* header first so placement is known before the big copy */
+ ret = smh_read(fd, (void *)load_addr, 64);
+ if (ret != 64) {
+ smh_close(fd);
+ return -3;
+ }
+
+ if (tb_image_setup(load_addr, img)) {
+ smh_close(fd);
+ return -4;
+ }
+
+ if (img->load != load_addr) {
+ /* the image wants to sit elsewhere, copy the header there */
+ memmove((void *)img->load, (void *)load_addr, 64);
+ }
+
+ ret = smh_read(fd, (void *)(img->load + 64), len - 64);
+ if (ret != len - 64) {
+ smh_close(fd);
+ return -5;
+ }
+
+ smh_close(fd);
+ return 0;
+}
diff --git a/common/main.c b/common/main.c
new file mode 100644
index 0000000..fb388cb
--- /dev/null
+++ b/common/main.c
@@ -0,0 +1,75 @@
+/*
+ * main.c - the C entry. console up first, then load the payload and
+ * jump. called from start.S with x0 = whatever the firmware passed.
+ *
+ * the osdev model, arm64: the firmware services do the work, the
+ * kernel goes at a fixed known address, whatever x0 we were handed
+ * goes straight through to the payload.
+ *
+ * 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 <debug.h>
+#include <semihosting.h>
+#include <boot.h>
+
+#define TB_VERSION "0.1"
+
+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.
+ */
+#define TB_LOAD_ADDR 0x40200000
+
+/* the file semihosting serves as the payload */
+#define TB_BOOTFILE "Image"
+
+extern void __NO_RETURN tb_boot_linux(uintptr_t ep, uintptr_t fw_arg);
+
+void tashaboot_main(uintptr_t fw_arg)
+{
+ struct tb_image img;
+ int ret;
+
+ if (tb_console_init())
+ return;
+
+ dprintf(ALWAYS, "tashaboot " TB_VERSION "\n");
+
+ ret = tb_load_semihosting(TB_BOOTFILE, TB_LOAD_ADDR, &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, fw_arg);
+}