aboutsummaryrefslogtreecommitdiff
path: root/hw/loongarch/boot.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/loongarch/boot.c')
-rw-r--r--hw/loongarch/boot.c178
1 files changed, 108 insertions, 70 deletions
diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c
index 354cf45..8857a04 100644
--- a/hw/loongarch/boot.c
+++ b/hw/loongarch/boot.c
@@ -35,19 +35,19 @@ struct loongarch_linux_hdr {
uint32_t pe_header_offset;
} QEMU_PACKED;
-struct memmap_entry *memmap_table;
-unsigned memmap_entries;
-
-ram_addr_t initrd_offset;
-uint64_t initrd_size;
-
-static const unsigned int slave_boot_code[] = {
+static const unsigned int aux_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 */
+ 0x03400000, /* nop */
+
+ 0x0400800c, /* csrrd $t0, LOONGARCH_CSR_CPUNUM */
+ 0x034ffd8c, /* andi $t0, $t0, 0x3ff */
+ 0x0015000d, /* move $t1, $zero */
+ 0x5800718d, /* beq $t0, $t1, 112 */
/* Clear mailbox. */
0x1400002d, /* lu12i.w $t1, 1(0x1) */
@@ -87,6 +87,26 @@ static const unsigned int slave_boot_code[] = {
0x06480dac, /* iocsrrd.d $t0, $t1 */
0x00150181, /* move $ra, $t0 */
0x4c000020, /* jirl $zero, $ra,0 */
+ /* BSP Core */
+ 0x03400000, /* nop */
+ 0x1800000d, /* pcaddi $t1, 0 */
+ 0x28c0a1a4, /* ld.d $a0, $t1, 40 */
+ 0x1800000d, /* pcaddi $t1, 0 */
+ 0x28c0a1a5, /* ld.d $a1, $t1, 40 */
+ 0x1800000d, /* pcaddi $t1, 0 */
+ 0x28c0a1a6, /* ld.d $a2, $t1, 40 */
+ 0x1800000d, /* pcaddi $t1, 0 */
+ 0x28c0a1ac, /* ld.d $t0, $t1, 40 */
+ 0x00150181, /* move $ra, $t0 */
+ 0x4c000020, /* jirl $zero, $ra,0 */
+ 0x00000000, /* .dword 0 A0 */
+ 0x00000000,
+ 0x00000000, /* .dword 0 A1 */
+ 0x00000000,
+ 0x00000000, /* .dword 0 A2 */
+ 0x00000000,
+ 0x00000000, /* .dword 0 PC */
+ 0x00000000,
};
static inline void *guidcpy(void *dst, const void *src)
@@ -94,12 +114,16 @@ 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,
+static void init_efi_boot_memmap(MachineState *ms,
+ 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;
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms);
+ struct memmap_entry *memmap_table;
+ unsigned int memmap_entries;
/* efi_configuration_table 1 */
guidcpy(&systab->tables[0].guid, &tbl_guid);
@@ -111,6 +135,8 @@ static void init_efi_boot_memmap(struct efi_system_table *systab,
boot_memmap->map_size = 0;
efi_memory_desc_t *map = p + sizeof(struct efi_boot_memmap);
+ memmap_table = lvms->memmap_table;
+ memmap_entries = lvms->memmap_entries;
for (i = 0; i < memmap_entries; i++) {
map = (void *)boot_memmap + sizeof(*map);
map[i].type = memmap_table[i].type;
@@ -121,7 +147,8 @@ static void init_efi_boot_memmap(struct efi_system_table *systab,
}
}
-static void init_efi_initrd_table(struct efi_system_table *systab,
+static void init_efi_initrd_table(struct loongarch_boot_info *info,
+ struct efi_system_table *systab,
void *p, void *start)
{
efi_guid_t tbl_guid = LINUX_EFI_INITRD_MEDIA_GUID;
@@ -132,8 +159,8 @@ static void init_efi_initrd_table(struct efi_system_table *systab,
systab->tables[1].table = (struct efi_configuration_table *)(p - start);
systab->nr_tables = 2;
- initrd_table->base = initrd_offset;
- initrd_table->size = initrd_size;
+ initrd_table->base = info->initrd_addr;
+ initrd_table->size = info->initrd_size;
}
static void init_efi_fdt_table(struct efi_system_table *systab)
@@ -146,10 +173,12 @@ static void init_efi_fdt_table(struct efi_system_table *systab)
systab->nr_tables = 3;
}
-static void init_systab(struct loongarch_boot_info *info, void *p, void *start)
+static void init_systab(MachineState *ms,
+ struct loongarch_boot_info *info, void *p, void *start)
{
void *bp_tables_start;
struct efi_system_table *systab = p;
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms);
info->a2 = p - start;
@@ -166,10 +195,10 @@ static void init_systab(struct loongarch_boot_info *info, void *p, void *start)
systab->tables = p;
bp_tables_start = p;
- init_efi_boot_memmap(systab, p, start);
+ init_efi_boot_memmap(ms, 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);
+ sizeof(efi_memory_desc_t) * lvms->memmap_entries, 64 * KiB);
+ init_efi_initrd_table(info, systab, p, start);
p += ROUND_UP(sizeof(struct efi_initrd), 64 * KiB);
init_efi_fdt_table(systab);
@@ -235,9 +264,48 @@ static int64_t load_loongarch_linux_image(const char *filename,
return size;
}
+static ram_addr_t alloc_initrd_memory(struct loongarch_boot_info *info,
+ uint64_t advice_start, ssize_t rd_size)
+{
+ hwaddr base, ram_size, gap, low_end;
+ ram_addr_t initrd_end, initrd_start;
+
+ base = VIRT_LOWMEM_BASE;
+ gap = VIRT_LOWMEM_SIZE;
+ initrd_start = advice_start;
+ initrd_end = initrd_start + rd_size;
+
+ ram_size = info->ram_size;
+ low_end = base + MIN(ram_size, gap);
+ if (initrd_end <= low_end) {
+ return initrd_start;
+ }
+
+ if (ram_size <= gap) {
+ error_report("The low memory too small for initial ram disk '%s',"
+ "You need to expand the ram",
+ info->initrd_filename);
+ exit(1);
+ }
+
+ /*
+ * Try to load initrd in the high memory
+ */
+ ram_size -= gap;
+ initrd_start = VIRT_HIGHMEM_BASE;
+ if (rd_size <= ram_size) {
+ return initrd_start;
+ }
+
+ error_report("The high memory too small for initial ram disk '%s',"
+ "You need to expand the ram",
+ info->initrd_filename);
+ exit(1);
+}
+
static int64_t load_kernel_info(struct loongarch_boot_info *info)
{
- uint64_t kernel_entry, kernel_low, kernel_high;
+ uint64_t kernel_entry, kernel_low, kernel_high, initrd_offset = 0;
ssize_t kernel_size;
kernel_size = load_elf(info->kernel_filename, NULL,
@@ -245,6 +313,7 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info)
&kernel_entry, &kernel_low,
&kernel_high, NULL, ELFDATA2LSB,
EM_LOONGARCH, 1, 0);
+ kernel_entry = cpu_loongarch_virt_to_phys(NULL, kernel_entry);
if (kernel_size < 0) {
kernel_size = load_loongarch_linux_image(info->kernel_filename,
&kernel_entry, &kernel_low,
@@ -259,48 +328,28 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info)
}
if (info->initrd_filename) {
- initrd_size = get_image_size(info->initrd_filename);
+ ssize_t initrd_size = get_image_size(info->initrd_filename, NULL);
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);
+ initrd_offset = alloc_initrd_memory(info, initrd_offset,
+ initrd_size);
+ initrd_size = load_image_targphys(info->initrd_filename,
+ initrd_offset, initrd_size, NULL);
}
- if (initrd_size == (target_ulong)-1) {
+ if (initrd_size == -1) {
error_report("could not load initial ram disk '%s'",
info->initrd_filename);
exit(1);
}
- } else {
- initrd_size = 0;
+
+ info->initrd_addr = initrd_offset;
+ info->initrd_size = initrd_size;
}
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)
{
@@ -334,22 +383,23 @@ static void loongarch_firmware_boot(LoongArchVirtMachineState *lvms,
fw_cfg_add_kernel_info(info, lvms->fw_cfg);
}
-static void init_boot_rom(struct loongarch_boot_info *info, void *p)
+static void init_boot_rom(MachineState *ms,
+ struct loongarch_boot_info *info, void *p)
{
void *start = p;
init_cmdline(info, p, start);
p += COMMAND_LINE_SIZE;
- init_systab(info, p, start);
+ init_systab(ms, info, p, start);
}
-static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info)
+static void loongarch_direct_kernel_boot(MachineState *ms,
+ struct loongarch_boot_info *info)
{
void *p, *bp;
int64_t kernel_addr = VIRT_FLASH0_BASE;
- LoongArchCPU *lacpu;
- CPUState *cs;
+ uint64_t *data;
if (info->kernel_filename) {
kernel_addr = load_kernel_info(info);
@@ -362,25 +412,19 @@ static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info)
/* Load cmdline and system tables at [0 - 1 MiB] */
p = g_malloc0(1 * MiB);
bp = p;
- init_boot_rom(info, p);
+ init_boot_rom(ms, 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));
+ memcpy(boot_code, &aux_boot_code, sizeof(aux_boot_code));
+ data = (uint64_t *)(boot_code + sizeof(aux_boot_code));
+ *(data - 4) = cpu_to_le64(info->a0);
+ *(data - 3) = cpu_to_le64(info->a1);
+ *(data - 2) = cpu_to_le64(info->a2);
+ *(data - 1) = cpu_to_le64(kernel_addr);
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);
}
@@ -388,12 +432,6 @@ static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info)
void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info)
{
LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_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;
@@ -402,6 +440,6 @@ void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info)
if (lvms->bios_loaded) {
loongarch_firmware_boot(lvms, info);
} else {
- loongarch_direct_kernel_boot(info);
+ loongarch_direct_kernel_boot(ms, info);
}
}