]> 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 5696d6b6fc741417c7b47eb0fe3af3921f52fd94..e61c26b0ea09b7ab9641b8450346d40bd08ce6d6 100644 (file)
@@ -18,6 +18,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 3360b99141c80125a86175a90a0418040a25ba86..b5c3505cd666b357dff7293d6725e28cba3d8111 100644 (file)
@@ -24,6 +24,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 e7096470ff9108843be2775712b6813fcc84bf84..371e84d9ede692e25dfd985e29872cef2bb42b58 100644 (file)
@@ -44,6 +44,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;
@@ -68,9 +69,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;
@@ -83,6 +85,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 5ef8248dd78734360e2c7f95829862743511a8e3..e73d9fa811c901fb4c48c4f10484f0e4f92d5006 100644 (file)
@@ -80,7 +80,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',
@@ -100,14 +100,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();
 
@@ -167,8 +174,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 341d4d481469b663752537ef2bfb80bcd2f6966a..a3a064caa3bdc55b1885b3a41de84dc2c06243b4 100644 (file)
@@ -20,6 +20,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 4c4ebf7b686279dafaec5f60adf97a4df2c93fdf..55cace12a8ca14d67ba5c6e4293f50b50162122c 100644 (file)
@@ -6,6 +6,7 @@
 #include "uaccess.h"
 #include "console.h"
 #include "utils.h"
+#include "kernel_va_map.h"
 
 #include <stddef.h>
 
@@ -27,7 +28,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 158a8ed334b8689f78f98805328c8462371bf471..c927adc605d1334d1b776c0a8ec893da00a2813a 100644 (file)
@@ -1,6 +1,7 @@
 #include "hal/mm.h"
 
 #include "vmm.h"
+#include "kernel_va_map.h"
 
 #include <stddef.h>
 
@@ -9,7 +10,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 43469aebacb474980fc94ed4d57936b9b2d55a34..a1029e1846f0ec2d8c854a36c4bf452501aa023a 100644 (file)
@@ -113,11 +113,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 d86ab4849efe77662e576810cebce27e534592da..39dffb433e35a8ed0b26f0b3ec280f21768ddc12 100644 (file)
@@ -166,6 +166,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 25ad2843a8cb471582891ac6a655d1f9f5dae54f..c7b5738495f8a89b056ca89fd451b04171a92708 100644 (file)
@@ -27,6 +27,7 @@
 #include "arch_signal.h"
 #include "arch_syscall.h"
 #include "arch_process.h"
+#include "rtc.h"
 
 #include <stddef.h>
 
@@ -1731,7 +1732,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 {