*.iso
*.img
*.log
+*.elf
# ISO staging: keep config, ignore generated kernel binaries
iso/boot/*.bin
compile_commands.json
clang-tidy.txt
cppcheck.txt
+cppcheck2.txt
+cppcheck3.txt
scanbuild-out/
gcc-analyzer.txt
splint.txt
+# Tools (built binaries)
+tools/mkinitrd
+
# Temporary folders
iso_root/
isodir/
ASM_SOURCES := $(wildcard $(SRC_DIR)/arch/x86/*.S)
C_SOURCES += $(wildcard $(SRC_DIR)/arch/x86/*.c)
+
+ USER_ELF := user/init.elf
+ INITRD_IMG := initrd.img
+ MKINITRD := tools/mkinitrd
endif
# --- ARM64 Configuration ---
OBJ := $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(C_SOURCES))
OBJ += $(patsubst $(SRC_DIR)/%.S, $(BUILD_DIR)/%.o, $(ASM_SOURCES))
+QEMU_DFLAGS :=
+ifneq ($(QEMU_DEBUG),)
+QEMU_DFLAGS := -d guest_errors,cpu_reset -D qemu.log
+endif
+
+ifneq ($(QEMU_INT),)
+QEMU_DFLAGS := $(QEMU_DFLAGS) -d int
+endif
+
BOOT_OBJ := $(BUILD_DIR)/arch/x86/boot.o
KERNEL_OBJ := $(filter-out $(BOOT_OBJ), $(OBJ))
@echo " LD $@"
@$(LD) $(LDFLAGS) -n -o $@ $(BOOT_OBJ) $(KERNEL_OBJ)
-iso: $(KERNEL_NAME)
+iso: $(KERNEL_NAME) $(INITRD_IMG)
@mkdir -p iso/boot
@cp -f $(KERNEL_NAME) iso/boot/$(KERNEL_NAME)
+ @cp -f $(INITRD_IMG) iso/boot/$(INITRD_IMG)
@echo " GRUB-MKRESCUE adros-$(ARCH).iso"
@grub-mkrescue -o adros-$(ARCH).iso iso > /dev/null
+$(MKINITRD): tools/mkinitrd.c
+ @gcc tools/mkinitrd.c -o $(MKINITRD)
+
+$(USER_ELF): user/init.c user/linker.ld
+ @i686-elf-gcc -m32 -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(USER_ELF) user/init.c
+
+$(INITRD_IMG): $(MKINITRD) $(USER_ELF)
+ @./$(MKINITRD) $(INITRD_IMG) $(USER_ELF)
+
run: iso
@rm -f serial.log qemu.log
@qemu-system-i386 -boot d -cdrom adros-$(ARCH).iso -m 128M -display none \
-serial file:serial.log -monitor none -no-reboot -no-shutdown \
- $(if $(QEMU_DEBUG),-d guest_errors -D qemu.log,)
+ $(QEMU_DFLAGS)
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
@mkdir -p $(dir $@)
--- /dev/null
+#ifndef ARCH_X86_USERMODE_H
+#define ARCH_X86_USERMODE_H
+
+#include <stdint.h>
+
+#if defined(__i386__)
+__attribute__((noreturn)) void x86_enter_usermode(uintptr_t user_eip, uintptr_t user_esp);
+#endif
+
+#endif
--- /dev/null
+#ifndef ELF_H
+#define ELF_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+#define EI_NIDENT 16
+
+typedef struct {
+ unsigned char e_ident[EI_NIDENT];
+ uint16_t e_type;
+ uint16_t e_machine;
+ uint32_t e_version;
+ uint32_t e_entry;
+ uint32_t e_phoff;
+ uint32_t e_shoff;
+ uint32_t e_flags;
+ uint16_t e_ehsize;
+ uint16_t e_phentsize;
+ uint16_t e_phnum;
+ uint16_t e_shentsize;
+ uint16_t e_shnum;
+ uint16_t e_shstrndx;
+} elf32_ehdr_t;
+
+typedef struct {
+ uint32_t p_type;
+ uint32_t p_offset;
+ uint32_t p_vaddr;
+ uint32_t p_paddr;
+ uint32_t p_filesz;
+ uint32_t p_memsz;
+ uint32_t p_flags;
+ uint32_t p_align;
+} elf32_phdr_t;
+
+#define ELF_MAGIC0 0x7F
+#define ELF_MAGIC1 'E'
+#define ELF_MAGIC2 'L'
+#define ELF_MAGIC3 'F'
+
+#define ELFCLASS32 1
+#define ELFDATA2LSB 1
+
+#define ET_EXEC 2
+#define EM_386 3
+
+#define PT_LOAD 1
+
+#define PF_X 0x1
+#define PF_W 0x2
+#define PF_R 0x4
+
+int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uintptr_t* user_stack_top_out);
+
+#endif
set timeout=5
-set default=0
+set default=1
#serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1
#terminal_input serial
#terminal_output serial
menuentry "AdrOS (x86)" {
+ multiboot2 /boot/adros-x86.bin
+ module2 /boot/initrd.img
+ boot
+}
+
+menuentry "AdrOS (x86) - ring3 test" {
multiboot2 /boot/adros-x86.bin ring3
+ module2 /boot/initrd.img
boot
}
.align 16
stack_bottom:
.skip 16384
+.global stack_top
stack_top:
.section .note.GNU-stack,"",@progbits
#include "gdt.h"
#include "uart_console.h"
+ #include "utils.h"
struct gdt_entry {
uint16_t limit_low;
idt_set_gate(0, (uint32_t)isr0, 0x08, 0x8E);
idt_set_gate(1, (uint32_t)isr1, 0x08, 0x8E);
idt_set_gate(2, (uint32_t)isr2, 0x08, 0x8E);
- idt_set_gate(3, (uint32_t)isr3, 0x08, 0x8E);
+ idt_set_gate(3, (uint32_t)isr3, 0x08, 0xEE);
idt_set_gate(4, (uint32_t)isr4, 0x08, 0x8E);
idt_set_gate(5, (uint32_t)isr5, 0x08, 0x8E);
idt_set_gate(6, (uint32_t)isr6, 0x08, 0x8E);
#include "vmm.h"
#include "uart_console.h"
#include "utils.h"
+ #include "arch/x86/usermode.h"
#if defined(__i386__)
return NULL;
}
-__attribute__((noreturn)) static void enter_usermode(uintptr_t user_eip, uintptr_t user_esp) {
+__attribute__((noreturn)) void x86_enter_usermode(uintptr_t user_eip, uintptr_t user_esp) {
+ uart_print("[USER] enter ring3 eip=");
+ char tmp[16];
+ itoa_hex((uint32_t)user_eip, tmp);
+ uart_print(tmp);
+ uart_print(" esp=");
+ itoa_hex((uint32_t)user_esp, tmp);
+ uart_print(tmp);
+ uart_print("\n");
+
__asm__ volatile(
"cli\n"
- "mov $0x23, %%ax\n"
- "mov %%ax, %%ds\n"
- "mov %%ax, %%es\n"
- "mov %%ax, %%fs\n"
- "mov %%ax, %%gs\n"
"pushl $0x23\n" /* ss */
"pushl %[esp]\n" /* esp */
- "pushfl\n"
- "popl %%eax\n"
- "orl $0x200, %%eax\n" /* IF=1 */
- "pushl %%eax\n"
+ "pushl $0x202\n" /* eflags: IF=1 */
"pushl $0x1B\n" /* cs */
"pushl %[eip]\n" /* eip */
"iret\n"
:
: [eip] "r"(user_eip), [esp] "r"(user_esp)
- : "eax", "memory"
+ : "memory"
);
__builtin_unreachable();
memcpy((void*)((uintptr_t)code_phys + 0x300), "Hello from ring3!\n", msg_len);
uintptr_t user_esp = user_stack_vaddr + 4096;
- enter_usermode(user_code_vaddr, user_esp);
+ x86_enter_usermode(user_code_vaddr, user_esp);
}
#endif
root_nodes = (fs_node_t*)kmalloc(sizeof(fs_node_t) * n_root_nodes);
for (int i = 0; i < n_root_nodes; i++) {
- file_headers[i].offset += location; // Fixup offset to be absolute address
-
strcpy(root_nodes[i].name, file_headers[i].name);
root_nodes[i].inode = i;
root_nodes[i].length = file_headers[i].length;
--- /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 <stdint.h>
+
+#if defined(__i386__)
+#define X86_KERNEL_VIRT_BASE 0xC0000000U
+
+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 -1;
+ if (file_len < sizeof(*eh)) return -1;
+
+ 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 -1;
+ }
+ if (eh->e_ident[4] != ELFCLASS32) return -1;
+ if (eh->e_ident[5] != ELFDATA2LSB) return -1;
+
+ if (eh->e_type != ET_EXEC) return -1;
+ if (eh->e_machine != EM_386) return -1;
+
+ if (eh->e_phentsize != sizeof(elf32_phdr_t)) return -1;
+ if (eh->e_phnum == 0) return -1;
+
+ 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 -1;
+ if (ph_end > file_len) return -1;
+
+ if (eh->e_entry == 0) return -1;
+ if (eh->e_entry >= X86_KERNEL_VIRT_BASE) return -1;
+
+ return 0;
+}
+
+static int elf32_map_user_range(uintptr_t vaddr, size_t len, uint32_t flags) {
+ if (len == 0) return 0;
+ if (vaddr == 0) return -1;
+ if (vaddr >= X86_KERNEL_VIRT_BASE) return -1;
+
+ uintptr_t end = vaddr + len - 1;
+ if (end < vaddr) return -1;
+ if (end >= X86_KERNEL_VIRT_BASE) return -1;
+
+ uintptr_t start_page = vaddr & ~(uintptr_t)0xFFF;
+ uintptr_t end_page = end & ~(uintptr_t)0xFFF;
+
+ for (uintptr_t va = start_page;; va += 0x1000) {
+ void* phys = pmm_alloc_page_low_16mb();
+ if (!phys) return -1;
+
+ vmm_map_page((uint64_t)(uintptr_t)phys, (uint64_t)va, flags | VMM_FLAG_PRESENT | VMM_FLAG_USER);
+
+ if (va == end_page) break;
+ }
+
+ return 0;
+}
+
+int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uintptr_t* user_stack_top_out) {
+ if (!filename || !entry_out || !user_stack_top_out) return -1;
+ if (!fs_root) return -1;
+
+ fs_node_t* node = fs_root->finddir ? fs_root->finddir(fs_root, (char*)filename) : NULL;
+ if (!node) {
+ uart_print("[ELF] file not found: ");
+ uart_print(filename);
+ uart_print("\n");
+ return -1;
+ }
+
+ uint32_t file_len = node->length;
+ if (file_len < sizeof(elf32_ehdr_t)) return -1;
+
+ uint8_t* file = (uint8_t*)kmalloc(file_len);
+ if (!file) return -1;
+
+ uint32_t rd = vfs_read(node, 0, file_len, file);
+ if (rd != file_len) {
+ kfree(file);
+ return -1;
+ }
+
+ const elf32_ehdr_t* eh = (const elf32_ehdr_t*)file;
+ if (elf32_validate(eh, file_len) < 0) {
+ uart_print("[ELF] invalid ELF header\n");
+ kfree(file);
+ return -1;
+ }
+
+ const elf32_phdr_t* ph = (const elf32_phdr_t*)(file + eh->e_phoff);
+
+ 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);
+ return -1;
+ }
+ if (ph[i].p_vaddr >= X86_KERNEL_VIRT_BASE) {
+ uart_print("[ELF] PT_LOAD in kernel range rejected\n");
+ kfree(file);
+ return -1;
+ }
+
+ uint32_t seg_end = ph[i].p_vaddr + ph[i].p_memsz;
+ if (seg_end < ph[i].p_vaddr) {
+ kfree(file);
+ return -1;
+ }
+ if (seg_end >= X86_KERNEL_VIRT_BASE) {
+ kfree(file);
+ return -1;
+ }
+
+ 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);
+ return -1;
+ }
+
+ const uint32_t map_flags = VMM_FLAG_RW;
+
+ if (elf32_map_user_range((uintptr_t)ph[i].p_vaddr, (size_t)ph[i].p_memsz, map_flags) < 0) {
+ uart_print("[ELF] OOM mapping user segment\n");
+ kfree(file);
+ return -1;
+ }
+
+ 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);
+ }
+ }
+
+ const uintptr_t user_stack_base = 0x00800000U;
+ const size_t user_stack_size = 0x1000;
+
+ if (elf32_map_user_range(user_stack_base, user_stack_size, VMM_FLAG_RW) < 0) {
+ uart_print("[ELF] OOM mapping user stack\n");
+ kfree(file);
+ return -1;
+ }
+
+ *entry_out = (uintptr_t)eh->e_entry;
+ *user_stack_top_out = user_stack_base + user_stack_size;
+
+ kfree(file);
+ return 0;
+}
+#else
+int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uintptr_t* user_stack_top_out) {
+ (void)filename;
+ (void)entry_out;
+ (void)user_stack_top_out;
+ return -1;
+}
+#endif
#include "multiboot2.h"
#include "initrd.h"
#include "fs.h"
+ #include "elf.h"
+ #include "uaccess.h"
#include "kernel/boot_info.h"
#include "hal/cpu.h"
+#if defined(__i386__)
+#include "arch/x86/usermode.h"
+#endif
+
#if defined(__i386__)
extern void x86_usermode_test_start(void);
+static uint8_t ring0_trap_stack[16384] __attribute__((aligned(16)));
+
static int cmdline_has_token(const char* cmdline, const char* token) {
if (!cmdline || !token) return 0;
hal_cpu_enable_interrupts();
-#if defined(__i386__)
- if (bi && cmdline_has_token(bi->cmdline, "ring3")) {
- x86_usermode_test_start();
- }
-#endif
-
// 9. Load InitRD (if available)
if (bi && bi->initrd_start) {
const uintptr_t initrd_virt_base = 0xE0000000U;
uintptr_t initrd_virt = initrd_virt_base + ((uintptr_t)bi->initrd_start - phys_start);
fs_root = initrd_init((uint32_t)initrd_virt);
}
+
+#if defined(__i386__)
+ if (fs_root) {
+ uintptr_t entry = 0;
+ uintptr_t user_sp = 0;
+ if (elf32_load_user_from_initrd("init.elf", &entry, &user_sp) == 0) {
+ uart_print("[ELF] starting init.elf\n");
+
+ uart_print("[ELF] user_range_ok(entry)=");
+ uart_put_char(user_range_ok((const void*)entry, 1) ? '1' : '0');
+ uart_print(" user_range_ok(stack)=");
+ uart_put_char(user_range_ok((const void*)(user_sp - 16), 16) ? '1' : '0');
+ uart_print("\n");
+
+ hal_cpu_set_kernel_stack((uintptr_t)&ring0_trap_stack[sizeof(ring0_trap_stack)]);
+ x86_enter_usermode(entry, user_sp);
+ }
+ }
+
+ if (bi && cmdline_has_token(bi->cmdline, "ring3")) {
+ x86_usermode_test_start();
+ }
+#endif
// Start Shell as the main interaction loop
shell_init();
spin_unlock_irqrestore(&sched_lock, flags);
uart_print("[SCHED] OOM allocating kernel process struct.\n");
for(;;) hal_cpu_idle();
+ __builtin_unreachable();
}
kernel_proc->pid = 0;
}
#endif
- uint64_t kernel_size = phys_end - phys_start;
-
- pmm_mark_region(phys_start, kernel_size, 1); // Mark Used
+ uint64_t phys_start_aligned = align_down(phys_start, PAGE_SIZE);
+ uint64_t phys_end_aligned = align_up(phys_end, PAGE_SIZE);
+ if (phys_end_aligned < phys_start_aligned) {
+ phys_end_aligned = phys_start_aligned;
+ }
+ uint64_t kernel_size = phys_end_aligned - phys_start_aligned;
- // 3. Protect Multiboot info (if x86)
+ pmm_mark_region(phys_start_aligned, kernel_size, 1); // Mark Used
+
+#if defined(__i386__) || defined(__x86_64__)
+ // 3. Protect Multiboot2 modules (e.g. initrd)
+ // The initrd is loaded by GRUB into physical memory. If we don't reserve it,
+ // the PMM may allocate those frames and overwrite the initrd header/data.
if (boot_info) {
+ struct multiboot_tag *tag;
+ for (tag = (struct multiboot_tag *)((uint8_t *)boot_info + 8);
+ tag->type != MULTIBOOT_TAG_TYPE_END;
+ tag = (struct multiboot_tag *)((uint8_t *)tag + ((tag->size + 7) & ~7)))
+ {
+ if (tag->type == MULTIBOOT_TAG_TYPE_MODULE) {
+ struct multiboot_tag_module* mod = (struct multiboot_tag_module*)tag;
+ uint64_t mod_start = (uint64_t)mod->mod_start;
+ uint64_t mod_end = (uint64_t)mod->mod_end;
+ if (mod_end < mod_start) mod_end = mod_start;
+
+ uint64_t mod_start_aligned = align_down(mod_start, PAGE_SIZE);
+ uint64_t mod_end_aligned = align_up(mod_end, PAGE_SIZE);
+ if (mod_end_aligned < mod_start_aligned) {
+ mod_end_aligned = mod_start_aligned;
+ }
+
+ pmm_mark_region(mod_start_aligned, mod_end_aligned - mod_start_aligned, 1);
+ }
+ }
+
+ // 4. Protect Multiboot info (if x86)
uintptr_t bi_ptr = (uintptr_t)boot_info;
if (bi_ptr < 0xC0000000U) {
pmm_mark_region((uint64_t)bi_ptr, 4096, 1); // Protect at least 1 page
}
}
+#endif
uart_print("[PMM] Initialized.\n");
}
// Prepare headers
for(int i = 0; i < nheader; i++) {
printf("Adding: %s\n", argv[i+2]);
- strcpy(headers[i].name, argv[i+2]); // Warning: Buffer overflow unsafe, good enough for tool
+
+ const char* in = argv[i+2];
+ const char* base = strrchr(in, '/');
+ base = base ? (base + 1) : in;
+
+ strcpy(headers[i].name, base); // Warning: Buffer overflow unsafe, good enough for tool
headers[i].offset = data_offset;
headers[i].magic = 0xBF;
- FILE *stream = fopen(argv[i+2], "r");
+ FILE *stream = fopen(argv[i+2], "rb");
if(!stream) {
printf("Error opening file: %s\n", argv[i+2]);
return 1;
fclose(stream);
}
- FILE *wstream = fopen(argv[1], "w");
+ FILE *wstream = fopen(argv[1], "wb");
if(!wstream) {
printf("Error opening output: %s\n", argv[1]);
return 1;
// Write data
for(int i = 0; i < nheader; i++) {
- FILE *stream = fopen(argv[i+2], "r");
+ FILE *stream = fopen(argv[i+2], "rb");
unsigned char *buf = (unsigned char *)malloc(headers[i].length);
fread(buf, 1, headers[i].length, stream);
fwrite(buf, 1, headers[i].length, wstream);
--- /dev/null
+#include <stdint.h>
+
+enum {
+ SYSCALL_WRITE = 1,
+ SYSCALL_EXIT = 2,
+};
+
+static int sys_write(int fd, const void* buf, uint32_t len) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_WRITE), "b"(fd), "c"(buf), "d"(len)
+ : "memory"
+ );
+ return ret;
+}
+
+__attribute__((noreturn)) static void sys_exit(int code) {
+ (void)code;
+ __asm__ volatile(
+ "int $0x80\n"
+ "1: jmp 1b\n"
+ :
+ : "a"(SYSCALL_EXIT)
+ : "memory"
+ );
+ __builtin_unreachable();
+}
+
+void _start(void) {
+ __asm__ volatile(
+ "mov $0x23, %ax\n"
+ "mov %ax, %ds\n"
+ "mov %ax, %es\n"
+ "mov %ax, %fs\n"
+ "mov %ax, %gs\n"
+ );
+
+ static const char msg[] = "[init] hello from init.elf\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ sys_exit(0);
+}
--- /dev/null
+ENTRY(_start)
+
+PHDRS
+{
+ text PT_LOAD FLAGS(7);
+}
+
+SECTIONS
+{
+ . = 0x00400000;
+
+ .text : {
+ *(.text*)
+ } :text
+
+ .rodata : {
+ *(.rodata*)
+ } :text
+
+ .data : {
+ *(.data*)
+ } :text
+
+ .bss : {
+ *(.bss*)
+ *(COMMON)
+ } :text
+}