Skip to content

ipankajg/rix

Repository files navigation

Rix Rix - A Minimal x64 Operating System

Created by Intelligentia Artificialis, guided by Homo Sapiens!

Rix is a minimalist 64-bit operating system written from scratch in Rust and x86-64 assembly. It boots directly without GRUB or any other bootloader, switching from real mode to 64-bit long mode entirely on its own. This is a memory-safe OS, leveraging Rust's ownership system to prevent entire classes of bugs.

Rix Screenshot

Features

  • Self-contained bootloader: No GRUB or external bootloader needed
  • 64-bit long mode: Full x86_64 operation
  • Memory-safe kernel: Rust's ownership prevents use-after-free, double-free, and buffer overflows
  • Type-safe by design: Strong typing eliminates many classes of bugs at compile time
  • VGA text mode: 80x25 color terminal
  • PS/2 keyboard support: Full keyboard input
  • In-memory filesystem: Create, edit, and manage files
  • Interactive shell: Unix-like command interface
  • App loader: Run precompiled apps bundled in the OS image
  • User mode execution: Ring 3 applications with memory protection
  • Preemptive multitasking: Timer-based process scheduling with background process support
  • Per-process page tables: Each user process gets isolated virtual address space
  • Process management: PCB-based process tracking with parent-child relationships
  • System calls: Clean syscall interface via INT 0x80
  • Exception handling: Graceful handling of CPU exceptions

Architecture

Dual Shell Design

Rix includes two shells:

Shell Mode File Description
Kernel Shell (kshell) Ring 0 kernel/kshell.rs Built into the kernel, runs with full privileges
User Shell (shell) Ring 3 apps/shell/shell.rs Runs as a user-mode application with memory protection

Default behavior: The OS launches the user shell (shell) by default. If the user shell crashes or exits, the kernel automatically restarts it. If the user shell is not available, the kernel falls back to the kernel shell.

Switching to Kernel Shell as Default

To use the kernel shell as the default instead of the user shell, modify kernel/kernel.rs:

// Change this in kernel_main():
loop {
    if loader::app_exists("shell") {
        // Launch user shell
        let result = loader::app_execute("shell", 0, core::ptr::null());
        // ... restart logic ...
    } else {
        // Fall back to kernel shell
        kshell::run();
    }
}

// To always use kernel shell, replace the loop with:
loop {
    kshell::run();
}

Two Types of Applications

Rix supports two types of applications:

Type Execution Mode Output Method Example
Kernel Apps Ring 0 (kernel mode) Function pointer from kernel kcalc
User Apps Ring 3 (user mode) System calls (INT 0x80) calc, shell, hello

Kernel apps run with full system privileges and can directly access hardware. They receive a function pointer to the kernel's print function.

User apps run in protected mode with memory isolation. They communicate with the kernel through system calls. If a user app crashes, the kernel catches the exception and terminates it gracefully without affecting system stability.

Process Management

Each user application runs as a separate process with:

Component Description
PID Unique process identifier (1-15)
PCB Process Control Block with state, context, and parent PID
Page Tables Separate PML4, PDPT, PD, and user PT per process
CR3 Dedicated page table root address
Physical Pages Isolated physical memory for code, data, and stack

Process lifecycle:

  1. create_process() - Allocates PID, creates page tables, maps user memory
  2. setup_process_context() - Initializes registers, entry point, stack pointer
  3. Process executes in Ring 3 with its own CR3
  4. exit_process() - Marks process as zombie, prepares for cleanup
  5. destroy_process() - Frees all physical pages and page tables

Memory isolation: Each process has its own virtual-to-physical mappings for the user space region (0x400000-0x500000). Kernel memory is shared but supervisor-only, preventing user code from accessing kernel data.

Shell Commands

Built-in Commands

