-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge tag 'pull-loongarch-20240429' of https://gitlab.com/gaosong/qemu …
…into staging Add boot LoongArch elf kernel with FDT v2: Fix 'make check-tcg' fail. # -----BEGIN PGP SIGNATURE----- # # iLMEAAEKAB0WIQS4/x2g0v3LLaCcbCxAov/yOSY+3wUCZi8F3AAKCRBAov/yOSY+ # 35VrBADb6f1mYNUTG5iDvKppvA8RG1TybxfXfgA+Z9vPkJqFkT6wt8J+JFwgh3UT # w0xY2Y2xZkPjxnUpEhZiVJjp5hk+BEzr3vE4M5DzKC1vpCYpbojxxN6FB41Up862 # kS7slW6XsZgKpLLvUkFttPt3G4DUN29CscVgy4Ci0zrqyNjnsw== # =ufbc # -----END PGP SIGNATURE----- # gpg: Signature made Sun 28 Apr 2024 07:28:44 PM PDT # gpg: using RSA key B8FF1DA0D2FDCB2DA09C6C2C40A2FFF239263EDF # gpg: Good signature from "Song Gao <m17746591750@163.com>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: B8FF 1DA0 D2FD CB2D A09C 6C2C 40A2 FFF2 3926 3EDF * tag 'pull-loongarch-20240429' of https://gitlab.com/gaosong/qemu: hw/loongarch: Add cells missing from rtc node hw/loongarch: Add cells missing from uart node hw/loongarch: fdt remove unused irqchip node hw/loongarch: fdt adds pcie irq_map node hw/loongarch: fdt adds pch_msi Controller hw/loongarch: fdt adds pch_pic Controller hw/loongarch: fdt adds Extend I/O Interrupt Controller hw/loongarch: fdt adds cpu interrupt controller node hw/loongarch: Fix fdt memory node wrong 'reg' hw/loongarch: Init efi_fdt table hw/loongarch: Init efi_initrd table hw/loongarch: Init efi_boot_memmap table hw/loongarch: Init efi_system_table hw/loongarch: Add init_cmdline hw/loongarch: Add slave cpu boot_code hw/loongarch: Add load initrd hw/loongarch: Move boot functions to boot.c Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
- Loading branch information
Showing
8 changed files
with
670 additions
and
162 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,336 @@ | ||
/* SPDX-License-Identifier: GPL-2.0-or-later */ | ||
/* | ||
* LoongArch boot helper functions. | ||
* | ||
* Copyright (c) 2023 Loongson Technology Corporation Limited | ||
*/ | ||
|
||
#include "qemu/osdep.h" | ||
#include "qemu/units.h" | ||
#include "target/loongarch/cpu.h" | ||
#include "hw/loongarch/virt.h" | ||
#include "hw/loader.h" | ||
#include "elf.h" | ||
#include "qemu/error-report.h" | ||
#include "sysemu/reset.h" | ||
#include "sysemu/qtest.h" | ||
|
||
ram_addr_t initrd_offset; | ||
uint64_t initrd_size; | ||
|
||
static const unsigned int slave_boot_code[] = { | ||
/* Configure reset ebase. */ | ||
0x0400302c, /* csrwr $t0, LOONGARCH_CSR_EENTRY */ | ||
|
||
/* Disable interrupt. */ | ||
0x0380100c, /* ori $t0, $zero,0x4 */ | ||
0x04000180, /* csrxchg $zero, $t0, LOONGARCH_CSR_CRMD */ | ||
|
||
/* Clear mailbox. */ | ||
0x1400002d, /* lu12i.w $t1, 1(0x1) */ | ||
0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */ | ||
0x06481da0, /* iocsrwr.d $zero, $t1 */ | ||
|
||
/* Enable IPI interrupt. */ | ||
0x1400002c, /* lu12i.w $t0, 1(0x1) */ | ||
0x0400118c, /* csrxchg $t0, $t0, LOONGARCH_CSR_ECFG */ | ||
0x02fffc0c, /* addi.d $t0, $r0,-1(0xfff) */ | ||
0x1400002d, /* lu12i.w $t1, 1(0x1) */ | ||
0x038011ad, /* ori $t1, $t1, CORE_EN_OFF */ | ||
0x064819ac, /* iocsrwr.w $t0, $t1 */ | ||
0x1400002d, /* lu12i.w $t1, 1(0x1) */ | ||
0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */ | ||
|
||
/* Wait for wakeup <.L11>: */ | ||
0x06488000, /* idle 0x0 */ | ||
0x03400000, /* andi $zero, $zero, 0x0 */ | ||
0x064809ac, /* iocsrrd.w $t0, $t1 */ | ||
0x43fff59f, /* beqz $t0, -12(0x7ffff4) # 48 <.L11> */ | ||
|
||
/* Read and clear IPI interrupt. */ | ||
0x1400002d, /* lu12i.w $t1, 1(0x1) */ | ||
0x064809ac, /* iocsrrd.w $t0, $t1 */ | ||
0x1400002d, /* lu12i.w $t1, 1(0x1) */ | ||
0x038031ad, /* ori $t1, $t1, CORE_CLEAR_OFF */ | ||
0x064819ac, /* iocsrwr.w $t0, $t1 */ | ||
|
||
/* Disable IPI interrupt. */ | ||
0x1400002c, /* lu12i.w $t0, 1(0x1) */ | ||
0x04001180, /* csrxchg $zero, $t0, LOONGARCH_CSR_ECFG */ | ||
|
||
/* Read mail buf and jump to specified entry */ | ||
0x1400002d, /* lu12i.w $t1, 1(0x1) */ | ||
0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */ | ||
0x06480dac, /* iocsrrd.d $t0, $t1 */ | ||
0x00150181, /* move $ra, $t0 */ | ||
0x4c000020, /* jirl $zero, $ra,0 */ | ||
}; | ||
|
||
static inline void *guidcpy(void *dst, const void *src) | ||
{ | ||
return memcpy(dst, src, sizeof(efi_guid_t)); | ||
} | ||
|
||
static void init_efi_boot_memmap(struct efi_system_table *systab, | ||
void *p, void *start) | ||
{ | ||
unsigned i; | ||
struct efi_boot_memmap *boot_memmap = p; | ||
efi_guid_t tbl_guid = LINUX_EFI_BOOT_MEMMAP_GUID; | ||
|
||
/* efi_configuration_table 1 */ | ||
guidcpy(&systab->tables[0].guid, &tbl_guid); | ||
systab->tables[0].table = (struct efi_configuration_table *)(p - start); | ||
systab->nr_tables = 1; | ||
|
||
boot_memmap->desc_size = sizeof(efi_memory_desc_t); | ||
boot_memmap->desc_ver = 1; | ||
boot_memmap->map_size = 0; | ||
|
||
efi_memory_desc_t *map = p + sizeof(struct efi_boot_memmap); | ||
for (i = 0; i < memmap_entries; i++) { | ||
map = (void *)boot_memmap + sizeof(*map); | ||
map[i].type = memmap_table[i].type; | ||
map[i].phys_addr = ROUND_UP(memmap_table[i].address, 64 * KiB); | ||
map[i].num_pages = ROUND_DOWN(memmap_table[i].address + | ||
memmap_table[i].length - map[i].phys_addr, 64 * KiB); | ||
p += sizeof(efi_memory_desc_t); | ||
} | ||
} | ||
|
||
static void init_efi_initrd_table(struct efi_system_table *systab, | ||
void *p, void *start) | ||
{ | ||
efi_guid_t tbl_guid = LINUX_EFI_INITRD_MEDIA_GUID; | ||
struct efi_initrd *initrd_table = p; | ||
|
||
/* efi_configuration_table 2 */ | ||
guidcpy(&systab->tables[1].guid, &tbl_guid); | ||
systab->tables[1].table = (struct efi_configuration_table *)(p - start); | ||
systab->nr_tables = 2; | ||
|
||
initrd_table->base = initrd_offset; | ||
initrd_table->size = initrd_size; | ||
} | ||
|
||
static void init_efi_fdt_table(struct efi_system_table *systab) | ||
{ | ||
efi_guid_t tbl_guid = DEVICE_TREE_GUID; | ||
|
||
/* efi_configuration_table 3 */ | ||
guidcpy(&systab->tables[2].guid, &tbl_guid); | ||
systab->tables[2].table = (void *)FDT_BASE; | ||
systab->nr_tables = 3; | ||
} | ||
|
||
static void init_systab(struct loongarch_boot_info *info, void *p, void *start) | ||
{ | ||
void *bp_tables_start; | ||
struct efi_system_table *systab = p; | ||
|
||
info->a2 = p - start; | ||
|
||
systab->hdr.signature = EFI_SYSTEM_TABLE_SIGNATURE; | ||
systab->hdr.revision = EFI_SPECIFICATION_VERSION; | ||
systab->hdr.revision = sizeof(struct efi_system_table), | ||
systab->fw_revision = FW_VERSION << 16 | FW_PATCHLEVEL << 8; | ||
systab->runtime = 0; | ||
systab->boottime = 0; | ||
systab->nr_tables = 0; | ||
|
||
p += ROUND_UP(sizeof(struct efi_system_table), 64 * KiB); | ||
|
||
systab->tables = p; | ||
bp_tables_start = p; | ||
|
||
init_efi_boot_memmap(systab, p, start); | ||
p += ROUND_UP(sizeof(struct efi_boot_memmap) + | ||
sizeof(efi_memory_desc_t) * memmap_entries, 64 * KiB); | ||
init_efi_initrd_table(systab, p, start); | ||
p += ROUND_UP(sizeof(struct efi_initrd), 64 * KiB); | ||
init_efi_fdt_table(systab); | ||
|
||
systab->tables = (struct efi_configuration_table *)(bp_tables_start - start); | ||
} | ||
|
||
static void init_cmdline(struct loongarch_boot_info *info, void *p, void *start) | ||
{ | ||
hwaddr cmdline_addr = p - start; | ||
|
||
info->a0 = 1; | ||
info->a1 = cmdline_addr; | ||
|
||
memcpy(p, info->kernel_cmdline, COMMAND_LINE_SIZE); | ||
} | ||
|
||
static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr) | ||
{ | ||
return addr & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS); | ||
} | ||
|
||
static int64_t load_kernel_info(struct loongarch_boot_info *info) | ||
{ | ||
uint64_t kernel_entry, kernel_low, kernel_high; | ||
ssize_t kernel_size; | ||
|
||
kernel_size = load_elf(info->kernel_filename, NULL, | ||
cpu_loongarch_virt_to_phys, NULL, | ||
&kernel_entry, &kernel_low, | ||
&kernel_high, NULL, 0, | ||
EM_LOONGARCH, 1, 0); | ||
|
||
if (kernel_size < 0) { | ||
error_report("could not load kernel '%s': %s", | ||
info->kernel_filename, | ||
load_elf_strerror(kernel_size)); | ||
exit(1); | ||
} | ||
|
||
if (info->initrd_filename) { | ||
initrd_size = get_image_size(info->initrd_filename); | ||
if (initrd_size > 0) { | ||
initrd_offset = ROUND_UP(kernel_high + 4 * kernel_size, 64 * KiB); | ||
|
||
if (initrd_offset + initrd_size > info->ram_size) { | ||
error_report("memory too small for initial ram disk '%s'", | ||
info->initrd_filename); | ||
exit(1); | ||
} | ||
|
||
initrd_size = load_image_targphys(info->initrd_filename, initrd_offset, | ||
info->ram_size - initrd_offset); | ||
} | ||
|
||
if (initrd_size == (target_ulong)-1) { | ||
error_report("could not load initial ram disk '%s'", | ||
info->initrd_filename); | ||
exit(1); | ||
} | ||
} else { | ||
initrd_size = 0; | ||
} | ||
|
||
return kernel_entry; | ||
} | ||
|
||
static void reset_load_elf(void *opaque) | ||
{ | ||
LoongArchCPU *cpu = opaque; | ||
CPULoongArchState *env = &cpu->env; | ||
|
||
cpu_reset(CPU(cpu)); | ||
if (env->load_elf) { | ||
if (cpu == LOONGARCH_CPU(first_cpu)) { | ||
env->gpr[4] = env->boot_info->a0; | ||
env->gpr[5] = env->boot_info->a1; | ||
env->gpr[6] = env->boot_info->a2; | ||
} | ||
cpu_set_pc(CPU(cpu), env->elf_address); | ||
} | ||
} | ||
|
||
static void fw_cfg_add_kernel_info(struct loongarch_boot_info *info, | ||
FWCfgState *fw_cfg) | ||
{ | ||
/* | ||
* Expose the kernel, the command line, and the initrd in fw_cfg. | ||
* We don't process them here at all, it's all left to the | ||
* firmware. | ||
*/ | ||
load_image_to_fw_cfg(fw_cfg, | ||
FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA, | ||
info->kernel_filename, | ||
false); | ||
|
||
if (info->initrd_filename) { | ||
load_image_to_fw_cfg(fw_cfg, | ||
FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA, | ||
info->initrd_filename, false); | ||
} | ||
|
||
if (info->kernel_cmdline) { | ||
fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, | ||
strlen(info->kernel_cmdline) + 1); | ||
fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, | ||
info->kernel_cmdline); | ||
} | ||
} | ||
|
||
static void loongarch_firmware_boot(LoongArchMachineState *lams, | ||
struct loongarch_boot_info *info) | ||
{ | ||
fw_cfg_add_kernel_info(info, lams->fw_cfg); | ||
} | ||
|
||
static void init_boot_rom(struct loongarch_boot_info *info, void *p) | ||
{ | ||
void *start = p; | ||
|
||
init_cmdline(info, p, start); | ||
p += COMMAND_LINE_SIZE; | ||
|
||
init_systab(info, p, start); | ||
} | ||
|
||
static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info) | ||
{ | ||
void *p, *bp; | ||
int64_t kernel_addr = 0; | ||
LoongArchCPU *lacpu; | ||
CPUState *cs; | ||
|
||
if (info->kernel_filename) { | ||
kernel_addr = load_kernel_info(info); | ||
} else { | ||
if(!qtest_enabled()) { | ||
error_report("Need kernel filename\n"); | ||
exit(1); | ||
} | ||
} | ||
|
||
/* Load cmdline and system tables at [0 - 1 MiB] */ | ||
p = g_malloc0(1 * MiB); | ||
bp = p; | ||
init_boot_rom(info, p); | ||
rom_add_blob_fixed_as("boot_info", bp, 1 * MiB, 0, &address_space_memory); | ||
|
||
/* Load slave boot code at pflash0 . */ | ||
void *boot_code = g_malloc0(VIRT_FLASH0_SIZE); | ||
memcpy(boot_code, &slave_boot_code, sizeof(slave_boot_code)); | ||
rom_add_blob_fixed("boot_code", boot_code, VIRT_FLASH0_SIZE, VIRT_FLASH0_BASE); | ||
|
||
CPU_FOREACH(cs) { | ||
lacpu = LOONGARCH_CPU(cs); | ||
lacpu->env.load_elf = true; | ||
if (cs == first_cpu) { | ||
lacpu->env.elf_address = kernel_addr; | ||
} else { | ||
lacpu->env.elf_address = VIRT_FLASH0_BASE; | ||
} | ||
lacpu->env.boot_info = info; | ||
} | ||
|
||
g_free(boot_code); | ||
g_free(bp); | ||
} | ||
|
||
void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info) | ||
{ | ||
LoongArchMachineState *lams = LOONGARCH_MACHINE(ms); | ||
int i; | ||
|
||
/* register reset function */ | ||
for (i = 0; i < ms->smp.cpus; i++) { | ||
qemu_register_reset(reset_load_elf, LOONGARCH_CPU(qemu_get_cpu(i))); | ||
} | ||
|
||
info->kernel_filename = ms->kernel_filename; | ||
info->kernel_cmdline = ms->kernel_cmdline; | ||
info->initrd_filename = ms->initrd_filename; | ||
|
||
if (lams->bios_loaded) { | ||
loongarch_firmware_boot(lams, info); | ||
} else { | ||
loongarch_direct_kernel_boot(info); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.