BarryServer : Git

All the code for all my projects
// BarryServer : Git / Orion / commit / d41a53cbc7d055b1c00cf0a339dbed6925f4f02c / proc / apic.c

// Related

Orion

Barry Importing existing Orion kernel d41a53c (2 years, 4 months ago)
diff --git a/proc/apic.c b/proc/apic.c
new file mode 100644
index 0000000..a784f50
--- /dev/null
+++ b/proc/apic.c
@@ -0,0 +1,258 @@
+/*
+ * This file contains code dealing with the Advanced Programmable Interrupt
+ * Controller system.  It should proved a drop-in replacement for the standard
+ * PIC system, abstracting it in the same way.  It should also be able to handle
+ * a non-SMP system, and fall back on the PIC.
+ */
+
+#include <stdint.h>
+#include "proc.h"
+#include "../mem/heap.h"
+#include "../mem/mem.h"
+#include "../task/task.h"
+#include "../io.h"
+#include "../screen.h"
+#include "../spinlock.h"
+
+/* RSDP Descriptor */
+typedef struct RSDPDescriptor {
+	char signature[8];
+	uint8_t checksum;
+	char oem[6];
+	uint8_t revision;
+	uint32_t rsdt;
+} __attribute__((packed)) RSDPDescriptor;
+
+/* System Descriptor Table Header */
+typedef struct SDTHeader {
+	char signature[4];
+	uint32_t length;
+	uint8_t revision;
+	uint8_t checksum;
+	char oem[6];
+	char oemTable[8];
+	uint32_t oemRevision;
+	uint32_t creator;
+	uint32_t creatorRevision;
+} SDTHeader;
+
+/* IOAPIC Interrupt Source Override */
+typedef struct IOAPICOverride {
+	uint8_t type, length;
+	uint8_t bus, irq;
+	uint8_t gsi, flags;
+} IOAPICOverride;
+
+void cpu_load(void);
+extern void ap_trampoline(void);
+
+uint8_t numCores;
+uint8_t bspId;
+uint32_t lapicPtr, ioapicPtr;
+uint8_t lapicIds[MAX_CPUS], lapicNums[MAX_CPUS];
+Spinlock timerLock;
+
+/* Enable APIC */
+static void
+enable_apic(void)
+{
+	LAPIC(0xF0) = 0x1FF;
+	LAPIC(0x80) = 0;
+}
+
+/* Read from the IOAPIC */
+static uint32_t
+read_io_apic(uint32_t reg)
+{
+	uint32_t volatile *ioapic = (uint32_t volatile *) ioapicPtr;
+	ioapic[0] = (reg & 0xFF);
+	return ioapic[4];
+}
+
+/* Write to the IOAPIC */
+static void
+write_io_apic(uint32_t reg, uint32_t value)
+{
+	uint32_t volatile *ioapic = (uint32_t volatile *) ioapicPtr;
+	ioapic[0] = (reg & 0xFF);
+	ioapic[4] = value;
+}
+
+/* Delay in gaps of 100 microseconds */
+static void
+delay(uint32_t time)
+{
+	float calc = (1193182 / 10000) * time;
+	uint32_t divisor = (uint32_t) calc;
+	outb(0x43, 0x30);
+	outb(0x40, divisor & 0xFF);
+	outb(0x40, (divisor >> 8) & 0xFF);
+	do outb(0x43, 0xE2); while (!(inb(0x40) & (1 << 7)));
+}
+
+/* Start the APIC timer */
+static void
+apic_start_timer(void)
+{
+	/* Don't run while another timer is calibrating */
+	acquire(&timerLock);
+
+	/* Start LAPIC countdown from -1 */
+	LAPIC(0x3E0) = 3;
+	LAPIC(0x380) = 0xFFFFFFFF;
+	delay(10);
+
+	/* Stop after PIT fires, count LAPIC ticks */
+	LAPIC(0x320) = 0x10000;
+	uint32_t ticks = 0xFFFFFFFF - LAPIC(0x390);
+
+	/* Tick every 1ms */
+	LAPIC(0x320) = 0x20000 | 32;
+	LAPIC(0x3E0) = 3;
+	LAPIC(0x380) = ticks;
+
+	release(&timerLock);
+}
+
+/* Initialise SMP (RSDT method) */
+void
+init_multicore(void *ebda)
+{
+	numCores = 1;
+	memset(lapicIds, 0, MAX_CPUS);
+	memset(lapicNums, 0, MAX_CPUS);
+
+	init_lock(&timerLock);
+
+	/* Search for SMP tables, start from EBDA start */
+	char *signature = (char *) ebda;
+	uint8_t found;
+	while (signature < (char *) 0x100000) {
+		/* RSDT */
+		if (!memcmp(signature, "RSD PTR ", 8)) {
+			found = 1;
+			break;
+		}
+		signature++;
+	}
+	/* Fall back to PIC */
+	if (!found)
+		return;
+	/* RSDT checksum */
+	uint32_t check = 0, i, j;
+	for (i = 0; i < sizeof(RSDPDescriptor); i++)
+		check += signature[i];
+	if (check % 0x100)
+		return;
+
+	uint8_t *ptr, *ptr2;
+	uint8_t *rsdt, len;
+	uint8_t overrides[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
+	IOAPICOverride *iso;
+
+	/* Find APIC table */
+	kprintf("Searching for APIC table");
+	asm volatile("mov $1, %%eax; cpuid; shrl $24, %%ebx" : "=b" (bspId));
+	numCores = 0;
+	rsdt = (uint8_t *) ((RSDPDescriptor *) signature)->rsdt;
+	len = *((uint32_t *) (rsdt + 4));
+	for (ptr2 = rsdt + 36; ptr2 < rsdt + len; ptr2 += 4) {
+		ptr = *((uint8_t **) ptr2);
+		if (!memcmp(ptr, "APIC", 4))
+			break;
+	}
+	if (memcmp(ptr, "APIC", 4))
+		return;
+
+	/* Read APIC table */
+	lapicPtr = *((uint32_t *) (ptr + 0x24));
+	ptr2 = ptr + *((uint32_t *) (ptr + 4));
+	for (ptr += 44; ptr < ptr2; ptr += ptr[1]) {
+		/* Entries */
+		switch (ptr[0]) {
+		case 0: /* Processor Local APIC */
+			if (numCores >= MAX_CPUS)
+				continue;
+			if (ptr[4] & 1) {
+				lapicNums[ptr[3]] = numCores;
+				lapicIds[numCores] = ptr[3];
+				numCores++;
+			}
+			break;
+		case 1: /* IOAPIC */
+			ioapicPtr = *((uint32_t *) (ptr + 4));
+			break;
+		case 2: /* IOAPIC ISO */
+			iso = (void *) ptr;
+			overrides[iso->irq] = iso->gsi;
+			kprintf("  IRQ#%.2d -> GSI#%.2d",
+			        iso->irq, iso->gsi);
+			break;
+		}
+	}
+
+	/* Send INT#1 to IRQ#33 on CPU#0 */
+	write_io_apic(0x10 + (2 * overrides[1] + 0), 33);
+	write_io_apic(0x10 + (2 * overrides[1] + 1), bspId);
+
+	kprintf("Detected %d CPU%c", numCores, (numCores>1)?'s':'\0');
+
+	/* Initialise APs */
+	uintptr_t apTrampoline = 0x2000;
+	memcpy((void *) apTrampoline, &ap_trampoline, 0x1000);
+	for (i = 0; i < numCores; i++) {
+		if (lapicIds[i] == bspId)
+			continue;
+		/* Give each CPU a separate stack */
+		*((uint32_t *) (apTrampoline + 0xFF0)) = 0x7A000 - (i * 0x2000);
+		/* Send INIT IPI */
+		LAPIC(0x280) = 0;
+		LAPIC(0x310) = (LAPIC(0x310) & 0x00FFFFFF)
+		             | (lapicIds[i] << 24);
+		LAPIC(0x300) = (LAPIC(0x300) & 0xFFF00000)
+		             | 0xC500;
+		do {
+			asm("pause" ::: "memory");
+		} while (LAPIC(0x300) & (1 << 12));
+		LAPIC(0x310) = (LAPIC(0x310) & 0x00FFFFFF)
+		             | (lapicIds[i] << 24);
+		LAPIC(0x300) = (LAPIC(0x300) & 0xFFF00000)
+		             | 0x8500;
+		do {
+			asm("pause" ::: "memory");
+		} while (LAPIC(0x300) & (1 << 12));
+		delay(100);
+		/* Send STARTUP IPI (twice) */
+		for (j = 0; j < 2; j++) {
+			LAPIC(0x280) = 0;
+			LAPIC(0x310) = (LAPIC(0x310) & 0x00FFFFFF)
+			             | (lapicIds[i] << 24);
+			LAPIC(0x300) = (LAPIC(0x300) & 0xFFF0F800)
+			             | (0x0600 + ((apTrampoline >> 12) & 0xFF));
+			delay(2);
+			do {
+				asm("pause" ::: "memory");
+			} while (LAPIC(0x300) & (1 << 12));
+		}
+	}
+
+	/* Enable APIC */
+	enable_apic();
+	/* Disable PIC */
+	outb(0x21, 0xFF);
+	outb(0xA1, 0xFF);
+
+	apic_start_timer();
+}
+
+/* AP initialisation routine */
+void
+ap_startup(void)
+{
+//	enable_apic();
+	cpu_load();
+//	apic_start_timer();
+	current = NULL;
+	kprintf("CPU#%d idling", CPUID);
+	while (1) asm volatile("hlt");
+}