xref: /openbsd/sys/dev/acpi/acpimadt.c (revision 4cfece93)
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 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
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
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
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 			printf("%s: unknown apic structure type %x\n",
422 			    self->dv_xname, entry->madt_lapic.apic_type);
423 		}
424 
425 		addr += entry->madt_lapic.length;
426 	}
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 	for (pin = 0; pin < ICU_LEN; pin++) {
434 		/* Skip if we already have a mapping for this interrupt. */
435 		for (map = mp_isa_bus->mb_intrs; map != NULL; map = map->next)
436 			if (map->bus_pin == pin)
437 				break;
438 		if (map != NULL)
439 			continue;
440 
441 		apic = ioapic_find_bybase(pin);
442 
443 		map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO);
444 		if (map == NULL)
445 			return;
446 
447 		map->ioapic = apic;
448 		map->ioapic_pin = pin;
449 		map->bus_pin = pin;
450 		map->redir = (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT);
451 
452 		map->ioapic_ih = APIC_INT_VIA_APIC |
453 		    ((apic->sc_apicid << APIC_INT_APIC_SHIFT) |
454 		    (pin << APIC_INT_PIN_SHIFT));
455 
456 		apic->sc_pins[pin].ip_map = map;
457 
458 		map->next = mp_isa_bus->mb_intrs;
459 		mp_isa_bus->mb_intrs = map;
460 	}
461 }
462 
463 int
464 acpimadt_print(void *aux, const char *pnp)
465 {
466 	struct apic_attach_args *aaa = aux;
467 
468 	if (pnp)
469 		printf("%s at %s:", aaa->aaa_name, pnp);
470 
471 	return (UNCONF);
472 }
473