Paging and Multitasking
Posted: Tue Dec 23, 2008 9:04 am
I have paging already implemented in my kernel. I used JamesM's tutorials however I did it a completely different way, but it works. Now that I am at multitasking I don't know how to implement the clone page routines. I have done them how I think it will work but when I switch to the cloned page directory the CPU triple faults. I have tried to debug it but with no success. I was wondering if anyone can tell me if I have made an obvios mistake I am missing.
Here is my Pascal code for the page cloning:
And Here is the tutorial code:
That is all the cloning code here is the init code.
JamesM's:
It triple faults when I Switch The page dir. However my test page dir should be the same as the original.
Here is my Pascal code for the page cloning:
Code: Select all
function GetPage(Addr : LongWord; Make : Boolean; PageDir : PLongWord): PLongWord;
var
TableIdx, Temp : LongWord;
begin
Addr := Addr div $1000;
TableIdx := Addr div 1024;
if PageDir[TableIdx] <> 2 then // If the table is assigned already
GetPage := PLongWord(PLongWord(PageDir[TableIdx])[Addr mod 1024])
else if Make then
begin
PageDir[TableIdx] := LongWord(kmalloc(SizeOf(LongWord), true,@Temp));
FillByte(PageDir[TableIdx],$1000,0);
PageDir[TableIdx] := Temp or $7; //Present, ReadWrite, UserMode
GetPage:=PLongWord(PLongWord(PageDir[TableIdx])[Addr mod 1024]);
end
else
GetPage := nil;
end;
function ClonePageDir(var src : PLongWord) : PLongWord;
var
Phys : LongWord;
Dir : PLongWord;
i : integer;
begin
Dir := PLongWord(kmalloc(SizeOf(LongWord), true, @Phys));
FillByte(Dir,SizeOf(LongWord),0);
for i := 0 to 1023 do
begin
if src[i] <> 0 then
Exit;
if PageDirect[i] = src[i] then
Dir[i] := src[i]
else
begin
Dir[i] := (CloneTable(PLongWord(Dir[i]), @Phys))^;
Dir[i] := Phys or $7;
end;
end;
ClonePageDir := Dir;
end;
function CloneTable(src : PLongWord;Phys : PLongWord) : PLongWord;
var
Table : PLongWord;
i : integer;
begin
Table := PLongWord(kmalloc(SizeOf(LongWord), true, @Phys));
FillByte(Table,SizeOf(LongWord),0);
for i := 0 to 1023 do
begin
if (src[i] shr 12) <> 0 then
Exit;
AllocFrame(Table^,false,false);
if src[i] = 1 then Table[i] := 1;
if (src[i] shr 1) = 1 then Table[i] := 1 shl 1;
if (src[i] shr 2) = 1 then Table[i] := 1 shl 2;
if (src[i] shr 5) = 1 then Table[i] := 1 shl 5;
if (src[i] shr 6) = 1 then Table[i] := 1 shl 6;
CopyPagePhysical(PtrUInt((Src[i] shr 12) * $1000), PtrUInt((Table[i] shr 12) * $1000));
end;
CloneTable := Table;
end;
procedure CopyPagePhysical(Src, Dest : LongWord);assembler;nostackframe;
label loop;
asm
push ebx //; According to __cdecl, we must preserve the contents of EBX.
pushf //; push EFLAGS, so we can pop it and reenable interrupts
//; later, if they were enabled anyway.
cli //; Disable interrupts, so we aren't interrupted.
//; Load these in BEFORE we disable paging!
mov ebx, Src //; Source address
mov ecx, Dest //; Destination address
mov edx, cr0 //;Get the control register...
and edx, $7fffffff //; and...
mov cr0, edx //; Disable paging.
mov edx, 1024 //; 1024*4bytes = 4096 bytes to copy
loop:
mov eax, [ebx] //; Get the word at the source address
mov [ecx], eax //; Store it at the dest address
add ebx, 4 //; Source address += sizeof(word)
add ecx, 4 //; Dest address += sizeof(word)
dec edx //; One less word to do
jnz loop
mov edx, cr0 //; Get the control register again
or edx, $80000000 //; and...
mov cr0, edx //; Enable paging.
popf //; Pop EFLAGS back.
pop ebx //; Get the original value of EBX back.
ret
end;
procedure SetupPaging(const Value : LongWord);assembler;nostackframe;
asm
mov cr3, Value
mov eax, cr0
or eax, $80000000
mov cr0, eax
end;
procedure SwitchPageDir(PageDir: PLongWord);
begin
CurrentDirectory := PageDir;
SetupPaging(PtrUInt(PageDir));
end;
Code: Select all
page_directory_t *clone_directory(page_directory_t *src)
{
u32int phys;
// Make a new page directory and obtain its physical address.
page_directory_t *dir = (page_directory_t*)kmalloc_ap(sizeof(page_directory_t), &phys);
// Ensure that it is blank.
memset(dir, 0, sizeof(page_directory_t));
// Get the offset of tablesPhysical from the start of the page_directory_t structure.
u32int offset = (u32int)dir->tablesPhysical - (u32int)dir;
// Then the physical address of dir->tablesPhysical is:
dir->physicalAddr = phys + offset;
int i;
for (i = 0; i < 1024; i++)
{
if (!src->tables[i])
continue;
current_directory == kernel_directory;
if (kernel_directory->tables[i] == src->tables[i])
{
// It's in the kernel, so just use the same pointer.
dir->tables[i] = src->tables[i];
dir->tablesPhysical[i] = src->tablesPhysical[i];
}
else
{
// Copy the table.
u32int phys;
dir->tables[i] = clone_table(src->tables[i], &phys);
dir->tablesPhysical[i] = phys | 0x07;
}
}
return dir; }
static page_table_t *clone_table(page_table_t *src, u32int *physAddr)
{
// Make a new page table, which is page aligned.
page_table_t *table = (page_table_t*)kmalloc_ap(sizeof(page_table_t), physAddr);
// Ensure that the new table is blank.
memset(table, 0, sizeof(page_directory_t));
// For every entry in the table...
int i;
for (i = 0; i < 1024; i++)
{
if (!src->pages[i].frame)
continue;
// Get a new frame.
alloc_frame(&table->pages[i], 0, 0);
// Clone the flags from source to destination.
if (src->pages[i].present) table->pages[i].present = 1;
if (src->pages[i].rw) table->pages[i].rw = 1;
if (src->pages[i].user) table->pages[i].user = 1;
if (src->pages[i].accessed)table->pages[i].accessed = 1;
if (src->pages[i].dirty) table->pages[i].dirty = 1;
// Physically copy the data across. This function is in process.s.
copy_page_physical(src->pages[i].frame*0x1000, table->pages[i].frame*0x1000);
}
return table;
}
[GLOBAL copy_page_physical]
copy_page_physical:
push ebx ; According to __cdecl, we must preserve the contents of EBX.
pushf ; push EFLAGS, so we can pop it and reenable interrupts
; later, if they were enabled anyway.
cli ; Disable interrupts, so we aren't interrupted.
; Load these in BEFORE we disable paging!
mov ebx, [esp+12] ; Source address
mov ecx, [esp+16] ; Destination address
mov edx, cr0 ; Get the control register...
and edx, 0x7fffffff ; and...
mov cr0, edx ; Disable paging.
mov edx, 1024 ; 1024*4bytes = 4096 bytes to copy
.loop:
mov eax, [ebx] ; Get the word at the source address
mov [ecx], eax ; Store it at the dest address
add ebx, 4 ; Source address += sizeof(word)
add ecx, 4 ; Dest address += sizeof(word)
dec edx ; One less word to do
jnz .loop
mov edx, cr0 ; Get the control register again
or edx, 0x80000000 ; and...
mov cr0, edx ; Enable paging.
popf ; Pop EFLAGS back.
pop ebx ; Get the original value of EBX back.
ret
Code: Select all
procedure InitPaging(MemSizeInKB : LongWord);
var
i, Mem : LongWord;
Phys, test : PLongWord;
procedure DetectMemory;
var
S,SizeSuffix, sTmp : String;
begin
PrintString(#10'Detecting Memory... ');
Str(MemSizeInKB,S);
case Length(S) of
1..3: SizeSuffix:=' KB';
4..6: begin
MemSizeInKB := (MemSizeInKB shr 10)+1;
SizeSuffix:=' MB';
end;
else begin
MemSizeInKB := (MemSizeInKB shr 20)+1;
SizeSuffix :=' GB';
end;
end;
MemorySize := MemSizeInKB shl 10; // Turns KB into Bytes
FrameCount := MemorySize div PageSize;
Str(MemSizeInKB,sTmp);
PrintString(#10'You have : ' + sTmp);
PrintString(SizeSuffix + ' of memory available');
end;
begin
DetectMemory;
PrintString(#10'Initialising Paging');
{Frames := PLongWord(kmalloc(IndexFromBit(FrameCount),false,nil));
FillByte(Frames,IndexFromBit(FrameCount),0); }
// Create our page directory
PageDirect := PLongWord((PtrUInt(@KernelEnd) and $FFFFF000) + PageSize);
{PageDir := PLongWord(kmalloc(SizeOf(PLongWord),true,nil));
FillByte(PageDir,SizeOf(PLongWord),0);
CurrentDirectory := PageDir;
i := 0;
while i < PlacementAddr do
begin
AllocFrame(GetPage(i, true, PageDir)^,false,false);
i := i + $1000;
end; }
//Create our identity map to map virtual memory to physical memory
PageTable[0] := PageDirect + PageSize;
PageTable[768] := PageTable[0] + PageSize;
FillPageTable(PageTable[0],0); // Identity Map
FillPageTable(PageTable[768],$100000);
// The next 3 instructions MUST be kept ordered!!!
InitPageDirectory;
PageDirect[0] := PtrUInt(PageTable[0]) or 3;
PageDirect[768] := PtrUInt(PageTable[768]) or 3;
Frames := PLongWord(kmalloc(IndexFromBit(FrameCount),false,nil));
FillByte(Frames,IndexFromBit(FrameCount),0);
SetIDTGate(14,PtrUInt(@PageFaultHandler),$08,$8E);
SwitchPageDir(PageDirect);
PrintString(#10'Done');
test := ClonePageDir(PageDirect);
SwitchPageDir(test);
end;
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; // ********** ADDED ***********
kernel_directory = (page_directory_t*)kmalloc_a(sizeof(page_directory_t));
memset(kernel_directory, 0, sizeof(page_directory_t));
// *********** MODIFIED ************
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_END; 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.
register_interrupt_handler(14, page_fault);
// 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);
// ******** ADDED *********
current_directory = clone_directory(kernel_directory);
switch_page_directory(current_directory);
}