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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
#include "arch/devices/local_apic.hpp"
#include "kapi/devices.hpp"
#include "kapi/memory.hpp"
#include <kstd/print>
#include <cstddef>
#include <cstdint>
#include <utility>
namespace arch::devices
{
namespace
{
constexpr auto lapic_enable_bit = 0x100u;
constexpr auto spurious_interrupt_vector = 0xFFu;
constexpr auto offset_of_version = 0u;
constexpr auto offset_of_max_lvt_entry = 16u;
constexpr auto offset_of_eoi_suppression = 24u;
} // namespace
enum struct local_apic::registers : std::ptrdiff_t
{
id = 0x020,
version = 0x030,
task_priority = 0x080,
arbitration_priority = 0x090,
processor_priority = 0x0a0,
eoi = 0x0b0,
remote_read = 0x0c0,
logical_destination = 0x0d0,
destination_format = 0x0e0,
spurious_interrupt_vector = 0x0f0,
in_service_0 = 0x100,
in_service_1 = 0x110,
in_service_2 = 0x120,
in_service_3 = 0x130,
in_service_4 = 0x140,
in_service_5 = 0x150,
in_service_6 = 0x160,
in_service_7 = 0x170,
trigger_mode_0 = 0x180,
trigger_mode_1 = 0x190,
trigger_mode_2 = 0x1a0,
trigger_mode_3 = 0x1b0,
trigger_mode_4 = 0x1c0,
trigger_mode_5 = 0x1d0,
trigger_mode_6 = 0x1e0,
trigger_mode_7 = 0x1f0,
interrupt_request_0 = 0x200,
interrupt_request_1 = 0x210,
interrupt_request_2 = 0x220,
interrupt_request_3 = 0x230,
interrupt_request_4 = 0x240,
interrupt_request_5 = 0x250,
interrupt_request_6 = 0x260,
interrupt_request_7 = 0x270,
error_status = 0x280,
lvt_corrected_machine_check_interrupt = 0x2f0,
interrupt_command_0 = 0x300,
interrupt_command_1 = 0x310,
lvt_timer = 0x320,
lvt_thermal_sensors = 0x330,
lvt_performance_monitoring_counters = 0x340,
lvt_local_interrupt_0 = 0x350,
lvt_local_interrupt_1 = 0x360,
lvt_error = 0x370,
initial_count = 0x380,
current_count = 0x390,
divide_configuration = 0x3e0,
};
local_apic::local_apic(std::size_t major, std::size_t minor, std::uint64_t hardware_id,
kapi::memory::physical_address base, bool is_bsp)
: kapi::devices::device{major, minor, "lapic"}
, m_hardware_id{hardware_id}
, m_base{base}
, m_is_bsp{is_bsp}
{}
auto local_apic::init() -> bool
{
auto static shared_virtual_base = kapi::memory::allocate_mmio_region(1);
auto static is_mapped = false;
if (!is_mapped)
{
if (!kapi::memory::map_mmio_region(shared_virtual_base, m_base, kapi::memory::page_mapper::flags::writable))
{
kstd::println("[x86_64:DEV] LAPIC {} MMIO mapping failed!", m_hardware_id);
return false;
}
is_mapped = true;
}
m_virtual_base = shared_virtual_base;
if (m_is_bsp)
{
auto raw_version = read_register(registers::version);
m_version = (raw_version >> offset_of_version) & 0xff;
m_highest_lvt_entry_index = (raw_version >> offset_of_max_lvt_entry) & 0xff;
m_supports_eoi_broadcast_suppression = (raw_version >> offset_of_eoi_suppression) & 0x1;
write_register(registers::spurious_interrupt_vector, lapic_enable_bit | spurious_interrupt_vector);
kstd::println("[x86_64:DEV] LAPIC initialized. version: {#x} | max_lvt_entry: {} | eoi_suppresion: {:s}",
m_version, m_highest_lvt_entry_index, m_supports_eoi_broadcast_suppression);
}
else
{
kstd::println("[x86_64:DEV] LAPIC {} is not on the BSP, deferring intialization.", m_hardware_id);
}
return true;
}
auto local_apic::read_register(registers id) const -> std::uint32_t
{
auto reg = static_cast<std::uint32_t volatile *>(m_virtual_base + std::to_underlying(id));
return *reg;
}
auto local_apic::write_register(registers id, std::uint32_t value) -> void
{
auto reg = static_cast<std::uint32_t volatile *>(m_virtual_base + std::to_underlying(id));
*reg = value;
}
} // namespace arch::devices
|