Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/pastaboy12345/SilverOS/llms.txt

Use this file to discover all available pages before exploring further.

SilverOS uses the first COM port (COM1) as its primary debug output channel. The serial driver is initialized early in the boot sequence — before the GDT is reloaded, before the IDT is set up, and before the framebuffer is available — so any log message emitted by any subsystem can be captured from the very first instruction the kernel runs. All structured boot messages, driver initialization confirmations, and runtime diagnostics flow through this channel using a consistent [TAG] message convention.

Serial API

The full serial interface is declared in serial.h:
#define COM1 0x3F8

void serial_init(void);                        /* init COM1 at 115200 baud, 8N1 */
void serial_putc(char c);                      /* write single character */
void serial_puts(const char *str);             /* write null-terminated string */
void serial_printf(const char *fmt, ...);      /* printf-style formatted output */
FunctionDescription
serial_initPrograms the COM1 UART at I/O base 0x3F8 for 115200 baud, 8 data bits, no parity, 1 stop bit (8N1). Sets the DLAB bit to write the divisor, then clears DLAB and configures the FIFO control and modem control registers.
serial_putcPolls the line status register (offset +5 from the base port) until the transmit-holding-register-empty bit is set, then writes the character. Converts \n to \r\n automatically so output renders correctly in terminal emulators.
serial_putsCalls serial_putc for each character in the null-terminated string.
serial_printfFormats a message into a stack buffer using vsnprintf, then calls serial_puts. Accepts the same format specifiers as standard printf (%d, %u, %x, %s, %c, etc.).
The divisor for 115200 baud is calculated as 115200 / 115200 = 1, written as a 16-bit value to the divisor latch registers (0x3F8 low byte, 0x3F9 high byte) while the DLAB bit in the line control register is set.

How to view serial output

QEMU maps COM1 to the host’s standard I/O streams when launched with the -serial stdio flag. All characters written by the kernel appear directly in the terminal running QEMU.
qemu-system-x86_64 \
    -cdrom build/silveros.iso \
    -hda build/disk.svd \
    -m 256M \
    -serial stdio \
    -vga std \
    -nic user,model=rtl8139
With this invocation, the QEMU graphical window shows the SilverOS desktop while the host terminal simultaneously streams the full serial log. You can pipe or redirect the terminal output to a file for post-run analysis:
qemu-system-x86_64 [flags] -serial stdio 2>&1 | tee boot.log
Serial output is the most reliable debug channel in SilverOS. It works even before fb_init is called, continues working if the framebuffer driver crashes, and remains readable through triple faults and kernel panics that corrupt the display. Always instrument new drivers with serial_printf calls before adding framebuffer-based output.

Boot log format

Every subsystem prefixes its serial messages with a short uppercase tag in square brackets. This makes it easy to grep the log for a specific component and to spot initialization order problems at a glance. A typical boot sequence looks like this:
=================================
  SilverOS v0.1.0 — Booting...
=================================

[BOOT] Multiboot2 magic OK: 0x36D76289
[BOOT] Multiboot2 info at: 0x...
[FB]   Initialized: 1024x768, 32 bpp
[INIT] GDT...OK
[INIT] PIC...OK
[INIT] IDT...OK
[PMM]  Initialized: N pages free
[HEAP] Initialized at 0x1000000, size=4096 KB
[KEYBOARD] PS/2 keyboard initialized
[SilverFS] Mounted SVD Volume 'SilverOS'
[NET]  Network Stack Initialized. IP: 10.0.2.15
Each driver calls serial_printf with its own tag inside its _init function. The tags used by the built-in drivers are:
TagSubsystem
[BOOT]Early kernel entry, Multiboot2 validation
[FB]Framebuffer driver (fb_init)
[INIT]GDT, PIC, IDT initialization
[PMM]Physical memory manager
[HEAP]Kernel heap allocator
[KEYBOARD]PS/2 keyboard driver
[SilverFS]Filesystem mount
[NET]Network stack

Kernel printf

kprintf, declared in console.h, is the everyday logging function once the framebuffer is available. Internally it calls both console_puts (which writes to the framebuffer console and calls fb_swap_buffers()) and serial_puts, so a single kprintf call produces output in both places simultaneously:
kprintf("[INIT] Memory: %u MB\n", (uint32_t)(total_mem / (1024 * 1024)));
serial_printf is the lower-level alternative used before fb_init has been called or in contexts where writing to the framebuffer would be unsafe (such as inside an exception handler). The rule of thumb is:
  • Use serial_printf in drivers that initialize before the framebuffer, in interrupt handlers, and in panic paths.
  • Use kprintf everywhere else — it routes to serial automatically, so you never lose the message.

Build docs developers (and LLMs) love