limine: Specify and implement base revision 1 memory mappings
diff --git a/PROTOCOL.md b/PROTOCOL.md
index dfcd62ef..721370cc 100644
--- a/PROTOCOL.md
+++ b/PROTOCOL.md
@@ -133,14 +133,29 @@ where the kernel is loaded in physical memory, see the Kernel Address feature
below.
Alongside the loaded kernel, the bootloader will set up memory mappings as such:
+
```
- Base Physical Address | | Base Virtual Address
- 0x0000000000001000 | (4 GiB - 0x1000) and any additional memory map region | 0x0000000000001000
- 0x0000000000000000 | 4 GiB and any additional memory map region | HHDM start
+ Base Physical Address | | Base Virtual Address
+ ----------------------+-------------------------------+-----------------------
+ | (4 GiB - 0x1000) and any |
+ 0x0000000000001000 | additional memory map region | 0x0000000000001000
+ | (Base revision 0 only) |
+ ----------------------+-------------------------------+-----------------------
+ | 4 GiB and additional |
+ 0x0000000000000000 | memory map regions depending | HHDM start
+ | on base revision |
```
Where "HHDM start" is returned by the Higher Half Direct Map feature (see below).
These mappings are supervisor, read, write, execute (-rwx).
+For base revision 0, the above-4GiB identity and HHDM mappings cover any memory
+map region.
+
+For base revision 1, the above-4GiB HHDM mappings do not comprise memory map regions
+of types:
+ - Reserved
+ - Bad memory
+
The bootloader page tables are in bootloader-reclaimable memory (see Memory Map
feature below), and their specific layout is undefined as long as they provide
the above memory mappings.
diff --git a/common/protos/limine.c b/common/protos/limine.c
index d6de2c08..7d4a4039 100644
--- a/common/protos/limine.c
+++ b/common/protos/limine.c
@@ -34,7 +34,8 @@
#define MAX_REQUESTS 128
-static pagemap_t build_pagemap(int paging_mode, bool nx, struct elf_range *ranges, size_t ranges_count,
+static pagemap_t build_pagemap(int base_revision,
+ int paging_mode, bool nx, struct elf_range *ranges, size_t ranges_count,
uint64_t physical_base, uint64_t virtual_base,
uint64_t direct_map_offset) {
pagemap_t pagemap = new_pagemap(paging_mode);
@@ -62,6 +63,24 @@ static pagemap_t build_pagemap(int paging_mode, bool nx, struct elf_range *range
}
}
+ // Map 0x1000->4GiB range to identity if base revision == 0
+ if (base_revision == 0) {
+ // Sub 2MiB mappings
+ for (uint64_t i = 0x1000; i < 0x200000; i += 0x1000) {
+ map_page(pagemap, i, i, VMM_FLAG_WRITE, Size4KiB);
+ }
+
+ // Map 2MiB to 4GiB
+ for (uint64_t i = 0x200000; i < 0x40000000; i += 0x200000) {
+ map_page(pagemap, i, i, VMM_FLAG_WRITE, Size2MiB);
+ }
+
+ // Map the rest
+ for (uint64_t i = 0x40000000; i < 0x100000000; i += 0x40000000) {
+ map_page(pagemap, i, i, VMM_FLAG_WRITE, Size1GiB);
+ }
+ }
+
// Map 0->4GiB range to HHDM
for (uint64_t i = 0; i < 0x100000000; i += 0x40000000) {
map_page(pagemap, direct_map_offset + i, i, VMM_FLAG_WRITE, Size1GiB);
@@ -75,9 +94,9 @@ static pagemap_t build_pagemap(int paging_mode, bool nx, struct elf_range *range
// Map all free memory regions to the higher half direct map offset
for (size_t i = 0; i < _memmap_entries; i++) {
- if (_memmap[i].type != MEMMAP_USABLE
- && _memmap[i].type != MEMMAP_BOOTLOADER_RECLAIMABLE
- && _memmap[i].type != MEMMAP_KERNEL_AND_MODULES) {
+ if (base_revision >= 1 && (
+ _memmap[i].type == MEMMAP_RESERVED
+ || _memmap[i].type == MEMMAP_BAD_MEMORY)) {
continue;
}
@@ -99,6 +118,9 @@ static pagemap_t build_pagemap(int paging_mode, bool nx, struct elf_range *range
for (uint64_t j = 0; j < aligned_length; j += 0x40000000) {
uint64_t page = aligned_base + j;
+ if (base_revision == 0) {
+ map_page(pagemap, page, page, VMM_FLAG_WRITE, Size1GiB);
+ }
map_page(pagemap, direct_map_offset + page, page, VMM_FLAG_WRITE, Size1GiB);
}
}
@@ -119,14 +141,19 @@ static pagemap_t build_pagemap(int paging_mode, bool nx, struct elf_range *range
for (uint64_t j = 0; j < aligned_length; j += 0x1000) {
uint64_t page = aligned_base + j;
+ if (base_revision == 0) {
+ map_page(pagemap, page, page, VMM_FLAG_WRITE | VMM_FLAG_FB, Size4KiB);
+ }
map_page(pagemap, direct_map_offset + page, page, VMM_FLAG_WRITE | VMM_FLAG_FB, Size4KiB);
}
}
// XXX we do this as a quick and dirty way to switch to the higher half
#if defined (__x86_64__) || defined (__i386__)
- for (uint64_t i = 0; i < 0x100000000; i += 0x40000000) {
- map_page(pagemap, i, i, VMM_FLAG_WRITE, Size1GiB);
+ if (base_revision >= 1) {
+ for (uint64_t i = 0; i < 0x100000000; i += 0x40000000) {
+ map_page(pagemap, i, i, VMM_FLAG_WRITE, Size1GiB);
+ }
}
#endif
@@ -939,7 +966,7 @@ FEAT_END
#endif
pagemap_t pagemap = {0};
- pagemap = build_pagemap(paging_mode, nx_available, ranges, ranges_count,
+ pagemap = build_pagemap(base_revision, paging_mode, nx_available, ranges, ranges_count,
physical_base, virtual_base, direct_map_offset);
#if defined (UEFI)
@@ -1102,12 +1129,14 @@ FEAT_END
uint64_t reported_stack = reported_addr(stack);
- common_spinup(limine_spinup_32, 10,
+ common_spinup(limine_spinup_32, 11,
paging_mode, (uint32_t)(uintptr_t)pagemap.top_level,
(uint32_t)entry_point, (uint32_t)(entry_point >> 32),
(uint32_t)reported_stack, (uint32_t)(reported_stack >> 32),
(uint32_t)(uintptr_t)local_gdt, nx_available,
- (uint32_t)direct_map_offset, (uint32_t)(direct_map_offset >> 32));
+ (uint32_t)direct_map_offset, (uint32_t)(direct_map_offset >> 32),
+ (uint32_t)base_revision
+ );
#elif defined (__aarch64__)
vmm_assert_4k_pages();
diff --git a/common/protos/limine_32.asm_x86 b/common/protos/limine_32.asm_x86
index 3b8a7f04..797ad787 100644
--- a/common/protos/limine_32.asm_x86
+++ b/common/protos/limine_32.asm_x86
@@ -77,6 +77,9 @@ bits 64
retq
.hh:
+ cmp dword [rsp+44], 1
+ jb .no_unmap_lower_half
+
; Unmap lower half entirely
mov rsi, cr3
lea rdi, [rsi + rax]
@@ -85,6 +88,8 @@ bits 64
rep stosq
mov cr3, rsi
+ .no_unmap_lower_half:
+
; Push fake return address
mov rsi, [rsp+20] ; stack
sub rsi, 8
