bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 5/5] ioapic interrupts and lapic timer support


From: Damien Zammit
Subject: [PATCH 5/5] ioapic interrupts and lapic timer support
Date: Fri, 26 Mar 2021 20:48:50 +1100

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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]