Command Description
help Display available commands
clear Clear the screen
apps List available apps
ps List running processes
kill <pid> Terminate a process by PID
ls [path] List directory contents
cat <file> Display file contents
touch <file> Create empty file
mkdir <dir> Create directory
rm <path> Remove file or empty directory
cd <dir> Change directory
pwd Print working directory
echo <text> Print text
reboot Reboot the system (kernel shell only)

Background Processes

You can run any app in the background by appending & to the command:

tick &          # Run tick in background
tock &          # Run tock in background
ps              # Show running processes
kill 2          # Kill process with PID 2

Available Apps

kcalc - Kernel Mode Calculator

Kernel-mode calculator that runs with Ring 0 privileges. Uses direct function calls for output.

Usage:

kcalc <expression>

Operators: + - * x / % ()

Examples:

kcalc 10+5         # Result: 15
kcalc "42 * 3"     # Result: 126
kcalc 2(3+4)       # Result: 14 (implicit multiplication)

calc - User Mode Calculator

User-mode calculator that runs with Ring 3 protection. Uses system calls for output. Same functionality as kcalc but demonstrates user-mode execution.

Usage:

calc <expression>

Examples:

calc 10+5          # Result = 15
calc "100 / 4"     # Result = 25
calc 2(3+4)        # Result = 14

crash - User Mode Crash Tester

Testing tool for kernel exception handlers. Verifies that CPU exceptions from user mode are caught gracefully without crashing the kernel. Defaults to divide-by-zero test (crash type 0).

Usage:

crash

