[Paging] How to fill the stack on process creation?
- max
- Member
- Posts: 616
- Joined: Mon Mar 05, 2012 11:23 am
- Libera.chat IRC: maxdev
- Location: Germany
- Contact:
[Paging] How to fill the stack on process creation?
Hey guys!
I'm currently in the following situation. My kernel is running in the higher half, starting at 0xC0000000. In my initial paging directory I created the tables at index 0 and 768-1022 as kernel-tables (to ensure that any change to the kernel space happens in all processes) and entry 1023 mapped to the directory itself. So far so good - now the time has come to implement preemptive multitasking. My problem is the following: I am now inside of the setup function of my kernel. Now I want to kick off the first process. To do so, I have to create a copy of the page directory (copying only the kernel tables), so entries 1-767 can be used as user space.
Now, I ask my physical page allocator for a new page to create a directory for this new process. But, as currently the initial page directory is enabled, I have no access to the physical pages. The same problem occurs with the kernel stack - I need to put the initial register values on it, to allow my interrupt stub to pop them off the stack initially. And when I finally want to load a binary image to that space, I again have that problem.. How do you solve this?
I figured out these two methods:
a) Temporarily map the pages of the directory, the kernel stack and so on to the current (process-creating) directory, and unmap them after creating the process? (this seems dirty to me..)
b) Fetch the current page directory, temporarily switch to the newly created directory, and after creation switch back? (this seems even dirtier, also I am still forced to map at least the new page directory temporarily to copy the values)
Also, my physical page allocator does not give back zeroed pages, for the same reason as above, and I also cannot 0 all pages when pushing them into the allocator, that would be really slow. So that is another place where I temporarily have to map..
Is there an elegant solution for all of this?
Thanks a lot!
Greets, Max
I'm currently in the following situation. My kernel is running in the higher half, starting at 0xC0000000. In my initial paging directory I created the tables at index 0 and 768-1022 as kernel-tables (to ensure that any change to the kernel space happens in all processes) and entry 1023 mapped to the directory itself. So far so good - now the time has come to implement preemptive multitasking. My problem is the following: I am now inside of the setup function of my kernel. Now I want to kick off the first process. To do so, I have to create a copy of the page directory (copying only the kernel tables), so entries 1-767 can be used as user space.
Now, I ask my physical page allocator for a new page to create a directory for this new process. But, as currently the initial page directory is enabled, I have no access to the physical pages. The same problem occurs with the kernel stack - I need to put the initial register values on it, to allow my interrupt stub to pop them off the stack initially. And when I finally want to load a binary image to that space, I again have that problem.. How do you solve this?
I figured out these two methods:
a) Temporarily map the pages of the directory, the kernel stack and so on to the current (process-creating) directory, and unmap them after creating the process? (this seems dirty to me..)
b) Fetch the current page directory, temporarily switch to the newly created directory, and after creation switch back? (this seems even dirtier, also I am still forced to map at least the new page directory temporarily to copy the values)
Also, my physical page allocator does not give back zeroed pages, for the same reason as above, and I also cannot 0 all pages when pushing them into the allocator, that would be really slow. So that is another place where I temporarily have to map..
Is there an elegant solution for all of this?
Thanks a lot!
Greets, Max
Last edited by max on Mon May 05, 2014 2:13 am, edited 1 time in total.
- Combuster
- Member
- Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 1½m distance
- Contact:
Re: How to fill the stack on process creation?
As you said, you will have to map memory one way or another if you're not considering disabling paging altogether (which is probably even more ugly - especially for a higherhalf kernel).
At any rate, being able to randomly map pages is something you'll be using on numerous occasions anyway...
At any rate, being able to randomly map pages is something you'll be using on numerous occasions anyway...
- max
- Member
- Posts: 616
- Joined: Mon Mar 05, 2012 11:23 am
- Libera.chat IRC: maxdev
- Location: Germany
- Contact:
Re: How to fill the stack on process creation?
Okay I already thought that this would be the case..Combuster wrote:At any rate, being able to randomly map pages is something you'll be using on numerous occasions anyway...
But, is there some kind of best practice to implement this? Like, you have something like a function "virt_addr mapTemporarily(phys_addr)" that uses some address space (like 0xB0000000 to 0xC0000000)? It just feels a little like misung these addresses
(using that area, would also conflict a little with my idea of putting the kernel stack to 0xBFFFF000 and the user stack to 0xBFFFE000, but okay, i can put them somewhere else)
- thepowersgang
- Member
- Posts: 734
- Joined: Tue Dec 25, 2007 6:03 am
- Libera.chat IRC: thePowersGang
- Location: Perth, Western Australia
- Contact:
Re: How to fill the stack on process creation?
Hey, that's what I do. For now I have a range of 16 pages set aside around the top of the address space (just above the area I use for hardware mappings) that are used by MM_MapTemp and MM_FreeTemp. It works pretty well for those few cases when you need to manipulate something outside your current address space.
Kernel Development, It's the brain surgery of programming.
Acess2 OS (c) | Tifflin OS (rust) | mrustc - Rust compiler
Currently Working on: mrustc
Acess2 OS (c) | Tifflin OS (rust) | mrustc - Rust compiler
Currently Working on: mrustc
Re: [Paging] How to fill the stack on process creation?
my mmap can also take physical address as a parameter:
Now it become handy to map/unmap any physical address to the "temp virtual address pool".
Code: Select all
int MMU_mmap (void* mem, MMU_PADDR paddr, size_t size, unsigned int flag);
Re: [Paging] How to fill the stack on process creation?
For the initial process creation Linux creates the address space for init then switches to that space, and works with the memory from there.
http://lxr.free-electrons.com/source/init/main.c#L780
http://lxr.free-electrons.com/source/fs/exec.c#L1440
You should switch to the new process directory (or use the existing for the new process) and I think you should be able to just leave it at that since your kernel is still mapped in. Then once your scheduler fires it can actually switch to the process thread (and of course either set CR3/TTBR or not if it is already set but writing it again should not hurt anything and keeps you from having to insert a condition in your scheduler code path). Your very first page directory could even be used for the process maybe? Unless, you are going to use it somewhere else.
The Way I Am Doing It Currently
My way is likely not the fastest by any means. But, I just switch to the target address space. I use the address space already created for the process (not a temporary one). I flush the entire TLB for that area so of course there is a performance hit. But, I am not doing this every second. The only reason I have to switch to the target address space is to manipulate memory there like copying the ELF sections into memory. I can actually create a process and setup it's threads with out switching, but I understand you are not in that situation. I like doing it this way because it is straight forward and simple.
Also, for creating threads I do not have to switch address spaces. I just create a new thread structure and place it into the system's scheduler infrastructure. If you have one kernel stack per thread then you could consider having the stack inside the kernel address space since that would prevent you from having to switch to the target address space just to set it up.
I guess I should add that I am on ARM so I have two different page directories set at one time. One is for the kernel space and the other is for user space so when I boot up the user space is simply set back to the kernel. So I do not have the problem of having to use a page directory for the original process but rather the problem of having to create one to begin with (and always using the kernel directory).
http://lxr.free-electrons.com/source/init/main.c#L780
http://lxr.free-electrons.com/source/fs/exec.c#L1440
You should switch to the new process directory (or use the existing for the new process) and I think you should be able to just leave it at that since your kernel is still mapped in. Then once your scheduler fires it can actually switch to the process thread (and of course either set CR3/TTBR or not if it is already set but writing it again should not hurt anything and keeps you from having to insert a condition in your scheduler code path). Your very first page directory could even be used for the process maybe? Unless, you are going to use it somewhere else.
The Way I Am Doing It Currently
My way is likely not the fastest by any means. But, I just switch to the target address space. I use the address space already created for the process (not a temporary one). I flush the entire TLB for that area so of course there is a performance hit. But, I am not doing this every second. The only reason I have to switch to the target address space is to manipulate memory there like copying the ELF sections into memory. I can actually create a process and setup it's threads with out switching, but I understand you are not in that situation. I like doing it this way because it is straight forward and simple.
Also, for creating threads I do not have to switch address spaces. I just create a new thread structure and place it into the system's scheduler infrastructure. If you have one kernel stack per thread then you could consider having the stack inside the kernel address space since that would prevent you from having to switch to the target address space just to set it up.
I guess I should add that I am on ARM so I have two different page directories set at one time. One is for the kernel space and the other is for user space so when I boot up the user space is simply set back to the kernel. So I do not have the problem of having to use a page directory for the original process but rather the problem of having to create one to begin with (and always using the kernel directory).
Re: How to fill the stack on process creation?
Why do you need a separate area for temporary mappings? Can't you just take the general kernel area?max wrote:But, is there some kind of best practice to implement this? Like, you have something like a function "virt_addr mapTemporarily(phys_addr)" that uses some address space (like 0xB0000000 to 0xC0000000)? It just feels a little like misung these addresses
And yes, tyndur has an interface that looks somewhat like what you suggested (even though it's just a convenience wrapper around mmc_automap() which has a few more arguments):
Code: Select all
vaddr_t vmm_kernel_automap(paddr_t start, size_t size);
void vmm_kernel_unmap(vaddr_t start, size_t size);
- max
- Member
- Posts: 616
- Joined: Mon Mar 05, 2012 11:23 am
- Libera.chat IRC: maxdev
- Location: Germany
- Contact:
Re: How to fill the stack on process creation?
Thanks for the suggestions!! Looks quite much like the things I'd imagined.
0xC0000000 - ca. 0xC0500000: Kernel binary
0xC0500000 - 0xC1000000: Kernel heap (grows upwards if necessary)
0xFFC00000 - 0xFFFFFFFF: The recursively mapped directory
and now I'm planning to use the areas 0xFFB00000 - 0xFFC00000 as temporary addresses for these special cases. Thats how I seperate it.
Currently my kernel is layouted like this in higher memory:Kevin wrote:Why do you need a separate area for temporary mappings? Can't you just take the general kernel area?
And yes, tyndur has an interface that looks somewhat like what you suggested (even though it's just a convenience wrapper around mmc_automap() which has a few more arguments):Code: Select all
vaddr_t vmm_kernel_automap(paddr_t start, size_t size); void vmm_kernel_unmap(vaddr_t start, size_t size);
0xC0000000 - ca. 0xC0500000: Kernel binary
0xC0500000 - 0xC1000000: Kernel heap (grows upwards if necessary)
0xFFC00000 - 0xFFFFFFFF: The recursively mapped directory
and now I'm planning to use the areas 0xFFB00000 - 0xFFC00000 as temporary addresses for these special cases. Thats how I seperate it.
Re: [Paging] How to fill the stack on process creation?
Normally you only need to map one single new page for the startup thread's stack.
Everything else, like loading the executable, can be done on new thread's own context.
Everything else, like loading the executable, can be done on new thread's own context.