1 /*
2 * LAPIC
3 */
4
5 #include <stdbool.h>
6 #include <stdint.h>
7
8 #include <sys/kassert.h>
9 #include <sys/kdebug.h>
10
11 #include <machine/amd64.h>
12 #include <machine/amd64op.h>
13 #include <machine/pmap.h>
14 #include <machine/trap.h>
15
16 #define CPUID_FLAG_APIC 0x100
17
18 #define IA32_APIC_BASE_MSR 0x1B
19 #define IA32_APIC_BASE_MSR_BSP 0x100
20 #define IA32_APIC_BASE_MSR_ENABLE 0x800
21
22 #define LAPIC_ID 0x0020 /* CPU ID */
23 #define LAPIC_VERSION 0x0030 /* Version */
24 #define LAPIC_VERSION_LVTMASK 0x00FF0000
25 #define LAPIC_VERSION_LVTSHIFT 0x10
26 #define LAPIC_TPR 0x0080 /* Task Priority Register */
27 #define LAPIC_EOI 0x00B0 /* End of Interrupt */
28 #define LAPIC_SIV 0x00F0 /* Spurious Interrupt Vector */
29 #define LAPIC_SIV_ENABLE 0x100
30
31 #define LAPIC_ESR 0x0280 /* Error Status Register */
32 #define LAPIC_LVT_CMCI 0x02F0 /* LVT CMCI */
33
34 #define LAPIC_ICR_LO 0x0300 /* Interrupt Command Register */
35 #define LAPIC_ICR_HI 0x0310 /* Interrupt Command Register */
36 #define LAPIC_ICR_FIXED 0x0000 /* Delivery Mode */
37 #define LAPIC_ICR_NMI 0x0400
38 #define LAPIC_ICR_INIT 0x0500
39 #define LAPIC_ICR_STARTUP 0x0600
40 #define LAPIC_ICR_ASSERT 0x4000
41 #define LAPIC_ICR_TRIG 0x8000
42 #define LAPIC_ICR_SELF 0x00080000 /* Destination */
43 #define LAPIC_ICR_INCSELF 0x00080000
44 #define LAPIC_ICR_EXCSELF 0x000C0000
45 #define LAPIC_ICR_DELIVERY_PENDING 0x1000 /* Delivery Pending */
46
47 #define LAPIC_LVT_TIMER 0x0320 /* LVT Timer */
48 #define LAPIC_LVT_TIMER_ONESHOT 0x00000000
49 #define LAPIC_LVT_TIMER_PERIODIC 0x00020000
50 #define LAPIC_LVT_TIMER_TSCDEADLINE 0x00040000
51 #define LAPIC_LVT_THERMAL 0x0330 /* LVT Thermal Sensor */
52 #define LAPIC_LVT_PMCR 0x0340 /* LVT Performance Monitoring Counter */
53 #define LAPIC_LVT_LINT0 0x0350 /* LVT LINT0 */
54 #define LAPIC_LVT_LINT1 0x0360 /* LVT LINT1 */
55 #define LAPIC_LVT_ERROR 0x0370 /* LVT Error */
56 #define LAPIC_LVT_FLAG_MASKED 0x00010000 /* Masked */
57 #define LAPIC_LVT_FLAG_NMI 0x00000400 /* NMI */
58 #define LAPIC_LVT_FLAG_EXTINT 0x00000700 /* ExtINT */
59
60 #define LAPIC_TICR 0x0380 /* Timer Initial Count Register */
61 #define LAPIC_TCCR 0x0390 /* Timer Currnet Count Register */
62 #define LAPIC_TDCR 0x03E0 /* Time Divide Configuration Register */
63 #define LAPIC_TDCR_X1 0x000B /* Divide counts by 1 */
64
65 bool lapicInitialized = false;
66
67 static uint32_t *
LAPIC_GetBase()68 LAPIC_GetBase()
69 {
70 uint64_t base = rdmsr(IA32_APIC_BASE_MSR) & 0xFFFFFFFFFFFFF000ULL;
71
72 return (uint32_t *)DMPA2VA(base);
73 }
74
75 uint32_t
LAPIC_Read(uint16_t reg)76 LAPIC_Read(uint16_t reg)
77 {
78 uint32_t volatile *lapic = (uint32_t volatile *) LAPIC_GetBase();
79
80 return lapic[reg >> 2];
81 }
82
83 void
LAPIC_Write(uint16_t reg,uint32_t val)84 LAPIC_Write(uint16_t reg, uint32_t val)
85 {
86 uint32_t volatile *lapic = (uint32_t volatile *)LAPIC_GetBase();
87
88 lapic[reg >> 2] = val;
89 lapic[LAPIC_ID >> 2];
90 }
91
92 uint32_t
LAPIC_CPU()93 LAPIC_CPU()
94 {
95 if (lapicInitialized)
96 return LAPIC_Read(LAPIC_ID) >> 24;
97 else
98 return 0;
99 }
100
101 void
LAPIC_SendEOI()102 LAPIC_SendEOI()
103 {
104 LAPIC_Write(LAPIC_EOI, 0);
105 }
106
107 void
LAPIC_Periodic(uint64_t rate)108 LAPIC_Periodic(uint64_t rate)
109 {
110 LAPIC_Write(LAPIC_TDCR, LAPIC_TDCR_X1);
111 LAPIC_Write(LAPIC_LVT_TIMER, LAPIC_LVT_TIMER_PERIODIC | T_IRQ_TIMER);
112 LAPIC_Write(LAPIC_TICR, rate);
113 }
114
115 void
LAPIC_StartAP(uint8_t apicid,uint32_t addr)116 LAPIC_StartAP(uint8_t apicid, uint32_t addr)
117 {
118 // Setup CMOS stuff
119 outb(0x70, 0x0F);
120 outb(0x71, 0x0A);
121 uint16_t *cmosStartup = (uint16_t *)DMPA2VA(0x467);
122 cmosStartup[0] = 0;
123 cmosStartup[1] = addr >> 4;
124
125 // Send INIT
126 LAPIC_Write(LAPIC_ICR_HI, apicid << 24);
127 LAPIC_Write(LAPIC_ICR_LO, LAPIC_ICR_INIT | LAPIC_ICR_TRIG | LAPIC_ICR_ASSERT);
128 // XXX: Delay
129 LAPIC_Write(LAPIC_ICR_HI, apicid << 24);
130 LAPIC_Write(LAPIC_ICR_LO, LAPIC_ICR_INIT | LAPIC_ICR_TRIG);
131 // XXX: Delay
132
133 // Send STARTUP
134 LAPIC_Write(LAPIC_ICR_HI, apicid << 24);
135 LAPIC_Write(LAPIC_ICR_LO, LAPIC_ICR_STARTUP | (addr >> 12));
136 // XXX: Delay
137 LAPIC_Write(LAPIC_ICR_HI, apicid << 24);
138 LAPIC_Write(LAPIC_ICR_LO, LAPIC_ICR_STARTUP | (addr >> 12));
139 // XXX: Delay
140 }
141
142 int
LAPIC_Broadcast(int vector)143 LAPIC_Broadcast(int vector)
144 {
145 int i = 0;
146 LAPIC_Write(LAPIC_ICR_LO, LAPIC_ICR_EXCSELF | vector);
147
148 while ((LAPIC_Read(LAPIC_ICR_LO) & LAPIC_ICR_DELIVERY_PENDING) != 0) {
149 pause();
150
151 if (i > 1000000) {
152 kprintf("IPI not delivered?\n");
153 return -1;
154 }
155 }
156
157 return 0;
158 }
159
160 int
LAPIC_BroadcastNMI(int vector)161 LAPIC_BroadcastNMI(int vector)
162 {
163 int i = 0;
164 LAPIC_Write(LAPIC_ICR_LO, LAPIC_ICR_EXCSELF | LAPIC_ICR_NMI | vector);
165
166 while ((LAPIC_Read(LAPIC_ICR_LO) & LAPIC_ICR_DELIVERY_PENDING) != 0) {
167 pause();
168
169 if (i > 1000000) {
170 kprintf("IPI not delivered?\n");
171 return -1;
172 }
173 }
174
175 return 0;
176 }
177
178 void
LAPIC_Init()179 LAPIC_Init()
180 {
181 uint32_t version;
182 uint32_t lvts;
183 uint32_t edx;
184 uint64_t base;
185
186 cpuid(1, NULL, NULL, NULL, &edx);
187 if ((edx & CPUID_FLAG_APIC) == 0)
188 Panic("APIC is required!\n");
189
190 // Disable ATPIC
191 if (LAPIC_CPU() == 0) {
192 outb(0xA1, 0xFF);
193 outb(0x21, 0xFF);
194 }
195
196 // Enable LAPIC
197 base = rdmsr(IA32_APIC_BASE_MSR);
198 wrmsr(IA32_APIC_BASE_MSR, base | IA32_APIC_BASE_MSR_ENABLE);
199
200 // Convert to Direct Map Address
201 base = DMPA2VA(base);
202
203 lapicInitialized = true;
204
205 kprintf("LAPIC: CPU %d found at 0x%016llx\n", LAPIC_CPU(), base);
206
207 version = LAPIC_Read(LAPIC_VERSION);
208 lvts = (version & LAPIC_VERSION_LVTMASK) >> LAPIC_VERSION_LVTSHIFT;
209
210 // Enable interrupts
211 LAPIC_Write(LAPIC_SIV, LAPIC_SIV_ENABLE | T_IRQ_SPURIOUS);
212
213 // Error Interrupt
214 LAPIC_Write(LAPIC_LVT_ERROR, T_IRQ_ERROR);
215
216 // Setup LINT0/1
217 if (LAPIC_CPU() == 0) {
218 LAPIC_Write(LAPIC_LVT_LINT0, LAPIC_LVT_FLAG_EXTINT);
219 LAPIC_Write(LAPIC_LVT_LINT1, LAPIC_LVT_FLAG_NMI);
220 } else {
221 LAPIC_Write(LAPIC_LVT_LINT0, LAPIC_LVT_FLAG_MASKED);
222 LAPIC_Write(LAPIC_LVT_LINT1, LAPIC_LVT_FLAG_MASKED);
223 }
224
225 // Performance Counter Interrupt
226 if (lvts >= 4) {
227 LAPIC_Write(LAPIC_LVT_PMCR, LAPIC_LVT_FLAG_MASKED);
228 }
229
230 // Thermal Interrupt
231 if (lvts >= 5) {
232 LAPIC_Write(LAPIC_LVT_THERMAL, T_IRQ_THERMAL);
233 }
234
235 // Machine Check Interrupt
236 if (lvts >= 6) {
237 LAPIC_Write(LAPIC_LVT_CMCI, LAPIC_LVT_FLAG_MASKED);
238 }
239
240 LAPIC_Periodic(10000000); // XXX: 100 Hz (changes must update trap.c as well)
241
242 // Clear any remaining errors
243 LAPIC_Write(LAPIC_ESR, 0);
244 LAPIC_Write(LAPIC_ESR, 0);
245
246 LAPIC_Write(LAPIC_ICR_HI, 0);
247 LAPIC_Write(LAPIC_ICR_LO, LAPIC_ICR_INCSELF | LAPIC_ICR_INIT | LAPIC_ICR_TRIG);
248 while (LAPIC_Read(LAPIC_ICR_LO) & LAPIC_ICR_DELIVERY_PENDING)
249 {
250 // XXX: Timeout
251 }
252
253 LAPIC_SendEOI();
254
255 LAPIC_Write(LAPIC_TPR, 0);
256 }
257
258 static void
Debug_LAPIC(int argc,const char * argv[])259 Debug_LAPIC(int argc, const char *argv[])
260 {
261 uint32_t version = LAPIC_Read(LAPIC_VERSION);
262 uint32_t lvts = (version & LAPIC_VERSION_LVTMASK) >> LAPIC_VERSION_LVTSHIFT;
263
264 kprintf("LAPIC %d\n", LAPIC_CPU());
265 kprintf("VERSION: %08x\n", LAPIC_Read(LAPIC_VERSION));
266 kprintf("ESR: %08x\n", LAPIC_Read(LAPIC_ESR));
267 kprintf("ICRLO: %08x\n", LAPIC_Read(LAPIC_ICR_LO));
268 kprintf("ICRHI: %08x\n", LAPIC_Read(LAPIC_ICR_HI));
269 kprintf("SIV: %08x\n", LAPIC_Read(LAPIC_SIV));
270 kprintf("ERROR: %08x\n", LAPIC_Read(LAPIC_LVT_ERROR));
271 if (lvts >= 5) {
272 kprintf("THERMAL: %08x\n", LAPIC_Read(LAPIC_LVT_THERMAL));
273 }
274 kprintf("LINT0: %08x\n", LAPIC_Read(LAPIC_LVT_LINT0));
275 kprintf("LINT1: %08x\n", LAPIC_Read(LAPIC_LVT_LINT1));
276 if (lvts >= 4) {
277 kprintf("PMCR: %08x\n", LAPIC_Read(LAPIC_LVT_PMCR));
278 }
279 if (lvts >= 6) {
280 kprintf("CMCI: %08x\n", LAPIC_Read(LAPIC_LVT_CMCI));
281 }
282 }
283
284 REGISTER_DBGCMD(lapic, "LAPIC Status", Debug_LAPIC);
285
286