The program runs automatically and triggers a divide-by-zero exception (#DE). The kernel catches it, terminates the user program, and returns to the shell.

Available Crash Types:

To test other crash types, modify the source and recompile:

  • 0 - Divide by zero (#DE) - default
  • 1 - Invalid opcode (#UD)
  • 2 - General protection fault (#GP)
  • 3 - Page fault - read (#PF)
  • 4 - Page fault - write (#PF)
  • 5 - Breakpoint (#BP)
  • 6 - Overflow (#OF)

Example Session:

> crash

CRASH - User Mode Crash Tester
================================

Triggering crash type 0: Divide by zero (#DE)

[USERMODE] Exited with code: 0
> _

The program tests that:

  • Exceptions are caught and handled correctly
  • User program terminates gracefully
  • Kernel and shell remain stable
  • Serial log shows exception markers (!0, !GP, !PF, etc.)

hello - Simple User Mode Demo

Minimal user mode program that prints a greeting and exits. Demonstrates basic user mode execution and syscalls.

Usage:

hello

tick - Background Process Demo

Prints "tick" every 2 seconds for 10 seconds. Demonstrates background process execution and preemptive multitasking.

Usage:

tick &          # Run in background

tock - Background Process Demo

Prints "tock" every 2 seconds for 10 seconds. Run alongside tick to see multitasking in action.

Usage:

tock &          # Run in background

Example multitasking session:

tick &          # Start tick in background
tock &          # Start tock in background  
ps              # See both processes running
# Watch as tick and tock alternate printing
# After 10 seconds, both will finish automatically

Building

Prerequisites

macOS:

  • nasm - Netwide Assembler
  • rustc (nightly) - Rust compiler
  • cargo - Rust package manager
  • llvm - For linker and objcopy
  • qemu-system-x86_64 - For testing (optional)

Linux:

  • nasm - Netwide Assembler
  • rustc (nightly) - Rust compiler
  • cargo - Rust package manager
  • lld - LLVM Linker
  • make - GNU Make
  • qemu-system-x86_64 - For testing (optional)

Quick Setup (Recommended)

The easiest way to get started is using the provided setup script:

# This will install Rust nightly and required components
./setup.sh

# Then build and run
make
make run

The setup script handles Rust installation and configuration automatically. This is particularly useful for the Rust version as it requires specific nightly toolchain setup.

Manual Setup

If you prefer to set up dependencies manually:

macOS:

# Install Rust (nightly)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup default nightly
rustup component add rust-src llvm-tools

# Install dependencies via Homebrew
brew install nasm qemu llvm

# Build
make

Linux:

# Install Rust (nightly)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup default nightly
rustup component add rust-src llvm-tools

# Install dependencies (Debian/Ubuntu)
sudo apt install nasm lld make qemu-system-x86

# Build
make

Build Output

The build creates:

  • build/rix.img - Bootable OS image with bundled apps
  • build/kernel.bin - Kernel binary
  • build/apps.img - App table with all apps

Running

With QEMU (recommended)

make run

Or manually:

qemu-system-x86_64 -drive format=raw,file=build/rix.img -m 256M

With Serial Console

qemu-system-x86_64 -drive format=raw,file=build/rix.img -m 256M -serial stdio

Troubleshooting

Invalid Opcode when running apps (#UD / exception 6)

If the system reboots or you see an Invalid Opcode exception (!6 on serial) when launching an app (e.g., calc), ensure the kernel enables SSE/SSE2 early. Modern x86_64 compilers emit SSE instructions by default. The kernel must:

  • Clear CR0.EM (bit 2)
  • Set CR0.MP (bit 1)
  • Set CR4.OSFXSR (bit 9)
  • Set CR4.OSXMMEXCPT (bit 10)

This project configures these in kernel/entry.asm before calling into Rust. Without this, executing SSE instructions will raise #UD.

On Real Hardware

Write the image to a USB drive (⚠️ be careful with the device name!):

sudo dd if=build/rix.img of=/dev/sdX bs=512

Project Structure

raios/
├── boot/
│   ├── stage1.asm       # Stage 1 bootloader (MBR, loads kernel and apps)
│   └── stage2.asm       # Stage 2 bootloader (mode switching to 64-bit)
├── kernel/
│   ├── entry.asm        # Kernel entry point (assembly)
│   ├── kernel.rs        # Main kernel code
│   ├── vga.rs           # VGA text mode driver
│   ├── keyboard.rs      # PS/2 keyboard driver
│   ├── fs.rs            # In-memory filesystem
│   ├── shell.rs         # Command shell
│   ├── loader.rs        # App loader
│   ├── syscall.rs       # System call handler
│   ├── usermode.rs      # User mode support
│   ├── paging.rs        # Page table management and physical memory allocator
│   ├── process.rs       # Process Control Block and process management
│   ├── string.rs        # String utilities
│   ├── io.rs            # Port I/O functions
│   ├── linker.ld        # Linker script
│   ├── build.rs         # Build script
│   └── Cargo.toml       # Kernel dependencies
├── apps/
│   ├── userlib.rs       # User mode library
│   ├── app.ld           # App linker script
│   ├── calc/            # Calculator app
│   │   ├── Cargo.toml
│   │   └── calc.rs
│   ├── hello/           # Simple user mode demo
│   │   ├── Cargo.toml
│   │   └── hello.rs
│   └── ucrash/          # Exception handler tester
│       ├── Cargo.toml
│       └── ucrash.rs
├── build_apptable.py    # Script to create app table
├── setup.sh             # First-time setup script
├── Makefile             # Build system
├── Cargo.toml           # Workspace configuration
└── README.md            # This file

Technical Details

Boot Process

  1. BIOS loads Stage 1 bootloader from MBR to 0x7C00
  2. Stage 1 bootloader:
    • Sets up stack and segments
    • Loads Stage 2 bootloader (sectors 2-5)
    • Loads kernel from disk to memory below 1MB (sectors 6+)
    • Loads app table (right after kernel, LBA sector 69) to 0x20000 using extended BIOS reads
    • Jumps to Stage 2
  3. Stage 2 bootloader:
    • Checks for 64-bit CPU support
    • Enables A20 line
    • Enters 32-bit protected mode
    • Copies kernel to 1MB mark (0x100000)
    • Copies app table to 2MB mark (0x200000)
    • Sets up identity-mapped page tables
    • Enables PAE and long mode
    • Enters 64-bit long mode
    • Jumps to kernel
  4. Kernel:
    • Initializes VGA, keyboard, filesystem
    • Loads and verifies app table
    • Starts interactive shell

Boot Debug Markers

When running with serial output (-serial stdio), the bootloader outputs single-character markers to indicate progress. This is useful for debugging boot issues.

Marker Stage Meaning
1 stage1.asm Stage 1 started
2 stage1.asm Stage 2 loaded
3 stage2.asm Stage 2 started
4 stage2.asm App table loaded
X stage2.asm App table load FAILED
5 stage2.asm Long mode check passed
P stage2.asm Protected mode
6 stage2.asm Kernel loaded via ATA PIO
7 stage2.asm App table copied to final address (5MB)
A stage2.asm Page tables cleared
B stage2.asm Page table setup done
C stage2.asm Jump to 64-bit long mode
D stage2.asm Entered long mode
8 entry.asm Entered kernel

Normal boot sequence: 12345P67ABCD8

If you see X instead of 4, the app table failed to load from disk.

Memory Map

Address Usage
0x00000 - 0x07BFF Free (BIOS data)
0x07C00 - 0x07DFF Stage 1 bootloader (512 bytes)
0x07E00 - 0x0FDFF Stage 2 bootloader (2KB)
0x10000 - 0x1FFFF Kernel temp load area
0x20000 - 0x2FFFF App table temp load area
0x70000 - 0x73FFF Boot page tables (16KB)
0x90000 Stack
0xB8000 VGA text buffer
0x100000 (1MB) Kernel code and data
0x400000 (4MB) User virtual space (per-process mapped)
0x500000 (5MB) App table and app binaries
0x1000000 (16MB)+ Physical pages for process page tables and user memory

Virtual Memory Layout (Per-Process)

Each user process has its own page tables providing isolated virtual address space:

Virtual Address Physical Mapping Access
0x000000 - 0x200000 Identity mapped Supervisor only (kernel code)
0x200000 - 0x400000 Identity mapped Supervisor only (kernel data)
0x400000 - 0x4F0000 Per-process physical pages User (code, data, stack)
0x4F0000 - 0x500000 Per-process physical pages User (stack region)
0x500000 - 0x1800000 Identity mapped (2MB pages) Supervisor only (app table, kernel data)

Page Table Architecture

Rix uses x86-64 4-level paging with per-process page tables:

PML4 (Page Map Level 4)
  └── PDPT (Page Directory Pointer Table)
        └── PD (Page Directory)
              ├── [0-1] → Kernel PT (4KB pages, supervisor only)
              ├── [2]   → User PT (4KB pages, per-process)
              └── [3-11] → 2MB pages (kernel data, supervisor only)

Key features:

  • Kernel pages shared: PD entries 0-1 point to kernel page tables (supervisor only)
  • User pages isolated: PD entry 2 points to per-process page table
  • Physical allocator: Bitmap-based, allocates from 16MB onwards
  • CR3 switching: Each process switch updates CR3 to the process's PML4

App Loader Architecture

App Table Structure

The app table is stored right after the kernel (LBA sector 69) and contains:

+------------------+
| Table Header     | (16 bytes)
|   - Magic: APP_  |
|   - Version: 1   |
|   - App count    |
|   - Reserved     |
+------------------+
| App Header 1     | (48 bytes each)
|   - Name (32)    |
|   - Offset       |
|   - Size         |
|   - Entry offset |
+------------------+
| App Header 2     |
+------------------+
| ...              |
+------------------+
| App Binary 1     | (variable size)
+------------------+
| App Binary 2     |
+------------------+
| ...              |
+------------------+

Loading Process

  1. Stage 1 bootloader loads app table from disk using INT 13h extended read (LBA mode)
  2. Stage 2 bootloader copies app table from 0x20000 to 0x200000 in protected mode
  3. Kernel initializes loader and verifies magic number
  4. Shell can execute apps by name

App Execution

  1. User types app name and arguments
  2. Shell checks if command is built-in, then checks if it's an app
  3. Loader finds app in table by name
  4. Loader calls app entry point with argc/argv
  5. App executes and returns output buffer offset
  6. Loader displays app output via VGA
  7. Control returns to shell

Design Philosophy

  • Minimal assembly: Only bootloader and entry point are in assembly
  • Maximum Rust code: All drivers, shell, and loader implemented in Rust
  • Memory safety: Rust's ownership prevents entire classes of bugs
  • No external dependencies: No std library, no GRUB, completely freestanding
  • Extensible: Apps can be added without modifying the kernel
  • Educational: Clean, readable code demonstrating safe systems programming

Developing Apps

Creating a New App

Create a new directory in apps/ with a Cargo.toml and source file:

mkdir apps/myapp

Create apps/myapp/Cargo.toml:

[package]
name = "myapp"
version = "1.0.0"
edition = "2021"

[lib]
crate-type = ["staticlib"]

Create apps/myapp/src/lib.rs:

App Template

#![no_std]
#![no_main]

use core::panic::PanicInfo;

#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
    loop {}
}

// Syscall constants
const SYS_WRITE: u64 = 1;
const SYS_EXIT: u64 = 0;

// Helper function to print strings
fn print(s: &str) {
    unsafe {
        core::arch::asm!(
            "int 0x80",
            in("rax") SYS_WRITE,
            in("rdi") 1u64,
            in("rsi") s.as_ptr() as u64,
            in("rdx") s.len() as u64,
        );
    }
}

// App entry point
#[no_mangle]
pub extern "C" fn _start() -> i32 {
    print("Hello from myapp!\n");
    
    // Your app logic here
    
    // Exit syscall
    unsafe {
        core::arch::asm!(
            "int 0x80",
            in("rax") SYS_EXIT,
            in("rdi") 0u64,
        );
    }
    0
}

Building Apps

  1. Add your app directory to apps/
  2. Update workspace Cargo.toml to include the new app
  3. Update Makefile:
    • Add to APP_BINS
    • Add build rules (see calc example)
  4. Run make to build
  5. Run make run to test

App Limitations

  • Apps run in user mode (Ring 3) with memory protection
  • No heap allocator yet (stack-based allocation only)
  • Communication via syscalls (SYS_WRITE, SYS_EXIT)
  • Output via syscall interface
  • No standard library (must use no_std)
  • Position-independent code recommended

Safety Features

Memory Safety

  • Ownership system: Prevents use-after-free and double-free
  • Bounds checking: Array bounds checking prevents buffer overflows
  • Option: Makes null handling explicit
  • Strong typing: Enums and pattern matching catch logic errors
  • Concurrency safety: Compiler prevents data races

Unsafe Code

Rust still requires unsafe for:

  • Hardware Access: Port I/O, memory-mapped I/O
  • Assembly Integration: Calling assembly functions, inline asm
  • Global Mutable State: Static variables (can use atomics to make safer)

However, these unsafe blocks are:

  1. Clearly marked with unsafe keyword
  2. Isolated in specific modules (io, vga, keyboard)
  3. Wrapped with safe APIs for the rest of the kernel
  4. Much smaller surface area than traditional C kernels (~7% of code)

Future Enhancements

Short Term

  • Heap allocator (enable alloc crate)
  • More syscalls (read, open, close)
  • Better error handling with Result<T, E>
  • Standard app library
  • Nested process execution (launching apps from shell)

Long Term

  • Dynamic memory allocation for apps
  • App-to-app communication
  • File I/O from apps
  • Preemptive multitasking
  • Network stack

Recently Completed

  • ✅ Per-process page tables with CR3 switching
  • ✅ Process Control Block (PCB) management
  • ✅ Physical memory allocator for user processes
  • ✅ Memory isolation between processes | Code Size | ~3000 LOC | ~4000 LOC | | Unsafe Code | All code | ~7% isolated |

License

This is free and unencumbered software released into the public domain.


Contributing to Rix

Code Philosophy

Rix is designed as an educational operating system with emphasis on clarity, safety, and maintainability. When contributing code or using AI tools to generate code, please adhere to these principles.

Language Preferences

Rust First, Assembly Second

  • Prefer Rust for all new code: Maximum amount of code should be written in Rust
  • Assembly only when absolutely necessary: Use assembly language only for:
    • Bootloader (real mode, protected mode, long mode transitions)
    • Kernel entry point (entry.asm)
    • CPU-specific operations that cannot be done in Rust (e.g., loading GDT, IDT, control registers)
    • Performance-critical inline assembly within Rust functions

Why Rust Over Assembly?

  1. Memory Safety: Rust prevents memory corruption bugs at compile time
  2. Type Safety: Strong type system catches bugs early
  3. Readability: Rust code is easier to understand and maintain than assembly
  4. Productivity: Faster development with modern language features
  5. Educational: Rust teaches safe systems programming practices

Code Quality Standards

Simplicity

  • Write clear, understandable code: Prefer clarity over cleverness
  • Use descriptive names: Variables, functions, and types should have meaningful names
  • Keep functions focused: Each function should do one thing well
  • Comment complex logic: If code is not immediately obvious, add comments
  • Avoid premature optimization: Correct first, optimize later if needed

Example:

// Good - Clear and simple
fn calculate_total(items: &[i32]) -> i32 {
    items.iter().sum()
}

// Avoid - Unnecessarily clever
fn calculate_total(i: &[i32]) -> i32 {
    i.iter().fold(0, |a, &b| a + b)
}

Safety

  • Minimize unsafe code: Only use unsafe when absolutely necessary
  • Wrap unsafe in safe APIs: Encapsulate unsafe code in safe abstractions
  • Check all inputs: Validate function parameters
  • Use Option and Result<T, E>: Make error handling explicit
  • Bounds checking: Rust does this automatically with slices

Example:

// Good - Safe with validation
fn read_file(filename: &str, buffer: &mut [u8]) -> Result<usize, &'static str> {
    if filename.is_empty() {
        return Err("Empty filename");
    }
    
    if buffer.is_empty() {
        return Err("Empty buffer");
    }
    
    // ... rest of implementation
    Ok(0)
}

Robustness

  • Handle errors explicitly: Use Result<T, E> for operations that can fail
  • Avoid panics in production code: Panic only for unrecoverable errors
  • Resource management: Rust's RAII automatically cleans up resources
  • Defensive programming: Use assertions and type system to enforce invariants

Security Considerations

Memory Safety

  • Leverage Rust's guarantees: Ownership, borrowing, and lifetimes prevent most memory bugs
  • Minimize unsafe blocks: Keep them small, well-documented, and auditable
  • Use safe abstractions: Wrap hardware access in safe APIs

Input Validation

  • Never trust user input: Always validate input from keyboard, files, apps
  • Sanitize strings: Use Rust's string handling to prevent buffer overflows
  • Validate indices: Rust's bounds checking prevents out-of-bounds access

Privilege Separation

  • Kernel/user separation: Maintain clear separation between Ring 0 and Ring 3
  • Capability-based security: Limit what apps can do
  • No unnecessary privileges: Apps should run with minimal rights

Code Structure

Module Organization

kernel/
  ├── kernel.rs      # Main kernel entry
  ├── vga.rs         # VGA driver
  ├── keyboard.rs    # Keyboard driver
  ├── fs.rs          # Filesystem
  ├── shell.rs       # Command shell
  ├── loader.rs      # App loader
  └── ...            # One feature per file

apps/
  ├── calc/          # Calculator app
  │   ├── Cargo.toml
  │   └── calc.rs
  └── ...            # One app per directory

Public APIs

  • Document public functions: Use doc comments (///)
  • Keep APIs minimal: Expose only what's necessary
  • Use strong types: Create wrapper types instead of using primitives
  • Make impossible states unrepresentable: Use the type system

Example:

/// Initialize the VGA text mode driver
/// 
/// Sets up 80x25 color text mode and clears the screen.
/// This must be called before any VGA operations.
pub fn init() {
    // Implementation
}

Coding Style

Formatting

  • Use rustfmt: Always format code with cargo fmt
  • Follow Rust conventions: Use standard Rust naming and style
  • Line length: Prefer 80-100 characters

Naming Conventions

  • Functions: snake_case
  • Types/Structs: PascalCase
  • Constants: SCREAMING_SNAKE_CASE
  • Modules: snake_case

Comments

  • Doc comments for public APIs: Use /// for documentation
  • Inline comments for complex logic: Explain why, not what
  • TODO comments: Mark areas that need work with // TODO:

Testing

Manual Testing

  • Test in QEMU: Always test in emulator first
  • Test edge cases: Try boundary values
  • Test error paths: Verify error handling works
  • Test on real hardware: Eventually test on physical machines

Code Review

  • Self-review: Review your own code before committing
  • Run cargo check: Verify code compiles
  • Run cargo clippy: Fix all warnings
  • Test thoroughly: Don't assume code works

Unsafe Code Guidelines

When you must use unsafe:

  1. Document why it's necessary: Explain what safety invariants you're maintaining
  2. Keep it minimal: Make unsafe blocks as small as possible
  3. Encapsulate in safe APIs: Don't expose unsafe to callers
  4. Add safety comments: Document all safety requirements

Example:

/// Write a byte to a port
/// 
/// # Safety
/// 
/// The caller must ensure that writing to this port is safe
/// and will not cause undefined behavior or hardware issues.
pub unsafe fn outb(port: u16, value: u8) {
    core::arch::asm!(
        "out dx, al",
        in("dx") port,
        in("al") value,
    );
}

AI Tool Usage

When using AI tools (like GitHub Copilot, ChatGPT, etc.) to generate code:

  1. Specify Rust preference: Always request Rust implementations over C/assembly
  2. Request safe code: Ask for code that minimizes unsafe
  3. Ask for error handling: Request Result<T, E> for fallible operations
  4. Request documentation: Ask for doc comments
  5. Review generated code: Never blindly accept AI-generated code
  6. Test thoroughly: AI can make mistakes - always test
  7. Run clippy: Check for common issues

Documentation Guidelines for AI Tools

IMPORTANT: AI tools should NOT create separate documentation files unless specifically requested.

Rules for documentation:

  1. Use existing documentation files only:

    • README.md - Main project documentation, user-facing features, and contributing guidelines
  2. Update README.md for new features:

    • Add new apps to the appropriate section
    • Add new shell commands to the commands list
    • Update examples and usage instructions
    • Keep formatting consistent with existing sections
  3. Use doc comments in code:

    • Document public APIs with ///
    • Add module-level documentation with //!
    • Explain complex algorithms in comments
  4. No temporary documentation files:

    • Don't create separate guide files
    • Use TODO comments in code instead
    • Use git commit messages for development notes

Example AI Prompt

Good prompt:

"Write a Rust function to parse a string into integers. The function should:
- Use safe Rust (no unsafe)
- Return Result<Vec<i32>, &'static str> for error handling
- Validate all inputs
- Be simple and easy to understand
- Include doc comments
- Follow Rix coding standards"

Future Considerations

As Rix evolves, keep in mind:

  • Heap allocation: Plan for adding the alloc crate
  • Async/await: Consider async I/O for future features
  • Multi-tasking: Design with multitasking in mind
  • Safe abstractions: Always wrap unsafe code in safe APIs
  • Testing: Add unit tests when possible

Questions?

When in doubt:

  1. Prefer safe Rust over unsafe
  2. Prefer simple over clever
  3. Use the type system to prevent bugs
  4. Add comments when code isn't obvious
  5. Test thoroughly

Remember: This is an educational OS. The goal is to learn and teach safe systems programming. Clarity, correctness, and safety above all else.


Resources

Credits

Created as an educational project to demonstrate memory-safe operating system development using Rust.

About

Rust based Unix like OS, written by AI, guided by Humans.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published