354 lines
10 KiB
C
354 lines
10 KiB
C
#include "intr.h"
|
|
#include "cpu.h"
|
|
#include "clib.h"
|
|
#include "memory_layout.h"
|
|
#include "print.h"
|
|
#include "paging.h"
|
|
#include "thread.h"
|
|
#include "error.h"
|
|
|
|
/**
|
|
* IDT Defns
|
|
*/
|
|
#define GATE_DPL_0 (0ull << 13)
|
|
#define GATE_DPL_1 (1ull << 13)
|
|
#define GATE_DPL_2 (2ull << 13)
|
|
#define GATE_DPL_3 (3ull << 13)
|
|
#define GATE_PRESENT (1ull << 15)
|
|
#define GATE_TYPE_CALL (12ull << 8)
|
|
#define GATE_TYPE_INTERRUPT (14ull << 8)
|
|
#define GATE_TYPE_TRAP (15ull << 8)
|
|
|
|
/**
|
|
GDT Defns
|
|
**/
|
|
#define SEG_GRANULARITY (1ull << 55)
|
|
#define SEG_LONG (1ull << 53)
|
|
#define SEG_DPL_0 (0ull << 45)
|
|
#define SEG_DPL_1 (1ull << 45)
|
|
#define SEG_DPL_2 (2ull << 45)
|
|
#define SEG_DPL_3 (3ull << 45)
|
|
#define SEG_PRESENT (1ull << 47)
|
|
#define SEG_CODE_DATA (1ull << 44)
|
|
#define SEG_TYPE_DATA_RW (2ull << 40)
|
|
#define SEG_TYPE_DATA_R (0ull << 40)
|
|
#define SEG_TYPE_CODE_X (8ull << 40)
|
|
#define SEG_TYPE_CODE_XR (10ull << 40)
|
|
#define SEG_TYPE_CODE_XC (12ull << 40)
|
|
#define SEG_TYPE_CODE_XRC (14ull << 40)
|
|
#define SEG_AVAILABLE (1ull << 52)
|
|
#define SEG_32_BITS (1ull << 54)
|
|
|
|
// APIC Interrupt vectors
|
|
#define EDX_MASK_APIC (1 << 9)
|
|
#define MSR_APIC_BASE (0x1b)
|
|
#define MSR_MASK_APIC (1 << 11)
|
|
|
|
// PIC defs
|
|
#define PIC1_COMMAND (0x20)
|
|
#define PIC1_DATA (PIC1_COMMAND+1)
|
|
#define PIC2_COMMAND (0xa0)
|
|
#define PIC2_DATA (PIC2_COMMAND+1)
|
|
|
|
#define APIC_REG_SPURIOUS (0xF0)
|
|
#define APIC_REG_TIMER (0x320)
|
|
#define APIC_REG_EOI (0xB0)
|
|
#define APIC_REG_TIMER_DIV (0x3E0)
|
|
#define APIC_REG_TIMER_INIT_CNT (0x380)
|
|
#define APIC_REG_TIMER_CUR_CNT (0x390)
|
|
#define APIC_REG_ID (0x20)
|
|
#define APIC_REG_LINT0 (0x350)
|
|
#define APIC_REG_LINT1 (0x360)
|
|
#define APIC_REG_ICR_L (0x300)
|
|
#define APIC_REG_ICR_H (0x310)
|
|
|
|
#define IPI_DLM_FIXED (0 << 8)
|
|
#define IPI_DLM_START_UP (6 << 8)
|
|
|
|
#define IPI_DSM_PHYS (0 << 11)
|
|
#define IPI_DSM_LOGI (1 << 11)
|
|
#define IPI_STATUS_MASK (1 << 12)
|
|
|
|
#define IPI_LVL_ASS (1 << 15)
|
|
#define IPI_LVL_DEA (0 << 15)
|
|
|
|
// base on 1Ghz CPU
|
|
#define APIC_TIMER_INIT_CNT (10000000)
|
|
#define REG_SPURIOUS_APIC_ENABLE (1 << 8)
|
|
|
|
extern void *intr_stub_array[NUM_IDT_DESC];
|
|
|
|
static void *apic_base;
|
|
static struct tss utss;
|
|
static struct gdtr gdtptr;
|
|
static struct idtr idtptr;
|
|
static struct gdt_desc gdt[NUM_GDT_DESC];
|
|
static struct idt_desc idt[NUM_IDT_DESC];
|
|
static intr_handler intr_disp_tbl[NUM_IDT_DESC];
|
|
|
|
static void write_idt_desc(void *gate, uint64 offset, uint32 selector, uint32 attr)
|
|
{
|
|
((uint8 *) gate)[0] = (uint8) (offset & 0xFF);
|
|
((uint8 *) gate)[1] = (uint8) ((offset >> 8) & 0xFF);
|
|
((uint8 *) gate)[2] = (uint8) (selector & 0xFF);
|
|
((uint8 *) gate)[3] = (uint8) ((selector >> 8) & 0xFF);
|
|
((uint8 *) gate)[4] = (uint8) (attr & 0xFF);
|
|
((uint8 *) gate)[5] = (uint8) ((attr >> 8) & 0xFF);
|
|
((uint8 *) gate)[6] = (uint8) ((offset >> 16) & 0xFF);
|
|
((uint8 *) gate)[7] = (uint8) ((offset >> 24) & 0xFF);
|
|
((uint8 *) gate)[8] = (uint8) ((offset >> 32) & 0xFF);
|
|
((uint8 *) gate)[9] = (uint8) ((offset >> 40) & 0xFF);
|
|
((uint8 *) gate)[10] = (uint8) ((offset >> 48) & 0xFF);
|
|
((uint8 *) gate)[11] = (uint8) ((offset >> 56) & 0xFF);
|
|
((uint8 *) gate)[12] = 0;
|
|
((uint8 *) gate)[13] = 0;
|
|
((uint8 *) gate)[14] = 0;
|
|
((uint8 *) gate)[15] = 0;
|
|
}
|
|
|
|
static void write_gdt_desc(void *gdt, uint32 base, uint32 limit, uint64 attr)
|
|
{
|
|
uint64 seg_desc = (((uint64) base & 0xFFFF) << 16) | ((((uint64) base >> 16) & 0xFF) << 32) |
|
|
((((uint64) base >> 24) & 0xFF) << 56) | ((uint64) limit & 0xFFFF) |
|
|
((((uint64) limit >> 16) & 0xF) << 48) | attr;
|
|
((uint8 *) gdt)[0] = (uint8) (seg_desc & 0xFF);
|
|
((uint8 *) gdt)[1] = (uint8) ((seg_desc >> 8) & 0xFF);
|
|
((uint8 *) gdt)[2] = (uint8) ((seg_desc >> 16) & 0xFF);
|
|
((uint8 *) gdt)[3] = (uint8) ((seg_desc >> 24) & 0xFF);
|
|
((uint8 *) gdt)[4] = (uint8) ((seg_desc >> 32) & 0xFF);
|
|
((uint8 *) gdt)[5] = (uint8) ((seg_desc >> 40) & 0xFF);
|
|
((uint8 *) gdt)[6] = (uint8) ((seg_desc >> 48) & 0xFF);
|
|
((uint8 *) gdt)[7] = (uint8) ((seg_desc >> 56) & 0xFF);
|
|
}
|
|
|
|
static void disable_pic()
|
|
{
|
|
out_8(PIC1_COMMAND, 0x11); // starts the initialization sequence (in cascade mode)
|
|
out_8(PIC2_COMMAND, 0x11);
|
|
|
|
out_8(PIC1_DATA, 32); // ICW2: Master PIC vector offset
|
|
out_8(PIC2_DATA, 40); // ICW2: Slave PIC vector offset
|
|
|
|
out_8(PIC1_DATA, 4); // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100)
|
|
out_8(PIC2_DATA, 2); // ICW3: tell Slave PIC its cascade identity (0000 0010)
|
|
|
|
out_8(PIC1_DATA, 0x1); // set their modes to 8086
|
|
out_8(PIC2_DATA, 0x1);
|
|
|
|
out_8(PIC1_DATA, 0xff);
|
|
out_8(PIC2_DATA, 0xff);
|
|
}
|
|
|
|
static void write_apic_reg(uint32 reg, uint32 val)
|
|
{
|
|
*(uint32 *) ((uintptr) apic_base + reg) = val;
|
|
}
|
|
|
|
static uint32 read_apic_reg(uint32 reg)
|
|
{
|
|
return *(uint32 *) ((uintptr) apic_base + reg);
|
|
}
|
|
|
|
static int32 init_apic()
|
|
{
|
|
uint32 eax = 1, ebx, ecx, edx;
|
|
cpuid(&eax, &ebx, &ecx, &edx);
|
|
|
|
if (!(edx & EDX_MASK_APIC))
|
|
{
|
|
return ENOSUPPORT;
|
|
}
|
|
|
|
disable_pic();
|
|
|
|
// hardware enable APIC
|
|
ecx = MSR_APIC_BASE;
|
|
read_msr(&ecx, &edx, &eax);
|
|
apic_base = R_PADDR(eax & 0xFFFF0000);
|
|
eax |= MSR_MASK_APIC;
|
|
write_msr(&ecx, &edx, &eax);
|
|
#ifdef KDBG
|
|
kprintf("APIC base address: 0x%x\n", (uint64) apic_base);
|
|
#endif
|
|
|
|
// map spurious interrupt and software enable APIC
|
|
uint32 reg = read_apic_reg(APIC_REG_SPURIOUS);
|
|
write_apic_reg(APIC_REG_SPURIOUS, reg | REG_SPURIOUS_APIC_ENABLE);
|
|
|
|
// Mask LINT0 LINT1
|
|
reg = read_apic_reg(APIC_REG_LINT0);
|
|
write_apic_reg(APIC_REG_LINT0, reg | (1 << 16));
|
|
reg = read_apic_reg(APIC_REG_LINT1);
|
|
write_apic_reg(APIC_REG_LINT1, reg | (1 << 16));
|
|
|
|
// configure APIC timer
|
|
write_apic_reg(APIC_REG_TIMER_DIV, 0xB); // 0x1011, divide by 1
|
|
write_apic_reg(APIC_REG_TIMER_INIT_CNT, APIC_TIMER_INIT_CNT);
|
|
write_apic_reg(APIC_REG_TIMER_CUR_CNT, APIC_REG_TIMER_INIT_CNT);
|
|
write_apic_reg(APIC_REG_TIMER, 0xFF020000 | INTR_VEC_TIMER); // periodic
|
|
|
|
// unblock all interrupts
|
|
write_cr8(0);
|
|
|
|
return ESUCCESS;
|
|
}
|
|
|
|
static void *timer_intr_handler(struct intr_frame *frame)
|
|
{
|
|
struct tcb *cur = get_cur_thread();
|
|
|
|
if (cur != NULL)
|
|
{
|
|
// hack to get away first scheduling 1st time
|
|
// save the stack pointer of the current thread
|
|
cur->rsp0 = (uint64) frame;
|
|
}
|
|
|
|
// run the scheduler
|
|
thread_schedule();
|
|
|
|
cur = get_cur_thread();
|
|
// now cur is the next thread to run
|
|
// swap address space
|
|
// which shouldn't page fault as we are in kernel
|
|
write_cr3(cur->proc->cr3);
|
|
flush_tlb();
|
|
|
|
// write tss segment
|
|
utss.rsp0 = (uint64)((uintptr)cur->kstack + cur->kstack_sz);
|
|
|
|
// now we are in the target address space
|
|
// the only thing to do is to switch stack, which will be done in ASM handler
|
|
return (void *) cur->rsp0;
|
|
}
|
|
|
|
extern char init_stack[];
|
|
|
|
int32 intr_init()
|
|
{
|
|
int32 ret;
|
|
// init GDT
|
|
write_gdt_desc(&gdt[GDT_NULL], 0, 0, 0); // empty desc
|
|
write_gdt_desc(&gdt[GDT_K_CODE], 0, 0xFFFFFFFF,
|
|
SEG_LONG | SEG_DPL_0 | SEG_PRESENT | SEG_GRANULARITY | SEG_CODE_DATA |
|
|
SEG_TYPE_CODE_XR); // kernel code
|
|
write_gdt_desc(&gdt[GDT_K_DATA], 0, 0xFFFFFFFF,
|
|
SEG_LONG | SEG_DPL_0 | SEG_PRESENT | SEG_GRANULARITY | SEG_CODE_DATA |
|
|
SEG_TYPE_DATA_RW); // kernel data
|
|
write_gdt_desc(&gdt[GDT_U_CODE], 0, 0xFFFFFFFF,
|
|
SEG_LONG | SEG_DPL_3 | SEG_PRESENT | SEG_GRANULARITY | SEG_CODE_DATA |
|
|
SEG_TYPE_CODE_XR); // user code
|
|
write_gdt_desc(&gdt[GDT_U_DATA], 0, 0xFFFFFFFF,
|
|
SEG_LONG | SEG_DPL_3 | SEG_PRESENT | SEG_GRANULARITY | SEG_CODE_DATA |
|
|
SEG_TYPE_DATA_RW); // user data
|
|
// write tss
|
|
write_gdt_desc(&gdt[GDT_U_TSS], (uint32) &utss, sizeof(struct tss),
|
|
SEG_PRESENT | SEG_DPL_3 | ((uint64)9 << 40));
|
|
// write high bits
|
|
*(uint64 *) &gdt[GDT_U_TSS + 1] = ((uintptr)&utss) >> 32;
|
|
|
|
// init tss
|
|
mem_set(&utss, 0, sizeof(struct tss));
|
|
|
|
gdtptr.size = GDT_SIZE - 1;
|
|
gdtptr.offset = (uintptr) gdt;
|
|
flush_gdt(&gdtptr, SEL(GDT_K_CODE, 0, 0), SEL(GDT_K_DATA, 0, 0));
|
|
flush_tss(SEL(GDT_U_TSS, 0, 3));
|
|
|
|
idtptr.offset = (uintptr) idt;
|
|
idtptr.size = IDT_SIZE - 1;
|
|
for (int i = 0; i < NUM_IDT_DESC; i++)
|
|
{
|
|
write_idt_desc(&idt[i], (uintptr) intr_stub_array[i],
|
|
SEL(GDT_K_CODE, 0, 0),
|
|
GATE_DPL_3 | GATE_TYPE_INTERRUPT | GATE_PRESENT);
|
|
}
|
|
mem_set(intr_disp_tbl, 0, sizeof(intr_disp_tbl));
|
|
flush_idt(&idtptr);
|
|
ret = init_apic();
|
|
|
|
if (ret == ESUCCESS)
|
|
{
|
|
// enable interrupt flag but mask all external interrupts
|
|
WRITE_IRQ(0xf);
|
|
sti();
|
|
|
|
set_intr_handler(INTR_VEC_TIMER, timer_intr_handler);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void set_intr_handler(uint32 vec, intr_handler handler)
|
|
{
|
|
intr_disp_tbl[vec] = handler;
|
|
}
|
|
|
|
void stop_cpu()
|
|
{
|
|
// stop interrupt and halt
|
|
cli();
|
|
hlt();
|
|
}
|
|
|
|
// this returns info returned by intr_disp_tbl
|
|
// in case the assembly stub needs them (e.g. ctx swap)
|
|
void *intr_dispatcher(uint32 vec, struct intr_frame *frame)
|
|
{
|
|
void *ret = NULL;
|
|
|
|
if (intr_disp_tbl[vec] != NULL)
|
|
{
|
|
ret = intr_disp_tbl[vec](frame);
|
|
}
|
|
else
|
|
{
|
|
if (vec <= INTR_LIMIT_INTEL)
|
|
{
|
|
kprintf("[PANIC] Exception %d has no handler. RIP: 0x%x ERR:0x%x\n", (uint64) vec, (uint64) frame->rip,
|
|
(uint64) frame->error_code);
|
|
stop_cpu();
|
|
}
|
|
else
|
|
{
|
|
kprintf("[WARN] Interrupt %d has no handler. RIP: 0x%x\n", (uint64) vec, (uint64) frame->rip);
|
|
}
|
|
}
|
|
|
|
if (vec >= INTR_VEC_TIMER && vec != INTR_VEC_SPURIOUS)
|
|
{
|
|
// if it's delivered by local APIC, signal EOI
|
|
write_apic_reg(APIC_REG_EOI, 0);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static uint32 get_core()
|
|
{
|
|
return read_apic_reg(APIC_REG_ID) >> 24;
|
|
}
|
|
|
|
|
|
void send_ipi(uint32 vec)
|
|
{
|
|
// we decide to not support multicore for now. So hardcode to the current core
|
|
// used to emulate IO stuff
|
|
uint64 reg = ((uint64) get_core() << 56) | IPI_DLM_FIXED | IPI_DSM_PHYS | IPI_LVL_ASS | (vec & 0xff);
|
|
|
|
// block all interrupts because there is no context
|
|
uint64 irq = READ_IRQ();
|
|
WRITE_IRQ(0xf);
|
|
|
|
write_apic_reg(APIC_REG_ICR_L, (uint32)reg);
|
|
write_apic_reg(APIC_REG_ICR_H, (uint32)(reg >> 32));
|
|
// block until successfully delivered
|
|
while (reg & IPI_STATUS_MASK)
|
|
{
|
|
reg = read_apic_reg(APIC_REG_ICR_L);
|
|
}
|
|
|
|
WRITE_IRQ(irq);
|
|
}
|
|
|