protos/limine: Make aarch64 EL2 entry automatic based on firmware state
diff --git a/common/lib/spinup.asm_aarch64 b/common/lib/spinup.asm_aarch64
index bceb8f52..7efc6413 100644
--- a/common/lib/spinup.asm_aarch64
+++ b/common/lib/spinup.asm_aarch64
@@ -5,16 +5,13 @@
// noreturn void enter_in_el1(uint64_t entry, uint64_t sp, uint64_t sctlr,
// uint64_t mair, uint64_t tcr, uint64_t ttbr0,
// uint64_t ttbr1, uint64_t direct_map_offset)
-// Potentially drop to EL1 from EL2 (and also disable trapping to EL2), then
-// configure EL1 state and jump to kernel.
+// Configure EL1 state and jump to kernel. Must be called at EL1.
.global enter_in_el1
enter_in_el1:
msr spsel, #0
mov sp, x1
- PICK_EL x8, 0f, 2f
-0:
// Switch to the new page tables
// Point the EL1t handler to the continuation, such that after we page fault,
@@ -59,61 +56,6 @@ enter_in_el1:
eret
-2:
- // Check HCR_EL2.E2H
- mrs x8, hcr_el2
- tbnz x8, #34, 3f
-
- // Configure EL1 state (normal silicon)
- msr mair_el1, x3
- msr tcr_el1, x4
- msr ttbr0_el1, x5
- msr ttbr1_el1, x6
- msr sctlr_el1, x2
- msr cpacr_el1, xzr
- dsb sy
- isb
- b 4f
-
-3:
- // Configure EL1 state (apple silicon)
- msr s3_5_c10_c2_0, x3 // MAIR_EL12
- msr s3_5_c2_c0_2, x4 // TCR_EL12
- msr s3_5_c2_c0_0, x5 // TTBR0_EL12
- msr s3_5_c2_c0_1, x6 // TTBR1_EL12
- msr s3_5_c1_c0_0, x2 // SCTLR_EL12
- msr s3_5_c1_c0_2, xzr // CPACR_EL12
- dsb sy
- isb
-
-4:
-
- // Configure EL2-specific state for EL1
-
- // Don't trap counters to EL2
- mov x8, #3
- msr cnthctl_el2, x8
- msr cntvoff_el2, xzr
-
- // Enable AArch64 in EL1
- ldr x8, =0x80000002
- msr hcr_el2, x8
-
- // Don't trap FP/SIMD to EL2
- mov x8, #0x33FF
- msr cptr_el2, x8
- msr hstr_el2, xzr
-
- // Enter kernel in EL1
- mov x8, #0x3c4
- msr spsr_el2, x8
- msr elr_el2, x0
-
- mov x0, xzr
- ZERO_REGS_EXCEPT_X0
-
- eret
-
// noreturn void enter_in_el2(uint64_t entry, uint64_t sp, uint64_t sctlr,
// uint64_t mair, uint64_t tcr, uint64_t ttbr0,
// uint64_t ttbr1, uint64_t direct_map_offset)
diff --git a/common/protos/limine.c b/common/protos/limine.c
index 9d64cd2b..2e59c48c 100644
--- a/common/protos/limine.c
+++ b/common/protos/limine.c
@@ -426,6 +426,17 @@ noreturn void limine_load(char *config, char *cmdline) {
uint32_t eax, ebx, ecx, edx;
#endif
+#if defined (__aarch64__)
+ // Booting at EL2 without VHE is not supported.
+ if (current_el() == 2) {
+ uint64_t mmfr1;
+ asm volatile ("mrs %0, id_aa64mmfr1_el1" : "=r"(mmfr1));
+ if (!((mmfr1 >> 8) & 0xF)) {
+ panic(true, "limine: Booting at EL2 without VHE support is not supported");
+ }
+ }
+#endif
+
char *kernel_path = config_get_value(config, 0, "PATH");
if (kernel_path == NULL) {
kernel_path = config_get_value(config, 0, "KERNEL_PATH");
@@ -1507,29 +1518,8 @@ FEAT_END
physical_base, virtual_base, direct_map_offset);
#if defined (__aarch64__)
- // aarch64 EL2
- bool want_el2 = false;
-FEAT_START
- struct limine_aarch64_el2_request *el2_request =
- get_request(LIMINE_AARCH64_EL2_REQUEST_ID);
- if (el2_request == NULL) {
- break;
- }
-
- // Grant EL2 if we are at EL2 and VHE is active (E2H enabled early)
- if (current_el() == 2) {
- uint64_t hcr;
- asm volatile ("mrs %0, hcr_el2" : "=r"(hcr));
- if (hcr & (1ULL << 34)) {
- want_el2 = true;
-
- struct limine_aarch64_el2_response *el2_response =
- ext_mem_alloc(sizeof(struct limine_aarch64_el2_response));
-
- el2_request->response = reported_addr(el2_response);
- }
- }
-FEAT_END
+ // Enter at EL2 with VHE if we are at EL2 (VHE check done at function entry)
+ bool want_el2 = (current_el() == 2);
#endif
// MP
@@ -1553,7 +1543,7 @@ FEAT_START
mp_info = init_smp(config, &cpu_count, &bsp_mpidr,
pagemap, LIMINE_MAIR(fb_attr), LIMINE_TCR(tsz, pa), LIMINE_SCTLR,
- direct_map_offset, want_el2);
+ direct_map_offset);
#elif defined (__riscv)
mp_info = init_smp(&cpu_count, pagemap, direct_map_offset);
#elif defined (__loongarch64)
diff --git a/common/sys/smp.c b/common/sys/smp.c
index 1ae3ce1d..b2313132 100644
--- a/common/sys/smp.c
+++ b/common/sys/smp.c
@@ -324,7 +324,7 @@ struct limine_mp_info *init_smp(size_t *cpu_count,
#elif defined (__aarch64__)
struct trampoline_passed_info {
- uint64_t smp_tpl_enter_in_el2;
+ uint64_t smp_tpl_ap_el;
uint64_t smp_tpl_booted_flag;
@@ -353,7 +353,7 @@ static bool try_start_ap(int boot_method, uint64_t method_ptr,
struct limine_mp_info *info_struct,
uint64_t ttbr0, uint64_t ttbr1, uint64_t mair,
uint64_t tcr, uint64_t sctlr,
- uint64_t hhdm_offset, bool enter_in_el2) {
+ uint64_t hhdm_offset) {
// Prepare the trampoline
static void *trampoline = NULL;
if (trampoline == NULL) {
@@ -370,13 +370,13 @@ static bool try_start_ap(int boot_method, uint64_t method_ptr,
passed_info->smp_tpl_info_struct = (uint64_t)(uintptr_t)info_struct;
passed_info->smp_tpl_booted_flag = 0;
+ passed_info->smp_tpl_ap_el = 0;
passed_info->smp_tpl_ttbr0 = ttbr0;
passed_info->smp_tpl_ttbr1 = ttbr1;
passed_info->smp_tpl_mair = mair;
passed_info->smp_tpl_tcr = tcr;
passed_info->smp_tpl_sctlr = sctlr;
passed_info->smp_tpl_hhdm_offset = hhdm_offset;
- passed_info->smp_tpl_enter_in_el2 = enter_in_el2 ? 1 : 0;
// Cache coherency between the I-Cache and D-Cache is not guaranteed by the
// architecture and as such we must perform I-Cache invalidation.
@@ -447,6 +447,12 @@ static bool try_start_ap(int boot_method, uint64_t method_ptr,
// set this flag, it has enabled its caches
if (locked_read(&passed_info->smp_tpl_booted_flag) == 1) {
+ uint64_t ap_el = locked_read(&passed_info->smp_tpl_ap_el);
+ uint64_t bsp_el = current_el();
+ if (ap_el != bsp_el) {
+ panic(false, "smp: AP started at EL%u but BSP is at EL%u",
+ (uint32_t)ap_el, (uint32_t)bsp_el);
+ }
return true;
}
stall(100);
@@ -461,8 +467,7 @@ static struct limine_mp_info *try_acpi_smp(size_t *cpu_count,
uint64_t mair,
uint64_t tcr,
uint64_t sctlr,
- uint64_t hhdm_offset,
- bool enter_in_el2) {
+ uint64_t hhdm_offset) {
int boot_method = BOOT_WITH_ACPI_PARK;
// Search for FADT table
@@ -566,8 +571,7 @@ static struct limine_mp_info *try_acpi_smp(size_t *cpu_count,
if (!try_start_ap(boot_method, gicc->parking_addr, info_struct,
(uint64_t)(uintptr_t)pagemap.top_level[0],
(uint64_t)(uintptr_t)pagemap.top_level[1],
- mair, tcr, sctlr, hhdm_offset,
- enter_in_el2)) {
+ mair, tcr, sctlr, hhdm_offset)) {
print("smp: FAILED to bring-up AP\n");
continue;
}
@@ -595,8 +599,7 @@ static struct limine_mp_info *try_dtb_smp( void *dtb,
uint64_t mair,
uint64_t tcr,
uint64_t sctlr,
- uint64_t hhdm_offset,
- bool enter_in_el2) {
+ uint64_t hhdm_offset) {
uint64_t bsp_mpidr;
asm volatile ("mrs %0, mpidr_el1" : "=r"(bsp_mpidr));
@@ -761,8 +764,7 @@ static struct limine_mp_info *try_dtb_smp( void *dtb,
if (!try_start_ap(boot_method, method_ptr, info_struct,
(uint64_t)(uintptr_t)pagemap.top_level[0],
(uint64_t)(uintptr_t)pagemap.top_level[1],
- mair, tcr, sctlr, hhdm_offset,
- enter_in_el2)) {
+ mair, tcr, sctlr, hhdm_offset)) {
print("smp: FAILED to bring-up AP\n");
continue;
}
@@ -783,14 +785,12 @@ struct limine_mp_info *init_smp(const char *config,
uint64_t mair,
uint64_t tcr,
uint64_t sctlr,
- uint64_t hhdm_offset,
- bool enter_in_el2) {
+ uint64_t hhdm_offset) {
struct limine_mp_info *info = NULL;
if (acpi_get_rsdp() && (info = try_acpi_smp(
cpu_count, bsp_mpidr, pagemap,
- mair, tcr, sctlr, hhdm_offset,
- enter_in_el2)))
+ mair, tcr, sctlr, hhdm_offset)))
return info;
// No RSDP means no ACPI, try device trees in that case.
@@ -798,8 +798,7 @@ struct limine_mp_info *init_smp(const char *config,
if (dtb) {
info = try_dtb_smp(dtb,
cpu_count, bsp_mpidr, pagemap,
- mair, tcr, sctlr, hhdm_offset,
- enter_in_el2);
+ mair, tcr, sctlr, hhdm_offset);
pmm_free(dtb, fdt_totalsize(dtb));
return info;
}
diff --git a/common/sys/smp.h b/common/sys/smp.h
index c914f720..7e692d76 100644
--- a/common/sys/smp.h
+++ b/common/sys/smp.h
@@ -30,8 +30,7 @@ struct limine_mp_info *init_smp(const char *config,
uint64_t mair,
uint64_t tcr,
uint64_t sctlr,
- uint64_t hhdm_offset,
- bool enter_in_el2);
+ uint64_t hhdm_offset);
#elif defined (__riscv)
diff --git a/common/sys/smp_trampoline.asm_aarch64 b/common/sys/smp_trampoline.asm_aarch64
index d11db4f4..bb4f3812 100644
--- a/common/sys/smp_trampoline.asm_aarch64
+++ b/common/sys/smp_trampoline.asm_aarch64
@@ -1,6 +1,6 @@
#include <lib/macros.aarch64_asm.h>
-.set tpl_enter_in_el2, -72
+.set tpl_ap_el, -72
.set tpl_booted_flag, -64
.set tpl_hhdm_offset, -56
.set tpl_ttbr0, -48
@@ -32,55 +32,15 @@ smp_trampoline_start:
PICK_EL x8, 1f, 0f
0:
- // EL2 path
+ // EL2 path - enable VHE and stay at EL2
- // Check HCR_EL2.E2H
- mrs x8, hcr_el2
- tbnz x8, #34, 6f
-
- // Non-VHE: check if we should stay at EL2 anyway
- ldr x8, [x1, tpl_enter_in_el2]
- cbnz x8, 10f
-
- // Non-VHE drop to EL1: configure real EL1 page tables directly
- msr mair_el1, x3
- msr tcr_el1, x4
- msr ttbr0_el1, x5
- msr ttbr1_el1, x6
- msr sctlr_el1, x2
- msr cpacr_el1, xzr
- isb
- dsb sy
- isb
- b 7f
-
-10:
- // Enable E2H on this AP and use VHE stay path
+ // Enable E2H if not already set
mrs x8, hcr_el2
orr x8, x8, #(1 << 34)
msr hcr_el2, x8
isb
- b 8f
-
-6:
- // VHE (E2H=1): check if we should stay at EL2
- ldr x8, [x1, tpl_enter_in_el2]
- cbnz x8, 8f
-
- // VHE drop to EL1: use EL12 aliases for real EL1 registers
- msr s3_5_c10_c2_0, x3 // MAIR_EL12
- msr s3_5_c2_c0_2, x4 // TCR_EL12
- msr s3_5_c2_c0_0, x5 // TTBR0_EL12
- msr s3_5_c2_c0_1, x6 // TTBR1_EL12
- msr s3_5_c1_c0_0, x2 // SCTLR_EL12
- msr s3_5_c1_c0_2, xzr // CPACR_EL12
- isb
- dsb sy
- isb
- b 7f
-8:
- // VHE stay at EL2: configure EL2 state before enabling MMU
+ // Configure EL2 state for VHE
msr spsel, #0
mov x8, #3
@@ -121,35 +81,6 @@ smp_trampoline_start:
// Jump to the higher half mapping in case we didn't immediately crash
br x8
-7:
- // Common EL2-to-EL1 drop path
-
- // Don't trap counters to EL2
- mov x8, #3
- msr cnthctl_el2, x8
- msr cntvoff_el2, xzr
-
- // Enable AArch64 in EL1
- mov x8, xzr
- orr x8, x8, #(1 << 31)
- orr x8, x8, #(1 << 1)
- msr hcr_el2, x8
-
- // Don't trap FP/SIMD to EL2
- mov x8, #0x33FF
- msr cptr_el2, x8
- msr hstr_el2, xzr
-
- // Run rest of trampoline in EL1
- mov x8, #0x3c4
- msr spsr_el2, x8
- adrp x8, 3f
- add x8, x8, :lo12:3f
- add x8, x8, x7 // Add HHDM offset
- msr elr_el2, x8
-
- eret
-
1:
// EL1 path
msr spsel, #0
@@ -192,6 +123,12 @@ smp_trampoline_start:
// Add HHDM offset to data pointer
add x1, x1, x7
+ // Report our EL to the BSP
+ mrs x8, currentel
+ lsr x8, x8, #2
+ add x9, x1, tpl_ap_el
+ stlr x8, [x9]
+
// Notify BSP we are alive
mov x8, #1
add x9, x1, tpl_booted_flag
@@ -214,10 +151,11 @@ smp_trampoline_start:
ldr x8, [x0, #16]
mov sp, x8
- // Enter kernel
+ // Enter kernel - determine SPSR from current EL
mov x8, #0x3c4
- ldr x9, [x1, tpl_enter_in_el2]
- cbz x9, 9f
+ mrs x9, currentel
+ cmp x9, #0b1000
+ b.ne 9f
mov x8, #0x3c8
9:
msr spsr_el1, x8
