master
c 491 lines 16.4 KB
Raw
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/core/loader.h"
13 #include "elf.h"
14 #include "qemu/error-report.h"
15 #include "system/reset.h"
16 #include "system/qtest.h"
17
18 /*
19 * Linux Image Format
20 * https://docs.kernel.org/arch/loongarch/booting.html
21 */
22 #define LINUX_PE_MAGIC 0x818223cd
23 #define MZ_MAGIC 0x5a4d /* "MZ" */
24
25 struct loongarch_linux_hdr {
26 uint32_t mz_magic;
27 uint32_t res0;
28 uint64_t kernel_entry;
29 uint64_t kernel_size;
30 uint64_t load_offset;
31 uint64_t res1;
32 uint64_t res2;
33 uint64_t res3;
34 uint32_t linux_pe_magic;
35 uint32_t pe_header_offset;
36 } QEMU_PACKED;
37
38 static const unsigned int aux_boot_code[] = {
39 /* Configure reset ebase. */
40 0x0400302c, /* csrwr $t0, LOONGARCH_CSR_EENTRY */
41
42 /* Disable interrupt. */
43 0x0380100c, /* ori $t0, $zero,0x4 */
44 0x04000180, /* csrxchg $zero, $t0, LOONGARCH_CSR_CRMD */
45 0x03400000, /* nop */
46
47 0x0400800c, /* csrrd $t0, LOONGARCH_CSR_CPUNUM */
48 0x034ffd8c, /* andi $t0, $t0, 0x3ff */
49 0x0015000d, /* move $t1, $zero */
50 0x5800718d, /* beq $t0, $t1, 112 */
51
52 /* Clear mailbox. */
53 0x1400002d, /* lu12i.w $t1, 1(0x1) */
54 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */
55 0x06481da0, /* iocsrwr.d $zero, $t1 */
56
57 /* Enable IPI interrupt. */
58 0x1400002c, /* lu12i.w $t0, 1(0x1) */
59 0x0400118c, /* csrxchg $t0, $t0, LOONGARCH_CSR_ECFG */
60 0x02fffc0c, /* addi.d $t0, $r0,-1(0xfff) */
61 0x1400002d, /* lu12i.w $t1, 1(0x1) */
62 0x038011ad, /* ori $t1, $t1, CORE_EN_OFF */
63 0x064819ac, /* iocsrwr.w $t0, $t1 */
64 0x1400002d, /* lu12i.w $t1, 1(0x1) */
65 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */
66
67 /* Wait for wakeup <.L11>: */
68 0x06488000, /* idle 0x0 */
69 0x03400000, /* andi $zero, $zero, 0x0 */
70 0x064809ac, /* iocsrrd.w $t0, $t1 */
71 0x43fff59f, /* beqz $t0, -12(0x7ffff4) # 48 <.L11> */
72
73 /* Read and clear IPI interrupt. */
74 0x1400002d, /* lu12i.w $t1, 1(0x1) */
75 0x064809ac, /* iocsrrd.w $t0, $t1 */
76 0x1400002d, /* lu12i.w $t1, 1(0x1) */
77 0x038031ad, /* ori $t1, $t1, CORE_CLEAR_OFF */
78 0x064819ac, /* iocsrwr.w $t0, $t1 */
79
80 /* Disable IPI interrupt. */
81 0x1400002c, /* lu12i.w $t0, 1(0x1) */
82 0x04001180, /* csrxchg $zero, $t0, LOONGARCH_CSR_ECFG */
83
84 /* Read mail buf and jump to specified entry */
85 0x1400002d, /* lu12i.w $t1, 1(0x1) */
86 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */
87 0x06480dac, /* iocsrrd.d $t0, $t1 */
88 0x00150181, /* move $ra, $t0 */
89 0x4c000020, /* jirl $zero, $ra,0 */
90 /* BSP Core */
91 0x03400000, /* nop */
92 0x1800000d, /* pcaddi $t1, 0 */
93 0x28c0a1a4, /* ld.d $a0, $t1, 40 */
94 0x1800000d, /* pcaddi $t1, 0 */
95 0x28c0a1a5, /* ld.d $a1, $t1, 40 */
96 0x1800000d, /* pcaddi $t1, 0 */
97 0x28c0a1a6, /* ld.d $a2, $t1, 40 */
98 0x1800000d, /* pcaddi $t1, 0 */
99 0x28c0a1ac, /* ld.d $t0, $t1, 40 */
100 0x00150181, /* move $ra, $t0 */
101 0x4c000020, /* jirl $zero, $ra,0 */
102 0x00000000, /* .dword 0 A0 */
103 0x00000000,
104 0x00000000, /* .dword 0 A1 */
105 0x00000000,
106 0x00000000, /* .dword 0 A2 */
107 0x00000000,
108 0x00000000, /* .dword 0 PC */
109 0x00000000,
110 };
111
112 static inline void *guidcpy(void *dst, const void *src)
113 {
114 return memcpy(dst, src, sizeof(efi_guid_t));
115 }
116
117 static void init_efi_boot_memmap(MachineState *ms,
118 struct efi_system_table *systab,
119 void *p, void *start)
120 {
121 unsigned i;
122 struct efi_boot_memmap *boot_memmap = p;
123 efi_guid_t tbl_guid = LINUX_EFI_BOOT_MEMMAP_GUID;
124 LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms);
125 struct memmap_entry *memmap_table;
126 unsigned int memmap_entries;
127
128 /* efi_configuration_table 1 */
129 guidcpy(&systab->tables[0].guid, &tbl_guid);
130 systab->tables[0].table = (struct efi_configuration_table *)(p - start);
131 systab->nr_tables = 1;
132
133 boot_memmap->desc_size = sizeof(efi_memory_desc_t);
134 boot_memmap->desc_ver = 1;
135 boot_memmap->map_size = 0;
136
137 efi_memory_desc_t *map = p + sizeof(struct efi_boot_memmap);
138 memmap_table = lvms->memmap_table;
139 memmap_entries = lvms->memmap_entries;
140 for (i = 0; i < memmap_entries; i++) {
141 map = (void *)boot_memmap + sizeof(*map);
142 map[i].type = memmap_table[i].type;
143 map[i].phys_addr = ROUND_UP(memmap_table[i].address, 64 * KiB);
144 map[i].num_pages = ROUND_DOWN(memmap_table[i].address +
145 memmap_table[i].length - map[i].phys_addr, 64 * KiB);
146 p += sizeof(efi_memory_desc_t);
147 }
148 }
149
150 static void init_efi_initrd_table(struct loongarch_boot_info *info,
151 struct efi_system_table *systab,
152 void *p, void *start)
153 {
154 efi_guid_t tbl_guid = LINUX_EFI_INITRD_MEDIA_GUID;
155 struct efi_initrd *initrd_table = p;
156
157 /* efi_configuration_table 2 */
158 guidcpy(&systab->tables[1].guid, &tbl_guid);
159 systab->tables[1].table = (struct efi_configuration_table *)(p - start);
160 systab->nr_tables = 2;
161
162 initrd_table->base = info->initrd_addr;
163 initrd_table->size = info->initrd_size;
164 }
165
166 static void init_efi_fdt_table(struct efi_system_table *systab)
167 {
168 efi_guid_t tbl_guid = DEVICE_TREE_GUID;
169
170 /* efi_configuration_table 3 */
171 guidcpy(&systab->tables[2].guid, &tbl_guid);
172 systab->tables[2].table = (void *)FDT_BASE;
173 systab->nr_tables = 3;
174 }
175
176 static void init_systab(MachineState *ms, size_t size,
177 struct loongarch_boot_info *info, void *p, void *start)
178 {
179 void *bp_tables_start;
180 struct efi_system_table *systab = p;
181 LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms);
182 size_t len;
183
184 len = ROUND_UP(sizeof(struct efi_system_table), 64 * KiB);
185 if (len > size) {
186 error_report("could not init efi_system_table");
187 exit(1);
188 }
189
190 info->a2 = p - start;
191
192 systab->hdr.signature = EFI_SYSTEM_TABLE_SIGNATURE;
193 systab->hdr.revision = EFI_SPECIFICATION_VERSION;
194 systab->hdr.revision = sizeof(struct efi_system_table),
195 systab->fw_revision = FW_VERSION << 16 | FW_PATCHLEVEL << 8;
196 systab->runtime = 0;
197 systab->boottime = 0;
198 systab->nr_tables = 0;
199
200 p += len;
201 size -= len;
202
203 systab->tables = p;
204 bp_tables_start = p;
205
206 len = ROUND_UP(sizeof(struct efi_boot_memmap) +
207 sizeof(efi_memory_desc_t) * lvms->memmap_entries, 64 * KiB);
208 if (len > size) {
209 error_report("could not init efi_boot_memmap");
210 exit(1);
211 }
212
213 init_efi_boot_memmap(ms, systab, p, start);
214 p += len;
215 size -= len;
216
217 len = ROUND_UP(sizeof(struct efi_initrd), 64 * KiB);
218 if (len > size) {
219 error_report("could not init efi_initrd");
220 exit(1);
221 }
222
223 init_efi_initrd_table(info, systab, p, start);
224 p += len;
225 size -= len;
226 init_efi_fdt_table(systab);
227
228 systab->tables = (struct efi_configuration_table *)(bp_tables_start - start);
229 }
230
231 static size_t init_cmdline(struct loongarch_boot_info *info, void *p,
232 void *start, size_t size)
233 {
234 hwaddr cmdline_addr = p - start;
235 size_t len;
236
237 info->a0 = 1;
238 info->a1 = cmdline_addr;
239
240 len = g_strlcpy(p, info->kernel_cmdline, size);
241
242 /* include terminating null byte */
243 if (len < size) {
244 len += 1;
245 } else {
246 len = size;
247 }
248
249 return len;
250 }
251
252 static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr)
253 {
254 uint64_t *phys_addr_mask = opaque;
255 return addr & *phys_addr_mask;
256 }
257
258 static int64_t load_loongarch_linux_image(const char *filename,
259 uint64_t phys_addr_mask,
260 uint64_t *kernel_entry,
261 uint64_t *kernel_low,
262 uint64_t *kernel_high)
263 {
264 gsize len;
265 ssize_t size;
266 uint8_t *buffer;
267 struct loongarch_linux_hdr *hdr;
268
269 /* Load as raw file otherwise */
270 if (!g_file_get_contents(filename, (char **)&buffer, &len, NULL)) {
271 return -1;
272 }
273 size = len;
274
275 /* Unpack the image if it is a EFI zboot image */
276 if (unpack_efi_zboot_image(&buffer, &size) < 0) {
277 g_free(buffer);
278 return -1;
279 }
280
281 hdr = (struct loongarch_linux_hdr *)buffer;
282
283 if (extract32(le32_to_cpu(hdr->mz_magic), 0, 16) != MZ_MAGIC ||
284 le32_to_cpu(hdr->linux_pe_magic) != LINUX_PE_MAGIC) {
285 g_free(buffer);
286 return -1;
287 }
288
289 /* Early kernel versions may have those fields in virtual address */
290 *kernel_entry = le64_to_cpu(hdr->kernel_entry) & phys_addr_mask;
291 *kernel_low = le64_to_cpu(hdr->load_offset) & phys_addr_mask;
292 *kernel_high = *kernel_low + size;
293
294 rom_add_blob_fixed(filename, buffer, size, *kernel_low);
295
296 g_free(buffer);
297
298 return size;
299 }
300
301 static ram_addr_t alloc_initrd_memory(struct loongarch_boot_info *info,
302 uint64_t advice_start, ssize_t rd_size)
303 {
304 hwaddr base, ram_size, gap, low_end;
305 ram_addr_t initrd_end, initrd_start;
306
307 base = VIRT_LOWMEM_BASE;
308 gap = VIRT_LOWMEM_SIZE;
309 initrd_start = advice_start;
310 initrd_end = initrd_start + rd_size;
311
312 ram_size = info->ram_size;
313 low_end = base + MIN(ram_size, gap);
314 if (initrd_end <= low_end) {
315 return initrd_start;
316 }
317
318 if (ram_size <= gap) {
319 error_report("The low memory too small for initial ram disk '%s',"
320 "You need to expand the ram",
321 info->initrd_filename);
322 exit(1);
323 }
324
325 /*
326 * Try to load initrd in the high memory
327 */
328 ram_size -= gap;
329 initrd_start = VIRT_HIGHMEM_BASE;
330 if (rd_size <= ram_size) {
331 return initrd_start;
332 }
333
334 error_report("The high memory too small for initial ram disk '%s',"
335 "You need to expand the ram",
336 info->initrd_filename);
337 exit(1);
338 }
339
340 static int64_t load_kernel_info(struct loongarch_boot_info *info,
341 uint64_t phys_addr_mask)
342 {
343 uint64_t kernel_entry, kernel_low, kernel_high, initrd_offset = 0;
344 ssize_t kernel_size;
345
346 kernel_size = load_elf(info->kernel_filename, NULL,
347 cpu_loongarch_virt_to_phys, &phys_addr_mask,
348 &kernel_entry, &kernel_low,
349 &kernel_high, NULL, ELFDATA2LSB,
350 EM_LOONGARCH, 1, 0);
351 kernel_entry = cpu_loongarch_virt_to_phys(&phys_addr_mask, kernel_entry);
352 if (kernel_size < 0) {
353 kernel_size = load_loongarch_linux_image(info->kernel_filename,
354 phys_addr_mask,
355 &kernel_entry, &kernel_low,
356 &kernel_high);
357 }
358
359 if (kernel_size < 0) {
360 error_report("could not load kernel '%s': %s",
361 info->kernel_filename,
362 load_elf_strerror(kernel_size));
363 exit(1);
364 }
365
366 if (info->initrd_filename) {
367 ssize_t initrd_size = get_image_size(info->initrd_filename, NULL);
368 if (initrd_size > 0) {
369 initrd_offset = ROUND_UP(kernel_high + 4 * kernel_size, 64 * KiB);
370 initrd_offset = alloc_initrd_memory(info, initrd_offset,
371 initrd_size);
372 initrd_size = load_image_targphys(info->initrd_filename,
373 initrd_offset, initrd_size, NULL);
374 }
375
376 if (initrd_size == -1) {
377 error_report("could not load initial ram disk '%s'",
378 info->initrd_filename);
379 exit(1);
380 }
381
382 info->initrd_addr = initrd_offset;
383 info->initrd_size = initrd_size;
384 }
385
386 return kernel_entry;
387 }
388
389 static void fw_cfg_add_kernel_info(struct loongarch_boot_info *info,
390 FWCfgState *fw_cfg)
391 {
392 /*
393 * Expose the kernel, the command line, and the initrd in fw_cfg.
394 * We don't process them here at all, it's all left to the
395 * firmware.
396 */
397 load_image_to_fw_cfg(fw_cfg,
398 FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA,
399 info->kernel_filename,
400 false);
401
402 if (info->initrd_filename) {
403 load_image_to_fw_cfg(fw_cfg,
404 FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA,
405 info->initrd_filename, false);
406 }
407
408 if (info->kernel_cmdline) {
409 fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
410 strlen(info->kernel_cmdline) + 1);
411 fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA,
412 info->kernel_cmdline);
413 }
414 }
415
416 static void loongarch_firmware_boot(LoongArchVirtMachineState *lvms,
417 struct loongarch_boot_info *info)
418 {
419 fw_cfg_add_kernel_info(info, lvms->fw_cfg);
420 }
421
422 static void init_boot_rom(MachineState *ms, size_t size,
423 struct loongarch_boot_info *info, void *p)
424 {
425 void *start = p;
426 size_t len;
427
428 len = init_cmdline(info, p, start, size);
429 len = ROUND_UP(len, 64 * KiB);
430 if (len > size) {
431 error_report("could not init systab");
432 exit(1);
433 }
434
435 p += len;
436 init_systab(ms, size - len, info, p, start);
437 }
438
439 static void loongarch_direct_kernel_boot(MachineState *ms,
440 struct loongarch_boot_info *info,
441 uint64_t phys_addr_mask)
442 {
443 void *p, *bp;
444 int64_t kernel_addr = VIRT_FLASH0_BASE;
445 uint64_t *data;
446 size_t size;
447
448 if (info->kernel_filename) {
449 kernel_addr = load_kernel_info(info, phys_addr_mask);
450 } else {
451 if (!qtest_enabled()) {
452 warn_report("No kernel provided, booting from flash drive.");
453 }
454 }
455
456 /* Load cmdline and system tables at [0 - 1 MiB] */
457 p = g_malloc0(1 * MiB);
458 bp = p;
459 size = 1 * MiB;
460 init_boot_rom(ms, size, info, p);
461 rom_add_blob_fixed_as("boot_info", bp, size, 0, &address_space_memory);
462
463 /* Load slave boot code at pflash0 . */
464 void *boot_code = g_malloc0(VIRT_FLASH0_SIZE);
465 memcpy(boot_code, &aux_boot_code, sizeof(aux_boot_code));
466 data = (uint64_t *)(boot_code + sizeof(aux_boot_code));
467 *(data - 4) = cpu_to_le64(info->a0);
468 *(data - 3) = cpu_to_le64(info->a1);
469 *(data - 2) = cpu_to_le64(info->a2);
470 *(data - 1) = cpu_to_le64(kernel_addr);
471 rom_add_blob_fixed("boot_code", boot_code, VIRT_FLASH0_SIZE, VIRT_FLASH0_BASE);
472
473 g_free(boot_code);
474 g_free(bp);
475 }
476
477 void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info,
478 uint64_t phys_addr_mask)
479 {
480 LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms);
481
482 info->kernel_filename = ms->kernel_filename;
483 info->kernel_cmdline = ms->kernel_cmdline;
484 info->initrd_filename = ms->initrd_filename;
485
486 if (lvms->bios_loaded) {
487 loongarch_firmware_boot(lvms, info);
488 } else {
489 loongarch_direct_kernel_boot(ms, info, phys_addr_mask);
490 }
491 }