1
0
Fork 0
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:
Stefan Lankes 2015-05-31 00:30:13 +02:00
parent f781f4923b
commit 047951bafd
10 changed files with 184 additions and 37 deletions

View file

@ -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

View file

@ -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!

View file

@ -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;
}

View file

@ -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

View file

@ -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));

View file

@ -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

View file

@ -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();

View file

@ -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) );

View file

@ -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;

View file

@ -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;