Page 1 of 1

Virtual Memory (Paging Specfically)

Posted: Sun Apr 03, 2011 7:46 pm
by xfelix
In my operating systems class, we're on project 4, and we're adding Virtual Memory to GeekOS.
The specs are right here (http://www.cs.umd.edu/class/spring2011/ ... /project4/)
For part1 of this project, we don't need to have the backstore to the hard disk setup (That is part2 of the project).
In part1, we're simply setting up the page directory, and page tables to all the physical memory. We're also letting user programs access
kernel memory temporarily, that way we know it's working correctly (all we need to do is run a program, and not crash :)).

Now when I go to call Enable_Paging with the pointer to the g_kernelDirectory. QEMU keeps resetting! (anyone know why this is?)
All Enable_Paging is doing is setting the cr3 register to the base of the page directory, and setting the bit in the cr0 register that paging is enabled.

Also, Since the memory is still static (given I can't swap between main memory and the hard disk), I can only create 32 page tables. This is because for each page table, I'm creating 1024 entries (note a page is 4K), and each entry has a 4K physical frame. This means each page table is allocating 2^12(2^10) = 2^22 = 4M of physical memory. When ever I try to make more then 32 page tables, GeekOS freezes up, and the Alloc_Page (method given to us that allocs 4K, and sets relevant flags, pageable etc.) is returning null. So anyway, the page directory only contains 32 page tables at the moment.

Questions
1. Why is QEMU resetting?
1a. Could this be happening, because of invalidly setting data in the entries? I have read over the table in 4-5/4-6 in the intel IA32 manual that explains everything.
Here are prototypes of the structs (pde_t is page directory entry type/pte_t is page table entry type)

Code: Select all

typedef struct {
    uint_t present:1;
    uint_t flags:4;
    uint_t accesed:1;
    uint_t reserved:1;
    uint_t largePages:1;
    uint_t globalPage:1;
    uint_t kernelInfo:3;
    uint_t pageTableBaseAddr:20;
} pde_t;

typedef struct {
    uint_t present:1;
    uint_t flags:4;
    uint_t accesed:1;
    uint_t dirty:1;
    uint_t pteAttribute:1;
    uint_t globalPage:1;
    uint_t kernelInfo:3;
    uint_t pageBaseAddr:20;
} pte_t;
1b. Could it be messing up because I haven't set the page fault interrupt yet? (Everything should be in memory though I think)

2. What do you master, professionals think? :mrgreen:

Any help is greatly appreciated! I've been stuck for a couple days now, and neither the professors or TA's are helping on the class forums. :(
(https://forum.cs.umd.edu/forumdisplay.php?f=229)

Re: Virtual Memory (Paging Specfically)

Posted: Sun Apr 03, 2011 9:01 pm
by thepowersgang
1. Triple Fault, use bochs and it will tell you the cause of the fault.
1a. Don't use bit-fields, they're not strictly defined so it's up to the compiler what order the bits are in (in the given case, 'present' could point to bit 31 instead of 0)
1b. Good idea, always at least have a stub page fault handler (esp. with qemu) it makes debugging that much less painful.
2. (rather open question) Well, your OS course kicks the one at my uni :)

Also, don't allocate the page tables unless needed (just leave that 4MiB unmapped by keeping the PD entry zero) - this will stop those out of memory errors.

Re: Virtual Memory (Paging Specfically)

Posted: Sun Apr 03, 2011 9:09 pm
by NickJohnson
1. QEMU resets when there is a triple fault, so most likely you're causing a page fault which causes a double/triple fault because you have no interrupt handler for it. Of course, knowing this doesn't help you much for debugging.

1a. Are you definitely putting the upper 20 bits of the page table/page directory physical addresses in the pageTableBaseAddr and pageBaseAddr fields, instead of the addresses themselves? You should make sure to set pageBaseAddr/pageTableBaseAddr to (address >> 12), since you're using bitfields to implicitly left shift the address 12 bits. The lower 12 bits are always zero, by the way, because the addresses are page-aligned. Alternatively, use macros, shifts, and bitwise operations instead of bitfields: they're more well-defined.

1b. No; even if you were to set up a page fault handler, it would likely still triple fault, since turning on paging improperly is going to cause everything to break, even the IDT and interrupt handlers. Once things stop completely exploding, however, a page fault handler becomes extremely useful.

2. I'm neither a master nor a professional :wink:

Also, since you seem to be mapping 4MB of physical memory at a time anyway, you may consider using the 4MB page extension (found in everything above Pentium IIRC): it probably violates the parameters of your assignment, but it makes the two-tiered paging system into a one-tiered one, and therefore eliminates a lot of variables in the implementation. If you really can't get normal paging to work, it may be a good stepping stone to make sure at least part of your implementation is working.

Re: Virtual Memory (Paging Specfically)

Posted: Sun Apr 03, 2011 9:15 pm
by Yargh
1a. __attribute__((packed)) ? There might be added padding between the values.

Re: Virtual Memory (Paging Specfically)

Posted: Tue Apr 05, 2011 6:10 pm
by xfelix
@NickJohnson
"The lower 12 bits are always zero, by the way, because the addresses are page-aligned. Alternatively, use macros, shifts, and bitwise operations instead of bitfields: they're more well-defined."
This is my first time using bitfields as a C programmer. I never even knew they existed before this project. I think this is my problem!
First off, everyone is telling me that this pageBaseAddr field is an index into the physical memory (where I thought it was a base of a physical frame). That way you can ultimately access all 4G of memory.

Are you saying that I need to make sure the base address is being set like this?
000000000001 | (other bit fields here) // for first entry
000000000010 | (other bit fields here) // for second entry etc..

@thepowergang
1. Triple Fault, use bochs and it will tell you the cause of the fault.
ok at least I know why QEMU is failing. If I had time to play with a new emulater right now I would, but this is due friday at midnight

@yargh
1a. __attribute__((packed)) ? There might be added padding between the values.
on the advent, bitfields tick me off so bad, I'm probably just going to change the entries to some 4 byte values and manually do it. Because this is getting really confusing. Here is a code snippet of what I'm doing right now that is still triple faulting...

Code: Select all

void* Create_Page_Directory() {
  int i;
  pde_t* pageDirectory = (pde_t*)Alloc_Page();
  Print("Create Page Directory 0x%08x\n", pageDirectory);
  // fix this to work with a dynamically changing RAM size 
  for(i=0; i < 2; i++) {
    pageDirectory[i].present = 1;
    //pageDirectory[i].flags = 0;   //temporarily
    pageDirectory[i].accesed = 0;
    pageDirectory[i].largePages = 1;
    pageDirectory[i].globalPage = 0; // temporary 
    pageDirectory[i].kernelInfo = 0; 
    pageDirectory[i].pageTableBaseAddr = Create_Page_Table();
    Print("Inserted Page Table [0x%08x] @ Directory Entry %d\n", pageDirectory[i].pageTableBaseAddr, i);
  }
  return pageDirectory;
}

void* Create_Page_Table() {
  int i;
  pte_t* pageTable = (pte_t*)Alloc_Page();
  for(i=0; i < NUM_PAGE_TABLE_ENTRIES; i++) {
    pageTable[i].present = 1;
    pageTable[i].flags |= VM_USER;
    pageTable[i].accesed = 0;
    pageTable[i].dirty = 0;
    pageTable[i].pteAttribute = 0;
    pageTable[i].globalPage = 0;
    pageTable[i].kernelInfo = 0;
    pageTable[i].pageBaseAddr = ++s_pageCount;
    Print("Inserted Physical Frame Base [0x%08x] @ Page Table Entry %d\n", pageTable[i].pageBaseAddr*4096,i);
  }
  return pageTable;
}
Really appreciate the help guys, thanks! :D

Re: Virtual Memory (Paging Specfically)

Posted: Tue Apr 05, 2011 6:44 pm
by NickJohnson
Here's the essence of the issue. This is what happens when you set foo.pageTableBaseAddr to 0x12345000, and look at it as a 32-bit integer: 0x45000XXX (where XXX depends on the other fields.) What you want is 0x12345XXX. The bitfield performs a shift behind the scenes of 12 bits because that's what makes sense usually. However, the Intel designers didn't want to waste 12 bits storing the lower 12 bits of the frame address, which must be zero due to alignment, so they used the lower bits for flags instead.

However, bitfields are bad because they're not well defined. These are the macros I use for setting frame values as 32 bit integers:

Code: Select all

#define PF_PRES 0x001 /* Is present */
#define PF_RW   0x002 /* Is writeable */
#define PF_USER 0x004 /* Is user-mode */
#define PF_WRTT 0x008 /* Write-through enabled */
#define PF_DISC 0x010 /* Cache disabled */
#define PF_DIRT 0x020 /* Is dirty */
#define PF_ACCS 0x040 /* Has been accessed */

#define PF_MASK 0x07F /* Page flags that can be used */

#define page_fmt(base,flags) (((base)&0xFFFFF000)|((flags)&PF_MASK))
#define page_ufmt(page) ((page)&0xFFFFF000)
page_fmt applies flags to a frame address; page_ufmt gets the frame address from a frame.