Paging resources and wrong code
Posted: Sun Apr 11, 2021 8:37 am
...
The Place to Start for Operating System Developers
http://f.osdev.org/
Run qemu with "-d int" that will tell you.ngx wrote:For the last week and I half, I was trying to implement a vmm, but for some reason it fails every time with a reboot(triple fault probably) when I reload the tables set up by the bootloader(load the address of my new PML4 into CR3).
Use bochs. It has a very capable debugger, once you see the faulting address in CR2, you can do a "page" command to list the translation walking through the page tables. No other debugger can dump the tables like that, it is very useful to figure out what's wrong.ngx wrote:I can't understand why does there occur a triple fault after page tables are loaded, I looked through the code and as I see all pages are zeroed(so no old data interferes with addresses or flags), R/W and present flags are set on them and all of them are filed with entries starting from where the kernel is, what could the problem be?
Well, yes, there are books, but the thing is, all OS like to do things differently. There's no golden rule or algorithm you can use. Memory management is a risky business where you must make compromises, and it's up to you what you prefer: smaller footprint and slower execution or larger footprint and faster execution.ngx wrote:Also even if I manage to fix my VMM(hopefully someone will help me with that), that would probably still leave it in a pretty messy state. So are there any books that talk about designing a VMM, as from what I read(not everything, but I looked through all of the MM chapter) now(Tanenbaum and several others), they talk about what paging is, then talk a lot about TLB and processes which is not what I currently want?
Probably try Minix3 or xv6, but as I've said these are pretty OS-specific things. My OS/Z uses a totally different approach to those by separating address spaces from processes into a new layer (and then I call that architecture specific vmm_new() in the architecture-independent task_new() function, which in turn is called during process creation. This isn't necessarily the best approach, just one of the many).ngx wrote:Also is there any good vmm code(it would be good if it was for x86, and even better if it is cross-platform) that I could read(I would like some not really hard VMM as it is my first time making something like VMM)?
That depends. The paging tables differ on 32 bit and 64 bit, but you can have some ifdefs and share the most of the code. Or you can have two entirely separated and optimized implementations. Up to you, no "best" solution exists.ngx wrote: - Should I make a separate VMM for 32-bit and 64-bit mode?
That's a difficult question. I'd suggest to study the architecture manuals for all platforms you want to support, and figure out what they have in common, and what are the platform-specific things.ngx wrote: - What should I take into consideration to make it portable?
No, it will output one block per exception. Look for the first one. (You can use "-d in_asm" to output every instruction).ngx wrote:Does it output statuses of all registers for every instruction?
My advice is try to solve that issue. It's always better if you test your OS in multiple VMs, plus bochs' debugger is really spectacular (since it's an emulator, you can debug things with it that no other VM capable of.) Here's a very (very) minimal configuration file you could try, it looks for the file named "disk-x86.img" as the disk image. If you're using Ubuntu, you'll have to install multiple packages, one with the front-end (called bochs-sdl or bochs-x), one with the bios images (bochsbios) and another with the vga rom images (vgabios). This config uses X11, so if you install the SDL version (bochs-sdl), then replace "display_library: x" with "display_library: sdl" in it. Also make sure that the bios and rom filenames matches the one you install, and then it should work.ngx wrote:For some reason it would not start on my system, more correctly it would - but it would not work
Yes, that should do the trick. It also flushes the entire cache, so whenever possible try to use the INVPGL instruction instead.ngx wrote:Also, am I reloading page tables correctly - I just load the new address(of PML4) into the CR3?
CR2 is the linear address that caused the page fault. It might be the instruction being executed, or it might be data the instruction is referencing. You probably shouldn't care beyond whether the desired reference is valid. You'll get an error code pushed onto the #PF fault stack, which will indicate whether the page fault was:ngx wrote: And also
Page fault puts the address into the CR2 - is it the address of instruction that caused fault or the address that the instruction tried to access the address, and if it is physical address in CR2 how would I debug(as it does not correspond to virtual because kernel is mapped into higher half) and obtain the virtual address from it?
You can map the page tables into virtual memory, using recursive page mapping. It makes inspecting and updating the page table easier, as the page table is just a mapped array which we can use a simple C pointer to.ngx wrote: Do I need to map page tables into virtual RAM in order for them to work or the CPU will work fine with physical addresses that are not mapped anywhere?
It's included in the debug output.ngx wrote:Thanks for your help. I tried using the qemu debugging method and found out that it is a page fault, but the problem is that page fault puts the error on top of the stack - so how should I obtain the error code off the stack right after the exception(as there is no way I will be fast enough to pause qemu and dump the ram)?
ngx wrote:There are a lot of entries, do you know which one exactly?
Code: Select all
195: v=08 e=0000 i=0 cpl=0 IP=0008:ffffffff8010203a pc=ffffffff8010203a SP=0010:ffffffff801116a0 env-
Code: Select all
check_exception old: 0x8 new 0xe
Your kernel can access all of the virtual address space. Whether or not you mapped anything in low memory doesn't mean that you don't have a bug where your code tries to access it. But again, you haven't showed us the proper dump for the page fault, so we still don't know which address the CPU was trying to access.ngx wrote: the problem is that there is no possibility my kernel would even try to access anything near this address after enabling interrupts as it is mapped in the last 2GB of the higher half(0xffffffff80100000) and I haven't mapped anything below that address
This line is printed when a new exception happens. It is telling us that the last exception was a double fault (we know that, you copied the dump) and that the next one is a page fault (which you haven't showed us).ngx wrote:Then why is there written - check_exeption old: 0x8 new 0xe in the end?