qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] Support load kernel(vmlinux)/dtb/initrd separately


From: Archer Yan
Subject: [Qemu-devel] [PATCH] Support load kernel(vmlinux)/dtb/initrd separately for Boston in QEMU.
Date: Mon, 1 Apr 2019 06:36:51 +0000

Currently boston in QEMU only supports boot with FIT format. Since ELF file
can provide symbol infomation in debug, this patch enables Boston boot from
vmlinux&dtb.

Signed-off-by: Archer Yan <address@hidden>
---
 hw/mips/boston.c | 224 ++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 201 insertions(+), 23 deletions(-)

diff --git a/hw/mips/boston.c b/hw/mips/boston.c
index e5bab3cadc..5910ffdc8a 100644
--- a/hw/mips/boston.c
+++ b/hw/mips/boston.c
@@ -39,6 +39,10 @@
 #include "sysemu/device_tree.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/qtest.h"
+#include "elf.h"
+#include "sysemu/kvm.h"
+#include "hw/mips/mips.h"
+#include "qemu/option.h"
 
 #include <libfdt.h>
 
@@ -58,6 +62,11 @@ typedef struct {
 
     hwaddr kernel_entry;
     hwaddr fdt_base;
+    long kernel_size;
+
+    uint64_t initrd_size;
+    uint64_t initrd_start;
+    uint64_t initrd_offset;
 } BostonState;
 
 enum boston_plat_reg {
@@ -328,31 +337,59 @@ static void gen_firmware(uint32_t *p, hwaddr 
kernel_entry, hwaddr fdt_addr,
     stl_p(p++, 0x03200009);                     /* jr   $25 */
 }
 
-static const void *boston_fdt_filter(void *opaque, const void *fdt_orig,
-                                     const void *match_data, hwaddr *load_addr)
+typedef uint64_t (*xlate_to_kseg0)(void *, uint64_t);
+static xlate_to_kseg0 get_xlate_to_kseg0_fn(BostonState *s)
+{
+    /* Check where the kernel has been linked */
+    if (s->kernel_entry & 0x80000000ll) {
+        if (kvm_enabled()) {
+            error_report("KVM guest kernels must be linked in useg. "
+                         "Did you forget to enable CONFIG_KVM_GUEST?");
+            return NULL;
+        }
+        return cpu_mips_phys_to_kseg0;
+    } else {
+        /* if kernel entry is in useg it is probably a KVM T&E kernel */
+        mips_um_ksegs_enable();
+        return cpu_mips_kvm_um_phys_to_kseg0;
+    }
+}
+
+/*Adapt fdt to insert initrd parameters*/
+static int boston_initrd_fdt(BostonState *s, void *fdt)
 {
-    BostonState *s = BOSTON(opaque);
-    MachineState *machine = s->mach;
     const char *cmdline;
+    char *args_str = NULL;
+    MachineState *machine = s->mach;
+    int initrd_args_len = 64;
     int err;
-    void *fdt;
-    size_t fdt_sz, ram_low_sz, ram_high_sz;
+    size_t ram_low_sz, ram_high_sz;
+    uint64_t (*xlate_to_kseg0_fn) (void *opaque, uint64_t addr);
 
-    fdt_sz = fdt_totalsize(fdt_orig) * 2;
-    fdt = g_malloc0(fdt_sz);
+    cmdline = (machine->kernel_cmdline && machine->kernel_cmdline[0])
+            ? machine->kernel_cmdline : " ";
+    xlate_to_kseg0_fn = get_xlate_to_kseg0_fn(s);
+    if (NULL == xlate_to_kseg0_fn) {
+        fprintf(stderr, "couldn't get xlate_to_kseg0\n");
+        return -1;
+    }
 
-    err = fdt_open_into(fdt_orig, fdt, fdt_sz);
-    if (err) {
-        fprintf(stderr, "unable to open FDT\n");
-        return NULL;
+    s->initrd_start = xlate_to_kseg0_fn(NULL, s->initrd_offset);
+
+    if (s->initrd_size) {
+        args_str = g_malloc(strlen(cmdline) + initrd_args_len);
+        if (args_str != NULL) {
+            sprintf((char *)args_str, "rd_start=0x%lx rd_size=0x%lx %s",
+                    s->initrd_start, s->initrd_size, cmdline);
+            cmdline = args_str;
+        }
     }
 
-    cmdline = (machine->kernel_cmdline && machine->kernel_cmdline[0])
-            ? machine->kernel_cmdline : " ";
     err = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
     if (err < 0) {
         fprintf(stderr, "couldn't set /chosen/bootargs\n");
-        return NULL;
+        g_free(args_str);
+        return -1;
     }
 
     ram_low_sz = MIN(256 * MiB, machine->ram_size);
@@ -360,10 +397,41 @@ static const void *boston_fdt_filter(void *opaque, const 
void *fdt_orig,
     qemu_fdt_setprop_sized_cells(fdt, "/address@hidden", "reg",
                                  1, 0x00000000, 1, ram_low_sz,
                                  1, 0x90000000, 1, ram_high_sz);
+    g_free(args_str);
+    return 0;
+}
 
-    fdt = g_realloc(fdt, fdt_totalsize(fdt));
-    qemu_fdt_dumpdtb(fdt, fdt_sz);
+static const void *boston_fdt_filter(void *opaque, const void *fdt_orig,
+                                     const void *match_data, hwaddr *load_addr)
+{
+    BostonState *s = BOSTON(opaque);
+    MachineState *machine = s->mach;
+    int err;
+    void *fdt;
+    int fdt_sz;
+    if (machine->dtb) {
+        /*Use QEMU cmd specified dtb*/
+        fdt = load_device_tree(machine->dtb, &fdt_sz);
+    } else {
+        /*Use default dtb contained in FIT image*/
+        fdt_sz = fdt_totalsize(fdt_orig) * 2;
+        fdt = g_malloc0(fdt_sz);
+
+        err = fdt_open_into(fdt_orig, fdt, fdt_sz);
+        if (err) {
+            fprintf(stderr, "unable to open FDT\n");
+            return NULL;
+        }
+
+        fdt = g_realloc(fdt, fdt_totalsize(fdt));
+    }
 
+    err = boston_initrd_fdt(s, fdt);
+    if (err) {
+        return NULL;
+    }
+
+    qemu_fdt_dumpdtb(fdt, fdt_sz);
     s->fdt_base = *load_addr;
 
     return fdt;
@@ -391,6 +459,99 @@ static const struct fit_loader boston_fit_loader = {
     .kernel_filter = boston_kernel_filter,
 };
 
+static int boston_load_dtb(BostonState *s)
+{
+    void *fdt = NULL;
+    int size = 0;
+    int err;
+    MachineState *machine = s->mach;
+    hwaddr load_addr;
+    hwaddr kernel_end = 0xffffffff80100000 + s->kernel_size;
+
+    fdt = load_device_tree(machine->dtb, &size);
+    if (!fdt) {
+        fprintf(stderr, "Couldn't open dtb file %s\n", machine->dtb);
+        return -1;
+    }
+    load_addr = ROUND_UP(kernel_end, 64 * KiB) + (10 * MiB);
+    err = boston_initrd_fdt(s, fdt);
+    if (err) {
+        return -1;
+    }
+
+    qemu_fdt_dumpdtb(fdt, size);
+
+    s->fdt_base = load_addr;
+    load_addr = cpu_mips_kseg0_to_phys(s, load_addr);
+    rom_add_blob_fixed("address@hidden", fdt, size, load_addr);
+
+    return 0;
+
+}
+
+static int boston_load_initrd(BostonState *s)
+{
+    int64_t initrd_size = 0;
+    ram_addr_t initrd_offset = 0;
+    int ram_low_size;
+    MachineState *machine = s->mach;
+    ram_low_size = MIN(machine->ram_size, 256 * MiB);
+    if (machine->initrd_filename) {
+        initrd_size = get_image_size(machine->initrd_filename);
+        if (initrd_size > 0) {
+            /*
+             * The kernel allocates the bootmap memory in the low memory after
+             *  the initrd.  It takes at most 128kiB for 2GB RAM and 4kiB
+             *  pages.
+             */
+            initrd_offset = (ram_low_size - initrd_size
+                             - (128 * KiB)
+                             - ~INITRD_PAGE_MASK) & INITRD_PAGE_MASK;
+
+            initrd_size = load_image_targphys(machine->initrd_filename,
+                                              initrd_offset,
+                                              ram_size - initrd_offset);
+        }
+        if (initrd_size == (target_ulong) -1) {
+            error_report("could not load initial ram disk '%s'",
+                         machine->initrd_filename);
+            return -1;
+        }
+    }
+    s->initrd_offset = initrd_offset;
+    s->initrd_size = initrd_size;
+    return 0;
+}
+
+static int boston_load_kernel(BostonState *s)
+{
+    int64_t kernel_entry, kernel_high;
+    long kernel_size;
+    int big_endian;
+    MachineState *machine = s->mach;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+    big_endian = 1;
+#else
+    big_endian = 0;
+#endif
+
+    kernel_size = load_elf(machine->kernel_filename, NULL,
+                           cpu_mips_kseg0_to_phys,  NULL,
+                           (uint64_t *)&kernel_entry, NULL,
+                           (uint64_t *)&kernel_high, big_endian, EM_MIPS, 1, 
0);
+    if (kernel_size < 0) {
+        error_report("could not load kernel '%s': %s",
+                     machine->kernel_filename,
+                     load_elf_strerror(kernel_size));
+        return -1;
+    }
+
+    s->kernel_entry = kernel_entry;
+    s->kernel_size = kernel_size;
+    return 0;
+}
+
 static inline XilinxPCIEHost *
 xilinx_pcie_init(MemoryRegion *sys_mem, uint32_t bus_nr,
                  hwaddr cfg_base, uint64_t cfg_size,
@@ -433,7 +594,7 @@ static void boston_mach_init(MachineState *machine)
     PCIDevice *ahci;
     DriveInfo *hd[6];
     Chardev *chr;
-    int fw_size, fit_err;
+    int fw_size, load_err;
     bool is_64b;
 
     if ((machine->ram_size % GiB) ||
@@ -533,12 +694,29 @@ static void boston_mach_init(MachineState *machine)
             exit(1);
         }
     } else if (machine->kernel_filename) {
-        fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, s);
-        if (fit_err) {
-            error_printf("unable to load FIT image\n");
-            exit(1);
+        if (machine->initrd_filename) {
+            load_err = boston_load_initrd(s);
+            if (load_err) {
+                error_printf("unable to separately load initrd image\n");
+                exit(1);
+            }
+        }
+        load_err = load_fit(&boston_fit_loader, machine->kernel_filename, s);
+        if (load_err) {
+            error_printf("unable to load FIT image, try load as ELF\n");
+            load_err = boston_load_kernel(s);
+            if (load_err) {
+                error_printf("unable to separately load kernel image\n");
+                exit(1);
+            }
+            if (machine->dtb) {
+                load_err = boston_load_dtb(s);
+                if (load_err) {
+                    error_printf("unable to separately load dtb image\n");
+                    exit(1);
+                }
+            }
         }
-
         gen_firmware(memory_region_get_ram_ptr(flash) + 0x7c00000,
                      s->kernel_entry, s->fdt_base, is_64b);
     } else if (!qtest_enabled()) {
-- 
2.17.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]