<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daxo</title>
    <description>The latest articles on DEV Community by Daxo (@aitoolsdxglitch).</description>
    <link>https://dev.to/aitoolsdxglitch</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3993814%2F4793b813-3693-4181-a439-7bdc188d47fe.png</url>
      <title>DEV Community: Daxo</title>
      <link>https://dev.to/aitoolsdxglitch</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aitoolsdxglitch"/>
    <language>en</language>
    <item>
      <title>Stripping Away Abstractions: How I Built a Rust Microkernel from Scratch in 100 Hours (At Age 13)</title>
      <dc:creator>Daxo</dc:creator>
      <pubDate>Sat, 20 Jun 2026 09:27:03 +0000</pubDate>
      <link>https://dev.to/aitoolsdxglitch/stripping-away-abstractions-how-i-built-a-rust-microkernel-from-scratch-in-100-hours-at-age-13-3cik</link>
      <guid>https://dev.to/aitoolsdxglitch/stripping-away-abstractions-how-i-built-a-rust-microkernel-from-scratch-in-100-hours-at-age-13-3cik</guid>
      <description>&lt;p&gt;Most developers take the operating system for granted-treating it as an invisible, obedient foundation for browsers, Docker, and heavy IDEs. For me, this foundation was always the ultimate mystery. I wanted to understand: what actually happens on the bare metal when you strip away high-level abstractions?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's how my 15-day experiment started. I carved out 100 hours&lt;/strong&gt; from a tight schedule between school and intense martial arts training (judo and wrestling) to build a standalone, microkernel-based operating system from scratch: Daxo OS, written in Rust.&lt;/p&gt;

