Changeset View
Standalone View
sys/x86/x86/io_apic.c
Show First 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | struct ioapic_intsrc { | ||||
int io_bus:4; | int io_bus:4; | ||||
uint32_t io_lowreg; | uint32_t io_lowreg; | ||||
u_int io_remap_cookie; | u_int io_remap_cookie; | ||||
}; | }; | ||||
struct ioapic { | struct ioapic { | ||||
struct pic io_pic; | struct pic io_pic; | ||||
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:8; | ||||
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 */ | device_t pci_dev; /* matched pci device, if found */ | ||||
struct resource *pci_wnd; /* BAR 0, should be same or alias to | struct resource *pci_wnd; /* BAR 0, should be same or alias to | ||||
▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | if (io->io_haseoi) { | ||||
*/ | */ | ||||
if (!locked) | if (!locked) | ||||
mtx_lock_spin(&icu_lock); | mtx_lock_spin(&icu_lock); | ||||
low1 = src->io_lowreg; | low1 = src->io_lowreg; | ||||
low1 &= ~IOART_TRGRLVL; | low1 &= ~IOART_TRGRLVL; | ||||
low1 |= IOART_TRGREDG | IOART_INTMSET; | low1 |= IOART_TRGREDG | IOART_INTMSET; | ||||
ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(src->io_intpin), | ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(src->io_intpin), | ||||
low1); | low1); | ||||
low1 = src->io_lowreg; | |||||
if (src->io_masked != 0) | |||||
low1 |= IOART_INTMSET; | |||||
ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(src->io_intpin), | ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(src->io_intpin), | ||||
src->io_lowreg); | low1); | ||||
if (!locked) | if (!locked) | ||||
mtx_unlock_spin(&icu_lock); | mtx_unlock_spin(&icu_lock); | ||||
} | } | ||||
} | } | ||||
static u_int | static u_int | ||||
ioapic_read(volatile ioapic_t *apic, int reg) | ioapic_read(volatile ioapic_t *apic, int reg) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 400 Lines • ▼ Show 20 Lines | |||||
* Create a plain I/O APIC object. | * Create a plain I/O APIC object. | ||||
*/ | */ | ||||
void * | void * | ||||
ioapic_create(vm_paddr_t addr, int32_t apic_id, int intbase) | ioapic_create(vm_paddr_t addr, int32_t apic_id, int intbase) | ||||
{ | { | ||||
struct ioapic *io; | struct ioapic *io; | ||||
struct ioapic_intsrc *intpin; | struct ioapic_intsrc *intpin; | ||||
volatile ioapic_t *apic; | volatile ioapic_t *apic; | ||||
u_int numintr, i; | u_int numintr, i, old_id; | ||||
uint32_t value; | uint32_t value; | ||||
/* Map the register window so we can access the device. */ | /* Map the register window so we can access the device. */ | ||||
apic = pmap_mapdev(addr, IOAPIC_MEM_REGION); | apic = pmap_mapdev(addr, IOAPIC_MEM_REGION); | ||||
mtx_lock_spin(&icu_lock); | mtx_lock_spin(&icu_lock); | ||||
value = ioapic_read(apic, IOAPIC_VER); | value = ioapic_read(apic, IOAPIC_VER); | ||||
mtx_unlock_spin(&icu_lock); | mtx_unlock_spin(&icu_lock); | ||||
Show All 9 Lines | 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_dev = NULL; | ||||
io->pci_wnd = 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) { | ||||
old_id = io->io_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); | |||||
io->io_apic_id = apic_id; | io->io_apic_id = apic_id; | ||||
printf("ioapic%u: Changing APIC ID to %d\n", io->io_id, | mtx_unlock_spin(&icu_lock); | ||||
apic_id); | printf("ioapic%u: Changing APIC ID from %d to %d\n", io->io_id, | ||||
old_id, apic_id); | |||||
} else | } else | ||||
mtx_unlock_spin(&icu_lock); | mtx_unlock_spin(&icu_lock); | ||||
if (intbase == -1) { | if (intbase == -1) { | ||||
intbase = next_ioapic_base; | intbase = next_ioapic_base; | ||||
printf("ioapic%u: Assuming intbase of %d\n", io->io_id, | printf("ioapic%u: Assuming intbase of %d\n", io->io_id, | ||||
intbase); | intbase); | ||||
} else if (intbase != next_ioapic_base && bootverbose) | } else if (intbase != next_ioapic_base && bootverbose) | ||||
printf("ioapic%u: WARNING: intbase %d != expected base %d\n", | printf("ioapic%u: WARNING: intbase %d != expected base %d\n", | ||||
▲ Show 20 Lines • Show All 360 Lines • ▼ Show 20 Lines | ioapic_pci_attach(device_t dev) | ||||
apic_id = ioapic_read(apic, IOAPIC_ID) >> APIC_ID_SHIFT; | apic_id = ioapic_read(apic, IOAPIC_ID) >> APIC_ID_SHIFT; | ||||
/* First match by io window address */ | /* First match by io window address */ | ||||
STAILQ_FOREACH(io, &ioapic_list, io_next) { | STAILQ_FOREACH(io, &ioapic_list, io_next) { | ||||
if (io->io_paddr == (vm_paddr_t)rman_get_start(res)) | if (io->io_paddr == (vm_paddr_t)rman_get_start(res)) | ||||
goto found; | goto found; | ||||
} | } | ||||
/* Then by apic id */ | /* Then by apic id */ | ||||
STAILQ_FOREACH(io, &ioapic_list, io_next) { | STAILQ_FOREACH(io, &ioapic_list, io_next) { | ||||
if (io->io_apic_id == apic_id) | if (io->io_apic_id == apic_id) | ||||
jhb: So this seems really odd. It should be the same register such that if we actually renumber, it… | |||||
Done Inline ActionsBAR and MADT base yes, ids no. That said, I now asking myself why do we need to change the apic id at all ? Should we just leave the id as is and use just io_old_apic_id always. kib: BAR and MADT base yes, ids no.
That said, I now asking myself why do we need to change the… | |||||
Done Inline ActionsAfter looking at more datasheets, I found the following description of the IOAPICID for the recent Intel CPU: APICID: APICID This register uniquely identifies an APIC in the system. This register is not used by OS'es anymore and is still implemented in hardware because of FUD. I think this supports idea of stopping trying to renumber IOAPICs. kib: After looking at more datasheets, I found the following description of the IOAPICID for the… | |||||
Not Done Inline ActionsYes, the renumbering only ever makes sense if you have systems with an actual APIC BUS (I think pre-PPro?) We do try to use it here to match devices, but mismatching isn't really fatal as the only role of this PCI device is to quiet it. It used to matter more before PCI devices implicitly reserved their BARs, but we could probably just assume mismatches won't ever happen and stub out the attach routine. jhb: Yes, the renumbering only ever makes sense if you have systems with an actual APIC BUS (I think… | |||||
Done Inline ActionsI think even with APIC bus (actually pre-PIV) it is only important to have unique APIC IDs assigned, so that the arbitration work. But, matching PCI devices to APICs is very important to have interrupt remapping working. The originator of the IOAPIC interrupt message is used by DMAR to select the table, and originator is named by the PCI device id. I will proceed with removing the renumbering. kib: I think even with APIC bus (actually pre-PIV) it is only important to have unique APIC IDs… | |||||
Not Done Inline ActionsTo be clear, I think the renumbering to match the table is how you ensure unique APIC IDs for systems with an APIC bus. However, I don't think I've ever seen a boot that actually renumbered anything. I suspect it was just paranoia in the early (3.x) SMP code as presumably the BIOS has to assign unique IDs as part of POST. I guess if you had a BIOS that didn't start the APs at all during POST and the OS had to do the raw bootstrap you might need to renumber, but those systems are probably very ancient at this point and not worth supporting. jhb: To be clear, I think the renumbering to match the table is how you ensure unique APIC IDs for… | |||||
goto found; | goto found; | ||||
} | } | ||||
mtx_unlock_spin(&icu_lock); | mtx_unlock_spin(&icu_lock); | ||||
if (bootverbose) | if (bootverbose) | ||||
device_printf(dev, | device_printf(dev, | ||||
"cannot match pci bar apic id %d against MADT\n", | "cannot match pci bar apic id %d against MADT, " | ||||
apic_id); | "BAR0 %#jx io id %d\n", | ||||
apic_id, (uintmax_t)rman_get_start(res), apic_id); | |||||
Done Inline ActionsThis just prints the same apic_id twice? jhb: This just prints the same `apic_id` twice? | |||||
Done Inline ActionsWill fix. kib: Will fix. | |||||
fail: | fail: | ||||
bus_release_resource(dev, SYS_RES_MEMORY, rid, res); | bus_release_resource(dev, SYS_RES_MEMORY, rid, res); | ||||
return (ENXIO); | return (ENXIO); | ||||
found: | found: | ||||
KASSERT(io->pci_dev == NULL, | KASSERT(io->pci_dev == NULL, | ||||
("ioapic %d pci_dev not NULL", io->io_id)); | ("ioapic %d pci_dev not NULL", io->io_id)); | ||||
KASSERT(io->pci_wnd == NULL, | KASSERT(io->pci_wnd == NULL, | ||||
("ioapic %d pci_wnd not NULL", io->io_id)); | ("ioapic %d pci_wnd not NULL", io->io_id)); | ||||
io->pci_dev = dev; | io->pci_dev = dev; | ||||
io->pci_wnd = res; | io->pci_wnd = res; | ||||
if (bootverbose && (io->io_paddr != (vm_paddr_t)rman_get_start(res) || | if (bootverbose && (io->io_paddr != (vm_paddr_t)rman_get_start(res) || | ||||
io->io_apic_id != apic_id)) { | io->io_apic_id != apic_id)) { | ||||
Done Inline ActionsI think this should be io_hw_apic_id? jhb: I think this should be io_hw_apic_id? | |||||
device_printf(dev, "pci%d:%d:%d:%d pci BAR0@%jx id %d " | device_printf(dev, "pci%d:%d:%d:%d pci BAR0@%jx id %d " | ||||
"MADT id %d paddr@%jx\n", | "MADT id %d paddr@%jx\n", | ||||
pci_get_domain(dev), pci_get_bus(dev), | pci_get_domain(dev), pci_get_bus(dev), | ||||
pci_get_slot(dev), pci_get_function(dev), | pci_get_slot(dev), pci_get_function(dev), | ||||
(uintmax_t)rman_get_start(res), apic_id, | (uintmax_t)rman_get_start(res), apic_id, | ||||
io->io_apic_id, (uintmax_t)io->io_paddr); | io->io_apic_id, (uintmax_t)io->io_paddr); | ||||
} | } | ||||
mtx_unlock_spin(&icu_lock); | mtx_unlock_spin(&icu_lock); | ||||
▲ Show 20 Lines • Show All 203 Lines • Show Last 20 Lines |
So this seems really odd. It should be the same register such that if we actually renumber, it should change in both places? Perhaps what is happening is that the write isn't working at all? Do you have output showing the MADT addresses and the BAR0 addresses for all of the I/O APIC devices and the ID's read from each one?