Skip to content

Commit 240ecc2

Browse files
committed
protos/linux_risc: add loongarch64 support
LoongArch kernels have more address space requirements than aarch64/ riscv64. All params (commandline, initrd, etc.) should be passed as EFI configuration tables. This has been tested on qemu with ACPI on. DTB support isn't tested and isn't really matter, either. Most LoongArch devices come with an ACPI-compatible firmware. Signed-off-by: Yao Zi <[email protected]>
1 parent 3f57bb5 commit 240ecc2

File tree

2 files changed

+55
-7
lines changed

2 files changed

+55
-7
lines changed

common/menu.c

-5
Original file line numberDiff line numberDiff line change
@@ -1190,12 +1190,7 @@ noreturn void boot(char *config) {
11901190
if (!strcmp(proto, "limine")) {
11911191
limine_load(config, cmdline);
11921192
} else if (!strcmp(proto, "linux")) {
1193-
#if defined (__loongarch64)
1194-
quiet = false;
1195-
print("TODO: Linux is not available on LoongArch64.\n\n");
1196-
#else
11971193
linux_load(config, cmdline);
1198-
#endif
11991194
} else if (!strcmp(proto, "multiboot1") || !strcmp(proto, "multiboot")) {
12001195
#if defined (__x86_64__) || defined (__i386__)
12011196
multiboot1_load(config, cmdline);

common/protos/linux_risc.c

+55-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#if defined(__riscv) || defined(__aarch64__)
1+
#if defined(__riscv) || defined(__aarch64__) || defined(__loongarch__)
22

33
#include <stdint.h>
44
#include <stddef.h>
@@ -22,6 +22,7 @@
2222
// kernel headers released under GPL-2.0 WITH Linux-syscall-note
2323
// allowing their inclusion in non GPL compliant code.
2424

25+
#if defined(__riscv) || defined(__aarch64__)
2526
struct linux_header {
2627
uint32_t code0;
2728
uint32_t code1;
@@ -35,6 +36,22 @@ struct linux_header {
3536
uint32_t magic2;
3637
uint32_t res4;
3738
} __attribute__((packed));
39+
#elif defined(__loongarch__)
40+
struct linux_header {
41+
uint32_t mz;
42+
uint32_t res0;
43+
uint64_t kernel_entry;
44+
uint64_t image_size;
45+
uint64_t load_offset;
46+
uint64_t res1;
47+
uint64_t res2;
48+
uint64_t res3;
49+
uint32_t magic2; // LINUX_PE_MAGIC
50+
uint32_t pe_offset;
51+
} __attribute__((packed));
52+
#else
53+
#error "Unknown architecture"
54+
#endif
3855

3956
struct linux_efi_memreserve {
4057
int size;
@@ -74,6 +91,8 @@ struct boot_param {
7491
#define LINUX_HEADER_MINOR_VER(ver) (((ver) >> 0) & 0xffff)
7592
#elif defined(__aarch64__)
7693
#define LINUX_HEADER_MAGIC2 0x644d5241
94+
#elif defined(__loongarch__)
95+
#define LINUX_HEADER_MAGIC2 0x818223cd
7796
#endif
7897

7998
const char *verify_kernel(struct boot_param *p) {
@@ -334,7 +353,13 @@ void prepare_mmap(struct boot_param *p) {
334353
EFI_MEMORY_DESCRIPTOR *entry = (void *)efi_mmap + i * efi_desc_size;
335354

336355
if (entry->Attribute & EFI_MEMORY_RUNTIME) {
337-
entry->VirtualStart = entry->PhysicalStart;
356+
// LoongArch kernel requires the virtual address stays in the
357+
// privileged, direct-mapped window
358+
#if defined(__loongarch__)
359+
entry->VirtualStart = entry->PhysicalStart | (0x8ULL << 60);
360+
#else
361+
entry->VirtualStart = entry->PhysicalStart;
362+
#endif
338363
}
339364
}
340365

@@ -360,6 +385,34 @@ noreturn void jump_to_kernel(struct boot_param *p) {
360385
void (*kernel_entry)(uint64_t dtb, uint64_t res0, uint64_t res1, uint64_t res2) = p->kernel_base;
361386
asm ("msr daifset, 0xF");
362387
kernel_entry((uint64_t)p->dtb, 0, 0, 0);
388+
#elif defined(__loongarch__)
389+
// LoongArch kernel used to store virtual address in header.kernel_entry
390+
// clearing the high 16bits ensures compatibility
391+
#define TO_PHYS(addr) ((addr) & ((1ULL << 48) - 1))
392+
#define CSR_DMW_PLV0 1ULL
393+
#define CSR_DMW0_VSEG 0x8000ULL
394+
#define CSR_DMW0_BASE (CSR_DMW0_VSEG << 48)
395+
#define CSR_DMW0_INIT (CSR_DMW0_BASE | CSR_DMW_PLV0)
396+
#define CSR_DMW1_MAT (1 << 4)
397+
#define CSR_DMW1_VSEG 0x9000ULL
398+
#define CSR_DMW1_BASE (CSR_DMW1_VSEG << 48)
399+
#define CSR_DMW1_INIT (CSR_DMW1_BASE | CSR_DMW1_MAT | CSR_DMW_PLV0)
400+
#define CSR_DMW2_VSEG 0xa000ULL
401+
#define CSR_DMW2_MAT (2 << 4)
402+
#define CSR_DMW2_BASE (CSR_DMW2_VSEG << 48)
403+
#define CSR_DMW2_INIT (CSR_DMW2_BASE | CSR_DMW2_MAT | CSR_DMW_PLV0)
404+
#define CSR_DMW3_INIT 0
405+
406+
struct linux_header *header = p->kernel_base;
407+
void (*kernel_entry)(uint64_t efi_boot, uint64_t cmdline, uint64_t st);
408+
kernel_entry = p->kernel_base + (TO_PHYS(header->kernel_entry) - header->load_offset);
409+
410+
asm volatile ("csrxchg $r0, %0, 0x0" :: "r" (0x4) : "memory");
411+
asm volatile ("csrwr %0, 0x180" :: "r" (CSR_DMW0_INIT) : "memory");
412+
asm volatile ("csrwr %0, 0x181" :: "r" (CSR_DMW1_INIT) : "memory");
413+
asm volatile ("csrwr %0, 0x182" :: "r" (CSR_DMW2_INIT) : "memory");
414+
asm volatile ("csrwr %0, 0x183" :: "r" (CSR_DMW3_INIT) : "memory");
415+
kernel_entry(1, (uint64_t)p->cmdline, (uint64_t)gST);
363416
#endif
364417
__builtin_unreachable();
365418
}

0 commit comments

Comments
 (0)