Paging question

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
mike13
Posts: 11
Joined: Sat Jun 24, 2006 8:38 am

Paging question

Post by mike13 »

Hey, I have put together a fairly basic os (Interrupts, nice keyboard driver, GDT, IDT, and a large portion of libc ported) , and now I'm starting work on a memory manager, I chose paging because of it's supposed portability, and now I'm having a little trouble with implementation, ok I have a very very limited understanding of how paging works, here's how I see it:
A page directory is an array of page tables which is an array of pages, so in this tutorial http://osdever.net/tutorials/paging.php?the_id=43 I've compiled the snippets into a single file and here is what I have:

Code: Select all

#ifndef _MM0_C_
#define _MM0_C_

unsigned long *page_directory = (unsigned long *) 0x9C000;
/* the page table comes directly after the page directory (4kb aligned) */
/* 0x9D000 - 0x9C000 = 1000 <to> = 4096 bytes (4kb) */
unsigned long *page_table = (unsigned long *) 0x9D000;

/* externed from start.asm */
extern void write_cr3(unsigned long *addr);
extern void write_cr0(unsigned long addr);
extern unsigned long read_cr0(void);

void map_pages(void)
{
	unsigned long address=0; /* holds the physical address of where a page is */
	unsigned int i;
	
	for(i=0; i<1024; i++)
	{
		page_table[i] = address | 3; // attribute set to: supervisor level, read/write, present(011 in binary)
		address = address + 4096; // 4096 = 4kb
	}

	// fill the first entry of the page directory
	page_directory[0] = page_table; // attribute set to: supervisor level, read/write, present(011 in binary)
	page_directory[0] = page_directory[0] | 3;

	for(i=1; i<1024; i++)
	{
		page_directory[i] = 0 | 2; // attribute set to: supervisor level, read/write, not present(010 in binary)
	}
}

void enable_paging(void)
{
	/* places the address of the page directory in segment register CR3 */
	write_cr3(page_directory);
	/* enable paging by ssetting the paging bit (31) in CR0 to '1' */
	write_cr0(read_cr0() | 0x80000000);
}


void enable_mm0(void)
{
	map_pages();
	enable_paging();
	printf(" - Paging Enabled.\n");
}

#endif

First off, this code does compile and doesn't crash my kernel :D , but with a warning about line 10 of map_pages(); which I'm not concerned about right now.

So my first question... do I have my write_cr3, write_cr0, and read_cr0 prototypes correct?

My second question, the author of this tutorial mentioned that this code only maps 4MB of pages into memory, and I see how that works, but how would I go about adapting this code to map the rest of my system memory?

I have access to the memory information GRUB provides, and I know how to find the number of bytes in the extended memory, number of pages and pages_tables to cover all of my system's memory, but I don't understand how I would modify this code to allow me to make an array of page tables.... It seems like it would be very easy to do, and I have tried, but I am missing something here beause my system just restarts if I don't have all of the page tables either present or not present

- Thanks in advance
User avatar
chase
Site Admin
Posts: 710
Joined: Wed Oct 20, 2004 10:46 pm
Libera.chat IRC: chase_osdev
Location: Texas
Discord: chase/matt.heimer
Contact:

Post by chase »

With your code you should end up with a single page table at memory location 0x9D000;

The upper 20 bits of the first entry will contain 0, thus mapping virtual page 0 to physical page 0.

There is a page table directory at 0x9C000;

The page directory has one entry that points to the location of the first page table.

(added some comments)

Code: Select all

void map_pages(void)
{
   unsigned long address=0; /* holds the physical address of where a page is */
   unsigned int i;
   

   // This is setting up a page table, each entry in the page table mapping to 4k
   // A page table has 1024 entries
   // So 1024 x 4k gives 4M of mapped pages
   for(i=0; i<1024; i++)
   {
      page_table[i] = address | 3; // attribute set to: supervisor level, read/write, present(011 in binary)
      address = address + 4096; // 4096 = 4kb
   }

   // fill the first entry of the page directory
   // a page directory keeps track of page tables
   // the directory has 1024 entries
   // number of present page tables in a directory * amount of memory a single table keeps track of gives us the amount of mapped memory
   page_directory[0] = page_table; // attribute set to: supervisor level, read/write, present(011 in binary)
   page_directory[0] = page_directory[0] | 3;

   for(i=1; i<1024; i++)
   {
      page_directory[i] = 0 | 2; // attribute set to: supervisor level, read/write, not present(010 in binary)
   }
} 

So if we want to modify the above example to give us 8 megs mapped it'd be something like(being lazy and making the page tables consecutive):

Code: Select all

