Skip to content

Commit 900f33e

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 bbd6990 commit 900f33e

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) {
@@ -335,7 +354,13 @@ void prepare_mmap(struct boot_param *p) {
335354
EFI_MEMORY_DESCRIPTOR *entry = (void *)efi_mmap + i * efi_desc_size;
336355

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

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

0 commit comments

Comments
 (0)