Creating a Kernel Profile

Step-by-step guide to creating your own custom Helix kernel profile — from directory setup to first boot.

Documentation

Introduction

Helix is built around profiles — self-contained kernel configurations that define exactly which subsystems, modules, and features your OS includes. Creating a profile is the primary way to build a custom Helix-based operating system tailored to your target platform.

A profile is a Cargo crate inside the profiles/ directory. It contains:

  • A helix.toml — your kernel configuration manifest
  • A linker.ld — the linker script for your target boot protocol
  • A src/main.rs — the kernel entry point
  • Optional additional sources for boot-time setup, framebuffer init, etc.

Today only profiles/minimal/ is a fully runnable profile. The other directories (limine/, uefi/, common/) provide linker scripts only.


Directory Structure

Create a new directory under profiles/ with the following layout:

terminal
bash
1
mkdir -p profiles/my-kernel/src
2
touch profiles/my-kernel/helix.toml
3
touch profiles/my-kernel/linker.ld
4
touch profiles/my-kernel/Cargo.toml
5
touch profiles/my-kernel/src/main.rs

Your profile directory should look like this:

profiles/my-kernel/
├── Cargo.toml        # Crate manifest (depends on helix-core, helix-hal, etc.)
├── helix.toml        # Kernel configuration
├── linker.ld         # Linker script for your boot protocol
└── src/
    └── main.rs       # Kernel entry point (_start / kernel_main)

helix.toml Configuration

The helix.toml file defines every aspect of your kernel: target architecture, enabled features, memory layout, scheduler, console, module selection, boot sequence, and build options.

Here is a minimal example:

profiles/my-kernel/helix.toml
toml
1
[profile]
2
name = "my-kernel"
3
version = "1.0.0"
4
description = "My custom Helix kernel"
5
target = "embedded"
6
7
[profile.arch]
8
primary = "x86_64"
9
supported = ["x86_64"]
10
11
[profile.features]
12
multicore = false
13
hot_reload = false
14
userspace = false
15
networking = false
16
filesystem = false
17
graphics = false
18
19
[memory]
20
min_ram_mb = 4
21
max_ram_mb = 64
22
heap_size_kb = 256
23
stack_size_kb = 16
24
virtual_memory = false
25
26
[scheduler]
27
module = "round_robin"
28
time_slice_ms = 10
29
priority_levels = 8
30
load_balancing = false
31
32
[console]
33
backend = "serial"
34
baud_rate = 115200
35
early_console = true
36
37
[modules]
38
static = ["helix-scheduler-round-robin"]
39
dynamic = []
40
41
[boot]
42
entry = "kernel_main"
43
early_init = ["console", "memory", "interrupts"]
44
late_init = ["scheduler"]
45
cmdline = "quiet loglevel=3"
46
47
[debug]
48
level = "minimal"
49
symbols = false
50
stack_traces = true
51
panic_behavior = "halt"
52
53
[build]
54
opt_level = "s"
55
lto = true
56
panic = "abort"
57
strip = true

For the full configuration schema and every available option, see the helix.toml Reference page.


Linker Script

Every profile needs a linker script that tells the linker how to lay out the kernel binary in memory. The script depends on your boot protocol:

  • Multiboot2 — flat physical layout at 1 MB
  • Limine — PIE binary with HHDM (Higher-Half Direct Mapping)
  • UEFI — PIE binary with RELRO

Here's a minimal Multiboot2 linker script to get started:

profiles/my-kernel/linker.ld
ld
1
ENTRY(_start)
2
3
SECTIONS
4
{
5
. = 1M;
6
7
.multiboot_header : ALIGN(8) {
8
KEEP(*(.multiboot_header))
9
}
10
11
.boot : ALIGN(4K) { *(.boot) }
12
.text : ALIGN(4K) { *(.text .text.*) }
13
.rodata : ALIGN(4K) { *(.rodata .rodata.*) }
14
.data : ALIGN(4K) { *(.data .data.*) }
15
16
.bss : ALIGN(4K) {
17
__bss_start = .;
18
*(.bss .bss.*) *(COMMON)
19
__bss_end = .;
20
}
21
22
_kernel_end = .;
23
}

For PIE/KASLR-ready layouts and architecture-specific scripts, see the Linker Scripts page.


Kernel Entry Point

Your src/main.rs is the kernel entry point. It must be a #![no_std] + #![no_main] crate with an _start or kernel_main function (matching your helix.toml boot.entry).

profiles/my-kernel/Cargo.toml
toml
1
[package]
2
name = "my-kernel"
3
version = "0.1.0"
4
edition = "2021"
5
6
[dependencies]
7
helix-core = { path = "../../core" }
8
helix-hal = { path = "../../hal" }
profiles/my-kernel/src/main.rs
rust
1
#![no_std]
2
#![no_main]
3
4
use core::panic::PanicInfo;
5
6
/// Kernel entry point — called by the bootloader after setup.
7
#[no_mangle]
8
pub extern "C" fn kernel_main() -> ! {
9
// Initialize the serial console for debug output
10
// helix_core::console::init();
11
12
// Initialize memory subsystem
13
// helix_core::memory::init();
14
15
// Initialize interrupts
16
// helix_core::interrupts::init();
17
18
// Your kernel logic here...
19
20
loop {
21
// Halt the CPU until the next interrupt
22
unsafe { core::arch::asm!("hlt") };
23
}
24
}
25
26
#[panic_handler]
2 refs
fn panic(_info: &PanicInfo) -> ! {
28
loop {
29
unsafe { core::arch::asm!("hlt") };
30
}
31
}
Index

Workspace Setup

After creating your profile, register it in the root Cargo.toml workspace:

Cargo.toml (workspace root)
toml
1
[workspace]
2
resolver = "2"
3
4
members = [
5
# ... existing members ...
6
7
# Your new profile
8
"profiles/my-kernel",
9
]

Make sure your Cargo.toml specifies the correct target and linker:

profiles/my-kernel/.cargo/config.toml
toml
1
[build]
2
target = "x86_64-unknown-none"
3
4
[target.x86_64-unknown-none]
5
rustflags = ["-C", "link-arg=-T../../profiles/my-kernel/linker.ld"]

Build & Test

Once your profile is set up, use the standard Helix build pipeline:

terminal
bash
1
# Build the kernel (12-step pipeline)
2
./scripts/build.sh
3
4
# Build with debug symbols
5
./scripts/build.sh --debug
6
7
# Run in QEMU
8
./scripts/run_qemu.sh
9
10
# Run with GDB debug server
11
./scripts/run_qemu.sh --debug
12
13
# Run all tests
14
./scripts/test.sh

Checklist

Before submitting your profile:

StepCommandExpected Result
1. Build./scripts/build.shNo errors, ELF produced
2. Boot./scripts/run_qemu.shKernel reaches kernel_main
3. Formatcargo fmt --allNo formatting changes
4. Lintcargo clippy --all-targetsNo warnings
5. Testcargo test --target x86_64-unknown-linux-gnuAll tests pass

Tip: Use the profiles/minimal/ profile as a reference implementation. It's the most complete and well-tested profile in the repository.