summaryrefslogtreecommitdiff
path: root/kernel/apic.c
diff options
context:
space:
mode:
authorMiguel <m.i@gmx.at>2018-09-10 19:08:04 +0200
committerMiguel <m.i@gmx.at>2018-09-10 19:08:04 +0200
commitee2ec6abbcfb6de48a8f5594e05ba0a837216fa8 (patch)
tree6173e5182459529ac5dc3fbb59b30fd37c5ee4ab /kernel/apic.c
parent65f5cca027af81e77b3e06da658b6d13f1861a03 (diff)
apic et al.
Diffstat (limited to 'kernel/apic.c')
-rw-r--r--kernel/apic.c169
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
+}