rust-osdev/bootloader

Bootloader incorrectly marks up the memory occupied by itself

Closed this issue · 9 comments

Trying to figure out why I can't initialize the allocator for ISA DMA memory, I found that I don't have any usable memory in the first 16 MB.

log::info!("Used by kernel: 0x{:x} {}", boot_info.kernel_addr, boot_info.kernel_len);
log::info!("Memory map: {:#x?}", boot_info.memory_regions.iter());

That's what data I get:

Used by kernel: 0x1000000 2865032
...
MemoryRegion {
            start: 0x100000,
            end: 0x12ff000,
            kind: Bootloader,
        }
...

0x12ff000 - 0x100000 = 0x11ff000(18870272)
18 MB from first MB used by bootloader
I doubt that the bootloader has placed so much data in this region. It looks like the memory map is incorrect.

Bootloader on stage 4 contains this memory map

...
E820MemoryRegion {
    start_addr: 0x100000, 
    len: 0x7ee0000, ~128 MB
    region_type: 0x1, Usable
    acpi_extended_attributes: 0x0,
}
...

Bootloader places something incredibly large in this 16 megabytes

I dug into the bootloader's code and found that last_used_addr when initializing LegacyFrameAllocator is 0x12bd000, it seems that even before creating the allocator, the first 18 megabytes are occupied with something.

Maybe a stupid question, but how big is your kernel binary?

Maybe a stupid question, but how big is your kernel binary?

The size of the elf kernel file exactly corresponds to boot_info.kernel_len: 2865032

This memory also cannot be occupied by page tables, since I am running qemu with 128 megabytes.
Disabling mapping of all physical memory also does not affect the occupancy of this area.

I think I found a bug that makes the first 16 megabytes disappear.
I investigated bios stage2 and saw that info.last_used_addr is always above 16 MB, and since info.last_used_addr is used as next_free_frame in frame_allocator, when LegacyFrameAllocator::construct_memory_map is called, the whole chunk from 0x100000 (1 MB) to next_free (which is always above 16 MB because of this line”) will be marked as used by bootloader.

Thus the first 16 MB are always marked as occupied by the bootloader, which not only wastes memory but also makes it impossible to allocate memory for DMA.

Also bootloader freezes if physical memory size is less than 16 + kernel_size or something, probably it's related.
Anyway, this code always(if the region from 0x100000 is larger than the kernel (which is almost always the case)) marks the region 0x100000 to (16 MB + kernel_size)] as occupied by bootloader, although it is not.

but also makes it impossible to allocate memory for DMA.

Why is that? DMA should work fine everywhere in the address space where RAM is mapped in the first place.

DMA should work fine everywhere in the address space where RAM is mapped in the first place.

Since the first 16 megabytes are always marked as occupied by the bootloader, I simply cannot know which of these addresses are really free and can be used by me to allocate memory for ISA DMA needs.

Ah, this is for ISA DMA. I figured you wanted PCI DMA, which even for old devices should work everywhere in the first 4GB. I agree that the bootloader should be fixed.

I can't reproduce this on the main branch. I think this issue was fixed by #446 (even though the PR title doesn't mention 16MiB). Could you please test on main and see if the memory map still doesn't contain usable memory in the ISA DMA range? If this issue has already been fixed, we can do a patch release.

@Freax13, You are correct, it has been fixed in the main branch! Nice work!