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