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