Defaults to --enable-apic To build with SMP and APIC support, use for example:
--enable-ncpus=4 Otherwise to build with PIC only and no SMP: --enable-apic=no --- i386/Makefrag.am | 13 +- i386/configfrag.ac | 12 ++ i386/i386/apic.c | 34 ++- i386/i386/apic.h | 74 ++++++- i386/i386/fpu.c | 2 +- i386/i386/irq.c | 4 + i386/i386/irq.h | 6 +- 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/autoconf.c | 2 +- i386/i386at/idt.h | 17 +- i386/i386at/int_init.c | 8 +- i386/i386at/interrupt.S | 16 +- i386/i386at/ioapic.c | 345 +++++++++++++++++++++++++++++++ i386/i386at/kd_mouse.c | 2 +- i386/i386at/model_dep.c | 21 +- linux/dev/arch/i386/kernel/irq.c | 26 +-- 21 files changed, 594 insertions(+), 66 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..099f80a7 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. @@ -118,16 +131,14 @@ apic_get_lapic(void) /* * apic_get_ioapic: returns the IOAPIC identified by its kernel ID. * Receives as input the IOAPIC's Kernel ID. - * Returns a ioapic_data structure with the IOAPIC's data. + * Returns a ioapic_data structure pointer with the IOAPIC's data. */ -struct IoApicData +struct IoApicData * apic_get_ioapic(int kernel_id) { - IoApicData io_apic = {}; - if (kernel_id < MAX_IOAPICS) - return apic_data.ioapic_list[kernel_id]; - return io_apic; + return &apic_data.ioapic_list[kernel_id]; + return (struct IoApicData *)0; } /* apic_get_numcpus: returns the current number of cpus. */ @@ -204,18 +215,21 @@ void apic_print_info(void) uint16_t lapic_id; uint16_t ioapic_id; - IoApicData ioapic; + IoApicData *ioapic; printf("CPUS:\n"); for (i = 0; i < ncpus; i++) { lapic_id = apic_get_cpu_apic_id(i); - printf(" CPU %d - APIC ID %x\n", i, lapic_id); + printf(" CPU %d - APIC ID %x - addr=0x%p\n", i, lapic_id, apic_get_lapic()); } printf("IOAPICS:\n"); for (i = 0; i < nioapics; i++) { ioapic = apic_get_ioapic(i); - ioapic_id = ioapic.apic_id; - printf(" IOAPIC %d - APIC ID %x\n", i, ioapic_id); + if (!ioapic) { + printf("ERROR: invalid IOAPIC ID %x\n", i); + } + ioapic_id = ioapic->apic_id; + printf(" IOAPIC %d - APIC ID %x - addr=0x%p\n", i, ioapic_id, ioapic->ioapic); } } diff --git a/i386/i386/apic.h b/i386/i386/apic.h index e2d2c508..f62f2ef9 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,11 +103,14 @@ 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 +#define APIC_IRQ_OVERRIDE_POLARITY_MASK 1 #define APIC_IRQ_OVERRIDE_LEVEL_TRIGGERED 8 +#define APIC_IRQ_OVERRIDE_TRIGGER_MASK 4 typedef struct IrqOverrideData { uint8_t bus; @@ -112,14 +136,27 @@ 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); +struct IoApicData *apic_get_ioapic(int kernel_id); uint8_t apic_get_numcpus(void); 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 +165,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/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..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..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..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 9dbc6825..449dba71 100644 --- a/i386/i386at/acpi_parse_apic.c +++ b/i386/i386at/acpi_parse_apic.c @@ -280,7 +280,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)); @@ -326,8 +326,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); } @@ -414,6 +416,8 @@ acpi_apic_parse_table(struct acpi_apic *apic) acpi_apic_add_irq_override(irq_override_entry); break; + default: + break; } /* Get next APIC entry. */ @@ -446,7 +450,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 */ @@ -456,12 +460,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/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 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..50e0391d 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) @@ -29,6 +33,8 @@ ENTRY(interrupt) pushl %eax /* save irq number */ movl %eax,%ecx /* copy irq number */ + cmpl $255,%eax /* was this a spurious intr? */ + je 2f /* if so, null handler */ shll $2,%ecx /* irq * 4 */ call spl7 /* set ipl */ movl EXT(iunit)(%ecx),%edx /* get device unit number */ @@ -38,10 +44,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 +83,9 @@ ENTRY(interrupt) outb %al,$(PIC_MASTER_OCW) /* unmask master */ 2: ret +#else +2: + 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..b13eb1b3 --- /dev/null +++ b/i386/i386at/ioapic.c @@ -0,0 +1,345 @@ +/* + * 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 <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); +} + +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 */ + 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) +{ + if (override->flags & APIC_IRQ_OVERRIDE_POLARITY_MASK) { + entry->both.polarity = (override->flags & APIC_IRQ_OVERRIDE_ACTIVE_LOW) ? + IOAPIC_ACTIVE_LOW : IOAPIC_ACTIVE_HIGH; + } + if (override->flags & APIC_IRQ_OVERRIDE_TRIGGER_MASK) { + entry->both.trigger = (override->flags & APIC_IRQ_OVERRIDE_LEVEL_TRIGGERED) ? + IOAPIC_LEVEL_TRIGGERED : IOAPIC_EDGE_TRIGGERED; + } + 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}}; + + 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 (pin = 0; pin < 16; pin++) { + gsi = pin; + 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); + } + + /* PCI IRQs PIRQ A-H */ + entry.both.polarity = IOAPIC_ACTIVE_LOW; + entry.both.trigger = IOAPIC_LEVEL_TRIGGERED; + + for (pin = 16; pin < 24; pin++) { + gsi = pin; + 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; + + /* 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/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 1e98c5c3..17d0638d 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 */ @@ -682,7 +689,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 06889e58..656c1470 100644 --- a/linux/dev/arch/i386/kernel/irq.c +++ b/linux/dev/arch/i386/kernel/irq.c @@ -29,7 +29,7 @@ #include <kern/assert.h> #include <i386/spl.h> -#include <i386/pic.h> +#include <i386/irq.h> #include <i386/pit.h> #define MACH_INCLUDE @@ -84,13 +84,7 @@ struct linux_action user_intr_t *user_intr; }; -static struct linux_action *irq_action[16] = -{ - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL -}; +static struct linux_action *irq_action[NINTR] = {0}; /* * Generic interrupt handler for Linux devices. @@ -232,7 +226,7 @@ install_user_intr_handler (struct irqdev *dev, int id, unsigned long flags, unsigned int irq = dev->irq[id]; - assert (irq < 16); + assert (irq < NINTR); /* Test whether the irq handler has been set */ // TODO I need to protect the array when iterating it. @@ -279,7 +273,7 @@ request_irq (unsigned int irq, void (*handler) (int, void *, struct pt_regs *), struct linux_action *action; int retval; - assert (irq < 16); + assert (irq < NINTR); if (!handler) return -EINVAL; @@ -315,7 +309,7 @@ free_irq (unsigned int irq, void *dev_id) struct linux_action *action, **p; unsigned long flags; - if (irq > 15) + if (irq >= NINTR) panic ("free_irq: bad irq number"); for (p = irq_action + irq; (action = *p) != NULL; p = &action->next) @@ -354,7 +348,7 @@ probe_irq_on (void) /* * Allocate all available IRQs. */ - for (i = 15; i > 0; i--) + for (i = NINTR - 1; i > 0; i--) { if (!irq_action[i] && ivect[i] == intnull) { @@ -387,7 +381,7 @@ probe_irq_off (unsigned long irqs) /* * Disable unnecessary IRQs. */ - for (i = 15; i > 0; i--) + for (i = NINTR - 1; i > 0; i--) { if (!irq_action[i] && ivect[i] == intnull) { @@ -427,7 +421,7 @@ reserve_mach_irqs (void) { unsigned int i; - for (i = 0; i < 16; i++) + for (i = 0; i < NINTR; i++) { if (ivect[i] != intnull) /* This dummy action does not specify SA_SHIRQ, so @@ -720,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. */ -- 2.30.1