OSDev.org

The Place to Start for Operating System Developers
It is currently Wed Apr 24, 2024 4:53 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 40 posts ]  Go to page Previous  1, 2, 3
Author Message
 Post subject: Re: Paging: 32 Bits inside 20 Bits?
PostPosted: Thu Jun 01, 2017 12:53 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 4594
Location: Chichester, UK
Of course the Page Directory isn't fake. As long as you think that way you'll never understand the paging mechanism.


Top
 Profile  
 
 Post subject: Re: Paging: 32 Bits inside 20 Bits?
PostPosted: Thu Jun 01, 2017 9:24 am 
Offline
Member
Member

Joined: Thu Aug 13, 2015 4:57 pm
Posts: 384
Octacone wrote:
Thank you both guys for further explanation. I think I know what it going on!
So:
Page directory is fake, it doesn't even exist. It is just a creation of our imagination, an abstraction. What I really want to put inside CR3 is an address of all the physical page directory entries! Then each one of those page directory entries contain an address of its virtual representative we use for translating. I think that was the key part missing in this entire story. Now I just need a strategy for actually managing the virtual memory. What are the functions I need, standard ones perhaps? Something like allocate block/s free blocks/s but with ability to set the flags and a virtual address itself (the one we want). Okay let's put that aside. Now the real question is, are all the structures already pre-allocated since there is a finite number of entries (cause compiler knows what to reserve), that is the data section right? So that means I only need to allocate virtual page tables, right? This is so exciting I think I am slowly putting everything in perspective. :D


