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.
For some reason when I try enable paging I am getting a page fault which eventually turns into a triple fault right after enabling paging before returning from the assembly function call.
Is there anything obvious I have missed?
The code should map everything 1:1 to start off with as a test.
"Address" here isn't right. That field is the Page Frame Number (PFN) so multiplying it by the Page Size does not make sense nor does trying to store a 32 bit address in a 20 bit field. It needs to be the PFN of the page tables and pages respectively. I also recommend just using the same structure for the directory and table...you'll simplify a lot of code later on.
For testing paging code, I always found Bochs to be helpful. A simple "info page" is all that is needed to check the page tables and current mapping.
typedef struct _MMPTE_I386 {
uint32_t valid: 1;
uint32_t write: 1;
uint32_t user: 1;
uint32_t writeThrough: 1;
uint32_t cacheDisable: 1;
uint32_t accessed: 1;
uint32_t dirty: 1;
uint32_t largePage: 1;
uint32_t global: 1;
/* following 3 bits we can use for any purpose. */
uint32_t copyOnWrite: 1;
uint32_t reserved: 1;
uint32_t reserved2: 1;
uint32_t pageFrameNumber: 20;
}MMPTE_I386;
The basic idea is that the physical address this maps is pageFrameNumber * PAGE_SIZE. Or, another way of looking at it, instead of "entry.Address = i * 4096" this would be "entry.pageFrameNumber = i".
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
"Address" here isn't right. That field is the Page Frame Number (PFN) so multiplying it by the Page Size does not make sense nor does trying to store a 32 bit address in a 20 bit field. It needs to be the PFN of the page tables and pages respectively. I also recommend just using the same structure for the directory and table...you'll simplify a lot of code later on.
For testing paging code, I always found Bochs to be helpful. A simple "info page" is all that is needed to check the page tables and current mapping.
typedef struct _MMPTE_I386 {
uint32_t valid: 1;
uint32_t write: 1;
uint32_t user: 1;
uint32_t writeThrough: 1;
uint32_t cacheDisable: 1;
uint32_t accessed: 1;
uint32_t dirty: 1;
uint32_t largePage: 1;
uint32_t global: 1;
/* following 3 bits we can use for any purpose. */
uint32_t copyOnWrite: 1;
uint32_t reserved: 1;
uint32_t reserved2: 1;
uint32_t pageFrameNumber: 20;
}MMPTE_I386;
The basic idea is that the physical address this maps is pageFrameNumber * PAGE_SIZE. Or, another way of looking at it, instead of "entry.Address = i * 4096" this would be "entry.pageFrameNumber = i".
I know you can't store a 32 bit address in 20 bits but the compiler implicitly takes the biggest 20 bits which does the job.
And huh? I must've gotten confused by the documentation, it is my first time hearing about page frames.
Just to reiterate, what should the "address" / 20 bit field contain for the directory table and page table respectively?
This shouldnt be the first time you heard about page frames. Have you not written a page frame allocator ("physical memory manager")?
I.e. PFN of 1 refers to address 0x1000. A PFN of 2 refers to address 0x2000 and so on. Think of it as PFN = address / PAGE_SIZE. This is why those structures must be paged aligned. PFN's only become necessary here because you are using bit fields as the underlaying structure does not store addresses.
In other words... you are storing an address in a field not meant to hold an address.
If you want to think in terms of addresses rather then PFN's, do what most here do and don't use bit fields. Then you can mask off the bits you don't want (and your idea applies.) But if you want to stick with bit fields (its what I use as well) then learn to use page frame numbers.
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
This shouldnt be the first time you heard about page frames. Have you not written a page frame allocator ("physical memory manager")?
I.e. PFN of 1 refers to address 0x1000. A PFN of 2 refers to address 0x2000 and so on. Think of it as PFN = address / PAGE_SIZE. This is why those structures must be paged aligned. PFN's only become necessary here because you are using bit fields as the underlaying structure does not store addresses.
In other words... you are storing an address in a field not meant to hold an address.
If you want to think in terms of addresses rather then PFN's, do what most here do and don't use bit fields. Then you can mask off the bits you don't want (and your idea applies.) But if you want to stick with bit fields (its what I use as well) then learn to use page frame numbers.
Ah okay that makes sense why I need to align the structures.
Instead of referring to addresses as 32 bit memory locations I should refer to them in page frames?
Have you not written a page frame allocator ("physical memory manager")?
I would have thought I would need to write one after I get paging working.
That did work! Thank you for informing me. The wiki and some of the examples gave me the impression that I should refer to addresses in actual addresses instead of the number of pages.
Another way of looking at it. Here is code from our Executive that sets up the initial Identity Space. Notice all we do is use the index? This refers to physical pages 0 - 1024 which is the first 4MB of the physical address space. This specific code was selected as it is the simplest example of how this is used. In more general cases, this "Index" comes from the Physical Frame allocator as it refers to a free frame.
Most tutorials and examples don't use this as a bit field but rather as an uint32_t which you can just set and mask off the bits you dont want. It is unfortunate, as looking at it in terms of page frames like above makes the code far simpler.
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}