OpenComp is built around a component-based architecture that allows features to be added modularly without modifying the core kernel.
- GRUB Loads Kernel - Multiboot2 bootloader loads
tinykernel.elfat0x100000 - Assembly Entry (
start.S) - Sets up stack at0x7C00and callskernel_main() - Kernel Initialization (
kernel.c) - Initializes VGA and scans for components - Component Registration - Calls
init()on each component in.compssection - Main Loop - Repeatedly calls
tick()on all components
0x000000 - 0x0FFFFF : Reserved (BIOS, VGA, etc.)
0x100000 - 0x1FFFFF : Kernel code and data
0x200000 - 0x1FFFFF : Managed physical memory (16MB)
The memory component (memory.c) implements a simple bitmap allocator:
- Page Size: 4096 bytes (4KB)
- Total Pages: 4096 (16MB total)
- Bitmap: 512 bytes tracking page allocation
- Functions:
kalloc_page()- Allocate one 4KB pagekfree_page()- Free a pageget_free_pages()- Query available pages
struct component {
const char *name; // Component identifier
void (*init)(void); // Called once at boot
void (*tick)(void); // Called repeatedly in main loop
};The linker script defines special sections:
.compobjs- Contains actual component structures.comps- Contains pointers to components
The linker provides symbols:
__start_comps- Start of component pointer array__stop_comps- End of component pointer array
- Define init and tick functions
- Create component structure in
.compobjssection - Create pointer to component in
.compssection - Add to Makefile
Example:
static void my_init(void) { /* ... */ }
static void my_tick(void) { /* ... */ }
__attribute__((section(".compobjs")))
static struct component my_comp = {
.name = "my_component",
.init = my_init,
.tick = my_tick
};
__attribute__((section(".comps")))
struct component *p_my_comp = &my_comp;- Resolution: 80x25 characters
- Memory:
0xB8000-0xB8FA0 - Format: 16-bit values (8-bit character + 8-bit color)
Bits 0-3: Foreground color
Bits 4-6: Background color
Bit 7: Blink
vga_putchar(char c)- Write character at cursorvga_putchar_at(x, y, c, color)- Write at specific positionvga_clear(color)- Clear screen with color
The desktop component manages up to 8 windows:
typedef struct {
int active; // Window enabled
int x, y; // Position
int width, height; // Dimensions
char title[32]; // Window title
char content[256]; // Window content
uint8_t color; // Border/text color
} Window;Commands are entered at the bottom command line (CMD> prompt):
- User types command
- Keyboard component captures input
- Desktop component buffers keystrokes
- Enter key triggers
handle_command() - Command parsed and executed
- New window created to display results
tick() called every ~100ms
↓
Check if redraw needed (every 10 ticks)
↓
Clear screen (blue background)
↓
Draw title bar
↓
For each active window:
- Draw border
- Draw title
- Draw content
↓
Draw command line and cursor
- Data Port:
0x60 - Status Port:
0x64
The keyboard driver converts scancodes to ASCII:
- Read status port (
0x64) - check if data available - Read data port (
0x60) - get scancode - Ignore release codes (bit 7 set)
- Look up ASCII character in translation table
- Add to ring buffer
- Size: 64 characters
- Read Pointer: Next character to read
- Write Pointer: Next position to write
Functions:
keyboard_has_key()- Check if keys availablekeyboard_get_key()- Read and remove one key
-O2 : Optimize for speed
-ffreestanding : Freestanding environment (no standard library)
-nostdlib : Don't link standard library
-fno-builtin : Don't use built-in functions
-mno-red-zone : Required for kernel code (x86-64)
- Compile each
.cfile to.oobject - Link objects using
linker.ldscript - Produces
tinykernel.elfexecutable - Copy to ISO directory structure
- Create bootable ISO with
grub-mkrescue
To support virtual memory:
- Set up page tables in
memory.c - Enable paging in
start.S - Map kernel to higher half (
0xFFFFFFFF80000000) - Update linker script addresses
To add interrupt support:
- Create IDT (Interrupt Descriptor Table)
- Write interrupt handlers in
start.S - Register handlers for keyboard, timer, etc.
- Enable interrupts with
stiinstruction
To add multitasking:
- Create process/task structures
- Implement context switching
- Add scheduler component
- Create system call interface
To add filesystem:
- Create initrd (initial ramdisk)
- Parse filesystem format (tar, ext2, custom)
- Add VFS (Virtual File System) layer
- Create file operations component
- Polling-based I/O: Components poll in tight loop (inefficient)
- No Interrupts: Can't respond to hardware events asynchronously
- Single-threaded: One component blocks all others
- Fixed Tick Rate: Hardcoded delay in main loop
- Add Timer Interrupts: More precise timing control
- Event Queue: Components register for specific events
- Priorities: Give important components more CPU time
- Preemption: Allow high-priority tasks to interrupt others
# Run with serial output
qemu-system-x86_64 -cdrom opencomp.iso -serial stdio
# Run with GDB support
qemu-system-x86_64 -cdrom opencomp.iso -s -S
# In another terminal:
gdb tinykernel.elf
(gdb) target remote localhost:1234
(gdb) continueAdd serial port output for debugging:
static inline void outb(uint16_t port, uint8_t val) {
__asm__ volatile("outb %0, %1" : : "a"(val), "Nd"(port));
}
void debug_print(const char *s) {
while (*s) {
outb(0x3F8, *s++); // COM1 serial port
}
}