rust-osdev / bootloader

An experimental pure-Rust x86 bootloader

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

When not setting a physical_memory config in the bootloader it is impossible to access the existing page table.

wgaylord opened this issue · comments

When you don't setup some sort of offset for your kernel using physical_memory you can not access the bootloader's page table. Making it nearly impossible to copy it to a new location to modify it and set up a new page table that still has the kernel properly mapped.

I am not sure if I am just doing something wrong, (very likely) or it I am supposed to just make my own page table if I don't set up an offset. (If so I am not sure how I would do that without knowing how the kernel is mapped to begin with.)

Also if I have to create my own page table, where would I locate it since there is no way to see what memory is mapped or unmapped to be able to actually use to make that new page table.

Right now, the bootloader crate supports two ways of making the physical memory accessible: mappings.physical_memory and mappings.page_table_recursive. Both keys map the whole physical address space.

When you don't setup some sort of offset for your kernel using physical_memory you can not access the bootloader's page table. Making it nearly impossible to copy it to a new location to modify it and set up a new page table that still has the kernel properly mapped.

So you want to create a new level 4 page table that still refers to the previous level 3 page tables? One way to do that could be to instruct the bootloader to map the physical memory (or set up a recursive mapping) at a specified address. Then you can traverse all the page tables and build your own copy of it. Since you know the address of the physical mapping (or the index of the recursive mapping), you can just skip this level 4 entry for you new page table hierarchy.

Maybe you could tell us a bit more about your use case? Do you want to access all page tables or just the level 4 page table? I'm open to add config options for mapping only a subset of the physical address space, if that's something that is commonly needed.

Also if I have to create my own page table, where would I locate it since there is no way to see what memory is mapped or unmapped to be able to actually use to make that new page table.

Are you talking about physical or virtual memory?

For physical memory, you can use the BootInfo.memory_regions field, which specifies all available physical memory. Memory allocated by the bootloader will be marked as MemoryRegionKind::Bootloader.

We don't provide such a structure for virtual memory yet. There is an open PR at #346 to include the kernel's relocation offset in the boot information. We could do something similar for the other fields that might be relocated too.

Memory allocated by the bootloader will be marked as MemoryRegionKind::Bootloader.

Is it OK to reclaim bootloader memory once I've allocated my own GDT, IDT and page tables? None of the bootloader code will ever be needed again, right?

Then the bootloader won't have to do anything about the lower 1MB because I can just reclaim that myself once I've initialized everything.

MemoryRegionKind::Bootloader is also used for the memory backing the kernel.

It's possible to access the existing page table without having to map all of physical memory by

MemoryRegionKind::Bootloader is also used for the memory backing the kernel.

maybe someone should add more enum variants so I can easily tell what is expendable :3

(or someone could push the last couple months of unpublished changes to crates.io, so I can access the kernel image location)

honestly a map of virtual memory would be pretty convenient, then I wouldn't have to manually read everything out from the page tables (and hueristics can only go so far with merging adjacent pages and guessing if they are the same region or not, especially if there is ASLR)

maybe someone should add more enum variants so I can easily tell what is expendable :3

On UEFI, we do the physical memory allocations through the firmware. We tried to use custom memory types to differentiate memory allocations for the bootloader and kernel, but unfortunately this led to errors on some hardware: #232 (comment). So we currently use some manual code to decided which areas are still needed after switching to the kernel.

In general, we try to reserve as little physical memory as needed. I think it mostly falls into one of the following categories:

  • memory backing the kernel
  • page tables
  • GDT
  • memory storing the boot info
  • UEFI runtime memory that should be preserved by the bootloader and OS

I'm definitely open to adding more details about this to the boot info! Then you could e.g. recycle the page table memory once you allocated a new hierarchy.

honestly a map of virtual memory would be pretty convenient, then I wouldn't have to manually read everything out from the page tables (and hueristics can only go so far with merging adjacent pages and guessing if they are the same region or not, especially if there is ASLR)

