I'm trying to implement multithreading in my OS, but context switching causes a general protection fault.
Data structures:
Code: Select all
/**
* Kernel thread type
*/
typedef struct
{
uint32_t* stack; /**< Pointer to the head of the stack */
uint32_t* stackstart; /**< Pointer to the base of the stack */
uint32_t stacksize; /**< Size of the stack */
void (*threadfunc)(void); /**< Thread's main function */
void* threadargs; /**< Argument of the function */
} kthread_t;
/**
* Thread queue type
*/
typedef struct
{
kthread_t** threads; /**< List of threads */
size_t num; /**< Number of threads */
size_t cur; /**< ID of currently executing thread */
uint8_t inkernel; /**< Whether we're in the kernel thread or not */
uint8_t running; /**< Whether we're running or not */
} schedule_t;
Code: Select all
/**
* Switch to the next thread, if there is one
*/
uint32_t*
schedule (uint32_t* context)
{
/* Check if we're running */
if (!queue.running)
return context;
/* Check if there are any threads */
if (queue.num == 0)
{
kprintf((string)"There are no threads. Halting.\n");
halt ();
}
/* Check if there is a thread to switch to */
if (queue.num == 1 && !queue.inkernel)
return context;
/* Save the old stack context */
if (!queue.inkernel)
queue.threads[queue.cur]->stack = context;
/* Get the next thread */
if (!queue.inkernel)
queue.cur = (queue.cur + 1) % queue.num;
/* Set the new stack context */
kprintf((string)"Switching from stack context %x to %x\n", context, queue.threads[queue.cur]->stack);
/* If we were in the kernel thread, we're not any more */
queue.inkernel = 0;
/* Acknowledge the interrupt */
outport (PIC1, PIC_ACK);
/* Return the new stack context */
return queue.threads[queue.cur]->stack;
}
Code: Select all
extern schedule
;;; IRQ handler
%macro IRQ 2
global irq%1
irq%1:
cli
push byte 0
push byte %2
jmp handler
%endmacro
;;; IRQs which invoke the scheduler
IRQ 0, 32
;;; Push CPU state to the stack, call the handler, and pop the state back
handler:
pusha
push ds
push es
push fs
push gs
mov eax, 0x10
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
push esp
call schedule
mov esp, eax
pop gs
pop fs
pop es
pop ds
popa
add esp, 8
iret
Code: Select all
/**
* Create a thread, and add it to the scheduler queue
*/
void
kthread_create (void (*threadfunc)(void), void* threadargs)
{
kthread_t* kthread = (kthread_t*) kcalloc (1, sizeof (kthread_t));
/* Set up the stack */
kthread->stackstart = (uint32_t*) kmalloc (THREAD_STACK_SIZE);
kthread->stack = kthread->stackstart + THREAD_STACK_SIZE / sizeof(*kthread->stack);
kthread->stacksize = THREAD_STACK_SIZE;
/* Set up the initial stack */
*(--kthread->stack) = EFLAGS_IF | EFLAGS_RESERVED; /* EFLAGS */
*(--kthread->stack) = GDT_CODE_ID << 3; /* CS */
*(--kthread->stack) = (uint32_t)threadfunc; /* EIP */
*(--kthread->stack) = 0; /* EDI */
*(--kthread->stack) = 0; /* EDI */
*(--kthread->stack) = 0; /* EBP */
*(--kthread->stack) = 0; /* NULL */
*(--kthread->stack) = 0; /* EBX */
*(--kthread->stack) = 0; /* EDX */
*(--kthread->stack) = 0; /* ECX */
*(--kthread->stack) = 0; /* EAX */
*(--kthread->stack) = GDT_DATA_ID << 3; /* DS */
*(--kthread->stack) = GDT_DATA_ID << 3; /* ES */
*(--kthread->stack) = GDT_DATA_ID << 3; /* FS */
*(--kthread->stack) = GDT_DATA_ID << 3; /* GS */
/* Save the function and arguments */
kthread->threadfunc = threadfunc;
kthread->threadargs = threadargs;
/* Add to the schedulers queue */
scheduler_add_thread (kthread);
}