This patch enables the first IOAPIC when --enable-apic is used, replacing the PIC. It also starts a LAPIC timer, but I'm not sure if it is correctly being used.
I am posting this patch even though it is unfinished, because I noticed a strange error that could indicate something is missing from the rest of SMP support. ... LAPIC timer configured TODO: intel_startCPU TODO: intel_startCPU TODO: intel_startCPU {cpu0} ../kern/slab.c:1021: kmem_cache_free_to_slab: Assertion `((unsigned long) buf + cache->buf_size) <= vm_page_trunc((unsigned long)slab->addr + cache->slab_ size)' failed.Debugger invoked: assertion failure db{0}> show all tasks ID TASK NAME [THREADS] 0 f63b5ea0 gnumach [9] 1 f63b5dd0 ext2fs [1] 2 f63b5d00 exec [0] Is something wrong with the cache in SMP mode? I ran qemu with -smp 2 --- i386/Makefrag.am | 13 +- i386/configfrag.ac | 12 ++ i386/i386/apic.c | 13 ++ i386/i386/apic.h | 70 ++++++- i386/i386/irq.c | 4 + i386/i386/irq.h | 4 + i386/i386/locore.S | 13 ++ i386/i386/pic.h | 4 +- i386/i386/pit.c | 38 +++- i386/i386/pit.h | 2 + i386/i386at/acpi_parse_apic.c | 19 +- i386/i386at/acpi_parse_apic.h | 2 +- i386/i386at/idt.h | 17 +- i386/i386at/int_init.c | 8 +- i386/i386at/interrupt.S | 16 ++ i386/i386at/ioapic.c | 320 +++++++++++++++++++++++++++++++ i386/i386at/model_dep.c | 23 ++- linux/dev/arch/i386/kernel/irq.c | 6 +- 18 files changed, 549 insertions(+), 35 deletions(-) create mode 100644 i386/i386at/ioapic.c diff --git a/i386/Makefrag.am b/i386/Makefrag.am index 73df45f4..de3cddc8 100644 --- a/i386/Makefrag.am +++ b/i386/Makefrag.am @@ -57,7 +57,6 @@ libkernel_a_SOURCES += \ i386/i386at/kdsoft.h \ i386/i386at/mem.c \ i386/i386at/mem.h \ - i386/i386at/pic_isa.c \ i386/i386at/rtc.c \ i386/i386at/rtc.h endif @@ -155,6 +154,16 @@ EXTRA_DIST += \ i386/i386/mach_i386.srv if PLATFORM_at +if enable_apic +libkernel_a_SOURCES += \ + i386/i386at/ioapic.c +else +libkernel_a_SOURCES += \ + i386/i386/pic.c \ + i386/i386/pic.h \ + i386/i386at/pic_isa.c +endif + libkernel_a_SOURCES += \ i386/i386/apic.h \ i386/i386/apic.c \ @@ -163,8 +172,6 @@ libkernel_a_SOURCES += \ i386/i386/io_map.c \ i386/i386/irq.c \ i386/i386/irq.h \ - i386/i386/pic.c \ - i386/i386/pic.h \ i386/i386/pit.c \ i386/i386/pit.h endif diff --git a/i386/configfrag.ac b/i386/configfrag.ac index bf4af110..9a39ccbb 100644 --- a/i386/configfrag.ac +++ b/i386/configfrag.ac @@ -92,6 +92,18 @@ if [ x"$enable_lpr" = xyes ]; then] AM_CONDITIONAL([enable_lpr], [false]) [fi] +AC_ARG_ENABLE([apic], + AS_HELP_STRING([--enable-apic], [LAPIC/IOAPIC support (ix86-only); enabled by default])) +[case $host_platform:$host_cpu in + *:i?86) + enable_apic=${enable_apic-yes};; +esac +if [ x"$enable_apic" = xyes ]; then] + AC_DEFINE([APIC], [1], [APIC support]) + AM_CONDITIONAL([enable_apic], [true]) +[else] + AM_CONDITIONAL([enable_apic], [false]) +[fi] [case $host_platform:$host_cpu in xen:i?86) diff --git a/i386/i386/apic.c b/i386/i386/apic.c index f0b4a153..f60276e9 100644 --- a/i386/i386/apic.c +++ b/i386/i386/apic.c @@ -95,6 +95,19 @@ apic_add_irq_override(IrqOverrideData irq_over) apic_data.nirqoverride++; } +IrqOverrideData * +acpi_get_irq_override(uint8_t gsi) +{ + int i; + + for (i = 0; i < apic_data.nirqoverride; i++) { + if (apic_data.irq_override_list[i].gsi == gsi) { + return &apic_data.irq_override_list[i]; + } + } + return NULL; +} + /* * apic_get_cpu_apic_id: returns the apic_id of a cpu. * Receives as input the kernel ID of a CPU. diff --git a/i386/i386/apic.h b/i386/i386/apic.h index e2d2c508..451b0135 100644 --- a/i386/i386/apic.h +++ b/i386/i386/apic.h @@ -28,8 +28,8 @@ #include <stdint.h> typedef struct ApicReg { - unsigned r; /* the actual register */ - unsigned p[3]; /* pad to the next 128-bit boundary */ + uint32_t r; /* the actual register */ + uint32_t p[3]; /* pad to the next 128-bit boundary */ } ApicReg; typedef struct ApicIoUnit { @@ -37,6 +37,27 @@ typedef struct ApicIoUnit { ApicReg window; } ApicIoUnit; +struct ioapic_route_entry { + uint32_t vector : 8, + delvmode : 3, /* 000=fixed 001=lowest 111=ExtInt */ + destmode : 1, /* 0=physical 1=logical */ + delvstatus : 1, + polarity : 1, /* 0=activehigh 1=activelow */ + irr : 1, + trigger : 1, /* 0=edge 1=level */ + mask : 1, /* 0=enabled 1=disabled */ + reserved1 : 15; + uint32_t reserved2 : 24, + dest : 8; +} __attribute__ ((packed)); + +union ioapic_route_entry_union { + struct { + uint32_t lo; + uint32_t hi; + }; + struct ioapic_route_entry both; +}; typedef struct ApicLocalUnit { ApicReg reserved0; /* 0x000 */ @@ -82,7 +103,8 @@ typedef struct ApicLocalUnit { typedef struct IoApicData { uint8_t apic_id; uint32_t addr; - uint32_t base; + uint32_t gsi_base; + ApicIoUnit *ioapic; } IoApicData; #define APIC_IRQ_OVERRIDE_ACTIVE_LOW 2 @@ -112,6 +134,7 @@ void apic_add_cpu(uint16_t apic_id); void apic_lapic_init(ApicLocalUnit* lapic_ptr); void apic_add_ioapic(struct IoApicData); void apic_add_irq_override(struct IrqOverrideData irq_over); +IrqOverrideData *acpi_get_irq_override(uint8_t gsi); uint16_t apic_get_cpu_apic_id(int kernel_id); volatile ApicLocalUnit* apic_get_lapic(void); struct IoApicData apic_get_ioapic(int kernel_id); @@ -120,6 +143,18 @@ uint8_t apic_get_num_ioapics(void); uint16_t apic_get_current_cpu(void); void apic_print_info(void); int apic_refit_cpulist(void); +void picdisable(void); +void lapic_eoi(void); +void lapic_enable_timer(void); +void ioapic_mask_irqs(void); +void ioapic_toggle(int pin, int mask); +void ioapic_configure(void); + +extern int curr_pic_mask; +extern void intnull(int unit); +extern volatile ApicLocalUnit* lapic; +extern inline void mask_irq (unsigned int irq_nr); +extern inline void unmask_irq (unsigned int irq_nr); #endif @@ -128,6 +163,35 @@ int apic_refit_cpulist(void); #define APIC_IO_REDIR_LOW(int_pin) (0x10+(int_pin)*2) #define APIC_IO_REDIR_HIGH(int_pin) (0x11+(int_pin)*2) +#define IMCR_SELECT 0x22 +#define IMCR_DATA 0x23 +#define MODE_IMCR 0x70 +# define IMCR_USE_PIC 0 +# define IMCR_USE_APIC 1 + +#define LAPIC_ENABLE 0x100 +#define LAPIC_NMI 0x400 +#define LAPIC_DISABLE 0x10000 +#define LAPIC_TIMER_PERIODIC 0x20000 +#define LAPIC_TIMER_DIVIDE_2 0 +#define LAPIC_TIMER_DIVIDE_4 1 +#define LAPIC_TIMER_DIVIDE_8 2 +#define LAPIC_TIMER_DIVIDE_16 3 +#define LAPIC_TIMER_BASEDIV 0x100000 + +#define NINTR 24 +#define IOAPIC_FIXED 0 +#define IOAPIC_PHYSICAL 0 +#define IOAPIC_LOGICAL 1 +#define IOAPIC_NMI 4 +#define IOAPIC_EXTINT 7 +#define IOAPIC_ACTIVE_HIGH 0 +#define IOAPIC_ACTIVE_LOW 1 +#define IOAPIC_EDGE_TRIGGERED 0 +#define IOAPIC_LEVEL_TRIGGERED 1 +#define IOAPIC_MASK_ENABLED 0 +#define IOAPIC_MASK_DISABLED 1 + /* Set or clear a bit in a 255-bit APIC mask register. These registers are spread through eight 32-bit registers. */ #define APIC_SET_MASK_BIT(reg, bit) \ diff --git a/i386/i386/irq.c b/i386/i386/irq.c index 35681191..8f576982 100644 --- a/i386/i386/irq.c +++ b/i386/i386/irq.c @@ -62,6 +62,10 @@ __enable_irq (irq_t irq_nr) struct irqdev irqtab = { "irq", irq_eoi, &main_intr_queue, 0, +#ifdef APIC + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, +#else {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, +#endif }; diff --git a/i386/i386/irq.h b/i386/i386/irq.h index d48a8e92..d629bb9b 100644 --- a/i386/i386/irq.h +++ b/i386/i386/irq.h @@ -15,7 +15,11 @@ #ifndef _I386_IRQ_H #define _I386_IRQ_H +#ifdef APIC +#include <i386/apic.h> +#else #include <i386/pic.h> +#endif typedef unsigned int irq_t; diff --git a/i386/i386/locore.S b/i386/i386/locore.S index bee3630c..4b8c8319 100644 --- a/i386/i386/locore.S +++ b/i386/i386/locore.S @@ -607,6 +607,7 @@ ENTRY(call_continuation) jmp *%eax /* goto continuation */ +/* IOAPIC has 24 interrupts, put spurious in the same array */ #define INTERRUPT(n) \ .data 2 ;\ @@ -621,6 +622,7 @@ ENTRY(call_continuation) .data 2 DATA(int_entry_table) .text +/* Legacy emulated interrupts */ INTERRUPT(0) INTERRUPT(1) INTERRUPT(2) @@ -637,6 +639,17 @@ INTERRUPT(12) INTERRUPT(13) INTERRUPT(14) INTERRUPT(15) +/* PCI interrupts PIRQ A-H */ +INTERRUPT(16) +INTERRUPT(17) +INTERRUPT(18) +INTERRUPT(19) +INTERRUPT(20) +INTERRUPT(21) +INTERRUPT(22) +INTERRUPT(23) +/* Spurious interrupt hack, set irq number to vect number */ +INTERRUPT(255) /* XXX handle NMI - at least print a warning like Linux does. */ diff --git a/i386/i386/pic.h b/i386/i386/pic.h index 6434bf08..0ccf1c9a 100644 --- a/i386/i386/pic.h +++ b/i386/i386/pic.h @@ -96,7 +96,7 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #if defined(AT386) || defined(ATX86_64) -#define PICM_VECTBASE 0x40 +#define PICM_VECTBASE 0x20 #define PICS_VECTBASE PICM_VECTBASE + 0x08 #endif /* defined(AT386) */ @@ -176,6 +176,8 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #define READ_IR_ONRD 0x00 #define READ_IS_ONRD 0x01 +#define PIC_MASK_ZERO 0x00 + #ifndef __ASSEMBLER__ extern void picinit (void); extern int curr_pic_mask; diff --git a/i386/i386/pit.c b/i386/i386/pit.c index 4e3feeec..125036f7 100644 --- a/i386/i386/pit.c +++ b/i386/i386/pit.c @@ -51,7 +51,7 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include <kern/mach_clock.h> #include <i386/ipl.h> -#include <i386/pic.h> +#include <machine/irq.h> #include <i386/pit.h> #include <i386/pio.h> #include <kern/cpu_number.h> @@ -66,14 +66,44 @@ int pit0_mode = PIT_C0|PIT_SQUAREMODE|PIT_READMODE ; unsigned int clknumb = CLKNUM; /* interrupt interval for timer 0 */ void -clkstart(void) +pit_prepare_sleep(int hz) { - unsigned char byte; - unsigned long s; + /* Prepare to sleep for 1/hz seconds */ + int val = 0; + int lsb, msb; + + val = (inb(0x61) & 0xfd) | 0x1; + outb(0x61, val); + outb(0x43, 0xb2); + val = CLKNUM / hz; + lsb = val & 0xff; + msb = val >> 8; + outb(0x42, lsb); + val = inb(0x60); + outb(0x42, msb); + /* Start counting down */ + val = inb(0x61) & 0xfe; + outb(0x61, val); /* Gate low */ + val |= 0x1; + outb(0x61, val); /* Gate high */ +} + +void +pit_sleep(void) +{ + /* Wait until counter reaches zero */ + while ((inb(0x61) & 0x20) == 0); +} + +void +clkstart(void) +{ if (cpu_number() != 0) /* Only one PIT initialization is needed */ return; + unsigned long s; + unsigned char byte; s = sploff(); /* disable interrupts */ diff --git a/i386/i386/pit.h b/i386/i386/pit.h index e004c37c..6be7a9d4 100644 --- a/i386/i386/pit.h +++ b/i386/i386/pit.h @@ -82,5 +82,7 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #endif /* AT386 */ extern void clkstart(void); +extern void pit_prepare_sleep(int hz); +extern void pit_sleep(void); #endif /* _I386_PIT_H_ */ diff --git a/i386/i386at/acpi_parse_apic.c b/i386/i386at/acpi_parse_apic.c index 7855e8a5..e1eeab24 100644 --- a/i386/i386at/acpi_parse_apic.c +++ b/i386/i386at/acpi_parse_apic.c @@ -287,7 +287,7 @@ acpi_get_apic(struct acpi_rsdt *rsdt, int acpi_rsdt_n) /* Search APIC entries in rsdt table. */ for (int i = 0; i < acpi_rsdt_n; i++) { descr_header = (struct acpi_dhdr*) kmem_map_aligned_table(rsdt->entry[i], sizeof(struct acpi_dhdr), - VM_PROT_READ | VM_PROT_WRITE); + VM_PROT_READ); /* Check if the entry contains an APIC. */ check_signature = acpi_check_signature(descr_header->signature, ACPI_APIC_SIG, 4*sizeof(uint8_t)); @@ -333,8 +333,10 @@ acpi_apic_add_ioapic(struct acpi_apic_ioapic *ioapic_entry) /* Fill IOAPIC structure with its main fields */ io_apic.apic_id = ioapic_entry->apic_id; io_apic.addr = ioapic_entry->addr; - io_apic.base = ioapic_entry->base; - + io_apic.gsi_base = ioapic_entry->gsi_base; + io_apic.ioapic = (ApicIoUnit *)kmem_map_aligned_table(ioapic_entry->addr, + sizeof(ApicIoUnit), + VM_PROT_READ | VM_PROT_WRITE); /* Insert IOAPIC in the list. */ apic_add_ioapic(io_apic); } @@ -421,6 +423,8 @@ acpi_apic_parse_table(struct acpi_apic *apic) acpi_apic_add_irq_override(irq_override_entry); break; + default: + break; } /* Get next APIC entry. */ @@ -453,7 +457,7 @@ static int acpi_apic_setup(struct acpi_apic *apic) { int apic_checksum; - ApicLocalUnit* lapic; + ApicLocalUnit* lapic_unit; uint8_t ncpus, nioapics; /* Check the checksum of the APIC */ @@ -463,12 +467,13 @@ acpi_apic_setup(struct acpi_apic *apic) return ACPI_BAD_CHECKSUM; /* map common lapic address */ - lapic = kmem_map_aligned_table(apic->lapic_addr, sizeof(ApicLocalUnit), VM_PROT_READ); + lapic_unit = kmem_map_aligned_table(apic->lapic_addr, sizeof(ApicLocalUnit), + VM_PROT_READ | VM_PROT_WRITE); - if (lapic == NULL) + if (lapic_unit == NULL) return ACPI_NO_LAPIC; - apic_lapic_init(lapic); + apic_lapic_init(lapic_unit); acpi_apic_parse_table(apic); ncpus = apic_get_numcpus(); diff --git a/i386/i386at/acpi_parse_apic.h b/i386/i386at/acpi_parse_apic.h index d071da4f..97a59a2e 100644 --- a/i386/i386at/acpi_parse_apic.h +++ b/i386/i386at/acpi_parse_apic.h @@ -139,7 +139,7 @@ struct acpi_apic_ioapic { uint8_t apic_id; uint8_t reserved; uint32_t addr; - uint32_t base; + uint32_t gsi_base; } __attribute__((__packed__)); /* diff --git a/i386/i386at/idt.h b/i386/i386at/idt.h index 56e6296c..637f8b44 100644 --- a/i386/i386at/idt.h +++ b/i386/i386at/idt.h @@ -24,13 +24,18 @@ #ifndef _I386AT_IDT_ #define _I386AT_IDT_ -/* On a standard PC, we only need 16 interrupt vectors, - because that's all the PIC hardware supports. */ -/* XX But for some reason we program the PIC - to use vectors 0x40-0x4f rather than 0x20-0x2f. Fix. */ -#define IDTSZ (0x20+0x20+0x10) +/* There are 256 interrupt vectors on x86, + * the first 32 are taken by cpu faults */ +#define IDTSZ (0x100) -#define PIC_INT_BASE 0x40 +/* PIC now sits (unused) at 0x20-0x2f */ +#define PIC_INT_BASE 0x20 + +/* IOAPIC sits at 0x30-0x47 */ +#define IOAPIC_INT_BASE 0x30 + +/* IOAPIC spurious interrupt vector set to 0xff */ +#define IOAPIC_SPURIOUS_BASE 0xff #include <i386/idt-gen.h> diff --git a/i386/i386at/int_init.c b/i386/i386at/int_init.c index 43daad8b..3c96c589 100644 --- a/i386/i386at/int_init.c +++ b/i386/i386at/int_init.c @@ -31,9 +31,13 @@ void int_init(void) { int i; - for (i = 0; i < 16; i++) - fill_idt_gate(PIC_INT_BASE + i, + for (i = 0; i < 24; i++) + fill_idt_gate(IOAPIC_INT_BASE + i, int_entry_table[i], KERNEL_CS, ACC_PL_K|ACC_INTR_GATE, 0); + + fill_idt_gate(IOAPIC_SPURIOUS_BASE, + int_entry_table[24], KERNEL_CS, + ACC_PL_K|ACC_INTR_GATE, 0); } diff --git a/i386/i386at/interrupt.S b/i386/i386at/interrupt.S index 23a2e582..512163a6 100644 --- a/i386/i386at/interrupt.S +++ b/i386/i386at/interrupt.S @@ -29,6 +29,8 @@ ENTRY(interrupt) pushl %eax /* save irq number */ movl %eax,%ecx /* copy irq number */ + cmpl $255,%eax /* was this a spurious intr? */ + je 0f /* if so, null handler */ shll $2,%ecx /* irq * 4 */ call spl7 /* set ipl */ movl EXT(iunit)(%ecx),%edx /* get device unit number */ @@ -40,6 +42,7 @@ ENTRY(interrupt) addl $4,%esp /* pop previous ipl */ cli /* XXX no more nested interrupts */ +#ifndef APIC popl %ecx /* restore irq number */ movl $1,%eax @@ -78,4 +81,17 @@ ENTRY(interrupt) outb %al,$(PIC_MASTER_OCW) /* unmask master */ 2: ret +#else + popl %ecx /* restore irq number */ + movl %ecx,%eax /* put irq number in eax */ + call EXT(mask_irq) /* mask irq */ + call EXT(lapic_eoi) /* EOI */ + call EXT(unmask_irq) /* unmask irq */ + ret /* return */ +0: + popl %ecx /* restore irq number */ + + call EXT(lapic_eoi) /* EOI */ + ret /* return */ +#endif END(interrupt) diff --git a/i386/i386at/ioapic.c b/i386/i386at/ioapic.c new file mode 100644 index 00000000..72830653 --- /dev/null +++ b/i386/i386at/ioapic.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2019 Damien Zammit + */ + +#include <sys/types.h> +#include <i386/ipl.h> +#include <machine/irq.h> +#include <i386/fpu.h> +#include <i386/hardclock.h> +#include <i386at/kd.h> +#include <i386at/idt.h> +#include <i386/pio.h> +#include <i386/pit.h> +#include <mach/machine.h> +#include <kern/printf.h> + +uint32_t lapic_timer_val = 0; +uint32_t calibrated_ticks = 0; + +spl_t curr_ipl; +int curr_pic_mask; + +int iunit[NINTR] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23}; + +void (*ivect[NINTR])() = { + /* 00 */ hardclock, /* always */ + /* 01 */ kdintr, /* kdintr, ... */ + /* 02 */ intnull, + /* 03 */ intnull, /* lnpoll, comintr, ... */ + + /* 04 */ intnull, /* comintr, ... */ + /* 05 */ intnull, /* comintr, wtintr, ... */ + /* 06 */ intnull, /* fdintr, ... */ + /* 07 */ intnull, /* qdintr, ... */ + + /* 08 */ intnull, + /* 09 */ intnull, /* ether */ + /* 10 */ intnull, + /* 11 */ intnull, + + /* 12 */ intnull, + /* 13 */ fpintr, /* always */ + /* 14 */ intnull, /* hdintr, ... */ + /* 15 */ intnull, /* ??? */ + + /* 16 */ intnull, /* PIRQA */ + /* 17 */ intnull, /* PIRQB */ + /* 18 */ intnull, /* PIRQC */ + /* 19 */ intnull, /* PIRQD */ + /* 20 */ intnull, /* PIRQE */ + /* 21 */ intnull, /* PIRQF */ + /* 22 */ intnull, /* PIRQG */ + /* 23 */ intnull, /* PIRQH */ +}; + +void +picdisable(void) +{ + asm("cli"); + + /* + ** Disable PIC + */ + outb ( 0xa1, 0xff ); + outb ( 0x21, 0xff ); + + /* + ** Route interrupts through IOAPIC + */ + outb(IMCR_SELECT, MODE_IMCR); + outb(IMCR_DATA, IMCR_USE_APIC); +} + +void +intnull(int unit_dev) +{ + printf("intnull(%d)\n", unit_dev); +} + +static uint32_t +ioapic_read(uint8_t id, uint8_t reg) +{ + volatile ApicIoUnit *ioapic = apic_get_ioapic(id).ioapic; + ioapic->select.r = reg; + return ioapic->window.r; +} + +static void +ioapic_write(uint8_t id, uint8_t reg, uint32_t value) +{ + volatile ApicIoUnit *ioapic = apic_get_ioapic(id).ioapic; + ioapic->select.r = reg; + ioapic->window.r = value; +} + +static struct ioapic_route_entry +ioapic_read_entry(int apic, int pin) +{ + union ioapic_route_entry_union entry; + + entry.lo = ioapic_read(apic, APIC_IO_REDIR_LOW(pin)); + entry.hi = ioapic_read(apic, APIC_IO_REDIR_HIGH(pin)); + + return entry.both; +} + +/* Write the high word first because mask bit is in low word */ +static void +ioapic_write_entry(int apic, int pin, struct ioapic_route_entry e) +{ + union ioapic_route_entry_union entry = {{0, 0}}; + + entry.both = e; + ioapic_write(apic, APIC_IO_REDIR_HIGH(pin), entry.hi); + ioapic_write(apic, APIC_IO_REDIR_LOW(pin), entry.lo); +} + +/* When toggling the interrupt via mask, write low word only */ +static void +ioapic_toggle_entry(int apic, int pin, int mask) +{ + union ioapic_route_entry_union entry; + + entry.both = ioapic_read_entry(apic, pin); + entry.both.mask = mask & 0x1; + ioapic_write(apic, APIC_IO_REDIR_LOW(pin), entry.lo); +} + +#if 0 +static void +global_enable_apic(void) +{ + uint32_t val = 0; + uint32_t msr = 0x1b; + + __asm__ __volatile__("rdmsr" + : "=A" (val) + : "c" (msr)); + if (!(val & (1 << 11))) { + val |= (1 << 11); + + __asm__ __volatile__("wrmsr" + : /* no Outputs */ + : "c" (msr), "A" (val)); + } +} +#endif + +static uint32_t +pit_measure_apic_hz(void) +{ + uint32_t start = 0xffffffff; + + /* Prepare accurate delay for 1/100 seconds */ + pit_prepare_sleep(100); + + /* Set APIC timer */ + lapic->init_count.r = start; + + /* zZz */ + pit_sleep(); + + /* Stop APIC timer */ + lapic->lvt_timer.r = LAPIC_DISABLE; + + return start - lapic->cur_count.r; +} + +void lapic_update_timer(void) +{ + /* Timer decrements until zero and then calls this on every interrupt */ + lapic_timer_val += calibrated_ticks; +} + +void +lapic_enable_timer(void) +{ + spl_t s; + + s = sploff(); + asm("cli"); + + /* Set up counter */ + lapic->init_count.r = calibrated_ticks; + lapic->divider_config.r = LAPIC_TIMER_DIVIDE_16; + + /* Set the timer to interrupt periodically */ + lapic->lvt_timer.r = IOAPIC_INT_BASE | LAPIC_TIMER_PERIODIC; + + /* Some buggy hardware requires this set again */ + lapic->divider_config.r = LAPIC_TIMER_DIVIDE_16; + + /* Unmask the timer irq */ + ioapic_toggle(0, IOAPIC_MASK_ENABLED); + + splon(s); + printf("LAPIC timer configured\n"); +} + +void +ioapic_toggle(int pin, int mask) +{ + int apic = 0; + ioapic_toggle_entry(apic, pin, mask); +} + +void +ioapic_mask_irqs(void) +{ + int i, bitmask = 0x1; + + for (i = 0; i < NINTR; i++, bitmask<<=1) { + if (curr_pic_mask & bitmask) { + ioapic_toggle(i, IOAPIC_MASK_DISABLED); + } else { + ioapic_toggle(i, IOAPIC_MASK_ENABLED); + } + } +} + +void +lapic_eoi(void) +{ + lapic_update_timer(); + lapic->eoi.r = 0; +} + +void +unmask_irq(unsigned int irq) +{ + ioapic_toggle(irq, IOAPIC_MASK_ENABLED); +} + +void +mask_irq(unsigned int irq) +{ + ioapic_toggle(irq, IOAPIC_MASK_DISABLED); +} + +static unsigned int +override_irq(IrqOverrideData *override, union ioapic_route_entry_union *entry) +{ + entry->both.polarity = (override->flags & APIC_IRQ_OVERRIDE_ACTIVE_LOW) ? + IOAPIC_ACTIVE_LOW : IOAPIC_ACTIVE_HIGH; + + entry->both.trigger = (override->flags & APIC_IRQ_OVERRIDE_LEVEL_TRIGGERED) ? + IOAPIC_LEVEL_TRIGGERED : IOAPIC_EDGE_TRIGGERED; + return override->irq; +} + +void +ioapic_configure(void) +{ + /* Assume first IO APIC maps to GSI base 0 */ + int gsi, apic = 0, bsp = 0, pin; + IrqOverrideData *irq_over; + + /* Disable IOAPIC interrupts and set spurious interrupt */ + lapic->spurious_vector.r = IOAPIC_SPURIOUS_BASE; + + union ioapic_route_entry_union entry = {{0, 0}}; + + entry.both.delvmode = IOAPIC_FIXED; + entry.both.destmode = IOAPIC_PHYSICAL; + entry.both.mask = IOAPIC_MASK_DISABLED; + entry.both.dest = apic_get_cpu_apic_id(bsp); + + /* ISA legacy IRQs */ + entry.both.polarity = IOAPIC_ACTIVE_HIGH; + entry.both.trigger = IOAPIC_EDGE_TRIGGERED; + + for (gsi = 0; gsi < 16; gsi++) { + pin = gsi; + entry.both.vector = IOAPIC_INT_BASE + gsi; + + if ((irq_over = acpi_get_irq_override(gsi))) { + pin = override_irq(irq_over, &entry); + } + ioapic_write_entry(apic, pin, entry.both); + } + + /* PCI IRQs PIRQ A-H */ + entry.both.polarity = IOAPIC_ACTIVE_LOW; + entry.both.trigger = IOAPIC_LEVEL_TRIGGERED; + + for (gsi = 16; gsi < 24; gsi++) { + pin = gsi; + entry.both.vector = IOAPIC_INT_BASE + gsi; + if ((irq_over = acpi_get_irq_override(gsi))) { + pin = override_irq(irq_over, &entry); + } + ioapic_write_entry(apic, pin, entry.both); + } + + /* Start the IO APIC receiving interrupts */ + lapic->dest_format.r = 0xffffffff; /* flat model */ + lapic->logical_dest.r = 0x00000000; /* default, but we use physical */ + lapic->lvt_timer.r = LAPIC_DISABLE; + lapic->lvt_performance_monitor.r = LAPIC_NMI; + lapic->lvt_lint0.r = LAPIC_DISABLE; + lapic->lvt_lint1.r = LAPIC_DISABLE; + lapic->task_pri.r = 0; + + //global_enable_apic(); + + /* Enable IOAPIC interrupts */ + lapic->spurious_vector.r |= LAPIC_ENABLE; + + /* Set one-shot timer */ + lapic->divider_config.r = LAPIC_TIMER_DIVIDE_16; + lapic->lvt_timer.r = IOAPIC_INT_BASE; + + /* Measure number of APIC timer ticks in 10ms */ + calibrated_ticks = pit_measure_apic_hz(); + + /* Set up counter later */ + lapic->lvt_timer.r = LAPIC_DISABLE; + printf("IOAPIC 0 configured\n"); +} diff --git a/i386/i386at/model_dep.c b/i386/i386at/model_dep.c index 1e98c5c3..6ad95f64 100644 --- a/i386/i386at/model_dep.c +++ b/i386/i386at/model_dep.c @@ -59,7 +59,6 @@ #include <i386/ldt.h> #include <i386/machspl.h> #include <i386/mp_desc.h> -#include <i386/pic.h> #include <i386/pit.h> #include <i386/pmap.h> #include <i386/proc_reg.h> @@ -75,6 +74,7 @@ #include <i386at/kd.h> #include <i386at/rtc.h> #include <i386at/model_dep.h> +#include <machine/irq.h> #ifdef MACH_XEN #include <xen/console.h> @@ -169,17 +169,18 @@ void machine_init(void) #ifdef MACH_HYP hyp_init(); #else /* MACH_HYP */ -#ifdef LINUX_DEV - /* - * Initialize Linux drivers. - */ - linux_init(); -#endif #if NCPUS > 1 smp_init(); + ioapic_configure(); #endif /* NCPUS > 1 */ +#ifdef LINUX_DEV + /* + * Initialize Linux drivers. + */ + //linux_init(); +#endif /* * Find the devices */ @@ -356,7 +357,11 @@ i386at_init(void) * Initialize the PIC prior to any possible call to an spl. */ #ifndef MACH_HYP +# ifdef APIC + picdisable(); +# else picinit(); +# endif #else /* MACH_HYP */ hyp_intrinit(); #endif /* MACH_HYP */ @@ -682,7 +687,11 @@ timemmap(dev, off, prot) void startrtclock(void) { +#ifdef APIC + lapic_enable_timer(); +#else clkstart(); +#endif } void diff --git a/linux/dev/arch/i386/kernel/irq.c b/linux/dev/arch/i386/kernel/irq.c index 522ed0ac..786376aa 100644 --- a/linux/dev/arch/i386/kernel/irq.c +++ b/linux/dev/arch/i386/kernel/irq.c @@ -29,7 +29,11 @@ #include <kern/assert.h> #include <i386/spl.h> -#include <i386/pic.h> +#ifdef APIC +# include <i386/apic.h> +#else +# include <i386/pic.h> +#endif #include <i386/pit.h> #define MACH_INCLUDE -- 2.30.1