From: Jiaxun Yang <[email protected]>

Implement loading and booting functions for LoongArch
standard kernel image as per spec.

LoongArch kernel do expect us to fake a efi systemtable
for passing fdt to kernel, we don't need to implement any
EFI functions for kernel because it won't look into anything
beside devicetree from that table if we tell kernel we are
not efi compatible by setting a0 boot argument to zero.

Link: https://docs.kernel.org/arch/loongarch/booting.html
Signed-off-by: Jiaxun Yang <[email protected]>
Signed-off-by: Yao Zi <[email protected]>
---

Changed from v1
- Remove now unused arch_lmb_reserve()

 arch/loongarch/lib/Makefile |   2 +
 arch/loongarch/lib/bootm.c  | 163 ++++++++++++++++++++++++++++++++++++
 arch/loongarch/lib/image.c  |  66 +++++++++++++++
 cmd/Kconfig                 |   2 +-
 4 files changed, 232 insertions(+), 1 deletion(-)
 create mode 100644 arch/loongarch/lib/bootm.c
 create mode 100644 arch/loongarch/lib/image.c

diff --git a/arch/loongarch/lib/Makefile b/arch/loongarch/lib/Makefile
index 3c17b9cd85af..e65e66357a9b 100644
--- a/arch/loongarch/lib/Makefile
+++ b/arch/loongarch/lib/Makefile
@@ -3,6 +3,8 @@
 # Copyright (C) 2024 Jiaxun yang <[email protected]>
 #
 
+obj-$(CONFIG_CMD_BOOTM) += bootm.o
+obj-$(CONFIG_CMD_BOOTI) += bootm.o image.o
 obj-$(CONFIG_CMD_GO) += boot.o
 obj-y  += cache.o
 obj-y  += interrupts.o
