[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH] WIP: ioapic support - linux drivers disabled
From: |
Damien Zammit |
Subject: |
[PATCH] WIP: ioapic support - linux drivers disabled |
Date: |
Sun, 21 Mar 2021 11:48:29 +1100 |
This patch enables the first IOAPIC when --enable-apic is used,
replacing the PIC. It also starts a LAPIC timer, but I'm not sure
if it is correctly being used.
I am posting this patch even though it is unfinished, because
I noticed a strange error that could indicate something is missing from
the rest of SMP support.
...
LAPIC timer configured
TODO: intel_startCPU
TODO: intel_startCPU
TODO: intel_startCPU
{cpu0} ../kern/slab.c:1021: kmem_cache_free_to_slab: Assertion `((unsigned long)
buf + cache->buf_size) <= vm_page_trunc((unsigned long)slab->addr + cache->slab_
size)' failed.Debugger invoked: assertion failure
db{0}> show all tasks
ID TASK NAME [THREADS]
0 f63b5ea0 gnumach [9]
1 f63b5dd0 ext2fs [1]
2 f63b5d00 exec [0]
Is something wrong with the cache in SMP mode?
I ran qemu with -smp 2
---
i386/Makefrag.am | 13 +-
i386/configfrag.ac | 12 ++
i386/i386/apic.c | 13 ++
i386/i386/apic.h | 70 ++++++-
i386/i386/irq.c | 4 +
i386/i386/irq.h | 4 +
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/idt.h | 17 +-
i386/i386at/int_init.c | 8 +-
i386/i386at/interrupt.S | 16 ++
i386/i386at/ioapic.c | 320 +++++++++++++++++++++++++++++++
i386/i386at/model_dep.c | 23 ++-
linux/dev/arch/i386/kernel/irq.c | 6 +-
18 files changed, 549 insertions(+), 35 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..f60276e9 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.
diff --git a/i386/i386/apic.h b/i386/i386/apic.h
index e2d2c508..451b0135 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,7 +103,8 @@ 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
@@ -112,6 +134,7 @@ 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);
@@ -120,6 +143,18 @@ 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 +163,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/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..d629bb9b 100644
--- a/i386/i386/irq.h
+++ b/i386/i386/irq.h
@@ -15,7 +15,11 @@
#ifndef _I386_IRQ_H
#define _I386_IRQ_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 7855e8a5..e1eeab24 100644
--- a/i386/i386at/acpi_parse_apic.c
+++ b/i386/i386at/acpi_parse_apic.c
@@ -287,7 +287,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));
@@ -333,8 +333,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);
}
@@ -421,6 +423,8 @@ acpi_apic_parse_table(struct acpi_apic *apic)
acpi_apic_add_irq_override(irq_override_entry);
break;
+ default:
+ break;
}
/* Get next APIC entry. */
@@ -453,7 +457,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 */
@@ -463,12 +467,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/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..512163a6 100644
--- a/i386/i386at/interrupt.S
+++ b/i386/i386at/interrupt.S
@@ -29,6 +29,8 @@
ENTRY(interrupt)
pushl %eax /* save irq number */
movl %eax,%ecx /* copy irq number */
+ cmpl $255,%eax /* was this a spurious intr? */
+ je 0f /* if so, null handler */
shll $2,%ecx /* irq * 4 */
call spl7 /* set ipl */
movl EXT(iunit)(%ecx),%edx /* get device unit number */
@@ -40,6 +42,7 @@ ENTRY(interrupt)
addl $4,%esp /* pop previous ipl */
cli /* XXX no more nested interrupts */
+#ifndef APIC
popl %ecx /* restore irq number */
movl $1,%eax
@@ -78,4 +81,17 @@ ENTRY(interrupt)
outb %al,$(PIC_MASTER_OCW) /* unmask master */
2:
ret
+#else
+ popl %ecx /* restore irq number */
+ movl %ecx,%eax /* put irq number in eax */
+ call EXT(mask_irq) /* mask irq */
+ call EXT(lapic_eoi) /* EOI */
+ call EXT(unmask_irq) /* unmask irq */
+ ret /* return */
+0:
+ popl %ecx /* restore irq number */
+
+ 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..72830653
--- /dev/null
+++ b/i386/i386at/ioapic.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2019 Damien Zammit
+ */
+
+#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);
+}
+
+#if 0
+static void
+global_enable_apic(void)
+{
+ uint32_t val = 0;
+ uint32_t msr = 0x1b;
+
+ __asm__ __volatile__("rdmsr"
+ : "=A" (val)
+ : "c" (msr));
+ if (!(val & (1 << 11))) {
+ val |= (1 << 11);
+
+ __asm__ __volatile__("wrmsr"
+ : /* no Outputs */
+ : "c" (msr), "A" (val));
+ }
+}
+#endif
+
+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)
+{
+ entry->both.polarity = (override->flags & APIC_IRQ_OVERRIDE_ACTIVE_LOW) ?
+ IOAPIC_ACTIVE_LOW : IOAPIC_ACTIVE_HIGH;
+
+ entry->both.trigger = (override->flags &
APIC_IRQ_OVERRIDE_LEVEL_TRIGGERED) ?
+ IOAPIC_LEVEL_TRIGGERED : IOAPIC_EDGE_TRIGGERED;
+ return override->irq;
+}
+
+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 (gsi = 0; gsi < 16; gsi++) {
+ pin = gsi;
+ entry.both.vector = IOAPIC_INT_BASE + gsi;
+
+ if ((irq_over = acpi_get_irq_override(gsi))) {
+ pin = override_irq(irq_over, &entry);
+ }
+ 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 (gsi = 16; gsi < 24; gsi++) {
+ pin = gsi;
+ entry.both.vector = IOAPIC_INT_BASE + gsi;
+ if ((irq_over = acpi_get_irq_override(gsi))) {
+ pin = override_irq(irq_over, &entry);
+ }
+ 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/model_dep.c b/i386/i386at/model_dep.c
index 1e98c5c3..6ad95f64 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,18 @@ void machine_init(void)
#ifdef MACH_HYP
hyp_init();
#else /* MACH_HYP */
-#ifdef LINUX_DEV
- /*
- * Initialize Linux drivers.
- */
- linux_init();
-#endif
#if NCPUS > 1
smp_init();
+ ioapic_configure();
#endif /* NCPUS > 1 */
+#ifdef LINUX_DEV
+ /*
+ * Initialize Linux drivers.
+ */
+ //linux_init();
+#endif
/*
* Find the devices
*/
@@ -356,7 +357,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 +687,11 @@ timemmap(dev, off, prot)
void
startrtclock(void)
{
+#ifdef APIC
+ lapic_enable_timer();
+#else
clkstart();
+#endif
}
void
diff --git a/linux/dev/arch/i386/kernel/irq.c b/linux/dev/arch/i386/kernel/irq.c
index 522ed0ac..786376aa 100644
--- a/linux/dev/arch/i386/kernel/irq.c
+++ b/linux/dev/arch/i386/kernel/irq.c
@@ -29,7 +29,11 @@
#include <kern/assert.h>
#include <i386/spl.h>
-#include <i386/pic.h>
+#ifdef APIC
+# include <i386/apic.h>
+#else
+# include <i386/pic.h>
+#endif
#include <i386/pit.h>
#define MACH_INCLUDE
--
2.30.1
- [PATCH] WIP: ioapic support - linux drivers disabled,
Damien Zammit <=