diff options
| author | Miguel <m.i@gmx.at> | 2018-09-10 19:08:04 +0200 |
|---|---|---|
| committer | Miguel <m.i@gmx.at> | 2018-09-10 19:08:04 +0200 |
| commit | ee2ec6abbcfb6de48a8f5594e05ba0a837216fa8 (patch) | |
| tree | 6173e5182459529ac5dc3fbb59b30fd37c5ee4ab /kernel/apic.c | |
| parent | 65f5cca027af81e77b3e06da658b6d13f1861a03 (diff) | |
apic et al.
Diffstat (limited to 'kernel/apic.c')
| -rw-r--r-- | kernel/apic.c | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/kernel/apic.c b/kernel/apic.c new file mode 100644 index 0000000..a670208 --- /dev/null +++ b/kernel/apic.c @@ -0,0 +1,169 @@ +#include "kernel.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; + io_apic_addr=info->io_apic_address; + 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 + // PIT irq 00 -> 02 flags 0 -> 0x90 + // kb irq 01 -> 01 flags ? -> 0x91 + // mouse irq 12 -> 12 flags ? -> 0x92 + ioapic_config_entry(2,0x90,0x0); + ioapic_config_entry(1,0x91,0x0); + ioapic_config_entry(12,0x92,0x0); +} + +/** 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 +} |
