[Beginner] Basic paging

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
platonman1
Posts: 4
Joined: Mon Feb 22, 2010 9:13 am
Location: Strasbourg, France

[Beginner] Basic paging

Post 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 ! :?
Selenic
Member
Member
Posts: 123
Joined: Sat Jan 23, 2010 2:56 pm

Re: [Beginner] Basic paging

Post 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.
User avatar
platonman1
Posts: 4
Joined: Mon Feb 22, 2010 9:13 am
Location: Strasbourg, France

Re: [Beginner] Basic paging

Post 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 !
User avatar
thepowersgang
Member
Member
Posts: 734
Joined: Tue Dec 25, 2007 6:03 am
Libera.chat IRC: thePowersGang
Location: Perth, Western Australia
Contact:

Re: [Beginner] Basic paging

Post 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.
Kernel Development, It's the brain surgery of programming.
Acess2 OS (c) | Tifflin OS (rust) | mrustc - Rust compiler
Currently Working on: mrustc
User avatar
platonman1
Posts: 4
Joined: Mon Feb 22, 2010 9:13 am
Location: Strasbourg, France

Re: [Beginner] Basic paging

Post 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... :?
User avatar
Combuster
Member
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: [Beginner] Basic paging

Post by Combuster »

For one, page tables and directories need to be 4k aligned, malloc() by definition does not have that restriction.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
platonman1
Posts: 4
Joined: Mon Feb 22, 2010 9:13 am
Location: Strasbourg, France

Re: [Beginner] Basic paging

Post 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;
}
User avatar
Combuster
Member
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: [Beginner] Basic paging

Post by Combuster »

No, it does not align properly, it even throws away unnecessary memory
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Selenic
Member
Member
Posts: 123
Joined: Sat Jan 23, 2010 2:56 pm

Re: [Beginner] Basic paging

Post 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.
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: [Beginner] Basic paging

Post 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
Post Reply