I agree that this would be useful! I don't have time to look into it myself right now, but I'd be happy to merge a PR!

I agree that this would be useful! I don't have time to look into it myself right now, but I'd be happy to merge a PR!

I'll look into it, right now for example there's no way to tell where the kernel stack is located (especially the guard page ! which cannot be located in the page table as it's simply "the unmapped page that resides directly before the stack") and that's kind of annoying bleh.

It would also be nice for me to get a local copy installed so I can benefit from the past few months of fixes that haven't been published to crates.io.

We tried to use custom memory types to differentiate memory allocations for the bootloader and kernel, but unfortunately this led to errors on some hardware: #232 (comment). So we currently use some manual code to decided which areas are still needed after switching to the kernel.

I mean MemoryKind.

Once I've taken control of the page tables, taken some space for my own GDT and IDT, copied everything I needed out of the BootInfo and so on, there should be no need for any of bootloader's own stuff to stick around anymore. But I need to be able to preserve virtual mappings for specifically, at minimum:

  • kernel code
  • kernel data
  • kernel stack
  • framebuffer

and for purism's sake I'd like to avoid scanning the existing page table and using hueristics to guess where these actually reside.

Just so you know, this all came about because I'm annoyed and upset at bootloader mapping all sorts of undocumented / hidden things, and then not giving me any information about where they reside in virtual memory, so where am I supposed to create pages to map the newly allocated frames from my frame allocator? I need to choose some arbitrary virtual address to make my frames accessible through, but I do not just want to pick a magic value, I want to get a new page table installed with the bare minimum mappings and have everything under control myself. But in order to do that I need the proper information to construct a page table from scratch which I don't yet have.

(especially the guard page ! which cannot be located in the page table as it's simply "the unmapped page that resides directly before the stack") and that's kind of annoying bleh.

Oh yeah, good point!

It would also be nice for me to get a local copy installed so I can benefit from the past few months of fixes that haven't been published to crates.io.

Sorry about that! I can create a new release if you like, looks like it's mostly #390, #391, and #346 that aren't released yet.

there should be no need for any of bootloader's own stuff to stick around anymore.

One exception is the UEFI runtime data, which should be preserved by the bootloader/OS according to the UEFI standard. But yes, everything else should be reusable.

and for purism's sake I'd like to avoid scanning the existing page table and using hueristics to guess where these actually reside.

I think #346 started to fix this in parts (by including the kernel ELF offsets). It would be nice to also do something similar for all the other mappings.

One exception is the UEFI runtime data, which should be preserved by the bootloader/OS according to the UEFI standard. But yes, everything else should be reusable.

This should just be MemoryKind::Uefi, right? I wouldn't touch those, only the ones that are MemoryKind::Bootloader.

It would be nice to also do something similar for all the other mappings.

Yeah, at this point bootloader should probably have a couple exhaustive structs that store this stuff. If I'm feeling up to it later I'll see if I can do something, it'd be a new major release though. (minor while in zerover)

For physical memory, you can use the BootInfo.memory_regions field, which specifies all available physical memory. Memory allocated by the bootloader will be marked as MemoryRegionKind::Bootloader.

Finally getting back to trying to work with this, at the time, from what I can remember I couldn't actually access the memory shown in BootInfo.memory_regions to setup a new page table because it wasn't mapped. At least that's what I thought the issue was, since I have been away so long, I plan on starting fresh on my OS since I never really got far with it to begin with.

I am going to be closing this since it probably was just me not understanding how the bootloader was doing stuff.

I am going to be closing this since it probably was just me not understanding how the bootloader was doing stuff.

Considering you're not the only person with this problem, I wouldn't be so quick to assume it's purely your fault, as far as I'm aware this has still not been solved yet. But I've also stopped messing with OS dev stuff lately, too, at least for now (probably forever, thanks ADHD)