Line data Source code
1 : /* $OpenBSD: acpimadt.c,v 1.37 2018/06/29 17:39:18 kettenis 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 : 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
59 0 : acpimadt_match(struct device *parent, void *match, void *aux)
60 : {
61 0 : 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 0 : if (aaa->aaa_table == NULL)
68 0 : return (0);
69 :
70 : /*
71 : * If it is an MADT table, we can attach
72 : */
73 0 : hdr = (struct acpi_table_header *)aaa->aaa_table;
74 0 : if (memcmp(hdr->signature, MADT_SIG, sizeof(MADT_SIG) - 1) != 0)
75 0 : return (0);
76 :
77 0 : return (1);
78 0 : }
79 :
80 : int
81 0 : acpimadt_validate(struct acpi_madt *madt)
82 : {
83 0 : caddr_t addr = (caddr_t)(madt + 1);
84 :
85 0 : while (addr < (caddr_t)madt + madt->hdr.length) {
86 0 : union acpi_madt_entry *entry = (union acpi_madt_entry *)addr;
87 0 : uint8_t length = entry->madt_lapic.length;
88 :
89 0 : if (length < 2)
90 0 : return (0);
91 :
92 0 : if (addr + length > (caddr_t)madt + madt->hdr.length)
93 0 : return (0);
94 :
95 0 : switch (entry->madt_lapic.apic_type) {
96 : case ACPI_MADT_LAPIC:
97 0 : if (length != sizeof(entry->madt_lapic))
98 0 : return (0);
99 : break;
100 : case ACPI_MADT_IOAPIC:
101 0 : if (length != sizeof(entry->madt_ioapic))
102 0 : return (0);
103 : break;
104 : case ACPI_MADT_OVERRIDE:
105 0 : if (length != sizeof(entry->madt_override))
106 0 : return (0);
107 : break;
108 : case ACPI_MADT_NMI:
109 0 : if (length != sizeof(entry->madt_nmi))
110 0 : return (0);
111 : break;
112 : case ACPI_MADT_LAPIC_NMI:
113 0 : if (length != sizeof(entry->madt_lapic_nmi))
114 0 : return (0);
115 : break;
116 : case ACPI_MADT_LAPIC_OVERRIDE:
117 0 : if (length != sizeof(entry->madt_lapic_override))
118 0 : return (0);
119 : break;
120 : case ACPI_MADT_IO_SAPIC:
121 0 : if (length != sizeof(entry->madt_io_sapic))
122 0 : return (0);
123 : break;
124 : case ACPI_MADT_LOCAL_SAPIC:
125 0 : if (length != sizeof(entry->madt_local_sapic))
126 0 : return (0);
127 : break;
128 : case ACPI_MADT_PLATFORM_INT:
129 0 : if (length != sizeof(entry->madt_platform_int))
130 0 : return (0);
131 : break;
132 : case ACPI_MADT_X2APIC:
133 0 : if (length != sizeof(entry->madt_x2apic))
134 0 : return (0);
135 : break;
136 : case ACPI_MADT_X2APIC_NMI:
137 0 : if (length != sizeof(entry->madt_x2apic_nmi))
138 0 : return (0);
139 : break;
140 : }
141 :
142 : addr += length;
143 0 : }
144 :
145 0 : return (1);
146 0 : }
147 :
148 : struct mp_bus acpimadt_busses[256];
149 : struct mp_bus acpimadt_isa_bus;
150 :
151 : int
152 0 : acpimadt_cfg_intr(int flags, uint32_t *redir)
153 : {
154 0 : int mpspo = (flags >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK;
155 0 : int mpstrig = (flags >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK;
156 :
157 0 : *redir &= ~IOAPIC_REDLO_DEL_MASK;
158 0 : switch (mpspo) {
159 : case MPS_INTPO_DEF:
160 : case MPS_INTPO_ACTHI:
161 0 : *redir &= ~IOAPIC_REDLO_ACTLO;
162 0 : break;
163 : case MPS_INTPO_ACTLO:
164 0 : *redir |= IOAPIC_REDLO_ACTLO;
165 0 : break;
166 : default:
167 0 : return (0);
168 : }
169 :
170 0 : *redir |= (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT);
171 :
172 0 : switch (mpstrig) {
173 : case MPS_INTTR_LEVEL:
174 0 : *redir |= IOAPIC_REDLO_LEVEL;
175 0 : break;
176 : case MPS_INTTR_DEF:
177 : case MPS_INTTR_EDGE:
178 0 : *redir &= ~IOAPIC_REDLO_LEVEL;
179 0 : break;
180 : default:
181 0 : return (0);
182 : }
183 :
184 0 : return (1);
185 0 : }
186 :
187 : static uint8_t lapic_map[256];
188 :
189 : void
190 0 : acpimadt_attach(struct device *parent, struct device *self, void *aux)
191 : {
192 0 : struct acpi_softc *acpi_sc = (struct acpi_softc *)parent;
193 0 : struct device *mainbus = parent->dv_parent->dv_parent;
194 0 : struct acpi_attach_args *aaa = aux;
195 0 : struct acpi_madt *madt = (struct acpi_madt *)aaa->aaa_table;
196 0 : caddr_t addr = (caddr_t)(madt + 1);
197 0 : 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 0 : if (!acpimadt_validate(madt)) {
205 0 : printf(": invalid, skipping\n");
206 0 : return;
207 : }
208 :
209 0 : printf(" addr 0x%x", madt->local_apic_address);
210 0 : if (madt->flags & ACPI_APIC_PCAT_COMPAT)
211 0 : printf(": PC-AT compat");
212 0 : printf("\n");
213 :
214 : /* Tell the BIOS we will be using APIC mode. */
215 0 : memset(&arg, 0, sizeof(arg));
216 0 : arg.type = AML_OBJTYPE_INTEGER;
217 0 : arg.v_integer = 1;
218 :
219 0 : aml_evalname(acpi_sc, NULL, "\\_PIC", 1, &arg, NULL);
220 :
221 0 : mp_busses = acpimadt_busses;
222 0 : mp_nbusses = nitems(acpimadt_busses);
223 0 : mp_isa_bus = &acpimadt_isa_bus;
224 :
225 0 : lapic_boot_init(madt->local_apic_address);
226 :
227 : /* 1st pass, get CPUs and IOAPICs */
228 0 : while (addr < (caddr_t)madt + madt->hdr.length) {
229 0 : union acpi_madt_entry *entry = (union acpi_madt_entry *)addr;
230 0 : struct cpu_attach_args caa;
231 0 : struct apic_attach_args aaa;
232 :
233 0 : switch (entry->madt_lapic.apic_type) {
234 : case ACPI_MADT_LAPIC_OVERRIDE:
235 0 : if (entry->madt_lapic_override.lapic_address !=
236 0 : madt->local_apic_address) {
237 0 : printf("%s: ignored LAPIC override 0x%llx\n",
238 0 : self->dv_xname,
239 : entry->madt_lapic_override.lapic_address);
240 0 : }
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 0 : if ((entry->madt_lapic.flags & ACPI_PROC_ENABLE) == 0)
249 : break;
250 :
251 0 : lapic_map[entry->madt_lapic.acpi_proc_id] =
252 0 : entry->madt_lapic.apic_id;
253 :
254 0 : memset(&caa, 0, sizeof(struct cpu_attach_args));
255 0 : if (lapic_cpu_number() == entry->madt_lapic.apic_id)
256 0 : caa.cpu_role = CPU_ROLE_BP;
257 : else {
258 0 : caa.cpu_role = CPU_ROLE_AP;
259 0 : ncpusfound++;
260 : }
261 0 : caa.caa_name = "cpu";
262 0 : caa.cpu_apicid = entry->madt_lapic.apic_id;
263 0 : caa.cpu_acpi_proc_id = entry->madt_lapic.acpi_proc_id;
264 : #ifdef MULTIPROCESSOR
265 0 : 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 0 : config_found(mainbus, &caa, acpimadt_print);
280 0 : 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 0 : memset(&aaa, 0, sizeof(struct apic_attach_args));
288 0 : aaa.aaa_name = "ioapic";
289 0 : aaa.apic_id = entry->madt_ioapic.acpi_ioapic_id;
290 0 : aaa.apic_address = entry->madt_ioapic.address;
291 0 : aaa.apic_vecbase = entry->madt_ioapic.global_int_base;
292 :
293 0 : config_found(mainbus, &aaa, acpimadt_print);
294 0 : break;
295 : case ACPI_MADT_LAPIC_NMI:
296 0 : nlapic_nmis++;
297 0 : 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 0 : if (entry->madt_x2apic.apic_id > 255 ||
305 0 : (entry->madt_x2apic.flags & ACPI_PROC_ENABLE) == 0)
306 : break;
307 :
308 0 : memset(&caa, 0, sizeof(struct cpu_attach_args));
309 0 : if (lapic_cpu_number() == entry->madt_x2apic.apic_id)
310 0 : caa.cpu_role = CPU_ROLE_BP;
311 : else {
312 0 : caa.cpu_role = CPU_ROLE_AP;
313 0 : ncpusfound++;
314 : }
315 0 : caa.caa_name = "cpu";
316 0 : caa.cpu_apicid = entry->madt_x2apic.apic_id;
317 0 : caa.cpu_acpi_proc_id = entry->madt_x2apic.acpi_proc_uid;
318 : #ifdef MULTIPROCESSOR
319 0 : 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 0 : config_found(mainbus, &caa, acpimadt_print);
334 0 : break;
335 : }
336 0 : addr += entry->madt_lapic.length;
337 0 : }
338 :
339 0 : mp_intrs = mallocarray(nlapic_nmis, sizeof(struct mp_intr_map),
340 : M_DEVBUF, M_NOWAIT);
341 0 : if (mp_intrs == NULL)
342 0 : return;
343 :
344 : /* 2nd pass, get interrupt overrides */
345 : addr = (caddr_t)(madt + 1);
346 0 : while (addr < (caddr_t)madt + madt->hdr.length) {
347 0 : union acpi_madt_entry *entry = (union acpi_madt_entry *)addr;
348 :
349 0 : 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 0 : pin = entry->madt_override.global_int;
362 0 : apic = ioapic_find_bybase(pin);
363 :
364 0 : map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO);
365 0 : if (map == NULL)
366 0 : return;
367 :
368 0 : map->ioapic = apic;
369 0 : map->ioapic_pin = pin - apic->sc_apic_vecbase;
370 0 : map->bus_pin = entry->madt_override.source;
371 0 : map->flags = entry->madt_override.flags;
372 :
373 0 : if (!acpimadt_cfg_intr(entry->madt_override.flags, &map->redir)) {
374 0 : printf("%s: bogus override for pin %d\n",
375 0 : self->dv_xname, pin);
376 0 : free(map, M_DEVBUF, sizeof(*map));
377 0 : break;
378 : }
379 :
380 0 : map->ioapic_ih = APIC_INT_VIA_APIC |
381 0 : ((apic->sc_apicid << APIC_INT_APIC_SHIFT) |
382 0 : (pin << APIC_INT_PIN_SHIFT));
383 :
384 0 : apic->sc_pins[pin].ip_map = map;
385 :
386 0 : map->next = mp_isa_bus->mb_intrs;
387 0 : mp_isa_bus->mb_intrs = map;
388 0 : 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 0 : pin = entry->madt_lapic_nmi.local_apic_lint;
397 :
398 0 : map = &mp_intrs[mp_nintrs++];
399 0 : memset(map, 0, sizeof *map);
400 0 : map->cpu_id = lapic_map[entry->madt_lapic_nmi.acpi_proc_id];
401 0 : map->ioapic_pin = pin;
402 0 : map->flags = entry->madt_lapic_nmi.flags;
403 :
404 0 : if ((pin != 0 && pin != 1) ||
405 0 : !acpimadt_cfg_intr(entry->madt_lapic_nmi.flags, &map->redir)) {
406 0 : printf("%s: bogus nmi for apid %d\n",
407 0 : self->dv_xname, map->cpu_id);
408 0 : mp_nintrs--;
409 0 : break;
410 : }
411 :
412 0 : map->redir &= ~IOAPIC_REDLO_DEL_MASK;
413 0 : map->redir |= (IOAPIC_REDLO_DEL_NMI << IOAPIC_REDLO_DEL_SHIFT);
414 0 : break;
415 :
416 : case ACPI_MADT_X2APIC:
417 : case ACPI_MADT_X2APIC_NMI:
418 : break;
419 :
420 : default:
421 0 : printf("%s: unknown apic structure type %x\n",
422 0 : self->dv_xname, entry->madt_lapic.apic_type);
423 0 : }
424 :
425 0 : addr += entry->madt_lapic.length;
426 0 : }
427 :
428 : /*
429 : * ISA interrupts are supposed to be identity mapped unless
430 : * there is an override, in which case we will already have a
431 : * mapping for the interrupt.
432 : */
433 0 : for (pin = 0; pin < ICU_LEN; pin++) {
434 : /* Skip if we already have a mapping for this interrupt. */
435 0 : for (map = mp_isa_bus->mb_intrs; map != NULL; map = map->next)
436 0 : if (map->bus_pin == pin)
437 : break;
438 0 : if (map != NULL)
439 : continue;
440 :
441 0 : apic = ioapic_find_bybase(pin);
442 :
443 0 : map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO);
444 0 : if (map == NULL)
445 0 : return;
446 :
447 0 : map->ioapic = apic;
448 0 : map->ioapic_pin = pin;
449 0 : map->bus_pin = pin;
450 0 : map->redir = (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT);
451 :
452 0 : map->ioapic_ih = APIC_INT_VIA_APIC |
453 0 : ((apic->sc_apicid << APIC_INT_APIC_SHIFT) |
454 0 : (pin << APIC_INT_PIN_SHIFT));
455 :
456 0 : apic->sc_pins[pin].ip_map = map;
457 :
458 0 : map->next = mp_isa_bus->mb_intrs;
459 0 : mp_isa_bus->mb_intrs = map;
460 0 : }
461 0 : }
462 :
463 : int
464 0 : acpimadt_print(void *aux, const char *pnp)
465 : {
466 0 : struct apic_attach_args *aaa = aux;
467 :
468 0 : if (pnp)
469 0 : printf("%s at %s:", aaa->aaa_name, pnp);
470 :
471 0 : return (UNCONF);
472 : }
|