1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-06-01 09:48:11 +00:00
serenity/Kernel/Prekernel/Arch/aarch64/init.cpp
Marcin Undak d14d7ee78b Kernel: Discover initial exception level when booting Aarch64
When booting on RPI3 firmware puts CPU in EL2 mode which is
different from QEMU's default EL3.

I've added logic to discover initial mode at boot
and then act accordingly. This results in Serenity corectly
switching to EL1 on target hardware now.
2021-10-15 17:47:39 +01:00

214 lines
6.1 KiB
C++

/*
* Copyright (c) 2021, Nico Weber <thakis@chromium.org>
* Copyright (c) 2021, Marcin Undak <mcinek@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Types.h>
#include <Kernel/Prekernel/Arch/aarch64/Aarch64_asm_utils.h>
#include <Kernel/Prekernel/Arch/aarch64/Mailbox.h>
#include <Kernel/Prekernel/Arch/aarch64/Timer.h>
#include <Kernel/Prekernel/Arch/aarch64/UART.h>
extern "C" [[noreturn]] void halt();
extern "C" [[noreturn]] void init();
extern "C" [[noreturn]] void os_start();
static void set_up_el1_mode();
static void set_up_el2_mode();
static void set_up_el3_mode();
static void print_current_exception_level(const char* msg);
[[noreturn]] static void jump_to_os_start_from_el2();
[[noreturn]] static void jump_to_os_start_from_el3();
extern "C" [[noreturn]] void init()
{
auto& uart = Prekernel::UART::the();
uart.print_str("\r\nWelcome to Serenity OS!\r\n");
uart.print_str("Imagine this being your ideal operating system.\r\n");
uart.print_str("Observed deviations from that ideal are shortcomings of your imagination.\r\n\r\n");
u32 firmware_version = Prekernel::Mailbox::query_firmware_version();
uart.print_str("Firmware version: ");
uart.print_num(firmware_version);
uart.print_str("\r\n");
print_current_exception_level("CPU started in:");
set_up_el2_mode();
set_up_el1_mode();
auto current_exception_level = get_current_exception_level();
switch (current_exception_level) {
case 2:
jump_to_os_start_from_el2();
break;
case 3:
set_up_el3_mode();
jump_to_os_start_from_el3();
break;
default:
uart.print_str("FATAL: CPU booted in unsupported exception mode!\r\n");
halt();
}
}
extern "C" [[noreturn]] void os_start()
{
auto& uart = Prekernel::UART::the();
print_current_exception_level("CPU switched to:");
auto& timer = Prekernel::Timer::the();
u64 start_musec = 0;
for (;;) {
u64 now_musec;
while ((now_musec = timer.microseconds_since_boot()) - start_musec < 1'000'000)
;
start_musec = now_musec;
uart.print_str("Timer: ");
uart.print_num(now_musec);
uart.print_str("\r\n");
}
}
// FIXME: Share this with the Intel Prekernel.
extern size_t __stack_chk_guard;
size_t __stack_chk_guard;
extern "C" [[noreturn]] void __stack_chk_fail();
[[noreturn]] void halt()
{
for (;;) {
asm volatile("wfi");
}
}
void __stack_chk_fail()
{
halt();
}
[[noreturn]] void __assertion_failed(char const*, char const*, unsigned int, char const*)
{
halt();
}
static void set_up_el1_mode()
{
Kernel::Aarch64_SCTLR_EL1 system_control_register_el1 = {};
// Those bits are reserved on ARMv8.0
system_control_register_el1.LSMAOE = 1;
system_control_register_el1.nTLSMD = 1;
system_control_register_el1.SPAN = 1;
system_control_register_el1.IESB = 1;
// Don't trap access to CTR_EL0
system_control_register_el1.UCT = 1;
// Don't trap WFE instructions
system_control_register_el1.nTWE = 1;
// Don't trap WFI instructions
system_control_register_el1.nTWI = 1;
// Don't trap DC ZVA instructions
system_control_register_el1.DZE = 1;
// Don't trap access to DAIF (debugging) flags of EFLAGS register
system_control_register_el1.UMA = 1;
// Enable stack access alignment check for EL0
system_control_register_el1.SA0 = 1;
// Enable stack access alignment check for EL1
system_control_register_el1.SA = 1;
// Enable memory access alignment check
system_control_register_el1.A = 1;
// Set the register
asm("msr sctlr_el1, %[value]" ::[value] "r"(system_control_register_el1));
}
static void set_up_el2_mode()
{
Kernel::Aarch64_HCR_EL2 hypervisor_configuration_register_el2 = {};
// EL1 to use 64-bit mode
hypervisor_configuration_register_el2.RW = 1;
// Set the register
asm("msr hcr_el2, %[value]" ::[value] "r"(hypervisor_configuration_register_el2));
}
static void set_up_el3_mode()
{
Kernel::Aarch64_SCR_EL3 secure_configuration_register_el3 = {};
// Don't trap access to Counter-timer Physical Secure registers
secure_configuration_register_el3.ST = 1;
// Lower level to use Aarch64
secure_configuration_register_el3.RW = 1;
// Enable Hypervisor instructions at all levels
secure_configuration_register_el3.HCE = 1;
// Set the register
asm("msr scr_el3, %[value]" ::[value] "r"(secure_configuration_register_el3));
}
[[noreturn]] static void jump_to_os_start_from_el2()
{
// Processor state to set when returned from this function (in new EL1 world)
Kernel::Aarch64_SPSR_EL2 saved_program_status_register_el2 = {};
// Mask (disable) all interrupts
saved_program_status_register_el2.A = 1;
saved_program_status_register_el2.I = 1;
saved_program_status_register_el2.F = 1;
// Indicate EL1 as exception origin mode (so we go back there)
saved_program_status_register_el2.M = Kernel::Aarch64_SPSR_EL2::Mode::EL1h;
// Set the register
asm("msr spsr_el2, %[value]" ::[value] "r"(saved_program_status_register_el2));
// This will jump into os_start()
return_from_el2();
}
[[noreturn]] static void jump_to_os_start_from_el3()
{
// Processor state to set when returned from this function (in new EL1 world)
Kernel::Aarch64_SPSR_EL3 saved_program_status_register_el3 = {};
// Mask (disable) all interrupts
saved_program_status_register_el3.A = 1;
saved_program_status_register_el3.I = 1;
saved_program_status_register_el3.F = 1;
// Indicate EL1 as exception origin mode (so we go back there)
saved_program_status_register_el3.M = Kernel::Aarch64_SPSR_EL3::Mode::EL1h;
// Set the register
asm("msr spsr_el3, %[value]" ::[value] "r"(saved_program_status_register_el3));
// This will jump into os_start() below
return_from_el3();
}
static void print_current_exception_level(const char* msg)
{
auto& uart = Prekernel::UART::the();
auto exception_level = get_current_exception_level();
uart.print_str(msg);
uart.print_str(" EL");
uart.print_num(exception_level);
uart.print_str("\r\n");
}