1 /* $OpenBSD: acpimadt.c,v 1.39 2022/11/24 04:04:39 jmatthew Exp $ */
2 /*
3 * Copyright (c) 2006 Mark Kettenis <kettenis@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 #include <sys/malloc.h>
22
23 #include <machine/apicvar.h>
24 #include <machine/cpuvar.h>
25 #include <machine/bus.h>
26
27 #include <dev/acpi/acpireg.h>
28 #include <dev/acpi/acpivar.h>
29 #include <dev/acpi/acpidev.h>
30 #include <dev/acpi/amltypes.h>
31 #include <dev/acpi/dsdt.h>
32
33 #include <machine/i8259.h>
34 #include <machine/i82093reg.h>
35 #include <machine/i82093var.h>
36 #include <machine/i82489reg.h>
37 #include <machine/i82489var.h>
38
39 #include <machine/mpbiosvar.h>
40
41 #include "ioapic.h"
42
43 int acpimadt_match(struct device *, void *, void *);
44 void acpimadt_attach(struct device *, struct device *, void *);
45
46 const struct cfattach acpimadt_ca = {
47 sizeof(struct device), acpimadt_match, acpimadt_attach
48 };
49
50 struct cfdriver acpimadt_cd = {
51 NULL, "acpimadt", DV_DULL
52 };
53
54 int acpimadt_validate(struct acpi_madt *);
55 int acpimadt_cfg_intr(int, uint32_t *);
56 int acpimadt_print(void *, const char *);
57
58 int
acpimadt_match(struct device * parent,void * match,void * aux)59 acpimadt_match(struct device *parent, void *match, void *aux)
60 {
61 struct acpi_attach_args *aaa = aux;
62 struct acpi_table_header *hdr;
63
64 /*
65 * If we do not have a table, it is not us
66 */
67 if (aaa->aaa_table == NULL)
68 return (0);
69
70 /*
71 * If it is an MADT table, we can attach
72 */
73 hdr = (struct acpi_table_header *)aaa->aaa_table;
74 if (memcmp(hdr->signature, MADT_SIG, sizeof(MADT_SIG) - 1) != 0)
75 return (0);
76
77 return (1);
78 }
79
80 int
acpimadt_validate(struct acpi_madt * madt)81 acpimadt_validate(struct acpi_madt *madt)
82 {
83 caddr_t addr = (caddr_t)(madt + 1);
84
85 while (addr < (caddr_t)madt + madt->hdr.length) {
86 union acpi_madt_entry *entry = (union acpi_madt_entry *)addr;
87 uint8_t length = entry->madt_lapic.length;
88
89 if (length < 2)
90 return (0);
91
92 if (addr + length > (caddr_t)madt + madt->hdr.length)
93 return (0);
94
95 switch (entry->madt_lapic.apic_type) {
96 case ACPI_MADT_LAPIC:
97 if (length != sizeof(entry->madt_lapic))
98 return (0);
99 break;
100 case ACPI_MADT_IOAPIC:
101 if (length != sizeof(entry->madt_ioapic))
102 return (0);
103 break;
104 case ACPI_MADT_OVERRIDE:
105 if (length != sizeof(entry->madt_override))
106 return (0);
107 break;
108 case ACPI_MADT_NMI:
109 if (length != sizeof(entry->madt_nmi))
110 return (0);
111 break;
112 case ACPI_MADT_LAPIC_NMI:
113 if (length != sizeof(entry->madt_lapic_nmi))
114 return (0);
115 break;
116 case ACPI_MADT_LAPIC_OVERRIDE:
117 if (length != sizeof(entry->madt_lapic_override))
118 return (0);
119 break;
120 case ACPI_MADT_IO_SAPIC:
121 if (length != sizeof(entry->madt_io_sapic))
122 return (0);
123 break;
124 case ACPI_MADT_LOCAL_SAPIC:
125 if (length != sizeof(entry->madt_local_sapic))
126 return (0);
127 break;
128 case ACPI_MADT_PLATFORM_INT:
129 if (length != sizeof(entry->madt_platform_int))
130 return (0);
131 break;
132 case ACPI_MADT_X2APIC:
133 if (length != sizeof(entry->madt_x2apic))
134 return (0);
135 break;
136 case ACPI_MADT_X2APIC_NMI:
137 if (length != sizeof(entry->madt_x2apic_nmi))
138 return (0);
139 break;
140 }
141
142 addr += length;
143 }
144
145 return (1);
146 }
147
148 struct mp_bus acpimadt_busses[256];
149 struct mp_bus acpimadt_isa_bus;
150
151 int
acpimadt_cfg_intr(int flags,uint32_t * redir)152 acpimadt_cfg_intr(int flags, uint32_t *redir)
153 {
154 int mpspo = (flags >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK;
155 int mpstrig = (flags >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK;
156
157 *redir &= ~IOAPIC_REDLO_DEL_MASK;
158 switch (mpspo) {
159 case MPS_INTPO_DEF:
160 case MPS_INTPO_ACTHI:
161 *redir &= ~IOAPIC_REDLO_ACTLO;
162 break;
163 case MPS_INTPO_ACTLO:
164 *redir |= IOAPIC_REDLO_ACTLO;
165 break;
166 default:
167 return (0);
168 }
169
170 *redir |= (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT);
171
172 switch (mpstrig) {
173 case MPS_INTTR_LEVEL:
174 *redir |= IOAPIC_REDLO_LEVEL;
175 break;
176 case MPS_INTTR_DEF:
177 case MPS_INTTR_EDGE:
178 *redir &= ~IOAPIC_REDLO_LEVEL;
179 break;
180 default:
181 return (0);
182 }
183
184 return (1);
185 }
186
187 static uint8_t lapic_map[256];
188
189 void
acpimadt_attach(struct device * parent,struct device * self,void * aux)190 acpimadt_attach(struct device *parent, struct device *self, void *aux)
191 {
192 struct acpi_softc *acpi_sc = (struct acpi_softc *)parent;
193 struct device *mainbus = parent->dv_parent->dv_parent;
194 struct acpi_attach_args *aaa = aux;
195 struct acpi_madt *madt = (struct acpi_madt *)aaa->aaa_table;
196 caddr_t addr = (caddr_t)(madt + 1);
197 struct aml_value arg;
198 struct mp_intr_map *map;
199 struct ioapic_softc *apic;
200 int nlapic_nmis = 0;
201 int pin;
202
203 /* Do some sanity checks before committing to run in APIC mode. */
204 if (!acpimadt_validate(madt)) {
205 printf(": invalid, skipping\n");
206 return;
207 }
208
209 printf(" addr 0x%x", madt->local_apic_address);
210 if (madt->flags & ACPI_APIC_PCAT_COMPAT)
211 printf(": PC-AT compat");
212 printf("\n");
213
214 /* Tell the BIOS we will be using APIC mode. */
215 memset(&arg, 0, sizeof(arg));
216 arg.type = AML_OBJTYPE_INTEGER;
217 arg.v_integer = 1;
218
219 aml_evalname(acpi_sc, NULL, "\\_PIC", 1, &arg, NULL);
220
221 mp_busses = acpimadt_busses;
222 mp_nbusses = nitems(acpimadt_busses);
223 mp_isa_bus = &acpimadt_isa_bus;
224
225 lapic_boot_init(madt->local_apic_address);
226
227 /* 1st pass, get CPUs and IOAPICs */
228 while (addr < (caddr_t)madt + madt->hdr.length) {
229 union acpi_madt_entry *entry = (union acpi_madt_entry *)addr;
230 struct cpu_attach_args caa;
231 struct apic_attach_args aaa;
232
233 switch (entry->madt_lapic.apic_type) {
234 case ACPI_MADT_LAPIC_OVERRIDE:
235 if (entry->madt_lapic_override.lapic_address !=
236 madt->local_apic_address) {
237 printf("%s: ignored LAPIC override 0x%llx\n",
238 self->dv_xname,
239 entry->madt_lapic_override.lapic_address);
240 }
241 break;
242 case ACPI_MADT_LAPIC:
243 dprintf("%s: LAPIC: acpi_proc_id %x, apic_id %x, flags 0x%x\n",
244 self->dv_xname, entry->madt_lapic.acpi_proc_id,
245 entry->madt_lapic.apic_id,
246 entry->madt_lapic.flags);
247
248 if ((entry->madt_lapic.flags & ACPI_PROC_ENABLE) == 0)
249 break;
250
251 lapic_map[entry->madt_lapic.acpi_proc_id] =
252 entry->madt_lapic.apic_id;
253
254 memset(&caa, 0, sizeof(struct cpu_attach_args));
255 if (lapic_cpu_number() == entry->madt_lapic.apic_id)
256 caa.cpu_role = CPU_ROLE_BP;
257 else {
258 caa.cpu_role = CPU_ROLE_AP;
259 ncpusfound++;
260 }
261 caa.caa_name = "cpu";
262 caa.cpu_apicid = entry->madt_lapic.apic_id;
263 caa.cpu_acpi_proc_id = entry->madt_lapic.acpi_proc_id;
264 #ifdef MULTIPROCESSOR
265 caa.cpu_func = &mp_cpu_funcs;
266 #endif
267 #ifdef __i386__
268 /*
269 * XXX utterly wrong. These are the
270 * cpu_feature/cpu_id from the BSP cpu, now
271 * being given to another cpu. This is
272 * bullshit.
273 */
274 extern int cpu_id, cpu_feature;
275 caa.cpu_signature = cpu_id;
276 caa.feature_flags = cpu_feature;
277 #endif
278
279 config_found(mainbus, &caa, acpimadt_print);
280 break;
281 case ACPI_MADT_IOAPIC:
282 dprintf("%s: IOAPIC: acpi_ioapic_id %x, address 0x%x, global_int_base 0x%x\n",
283 self->dv_xname, entry->madt_ioapic.acpi_ioapic_id,
284 entry->madt_ioapic.address,
285 entry->madt_ioapic.global_int_base);
286
287 memset(&aaa, 0, sizeof(struct apic_attach_args));
288 aaa.aaa_name = "ioapic";
289 aaa.apic_id = entry->madt_ioapic.acpi_ioapic_id;
290 aaa.apic_address = entry->madt_ioapic.address;
291 aaa.apic_vecbase = entry->madt_ioapic.global_int_base;
292
293 config_found(mainbus, &aaa, acpimadt_print);
294 break;
295 case ACPI_MADT_LAPIC_NMI:
296 nlapic_nmis++;
297 break;
298 case ACPI_MADT_X2APIC:
299 dprintf("%s: X2APIC: acpi_proc_uid %x, apic_id %x, flags 0x%x\n",
300 self->dv_xname, entry->madt_x2apic.acpi_proc_uid,
301 entry->madt_x2apic.apic_id,
302 entry->madt_x2apic.flags);
303
304 if (entry->madt_x2apic.apic_id > 255 ||
305 (entry->madt_x2apic.flags & ACPI_PROC_ENABLE) == 0)
306 break;
307
308 memset(&caa, 0, sizeof(struct cpu_attach_args));
309 if (lapic_cpu_number() == entry->madt_x2apic.apic_id)
310 caa.cpu_role = CPU_ROLE_BP;
311 else {
312 caa.cpu_role = CPU_ROLE_AP;
313 ncpusfound++;
314 }
315 caa.caa_name = "cpu";
316 caa.cpu_apicid = entry->madt_x2apic.apic_id;
317 caa.cpu_acpi_proc_id = entry->madt_x2apic.acpi_proc_uid;
318 #ifdef MULTIPROCESSOR
319 caa.cpu_func = &mp_cpu_funcs;
320 #endif
321 #ifdef __i386__
322 /*
323 * XXX utterly wrong. These are the
324 * cpu_feature/cpu_id from the BSP cpu, now
325 * being given to another cpu. This is
326 * bullshit.
327 */
328 extern int cpu_id, cpu_feature;
329 caa.cpu_signature = cpu_id;
330 caa.feature_flags = cpu_feature;
331 #endif
332
333 config_found(mainbus, &caa, acpimadt_print);
334 break;
335 }
336 addr += entry->madt_lapic.length;
337 }
338
339 mp_intrs = mallocarray(nlapic_nmis, sizeof(struct mp_intr_map),
340 M_DEVBUF, M_NOWAIT);
341 if (mp_intrs == NULL)
342 return;
343
344 /* 2nd pass, get interrupt overrides */
345 addr = (caddr_t)(madt + 1);
346 while (addr < (caddr_t)madt + madt->hdr.length) {
347 union acpi_madt_entry *entry = (union acpi_madt_entry *)addr;
348
349 switch (entry->madt_lapic.apic_type) {
350 case ACPI_MADT_LAPIC:
351 case ACPI_MADT_IOAPIC:
352 break;
353
354 case ACPI_MADT_OVERRIDE:
355 dprintf("%s: OVERRIDE: bus %x, source %x, global_int %x, flags %x\n",
356 self->dv_xname, entry->madt_override.bus,
357 entry->madt_override.source,
358 entry->madt_override.global_int,
359 entry->madt_override.flags);
360
361 pin = entry->madt_override.global_int;
362 apic = ioapic_find_bybase(pin);
363
364 map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO);
365 if (map == NULL)
366 return;
367
368 map->ioapic = apic;
369 map->ioapic_pin = pin - apic->sc_apic_vecbase;
370 map->bus_pin = entry->madt_override.source;
371 map->flags = entry->madt_override.flags;
372
373 if (!acpimadt_cfg_intr(entry->madt_override.flags, &map->redir)) {
374 printf("%s: bogus override for pin %d\n",
375 self->dv_xname, pin);
376 free(map, M_DEVBUF, sizeof(*map));
377 break;
378 }
379
380 map->ioapic_ih = APIC_INT_VIA_APIC |
381 ((apic->sc_apicid << APIC_INT_APIC_SHIFT) |
382 (pin << APIC_INT_PIN_SHIFT));
383
384 apic->sc_pins[pin].ip_map = map;
385
386 map->next = mp_isa_bus->mb_intrs;
387 mp_isa_bus->mb_intrs = map;
388 break;
389
390 case ACPI_MADT_LAPIC_NMI:
391 dprintf("%s: LAPIC_NMI: acpi_proc_id %x, local_apic_lint %x, flags %x\n",
392 self->dv_xname, entry->madt_lapic_nmi.acpi_proc_id,
393 entry->madt_lapic_nmi.local_apic_lint,
394 entry->madt_lapic_nmi.flags);
395
396 pin = entry->madt_lapic_nmi.local_apic_lint;
397
398 map = &mp_intrs[mp_nintrs++];
399 memset(map, 0, sizeof *map);
400 map->cpu_id = lapic_map[entry->madt_lapic_nmi.acpi_proc_id];
401 map->ioapic_pin = pin;
402 map->flags = entry->madt_lapic_nmi.flags;
403
404 if ((pin != 0 && pin != 1) ||
405 !acpimadt_cfg_intr(entry->madt_lapic_nmi.flags, &map->redir)) {
406 printf("%s: bogus nmi for apid %d\n",
407 self->dv_xname, map->cpu_id);
408 mp_nintrs--;
409 break;
410 }
411
412 map->redir &= ~IOAPIC_REDLO_DEL_MASK;
413 map->redir |= (IOAPIC_REDLO_DEL_NMI << IOAPIC_REDLO_DEL_SHIFT);
414 break;
415
416 case ACPI_MADT_X2APIC:
417 case ACPI_MADT_X2APIC_NMI:
418 break;
419
420 default:
421 if (entry->madt_lapic.apic_type < ACPI_MADT_OEM_RSVD) {
422 printf("%s: unknown apic structure type %x\n",
423 self->dv_xname,
424 entry->madt_lapic.apic_type);
425 }
426 }
427
428 addr += entry->madt_lapic.length;
429 }
430
431 /*
432 * ISA interrupts are supposed to be identity mapped unless
433 * there is an override, in which case we will already have a
434 * mapping for the interrupt.
435 */
436 for (pin = 0; pin < ICU_LEN; pin++) {
437 /* Skip if we already have a mapping for this interrupt. */
438 for (map = mp_isa_bus->mb_intrs; map != NULL; map = map->next)
439 if (map->bus_pin == pin)
440 break;
441 if (map != NULL)
442 continue;
443
444 apic = ioapic_find_bybase(pin);
445
446 map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO);
447 if (map == NULL)
448 return;
449
450 map->ioapic = apic;
451 map->ioapic_pin = pin;
452 map->bus_pin = pin;
453 map->redir = (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT);
454
455 map->ioapic_ih = APIC_INT_VIA_APIC |
456 ((apic->sc_apicid << APIC_INT_APIC_SHIFT) |
457 (pin << APIC_INT_PIN_SHIFT));
458
459 apic->sc_pins[pin].ip_map = map;
460
461 map->next = mp_isa_bus->mb_intrs;
462 mp_isa_bus->mb_intrs = map;
463 }
464 }
465
466 int
acpimadt_print(void * aux,const char * pnp)467 acpimadt_print(void *aux, const char *pnp)
468 {
469 struct apic_attach_args *aaa = aux;
470
471 if (pnp)
472 printf("%s at %s:", aaa->aaa_name, pnp);
473
474 return (UNCONF);
475 }
476