xref: /openbsd/sys/dev/acpi/acpimadt.c (revision a6445c1d)
1 /* $OpenBSD: acpimadt.c,v 1.29 2014/07/12 18:48:17 tedu 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 u_int8_t acpi_lapic_flags[LAPIC_MAP_SIZE];
44 
45 int acpimadt_match(struct device *, void *, void *);
46 void acpimadt_attach(struct device *, struct device *, void *);
47 
48 struct cfattach acpimadt_ca = {
49 	sizeof(struct device), acpimadt_match, acpimadt_attach
50 };
51 
52 struct cfdriver acpimadt_cd = {
53 	NULL, "acpimadt", DV_DULL
54 };
55 
56 int acpimadt_validate(struct acpi_madt *);
57 int acpimadt_cfg_intr(int, u_int32_t *);
58 int acpimadt_print(void *, const char *);
59 
60 int
61 acpimadt_match(struct device *parent, void *match, void *aux)
62 {
63 	struct acpi_attach_args *aaa = aux;
64 	struct acpi_table_header *hdr;
65 
66 	/*
67 	 * If we do not have a table, it is not us
68 	 */
69 	if (aaa->aaa_table == NULL)
70 		return (0);
71 
72 	/*
73 	 * If it is an MADT table, we can attach
74 	 */
75 	hdr = (struct acpi_table_header *)aaa->aaa_table;
76 	if (memcmp(hdr->signature, MADT_SIG, sizeof(MADT_SIG) - 1) != 0)
77 		return (0);
78 
79 	return (1);
80 }
81 
82 int
83 acpimadt_validate(struct acpi_madt *madt)
84 {
85 	caddr_t addr = (caddr_t)(madt + 1);
86 
87 	while (addr < (caddr_t)madt + madt->hdr.length) {
88 		union acpi_madt_entry *entry = (union acpi_madt_entry *)addr;
89 		u_int8_t length = entry->madt_lapic.length;
90 
91 		if (length < 2)
92 			return (0);
93 
94 		if (addr + length > (caddr_t)madt + madt->hdr.length)
95 			return (0);
96 
97 		switch (entry->madt_lapic.apic_type) {
98 		case ACPI_MADT_LAPIC:
99 			if (length != sizeof(entry->madt_lapic))
100 				return (0);
101 			break;
102 		case ACPI_MADT_IOAPIC:
103 			if (length != sizeof(entry->madt_ioapic))
104 				return (0);
105 			break;
106 		case ACPI_MADT_OVERRIDE:
107 			if (length != sizeof(entry->madt_override))
108 				return (0);
109 			break;
110 		case ACPI_MADT_NMI:
111 			if (length != sizeof(entry->madt_nmi))
112 				return (0);
113 			break;
114 		case ACPI_MADT_LAPIC_NMI:
115 			if (length != sizeof(entry->madt_lapic_nmi))
116 				return (0);
117 			break;
118 		case ACPI_MADT_LAPIC_OVERRIDE:
119 			if (length != sizeof(entry->madt_lapic_override))
120 				return (0);
121 			break;
122 		case ACPI_MADT_IO_SAPIC:
123 			if (length != sizeof(entry->madt_io_sapic))
124 				return (0);
125 			break;
126 		case ACPI_MADT_LOCAL_SAPIC:
127 			if (length != sizeof(entry->madt_local_sapic))
128 				return (0);
129 			break;
130 		case ACPI_MADT_PLATFORM_INT:
131 			if (length != sizeof(entry->madt_platform_int))
132 				return (0);
133 			break;
134 		case ACPI_MADT_X2APIC:
135 			if (length != sizeof(entry->madt_x2apic))
136 				return (0);
137 			break;
138 		case ACPI_MADT_X2APIC_NMI:
139 			if (length != sizeof(entry->madt_x2apic_nmi))
140 				return (0);
141 			break;
142 		}
143 
144 		addr += length;
145 	}
146 
147 	return (1);
148 }
149 
150 struct mp_bus acpimadt_busses[256];
151 struct mp_bus acpimadt_isa_bus;
152 
153 int
154 acpimadt_cfg_intr(int flags, u_int32_t *redir)
155 {
156 	int mpspo = (flags >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK;
157 	int mpstrig = (flags >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK;
158 
159 	*redir &= ~IOAPIC_REDLO_DEL_MASK;
160 	switch (mpspo) {
161 	case MPS_INTPO_DEF:
162 	case MPS_INTPO_ACTHI:
163 		*redir &= ~IOAPIC_REDLO_ACTLO;
164 		break;
165 	case MPS_INTPO_ACTLO:
166 		*redir |= IOAPIC_REDLO_ACTLO;
167 		break;
168 	default:
169 		return (0);
170 	}
171 
172 	*redir |= (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT);
173 
174 	switch (mpstrig) {
175 	case MPS_INTTR_LEVEL:
176 		*redir |= IOAPIC_REDLO_LEVEL;
177 		break;
178 	case MPS_INTTR_DEF:
179 	case MPS_INTTR_EDGE:
180 		*redir &= ~IOAPIC_REDLO_LEVEL;
181 		break;
182 	default:
183 		return (0);
184 	}
185 
186 	return (1);
187 }
188 
189 static u_int8_t lapic_map[256];
190 
191 void
192 acpimadt_attach(struct device *parent, struct device *self, void *aux)
193 {
194 	struct acpi_softc *acpi_sc = (struct acpi_softc *)parent;
195 	struct device *mainbus = parent->dv_parent->dv_parent;
196 	struct acpi_attach_args *aaa = aux;
197 	struct acpi_madt *madt = (struct acpi_madt *)aaa->aaa_table;
198 	caddr_t addr = (caddr_t)(madt + 1);
199 	struct aml_value arg;
200 	struct mp_intr_map *map;
201 	struct ioapic_softc *apic;
202 	int nlapic_nmis = 0;
203 	int pin;
204 
205 	/* Do some sanity checks before committing to run in APIC mode. */
206 	if (!acpimadt_validate(madt)) {
207 		printf(": invalid, skipping\n");
208 		return;
209 	}
210 
211 	printf(" addr 0x%x", madt->local_apic_address);
212 	if (madt->flags & ACPI_APIC_PCAT_COMPAT)
213 		printf(": PC-AT compat");
214 	printf("\n");
215 
216 	/* Tell the BIOS we will be using APIC mode. */
217 	memset(&arg, 0, sizeof(arg));
218 	arg.type = AML_OBJTYPE_INTEGER;
219 	arg.v_integer = 1;
220 
221 	aml_evalname(acpi_sc, NULL, "\\_PIC", 1, &arg, NULL);
222 
223 	mp_busses = acpimadt_busses;
224 	mp_nbusses = nitems(acpimadt_busses);
225 	mp_isa_bus = &acpimadt_isa_bus;
226 
227 	lapic_boot_init(madt->local_apic_address);
228 
229 	/* 1st pass, get CPUs and IOAPICs */
230 	while (addr < (caddr_t)madt + madt->hdr.length) {
231 		union acpi_madt_entry *entry = (union acpi_madt_entry *)addr;
232 		struct cpu_attach_args caa;
233 		struct apic_attach_args aaa;
234 
235 		switch (entry->madt_lapic.apic_type) {
236 		case ACPI_MADT_LAPIC:
237 			dprintf("%s: LAPIC: acpi_proc_id %x, apic_id %x, flags 0x%x\n",
238 			    self->dv_xname, entry->madt_lapic.acpi_proc_id,
239 			    entry->madt_lapic.apic_id,
240 			    entry->madt_lapic.flags);
241 
242 			lapic_map[entry->madt_lapic.acpi_proc_id] =
243 			    entry->madt_lapic.apic_id;
244 			acpi_lapic_flags[entry->madt_lapic.acpi_proc_id] =
245 			    entry->madt_lapic.flags;
246 
247 			if ((entry->madt_lapic.flags & ACPI_PROC_ENABLE) == 0)
248 				break;
249 
250 			memset(&caa, 0, sizeof(struct cpu_attach_args));
251 			if (lapic_cpu_number() == entry->madt_lapic.apic_id)
252 				caa.cpu_role = CPU_ROLE_BP;
253 			else {
254 				caa.cpu_role = CPU_ROLE_AP;
255 				ncpusfound++;
256 			}
257 			caa.caa_name = "cpu";
258 			caa.cpu_number = entry->madt_lapic.apic_id;
259 #ifdef MULTIPROCESSOR
260 			caa.cpu_func = &mp_cpu_funcs;
261 #endif
262 #ifdef __i386__
263 			/*
264 			 * XXX utterly wrong.  These are the
265 			 * cpu_feature/cpu_id from the BSP cpu, now
266 			 * being given to another cpu.  This is
267 			 * bullshit.
268 			 */
269 			extern int cpu_id, cpu_feature;
270 			caa.cpu_signature = cpu_id;
271 			caa.feature_flags = cpu_feature;
272 #endif
273 
274 			config_found(mainbus, &caa, acpimadt_print);
275 			break;
276 		case ACPI_MADT_IOAPIC:
277 			dprintf("%s: IOAPIC: acpi_ioapic_id %x, address 0x%x, global_int_base 0x%x\n",
278 			    self->dv_xname, entry->madt_ioapic.acpi_ioapic_id,
279 			    entry->madt_ioapic.address,
280 			    entry->madt_ioapic.global_int_base);
281 
282 			memset(&aaa, 0, sizeof(struct apic_attach_args));
283 			aaa.aaa_name = "ioapic";
284 			aaa.apic_id = entry->madt_ioapic.acpi_ioapic_id;
285 			aaa.apic_address = entry->madt_ioapic.address;
286 			aaa.apic_vecbase = entry->madt_ioapic.global_int_base;
287 
288 			config_found(mainbus, &aaa, acpimadt_print);
289 			break;
290 		case ACPI_MADT_LAPIC_NMI:
291 			nlapic_nmis++;
292 			break;
293 		}
294 		addr += entry->madt_lapic.length;
295 	}
296 
297 	mp_intrs = malloc(nlapic_nmis * sizeof (struct mp_intr_map), M_DEVBUF, M_NOWAIT);
298 	if (mp_intrs == NULL)
299 		return;
300 
301 	/* 2nd pass, get interrupt overrides */
302 	addr = (caddr_t)(madt + 1);
303 	while (addr < (caddr_t)madt + madt->hdr.length) {
304 		union acpi_madt_entry *entry = (union acpi_madt_entry *)addr;
305 
306 		switch (entry->madt_lapic.apic_type) {
307 		case ACPI_MADT_LAPIC:
308 		case ACPI_MADT_IOAPIC:
309 			break;
310 
311 		case ACPI_MADT_OVERRIDE:
312 			dprintf("%s: OVERRIDE: bus %x, source %x, global_int %x, flags %x\n",
313 			    self->dv_xname, entry->madt_override.bus,
314 			    entry->madt_override.source,
315 			    entry->madt_override.global_int,
316 			    entry->madt_override.flags);
317 
318 			pin = entry->madt_override.global_int;
319 			apic = ioapic_find_bybase(pin);
320 
321 			map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO);
322 			if (map == NULL)
323 				return;
324 
325 			map->ioapic = apic;
326 			map->ioapic_pin = pin - apic->sc_apic_vecbase;
327 			map->bus_pin = entry->madt_override.source;
328 			map->flags = entry->madt_override.flags;
329 
330 			if (!acpimadt_cfg_intr(entry->madt_override.flags, &map->redir)) {
331 				printf("%s: bogus override for pin %d\n",
332 				    self->dv_xname, pin);
333 				free(map, M_DEVBUF, 0);
334 				break;
335 			}
336 
337 			map->ioapic_ih = APIC_INT_VIA_APIC |
338 			    ((apic->sc_apicid << APIC_INT_APIC_SHIFT) |
339 			    (pin << APIC_INT_PIN_SHIFT));
340 
341 			apic->sc_pins[pin].ip_map = map;
342 
343 			map->next = mp_isa_bus->mb_intrs;
344 			mp_isa_bus->mb_intrs = map;
345 			break;
346 
347 		case ACPI_MADT_LAPIC_NMI:
348 			dprintf("%s: LAPIC_NMI: acpi_proc_id %x, local_apic_lint %x, flags %x\n",
349 			    self->dv_xname, entry->madt_lapic_nmi.acpi_proc_id,
350 			    entry->madt_lapic_nmi.local_apic_lint,
351 			    entry->madt_lapic_nmi.flags);
352 
353 			pin = entry->madt_lapic_nmi.local_apic_lint;
354 
355 			map = &mp_intrs[mp_nintrs++];
356 			memset(map, 0, sizeof *map);
357 			map->cpu_id = lapic_map[entry->madt_lapic_nmi.acpi_proc_id];
358 			map->ioapic_pin = pin;
359 			map->flags = entry->madt_lapic_nmi.flags;
360 
361 			if (!acpimadt_cfg_intr(entry->madt_lapic_nmi.flags, &map->redir)) {
362 				printf("%s: bogus nmi for apid %d\n",
363 				    self->dv_xname, map->cpu_id);
364 				mp_nintrs--;
365 				break;
366 			}
367 
368 			map->redir &= ~IOAPIC_REDLO_DEL_MASK;
369 			map->redir |= (IOAPIC_REDLO_DEL_NMI << IOAPIC_REDLO_DEL_SHIFT);
370 			break;
371 
372 		case ACPI_MADT_X2APIC:
373 		case ACPI_MADT_X2APIC_NMI:
374 			break;
375 
376 		default:
377 			printf("%s: unknown apic structure type %x\n",
378 			    self->dv_xname, entry->madt_lapic.apic_type);
379 		}
380 
381 		addr += entry->madt_lapic.length;
382 	}
383 
384 	/*
385 	 * ISA interrupts are supposed to be identity mapped unless
386 	 * there is an override, in which case we will already have a
387 	 * mapping for the interrupt.
388 	 */
389 	for (pin = 0; pin < ICU_LEN; pin++) {
390 		/* Skip if we already have a mapping for this interrupt. */
391 		for (map = mp_isa_bus->mb_intrs; map != NULL; map = map->next)
392 			if (map->bus_pin == pin)
393 				break;
394 		if (map != NULL)
395 			continue;
396 
397 		apic = ioapic_find_bybase(pin);
398 
399 		map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO);
400 		if (map == NULL)
401 			return;
402 
403 		map->ioapic = apic;
404 		map->ioapic_pin = pin;
405 		map->bus_pin = pin;
406 		map->redir = (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT);
407 
408 		map->ioapic_ih = APIC_INT_VIA_APIC |
409 		    ((apic->sc_apicid << APIC_INT_APIC_SHIFT) |
410 		    (pin << APIC_INT_PIN_SHIFT));
411 
412 		apic->sc_pins[pin].ip_map = map;
413 
414 		map->next = mp_isa_bus->mb_intrs;
415 		mp_isa_bus->mb_intrs = map;
416 	}
417 }
418 
419 int
420 acpimadt_print(void *aux, const char *pnp)
421 {
422 	struct apic_attach_args *aaa = aux;
423 
424 	if (pnp)
425 		printf("%s at %s:", aaa->aaa_name, pnp);
426 
427 	return (UNCONF);
428 }
429