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;
|
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)
|
int test_init(void)
|
||||||
{
|
{
|
||||||
// char* argv[] = {"/bin/mshell", NULL};
|
// char* argv[] = {"/bin/mshell", NULL};
|
||||||
|
@ -424,8 +487,11 @@ int test_init(void)
|
||||||
//sem_init(&consuming, 0);
|
//sem_init(&consuming, 0);
|
||||||
//mailbox_int32_init(&mbox);
|
//mailbox_int32_init(&mbox);
|
||||||
|
|
||||||
create_kernel_task(NULL, foo, "Hello from foo1", NORMAL_PRIO);
|
create_kernel_task(NULL, measure_ctx_switch, (int)0, NORMAL_PRIO);
|
||||||
create_kernel_task(NULL, join_test, NULL, 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, producer, , NORMAL_PRIO);
|
||||||
//create_kernel_task(NULL, consumer, NULL, NORMAL_PRIO);
|
//create_kernel_task(NULL, consumer, NULL, NORMAL_PRIO);
|
||||||
//create_kernel_task(NULL, mail_ping, 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, laplace, NULL, NORMAL_PRIO);
|
||||||
//create_kernel_task(NULL, jacobi, NULL, NORMAL_PRIO);
|
//create_kernel_task(NULL, jacobi, NULL, NORMAL_PRIO);
|
||||||
//create_user_task(NULL, "/bin/hello", argv);
|
//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/jacobi", argv);
|
||||||
//create_user_task(NULL, "/bin/mshell", argv);
|
//create_user_task(NULL, "/bin/mshell", argv);
|
||||||
//create_user_task(NULL, "/bin/jacobi", argv);
|
//create_user_task(NULL, "/bin/jacobi", argv);
|
||||||
|
|
|
@ -507,6 +507,40 @@ hack:
|
||||||
jmp 0x00 : 0xDEADBEAF
|
jmp 0x00 : 0xDEADBEAF
|
||||||
ret
|
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
|
; 32: IRQ0
|
||||||
irq0:
|
irq0:
|
||||||
; irq0 - irq15 are registered as "Interrupt Gate"
|
; irq0 - irq15 are registered as "Interrupt Gate"
|
||||||
|
|
|
@ -27,7 +27,11 @@
|
||||||
#include <asm/page.h>
|
#include <asm/page.h>
|
||||||
|
|
||||||
gdt_ptr_t gp;
|
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)));
|
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};
|
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);
|
uint32_t default_stack_pointer = (uint32_t) kstacks[0] + KERNEL_STACK_SIZE - sizeof(size_t);
|
||||||
// currently, our kernel has full access to the ioports
|
// 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)
|
int arch_fork(task_t* task)
|
||||||
{
|
{
|
||||||
|
#ifndef SW_TASK_SWITCH
|
||||||
uint16_t cs = 0x08;
|
uint16_t cs = 0x08;
|
||||||
uint16_t ds = 0x10;
|
uint16_t ds = 0x10;
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
|
@ -120,6 +125,7 @@ int arch_fork(task_t* task)
|
||||||
asm volatile ("pushf; pop %%eax" : "=a"(task_state_segments[id].eflags));
|
asm volatile ("pushf; pop %%eax" : "=a"(task_state_segments[id].eflags));
|
||||||
// This will be the entry point for the new task.
|
// This will be the entry point for the new task.
|
||||||
asm volatile ("call read_eip" : "=a"(task_state_segments[id].eip));
|
asm volatile ("call read_eip" : "=a"(task_state_segments[id].eip));
|
||||||
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -130,10 +136,60 @@ int create_default_frame(task_t* task, internal_entry_point_t ep, void* arg)
|
||||||
uint16_t ds = 0x10;
|
uint16_t ds = 0x10;
|
||||||
uint32_t id;
|
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))
|
if (BUILTIN_EXPECT(!task, 0))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
id = task->id;
|
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 */
|
/* reset buffers */
|
||||||
memset(task_state_segments+id, 0x00, sizeof(tss_t));
|
memset(task_state_segments+id, 0x00, sizeof(tss_t));
|
||||||
memset(kstacks[id], 0xCD, KERNEL_STACK_SIZE);
|
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 */
|
/* setup for the kernel stack frame */
|
||||||
task_state_segments[id].ss0 = 0x10;
|
task_state_segments[id].ss0 = 0x10;
|
||||||
task_state_segments[id].esp0 = (uint32_t) kstacks[id] + KERNEL_STACK_SIZE - sizeof(size_t);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Setup a descriptor in the Global Descriptor Table */
|
/* Setup a descriptor in the Global Descriptor Table */
|
||||||
static void gdt_set_gate(int num, unsigned long base, unsigned long limit,
|
static void gdt_set_gate(int num, unsigned long base, unsigned long limit,
|
||||||
|
@ -203,7 +298,11 @@ void gdt_install(void)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
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));
|
memset(task_state_segments, 0x00, MAX_TASKS*sizeof(tss_t));
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Setup the GDT pointer and limit */
|
/* Setup the GDT pointer and limit */
|
||||||
gp.limit = (sizeof(gdt_entry_t) * GDT_ENTRIES) - 1;
|
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)
|
* 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++) {
|
for(i=0; i<MAX_TASKS; i++) {
|
||||||
|
#endif
|
||||||
gdt_set_gate(5+i, (unsigned long) (task_state_segments+i), sizeof(tss_t)-1,
|
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_PRESENT | GDT_FLAG_TSS | GDT_FLAG_RING0,
|
||||||
GDT_FLAG_32_BIT);
|
GDT_FLAG_32_BIT);
|
||||||
|
|
|
@ -42,6 +42,8 @@ extern "C" {
|
||||||
#define VIDEO_MEM_ADDR 0xB8000 // the video memora address
|
#define VIDEO_MEM_ADDR 0xB8000 // the video memora address
|
||||||
#define SMP_SETUP_ADDR 0x07000
|
#define SMP_SETUP_ADDR 0x07000
|
||||||
|
|
||||||
|
#define SW_TASK_SWITCH
|
||||||
|
|
||||||
#define BYTE_ORDER LITTLE_ENDIAN
|
#define BYTE_ORDER LITTLE_ENDIAN
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -67,6 +67,9 @@ struct page_dir;
|
||||||
|
|
||||||
/** @brief The task_t structure */
|
/** @brief The task_t structure */
|
||||||
typedef struct task {
|
typedef struct task {
|
||||||
|
#ifdef SW_TASK_SWITCH
|
||||||
|
uint32_t stack;
|
||||||
|
#endif
|
||||||
/// Task id = position in the task table
|
/// Task id = position in the task table
|
||||||
tid_t id;
|
tid_t id;
|
||||||
/// Task status (INVALID, READY, RUNNING, ...)
|
/// Task status (INVALID, READY, RUNNING, ...)
|
||||||
|
|
|
@ -46,9 +46,15 @@
|
||||||
*
|
*
|
||||||
* A task's id will be its position in this array.
|
* 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] = { \
|
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}, \
|
[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}};
|
[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;
|
static spinlock_irqsave_t table_lock = SPINLOCK_IRQSAVE_INIT;
|
||||||
#if MAX_CORES > 1
|
#if MAX_CORES > 1
|
||||||
|
@ -1372,16 +1378,21 @@ get_task_out:
|
||||||
orig_task->flags &= ~TASK_FPU_USED;
|
orig_task->flags &= ~TASK_FPU_USED;
|
||||||
}
|
}
|
||||||
|
|
||||||
//kprintf("schedule from %u to %u with prio %u on core %u\n",
|
//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);
|
||||||
// orig_task->id, curr_task->id, (uint32_t)curr_task->prio, CORE_ID);
|
#ifndef SW_TASK_SWITCH
|
||||||
switch_task(curr_task->id);
|
switch_task(curr_task->id);
|
||||||
|
#endif
|
||||||
finish_task_switch(0);
|
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)
|
void reschedule(void)
|
||||||
{
|
{
|
||||||
uint32_t flags = irq_nested_disable();
|
uint32_t flags = irq_nested_disable();
|
||||||
scheduler();
|
scheduler();
|
||||||
irq_nested_enable(flags);
|
irq_nested_enable(flags);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue