See also: Home, Build Guide, Roadmap
Contributing
Thank you for your interest in contributing to Valecium OS. Please read this guide fully before opening a pull request or submitting a patch.
Setting Up and Building
Building is covered in detail in Building Valecium OS. The build system uses SCons. Key files are:
-
SConstruct— Top-level build configuration (version 0.29) -
kernel/SConscript— Kernel build configuration -
scripts/scons/arch.py— Architecture detection (i686, x86_64, aarch64) -
scripts/base/toolchain.py— Cross-compiler builder (binutils 2.45 + musl 1.2.6 + GCC 15.2.0)
Common build commands:
scons # Build everything (debug)
scons BuildConfig=release # Release build
scons BuildArch=i686 BuildConfig=debug # Specific architecture
scons BuildType=kernel # Kernel only
scons BuildType=bootloader # Bootloader only
Testing and Debugging
GDB
scons debug
This launches QEMU with -s -S (waits for GDB on TCP port 1234) and opens GDB with kernel symbols pre-loaded.
Self-Tests
Several subsystems run self-tests on initialisation:
void Crypto_SelfTest(void); // MD5 and SHA1 verification
void VFS_SelfTest(void); // Virtual filesystem operations
void Process_SelfTest(void); // Process creation and scheduling
void Heap_SelfTest(void); // Kernel heap allocator
Git Workflow
This project uses Git. Requirements for contributions:
Commits: The message must describe the change meaningfully. Placeholders like update are not acceptable.
Pull requests: Always branch off a feature branch, never your fork’s master. Keep one PR per logical change. Describe what changed and why.
Patches: Send via email, generated from committed changes with git send-email (not a raw diff). Include a brief explanation in the email body.
Coding Style
Licensing Headers
Every source file must begin with an SPDX identifier:
// SPDX-License-Identifier: GPL-3.0-only // kernel C/assembly files
# SPDX-License-Identifier: BSD-3-Clause // build scripts
// SPDX-License-Identifier: CC-BY-SA-4.0 // documentation
Indentation and Formatting
-
3 spaces per indent level. No tabs.
-
Maximum line length: 80 characters (enforced by the formatter).
-
Spaces around binary operators:
a + b,x = y. -
Space after control-flow keywords:
if (x),for (;;),while (c).
Naming Conventions
Functions
Global (non-static) functions use PascalCase with a subsystem prefix:
void CPU_Initialize(void);
void Keyboard_HandleScancode(uint8_t scancode);
Static (file-local) functions use snake_case:
static void keyboard_buffer_push(char c);
static inline void mark_dirty(TTY_Device *tty, int row);
Variables
Variable naming depends on scope — this distinction is mandatory:
| Scope | Convention | Example |
|---|---|---|
Global (extern or file-scope non-static) |
|
|
File-scope static |
|
|
Local (function-scope) |
|
|
Struct members — values and nested structs |
|
|
Struct members — function pointers |
|
|
Function pointer members follow the global function convention because they behave as callable operations. A struct mixing both kinds looks like:
typedef struct
{
// value and nested-struct members: snake_case
uint32_t base_addr;
uint32_t size;
FS_File root_file;
// function pointer members that have the structure as their parent subsystem: Name
int (*Open)(const char *path, int flags);
void (*Close)(int fd);
// function pointers that does not relate to the subsystem the structure is at: Subsystem_Name
int (*INODE_GetName)(int inode);
} FS_Operations;
Macros and Constants
All macros and compile-time constants use UPPER_SNAKE_CASE with at most three underscore-separated words excluding suffixes:
#define PAGE_SIZE 4096
#define MAX_OPEN_FILES 8
#define SECTOR_SIZE_ISO 2048
#define PAGE_ALIGN_UP(v) (((v) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))
#define PAGE_ALIGN_DOWN(v) ((v) & ~(PAGE_SIZE - 1))
Types and Structs
Struct typedefs use PascalCase prefixed by the subsystem if that structure is global:
typedef struct { ... } FS_File;
typedef struct { ... } MBR_PartitionEntry;
typedef struct { ... } GDTEntry;
Private structures defined in C files should only have the Name in PascalCase with no subsystem declaration:
typedef struct { ... } CoreFsOperations;
typedef struct { ... } MbiTagFramebuffer;
Enums follow the same pattern, with members prefixed by the enum name:
typedef enum
{
GDT_ACCESS_CODE_READABLE = 0x02,
GDT_ACCESS_RING0 = 0x00,
GDT_ACCESS_PRESENT = 0x80,
} GDT_ACCESS;
Comments
Keep comments short and purposeful. A comment should explain why something is done, not restate what the code already says. Avoid large decorative comment blocks.
Single-line C comments use //:
// Initialize the keyboard subsystem
void Keyboard_Initialize(void);
Multi-line documentation should be short and simple, keep it at 1 line after the function prototype.
void Keyboard_HandleScancode(uint8_t scancode); // Generic keyboard handler
Inline clarifications use /* */ sparingly:
/* Cap at 32-bit max to prevent wrap-around */
if (desired_end > 0xFFFFFFFFu)
heap_end = 0xFFFFFFFFu;
Assembly (.S files) uses #:
pushl %ebp # save old call frame
movl %esp, %ebp # initialize new call frame
Avoid:
-
Multi-line banners or box-drawing comments
-
Redundant comments that repeat the function name or variable type
-
Comments that become stale and contradict the code
Brace Style
Opening braces go on the next line (Allman style):
void CPU_Initialize(void)
{
Process_SelfTest();
}
if (x < y)
{
do_something();
}
else
{
do_other();
}
Source File Layout
Files must follow this top-down order:
-
License identifier and
#includedirectives -
Forward declarations (struct tags and
staticfunction prototypes) -
Macros (
#defineconstants, at most 3 underscore-separated words per name) -
Struct and typedef definitions
-
Global and static variable definitions
-
External declarations
-
Private (static) helper functions
-
Public API functions
// 1. License & includes
// SPDX-License-Identifier: GPL-3.0-only
#include <stdbool.h>
#include <stdint.h>
#include <cpu/cpu.h>
// 2. Forward declarations
struct FS_File;
static int resolve_path(const char *path, uint32_t *out_lba);
// 3. Macros
#define MAX_OPEN_FILES 8
// 4. Structs
typedef struct { int used; uint64_t start_lba; uint32_t size; uint32_t pos; } FS_File;
// 5. Variables
static uint32_t s_BootDrive = 0;
// 6. External declarations
extern int DISK_ReadLBA(uint8_t drive, uint64_t lba, uint16_t count, void *buf);
// 7. Private functions
static int resolve_path(const char *path, uint32_t *out_lba) { /* ... */ }
// 8. Public functions
int FS_Open(const char *path) { /* ... */ }
Include Order
System headers first, then project headers, separated by a blank line. Within each group, headers are sorted alphabetically (enforced by the formatter):
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <cpu/cpu.h>
#include <mem/mm_kernel.h>
#include <std/stdio.h>
Inline Functions
Use static inline for simple, frequently-called operations:
static inline void invalidate_tlb_entry(uint32_t vaddr)
{
__asm__ volatile("invlpg (%0)" ::"r"(vaddr) : "memory");
}
static inline FAT_Instance *fat_inst(const Partition *disk)
{
return (FAT_Instance *)disk->fs_instance;
}
Pointer Style
Pointer declarators attach to the variable name:
void *ptr;
char *str;
int *array;
Always check pointers before dereferencing:
if (!page_dir)
{
logfmt(LOG_ERROR, "[MEM] no kernel page directory!\n");
return;
}
Error Handling
Follow Unix conventions: return 0 on success, -ERRNO on failure (list of errnos can be found at /include/constants.h). Use NULL for pointer-returning functions that fail:
int Mem_Brk(void *addr); // 0 on success, -EINVAL on failure
void *Mem_Sbrk(intptr_t inc); // previous break, or NULL on failure
int SYS_Open(const char *path, int flags)
{
if (!path) return -EINVAL;
// ...
}
void *DL_Open(const char *path)
{
if (!path) return NULL;
// ...
}
Log errors and events with logfmt() in kernel:
logfmt(LOG_INFO, "[MEM] start=0x%08x end=0x%08x size=%u MB\n",
(uint32_t)heap_start, (uint32_t)heap_end, size_mb);
logfmt(LOG_ERROR, "[VFS] Mount point '%s' must start with '/'", location);
Header Guards
Prefer #pragma once:
#pragma once
#define min(a, b) ((a) < (b) ? (a) : (b))
Traditional guards are acceptable for compatibility:
#ifndef SCHEDULER_H
#define SCHEDULER_H
// ...
#endif
Low-Level Structs and Macros
Use attributepacked for hardware-mapped structures:
typedef struct
{
uint16_t limit_low;
uint16_t base_low;
uint8_t base_mid;
uint8_t access;
uint8_t flags_limit_hi;
uint8_t base_high;
} __attribute__((packed)) GDT_Entry;
Parametric macros for structure construction:
#define GDT_ENTRY(base, limit, access, flags) \
{ \
GDT_LIMIT_LOW(limit), \
GDT_BASE_LOW(base), \
GDT_BASE_MID(base), \
access, \
GDT_FLAGS_LIMIT_HI(limit, flags), \
GDT_BASE_HIGH(base) \
}
Hardware Abstraction Layer (HAL)
Valecium targets multiple architectures. Never put x86-specific assembly in platform-independent code. All architecture-specific operations go through the HAL.
Using HAL Operations
// Correct: platform-independent
uintptr_t ebp = g_HalStackOperations->Stack_GetEBP();
g_HalPagingOperations->Paging_FlushTlb();
g_HalIoOperations->IO_Panic();
// Wrong: never call arch functions directly from kernel code
// i686_Stack_GetEBP();
Architecture Directory Layout
Architecture implementations live under kernel/arch/<arch>/:
-
boot/— Entry point, multiboot parsing -
cpu/— GDT, IDT, ISR, IRQ, TSS, PIT, PIC, scheduler, usermode -
drivers/— PS/2, serial -
io/— Port I/O and interrupt control -
mem/— Paging, TLB, stack, memory layout -
signal/— Signal frame, sigreturn -
syscall/—int 0x80handler -
video/— VGA text mode
Assembly File Rules
Assembly implementations must be placed in .S files (GNU assembler with C preprocessor) inside the appropriate kernel/arch/<arch>/ subdirectory, exported via .global:
# kernel/arch/i686/cpu/gdt_asm.S
# SPDX-License-Identifier: GPL-3.0-only
.code32
# void i686_GDT_Load(GDTDescriptor *descriptor, ...)
.global i686_GDT_Load
i686_GDT_Load:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
lgdt (%eax)
popl %ebp
ret
HAL Mapping Pattern
Architecture functions are mapped in headers with #if defined(ARCH):
// kernel/hal/stack.h
#if defined(I686)
#include <arch/i686/cpu/stack.h>
#define HAL_ARCH_Stack_GetEBP i686_Stack_GetEBP
#define HAL_ARCH_Stack_GetESP i686_Stack_GetESP
#else
#error "Unsupported architecture for HAL Stack"
#endif
Then wired up in hal.c:
const HAL_StackOperations *g_HalStackOperations = &(HAL_StackOperations){
.Stack_GetEBP = HAL_ARCH_Stack_GetEBP,
.Stack_GetESP = HAL_ARCH_Stack_GetESP,
};
Kernel code only ever touches g_Hal* — never arch-specific names.
Subsystems
Crypto (kernel/crypto/)
MD5 and SHA1 implementations, each with Init, Update, Final, Calculate, ToHex, and SelfTest functions. Self-tests run during kernel initialisation to verify correctness.
Kernel Module System (kernel/sys/kmod/)
Supports dynamic loading of kernel modules (kmod):
-
Loading from disk or memory image
-
Symbol resolution via a global symbol table
-
Dependency tracking
-
PLT/GOT relocation patching
-
Up to 16 modules, 256 symbols per module, 1024 global symbols
Virtual Memory Layout (i686)
Defined in kernel/arch/i686/mem/vm_layout.h:
| Region | Address |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Documentation Standards
All documentation uses AsciiDoc (.ad). Files must include:
= Document Title
:toc: left
:icons: font
:sectlinks:
Ensure compatibility with pandoc conversion to PDF, HTML, and man pages.
Revision History
Every documentation file must include a Revision History section at the end. Use semantic versioning: major for significant reorganisation, minor for clarifications or new sections, patch for typo fixes (batch into next minor).
== Revision History
=== v1.0
Initial documentation.
=== v1.1
Clarified X, added examples for Y.
Revision History
v3.2
Added struct member naming exception: function pointer members use Subsystem_Name (same as global functions); plain value and nested-struct members keep snake_case. Added illustrative mixed-member struct example. Updated HAL examples accordingly.
v3.1
Fixed all code examples to comply with stated conventions: struct members corrected to snake_case (GDTEntry fields, HAL function pointer members); global functions corrected to PascalCase subsystem prefix (Mem_Brk, Mem_Sbrk, Sys_Open, Dl_Open); assembly comment style corrected to #; column limit updated to 80; include examples sorted alphabetically per SortIncludes: true; HAL macro names updated to match corrected member names.
v3.0
Complete reorganisation: unified structure, expanded and clarified coding style (scope-based variable naming, comment rules, file layout order, naming convention table), removed redundancy, converted tables to AsciiDoc format.
v2.0
Updated for v0.28: added crypto subsystem, kernel module system, virtual memory layout, expanded architecture section, updated debugging with self-test details.
v1.3
Added Git version control section.
v1.2
Added structures for other components including welcome message and Git version controls.
v1.1
Clarified HAL assembly strategy for multi-architecture support, documented 3-space indentation.
v1.0
Documented initial coding style and conventions.