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