>
> +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");
> +}
A simple question: why is the lapic timer implemented in the ioapic.c file?
Couldn't be better to move it to a new lapic file?
Excuse me the inconvenience.
El vie, 26 mar 2021 a las 10:50, Damien Zammit (<[email protected]>)
escribió:
> 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
>
>
>