Few thoughts:
- The Page Directory is an array that contains 1024 entries, that collection of 1024 entries is what we call a PD, each entry is called PDE. The address of the PD is the same as the address of the first PDE, though the first PDE (as all the PDE's) contains a physical address that points to the respective PT.
- So both the PD and PT are simple 4 byte arrays of 1024 elements or entries each. They work like arrays in the C language.
- Consider creating a PMM (Physical Memory Manager) that only manages physical memory and only gives it or receives it from the VMM (Virtual Memory Manager). This makes the PMM very simple. The PMM when receiving a page frame (of physical memory during deallocation; or initialization) records that somewhere (stack, linked list, bitmap, etc.) so that later when a page frame is requested it can allocate one and return it to the VMM.
- Remember that the PMM (and VMM, like any other code) is running in virtual memory, so the container (stack, linked list, bitmap, etc) is addressed with virtual memory, not physical, but the data you store in that container is physical memory addresses (page frames).
- The VMM is simple if you make it simple. When app requests memory from VMM you do only basic sanity check, like app is not allowed to request virtual addresses that belong to kernel, you may also want to check that the requested address is not already mapped to the app (though later for performance reasons you may want to allow apps to arbitrarily request remapping of their memory).
- After that the VMM marks the page(s) in the current processes PD (and associated PDE(s), PT(s) and PTE(s)) as allocated, but _NOT_ present. You can simplify this too by only allocating one 4KiB page at a time, later for better performance you can allow arbitrary sizes to be mapped.
- When the app attempts to use the address it just requested to be mapped it will cause a #PF (Page Fault exception) because we the relevant PTE as not present. In the #PF handler you check the address that caused the fault, go thru the tables and see that the address has been _allocated_ but is not present, so now you request a physical page frame from the PMM and map the relevant entry to point to that physical page frame you got from the PMM and mark it as present and then return from the exception handler.
- That last part can also be done in the VMM when doing the allocation, but that means all memory that has been requested is also allocated which means more physical memory is used when it might not all be necessary, this is usually called "demand allocation". Which way you want to do it is up to you..
- This leaves only the app side remaining, in C you usually use malloc and C++ "new" operator. You will need to implement those yourself or use some pre-existing portable library.
- Normally you might say malloc(100) which would allocate 100 bytes, but the OS can only give out 4KiB (or more, but with 4KiB granularity at the minimum) at a time, so usually malloc checks if it has already requested memory and has some available (100+ bytes in this case) and if so, returns that, the OS/kernel is not involved. If malloc does not itself have it, then it will allocate as much as is needed and then it does have it, and thus the previous sentence now applies. Or it fails (because OS/kernel refused) and then you go into error handling mode.
- Initially you can create extremely simple malloc that always requests 4KiB for all allocations and doesn't hold any itself, this is less efficient but easier, you can later improve it. This is the way I would do it, so you get the whole memory system working. Then when the bad performance, both higher memory consumption as well as higher runtime overhead, becomes a problem you can optimize it. At that point you have something you can actually measure and see what optimizations make sense. And at that point you may want to use a pre-existing library and put in the effort it takes to port it to your system, instead of creating your own. Malloc itself can be somewhat complex and time consuming task and unless you have some novel ideas or are particularly interested on that specific topic I see no good reason why you need to create your own proper malloc implementation.

Hopefully this will clarify the situation further. So you have PMM, VMM and then "something" in userland, from kernel perspective that "something" is irrelevant and it doesn't care. The common API between userland and kernel is either brk/sbrk or some form of mmap. Brk/sbrk is slightly simpler, it just moves a "high-water mark", which means VMM would simply mark the relevant pages as allocated/not-allocated based on what was previously allocated and what was requested. Mmap is superior and the way forward, brk/sbrk isn't sufficiently simpler that I would recommend it, only reason to even consider brk/sbrk is if you need interoperability with POSIX/Linux or some other legacy system. So go for mmap, ignore brk/sbrk.


Top
 Profile  
 
 Post subject: Re: Paging: 32 Bits inside 20 Bits?
PostPosted: Thu Jun 01, 2017 10:40 am 
Offline
Member
Member
User avatar

Joined: Fri Oct 21, 2011 9:47 pm
Posts: 286
Location: Tustin, CA USA
LtG wrote:
Few thoughts:


I would also like too add a couple; LtG beat me to reply.

- Consider allocating your Page Directory array during your VMM initialization.
- The Page Directory conceptually is really and array of arrays. Yes it does exist and you need to allocate space for it. This Page Directory Physical Address must end in 0x000.
- Virtual Addresses and Physical Addresses share the same values and unless you are very clear about which you are referring to even in your own thought process, it helps to change the values a bit to maintain the mental separation. For me, I refer to Physical Address 0x10203000 as Frame 0x10203. Note the following 2 equations are both true: 0x10203000 == 0x10203 << 12; 0x10203000 == 0x10203 * 4096. By the way, shifting is far faster than multiplying, though a good compiler should be able to optimize this particular multiplication.
- As long as you are identity mapping this space, you will be fine. Once you start mapping Virtual Address Space to different Physical Address Space things will get very complicated quickly. Make sure you have a full understanding (and not just working) of the structures and how they work before you make this additional leap forward.

Octacone wrote:
What are the functions I need, standard ones perhaps?

LtG wrote:
Consider creating a PMM (Physical Memory Manager) that only manages physical memory and only gives it or receives it from the VMM (Virtual Memory Manager).


I cannot agree more. First, make sure your PMM is working perfectly (maybe write it as a normal user-space program first and exercise the heck out of it). There are several ways to manage this memory such as bitmaps and stacks. Some functions to consider:
Code:
void FrameAlloc(uint32_t frame);     // allocate the frame
void FrameDealloc(uint32_t frame);           // deallocate the frame
void FrameAllocRange(uint32_t frame, size_t count);     // allocate a range of frames
bool IsFrameAllocated(uint32_t frame);        // is the frame allocated?
uint32_t FrameNew(void);             // go find an unallocated frame and allocate it; return the frame number allocated


Then, your VMM is simple as well:
Code:
bool  VmmMapPageToFrame(uint32_t pageAddr,  uint32_t frame);              // map a virtual address to a frame
bool VmmUnmapPage(uint32_t pageAddr);                                              // unmap a virtual address and release the frame


You will have a little more sanity checking in these functions. You can also optionally pass in the address of the Page Directory to operate because you are thinking ahead to managing user-space page tables as well.

You might also want to look at some code help solidify concepts. I have a 64-bit kernel written in asm which at one point had statically allocated page tables (4 levels). These are not easily copied from my kernel to yours so you would have to understand the implementation, but they might help with the concepts. I eventually abandoned the static tables. https://github.com/eryjus/Century-64/bl ... getables.s. This kernel is no longer actively maintained, but there are other iterations in this repo.

_________________
Adam

The name is fitting: Century Hobby OS -- At this rate, it's gonna take me that long!
Read about my mistakes and missteps with this iteration: Journal

"Sometimes things just don't make sense until you figure them out." -- Phil Stahlheber


Top
 Profile  
 
 Post subject: Re: Paging: 32 Bits inside 20 Bits?
PostPosted: Thu Jun 01, 2017 10:55 am 
Offline
Member
Member

Joined: Wed Jul 10, 2013 9:11 am
Posts: 51
One more thing. Since you are using C++ like I am, I should mention at some point you should consider having an "early_main" function that initializes paging and sets up mappings (for your kernel/video memory etc.) before you call "init_" (the stub where GCC will insert global constructor initialization code). This will allow you to have globals that make memory allocations; whether or not that's good practice is a different story.

In particular this will allow you for example to have a non-trivial global object like boost::circular_buffer in your IRQ file.

The catch is that since C++'s static initialization order is undefined, you will need to use the "construct upon first use" idiom for some objects like your printf/terminal class. Example:

Code:
      Terminal& terminal()
      {
          static Terminal* terminal = new Terminal();
          return *terminal;
      }


Then replace all instances of "terminal." with "terminal()."

This will work so long as your first use of terminal() is after you set up paging, but ensures that terminal is initialized before any output happens.


Top
 Profile  
 
 Post subject: Re: Paging: 32 Bits inside 20 Bits?
PostPosted: Sat Jun 03, 2017 4:05 am 
Offline
Member
Member
User avatar

Joined: Fri Aug 07, 2015 6:13 am
Posts: 1134
LtG wrote:
Octacone wrote:
Thank you both guys for further explanation. I think I know what it going on!
So:
Page directory is fake, it doesn't even exist. It is just a creation of our imagination, an abstraction. What I really want to put inside CR3 is an address of all the physical page directory entries! Then each one of those page directory entries contain an address of its virtual representative we use for translating. I think that was the key part missing in this entire story. Now I just need a strategy for actually managing the virtual memory. What are the functions I need, standard ones perhaps? Something like allocate block/s free blocks/s but with ability to set the flags and a virtual address itself (the one we want). Okay let's put that aside. Now the real question is, are all the structures already pre-allocated since there is a finite number of entries (cause compiler knows what to reserve), that is the data section right? So that means I only need to allocate virtual page tables, right? This is so exciting I think I am slowly putting everything in perspective. :D


Few thoughts:
- The Page Directory is an array that contains 1024 entries, that collection of 1024 entries is what we call a PD, each entry is called PDE. The address of the PD is the same as the address of the first PDE, though the first PDE (as all the PDE's) contains a physical address that points to the respective PT.
- So both the PD and PT are simple 4 byte arrays of 1024 elements or entries each. They work like arrays in the C language.
- Consider creating a PMM (Physical Memory Manager) that only manages physical memory and only gives it or receives it from the VMM (Virtual Memory Manager). This makes the PMM very simple. The PMM when receiving a page frame (of physical memory during deallocation; or initialization) records that somewhere (stack, linked list, bitmap, etc.) so that later when a page frame is requested it can allocate one and return it to the VMM.
- Remember that the PMM (and VMM, like any other code) is running in virtual memory, so the container (stack, linked list, bitmap, etc) is addressed with virtual memory, not physical, but the data you store in that container is physical memory addresses (page frames).
- The VMM is simple if you make it simple. When app requests memory from VMM you do only basic sanity check, like app is not allowed to request virtual addresses that belong to kernel, you may also want to check that the requested address is not already mapped to the app (though later for performance reasons you may want to allow apps to arbitrarily request remapping of their memory).
- After that the VMM marks the page(s) in the current processes PD (and associated PDE(s), PT(s) and PTE(s)) as allocated, but _NOT_ present. You can simplify this too by only allocating one 4KiB page at a time, later for better performance you can allow arbitrary sizes to be mapped.
- When the app attempts to use the address it just requested to be mapped it will cause a #PF (Page Fault exception) because we the relevant PTE as not present. In the #PF handler you check the address that caused the fault, go thru the tables and see that the address has been _allocated_ but is not present, so now you request a physical page frame from the PMM and map the relevant entry to point to that physical page frame you got from the PMM and mark it as present and then return from the exception handler.
- That last part can also be done in the VMM when doing the allocation, but that means all memory that has been requested is also allocated which means more physical memory is used when it might not all be necessary, this is usually called "demand allocation". Which way you want to do it is up to you..
- This leaves only the app side remaining, in C you usually use malloc and C++ "new" operator. You will need to implement those yourself or use some pre-existing portable library.
- Normally you might say malloc(100) which would allocate 100 bytes, but the OS can only give out 4KiB (or more, but with 4KiB granularity at the minimum) at a time, so usually malloc checks if it has already requested memory and has some available (100+ bytes in this case) and if so, returns that, the OS/kernel is not involved. If malloc does not itself have it, then it will allocate as much as is needed and then it does have it, and thus the previous sentence now applies. Or it fails (because OS/kernel refused) and then you go into error handling mode.
- Initially you can create extremely simple malloc that always requests 4KiB for all allocations and doesn't hold any itself, this is less efficient but easier, you can later improve it. This is the way I would do it, so you get the whole memory system working. Then when the bad performance, both higher memory consumption as well as higher runtime overhead, becomes a problem you can optimize it. At that point you have something you can actually measure and see what optimizations make sense. And at that point you may want to use a pre-existing library and put in the effort it takes to port it to your system, instead of creating your own. Malloc itself can be somewhat complex and time consuming task and unless you have some novel ideas or are particularly interested on that specific topic I see no good reason why you need to create your own proper malloc implementation.

Hopefully this will clarify the situation further. So you have PMM, VMM and then "something" in userland, from kernel perspective that "something" is irrelevant and it doesn't care. The common API between userland and kernel is either brk/sbrk or some form of mmap. Brk/sbrk is slightly simpler, it just moves a "high-water mark", which means VMM would simply mark the relevant pages as allocated/not-allocated based on what was previously allocated and what was requested. Mmap is superior and the way forward, brk/sbrk isn't sufficiently simpler that I would recommend it, only reason to even consider brk/sbrk is if you need interoperability with POSIX/Linux or some other legacy system. So go for mmap, ignore brk/sbrk.


I already have a working PMM implementation (for a long time now). I can't afford myself to do any of the dynamic memory allocation at the moment. My only consideration right now is identity mapping. Once I get that all sorted out, I can start thinking about other concepts. I am not going to port any external allocation libraries because they are ugly -> thousands and thousands of lines of code, ugly syntax, not easy to understand, requires other functions implemented. Why do you all keep mentioning user-space, I am not anywhere near that point. It is not that my apps need that, my kernel really badly needs it too. Currently I don't have any apps, since I don't have a multitasking implementation (that is why I need paging).

_________________
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader


Top
 Profile  
 
 Post subject: Re: Paging: 32 Bits inside 20 Bits?
PostPosted: Sat Jun 03, 2017 4:12 am 
Offline
Member
Member
User avatar

Joined: Fri Aug 07, 2015 6:13 am
Posts: 1134
eryjus wrote:
LtG wrote:
Few thoughts:


I would also like too add a couple; LtG beat me to reply.

- Consider allocating your Page Directory array during your VMM initialization.
- The Page Directory conceptually is really and array of arrays. Yes it does exist and you need to allocate space for it. This Page Directory Physical Address must end in 0x000.
- Virtual Addresses and Physical Addresses share the same values and unless you are very clear about which you are referring to even in your own thought process, it helps to change the values a bit to maintain the mental separation. For me, I refer to Physical Address 0x10203000 as Frame 0x10203. Note the following 2 equations are both true: 0x10203000 == 0x10203 << 12; 0x10203000 == 0x10203 * 4096. By the way, shifting is far faster than multiplying, though a good compiler should be able to optimize this particular multiplication.
- As long as you are identity mapping this space, you will be fine. Once you start mapping Virtual Address Space to different Physical Address Space things will get very complicated quickly. Make sure you have a full understanding (and not just working) of the structures and how they work before you make this additional leap forward.

Octacone wrote:
What are the functions I need, standard ones perhaps?

LtG wrote:
Consider creating a PMM (Physical Memory Manager) that only manages physical memory and only gives it or receives it from the VMM (Virtual Memory Manager).


I cannot agree more. First, make sure your PMM is working perfectly (maybe write it as a normal user-space program first and exercise the heck out of it). There are several ways to manage this memory such as bitmaps and stacks. Some functions to consider:
Code:
void FrameAlloc(uint32_t frame);     // allocate the frame
void FrameDealloc(uint32_t frame);           // deallocate the frame
void FrameAllocRange(uint32_t frame, size_t count);     // allocate a range of frames
bool IsFrameAllocated(uint32_t frame);        // is the frame allocated?
uint32_t FrameNew(void);             // go find an unallocated frame and allocate it; return the frame number allocated


Then, your VMM is simple as well:
Code:
bool  VmmMapPageToFrame(uint32_t pageAddr,  uint32_t frame);              // map a virtual address to a frame
bool VmmUnmapPage(uint32_t pageAddr);                                              // unmap a virtual address and release the frame


You will have a little more sanity checking in these functions. You can also optionally pass in the address of the Page Directory to operate because you are thinking ahead to managing user-space page tables as well.

You might also want to look at some code help solidify concepts. I have a 64-bit kernel written in asm which at one point had statically allocated page tables (4 levels). These are not easily copied from my kernel to yours so you would have to understand the implementation, but they might help with the concepts. I eventually abandoned the static tables. https://github.com/eryjus/Century-64/bl ... getables.s. This kernel is no longer actively maintained, but there are other iterations in this repo.


I do have a perfectly working bitmap physical memory manager, that has all those function working. What I don't have are those VMM functions. Right now I am trying to identity map my kernel, which is not going so well. Yeah sure, I am thinking about multitasking and switching the different page tables around. I just need a better understanding of the concept itself. I will take a look at the links provided to see if I can understand it any better.

_________________
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader


Top
 Profile  
 
 Post subject: Re: Paging: 32 Bits inside 20 Bits?
PostPosted: Sat Jun 03, 2017 4:16 am 
Offline
Member
Member
User avatar

Joined: Fri Aug 07, 2015 6:13 am
Posts: 1134
goku420 wrote:
One more thing. Since you are using C++ like I am, I should mention at some point you should consider having an "early_main" function that initializes paging and sets up mappings (for your kernel/video memory etc.) before you call "init_" (the stub where GCC will insert global constructor initialization code). This will allow you to have globals that make memory allocations; whether or not that's good practice is a different story.

In particular this will allow you for example to have a non-trivial global object like boost::circular_buffer in your IRQ file.

The catch is that since C++'s static initialization order is undefined, you will need to use the "construct upon first use" idiom for some objects like your printf/terminal class. Example:

Code:
      Terminal& terminal()
      {
          static Terminal* terminal = new Terminal();
          return *terminal;
      }


Then replace all instances of "terminal." with "terminal()."

This will work so long as your first use of terminal() is after you set up paging, but ensures that terminal is initialized before any output happens.


I don't need to change any of those since I don't have any problems with the current implementation. Also the constructors are working flawlessly. Nothing to complain about. Why to change something that already works beautifully? :)

_________________
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader


Top
 Profile  
 
 Post subject: Re: Paging: 32 Bits inside 20 Bits?
PostPosted: Sat Jun 03, 2017 8:45 am 
Offline
Member
Member
User avatar

Joined: Fri Aug 07, 2015 6:13 am
Posts: 1134
I am getting somewhere right?
Code:
uint32_t aligned_kernel_end = Memory_Core.Page_Align_Address(kernel_end);
uint32_t current_address;
page_directory = (page_directory_t*) PMM.Allocate_Blocks(sizeof(page_directory_t) / 4096);
page_directory->virtual_page_tables[0] = (page_table_t*) PMM.Allocate_Blocks(sizeof(page_table_t) / 4096);
String.Memory_Set(page_directory->virtual_page_tables[0], 0, 4096);
while(current_address < aligned_kernel_end)
{
   page_directory->physical_page_tables[current_address >> 22].present = 1;
   page_directory->physical_page_tables[current_address >> 22].write_enabled = 1;
   page_directory->physical_page_tables[current_address >> 22].user_page_table = 0;
   page_directory->physical_page_tables[current_address >> 22].page_table_address = ((uint32_t) (page_directory->virtual_page_tables[current_address >> 22]->pages[current_address >> 12) & 0x3FFF] >> 12); //fixed in the
        latest version 
   page_directory->virtual_page_tables[current_address >> 22]->pages[(current_address >> 12) & 0x3FF].present = 1;
   page_directory->virtual_page_tables[current_address >> 22]->pages[(current_address >> 12) & 0x3FF].write_enabled = 1;
   page_directory->virtual_page_tables[current_address >> 22]->pages[(current_address >> 12) & 0x3FF].user_page = 0;
   page_directory->virtual_page_tables[current_address >> 22]->pages[(current_address >> 12) & 0x3FF].frame_address = (current_address >> 12);
   current_address += 0x1000;
}
Switch_Page_Directory((uint32_t) & page_directory->physical_page_tables);
Enable_Paging()


I have no idea what is going on.
Qemu still crashes, Bochs gives no errors.

Edit:
Looks like my mappings are okay:
Code:
<bochs:2> page 0x0
PDE: 0x0000000000111023    ps         A pcd pwt S W P
PTE: 0x0000000000000003       g pat d a pcd pwt S W P
linear page 0x0000000000000000 maps to physical page 0x0000000000000000

Code:
<bochs:3> page 0xB8000
PDE: 0x0000000000111023    ps         A pcd pwt S W P
PTE: 0x00000000000b8003       g pat d a pcd pwt S W P
linear page 0x00000000000b8000 maps to physical page 0x00000000000b8000

Code:
This one is strange! Page directory is located at this address.
<bochs:5> page 0x10F000
PDE: 0x0000000000111023    ps         A pcd pwt S W P
PTE: 0x0000000000000000       g pat d a pcd pwt S R p
physical address not available for linear 0x000000000010f000

_________________
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader


Last edited by Octacone on Sat Jun 03, 2017 10:26 am, edited 3 times in total.

Top
 Profile  
 
 Post subject: Fixed: Paging: 32 Bits inside 20 Bits + Faults
PostPosted: Sat Jun 03, 2017 10:23 am 
Offline
Member
Member
User avatar

Joined: Fri Aug 07, 2015 6:13 am
Posts: 1134
Topic Closed! Thanks everyone for helping me out!

It works! My kernel is now identity mapped! Shock! Oh, this took so long!
Such a stupid mistake, how was the CPU supposed to access all the structures when they weren't even mapped themselves!? :oops:

Such an amazing feel! I was literally about to give up, but decided not to. :lol: :-"
Sometimes reading your code for 1000 times will reveal you the problem.
It is surely possible to enable paging without any tutorials, it just takes... couple of weeks.

_________________
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader


Top
 Profile  
 
 Post subject: Re: Fixed: Paging: 32 Bits inside 20 Bits + Faults
PostPosted: Sat Jun 03, 2017 10:25 pm 
Offline
Member
Member
User avatar

Joined: Sat Dec 27, 2014 9:11 am
Posts: 901
Location: Maadi, Cairo, Egypt
Octacone wrote:
It is surely possible to enable paging without any tutorials, it just takes... couple of weeks.

lol, it took me roughly four hours.

_________________
You know your OS is advanced when you stop using the Intel programming guide as a reference.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 40 posts ]  Go to page Previous  1, 2, 3

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 178 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group