hw/loongarch: Add linux kernel booting support

There are two situations to start system by kernel file. If exists bios
option, system will boot from loaded bios file, else system will boot
from hardcoded auxcode, and jump to kernel elf entry.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Message-Id: <20220712083206.4187715-4-yangxiaojuan@loongson.cn>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Xiaojuan Yang 2022-07-12 16:32:03 +08:00 committed by Richard Henderson
parent 98afb0d4e9
commit fb1cd3a292
1 changed files with 99 additions and 15 deletions

View File

@ -103,6 +103,8 @@ static const MemoryRegionOps loongarch_virt_pm_ops = {
static struct _loaderparams {
uint64_t ram_size;
const char *kernel_filename;
const char *kernel_cmdline;
const char *initrd_filename;
} loaderparams;
static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr)
@ -352,18 +354,97 @@ static void reset_load_elf(void *opaque)
}
}
/* Load an image file into an fw_cfg entry identified by key. */
static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key,
uint16_t data_key, const char *image_name,
bool try_decompress)
{
size_t size = -1;
uint8_t *data;
if (image_name == NULL) {
return;
}
if (try_decompress) {
size = load_image_gzipped_buffer(image_name,
LOAD_IMAGE_MAX_GUNZIP_BYTES, &data);
}
if (size == (size_t)-1) {
gchar *contents;
gsize length;
if (!g_file_get_contents(image_name, &contents, &length, NULL)) {
error_report("failed to load \"%s\"", image_name);
exit(1);
}
size = length;
data = (uint8_t *)contents;
}
fw_cfg_add_i32(fw_cfg, size_key, size);
fw_cfg_add_bytes(fw_cfg, data_key, data, size);
}
static void fw_cfg_add_kernel_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,
loaderparams.kernel_filename,
false);
if (loaderparams.initrd_filename) {
load_image_to_fw_cfg(fw_cfg,
FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA,
loaderparams.initrd_filename, false);
}
if (loaderparams.kernel_cmdline) {
fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
strlen(loaderparams.kernel_cmdline) + 1);
fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA,
loaderparams.kernel_cmdline);
}
}
static void loongarch_firmware_boot(LoongArchMachineState *lams)
{
fw_cfg_add_kernel_info(lams->fw_cfg);
}
static void loongarch_direct_kernel_boot(LoongArchMachineState *lams)
{
MachineState *machine = MACHINE(lams);
int64_t kernel_addr = 0;
LoongArchCPU *lacpu;
int i;
kernel_addr = load_kernel_info();
if (!machine->firmware) {
for (i = 0; i < machine->smp.cpus; i++) {
lacpu = LOONGARCH_CPU(qemu_get_cpu(i));
lacpu->env.load_elf = true;
lacpu->env.elf_address = kernel_addr;
}
}
}
static void loongarch_init(MachineState *machine)
{
LoongArchCPU *lacpu;
const char *cpu_model = machine->cpu_type;
const char *kernel_filename = machine->kernel_filename;
ram_addr_t offset = 0;
ram_addr_t ram_size = machine->ram_size;
uint64_t highram_size = 0;
MemoryRegion *address_space_mem = get_system_memory();
LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
LoongArchCPU *lacpu;
int i;
int64_t kernel_addr = 0;
if (!cpu_model) {
cpu_model = LOONGARCH_CPU_TYPE_NAME("la464");
@ -412,20 +493,23 @@ static void loongarch_init(MachineState *machine)
memmap_table,
sizeof(struct memmap_entry) * (memmap_entries));
}
if (kernel_filename) {
loaderparams.ram_size = ram_size;
loaderparams.kernel_filename = kernel_filename;
kernel_addr = load_kernel_info();
if (!machine->firmware) {
loaderparams.kernel_filename = machine->kernel_filename;
loaderparams.kernel_cmdline = machine->kernel_cmdline;
loaderparams.initrd_filename = machine->initrd_filename;
/* load the kernel. */
if (loaderparams.kernel_filename) {
if (lams->bios_loaded) {
loongarch_firmware_boot(lams);
} else {
loongarch_direct_kernel_boot(lams);
}
}
/* register reset function */
for (i = 0; i < machine->smp.cpus; i++) {
lacpu = LOONGARCH_CPU(qemu_get_cpu(i));
lacpu->env.load_elf = true;
lacpu->env.elf_address = kernel_addr;
qemu_register_reset(reset_load_elf, lacpu);
}
}
}
/* Initialize the IO interrupt subsystem */
loongarch_irq_init(lams);
}