add SMP support
- this is experimental version - by setting MAX_CORES to 1, you are to disable the SMP support
This commit is contained in:
parent
09b3655ea4
commit
e595fae384
8 changed files with 234 additions and 63 deletions
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -28,7 +28,9 @@
|
|||
#include <metalsvm/page.h>
|
||||
#include <metalsvm/spinlock.h>
|
||||
#include <metalsvm/mmu.h>
|
||||
#include <metalsvm/tasks.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/idt.h>
|
||||
#include <asm/irqflags.h>
|
||||
#include <asm/apic.h>
|
||||
#include <asm/multiboot.h>
|
||||
|
@ -36,13 +38,6 @@
|
|||
#include <asm/RCCE_lib.h>
|
||||
#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<sizeof(boot_code); i+=sizeof(size_t))
|
||||
for(i=1; (i<ncores) && (i<MAX_CORES); i++)
|
||||
{
|
||||
// replace 0xDEADC0DE with the address of the smp entry code
|
||||
if (*((size_t*) (bootaddr+i)) == 0xDEADC0DE)
|
||||
*((size_t*) (bootaddr+i)) = (size_t) smp_main;
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* The page at SMP_SETUP_ADDR is already reserved for this hack!
|
||||
*/
|
||||
bootaddr = (char*) SMP_SETUP_ADDR;
|
||||
memcpy(bootaddr, boot_code, sizeof(boot_code));
|
||||
for(j=0; j<sizeof(boot_code)-4; j++)
|
||||
{
|
||||
// replace 0xDEADC0DE with the address of the smp entry code
|
||||
if (*((size_t*) (bootaddr+j)) == 0xDEADC0DE) {
|
||||
*((size_t*) (bootaddr+j)) = (size_t) smp_start;
|
||||
kprintf("Set entry point of the application processors at 0x%x\n", (size_t) smp_start);
|
||||
}
|
||||
|
||||
// replace APIC ID 0xDEADDEAD
|
||||
if (*((size_t*) (bootaddr+j)) == 0xDEADDEAD)
|
||||
*((size_t*) (bootaddr+j)) = i;
|
||||
|
||||
// replace 0xDEADBEEF with the addres of the stack
|
||||
if (*((size_t*) (bootaddr+j)) == 0xDEADBEEF) {
|
||||
uint32_t esp = get_idle_task(i);
|
||||
*((size_t*) (bootaddr+j)) = esp;
|
||||
if ((int) esp < 0)
|
||||
kprintf("Invalid stack value\n");
|
||||
kprintf("Set stack of the application processors to 0x%x\n", esp);
|
||||
}
|
||||
}
|
||||
|
||||
//kprintf("size of the boot_code %d\n", sizeof(boot_code));
|
||||
err = wakeup_ap((uint32_t)bootaddr, i);
|
||||
if (err)
|
||||
kprintf("Unable to wakeup application processor %d: %d\n", i, err);
|
||||
|
||||
j = 0;
|
||||
while((ncores != atomic_int32_read(&cpu_online)) && (j < 100)) {
|
||||
udelay(1000);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
kprintf("size of the boot_code %d\n", sizeof(boot_code));
|
||||
err = wakeup_all_aps(bootaddr);
|
||||
if (err)
|
||||
kprintf("Unable to wakeup application processors: %d\n", err);
|
||||
|
||||
i = 0;
|
||||
while((ncores != atomic_int32_read(&cpu_online)) && (i < 1000))
|
||||
i++;
|
||||
|
||||
kprintf("%d cores online\n", atomic_int32_read(&cpu_online));
|
||||
|
||||
return 0;
|
||||
|
@ -379,9 +487,6 @@ int apic_calibration(void)
|
|||
kprintf("APIC calibration determines an ICR of 0x%x\n", icr);
|
||||
|
||||
flags = irq_nested_disable();
|
||||
#if MAX_CORES > 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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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; i<MAX_TASKS; i++) {
|
||||
if (task_table[i].status == TASK_IDLE) {
|
||||
per_core(current_task) = task_table+i;
|
||||
goto get_task_out;
|
||||
}
|
||||
}
|
||||
per_core(current_task) = task_table+smp_id();
|
||||
|
||||
get_task_out:
|
||||
//kprintf("schedule %d on core %d\n", per_core(current_task)->id, smp_id());
|
||||
|
||||
#if MAX_CORES > 1
|
||||
spinlock_irqsave_unlock(&table_lock);
|
||||
#else
|
||||
|
|
Loading…
Add table
Reference in a new issue