Page 1 of 1

Paging and Multitasking

Posted: Tue Dec 23, 2008 9:04 am
by System123
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:

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;
And Here is the tutorial code:

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

That is all the cloning code here is the init code.

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;
JamesM's:

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);
}
It triple faults when I Switch The page dir. However my test page dir should be the same as the original.