0

I'm trying to understand as best I can what this OSDEV tutorial is on about. I have read the AMD64 programmers manaul (vol.2) chapters 4 and 5 as suggested, but I do not understand what happens when he goes through this loop:

    mov ebx, 0x00000003          ; Set the B-register to 0x00000003.
    mov ecx, 512                 ; Set the C-register to 512.

.SetEntry:
    mov DWORD [edi], ebx         ; Set the uint32_t at the destination index to the B-register.
    add ebx, 0x1000              ; Add 0x1000 to the B-register.
    add edi, 8                   ; Add eight to the destination index.
    loop .SetEntry               ; Set the next entry.

I get why he sets ebx to 3, but beyond that, I don't see what he is doing to ID map 2 MB.

Mike
  • 175
  • 1
  • 13
  • At this point in the code _EDI_ is pointing at a page table located at memory address 0x4000. There are 512 page table entries representing 4kb each which is a total of 2mb (512*4096) that need to be initialized. The loop adds 0x1000(4096=4kb) through each iteration to set the physical address page. It effectively maps this page table (PT) to the first 2mb of physical ram. – Michael Petch Aug 13 '17 at 23:09
  • Ok thank you. Not sure why I couldn't wrap my head around that. Would you say this is a good way to set up paging before the kernel deals with it? – Mike Aug 13 '17 at 23:32
  • If you intend to identity map the first 2mb of memory nothing wrong with it. If you don't intend to keep the identity mapping after enabling paging you could consider using [2MB paging rather than 4kb which is a feature of PAE](http://wiki.osdev.org/Page_Tables#2_MiB_pages) . This simplifies things as you only need a Page Directory pointer table and a Page Directory. Doesn't require a loop. It can also be unmapped and invalidated easily. 2mb paging may not be small enough for many uses but it is convenient to more easily identity map the first 2mb temporarily. – Michael Petch Aug 13 '17 at 23:44
  • My last comment about 2mb paging and PAE paging structures applies while you are in 32-bit protect mode. – Michael Petch Aug 13 '17 at 23:51
  • Is there any reason why that tutorial would cause me to triple fault? My code works until I try switch into long mode. if not, what would case me to triple fault? – Mike Aug 15 '17 at 18:04
  • Are you running it a 64-bit emulator? – Michael Petch Aug 15 '17 at 18:05
  • Yeah I'm using qemu-system-x86_64 – Mike Aug 15 '17 at 18:08
  • I'd have to see all of the code you are trying to test (and how you compile/assemble/build it). If you tossed it into a github project I could take a look (or email me an archive of your files to mpetch@capp-sysware.com ) – Michael Petch Aug 15 '17 at 18:09
  • This is the project on GitHub: https://github.com/theMike97/OS_Developement – Mike Aug 15 '17 at 18:16
  • It seems that you have interrupts enabled when you enter long mode but you have no interrupt descriptor table. I believe that is causing your switch into long mode to fail with a tripe fault when the first interrupt is received. Temporarily try removing the `sti` instruction in `main.asm` and see what happens. – Michael Petch Aug 15 '17 at 19:35
  • That was the problem. Are there any resources you would recommend so I can get better at this? – Mike Aug 15 '17 at 20:05
  • OSDev is one fo the better ones but sometimes it is difficult to make sense of some of the material as it it is often disjoint. I really have no recommendation. OS Development is difficult. What I do suggest though is that you learn to use Bochs with its internal debugger to debug issues or QEMU and it's remote debugger. Bochs is better for things like debugging code that gets into protected and long mode etc. Being able to step through your code to discover bugs is a valuable skill. – Michael Petch Aug 15 '17 at 20:09
  • In your case I loaded up Bochs ran your code and an error on the console about the IDT tipped me off to your problem before it triple faulted. – Michael Petch Aug 15 '17 at 20:10

1 Answers1

0

mov ebx, 0x00000003          ; Set the B-register to 0x00000003.

The code uses ebx to hold the value to be written to the next page table entry. It initializes it to 3 to set the present and writable bits (bit 0 and 1). From here on, it adds 0x1000 to it at each loop iteration, which does not affect those two bits. The physical address field is zero, so this makes the first entry map to physical address zero.


mov ecx, 512                 ; Set the C-register to 512.

The loop runs for 512 iterations, one iteration per page table entry. Each entry is 64 bits (8 bytes). One page table in long mode can hold 512 entries. 512 is 4096 / 8.


.SetEntry:
mov DWORD [edi], ebx         ; Set the uint32_t at the destination index to the B-register.

Write one page table entry.


add ebx, 0x1000              ; Add 0x1000 to the B-register.

Advance the physical address to be written into the next page table entry by 4KB. Each page table entry maps 4KB.


add edi, 8                   ; Add eight to the destination index.

Write the next page table entry into the next entry of the page table. Each page table entry is 8 bytes.


loop .SetEntry               ; Set the next entry.

The loop instruction means ecx = ecx - 1 jnz .SetEntry. It runs another iteration of the loop if ecx is not zero after reducing ecx by 1. Since ecx was initialized to 512 before the loop, this makes the above instructions repeat 512 times.


Since the first entry uses physical address zero, and each subsequent entries' physical address is 4KB higher, and each entry maps 4KB, and there are 512 of them, this code fills the page table entries for virtual address 0 (inclusive) through 2MB (exclusive) with entries that identity map the first 2MB of physical memory. "Identity" mapping means mapping it so the physical address is equal to the linear address. Each page table in long mode maps 2MB of address space. Since this code fills one page table, it maps 2MB.

Note that this code alone is not sufficient to set up paging. This code only fills the page table entries. This code would work for 32-bit PAE paging or long mode paging.

In PAE paging, you would need to set up two other pages as well. For long mode, you would need to set up three other pages. PAE paging uses a 3-level translation, where the top level maps 1GB regions, the middle level maps 2MB regions, and the page table level maps 4KB regions.

Long mode paging uses a 4-level (or on the latest processors, it can be 5-level) paging. In long mode the top level maps 512GB regions, the second level maps 1GB regions, the third level maps 2MB regions, and the outermost level maps 4KB regions.

Code that sets up identity mapping would need to set up the pages described above, in addition to the setup shown in the code in your question.

doug65536
  • 6,562
  • 3
  • 43
  • 53