xref: /openbsd-src/sys/dev/acpi/acpimadt.c (revision 850e275390052b330d93020bf619a739a3c277ac)
1 /* $OpenBSD: acpimadt.c,v 1.21 2008/09/15 19:25:36 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 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 void acpimadt_cfg_intr(int, u_int32_t *);
57 int acpimadt_print(void *, const char *);
58 
59 int
60 acpimadt_match(struct device *parent, void *match, void *aux)
61 {
62 	struct acpi_attach_args *aaa = aux;
63 	struct acpi_table_header *hdr;
64 
65 	/*
66 	 * If we do not have a table, it is not us
67 	 */
68 	if (aaa->aaa_table == NULL)
69 		return (0);
70 
71 	/*
72 	 * If it is an MADT table, we can attach
73 	 */
74 	hdr = (struct acpi_table_header *)aaa->aaa_table;
75 	if (memcmp(hdr->signature, MADT_SIG, sizeof(MADT_SIG) - 1) != 0)
76 		return (0);
77 
78 	return (1);
79 }
80 
81 struct mp_bus acpimadt_busses[256];
82 struct mp_bus acpimadt_isa_bus;
83 
84 void
85 acpimadt_cfg_intr(int flags, u_int32_t *redir)
86 {
87 	int mpspo = (flags >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK;
88 	int mpstrig = (flags >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK;
89 
90 	*redir &= ~IOAPIC_REDLO_DEL_MASK;
91 	switch (mpspo) {
92 	case MPS_INTPO_DEF:
93 	case MPS_INTPO_ACTHI:
94 		*redir &= ~IOAPIC_REDLO_ACTLO;
95 		break;
96 	case MPS_INTPO_ACTLO:
97 		*redir |= IOAPIC_REDLO_ACTLO;
98 		break;
99 	default:
100 		panic("unknown MPS interrupt polarity %d", mpspo);
101 	}
102 
103 	*redir |= (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT);
104 
105 	switch (mpstrig) {
106 	case MPS_INTTR_LEVEL:
107 		*redir |= IOAPIC_REDLO_LEVEL;
108 		break;
109 	case MPS_INTTR_DEF:
110 	case MPS_INTTR_EDGE:
111 		*redir &= ~IOAPIC_REDLO_LEVEL;
112 		break;
113 	default:
114 		panic("unknown MPS interrupt trigger %d", mpstrig);
115 	}
116 }
117 
118 static u_int8_t lapic_map[256];
119 
120 void
121 acpimadt_attach(struct device *parent, struct device *self, void *aux)
122 {
123 	struct acpi_softc *acpi_sc = (struct acpi_softc *)parent;
124 	struct device *mainbus = parent->dv_parent->dv_parent;
125 	struct acpi_attach_args *aaa = aux;
126 	struct acpi_madt *madt = (struct acpi_madt *)aaa->aaa_table;
127 	caddr_t addr = (caddr_t)(madt + 1);
128 	struct aml_value arg;
129 	struct mp_intr_map *map;
130 	struct ioapic_softc *apic;
131 	int nlapic_nmis = 0;
132 	int pin;
133 
134 	printf(" addr 0x%x", madt->local_apic_address);
135 	if (madt->flags & ACPI_APIC_PCAT_COMPAT)
136 		printf(": PC-AT compat");
137 	printf("\n");
138 
139 	/* Tell the BIOS we will be using APIC mode. */
140 	memset(&arg, 0, sizeof(arg));
141 	arg.type = AML_OBJTYPE_INTEGER;
142 	arg.v_integer = 1;
143 
144 	if (aml_evalname(acpi_sc, NULL, "\\_PIC", 1, &arg, NULL) != 0)
145 		return;
146 
147 	mp_busses = acpimadt_busses;
148 	mp_isa_bus = &acpimadt_isa_bus;
149 
150 	lapic_boot_init(madt->local_apic_address);
151 
152 	/* 1st pass, get CPUs and IOAPICs */
153 	while (addr < (caddr_t)madt + madt->hdr.length) {
154 		union acpi_madt_entry *entry = (union acpi_madt_entry *)addr;
155 		struct cpu_attach_args caa;
156 		struct apic_attach_args aaa;
157 
158 		switch (entry->madt_lapic.apic_type) {
159 		case ACPI_MADT_LAPIC:
160 			dprintf("%s: LAPIC: acpi_proc_id %x, apic_id %x, flags 0x%x\n",
161 			    self->dv_xname, entry->madt_lapic.acpi_proc_id,
162 			    entry->madt_lapic.apic_id,
163 			    entry->madt_lapic.flags);
164 
165 			lapic_map[entry->madt_lapic.acpi_proc_id] =
166 			    entry->madt_lapic.apic_id;
167 			acpi_lapic_flags[entry->madt_lapic.acpi_proc_id] =
168 			    entry->madt_lapic.flags;
169 
170 			if ((entry->madt_lapic.flags & ACPI_PROC_ENABLE) == 0)
171 				break;
172 
173 			memset(&caa, 0, sizeof(struct cpu_attach_args));
174 			if (lapic_cpu_number() == entry->madt_lapic.apic_id)
175 				caa.cpu_role = CPU_ROLE_BP;
176 			else
177 				caa.cpu_role = CPU_ROLE_AP;
178 			caa.caa_name = "cpu";
179 			caa.cpu_number = entry->madt_lapic.apic_id;
180 #ifdef MULTIPROCESSOR
181 			caa.cpu_func = &mp_cpu_funcs;
182 #endif
183 #ifdef __i386__
184 			/*
185 			 * XXX utterly wrong.  These are the
186 			 * cpu_feature/cpu_id from the BSP cpu, now
187 			 * being given to another cpu.  This is
188 			 * bullshit.
189 			 */
190 			extern int cpu_id, cpu_feature;
191 			caa.cpu_signature = cpu_id;
192 			caa.feature_flags = cpu_feature;
193 #endif
194 
195 			config_found(mainbus, &caa, acpimadt_print);
196 			break;
197 		case ACPI_MADT_IOAPIC:
198 			dprintf("%s: IOAPIC: acpi_ioapic_id %x, address 0x%x, global_int_base 0x%x\n",
199 			    self->dv_xname, entry->madt_ioapic.acpi_ioapic_id,
200 			    entry->madt_ioapic.address,
201 			    entry->madt_ioapic.global_int_base);
202 
203 			memset(&aaa, 0, sizeof(struct apic_attach_args));
204 			aaa.aaa_name = "ioapic";
205 			aaa.apic_id = entry->madt_ioapic.acpi_ioapic_id;
206 			aaa.apic_address = entry->madt_ioapic.address;
207 			aaa.apic_vecbase = entry->madt_ioapic.global_int_base;
208 
209 			config_found(mainbus, &aaa, acpimadt_print);
210 			break;
211 		case ACPI_MADT_LAPIC_NMI:
212 			nlapic_nmis++;
213 			break;
214 		}
215 		addr += entry->madt_lapic.length;
216 	}
217 
218 	mp_intrs = malloc(nlapic_nmis * sizeof (struct mp_intr_map), M_DEVBUF, M_NOWAIT);
219 	if (mp_intrs == NULL)
220 		return;
221 
222 	/* 2nd pass, get interrupt overrides */
223 	addr = (caddr_t)(madt + 1);
224 	while (addr < (caddr_t)madt + madt->hdr.length) {
225 		union acpi_madt_entry *entry = (union acpi_madt_entry *)addr;
226 
227 		switch (entry->madt_lapic.apic_type) {
228 		case ACPI_MADT_LAPIC:
229 		case ACPI_MADT_IOAPIC:
230 			break;
231 
232 		case ACPI_MADT_OVERRIDE:
233 			dprintf("%s: OVERRIDE: bus %x, source %x, global_int %x, flags %x\n",
234 			    self->dv_xname, entry->madt_override.bus,
235 			    entry->madt_override.source,
236 			    entry->madt_override.global_int,
237 			    entry->madt_override.flags);
238 
239 			pin = entry->madt_override.global_int;
240 			apic = ioapic_find_bybase(pin);
241 
242 			map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO);
243 			if (map == NULL)
244 				return;
245 
246 			map->ioapic = apic;
247 			map->ioapic_pin = pin - apic->sc_apic_vecbase;
248 			map->bus_pin = entry->madt_override.source;
249 			map->flags = entry->madt_override.flags;
250 #ifdef __amd64__ /* XXX	*/
251 			map->global_int = entry->madt_override.global_int;
252 #endif
253 			acpimadt_cfg_intr(entry->madt_override.flags, &map->redir);
254 
255 			map->ioapic_ih = APIC_INT_VIA_APIC |
256 			    ((apic->sc_apicid << APIC_INT_APIC_SHIFT) |
257 			    (pin << APIC_INT_PIN_SHIFT));
258 
259 			apic->sc_pins[pin].ip_map = map;
260 
261 			map->next = mp_isa_bus->mb_intrs;
262 			mp_isa_bus->mb_intrs = map;
263 			break;
264 
265 		case ACPI_MADT_LAPIC_NMI:
266 			dprintf("%s: LAPIC_NMI: acpi_proc_id %x, local_apic_lint %x, flags %x\n",
267 			    self->dv_xname, entry->madt_lapic_nmi.acpi_proc_id,
268 			    entry->madt_lapic_nmi.local_apic_lint,
269 			    entry->madt_lapic_nmi.flags);
270 
271 			pin = entry->madt_lapic_nmi.local_apic_lint;
272 
273 			map = &mp_intrs[mp_nintrs++];
274 			memset(map, 0, sizeof *map);
275 			map->cpu_id = lapic_map[entry->madt_lapic_nmi.acpi_proc_id];
276 			map->ioapic_pin = pin;
277 			map->flags = entry->madt_lapic_nmi.flags;
278 
279 			acpimadt_cfg_intr(entry->madt_lapic_nmi.flags, &map->redir);
280 			map->redir &= ~IOAPIC_REDLO_DEL_MASK;
281 			map->redir |= (IOAPIC_REDLO_DEL_NMI << IOAPIC_REDLO_DEL_SHIFT);
282 			break;
283 
284 		default:
285 			printf("%s: unknown apic structure type %x\n",
286 			    self->dv_xname, entry->madt_lapic.apic_type);
287 		}
288 
289 		addr += entry->madt_lapic.length;
290 	}
291 
292 	/*
293 	 * ISA interrupts are supposed to be identity mapped unless
294 	 * there is an override, in which case we will already have a
295 	 * mapping for the interrupt.
296 	 */
297 	for (pin = 0; pin < ICU_LEN; pin++) {
298 		/* Skip if we already have a mapping for this interrupt. */
299 		for (map = mp_isa_bus->mb_intrs; map != NULL; map = map->next)
300 			if (map->bus_pin == pin)
301 				break;
302 		if (map != NULL)
303 			continue;
304 
305 		apic = ioapic_find_bybase(pin);
306 
307 		map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO);
308 		if (map == NULL)
309 			return;
310 
311 		map->ioapic = apic;
312 		map->ioapic_pin = pin;
313 		map->bus_pin = pin;
314 #ifdef __amd64__ /* XXX */
315 		map->global_int = -1;
316 #endif
317 		map->redir = (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT);
318 
319 		map->ioapic_ih = APIC_INT_VIA_APIC |
320 		    ((apic->sc_apicid << APIC_INT_APIC_SHIFT) |
321 		    (pin << APIC_INT_PIN_SHIFT));
322 
323 		apic->sc_pins[pin].ip_map = map;
324 
325 		map->next = mp_isa_bus->mb_intrs;
326 		mp_isa_bus->mb_intrs = map;
327 	}
328 }
329 
330 int
331 acpimadt_print(void *aux, const char *pnp)
332 {
333 	struct apic_attach_args *aaa = aux;
334 
335 	if (pnp)
336 		printf("%s at %s:", aaa->aaa_name, pnp);
337 
338 	return (UNCONF);
339 }
340