Page 1 of 1

[Beginner] Basic paging

Posted: Mon Feb 22, 2010 11:06 am
by platonman1
Hello,

I'm having problems implementing basic paging.
What I'm trying to do is identity mapping all available memory just to test if it works correctly.
I've tried all sorts of different solutions I could think off, but no result...
The kernel initializes a GDT (for code and data, base=0 and limit=4G) and calls kPagingSetup(32000);
Could you please help me find out where the problem is ?


paging.h

Code: Select all

typedef struct page_t
{
   unsigned long int present : 1;
   unsigned long int rw : 1;
   unsigned long int user : 1;
   unsigned long int accessed : 1;
   unsigned long int dirty : 1;
   unsigned long int unused : 7 ;
   unsigned long int frame : 20;
}  __attribute__((packed)) page;


typedef struct pagetable_t
{
   page pages[1024];
} pagetable;


typedef struct pagedir_t
{
   pagetable* tables[1024];
} pagedir;


void kPagingAllocateFrame(pagedir* dir, unsigned long int addr, int user, int writable);
void kPagingFreeFrame(pagedir* dir, unsigned long int addr);
void kPagingSetup(unsigned long int end_mem);
void kPagingSwitchPagedir(pagedir* pd);

paging.c

Code: Select all

pagedir* kdir=0;


void kPagingAllocateFrame(pagedir* dir, unsigned long int addr, int user, int writable)
{
   // Compute offsets
   unsigned long int pdoffset = (addr & 0xFFC00000) >> 22;
   unsigned long int ptoffset = (addr & 0x3FF000) >> 12;

   if(dir->tables[pdoffset] != 0)
   {
      if(dir->tables[pdoffset]->pages[ptoffset].present == 1)
      {
         return; // Page already allocated
      }
      else
      {
         mmap_map(addr);
         dir->tables[pdoffset]->pages[ptoffset].present = 1;
         dir->tables[pdoffset]->pages[ptoffset].rw = 1;
         dir->tables[pdoffset]->pages[ptoffset].user = 1;
         dir->tables[pdoffset]->pages[ptoffset].frame = (addr & 0xFFFFF000) >> 12;
      }
   }
   else
   {
      pagetable* pt;
      pt = (pagetable*) kmalloc_a(sizeof(pagetable));
      memzero(pt, sizeof(pagetable));
      dir->tables[pdoffset] = pt;

      mmap_map(addr);
      dir->tables[pdoffset]->pages[ptoffset].present = 1;
      dir->tables[pdoffset]->pages[ptoffset].rw = 1;
      dir->tables[pdoffset]->pages[ptoffset].user = 1;
      dir->tables[pdoffset]->pages[ptoffset].frame = (addr & 0xFFFFF000) >> 12;
   }
}



void kPagingFreeFrame(pagedir* dir, unsigned long int addr)
{
   unsigned long int pdoffset = (addr & 0xFFC00000) >> 22;
   unsigned long int ptoffset = (addr & 0x3FF000) >> 12;

   if(dir->tables[pdoffset]->pages[ptoffset].present == 0)
      return;

   mmap_unmap(addr);
   dir->tables[pdoffset]->pages[ptoffset].present = 0;
}


void kPagingSetup(unsigned long int end_mem)
{
   end_mem *= 1024; // Convert end_mem from KBytes to bytes

   // Create kernel pagedir
   kdir = (pagedir*) kmalloc_a(sizeof(pagedir));
   memzero(kdir, sizeof(pagedir));

   int i;
   for(i=0; i<end_mem; i += 0x1000)
   {
      // TEST : Map all memory 1:1
      kPagingAllocateFrame(kdir, i, 0, 1);
   }

   print("[CORE]   Switching pagedir\n");
   kPagingSwitchPagedir(kdir);
}


void kPagingSwitchPagedir(pagedir* pd)
{
   asm volatile("mov %0, %%cr3" :: "r"(pd));

   unsigned long int cr0;
   asm volatile("mov %%cr0, %0" : "=r"(cr0));

   cr0 |= 0x80000000;
   asm volatile("mov %0, %%cr0" :: "r"(cr0));
}
Thanks alot ! :?

Re: [Beginner] Basic paging

Posted: Mon Feb 22, 2010 11:40 am
by Selenic
From a quick look at your code, you're not setting up the page directory correctly (page tables look fine) - you're just setting it to point at the page table entry and not setting anything else, including the present bit... (see the wiki page on Paging for the flags)

