summaryrefslogtreecommitdiff
path: root/common/load.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/load.c')
-rw-r--r--common/load.c70
1 files changed, 70 insertions, 0 deletions
diff --git a/common/load.c b/common/load.c
new file mode 100644
index 0000000..4669c0b
--- /dev/null
+++ b/common/load.c
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * load.c - pull the payload into RAM. semihosting is the qemu path,
+ * flash and block devices follow the same shape once they exist.
+ *
+ * Copyright (C) 2026 Bradley Morgan <brads@mainlining.org>
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <semihosting.h>
+#include <boot.h>
+
+/*
+ * load fname into mem_base. the header decides the real placement, so
+ * we read the whole file first and let tb_image_setup place it.
+ */
+int tb_load_semihosting(const char *fname, uintptr_t load_addr,
+ uintptr_t mem_base, uint32 mem_size,
+ struct tb_image *img)
+{
+ struct Image_header *ih = (struct Image_header *)load_addr;
+ long fd;
+ long len;
+ long 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;
+ }
+
+ if ((uint32)len > mem_size) {
+ smh_close(fd);
+ return -2;
+ }
+
+ /* header first so placement is known before the big copy */
+ ret = smh_read(fd, (void *)load_addr, sizeof(*ih));
+ if (ret != sizeof(*ih)) {
+ smh_close(fd);
+ return -3;
+ }
+
+ if (tb_image_setup(load_addr, mem_base, mem_size, img)) {
+ smh_close(fd);
+ return -4;
+ }
+
+ if (img->load != load_addr) {
+ /* the image wants to sit elsewhere, copy it there */
+ memmove((void *)img->load, (void *)load_addr,
+ sizeof(*ih));
+ }
+
+ ret = smh_read(fd, (void *)(img->load + sizeof(*ih)),
+ len - sizeof(*ih));
+ if (ret != len - sizeof(*ih)) {
+ smh_close(fd);
+ return -5;
+ }
+
+ smh_close(fd);
+ return 0;
+}