diff --git a/arch/loongarch/lib/bootm.c b/arch/loongarch/lib/bootm.c
new file mode 100644
index 000000000000..432ad1cfe970
--- /dev/null
+++ b/arch/loongarch/lib/bootm.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2024 Jiaxun Yang <[email protected]>
+ */
+
+#include <bootstage.h>
+#include <bootm.h>
+#include <command.h>
+#include <dm.h>
+#include <efi.h>
+#include <efi_api.h>
+#include <fdt_support.h>
+#include <hang.h>
+#include <log.h>
+#include <linux/sizes.h>
+#include <memalign.h>
+#include <asm/global_data.h>
+#include <dm/root.h>
+#include <image.h>
+#include <asm/byteorder.h>
+#include <dm/device.h>
+#include <dm/root.h>
+#include <u-boot/zlib.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const efi_guid_t efi_guid_fdt = EFI_FDT_GUID;
+
+__weak void board_quiesce_devices(void)
+{
+}
+
+/**
+ * announce_and_cleanup() - Print message and prepare for kernel boot
+ *
+ * @fake: non-zero to do everything except actually boot
+ */
+static void announce_and_cleanup(int fake)
+{
+       printf("\nStarting kernel ...%s\n\n", fake ?
+               "(fake run for tracing)" : "");
+       bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
+#if CONFIG_IS_ENABLED(BOOTSTAGE_FDT)
+       bootstage_fdt_add_report();
+#endif
+#if CONFIG_IS_ENABLED(BOOTSTAGE_REPORT)
+       bootstage_report();
+#endif
+
+#if CONFIG_IS_ENABLED(USB_DEVICE)
+       udc_disconnect();
+#endif
+
+       board_quiesce_devices();
+
+       /*
+        * Call remove function of all devices with a removal flag set.
+        * This may be useful for last-stage operations, like cancelling
+        * of DMA operation or releasing device internal buffers.
+        */
+       dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
+
+       cleanup_before_linux();
+}
+
+/* LoongArch do expect a EFI style systab */
+static int generate_systab(struct bootm_headers *images)
+{
+       const int nr_cfgtab = 1;
+       struct bd_info *kbd = images->kbd;
+       struct efi_system_table *systab;
+       struct efi_configuration_table *cfgtab;
+       size_t table_size = sizeof(struct efi_system_table) +
+                           nr_cfgtab * sizeof(struct efi_configuration_table);
+
+       systab = memalign(SZ_64K, table_size);
+       if (!systab) {
+               log_warning("Failed to allocate memory for systab\n");
+               return -ENOMEM;
+       }
+       memset(systab, 0, table_size);
+
+       cfgtab = (void *)systab + sizeof(struct efi_system_table);
+
+       systab->hdr.signature = EFI_SYSTEM_TABLE_SIGNATURE;
+       systab->hdr.headersize = sizeof(struct efi_system_table);
+       systab->nr_tables = nr_cfgtab;
+       systab->tables = cfgtab;
+       systab->hdr.crc32 = crc32(0, (const unsigned char *)systab,
+                               systab->hdr.headersize);
+
+       cfgtab[0].guid = efi_guid_fdt;
+       cfgtab[0].table = images->ft_addr;
+
+       kbd->bi_boot_params = (phys_addr_t)systab;
+
+       return 0;
+}
+
+static void boot_prep_linux(struct bootm_headers *images)
+{
+       if (CONFIG_IS_ENABLED(OF_LIBFDT) && IS_ENABLED(CONFIG_LMB) && 
images->ft_len) {
+               debug("using: FDT\n");
+               if (image_setup_linux(images)) {
+                       printf("FDT creation failed! hanging...");
+                       hang();
+               }
+               if (generate_systab(images)) {
+                       printf("Failed to generate EFI systab\n");
+                       hang();
+               }
+       } else {
+               printf("Device tree not found or missing FDT support\n");
+               hang();
+       }
+}
+
+static void boot_jump_linux(struct bootm_headers *images, int flag)
+{
+       void (*kernel)(ulong efi_boot, char *argc, void *dtb);
+       int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
+
+       kernel = (void (*)(ulong efi_boot, char *argc, void *dtb))images->ep;
+
+       bootstage_mark(BOOTSTAGE_ID_RUN_OS);
+
+       debug("## Transferring control to kernel (at address %08lx) ...\n",
+             (ulong)kernel);
+
+       announce_and_cleanup(fake);
+
+       if (!fake) {
+               if (CONFIG_IS_ENABLED(OF_LIBFDT) && images->ft_len)
+                       kernel(0, NULL, (void *)images->kbd->bi_boot_params);
+       }
+}
+
+int do_bootm_linux(int flag, struct bootm_info *bmi)
+{
+       struct bootm_headers *images = bmi->images;
+
+       if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
+               return -1;
+
+       if (flag & BOOTM_STATE_OS_PREP) {
+               boot_prep_linux(images);
+               return 0;
+       }
+
+       if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) {
+               boot_jump_linux(images, flag);
+               return 0;
+       }
+
+       boot_prep_linux(images);
+       boot_jump_linux(images, flag);
+       return 0;
+}
+
+int do_bootm_vxworks(int flag, struct bootm_info *bmi)
+{
+       return do_bootm_linux(flag, bmi);
+}
diff --git a/arch/loongarch/lib/image.c b/arch/loongarch/lib/image.c
new file mode 100644
index 000000000000..a10a35f6e90c
--- /dev/null
+++ b/arch/loongarch/lib/image.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2024 Jiaxun Yang <[email protected]>
+ *
+ * Based on riscv/lib/image.c
+ */
+
+#include <image.h>
+#include <mapmem.h>
+#include <errno.h>
+#include <asm/global_data.h>
+#include <linux/sizes.h>
+#include <linux/stddef.h>
+#include <asm/addrspace.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define LINUX_LOONGARCH_IMAGE_MAGIC    0x818223cd
+
+struct linux_image_h {
+       uint32_t        code0;                  /* Executable code */
+       uint32_t        code1;                  /* Executable code */
+       uint64_t        kernel_entry;   /* Kernel entry point */
+       uint64_t        image_size;             /* Effective Image size */
+       uint64_t        load_offset;    /* load offset */
+       uint64_t        res1;                   /* reserved */
+       uint64_t        res2;                   /* reserved */
+       uint64_t        res3;                   /* reserved */
+       uint32_t        magic;                  /* Magic number */
+       uint32_t        pe_header;              /* Offset to the PE header */
+};
+
+int booti_setup(ulong image, ulong *relocated_addr, ulong *size, ulong *ep,
+               bool force_reloc)
+{
+       struct linux_image_h *lhdr;
+       phys_addr_t ep_phys;
+
+       lhdr = (struct linux_image_h *)map_sysmem(image, 0);
+
+       if (lhdr->magic != LINUX_LOONGARCH_IMAGE_MAGIC) {
+               puts("Bad Linux LoongArch Image magic!\n");
+               return -EINVAL;
+       }
+
+       if (lhdr->image_size == 0) {
+               puts("Image lacks image_size field, error!\n");
+               return -EINVAL;
+       }
+
+       *size = lhdr->image_size;
+       if (force_reloc ||
+           (gd->ram_base <= image && image < gd->ram_base + gd->ram_size)) {
+               *relocated_addr = gd->ram_base + lhdr->load_offset;
+       } else {
+               *relocated_addr = image;
+       }
+
+       /* To workaround kernel supplying DMW based virtual address */
+       ep_phys = TO_PHYS(lhdr->kernel_entry);
+       *ep = *relocated_addr + (ep_phys - lhdr->load_offset);
+
+       unmap_sysmem(lhdr);
+
+       return 0;
+}
diff --git a/cmd/Kconfig b/cmd/Kconfig
index d0fb7397067e..e874b245086d 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -395,7 +395,7 @@ config CMD_BOOTZ
 
 config CMD_BOOTI
        bool "booti"
-       depends on ARM64 || RISCV || SANDBOX
+       depends on ARM64 || LOONGARCH || RISCV || SANDBOX
        depends on LMB
        default y
        select LIB_BOOTI
-- 
2.54.0

Reply via email to