Local APIC

- local APIC timer used as the source of time

- PIC is still used as the hw interrupt controller as we don't have
  enough info without ACPI or MPS to set up IO APICs

- remapping of APIC when switching paging on, uses the new mechanism
  to tell VM what phys areas to map in kernel's virtual space

- one more step to SMP

based on code by Arun C.
This commit is contained in:
Tomas Hruby 2009-11-16 21:41:44 +00:00
parent 6515c93ecf
commit 8a44a44cb9
24 changed files with 1457 additions and 40 deletions

View file

@ -2,8 +2,10 @@
#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));

View file

@ -70,7 +70,9 @@ sys/vm_i386.h
#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__ */

View file

@ -20,7 +20,9 @@ OBJS= arch_do_vmctl.o \
memory.o \
mpx386.o \
protect.o \
system.o
system.o \
apic.o \
apic_asm.o
CPPFLAGS=-Iinclude
CFLAGS=$(CPPFLAGS) -Wall $(CPROFILE)
@ -80,6 +82,11 @@ mpx386.o: mpx386.S
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 $@ $<

559
kernel/arch/i386/apic.c Normal file
View file

@ -0,0 +1,559 @@
/*
* 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;
}

105
kernel/arch/i386/apic.h Normal file
View file

@ -0,0 +1,105 @@
#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__ */

447
kernel/arch/i386/apic_asm.S Normal file
View file

@ -0,0 +1,447 @@
#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 */

View file

@ -0,0 +1,44 @@
#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__ */

View file

@ -8,6 +8,10 @@
#include "../../clock.h"
#ifdef CONFIG_APIC
#include "apic.h"
#endif
#define CLOCK_ACK_BIT 0x80 /* PS/2 clock interrupt acknowledge bit */
/* Clock parameters. */
@ -69,24 +73,49 @@ PUBLIC clock_t read_8253A_timer(void)
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;
#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);
put_irq_handler(&pic_timer_hook, CLOCK_IRQ,
(irq_handler_t)bsp_timer_int_handler);
put_irq_handler(&pic_timer_hook, CLOCK_IRQ, handler);
}
return 0;
}

7
kernel/arch/i386/glo.h Normal file
View file

@ -0,0 +1,7 @@
#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__ */

View file

@ -19,14 +19,17 @@
#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
@ -35,24 +38,31 @@ int mine;
*/
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
@ -81,7 +91,6 @@ PUBLIC int intr_disabled(void)
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)));
}
@ -90,3 +99,12 @@ PUBLIC void irq_8259_mask(int irq)
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);
}

View file

@ -12,7 +12,7 @@
/* 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 */
@ -100,6 +100,10 @@
#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. */
@ -140,4 +144,14 @@
#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 */

View file

@ -45,6 +45,7 @@
.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
@ -144,15 +145,44 @@ csinit:
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 */
@ -487,7 +517,7 @@ level0:
read_cpu_flags:
pushf
mov (%esp), %eax
popf
add $4, %esp
ret
read_ds:
@ -572,3 +602,62 @@ getcr3val:
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

View file

@ -19,6 +19,10 @@
#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 +\
@ -53,7 +57,6 @@ PUBLIC void vm_init(struct proc *newptproc)
}
#define TYPEDIRECT 0
#define TYPEPROCMAP 1
#define TYPEPHYS 2
@ -1031,16 +1034,40 @@ void i386_freepde(int pde)
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;
}

View file

@ -29,8 +29,6 @@ PUBLIC struct segdesc_s gdt[GDT_SIZE]; /* used in klib.s and mpx.s */
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) );
@ -141,6 +139,11 @@ PUBLIC void prot_init(void)
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;
@ -250,7 +253,7 @@ vir_bytes size;
/*===========================================================================*
* 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;
@ -460,6 +463,7 @@ PUBLIC int prot_set_kern_seg_limit(vir_bytes limit)
continue;
rp->p_memmap[S].mem_len += incr_clicks;
alloc_segments(rp);
rp->p_memmap[S].mem_len -= incr_clicks;
}
return OK;

View file

@ -80,7 +80,10 @@ _PROTOTYPE( void phys_outsb, (U16_t port, phys_bytes buf, size_t count) );
_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 {
@ -140,10 +143,14 @@ EXTERN struct gate_table_s gate_table_pic[];
/* 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"

View file

@ -128,7 +128,6 @@
mov %esi, %ss:BPREG(%ebp) ;\
\
RESTORE_KERNEL_SEGS ;\
SAVE_TRAP_CTX(displ, %ebp, %esi) ;\
;
SAVE_TRAP_CTX(displ, %ebp, %esi) ;
#endif /* __SCONST_H__ */

View file

@ -16,6 +16,10 @@
#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));
@ -25,6 +29,8 @@ PUBLIC void arch_monitor(void)
level0(monitor);
}
PUBLIC int cpu_has_tsc;
PUBLIC void arch_shutdown(int how)
{
/* Mask all interrupts, including the clock. */
@ -125,14 +131,34 @@ PUBLIC void tss_init(struct tss_s * tss, void * kernel_stack, unsigned cpu)
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

View file

@ -75,4 +75,15 @@
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 */

View file

@ -62,6 +62,10 @@ EXTERN u32_t magictest; /* global magic number */
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;

View file

@ -1,6 +1,11 @@
#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.
*/

View file

@ -39,9 +39,6 @@ PUBLIC void main()
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;
@ -184,6 +181,9 @@ PUBLIC void main()
alloc_segments(rp);
}
/* Architecture-dependent initialization. */
arch_init();
#if SPROFILE
sprofiling = 0; /* we're not profiling until instructed to */
#endif /* SPROFILE */
@ -270,8 +270,8 @@ timer_t *tp;
* 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);
}

View file

@ -19,7 +19,6 @@ _PROTOTYPE( void reset_timer, (struct timer *tp) );
_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) );
@ -134,7 +133,7 @@ _PROTOTYPE( int vm_phys_memset, (phys_bytes source, u8_t pattern,
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) );

View file

@ -6,7 +6,7 @@
#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));
/*===========================================================================*
@ -76,11 +76,19 @@ U16_t parmoff, parmsize; /* boot parameters offset and length */
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);
}
/*===========================================================================*

View file

@ -27,6 +27,10 @@ int _cpufeature(int cpufeature)
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;