Lots of new code up in CVS, simple multitasking is now working. Each task gets its own page directory so it has its own 4GB address space. The kernel is mapped across all tasks so we can still run code from the interrupts, very important if we wish to perform any task switching at all. The kernels heap must also be mapped in if we wish to access the tasks info structure during scheduling, it might be better to switch over to the kernels address space during this to avoid mapping the kernels heap into each task (especially as the heap will be changing over time), we could then switch back to the tasks address space when we are done. This would leave the task stack passed from the isr_dispatcher() unusable as it would be referencing memory mapped in the task and not the kernel, but that's unused at the moment so shouldn't be a problem. I'll have to play around with this to see which works best.
The paging code has been revamped to allow the creation, destruction and modification of multiple page directory's and all their entry's which is proving quite a powerful feature set for manipulating multiple address spaces and sharing memory.
Creating a task is easy enough:
struct TASK_INFO * task_create( void (*entrypoint)() )
{
struct TASK_STACK * stack;
struct TASK_INFO * task;
void * physicalAddress;
// create a new task info structure
task = mm_malloc( sizeof( struct TASK_INFO ) );
// assign a task id
task->id = task_total++;
// give it an initial tick slice
task->tick_slice = 1;
// set the initial task state
task->state = READY;
// create the new tasks page directory
task->page_dir = paging_createDirectory();
// map in the kernel
paging_mapKernel( task->page_dir );
// map in the kernel's heap
paging_mapKernelHeap( task->page_dir );
// identity map bottom 4MB's
for( physicalAddress=0L ; physicalAddress<(void *)(1024*PAGE_SIZE) ; physicalAddress+=PAGE_SIZE ) paging_setPageTableEntry( task->page_dir, physicalAddress, physicalAddress, TRUE );
// allocate a page for the stack
physicalAddress = physical_pageAlloc();
// map in the stack to the kernels address space so we can write to it
paging_setPageTableEntry( paging_kernelPageDir, TASK_STACKADDRESS, physicalAddress, TRUE );
// map in the stack to the tasks address space
paging_setPageTableEntry( task->page_dir, TASK_STACKADDRESS, physicalAddress, TRUE );
// save the physical stack address
task->stack = physicalAddress;
// create the initial stack fo we can perform a task switch
stack = (struct TASK_STACK *)( TASK_STACKADDRESS + TASK_STACKSIZE - sizeof(struct TASK_STACK) );
// clear the stack
mm_memset( (BYTE *)stack, 0x00, sizeof(struct TASK_STACK) );
// set the code segment
stack->cs = 0x08;
// set the data segments
stack->ds = 0x10;
stack->es = 0x10;
stack->fs = 0x10;
stack->gs = 0x10;
// set the eflags register
stack->eflags = 0x0202;
// set our initial entrypoint
stack->eip = (DWORD)entrypoint;
// set the interrupt number we will return from
stack->intnumber = IRQ0;
// set the tasks current esp to the stack
task->current_esp = (DWORD)stack;
// unmap the stack from the kernels address space
paging_setPageTableEntry( paging_kernelPageDir, TASK_STACKADDRESS, NULL, FALSE );
// add the task to the scheduler
scheduler_addTask( task );
// return with new task info
return task;
}
When I have the scheduler a bit more refined I'll post up the details about performing actual context switches.