diff --git a/drivers/acpi/acpi.c b/drivers/acpi/acpi.c index 8581e1786..bac095b60 100644 --- a/drivers/acpi/acpi.c +++ b/drivers/acpi/acpi.c @@ -12,6 +12,7 @@ PUBLIC struct machine machine; #define IRQ_TABLE_ENTRIES (PCI_MAX_DEVICES * PCI_MAX_PINS) PRIVATE int irqtable[IRQ_TABLE_ENTRIES]; +PRIVATE ACPI_HANDLE pci_root_handle; /* don't know where ACPI tables are, we may need to access any memory */ PRIVATE int init_mem_priv(void) @@ -93,7 +94,7 @@ PRIVATE ACPI_STATUS get_irq_resource(ACPI_RESOURCE *res, void *context) irq = &res->Data.Irq; add_irq(tbl->Address >> 16, tbl->Pin, irq->Interrupts[tbl->SourceIndex]); - } if (res->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) { + } else if (res->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) { ACPI_RESOURCE_EXTENDED_IRQ *irq; add_irq(tbl->Address >> 16, tbl->Pin, @@ -103,32 +104,18 @@ PRIVATE ACPI_STATUS get_irq_resource(ACPI_RESOURCE *res, void *context) return AE_OK; } -PRIVATE ACPI_STATUS add_pci_dev(ACPI_HANDLE handle, - UINT32 level, - void *context, - void **retval) +PRIVATE ACPI_STATUS get_pci_irq_routing(ACPI_HANDLE handle) { ACPI_STATUS status; ACPI_BUFFER abuff; char buff[4096]; ACPI_PCI_ROUTING_TABLE *tbl; - int i; - static unsigned called; - if (++called > 1) { - printf("ACPI: Warning! Multi rooted PCI is not supported!\n"); - return AE_OK; - } - abuff.Length = sizeof(buff); abuff.Pointer = buff; - for (i = 0; i < IRQ_TABLE_ENTRIES; i++) - irqtable[i] = -1; - status = AcpiGetIrqRoutingTable(handle, &abuff); if (ACPI_FAILURE(status)) { - printf("ACPI: ACPI no routing table\n"); return AE_OK; } @@ -154,15 +141,51 @@ PRIVATE ACPI_STATUS add_pci_dev(ACPI_HANDLE handle, continue; } } - + return AE_OK; } +PRIVATE ACPI_STATUS add_pci_root_dev(ACPI_HANDLE handle, + UINT32 level, + void *context, + void **retval) +{ + int i; + static unsigned called; + + if (++called > 1) { + printf("ACPI: Warning! Multi rooted PCI is not supported!\n"); + return AE_OK; + } + + for (i = 0; i < IRQ_TABLE_ENTRIES; i++) + irqtable[i] = -1; + + return get_pci_irq_routing(handle); +} + +PRIVATE ACPI_STATUS add_pci_dev(ACPI_HANDLE handle, + UINT32 level, + void *context, + void **retval) +{ + /* skip pci root when we get to it again */ + if (handle == pci_root_handle) + return AE_OK; + + return get_pci_irq_routing(handle); +} + PRIVATE void scan_devices(void) { ACPI_STATUS(status); + + /* get the root first */ + status = AcpiGetDevices("PNP0A03", add_pci_root_dev, NULL, NULL); + assert(ACPI_SUCCESS(status)); - status = AcpiGetDevices("PNP0A03", add_pci_dev, NULL, NULL); + /* get the rest of the devices that implement _PRT */ + status = AcpiGetDevices(NULL, add_pci_dev, NULL, NULL); assert(ACPI_SUCCESS(status)); } PRIVATE ACPI_STATUS init_acpica(void) diff --git a/drivers/acpi/osminixxf.c b/drivers/acpi/osminixxf.c index 4d55acd1a..97e6d54d8 100644 --- a/drivers/acpi/osminixxf.c +++ b/drivers/acpi/osminixxf.c @@ -883,7 +883,9 @@ AcpiOsReadMemory ( UINT32 *Value, UINT32 Width) { - panic("NOTIMPLEMENTED %s\n", __func__); + /* FIXME this operation is ignored */ + *Value = 0; + return (AE_OK); } @@ -908,7 +910,7 @@ AcpiOsWriteMemory ( UINT32 Value, UINT32 Width) { - panic("NOTIMPLEMENTED %s\n", __func__); + /* FIXME this operation is ignored */ return (AE_OK); } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c75198972..e64e91edd 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -962,10 +962,10 @@ PRIVATE int acpi_get_irq(unsigned dev, unsigned pin) PRIVATE int derive_irq(struct pcidev * dev, int pin) { - struct pcidev * parent_brige; + struct pcidev * parent_bridge; int slot; - parent_brige = &pcidev[pcibus[get_busind(dev->pd_busnr)].pb_devind]; + parent_bridge = &pcidev[pcibus[get_busind(dev->pd_busnr)].pb_devind]; /* * We don't support PCI-Express, no ARI, decode the slot of the device @@ -973,7 +973,7 @@ PRIVATE int derive_irq(struct pcidev * dev, int pin) */ slot = ((dev->pd_func) >> 3) & 0x1f; - return acpi_get_irq(parent_brige->pd_dev, (pin + slot) % 4); + return acpi_get_irq(parent_bridge->pd_dev, (pin + slot) % 4); } /*===========================================================================* @@ -998,15 +998,16 @@ int devind; if (irq >= 0) { ilr = irq; pci_attr_w8(devind, PCI_ILR, ilr); - printf("PCI: ACPI IRQ %d for " - "device %d.%d.%d INT%c\n", - irq, - pcidev[devind].pd_busnr, - pcidev[devind].pd_dev, - pcidev[devind].pd_func, - 'A' + ipr-1); + if (debug) + printf("PCI: ACPI IRQ %d for " + "device %d.%d.%d INT%c\n", + irq, + pcidev[devind].pd_busnr, + pcidev[devind].pd_dev, + pcidev[devind].pd_func, + 'A' + ipr-1); } - else { + else if (debug) { printf("PCI: no ACPI IRQ routing for " "device %d.%d.%d INT%c\n", pcidev[devind].pd_busnr, diff --git a/kernel/Makefile b/kernel/Makefile index 4a3d3160f..6a4cf359d 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -23,8 +23,8 @@ DPADD+= ${LIBC} LDADD+= -lc .endif -CPPFLAGS+= -I${.CURDIR}/arch/${ARCH}/include -I${MINIXSRCDIR} -AFLAGS+= -I${.CURDIR}/arch/${ARCH}/include -I${MINIXSRCDIR} +CPPFLAGS+= -I${.CURDIR} -I${.CURDIR}/arch/${ARCH}/include -I${MINIXSRCDIR} +AFLAGS+= -I${.CURDIR} -I${.CURDIR}/arch/${ARCH}/include -I${MINIXSRCDIR} INSTALLFLAGS+= -S 0 BINDIR= /usr/sbin diff --git a/kernel/arch/i386/acpi.c b/kernel/arch/i386/acpi.c index a3342214c..159f15b98 100644 --- a/kernel/arch/i386/acpi.c +++ b/kernel/arch/i386/acpi.c @@ -1,83 +1,5 @@ #include "kernel/kernel.h" - -/* ACPI root system description pointer */ -struct acpi_rsdp { - char signature[8]; /* must be "RSD PTR " */ - u8_t checksum; - char oemid[6]; - u8_t revision; - u32_t rsdt_addr; - u32_t length; -}; - -#define ACPI_SDT_SIGNATURE_LEN 4 - -#define ACPI_SDT_SIGNATURE(name) #name - -/* header common to all system description tables */ -struct acpi_sdt_header { - char signature[ACPI_SDT_SIGNATURE_LEN]; - u32_t length; - u8_t revision; - u8_t checksum; - char oemid[6]; - char oem_table_id[8]; - u32_t oem_revision; - u32_t creator_id; - u32_t creator_revision; -}; - -struct acpi_madt_hdr { - struct acpi_sdt_header hdr; - u32_t local_apic_address; - u32_t flags; -}; - -#define ACPI_MADT_TYPE_LAPIC 0 -#define ACPI_MADT_TYPE_IOAPIC 1 -#define ACPI_MADT_TYPE_INT_SRC 2 -#define ACPI_MADT_TYPE_NMI_SRC 3 -#define ACPI_MADT_TYPE_LAPIC_NMI 4 -#define ACPI_MADT_TYPE_LAPIC_ADRESS 5 -#define ACPI_MADT_TYPE_IOSAPIC 6 -#define ACPI_MADT_TYPE_LSAPIC 7 -#define ACPI_MADT_TYPE_PLATFORM_INT_SRC 8 -#define ACPI_MADT_TYPE_Lx2APIC 9 -#define ACPI_MADT_TYPE_Lx2APIC_NMI 10 - -struct acpi_madt_item_hdr{ - u8_t type; - u8_t length; -}; - -struct acpi_madt_lapic { - struct acpi_madt_item_hdr hdr; - u8_t acpi_cpu_id; - u8_t apic_id; - u32_t flags; -}; - -struct acpi_madt_ioapic { - struct acpi_madt_item_hdr hdr; - u8_t id; - u8_t __reserved; - u32_t address; - u32_t global_int_base; -}; - -struct acpi_madt_int_src { - struct acpi_madt_item_hdr hdr; - u8_t bus; - u8_t bus_int; - u32_t global_int; - u16_t mps_flags; -}; - -struct acpi_madt_nmi { - struct acpi_madt_item_hdr hdr; - u16_t flags; - u32_t global_int; -}; +#include "acpi.h" typedef int ((* acpi_read_t)(phys_bytes addr, void * buff, size_t size)); @@ -299,3 +221,25 @@ PUBLIC void acpi_init(void) sdt_trans[i].length = hdr.length; } } + +PUBLIC struct acpi_madt_ioapic * acpi_get_ioapic_next(void) +{ + static unsigned idx = 0; + static struct acpi_madt_hdr * madt_hdr; + + struct acpi_madt_ioapic * ret; + + if (idx == 0) { + madt_hdr = (struct acpi_madt_hdr *) + phys2vir(acpi_get_table_base("APIC")); + if (madt_hdr == NULL) + return NULL; + } + + ret = (struct acpi_madt_ioapic *) + acpi_madt_get_typed_item(madt_hdr, ACPI_MADT_TYPE_IOAPIC, idx); + if (ret) + idx++; + + return ret; +} diff --git a/kernel/arch/i386/acpi.h b/kernel/arch/i386/acpi.h index c273ac4ec..e2cf35217 100644 --- a/kernel/arch/i386/acpi.h +++ b/kernel/arch/i386/acpi.h @@ -3,6 +3,92 @@ #include "kernel/kernel.h" +/* ACPI root system description pointer */ +struct acpi_rsdp { + char signature[8]; /* must be "RSD PTR " */ + u8_t checksum; + char oemid[6]; + u8_t revision; + u32_t rsdt_addr; + u32_t length; +}; + +#define ACPI_SDT_SIGNATURE_LEN 4 + +#define ACPI_SDT_SIGNATURE(name) #name + +/* header common to all system description tables */ +struct acpi_sdt_header { + char signature[ACPI_SDT_SIGNATURE_LEN]; + u32_t length; + u8_t revision; + u8_t checksum; + char oemid[6]; + char oem_table_id[8]; + u32_t oem_revision; + u32_t creator_id; + u32_t creator_revision; +}; + +struct acpi_madt_hdr { + struct acpi_sdt_header hdr; + u32_t local_apic_address; + u32_t flags; +}; + +#define ACPI_MADT_TYPE_LAPIC 0 +#define ACPI_MADT_TYPE_IOAPIC 1 +#define ACPI_MADT_TYPE_INT_SRC 2 +#define ACPI_MADT_TYPE_NMI_SRC 3 +#define ACPI_MADT_TYPE_LAPIC_NMI 4 +#define ACPI_MADT_TYPE_LAPIC_ADRESS 5 +#define ACPI_MADT_TYPE_IOSAPIC 6 +#define ACPI_MADT_TYPE_LSAPIC 7 +#define ACPI_MADT_TYPE_PLATFORM_INT_SRC 8 +#define ACPI_MADT_TYPE_Lx2APIC 9 +#define ACPI_MADT_TYPE_Lx2APIC_NMI 10 + +struct acpi_madt_item_hdr{ + u8_t type; + u8_t length; +}; + +struct acpi_madt_lapic { + struct acpi_madt_item_hdr hdr; + u8_t acpi_cpu_id; + u8_t apic_id; + u32_t flags; +}; + +struct acpi_madt_ioapic { + struct acpi_madt_item_hdr hdr; + u8_t id; + u8_t __reserved; + u32_t address; + u32_t global_int_base; +}; + +struct acpi_madt_int_src { + struct acpi_madt_item_hdr hdr; + u8_t bus; + u8_t bus_int; + u32_t global_int; + u16_t mps_flags; +}; + +struct acpi_madt_nmi { + struct acpi_madt_item_hdr hdr; + u16_t flags; + u32_t global_int; +}; + _PROTOTYPE(void acpi_init, (void)); +/* + * Returns a pointer to the io acpi structure in the MADT table in ACPI. The + * pointer is valid only until paging is turned off. No memory is allocated in + * this function thus no memory needs to be freed + */ +_PROTOTYPE(struct acpi_madt_ioapic * acpi_get_ioapic_next, (void)); + #endif /* __ACPI_H__ */ diff --git a/kernel/arch/i386/apic.c b/kernel/arch/i386/apic.c index 4b0804cfe..54680df30 100644 --- a/kernel/arch/i386/apic.c +++ b/kernel/arch/i386/apic.c @@ -2,11 +2,13 @@ * APIC handling routines. APIC is a requirement for SMP */ #include "kernel/kernel.h" +#include #include #include #include +#include #include "kernel/proc.h" #include "kernel/glo.h" @@ -18,25 +20,111 @@ #include "apic_asm.h" #include "kernel/clock.h" #include "glo.h" +#include "hw_intr.h" + +#include "acpi.h" #ifdef CONFIG_WATCHDOG #include "kernel/watchdog.h" #endif +#define APIC_ENABLE 0x100 +#define APIC_FOCUS_DISABLED (1 << 9) +#define APIC_SIV 0xFF + +#define APIC_TDCR_2 0x00 +#define APIC_TDCR_4 0x01 +#define APIC_TDCR_8 0x02 +#define APIC_TDCR_16 0x03 +#define APIC_TDCR_32 0x08 +#define APIC_TDCR_64 0x09 +#define APIC_TDCR_128 0x0a +#define APIC_TDCR_1 0x0b + +#define IS_SET(mask) (mask) +#define IS_CLEAR(mask) 0 + +#define APIC_LVTT_VECTOR_MASK 0x000000FF +#define APIC_LVTT_DS_PENDING (1 << 12) +#define APIC_LVTT_MASK (1 << 16) +#define APIC_LVTT_TM (1 << 17) + +#define APIC_LVT_IIPP_MASK 0x00002000 +#define APIC_LVT_IIPP_AH 0x00002000 +#define APIC_LVT_IIPP_AL 0x00000000 + +#define APIC_LVT_TM_ONESHOT IS_CLEAR(APIC_LVTT_TM) +#define APIC_LVT_TM_PERIODIC IS_SET(APIC_LVTT_TM) + +#define IOAPIC_REGSEL 0x0 +#define IOAPIC_RW 0x10 + +#define APIC_ICR_DM_MASK 0x00000700 +#define APIC_ICR_VECTOR APIC_LVTT_VECTOR_MASK +#define APIC_ICR_DM_FIXED (0 << 8) +#define APIC_ICR_DM_LOWEST_PRIORITY (1 << 8) +#define APIC_ICR_DM_SMI (2 << 8) +#define APIC_ICR_DM_RESERVED (3 << 8) +#define APIC_ICR_DM_NMI (4 << 8) +#define APIC_ICR_DM_INIT (5 << 8) +#define APIC_ICR_DM_STARTUP (6 << 8) +#define APIC_ICR_DM_EXTINT (7 << 8) + +#define APIC_ICR_DM_PHYSICAL (0 << 11) +#define APIC_ICR_DM_LOGICAL (1 << 11) + +#define APIC_ICR_DELIVERY_PENDING (1 << 12) + +#define APIC_ICR_INT_POLARITY (1 << 13) +#define APIC_ICR_INTPOL_LOW IS_SET(APIC_ICR_INT_POLARITY) +#define APIC_ICR_INTPOL_HIGH IS_CLEAR(APIC_ICR_INT_POLARITY) + +#define APIC_ICR_LEVEL_ASSERT (1 << 14) +#define APIC_ICR_LEVEL_DEASSERT (0 << 14) + +#define APIC_ICR_TRIGGER (1 << 15) +#define APIC_ICR_TM_LEVEL IS_CLEAR(APIC_ICR_TRIGGER) +#define APIC_ICR_TM_EDGE IS_CLEAR(APIC_ICR_TRIGGER) + +#define APIC_ICR_INT_MASK (1 << 16) + +#define APIC_ICR_DEST_FIELD (0 << 18) +#define APIC_ICR_DEST_SELF (1 << 18) +#define APIC_ICR_DEST_ALL (2 << 18) +#define APIC_ICR_DEST_ALL_BUT_SELF (3 << 18) + #define IA32_APIC_BASE 0x1b #define IA32_APIC_BASE_ENABLE_BIT 11 +/* FIXME we should spread the irqs across as many priority levels as possible + * due to buggy hw */ +#define LAPIC_VECTOR(irq) (IRQ0_VECTOR +(irq)) + +#define IOAPIC_IRQ_STATE_MASKED 0x1 + /* currently only 2 interrupt priority levels are used */ #define SPL0 0x0 #define SPLHI 0xF -/* - * to make APIC work if SMP is not configured, we need to set the maximal number - * of CPUS to 1, cpuid to return 0 and the current cpu is always BSP - */ -#define CONFIG_MAX_CPUS 1 #define cpu_is_bsp(x) 1 +PUBLIC struct io_apic io_apic[MAX_NR_IOAPICS]; +PUBLIC unsigned nioapics; + +struct irq; +typedef void (* eoi_method_t)(struct irq *); + +struct irq { + struct io_apic * ioa; + unsigned pin; + unsigned vector; + eoi_method_t eoi; + unsigned state; +}; + +PRIVATE struct irq io_apic_irq[NR_IRQ_VECTORS]; + + #define lapic_write_icr1(val) lapic_write(LAPIC_ICR1, val) #define lapic_write_icr2(val) lapic_write(LAPIC_ICR2, val) @@ -45,23 +133,20 @@ #define VERBOSE_APIC(x) x -PRIVATE int ioapic_enabled; -PRIVATE u32_t ioapic_id_mask[8]; +PUBLIC int ioapic_enabled; PUBLIC u32_t lapic_addr_vaddr; PUBLIC vir_bytes lapic_addr; PUBLIC vir_bytes lapic_eoi_addr; -PRIVATE volatile int probe_ticks; +PRIVATE volatile unsigned probe_ticks; PRIVATE u64_t tsc0, tsc1; PRIVATE u32_t lapic_tctr0, lapic_tctr1; -/* FIXME: this is only accessed from assembly, never from C. Move to asm? */ -PUBLIC u8_t apicid2cpuid[MAX_NR_APICIDS+1]; /* Accessed from asm */ - PRIVATE unsigned apic_imcrp; -PRIVATE unsigned nintrs; PRIVATE const unsigned nlints = 0; +#define apic_eoi() do { *((volatile u32_t *) lapic_eoi_addr) = 0; } while(0) + /* * FIXME this should be a cpulocal variable but there are some problems with * arch specific cpulocals. As this variable is write-once-read-only it is ok to @@ -71,8 +156,224 @@ PRIVATE u32_t lapic_bus_freq[CONFIG_MAX_CPUS]; /* the probe period will be roughly 100ms */ #define PROBE_TICKS (system_hz / 10) -PRIVATE u32_t pci_config_intr_data; -PRIVATE int lapic_extint_assigned = 0; +#define IOAPIC_IOREGSEL 0x0 +#define IOAPIC_IOWIN 0x10 + +PUBLIC u32_t ioapic_read(u32_t ioa_base, u32_t reg) +{ + *((u32_t *)(ioa_base + IOAPIC_IOREGSEL)) = (reg & 0xff); + return *(u32_t *)(ioa_base + IOAPIC_IOWIN); +} + +PRIVATE void ioapic_write(u32_t ioa_base, u8_t reg, u32_t val) +{ + *((u32_t *)(ioa_base + IOAPIC_IOREGSEL)) = reg; + *((u32_t *)(ioa_base + IOAPIC_IOWIN)) = val; +} + +FORWARD _PROTOTYPE(void lapic_microsec_sleep, (unsigned count)); +FORWARD _PROTOTYPE(void apic_idt_init, (const int reset)); + +PRIVATE void ioapic_enable_pin(vir_bytes ioapic_addr, int pin) +{ + u32_t lo = ioapic_read(ioapic_addr, IOAPIC_REDIR_TABLE + pin * 2); + + lo &= ~APIC_ICR_INT_MASK; + ioapic_write(ioapic_addr, IOAPIC_REDIR_TABLE + pin * 2, lo); +} + +PRIVATE void ioapic_disable_pin(vir_bytes ioapic_addr, int pin) +{ + u32_t lo = ioapic_read(ioapic_addr, IOAPIC_REDIR_TABLE + pin * 2); + + lo |= APIC_ICR_INT_MASK; + ioapic_write(ioapic_addr, IOAPIC_REDIR_TABLE + pin * 2, lo); +} + +PRIVATE void ioapic_redirt_entry_read(void * ioapic_addr, + int entry, + u32_t *hi, + u32_t *lo) +{ + *lo = ioapic_read((u32_t)ioapic_addr, (u8_t) (IOAPIC_REDIR_TABLE + entry * 2)); + *hi = ioapic_read((u32_t)ioapic_addr, (u8_t) (IOAPIC_REDIR_TABLE + entry * 2 + 1)); + +} + +PRIVATE void ioapic_redirt_entry_write(void * ioapic_addr, + int entry, + u32_t hi, + u32_t lo) +{ +#if 0 + VERBOSE_APIC(printf("IO apic redir entry %3d " + "write 0x%08x 0x%08x\n", entry, hi, lo)); +#endif + ioapic_write((u32_t)ioapic_addr, (u8_t) (IOAPIC_REDIR_TABLE + entry * 2 + 1), hi); + ioapic_write((u32_t)ioapic_addr, (u8_t) (IOAPIC_REDIR_TABLE + entry * 2), lo); +} + +#define apic_read_tmr_vector(vec) \ + lapic_read(LAPIC_TMR + 0x10 * ((vec) >> 5)) + +#define apic_read_irr_vector(vec) \ + lapic_read(LAPIC_IRR + 0x10 * ((vec) >> 5)) + +#define apic_read_isr_vector(vec) \ + lapic_read(LAPIC_ISR + 0x10 * ((vec) >> 5)) + +#define lapic_test_delivery_val(val, vector) ((val) & (1 << ((vector) & 0x1f))) + +PRIVATE void ioapic_eoi_level(struct irq * irq) +{ + reg_t tmr; + + tmr = apic_read_tmr_vector(irq->vector); + apic_eoi(); + + /* + * test if it was a level or edge triggered interrupt. If delivered as + * edge exec the workaround for broken chipsets + */ + if (!lapic_test_delivery_val(tmr, irq->vector)) { + int is_masked; + u32_t lo; + + panic("EDGE instead of LEVEL!"); + + lo = ioapic_read(irq->ioa->addr, + IOAPIC_REDIR_TABLE + irq->pin * 2); + + is_masked = lo & APIC_ICR_INT_MASK; + + /* set mask and edge */ + lo |= APIC_ICR_INT_MASK; + lo &= ~APIC_ICR_TRIGGER; + ioapic_write(irq->ioa->addr, + IOAPIC_REDIR_TABLE + irq->pin * 2, lo); + + /* set back to level and restore the mask bit */ + lo = ioapic_read(irq->ioa->addr, + IOAPIC_REDIR_TABLE + irq->pin * 2); + + lo |= APIC_ICR_TRIGGER; + if (is_masked) + lo |= APIC_ICR_INT_MASK; + else + lo &= ~APIC_ICR_INT_MASK; + ioapic_write(irq->ioa->addr, + IOAPIC_REDIR_TABLE + irq->pin * 2, lo); + } +} + +PRIVATE void ioapic_eoi_edge(__unused struct irq * irq) +{ + apic_eoi(); +} + +PUBLIC void ioapic_eoi(int irq) +{ + if (ioapic_enabled) { + io_apic_irq[irq].eoi(&io_apic_irq[irq]); + } + else + irq_8259_eoi(irq); +} + +PUBLIC int ioapic_enable_all(void) +{ + i8259_disable(); + + if (apic_imcrp) { + /* Select IMCR and disconnect 8259s. */ + outb(0x22, 0x70); + outb(0x23, 0x01); + } + + return ioapic_enabled = 1; +} + +/* disables a single IO APIC */ +PRIVATE void ioapic_disable(struct io_apic * ioapic) +{ + unsigned p; + + for (p = 0; p < io_apic->pins; p++) { + u32_t low_32, hi_32; + low_32 = ioapic_read((u32_t)ioapic->addr, + (uint8_t) (IOAPIC_REDIR_TABLE + p * 2)); + hi_32 = ioapic_read((u32_t)ioapic->addr, + (uint8_t) (IOAPIC_REDIR_TABLE + p * 2 + 1)); + + if (!(low_32 & APIC_ICR_INT_MASK)) { + low_32 |= APIC_ICR_INT_MASK; + ioapic_write((u32_t)ioapic->addr, + (uint8_t) (IOAPIC_REDIR_TABLE + p * 2 + 1), hi_32); + ioapic_write((u32_t)ioapic->addr, + (uint8_t) (IOAPIC_REDIR_TABLE + p * 2), low_32); + } + } +} + +/* disables all IO APICs */ +PUBLIC void ioapic_disable_all(void) +{ + unsigned ioa; + if (!ioapic_enabled) + return; + + for (ioa = 0 ; ioa < nioapics; ioa++) + ioapic_disable(&io_apic[ioa]); + + ioapic_enabled = 0; /* io apic, disabled */ + + /* Enable 8259 - write 0x00 in OCW1 master and slave. */ + if (apic_imcrp) { + outb(0x22, 0x70); + outb(0x23, 0x00); + } + + lapic_microsec_sleep(200); /* to enable APIC to switch to PIC */ + + apic_idt_init(TRUE); /* reset */ + idt_reload(); + + intr_init(INTS_ORIG, 0); /* no auto eoi */ +} + +PRIVATE void ioapic_disable_irq(unsigned irq) +{ + assert(io_apic_irq[irq].ioa); + + ioapic_disable_pin(io_apic_irq[irq].ioa->addr, io_apic_irq[irq].pin); + io_apic_irq[irq].state |= IOAPIC_IRQ_STATE_MASKED; +} + +PRIVATE void ioapic_enable_irq(unsigned irq) +{ + assert(io_apic_irq[irq].ioa); + + ioapic_enable_pin(io_apic_irq[irq].ioa->addr, io_apic_irq[irq].pin); + io_apic_irq[irq].state &= ~IOAPIC_IRQ_STATE_MASKED; +} + +PUBLIC void ioapic_unmask_irq(unsigned irq) +{ + if (ioapic_enabled) + ioapic_enable_irq(irq); + else + /* FIXME unlikely */ + irq_8259_unmask(irq); +} + +PUBLIC void ioapic_mask_irq(unsigned irq) +{ + if (ioapic_enabled) + ioapic_disable_irq(irq); + else + /* FIXME unlikely */ + irq_8259_mask(irq); +} PRIVATE int calib_clk_handler(irq_hook_t * UNUSED(hook)) { @@ -91,6 +392,7 @@ PRIVATE int calib_clk_handler(irq_hook_t * UNUSED(hook)) else if (probe_ticks == PROBE_TICKS) { lapic_tctr1 = tcrt; tsc1 = tsc; + stop_8253A_timer(); } return 1; @@ -136,8 +438,8 @@ PRIVATE void apic_calibrate_clocks(void) put_irq_handler(&calib_clk, CLOCK_IRQ, calib_clk_handler); /* set the PIC timer to get some time */ - intr_enable(); init_8253A_timer(system_hz); + intr_enable(); /* loop for some time to get a sample */ while(probe_ticks < PROBE_TICKS) { @@ -145,7 +447,6 @@ PRIVATE void apic_calibrate_clocks(void) } intr_disable(); - stop_8253A_timer(); /* remove the probe */ rm_irq_handler(&calib_clk); @@ -210,7 +511,7 @@ PUBLIC void lapic_stop_timer(void) PRIVATE void lapic_microsec_sleep(unsigned count) { lapic_set_timer_one_shot(count); - while (lapic_read (LAPIC_TIMER_CCR)); + while (lapic_read(LAPIC_TIMER_CCR)); } PRIVATE u32_t lapic_errstatus(void) @@ -219,29 +520,49 @@ PRIVATE u32_t lapic_errstatus(void) return lapic_read(LAPIC_ESR); } -PRIVATE void lapic_enable_no_lints(void) +PRIVATE int lapic_disable_in_msr(void) { + u64_t msr; + u32_t addr; + + ia32_msr_read(IA32_APIC_BASE, &msr.hi, &msr.lo); + + msr.lo &= ~(1 << IA32_APIC_BASE_ENABLE_BIT); + ia32_msr_write(IA32_APIC_BASE, msr.hi, msr.lo); + + return 1; +} + +PUBLIC void lapic_disable(void) +{ + /* Disable current APIC and close interrupts from PIC */ u32_t val; - val = lapic_read(LAPIC_LINT0); - lapic_extint_assigned = (val & APIC_ICR_DM_MASK) == APIC_ICR_DM_EXTINT; - val &= ~(APIC_ICR_DM_MASK|APIC_ICR_INT_MASK); - - if (!ioapic_enabled && cpu_is_bsp(cpuid)) - val |= (APIC_ICR_DM_EXTINT); /* ExtINT at LINT0 */ - else - val |= (APIC_ICR_DM_EXTINT|APIC_ICR_INT_MASK); /* Masked ExtINT at LINT0 */ + if (!lapic_addr) + return; + + if (!apic_imcrp) { + /* leave it enabled if imcr is not set */ + val = lapic_read(LAPIC_LINT0); + val &= ~(APIC_ICR_DM_MASK|APIC_ICR_INT_MASK); + val |= APIC_ICR_DM_EXTINT; /* ExtINT at LINT0 */ + lapic_write (LAPIC_LINT0, val); + return; + } + val = lapic_read(LAPIC_LINT0) & 0xFFFE58FF; + val |= APIC_ICR_INT_MASK; lapic_write (LAPIC_LINT0, val); - val = lapic_read(LAPIC_LINT1); - val &= ~(APIC_ICR_DM_MASK|APIC_ICR_INT_MASK); - - if (!ioapic_enabled && cpu_is_bsp(cpuid)) - val |= APIC_ICR_DM_NMI; - else - val |= (APIC_ICR_DM_NMI | APIC_ICR_INT_MASK); /* NMI at LINT1 */ + val = lapic_read(LAPIC_LINT1) & 0xFFFE58FF; + val |= APIC_ICR_INT_MASK; lapic_write (LAPIC_LINT1, val); + + val = lapic_read(LAPIC_SIVR) & 0xFFFFFF00; + val &= ~APIC_ENABLE; + lapic_write(LAPIC_SIVR, val); + + lapic_disable_in_msr(); } PRIVATE int lapic_enable_in_msr(void) @@ -270,9 +591,13 @@ PRIVATE int lapic_enable_in_msr(void) return 1; } -PRIVATE int lapic_enable(void) +PUBLIC int lapic_enable(void) { u32_t val, nlvt; +#if 0 + u32_t timeout = 0xFFFF; + u32_t errstatus = 0; +#endif unsigned cpu = cpuid; if (!lapic_addr) @@ -288,19 +613,21 @@ PRIVATE int lapic_enable(void) if (!lapic_enable_in_msr()) return 0; + /* set the highest priority for ever */ + lapic_write(LAPIC_TPR, 0x0); + lapic_eoi_addr = LAPIC_EOI; /* clear error state register. */ val = lapic_errstatus (); /* Enable Local APIC and set the spurious vector to 0xff. */ - - val = lapic_read(LAPIC_SIVR) & 0xFFFFFF00; + val = lapic_read(LAPIC_SIVR); val |= APIC_ENABLE | APIC_SPURIOUS_INT_VECTOR; val &= ~APIC_FOCUS_DISABLED; lapic_write(LAPIC_SIVR, val); (void) lapic_read(LAPIC_SIVR); - *((u32_t *)lapic_eoi_addr) = 0; + apic_eoi(); cpu = cpuid; @@ -313,10 +640,6 @@ PRIVATE int lapic_enable(void) val = lapic_read(LAPIC_DFR) | 0xF0000000; lapic_write (LAPIC_DFR, val); - if (nlints == 0) { - lapic_enable_no_lints(); - } - val = lapic_read (LAPIC_LVTER) & 0xFFFFFF00; lapic_write (LAPIC_LVTER, val); @@ -338,7 +661,7 @@ PRIVATE int lapic_enable(void) lapic_write (LAPIC_TPR, val & ~0xFF); (void) lapic_read (LAPIC_SIVR); - *((u32_t *)lapic_eoi_addr) = 0; + apic_eoi(); apic_calibrate_clocks(); BOOT_VERBOSE(printf("APIC timer calibrated\n")); @@ -352,24 +675,79 @@ PRIVATE void apic_spurios_intr(void) for(;;); } +PRIVATE void apic_error_intr(void) +{ + printf("WARNING local apic error interrupt\n"); + for(;;); +} + PRIVATE struct gate_table_s gate_table_ioapic[] = { - { apic_hwint00, VECTOR( 0), INTR_PRIVILEGE }, - { apic_hwint01, VECTOR( 1), INTR_PRIVILEGE }, - { apic_hwint02, VECTOR( 2), INTR_PRIVILEGE }, - { apic_hwint03, VECTOR( 3), INTR_PRIVILEGE }, - { apic_hwint04, VECTOR( 4), INTR_PRIVILEGE }, - { apic_hwint05, VECTOR( 5), INTR_PRIVILEGE }, - { apic_hwint06, VECTOR( 6), INTR_PRIVILEGE }, - { apic_hwint07, VECTOR( 7), INTR_PRIVILEGE }, - { apic_hwint08, VECTOR( 8), INTR_PRIVILEGE }, - { apic_hwint09, VECTOR( 9), INTR_PRIVILEGE }, - { apic_hwint10, VECTOR(10), INTR_PRIVILEGE }, - { apic_hwint11, VECTOR(11), INTR_PRIVILEGE }, - { apic_hwint12, VECTOR(12), INTR_PRIVILEGE }, - { apic_hwint13, VECTOR(13), INTR_PRIVILEGE }, - { apic_hwint14, VECTOR(14), INTR_PRIVILEGE }, - { apic_hwint15, VECTOR(15), INTR_PRIVILEGE }, + { apic_hwint0, LAPIC_VECTOR( 0), INTR_PRIVILEGE }, + { apic_hwint1, LAPIC_VECTOR( 1), INTR_PRIVILEGE }, + { apic_hwint2, LAPIC_VECTOR( 2), INTR_PRIVILEGE }, + { apic_hwint3, LAPIC_VECTOR( 3), INTR_PRIVILEGE }, + { apic_hwint4, LAPIC_VECTOR( 4), INTR_PRIVILEGE }, + { apic_hwint5, LAPIC_VECTOR( 5), INTR_PRIVILEGE }, + { apic_hwint6, LAPIC_VECTOR( 6), INTR_PRIVILEGE }, + { apic_hwint7, LAPIC_VECTOR( 7), INTR_PRIVILEGE }, + { apic_hwint8, LAPIC_VECTOR( 8), INTR_PRIVILEGE }, + { apic_hwint9, LAPIC_VECTOR( 9), INTR_PRIVILEGE }, + { apic_hwint10, LAPIC_VECTOR(10), INTR_PRIVILEGE }, + { apic_hwint11, LAPIC_VECTOR(11), INTR_PRIVILEGE }, + { apic_hwint12, LAPIC_VECTOR(12), INTR_PRIVILEGE }, + { apic_hwint13, LAPIC_VECTOR(13), INTR_PRIVILEGE }, + { apic_hwint14, LAPIC_VECTOR(14), INTR_PRIVILEGE }, + { apic_hwint15, LAPIC_VECTOR(15), INTR_PRIVILEGE }, + { apic_hwint16, LAPIC_VECTOR(16), INTR_PRIVILEGE }, + { apic_hwint17, LAPIC_VECTOR(17), INTR_PRIVILEGE }, + { apic_hwint18, LAPIC_VECTOR(18), INTR_PRIVILEGE }, + { apic_hwint19, LAPIC_VECTOR(19), INTR_PRIVILEGE }, + { apic_hwint20, LAPIC_VECTOR(20), INTR_PRIVILEGE }, + { apic_hwint21, LAPIC_VECTOR(21), INTR_PRIVILEGE }, + { apic_hwint22, LAPIC_VECTOR(22), INTR_PRIVILEGE }, + { apic_hwint23, LAPIC_VECTOR(23), INTR_PRIVILEGE }, + { apic_hwint24, LAPIC_VECTOR(24), INTR_PRIVILEGE }, + { apic_hwint25, LAPIC_VECTOR(25), INTR_PRIVILEGE }, + { apic_hwint26, LAPIC_VECTOR(26), INTR_PRIVILEGE }, + { apic_hwint27, LAPIC_VECTOR(27), INTR_PRIVILEGE }, + { apic_hwint28, LAPIC_VECTOR(28), INTR_PRIVILEGE }, + { apic_hwint29, LAPIC_VECTOR(29), INTR_PRIVILEGE }, + { apic_hwint30, LAPIC_VECTOR(30), INTR_PRIVILEGE }, + { apic_hwint31, LAPIC_VECTOR(31), INTR_PRIVILEGE }, + { apic_hwint32, LAPIC_VECTOR(32), INTR_PRIVILEGE }, + { apic_hwint33, LAPIC_VECTOR(33), INTR_PRIVILEGE }, + { apic_hwint34, LAPIC_VECTOR(34), INTR_PRIVILEGE }, + { apic_hwint35, LAPIC_VECTOR(35), INTR_PRIVILEGE }, + { apic_hwint36, LAPIC_VECTOR(36), INTR_PRIVILEGE }, + { apic_hwint37, LAPIC_VECTOR(37), INTR_PRIVILEGE }, + { apic_hwint38, LAPIC_VECTOR(38), INTR_PRIVILEGE }, + { apic_hwint39, LAPIC_VECTOR(39), INTR_PRIVILEGE }, + { apic_hwint40, LAPIC_VECTOR(40), INTR_PRIVILEGE }, + { apic_hwint41, LAPIC_VECTOR(41), INTR_PRIVILEGE }, + { apic_hwint42, LAPIC_VECTOR(42), INTR_PRIVILEGE }, + { apic_hwint43, LAPIC_VECTOR(43), INTR_PRIVILEGE }, + { apic_hwint44, LAPIC_VECTOR(44), INTR_PRIVILEGE }, + { apic_hwint45, LAPIC_VECTOR(45), INTR_PRIVILEGE }, + { apic_hwint46, LAPIC_VECTOR(46), INTR_PRIVILEGE }, + { apic_hwint47, LAPIC_VECTOR(47), INTR_PRIVILEGE }, + { apic_hwint48, LAPIC_VECTOR(48), INTR_PRIVILEGE }, + { apic_hwint49, LAPIC_VECTOR(49), INTR_PRIVILEGE }, + { apic_hwint50, LAPIC_VECTOR(50), INTR_PRIVILEGE }, + { apic_hwint51, LAPIC_VECTOR(51), INTR_PRIVILEGE }, + { apic_hwint52, LAPIC_VECTOR(52), INTR_PRIVILEGE }, + { apic_hwint53, LAPIC_VECTOR(53), INTR_PRIVILEGE }, + { apic_hwint54, LAPIC_VECTOR(54), INTR_PRIVILEGE }, + { apic_hwint55, LAPIC_VECTOR(55), INTR_PRIVILEGE }, + { apic_hwint56, LAPIC_VECTOR(56), INTR_PRIVILEGE }, + { apic_hwint57, LAPIC_VECTOR(57), INTR_PRIVILEGE }, + { apic_hwint58, LAPIC_VECTOR(58), INTR_PRIVILEGE }, + { apic_hwint59, LAPIC_VECTOR(59), INTR_PRIVILEGE }, + { apic_hwint60, LAPIC_VECTOR(60), INTR_PRIVILEGE }, + { apic_hwint61, LAPIC_VECTOR(61), INTR_PRIVILEGE }, + { apic_hwint62, LAPIC_VECTOR(62), INTR_PRIVILEGE }, + { apic_hwint63, LAPIC_VECTOR(63), INTR_PRIVILEGE }, { apic_spurios_intr, APIC_SPURIOUS_INT_VECTOR, INTR_PRIVILEGE }, + { apic_error_intr, APIC_ERROR_INT_VECTOR, INTR_PRIVILEGE }, { NULL, 0, 0} }; @@ -379,11 +757,21 @@ PRIVATE struct gate_table_s gate_table_common[] = { { NULL, 0, 0} }; +#ifdef CONFIG_SMP +PRIVATE struct gate_table_s gate_table_smp[] = { + { smp_ipi_sched, SMP_SCHED_PROC, INTR_PRIVILEGE }, + { smp_ipi_dequeue, SMP_DEQUEUE_PROC, INTR_PRIVILEGE }, + { smp_ipi_reboot,SMP_CPU_REBOOT, INTR_PRIVILEGE }, + { smp_ipi_stop, SMP_CPU_HALT, INTR_PRIVILEGE }, + { NULL, 0, 0} +}; +#endif + #ifdef CONFIG_APIC_DEBUG PRIVATE void lapic_set_dummy_handlers(void) { char * handler; - int vect = 32; + int vect = 32; /* skip the reserved vectors */ handler = &lapic_intr_dummy_handles_start; handler += vect * LAPIC_INTR_DUMMY_HANDLER_SIZE; @@ -399,6 +787,8 @@ PRIVATE void lapic_set_dummy_handlers(void) /* Build descriptors for interrupt gates in IDT. */ PRIVATE void apic_idt_init(const int reset) { + u32_t val; + /* Set up idt tables for smp mode. */ vir_bytes local_timer_intr_handler; @@ -423,6 +813,17 @@ PRIVATE void apic_idt_init(const int reset) idt_copy_vectors(gate_table_common); +#ifdef CONFIG_SMP + idt_copy_vectors(gate_table_smp); +#endif + + /* Setup error interrupt vector */ + val = lapic_read(LAPIC_LVTER); + val |= APIC_ERROR_INT_VECTOR; + val &= ~ APIC_ICR_INT_MASK; + lapic_write(LAPIC_LVTER, val); + (void) lapic_read(LAPIC_LVTER); + /* configure the timer interupt handler */ if (cpu_is_bsp(cpuid)) { local_timer_intr_handler = (vir_bytes) lapic_bsp_timer_int_handler; @@ -437,6 +838,47 @@ PRIVATE void apic_idt_init(const int reset) PRESENT | INT_GATE_TYPE | (INTR_PRIVILEGE << DPL_SHIFT)); } +PRIVATE int acpi_get_ioapics(struct io_apic * ioa, unsigned * nioa, unsigned max) +{ + unsigned n = 0; + struct acpi_madt_ioapic * acpi_ioa; + + while (n < max) { + acpi_ioa = acpi_get_ioapic_next(); + if (acpi_ioa == NULL) + break; + + ioa[n].id = acpi_ioa->id; + ioa[n].addr = phys2vir(acpi_ioa->address); + ioa[n].paddr = (phys_bytes) acpi_ioa->address; + ioa[n].gsi_base = acpi_ioa->global_int_base; + ioa[n].pins = ((ioapic_read(ioa[n].addr, + IOAPIC_VERSION) & 0xff0000) >> 16)+1; + printf("IO APIC %d addr 0x%x paddr 0x%x pins %d\n", + acpi_ioa->id, ioa[n].addr, ioa[n].paddr, + ioa[n].pins); + + n++; + } + + *nioa = n; + return n; +} + +PRIVATE int detect_ioapics(void) +{ + int status; + + if (machine.acpi_rsdp) + status = acpi_get_ioapics(io_apic, &nioapics, MAX_NR_IOAPICS); + if (!status) { + /* try something different like MPS */ + } + + printf("nioapics %d\n", nioapics); + return status; +} + PUBLIC int apic_single_cpu_init(void) { if (!cpu_feature_apic_on_chip()) @@ -450,7 +892,168 @@ PUBLIC int apic_single_cpu_init(void) return 0; } + acpi_init(); + + if (!detect_ioapics()) { + lapic_disable(); + lapic_addr = 0x0; + return 0; + } + + ioapic_enable_all(); + + if (ioapic_enabled) + machine.apic_enabled = 1; + apic_idt_init(0); /* Not a reset ! */ idt_reload(); return 1; } + +PRIVATE eoi_method_t set_eoi_method(unsigned irq) +{ + /* + * in APIC mode the lowest 16 IRQs are reserved for legacy (E)ISA edge + * triggered interrupts. All the rest is for PCI level triggered + * interrupts + */ + if (irq < 16) + return ioapic_eoi_edge; + else + return ioapic_eoi_level; +} + +PUBLIC void set_irq_redir_low(unsigned irq, u32_t * low) +{ + u32_t val = 0; + + /* clear the polarity, trigger, mask and vector fields */ + val &= ~(APIC_ICR_VECTOR | APIC_ICR_INT_MASK | + APIC_ICR_TRIGGER | APIC_ICR_INT_POLARITY); + + if (irq < 16) { + /* ISA active-high */ + val &= ~APIC_ICR_INT_POLARITY; + /* ISA edge triggered */ + val &= ~APIC_ICR_TRIGGER; + } + else { + /* PCI active-low */ + val |= APIC_ICR_INT_POLARITY; + /* PCI level triggered */ + val |= APIC_ICR_TRIGGER; + } + + val |= io_apic_irq[irq].vector; + + *low = val; +} + +PUBLIC void ioapic_set_irq(unsigned irq) +{ + unsigned ioa; + + assert(irq < NR_IRQ_VECTORS); + + /* shared irq, already set */ + if (io_apic_irq[irq].ioa && io_apic_irq[irq].eoi) + return; + + assert(!io_apic_irq[irq].ioa || !io_apic_irq[irq].eoi); + + for (ioa = 0; ioa < nioapics; ioa++) { + if (io_apic[ioa].gsi_base <= irq && + io_apic[ioa].gsi_base + + io_apic[ioa].pins > irq) { + u32_t hi_32, low_32; + + io_apic_irq[irq].ioa = &io_apic[ioa]; + io_apic_irq[irq].pin = irq - io_apic[ioa].gsi_base; + io_apic_irq[irq].eoi = set_eoi_method(irq); + io_apic_irq[irq].vector = LAPIC_VECTOR(irq); + + set_irq_redir_low(irq, &low_32); + /* + * route the interrupts to the bsp by default + */ + hi_32 = 0; + ioapic_redirt_entry_write((void *) io_apic[ioa].addr, + io_apic_irq[irq].pin, hi_32, low_32); + } + } +} + +PUBLIC void ioapic_unset_irq(unsigned irq) +{ + assert(irq < NR_IRQ_VECTORS); + + ioapic_disable_irq(irq); + io_apic_irq[irq].ioa = NULL; + io_apic_irq[irq].eoi = NULL; +} + +PUBLIC void ioapic_reset_pic(void) +{ + apic_idt_init(TRUE); /* reset */ + idt_reload(); + + /* Enable 8259 - write 0x00 in OCW1 + * master and slave. */ + outb(0x22, 0x70); + outb(0x23, 0x00); + + intr_init(INTS_ORIG, 0); /* no auto eoi */ +} + +PRIVATE void irq_lapic_status(int irq) +{ + u32_t lo; + reg_t tmr, irr, isr; + int vector; + struct irq * intr; + + intr = &io_apic_irq[irq]; + + if (!intr->ioa) + return; + + vector = LAPIC_VECTOR(irq); + tmr = apic_read_tmr_vector(vector); + irr = apic_read_irr_vector(vector); + isr = apic_read_isr_vector(vector); + + + if (lapic_test_delivery_val(isr, vector)) { + printf("IRQ %d vec %d trigger %s irr %d isr %d\n", + irq, vector, + lapic_test_delivery_val(tmr, vector) ? + "level" : "edge", + lapic_test_delivery_val(irr, vector) ? 1 : 0, + lapic_test_delivery_val(isr, vector) ? 1 : 0); + } else { + printf("IRQ %d vec %d irr %d\n", + irq, vector, + lapic_test_delivery_val(irr, vector) ? 1 : 0); + } + + lo = ioapic_read(intr->ioa->addr, + IOAPIC_REDIR_TABLE + intr->pin * 2); + printf("\tpin %2d vec 0x%02x ioa %d redir_lo 0x%08x %s\n", + intr->pin, + intr->vector, + intr->ioa->id, + lo, + intr->state & IOAPIC_IRQ_STATE_MASKED ? + "masked" : "unmasked"); +} + +PUBLIC void dump_apic_irq_state(void) +{ + int irq; + + printf("--- IRQs state dump ---\n"); + for (irq = 0; irq < NR_IRQ_VECTORS; irq++) { + irq_lapic_status(irq); + } + printf("--- all ---\n"); +} diff --git a/kernel/arch/i386/apic.h b/kernel/arch/i386/apic.h index 3810ad65c..49adaa8b9 100644 --- a/kernel/arch/i386/apic.h +++ b/kernel/arch/i386/apic.h @@ -66,6 +66,9 @@ #define LAPIC_LDR (lapic_addr + 0x0d0) #define LAPIC_DFR (lapic_addr + 0x0e0) #define LAPIC_SIVR (lapic_addr + 0x0f0) +#define LAPIC_ISR (lapic_addr + 0x100) +#define LAPIC_TMR (lapic_addr + 0x180) +#define LAPIC_IRR (lapic_addr + 0x200) #define LAPIC_ESR (lapic_addr + 0x280) #define LAPIC_ICR1 (lapic_addr + 0x300) #define LAPIC_ICR2 (lapic_addr + 0x310) @@ -84,10 +87,10 @@ #define IOAPIC_ARB 0x2 #define IOAPIC_REDIR_TABLE 0x10 -#define APIC_TIMER_INT_VECTOR 0xfe +#define APIC_TIMER_INT_VECTOR 0xf0 +#define APIC_ERROR_INT_VECTOR 0xfe #define APIC_SPURIOUS_INT_VECTOR 0xff - #ifndef __ASSEMBLY__ #include "kernel/kernel.h" @@ -95,34 +98,50 @@ EXTERN vir_bytes lapic_addr; EXTERN vir_bytes lapic_eoi_addr; -#define MAX_NR_IOAPICS 32 -#define MAX_NR_BUSES 32 -#define MAX_NR_APICIDS 255 -#define MAX_NR_LCLINTS 2 +#define MAX_NR_IOAPICS 32 +#define MAX_IOAPIC_IRQS 64 -EXTERN u8_t apicid2cpuid[MAX_NR_APICIDS+1]; +EXTERN int ioapic_enabled; + +struct io_apic { + unsigned id; + vir_bytes addr; /* presently used address */ + phys_bytes paddr; /* where is it inphys space */ + vir_bytes vaddr; /* adress after paging s on */ + unsigned pins; + unsigned gsi_base; +}; + +EXTERN struct io_apic io_apic[MAX_NR_IOAPICS]; +EXTERN unsigned nioapics; EXTERN u32_t lapic_addr_vaddr; /* we remember the virtual address here until we switch to paging */ -/* -_PROTOTYPE (u32_t ioapic_read, (u32_t addr, u32_t offset)); -_PROTOTYPE (void ioapic_write, (u32_t addr, u32_t offset, u32_t data)); -_PROTOTYPE (void lapic_eoi, (void)); -*/ - _PROTOTYPE(int apic_single_cpu_init, (void)); _PROTOTYPE(void lapic_set_timer_periodic, (unsigned freq)); _PROTOTYPE(void lapic_stop_timer, (void)); +_PROTOTYPE(void ioapic_set_irq, (unsigned irq)); +_PROTOTYPE(void ioapic_unset_irq, (unsigned irq)); + + /* signal the end of interrupt handler to apic */ +_PROTOTYPE(void ioapic_eoi, (int irq)); + +_PROTOTYPE(void lapic_disable, (void)); +_PROTOTYPE(void ioapic_disable_all, (void)); +_PROTOTYPE(void ioapic_reset_pic, (void)); + +_PROTOTYPE(void dump_apic_irq_state, (void)); + #include #define cpu_feature_apic_on_chip() _cpufeature(_CPUF_I386_APIC_ON_CHIP) -#define lapic_read(what) (*((u32_t *)((what)))) +#define lapic_read(what) (*((volatile u32_t *)((what)))) #define lapic_write(what, data) do { \ - (*((u32_t *)((what)))) = data; \ + (*((volatile u32_t *)((what)))) = data; \ } while(0) #endif /* __ASSEMBLY__ */ diff --git a/kernel/arch/i386/apic_asm.S b/kernel/arch/i386/apic_asm.S index ddbd30f59..8a9f7b87c 100644 --- a/kernel/arch/i386/apic_asm.S +++ b/kernel/arch/i386/apic_asm.S @@ -5,18 +5,17 @@ #include #define APIC_IRQ_HANDLER(irq) \ - push $irq ;\ - call _C_LABEL(irq_handle) /* intr_handle(irq_handlers[irq]) */ ;\ - add $4, %esp ;\ - mov _C_LABEL(lapic_eoi_addr), %eax ;\ - movl $0, (%eax) ;\ + push $irq ;\ + call _C_LABEL(irq_handle) /* intr_handle(irq) */ ;\ + add $4, %esp ; /*===========================================================================*/ /* interrupt handlers */ /* interrupt handlers for 386 32-bit protected mode */ /* APIC interrupt handlers for 386 32-bit protected mode */ /*===========================================================================*/ -#define apic_hwint(irq) \ +#define apic_hwint(irq) \ +ENTRY(apic_hwint##irq) \ TEST_INT_IN_KERNEL(4, 0f) ;\ \ SAVE_PROCESS_CTX(0) ;\ @@ -35,73 +34,6 @@ popa ;\ iret ; -/* Handlers for hardware interrupts */ -/* Each of these entry points is an expansion of the hwint_master macro */ -ENTRY(apic_hwint00) -/* Interrupt routine for irq 0 (the clock). */ - apic_hwint(0) - -ENTRY(apic_hwint01) -/* Interrupt routine for irq 1 (keyboard) */ - apic_hwint(1) - -ENTRY(apic_hwint02) -/* Interrupt routine for irq 2 (cascade!) */ - apic_hwint(2) - -ENTRY(apic_hwint03) -/* Interrupt routine for irq 3 (second serial) */ - apic_hwint(3) - -ENTRY(apic_hwint04) -/* Interrupt routine for irq 4 (first serial) */ - apic_hwint(4) - -ENTRY(apic_hwint05) -/* Interrupt routine for irq 5 (XT winchester) */ - apic_hwint(5) - -ENTRY(apic_hwint06) -/* Interrupt routine for irq 6 (floppy) */ - apic_hwint(6) - -ENTRY(apic_hwint07) -/* Interrupt routine for irq 7 (printer) */ - apic_hwint(7) - -ENTRY(apic_hwint08) -/* Interrupt routine for irq 8 (realtime clock) */ - apic_hwint(8) - -ENTRY(apic_hwint09) -/* Interrupt routine for irq 9 (irq 2 redirected) */ - apic_hwint(9) - -ENTRY(apic_hwint10) -/* Interrupt routine for irq 10 */ - apic_hwint(10) - -ENTRY(apic_hwint11) -/* Interrupt routine for irq 11 */ - apic_hwint(11) - -ENTRY(apic_hwint12) -/* Interrupt routine for irq 12 */ - apic_hwint(12) - -ENTRY(apic_hwint13) -/* Interrupt routine for irq 13 (FPU exception) */ - apic_hwint(13) - -ENTRY(apic_hwint14) -/* Interrupt routine for irq 14 (AT winchester) */ - apic_hwint(14) - -ENTRY(apic_hwint15) -/* Interrupt routine for irq 15 */ - apic_hwint(15) - - #define LAPIC_INTR_HANDLER(func) \ movl $func, %eax ;\ call *%eax /* call the actual handler */ ;\ @@ -142,7 +74,7 @@ ENTRY(lapic_ap_timer_int_handler) .data lapic_intr_dummy_handler_msg: -.ascii "UNHABLED APIC interrupt vector %d\n" +.ascii "UNHANDLED APIC interrupt vector %d\n" .text @@ -156,6 +88,71 @@ lapic_intr_dummy_handler_msg: .balign LAPIC_INTR_DUMMY_HANDLER_SIZE; \ lapic_intr_dummy_handler_##vect: lapic_intr_dummy_handler(vect) +apic_hwint(0) +apic_hwint(1) +apic_hwint(2) +apic_hwint(3) +apic_hwint(4) +apic_hwint(5) +apic_hwint(6) +apic_hwint(7) +apic_hwint(8) +apic_hwint(9) +apic_hwint(10) +apic_hwint(11) +apic_hwint(12) +apic_hwint(13) +apic_hwint(14) +apic_hwint(15) +apic_hwint(16) +apic_hwint(17) +apic_hwint(18) +apic_hwint(19) +apic_hwint(20) +apic_hwint(21) +apic_hwint(22) +apic_hwint(23) +apic_hwint(24) +apic_hwint(25) +apic_hwint(26) +apic_hwint(27) +apic_hwint(28) +apic_hwint(29) +apic_hwint(30) +apic_hwint(31) +apic_hwint(32) +apic_hwint(33) +apic_hwint(34) +apic_hwint(35) +apic_hwint(36) +apic_hwint(37) +apic_hwint(38) +apic_hwint(39) +apic_hwint(40) +apic_hwint(41) +apic_hwint(42) +apic_hwint(43) +apic_hwint(44) +apic_hwint(45) +apic_hwint(46) +apic_hwint(47) +apic_hwint(48) +apic_hwint(49) +apic_hwint(50) +apic_hwint(51) +apic_hwint(52) +apic_hwint(53) +apic_hwint(54) +apic_hwint(55) +apic_hwint(56) +apic_hwint(57) +apic_hwint(58) +apic_hwint(59) +apic_hwint(60) +apic_hwint(61) +apic_hwint(62) +apic_hwint(63) + LABEL(lapic_intr_dummy_handles_start) LAPIC_INTR_DUMMY_HANDLER(0) LAPIC_INTR_DUMMY_HANDLER(1) diff --git a/kernel/arch/i386/apic_asm.h b/kernel/arch/i386/apic_asm.h index fdb3eb850..8086100d9 100644 --- a/kernel/arch/i386/apic_asm.h +++ b/kernel/arch/i386/apic_asm.h @@ -5,22 +5,70 @@ #ifndef __ASSEMBLY__ #include "kernel/kernel.h" -_PROTOTYPE( void apic_hwint00, (void) ); -_PROTOTYPE( void apic_hwint01, (void) ); -_PROTOTYPE( void apic_hwint02, (void) ); -_PROTOTYPE( void apic_hwint03, (void) ); -_PROTOTYPE( void apic_hwint04, (void) ); -_PROTOTYPE( void apic_hwint05, (void) ); -_PROTOTYPE( void apic_hwint06, (void) ); -_PROTOTYPE( void apic_hwint07, (void) ); -_PROTOTYPE( void apic_hwint08, (void) ); -_PROTOTYPE( void apic_hwint09, (void) ); +_PROTOTYPE( void apic_hwint0, (void) ); +_PROTOTYPE( void apic_hwint1, (void) ); +_PROTOTYPE( void apic_hwint2, (void) ); +_PROTOTYPE( void apic_hwint3, (void) ); +_PROTOTYPE( void apic_hwint4, (void) ); +_PROTOTYPE( void apic_hwint5, (void) ); +_PROTOTYPE( void apic_hwint6, (void) ); +_PROTOTYPE( void apic_hwint7, (void) ); +_PROTOTYPE( void apic_hwint8, (void) ); +_PROTOTYPE( void apic_hwint9, (void) ); _PROTOTYPE( void apic_hwint10, (void) ); _PROTOTYPE( void apic_hwint11, (void) ); _PROTOTYPE( void apic_hwint12, (void) ); _PROTOTYPE( void apic_hwint13, (void) ); _PROTOTYPE( void apic_hwint14, (void) ); _PROTOTYPE( void apic_hwint15, (void) ); +_PROTOTYPE( void apic_hwint16, (void) ); +_PROTOTYPE( void apic_hwint17, (void) ); +_PROTOTYPE( void apic_hwint18, (void) ); +_PROTOTYPE( void apic_hwint19, (void) ); +_PROTOTYPE( void apic_hwint20, (void) ); +_PROTOTYPE( void apic_hwint21, (void) ); +_PROTOTYPE( void apic_hwint22, (void) ); +_PROTOTYPE( void apic_hwint23, (void) ); +_PROTOTYPE( void apic_hwint24, (void) ); +_PROTOTYPE( void apic_hwint25, (void) ); +_PROTOTYPE( void apic_hwint26, (void) ); +_PROTOTYPE( void apic_hwint27, (void) ); +_PROTOTYPE( void apic_hwint28, (void) ); +_PROTOTYPE( void apic_hwint29, (void) ); +_PROTOTYPE( void apic_hwint30, (void) ); +_PROTOTYPE( void apic_hwint31, (void) ); +_PROTOTYPE( void apic_hwint32, (void) ); +_PROTOTYPE( void apic_hwint33, (void) ); +_PROTOTYPE( void apic_hwint34, (void) ); +_PROTOTYPE( void apic_hwint35, (void) ); +_PROTOTYPE( void apic_hwint36, (void) ); +_PROTOTYPE( void apic_hwint37, (void) ); +_PROTOTYPE( void apic_hwint38, (void) ); +_PROTOTYPE( void apic_hwint39, (void) ); +_PROTOTYPE( void apic_hwint40, (void) ); +_PROTOTYPE( void apic_hwint41, (void) ); +_PROTOTYPE( void apic_hwint42, (void) ); +_PROTOTYPE( void apic_hwint43, (void) ); +_PROTOTYPE( void apic_hwint44, (void) ); +_PROTOTYPE( void apic_hwint45, (void) ); +_PROTOTYPE( void apic_hwint46, (void) ); +_PROTOTYPE( void apic_hwint47, (void) ); +_PROTOTYPE( void apic_hwint48, (void) ); +_PROTOTYPE( void apic_hwint49, (void) ); +_PROTOTYPE( void apic_hwint50, (void) ); +_PROTOTYPE( void apic_hwint51, (void) ); +_PROTOTYPE( void apic_hwint52, (void) ); +_PROTOTYPE( void apic_hwint53, (void) ); +_PROTOTYPE( void apic_hwint54, (void) ); +_PROTOTYPE( void apic_hwint55, (void) ); +_PROTOTYPE( void apic_hwint56, (void) ); +_PROTOTYPE( void apic_hwint57, (void) ); +_PROTOTYPE( void apic_hwint58, (void) ); +_PROTOTYPE( void apic_hwint59, (void) ); +_PROTOTYPE( void apic_hwint60, (void) ); +_PROTOTYPE( void apic_hwint61, (void) ); +_PROTOTYPE( void apic_hwint62, (void) ); +_PROTOTYPE( void apic_hwint63, (void) ); /* The local APIC timer tick handlers */ _PROTOTYPE(void lapic_bsp_timer_int_handler, (void)); diff --git a/kernel/arch/i386/arch_system.c b/kernel/arch/i386/arch_system.c index b96674207..438791d2d 100644 --- a/kernel/arch/i386/arch_system.c +++ b/kernel/arch/i386/arch_system.c @@ -433,6 +433,11 @@ PRIVATE void ser_debug(const int c) } TOGGLECASE('8', VF_SCHEDULING) TOGGLECASE('9', VF_PICKPROC) +#endif +#ifdef CONFIG_APIC + case 'I': + dump_apic_irq_state(); + break; #endif } serial_debug_active = 0; diff --git a/kernel/arch/i386/i8259.c b/kernel/arch/i386/i8259.c index 42674bb5a..c76d68468 100644 --- a/kernel/arch/i386/i8259.c +++ b/kernel/arch/i386/i8259.c @@ -84,10 +84,17 @@ PUBLIC void irq_8259_mask(const int irq) } /* Disable 8259 - write 0xFF in OCW1 master and slave. */ -PRIVATE void i8259_disable(void) +PUBLIC void i8259_disable(void) { outb(INT2_CTLMASK, 0xFF); outb(INT_CTLMASK, 0xFF); inb(INT_CTLMASK); } +PUBLIC void irq_8259_eoi(int irq) +{ + if (irq < 8) + eoi_8259_master(); + else + eoi_8259_slave(); +} diff --git a/kernel/arch/i386/include/hw_intr.h b/kernel/arch/i386/include/hw_intr.h index 6f70495c9..eaf2c808e 100644 --- a/kernel/arch/i386/include/hw_intr.h +++ b/kernel/arch/i386/include/hw_intr.h @@ -2,14 +2,48 @@ #define __HW_INTR_X86_H__ #include "kernel/kernel.h" - -/* legacy PIC */ - -_PROTOTYPE(void irq_8259_unmask,(int irq)); -_PROTOTYPE(void irq_8259_mask,(int irq)); +_PROTOTYPE(int irq_8259_unmask,(int irq)); +_PROTOTYPE(int irq_8259_mask,(int irq)); +_PROTOTYPE(int irq_8259_eoi, (int irq)); _PROTOTYPE(void irq_handle,(int irq)); +_PROTOTYPE(void i8259_disable,(void)); + +/* + * we don't use IO APIC if not configured for SMP as we cannot read any info + * about it unless we use MPS which is not present on all single CPU + * configurations. ACPI would be another option, however we don't support it + * either + */ +#if defined(CONFIG_APIC) +#include "arch/i386/apic.h" + +#define hw_intr_mask(irq) ioapic_mask_irq(irq) +#define hw_intr_unmask(irq) ioapic_unmask_irq(irq) +#define hw_intr_ack(irq) ioapic_eoi(irq) +#define hw_intr_used(irq) do { \ + if (ioapic_enabled) \ + ioapic_set_irq(irq); \ + } while (0) +#define hw_intr_not_used(irq) do { \ + if (ioapic_enabled) \ + ioapic_unset_irq(irq); \ + } while (0) +#define hw_intr_disable_all() do { \ + ioapic_disable_all(); \ + ioapic_reset_pic(); \ + lapic_disable(); \ + } while (0) + +#else +/* legacy PIC */ #define hw_intr_mask(irq) irq_8259_mask(irq) #define hw_intr_unmask(irq) irq_8259_unmask(irq) +#define hw_intr_ack(irq) irq_8259_eoi(irq) +#define hw_intr_used(irq) +#define hw_intr_not_used(irq) +#define hw_intr_disable_all() + +#endif #endif /* __HW_INTR_X86_H__ */ diff --git a/kernel/arch/i386/klib.S b/kernel/arch/i386/klib.S index 467fd518a..7f0f93d3c 100644 --- a/kernel/arch/i386/klib.S +++ b/kernel/arch/i386/klib.S @@ -888,6 +888,19 @@ ENTRY(poweroff_jmp) /* Jump to 16-bit code that is copied to below 1MB */ ljmp $MON_CS_SELECTOR, $0 + +/* acknowledge just the master PIC */ +ENTRY(eoi_8259_master) + movb $END_OF_INT, %al + outb $INT_CTL + ret + +/* we have to acknowledge both PICs */ +ENTRY(eoi_8259_slave) + movb $END_OF_INT, %al + outb $INT_CTL + outb $INT2_CTL + ret .data idt_ptr: diff --git a/kernel/arch/i386/memory.c b/kernel/arch/i386/memory.c index d1d08082c..0feca6814 100644 --- a/kernel/arch/i386/memory.c +++ b/kernel/arch/i386/memory.c @@ -1,5 +1,4 @@ - #include "kernel/kernel.h" #include "kernel/proc.h" #include "kernel/vm.h" @@ -955,7 +954,7 @@ void i386_freepde(const int pde) freepdes[nfreepdes++] = pde; } -PRIVATE int lapic_mapping_index = -1, oxpcie_mapping_index = -1; +PRIVATE int oxpcie_mapping_index = -1; PUBLIC int arch_phys_map(const int index, phys_bytes *addr, phys_bytes *len, int *flags) @@ -967,7 +966,9 @@ PUBLIC int arch_phys_map(const int index, phys_bytes *addr, if(first) { #ifdef CONFIG_APIC if(lapic_addr) - lapic_mapping_index = freeidx++; + freeidx++; + if (ioapic_enabled) + freeidx += nioapics; #endif #ifdef CONFIG_OXPCIE @@ -984,12 +985,20 @@ PUBLIC int arch_phys_map(const int index, phys_bytes *addr, #ifdef CONFIG_APIC /* map the local APIC if enabled */ - if (index == lapic_mapping_index) { + if (index == 0) { + if (!lapic_addr) + return EINVAL; *addr = vir2phys(lapic_addr); *len = 4 << 10 /* 4kB */; *flags = VMMF_UNCACHED; return OK; } + else if (ioapic_enabled && index <= nioapics) { + *addr = io_apic[index - 1].paddr; + *len = 4 << 10 /* 4kB */; + *flags = VMMF_UNCACHED; + return OK; + } #endif #if CONFIG_OXPCIE @@ -1008,18 +1017,24 @@ PUBLIC int arch_phys_map_reply(const int index, const vir_bytes addr) { #ifdef CONFIG_APIC /* if local APIC is enabled */ - if (index == lapic_mapping_index && lapic_addr) { + if (index == 0 && lapic_addr) { lapic_addr_vaddr = addr; + return OK; + } + else if (ioapic_enabled && index <= nioapics) { + io_apic[index - 1].vaddr = addr; + return OK; } #endif #if CONFIG_OXPCIE if (index == oxpcie_mapping_index) { oxpcie_set_vaddr((unsigned char *) addr); + return OK; } #endif - return OK; + return EINVAL; } PUBLIC int arch_enable_paging(struct proc * caller, const message * m_ptr) @@ -1050,11 +1065,21 @@ PUBLIC int arch_enable_paging(struct proc * caller, const message * m_ptr) panic("arch_enable_paging: newmap failed"); #ifdef CONFIG_APIC + /* start using the virtual addresses */ + /* if local APIC is enabled */ if (lapic_addr) { lapic_addr = lapic_addr_vaddr; lapic_eoi_addr = LAPIC_EOI; } + /* if IO apics are enabled */ + if (ioapic_enabled) { + int i; + + for (i = 0; i < nioapics; i++) { + io_apic[i].addr = io_apic[i].vaddr; + } + } #endif #ifdef CONFIG_WATCHDOG /* diff --git a/kernel/interrupt.c b/kernel/interrupt.c index 61a4a6c17..661a4e1c8 100644 --- a/kernel/interrupt.c +++ b/kernel/interrupt.c @@ -20,6 +20,7 @@ #include "archconst.h" #include "hw_intr.h" + /* number of lists of IRQ hooks, one list per supported line. */ PRIVATE irq_hook_t* irq_handlers[NR_IRQ_VECTORS] = {0}; @@ -58,13 +59,13 @@ PUBLIC void put_irq_handler( irq_hook_t* hook, int irq, hook->irq = irq; hook->id = id; *line = hook; - irq_use |= 1 << irq; /* this does not work for irq >= 32 */ /* And as last enable the irq at the hardware. * * Internal this activates the line or source of the given interrupt. */ if((irq_actids[hook->irq] &= ~hook->id) == 0) { + hw_intr_used(irq); hw_intr_unmask(hook->irq); } } @@ -86,8 +87,6 @@ PUBLIC void rm_irq_handler( const irq_hook_t* hook ) { while( (*line) != NULL ) { if((*line)->id == id) { (*line) = (*line)->next; - if(!irq_handlers[irq]) - irq_use &= ~(1 << irq); if (irq_actids[irq] & id) irq_actids[irq] &= ~id; } @@ -101,6 +100,7 @@ PUBLIC void rm_irq_handler( const irq_hook_t* hook ) { */ if (irq_handlers[irq] == NULL) { hw_intr_mask(irq); + hw_intr_not_used(irq); } else if (irq_actids[irq] == 0) { hw_intr_unmask(irq); @@ -155,6 +155,8 @@ PUBLIC void irq_handle(int irq) /* reenable the IRQ only if there is no active handler */ if (irq_actids[irq] == 0) hw_intr_unmask(irq); + + hw_intr_ack(irq); } /* Enable/Disable a interrupt line. */ diff --git a/kernel/main.c b/kernel/main.c index b95ca5bef..df88e040c 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -19,6 +19,7 @@ #include "proc.h" #include "debug.h" #include "clock.h" +#include "hw_intr.h" /* Prototype declarations for PRIVATE functions. */ FORWARD _PROTOTYPE( void announce, (void)); @@ -323,6 +324,7 @@ PUBLIC void minix_shutdown(timer_t *tp) * monitor), RBT_MONITOR (execute given code), RBT_RESET (hard reset). */ arch_stop_local_timer(); + hw_intr_disable_all(); intr_init(INTS_ORIG, 0); arch_shutdown(tp ? tmr_arg(tp)->ta_int : RBT_PANIC); }