Nucleus
Barry Per-CPU Scheduler bb0cb77 (3 years, 2 months ago)
diff --git a/kernel/acpi/apic.c b/kernel/acpi/apic.c
index c7f8259..5c2297d 100644
--- a/kernel/acpi/apic.c
+++ b/kernel/acpi/apic.c
@@ -55,7 +55,7 @@ struct ISOEntry {
};
int apic = 0;
-size_t numCores = 1;
+size_t ncpus = 1;
uintptr_t lapicPtr, ioapicPtr;
cpu_t lapicIds[MAX_CPUS], lapicNums[MAX_CPUS];
@@ -128,7 +128,7 @@ void
init_apic(struct SDTHeader *header)
{
apic = 1;
- numCores = 0;
+ ncpus = 0;
uint8_t overrides[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
@@ -144,14 +144,14 @@ init_apic(struct SDTHeader *header)
while ((void *) entry < (((void *) header) + header->length)) {
switch (entry->type) {
case ENTRY_LAPIC:
- if (numCores >= MAX_CPUS)
+ if (ncpus >= MAX_CPUS)
break;
lapic = (void *) entry;
if (!(lapic->flags & 1))
break;
- lapicNums[lapic->id] = numCores;
- lapicIds[numCores] = lapic->id;
- numCores++;
+ lapicNums[lapic->id] = ncpus;
+ lapicIds[ncpus] = lapic->id;
+ ncpus++;
break;
case ENTRY_IOAPIC:
ioapic = (void *) entry;
@@ -176,7 +176,7 @@ init_apic(struct SDTHeader *header)
uint32_t i, j;
uintptr_t apTrampoline = 0x1000, stack;
memcpy((void *) apTrampoline, &ap_trampoline, PAGE_SIZE);
- for (i = 1; i < numCores; i++) {
+ for (i = 1; i < ncpus; i++) {
/* Give each processor a separate stack */
stack = alloc_frame() + PAGE_SIZE - sizeof(uintptr_t);
*((uint32_t *) (apTrampoline + 0xF00 + i)) = stack;
@@ -230,3 +230,14 @@ ap_startup(void)
schedule();
__builtin_unreachable();
}
+
+/* Send an IPI */
+void
+send_ipi(cpu_t target, uint8_t num)
+{
+ LAPIC(0x310) = (LAPIC(0x310) & 0x00FFFFFF)
+ | (lapicIds[target] << 24);
+ LAPIC(0x300) = (LAPIC(0x300) & 0xFFF32000)
+ | (0x5000 + num + 48);
+ while ((LAPIC(0x300) >> 12) & 1);
+}
diff --git a/kernel/cpu.c b/kernel/cpu.c
new file mode 100644
index 0000000..be56047
--- /dev/null
+++ b/kernel/cpu.c
@@ -0,0 +1,154 @@
+/*
+ * This file implements the CPU setup and control functions, in particular the
+ * IPI messaging system. Each CPU has a message queue (IPIQ) which can be
+ * written by other CPUs. Each message describes a function that shoudl be run
+ * on the target CPU. When signalled via an IPI, a CPU will check it's queue for
+ * work and execute anything outstanding. Sending a message to an IPIQ will
+ * wait for the IPI to be received. If the message is synchronous (IPIQ_SYNC)
+ * then the call will also wait for the function to return on the target. If
+ * the message is asynchronous (IPIQ_ASYNC) then it will return immediately and
+ * the call may be completed at any time, with the target performing any
+ * necessary clean-up.
+ */
+
+#include <nucleus/cpu.h>
+#include <nucleus/memory.h>
+#include <nucleus/task.h>
+#include "desc.h"
+
+/* Structure for a IPI Message Queue */
+struct IPIQueue {
+ struct IPIMessage *msg[32];
+ uint32_t slots;
+ int working;
+};
+
+/* Structure for an IPI Message */
+struct IPIMessage {
+ enum IPIQFlag flags;
+ ipiq_func_t func;
+ void *arg;
+};
+
+void send_ipi(cpu_t target, uint8_t num);
+
+Processor __seg_gs *const cpu = 0;
+Processor *cpus[MAX_CPUS];
+
+/* Per-CPU setup */
+void
+cpu_load(void)
+{
+ /* Initialise SSE */
+ asm volatile(
+ "mov %cr0, %eax;"
+ "andl $0xfffffffb, %eax;"
+ "orl $0x2, %eax;"
+ "mov %eax, %cr0;"
+ "mov %cr4, %eax;"
+ "orl $0x600, %eax;"
+ "mov %eax, %cr4;"
+ );
+
+ /* Initialise CR4.PGE */
+ asm volatile(
+ "mov %cr4, %eax;"
+ "orl $0x10, %eax;"
+ "mov %eax, %cr4;"
+ );
+
+ /* Tables */
+ cpu_load_idt();
+ cpu_load_gdt();
+
+ /* Processor structure */
+ cpu_t id = CPUID;
+ cpus[id] = kmalloc(sizeof(Processor));
+ set_gs_base((uintptr_t) cpus[id]);
+ cpu->self = cpus[id];
+ cpu->id = id;
+ cpu->scheduler = new(&schedulerType);
+ cpu->ipiq = kmalloc(sizeof(struct IPIQueue));
+
+ asm volatile("sti");
+}
+
+/* IPI Queue handler */
+void
+ipiq_handler(struct InterruptFrame *frame)
+{
+ struct IPIMessage *msg;
+ struct IPIQueue *queue = cpu->ipiq;
+
+ if (queue->working || !queue->slots)
+ return;
+
+ enter_critical_section();
+ queue->working++;
+
+ /* Dispatch all messages that are pending */
+ uint8_t slot;
+ for (slot = 0; slot < 32; slot++) {
+ msg = queue->msg[slot];
+ /* Check the bitset for used slots */
+ if (!(queue->slots & (1 << slot)))
+ continue;
+ queue->slots &= ~(1 << slot);
+ msg->func(msg->arg);
+ if (msg->flags & IPIQ_SYNC)
+ msg->flags |= IPIQ_DONE;
+ else
+ kfree(msg);
+ }
+
+ queue->working--;
+ exit_critical_section();
+}
+
+/* Request a CPU run a function */
+void
+send_ipiq(cpu_t targid, ipiq_func_t func, void *arg, enum IPIQFlag flags)
+{
+ enter_critical_section();
+ if (__builtin_expect((targid == cpu->id), 0)) {
+ func(arg);
+ goto end;
+ }
+
+ /* Construct IPI message */
+ struct IPIMessage *msg = kmalloc(sizeof(struct IPIMessage));
+ msg->func = func;
+ msg->arg = arg;
+ msg->flags = flags & ~IPIQ_DONE;
+
+ /* Find slot and send */
+ uint8_t slot;
+ struct IPIQueue *queue = cpus[targid]->ipiq;
+find_slot:
+ for (slot = 0; slot < 32; slot++) {
+ /*
+ * Search for a free slot by testing and setting each bit in the
+ * bitset until one is changed. One bit per slot minimises the
+ * contention and guarantees synchronisation when acted on
+ * atomically. Only try to set one bit at a time, then test that
+ * bit, none of the others.
+ */
+ if (!((__atomic_fetch_xor(&queue->slots, 1 << slot,
+ __ATOMIC_ACQUIRE) >> slot) & 1))
+ break;
+ }
+ if (slot == 32)
+ goto find_slot;
+ queue->msg[slot] = msg;
+ if (!queue->working)
+ send_ipi(targid, 0);
+
+ /* Wait for completion */
+ if (flags & IPIQ_SYNC) {
+ while (!(msg->flags & IPIQ_DONE));
+ kfree(msg);
+ }
+
+end:
+ exit_critical_section();
+}
diff --git a/kernel/idt.c b/kernel/idt.c
index 6aceb88..37d38d6 100644
--- a/kernel/idt.c
+++ b/kernel/idt.c
@@ -21,7 +21,6 @@ static struct IDTEntry {
uint16_t offsetHigher;
} __attribute__((packed)) *IDT;
-void (**exceptions)(struct InterruptFrame *);
void (**interrupts)(struct InterruptFrame *);
/* Install an IDT Entry */
@@ -43,12 +42,12 @@ install_idt_entry(uint8_t num, void *addr)
void
isr_handler(struct InterruptFrame frame)
{
- if (!exceptions[frame.intnum] && frame.intnum < 32)
+ if (!interrupts[frame.intnum] && frame.intnum < 32)
panic("[CPU#%d] Failed to handle exception %d (%#.8x) @ %#.8x",
cpu->id, frame.intnum, frame.err, frame.eip);
/* Run registered handler */
- exc_handler_t handler = exceptions[frame.intnum];
+ int_handler_t handler = interrupts[frame.intnum];
if (handler)
handler(&frame);
@@ -65,18 +64,25 @@ isr_handler(struct InterruptFrame frame)
/* Register an exception handler */
void
-register_exception(int num, exc_handler_t addr)
+register_exception(uint8_t num, int_handler_t addr)
{
- if (num >= 0 && num < 256)
- exceptions[num] = addr;
+ interrupts[num] = addr;
}
/* Register an interrupt handler */
void
-register_interrupt(int num, int_handler_t addr)
+register_interrupt(uint8_t num, int_handler_t addr)
{
if (num >= 0 && num < 16)
- interrupts[num] = addr;
+ register_exception(num + 32, addr);
+}
+
+/* Register an IPI handler */
+void
+register_ipi(uint8_t num, int_handler_t addr)
+{
+ if (num >= 0 && num < 16)
+ register_exception(num + 48, addr);
}
/* Exceptions */
@@ -88,6 +94,10 @@ extern char exc0[], exc1[], exc2[], exc3[], exc4[], exc5[], exc6[],
extern char exc32[], exc33[], exc34[], exc35[], exc36[], exc37[], exc38[],
exc39[], exc40[], exc41[], exc42[], exc43[], exc44[], exc45[],
exc46[], exc47[];
+/* IPIs */
+extern char exc48[], exc49[], exc50[], exc51[], exc52[], exc53[], exc54[],
+ exc55[], exc56[], exc57[], exc58[], exc59[], exc60[], exc61[],
+ exc62[], exc63[];
/* System Call */
extern char exc128[];
@@ -139,12 +149,29 @@ init_idt(void)
install_idt_entry(46, exc46);
install_idt_entry(47, exc47);
+ /* Install IPIs */
+ install_idt_entry(48, exc48);
+ install_idt_entry(49, exc49);
+ install_idt_entry(50, exc50);
+ install_idt_entry(51, exc51);
+ install_idt_entry(52, exc52);
+ install_idt_entry(53, exc53);
+ install_idt_entry(54, exc54);
+ install_idt_entry(55, exc55);
+ install_idt_entry(56, exc56);
+ install_idt_entry(57, exc57);
+ install_idt_entry(58, exc58);
+ install_idt_entry(59, exc59);
+ install_idt_entry(60, exc60);
+ install_idt_entry(61, exc61);
+ install_idt_entry(62, exc62);
+ install_idt_entry(63, exc63);
+
/* Install system call handler */
install_idt_entry(128, exc128);
- exceptions = (void *) (IDT + 256);
- interrupts = (void *) (exceptions + 32);
- memset(exceptions, 0, 1024);
+ interrupts = (void *) (IDT + 256);
+ memset(interrupts, 0, 1024);
}
/* Load the IDT */
diff --git a/kernel/isr.S b/kernel/isr.S
index 914d855..b1b849e 100644
--- a/kernel/isr.S
+++ b/kernel/isr.S
@@ -63,6 +63,23 @@ EXC_NOERR 44
EXC_NOERR 45
EXC_NOERR 46
EXC_NOERR 47
+/* IPIs */
+EXC_NOERR 48
+EXC_NOERR 49
+EXC_NOERR 50
+EXC_NOERR 51
+EXC_NOERR 52
+EXC_NOERR 53
+EXC_NOERR 54
+EXC_NOERR 55
+EXC_NOERR 56
+EXC_NOERR 57
+EXC_NOERR 58
+EXC_NOERR 59
+EXC_NOERR 60
+EXC_NOERR 61
+EXC_NOERR 62
+EXC_NOERR 63
/* System Call */
EXC_NOERR 128
diff --git a/kernel/main.c b/kernel/main.c
index 76d5ef1..b7d8eb8 100644
--- a/kernel/main.c
+++ b/kernel/main.c
@@ -17,41 +17,10 @@
#include "desc.h"
#include "acpi/acpi.h"
-struct CPUData __seg_gs *cpu = 0;
-
extern char _bss[], _end[];
void page_fault_handler(struct InterruptFrame *frame);
-
-/* Per-CPU Setup */
-void
-cpu_load(void)
-{
- /* Initialise SSE */
- asm volatile(
- "mov %cr0, %eax;"
- "andl $0xfffffffb, %eax;"
- "orl $0x2, %eax;"
- "mov %eax, %cr0;"
- "mov %cr4, %eax;"
- "orl $0x600, %eax;"
- "mov %eax, %cr4;"
- );
-
- /* Initialise CR4.PGE */
- asm volatile(
- "mov %cr4, %eax;"
- "orl $0x10, %eax;"
- "mov %eax, %cr4;"
- );
-
- /* Tables */
- cpu_load_idt();
- cpu_load_gdt();
- set_gs_base((uintptr_t) kmalloc(sizeof(struct CPUData)));
- cpu->id = CPUID;
-
- asm volatile("sti");
-}
+void ipiq_handler(struct InterruptFrame *frame);
+void cpu_load(void);
/* Kernel main function */
_Noreturn void
@@ -67,6 +36,7 @@ kmain(struct MultibootInfo *mbinfo)
init_idt();
init_pic();
cpu_load();
+ register_ipi(0, ipiq_handler);
/* Initialise paging */
init_paging();
diff --git a/kernel/panic.c b/kernel/panic.c
index 2c07d24..68b68b5 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -5,6 +5,8 @@
* be compiled in.
*/
+#include <string.h>
+#include <nucleus/cpu.h>
#include <nucleus/kernel.h>
#include <io.h>
@@ -12,6 +14,15 @@
_Noreturn void
panic(char *fmt, ...)
{
+ if (!fmt)
+ goto halt;
+ Processor *proc;
+ for_each_cpu(proc) {
+ if (proc == cpu->self)
+ continue;
+ send_ipiq(proc->id, (ipiq_func_t) panic, NULL, IPIQ_ASYNC);
+ }
+
outb(0xE9, '\033');
outb(0xE9, '[');
outb(0xE9, '3');
@@ -19,6 +30,13 @@ panic(char *fmt, ...)
outb(0xE9, 'm');
char buf[1024], *p = buf;
+ memset(buf, 0, 1024);
+
+ sprintf(buf, "[CPU#%d] ", cpu->id);
+ while (*p)
+ outb(0xE9, *p++);
+ memset(buf, 0, 1024);
+ p = buf;
/* Print error to serial port */
va_list args;
@@ -29,6 +47,7 @@ panic(char *fmt, ...)
outb(0xE9, *p++);
outb(0xE9, '\n');
+halt:
/* Halt processor */
while (1) asm volatile("cli; hlt");
__builtin_unreachable();
diff --git a/kernel/printf.c b/kernel/printf.c
index d0d55fa..858f26e 100644
--- a/kernel/printf.c
+++ b/kernel/printf.c
@@ -21,6 +21,13 @@ kprintf(char *fmt, ...)
outb(0xE9, 'm');
char buf[1024], *p = buf;
+ memset(buf, 0, 1024);
+
+ sprintf(buf, "[CPU#%d] ", cpu->id);
+ while (*p)
+ outb(0xE9, *p++);
+ memset(buf, 0, 1024);
+ p = buf;
/* Print message to debug port */
va_list args;
diff --git a/kernel/uname.c b/kernel/uname.c
index cf0a931..10e52c0 100644
--- a/kernel/uname.c
+++ b/kernel/uname.c
@@ -9,7 +9,7 @@
#include <nucleus/memory.h>
static const char *SYSNAME = "Nucleus";
-static const char *RELEASE = "0.9.1";
+static const char *RELEASE = "0.9.2";
static const char *VERSION = "SMP PREEMPT "__DATE__" "__TIME__;
static const char *MACHINE = "x86";