Hi, anyone!
I'm writing an OS and want to improve multitasking. I've got the code (from Jame'S tutorial), but it doesn't work. I've got page fault when I call switch_task(). (Of course, paging is else from Jame'S tutorial ) If anyone know what can it be, please, tell me.
Thanks, anyone.
Problem with mixing multitasking and paging
Problem with mixing multitasking and paging
Don't think a ****, but in ukrainian schools English is TOO BAD!
Re: Problem with mixing multitasking and paging
Hi,
There are a few things here that could be causing the problem, but we really need more information - a Bochs register dump would be a good start. Also, what error code are you getting? Are you setting up a ring 3 task, or are you still in ring 0?
To load a new task, the kernel-mode stack, user-mode stack, kernel heap, user mode heap, kernel binary and user mode binary all need to be paged in at the correct user/supervisor levels. Be more specific and I can try to be more help.
Cheers,
Adam
There are a few things here that could be causing the problem, but we really need more information - a Bochs register dump would be a good start. Also, what error code are you getting? Are you setting up a ring 3 task, or are you still in ring 0?
To load a new task, the kernel-mode stack, user-mode stack, kernel heap, user mode heap, kernel binary and user mode binary all need to be paged in at the correct user/supervisor levels. Be more specific and I can try to be more help.
Cheers,
Adam
Re: Problem with mixing multitasking and paging
Hi, AJ!
I've got no Bochs, so I use VMWare (under Windows). I made my page fault to show the state of some registers, so this is my page fault message:
Page Fault! ( present) at EIP - 0x6A08EC83, ESP - 0x10C483D0, EAX - 0xA0, EBX - 0x27247B83, EDX - 0xEC831276, DS - 0x9085048B, CS - 0xE8206A20, SS - 0x14EC83C3. Page fault at pages.c: 246
This page fault I get when I try to switch task. Commonly, switch_task() is used by timer IRQ, but even when I just call it after fork() I get the same!
So, here is some code (it's almost the same like in clear JameS tutorial):
The paging is initialized for 16MB of memory:
and HEAP constants:
Is it enough? Hope it will help you (it means and it will help me too ).
PS: When I asked JameS what can it be, he told me, that there is a million and a one reason why my code page faults. You've said exectly another. Nice!
I've got no Bochs, so I use VMWare (under Windows). I made my page fault to show the state of some registers, so this is my page fault message:
Page Fault! ( present) at EIP - 0x6A08EC83, ESP - 0x10C483D0, EAX - 0xA0, EBX - 0x27247B83, EDX - 0xEC831276, DS - 0x9085048B, CS - 0xE8206A20, SS - 0x14EC83C3. Page fault at pages.c: 246
This page fault I get when I try to switch task. Commonly, switch_task() is used by timer IRQ, but even when I just call it after fork() I get the same!
So, here is some code (it's almost the same like in clear JameS tutorial):
Code: Select all
task.c
// The currently running task.
volatile task_t *current_task;
// The start of the task linked list.
volatile task_t *ready_queue;
// Some externs are needed to access members in paging.c...
extern page_directory_t *kernel_directory;
extern page_directory_t *current_directory;
extern void alloc_frame(page_t*,int,int);
extern u32int initial_esp;
extern u32int read_eip();
// The next available process ID.
u32int next_pid = 1;
void initialise_tasking()
{
// Rather important stuff happening, no interrupts please!
asm volatile("cli");
// Relocate the stack so we know where it is.
move_stack((void*)0xE0000000, 0x2000);
// Initialise the first task (kernel task)
current_task = ready_queue = (task_t*)kmalloc(sizeof(task_t));
current_task->id = next_pid++;
current_task->esp = current_task->ebp = 0;
current_task->eip = 0;
current_task->page_directory = current_directory;
current_task->next = 0;
// Reenable interrupts.
asm volatile("sti");
}
void move_stack(void *new_stack_start, u32int size)
{
u32int i;
// Allocate some space for the new stack.
for( i = (u32int)new_stack_start;
i >= ((u32int)new_stack_start-size);
i -= 0x1000)
{
// General-purpose stack is in user-mode.
alloc_frame( get_page(i, 1, current_directory), 0 /* User mode */, 1 /* Is writable */ );
}
// Flush the TLB by reading and writing the page directory address again.
u32int pd_addr;
asm volatile("mov %%cr3, %0" : "=r" (pd_addr));
asm volatile("mov %0, %%cr3" : : "r" (pd_addr));
// Old ESP and EBP, read from registers.
u32int old_stack_pointer; asm volatile("mov %%esp, %0" : "=r" (old_stack_pointer));
u32int old_base_pointer; asm volatile("mov %%ebp, %0" : "=r" (old_base_pointer));
// Offset to add to old stack addresses to get a new stack address.
u32int offset = (u32int)new_stack_start - initial_esp;
// New ESP and EBP.
u32int new_stack_pointer = old_stack_pointer + offset;
u32int new_base_pointer = old_base_pointer + offset;
// Copy the stack.
memcpy((void*)new_stack_pointer, (void*)old_stack_pointer, initial_esp-old_stack_pointer);
// Backtrace through the original stack, copying new values into
// the new stack.
for(i = (u32int)new_stack_start; i > (u32int)new_stack_start-size; i -= 4)
{
u32int tmp = * (u32int*)i;
// If the value of tmp is inside the range of the old stack, assume it is a base pointer
// and remap it. This will unfortunately remap ANY value in this range, whether they are
// base pointers or not.
if (( old_stack_pointer < tmp) && (tmp < initial_esp))
{
tmp = tmp + offset;
u32int *tmp2 = (u32int*)i;
*tmp2 = tmp;
}
}
// Change stacks.
asm volatile("mov %0, %%esp" : : "r" (new_stack_pointer));
asm volatile("mov %0, %%ebp" : : "r" (new_base_pointer));
}
void switch_task()
{
// If we haven't initialised tasking yet, just return.
if (!current_task)
return;
// Read esp, ebp now for saving later on.
u32int esp, ebp, eip;
asm volatile("mov %%esp, %0" : "=r"(esp));
asm volatile("mov %%ebp, %0" : "=r"(ebp));
// Read the instruction pointer. We do some cunning logic here:
// One of two things could have happened when this function exits -
// (a) We called the function and it returned the EIP as requested.
// (b) We have just switched tasks, and because the saved EIP is essentially
// the instruction after read_eip(), it will seem as if read_eip has just
// returned.
// In the second case we need to return immediately. To detect it we put a dummy
// value in EAX further down at the end of this function. As C returns values in EAX,
// it will look like the return value is this dummy value! (0x12345).
eip = read_eip();
// Have we just switched tasks?
if (eip == 0x12345)
return;
// No, we didn't switch tasks. Let's save some register values and switch.
current_task->eip = eip;
current_task->esp = esp;
current_task->ebp = ebp;
// Get the next task to run.
current_task = current_task->next;
// If we fell off the end of the linked list start again at the beginning.
if (!current_task) current_task = ready_queue;
eip = current_task->eip;
esp = current_task->esp;
ebp = current_task->ebp;
// Make sure the memory manager knows we've changed page directory.
current_directory = current_task->page_directory;
// Here we:
// * Stop interrupts so we don't get interrupted.
// * Temporarily puts the new EIP location in ECX.
// * Loads the stack and base pointers from the new task struct.
// * Changes page directory to the physical address (physicalAddr) of the new directory.
// * Puts a dummy value (0x12345) in EAX so that above we can recognise that we've just
// switched task.
// * Restarts interrupts. The STI instruction has a delay - it doesn't take effect until after
// the next instruction.
// * Jumps to the location in ECX (remember we put the new EIP in there).
asm volatile(" \
cli; \
mov %0, %%ecx; \
mov %1, %%esp; \
mov %2, %%ebp; \
mov %3, %%cr3; \
mov $0x12345, %%eax; \
sti; \
jmp *%%ecx "
: : "r"(eip), "r"(esp), "r"(ebp), "r"(current_directory->physicalAddr));
}
int fork()
{
// We are modifying kernel structures, and so cannot
asm volatile("cli");
// Take a pointer to this process' task struct for later reference.
task_t *parent_task = (task_t*)current_task;
// Clone the address space.
page_directory_t *directory = clone_directory(current_directory);
// Create a new process.
task_t *new_task = (task_t*)kmalloc(sizeof(task_t));
new_task->id = next_pid++;
new_task->esp = new_task->ebp = 0;
new_task->eip = 0;
new_task->page_directory = directory;
new_task->next = 0;
// Add it to the end of the ready queue.
task_t *tmp_task = (task_t*)ready_queue;
while (tmp_task->next)
tmp_task = tmp_task->next;
tmp_task->next = new_task;
// This will be the entry point for the new process.
u32int eip = read_eip();
// We could be the parent or the child here - check.
if (current_task == parent_task)
{
// We are the parent, so set up the esp/ebp/eip for our child.
u32int esp; asm volatile("mov %%esp, %0" : "=r"(esp));
u32int ebp; asm volatile("mov %%ebp, %0" : "=r"(ebp));
new_task->esp = esp;
new_task->ebp = ebp;
new_task->eip = eip;
asm volatile("sti");
return new_task->id;
}
else
{
// We are the child.
return 0;
}
}
The paging is initialized for 16MB of memory:
Code: Select all
void initialise_paging()
{
// The size of physical memory. For the moment we
// assume it is 16MB big.
u32int mem_end_page = 0x1000000;
nframes = mem_end_page / 0x1000;
frames = (u32int*)kmalloc(INDEX_FROM_BIT(nframes));
memset(frames, 0, INDEX_FROM_BIT(nframes));
// Let's make a page directory.
u32int phys;
kernel_directory = (page_directory_t*)kmalloc_a(sizeof(page_directory_t));
memset(kernel_directory, 0, sizeof(page_directory_t));
kernel_directory->physicalAddr = (u32int)kernel_directory->tablesPhysical;
// Map some pages in the kernel heap area.
// Here we call get_page but not alloc_frame. This causes page_table_t's
// to be created where necessary. We can't allocate frames yet because they
// they need to be identity mapped first below, and yet we can't increase
// placement_address between identity mapping and enabling the heap!
int i = 0;
for (i = KHEAP_START; i < KHEAP_START+KHEAP_INITIAL_SIZE; i += 0x1000)
get_page(i, 1, kernel_directory);
// We need to identity map (phys addr = virt addr) from
// 0x0 to the end of used memory, so we can access this
// transparently, as if paging wasn't enabled.
// NOTE that we use a while loop here deliberately.
// inside the loop body we actually change placement_address
// by calling kmalloc(). A while loop causes this to be
// computed on-the-fly rather than once at the start.
// Allocate a lil' bit extra so the kernel heap can be
// initialised properly.
i = 0;
while (i < placement_address+0x1000)
{
// Kernel code is readable but not writeable from userspace.
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
i += 0x1000;
}
// Now allocate those pages we mapped earlier.
for (i = KHEAP_START; i < KHEAP_START+KHEAP_INITIAL_SIZE; i += 0x1000)
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
// Before we enable paging, we must register our page fault handler.
idt_set_gate(14, (u32int)page_fault, 0x08, 0x8E);
// Now, enable paging!
switch_page_directory(kernel_directory);
// Initialise the kernel heap.
kheap = create_heap(KHEAP_START, KHEAP_START+KHEAP_INITIAL_SIZE, 0xCFFFF000, 0, 0);
current_directory = clone_directory(kernel_directory);
switch_page_directory(current_directory);
}
Code: Select all
#define KHEAP_START 0xC0000000
#define KHEAP_INITIAL_SIZE 0x100000
#define HEAP_INDEX_SIZE 0x20000
#define HEAP_MAGIC 0x123890AB
#define HEAP_MIN_SIZE 0x70000
PS: When I asked JameS what can it be, he told me, that there is a million and a one reason why my code page faults. You've said exectly another. Nice!
Don't think a ****, but in ukrainian schools English is TOO BAD!
- Combuster
- Member
- Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 1½m distance
- Contact:
Re: Problem with mixing multitasking and paging
Then its time to change thatI've got no Bochs