diff --git a/hermit/Makefile b/hermit/Makefile index 996f3b804..be77d5039 100644 --- a/hermit/Makefile +++ b/hermit/Makefile @@ -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 diff --git a/hermit/arch/x86/include/asm/gdt.h b/hermit/arch/x86/include/asm/gdt.h index 10a982cc0..e66ef8972 100644 --- a/hermit/arch/x86/include/asm/gdt.h +++ b/hermit/arch/x86/include/asm/gdt.h @@ -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! diff --git a/hermit/arch/x86/include/asm/processor.h b/hermit/arch/x86/include/asm/processor.h index 2ae8b3dbe..7cbd90aa5 100644 --- a/hermit/arch/x86/include/asm/processor.h +++ b/hermit/arch/x86/include/asm/processor.h @@ -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; } diff --git a/hermit/arch/x86/include/asm/tasks.h b/hermit/arch/x86/include/asm/tasks.h index 5eaef1987..621ccc797 100644 --- a/hermit/arch/x86/include/asm/tasks.h +++ b/hermit/arch/x86/include/asm/tasks.h @@ -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 diff --git a/hermit/arch/x86/kernel/apic.c b/hermit/arch/x86/kernel/apic.c index 40f9349a6..83a70ab6b 100644 --- a/hermit/arch/x86/kernel/apic.c +++ b/hermit/arch/x86/kernel/apic.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -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)); diff --git a/hermit/arch/x86/kernel/entry.asm b/hermit/arch/x86/kernel/entry.asm index 2d1be8135..8b28eb62b 100644 --- a/hermit/arch/x86/kernel/entry.asm +++ b/hermit/arch/x86/kernel/entry.asm @@ -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 diff --git a/hermit/arch/x86/kernel/gdt.c b/hermit/arch/x86/kernel/gdt.c index 30536ceec..40ead0a3b 100644 --- a/hermit/arch/x86/kernel/gdt.c +++ b/hermit/arch/x86/kernel/gdt.c @@ -36,7 +36,7 @@ #include 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; iqueue); 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) ); diff --git a/hermit/kernel/main.c b/hermit/kernel/main.c index 40e980e8b..400d8ba33 100644 --- a/hermit/kernel/main.c +++ b/hermit/kernel/main.c @@ -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; diff --git a/hermit/kernel/tasks.c b/hermit/kernel/tasks.c index 7a65da294..10bc43189 100644 --- a/hermit/kernel/tasks.c +++ b/hermit/kernel/tasks.c @@ -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