Page 1 of 1
Paging and segmentation question
Posted: Wed Apr 02, 2014 9:04 pm
by newbee8
Hi Forum - I am a newbee trying to understand the theory behind OS design so I went though the baby steps tutes and I need your help to clear some questions / doubts.
Assumptions: For the sake of simplicity I am assuming the following aspects of machine and Kernel.
1. I am assuming a 32 bit Intel CPU and the machine has physical memory of 16 MBs
2. Kernel size is less than 1 MB.
3. Grub is used as boot loader.
4. I am assuming following memory model.
Code: Select all
----------------> 0x1000000 = 16 MB
| |
| |
| Remaining |
| 14 MBs |
| |
| |
| |
----------------> 0x200000 = 2MB = Start of Page directory
| |
----------------> End of Kernel
| Kernel |
| of |
| size < 1MB |
| |
----------------> 0x100000 = 1MB = Start of Kernel
| BIOS |
| Video |
| & |
| others |
----------------> 0x0000000
Questions:
1. Is my assumption about the memory correct? Meaning is this how it is lay'ed out in real OSes
2. While enabling paging how do I determine how many page directories and eventually how many page frames do I need?
3. Is it correct to assume that remaining 14 MBs will/can all be allocated to page frames, page tables and directory?
4. Where do we create GDT and LDT? Is it inside the kernel itself, meaning inside the memory that I have marked as start and end of kernel?
5. Who creates the segment selectors? Are these created by compilers / assemblers?
It would be really helpful for me to understand the design theory so thanks in advance for taking the time to answer these.
Thanks!
Re: Paging and segmentation question
Posted: Wed Apr 02, 2014 11:28 pm
by Brendan
Hi,
newbee8 wrote:1. Is my assumption about the memory correct? Meaning is this how it is lay'ed out in real OSes
Different OSs are different, so your assumption might be correct for some OSs and incorrect for others.
newbee8 wrote:2. While enabling paging how do I determine how many page directories and eventually how many page frames do I need?
When enabling paging, you need a minimum of one page directory, one page table and one page. Anything more than that is optional - e.g. more page tables, etc can be added before enabling paging, or soon after enabling paging, or after the OS has been running for 123 days.
newbee8 wrote:3. Is it correct to assume that remaining 14 MBs will/can all be allocated to page frames, page tables and directory?
Yes.
newbee8 wrote:4. Where do we create GDT and LDT? Is it inside the kernel itself, meaning inside the memory that I have marked as start and end of kernel?
We create the GDT and LDT where-ever we like. For LDT, I don't create one at all.
newbee8 wrote:5. Who creates the segment selectors? Are these created by compilers / assemblers?
The OS and/or its boot code creates segment selectors (and all other GDT, LDT and IDT entries). Languages typically don't know anything about segment selectors (but may know about "sections" which are quite different and not directly related to segments).
Cheers,
Brendan
Re: Paging and segmentation question
Posted: Thu Apr 03, 2014 6:45 am
by newbee8
Thanks Brendan! I have a follow-up question regarding the selectors. Assuming my kernel is using flat model with two segments, Code and Data, then while creating GDT will I create two selectors and store one in CS and other in DS registers?
Thanks!
Re: Paging and segmentation question
Posted: Thu Apr 03, 2014 7:04 am
by Combuster
The second one being in the DS, ES and SS registers, because DS isn't the only one getting used by "normal" code.
Re: Paging and segmentation question
Posted: Thu Apr 03, 2014 8:23 am
by newbee8
Thanks Combuster! I am guessing there is a little humor involved in "normal" and you are pointing to sensible instead of non-kernal/non-OS.
Re: Paging and segmentation question
Posted: Thu Apr 03, 2014 5:49 pm
by Brendan
Hi,
newbee8 wrote:Thanks Brendan! I have a follow-up question regarding the selectors. Assuming my kernel is using flat model with two segments, Code and Data, then while creating GDT will I create two selectors and store one in CS and other in DS registers?
Normally you need a minimum of 6 GDT descriptors:
- NULL descriptor
- CPL=0 code for kernel CS
- CPL=0 data for kernel SS
- CPL=3 code for user CS
- CPL=3 data for user SS, and both kernel and user DS and ES
- TSS per CPU
Notes:
- The SYSENTER, SYSEXIT, SYSCALL and SYSRET instructions expect GDT entries for kernel CS and SS and user CS and SS to be in the order I've listed them.
- An OS that supports both 64-bit and 32-bit processes would need an extra pair of user-space GDT entries (one for 64-bit processes and another for 32-bit processes)
- Using "CPL=3 data" for kernel DS and ES is fine (kernel can still use them to access kernel space), and allows you to treat them as constants, such that DS and ES are never loaded or saved for any reason. Loading segment registers is slow, so this speeds up the kernel API and all interrupt handlers. Note: Technically, malicious user code can load the NULL segment into DS or ES, so your general protection fault exception handler needs to detect and correct that, but all other kernel code can ignore it.
- If you have a "per CPU" area of kernel space (where the same virtual addresses use different physical pages for different CPUs) then you can put each CPU's TSS into the "per CPU" area of kernel space at the same virtual address, and in that case you only need one GDT entry for all TSSs.Note: Implementing a "per CPU" area of kernel space like this means you need a slightly different virtual address space for each thread in each process; and is therefore not recommended unless you're using a slightly different virtual address space for each thread in each process for other reasons.
- It's also possible to use one TSS for all CPUs without having a "per CPU" area of kernel space; just by modifying the GDT entry whenever a CPU does LTR to load its TSS (mostly only during boot). In this case you need something (e.g. spinlock) to guard against 2 or more CPUs trying to modify the GDT entry at the same time.
- If you don't use a "per CPU" area of kernel space; then you will need some other way to locate "per CPU" data structures. One way is to have an additional data segment for each CPU and load that into FS or GS. In this case, if you're using a GDT entry for each CPUs TSS then you can find the CPU's "per CPU area" segment by doing STR (to get the CPU's TSS entry) and adding a fixed amount to that to find the GDT entry to load into GS or FS. Note: Technically, malicious user code can load the NULL segment or the CPL=3 data segment into GS or FS, so your kernel has to deal with that possibility, either by correcting it if it happens (by using exceptions) or always loading a correct version (and adding overhead to kernel API and all interrupt handlers). For 64-bit kernels, the SWAPGS instruction can reduce the overhead of correcting GS and avoids the need for using STR trick.
Cheers,
Brendan
Re: Paging and segmentation question
Posted: Sat Apr 05, 2014 5:50 am
by tom9876543
Brendan, thank you for the detailed info.
Would you know the exact strategy Linux uses to locate "per CPU" data structures?
Does FreeBSD also use the same strategy?
Thanks
Re: Paging and segmentation question
Posted: Mon Apr 07, 2014 8:02 pm
by newbee8
Hello Brendan,
Thank you for providing the detailed information. I am a clean slate (beginner taking ASM and OS courses) and need your help to understand some more concepts.
1. I understand that with Flat model all the segments will have linear address range of 0 to 4 GB. The question I have is, how does paging distinguish Kernel from Non-kernel pages, since all segments have same Linear address range? I am going to try to answer myself based on the paging tutorial but I don't know if my understanding is correct.
I think this can be solved by mapping, in my example mentioned in the very first post I loaded my kernel at 0x100000 so I can map it (0x100000) to 0xc0000000 and define the Paging data structures accordingly. This means that any linear address before the start of my kernel can be mapped to addresses lower than 0xc0000000. This way I can separate Kernel space from user space, please advise if my understanding is correct.
2. The next doubt is, within the kernel space how to distinguish code from data? OR is that irrelevant because the addresses are absolute (not relative to a base)?
3. Third thing I need your help with is about the point mentioned about sections in the language.
The OS and/or its boot code creates segment selectors (and all other GDT, LDT and IDT entries). Languages typically don't know anything about segment selectors (but may know about "sections" which are quite different and not directly related to segments).
How are these sections represented in the memory in terms of segments? For example, the Kernel will have .text, .data, .bss etc how are these mapped to appropriate segments?
Also when we say code segment does that mean it stores the entire kernel image (including .text, .data, etc) or just .text?
Thanks,
Re: Paging and segmentation question
Posted: Wed Apr 09, 2014 8:38 am
by newbee8
Hi forum, you guidance to understand the above question will be really helpful.
Thanks,
Re: Paging and segmentation question
Posted: Wed Apr 09, 2014 9:26 am
by JAAman
newbee8 wrote:Hello Brendan,
Thank you for providing the detailed information. I am a clean slate (beginner taking ASM and OS courses) and need your help to understand some more concepts.
1. I understand that with Flat model all the segments will have linear address range of 0 to 4 GB. The question I have is, how does paging distinguish Kernel from Non-kernel pages, since all segments have same Linear address range? I am going to try to answer myself based on the paging tutorial but I don't know if my understanding is correct.
when you use the typical flat model, segmentation is essentially disabled, and everything goes through paging -- if you look, you will see that the paging structures contain bits to represent whether the page is "user" (non-kernel) or "supervisor" (kernel) -- refer to Intel 3A:4.11.2
I think this can be solved by mapping, in my example mentioned in the very first post I loaded my kernel at 0x100000 so I can map it (0x100000) to 0xc0000000 and define the Paging data structures accordingly. This means that any linear address before the start of my kernel can be mapped to addresses lower than 0xc0000000. This way I can separate Kernel space from user space, please advise if my understanding is correct.
yes, but you can also map addresses after the end of your kernel to lower addresses, or addresses lower than your kernel to higher virtual addresses -- any physical/linear address can be mapped to any virtual address at any time, or even to multiple virtual addresses (where multiple virtual addresses all refer to the same physical address) -- this provides good separation of kernel/user in virtual address space but does not require separation of physical address space
2. The next doubt is, within the kernel space how to distinguish code from data? OR is that irrelevant because the addresses are absolute (not relative to a base)?
using this model there is no clear CPU enforced separation of code/data, since all segment registers are the same, however newer systems using PAE paging do provide execution restrictions, allowing you to prevent data from being executed as code -- refer Intel 3A:4.13
3. Third thing I need your help with is about the point mentioned about sections in the language.
The OS and/or its boot code creates segment selectors (and all other GDT, LDT and IDT entries). Languages typically don't know anything about segment selectors (but may know about "sections" which are quite different and not directly related to segments).
How are these sections represented in the memory in terms of segments? For example, the Kernel will have .text, .data, .bss etc how are these mapped to appropriate segments?
Also when we say code segment does that mean it stores the entire kernel image (including .text, .data, etc) or just .text?
Thanks,
typically, most OSes "disable" segmentation by using the "flat model" so the sections have no relation to segments at all, though the sections can be loaded into different parts of memory
Re: Paging and segmentation question
Posted: Wed Apr 09, 2014 9:59 am
by Brendan
Hi,
newbee8 wrote:I think this can be solved by mapping, in my example mentioned in the very first post I loaded my kernel at 0x100000 so I can map it (0x100000) to 0xc0000000 and define the Paging data structures accordingly. This means that any linear address before the start of my kernel can be mapped to addresses lower than 0xc0000000. This way I can separate Kernel space from user space, please advise if my understanding is correct.
That's correct. Also note that you can separate different processes from each other in user-space by giving them their own virtual address space, where the kernel is mapped the same into all virtual address spaces. This makes it impossible for one process to trash another process.
newbee8 wrote:The OS and/or its boot code creates segment selectors (and all other GDT, LDT and IDT entries). Languages typically don't know anything about segment selectors (but may know about "sections" which are quite different and not directly related to segments).
How are these sections represented in the memory in terms of segments? For example, the Kernel will have .text, .data, .bss etc how are these mapped to appropriate segments?
Also when we say code segment does that mean it stores the entire kernel image (including .text, .data, etc) or just .text?
Typically; a "section" is something that the object file format/executable file format defines and describes in the file's header. A section might have a name, a set of attributes, an area within the file and an area within the process' virtual address space. For example, you might have a section called ".rodata", with attributes saying it's read-only, that is from offset 1234 to offset 2345 in the file and should be placed at virtual address 0x900000 in the process' virtual address space.
An executable file loader parses the executable file's header and maps sections into the virtual address space; and also sets the page attributes to match the section's attributes as best it can. For example, if the executable file's header says a section is read-only then the executable file loader might set the pages as "read-only, no execute", but if the CPU doesn't support "no execute" then the loader might only set the page up as "read-only".
Cheers,
Brendan
Re: Paging and segmentation question
Posted: Fri Apr 11, 2014 7:36 am
by newbee8
Thanks everyone for all the help and guidance.