Skip to main content

Overview

Portix OS uses a Python-based build system (build.py) that orchestrates the entire compilation pipeline from assembly bootloader to Rust kernel. The build system supports multiple output formats and boot modes.

Build System Architecture

The build process consists of several stages:
1

Assembly compilation

Compile the bootloader stages with NASM:
  • boot.asmboot.bin (Stage 1, 512 bytes)
  • stage2.asmstage2.bin (Stage 2, 64 sectors)
  • isr.asmisr.o (Interrupt service routines)
2

Rust kernel compilation

Build the kernel with cargo:
cargo build --release --target x86_64-portix.json
The kernel is compiled with no_std, panic=abort, and custom linker script.
3

Binary extraction

Extract raw binary from ELF:
objcopy -O binary kernel.elf kernel.bin
4

Disk image creation

Combine bootloader and kernel into disk image:
  • Write boot sector (LBA 0)
  • Write stage2 (LBA 1-64)
  • Write kernel (LBA 65+)
  • Update stage2 with kernel LBA
5

Format conversion

Generate distribution formats:
  • ISO image (CD-ROM bootable)
  • VDI image (VirtualBox)
  • VMDK image (VMware)

Build Script Options

The build.py script accepts several command-line arguments:
OptionDescriptionDefault
--mode=MODEOutput format: raw, iso, vdi, vmdk, ventoy-simraw
--runBuild and run in QEMUNot set
--size=MBDisk image size in MB8 MB
--debugEnable debug symbols and loggingNot set
--cleanClean build artifacts before buildingNot set
--no-serialDisable serial port loggingNot set

Build Modes

python scripts/build.py --mode=raw
Creates build/portix.img - a raw disk image that can be:
  • Written to USB with dd
  • Booted in QEMU with -drive format=raw,file=portix.img
  • Used with Rufus or Etcher

Build Process Details

Assembly Compilation

The bootloader is written in x86 assembly and compiled with NASM:
nasm -f bin boot/boot.asm -o build/boot.bin
nasm -f bin boot/stage2.asm -o build/stage2.bin
nasm -f elf64 kernel/src/arch/isr.asm -o build/isr.o
The boot sector must be exactly 512 bytes and end with the signature 0x55AA. Stage 2 is limited to 64 sectors (32 KB) to fit before the kernel.

Kernel Compilation

The kernel uses a custom target specification (x86_64-portix.json):
{
  "llvm-target": "x86_64-unknown-none-elf",
  "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128",
  "arch": "x86_64",
  "os": "none",
  "panic-strategy": "abort",
  "disable-redzone": true,
  "features": "-mmx,-sse,+soft-float",
  "linker-flavor": "ld.lld",
  "linker": "rust-lld"
}
Key features:
  • no_std: No standard library
  • panic=abort: No unwinding
  • disable-redzone: Required for interrupt handlers
  • soft-float: No FPU/SSE in kernel space

Disk Layout

The disk image follows this structure:
LBA 0:      Boot sector (512 bytes)
LBA 1-64:   Stage 2 bootloader (32 KB)
LBA 65+:    Kernel binary
The kernel start LBA is hardcoded in both stage2.asm and build.py. If you change STAGE2_SECTORS, update both locations.

Advanced Build Options

Debug Build

Enable debug symbols and verbose logging:
python scripts/build.py --debug --run
This enables:
  • Debug assertions in Rust code
  • Serial port logging to build/logs/serial.log
  • Extended error messages

Clean Build

Force a complete rebuild:
python scripts/build.py --clean --mode=iso
This removes:
  • build/ directory
  • target/ directory
  • All cached artifacts

Custom Disk Size

Specify disk image size:
python scripts/build.py --size=16 --mode=raw
Larger disk images allow for more FAT32 storage space. Minimum size is 8 MB to accommodate the kernel.

Build Artifacts

After a successful build, you’ll find:
build/
├── boot.bin           # Stage 1 bootloader
├── stage2.bin         # Stage 2 bootloader
├── isr.o              # Interrupt handlers
├── kernel.bin         # Kernel binary
├── portix.img         # Raw disk image
├── dist/
│   ├── portix.iso     # ISO image
│   ├── portix.vdi     # VirtualBox image
│   └── portix.vmdk    # VMware image
└── logs/
    ├── build.log      # Build output
    ├── serial.log     # Serial console output
    └── debug.log      # Debug messages

Troubleshooting

Install Rust nightly and set as default:
rustup toolchain install nightly
rustup default nightly
rustup component add rust-src --toolchain nightly
Install NASM assembler:
Install LLVM binutils:
  • Linux: sudo apt install llvm
  • macOS: brew install llvm
  • Windows: Install LLVM from https://llvm.org/
Ensure rust-lld is available:
rustup component add llvm-tools-preview
Install one of these tools:
  • xorriso: sudo apt install xorriso
  • pycdlib: pip install pycdlib
Or use --mode=raw instead.

Next Steps

Testing

Learn how to test your build in QEMU and VirtualBox

Contributing

Read the contribution guidelines

Build docs developers (and LLMs) love