1d771ca1cSSong Gao /* SPDX-License-Identifier: GPL-2.0-or-later */ 2d771ca1cSSong Gao /* 3d771ca1cSSong Gao * LoongArch boot helper functions. 4d771ca1cSSong Gao * 5d771ca1cSSong Gao * Copyright (c) 2023 Loongson Technology Corporation Limited 6d771ca1cSSong Gao */ 7d771ca1cSSong Gao 8d771ca1cSSong Gao #include "qemu/osdep.h" 9d771ca1cSSong Gao #include "qemu/units.h" 10d771ca1cSSong Gao #include "target/loongarch/cpu.h" 11d771ca1cSSong Gao #include "hw/loongarch/virt.h" 12d771ca1cSSong Gao #include "hw/loader.h" 13d771ca1cSSong Gao #include "elf.h" 14d771ca1cSSong Gao #include "qemu/error-report.h" 15d771ca1cSSong Gao #include "sysemu/reset.h" 16d771ca1cSSong Gao #include "sysemu/qtest.h" 17d771ca1cSSong Gao 18*7e0510d7SSong Gao static const unsigned int slave_boot_code[] = { 19*7e0510d7SSong Gao /* Configure reset ebase. */ 20*7e0510d7SSong Gao 0x0400302c, /* csrwr $t0, LOONGARCH_CSR_EENTRY */ 21*7e0510d7SSong Gao 22*7e0510d7SSong Gao /* Disable interrupt. */ 23*7e0510d7SSong Gao 0x0380100c, /* ori $t0, $zero,0x4 */ 24*7e0510d7SSong Gao 0x04000180, /* csrxchg $zero, $t0, LOONGARCH_CSR_CRMD */ 25*7e0510d7SSong Gao 26*7e0510d7SSong Gao /* Clear mailbox. */ 27*7e0510d7SSong Gao 0x1400002d, /* lu12i.w $t1, 1(0x1) */ 28*7e0510d7SSong Gao 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */ 29*7e0510d7SSong Gao 0x06481da0, /* iocsrwr.d $zero, $t1 */ 30*7e0510d7SSong Gao 31*7e0510d7SSong Gao /* Enable IPI interrupt. */ 32*7e0510d7SSong Gao 0x1400002c, /* lu12i.w $t0, 1(0x1) */ 33*7e0510d7SSong Gao 0x0400118c, /* csrxchg $t0, $t0, LOONGARCH_CSR_ECFG */ 34*7e0510d7SSong Gao 0x02fffc0c, /* addi.d $t0, $r0,-1(0xfff) */ 35*7e0510d7SSong Gao 0x1400002d, /* lu12i.w $t1, 1(0x1) */ 36*7e0510d7SSong Gao 0x038011ad, /* ori $t1, $t1, CORE_EN_OFF */ 37*7e0510d7SSong Gao 0x064819ac, /* iocsrwr.w $t0, $t1 */ 38*7e0510d7SSong Gao 0x1400002d, /* lu12i.w $t1, 1(0x1) */ 39*7e0510d7SSong Gao 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */ 40*7e0510d7SSong Gao 41*7e0510d7SSong Gao /* Wait for wakeup <.L11>: */ 42*7e0510d7SSong Gao 0x06488000, /* idle 0x0 */ 43*7e0510d7SSong Gao 0x03400000, /* andi $zero, $zero, 0x0 */ 44*7e0510d7SSong Gao 0x064809ac, /* iocsrrd.w $t0, $t1 */ 45*7e0510d7SSong Gao 0x43fff59f, /* beqz $t0, -12(0x7ffff4) # 48 <.L11> */ 46*7e0510d7SSong Gao 47*7e0510d7SSong Gao /* Read and clear IPI interrupt. */ 48*7e0510d7SSong Gao 0x1400002d, /* lu12i.w $t1, 1(0x1) */ 49*7e0510d7SSong Gao 0x064809ac, /* iocsrrd.w $t0, $t1 */ 50*7e0510d7SSong Gao 0x1400002d, /* lu12i.w $t1, 1(0x1) */ 51*7e0510d7SSong Gao 0x038031ad, /* ori $t1, $t1, CORE_CLEAR_OFF */ 52*7e0510d7SSong Gao 0x064819ac, /* iocsrwr.w $t0, $t1 */ 53*7e0510d7SSong Gao 54*7e0510d7SSong Gao /* Disable IPI interrupt. */ 55*7e0510d7SSong Gao 0x1400002c, /* lu12i.w $t0, 1(0x1) */ 56*7e0510d7SSong Gao 0x04001180, /* csrxchg $zero, $t0, LOONGARCH_CSR_ECFG */ 57*7e0510d7SSong Gao 58*7e0510d7SSong Gao /* Read mail buf and jump to specified entry */ 59*7e0510d7SSong Gao 0x1400002d, /* lu12i.w $t1, 1(0x1) */ 60*7e0510d7SSong Gao 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */ 61*7e0510d7SSong Gao 0x06480dac, /* iocsrrd.d $t0, $t1 */ 62*7e0510d7SSong Gao 0x00150181, /* move $ra, $t0 */ 63*7e0510d7SSong Gao 0x4c000020, /* jirl $zero, $ra,0 */ 64*7e0510d7SSong Gao }; 65*7e0510d7SSong Gao 66d771ca1cSSong Gao static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr) 67d771ca1cSSong Gao { 68d771ca1cSSong Gao return addr & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS); 69d771ca1cSSong Gao } 70d771ca1cSSong Gao 71d771ca1cSSong Gao static int64_t load_kernel_info(struct loongarch_boot_info *info) 72d771ca1cSSong Gao { 7302307a67SSong Gao uint64_t kernel_entry, kernel_low, kernel_high, initrd_size; 7402307a67SSong Gao ram_addr_t initrd_offset; 75d771ca1cSSong Gao ssize_t kernel_size; 76d771ca1cSSong Gao 77d771ca1cSSong Gao kernel_size = load_elf(info->kernel_filename, NULL, 78d771ca1cSSong Gao cpu_loongarch_virt_to_phys, NULL, 79d771ca1cSSong Gao &kernel_entry, &kernel_low, 80d771ca1cSSong Gao &kernel_high, NULL, 0, 81d771ca1cSSong Gao EM_LOONGARCH, 1, 0); 82d771ca1cSSong Gao 83d771ca1cSSong Gao if (kernel_size < 0) { 84d771ca1cSSong Gao error_report("could not load kernel '%s': %s", 85d771ca1cSSong Gao info->kernel_filename, 86d771ca1cSSong Gao load_elf_strerror(kernel_size)); 87d771ca1cSSong Gao exit(1); 88d771ca1cSSong Gao } 8902307a67SSong Gao 9002307a67SSong Gao if (info->initrd_filename) { 9102307a67SSong Gao initrd_size = get_image_size(info->initrd_filename); 9202307a67SSong Gao if (initrd_size > 0) { 9302307a67SSong Gao initrd_offset = ROUND_UP(kernel_high + 4 * kernel_size, 64 * KiB); 9402307a67SSong Gao 9502307a67SSong Gao if (initrd_offset + initrd_size > info->ram_size) { 9602307a67SSong Gao error_report("memory too small for initial ram disk '%s'", 9702307a67SSong Gao info->initrd_filename); 9802307a67SSong Gao exit(1); 9902307a67SSong Gao } 10002307a67SSong Gao 10102307a67SSong Gao initrd_size = load_image_targphys(info->initrd_filename, initrd_offset, 10202307a67SSong Gao info->ram_size - initrd_offset); 10302307a67SSong Gao } 10402307a67SSong Gao 10502307a67SSong Gao if (initrd_size == (target_ulong)-1) { 10602307a67SSong Gao error_report("could not load initial ram disk '%s'", 10702307a67SSong Gao info->initrd_filename); 10802307a67SSong Gao exit(1); 10902307a67SSong Gao } 11002307a67SSong Gao } else { 11102307a67SSong Gao initrd_size = 0; 11202307a67SSong Gao } 11302307a67SSong Gao 114d771ca1cSSong Gao return kernel_entry; 115d771ca1cSSong Gao } 116d771ca1cSSong Gao 117d771ca1cSSong Gao static void reset_load_elf(void *opaque) 118d771ca1cSSong Gao { 119d771ca1cSSong Gao LoongArchCPU *cpu = opaque; 120d771ca1cSSong Gao CPULoongArchState *env = &cpu->env; 121d771ca1cSSong Gao 122d771ca1cSSong Gao cpu_reset(CPU(cpu)); 123d771ca1cSSong Gao if (env->load_elf) { 124d771ca1cSSong Gao cpu_set_pc(CPU(cpu), env->elf_address); 125d771ca1cSSong Gao } 126d771ca1cSSong Gao } 127d771ca1cSSong Gao 128d771ca1cSSong Gao static void fw_cfg_add_kernel_info(struct loongarch_boot_info *info, 129d771ca1cSSong Gao FWCfgState *fw_cfg) 130d771ca1cSSong Gao { 131d771ca1cSSong Gao /* 132d771ca1cSSong Gao * Expose the kernel, the command line, and the initrd in fw_cfg. 133d771ca1cSSong Gao * We don't process them here at all, it's all left to the 134d771ca1cSSong Gao * firmware. 135d771ca1cSSong Gao */ 136d771ca1cSSong Gao load_image_to_fw_cfg(fw_cfg, 137d771ca1cSSong Gao FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA, 138d771ca1cSSong Gao info->kernel_filename, 139d771ca1cSSong Gao false); 140d771ca1cSSong Gao 141d771ca1cSSong Gao if (info->initrd_filename) { 142d771ca1cSSong Gao load_image_to_fw_cfg(fw_cfg, 143d771ca1cSSong Gao FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA, 144d771ca1cSSong Gao info->initrd_filename, false); 145d771ca1cSSong Gao } 146d771ca1cSSong Gao 147d771ca1cSSong Gao if (info->kernel_cmdline) { 148d771ca1cSSong Gao fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, 149d771ca1cSSong Gao strlen(info->kernel_cmdline) + 1); 150d771ca1cSSong Gao fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, 151d771ca1cSSong Gao info->kernel_cmdline); 152d771ca1cSSong Gao } 153d771ca1cSSong Gao } 154d771ca1cSSong Gao 155d771ca1cSSong Gao static void loongarch_firmware_boot(LoongArchMachineState *lams, 156d771ca1cSSong Gao struct loongarch_boot_info *info) 157d771ca1cSSong Gao { 158d771ca1cSSong Gao fw_cfg_add_kernel_info(info, lams->fw_cfg); 159d771ca1cSSong Gao } 160d771ca1cSSong Gao 161d771ca1cSSong Gao static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info) 162d771ca1cSSong Gao { 163d771ca1cSSong Gao int64_t kernel_addr = 0; 164d771ca1cSSong Gao LoongArchCPU *lacpu; 165d771ca1cSSong Gao CPUState *cs; 166d771ca1cSSong Gao 167d771ca1cSSong Gao if (info->kernel_filename) { 168d771ca1cSSong Gao kernel_addr = load_kernel_info(info); 169d771ca1cSSong Gao } else { 170d771ca1cSSong Gao if(!qtest_enabled()) { 171d771ca1cSSong Gao error_report("Need kernel filename\n"); 172d771ca1cSSong Gao exit(1); 173d771ca1cSSong Gao } 174d771ca1cSSong Gao } 175d771ca1cSSong Gao 176*7e0510d7SSong Gao /* Load slave boot code at pflash0 . */ 177*7e0510d7SSong Gao void *boot_code = g_malloc0(VIRT_FLASH0_SIZE); 178*7e0510d7SSong Gao memcpy(boot_code, &slave_boot_code, sizeof(slave_boot_code)); 179*7e0510d7SSong Gao rom_add_blob_fixed("boot_code", boot_code, VIRT_FLASH0_SIZE, VIRT_FLASH0_BASE); 180*7e0510d7SSong Gao 181d771ca1cSSong Gao CPU_FOREACH(cs) { 182d771ca1cSSong Gao lacpu = LOONGARCH_CPU(cs); 183d771ca1cSSong Gao lacpu->env.load_elf = true; 184*7e0510d7SSong Gao if (cs == first_cpu) { 185d771ca1cSSong Gao lacpu->env.elf_address = kernel_addr; 186*7e0510d7SSong Gao } else { 187*7e0510d7SSong Gao lacpu->env.elf_address = VIRT_FLASH0_BASE; 188d771ca1cSSong Gao } 189*7e0510d7SSong Gao lacpu->env.boot_info = info; 190*7e0510d7SSong Gao } 191*7e0510d7SSong Gao 192*7e0510d7SSong Gao g_free(boot_code); 193d771ca1cSSong Gao } 194d771ca1cSSong Gao 195d771ca1cSSong Gao void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info) 196d771ca1cSSong Gao { 197d771ca1cSSong Gao LoongArchMachineState *lams = LOONGARCH_MACHINE(ms); 198d771ca1cSSong Gao int i; 199d771ca1cSSong Gao 200d771ca1cSSong Gao /* register reset function */ 201d771ca1cSSong Gao for (i = 0; i < ms->smp.cpus; i++) { 202d771ca1cSSong Gao qemu_register_reset(reset_load_elf, LOONGARCH_CPU(qemu_get_cpu(i))); 203d771ca1cSSong Gao } 204d771ca1cSSong Gao 205d771ca1cSSong Gao info->kernel_filename = ms->kernel_filename; 206d771ca1cSSong Gao info->kernel_cmdline = ms->kernel_cmdline; 207d771ca1cSSong Gao info->initrd_filename = ms->initrd_filename; 208d771ca1cSSong Gao 209d771ca1cSSong Gao if (lams->bios_loaded) { 210d771ca1cSSong Gao loongarch_firmware_boot(lams, info); 211d771ca1cSSong Gao } else { 212d771ca1cSSong Gao loongarch_direct_kernel_boot(info); 213d771ca1cSSong Gao } 214d771ca1cSSong Gao } 215