--- /dev/null
+#include "elf.h"
+
+#include "fs.h"
+#include "heap.h"
+#include "pmm.h"
+#include "uart_console.h"
+#include "utils.h"
+#include "vmm.h"
+
+#include "errno.h"
+
+#include "hal/cpu.h"
+#include "hal/mm.h"
+
+#include <stdint.h>
+
+static void* pmm_alloc_page_low_16mb(void) {
+ for (int tries = 0; tries < 4096; tries++) {
+ void* p = pmm_alloc_page();
+ if (!p) return NULL;
+ if ((uintptr_t)p < 0x01000000U) {
+ return p;
+ }
+ pmm_free_page(p);
+ }
+ return NULL;
+}
+
+static int elf32_validate(const elf32_ehdr_t* eh, size_t file_len) {
+ if (!eh) return -EFAULT;
+ if (file_len < sizeof(*eh)) return -EINVAL;
+
+ if (eh->e_ident[0] != ELF_MAGIC0 ||
+ eh->e_ident[1] != ELF_MAGIC1 ||
+ eh->e_ident[2] != ELF_MAGIC2 ||
+ eh->e_ident[3] != ELF_MAGIC3) {
+ return -EINVAL;
+ }
+ if (eh->e_ident[4] != ELFCLASS32) return -EINVAL;
+ if (eh->e_ident[5] != ELFDATA2LSB) return -EINVAL;
+
+ if (eh->e_type != ET_EXEC) return -EINVAL;
+ if (eh->e_machine != EM_386) return -EINVAL;
+
+ if (eh->e_phentsize != sizeof(elf32_phdr_t)) return -EINVAL;
+ if (eh->e_phnum == 0) return -EINVAL;
+
+ uint32_t ph_end = eh->e_phoff + (uint32_t)eh->e_phnum * (uint32_t)sizeof(elf32_phdr_t);
+ if (ph_end < eh->e_phoff) return -EINVAL;
+ if (ph_end > file_len) return -EINVAL;
+
+ if (eh->e_entry == 0) return -EINVAL;
+ if (eh->e_entry >= hal_mm_kernel_virt_base()) return -EINVAL;
+
+ return 0;
+}
+
+static int elf32_map_user_range(uintptr_t as, uintptr_t vaddr, size_t len, uint32_t flags) {
+ if (len == 0) return 0;
+ if (vaddr == 0) return -EINVAL;
+ if (vaddr >= hal_mm_kernel_virt_base()) return -EINVAL;
+
+ uintptr_t end = vaddr + len - 1;
+ if (end < vaddr) return -EINVAL;
+ if (end >= hal_mm_kernel_virt_base()) return -EINVAL;
+
+ uintptr_t start_page = vaddr & ~(uintptr_t)0xFFF;
+ uintptr_t end_page = end & ~(uintptr_t)0xFFF;
+
+ uintptr_t old_as = hal_cpu_get_address_space();
+ vmm_as_activate(as);
+
+ for (uintptr_t va = start_page;; va += 0x1000) {
+ const uint32_t pdi = (uint32_t)(va >> 22);
+ const uint32_t pti = (uint32_t)((va >> 12) & 0x03FF);
+
+ volatile uint32_t* pd = (volatile uint32_t*)0xFFFFF000U;
+ int already_mapped = 0;
+ if ((pd[pdi] & 1U) != 0U) {
+ volatile uint32_t* pt = (volatile uint32_t*)0xFFC00000U + ((uintptr_t)pdi << 10);
+ if ((pt[pti] & 1U) != 0U) {
+ already_mapped = 1;
+ }
+ }
+
+ if (!already_mapped) {
+ void* phys = pmm_alloc_page_low_16mb();
+ if (!phys) {
+ vmm_as_activate(old_as);
+ return -ENOMEM;
+ }
+
+ vmm_map_page((uint64_t)(uintptr_t)phys, (uint64_t)va, flags | VMM_FLAG_PRESENT | VMM_FLAG_USER);
+ }
+
+ if (va == end_page) break;
+ }
+
+ vmm_as_activate(old_as);
+
+ return 0;
+}
+
+int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uintptr_t* user_stack_top_out, uintptr_t* addr_space_out, uintptr_t* heap_break_out) {
+ if (!filename || !entry_out || !user_stack_top_out || !addr_space_out) return -EFAULT;
+ if (!fs_root) return -EINVAL;
+
+ uintptr_t new_as = vmm_as_create_kernel_clone();
+ if (!new_as) return -ENOMEM;
+
+ uintptr_t old_as = hal_cpu_get_address_space();
+
+ fs_node_t* node = vfs_lookup(filename);
+ if (!node) {
+ uart_print("[ELF] file not found: ");
+ uart_print(filename);
+ uart_print("\n");
+ vmm_as_destroy(new_as);
+ return -ENOENT;
+ }
+
+ uint32_t file_len = node->length;
+ if (file_len < sizeof(elf32_ehdr_t)) {
+ vmm_as_activate(old_as);
+ vmm_as_destroy(new_as);
+ return -EINVAL;
+ }
+
+ uint8_t* file = (uint8_t*)kmalloc(file_len);
+ if (!file) {
+ vmm_as_destroy(new_as);
+ return -ENOMEM;
+ }
+
+ uint32_t rd = vfs_read(node, 0, file_len, file);
+ if (rd != file_len) {
+ kfree(file);
+ vmm_as_destroy(new_as);
+ return -EIO;
+ }
+
+ // Switch into the new address space only after the ELF image is fully buffered.
+ vmm_as_activate(new_as);
+
+ const elf32_ehdr_t* eh = (const elf32_ehdr_t*)file;
+ int vrc = elf32_validate(eh, file_len);
+ if (vrc < 0) {
+ uart_print("[ELF] invalid ELF header\n");
+ kfree(file);
+ vmm_as_activate(old_as);
+ vmm_as_destroy(new_as);
+ return vrc;
+ }
+
+ const elf32_phdr_t* ph = (const elf32_phdr_t*)(file + eh->e_phoff);
+ uintptr_t highest_seg_end = 0;
+
+ for (uint16_t i = 0; i < eh->e_phnum; i++) {
+ if (ph[i].p_type != PT_LOAD) continue;
+
+ if (ph[i].p_memsz == 0) continue;
+ if (ph[i].p_vaddr == 0) {
+ uart_print("[ELF] PT_LOAD with vaddr=0 rejected\n");
+ kfree(file);
+ vmm_as_activate(old_as);
+ vmm_as_destroy(new_as);
+ return -EINVAL;
+ }
+ if (ph[i].p_vaddr >= hal_mm_kernel_virt_base()) {
+ uart_print("[ELF] PT_LOAD in kernel range rejected\n");
+ kfree(file);
+ vmm_as_activate(old_as);
+ vmm_as_destroy(new_as);
+ return -EINVAL;
+ }
+
+ uint32_t seg_end = ph[i].p_vaddr + ph[i].p_memsz;
+ if (seg_end < ph[i].p_vaddr) {
+ kfree(file);
+ vmm_as_activate(old_as);
+ vmm_as_destroy(new_as);
+ return -EINVAL;
+ }
+ if (seg_end >= hal_mm_kernel_virt_base()) {
+ kfree(file);
+ vmm_as_activate(old_as);
+ vmm_as_destroy(new_as);
+ return -EINVAL;
+ }
+
+ if ((uint64_t)ph[i].p_offset + (uint64_t)ph[i].p_filesz > (uint64_t)file_len) {
+ uart_print("[ELF] segment outside file\n");
+ kfree(file);
+ vmm_as_activate(old_as);
+ vmm_as_destroy(new_as);
+ return -EINVAL;
+ }
+
+ const uint32_t map_flags = VMM_FLAG_RW;
+
+ int mrc = elf32_map_user_range(new_as, (uintptr_t)ph[i].p_vaddr, (size_t)ph[i].p_memsz, map_flags);
+ if (mrc < 0) {
+ uart_print("[ELF] OOM mapping user segment\n");
+ kfree(file);
+ vmm_as_activate(old_as);
+ vmm_as_destroy(new_as);
+ return mrc;
+ }
+
+ if (ph[i].p_filesz) {
+ memcpy((void*)(uintptr_t)ph[i].p_vaddr, file + ph[i].p_offset, ph[i].p_filesz);
+ }
+
+ if (ph[i].p_memsz > ph[i].p_filesz) {
+ memset((void*)(uintptr_t)(ph[i].p_vaddr + ph[i].p_filesz), 0, ph[i].p_memsz - ph[i].p_filesz);
+ }
+
+ if (seg_end > highest_seg_end) {
+ highest_seg_end = seg_end;
+ }
+ }
+
+ const uintptr_t user_stack_base = 0x00800000U;
+ const size_t user_stack_size = 0x1000;
+
+ int src2 = elf32_map_user_range(new_as, user_stack_base, user_stack_size, VMM_FLAG_RW);
+ if (src2 < 0) {
+ uart_print("[ELF] OOM mapping user stack\n");
+ kfree(file);
+ vmm_as_activate(old_as);
+ vmm_as_destroy(new_as);
+ return src2;
+ }
+
+ *entry_out = (uintptr_t)eh->e_entry;
+ *user_stack_top_out = user_stack_base + user_stack_size;
+ *addr_space_out = new_as;
+ if (heap_break_out) {
+ *heap_break_out = (highest_seg_end + 0xFFFU) & ~(uintptr_t)0xFFFU;
+ }
+
+ kfree(file);
+ vmm_as_activate(old_as);
+ return 0;
+}
#include "elf.h"
-
-#include "fs.h"
-#include "heap.h"
-#include "pmm.h"
-#include "uart_console.h"
-#include "utils.h"
-#include "vmm.h"
-
-#include "errno.h"
-
-#include "hal/cpu.h"
-#include "hal/mm.h"
-
#include <stdint.h>
-#if defined(__i386__)
-
-static void* pmm_alloc_page_low_16mb(void) {
- for (int tries = 0; tries < 4096; tries++) {
- void* p = pmm_alloc_page();
- if (!p) return NULL;
- if ((uintptr_t)p < 0x01000000U) {
- return p;
- }
- pmm_free_page(p);
- }
- return NULL;
-}
-
-static int elf32_validate(const elf32_ehdr_t* eh, size_t file_len) {
- if (!eh) return -EFAULT;
- if (file_len < sizeof(*eh)) return -EINVAL;
-
- if (eh->e_ident[0] != ELF_MAGIC0 ||
- eh->e_ident[1] != ELF_MAGIC1 ||
- eh->e_ident[2] != ELF_MAGIC2 ||
- eh->e_ident[3] != ELF_MAGIC3) {
- return -EINVAL;
- }
- if (eh->e_ident[4] != ELFCLASS32) return -EINVAL;
- if (eh->e_ident[5] != ELFDATA2LSB) return -EINVAL;
-
- if (eh->e_type != ET_EXEC) return -EINVAL;
- if (eh->e_machine != EM_386) return -EINVAL;
-
- if (eh->e_phentsize != sizeof(elf32_phdr_t)) return -EINVAL;
- if (eh->e_phnum == 0) return -EINVAL;
-
- uint32_t ph_end = eh->e_phoff + (uint32_t)eh->e_phnum * (uint32_t)sizeof(elf32_phdr_t);
- if (ph_end < eh->e_phoff) return -EINVAL;
- if (ph_end > file_len) return -EINVAL;
-
- if (eh->e_entry == 0) return -EINVAL;
- if (eh->e_entry >= hal_mm_kernel_virt_base()) return -EINVAL;
-
- return 0;
-}
-
-static int elf32_map_user_range(uintptr_t as, uintptr_t vaddr, size_t len, uint32_t flags) {
- if (len == 0) return 0;
- if (vaddr == 0) return -EINVAL;
- if (vaddr >= hal_mm_kernel_virt_base()) return -EINVAL;
-
- uintptr_t end = vaddr + len - 1;
- if (end < vaddr) return -EINVAL;
- if (end >= hal_mm_kernel_virt_base()) return -EINVAL;
-
- uintptr_t start_page = vaddr & ~(uintptr_t)0xFFF;
- uintptr_t end_page = end & ~(uintptr_t)0xFFF;
-
- uintptr_t old_as = hal_cpu_get_address_space();
- vmm_as_activate(as);
-
- for (uintptr_t va = start_page;; va += 0x1000) {
- const uint32_t pdi = (uint32_t)(va >> 22);
- const uint32_t pti = (uint32_t)((va >> 12) & 0x03FF);
-
- volatile uint32_t* pd = (volatile uint32_t*)0xFFFFF000U;
- int already_mapped = 0;
- if ((pd[pdi] & 1U) != 0U) {
- volatile uint32_t* pt = (volatile uint32_t*)0xFFC00000U + ((uintptr_t)pdi << 10);
- if ((pt[pti] & 1U) != 0U) {
- already_mapped = 1;
- }
- }
-
- if (!already_mapped) {
- void* phys = pmm_alloc_page_low_16mb();
- if (!phys) {
- vmm_as_activate(old_as);
- return -ENOMEM;
- }
-
- vmm_map_page((uint64_t)(uintptr_t)phys, (uint64_t)va, flags | VMM_FLAG_PRESENT | VMM_FLAG_USER);
- }
-
- if (va == end_page) break;
- }
-
- vmm_as_activate(old_as);
-
- return 0;
-}
-
-int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uintptr_t* user_stack_top_out, uintptr_t* addr_space_out, uintptr_t* heap_break_out) {
- if (!filename || !entry_out || !user_stack_top_out || !addr_space_out) return -EFAULT;
- if (!fs_root) return -EINVAL;
-
- uintptr_t new_as = vmm_as_create_kernel_clone();
- if (!new_as) return -ENOMEM;
-
- uintptr_t old_as = hal_cpu_get_address_space();
-
- fs_node_t* node = vfs_lookup(filename);
- if (!node) {
- uart_print("[ELF] file not found: ");
- uart_print(filename);
- uart_print("\n");
- vmm_as_destroy(new_as);
- return -ENOENT;
- }
-
- uint32_t file_len = node->length;
- if (file_len < sizeof(elf32_ehdr_t)) {
- vmm_as_activate(old_as);
- vmm_as_destroy(new_as);
- return -EINVAL;
- }
-
- uint8_t* file = (uint8_t*)kmalloc(file_len);
- if (!file) {
- vmm_as_destroy(new_as);
- return -ENOMEM;
- }
-
- uint32_t rd = vfs_read(node, 0, file_len, file);
- if (rd != file_len) {
- kfree(file);
- vmm_as_destroy(new_as);
- return -EIO;
- }
-
- // Switch into the new address space only after the ELF image is fully buffered.
- vmm_as_activate(new_as);
-
- const elf32_ehdr_t* eh = (const elf32_ehdr_t*)file;
- int vrc = elf32_validate(eh, file_len);
- if (vrc < 0) {
- uart_print("[ELF] invalid ELF header\n");
- kfree(file);
- vmm_as_activate(old_as);
- vmm_as_destroy(new_as);
- return vrc;
- }
-
- const elf32_phdr_t* ph = (const elf32_phdr_t*)(file + eh->e_phoff);
- uintptr_t highest_seg_end = 0;
-
- for (uint16_t i = 0; i < eh->e_phnum; i++) {
- if (ph[i].p_type != PT_LOAD) continue;
-
- if (ph[i].p_memsz == 0) continue;
- if (ph[i].p_vaddr == 0) {
- uart_print("[ELF] PT_LOAD with vaddr=0 rejected\n");
- kfree(file);
- vmm_as_activate(old_as);
- vmm_as_destroy(new_as);
- return -EINVAL;
- }
- if (ph[i].p_vaddr >= hal_mm_kernel_virt_base()) {
- uart_print("[ELF] PT_LOAD in kernel range rejected\n");
- kfree(file);
- vmm_as_activate(old_as);
- vmm_as_destroy(new_as);
- return -EINVAL;
- }
-
- uint32_t seg_end = ph[i].p_vaddr + ph[i].p_memsz;
- if (seg_end < ph[i].p_vaddr) {
- kfree(file);
- vmm_as_activate(old_as);
- vmm_as_destroy(new_as);
- return -EINVAL;
- }
- if (seg_end >= hal_mm_kernel_virt_base()) {
- kfree(file);
- vmm_as_activate(old_as);
- vmm_as_destroy(new_as);
- return -EINVAL;
- }
-
- if ((uint64_t)ph[i].p_offset + (uint64_t)ph[i].p_filesz > (uint64_t)file_len) {
- uart_print("[ELF] segment outside file\n");
- kfree(file);
- vmm_as_activate(old_as);
- vmm_as_destroy(new_as);
- return -EINVAL;
- }
-
- const uint32_t map_flags = VMM_FLAG_RW;
-
- int mrc = elf32_map_user_range(new_as, (uintptr_t)ph[i].p_vaddr, (size_t)ph[i].p_memsz, map_flags);
- if (mrc < 0) {
- uart_print("[ELF] OOM mapping user segment\n");
- kfree(file);
- vmm_as_activate(old_as);
- vmm_as_destroy(new_as);
- return mrc;
- }
-
- if (ph[i].p_filesz) {
- memcpy((void*)(uintptr_t)ph[i].p_vaddr, file + ph[i].p_offset, ph[i].p_filesz);
- }
-
- if (ph[i].p_memsz > ph[i].p_filesz) {
- memset((void*)(uintptr_t)(ph[i].p_vaddr + ph[i].p_filesz), 0, ph[i].p_memsz - ph[i].p_filesz);
- }
-
- if (seg_end > highest_seg_end) {
- highest_seg_end = seg_end;
- }
- }
-
- const uintptr_t user_stack_base = 0x00800000U;
- const size_t user_stack_size = 0x1000;
-
- int src2 = elf32_map_user_range(new_as, user_stack_base, user_stack_size, VMM_FLAG_RW);
- if (src2 < 0) {
- uart_print("[ELF] OOM mapping user stack\n");
- kfree(file);
- vmm_as_activate(old_as);
- vmm_as_destroy(new_as);
- return src2;
- }
-
- *entry_out = (uintptr_t)eh->e_entry;
- *user_stack_top_out = user_stack_base + user_stack_size;
- *addr_space_out = new_as;
- if (heap_break_out) {
- *heap_break_out = (highest_seg_end + 0xFFFU) & ~(uintptr_t)0xFFFU;
- }
-
- kfree(file);
- vmm_as_activate(old_as);
- return 0;
-}
-#else
+__attribute__((weak))
int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uintptr_t* user_stack_top_out, uintptr_t* addr_space_out, uintptr_t* heap_break_out) {
(void)filename;
(void)entry_out;
(void)heap_break_out;
return -1;
}
-#endif