Also, for testing purposes, you can use 4MB pages (assuming you enable it by ORing CR4 with 0x10) which means you don't need any page tables, only a single one-page page directory.

Re: [Beginner] Basic paging

Posted: Fri Feb 26, 2010 7:20 am
by platonman1
Hey,

Sorry for beeing so late, been busy last few days...
I've made some modifications pointed out by Selenic and from what I've read across the internet, but nothing to do...

Here's Bochs status :

Code: Select all

00028829728i[CPU0 ] CPU is in protected mode (active)
00028829728i[CPU0 ] CS.d_b = 32 bit
00028829728i[CPU0 ] SS.d_b = 32 bit
00028829728i[CPU0 ] EFER   = 0x00000000
00028829728i[CPU0 ] | RAX=00000000e0000011  RBX=000000000002ba00
00028829728i[CPU0 ] | RCX=00000000000b8000  RDX=00000000000b8a33
00028829728i[CPU0 ] | RSP=0000000000143be8  RBP=0000000000143bf8
00028829728i[CPU0 ] | RSI=000000000002bb76  RDI=000000000002bb77
00028829728i[CPU0 ] |  R8=0000000000000000   R9=0000000000000000
00028829728i[CPU0 ] | R10=0000000000000000  R11=0000000000000000
00028829728i[CPU0 ] | R12=0000000000000000  R13=0000000000000000
00028829728i[CPU0 ] | R14=0000000000000000  R15=0000000000000000
00028829728i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df IF tf SF zf af PF cf
00028829728i[CPU0 ] | SEG selector     base    limit G D
00028829728i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00028829728i[CPU0 ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
00028829728i[CPU0 ] |  DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00028829728i[CPU0 ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00028829728i[CPU0 ] |  ES:0010( 0002| 0|  0) 00000000 000fffff 1 1
00028829728i[CPU0 ] |  FS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00028829728i[CPU0 ] |  GS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00028829728i[CPU0 ] |  MSR_FS_BASE:0000000000000000
00028829728i[CPU0 ] |  MSR_GS_BASE:0000000000000000
00028829728i[CPU0 ] | RIP=00000000001018ca (00000000001018ca)
00028829728i[CPU0 ] | CR0=0xe0000011 CR1=0x0 CR2=0x00000000001034b0
00028829728i[CPU0 ] | CR3=0x0014c000 CR4=0x00000000
00028829728i[CPU0 ] (instruction unavailable) page not present
00028829728e[CPU0 ] exception(): 3rd (14) exception with no resolution, shutdown status is 00h, resetting
Paging.h

Code: Select all

typedef struct pagetable_t
{
   unsigned long int pages[1024];
} pagetable;

typedef struct pagedir_t
{
   pagetable* tables[1024];
} pagedir;

#define FLAG_PRESENT     0x1
#define FLAG_READWRITE   0x2
#define FLAG_SUPERVISOR  0x0
#define FLAG_USER        0x4
#define FLAG_GLOBAL      0x100
#define FLAG_KERNELPAGE  0x200
#define FLAG_IMMUTABLE   0x400

void kPagingAllocateFrame(pagedir* dir, unsigned long int addr, int user, int writable);
void kPagingFreeFrame(pagedir* dir, unsigned long int addr);
void kPagingSetup(unsigned long int end_mem);
void kPagingSwitchPagedir(pagedir* pd);
paging.c

Code: Select all

// Skipped includes...

pagedir* kdir=0;

void kPagingAllocateFrame(pagedir* dir, unsigned long int addr, int user, int writable)
{
   // Find index from address
   unsigned long int pdoffset = (addr & 0xFFC00000) >> 22;
   unsigned long int ptoffset = (addr & 0x3FF000) >> 12;

   if(dir->tables[pdoffset] != 0)
   {
     if((dir->tables[pdoffset]->pages[ptoffset] & FLAG_PRESENT) == 1)
      {
         return; // Page already allocated
      }
      else
      {
         mmap_map(addr);
         dir->tables[pdoffset]->pages[ptoffset] = (addr & 0xFFFFF000) | FLAG_PRESENT | FLAG_READWRITE | FLAG_GLOBAL;
      }
   }
   else
   {
      unsigned long int pt = kmalloc_a(sizeof(pagetable));
      memzero((void *)pt, sizeof(pagetable));

      dir->tables[pdoffset] = (pagetable *)((pt & 0xFFFFF000) | FLAG_PRESENT | FLAG_READWRITE | FLAG_GLOBAL);

      mmap_map(addr);
      dir->tables[pdoffset]->pages[ptoffset] = (addr & 0xFFFFF000) | FLAG_PRESENT | FLAG_READWRITE | FLAG_GLOBAL;
   }
}

void kPagingFreeFrame(pagedir* dir, unsigned long int addr)
{
   unsigned long int pdoffset = (addr & 0xFFC00000) >> 22;
   unsigned long int ptoffset = (addr & 0x3FF000) >> 12;

   if((dir->tables[pdoffset]->pages[ptoffset] & FLAG_PRESENT) == 0)
      return;

   mmap_unmap(addr);
   dir->tables[pdoffset]->pages[ptoffset] &= ~(FLAG_PRESENT);
}

void kPagingSetup(unsigned long int end_mem)
{
  end_mem *= 1024; // Convert from Kb to b

   // Creating kernel pagedir
   kdir = (pagedir*) kmalloc_a(sizeof(pagedir));
   memzero(kdir, sizeof(pagedir));

   int i;
   for(i=0; i<=end_mem; i += 0x1000)
   {
      // TEST : Map all memory 1:1
      kPagingAllocateFrame(kdir, i, 0, 1);
   }

   kPagingSwitchPagedir(kdir);
}

void kPagingSwitchPagedir(pagedir* pd)
{
   asm volatile("mov %0, %%cr3" :: "r"(pd));

   unsigned long int cr0;
   asm volatile("mov %%cr0, %0" : "=r"(cr0));

   cr0 |= 0x80000000;
   asm volatile("mov %0, %%cr0" :: "r"(cr0));
}

Thanx again !

Re: [Beginner] Basic paging

Posted: Fri Feb 26, 2010 8:49 am
by thepowersgang
Just from skimming over the code and error log, I would suggest actually having identity mapping in place when you enable paging. Otherwise, your kernel will quite quickly triple fault when it can't access the next instruction.

Re: [Beginner] Basic paging

Posted: Mon Mar 01, 2010 2:45 am
by platonman1
Hi,

Sorry, I don't really understand what you say (English is not my primary language...).
Identity mapping should be in place and correctly configured in the pagedir and pagetables, when I move the address of pagedir in CR3... :?

Re: [Beginner] Basic paging

Posted: Mon Mar 01, 2010 3:06 am
by Combuster
For one, page tables and directories need to be 4k aligned, malloc() by definition does not have that restriction.

Re: [Beginner] Basic paging

Posted: Mon Mar 01, 2010 3:27 am
by platonman1
Thanx Combuster, my implementation of kmalloc_a seems to correctly align adresses :

Code: Select all

// Those are defined in loader.s
// khs:
// resb 0x80000
// khe:
extern unsigned long int khs;
extern unsigned long int khe;

unsigned long int kheap_start = (unsigned long int)&khs;
unsigned long int kheap_end = (unsigned long int)&khe;
unsigned long int kheap_curr = (unsigned long int)&khs;

unsigned long int kmalloc_a(unsigned long int sz)
{
   if((kheap_curr+sz) > kheap_end)
   {
      print("Fatal error : not enough free memory !\n");
      for(;;)
         ;
   }

   if(kheap_curr & 0xFFFFF000)
   {
      kheap_curr &= 0xFFFFF000;
      kheap_curr += 0x1000;
   }

   unsigned long int allocated_address = kheap_curr;
   kheap_curr += sz;

   return allocated_address;
}

Re: [Beginner] Basic paging

Posted: Mon Mar 01, 2010 8:13 am
by Combuster
No, it does not align properly, it even throws away unnecessary memory

Re: [Beginner] Basic paging

Posted: Mon Mar 01, 2010 11:37 am
by Selenic
The way I think most kernels manage memory is to have two kernel memory managers: one which allocates pages and page ranges and one, built on top of that, which acts like malloc. Page tables are always allocated using the first, so that you *know* that it's page-aligned, while smaller objects are allocated using the latter to avoid wasting space.

Re: [Beginner] Basic paging

Posted: Mon Mar 01, 2010 1:38 pm
by Owen
Selenic wrote:The way I think most kernels manage memory is to have two kernel memory managers: one which allocates pages and page ranges and one, built on top of that, which acts like malloc. Page tables are always allocated using the first, so that you *know* that it's page-aligned, while smaller objects are allocated using the latter to avoid wasting space.
Or, better option: Just allocate page space from the physical memory allocator