I don't quite know what it's called, but the question is how do I get the CPU into whatever mode it needs to be in in order to use paging? I know that I need to set up the page directories and tables, and then set something in the control registers, but is there anything else that I should do first? Do I need to be in protected mode? If so, what must go in my GDT? Should I just use a flat memory map until I'm ready to enable paging? Once I set the bit in the control register to enable paging, must I perform a far jump as with entering protected mode? Where should the jump go? Can I set cr3 first, before I enable paging? etc.
Basically what I need is a code snippet, or at least a pseudocode snippit, for getting from real mode to whatever you call the mode where paging is used, minus of course the details of setting up the page tables as I think I can handle that myself.
How to get to paging?
-
- Member
- Posts: 1146
- Joined: Sat Mar 01, 2014 2:59 pm
How to get to paging?
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.
Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
Re: How to get to paging?
1. You need to be in Protected Mode.
2. Set up a GDT to support a flat address space.
3. Set up your Page Tables. You'll probably want to make the page that you are running in identity mapped to avoid problems when paging is enabled.
4. Load CR3 with the (physical) address of your Page Table.
5. Set the paging bit in CR0.
No jumps needed - you are now running with paging enabled and all addresses are virtual addresses. Almost certainly you will have done something wrong (getting the correct entries in the Page Table can be tricky at first) so you will probably get an instant exception. I'd recommend running under a debugger (AMD's SimNow is my favourite for this low-level debugging of boot code), single-stepping through the bit where you enable paging, and carefully inspect your Page Table to see that it contains the entries it should. A Page Fault will always push an error code telling you exactly what caused the fault and puts the faulting address in CR2 which helps to determine the problem. It's a good idea to set up exception handlers as soon as possible to make life easier when GPFs and PFs occur; mine dumps the stack frame and the values of the general purpose registers to the screen, plus registers CR2 and CR3, and then halts the processor. Later you will refine these, particularly the handler for a Page Fault, to do something more useful.
Remember that, from now on, you still need to use physical addresses when manipulating Page Tables, so you need some way of mapping those physical addresses to logical addresses. A simple solution is to produce a mapping that the kernel can use of all physical RAM (which is not as wasteful as it might at first appear to be); otherwise you can just create temporary mappings of individual pages as you need to or use recursive Page Tables (although this can be a bit difficult to get your head round at first).
2. Set up a GDT to support a flat address space.
3. Set up your Page Tables. You'll probably want to make the page that you are running in identity mapped to avoid problems when paging is enabled.
4. Load CR3 with the (physical) address of your Page Table.
5. Set the paging bit in CR0.
No jumps needed - you are now running with paging enabled and all addresses are virtual addresses. Almost certainly you will have done something wrong (getting the correct entries in the Page Table can be tricky at first) so you will probably get an instant exception. I'd recommend running under a debugger (AMD's SimNow is my favourite for this low-level debugging of boot code), single-stepping through the bit where you enable paging, and carefully inspect your Page Table to see that it contains the entries it should. A Page Fault will always push an error code telling you exactly what caused the fault and puts the faulting address in CR2 which helps to determine the problem. It's a good idea to set up exception handlers as soon as possible to make life easier when GPFs and PFs occur; mine dumps the stack frame and the values of the general purpose registers to the screen, plus registers CR2 and CR3, and then halts the processor. Later you will refine these, particularly the handler for a Page Fault, to do something more useful.
Remember that, from now on, you still need to use physical addresses when manipulating Page Tables, so you need some way of mapping those physical addresses to logical addresses. A simple solution is to produce a mapping that the kernel can use of all physical RAM (which is not as wasteful as it might at first appear to be); otherwise you can just create temporary mappings of individual pages as you need to or use recursive Page Tables (although this can be a bit difficult to get your head round at first).
-
- Member
- Posts: 5588
- Joined: Mon Mar 25, 2013 7:01 pm
Re: How to get to paging?
There's no "probably" here; the code that enables paging must be identity-mapped. If you're still using the 80386 manual, it's time for an update.iansjack wrote:3. Set up your Page Tables. You'll probably want to make the page that you are running in identity mapped to avoid problems when paging is enabled.
Re: How to get to paging?
There is a "probably" (IMO). The programmer could have chosen to copy the code to the location pointed to by the Page Table before enabling paging, in which case it would still work. But it's probably not the best way to do it (IMO).Octocontrabass wrote:There's no "probably" here; the code that enables paging must be identity-mapped. If you're still using the 80386 manual, it's time for an update.iansjack wrote:3. Set up your Page Tables. You'll probably want to make the page that you are running in identity mapped to avoid problems when paging is enabled.
Edit: Having read your link more carefully I guess you may be right. You let's say that the page must be identity mapped and you must jump immediately after enabling paging. I've always used identity mapping here so I've never come across this problem.
-
- Member
- Posts: 5588
- Joined: Mon Mar 25, 2013 7:01 pm
Re: How to get to paging?
Intel® 64 and IA-32 Architectures Software Developer’s Manual, volume 3, section 9.9.1If paging is enabled, the code for the MOV CR0 instruction and the JMP or CALL instruction must come from a page that is identity mapped (that is, the linear address before the jump is the same as the physical address after paging and protected mode is enabled). The target instruction for the JMP or CALL instruction does not need to be identity mapped.
-
- Member
- Posts: 1146
- Joined: Sat Mar 01, 2014 2:59 pm
Re: How to get to paging?
@iansjack: Thanks for your explanation, it was very helpful. However I still have a few questions. Firstly, why must I enter protected mode before setting up the identity-mapped GDT; I normally used to set my GDT up beforehand then enter protected mode. Secondly, I am wanting to use a higher-half kernel. You said that it is necessary to initially set my page tables up to identity-map the kernel - how do I then get the kernel mapped to the higher half? Must I initially map the kernel in both places - the identitty-mapped low memory where I have loaded the kernel AND the high memory area, then perform a far jump?
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.
Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
-
- Member
- Posts: 1146
- Joined: Sat Mar 01, 2014 2:59 pm
Re: How to get to paging?
I just realised that I guess I could identity-map the bootloader (which is where I will be setting up paging) and map the kernel only in the higher-half, then perform the far jump from the identity-mapped bootloader into the kernel's entry point in the high memory area. Then I can unmap the bootloader. Would this work?
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.
Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
-
- Member
- Posts: 5588
- Joined: Mon Mar 25, 2013 7:01 pm
Re: How to get to paging?
If your GDT is already set up for a flat address space, then you don't have to do anything for step 2. In fact, you can switch to protected mode and enable paging at the same time.onlyonemac wrote:Firstly, why must I enter protected mode before setting up the identity-mapped GDT; I normally used to set my GDT up beforehand then enter protected mode.
Yes.onlyonemac wrote:Would this work?
Re: How to get to paging?
Sorry, I didn't mean to imply that you have to be in Protected Mode before setting up the data structures. If they are within the addressable range of real mode you can set them up there. Most people tend to use Grub, or some similar bootloader, that set up the basics for you and put you into Protected Mode. I just meant that you need to be in Protected Mode before you can enable paging.