From 214866d58ac2bd1060132b250bf391a495110015 Mon Sep 17 00:00:00 2001 From: Damien Zammit Date: Sun, 4 Apr 2021 15:08:12 +1000 Subject: Add ioapic support disabled by default Use --enable-ncpus=x --enable-apic where x > 1 for SMP+APIC support. Use neither for no SMP and old PIC support. Message-Id: <20210404050812.145483-1-damien@zamaudio.com> --- i386/Makefrag.am | 13 +- i386/configfrag.ac | 8 + i386/i386/fpu.c | 2 +- i386/i386/irq.c | 9 +- i386/i386/irq.h | 6 +- i386/i386/locore.S | 15 ++ i386/i386/pic.h | 6 +- i386/i386/pit.c | 41 +++- i386/i386/pit.h | 4 + i386/i386at/autoconf.c | 2 +- i386/i386at/idt.h | 13 +- i386/i386at/int_init.c | 15 +- i386/i386at/interrupt.S | 27 ++- i386/i386at/ioapic.c | 400 +++++++++++++++++++++++++++++++++++++++ i386/i386at/kd_mouse.c | 2 +- i386/i386at/model_dep.c | 21 +- linux/dev/arch/i386/kernel/irq.c | 4 +- x86_64/interrupt.S | 24 ++- 18 files changed, 583 insertions(+), 29 deletions(-) create mode 100644 i386/i386at/ioapic.c diff --git a/i386/Makefrag.am b/i386/Makefrag.am index 73df45f4..679c0a3d 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 @@ -163,10 +162,18 @@ 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 + +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 endif # diff --git a/i386/configfrag.ac b/i386/configfrag.ac index bf4af110..f697e277 100644 --- a/i386/configfrag.ac +++ b/i386/configfrag.ac @@ -92,6 +92,14 @@ if [ x"$enable_lpr" = xyes ]; then] AM_CONDITIONAL([enable_lpr], [false]) [fi] +AC_ARG_ENABLE([apic], + AS_HELP_STRING([--enable-apic], [LAPIC/IOAPIC support])) +[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/fpu.c b/i386/i386/fpu.c index a8459d65..cdfe264b 100644 --- a/i386/i386/fpu.c +++ b/i386/i386/fpu.c @@ -51,7 +51,7 @@ #include #include #include -#include +#include #include #include #include "cpu_number.h" diff --git a/i386/i386/irq.c b/i386/i386/irq.c index 35681191..42921617 100644 --- a/i386/i386/irq.c +++ b/i386/i386/irq.c @@ -29,7 +29,10 @@ extern queue_head_t main_intr_queue; static void irq_eoi (struct irqdev *dev, int id) { - /* TODO EOI(dev->irq[id]) */ +#ifdef APIC + ioapic_irq_eoi (dev->irq[id]); + lapic_eoi (); +#endif } static unsigned int ndisabled_irq[NINTR]; @@ -62,6 +65,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..72bbe57b 100644 --- a/i386/i386/irq.h +++ b/i386/i386/irq.h @@ -15,7 +15,11 @@ #ifndef _I386_IRQ_H #define _I386_IRQ_H -#include +#ifdef APIC +# include +#else +# include +#endif typedef unsigned int irq_t; diff --git a/i386/i386/locore.S b/i386/i386/locore.S index bee3630c..8a1054a6 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 APIC interrupts or PIC interrupts */ INTERRUPT(0) INTERRUPT(1) INTERRUPT(2) @@ -637,6 +639,19 @@ INTERRUPT(12) INTERRUPT(13) INTERRUPT(14) INTERRUPT(15) +#ifdef APIC +/* APIC PCI interrupts PIRQ A-H */ +INTERRUPT(16) +INTERRUPT(17) +INTERRUPT(18) +INTERRUPT(19) +INTERRUPT(20) +INTERRUPT(21) +INTERRUPT(22) +INTERRUPT(23) +/* Spurious interrupt, set irq number to vect number */ +INTERRUPT(255) +#endif /* XXX handle NMI - at least print a warning like Linux does. */ diff --git a/i386/i386/pic.h b/i386/i386/pic.h index b3365ed9..3ded9aba 100644 --- a/i386/i386/pic.h +++ b/i386/i386/pic.h @@ -52,7 +52,9 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #ifndef _I386_PIC_H_ #define _I386_PIC_H_ +#ifndef APIC #define NINTR 0x10 +#endif #define NPICS 0x02 /* @@ -176,7 +178,9 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #define READ_IR_ONRD 0x00 #define READ_IS_ONRD 0x01 -#ifndef __ASSEMBLER__ +#define PIC_MASK_ZERO 0x00 + +#if !defined(__ASSEMBLER__) && !defined(APIC) extern void picinit (void); extern int curr_pic_mask; extern void intnull(int unit); diff --git a/i386/i386/pit.c b/i386/i386/pit.c index 4e3feeec..0ead8c9b 100644 --- a/i386/i386/pit.c +++ b/i386/i386/pit.c @@ -51,7 +51,7 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include -#include +#include #include #include #include @@ -66,14 +66,47 @@ 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(PITAUX_PORT); + val &= ~PITAUX_OUT2; + val |= PITAUX_GATE2; + outb (PITAUX_PORT, val); + outb (PITCTL_PORT, PIT_C2 | PIT_LOADMODE | PIT_RATEMODE); + val = CLKNUM / hz; + lsb = val & 0xff; + msb = val >> 8; + outb (PITCTR2_PORT, lsb); + val = inb(POST_PORT); /* ~1us i/o delay */ + outb (PITCTR2_PORT, msb); + /* Start counting down */ + val = inb(PITAUX_PORT); + val &= ~PITAUX_GATE2; + outb (PITAUX_PORT, val); /* Gate low */ + val |= PITAUX_GATE2; + outb (PITAUX_PORT, val); /* Gate high */ +} + +void +pit_sleep(void) +{ + /* Wait until counter reaches zero */ + while ((inb(PITAUX_PORT) & PITAUX_VAL) == 0); +} + +void +clkstart(void) +{ if (cpu_number() != 0) /* Only one PIT initialization is needed */ return; + unsigned char byte; + unsigned long s; s = sploff(); /* disable interrupts */ diff --git a/i386/i386/pit.h b/i386/i386/pit.h index cf0b74cc..bac4e985 100644 --- a/i386/i386/pit.h +++ b/i386/i386/pit.h @@ -78,6 +78,8 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* Used for Timer 2. */ #define PIT_C2 0x80 /* select counter 2 */ +#define POST_PORT 0x80 /* used for tiny i/o delay */ + /* * Clock speed for the timer in hz divided by the constant HZ * (defined in param.h) @@ -87,5 +89,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/autoconf.c b/i386/i386at/autoconf.c index 151e3fd2..0b1251f5 100644 --- a/i386/i386at/autoconf.c +++ b/i386/i386at/autoconf.c @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include diff --git a/i386/i386at/idt.h b/i386/i386at/idt.h index fd2f62d8..ac065aef 100644 --- a/i386/i386at/idt.h +++ b/i386/i386at/idt.h @@ -24,12 +24,19 @@ #ifndef _I386AT_IDT_ #define _I386AT_IDT_ -/* On a standard PC, we only need 16 interrupt vectors, - because that's all the PIC hardware supports. */ -#define IDTSZ (0x20+0x10) +/* There are 256 interrupt vectors on x86, + * the first 32 are taken by cpu faults */ +#define IDTSZ (0x100) +/* PIC sits 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 #ifndef __ASSEMBLER__ diff --git a/i386/i386at/int_init.c b/i386/i386at/int_init.c index 43daad8b..6da627dd 100644 --- a/i386/i386at/int_init.c +++ b/i386/i386at/int_init.c @@ -30,10 +30,21 @@ extern vm_offset_t int_entry_table[]; void int_init(void) { int i; - - for (i = 0; i < 16; i++) +#ifndef APIC + for (i = 0; i < 16; i++) { fill_idt_gate(PIC_INT_BASE + i, int_entry_table[i], KERNEL_CS, ACC_PL_K|ACC_INTR_GATE, 0); + } +#else + 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); +#endif } diff --git a/i386/i386at/interrupt.S b/i386/i386at/interrupt.S index 23a2e582..da0eb044 100644 --- a/i386/i386at/interrupt.S +++ b/i386/i386at/interrupt.S @@ -16,7 +16,11 @@ #include #include -#include +#ifdef APIC +# include +#else +# include +#endif #include #define READ_ISR (OCW_TEMPLATE|READ_NEXT_RD|READ_IS_ONRD) @@ -27,6 +31,10 @@ * On entry, %eax contains the irq number. */ ENTRY(interrupt) +#ifdef APIC + cmpl $255,%eax /* was this a spurious intr? */ + je _null_eoi /* if so, null eoi handler */ +#endif pushl %eax /* save irq number */ movl %eax,%ecx /* copy irq number */ shll $2,%ecx /* irq * 4 */ @@ -38,10 +46,9 @@ ENTRY(interrupt) addl $4,%esp /* pop unit number */ call splx_cli /* restore previous ipl */ addl $4,%esp /* pop previous ipl */ - cli /* XXX no more nested interrupts */ popl %ecx /* restore irq number */ - +#ifndef APIC movl $1,%eax shll %cl,%eax /* get corresponding IRQ mask */ orl EXT(curr_pic_mask),%eax /* add current mask */ @@ -78,4 +85,18 @@ ENTRY(interrupt) outb %al,$(PIC_MASTER_OCW) /* unmask master */ 2: ret +#else + cmpl $16,%ecx /* was this a low ISA intr? */ + jl _isa_eoi /* no, must be PCI */ + ret /* NB: let irq_acknowledge handle pci EOI */ +_isa_eoi: + pushl %ecx /* push irq number */ + call EXT(ioapic_irq_eoi) /* ioapic irq specific EOI */ + addl $4,%esp /* pop irq number */ + call EXT(lapic_eoi) /* lapic broadcast EOI */ + ret +_null_eoi: + call EXT(lapic_eoi) /* lapic broadcast EOI */ + ret +#endif END(interrupt) diff --git a/i386/i386at/ioapic.c b/i386/i386at/ioapic.c new file mode 100644 index 00000000..016bd941 --- /dev/null +++ b/i386/i386at/ioapic.c @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2019 Free Software Foundation, Inc. + * + * This file is part of GNU Mach. + * + * GNU Mach is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Mach is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* only for macros */ +#include +#include + +static int timer_gsi; +int timer_pin; + +uint32_t lapic_timer_val = 0; +uint32_t calibrated_ticks = 0; + +spl_t curr_ipl; + +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 */ intnull, /* install timer later */ + /* 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 ( PIC_SLAVE_OCW, PICS_MASK ); + outb ( PIC_MASTER_OCW, PICM_MASK ); + + /* + ** 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); +} + +static void +cpu_rdmsr(uint32_t msr, uint32_t *lo, uint32_t *hi) +{ + __asm__ __volatile__("rdmsr" : "=a"(*lo), "=d"(*hi) : "c"(msr)); +} + +static void +cpu_wrmsr(uint32_t msr, uint32_t lo, uint32_t hi) +{ + __asm__ __volatile__("wrmsr" : : "a"(lo), "d"(hi), "c"(msr)); +} + +static void +global_enable_apic(void) +{ + uint32_t lo = 0; + uint32_t hi = 0; + uint32_t msr = 0x1b; + + cpu_rdmsr(msr, &lo, &hi); + + if (!(lo & (1 << 11))) { + lo |= (1 << 11); + cpu_wrmsr(msr, lo, hi); + } +} + +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 on remapped timer GSI */ + lapic->lvt_timer.r = (IOAPIC_INT_BASE + timer_gsi) | LAPIC_TIMER_PERIODIC; + + /* Some buggy hardware requires this set again */ + lapic->divider_config.r = LAPIC_TIMER_DIVIDE_16; + + /* Unmask the remapped timer pin and pin 0 always */ + ioapic_toggle(0, IOAPIC_MASK_ENABLED); + ioapic_toggle(timer_pin, 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 +lapic_eoi(void) +{ + lapic->eoi.r = 0; +} + +void +ioapic_irq_eoi(int pin) +{ + int apic = 0; + union ioapic_route_entry_union oldentry, entry; + + /* Workaround for old IOAPICs with no specific EOI */ + + /* Mask the pin and change to edge triggered */ + oldentry.both = entry.both = ioapic_read_entry(apic, pin); + entry.both.mask = IOAPIC_MASK_DISABLED; + entry.both.trigger = IOAPIC_EDGE_TRIGGERED; + ioapic_write_entry(apic, pin, entry.both); + + /* Restore level entry */ + ioapic_write_entry(apic, pin, oldentry.both); +} + +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) +{ + if (override->flags & APIC_IRQ_OVERRIDE_TRIGGER_MASK) { + entry->both.trigger = (override->flags & APIC_IRQ_OVERRIDE_LEVEL_TRIGGERED) ? + IOAPIC_LEVEL_TRIGGERED : IOAPIC_EDGE_TRIGGERED; + } else { + if (override->bus == 0) { + /* ISA is edge-triggered by default */ + entry->both.trigger = IOAPIC_EDGE_TRIGGERED; + } else { + entry->both.trigger = IOAPIC_LEVEL_TRIGGERED; + } + } + + if (override->flags & APIC_IRQ_OVERRIDE_POLARITY_MASK) { + entry->both.polarity = (override->flags & APIC_IRQ_OVERRIDE_ACTIVE_LOW) ? + IOAPIC_ACTIVE_LOW : IOAPIC_ACTIVE_HIGH; + } else { + if (override->bus == 0) { + /* EISA is active-low for level-triggered interrupts */ + if (entry->both.trigger == IOAPIC_LEVEL_TRIGGERED) { + entry->both.polarity = IOAPIC_ACTIVE_LOW; + } else { + entry->both.polarity = IOAPIC_ACTIVE_HIGH; + } + } + } + printf("IRQ override: pin=%d gsi=%d trigger=%s polarity=%s\n", + override->irq, override->gsi, + entry->both.trigger == IOAPIC_LEVEL_TRIGGERED ? "LEVEL" : "EDGE", + entry->both.polarity == IOAPIC_ACTIVE_LOW ? "LOW" : "HIGH"); + + return override->gsi; +} + +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}}; + union ioapic_route_entry_union timer_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); + + for (pin = 0; pin < 16; pin++) { + gsi = pin; + + /* ISA legacy IRQs */ + entry.both.trigger = IOAPIC_EDGE_TRIGGERED; + entry.both.polarity = IOAPIC_ACTIVE_HIGH; + + if ((irq_over = acpi_get_irq_override(pin))) { + gsi = override_irq(irq_over, &entry); + } + entry.both.vector = IOAPIC_INT_BASE + gsi; + ioapic_write_entry(apic, pin, entry.both); + + /* Timer workaround for x86 */ + if (pin == 0) { + /* Save timer info */ + timer_gsi = gsi; + timer_entry = entry; + } else { + /* Get the actual timer pin by assuming that the pin + * with duplicated gsi from pin 0 maps to the timer pin */ + if (gsi == timer_gsi) { + timer_pin = pin; + /* Remap pin 0 interrupt vector to GSI base + * so we don't duplicate vectors */ + timer_entry.both.vector = IOAPIC_INT_BASE; + ioapic_write_entry(apic, 0, timer_entry.both); + } + } + } + + for (pin = 16; pin < 24; pin++) { + gsi = pin; + + /* PCI IRQs PIRQ A-H */ + entry.both.trigger = IOAPIC_LEVEL_TRIGGERED; + entry.both.polarity = IOAPIC_ACTIVE_LOW; + + if ((irq_over = acpi_get_irq_override(pin))) { + gsi = override_irq(irq_over, &entry); + } + entry.both.vector = IOAPIC_INT_BASE + gsi; + 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 + timer_gsi; + + /* Measure number of APIC timer ticks in 10ms */ + calibrated_ticks = pit_measure_apic_hz(); + + /* Set up counter later */ + lapic->lvt_timer.r = LAPIC_DISABLE; + + /* Install clock interrupt handler on both remapped timer pin and pin 0 + * since nobody knows how all x86 timers are wired up */ + ivect[0] = hardclock; + ivect[timer_pin] = hardclock; + + printf("IOAPIC 0 configured\n"); +} diff --git a/i386/i386at/kd_mouse.c b/i386/i386at/kd_mouse.c index 2995587c..4b883ba8 100644 --- a/i386/i386at/kd_mouse.c +++ b/i386/i386at/kd_mouse.c @@ -72,7 +72,7 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include -#include +#include #include #include #include diff --git a/i386/i386at/model_dep.c b/i386/i386at/model_dep.c index 34045dc5..f83d24cb 100644 --- a/i386/i386at/model_dep.c +++ b/i386/i386at/model_dep.c @@ -59,7 +59,6 @@ #include #include #include -#include #include #include #include @@ -75,6 +74,7 @@ #include #include #include +#include #ifdef MACH_XEN #include @@ -169,17 +169,20 @@ void machine_init(void) #ifdef MACH_HYP hyp_init(); #else /* MACH_HYP */ + +#if (NCPUS > 1) && defined(APIC) + smp_init(); + ioapic_configure(); + lapic_enable_timer(); + unmask_irq(1); +#endif /* NCPUS > 1 */ + #ifdef LINUX_DEV /* * Initialize Linux drivers. */ linux_init(); #endif - -#if NCPUS > 1 - smp_init(); -#endif /* NCPUS > 1 */ - /* * Find the devices */ @@ -356,7 +359,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 */ @@ -678,7 +685,9 @@ timemmap(dev, off, prot) void startrtclock(void) { +#ifndef APIC clkstart(); +#endif } void diff --git a/linux/dev/arch/i386/kernel/irq.c b/linux/dev/arch/i386/kernel/irq.c index b6729c12..656c1470 100644 --- a/linux/dev/arch/i386/kernel/irq.c +++ b/linux/dev/arch/i386/kernel/irq.c @@ -714,13 +714,15 @@ init_IRQ (void) */ (void) splhigh (); +#ifndef APIC /* * Program counter 0 of 8253 to interrupt hz times per second. */ outb_p (PIT_C0 | PIT_SQUAREMODE | PIT_READMODE, PITCTL_PORT); outb_p (latch & 0xff, PITCTR0_PORT); outb (latch >> 8, PITCTR0_PORT); - +#endif + /* * Install our clock interrupt handler. */ diff --git a/x86_64/interrupt.S b/x86_64/interrupt.S index e634e34b..779eae67 100644 --- a/x86_64/interrupt.S +++ b/x86_64/interrupt.S @@ -16,7 +16,11 @@ #include #include -#include +#ifdef APIC +# include +#else +# include +#endif #include #define READ_ISR (OCW_TEMPLATE|READ_NEXT_RD|READ_IS_ONRD) @@ -27,6 +31,10 @@ * On entry, %rax contains the irq number. */ ENTRY(interrupt) +#ifdef APIC + cmpl $255,%eax /* was this a spurious intr? */ + je _null_eoi /* if so, null eoi handler */ +#endif pushq %rax /* save irq number */ call spl7 /* set ipl */ pushq %rax /* save previous ipl */ @@ -45,6 +53,7 @@ ENTRY(interrupt) cli /* XXX no more nested interrupts */ popq %rcx /* restore irq number */ +#ifndef APIC movl $1,%eax shll %cl,%eax /* get corresponding IRQ mask */ orl EXT(curr_pic_mask),%eax /* add current mask */ @@ -81,4 +90,17 @@ ENTRY(interrupt) outb %al,$(PIC_MASTER_OCW) /* unmask master */ 2: ret +#else + cmpl $16,%ecx /* was this a low ISA intr? */ + jl _isa_eoi /* no, must be PCI */ + ret /* NB: let irq_acknowledge handle pci EOI */ +_isa_eoi: + movl %ecx,%edi /* load irq number as 1st arg */ + call EXT(ioapic_irq_eoi) /* ioapic irq specific EOI */ + call EXT(lapic_eoi) /* lapic broadcast EOI */ + ret +_null_eoi: + call EXT(lapic_eoi) /* lapic broadcast EOI */ + ret +#endif END(interrupt) -- cgit v1.2.3