]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
fix: cmdline parser, VBE framebuffer, VA collision, ring3 test, code audit
authorTulio A M Mendes <[email protected]>
Sat, 14 Feb 2026 04:17:00 +0000 (01:17 -0300)
committerTulio A M Mendes <[email protected]>
Sat, 14 Feb 2026 04:17:00 +0000 (01:17 -0300)
- fix(cmdline): don't skip token 0 when GRUB2+Multiboot2 omits kernel path
  GRUB2 may pass only arguments (e.g. 'ring3') without the kernel path.
  The parser now only skips token 0 if it starts with '/'.

- feat(vbe): add Multiboot2 framebuffer request tag to boot.S
  Requests 1024x768x32 linear framebuffer from GRUB (optional flag=1).
  Add fb_type field to boot_info for detecting framebuffer vs text mode.
  VGA text console conditionally disabled when linear framebuffer active.

- fix(va): hal_mm_map_physical_range used 0xE0000000 (KVA_FRAMEBUFFER)
  This caused the initrd mapping to be destroyed when VBE mapped the
  framebuffer at the same VA. Moved to KVA_PHYS_MAP at 0xDC000000.

- fix(ring3): run ring3 test in own kernel thread instead of PID 0
  x86_usermode_test_start() enters ring3 via iret and never returns.
  Previously hidden because ring3 flag was never recognized (cmdline bug).

- feat(console): wire console= cmdline parameter to console subsystem
  Supports console=serial, console=vga, console=ttyS0, console=tty0.

- refactor: use KVA_FRAMEBUFFER from kernel_va_map.h in vbe.c
- cleanup: replace inline extern rtc_unix_timestamp with #include rtc.h
- fix(multiboot2): remove break after MODULE tag to scan ALL tags

Build: clean. cppcheck: clean. Tests: 20/20 smoke, 47/47 host unit.

include/kernel/boot_info.h
include/kernel_va_map.h
src/arch/x86/arch_early_setup.c
src/arch/x86/arch_platform.c
src/arch/x86/boot.S
src/drivers/vbe.c
src/hal/x86/mm.c
src/kernel/cmdline.c
src/kernel/init.c
src/kernel/syscall.c

index 5b4774439bd8ae376a0f10aad609fb67a6e3227a..3d2d9e2b3443060249061f713389390fbfd7b6ac 100644 (file)
@@ -27,6 +27,7 @@ struct boot_info {
     uint32_t  fb_width;
     uint32_t  fb_height;
     uint8_t   fb_bpp;
+    uint8_t   fb_type;      /* 0=indexed, 1=direct RGB (linear), 2=EGA text */
 };
 
 #endif
index d797b1b1460fa994c490b48335547558aa37ed90..fa952790c64310c099512c50cc3379b18ef2b203 100644 (file)
@@ -33,6 +33,8 @@
  *   0xC0400000                 LAPIC MMIO (1 page)
  *   0xC8000000 ..              Kernel stacks (guard + 8KB per thread)
  *   0xD0000000 ..              Kernel heap (10 MB)
+ *   0xDC000000 ..              Initrd / generic phys mapping (up to 64 MB)
+ *   0xE0000000 ..              Framebuffer mapping (up to 16 MB)
  */
 
 /* IOAPIC (arch/x86/ioapic.c) */
 /* LAPIC (arch/x86/lapic.c) */
 #define KVA_LAPIC           0xC0400000U
 
+/* Initrd / generic physical range mapping (hal/x86/mm.c) */
+#define KVA_PHYS_MAP        0xDC000000U
+
+/* Framebuffer (drivers/vbe.c) — up to 16 MB for large resolutions */
+#define KVA_FRAMEBUFFER     0xE0000000U
+
 #endif
index cd47c152fcbddef4d841639711223f395902abcf..c8d115e6fdc913df6e9dbbae8b8f501d767b0410 100644 (file)
@@ -53,6 +53,7 @@ static uint32_t multiboot_copy_size;
     bi.fb_width = 0;
     bi.fb_height = 0;
     bi.fb_bpp = 0;
