Merge remote-tracking branch 'origin/ctx_switch' into x64_new
This commit is contained in:
commit
b39a84e07f
6 changed files with 226 additions and 6 deletions
72
apps/tests.c
72
apps/tests.c
|
@ -413,6 +413,69 @@ static int pi(void* arg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define REPS 10000
|
||||
|
||||
volatile uint64_t t1, t2;
|
||||
volatile int stop = !!0;
|
||||
volatile int sid = 0;
|
||||
|
||||
static int measure_ctx_switch(void* arg)
|
||||
{
|
||||
int id = !!(int)arg;
|
||||
int oid = !id;
|
||||
uint64_t freq = get_cpu_frequency() *1000 *1000;
|
||||
uint64_t diff, min = (uint64_t)-1, max = 0, avg = 0;
|
||||
int i;
|
||||
uint32_t a=0,b,c,d;
|
||||
|
||||
// Size of a timeslice in ticks
|
||||
uint64_t timeslice = freq / TIMER_FREQ;
|
||||
|
||||
kprintf("ID: %d, ", id);
|
||||
#ifdef SW_TASK_SWITCH
|
||||
kprintf("Measuring SW task switch.\n");
|
||||
#else
|
||||
kprintf("Measuring HW task switch.\n");
|
||||
#endif
|
||||
|
||||
for (i=0; i < REPS && stop == 0; i++) {
|
||||
while(id == sid && stop == 0) {
|
||||
t2 = rdtsc();
|
||||
cpuid(0,&a,&b,&c,&d);
|
||||
}
|
||||
|
||||
cpuid(0,&a,&b,&c,&d);
|
||||
diff = rdtsc() -t2;
|
||||
|
||||
// The last measurement is garbage
|
||||
if (stop) break;
|
||||
// The first ones are garbage, too
|
||||
if (i < 5) goto next_try;
|
||||
if (diff >= timeslice) {
|
||||
i--;
|
||||
goto next_try;
|
||||
}
|
||||
|
||||
kprintf("%i: diff= %llu, i= %i\n", id, diff, i);
|
||||
if (diff > max) max = diff;
|
||||
if (diff < min) min = diff;
|
||||
avg += diff;
|
||||
|
||||
next_try:
|
||||
sid = id;
|
||||
}
|
||||
avg /= i-5;
|
||||
|
||||
stop = 1;
|
||||
|
||||
kprintf("maximum gap: %llu ticks\n", max);
|
||||
kprintf("minimum gap: %llu ticks\n", min);
|
||||
kprintf("average gap: %llu ticks\n", avg);
|
||||
kprintf("Timeslice size: %llu ticks\n", timeslice);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_init(void)
|
||||
{
|
||||
// char* argv[] = {"/bin/mshell", NULL};
|
||||
|
@ -424,8 +487,11 @@ int test_init(void)
|
|||
//sem_init(&consuming, 0);
|
||||
//mailbox_int32_init(&mbox);
|
||||
|
||||
create_kernel_task(NULL, foo, "Hello from foo1", NORMAL_PRIO);
|
||||
create_kernel_task(NULL, join_test, NULL, NORMAL_PRIO);
|
||||
create_kernel_task(NULL, measure_ctx_switch, (int)0, NORMAL_PRIO);
|
||||
create_kernel_task(NULL, measure_ctx_switch, (int)1, NORMAL_PRIO);
|
||||
//create_kernel_task(NULL, foo, "Hello from foo1", NORMAL_PRIO);
|
||||
//create_kernel_task(NULL, foo, "Hello from foo2", NORMAL_PRIO);
|
||||
//create_kernel_task(NULL, join_test, NULL, NORMAL_PRIO);
|
||||
//create_kernel_task(NULL, producer, , NORMAL_PRIO);
|
||||
//create_kernel_task(NULL, consumer, NULL, NORMAL_PRIO);
|
||||
//create_kernel_task(NULL, mail_ping, NULL, NORMAL_PRIO);
|
||||
|
@ -436,7 +502,7 @@ int test_init(void)
|
|||
//create_kernel_task(NULL, laplace, NULL, NORMAL_PRIO);
|
||||
//create_kernel_task(NULL, jacobi, NULL, NORMAL_PRIO);
|
||||
//create_user_task(NULL, "/bin/hello", argv);
|
||||
create_user_task(NULL, "/bin/tests", argv);
|
||||
//create_user_task(NULL, "/bin/tests", argv);
|
||||
//create_user_task(NULL, "/bin/jacobi", argv);
|
||||
//create_user_task(NULL, "/bin/mshell", argv);
|
||||
//create_user_task(NULL, "/bin/jacobi", argv);
|
||||
|
|
|
@ -507,6 +507,40 @@ hack:
|
|||
jmp 0x00 : 0xDEADBEAF
|
||||
ret
|
||||
|
||||
; This procedure is used by scheduler() to switch tasks.
|
||||
; It is the software-equivalent to the hw-procedure switch_task from above.
|
||||
; Call it in C with the following arguments:
|
||||
; sw_switch_context(&old_tasks_stack_pointer, &new_tasks_stack_pointer)
|
||||
global sw_switch_context
|
||||
sw_switch_context:
|
||||
; The stack layout looks like this:
|
||||
; [new stack pointer]
|
||||
; [old stack pointer]
|
||||
;pushf ; [this procedure's return address] overwritten by: EFLAGS (*1)
|
||||
push DWORD 0x8 ; CS
|
||||
push DWORD [esp+4] ; EIP
|
||||
push DWORD 0 ; Interrupt number
|
||||
push DWORD 0xc0edbabe ; Error code
|
||||
pusha ; Registers...
|
||||
; ---- This will be popped off by iret later.
|
||||
|
||||
pushf
|
||||
pop eax
|
||||
mov [esp+48], eax ; Move EFLAGS to position (*1) by overwriting
|
||||
; the return address of sw_switch_context()
|
||||
|
||||
mov ecx, [esp+52]
|
||||
mov [ecx], esp ; Save stack position in old task structure
|
||||
mov ecx, [esp+56]
|
||||
mov esp, [ecx] ; Load new stack
|
||||
|
||||
sw_rollback:
|
||||
popa
|
||||
|
||||
add esp, 8
|
||||
iret
|
||||
|
||||
|
||||
; 32: IRQ0
|
||||
irq0:
|
||||
; irq0 - irq15 are registered as "Interrupt Gate"
|
||||
|
|
|
@ -27,7 +27,11 @@
|
|||
#include <asm/page.h>
|
||||
|
||||
gdt_ptr_t gp;
|
||||
#ifdef SW_TASK_SWITCH
|
||||
static tss_t task_state_segments[MAX_CORES] __attribute__ ((aligned (PAGE_SIZE)));
|
||||
#else
|
||||
static tss_t task_state_segments[MAX_TASKS] __attribute__ ((aligned (PAGE_SIZE)));
|
||||
#endif
|
||||
static unsigned char kstacks[MAX_TASKS][KERNEL_STACK_SIZE] __attribute__ ((aligned (PAGE_SIZE))) = {[0 ... MAX_TASKS-1][0 ... KERNEL_STACK_SIZE-1] = 0xCD};
|
||||
uint32_t default_stack_pointer = (uint32_t) kstacks[0] + KERNEL_STACK_SIZE - sizeof(size_t);
|
||||
// currently, our kernel has full access to the ioports
|
||||
|
@ -71,6 +75,7 @@ int register_task(task_t* task) {
|
|||
|
||||
int arch_fork(task_t* task)
|
||||
{
|
||||
#ifndef SW_TASK_SWITCH
|
||||
uint16_t cs = 0x08;
|
||||
uint16_t ds = 0x10;
|
||||
uint32_t id;
|
||||
|
@ -120,6 +125,7 @@ int arch_fork(task_t* task)
|
|||
asm volatile ("pushf; pop %%eax" : "=a"(task_state_segments[id].eflags));
|
||||
// This will be the entry point for the new task.
|
||||
asm volatile ("call read_eip" : "=a"(task_state_segments[id].eip));
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -130,10 +136,60 @@ int create_default_frame(task_t* task, internal_entry_point_t ep, void* arg)
|
|||
uint16_t ds = 0x10;
|
||||
uint32_t id;
|
||||
|
||||
#ifdef SW_TASK_SWITCH
|
||||
uint32_t *stack;
|
||||
struct state *stptr;
|
||||
uint32_t short_state_size = sizeof(struct state)/sizeof(uint32_t) -2;
|
||||
#endif
|
||||
|
||||
if (BUILTIN_EXPECT(!task, 0))
|
||||
return -EINVAL;
|
||||
id = task->id;
|
||||
|
||||
#ifdef SW_TASK_SWITCH
|
||||
memset(kstacks[id], 0xCD, KERNEL_STACK_SIZE);
|
||||
|
||||
/* The difference between setting up a task for SW-task-switching
|
||||
* and not for HW-task-switching is setting up a stack and not a TSS.
|
||||
* This is the stack which will be activated and popped off for iret later.
|
||||
*/
|
||||
stack = kstacks[id] +KERNEL_STACK_SIZE -sizeof(uint32_t);
|
||||
|
||||
/* The next three things on the stack are a marker for debugging purposes, ... */
|
||||
*stack-- = 0xDEADBEEF;
|
||||
/* the first-function-to-be-called's arguments, ... */
|
||||
*stack-- = arg;
|
||||
/* and the "caller" we shall return to.
|
||||
* This procedure cleans the task after exit. */
|
||||
*stack = leave_kernel_task;
|
||||
|
||||
/* Next bunch on the stack is the initial register state.
|
||||
* The stack must look like the stack of a task which was
|
||||
* scheduled away previously. */
|
||||
|
||||
/* short_state_size was introduced because the convenient "struct state"
|
||||
* is used for filling the stack with initial values. But the problem is that
|
||||
* "iret" will not remove the last two entries from the stack, since we're
|
||||
* "returning" from kernel space to kernel space. Therefore it is shortened
|
||||
* by its last two entries. */
|
||||
stack -= short_state_size;
|
||||
|
||||
stptr = stack;
|
||||
memset(stptr, 0x00, short_state_size*sizeof(uint32_t));
|
||||
stptr->esp = stack +short_state_size;
|
||||
stptr->int_no = 0xB16B00B5;
|
||||
stptr->error = 0xC03DB4B3;
|
||||
|
||||
/* The instruction pointer shall be set on the first function to be called
|
||||
* after IRETing */
|
||||
stptr->eip = ep;
|
||||
stptr->cs = cs;
|
||||
stptr->eflags = 0x1002;
|
||||
|
||||
/* Set the task's stack pointer entry to the stack we have crafted right now.
|
||||
* This is the pointer which will be used by sw_switch_task(old_task, new_task) later.*/
|
||||
task->stack = stack;
|
||||
#else
|
||||
/* reset buffers */
|
||||
memset(task_state_segments+id, 0x00, sizeof(tss_t));
|
||||
memset(kstacks[id], 0xCD, KERNEL_STACK_SIZE);
|
||||
|
@ -161,9 +217,48 @@ int create_default_frame(task_t* task, internal_entry_point_t ep, void* arg)
|
|||
/* setup for the kernel stack frame */
|
||||
task_state_segments[id].ss0 = 0x10;
|
||||
task_state_segments[id].esp0 = (uint32_t) kstacks[id] + KERNEL_STACK_SIZE - sizeof(size_t);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef SW_TASK_SWITCH
|
||||
int create_default_tss(int id)
|
||||
{
|
||||
uint16_t cs = 0x08;
|
||||
uint16_t ds = 0x10;
|
||||
|
||||
/* reset buffers */
|
||||
memset(task_state_segments+id, 0x00, sizeof(tss_t));
|
||||
|
||||
/* set default values of all registers */
|
||||
task_state_segments[id].cs = cs;
|
||||
task_state_segments[id].ss = ds;
|
||||
task_state_segments[id].ds = ds;
|
||||
task_state_segments[id].fs = ds;
|
||||
task_state_segments[id].gs = ds;
|
||||
task_state_segments[id].es = ds;
|
||||
task_state_segments[id].eflags = 0x1002; // 0x1202;
|
||||
//task_state_segments[id].cr3 = (uint32_t) (virt_to_phys((size_t)task->pgd));
|
||||
//task_state_segments[id].eip = (uint32_t) ep;
|
||||
task_state_segments[id].esp = (uint32_t) kstacks[id] + KERNEL_STACK_SIZE - sizeof(size_t);
|
||||
|
||||
/* build default stack frame */
|
||||
*((size_t*)task_state_segments[id].esp) = 0xDEADBEAF; /* dead-end */
|
||||
/*
|
||||
task_state_segments[id].ebp = task_state_segments[id].esp;
|
||||
task_state_segments[id].esp -= sizeof(size_t);
|
||||
*((size_t*)task_state_segments[id].esp) = (size_t) arg;
|
||||
task_state_segments[id].esp -= sizeof(size_t);
|
||||
*((size_t*)task_state_segments[id].esp) = (size_t) leave_kernel_task;
|
||||
*/
|
||||
|
||||
/* setup for the kernel stack frame */
|
||||
task_state_segments[id].ss0 = 0x10;
|
||||
task_state_segments[id].esp0 = (uint32_t) kstacks[id] + KERNEL_STACK_SIZE - sizeof(size_t);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Setup a descriptor in the Global Descriptor Table */
|
||||
static void gdt_set_gate(int num, unsigned long base, unsigned long limit,
|
||||
|
@ -203,7 +298,11 @@ void gdt_install(void)
|
|||
{
|
||||
unsigned int i;
|
||||
|
||||
#ifdef SW_TASK_SWITCH
|
||||
memset(task_state_segments, 0x00, MAX_CORES*sizeof(tss_t));
|
||||
#else
|
||||
memset(task_state_segments, 0x00, MAX_TASKS*sizeof(tss_t));
|
||||
#endif
|
||||
|
||||
/* Setup the GDT pointer and limit */
|
||||
gp.limit = (sizeof(gdt_entry_t) * GDT_ENTRIES) - 1;
|
||||
|
@ -247,7 +346,12 @@ void gdt_install(void)
|
|||
/*
|
||||
* Create TSS for each task at ring0 (we use these segments for task switching)
|
||||
*/
|
||||
#ifdef SW_TASK_SWITCH
|
||||
for(i=0; i<MAX_CORES; i++) {
|
||||
create_default_tss(i);
|
||||
#else
|
||||
for(i=0; i<MAX_TASKS; i++) {
|
||||
#endif
|
||||
gdt_set_gate(5+i, (unsigned long) (task_state_segments+i), sizeof(tss_t)-1,
|
||||
GDT_FLAG_PRESENT | GDT_FLAG_TSS | GDT_FLAG_RING0,
|
||||
GDT_FLAG_32_BIT);
|
||||
|
|
|
@ -42,6 +42,8 @@ extern "C" {
|
|||
#define VIDEO_MEM_ADDR 0xB8000 // the video memora address
|
||||
#define SMP_SETUP_ADDR 0x07000
|
||||
|
||||
#define SW_TASK_SWITCH
|
||||
|
||||
#define BYTE_ORDER LITTLE_ENDIAN
|
||||
|
||||
/*
|
||||
|
|
|
@ -67,6 +67,9 @@ struct page_dir;
|
|||
|
||||
/** @brief The task_t structure */
|
||||
typedef struct task {
|
||||
#ifdef SW_TASK_SWITCH
|
||||
uint32_t stack;
|
||||
#endif
|
||||
/// Task id = position in the task table
|
||||
tid_t id;
|
||||
/// Task status (INVALID, READY, RUNNING, ...)
|
||||
|
|
|
@ -46,9 +46,15 @@
|
|||
*
|
||||
* A task's id will be its position in this array.
|
||||
*/
|
||||
#ifdef SW_TASK_SWITCH
|
||||
static task_t task_table[MAX_TASKS] = { \
|
||||
[0] = {0, 0, TASK_IDLE, 0, 0, 0, NULL, NULL, 0, ATOMIC_INIT(0), SPINLOCK_INIT, NULL, SPINLOCK_INIT, NULL, NULL, 0, 0, 0, 0}, \
|
||||
[1 ... MAX_TASKS-1] = {0, 0, TASK_INVALID, 0, 0, 0, NULL, NULL, 0, ATOMIC_INIT(0), SPINLOCK_INIT, NULL, SPINLOCK_INIT, NULL, NULL, 0, 0, 0, 0}};
|
||||
#else
|
||||
static task_t task_table[MAX_TASKS] = { \
|
||||
[0] = {0, TASK_IDLE, 0, 0, 0, NULL, NULL, 0, ATOMIC_INIT(0), SPINLOCK_INIT, NULL, SPINLOCK_INIT, NULL, NULL, 0, 0, 0, 0}, \
|
||||
[1 ... MAX_TASKS-1] = {0, TASK_INVALID, 0, 0, 0, NULL, NULL, 0, ATOMIC_INIT(0), SPINLOCK_INIT, NULL, SPINLOCK_INIT, NULL, NULL, 0, 0, 0, 0}};
|
||||
#endif
|
||||
|
||||
static spinlock_irqsave_t table_lock = SPINLOCK_IRQSAVE_INIT;
|
||||
#if MAX_CORES > 1
|
||||
|
@ -1372,16 +1378,21 @@ get_task_out:
|
|||
orig_task->flags &= ~TASK_FPU_USED;
|
||||
}
|
||||
|
||||
//kprintf("schedule from %u to %u with prio %u on core %u\n",
|
||||
// orig_task->id, curr_task->id, (uint32_t)curr_task->prio, CORE_ID);
|
||||
//kprintf("schedule from %u to %u with prio %u on core %u\n", orig_task->id, curr_task->id, (uint32_t)curr_task->prio, CORE_ID);
|
||||
#ifndef SW_TASK_SWITCH
|
||||
switch_task(curr_task->id);
|
||||
#endif
|
||||
finish_task_switch(0);
|
||||
#ifdef SW_TASK_SWITCH
|
||||
write_cr3(virt_to_phys((size_t)curr_task->pgd));
|
||||
sw_switch_context(&orig_task->stack, &curr_task->stack);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void reschedule(void)
|
||||
{
|
||||
uint32_t flags = irq_nested_disable();
|
||||
scheduler();
|
||||
scheduler();
|
||||
irq_nested_enable(flags);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue