xref: /openbmc/qemu/hw/loongarch/boot.c (revision d771ca1c10ab146eae676dd6a6975a8f7cf84d65)
1*d771ca1cSSong Gao /* SPDX-License-Identifier: GPL-2.0-or-later */
2*d771ca1cSSong Gao /*
3*d771ca1cSSong Gao  * LoongArch boot helper functions.
4*d771ca1cSSong Gao  *
5*d771ca1cSSong Gao  * Copyright (c) 2023 Loongson Technology Corporation Limited
6*d771ca1cSSong Gao  */
7*d771ca1cSSong Gao 
8*d771ca1cSSong Gao #include "qemu/osdep.h"
9*d771ca1cSSong Gao #include "qemu/units.h"
10*d771ca1cSSong Gao #include "target/loongarch/cpu.h"
11*d771ca1cSSong Gao #include "hw/loongarch/virt.h"
12*d771ca1cSSong Gao #include "hw/loader.h"
13*d771ca1cSSong Gao #include "elf.h"
14*d771ca1cSSong Gao #include "qemu/error-report.h"
15*d771ca1cSSong Gao #include "sysemu/reset.h"
16*d771ca1cSSong Gao #include "sysemu/qtest.h"
17*d771ca1cSSong Gao 
18*d771ca1cSSong Gao static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr)
19*d771ca1cSSong Gao {
20*d771ca1cSSong Gao     return addr & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS);
21*d771ca1cSSong Gao }
22*d771ca1cSSong Gao 
23*d771ca1cSSong Gao static int64_t load_kernel_info(struct loongarch_boot_info *info)
24*d771ca1cSSong Gao {
25*d771ca1cSSong Gao     uint64_t kernel_entry, kernel_low, kernel_high;
26*d771ca1cSSong Gao     ssize_t kernel_size;
27*d771ca1cSSong Gao 
28*d771ca1cSSong Gao     kernel_size = load_elf(info->kernel_filename, NULL,
29*d771ca1cSSong Gao                            cpu_loongarch_virt_to_phys, NULL,
30*d771ca1cSSong Gao                            &kernel_entry, &kernel_low,
31*d771ca1cSSong Gao                            &kernel_high, NULL, 0,
32*d771ca1cSSong Gao                            EM_LOONGARCH, 1, 0);
33*d771ca1cSSong Gao 
34*d771ca1cSSong Gao     if (kernel_size < 0) {
35*d771ca1cSSong Gao         error_report("could not load kernel '%s': %s",
36*d771ca1cSSong Gao                      info->kernel_filename,
37*d771ca1cSSong Gao                      load_elf_strerror(kernel_size));
38*d771ca1cSSong Gao         exit(1);
39*d771ca1cSSong Gao     }
40*d771ca1cSSong Gao     return kernel_entry;
41*d771ca1cSSong Gao }
42*d771ca1cSSong Gao 
43*d771ca1cSSong Gao static void reset_load_elf(void *opaque)
44*d771ca1cSSong Gao {
45*d771ca1cSSong Gao     LoongArchCPU *cpu = opaque;
46*d771ca1cSSong Gao     CPULoongArchState *env = &cpu->env;
47*d771ca1cSSong Gao 
48*d771ca1cSSong Gao     cpu_reset(CPU(cpu));
49*d771ca1cSSong Gao     if (env->load_elf) {
50*d771ca1cSSong Gao         cpu_set_pc(CPU(cpu), env->elf_address);
51*d771ca1cSSong Gao     }
52*d771ca1cSSong Gao }
53*d771ca1cSSong Gao 
54*d771ca1cSSong Gao static void fw_cfg_add_kernel_info(struct loongarch_boot_info *info,
55*d771ca1cSSong Gao                                    FWCfgState *fw_cfg)
56*d771ca1cSSong Gao {
57*d771ca1cSSong Gao     /*
58*d771ca1cSSong Gao      * Expose the kernel, the command line, and the initrd in fw_cfg.
59*d771ca1cSSong Gao      * We don't process them here at all, it's all left to the
60*d771ca1cSSong Gao      * firmware.
61*d771ca1cSSong Gao      */
62*d771ca1cSSong Gao     load_image_to_fw_cfg(fw_cfg,
63*d771ca1cSSong Gao                          FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA,
64*d771ca1cSSong Gao                          info->kernel_filename,
65*d771ca1cSSong Gao                          false);
66*d771ca1cSSong Gao 
67*d771ca1cSSong Gao     if (info->initrd_filename) {
68*d771ca1cSSong Gao         load_image_to_fw_cfg(fw_cfg,
69*d771ca1cSSong Gao                              FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA,
70*d771ca1cSSong Gao                              info->initrd_filename, false);
71*d771ca1cSSong Gao     }
72*d771ca1cSSong Gao 
73*d771ca1cSSong Gao     if (info->kernel_cmdline) {
74*d771ca1cSSong Gao         fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
75*d771ca1cSSong Gao                        strlen(info->kernel_cmdline) + 1);
76*d771ca1cSSong Gao         fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA,
77*d771ca1cSSong Gao                           info->kernel_cmdline);
78*d771ca1cSSong Gao     }
79*d771ca1cSSong Gao }
80*d771ca1cSSong Gao 
81*d771ca1cSSong Gao static void loongarch_firmware_boot(LoongArchMachineState *lams,
82*d771ca1cSSong Gao                                     struct loongarch_boot_info *info)
83*d771ca1cSSong Gao {
84*d771ca1cSSong Gao     fw_cfg_add_kernel_info(info, lams->fw_cfg);
85*d771ca1cSSong Gao }
86*d771ca1cSSong Gao 
87*d771ca1cSSong Gao static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info)
88*d771ca1cSSong Gao {
89*d771ca1cSSong Gao     int64_t kernel_addr = 0;
90*d771ca1cSSong Gao     LoongArchCPU *lacpu;
91*d771ca1cSSong Gao     CPUState *cs;
92*d771ca1cSSong Gao 
93*d771ca1cSSong Gao     if (info->kernel_filename) {
94*d771ca1cSSong Gao         kernel_addr = load_kernel_info(info);
95*d771ca1cSSong Gao     } else {
96*d771ca1cSSong Gao         if(!qtest_enabled()) {
97*d771ca1cSSong Gao             error_report("Need kernel filename\n");
98*d771ca1cSSong Gao             exit(1);
99*d771ca1cSSong Gao         }
100*d771ca1cSSong Gao     }
101*d771ca1cSSong Gao 
102*d771ca1cSSong Gao     CPU_FOREACH(cs) {
103*d771ca1cSSong Gao         lacpu = LOONGARCH_CPU(cs);
104*d771ca1cSSong Gao         lacpu->env.load_elf = true;
105*d771ca1cSSong Gao         lacpu->env.elf_address = kernel_addr;
106*d771ca1cSSong Gao     }
107*d771ca1cSSong Gao }
108*d771ca1cSSong Gao 
109*d771ca1cSSong Gao void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info)
110*d771ca1cSSong Gao {
111*d771ca1cSSong Gao     LoongArchMachineState *lams = LOONGARCH_MACHINE(ms);
112*d771ca1cSSong Gao     int i;
113*d771ca1cSSong Gao 
114*d771ca1cSSong Gao     /* register reset function */
115*d771ca1cSSong Gao     for (i = 0; i < ms->smp.cpus; i++) {
116*d771ca1cSSong Gao         qemu_register_reset(reset_load_elf, LOONGARCH_CPU(qemu_get_cpu(i)));
117*d771ca1cSSong Gao     }
118*d771ca1cSSong Gao 
119*d771ca1cSSong Gao     info->kernel_filename = ms->kernel_filename;
120*d771ca1cSSong Gao     info->kernel_cmdline = ms->kernel_cmdline;
121*d771ca1cSSong Gao     info->initrd_filename = ms->initrd_filename;
122*d771ca1cSSong Gao 
123*d771ca1cSSong Gao     if (lams->bios_loaded) {
124*d771ca1cSSong Gao         loongarch_firmware_boot(lams, info);
125*d771ca1cSSong Gao     } else {
126*d771ca1cSSong Gao         loongarch_direct_kernel_boot(info);
127*d771ca1cSSong Gao     }
128*d771ca1cSSong Gao }
129