[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH] Add ioapic support disabled by default
From: |
Samuel Thibault |
Subject: |
Re: [PATCH] Add ioapic support disabled by default |
Date: |
Sun, 4 Apr 2021 16:55:12 +0200 |
User-agent: |
NeoMutt/20170609 (1.8.3) |
Applied, thanks!
Damien Zammit, le dim. 04 avril 2021 15:08:12 +1000, a ecrit:
> Use --enable-ncpus=x --enable-apic where x > 1 for SMP+APIC support.
>
> Use neither for no SMP and old PIC support.
> ---
> 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 <i386/thread.h>
> #include <i386/fpu.h>
> #include <i386/pio.h>
> -#include <i386/pic.h>
> +#include <i386/irq.h>
> #include <i386/locore.h>
> #include <i386/trap.h>
> #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 <i386/pic.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..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 <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,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 <kern/printf.h>
> #include <mach/std_types.h>
> -#include <i386/pic.h>
> +#include <i386/irq.h>
> #include <i386/ipl.h>
> #include <chips/busses.h>
>
> 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 <i386/idt-gen.h>
>
> #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 <mach/machine/asm.h>
>
> #include <i386/ipl.h>
> -#include <i386/pic.h>
> +#ifdef APIC
> +# include <i386/apic.h>
> +#else
> +# include <i386/pic.h>
> +#endif
> #include <i386/i386asm.h>
>
> #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 <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 <i386/pic.h> /* only for macros */
> +#include <mach/machine.h>
> +#include <kern/printf.h>
> +
> +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 <device/io_req.h>
> #include <device/subrs.h>
> #include <i386/ipl.h>
> -#include <i386/pic.h>
> +#include <i386/irq.h>
> #include <i386/pio.h>
> #include <chips/busses.h>
> #include <i386at/com.h>
> 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 <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,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 <mach/machine/asm.h>
>
> #include <i386/ipl.h>
> -#include <i386/pic.h>
> +#ifdef APIC
> +# include <i386/apic.h>
> +#else
> +# include <i386/pic.h>
> +#endif
> #include <i386/i386asm.h>
>
> #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)
> --
> 2.30.1
>
>