+    bi.fb_type = 0;
 
     if (mbi_phys) {
         uint32_t total_size = *(volatile uint32_t*)mbi_phys;
@@ -77,9 +78,10 @@ static uint32_t multiboot_copy_size;
              tag = (struct multiboot_tag*)((uint8_t*)tag + ((tag->size + 7) & ~7))) {
             if (tag->type == MULTIBOOT_TAG_TYPE_MODULE) {
                 const struct multiboot_tag_module* mod = (const struct multiboot_tag_module*)tag;
-                bi.initrd_start = mod->mod_start;
-                bi.initrd_end = mod->mod_end;
-                break;
+                if (!bi.initrd_start) {
+                    bi.initrd_start = mod->mod_start;
+                    bi.initrd_end = mod->mod_end;
+                }
             }
             if (tag->type == MULTIBOOT_TAG_TYPE_CMDLINE) {
                 const struct multiboot_tag_string* s = (const struct multiboot_tag_string*)tag;
@@ -92,6 +94,7 @@ static uint32_t multiboot_copy_size;
                 bi.fb_width = fb->framebuffer_width;
                 bi.fb_height = fb->framebuffer_height;
                 bi.fb_bpp = fb->framebuffer_bpp;
+                bi.fb_type = fb->framebuffer_type;
             }
         }
     }
index 25b1ddec8bda697e6da85698ff42a718556c5308..4e0bbe9dce77e28d5dbab283f92364871f20caeb 100644 (file)
@@ -89,7 +89,7 @@ static void userspace_init_thread(void) {
         }
     }
 
