diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h index f649a479..689ff366 100644 --- a/arch/x86/include/asm/apic.h +++ b/arch/x86/include/asm/apic.h @@ -187,6 +187,8 @@ typedef struct { } dest; } __attribute__ ((packed)) ioapic_route_t; +#define smp_id apic_cpu_id + int apic_init(void); void apic_eoi(void); uint32_t apic_cpu_id(void); diff --git a/arch/x86/include/asm/tasks.h b/arch/x86/include/asm/tasks.h index 8a1f25a2..551a167f 100644 --- a/arch/x86/include/asm/tasks.h +++ b/arch/x86/include/asm/tasks.h @@ -87,6 +87,12 @@ static inline int jump_to_user_code(uint32_t ep, uint32_t stack) return 0; } +/** @brief determines the stack of a specific task + * + * @return start address of a specific task + */ +size_t get_stack(uint32_t id); + #ifdef __cplusplus } #endif diff --git a/arch/x86/kernel/apic.c b/arch/x86/kernel/apic.c index d35ae989..b486bd43 100644 --- a/arch/x86/kernel/apic.c +++ b/arch/x86/kernel/apic.c @@ -28,7 +28,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -36,13 +38,6 @@ #include #endif -/* disable optimization for the following functions */ -//static int apic_send_ipi(uint32_t id, uint32_t mode, uint32_t vector) __attribute__((optimize(0))); -static int wakeup_all_aps(uint32_t start_eip) __attribute__((optimize(0))); -//int apic_calibration(void) __attribute__((optimize(0))); -//int ioapic_intoff(uint8_t irq, uint8_t apicid) __attribute__((optimize(0))); -//int ioapic_inton(uint8_t irq, uint8_t apicid) __attribute__((optimize(0))); - // IO APIC MMIO structure: write reg, then read or write data. typedef struct { uint32_t reg; @@ -60,12 +55,15 @@ static uint32_t icr = 0; static uint32_t ncores = 1; static uint8_t irq_redirect[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}; #if MAX_CORES > 1 -static uint8_t boot_code[] = {0xE9, 0x1E, 0x00, 0x17, 0x00, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00, 0x0F, 0x01, 0x16, 0x03, 0x00, 0x0F, 0x20, 0xC0, 0x0C, 0x01, 0x0F, 0x22, 0xC0, 0x66, 0xEA, 0x36, 0x00, 0x01, 0x00, 0x08, 0x00, 0xFA, 0x31, 0xC0, 0x66, 0xB8, 0x10, 0x00, 0x8E, 0xD8, 0x8E, 0xC0, 0x8E, 0xE0, 0x8E, 0xE8, 0x8E, 0xD0, 0x0F, 0x20, 0xC0, 0x25, 0xFF, 0xFF, 0xFF, 0x9F, 0x0D, 0x20, 0x00, 0x00, 0x00, 0x0F, 0x22, 0xC0, 0x31, 0xC0, 0x0F, 0x22, 0xD8, 0xBC, 0xEF, 0xBE, 0xAD, 0xDE, 0x31, 0xC0, 0x31, 0xDB, 0xEA, 0xDE, 0xC0, 0xAD, 0xDE, 0x08, 0x00}; +static uint8_t boot_code[] = { 0xFA, 0x0F, 0x01, 0x16, 0x3B, 0x70, 0x0F, 0x20, 0xC0, 0x0C, 0x01, 0x0F, 0x22, 0xC0, 0x66, 0xEA, 0x16, 0x70, 0x00, 0x00, 0x08, 0x00, 0x31, 0xC0, 0x66, 0xB8, 0x10, 0x00, 0x8E, 0xD8, 0x8E, 0xC0, 0x8E, 0xE0, 0x8E, 0xE8, 0x8E, 0xD0, 0xBC, 0xEF, 0xBE, 0xAD, 0xDE, 0x68, 0xAD, 0xDE, 0xAD, 0xDE, 0x6A, 0x00, 0xEA, 0xDE, 0xC0, 0xAD, 0xDE, 0x08, 0x00, 0xEB, 0xFE, 0x17, 0x00, 0x41, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00}; #endif static uint8_t initialized = 0; static atomic_int32_t cpu_online = ATOMIC_INIT(1); spinlock_t bootlock = SPINLOCK_INIT; +// forward declaration +static int lapic_reset(void); + static inline uint32_t lapic_read(uint32_t addr) { return *((volatile uint32_t*) (lapic+addr)); @@ -110,6 +108,12 @@ uint32_t apic_cpu_id(void) return 0; } +static inline void apic_set_cpu_id(uint32_t id) +{ + if (lapic && initialized) + lapic_write(APIC_ID, id << 24); +} + static inline uint32_t apic_version(void) { if (lapic) @@ -137,6 +141,14 @@ int apic_is_enabled(void) } #if MAX_CORES > 1 +static inline void set_ipi_dest(uint32_t cpu_id) { + uint32_t tmp; + tmp = lapic_read(APIC_ICR2); + tmp &= 0x00FFFFFF; + tmp |= (cpu_id << 24); + lapic_write(APIC_ICR2, tmp); +} + #if 0 static int apic_send_ipi(uint32_t id, uint32_t mode, uint32_t vector) { @@ -158,28 +170,49 @@ static int apic_send_ipi(uint32_t id, uint32_t mode, uint32_t vector) } #endif -static int wakeup_all_aps(uint32_t start_eip) +/* + * use the universal startup algorithm of Intel's MultiProcessor Specification + */ +static int wakeup_ap(uint32_t start_eip, uint32_t id) { + static char* reset_vector = 0; uint32_t i; - kputs("Wakeup all application processors via IPI\n"); + kprintf("Wakeup application processor %d via IPI\n", id); + + // set shutdown code to 0x0A + cmos_write(0x0F, 0x0A); - if(lapic_read(APIC_ICR1) & APIC_ICR_BUSY) { - kprintf("ERROR: previous send not complete"); + if (!reset_vector) { + reset_vector = (char*) map_region(0x00, 0x00, 1, MAP_KERNEL_SPACE|MAP_NO_CACHE); + reset_vector += 0x467; // add base address of the reset vector + kprintf("Map reset vector to %p\n", reset_vector); + } + *((volatile unsigned short *) (reset_vector+2)) = start_eip >> 4; + *((volatile unsigned short *) reset_vector) = 0x00; + + if (lapic_read(APIC_ICR1) & APIC_ICR_BUSY) { + kputs("ERROR: previous send not complete"); return -EIO; } - vga_puts("Send IPI\n"); - // send out INIT to all aps - lapic_write(APIC_ICR1, APIC_INT_ASSERT|APIC_DEST_ALLBUT|APIC_DM_INIT); + //kputs("Send IPI\n"); + // send out INIT to AP + set_ipi_dest(id); + lapic_write(APIC_ICR1, APIC_INT_LEVELTRIG|APIC_INT_ASSERT|APIC_DM_INIT); + udelay(200); + // reset INIT + lapic_write(APIC_ICR1, APIC_INT_LEVELTRIG|APIC_DM_INIT); udelay(10000); // send out the startup - lapic_write(APIC_ICR1, APIC_INT_ASSERT|APIC_DEST_ALLBUT|APIC_DM_STARTUP|(start_eip >> 12)); + set_ipi_dest(id); + lapic_write(APIC_ICR1, APIC_DM_STARTUP|(start_eip >> 12)); udelay(200); // do it again - lapic_write(APIC_ICR1, APIC_INT_ASSERT|APIC_DEST_ALLBUT|APIC_DM_STARTUP|(start_eip >> 12)); + set_ipi_dest(id); + lapic_write(APIC_ICR1, APIC_DM_STARTUP|(start_eip >> 12)); udelay(200); - vga_puts("IPI done...\n"); + //kputs("IPI done...\n"); i = 0; while((lapic_read(APIC_ICR1) & APIC_ICR_BUSY) && (i < 1000)) @@ -190,12 +223,65 @@ static int wakeup_all_aps(uint32_t start_eip) #endif #if MAX_CORES > 1 -static void smp_main(void) +/* + * This is defined in entry.asm. We use this to properly reload + * the new segment registers + */ +extern void gdt_flush(void); + +/* + * This is defined in entry.asm and initialized the processors. + */ +extern void cpu_init(void); + +/* + * platform independent entry point of the application processors + */ +extern int smp_main(void); + +void smp_start(uint32_t id) { - vga_puts("JJAJAJAJAJAJA\n"); - lowlevel_init(); + uint32_t i; + atomic_int32_inc(&cpu_online); - kputs("JAJAJAJ\n"); + + // reset APIC and set id + lapic_reset(); + apic_set_cpu_id(id); + + kprintf("Application processor %d is entering its idle task\n", apic_cpu_id()); + + // initialize default cpu features + cpu_init(); + + // use the same gdt like the boot processors + gdt_flush(); + + // install IDT + idt_install(); + + // enable additional cpu features + cpu_detection(); + + /* enable paging */ + write_cr3((uint32_t)get_boot_pgd()); + i = read_cr0(); + i = i | (1 << 31); + write_cr0(i); + + // reset APIC and set id + lapic_reset(); // sets also the timer interrupt + apic_set_cpu_id(id); + + /* + * we turned on paging + * => now, we are able to register our task for Task State Switching + */ + register_task(per_core(current_task)); + + smp_main(); + + // idle loop while(1) ; } #endif @@ -218,37 +304,59 @@ static unsigned int* search_apic(unsigned int base, unsigned int limit) { #if MAX_CORES > 1 int smp_init(void) { - uint32_t i; - size_t bootaddr; + uint32_t i, j; + char* bootaddr; int err; if (ncores <= 1) return -EINVAL; - /* - * dirty hack: Copy 16bit startup code (see tools/smp_setup.asm) - * to a 16bit address. Wakeup the other cores via IPI. They start - * at this address in real mode, switch to protected and finally - * they jump to smp_main. - */ - bootaddr = 0x10000; - map_region(bootaddr, get_pages(1), 1, MAP_KERNEL_SPACE); - for(i=0; i 1 - //smp_init(); -#endif if (ioapic) { // now, we don't longer need the IOAPIC timer and turn it off @@ -391,6 +496,9 @@ int apic_calibration(void) ioapic_inton(i, apic_processors[boot_processor]->id); } initialized = 1; +#if MAX_CORES > 1 + smp_init(); +#endif irq_nested_enable(flags); return 0; diff --git a/arch/x86/kernel/gdt.c b/arch/x86/kernel/gdt.c index ed863eaa..113e2643 100644 --- a/arch/x86/kernel/gdt.c +++ b/arch/x86/kernel/gdt.c @@ -34,11 +34,18 @@ static gdt_entry_t gdt[GDT_ENTRIES] = {[0 ... GDT_ENTRIES-1] = {0, 0, 0, 0, 0, unsigned char* default_stack_pointer __attribute__ ((section (".data"))) = kstacks[0] + KERNEL_STACK_SIZE - sizeof(size_t); /* - * This is in start.asm. We use this to properly reload + * This is defined in entry.asm. We use this to properly reload * the new segment registers */ extern void gdt_flush(void); +size_t get_stack(uint32_t id) +{ + if (BUILTIN_EXPECT(id >= MAX_TASKS, 0)) + return -EINVAL; + return (size_t) kstacks[id] + KERNEL_STACK_SIZE - sizeof(size_t); +} + int register_task(task_t* task) { uint16_t sel; uint32_t id = task->id; diff --git a/arch/x86/kernel/idt.c b/arch/x86/kernel/idt.c index fe3b2bf4..ae35b1f5 100644 --- a/arch/x86/kernel/idt.c +++ b/arch/x86/kernel/idt.c @@ -74,13 +74,19 @@ extern void isrsyscall(void); /* Installs the IDT */ void idt_install(void) { - /* Sets the special IDT pointer up, just like in 'gdt.c' */ - idtp.limit = (sizeof(idt_entry_t) * 256) - 1; - idtp.base = (unsigned int)&idt; + static int initialized = 0; - /* Add any new ISRs to the IDT here using idt_set_gate */ - idt_set_gate(INT_SYSCALL, (unsigned int)isrsyscall, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING3|IDT_FLAG_32BIT|IDT_FLAG_TRAPGATE); + if (!initialized) { + initialized = 1; + + /* Sets the special IDT pointer up, just like in 'gdt.c' */ + idtp.limit = (sizeof(idt_entry_t) * 256) - 1; + idtp.base = (unsigned int)&idt; + + /* Add any new ISRs to the IDT here using idt_set_gate */ + idt_set_gate(INT_SYSCALL, (unsigned int)isrsyscall, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING3|IDT_FLAG_32BIT|IDT_FLAG_TRAPGATE); + } /* Points the processor's internal register to the new IDT */ asm volatile("lidt %0" : : "m" (idtp)); diff --git a/include/metalsvm/tasks.h b/include/metalsvm/tasks.h index 86a41e18..d14c2917 100644 --- a/include/metalsvm/tasks.h +++ b/include/metalsvm/tasks.h @@ -126,6 +126,16 @@ void NORETURN sys_exit(int); * */ int sys_fork(void); +/** @brief Reserve an idel task for an additional core + * + * @param id core number + * + * return + * - address of the stack (< KERNEL_SPACE) + * - -ENIVAL (-22) on failure + */ +size_t get_idle_task(uint32_t id); + /** @brief System call to execute a program * * @param fname Filename of the executable diff --git a/kernel/main.c b/kernel/main.c index 566ff932..86646610 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -78,6 +78,20 @@ static void list_root(void) { list_fs(fs_root, 1); } +#if MAX_CORES > 1 +// idle loop of the application processors +int smp_main(void) +{ + irq_enable(); + + while(1) { + HALT; + } + + return 0; +} +#endif + int main(void) { lowlevel_init(); diff --git a/kernel/tasks.c b/kernel/tasks.c index 26774de7..26aa5eb6 100644 --- a/kernel/tasks.c +++ b/kernel/tasks.c @@ -66,15 +66,36 @@ int multitasking_init(void) { atomic_int32_set(&task_table[0].user_usage, 0); mailbox_wait_msg_init(&task_table[0].inbox); memset(task_table[0].outbox, 0x00, sizeof(mailbox_wait_msg_t*)*MAX_TASKS); - per_core(current_task) = task_table+0; - per_core(current_task)->pgd = get_boot_pgd(); + task_table[0].pgd = get_boot_pgd(); task_table[0].flags = TASK_DEFAULT_FLAGS; + per_core(current_task) = task_table+0; return 0; } return -ENOMEM; } +size_t get_idle_task(uint32_t id) +{ +#if MAX_CORES > 1 + if (BUILTIN_EXPECT((id >= MAX_TASKS) || (task_table[id].status != TASK_INVALID), 0)) + return -EINVAL; + + task_table[id].id = id; + task_table[id].status = TASK_IDLE; + atomic_int32_set(&task_table[id].user_usage, 0); + mailbox_wait_msg_init(&task_table[id].inbox); + memset(task_table[id].outbox, 0x00, sizeof(mailbox_wait_msg_t*)*MAX_TASKS); + task_table[id].pgd = get_boot_pgd(); + task_table[id].flags = TASK_DEFAULT_FLAGS; + current_task[id].var = task_table+id; + + return get_stack(id); +#else + return -EINVAL; +#endif +} + /** @brief Wakeup tasks which are waiting for a message from the current one * * @param result Current task's resulting return value @@ -744,14 +765,11 @@ void scheduler(void) * we switch to the idle task, if the current task terminates * and no other is ready */ - for(i=0; iid, smp_id()); + #if MAX_CORES > 1 spinlock_irqsave_unlock(&table_lock); #else