Page 2 of 2

Posted: Thu Nov 08, 2007 12:43 pm
by JAAman
Craze Frog wrote:Ehm, I can't map the page directory onto itself until it's allocated.
worse problem: you cant allocate anything in user space either.... nothing can be done until a page directory exists... so this is basic initialization for each task, regardless of how the kernel is run
I can't allocate memory for the page directory without allocating a page.
you allocate the page directory from the parent tasks address space (for your first task, its done before starting paging)
I can't allocate a page without mapping the page directory onto itself. That's the problem.
this is the basic problem -- you cannot enter a page directory until that page directory exists -- create the page directory and first page anywhere in memory -- it really doesnt matter, because its not going to continue existing once its done -- unmap it and then you can load it into CR3, and all else can be done in itself...
So basically I need to do some static setup of stuff.
yes... of course, every task will always need to be set up first

Posted: Thu Nov 08, 2007 1:02 pm
by Craze Frog
Map the last page directory entry to the physical address of the page directory.

Code: Select all

page_directory_t  * init_pdir;

void paging_init() {
    init_pdir = new_frame()*FRAME_SIZE;
    init_pdir[1023] = init_pdir;
Does that look like I'm doing what you describe? (new_frame() simply finds a free frame and marks it as allocated.)

Edit: Removed an *.

Posted: Thu Nov 08, 2007 1:28 pm
by Craze Frog
Before turning on paging I need to identity map the kernel. Then I need to add new pages tables to page directory. Those pages need to allocated somewhere. I do that with new_frame(). But now, when I'm done, I haven't identity mapped the frames allocated with new_frame(). Is that a problem?

Code: Select all

int i, t;
    t = to_frame(end);
    for (i = 0; i<=t; i++) {
        init_pdir[i] = new_frame();
        // fill in the init_pdir[i]->table[0..1023] here
    }
// Oups, haven't identity mapped all the results of new_frame()!

Posted: Thu Nov 08, 2007 3:05 pm
by AJ
Craze Frog wrote:

Code: Select all

page_directory_t  * init_pdir;

void paging_init() {
    init_pdir = new_frame()*FRAME_SIZE;
    init_pdir[1023] = init_pdir;
Does that look like I'm doing what you describe? (new_frame() simply finds a free frame and marks it as allocated.)
Yes - that looks like it. For a more complete example:

Code: Select all

unsigned long *pd;

void paging_init()
{
  /* paging is not on yet! */
  pd = pmalloc();   /* where pmalloc is your physical frame allocator */
  pd[0x3FF] = pd | PAGE_PRESENT | PAGE_WRITE;

  /* identity map the code that is now running and your stack here */

  write_cr3(pd);
  pd = 0xFFFFF000;
  write_cr0(read_cr0() | 0x80000000);
}
Paging is now enabled. To map a new area, you can now do something like:

Code: Select all

    6  void page_in(void *vaddr, u32 flags)
    7  {
    8 	//
    9 	u32	pde = (u32)vaddr / 0x400000;
   10 	u32    pte = (u32)vaddr / 0x1000;
   11 
   12 	if(!(pd[pde]&PAGE_PRESENT))
   13 	{
   14 		/*	need a new page table	*/
   15 		pd[pde] = (u32)pmalloc() | flags;
   16 	}
   17 	else if(pd[pde]&PAGE_4MB) return;
   18 
   19 	if(!(pt[pte]&PAGE_PRESENT))	pt[pte] = (u32)pmalloc() | flags;
   20 }
[EDIT]Sorry - pt is an unsigned long pointer to 0xFFC00000 (start of the page table 4MB range in virtual RAM. Sorry for any minor slips - I am rushing this![/EDIT]

HTH
Adam

Posted: Mon Nov 12, 2007 8:12 am
by AndrewAPrice
Craze Frog wrote:So basically I need to do some static setup of stuff.
If you're using a multiboot bootloader, it should pass how big the kernel is. Load the kernel. Using this you should be able to work out where you free memory begins. Place your free/used page bitmap here. Since you know how big the kernel is, and how much room the bitmap takes up, mark the pages these expand across as "used" in your bitmap. Your memory manager requires no more to setup. This bitmap is all you need to be able to find free pages. Then you can dynamically allocate a page for your page directories and page tables. The trick here is not to enable paging (or switch to your new page directory) until after you have constructed the page directory/tables.

How I do it in my kernel is:
- I link my kernel to be at 1GB+1MB.
- GRUB loads my kernel to 1MB.
- In my assembly stub code I have to place "- 0x40000000" next to every label until I map the top 1GB to the lower 1GB. (Use 4MB pages for this, it's easier).
- When my kernel switches to C++, I create a page bitmap and place it just after my kernel.
- Then using this bitmap I allocate the necessary page directories/tables.
- I set up the page tables to to cover the kernel, the bitmap, and the directories/tables.
- I then switch over to one of my new page directories.
- Now paging is set up and everything concerning memory management is functioning as normal.

Posted: Mon Nov 12, 2007 8:19 am
by Craze Frog
The trick here is not to enable paging (or switch to your new page directory) until after you have constructed the page directory/tables.
Which means I have to construct the page directory/tables with separate functions, some functions that works with paging on and some that works with paging off, which was what I wanted to avoid.

Posted: Mon Nov 12, 2007 10:56 am
by JAAman
Craze Frog wrote:
The trick here is not to enable paging (or switch to your new page directory) until after you have constructed the page directory/tables.
Which means I have to construct the page directory/tables with separate functions, some functions that works with paging on and some that works with paging off, which was what I wanted to avoid.
no you dont...

when you first enable paging, you will be working with a limited set -- and in many cases, the page directory and initial page tables can be hard-codded -- just load the table into memory, and assign it

for your second+ page directory/tables, you will be working in paging, just not in the page directory you are working on, these are create within the address space of the parent process, but it really doesnt matter because the code is located at the same address in every process -- so your code doesnt matter

the only thing that must be done outside of paging, is setting up the initial page tables for the first process -- and this is very basic initialization stuff, and should be considered a completely separate task -- like setting up the GDT/IDT/TSS/PMode/etc... all these things are one-time init things that must be handled specially, and only run once (normally) on startup, and are part of the boot process, not the kernel (i place them in the 2nd stage loader)

Posted: Mon Nov 12, 2007 11:07 am
by Craze Frog
Ok, thanks.

Posted: Mon Nov 12, 2007 9:08 pm
by iammisc
Your first page table doesn't need to be mapped into memory because there is no page table already in existence and paging isn't enabled.

It really isn't that hard.

I have a preliminary memory manager which allocates pages by adding 4kb to the kernel's end address aligned at a 4kb boundary. Then I use that as the first pdt. Then I use this simple memory manager to allocate page tables. Then a map the kernel to the right spot. Then i load cr3 with the pdt and turn paging on. Now the kernel has paging enabled. Then I start my physical memory manager which uses one struct for every available physical page. Each structure is a doubly linked list. I calculate how many pages are needed to store this information. Then I allocate that many pages allocating new page tables as I go along. Then I have function which will give me the flags of a physical page(whether or no it's available, it's NUMA domain, whether it's for DMA) from the physical address of the page.

I keep two pointers, one to the list of free pages, and one to the used pages.

Now I have a whole linked list of free kernel pages. Now my physical memory manager simply needs to take the first free page and set the free page pointer to the next page in the list. Then I take that free page structure and set it's next to the used page pointer and then set the used page pointer to that free page. Then I return that page address.

It really isn't that complex as you're making out to be.

This is a kernel you're going to have to make some compromises. You might not like my approach of allocating those initial pages in a different way than all the other and to be honest with you I hated it too until I realized that this isn't a userspace application. This is a kernel. By the time you're on your 4th kernel you'll realize this too.

In the end, I don't waste much space because that original page table becomes the page table of the init process.