void map_pages(void)
{
   unsigned long address=0; /* holds the physical address of where a page is */
   unsigned int i;
   
   for(i=0; i<2048; i++)
   {
      page_table[i] = address | 3; // attribute set to: supervisor level, read/write, present(011 in binary)
      address = address + 4096; // 4096 = 4kb
   }


   for(i=0; i<2; i++)
   {
       // attribute set to: supervisor level, read/write, present(011 in binary)
      page_directory[i] = (page_table + (0x1000 * i)) | 3;
   }
   


   for(; i<1024; i++)
   {
      page_directory[i] = 0 | 2; // attribute set to: supervisor level, read/write, not present(010 in binary)
   }
} 
Just increment the number of page table entries you have by the number of 4k pages in your system and increment the number of page directory entries by the number of 4 meg chucks of memory you have.


I haven't tested the above code but all you're really doing here is turning on paging. There really isn't any page address translation taking place because you virtual address and your physical address are the same.
mike13
Posts: 11
Joined: Sat Jun 24, 2006 8:38 am

Post by mike13 »

Ok, thanks! I tried the code, and it didn't compile because of the following line:

Code: Select all

page_directory[i] = (page_table + (0x1000 * i)) | 3; 
However I substituted it with:

Code: Select all

page_directory[i] = (page_table + (0x1000 * i));
page_directory[i] = page_directory[i] | 3;
And it compiled just fine, so... does it look like everything is is order? and if so, what direction should I take next, I'm assuming an allocator is a bit further down the line?
User avatar
chase
Site Admin
Posts: 710
Joined: Wed Oct 20, 2004 10:46 pm
Libera.chat IRC: chase_osdev
Location: Texas
Discord: chase/matt.heimer
Contact:

Post by chase »

mike13 wrote:Ok, thanks! I tried the code, and it didn't compile because of the following line:

Code: Select all

page_directory[i] = (page_table + (0x1000 * i)) | 3; 
However I substituted it with:

Code: Select all

page_directory[i] = (page_table + (0x1000 * i));
page_directory[i] = page_directory[i] | 3;
And it compiled just fine, so... does it look like everything is is order? and if so, what direction should I take next, I'm assuming an allocator is a bit further down the line?
Opps, sorry I spend to much time doing Java :)

Next I'd just make sure that you can setup page tables for all of physical ram and have them working. Make sure to test you memory sizing code on a couple of different machines. It's a whole other discussion but sometimes reading CMOS or running at BIOS routine gives the wrong totals, sometime you'd have to actually try to use all 4Gig (32-bit system) and see when the RAM stops working.

You might want to turn paging on, fill up some RAM with ascii, and then turn paging off. Then see if you can copy the ascii back to 0XB8000+ and display the right data. (So a PMode video text handler too if you don't have one yet, earlier you have on the easier debugging will be)

After that, probably the allocator. You'll need some way to keep track of which pages are in use by applications and other stuff. One possibly way is to have your allocator run in a task with a 1 to 1 exact mapping of virtual to physical address and then use the extra free bits in the page table entries. (free, memory io, kernel page, etc).

The deallocator is probably harder then the allocator so keep it in mind when you're doing the allocator. Remember applications don't always call free, sometimes they die and the OS has to figure what to remove afterwards.
User avatar
JAAman
Member
Member
Posts: 879
Joined: Wed Oct 27, 2004 11:00 pm
Location: WA

Post by JAAman »

I have access to the memory information GRUB provides, and I know how to find the number of bytes in the extended memory
grub provides 2 methods of reporting memory size -- make sure you use the map not the total -- there is a very important difference, and only use the 'total' value if the map isnt availible (see below, but the 'total' value is generally considered unsafe)

...but sometimes reading CMOS or running at BIOS routine gives the wrong totals, sometime you'd have to actually try to use all 4Gig (32-bit system) and see when the RAM stops working.
yes, but remember, it is possible to physically damage the computer doing this... as certain memory locations contain chip registers (on almost every componant in your computer) and placing the wrong values in these locations can physically damage the components (though it isnt very likely, its quite possible) -- even if you stop at the first hole it can happen especially if you have a lot of RAM -- in fact, using the other BIOS calls, id say if they dont report the full ram, its might be because its not safe to use the RAM, or there is a hole -- nothing requires the RAM to be contingeous

for my OS, if e820 is not availible (the grub memory map is a copy of the e820 BIOS call), i use the other BIOS methods (the 'total memory' number), and use probing only for last resort -- but if e820 isnt availible, i ignore any RAM above 64MB (32MB if i'm using probing) -- just to be safe -- though you would prob be safe most of the time to use a higher number, but im a little paranoid, especially since only older hardware will fail to support e820 -- and older hardware is more likely to have hardware fixed at lower addresses (most other people accept it as safe below 1GB)
Post Reply