#include "kernel.h" #include "log.h" #include "apic.h" #include "interrupts.h" #include "asm_pit.h" #define APIC_APICID 0x20 #define APIC_APICVER 0x30 #define APIC_TASKPRIOR 0x80 #define APIC_EOI 0x0B0 #define APIC_LDR 0x0D0 #define APIC_DFR 0x0E0 #define APIC_SPURIOUS 0x0F0 #define APIC_ESR 0x280 #define APIC_ICRL 0x300 #define APIC_ICRH 0x310 #define APIC_LVT_TMR 0x320 #define APIC_LVT_PERF 0x340 #define APIC_LVT_LINT0 0x350 #define APIC_LVT_LINT1 0x360 #define APIC_LVT_ERR 0x370 #define APIC_TMRINITCNT 0x380 #define APIC_TMRCURRCNT 0x390 #define APIC_TMRDIV 0x3E0 #define APIC_LAST 0x38F #define APIC_DISABLE 0x10000 #define APIC_SW_ENABLE 0x100 #define APIC_CPUFOCUS 0x200 #define APIC_NMI (4<<8) #define TMR_PERIODIC 0x20000 #define TMR_BASEDIV (1<<20) static uint32_t local_apic_addr; static uint32_t io_apic_addr; static uint32_t bus_speed; static void ioapic_write(uint32_t offset, uint32_t value) { uint32_t *reg=io_apic_addr; reg[0]=(offset & 0xff); reg[4]= value; } static uint32_t ioapic_read(uint32_t offset) { uint32_t *reg=io_apic_addr; reg[0]=(offset & 0xff); return reg[4]; } static void ioapic_config_entry(uint32_t irq, uint32_t low, uint32_t high) { ioapic_write(0x10+irq*2,low); ioapic_write(0x11+irq*2,high); } static void apic_write(uint32_t offset, uint32_t value) { uint32_t *reg; reg=local_apic_addr+offset; *reg=value; } static uint32_t apic_read(uint32_t offset) { uint32_t *reg; reg=local_apic_addr+offset; uint32_t value=*reg; return value; } uint32_t apic_id() { return apic_read(APIC_APICID)>>24; } void apic_eoi() { apic_write(0xB0,0); } void apic_ipi(uint8_t dest, uint8_t number) { apic_write(APIC_ICRH,dest<<24); // destination apic bits 24-27 apic_write(APIC_ICRL,number | (1<<14)); // send ipi } void apic_enable() { apic_write(APIC_SPURIOUS,apic_read(APIC_SPURIOUS)|0x100); apic_write(APIC_SPURIOUS,apic_read(APIC_SPURIOUS)|0xFF); // set spurious to 255 (default anyway) } /** select mode : divisor. * 0 - 1 * 1 - 2 * 2 - 4 * 3 - 8 * 4 - 16 * 5 - 32 * 6 - 64 * 7 - 128 */ static uint32_t apic_get_bus_speed(uint32_t sel) { uint32_t div[]={1,2,4,8,16,32,64,128}; uint32_t reg[]={0b1011,0,1,2,3,0b1000,0b1001,0b1010}; uint32_t divisor=div[sel]; klog("Probing bus speed for 50ms (div=%d)) ...",divisor); apic_write(APIC_TMRDIV, reg[sel]); apic_write(APIC_TMRINITCNT, 0xFFFFFFFF); asm_pit_sleep_50ms(); //writeAPIC(APIC_LVT_TMR, APIC_LVT_INT_MASKED); //?? uint32_t ticksInS = 0xFFFFFFFF - apic_read(APIC_TMRCURRCNT); ticksInS*=20; // adjust to one full second. klog("%d MHz (%d Hz) bus speed (ticks=%d)",ticksInS/(1000000/divisor),ticksInS*divisor,ticksInS/20); return ticksInS*divisor; } /** run this before anything else */ void apic_init(acpi_information *info) { fixme("how to support IPI addressing more than 16cpus?"); fixme("check via cpuid if apic exist?"); local_apic_addr=info->local_apic_address; local_apic_addr=VMEM_LAPIC; io_apic_addr=VMEM_IOAPIC; bus_speed=apic_get_bus_speed(4); // get bus speed (divisor: 16) } /** set ticks per second for timer interrupt*/ void apic_init_timer(uint32_t ticks_per_second) { uint32_t countdown=bus_speed/16/ticks_per_second; apic_write(APIC_TMRDIV, 0x3); // divisor 16 apic_write(APIC_LVT_TMR, INTERRUPT_APIC_TIMER | TMR_PERIODIC); apic_write(APIC_TMRINITCNT, countdown); } /** setup io apic */ void ioapic_config() { fixme("use acpi info to setup IOAPIC"); // setup IO APIC // DEVICE SOURCE OVERRIDE FLAGS FOOL-OS-INTERRUPT // PIT 00 -> 02 0 high/edge 0x90 / 144 // kb 01 -> 01 ? 0x91 / 145 // mouse 12 -> 12 ? 0x92 / 146 // e1000 11 -> 11 13? high/level 0x93 / 147 // ioapic_config_entry(3,0x90|0x2000,0x3<<24); // egde trigger on falling // ioapic_config_entry(2,0x90|0x8000,0x3<<24); // level trigger on high // ioapic_config_entry(2,0x90|0xa000,0x3<<24); // level trigger on low // CPU ioapic_config_entry(2, INTERRUPT_PIT_TIMER, 0x0<<24); // pit ioapic_config_entry(1, INTERRUPT_KEYBOARD, 0x0<<24); // kb ioapic_config_entry(12, INTERRUPT_MOUSE, 0x0<<24); // mouse ioapic_config_entry(11, INTERRUPT_E1000|0x8000, 0x0<<24); // e1000 (level trigger on high) } /** startup other cpus*/ void apic_sipi(uint32_t dest,uint32_t addy) { // uint32_t addy=0x7; fixme("poll flag/timeout"); apic_write(APIC_ICRH,dest<<24); //select destionation cpu/apic apic_write(APIC_ICRL,(5<<8)|(1<<14)); //101 INIT IPI // TODO: wait 10 milliseconds apic_write(APIC_ICRL,(6<<8)|(1<<14)|addy); /// 110 SIPI // TODO: poll a flag?(timeout 1ms) // TODO retry 110 SIPI with 1s timeout }