#ifndef _MINIX_CPUFEATURE_H
#define _MINIX_CPUFEATURE_H 1
-#define _CPUF_I386_PSE 1 /* Page Size Extension */
-#define _CPUF_I386_PGE 2 /* Page Global Enable */
+#define _CPUF_I386_PSE 1 /* Page Size Extension */
+#define _CPUF_I386_PGE 2 /* Page Global Enable */
+#define _CPUF_I386_APIC_ON_CHIP 3 /* APIC is present on the chip */
+#define _CPUF_I386_TSC 4 /* Timestamp counter present */
_PROTOTYPE(int _cpufeature, (int featureno));
#define I386_VM_PFE_U 0x04 /* CPU in user mode (otherwise supervisor) */
/* CPUID flags */
-#define CPUID1_EDX_PSE (1L << 3) /* Page Size Extension */
-#define CPUID1_EDX_PGE (1L << 13) /* Page Global (bit) Enable */
+#define CPUID1_EDX_PSE (1L << 3) /* Page Size Extension */
+#define CPUID1_EDX_PGE (1L << 13) /* Page Global (bit) Enable */
+#define CPUID1_EDX_APIC_ON_CHIP (1L << 9) /* APIC is present on the chip */
+#define CPUID1_EDX_TSC (1L << 4) /* Timestamp counter present */
#endif /* __SYS_VM_386_H__ */
memory.o \
mpx386.o \
protect.o \
- system.o
+ system.o \
+ apic.o \
+ apic_asm.o
CPPFLAGS=-Iinclude
CFLAGS=$(CPPFLAGS) -Wall $(CPROFILE)
gas2ack $@.tmp $@.s
$(CC) $(CFLAGS) -c -o $@ $@.s
+apic_asm.o: apic_asm.S
+ $(CC) $(CFLAGS) -E -D__ASSEMBLY__ -o $@.tmp $<
+ gas2ack $@.tmp $@.s
+ $(CC) $(CFLAGS) -c -o $@ $@.s
+
.c.o:
$(CC) $(CFLAGS) -c -o $@ $<
--- /dev/null
+/*
+ * APIC handling routines. APIC is a requirement for SMP
+ */
+#include "../../kernel.h"
+
+#include <unistd.h>
+#include <ibm/cmos.h>
+#include <ibm/bios.h>
+#include <minix/portio.h>
+
+#include <minix/syslib.h>
+
+#include "../../proc.h"
+#include "../..//glo.h"
+#include "proto.h"
+
+#include <minix/u64.h>
+
+#include "apic.h"
+#include "apic_asm.h"
+#include "../../clock.h"
+#include "glo.h"
+
+#define APIC_ENABLE 0x100
+#define APIC_FOCUS (~(1 << 9))
+#define APIC_SIV 0xFF
+
+#define APIC_TDCR_2 0x00
+#define APIC_TDCR_4 0x01
+#define APIC_TDCR_8 0x02
+#define APIC_TDCR_16 0x03
+#define APIC_TDCR_32 0x08
+#define APIC_TDCR_64 0x09
+#define APIC_TDCR_128 0x0a
+#define APIC_TDCR_1 0x0b
+
+#define IS_SET(mask) (mask)
+#define IS_CLEAR(mask) 0
+
+#define APIC_LVTT_VECTOR_MASK 0x000000FF
+#define APIC_LVTT_DS_PENDING (1 << 12)
+#define APIC_LVTT_MASK (1 << 16)
+#define APIC_LVTT_TM (1 << 17)
+
+#define APIC_LVT_IIPP_MASK 0x00002000
+#define APIC_LVT_IIPP_AH 0x00002000
+#define APIC_LVT_IIPP_AL 0x00000000
+
+#define APIC_LVT_TM_ONESHOT IS_CLEAR(APIC_LVTT_TM)
+#define APIC_LVT_TM_PERIODIC IS_SET(APIC_LVTT_TM)
+
+#define APIC_SVR_SWEN 0x00000100
+#define APIC_SVR_FOCUS 0x00000200
+
+#define IOAPIC_REGSEL 0x0
+#define IOAPIC_RW 0x10
+
+#define APIC_ICR_DM_MASK 0x00000700
+#define APIC_ICR_VECTOR APIC_LVTT_VECTOR_MASK
+#define APIC_ICR_DM_FIXED (0 << 8)
+#define APIC_ICR_DM_LOWEST_PRIORITY (1 << 8)
+#define APIC_ICR_DM_SMI (2 << 8)
+#define APIC_ICR_DM_RESERVED (3 << 8)
+#define APIC_ICR_DM_NMI (4 << 8)
+#define APIC_ICR_DM_INIT (5 << 8)
+#define APIC_ICR_DM_STARTUP (6 << 8)
+#define APIC_ICR_DM_EXTINT (7 << 8)
+
+#define APIC_ICR_DM_PHYSICAL (0 << 11)
+#define APIC_ICR_DM_LOGICAL (1 << 11)
+
+#define APIC_ICR_DELIVERY_PENDING (1 << 12)
+
+#define APIC_ICR_INT_POLARITY (1 << 13)
+#define APIC_ICR_INTPOL_LOW IS_SET(APIC_ICR_INT_POLARITY)
+#define APIC_ICR_INTPOL_HIGH IS_CLEAR(APIC_ICR_INT_POLARITY)
+
+#define APIC_ICR_LEVEL_ASSERT (1 << 14)
+#define APIC_ICR_LEVEL_DEASSERT (0 << 14)
+
+#define APIC_ICR_TRIGGER (1 << 15)
+#define APIC_ICR_TM_LEVEL IS_CLEAR(APIC_ICR_TRIGGER)
+#define APIC_ICR_TM_EDGE IS_CLEAR(APIC_ICR_TRIGGER)
+
+#define APIC_ICR_INT_MASK (1 << 16)
+
+#define APIC_ICR_DEST_FIELD (0 << 18)
+#define APIC_ICR_DEST_SELF (1 << 18)
+#define APIC_ICR_DEST_ALL (2 << 18)
+#define APIC_ICR_DEST_ALL_BUT_SELF (3 << 18)
+
+#define IA32_APIC_BASE 0x1b
+#define IA32_APIC_BASE_ENABLE_BIT 11
+
+/* currently only 2 interrupt priority levels are used */
+#define SPL0 0x0
+#define SPLHI 0xF
+
+/*
+ * to make APIC work if SMP is not configured, we need to set the maximal number
+ * fo CPUS to 1, cpuid to return 0 and the current cpu is always BSP
+ */
+#define CONFIG_MAX_CPUS 1
+#define cpu_is_bsp(x) 1
+
+PRIVATE int cpuid(void)
+{
+ return 0;
+}
+
+#define lapic_write_icr1(val) lapic_write(LAPIC_ICR1, val)
+#define lapic_write_icr2(val) lapic_write(LAPIC_ICR2, val)
+
+#define lapic_read_icr1(x) lapic_read(LAPIC_ICR1)
+#define lapic_read_icr2(x) lapic_read(LAPIC_ICR2)
+
+#define VERBOSE_APIC(x) x
+
+PUBLIC int reboot_type;
+PUBLIC int ioapic_enabled;
+PUBLIC u32_t ioapic_id_mask[8], lapic_id_mask[8];
+PUBLIC u32_t lapic_addr_vaddr;
+PUBLIC u32_t lapic_addr;
+PUBLIC u32_t lapic_eoi_addr;
+PUBLIC u32_t lapic_taskpri_addr;
+PUBLIC int bsp_lapic_id;
+
+PRIVATE volatile int probe_ticks;
+PRIVATE u64_t tsc0, tsc1;
+PRIVATE u32_t lapic_tctr0, lapic_tctr1;
+
+u8_t apicid2cpuid[MAX_NR_APICIDS+1];
+unsigned apic_imcrp;
+unsigned nioapics;
+unsigned nbuses;
+unsigned nintrs;
+unsigned nlints;
+
+/*
+ * FIXME this should be a cpulocal variable but there are some problems with
+ * arch specific cpulocals. As this variable is write-once-read-only it is ok to
+ * have at as an array until we resolve the cpulocals properly
+ */
+PRIVATE u32_t lapic_bus_freq[CONFIG_MAX_CPUS];
+/* the probe period will be roughly 100ms */
+#define PROBE_TICKS (system_hz / 10)
+
+PRIVATE u32_t pci_config_intr_data;
+PRIVATE u32_t ioapic_extint_assigned = 0;
+PRIVATE int lapic_extint_assigned = 0;
+
+PRIVATE int calib_clk_handler(irq_hook_t * hook)
+{
+ u32_t tcrt;
+ u64_t tsc;
+
+ probe_ticks++;
+ if (cpu_has_tsc) {
+ read_tsc_64(&tsc);
+ }
+ tcrt = lapic_read(LAPIC_TIMER_CCR);
+
+
+ if (probe_ticks == 1) {
+ lapic_tctr0 = tcrt;
+ tsc0 = tsc;
+ }
+ else if (probe_ticks == PROBE_TICKS) {
+ lapic_tctr1 = tcrt;
+ tsc1 = tsc;
+ }
+
+ return 1;
+}
+
+PUBLIC void apic_calibrate_clocks(void)
+{
+ u32_t lvtt, val, lapic_delta;
+ u64_t tsc_delta;
+ u32_t cpu_freq;
+
+ irq_hook_t calib_clk;
+
+ BOOT_VERBOSE(kprintf("Calibrating clock\n"));
+ /*
+ * Set Initial count register to the highest value so it does not
+ * underflow during the testing period
+ * */
+ val = 0xffffffff;
+ lapic_write (LAPIC_TIMER_ICR, val);
+
+ /* Set Current count register */
+ val = 0;
+ lapic_write (LAPIC_TIMER_CCR, val);
+
+ lvtt = lapic_read(LAPIC_TIMER_DCR) & ~0x0b;
+ /* Set Divide configuration register to 1 */
+ lvtt = APIC_TDCR_1;
+ lapic_write(LAPIC_TIMER_DCR, lvtt);
+
+ /*
+ * mask the APIC timer interrupt in the LVT Timer Register so that we
+ * don't get an interrupt upon underflow which we don't know how to
+ * handle right know. If underflow happens, the system will not continue
+ * as something is wrong with the clock IRQ 0 and we cannot calibrate
+ * the clock which mean that we cannot run processes
+ */
+ lvtt = lapic_read (LAPIC_LVTTR);
+ lvtt |= APIC_LVTT_MASK;
+ lapic_write (LAPIC_LVTTR, lvtt);
+
+ /* set the probe, we use the legacy timer, IRQ 0 */
+ put_irq_handler(&calib_clk, CLOCK_IRQ, calib_clk_handler);
+
+ /* set the PIC timer to get some time */
+ intr_enable();
+ init_8253A_timer(system_hz);
+
+ /* loop for some time to get a sample */
+ while(probe_ticks < PROBE_TICKS);
+
+ intr_disable();
+ stop_8253A_timer();
+
+ /* remove the probe */
+ rm_irq_handler(&calib_clk);
+
+ lapic_delta = lapic_tctr0 - lapic_tctr1;
+ tsc_delta = sub64(tsc1, tsc0);
+
+ lapic_bus_freq[cpuid()] = system_hz * lapic_delta / (PROBE_TICKS - 1);
+ BOOT_VERBOSE(kprintf("APIC bus freq %lu MHz\n",
+ lapic_bus_freq[cpuid()] / 1000000));
+ cpu_freq = div64u(tsc_delta, PROBE_TICKS - 1) * system_hz;
+ BOOT_VERBOSE(kprintf("CPU %d freq %lu MHz\n", cpuid(),
+ cpu_freq / 1000000));
+}
+
+PRIVATE void lapic_set_timer_one_shot(u32_t value)
+{
+ /* sleep in micro seconds */
+ u32_t lvtt;
+ u32_t ticks_per_us;
+ u8_t cpu = cpuid ();
+
+ ticks_per_us = lapic_bus_freq[cpu] / 1000000;
+
+ /* calculate divisor and count from value */
+ lvtt = APIC_TDCR_1;
+ lapic_write(LAPIC_TIMER_DCR, lvtt);
+
+ /* configure timer as one-shot */
+ lvtt = APIC_TIMER_INT_VECTOR;
+ lapic_write(LAPIC_LVTTR, lvtt);
+
+ lapic_write(LAPIC_TIMER_ICR, value * ticks_per_us);
+}
+
+PUBLIC void lapic_set_timer_periodic(unsigned freq)
+{
+ /* sleep in micro seconds */
+ u32_t lvtt;
+ u32_t lapic_ticks_per_clock_tick;
+ u8_t cpu = cpuid();
+
+ lapic_ticks_per_clock_tick = lapic_bus_freq[cpu] / freq;
+
+ lvtt = APIC_TDCR_1;
+ lapic_write(LAPIC_TIMER_DCR, lvtt);
+
+ /* configure timer as periodic */
+ lvtt = APIC_LVT_TM_PERIODIC | APIC_TIMER_INT_VECTOR;
+ lapic_write(LAPIC_LVTTR, lvtt);
+
+ lapic_write(LAPIC_TIMER_ICR, lapic_ticks_per_clock_tick);
+}
+
+PUBLIC void lapic_stop_timer(void)
+{
+ u32_t lvtt;
+ lapic_read(LAPIC_LVTTR);
+ lapic_write(LAPIC_LVTTR, lvtt | APIC_LVTT_MASK);
+}
+
+PUBLIC void lapic_microsec_sleep(unsigned count)
+{
+ lapic_set_timer_one_shot(count);
+ while (lapic_read (LAPIC_TIMER_CCR));
+}
+
+PUBLIC u32_t lapic_errstatus (void)
+{
+ lapic_write(LAPIC_ESR, 0);
+ return lapic_read(LAPIC_ESR);
+}
+
+PUBLIC void lapic_disable(void)
+{
+ /* Disable current APIC and close interrupts from PIC */
+ u32_t val;
+
+ if (!lapic_addr)
+ return;
+ {
+ /* leave it enabled if imcr is not set */
+ val = lapic_read(LAPIC_LINT0);
+ val &= ~(APIC_ICR_DM_MASK|APIC_ICR_INT_MASK);
+ val |= APIC_ICR_DM_EXTINT; /* ExtINT at LINT0 */
+ lapic_write (LAPIC_LINT0, val);
+ return;
+ }
+
+ val = lapic_read(LAPIC_LINT0) & 0xFFFE58FF;
+ val |= APIC_ICR_INT_MASK;
+ lapic_write (LAPIC_LINT0, val);
+
+ val = lapic_read(LAPIC_LINT1) & 0xFFFE58FF;
+ val |= APIC_ICR_INT_MASK;
+ lapic_write (LAPIC_LINT1, val);
+
+ val = lapic_read(LAPIC_SIVR) & 0xFFFFFF00;
+ val &= ~APIC_ENABLE;
+ lapic_write(LAPIC_SIVR, val);
+}
+
+PRIVATE void lapic_enable_no_lints(void)
+{
+ u32_t val;
+
+ val = lapic_read(LAPIC_LINT0);
+ lapic_extint_assigned = (val & APIC_ICR_DM_MASK) == APIC_ICR_DM_EXTINT;
+ val &= ~(APIC_ICR_DM_MASK|APIC_ICR_INT_MASK);
+
+ if (!ioapic_enabled && cpu_is_bsp(cpuid()))
+ val |= (APIC_ICR_DM_EXTINT); /* ExtINT at LINT0 */
+ else
+ val |= (APIC_ICR_DM_EXTINT|APIC_ICR_INT_MASK); /* Masked ExtINT at LINT0 */
+
+ lapic_write (LAPIC_LINT0, val);
+
+ val = lapic_read(LAPIC_LINT1);
+ val &= ~(APIC_ICR_DM_MASK|APIC_ICR_INT_MASK);
+
+ if (!ioapic_enabled && cpu_is_bsp(cpuid()))
+ val |= APIC_ICR_DM_NMI;
+ else
+ val |= (APIC_ICR_DM_NMI | APIC_ICR_INT_MASK); /* NMI at LINT1 */
+ lapic_write (LAPIC_LINT1, val);
+}
+
+PRIVATE int lapic_enable_in_msr(void)
+{
+ u64_t msr;
+ u32_t addr;
+
+ ia32_msr_read(IA32_APIC_BASE, &msr.hi, &msr.lo);
+
+ /*
+ * FIXME if the location is different (unlikely) then the one we expect,
+ * update it
+ */
+ addr = (msr.lo >> 12) | ((msr.hi & 0xf) << 20);
+ if (phys2vir(addr) != (lapic_addr >> 12)) {
+ if (msr.hi & 0xf) {
+ kprintf("ERROR : APIC address needs more then 32 bits\n");
+ return 0;
+ }
+ lapic_addr = phys2vir(msr.lo & ~((1 << 12) - 1));
+ }
+
+ msr.lo |= (1 << IA32_APIC_BASE_ENABLE_BIT);
+ ia32_msr_write(IA32_APIC_BASE, msr.hi, msr.lo);
+
+ return 1;
+}
+
+PUBLIC int lapic_enable(void)
+{
+ u32_t val, nlvt;
+ u32_t timeout = 0xFFFF;
+ u32_t errstatus = 0;
+ int i;
+ unsigned cpu = cpuid ();
+
+ if (!lapic_addr)
+ return 0;
+
+ if (!lapic_enable_in_msr())
+ return 0;
+
+ lapic_eoi_addr = LAPIC_EOI;
+ /* clear error state register. */
+ val = lapic_errstatus ();
+
+ /* Enable Local APIC and set the spurious vector to 0xff. */
+
+ val = lapic_read(LAPIC_SIVR) & 0xFFFFFF00;
+ val |= (APIC_ENABLE | APIC_FOCUS | APIC_SPURIOUS_INT_VECTOR);
+ lapic_write(LAPIC_SIVR, val);
+ lapic_read(LAPIC_SIVR);
+
+ *((u32_t *)lapic_eoi_addr) = 0;
+
+ cpu = cpuid ();
+
+ /* Program Logical Destination Register. */
+ val = lapic_read(LAPIC_LDR) & ~0xFF000000;
+ val |= (cpu & 0xFF) << 24;
+ lapic_write(LAPIC_LDR, val);
+
+ /* Program Destination Format Register for Flat mode. */
+ val = lapic_read(LAPIC_DFR) | 0xF0000000;
+ lapic_write (LAPIC_DFR, val);
+
+ if (nlints == 0) {
+ lapic_enable_no_lints();
+ }
+
+ val = lapic_read (LAPIC_LVTER) & 0xFFFFFF00;
+ lapic_write (LAPIC_LVTER, val);
+
+ nlvt = (lapic_read(LAPIC_VERSION)>>16) & 0xFF;
+
+ if(nlvt >= 4) {
+ val = lapic_read(LAPIC_LVTTMR);
+ lapic_write(LAPIC_LVTTMR, val | APIC_ICR_INT_MASK);
+ }
+
+ if(nlvt >= 5) {
+ val = lapic_read(LAPIC_LVTPCR);
+ lapic_write(LAPIC_LVTPCR, val | APIC_ICR_INT_MASK);
+ }
+
+ /* setup TPR to allow all interrupts. */
+ val = lapic_read (LAPIC_TPR);
+ /* accept all interrupts */
+ lapic_write (LAPIC_TPR, val & ~0xFF);
+
+ lapic_read (LAPIC_SIVR);
+ *((u32_t *)lapic_eoi_addr) = 0;
+
+
+ cpu_has_tsc = _cpufeature(_CPUF_I386_TSC);
+ BOOT_VERBOSE(if (cpu_has_tsc) kprintf("CPU has Timestamp counter\n"));
+
+ apic_calibrate_clocks();
+ BOOT_VERBOSE(kprintf("APIC timer calibrated\n"));
+
+ return 1;
+}
+
+PRIVATE void apic_spurios_intr(void)
+{
+ kprintf("WARNING spurious interrupt\n");
+ for(;;);
+}
+
+PRIVATE struct gate_table_s gate_table_ioapic[] = {
+ { apic_hwint00, VECTOR( 0), INTR_PRIVILEGE },
+ { apic_hwint01, VECTOR( 1), INTR_PRIVILEGE },
+ { apic_hwint02, VECTOR( 2), INTR_PRIVILEGE },
+ { apic_hwint03, VECTOR( 3), INTR_PRIVILEGE },
+ { apic_hwint04, VECTOR( 4), INTR_PRIVILEGE },
+ { apic_hwint05, VECTOR( 5), INTR_PRIVILEGE },
+ { apic_hwint06, VECTOR( 6), INTR_PRIVILEGE },
+ { apic_hwint07, VECTOR( 7), INTR_PRIVILEGE },
+ { apic_hwint08, VECTOR( 8), INTR_PRIVILEGE },
+ { apic_hwint09, VECTOR( 9), INTR_PRIVILEGE },
+ { apic_hwint10, VECTOR(10), INTR_PRIVILEGE },
+ { apic_hwint11, VECTOR(11), INTR_PRIVILEGE },
+ { apic_hwint12, VECTOR(12), INTR_PRIVILEGE },
+ { apic_hwint13, VECTOR(13), INTR_PRIVILEGE },
+ { apic_hwint14, VECTOR(14), INTR_PRIVILEGE },
+ { apic_hwint15, VECTOR(15), INTR_PRIVILEGE },
+ { apic_spurios_intr, APIC_SPURIOUS_INT_VECTOR, INTR_PRIVILEGE },
+ { NULL, 0, 0}
+};
+
+PRIVATE struct gate_table_s gate_table_common[] = {
+ { syscall_entry, SYS386_VECTOR, USER_PRIVILEGE },
+ { level0_call, LEVEL0_VECTOR, TASK_PRIVILEGE },
+ { NULL, 0, 0}
+};
+
+#ifdef CONFIG_APIC_DEBUG
+PRIVATE lapic_set_dummy_handlers(void)
+{
+ char * handler;
+ int vect = 32;
+
+ handler = &lapic_intr_dummy_handles_start;
+ handler += vect * LAPIC_INTR_DUMMY_HANDLER_SIZE;
+ for(; handler < &lapic_intr_dummy_handles_end;
+ handler += LAPIC_INTR_DUMMY_HANDLER_SIZE) {
+ int_gate(vect++, (vir_bytes) handler,
+ PRESENT | INT_GATE_TYPE |
+ (INTR_PRIVILEGE << DPL_SHIFT));
+ }
+}
+#endif
+
+/* Build descriptors for interrupt gates in IDT. */
+PUBLIC void apic_idt_init(int reset)
+{
+ /* Set up idt tables for smp mode.
+ */
+ vir_bytes local_timer_intr_handler;
+
+ if (reset) {
+ idt_copy_vectors(gate_table_pic);
+ idt_copy_vectors(gate_table_common);
+ return;
+ }
+
+#ifdef CONFIG_APIC_DEBUG
+ if (cpu_is_bsp(cpuid()))
+ kprintf("APIC debugging is enabled\n");
+ lapic_set_dummy_handlers();
+#endif
+
+ /* Build descriptors for interrupt gates in IDT. */
+ if (ioapic_enabled)
+ idt_copy_vectors(gate_table_ioapic);
+ else
+ idt_copy_vectors(gate_table_pic);
+
+ idt_copy_vectors(gate_table_common);
+
+ /* configure the timer interupt handler */
+ if (cpu_is_bsp(cpuid())) {
+ local_timer_intr_handler = (vir_bytes) lapic_bsp_timer_int_handler;
+ BOOT_VERBOSE(kprintf("Initiating BSP timer handler\n"));
+ } else {
+ local_timer_intr_handler = (vir_bytes) lapic_ap_timer_int_handler;
+ BOOT_VERBOSE(kprintf("Initiating AP timer handler\n"));
+ }
+
+ /* register the timer interrupt handler for this CPU */
+ int_gate(APIC_TIMER_INT_VECTOR, (vir_bytes) local_timer_intr_handler,
+ PRESENT | INT_GATE_TYPE | (INTR_PRIVILEGE << DPL_SHIFT));
+}
+
+PUBLIC int apic_single_cpu_init(void)
+{
+ if (!cpu_feature_apic_on_chip())
+ return 0;
+
+ lapic_addr = phys2vir(LOCAL_APIC_DEF_ADDR);
+ ioapic_enabled = 0;
+
+ if (!lapic_enable()) {
+ lapic_addr = 0x0;
+ return 0;
+ }
+
+ apic_idt_init(0); /* Not a reset ! */
+ idt_reload();
+ return 1;
+}
--- /dev/null
+#ifndef __APIC_H__
+#define __APIC_H__
+
+#define LOCAL_APIC_DEF_ADDR 0xfee00000 /* default local apic address */
+#define IO_APIC_DEF_ADDR 0xfec00000 /* default i/o apic address */
+
+#define LAPIC_ID (lapic_addr + 0x020)
+#define LAPIC_VERSION (lapic_addr + 0x030)
+#define LAPIC_TPR (lapic_addr + 0x080)
+#define LAPIC_EOI (lapic_addr + 0x0b0)
+#define LAPIC_LDR (lapic_addr + 0x0d0)
+#define LAPIC_DFR (lapic_addr + 0x0e0)
+#define LAPIC_SIVR (lapic_addr + 0x0f0)
+#define LAPIC_ESR (lapic_addr + 0x280)
+#define LAPIC_ICR1 (lapic_addr + 0x300)
+#define LAPIC_ICR2 (lapic_addr + 0x310)
+#define LAPIC_LVTTR (lapic_addr + 0x320)
+#define LAPIC_LVTTMR (lapic_addr + 0x330)
+#define LAPIC_LVTPCR (lapic_addr + 0x340)
+#define LAPIC_LINT0 (lapic_addr + 0x350)
+#define LAPIC_LINT1 (lapic_addr + 0x360)
+#define LAPIC_LVTER (lapic_addr + 0x370)
+#define LAPIC_TIMER_ICR (lapic_addr + 0x380)
+#define LAPIC_TIMER_CCR (lapic_addr + 0x390)
+#define LAPIC_TIMER_DCR (lapic_addr + 0x3e0)
+
+#define IOAPIC_ID 0x0
+#define IOAPIC_VERSION 0x1
+#define IOAPIC_ARB 0x2
+#define IOAPIC_REDIR_TABLE 0x10
+
+#define APIC_TIMER_INT_VECTOR 0xfe
+#define APIC_SPURIOUS_INT_VECTOR 0xff
+
+
+#ifndef __ASSEMBLY__
+
+#include "../../kernel.h"
+
+EXTERN int ioapic_enabled;
+EXTERN u32_t lapic_addr;
+EXTERN u32_t lapic_eoi_addr;
+EXTERN u32_t lapic_taskpri_addr;
+EXTERN int bsp_lapic_id;
+
+#define MAX_NR_IOAPICS 32
+#define MAX_NR_BUSES 32
+#define MAX_NR_APICIDS 255
+#define MAX_NR_LCLINTS 2
+
+EXTERN u8_t apicid2cpuid[MAX_NR_APICIDS+1];
+EXTERN unsigned apic_imcrp;
+EXTERN unsigned nioapics;
+EXTERN unsigned nbuses;
+EXTERN unsigned nintrs;
+EXTERN unsigned nlints;
+
+EXTERN u32_t ioapic_id_mask[8];
+EXTERN u32_t lapic_id_mask[8];
+EXTERN u32_t lapic_addr_vaddr; /* we remember the virtual address here until we
+ switch to paging */
+EXTERN u32_t lapic_addr;
+EXTERN u32_t lapic_eoi_addr;
+EXTERN u32_t lapic_taskpri_addr;
+
+_PROTOTYPE (void calc_bus_clock, (void));
+_PROTOTYPE (u32_t lapic_errstatus, (void));
+/*
+_PROTOTYPE (u32_t ioapic_read, (u32_t addr, u32_t offset));
+_PROTOTYPE (void ioapic_write, (u32_t addr, u32_t offset, u32_t data));
+_PROTOTYPE (void lapic_eoi, (void));
+*/
+_PROTOTYPE (void lapic_microsec_sleep, (unsigned count));
+_PROTOTYPE (void smp_ioapic_unmask, (void));
+_PROTOTYPE (void ioapic_disable_irqs, (u32_t irq));
+_PROTOTYPE (void ioapic_enable_irqs, (u32_t irq));
+_PROTOTYPE (u32_t ioapic_irqs_inuse, (void));
+_PROTOTYPE (void smp_recv_ipi, (int arg));
+_PROTOTYPE (void ioapic_config_pci_irq, (u32_t data));
+
+_PROTOTYPE (int lapic_enable, (void));
+_PROTOTYPE (void lapic_disable, (void));
+
+_PROTOTYPE (void ioapic_disable_all, (void));
+_PROTOTYPE (int ioapic_enable_all, (void));
+
+_PROTOTYPE(void apic_idt_init, (int reset));
+
+_PROTOTYPE(int apic_single_cpu_init, (void));
+
+_PROTOTYPE(void lapic_set_timer_periodic, (unsigned freq));
+_PROTOTYPE(void lapic_stop_timer, (void));
+
+#include <minix/cpufeature.h>
+
+#define cpu_feature_apic_on_chip() _cpufeature(_CPUF_I386_APIC_ON_CHIP)
+
+#define lapic_read(what) (*((u32_t *)((char*)(what))))
+#define lapic_write(what, data) do { \
+ (*((u32_t *)((char*)(what)))) = data; \
+} while(0)
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __APIC_H__ */
--- /dev/null
+#include <archconst.h>
+#include "apic.h"
+#include "sconst.h"
+#include "apic_asm.h"
+
+.globl apic_hwint00 /* handlers for hardware interrupts */
+.globl apic_hwint01
+.globl apic_hwint02
+.globl apic_hwint03
+.globl apic_hwint04
+.globl apic_hwint05
+.globl apic_hwint06
+.globl apic_hwint07
+.globl apic_hwint08
+.globl apic_hwint09
+.globl apic_hwint10
+.globl apic_hwint11
+.globl apic_hwint12
+.globl apic_hwint13
+.globl apic_hwint14
+.globl apic_hwint15
+
+#define APIC_IRQ_HANDLER(irq) \
+ push $irq ;\
+ call irq_handle /* intr_handle(irq_handlers[irq]) */ ;\
+ add $4, %esp ;\
+ mov lapic_eoi_addr, %eax ;\
+ movl $0, (%eax) ;\
+
+/*===========================================================================*/
+/* interrupt handlers */
+/* interrupt handlers for 386 32-bit protected mode */
+/* APIC interrupt handlers for 386 32-bit protected mode */
+/*===========================================================================*/
+#define apic_hwint(irq) \
+ TEST_INT_IN_KERNEL(4, 0f) ;\
+ \
+ SAVE_PROCESS_CTX(0) ;\
+ movl $0, %ebp /* for stack trace */ ;\
+ APIC_IRQ_HANDLER(irq) ;\
+ jmp restart ;\
+ \
+0: \
+ pusha ;\
+ APIC_IRQ_HANDLER(irq) ;\
+ popa ;\
+ iret ;
+
+/* Each of these entry points is an expansion of the hwint_master macro */
+.balign 16
+apic_hwint00:
+/* Interrupt routine for irq 0 (the clock). */
+ apic_hwint(0)
+
+.balign 16
+apic_hwint01:
+/* Interrupt routine for irq 1 (keyboard) */
+ apic_hwint(1)
+
+.balign 16
+apic_hwint02:
+/* Interrupt routine for irq 2 (cascade!) */
+ apic_hwint(2)
+
+.balign 16
+apic_hwint03:
+/* Interrupt routine for irq 3 (second serial) */
+ apic_hwint(3)
+
+.balign 16
+apic_hwint04:
+/* Interrupt routine for irq 4 (first serial) */
+ apic_hwint(4)
+
+.balign 16
+apic_hwint05:
+/* Interrupt routine for irq 5 (XT winchester) */
+ apic_hwint(5)
+
+.balign 16
+apic_hwint06:
+/* Interrupt routine for irq 6 (floppy) */
+ apic_hwint(6)
+
+.balign 16
+apic_hwint07:
+/* Interrupt routine for irq 7 (printer) */
+ apic_hwint(7)
+
+.balign 16
+apic_hwint08:
+/* Interrupt routine for irq 8 (realtime clock) */
+ apic_hwint(8)
+
+.balign 16
+apic_hwint09:
+/* Interrupt routine for irq 9 (irq 2 redirected) */
+ apic_hwint(9)
+
+.balign 16
+apic_hwint10:
+/* Interrupt routine for irq 10 */
+ apic_hwint(10)
+
+.balign 16
+apic_hwint11:
+/* Interrupt routine for irq 11 */
+ apic_hwint(11)
+
+.balign 16
+apic_hwint12:
+/* Interrupt routine for irq 12 */
+ apic_hwint(12)
+
+.balign 16
+apic_hwint13:
+/* Interrupt routine for irq 13 (FPU exception) */
+ apic_hwint(13)
+
+.balign 16
+apic_hwint14:
+/* Interrupt routine for irq 14 (AT winchester) */
+ apic_hwint(14)
+
+.balign 16
+apic_hwint15:
+/* Interrupt routine for irq 15 */
+ apic_hwint(15)
+
+
+#define LAPIC_INTR_HANDLER(func) \
+ movl $func, %eax ;\
+ call *%eax /* call the actual handler */ ;\
+ mov lapic_eoi_addr, %eax /* the end of handler*/ ;\
+ movl $0, (%eax) ;
+
+/*===========================================================================*/
+/* handler of the local APIC interrupts */
+/*===========================================================================*/
+
+#define lapic_intr(func) \
+ TEST_INT_IN_KERNEL(4, 0f) ;\
+ \
+ SAVE_PROCESS_CTX(0) ;\
+ movl $0, %ebp /* for stack trace */ ;\
+ LAPIC_INTR_HANDLER(func) ;\
+ jmp restart ;\
+ \
+0: \
+ pusha ;\
+ LAPIC_INTR_HANDLER(func) ;\
+ popa ;\
+ iret ;
+
+/* apic timer tick handlers */
+.globl lapic_bsp_timer_int_handler
+lapic_bsp_timer_int_handler:
+ lapic_intr(bsp_timer_int_handler)
+
+.globl lapic_ap_timer_int_handler
+lapic_ap_timer_int_handler:
+ lapic_intr(ap_timer_int_handler)
+
+#ifdef CONFIG_APIC_DEBUG
+
+.data
+lapic_intr_dummy_handler_msg:
+.ascii "UNHABLED APIC interrupt vector %d\n"
+
+.text
+
+#define lapic_intr_dummy_handler(vect) \
+ pushl $vect; \
+ push $lapic_intr_dummy_handler_msg; \
+ call kprintf; \
+1: jmp 1b; /* never return */
+
+#define LAPIC_INTR_DUMMY_HANDLER(vect) \
+ .balign LAPIC_INTR_DUMMY_HANDLER_SIZE; \
+ lapic_intr_dummy_handler_##vect: lapic_intr_dummy_handler(vect)
+
+.globl lapic_intr_dummy_handles_start
+lapic_intr_dummy_handles_start:
+
+LAPIC_INTR_DUMMY_HANDLER(0)
+LAPIC_INTR_DUMMY_HANDLER(1)
+LAPIC_INTR_DUMMY_HANDLER(2)
+LAPIC_INTR_DUMMY_HANDLER(3)
+LAPIC_INTR_DUMMY_HANDLER(4)
+LAPIC_INTR_DUMMY_HANDLER(5)
+LAPIC_INTR_DUMMY_HANDLER(6)
+LAPIC_INTR_DUMMY_HANDLER(7)
+LAPIC_INTR_DUMMY_HANDLER(8)
+LAPIC_INTR_DUMMY_HANDLER(9)
+LAPIC_INTR_DUMMY_HANDLER(10)
+LAPIC_INTR_DUMMY_HANDLER(11)
+LAPIC_INTR_DUMMY_HANDLER(12)
+LAPIC_INTR_DUMMY_HANDLER(13)
+LAPIC_INTR_DUMMY_HANDLER(14)
+LAPIC_INTR_DUMMY_HANDLER(15)
+LAPIC_INTR_DUMMY_HANDLER(16)
+LAPIC_INTR_DUMMY_HANDLER(17)
+LAPIC_INTR_DUMMY_HANDLER(18)
+LAPIC_INTR_DUMMY_HANDLER(19)
+LAPIC_INTR_DUMMY_HANDLER(20)
+LAPIC_INTR_DUMMY_HANDLER(21)
+LAPIC_INTR_DUMMY_HANDLER(22)
+LAPIC_INTR_DUMMY_HANDLER(23)
+LAPIC_INTR_DUMMY_HANDLER(24)
+LAPIC_INTR_DUMMY_HANDLER(25)
+LAPIC_INTR_DUMMY_HANDLER(26)
+LAPIC_INTR_DUMMY_HANDLER(27)
+LAPIC_INTR_DUMMY_HANDLER(28)
+LAPIC_INTR_DUMMY_HANDLER(29)
+LAPIC_INTR_DUMMY_HANDLER(30)
+LAPIC_INTR_DUMMY_HANDLER(31)
+LAPIC_INTR_DUMMY_HANDLER(32)
+LAPIC_INTR_DUMMY_HANDLER(33)
+LAPIC_INTR_DUMMY_HANDLER(34)
+LAPIC_INTR_DUMMY_HANDLER(35)
+LAPIC_INTR_DUMMY_HANDLER(36)
+LAPIC_INTR_DUMMY_HANDLER(37)
+LAPIC_INTR_DUMMY_HANDLER(38)
+LAPIC_INTR_DUMMY_HANDLER(39)
+LAPIC_INTR_DUMMY_HANDLER(40)
+LAPIC_INTR_DUMMY_HANDLER(41)
+LAPIC_INTR_DUMMY_HANDLER(42)
+LAPIC_INTR_DUMMY_HANDLER(43)
+LAPIC_INTR_DUMMY_HANDLER(44)
+LAPIC_INTR_DUMMY_HANDLER(45)
+LAPIC_INTR_DUMMY_HANDLER(46)
+LAPIC_INTR_DUMMY_HANDLER(47)
+LAPIC_INTR_DUMMY_HANDLER(48)
+LAPIC_INTR_DUMMY_HANDLER(49)
+LAPIC_INTR_DUMMY_HANDLER(50)
+LAPIC_INTR_DUMMY_HANDLER(51)
+LAPIC_INTR_DUMMY_HANDLER(52)
+LAPIC_INTR_DUMMY_HANDLER(53)
+LAPIC_INTR_DUMMY_HANDLER(54)
+LAPIC_INTR_DUMMY_HANDLER(55)
+LAPIC_INTR_DUMMY_HANDLER(56)
+LAPIC_INTR_DUMMY_HANDLER(57)
+LAPIC_INTR_DUMMY_HANDLER(58)
+LAPIC_INTR_DUMMY_HANDLER(59)
+LAPIC_INTR_DUMMY_HANDLER(60)
+LAPIC_INTR_DUMMY_HANDLER(61)
+LAPIC_INTR_DUMMY_HANDLER(62)
+LAPIC_INTR_DUMMY_HANDLER(63)
+LAPIC_INTR_DUMMY_HANDLER(64)
+LAPIC_INTR_DUMMY_HANDLER(65)
+LAPIC_INTR_DUMMY_HANDLER(66)
+LAPIC_INTR_DUMMY_HANDLER(67)
+LAPIC_INTR_DUMMY_HANDLER(68)
+LAPIC_INTR_DUMMY_HANDLER(69)
+LAPIC_INTR_DUMMY_HANDLER(70)
+LAPIC_INTR_DUMMY_HANDLER(71)
+LAPIC_INTR_DUMMY_HANDLER(72)
+LAPIC_INTR_DUMMY_HANDLER(73)
+LAPIC_INTR_DUMMY_HANDLER(74)
+LAPIC_INTR_DUMMY_HANDLER(75)
+LAPIC_INTR_DUMMY_HANDLER(76)
+LAPIC_INTR_DUMMY_HANDLER(77)
+LAPIC_INTR_DUMMY_HANDLER(78)
+LAPIC_INTR_DUMMY_HANDLER(79)
+LAPIC_INTR_DUMMY_HANDLER(80)
+LAPIC_INTR_DUMMY_HANDLER(81)
+LAPIC_INTR_DUMMY_HANDLER(82)
+LAPIC_INTR_DUMMY_HANDLER(83)
+LAPIC_INTR_DUMMY_HANDLER(84)
+LAPIC_INTR_DUMMY_HANDLER(85)
+LAPIC_INTR_DUMMY_HANDLER(86)
+LAPIC_INTR_DUMMY_HANDLER(87)
+LAPIC_INTR_DUMMY_HANDLER(88)
+LAPIC_INTR_DUMMY_HANDLER(89)
+LAPIC_INTR_DUMMY_HANDLER(90)
+LAPIC_INTR_DUMMY_HANDLER(91)
+LAPIC_INTR_DUMMY_HANDLER(92)
+LAPIC_INTR_DUMMY_HANDLER(93)
+LAPIC_INTR_DUMMY_HANDLER(94)
+LAPIC_INTR_DUMMY_HANDLER(95)
+LAPIC_INTR_DUMMY_HANDLER(96)
+LAPIC_INTR_DUMMY_HANDLER(97)
+LAPIC_INTR_DUMMY_HANDLER(98)
+LAPIC_INTR_DUMMY_HANDLER(99)
+LAPIC_INTR_DUMMY_HANDLER(100)
+LAPIC_INTR_DUMMY_HANDLER(101)
+LAPIC_INTR_DUMMY_HANDLER(102)
+LAPIC_INTR_DUMMY_HANDLER(103)
+LAPIC_INTR_DUMMY_HANDLER(104)
+LAPIC_INTR_DUMMY_HANDLER(105)
+LAPIC_INTR_DUMMY_HANDLER(106)
+LAPIC_INTR_DUMMY_HANDLER(107)
+LAPIC_INTR_DUMMY_HANDLER(108)
+LAPIC_INTR_DUMMY_HANDLER(109)
+LAPIC_INTR_DUMMY_HANDLER(110)
+LAPIC_INTR_DUMMY_HANDLER(111)
+LAPIC_INTR_DUMMY_HANDLER(112)
+LAPIC_INTR_DUMMY_HANDLER(113)
+LAPIC_INTR_DUMMY_HANDLER(114)
+LAPIC_INTR_DUMMY_HANDLER(115)
+LAPIC_INTR_DUMMY_HANDLER(116)
+LAPIC_INTR_DUMMY_HANDLER(117)
+LAPIC_INTR_DUMMY_HANDLER(118)
+LAPIC_INTR_DUMMY_HANDLER(119)
+LAPIC_INTR_DUMMY_HANDLER(120)
+LAPIC_INTR_DUMMY_HANDLER(121)
+LAPIC_INTR_DUMMY_HANDLER(122)
+LAPIC_INTR_DUMMY_HANDLER(123)
+LAPIC_INTR_DUMMY_HANDLER(124)
+LAPIC_INTR_DUMMY_HANDLER(125)
+LAPIC_INTR_DUMMY_HANDLER(126)
+LAPIC_INTR_DUMMY_HANDLER(127)
+LAPIC_INTR_DUMMY_HANDLER(128)
+LAPIC_INTR_DUMMY_HANDLER(129)
+LAPIC_INTR_DUMMY_HANDLER(130)
+LAPIC_INTR_DUMMY_HANDLER(131)
+LAPIC_INTR_DUMMY_HANDLER(132)
+LAPIC_INTR_DUMMY_HANDLER(133)
+LAPIC_INTR_DUMMY_HANDLER(134)
+LAPIC_INTR_DUMMY_HANDLER(135)
+LAPIC_INTR_DUMMY_HANDLER(136)
+LAPIC_INTR_DUMMY_HANDLER(137)
+LAPIC_INTR_DUMMY_HANDLER(138)
+LAPIC_INTR_DUMMY_HANDLER(139)
+LAPIC_INTR_DUMMY_HANDLER(140)
+LAPIC_INTR_DUMMY_HANDLER(141)
+LAPIC_INTR_DUMMY_HANDLER(142)
+LAPIC_INTR_DUMMY_HANDLER(143)
+LAPIC_INTR_DUMMY_HANDLER(144)
+LAPIC_INTR_DUMMY_HANDLER(145)
+LAPIC_INTR_DUMMY_HANDLER(146)
+LAPIC_INTR_DUMMY_HANDLER(147)
+LAPIC_INTR_DUMMY_HANDLER(148)
+LAPIC_INTR_DUMMY_HANDLER(149)
+LAPIC_INTR_DUMMY_HANDLER(150)
+LAPIC_INTR_DUMMY_HANDLER(151)
+LAPIC_INTR_DUMMY_HANDLER(152)
+LAPIC_INTR_DUMMY_HANDLER(153)
+LAPIC_INTR_DUMMY_HANDLER(154)
+LAPIC_INTR_DUMMY_HANDLER(155)
+LAPIC_INTR_DUMMY_HANDLER(156)
+LAPIC_INTR_DUMMY_HANDLER(157)
+LAPIC_INTR_DUMMY_HANDLER(158)
+LAPIC_INTR_DUMMY_HANDLER(159)
+LAPIC_INTR_DUMMY_HANDLER(160)
+LAPIC_INTR_DUMMY_HANDLER(161)
+LAPIC_INTR_DUMMY_HANDLER(162)
+LAPIC_INTR_DUMMY_HANDLER(163)
+LAPIC_INTR_DUMMY_HANDLER(164)
+LAPIC_INTR_DUMMY_HANDLER(165)
+LAPIC_INTR_DUMMY_HANDLER(166)
+LAPIC_INTR_DUMMY_HANDLER(167)
+LAPIC_INTR_DUMMY_HANDLER(168)
+LAPIC_INTR_DUMMY_HANDLER(169)
+LAPIC_INTR_DUMMY_HANDLER(170)
+LAPIC_INTR_DUMMY_HANDLER(171)
+LAPIC_INTR_DUMMY_HANDLER(172)
+LAPIC_INTR_DUMMY_HANDLER(173)
+LAPIC_INTR_DUMMY_HANDLER(174)
+LAPIC_INTR_DUMMY_HANDLER(175)
+LAPIC_INTR_DUMMY_HANDLER(176)
+LAPIC_INTR_DUMMY_HANDLER(177)
+LAPIC_INTR_DUMMY_HANDLER(178)
+LAPIC_INTR_DUMMY_HANDLER(179)
+LAPIC_INTR_DUMMY_HANDLER(180)
+LAPIC_INTR_DUMMY_HANDLER(181)
+LAPIC_INTR_DUMMY_HANDLER(182)
+LAPIC_INTR_DUMMY_HANDLER(183)
+LAPIC_INTR_DUMMY_HANDLER(184)
+LAPIC_INTR_DUMMY_HANDLER(185)
+LAPIC_INTR_DUMMY_HANDLER(186)
+LAPIC_INTR_DUMMY_HANDLER(187)
+LAPIC_INTR_DUMMY_HANDLER(188)
+LAPIC_INTR_DUMMY_HANDLER(189)
+LAPIC_INTR_DUMMY_HANDLER(190)
+LAPIC_INTR_DUMMY_HANDLER(191)
+LAPIC_INTR_DUMMY_HANDLER(192)
+LAPIC_INTR_DUMMY_HANDLER(193)
+LAPIC_INTR_DUMMY_HANDLER(194)
+LAPIC_INTR_DUMMY_HANDLER(195)
+LAPIC_INTR_DUMMY_HANDLER(196)
+LAPIC_INTR_DUMMY_HANDLER(197)
+LAPIC_INTR_DUMMY_HANDLER(198)
+LAPIC_INTR_DUMMY_HANDLER(199)
+LAPIC_INTR_DUMMY_HANDLER(200)
+LAPIC_INTR_DUMMY_HANDLER(201)
+LAPIC_INTR_DUMMY_HANDLER(202)
+LAPIC_INTR_DUMMY_HANDLER(203)
+LAPIC_INTR_DUMMY_HANDLER(204)
+LAPIC_INTR_DUMMY_HANDLER(205)
+LAPIC_INTR_DUMMY_HANDLER(206)
+LAPIC_INTR_DUMMY_HANDLER(207)
+LAPIC_INTR_DUMMY_HANDLER(208)
+LAPIC_INTR_DUMMY_HANDLER(209)
+LAPIC_INTR_DUMMY_HANDLER(210)
+LAPIC_INTR_DUMMY_HANDLER(211)
+LAPIC_INTR_DUMMY_HANDLER(212)
+LAPIC_INTR_DUMMY_HANDLER(213)
+LAPIC_INTR_DUMMY_HANDLER(214)
+LAPIC_INTR_DUMMY_HANDLER(215)
+LAPIC_INTR_DUMMY_HANDLER(216)
+LAPIC_INTR_DUMMY_HANDLER(217)
+LAPIC_INTR_DUMMY_HANDLER(218)
+LAPIC_INTR_DUMMY_HANDLER(219)
+LAPIC_INTR_DUMMY_HANDLER(220)
+LAPIC_INTR_DUMMY_HANDLER(221)
+LAPIC_INTR_DUMMY_HANDLER(222)
+LAPIC_INTR_DUMMY_HANDLER(223)
+LAPIC_INTR_DUMMY_HANDLER(224)
+LAPIC_INTR_DUMMY_HANDLER(225)
+LAPIC_INTR_DUMMY_HANDLER(226)
+LAPIC_INTR_DUMMY_HANDLER(227)
+LAPIC_INTR_DUMMY_HANDLER(228)
+LAPIC_INTR_DUMMY_HANDLER(229)
+LAPIC_INTR_DUMMY_HANDLER(230)
+LAPIC_INTR_DUMMY_HANDLER(231)
+LAPIC_INTR_DUMMY_HANDLER(232)
+LAPIC_INTR_DUMMY_HANDLER(233)
+LAPIC_INTR_DUMMY_HANDLER(234)
+LAPIC_INTR_DUMMY_HANDLER(235)
+LAPIC_INTR_DUMMY_HANDLER(236)
+LAPIC_INTR_DUMMY_HANDLER(237)
+LAPIC_INTR_DUMMY_HANDLER(238)
+LAPIC_INTR_DUMMY_HANDLER(239)
+LAPIC_INTR_DUMMY_HANDLER(240)
+LAPIC_INTR_DUMMY_HANDLER(241)
+LAPIC_INTR_DUMMY_HANDLER(242)
+LAPIC_INTR_DUMMY_HANDLER(243)
+LAPIC_INTR_DUMMY_HANDLER(244)
+LAPIC_INTR_DUMMY_HANDLER(245)
+LAPIC_INTR_DUMMY_HANDLER(246)
+LAPIC_INTR_DUMMY_HANDLER(247)
+LAPIC_INTR_DUMMY_HANDLER(248)
+LAPIC_INTR_DUMMY_HANDLER(249)
+LAPIC_INTR_DUMMY_HANDLER(250)
+LAPIC_INTR_DUMMY_HANDLER(251)
+LAPIC_INTR_DUMMY_HANDLER(252)
+LAPIC_INTR_DUMMY_HANDLER(253)
+LAPIC_INTR_DUMMY_HANDLER(254)
+LAPIC_INTR_DUMMY_HANDLER(255)
+
+.globl lapic_intr_dummy_handles_end
+lapic_intr_dummy_handles_end:
+
+
+#endif /* CONFIG_APIC_DEBUG */
+
--- /dev/null
+#ifndef __APIC_ASM_H__
+#define __APIC_ASM_H__
+
+
+#ifndef __ASSEMBLY__
+#include "../../kernel.h"
+
+_PROTOTYPE( void apic_hwint00, (void) );
+_PROTOTYPE( void apic_hwint01, (void) );
+_PROTOTYPE( void apic_hwint02, (void) );
+_PROTOTYPE( void apic_hwint03, (void) );
+_PROTOTYPE( void apic_hwint04, (void) );
+_PROTOTYPE( void apic_hwint05, (void) );
+_PROTOTYPE( void apic_hwint06, (void) );
+_PROTOTYPE( void apic_hwint07, (void) );
+_PROTOTYPE( void apic_hwint08, (void) );
+_PROTOTYPE( void apic_hwint09, (void) );
+_PROTOTYPE( void apic_hwint10, (void) );
+_PROTOTYPE( void apic_hwint11, (void) );
+_PROTOTYPE( void apic_hwint12, (void) );
+_PROTOTYPE( void apic_hwint13, (void) );
+_PROTOTYPE( void apic_hwint14, (void) );
+_PROTOTYPE( void apic_hwint15, (void) );
+
+/* The local APIC timer tick handlers */
+_PROTOTYPE(void lapic_bsp_timer_int_handler, (void));
+_PROTOTYPE(void lapic_ap_timer_int_handler, (void));
+
+#endif
+
+#define CONFIG_APIC_DEBUG
+
+#ifdef CONFIG_APIC_DEBUG
+
+#define LAPIC_INTR_DUMMY_HANDLER_SIZE 32
+
+#ifndef __ASSEMBLY__
+EXTERN char lapic_intr_dummy_handles_start;
+EXTERN char lapic_intr_dummy_handles_end;
+#endif
+
+#endif /* CONFIG_APIC_DEBUG */
+
+#endif /* __APIC_ASM_H__ */
#include "../../clock.h"
+#ifdef CONFIG_APIC
+#include "apic.h"
+#endif
+
#define CLOCK_ACK_BIT 0x80 /* PS/2 clock interrupt acknowledge bit */
/* Clock parameters. */
PUBLIC int arch_init_local_timer(unsigned freq)
{
- init_8253A_timer(freq);
+#ifdef CONFIG_APIC
+ /* if we know the address, lapic is enabled and we should use it */
+ if (lapic_addr) {
+ lapic_set_timer_periodic(freq);
+ } else
+ {
+ BOOT_VERBOSE(kprintf("Initiating legacy i8253 timer\n"));
+#else
+ {
+#endif
+ init_8253A_timer(freq);
+ }
return 0;
}
PUBLIC void arch_stop_local_timer(void)
{
- stop_8253A_timer();
+#ifdef CONFIG_APIC
+ if (lapic_addr) {
+ lapic_stop_timer();
+ } else
+#endif
+ {
+ stop_8253A_timer();
+ }
}
PUBLIC int arch_register_local_timer_handler(irq_handler_t handler)
{
- /* Using PIC, Initialize the CLOCK's interrupt hook. */
- pic_timer_hook.proc_nr_e = NONE;
-
- put_irq_handler(&pic_timer_hook, CLOCK_IRQ, handler);
- put_irq_handler(&pic_timer_hook, CLOCK_IRQ,
- (irq_handler_t)bsp_timer_int_handler);
+#ifdef CONFIG_APIC
+ if (lapic_addr) {
+ /* Using APIC, it is configured in apic_idt_init() */
+ BOOT_VERBOSE(kprintf("Using LAPIC timer as tick source\n"));
+ } else
+#endif
+ {
+ /* Using PIC, Initialize the CLOCK's interrupt hook. */
+ pic_timer_hook.proc_nr_e = NONE;
+ pic_timer_hook.irq = CLOCK_IRQ;
+
+ put_irq_handler(&pic_timer_hook, CLOCK_IRQ, handler);
+ }
return 0;
}
--- /dev/null
+#ifndef __GLO_X86_H__
+#define __GLO_X86_H__
+
+EXTERN int cpu_has_tsc; /* signal whether this cpu has time stamp register. This
+ feature was introduced by Pentium */
+
+#endif /* __GLO_X86_H__ */
#define ICW4_AT_MASTER 0x05 /* not SFNM, not buffered, normal EOI, 8086 */
#define ICW4_PC_SLAVE 0x09 /* not SFNM, buffered, normal EOI, 8086 */
#define ICW4_PC_MASTER 0x0D /* not SFNM, buffered, normal EOI, 8086 */
+#define ICW4_AT_AEOI_SLAVE 0x03 /* not SFNM, not buffered, auto EOI, 8086 */
+#define ICW4_AT_AEOI_MASTER 0x07 /* not SFNM, not buffered, auto EOI, 8086 */
+#define ICW4_PC_AEOI_SLAVE 0x0B /* not SFNM, buffered, auto EOI, 8086 */
+#define ICW4_PC_AEOI_MASTER 0x0F /* not SFNM, buffered, auto EOI, 8086 */
#define set_vec(nr, addr) ((void)0)
/*===========================================================================*
* intr_init *
*===========================================================================*/
-PUBLIC int intr_init(mine)
-int mine;
+PUBLIC int intr_init(int mine, int auto_eoi)
{
/* Initialize the 8259s, finishing with all interrupts disabled. This is
* only done in protected mode, in real mode we don't touch the 8259s, but
*/
int i;
- intr_disable();
+ if (!intr_disabled())
+ intr_disable();
/* The AT and newer PS/2 have two interrupt controllers, one master,
* one slaved at IRQ 2. (We don't have to deal with the PC that
* has just one controller, because it must run in real mode.)
*/
outb( INT_CTL, machine.ps_mca ? ICW1_PS : ICW1_AT);
- outb( INT_CTLMASK, mine ? IRQ0_VECTOR : BIOS_IRQ0_VEC);
+ outb( INT_CTLMASK, mine == INTS_MINIX ? IRQ0_VECTOR : BIOS_IRQ0_VEC);
/* ICW2 for master */
outb( INT_CTLMASK, (1 << CASCADE_IRQ));
/* ICW3 tells slaves */
- outb( INT_CTLMASK, ICW4_AT_MASTER);
+ if (auto_eoi)
+ outb( INT_CTLMASK, ICW4_AT_AEOI_MASTER);
+ else
+ outb( INT_CTLMASK, ICW4_AT_MASTER);
outb( INT_CTLMASK, ~(1 << CASCADE_IRQ)); /* IRQ 0-7 mask */
outb( INT2_CTL, machine.ps_mca ? ICW1_PS : ICW1_AT);
- outb( INT2_CTLMASK, mine ? IRQ8_VECTOR : BIOS_IRQ8_VEC);
+ outb( INT2_CTLMASK, mine == INTS_MINIX ? IRQ8_VECTOR : BIOS_IRQ8_VEC);
/* ICW2 for slave */
outb( INT2_CTLMASK, CASCADE_IRQ); /* ICW3 is slave nr */
- outb( INT2_CTLMASK, ICW4_AT_SLAVE);
+ if (auto_eoi)
+ outb( INT2_CTLMASK, ICW4_AT_AEOI_SLAVE);
+ else
+ outb( INT2_CTLMASK, ICW4_AT_SLAVE);
outb( INT2_CTLMASK, ~0); /* IRQ 8-15 mask */
/* Copy the BIOS vectors from the BIOS to the Minix location, so we
PUBLIC void irq_8259_unmask(int irq)
{
unsigned ctl_mask = irq < 8 ? INT_CTLMASK : INT2_CTLMASK;
-
outb(ctl_mask, inb(ctl_mask) & ~(1 << (irq & 0x7)));
}
unsigned ctl_mask = irq < 8 ? INT_CTLMASK : INT2_CTLMASK;
outb(ctl_mask, inb(ctl_mask) | (1 << (irq & 0x7)));
}
+
+/* Disable 8259 - write 0xFF in OCW1 master and slave. */
+PUBLIC void i8259_disable(void)
+{
+ outb(INT2_CTLMASK, 0xFF);
+ outb(INT_CTLMASK, 0xFF);
+ inb(INT_CTLMASK);
+}
+
/* Table sizes. */
#define GDT_SIZE (FIRST_LDT_INDEX + NR_TASKS + NR_PROCS)
/* spec. and LDT's */
-#define IDT_SIZE (IRQ8_VECTOR + 8) /* only up to the highest vector */
+#define IDT_SIZE 256 /* the table is set to it's maximal size */
/* Fixed global descriptors. 1 to 7 are prescribed by the BIOS. */
#define GDT_INDEX 1 /* GDT descriptor */
#define INT_286_GATE 6 /* interrupt gate, used for all vectors */
#define TRAP_286_GATE 7 /* not used */
+#define INT_GATE_TYPE (INT_286_GATE | DESC_386_BIT)
+#define TSS_TYPE (AVL_286_TSS | DESC_386_BIT)
+
+
/* Extra 386 hardware constants. */
/* Exception vector numbers. */
#define vir2phys(vir) (kinfo.data_base + (vir_bytes) (vir))
#define phys2vir(ph) ((vir_bytes) (ph) - kinfo.data_base)
+#define INTEL_CPUID_GEN_EBX 0x756e6547 /* ASCII value of "Genu" */
+#define INTEL_CPUID_GEN_EDX 0x49656e69 /* ASCII value of "ineI" */
+#define INTEL_CPUID_GEN_ECX 0x6c65746e /* ASCII value of "ntel" */
+
+#define AMD_CPUID_GEN_EBX 0x68747541 /* ASCII value of "Auth" */
+#define AMD_CPUID_GEN_EDX 0x69746e65 /* ASCII value of "enti" */
+#define AMD_CPUID_GEN_ECX 0x444d4163 /* ASCII value of "cAMD" */
+
+
+
#endif /* _I386_ACONST_H */
.globl read_ds
.globl read_cs
.globl read_ss
+.globl idt_reload /* reload idt when returning to monitor. */
/*
* The routines only guarantee to preserve the registers the C compiler
mov %ax, %ss
xchgl mon_sp, %esp /* unswitch stacks */
lidt gdt+IDT_SELECTOR /* reload interrupt descriptor table */
- andb $~0x02, gdt+TSS_SELECTOR+DESC_ACCESS /* clear TSS busy bit */
- mov $TSS_SELECTOR, %eax
- ltr %ax /* set TSS register */
+
+#ifdef CONFIG_APIC
+ cmpl $0x0, lapic_addr
+ jne 3f
+ mov $0, %ebx
+ jmp 4f
+
+3:
+ mov $FLAT_DS_SELECTOR, %ebx
+ mov %bx, %fs
+ movl lapic_addr, %eax
+ add $0x20, %eax
+ .byte 0x64; mov (%eax), %ebx
+ and $0xFF000000, %ebx
+ shr $24, %ebx
+ movzb %bl, %ebx
+
+4:
+ add $apicid2cpuid, %ebx
+ movzb (%ebx), %eax
+ shl $3, %eax
+ mov %eax, %ebx
+ add $TSS_SELECTOR, %eax
+ addl gdt+DESC_ACCESS, %eax
+ and $~0x02, %eax
+ ltr %bx /* set TSS register */
+
+ mov $DS_SELECTOR, %eax
+ mov %ax, %fs
+
+#endif /* CONFIG_APIC */
pop %eax
outb $INT_CTLMASK /* restore interrupt masks */
movb %ah, %al
outb $INT2_CTLMASK
+6:
addl %ecx, lost_ticks /* record lost clock ticks */
popf /* restore flags */
read_cpu_flags:
pushf
mov (%esp), %eax
- popf
+ add $4, %esp
ret
read_ds:
mov %eax, thecr3
ret
+/*
+ * Read the Model Specific Register (MSR) of IA32 architecture
+ *
+ * void ia32_msr_read(u32_t reg, u32_t * hi, u32_t * lo)
+ */
+.globl ia32_msr_read
+ia32_msr_read:
+ push %ebp
+ mov %esp, %ebp
+
+ mov 8(%ebp), %ecx
+ rdmsr
+ mov 12(%ebp), %ecx
+ mov %edx, (%ecx)
+ mov 16(%ebp), %ecx
+ mov %eax, (%ecx)
+
+ pop %ebp
+ ret
+
+/*
+ * Write the Model Specific Register (MSR) of IA32 architecture
+ *
+ * void ia32_msr_write(u32_t reg, u32_t hi, u32_t lo)
+ */
+.globl ia32_msr_write
+ia32_msr_write:
+ push %ebp
+ mov %esp, %ebp
+
+ mov 12(%ebp), %edx
+ mov 16(%ebp), %eax
+ mov 8(%ebp), %ecx
+ wrmsr
+
+ pop %ebp
+ ret
+
+/*===========================================================================*/
+/* idt_reload */
+/*===========================================================================*/
+/* PUBLIC void idt_reload (void); */
+.balign 16
+idt_reload:
+ lidt gdt+IDT_SELECTOR /* reload interrupt descriptor table */
+ ret
+
+/*
+ * void reload_segment_regs(void)
+ */
+
+#define RELOAD_SEG_REG(reg) \
+ mov reg, %ax ;\
+ mov %ax, reg ;
+
+.globl reload_ds
+reload_ds:
+ RELOAD_SEG_REG(%ds)
+ ret
#include "../../proto.h"
#include "../../debug.h"
+#ifdef CONFIG_APIC
+#include "apic.h"
+#endif
+
PRIVATE int psok = 0;
#define PROCPDEPTR(pr, pi) ((u32_t *) ((u8_t *) vm_pagedirs +\
}
-
#define TYPEDIRECT 0
#define TYPEPROCMAP 1
#define TYPEPHYS 2
PUBLIC arch_phys_map(int index, phys_bytes *addr, phys_bytes *len, int *flags)
{
+#ifdef CONFIG_APIC
+ /* map the local APIC if enabled */
+ if (index == 0 && lapic_addr) {
+ *addr = vir2phys(lapic_addr);
+ *len = 4 << 10 /* 4kB */;
+ *flags = VMMF_UNCACHED;
+ return OK;
+ }
+ return EINVAL;
+#else
/* we don't want anything */
return EINVAL;
+#endif
}
PUBLIC arch_phys_map_reply(int index, vir_bytes addr)
{
+#ifdef CONFIG_APIC
+ /* if local APIC is enabled */
+ if (index == 0 && lapic_addr) {
+ lapic_addr_vaddr = addr;
+ }
+#endif
return OK;
}
PUBLIC int arch_enable_paging(void)
{
+#ifdef CONFIG_APIC
+ /* if local APIC is enabled */
+ if (lapic_addr) {
+ lapic_addr = lapic_addr_vaddr;
+ lapic_eoi_addr = LAPIC_EOI;
+ }
+#endif
return OK;
}
PRIVATE struct gatedesc_s idt[IDT_SIZE]; /* zero-init so none present */
PUBLIC struct tss_s tss; /* zero init */
-FORWARD _PROTOTYPE( void int_gate, (unsigned vec_nr, vir_bytes offset,
- unsigned dpl_type) );
FORWARD _PROTOTYPE( void sdesc, (struct segdesc_s *segdp, phys_bytes base,
vir_bytes size) );
minix_panic("kinfo.data_base not aligned", NO_NUM);
kinfo.data_size = ((kinfo.data_size+CLICK_SIZE-1)/CLICK_SIZE) * CLICK_SIZE;
+ /* Click-round kernel. */
+ if(kinfo.data_base % CLICK_SIZE)
+ minix_panic("kinfo.data_base not aligned", NO_NUM);
+ kinfo.data_size = ((kinfo.data_size+CLICK_SIZE-1)/CLICK_SIZE) * CLICK_SIZE;
+
/* Build gdt and idt pointers in GDT where the BIOS expects them. */
dtp= (struct desctableptr_s *) &gdt[GDT_INDEX];
* (u16_t *) dtp->limit = (sizeof gdt) - 1;
/*===========================================================================*
* int_gate *
*===========================================================================*/
-PRIVATE void int_gate(vec_nr, offset, dpl_type)
+PUBLIC void int_gate(vec_nr, offset, dpl_type)
unsigned vec_nr;
vir_bytes offset;
unsigned dpl_type;
continue;
rp->p_memmap[S].mem_len += incr_clicks;
alloc_segments(rp);
+ rp->p_memmap[S].mem_len -= incr_clicks;
}
return OK;
_PROTOTYPE( void phys_outsw, (U16_t port, phys_bytes buf, size_t count) );
_PROTOTYPE( u32_t read_cr3, (void) );
_PROTOTYPE( void reload_cr3, (void) );
-_PROTOTYPE( void phys_memset, (phys_bytes ph, u32_t c, phys_bytes bytes) );
+_PROTOTYPE( void phys_memset, (phys_bytes ph, u32_t c, phys_bytes bytes));
+_PROTOTYPE( void reload_ds, (void) );
+_PROTOTYPE( void ia32_msr_read, (u32_t reg, u32_t * hi, u32_t * lo) );
+_PROTOTYPE( void ia32_msr_write, (u32_t reg, u32_t hi, u32_t lo) );
/* protect.c */
struct tss_s {
/* copies an array of vectors to the IDT. The last vector must be zero filled */
_PROTOTYPE(void idt_copy_vectors, (struct gate_table_s * first));
+_PROTOTYPE(void idt_reload, (void));
EXTERN void * k_boot_stktop;
_PROTOTYPE(void tss_init, (struct tss_s * tss, void * kernel_stack, unsigned cpu));
+_PROTOTYPE( void int_gate, (unsigned vec_nr, vir_bytes offset,
+ unsigned dpl_type) );
+_PROTOTYPE(void i8259_disable, (void));
/* functions defined in architecture-independent kernel source. */
#include "../../proto.h"
mov %esi, %ss:BPREG(%ebp) ;\
\
RESTORE_KERNEL_SEGS ;\
- SAVE_TRAP_CTX(displ, %ebp, %esi) ;\
- ;
+ SAVE_TRAP_CTX(displ, %ebp, %esi) ;
#endif /* __SCONST_H__ */
#include "../../proc.h"
#include "../../debug.h"
+#ifdef CONFIG_APIC
+#include "apic.h"
+#endif
+
#define CR0_EM 0x0004 /* set to enable trap on any FP instruction */
FORWARD _PROTOTYPE( void ser_debug, (int c));
level0(monitor);
}
+PUBLIC int cpu_has_tsc;
+
PUBLIC void arch_shutdown(int how)
{
/* Mask all interrupts, including the clock. */
PUBLIC void arch_init(void)
{
+#ifdef CONFIG_APIC
+ /*
+ * this is setting kernel segments to cover most of the phys memory. The
+ * value is high enough to reach local APIC nad IOAPICs before paging is
+ * turned on.
+ */
+ prot_set_kern_seg_limit(0xfff00000);
+ reload_ds();
+#endif
+
idt_init();
tss_init(&tss, &k_boot_stktop, 0);
+#if defined(CONFIG_APIC) && !defined(CONFIG_SMP)
+ if (config_no_apic) {
+ BOOT_VERBOSE(kprintf("APIC disabled, using legacy PIC\n"));
+ }
+ else if (!apic_single_cpu_init()) {
+ BOOT_VERBOSE(kprintf("APIC not present, using legacy PIC\n"));
+ }
+#endif
+
#if 0
/* Set CR0_EM until we get FP context switching */
write_cr0(read_cr0() | CR0_EM);
#endif
+
}
#define COM1_BASE 0x3F8
for(;;); \
} while(0)
+#define NOT_IMPLEMENTED do { \
+ kprintf("NOT_IMPLEMENTED at %s:%d\n", __FILE__, __LINE__); \
+ minix_panic("NOT_IMPLEMENTED", NO_NUM); \
+} while(0)
+
+#ifdef CONFIG_BOOT_VERBOSE
+#define BOOT_VERBOSE(x) x
+#else
+#define BOOT_VERBOSE(x)
+#endif
+
#endif /* DEBUG_H */
EXTERN int verboseflags;
#endif
+#ifdef CONFIG_APIC
+EXTERN int config_no_apic; /* optionaly turn off apic */
+#endif
+
/* VM */
EXTERN int vm_running;
EXTERN int catch_pagefaults;
#ifndef KERNEL_H
#define KERNEL_H
+/* APIC is turned on by default */
+#define CONFIG_APIC
+/* boot verbose */
+#define CONFIG_BOOT_VERBOSE
+
/* This is the master header for the kernel. It includes some other files
* and defines the principal constants.
*/
reg_t ktsb; /* kernel task stack base */
struct exec e_hdr; /* for a copy of an a.out header */
- /* Architecture-dependent initialization. */
- arch_init();
-
/* Global value to test segment sanity. */
magictest = MAGICTEST;
alloc_segments(rp);
}
+ /* Architecture-dependent initialization. */
+ arch_init();
+
#if SPROFILE
sprofiling = 0; /* we're not profiling until instructed to */
#endif /* SPROFILE */
* down MINIX. How to shutdown is in the argument: RBT_HALT (return to the
* monitor), RBT_MONITOR (execute given code), RBT_RESET (hard reset).
*/
- intr_init(INTS_ORIG);
arch_stop_local_timer();
+ intr_init(INTS_ORIG, 0);
arch_shutdown(tp ? tmr_arg(tp)->ta_int : RBT_PANIC);
}
_PROTOTYPE( void ser_dump_proc, (void) );
/* main.c */
-_PROTOTYPE( void main, (void) );
_PROTOTYPE( void prepare_shutdown, (int how) );
_PROTOTYPE( void minix_shutdown, (struct timer *tp) );
phys_bytes count) );
_PROTOTYPE( vir_bytes alloc_remote_segment, (u32_t *, segframe_t *,
int, phys_bytes, vir_bytes, int));
-_PROTOTYPE( int intr_init, (int) );
+_PROTOTYPE( int intr_init, (int, int) );
_PROTOTYPE( int intr_disabled, (void) );
_PROTOTYPE( void halt_cpu, (void) );
_PROTOTYPE( void arch_init, (void) );
#include <stdlib.h>
#include <string.h>
#include <archconst.h>
-#include <proto.h>
+#include "proto.h"
FORWARD _PROTOTYPE( char *get_value, (_CONST char *params, _CONST char *key));
/*===========================================================================*
if(value && atoi(value) == 0)
do_serial_debug=1;
+#ifdef CONFIG_APIC
+ value = get_value(params_buffer, "no_apic");
+ if(value)
+ config_no_apic = atoi(value);
+ else
+ config_no_apic = 0;
+#endif
+
/* Return to assembler code to switch to protected mode (if 286),
* reload selectors and call main().
*/
- intr_init(INTS_MINIX);
+ intr_init(INTS_MINIX, 0);
}
/*===========================================================================*
return cpuid_feature_edx & CPUID1_EDX_PSE;
case _CPUF_I386_PGE:
return cpuid_feature_edx & CPUID1_EDX_PGE;
+ case _CPUF_I386_APIC_ON_CHIP:
+ return cpuid_feature_edx & CPUID1_EDX_APIC_ON_CHIP;
+ case _CPUF_I386_TSC:
+ return cpuid_feature_edx & CPUID1_EDX_TSC;
}
return 0;