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