Changeset View
Changeset View
Standalone View
Standalone View
sys/x86/x86/io_apic.c
Show All 32 Lines | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/rman.h> | |||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <dev/pci/pcireg.h> | #include <dev/pci/pcireg.h> | ||||
#include <dev/pci/pcivar.h> | #include <dev/pci/pcivar.h> | ||||
#include <vm/vm.h> | #include <vm/vm.h> | ||||
#include <vm/pmap.h> | #include <vm/pmap.h> | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | struct ioapic { | ||||
u_int io_id:8; /* logical ID */ | u_int io_id:8; /* logical ID */ | ||||
u_int io_apic_id:4; | u_int io_apic_id:4; | ||||
u_int io_intbase:8; /* System Interrupt base */ | u_int io_intbase:8; /* System Interrupt base */ | ||||
u_int io_numintr:8; | u_int io_numintr:8; | ||||
u_int io_haseoi:1; | u_int io_haseoi:1; | ||||
volatile ioapic_t *io_addr; /* XXX: should use bus_space */ | volatile ioapic_t *io_addr; /* XXX: should use bus_space */ | ||||
vm_paddr_t io_paddr; | vm_paddr_t io_paddr; | ||||
STAILQ_ENTRY(ioapic) io_next; | STAILQ_ENTRY(ioapic) io_next; | ||||
device_t pci_dev; /* matched pci device, if found */ | |||||
struct resource *pci_wnd; /* BAR 0, should be same or alias to | |||||
io_paddr */ | |||||
struct ioapic_intsrc io_pins[0]; | struct ioapic_intsrc io_pins[0]; | ||||
}; | }; | ||||
static u_int ioapic_read(volatile ioapic_t *apic, int reg); | static u_int ioapic_read(volatile ioapic_t *apic, int reg); | ||||
static void ioapic_write(volatile ioapic_t *apic, int reg, u_int val); | static void ioapic_write(volatile ioapic_t *apic, int reg, u_int val); | ||||
static const char *ioapic_bus_string(int bus_type); | static const char *ioapic_bus_string(int bus_type); | ||||
static void ioapic_print_irq(struct ioapic_intsrc *intpin); | static void ioapic_print_irq(struct ioapic_intsrc *intpin); | ||||
static void ioapic_enable_source(struct intsrc *isrc); | static void ioapic_enable_source(struct intsrc *isrc); | ||||
▲ Show 20 Lines • Show All 507 Lines • ▼ Show 20 Lines | if (value == 0xffffffff) { | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
/* Determine the number of vectors and set the APIC ID. */ | /* Determine the number of vectors and set the APIC ID. */ | ||||
numintr = ((value & IOART_VER_MAXREDIR) >> MAXREDIRSHIFT) + 1; | numintr = ((value & IOART_VER_MAXREDIR) >> MAXREDIRSHIFT) + 1; | ||||
io = malloc(sizeof(struct ioapic) + | io = malloc(sizeof(struct ioapic) + | ||||
numintr * sizeof(struct ioapic_intsrc), M_IOAPIC, M_WAITOK); | numintr * sizeof(struct ioapic_intsrc), M_IOAPIC, M_WAITOK); | ||||
io->io_pic = ioapic_template; | io->io_pic = ioapic_template; | ||||
io->pci_dev = NULL; | |||||
io->pci_wnd = NULL; | |||||
mtx_lock_spin(&icu_lock); | mtx_lock_spin(&icu_lock); | ||||
io->io_id = next_id++; | io->io_id = next_id++; | ||||
io->io_apic_id = ioapic_read(apic, IOAPIC_ID) >> APIC_ID_SHIFT; | io->io_apic_id = ioapic_read(apic, IOAPIC_ID) >> APIC_ID_SHIFT; | ||||
if (apic_id != -1 && io->io_apic_id != apic_id) { | if (apic_id != -1 && io->io_apic_id != apic_id) { | ||||
ioapic_write(apic, IOAPIC_ID, apic_id << APIC_ID_SHIFT); | ioapic_write(apic, IOAPIC_ID, apic_id << APIC_ID_SHIFT); | ||||
mtx_unlock_spin(&icu_lock); | mtx_unlock_spin(&icu_lock); | ||||
io->io_apic_id = apic_id; | io->io_apic_id = apic_id; | ||||
printf("ioapic%u: Changing APIC ID to %d\n", io->io_id, | printf("ioapic%u: Changing APIC ID to %d\n", io->io_id, | ||||
▲ Show 20 Lines • Show All 316 Lines • ▼ Show 20 Lines | if (pci_get_class(dev) == PCIC_BASEPERIPH && | ||||
return (-10000); | return (-10000); | ||||
} | } | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
static int | static int | ||||
ioapic_pci_attach(device_t dev) | ioapic_pci_attach(device_t dev) | ||||
{ | { | ||||
struct resource *res; | |||||
volatile ioapic_t *apic; | |||||
struct ioapic *io; | |||||
int rid; | |||||
u_int apic_id; | |||||
/* | |||||
* Try to match the enumerated ioapic. Match BAR start | |||||
* against io_paddr. Due to a fear that PCI window is not the | |||||
* same as the MADT reported io window, but an alias, read the | |||||
* APIC ID from the mapped BAR and match against it. | |||||
*/ | |||||
rid = PCIR_BAR(0); | |||||
res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, | |||||
RF_ACTIVE | RF_SHAREABLE); | |||||
if (res == NULL) { | |||||
if (bootverbose) | |||||
device_printf(dev, "cannot activate BAR0\n"); | |||||
return (ENXIO); | |||||
} | |||||
apic = (volatile ioapic_t *)rman_get_virtual(res); | |||||
if (rman_get_size(res) < IOAPIC_WND_SIZE) { | |||||
if (bootverbose) | |||||
device_printf(dev, | |||||
"BAR0 too small (%jd) for IOAPIC window\n", | |||||
(uintmax_t)rman_get_size(res)); | |||||
goto fail; | |||||
} | |||||
mtx_lock_spin(&icu_lock); | |||||
apic_id = ioapic_read(apic, IOAPIC_ID) >> APIC_ID_SHIFT; | |||||
/* First match by io window address */ | |||||
STAILQ_FOREACH(io, &ioapic_list, io_next) { | |||||
if (io->io_paddr == (vm_paddr_t)rman_get_start(res)) | |||||
goto found; | |||||
} | |||||
/* Then by apic id */ | |||||
STAILQ_FOREACH(io, &ioapic_list, io_next) { | |||||
if (io->io_id == apic_id) | |||||
goto found; | |||||
} | |||||
mtx_unlock_spin(&icu_lock); | |||||
if (bootverbose) | |||||
device_printf(dev, | |||||
"cannot match pci bar apic id %d against MADT\n", | |||||
apic_id); | |||||
fail: | |||||
bus_release_resource(dev, SYS_RES_MEMORY, rid, res); | |||||
return (ENXIO); | |||||
found: | |||||
KASSERT(io->pci_dev == NULL, | |||||
("ioapic %d pci_dev not NULL", io->io_id)); | |||||
KASSERT(io->pci_wnd == NULL, | |||||
("ioapic %d pci_wnd not NULL", io->io_id)); | |||||
io->pci_dev = dev; | |||||
io->pci_wnd = res; | |||||
if (bootverbose && (io->io_paddr != (vm_paddr_t)rman_get_start(res) || | |||||
io->io_id != apic_id)) { | |||||
device_printf(dev, "pci%d:%d:%d:%d pci BAR0@%jx id %d " | |||||
"MADT id %d paddr@%jx\n", | |||||
pci_get_domain(dev), pci_get_bus(dev), | |||||
pci_get_slot(dev), pci_get_function(dev), | |||||
(uintmax_t)rman_get_start(res), apic_id, | |||||
io->io_id, (uintmax_t)io->io_paddr); | |||||
} | |||||
mtx_unlock_spin(&icu_lock); | |||||
return (0); | return (0); | ||||
} | } | ||||
static device_method_t ioapic_pci_methods[] = { | static device_method_t ioapic_pci_methods[] = { | ||||
/* Device interface */ | /* Device interface */ | ||||
DEVMETHOD(device_probe, ioapic_pci_probe), | DEVMETHOD(device_probe, ioapic_pci_probe), | ||||
DEVMETHOD(device_attach, ioapic_pci_attach), | DEVMETHOD(device_attach, ioapic_pci_attach), | ||||
{ 0, 0 } | { 0, 0 } | ||||
}; | }; | ||||
DEFINE_CLASS_0(ioapic, ioapic_pci_driver, ioapic_pci_methods, 0); | DEFINE_CLASS_0(ioapic, ioapic_pci_driver, ioapic_pci_methods, 0); | ||||
static devclass_t ioapic_devclass; | static devclass_t ioapic_devclass; | ||||
DRIVER_MODULE(ioapic, pci, ioapic_pci_driver, ioapic_devclass, 0, 0); | DRIVER_MODULE(ioapic, pci, ioapic_pci_driver, ioapic_devclass, 0, 0); | ||||
int | |||||
ioapic_get_rid(u_int apic_id, uint16_t *ridp) | |||||
{ | |||||
struct ioapic *io; | |||||
uintptr_t rid; | |||||
int error; | |||||
mtx_lock_spin(&icu_lock); | |||||
STAILQ_FOREACH(io, &ioapic_list, io_next) { | |||||
if (io->io_id == apic_id) | |||||
break; | |||||
} | |||||
mtx_unlock_spin(&icu_lock); | |||||
if (io == NULL || io->pci_dev == NULL) | |||||
return (EINVAL); | |||||
error = pci_get_id(io->pci_dev, PCI_ID_RID, &rid); | |||||
if (error != 0) | |||||
return (error); | |||||
*ridp = rid; | |||||
return (0); | |||||
} | |||||
/* | /* | ||||
* A new-bus driver to consume the memory resources associated with | * A new-bus driver to consume the memory resources associated with | ||||
* the APICs in the system. On some systems ACPI or PnPBIOS system | * the APICs in the system. On some systems ACPI or PnPBIOS system | ||||
* resource devices may already claim these resources. To keep from | * resource devices may already claim these resources. To keep from | ||||
* breaking those devices, we attach ourself to the nexus device after | * breaking those devices, we attach ourself to the nexus device after | ||||
* legacy0 and acpi0 and ignore any allocation failures. | * legacy0 and acpi0 and ignore any allocation failures. | ||||
*/ | */ | ||||
static void | static void | ||||
Show All 21 Lines | |||||
apic_add_resource(device_t dev, int rid, vm_paddr_t base, size_t length) | apic_add_resource(device_t dev, int rid, vm_paddr_t base, size_t length) | ||||
{ | { | ||||
int error; | int error; | ||||
error = bus_set_resource(dev, SYS_RES_MEMORY, rid, base, length); | error = bus_set_resource(dev, SYS_RES_MEMORY, rid, base, length); | ||||
if (error) | if (error) | ||||
panic("apic_add_resource: resource %d failed set with %d", rid, | panic("apic_add_resource: resource %d failed set with %d", rid, | ||||
error); | error); | ||||
bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 0); | bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_SHAREABLE); | ||||
} | } | ||||
static int | static int | ||||
apic_attach(device_t dev) | apic_attach(device_t dev) | ||||
{ | { | ||||
struct ioapic *io; | struct ioapic *io; | ||||
int i; | int i; | ||||
▲ Show 20 Lines • Show All 119 Lines • Show Last 20 Lines |