-    kprintf("[ELF] starting /bin/init.elf\n");
+    kprintf("[ELF] starting %s\n", init_path);
 
     kprintf("[ELF] user_range_ok(entry)=%c user_range_ok(stack)=%c\n",
             user_range_ok((const void*)entry, 1) ? '1' : '0',
@@ -109,14 +109,21 @@ static void userspace_init_thread(void) {
 #endif
 
 int arch_platform_setup(const struct boot_info* bi) {
-    (void)bi;
 #if defined(__i386__)
     vmm_init();
 
-    vga_init();
-    vga_set_color(0x0A, 0x00);
-    console_enable_vga(1);
-    kprintf("[AdrOS] Kernel Initialized (VGA).\n");
+    /* Enable VGA text console only if we are NOT in linear framebuffer mode.
+     * When GRUB provides a linear framebuffer (fb_type==1), the VGA text
+     * buffer at 0xB8000 is inactive — serial console carries all output. */
+    if (!bi || bi->fb_type != 1) {
+        vga_init();
+        vga_set_color(0x0A, 0x00);
+        console_enable_vga(1);
+        kprintf("[AdrOS] Kernel Initialized (VGA text mode).\n");
+    } else {
+        kprintf("[AdrOS] Kernel Initialized (framebuffer %ux%ux%u, VGA text disabled).\n",
+                (unsigned)bi->fb_width, (unsigned)bi->fb_height, (unsigned)bi->fb_bpp);
+    }
 
     syscall_init();
 
@@ -176,8 +183,13 @@ int arch_platform_start_userspace(const struct boot_info* bi) {
 #endif
 }
 
+static void ring3_test_thread(void) {
+    x86_usermode_test_start();
+    for (;;) hal_cpu_idle();
+}
+
 void arch_platform_usermode_test_start(void) {
 #if defined(__i386__)
-    x86_usermode_test_start();
+    process_create_kernel(ring3_test_thread);
 #endif
 }
index b779ee885ed9cecbdedb6af77b3766d0fe122e8f..d475d57e4061527eaf416b792dc8f9771011170a 100644 (file)
@@ -29,6 +29,20 @@ multiboot_header_start:
     .long MB_ARCH
     .long multiboot_header_end - multiboot_header_start
     .long 0x100000000 - (MB_MAGIC + MB_ARCH + (multiboot_header_end - multiboot_header_start))
+
+    /* Framebuffer request tag (type 5, optional) */
+    .align 8
+fb_tag_start:
+    .word 5                 /* type: framebuffer */
+    .word 1                 /* flags: optional (boot even if unavailable) */
+    .long fb_tag_end - fb_tag_start
+    .long 1024              /* preferred width */
+    .long 768               /* preferred height */
+    .long 32                /* preferred depth (bpp) */
+fb_tag_end:
+
+    /* End tag */
+    .align 8
     .word 0, 0
     .long 8
 multiboot_header_end:
index c2d2d8b8896557d3acfb727b13cb5086b5e1a3dc..c81886161177b5f13101d5b062b26c6548874516 100644 (file)
@@ -15,6 +15,7 @@
 #include "uaccess.h"
 #include "console.h"
 #include "utils.h"
+#include "kernel_va_map.h"
 
 #include <stddef.h>
 
@@ -36,7 +37,7 @@ int vbe_init(const struct boot_info* bi) {
     g_vbe.size = g_vbe.pitch * g_vbe.height;
 
     uint32_t pages = (g_vbe.size + 0xFFF) >> 12;
-    uintptr_t virt_base = 0xE0000000U;
+    uintptr_t virt_base = KVA_FRAMEBUFFER;
 
     for (uint32_t i = 0; i < pages; i++) {
         vmm_map_page((uint64_t)(g_vbe.phys_addr + i * 0x1000),
index 566bc5b40f9750c76dab57ff43e4863c916ff8f4..c012433f0b81369b54e0b1aabf92d0adaffcd055 100644 (file)
@@ -10,6 +10,7 @@
 #include "hal/mm.h"
 
 #include "vmm.h"
+#include "kernel_va_map.h"
 
 #include <stddef.h>
 
@@ -18,7 +19,7 @@ int hal_mm_map_physical_range(uintptr_t phys_start, uintptr_t phys_end, uint32_t
 
     if (phys_end < phys_start) phys_end = phys_start;
 
-    const uintptr_t virt_base = 0xE0000000U;
+    const uintptr_t virt_base = KVA_PHYS_MAP;
 
     uintptr_t phys_start_aligned = phys_start & ~(uintptr_t)0xFFF;
     uintptr_t phys_end_aligned = (phys_end + 0xFFF) & ~(uintptr_t)0xFFF;
index 1b560df0bf776c755e08dc537a5a239b8da6dcea..da62c6849a1b4da6a21eae4922125d819235c1ae 100644 (file)
@@ -122,11 +122,18 @@ void cmdline_parse(const char* raw) {
         return;
     }
 
-    /* Token 0 is the kernel path — skip it.
-     * Process remaining tokens. */
+    /* Token 0 may be the kernel path (e.g. "/boot/adros-x86.bin") —
+     * skip it only if it looks like a path.  GRUB2 with Multiboot2
+     * does NOT always include the kernel path in the cmdline string;
+     * it may pass only the arguments (e.g. "ring3" or "init=/bin/sh"). */
+    int start_idx = 0;
+    if (ntokens > 0 && tokens[0][0] == '/') {
+        start_idx = 1;
+    }
+
     int after_separator = 0;
 
-    for (int i = 1; i < ntokens; i++) {
+    for (int i = start_idx; i < ntokens; i++) {
         const char* tok = tokens[i];
 
         /* Check for "--" separator */
index 5f835ff4afd33ae3a8e9d0c461e45e27baf7e80b..8756b3bf14eac949f64267c865fe33e4888627d2 100644 (file)
@@ -175,6 +175,22 @@ int init_start(const struct boot_info* bi) {
     /* Parse kernel command line (Linux-like triaging) */
     cmdline_parse(bi ? bi->cmdline : NULL);
 
+    /* Apply console= parameter: serial, vga, or both (default) */
+    const char* con = cmdline_get("console");
+    if (con) {
+        if (strcmp(con, "serial") == 0 || strcmp(con, "ttyS0") == 0) {
+            console_enable_uart(1);
+            console_enable_vga(0);
+            kprintf("[CONSOLE] output: serial only\n");
+        } else if (strcmp(con, "vga") == 0 || strcmp(con, "tty0") == 0) {
+            console_enable_uart(0);
+            console_enable_vga(1);
+            kprintf("[CONSOLE] output: VGA only\n");
+        } else {
+            kprintf("[CONSOLE] unknown console=%s, using both\n", con);
+        }
+    }
+
     if (bi && bi->initrd_start) {
         uintptr_t initrd_virt = 0;
         if (hal_mm_map_physical_range((uintptr_t)bi->initrd_start, (uintptr_t)bi->initrd_end,
index 5f9e865cbd3fa9f041474dc36e54e15fb124ed38..ba535821a20ac1d1981bbfac949669c38740a8a9 100644 (file)
@@ -36,6 +36,7 @@
 #include "arch_signal.h"
 #include "arch_syscall.h"
 #include "arch_process.h"
+#include "rtc.h"
 
 #include <stddef.h>
 
@@ -1740,7 +1741,6 @@ static int syscall_clock_gettime_impl(uint32_t clk_id, struct timespec* user_tp)
 
     struct timespec tp;
     if (clk_id == CLOCK_REALTIME) {
-        extern uint32_t rtc_unix_timestamp(void);
         tp.tv_sec = rtc_unix_timestamp();
         tp.tv_nsec = 0;
     } else {