1/* @title: LAPIC */
2#pragma once
3#include <asm.h>
4#include <compiler/core.h>
5#include <console/printf.h>
6#include <drivers/mmio.h>
7#include <math/bit.h>
8#include <smp/core.h>
9#include <stdbool.h>
10#include <stdint.h>
11
12#define IA32_APIC_BASE 0x1B
13#define IA32_APIC_BASE_MSR 0x1B
14#define IA32_APIC_BASE_MASK 0xFFFFF000UL
15#define IA32_APIC_BASE_ENABLE BIT(11)
16#define APIC_X2APIC_ENABLE BIT(10)
17
18#define LAPIC_REG_ID 0x020
19#define LAPIC_REG_EOI 0x0B0
20#define LAPIC_REG_SVR 0x0F0
21
22#define LAPIC_REG_LVT_TIMER 0x320
23#define LAPIC_REG_TIMER_INIT 0x380
24#define LAPIC_REG_TIMER_CUR 0x390
25#define LAPIC_REG_TIMER_DIV 0x3E0
26#define LAPIC_LVT_MASK BIT(16)
27#define LAPIC_ENABLE 0x100
28#define LAPIC_SPURIOUS_REGISTER 0xF0
29
30#define TIMER_MODE_PERIODIC BIT(17)
31#define TIMER_MODE_ONESHOT (0 << 17)
32
33#define LAPIC_ICR_LOW 0x300
34#define LAPIC_ICR_HIGH 0x310
35
36#define LAPIC_DELIVERY_FIXED (0x0 << 8)
37#define LAPIC_DELIVERY_LOWEST (0x1 << 8)
38#define LAPIC_DELIVERY_SMI (0x2 << 8)
39#define LAPIC_DELIVERY_NMI (0x4 << 8)
40#define LAPIC_DELIVERY_INIT (0x5 << 8)
41#define LAPIC_DELIVERY_STARTUP (0x6 << 8)
42
43#define LAPIC_LEVEL_ASSERT BIT(14)
44#define LAPIC_TRIGGER_EDGE (0 << 15)
45#define LAPIC_TRIGGER_LEVEL BIT(15)
46#define LAPIC_IPI_IN_FLIGHT BIT(12)
47#define LAPIC_DEST_PHYSICAL (0 << 11)
48#define LAPIC_DEST_LOGICAL BIT(11)
49
50#define LAPIC_DEST_SHORTHAND_NONE (0x0 << 18)
51#define LAPIC_DEST_SHORTHAND_SELF (0x1 << 18)
52#define LAPIC_DEST_SHORTHAND_ALL (0x2 << 18)
53#define LAPIC_DEST_SHORTHAND_OTHERS (0x3 << 18)
54
55#define LAPIC_DEST_SHIFT 24
56
57/* LDR is read-only and derived from the ID.
58 * Cluster (ID[19:4]) in bits 31:16, one-hot ID[3:0] in bits 15:0 */
59#define X2APIC_CLUSTER_SHIFT 4
60#define X2APIC_CLUSTER_SIZE BIT(X2APIC_CLUSTER_SHIFT)
61#define X2APIC_CLUSTER_ID_MASK 0xFFFF
62#define X2APIC_LDR_CLUSTER_SHIFT 16
63
64#define IA32_X2APIC_BASE 0x800
65#define IA32_X2APIC_ID (IA32_X2APIC_BASE + 0x02)
66#define IA32_X2APIC_EOI (IA32_X2APIC_BASE + 0x0B)
67#define IA32_X2APIC_SVR (IA32_X2APIC_BASE + 0x0F)
68#define IA32_X2APIC_ICR (IA32_X2APIC_BASE + 0x30)
69#define IA32_X2APIC_LVT_TIMER (IA32_X2APIC_BASE + 0x32)
70
71#define IA32_X2APIC_TIMER_INIT (IA32_X2APIC_BASE + 0x38)
72#define IA32_X2APIC_TIMER_CUR (IA32_X2APIC_BASE + 0x39)
73#define IA32_X2APIC_TIMER_DIV (IA32_X2APIC_BASE + 0x3E)
74
75extern uint32_t cc_mem_io *lapic;
76extern bool x2apic_enabled;
77static inline uint32_t lapic_reg_to_x2apic_msr(uint32_t reg) {
78 switch (reg) {
79 case LAPIC_REG_ID: return IA32_X2APIC_ID;
80 case LAPIC_REG_EOI: return IA32_X2APIC_EOI;
81 case LAPIC_REG_SVR: return IA32_X2APIC_SVR;
82 case LAPIC_REG_LVT_TIMER: return IA32_X2APIC_LVT_TIMER;
83 case LAPIC_REG_TIMER_INIT: return IA32_X2APIC_TIMER_INIT;
84 case LAPIC_REG_TIMER_CUR: return IA32_X2APIC_TIMER_CUR;
85 case LAPIC_REG_TIMER_DIV: return IA32_X2APIC_TIMER_DIV;
86 default: return 0;
87 }
88}
89
90static inline void lapic_write(uint32_t reg, uint32_t val) {
91 if (x2apic_enabled) {
92 uint32_t msr = lapic_reg_to_x2apic_msr(reg);
93 if (!msr)
94 return;
95 wrmsr(msr, value: val);
96 } else {
97 mmio_write_32(address: (uint32_t cc_mem_io *) ((uintptr_t) lapic + reg), value: val);
98 }
99}
100
101static inline uint32_t lapic_read(uint32_t reg) {
102 if (x2apic_enabled) {
103 uint32_t msr = lapic_reg_to_x2apic_msr(reg);
104 if (!msr)
105 return 0;
106 return (uint32_t) rdmsr(msr);
107 } else {
108 return mmio_read_32(address: (uint32_t cc_mem_io *) ((uintptr_t) lapic + reg));
109 }
110}
111
112void lapic_init(void);
113void x2apic_init(void);
114uint64_t lapic_get_id(void);
115uint32_t x2apic_get_id(void);
116uint32_t lapic_this_id(void);
117
118/* IOAPIC entries and MSI addresses have 8 destination bits, larger ones
119 * need x2apic */
120uint8_t lapic_dest_8bit(uint32_t apic_id);
121struct irq_chip *lapic_get_chip(void);
122
123void lapic_icr_write(uint32_t dest, uint32_t lo);
124
125/* Same as above, just the try version */
126bool lapic_icr_try_write(uint32_t dest, uint32_t lo);
127
128/* NMIs happening between IPI sends can retarget the IPI */
129uint32_t lapic_icr_nmi_save(void);
130void lapic_icr_nmi_restore(uint32_t high);
131