&lt;p&gt;In this article, I’ll share how I broke free from standard tutorials, wrote a custom PIO ATA disk driver, brought async programming into Ring 0, and why Terry Davis and his legendary TempleOS became my ultimate engineering inspiration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;![Daxo OS in Action]&lt;/strong&gt;(&lt;a href="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/nwiom7r5gtvtviq6h6yd.jpg" rel="noopener noreferrer"&gt;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/nwiom7r5gtvtviq6h6yd.jpg&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspired by Madness: Why Bare-Metal?
&lt;/h2&gt;

&lt;p&gt;If you ask a regular software engineer why they are writing their own OS, they might think you're crazy. But if you’ve ever read about Terry Davis and TempleOS, you understand that pure, unadulterated engineering excitement. Terry built his world without looking back at Linux or Windows-communicating directly with CPU registers and controllers.&lt;/p&gt;

&lt;p&gt;I didn't want to build another CLI tool running on top of a ready-made Linux kernel. I wanted a system that manages physical hardware by my own rules. The choice was obvious: Rust Nightly. Its #[no_std] attribute allows writing blazing-fast code without a garbage collector while maintaining strict memory safety.&lt;/p&gt;

&lt;p&gt;Here is the anatomy of Daxo OS:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;daxo_os/&lt;br&gt;
├── .cargo/&lt;br&gt;
│   └── config.toml           # Linker settings and build flags&lt;br&gt;
├── src/&lt;br&gt;
│   ├── allocator/            # Custom heap allocators (Bump, Fixed Size Block)&lt;br&gt;
│   ├── task/                 # Async engine (Executor, keyboard handling)&lt;br&gt;
│   ├── allocator.rs          # Heap management interface&lt;br&gt;
│   ├── ata.rs                # The custom PIO ATA hard drive driver&lt;br&gt;
│   ├── framebuffer.rs        # Graphics output management&lt;br&gt;
│   ├── gdt.rs / interrupts.rs# Descriptor tables &amp;amp; interrupt handling&lt;br&gt;
│   ├── main.rs               # Kernel entry point (kernel_main)&lt;br&gt;
│   └── vga_buffer.rs         # Good old 80x25 text output&lt;br&gt;
├── disk.bin                  # Disk image for QEMU testing&lt;br&gt;
└── x86_64-blog_os.json       # Target platform specification&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;The Tutorial Illusion and My First Triple Fault&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
​Initially, like many OSdev beginners, I relied on Philipp Oppermann's amazing "Writing an OS in Rust" guide. Printing ASCII characters to the screen via direct VGA memory access at 0xb8000 is inspiring, but it's a linear experience. You are just following someone else's tracks.&lt;/p&gt;

&lt;p&gt;​Real engineering started where the guide ended. Since the modern Rust Nightly compiler moves fast, some older code broke. The rust-lld linker suddenly started throwing errors like undefined symbol: memcpy, and the kernel would plunge into a bootloop (Triple Fault) at the slightest mistake.&lt;/p&gt;

&lt;p&gt;​I realized that modern Rust editions require explicit encapsulation of raw operations inside unsafe blocks, even within unsafe fn in some contexts, alongside strict handling of compiler_builtins. Fixing this required diving into Cargo flags and manually rebuilding core libraries. It was the exact moment I transitioned from passively reading tutorials to hardcore systems research.&lt;br&gt;
**&lt;br&gt;
​Hardware with No Safety Nets: Writing a PIO ATA Driver**&lt;/p&gt;

&lt;p&gt;​My main critique of existing OSdev tutorials is that they often abandon the reader halfway through. You get text output, you get keyboard interrupts, but the system remains a "black box"-it cannot persist data.&lt;/p&gt;

&lt;p&gt;​I decided to fix this by writing a custom PIO ATA driver for hard drive interaction.&lt;/p&gt;

&lt;p&gt;​The hard part? The OS needs to read disk sectors before the Interrupt Descriptor Table (IDT) and the Programmable Interrupt Controller (PIC) are fully set up. If the drive fires a hardware signal when the kernel isn't ready to catch it, you get an immediate crash.&lt;br&gt;
​I used an architectural hack: isolated port polling right before turning on interrupts.&lt;/p&gt;

&lt;p&gt;​Here is how the boot logic looks in my main.rs:&lt;br&gt;
`#![no_std]&lt;/p&gt;

&lt;h1&gt;
  
  
  ![no_main]
&lt;/h1&gt;

&lt;h1&gt;
  
  
  ![feature(custom_test_frameworks)]
&lt;/h1&gt;

&lt;h1&gt;
  
  
  ![test_runner(daxo_os::test_runner)]
&lt;/h1&gt;

&lt;h1&gt;
  
  
  ![reexport_test_harness_main = "test_main"]
&lt;/h1&gt;

&lt;p&gt;extern crate alloc;&lt;/p&gt;

&lt;p&gt;use daxo_os::{print, println};&lt;br&gt;
use core::panic::PanicInfo;&lt;br&gt;
use bootloader::{BootInfo, entry_point};&lt;br&gt;
use x86_64::VirtAddr;&lt;br&gt;
use alloc::vec;&lt;/p&gt;

&lt;p&gt;// Import structures for async multitasking&lt;br&gt;
use daxo_os::task::{Task, keyboard};&lt;br&gt;
use daxo_os::task::executor::Executor;&lt;/p&gt;

&lt;p&gt;entry_point!(kernel_main);&lt;/p&gt;

&lt;p&gt;async fn async_number() -&amp;gt; u32 {&lt;br&gt;
    42&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;async fn example_task() {&lt;br&gt;
    let number = async_number().await;&lt;br&gt;
    println!("async number from example_task: {}", number);&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;fn kernel_main(boot_info: &amp;amp;'static BootInfo) -&amp;gt; ! {&lt;br&gt;
    // 1. First, print the ASCII banner (works via VGA, interrupts are not needed yet)&lt;br&gt;
    println!("     ######      ###    ##     ##  #######     #######   ######  ");&lt;br&gt;
    println!("     ##   ##    ## ##    ##   ##  ##     ##   ##     ## ##    ## ");&lt;br&gt;
    println!("     ##    ##  ##   ##    ## ##   ##     ##   ##     ## ##       ");&lt;br&gt;
    println!("     ##    ## ##     ##    ###    ##     ##   ##     ##  ######  ");&lt;br&gt;
    println!("     ##    ## #########   ## ##   ##     ##   ##     ##       ## ");&lt;br&gt;
    println!("     ##   ##  ##     ##  ##   ##  ##     ##   ##     ## ##    ## ");&lt;br&gt;
    println!("     ######   ##     ## ##     ##  #######     #######   ######  ");&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;println!("\n[Daxo OS Multitasking Kernel Booted]");

// 2. Set up the memory mapper and heap (interrupts are still disabled)
let phys_mem_offset = VirtAddr::new(boot_info.physical_memory_offset);
let mut mapper = unsafe { daxo_os::memory::init(phys_mem_offset) };
let mut frame_allocator = unsafe {
    daxo_os::memory::BootInfoFrameAllocator::init(&amp;amp;boot_info.memory_map)
};

daxo_os::allocator::init_heap(&amp;amp;mut mapper, &amp;amp;mut frame_allocator)
    .expect("heap initialization failed");

// 3. --- ATA DRIVE DRIVER TEST IN COMPLETE ISOLATION ---
// PIC controller is not enabled yet, timer isn't ticking, nothing to crash!
println!("[Testing ATA Drive Driver...]");

let mut disk_vec = vec![0u8; 512];

if let Ok(disk_buffer) = &amp;lt;&amp;amp;mut [u8; 512]&amp;gt;::try_from(&amp;amp;mut disk_vec[..]) {
    daxo_os::ata::ATA_BUS.lock().read_sector(0, disk_buffer);

    println!("Data read from LBA 0:");
    let mut data_found = false;
    for &amp;amp;byte in disk_buffer.iter().take(90) {
        if byte &amp;gt;= 32 &amp;amp;&amp;amp; byte &amp;lt;= 126 {
            print!("{}", byte as char);
            data_found = true;
        }
    }
    if !data_found {
        print!("[Sector contains binary data]");
    }
    println!("\n[ATA Test Finished]\n");
}
// ---------------------------------------------------------------------------------

// 4. NOW initialize IDT, GDT, and the PIC interrupt controller
// Any hardware signals from QEMU pending during the disk test will reset upon PIC initialization!
daxo_os::init(); 

#[cfg(test)]
test_main();

// 5. Create the task executor/scheduler
let mut executor = Executor::new();
executor.spawn(Task::new(example_task()));
executor.spawn(Task::new(keyboard::print_keypresses()));

// 6. Enable hardware interrupts on the CPU
x86_64::instructions::interrupts::enable();

// 7. Run the executor loop indefinitely
executor.run();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;h1&gt;
  
  
  [cfg(not(test))]
&lt;/h1&gt;

&lt;h1&gt;
  
  
  [panic_handler]
&lt;/h1&gt;

&lt;p&gt;fn panic(info: &amp;amp;PanicInfo) -&amp;gt; ! {&lt;br&gt;
    println!("{}", info);&lt;br&gt;
    daxo_os::hlt_loop();&lt;br&gt;
}&lt;/p&gt;

&lt;h1&gt;
  
  
  [cfg(test)]
&lt;/h1&gt;

&lt;h1&gt;
  
  
  [panic_handler]
&lt;/h1&gt;

&lt;p&gt;fn panic(info: &amp;amp;PanicInfo) -&amp;gt; ! {&lt;br&gt;
    daxo_os::test_panic_handler(info)&lt;br&gt;
}`&lt;/p&gt;

&lt;p&gt;To test the driver without a real file system, I used some Unix magic to write a test string into the first 512-byte sector of a blank disk.bin file:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;echo "Daxo OS ATA Test String" | dd of=disk.bin bs=512 count=1 conv=notrunc&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The conv=notrunc flag is critical here—it ensures dd overwrites only the initial bytes without truncating the disk image. I configured QEMU in Cargo.toml to mount this file as a hard drive on the IDE bus. When the kernel printed Daxo OS ATA Test String read directly from the controller ports, I knew it worked.&lt;/p&gt;

&lt;p&gt;​&lt;strong&gt;Async in Ring 0 Space&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;​Once the disk is verified, control goes to a custom asynchronous task executor. Implementing async/await mechanics inside a custom kernel without the standard library is pure joy. There is no Tokio or async-std here.&lt;/p&gt;

&lt;p&gt;​The architecture relies heavily on raw Future and Waker types. When a task (like waiting for a keypress in keyboard::print_keypresses) cannot proceed, it yields, giving CPU cycles to other tasks. As soon as the PIC catches a keyboard interrupt, the handler wakes that specific Waker, and the executor resumes it. It runs concurrently and endlessly inside executor.run().&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;​Conclusion: Real Engineering Starts Where Tutorials End&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;​This 100-hour sprint taught me the most important lesson: don't panic when facing complete darkness. When you are looking at a raw terminal crash log with a low-level error you can't Google, the only way out is to read dependency source code, look up x86_64 architecture specifications, and understand the physics of the process.&lt;/p&gt;

&lt;p&gt;​Moving forward, I plan to take Daxo OS further: shifting from Ring 0 to Ring 3 (separating kernel and user spaces), exploring process isolation, and researching microkernel security mitigations.&lt;/p&gt;

&lt;p&gt;​👉 Check out the full source code here: github.com/aitoolsdx-glitch/daxo_os&lt;br&gt;
&lt;em&gt;(Drop a star ⭐ or open an issue if you want to roast my unsafe code or contribute!)&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>osdev</category>
      <category>showdev</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
