mirror of
https://github.com/hermitcore/libhermit.git
synced 2025-03-09 00:00:03 +01:00
enable SMP support
This commit is contained in:
parent
f781f4923b
commit
047951bafd
10 changed files with 184 additions and 37 deletions
|
@ -101,6 +101,7 @@ doc:
|
|||
include/hermit/config.inc: include/hermit/config.h
|
||||
@echo "; This file is generated automatically from the config.h file." > include/hermit/config.inc
|
||||
@echo "; Before editing this, you should consider editing config.h." >> include/hermit/config.inc
|
||||
@awk '/^#define MAX_CORES/{ print "%define MAX_CORES", $$3 }' include/hermit/config.h >> include/hermit/config.inc
|
||||
@awk '/^#define KERNEL_STACK_SIZE/{ print "%define KERNEL_STACK_SIZE", $$3 }' include/hermit/config.h >> include/hermit/config.inc
|
||||
@awk '/^#define VIDEO_MEM_ADDR/{ print "%define VIDEO_MEM_ADDR", $$3 }' include/hermit/config.h >> include/hermit/config.inc
|
||||
@awk '/^#define CONFIG_VGA/{ print "%define CONFIG_VGA" }' include/hermit/config.h >> include/hermit/config.inc
|
||||
|
|
|
@ -111,7 +111,7 @@ typedef struct {
|
|||
} __attribute__ ((packed)) gdt_ptr_t;
|
||||
|
||||
// a TSS descriptor is twice larger than a code/data descriptor
|
||||
#define GDT_ENTRIES (6+1*2)
|
||||
#define GDT_ENTRIES (6+MAX_CORES*2)
|
||||
|
||||
#if GDT_ENTRIES > 8192
|
||||
#error Too many GDT entries!
|
||||
|
|
|
@ -454,13 +454,7 @@ static inline size_t lsb(size_t i)
|
|||
}
|
||||
|
||||
/// A one-instruction-do-nothing
|
||||
#define NOP1 asm volatile ("nop")
|
||||
/// A two-instruction-do-nothing
|
||||
#define NOP2 asm volatile ("nop;nop")
|
||||
/// A four-instruction-do-nothing
|
||||
#define NOP4 asm volatile ("nop;nop;nop;nop")
|
||||
/// A eight-instruction-do-nothing
|
||||
#define NOP8 asm volatile ("nop;nop;nop;nop;nop;nop;nop;nop")
|
||||
#define NOP asm volatile ("nop")
|
||||
/// The PAUSE instruction provides a hint to the processor that the code sequence is a spin-wait loop.
|
||||
#define PAUSE asm volatile ("pause")
|
||||
/// The HALT instruction stops the processor until the next interrupt arrives
|
||||
|
@ -503,6 +497,14 @@ uint32_t get_cpu_frequency(void);
|
|||
*/
|
||||
void udelay(uint32_t usecs);
|
||||
|
||||
/// Register a task's TSS at GDT
|
||||
static inline void register_task(void)
|
||||
{
|
||||
uint16_t sel = (CORE_ID*2+6) << 3;
|
||||
|
||||
asm volatile ("ltr %%ax" : : "a"(sel));
|
||||
}
|
||||
|
||||
/** @brief System calibration
|
||||
*
|
||||
* This procedure will detect the CPU frequency and calibrate the APIC timer.
|
||||
|
@ -512,10 +514,13 @@ void udelay(uint32_t usecs);
|
|||
inline static int system_calibration(void)
|
||||
{
|
||||
apic_init();
|
||||
register_task();
|
||||
irq_enable();
|
||||
detect_cpu_frequency();
|
||||
apic_calibration();
|
||||
|
||||
//kprintf("CR0 of core %u: 0x%x\n", apic_cpu_id(), read_cr0());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -61,20 +61,6 @@ void switch_context(size_t** stack);
|
|||
*/
|
||||
int create_default_frame(task_t* task, entry_point_t ep, void* arg);
|
||||
|
||||
/** @brief Register a task's TSS at GDT
|
||||
*
|
||||
* @return
|
||||
* - 0 on success
|
||||
*/
|
||||
static inline int register_task(void)
|
||||
{
|
||||
uint16_t sel = 6 << 3;
|
||||
|
||||
asm volatile ("ltr %%ax" : : "a"(sel));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @brief Jump to user code
|
||||
*
|
||||
* This function runs the user code after stopping it just as if
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <hermit/time.h>
|
||||
#include <hermit/spinlock.h>
|
||||
#include <hermit/vma.h>
|
||||
#include <hermit/tasks.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/idt.h>
|
||||
#include <asm/irqflags.h>
|
||||
|
@ -555,6 +556,44 @@ no_mp:
|
|||
goto check_lapic;
|
||||
}
|
||||
|
||||
extern int smp_main(void);
|
||||
extern void gdt_flush(void);
|
||||
extern int set_idle_task(void);
|
||||
|
||||
int smp_start(void)
|
||||
{
|
||||
if (lapic && has_x2apic()) // enable x2APIC support
|
||||
wrmsr(0x1B, 0xFEE00C00);
|
||||
|
||||
// reset APIC and set id
|
||||
lapic_reset();
|
||||
|
||||
kprintf("Processor %d is entering its idle task\n", apic_cpu_id());
|
||||
|
||||
// use the same gdt like the boot processors
|
||||
gdt_flush();
|
||||
|
||||
// install IDT
|
||||
idt_install();
|
||||
|
||||
/*
|
||||
* we turned on paging
|
||||
* => now, we are able to register our task
|
||||
*/
|
||||
register_task();
|
||||
|
||||
// enable additional cpu features
|
||||
cpu_detection();
|
||||
|
||||
//kprintf("CR0 of core %u: 0x%x\n", apic_cpu_id(), read_cr0());
|
||||
|
||||
set_idle_task();
|
||||
|
||||
irq_enable();
|
||||
|
||||
return smp_main();
|
||||
}
|
||||
|
||||
static void apic_err_handler(struct state *s)
|
||||
{
|
||||
kprintf("Got APIC error 0x%x\n", lapic_read(APIC_ESR));
|
||||
|
|
|
@ -72,10 +72,12 @@ mboot:
|
|||
global limit
|
||||
global cpu_freq
|
||||
global boot_processor
|
||||
global cpu_online
|
||||
base dd kernel_start
|
||||
limit dd 0
|
||||
cpu_freq dd 0
|
||||
boot_processor dd -1
|
||||
cpu_online dd 0
|
||||
|
||||
ALIGN 4
|
||||
; we need already a valid GDT to switch in the 64bit modus
|
||||
|
@ -114,6 +116,15 @@ stublet:
|
|||
add eax, ebp
|
||||
mov esp, eax
|
||||
add esp, KERNEL_STACK_SIZE - 16
|
||||
mov eax, boot_processor
|
||||
sub eax, kernel_start
|
||||
add eax, ebp
|
||||
mov eax, DWORD [eax]
|
||||
cmp eax, -1
|
||||
je L0
|
||||
imul eax, KERNEL_STACK_SIZE
|
||||
add esp, eax
|
||||
L0:
|
||||
|
||||
; Interpret multiboot information
|
||||
mov eax, mb_info
|
||||
|
@ -128,6 +139,20 @@ stublet:
|
|||
cpu_init:
|
||||
push edi
|
||||
|
||||
; do we have to relocate / to intialize the page tables?
|
||||
; only the boot_processor have to do it
|
||||
mov eax, cpu_online
|
||||
sub eax, kernel_start
|
||||
add eax, ebp
|
||||
mov eax, DWORD [eax]
|
||||
cmp eax, 0
|
||||
je short Lrelocate
|
||||
mov eax, Lmap_kernel
|
||||
sub eax, kernel_start
|
||||
add eax, ebp ; ebp contains the physical address of the kernel
|
||||
jmp eax
|
||||
|
||||
Lrelocate:
|
||||
; relocate page tables
|
||||
mov edi, boot_pml4
|
||||
sub edi, kernel_start
|
||||
|
@ -212,6 +237,7 @@ Lno_mbinfo:
|
|||
or eax, 0x183
|
||||
mov DWORD [edi], eax
|
||||
|
||||
Lmap_kernel:
|
||||
cmp ebp, kernel_start
|
||||
je Lno_remap
|
||||
|
||||
|
@ -317,9 +343,20 @@ start64:
|
|||
xor rdi, rdi
|
||||
|
||||
Lno_unmap:
|
||||
mov eax, DWORD [cpu_online]
|
||||
cmp eax, 0
|
||||
jne Lsmp_main
|
||||
|
||||
; set default stack pointer
|
||||
mov rsp, boot_stack
|
||||
add rsp, KERNEL_STACK_SIZE-16
|
||||
xor rax, rax
|
||||
mov eax, [boot_processor]
|
||||
cmp eax, -1
|
||||
je L1
|
||||
imul eax, KERNEL_STACK_SIZE
|
||||
add rsp, rax
|
||||
L1:
|
||||
mov rbp, rsp
|
||||
|
||||
; jump to the boot processors's C code
|
||||
|
@ -327,6 +364,28 @@ Lno_unmap:
|
|||
call main
|
||||
jmp $
|
||||
|
||||
Lsmp_main:
|
||||
; dirty to hack to determine the cpu id
|
||||
; with a temporary stack
|
||||
mov rsp, tmp_stack-16
|
||||
extern apic_cpu_id
|
||||
call apic_cpu_id
|
||||
|
||||
; set default stack pointer
|
||||
imul rax, KERNEL_STACK_SIZE
|
||||
add rax, boot_stack
|
||||
add rax, KERNEL_STACK_SIZE-16
|
||||
mov rsp, rax
|
||||
mov rbp, rsp
|
||||
|
||||
extern smp_start
|
||||
call smp_start
|
||||
jmp $
|
||||
|
||||
DQ 0, 0, 0, 0
|
||||
DQ 0, 0, 0, 0
|
||||
tmp_stack:
|
||||
|
||||
global gdt_flush
|
||||
extern gp
|
||||
|
||||
|
@ -605,7 +664,7 @@ mb_info:
|
|||
ALIGN 4096
|
||||
global boot_stack
|
||||
boot_stack:
|
||||
TIMES (KERNEL_STACK_SIZE) DB 0xcd
|
||||
TIMES (MAX_CORES*KERNEL_STACK_SIZE) DB 0xcd
|
||||
|
||||
; Bootstrap page tables are used during the initialization.
|
||||
ALIGN 4096
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
#include <asm/page.h>
|
||||
|
||||
gdt_ptr_t gp;
|
||||
static tss_t task_state_segment __attribute__ ((aligned (PAGE_SIZE)));
|
||||
static tss_t task_state_segments[MAX_CORES] __attribute__ ((aligned (PAGE_SIZE)));
|
||||
// currently, our kernel has full access to the ioports
|
||||
static gdt_entry_t gdt[GDT_ENTRIES] = {[0 ... GDT_ENTRIES-1] = {0, 0, 0, 0, 0, 0}};
|
||||
|
||||
|
@ -52,7 +52,7 @@ size_t get_kernel_stack(void)
|
|||
{
|
||||
task_t* curr_task = per_core(current_task);
|
||||
|
||||
return (size_t) curr_task->stack + KERNEL_STACK_SIZE - 16; // => stack is 16byte aligned
|
||||
return (size_t) curr_task->stack + curr_task->id * KERNEL_STACK_SIZE - 16; // => stack is 16byte aligned
|
||||
}
|
||||
|
||||
/* Setup a descriptor in the Global Descriptor Table */
|
||||
|
@ -89,9 +89,9 @@ void configure_gdt_entry(gdt_entry_t *dest_entry, unsigned long base, unsigned l
|
|||
void gdt_install(void)
|
||||
{
|
||||
unsigned long gran_ds, gran_cs, limit;
|
||||
int num = 0;
|
||||
int i, num = 0;
|
||||
|
||||
memset(&task_state_segment, 0x00, sizeof(tss_t));
|
||||
memset(task_state_segments, 0x00, MAX_CORES*sizeof(tss_t));
|
||||
|
||||
gran_cs = GDT_FLAG_64_BIT;
|
||||
gran_ds = 0;
|
||||
|
@ -138,9 +138,14 @@ void gdt_install(void)
|
|||
gdt_set_gate(num++, 0, limit,
|
||||
GDT_FLAG_RING3 | GDT_FLAG_SEGMENT | GDT_FLAG_CODESEG | GDT_FLAG_PRESENT, gran_cs);
|
||||
|
||||
task_state_segment.rsp0 = (size_t) &boot_stack - 0x10;
|
||||
gdt_set_gate(num++, (unsigned long) (&task_state_segment), sizeof(tss_t)-1,
|
||||
/*
|
||||
* Create TSS for each task at ring0 (we use these segments for task switching)
|
||||
*/
|
||||
for(i=0; i<MAX_CORES; i++) {
|
||||
task_state_segments[i].rsp0 = (size_t) &boot_stack + (i + 1) * KERNEL_STACK_SIZE - 16;
|
||||
gdt_set_gate(num+i*2, (unsigned long) (task_state_segments+i), sizeof(tss_t)-1,
|
||||
GDT_FLAG_PRESENT | GDT_FLAG_TSS | GDT_FLAG_RING0, gran_ds);
|
||||
}
|
||||
|
||||
/* Flush out the old GDT and install the new changes! */
|
||||
gdt_flush();
|
||||
|
|
|
@ -103,7 +103,7 @@ inline static int spinlock_lock(spinlock_t* s) {
|
|||
#if 1
|
||||
ticket = atomic_int32_inc(&s->queue);
|
||||
while(atomic_int32_read(&s->dequeue) != ticket) {
|
||||
NOP1;
|
||||
PAUSE;
|
||||
}
|
||||
s->owner = curr_task->id;
|
||||
s->counter = 1;
|
||||
|
@ -194,7 +194,7 @@ inline static int spinlock_irqsave_lock(spinlock_irqsave_t* s) {
|
|||
#if 1
|
||||
ticket = atomic_int32_inc(&s->queue);
|
||||
while (atomic_int32_read(&s->dequeue) != ticket) {
|
||||
NOP1;
|
||||
PAUSE;
|
||||
}
|
||||
#else
|
||||
while( atomic_int32_test_and_set(&s->dequeue,0) );
|
||||
|
|
|
@ -55,6 +55,8 @@ extern atomic_int32_t total_pages;
|
|||
extern atomic_int32_t total_allocated_pages;
|
||||
extern atomic_int32_t total_available_pages;
|
||||
|
||||
extern atomic_int32_t cpu_online;
|
||||
|
||||
static int foo(void* arg)
|
||||
{
|
||||
kprintf("hello from %s\n", (char*) arg);
|
||||
|
@ -77,11 +79,28 @@ static int hermit_init(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int smp_main(void)
|
||||
{
|
||||
int32_t cpu = atomic_int32_inc(&cpu_online);
|
||||
|
||||
kprintf("%d CPUs are now online\n", cpu);
|
||||
|
||||
create_kernel_task(NULL, foo, "foo2", NORMAL_PRIO);
|
||||
|
||||
while(1) {
|
||||
HALT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
hermit_init();
|
||||
system_calibration(); // enables also interrupts
|
||||
|
||||
atomic_int32_inc(&cpu_online);
|
||||
|
||||
kprintf("This is Hermit %s Build %u, %u\n", HERMIT_VERSION, &__BUILD_DATE, &__BUILD_TIME);
|
||||
kprintf("Kernel starts at %p and ends at %p\n", &kernel_start, &kernel_end);
|
||||
kprintf("Processor frequency: %u MHz\n", get_cpu_frequency());
|
||||
|
@ -89,7 +108,7 @@ int main(void)
|
|||
kprintf("Current allocated memory: %lu KiB\n", atomic_int32_read(&total_allocated_pages) * PAGE_SIZE / 1024);
|
||||
kprintf("Current available memory: %lu KiB\n", atomic_int32_read(&total_available_pages) * PAGE_SIZE / 1024);
|
||||
|
||||
create_kernel_task(NULL, foo, "foo", NORMAL_PRIO);
|
||||
create_kernel_task(NULL, foo, "foo1", NORMAL_PRIO);
|
||||
|
||||
while(1) {
|
||||
HALT;
|
||||
|
|
|
@ -71,23 +71,56 @@ uint32_t get_highest_priority(void)
|
|||
|
||||
int multitasking_init(void)
|
||||
{
|
||||
uint32_t core_id = CORE_ID;
|
||||
|
||||
if (BUILTIN_EXPECT(task_table[0].status != TASK_IDLE, 0)) {
|
||||
kputs("Task 0 is not an idle task\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
task_table[0].prio = IDLE_PRIO;
|
||||
task_table[0].stack = (char*) &boot_stack;
|
||||
task_table[0].stack = (char*) ((size_t)&boot_stack + core_id * KERNEL_STACK_SIZE);
|
||||
task_table[0].page_map = read_cr3();
|
||||
|
||||
readyqueues[CORE_ID].idle = task_table+0;
|
||||
|
||||
// register idle task
|
||||
register_task();
|
||||
readyqueues[core_id].idle = task_table+0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int set_idle_task(void)
|
||||
{
|
||||
uint32_t i, core_id = CORE_ID;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
spinlock_irqsave_lock(&table_lock);
|
||||
|
||||
for(i=0; i<MAX_TASKS; i++) {
|
||||
if (task_table[i].status == TASK_INVALID) {
|
||||
task_table[i].id = i;
|
||||
task_table[i].status = TASK_IDLE;
|
||||
task_table[i].last_core = core_id;
|
||||
task_table[i].last_stack_pointer = NULL;
|
||||
task_table[i].stack = (char*) ((size_t)&boot_stack + core_id * KERNEL_STACK_SIZE);;
|
||||
task_table[i].prio = IDLE_PRIO;
|
||||
spinlock_init(&task_table[i].vma_lock);
|
||||
task_table[i].vma_list = NULL;
|
||||
task_table[i].heap = NULL;
|
||||
spinlock_irqsave_init(&task_table[i].page_lock);
|
||||
atomic_int32_set(&task_table[i].user_usage, 0);
|
||||
task_table[i].page_map = read_cr3();
|
||||
per_core(current_task) = readyqueues[core_id].idle = task_table+i;
|
||||
ret = 0;
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
spinlock_irqsave_unlock(&table_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void finish_task_switch(void)
|
||||
{
|
||||
task_t* old;
|
||||
|
|
Loading…
Add table
Reference in a new issue