5#include <etl/io_port.h>
12 if (!cpu_io::msr::apic::IsGloballyEnabled()) {
13 cpu_io::msr::apic::EnableGlobally();
18 is_x2apic_mode_ =
true;
21 klog::Err(
"Failed to enable APIC in any mode");
24 is_x2apic_mode_ =
false;
29 if (is_x2apic_mode_) {
30 sivr = cpu_io::msr::apic::ReadSivr();
32 etl::io_port_ro<uint32_t> sivr_reg{
33 reinterpret_cast<void*
>(apic_base_ + kXApicSivrOffset)};
34 sivr = sivr_reg.read();
38 sivr |= kApicSoftwareEnableBit;
40 sivr |= kSpuriousVector;
42 if (is_x2apic_mode_) {
43 cpu_io::msr::apic::WriteSivr(sivr);
45 etl::io_port_wo<uint32_t> sivr_reg{
46 reinterpret_cast<void*
>(apic_base_ + kXApicSivrOffset)};
54 if (is_x2apic_mode_) {
55 cpu_io::msr::apic::WriteLvtTimer(kLvtMaskBit);
56 cpu_io::msr::apic::WriteLvtLint0(kLvtMaskBit);
57 cpu_io::msr::apic::WriteLvtLint1(kLvtMaskBit);
58 cpu_io::msr::apic::WriteLvtError(kLvtMaskBit);
61 etl::io_port_wo<uint32_t> lvt_timer{
62 reinterpret_cast<void*
>(apic_base_ + kXApicLvtTimerOffset)};
63 lvt_timer.write(kLvtMaskBit);
64 etl::io_port_wo<uint32_t> lvt_lint0{
65 reinterpret_cast<void*
>(apic_base_ + kXApicLvtLint0Offset)};
66 lvt_lint0.write(kLvtMaskBit);
67 etl::io_port_wo<uint32_t> lvt_lint1{
68 reinterpret_cast<void*
>(apic_base_ + kXApicLvtLint1Offset)};
69 lvt_lint1.write(kLvtMaskBit);
70 etl::io_port_wo<uint32_t> lvt_error{
71 reinterpret_cast<void*
>(apic_base_ + kXApicLvtErrorOffset)};
72 lvt_error.write(kLvtMaskBit);
79 return cpu_io::msr::apic::ReadVersion();
82 etl::io_port_ro<uint32_t> ver_reg{
84 return ver_reg.read();
90 cpu_io::msr::apic::WriteEoi(0);
93 etl::io_port_wo<uint32_t> eoi_reg{
101 if (is_x2apic_mode_) {
102 auto icr =
static_cast<uint64_t
>(vector);
103 icr |=
static_cast<uint64_t
>(destination_apic_id) << 32;
105 cpu_io::msr::apic::WriteIcr(icr);
107 while ((cpu_io::msr::apic::ReadIcr() & kIcrDeliveryStatusBit) != 0) {
114 auto icr_high = (destination_apic_id & kApicIdMask) << kIcrDestShift;
115 etl::io_port_wo<uint32_t> icr_high_reg{
116 reinterpret_cast<void*
>(apic_base_ + kXApicIcrHighOffset)};
117 icr_high_reg.write(icr_high);
120 auto icr_low =
static_cast<uint32_t
>(vector);
121 etl::io_port_wo<uint32_t> icr_low_wo{
122 reinterpret_cast<void*
>(apic_base_ + kXApicIcrLowOffset)};
123 icr_low_wo.write(icr_low);
126 etl::io_port_ro<uint32_t> icr_low_ro{
127 reinterpret_cast<void*
>(apic_base_ + kXApicIcrLowOffset)};
128 while ((icr_low_ro.read() & kIcrDeliveryStatusBit) != 0) {
137 if (is_x2apic_mode_) {
138 auto icr =
static_cast<uint64_t
>(vector);
142 cpu_io::msr::apic::WriteIcr(icr);
144 while ((cpu_io::msr::apic::ReadIcr() & kIcrDeliveryStatusBit) != 0) {
150 etl::io_port_wo<uint32_t> icr_high_reg{
151 reinterpret_cast<void*
>(apic_base_ + kXApicIcrHighOffset)};
152 icr_high_reg.write(0);
155 auto icr_low =
static_cast<uint32_t
>(vector) | kIcrBroadcastMode;
156 etl::io_port_wo<uint32_t> icr_low_wo{
157 reinterpret_cast<void*
>(apic_base_ + kXApicIcrLowOffset)};
158 icr_low_wo.write(icr_low);
161 etl::io_port_ro<uint32_t> icr_low_ro{
162 reinterpret_cast<void*
>(apic_base_ + kXApicIcrLowOffset)};
163 while ((icr_low_ro.read() & kIcrDeliveryStatusBit) != 0) {
172 if (is_x2apic_mode_) {
173 cpu_io::msr::apic::WriteTpr(
static_cast<uint32_t
>(priority));
175 etl::io_port_wo<uint32_t> tpr_reg{
176 reinterpret_cast<void*
>(apic_base_ + kXApicTprOffset)};
177 tpr_reg.write(
static_cast<uint32_t
>(priority));
183 return static_cast<uint8_t
>(cpu_io::msr::apic::ReadTpr() &
kApicIdMask);
185 etl::io_port_ro<uint32_t> tpr_reg{
187 auto tpr = tpr_reg.read();
193 uint8_t vector,
bool periodic)
const ->
void {
194 if (is_x2apic_mode_) {
195 cpu_io::msr::apic::WriteTimerDivide(divide_value);
197 auto lvt_timer =
static_cast<uint32_t
>(vector);
199 lvt_timer |= kLvtPeriodicMode;
202 cpu_io::msr::apic::WriteLvtTimer(lvt_timer);
203 cpu_io::msr::apic::WriteTimerInitCount(initial_count);
206 etl::io_port_wo<uint32_t> divide_reg{
207 reinterpret_cast<void*
>(apic_base_ + kXApicTimerDivideOffset)};
208 divide_reg.write(divide_value);
211 auto lvt_timer =
static_cast<uint32_t
>(vector);
213 lvt_timer |= kLvtPeriodicMode;
216 etl::io_port_wo<uint32_t> lvt_timer_reg{
217 reinterpret_cast<void*
>(apic_base_ + kXApicLvtTimerOffset)};
218 lvt_timer_reg.write(lvt_timer);
221 etl::io_port_wo<uint32_t> init_count_reg{
222 reinterpret_cast<void*
>(apic_base_ + kXApicTimerInitCountOffset)};
223 init_count_reg.write(initial_count);
229 auto lvt_timer = cpu_io::msr::apic::ReadLvtTimer();
231 cpu_io::msr::apic::WriteLvtTimer(lvt_timer);
232 cpu_io::msr::apic::WriteTimerInitCount(0);
234 etl::io_port_rw<uint32_t> lvt_timer_reg{
236 auto lvt_timer = lvt_timer_reg.read();
238 lvt_timer_reg.write(lvt_timer);
239 etl::io_port_wo<uint32_t> init_count_reg{
241 init_count_reg.write(0);
247 return cpu_io::msr::apic::ReadTimerCurrCount();
250 etl::io_port_ro<uint32_t> curr_count_reg{
252 return curr_count_reg.read();
262 auto initial_count = kDefaultApicClockHz / frequency_hz;
265 auto divide_value = kTimerDivideBy1;
266 if (initial_count > 0xFFFFFFFF) {
268 divide_value = kTimerDivideBy16;
269 initial_count = (kDefaultApicClockHz / 16) / frequency_hz;
272 EnableTimer(initial_count, divide_value, vector,
true);
281 (kDefaultApicClockHz / kMicrosecondsPerSecond) * microseconds;
284 auto divide_value = kTimerDivideBy1;
285 if (initial_count > 0xFFFFFFFF) {
286 divide_value = kTimerDivideBy16;
288 ((kDefaultApicClockHz / 16) / kMicrosecondsPerSecond) * microseconds;
291 EnableTimer(initial_count, divide_value, vector,
false);
295 if (is_x2apic_mode_) {
296 auto icr = kInitIpiMode;
297 icr |=
static_cast<uint64_t
>(destination_apic_id) << 32;
299 cpu_io::msr::apic::WriteIcr(icr);
301 while ((cpu_io::msr::apic::ReadIcr() & kIcrDeliveryStatusBit) != 0) {
306 auto icr_high = (destination_apic_id & kApicIdMask) << kIcrDestShift;
307 etl::io_port_wo<uint32_t> icr_high_reg{
308 reinterpret_cast<void*
>(apic_base_ + kXApicIcrHighOffset)};
309 icr_high_reg.write(icr_high);
312 auto icr_low = kInitIpiMode;
313 etl::io_port_wo<uint32_t> icr_low_wo{
314 reinterpret_cast<void*
>(apic_base_ + kXApicIcrLowOffset)};
315 icr_low_wo.write(icr_low);
318 etl::io_port_ro<uint32_t> icr_low_ro{
319 reinterpret_cast<void*
>(apic_base_ + kXApicIcrLowOffset)};
320 while ((icr_low_ro.read() & kIcrDeliveryStatusBit) != 0) {
326 klog::Info(
"INIT IPI sent to APIC ID {:#x}", destination_apic_id);
330 uint8_t start_page)
const ->
void {
331 if (is_x2apic_mode_) {
333 auto icr = kSipiMode | start_page;
334 icr |=
static_cast<uint64_t
>(destination_apic_id) << 32;
336 cpu_io::msr::apic::WriteIcr(icr);
338 while ((cpu_io::msr::apic::ReadIcr() & kIcrDeliveryStatusBit) != 0) {
343 uint32_t icr_high = (destination_apic_id & kApicIdMask) << kIcrDestShift;
344 etl::io_port_wo<uint32_t> icr_high_reg{
345 reinterpret_cast<void*
>(apic_base_ + kXApicIcrHighOffset)};
346 icr_high_reg.write(icr_high);
349 uint32_t icr_low = kSipiMode | start_page;
350 etl::io_port_wo<uint32_t> icr_low_wo{
351 reinterpret_cast<void*
>(apic_base_ + kXApicIcrLowOffset)};
352 icr_low_wo.write(icr_low);
355 etl::io_port_ro<uint32_t> icr_low_ro{
356 reinterpret_cast<void*
>(apic_base_ + kXApicIcrLowOffset)};
357 while ((icr_low_ro.read() & kIcrDeliveryStatusBit) != 0) {
370 cpu_io::msr::apic::WriteLvtLint1(
kNmiMode);
376 etl::io_port_wo<uint32_t> lint0_reg{
381 etl::io_port_wo<uint32_t> lint1_reg{
386 etl::io_port_wo<uint32_t> error_reg{
400 etl::io_port_rw<uint32_t> esr_reg{
403 return esr_reg.read();
416 uint32_t sivr = cpu_io::msr::apic::ReadSivr();
420 uint32_t lvt_timer = cpu_io::msr::apic::ReadLvtTimer();
423 uint32_t lvt_lint0 = cpu_io::msr::apic::ReadLvtLint0();
426 uint32_t lvt_lint1 = cpu_io::msr::apic::ReadLvtLint1();
429 uint32_t lvt_error = cpu_io::msr::apic::ReadLvtError();
432 etl::io_port_ro<uint32_t> sivr_reg{
434 uint32_t sivr = sivr_reg.read();
438 etl::io_port_ro<uint32_t> lvt_timer_reg{
440 uint32_t lvt_timer = lvt_timer_reg.read();
443 etl::io_port_ro<uint32_t> lvt_lint0_reg{
445 uint32_t lvt_lint0 = lvt_lint0_reg.read();
448 etl::io_port_ro<uint32_t> lvt_lint1_reg{
450 uint32_t lvt_lint1 = lvt_lint1_reg.read();
453 etl::io_port_ro<uint32_t> lvt_error_reg{
455 uint32_t lvt_error = lvt_error_reg.read();
463 return cpu_io::cpuid::HasX2Apic();
468 cpu_io::msr::apic::EnableGlobally();
470 cpu_io::msr::apic::DisableX2Apic();
476 cpu_io::msr::apic::DisableGlobally();
482 return cpu_io::msr::apic::IsGloballyEnabled() &&
483 !cpu_io::msr::apic::IsX2ApicEnabled();
494 cpu_io::msr::apic::EnableX2Apic();
502 cpu_io::msr::apic::DisableX2Apic();
507 return cpu_io::msr::apic::IsX2ApicEnabled();
511 uint8_t start_vector)
const ->
void {
513 SendInitIpi(destination_apic_id);
516 auto delay = 10 * kCalibrationDelayLoop;
518 __asm__
volatile(
"nop");
522 SendStartupIpi(destination_apic_id, start_vector);
525 delay = 200 * (kCalibrationDelayLoop / 1000);
527 __asm__
volatile(
"nop");
531 SendStartupIpi(destination_apic_id, start_vector);
534 delay = 200 * (kCalibrationDelayLoop / 1000);
536 __asm__
volatile(
"nop");
auto IsXApicEnabled() const -> bool
检查传统 xAPIC 是否启用
static constexpr uint32_t kXApicLvtLint0Offset
LVT LINT0 寄存器偏移
auto SetupOneShotTimer(uint32_t microseconds, uint8_t vector) const -> void
设置单次定时器
auto EnableXApic() const -> bool
启用传统 xAPIC 模式
static constexpr uint32_t kXApicEoiOffset
EOI 寄存器偏移
uint64_t apic_base_
APIC 基地址(仅用于 xAPIC 模式)
static constexpr uint32_t kXApicTimerCurrCountOffset
定时器当前计数寄存器偏移
static constexpr uint32_t kApicSoftwareEnableBit
APIC 软件启用位
static constexpr uint32_t kXApicVersionOffset
版本寄存器偏移
static constexpr uint32_t kXApicSivrOffset
虚假中断向量寄存器偏移
static constexpr uint32_t kXApicEsrOffset
错误状态寄存器偏移
auto DisableTimer() const -> void
禁用 Local APIC 定时器
static constexpr uint32_t kXApicLvtLint1Offset
LVT LINT1 寄存器偏移
auto DisableXApic() const -> bool
禁用传统 xAPIC 模式
auto GetApicVersion() const -> uint32_t
获取 APIC 版本信息
bool is_x2apic_mode_
当前 APIC 模式(true = x2APIC, false = xAPIC)
auto SendEoi() const -> void
发送中断结束信号 (EOI)
static constexpr uint32_t kExtIntMode
ExtINT 传递模式
auto DisableX2Apic() const -> bool
禁用 x2APIC 模式
static constexpr uint32_t kXApicTprOffset
任务优先级寄存器偏移
auto ReadErrorStatus() const -> uint32_t
读取错误状态
static constexpr uint32_t kApicIdMask
xAPIC ID 掩码
auto SendStartupIpi(uint32_t destination_apic_id, uint8_t start_page) const -> void
发送 SIPI (Startup IPI)
static constexpr uint32_t kNmiMode
NMI 传递模式
auto GetTaskPriority() const -> uint8_t
获取任务优先级
auto SetupPeriodicTimer(uint32_t frequency_hz, uint8_t vector) const -> void
设置周期性定时器
static constexpr uint32_t kLvtMaskBit
LVT 掩码位
auto EnableTimer(uint32_t initial_count, uint32_t divide_value, uint8_t vector, bool periodic=true) const -> void
启用 Local APIC 定时器
auto BroadcastIpi(uint8_t vector) const -> Expected< void >
广播 IPI 到所有其他 CPU
auto GetTimerCurrentCount() const -> uint32_t
获取定时器当前计数值
auto SendIpi(uint32_t destination_apic_id, uint8_t vector) const -> Expected< void >
发送处理器间中断 (IPI)
auto ConfigureLvtEntries() const -> void
配置 Local Vector Table 条目
static constexpr uint32_t kXApicTimerInitCountOffset
定时器初始计数寄存器偏移
static constexpr uint32_t kXApicLvtErrorOffset
LVT 错误寄存器偏移
auto IsX2ApicEnabled() const -> bool
检查 x2APIC 是否启用
auto PrintInfo() const -> void
打印 Local APIC 信息(调试用)
auto CheckX2ApicSupport() const -> bool
检查 CPU 是否支持 x2APIC
auto EnableX2Apic() const -> bool
启用 x2APIC 模式
static constexpr uint8_t kErrorVector
错误中断向量
auto Init() -> Expected< void >
初始化 Local APIC
static constexpr uint32_t kXApicLvtTimerOffset
LVT 定时器寄存器偏移
auto SendInitIpi(uint32_t destination_apic_id) const -> void
发送 INIT IPI
auto WakeupAp(uint32_t destination_apic_id, uint8_t start_vector) const -> void
唤醒应用处理器 (AP)
auto SetTaskPriority(uint8_t priority) const -> void
设置任务优先级
std::expected< T, Error > Expected
std::expected 别名模板
auto Err(etl::format_string< Args... > fmt, Args &&... args) -> void
以 ERROR 级别记录日志
auto Info(etl::format_string< Args... > fmt, Args &&... args) -> void
以 INFO 级别记录日志