:: commit 0f4f7b5bc35570c5a89594139446d874593b72f8

Mintsuki <mintsuki@protonmail.com> — 2026-03-24 14:11

parents: d33fb37a91

Revert "protos/limine: Drop support for base revisions 0-3, require 4 on x86"

This reverts commit f0e8264e1a4cfa067192a37cdeda2cce2b15c53a.
diff --git a/common/lib/elf.c b/common/lib/elf.c
index f7f3dd3b..ee686bd3 100644
--- a/common/lib/elf.c
+++ b/common/lib/elf.c
@@ -650,6 +650,73 @@ end_of_pt_segment:
     return true;
 }
 
+bool elf64_load_section(uint8_t *elf, size_t file_size, void *buffer, const char *name, size_t limit, uint64_t slide) {
+    struct elf64_hdr *hdr = (void *)elf;
+
+    elf64_validate(hdr);
+
+    if (hdr->sh_num == 0) {
+        return false;
+    }
+
+    if (hdr->shdr_size < sizeof(struct elf64_shdr)) {
+        panic(true, "elf: shdr_size < sizeof(struct elf64_shdr)");
+    }
+
+    if (hdr->shstrndx >= hdr->sh_num) {
+        return false;
+    }
+
+    // Validate section header table is within file bounds
+    uint64_t shdr_table_end = (uint64_t)hdr->shoff + (uint64_t)hdr->sh_num * hdr->shdr_size;
+    if (shdr_table_end > file_size) {
+        return false;
+    }
+
+    struct elf64_shdr *shstrtab = (void *)elf + (hdr->shoff + hdr->shstrndx * hdr->shdr_size);
+
+    // Validate shstrtab offset and size are within file bounds
+    if (shstrtab->sh_offset >= file_size || shstrtab->sh_size > file_size - shstrtab->sh_offset) {
+        return false;
+    }
+
+    char *names = (void *)elf + shstrtab->sh_offset;
+
+    for (uint16_t i = 0; i < hdr->sh_num; i++) {
+        struct elf64_shdr *section = (void *)elf + (hdr->shoff + i * hdr->shdr_size);
+
+        // Validate sh_name is within the string table
+        if (section->sh_name >= shstrtab->sh_size) {
+            continue;
+        }
+
+        // Ensure the string is NUL-terminated within the string table
+        if (!memchr(&names[section->sh_name], '\0', shstrtab->sh_size - section->sh_name)) {
+            continue;
+        }
+
+        if (strcmp(&names[section->sh_name], name) == 0) {
+            // Validate section data is within file bounds
+            if (section->sh_offset >= file_size || section->sh_size > file_size - section->sh_offset) {
+                return false;
+            }
+
+            if (limit == 0) {
+                *(void **)buffer = ext_mem_alloc(section->sh_size);
+                buffer = *(void **)buffer;
+                limit = section->sh_size;
+            }
+            if (section->sh_size > limit) {
+                return false;
+            }
+            memcpy(buffer, elf + section->sh_offset, section->sh_size);
+            return elf64_apply_relocations(elf, hdr, buffer, section->sh_addr, section->sh_size, slide);
+        }
+    }
+
+    return false;
+}
+
 static uint64_t elf64_max_align(uint8_t *elf) {
     uint64_t ret = 0;
 
diff --git a/common/lib/elf.h b/common/lib/elf.h
index f2931660..e8d5b280 100644
--- a/common/lib/elf.h
+++ b/common/lib/elf.h
@@ -24,6 +24,7 @@ int elf_bits(uint8_t *elf);
 struct elf_section_hdr_info elf64_section_hdr_info(uint8_t *elf);
 struct elf_section_hdr_info elf32_section_hdr_info(uint8_t *elf);
 
+bool elf64_load_section(uint8_t *elf, size_t file_size, void *buffer, const char *name, size_t limit, uint64_t slide);
 bool elf64_load(uint8_t *elf, size_t file_size, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct mem_range **ranges, uint64_t *ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *image_size, uint64_t *image_size_before_bss, bool *is_reloc);
 
 bool elf32_load_elsewhere(uint8_t *elf, size_t file_size, uint64_t *entry_point,
diff --git a/common/protos/limine.c b/common/protos/limine.c
index 2e35209c..8cc17bdd 100644
--- a/common/protos/limine.c
+++ b/common/protos/limine.c
@@ -59,11 +59,21 @@ static enum executable_format detect_kernel_format(uint8_t *kernel, size_t kerne
 
 static int paging_mode;
 
-static uint64_t get_hhdm_span_top(void) {
-    uint64_t ret = 0;
+static uint64_t get_hhdm_span_top(int base_revision) {
+    uint64_t ret = base_revision >= 3 ? 0 : 0x100000000;
     for (size_t i = 0; i < memmap_entries; i++) {
-        if (memmap[i].type == MEMMAP_RESERVED
-         || memmap[i].type == MEMMAP_BAD_MEMORY) {
+        if (((base_revision >= 1 && base_revision < 3) || base_revision >= 4) && (
+            memmap[i].type == MEMMAP_RESERVED
+         || memmap[i].type == MEMMAP_BAD_MEMORY)) {
+            continue;
+        }
+
+        if (base_revision == 3 && (
+            memmap[i].type != MEMMAP_USABLE
+         && memmap[i].type != MEMMAP_BOOTLOADER_RECLAIMABLE
+         && memmap[i].type != MEMMAP_KERNEL_AND_MODULES
+         && memmap[i].type != MEMMAP_FRAMEBUFFER
+         && memmap[i].type != MEMMAP_EFI_RECLAIMABLE)) {
             continue;
         }
 
@@ -71,6 +81,10 @@ static uint64_t get_hhdm_span_top(void) {
         uint64_t length = memmap[i].length;
         uint64_t top = base + length;
 
+        if (base_revision < 3 && base < 0x100000000) {
+            base = 0x100000000;
+        }
+
         if (base >= top) {
             continue;
         }
@@ -144,7 +158,8 @@ static void limine_memcpy_to_64(uint64_t dst, void *src, size_t count) {
 }
 #endif
 
-static pagemap_t build_pagemap(bool nx, struct mem_range *ranges, size_t ranges_count,
+static pagemap_t build_pagemap(int base_revision,
+                               bool nx, struct mem_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);
@@ -170,6 +185,16 @@ static pagemap_t build_pagemap(bool nx, struct mem_range *ranges, size_t ranges_
         map_pages(pagemap, virt, phys, pf, ranges[i].length);
     }
 
+    // Map 0x1000->4GiB range to identity if base revision == 0
+    if (base_revision == 0) {
+        map_pages(pagemap, 0x1000, 0x1000, VMM_FLAG_WRITE, 0x100000000 - 0x1000);
+    }
+
+    // Map 0->4GiB range to HHDM if base revision < 3
+    if (base_revision < 3) {
+        map_pages(pagemap, direct_map_offset, 0, VMM_FLAG_WRITE, 0x100000000);
+    }
+
     size_t _memmap_entries = memmap_entries;
     struct memmap_entry *_memmap =
         ext_mem_alloc(_memmap_entries * sizeof(struct memmap_entry));
@@ -178,8 +203,18 @@ static pagemap_t build_pagemap(bool nx, struct mem_range *ranges, size_t ranges_
 
     // 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_RESERVED
-         || _memmap[i].type == MEMMAP_BAD_MEMORY) {
+        if (((base_revision >= 1 && base_revision < 3) || base_revision >= 4) && (
+            _memmap[i].type == MEMMAP_RESERVED
+         || _memmap[i].type == MEMMAP_BAD_MEMORY)) {
+            continue;
+        }
+
+        if (base_revision == 3 && (
+            _memmap[i].type != MEMMAP_USABLE
+         && _memmap[i].type != MEMMAP_BOOTLOADER_RECLAIMABLE
+         && _memmap[i].type != MEMMAP_KERNEL_AND_MODULES
+         && _memmap[i].type != MEMMAP_FRAMEBUFFER
+         && _memmap[i].type != MEMMAP_EFI_RECLAIMABLE)) {
             continue;
         }
 
@@ -187,6 +222,10 @@ static pagemap_t build_pagemap(bool nx, struct mem_range *ranges, size_t ranges_
         uint64_t length = _memmap[i].length;
         uint64_t top    = base + length;
 
+        if (base_revision < 3 && base < 0x100000000) {
+            base = 0x100000000;
+        }
+
         if (base >= top) {
             continue;
         }
@@ -195,6 +234,9 @@ static pagemap_t build_pagemap(bool nx, struct mem_range *ranges, size_t ranges_
         uint64_t aligned_top    = ALIGN_UP(top, 0x1000);
         uint64_t aligned_length = aligned_top - aligned_base;
 
+        if (base_revision == 0) {
+            map_pages(pagemap, aligned_base, aligned_base, VMM_FLAG_WRITE, aligned_length);
+        }
         map_pages(pagemap, direct_map_offset + aligned_base, aligned_base, VMM_FLAG_WRITE, aligned_length);
     }
 
@@ -212,12 +254,17 @@ static pagemap_t build_pagemap(bool nx, struct mem_range *ranges, size_t ranges_
         uint64_t aligned_top    = ALIGN_UP(top, 0x1000);
         uint64_t aligned_length = aligned_top - aligned_base;
 
+        if (base_revision == 0) {
+            map_pages(pagemap, aligned_base, aligned_base, VMM_FLAG_WRITE | VMM_FLAG_FB, aligned_length);
+        }
         map_pages(pagemap, direct_map_offset + aligned_base, aligned_base, VMM_FLAG_WRITE | VMM_FLAG_FB, aligned_length);
     }
 
     // XXX we do this as a quick and dirty way to switch to the higher half
 #if defined (__x86_64__) || defined (__i386__)
-    map_pages(pagemap, 0, 0, VMM_FLAG_WRITE, 0x100000000);
+    if (base_revision >= 1) {
+        map_pages(pagemap, 0, 0, VMM_FLAG_WRITE, 0x100000000);
+    }
 #endif
 
     return pagemap;
@@ -499,11 +546,12 @@ noreturn void limine_load(char *config, char *cmdline) {
             }
             base_revision_found = true;
             base_revision = p[2];
-            if (p[2] > SUPPORTED_BASE_REVISION) {
-                panic(true, "limine: Requested base revision %u is too new (maximum supported: %u)", (int)p[2], SUPPORTED_BASE_REVISION);
+            if (p[2] <= SUPPORTED_BASE_REVISION) {
+                // Set to 0 to mean "supported"
+                base_rev_p2_ptr = &p[2];
+            } else {
+                base_revision = SUPPORTED_BASE_REVISION;
             }
-            // Set to 0 to mean "supported"
-            base_rev_p2_ptr = &p[2];
             base_rev_p1_ptr = &p[1];
         }
     }
@@ -514,52 +562,62 @@ noreturn void limine_load(char *config, char *cmdline) {
         *base_rev_p2_ptr = 0;
     }
 
-#if defined (__x86_64__) || defined (__i386__)
-    if (base_revision < 4) {
-        panic(true, "limine: Base revision %u is no longer supported (minimum: 4)", base_revision);
-    }
-#else
+#if !defined (__x86_64__) && !defined (__i386__)
     if (base_revision < 6) {
         panic(true, "limine: Base revision %u is no longer supported on this architecture (minimum: 6)", base_revision);
     }
 #endif
 
     // Load requests
+    uint64_t *limine_reqs = NULL;
     requests = ext_mem_alloc(MAX_REQUESTS * sizeof(void *));
     requests_count = 0;
-    uint64_t common_magic[2] = { LIMINE_COMMON_MAGIC };
-    for (size_t i = 0; i < ALIGN_DOWN(image_size_before_bss, 8); i += 8) {
-        uint64_t *p = (void *)(uintptr_t)physical_base + i;
-
-        // Check if start marker hit
-        if (p[0] == limine_requests_start_marker[0] && p[1] == limine_requests_start_marker[1]
-         && p[2] == limine_requests_start_marker[2] && p[3] == limine_requests_start_marker[3]) {
-            requests_count = 0;
-            continue;
+    if (base_revision == 0 && kernel_format == EXECUTABLE_FORMAT_ELF && elf64_load_section(kernel, kernel_file->size, &limine_reqs, ".limine_reqs", 0, slide)) {
+        for (size_t i = 0; ; i++) {
+            if (i >= MAX_REQUESTS) {
+                panic(true, "limine: Maximum requests exceeded");
+            }
+            if (limine_reqs[i] == 0) {
+                break;
+            }
+            requests[i] = (void *)(uintptr_t)((limine_reqs[i] - virtual_base) + physical_base);
+            requests_count++;
         }
+    } else {
+        uint64_t common_magic[2] = { LIMINE_COMMON_MAGIC };
+        for (size_t i = 0; i < ALIGN_DOWN(image_size_before_bss, 8); i += 8) {
+            uint64_t *p = (void *)(uintptr_t)physical_base + i;
+
+            // Check if start marker hit
+            if (p[0] == limine_requests_start_marker[0] && p[1] == limine_requests_start_marker[1]
+             && p[2] == limine_requests_start_marker[2] && p[3] == limine_requests_start_marker[3]) {
+                requests_count = 0;
+                continue;
+            }
 
-        // Check if end marker hit
-        if (p[0] == limine_requests_end_marker[0] && p[1] == limine_requests_end_marker[1]) {
-            break;
-        }
+            // Check if end marker hit
+            if (p[0] == limine_requests_end_marker[0] && p[1] == limine_requests_end_marker[1]) {
+                break;
+            }
 
-        if (p[0] != common_magic[0]) {
-            continue;
-        }
-        if (p[1] != common_magic[1]) {
-            continue;
-        }
+            if (p[0] != common_magic[0]) {
+                continue;
+            }
+            if (p[1] != common_magic[1]) {
+                continue;
+            }
 
-        if (requests_count == MAX_REQUESTS) {
-            panic(true, "limine: Maximum requests exceeded");
-        }
+            if (requests_count == MAX_REQUESTS) {
+                panic(true, "limine: Maximum requests exceeded");
+            }
 
-        // Check for a conflict
-        if (_get_request(p) != NULL) {
-            panic(true, "limine: Conflict detected for request ID %X %X", p[2], p[3]);
-        }
+            // Check for a conflict
+            if (_get_request(p) != NULL) {
+                panic(true, "limine: Conflict detected for request ID %X %X", p[2], p[3]);
+            }
 
-        requests[requests_count++] = p;
+            requests[requests_count++] = p;
+        }
     }
 
 #if defined (__x86_64__) || defined (__i386__)
@@ -569,7 +627,7 @@ noreturn void limine_load(char *config, char *cmdline) {
     }
 #endif
 
-    uint64_t hhdm_span_top = get_hhdm_span_top();
+    uint64_t hhdm_span_top = get_hhdm_span_top(base_revision);
 
 #if defined (__x86_64__) || defined (__i386__)
     uint64_t maxphyaddr;
@@ -983,7 +1041,7 @@ FEAT_START
     struct limine_rsdp_response *rsdp_response =
         ext_mem_alloc(sizeof(struct limine_rsdp_response));
 
-    rsdp_response->address = reported_addr(rsdp);
+    rsdp_response->address = (base_revision <= 2 || base_revision >= 4) ? reported_addr(rsdp) : (uintptr_t)rsdp;
 
     rsdp_request->response = reported_addr(rsdp_response);
 FEAT_END
@@ -1005,10 +1063,10 @@ FEAT_START
         ext_mem_alloc(sizeof(struct limine_smbios_response));
 
     if (smbios_entry_32) {
-        smbios_response->entry_32 = (base_revision >= 5) ? reported_addr(smbios_entry_32) : (uintptr_t)smbios_entry_32;
+        smbios_response->entry_32 = (base_revision <= 2 || base_revision >= 5) ? reported_addr(smbios_entry_32) : (uintptr_t)smbios_entry_32;
     }
     if (smbios_entry_64) {
-        smbios_response->entry_64 = (base_revision >= 5) ? reported_addr(smbios_entry_64) : (uintptr_t)smbios_entry_64;
+        smbios_response->entry_64 = (base_revision <= 2 || base_revision >= 5) ? reported_addr(smbios_entry_64) : (uintptr_t)smbios_entry_64;
     }
 
     smbios_request->response = reported_addr(smbios_response);
@@ -1025,7 +1083,7 @@ FEAT_START
     struct limine_efi_system_table_response *est_response =
         ext_mem_alloc(sizeof(struct limine_efi_system_table_response));
 
-    est_response->address = (base_revision >= 5) ? reported_addr(gST) : (uintptr_t)gST;
+    est_response->address = (base_revision <= 2 || base_revision >= 5) ? reported_addr(gST) : (uintptr_t)gST;
 
     est_request->response = reported_addr(est_response);
 FEAT_END
@@ -1557,17 +1615,24 @@ FEAT_START
 FEAT_END
 #endif
 
-    acpi_map_tables();
-    if (base_revision >= 5) {
-        smbios_map_tables();
+    if (base_revision < 3) {
+        pmm_sanitiser_keep_first_page = false;
+        pmm_sanitise_entries(memmap, &memmap_entries, true);
+    }
+
+    if (base_revision >= 4) {
+        acpi_map_tables();
+        if (base_revision >= 5) {
+            smbios_map_tables();
 #if defined (UEFI)
-        efi_map_runtime_entries();
+            efi_map_runtime_entries();
 #endif
+        }
+        pmm_sanitise_entries(memmap, &memmap_entries, true);
     }
-    pmm_sanitise_entries(memmap, &memmap_entries, true);
 
     pagemap_t pagemap = {0};
-    pagemap = build_pagemap(nx_available, ranges, ranges_count,
+    pagemap = build_pagemap(base_revision, nx_available, ranges, ranges_count,
                             physical_base, virtual_base, direct_map_offset);
 
 #if defined (__aarch64__)
@@ -1786,12 +1851,13 @@ 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 79b6b424..75819e4b 100644
--- a/common/protos/limine_32.asm_x86
+++ b/common/protos/limine_32.asm_x86
@@ -90,6 +90,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]
@@ -98,6 +101,8 @@ bits 64
     rep stosq
     mov cr3, rsi
 
+  .no_unmap_lower_half:
+
     ; Push fake return address
     mov rsi, [rsp+20] ; stack
     sub rsi, 8
tab: 248 wrap: offon