summaryrefslogtreecommitdiff
path: root/Kernel/Prekernel/Arch/aarch64/PrekernelExceptions.cpp
blob: 052675399a5905384f69d90af3dd466f7fd8bf46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/*
 * Copyright (c) 2021, James Mintram <me@jamesrm.com>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <Kernel/Arch/aarch64/Aarch64Registers.h>
#include <Kernel/Prekernel/Arch/aarch64/Aarch64_asm_utils.h>
#include <Kernel/Prekernel/Arch/aarch64/Prekernel.h>

extern "C" void enter_el2_from_el3();
extern "C" void enter_el1_from_el2();

using namespace Kernel;

namespace Prekernel {

static void drop_to_el2()
{
    Aarch64_SCR_EL3 secure_configuration_register_el3 = {};

    secure_configuration_register_el3.ST = 1;  // Don't trap access to Counter-timer Physical Secure registers
    secure_configuration_register_el3.RW = 1;  // Lower level to use Aarch64
    secure_configuration_register_el3.NS = 1;  // Non-secure state
    secure_configuration_register_el3.HCE = 1; // Enable Hypervisor instructions at all levels

    Aarch64_SCR_EL3::write(secure_configuration_register_el3);

    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;
    saved_program_status_register_el3.D = 1;

    // Indicate EL1 as exception origin mode (so we go back there)
    saved_program_status_register_el3.M = Aarch64_SPSR_EL3::Mode::EL2t;

    // Set the register
    Aarch64_SPSR_EL3::write(saved_program_status_register_el3);

    // This will jump into os_start() below
    enter_el2_from_el3();
}
static void drop_to_el1()
{
    Aarch64_HCR_EL2 hypervisor_configuration_register_el2 = {};
    hypervisor_configuration_register_el2.RW = 1; // EL1 to use 64-bit mode
    Aarch64_HCR_EL2::write(hypervisor_configuration_register_el2);

    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 = Aarch64_SPSR_EL2::Mode::EL1t;

    Aarch64_SPSR_EL2::write(saved_program_status_register_el2);
    enter_el1_from_el2();
}

static void set_up_el1()
{
    Aarch64_SCTLR_EL1 system_control_register_el1 = Aarch64_SCTLR_EL1::reset_value();

    system_control_register_el1.UCT = 1;  // Don't trap access to CTR_EL0
    system_control_register_el1.nTWE = 1; // Don't trap WFE instructions
    system_control_register_el1.nTWI = 1; // Don't trap WFI instructions
    system_control_register_el1.DZE = 1;  // Don't trap DC ZVA instructions
    system_control_register_el1.UMA = 1;  // Don't trap access to DAIF (debugging) flags of EFLAGS register
    system_control_register_el1.SA0 = 1;  // Enable stack access alignment check for EL0
    system_control_register_el1.SA = 1;   // Enable stack access alignment check for EL1
    system_control_register_el1.A = 1;    // Enable memory access alignment check

    Aarch64_SCTLR_EL1::write(system_control_register_el1);
}

void drop_to_exception_level_1()
{
    switch (get_current_exception_level()) {
    case ExceptionLevel::EL3:
        drop_to_el2();
        [[fallthrough]];
    case ExceptionLevel::EL2:
        drop_to_el1();
        [[fallthrough]];
    case ExceptionLevel::EL1:
        set_up_el1();
        break;
    default: {
        Prekernel::panic("FATAL: CPU booted in unsupported exception mode!\r\n");
    }
    }
}

}