/os-dev-scratch
OS development from scratch skill for bootloader through context switching. Use when building a minimal x86-64 OS, setting up GDT/IDT/page tables, writing keyboard/serial drivers, or using QEMU for kernel boot. Activates on queries about bootloader, long mode, page tables, IDT,
$ npx -y skills add mohitmishra786/low-level-dev-skills --skill os-dev-scratch --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/os-dev-scratch
Context preview
The summary Claude sees to decide when to auto-load this skill.
OS development from scratch skill for bootloader through context switching. Use when building a minimal x86-64 OS, setting up GDT/IDT/page tables, writing keyboard/serial drivers, or using QEMU for kernel boot. Activates on queries about bootloader, long mode, page tables, IDT,
SKILL.md
os-dev-scratch.SKILL.mdname: os-dev-scratch
description: OS development from scratch skill for bootloader through context switching. Use when building a minimal x86-64 OS, setting up GDT/IDT/page tables, writing keyboard/serial drivers, or using QEMU for kernel boot. Activates on queries about bootloader, long mode, page tables, IDT, PIC/APIC, xv6, or x86_64-elf-gcc.
OS Development from Scratch
Purpose
Guide agents through building a minimal operating system from scratch: bootloader stages (BIOS/GRUB vs UEFI/limine), 64-bit long mode setup with GDT and page tables, IDT and interrupt handlers, PIC/APIC configuration, basic keyboard and serial drivers, physical and virtual memory managers, context switching, with xv6-RISC-V as a reference architecture.
When to Use
- Learning how an OS boots from power-on to `main()`
- Implementing protected/long mode transitions on x86-64
- Writing a physical memory allocator (bitmap) and page table manager
- Handling timer, keyboard, and page fault interrupts
- Testing with QEMU `-kernel` and cross-compiler `x86_64-elf-gcc`
- Porting concepts from xv6 to a custom x86 or RISC-V kernel
Workflow
1. Boot stages overview
BIOS path (legacy)
├── BIOS POST
├── MBR (512 bytes) → boot sector loads stage2
├── GRUB/multiboot → loads kernel ELF
└── kernel entry (_start)
UEFI path (modern)
├── UEFI firmware
├── EFI bootloader (limine, systemd-boot)
├── Loads kernel + initrd from ESP
└── kernel entry (handoff with memory map)
2. Toolchain setup
# Cross-compiler for bare metal
brew install x86_64-elf-gcc x86_64-elf-binutils # macOS
# or build from source / apt install gcc-x86-64-elf
x86_64-elf-gcc --version
# QEMU for testing
qemu-system-x86_64 --version
Linker script essentials:
/* linker.ld */
ENTRY(_start)
SECTIONS {
. = 0x100000; /* 1MB — typical kernel load address */
.text : { *(.text .text.*) }
.rodata : { *(.rodata .rodata.*) }
.data : { *(.data .data.*) }
.bss : { *(.bss .bss.*) }
}x86_64-elf-gcc -ffreestanding -nostdlib -c kernel.c -o kernel.o
x86_64-elf-ld -T linker.ld kernel.o -o kernel.elf
3. Multiboot/limine boot
# QEMU direct kernel boot (no disk)
qemu-system-x86_64 \
-kernel kernel.elf \
-serial stdio \
-m 128M \
-no-reboot -no-shutdown
# With limine (UEFI)
qemu-system-x86_64 \
-bios /usr/share/ovmf/OVMF.fd \
-drive file=disk.img,format=raw \
-serial stdio
4. Long mode setup
Protected mode (32-bit) → enable PAE → setup 4-level page tables → enable long mode
// Minimal GDT entry (64-bit flat segments)
struct gdt_entry {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_mid;
uint8_t access;
uint8_t granularity;
uint8_t base_high;
} __attribute__((packed));
// Page table setup (4KB pages, identity map first 1GB)
uint64_t pml4[512] __attribute__((aligned(4096)));
uint64_t pdpt[512] __attribute__((aligned(4096)));
uint64_t pd[512] __attribute__((aligned(4096)));
void setup_paging(void) {
for (int i = 0; i < 512; i++)
pd[i] = (i * 0x200000) | 0x83; // 2MB huge pages
pdpt[0] = (uint64_t)pd | 0x03;
pml4[0] = (uint64_t)pdpt | 0x03;
__asm__ volatile("mov %0, %%cr3" :: "r"(pml4));
}5. IDT and interrupt handlers
struct idt_entry {
uint16_t offset_low;
uint16_t selector;
uint8_t ist;
uint8_t type_attr;
uint16_t offset_mid;
uint32_t offset_high;
uint32_t zero;
} __attribute__((packed));
// ISR stub (assembly) → common handler → dispatch by vector
void interrupt_handler(struct trap_frame *frame) {
if (frame->vector == 14) // page fault
handle_page_fault(frame->cr2, frame->error_code);
else if (frame->vector == 33) // keyboard IRQ remapped
keyboard_handler();
}# Test page fault
# QEMU monitor: info registers
6. PIC and APIC
// Legacy PIC remapping (8259)
// Remap IRQ 0-15 to vectors 32-47
outb(0x20, 0x11); outb(0xA0, 0x11);
outb(0x21, 0x20); outb(0xA1, 0x28); // vector offsets
// ...
// Modern: use APIC/IOAPIC (ACPI MADT parsing)
// LAPIC timer for preemption
7. Serial and keyboard drivers
// COM1 serial (0x3F8)
void serial_putc(char c) {
while ((inb(0x3F8 + 5) & 0x20) == 0);
outb(0x3F8, c);
}
// PS/2 keyboard scancode → ASCII lookup table
void keyboard_handler(void) {
uint8_t scancode = inb(0x60);
char c = scancode_to_ascii[scancode];
if (c) serial_putc(c);
outb(0x20, 0x20); // EOI to PIC
}qemu-system-x86_64 -kernel kernel.elf -serial stdio
# printk output appears in terminal
8. Physical memory manager
// Bitmap allocator over usable RAM regions
// From multiboot memory map or UEFI memory map
#define PAGE_SIZE 4096
uint8_t *frame_bitmap;
uint64_t total_frames;
uint64_t alloc_frame(void) {
for (uint64_t i = 0; i < total_frames; i++) {
if (!test_bit(frame_bitmap, i)) {
set_bit(frame_bitmap, i);
return i * PAGE_SIZE;
}
}
return 0; // OOM
}9. Context switching
struct context {
uint64_t rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp;
uint64_t r8, r9, r10, r11, r12, r13, r14, r15;
uint64_t rip;
};
void switch_context(struct context *old, struct context *new);
// Assembly: save callee-saved regs to old, restore from new, ret to new->ripCooperative scheduling first; add timer IRQ preemption later.
10. xv6-RISC-V reference
git clone https://github.com/mit-pdos/xv6-riscv
cd xv6-riscv && make qemu
| xv6 component | x86 equivalent | |---------------|----------------| | `kernel/vm.c` | Page table management | | `kernel/trap.c` | IDT/interrupt dispatch | | `kernel/proc.c` | Context switch, scheduler | | `kernel/plic.c` | PIC/APIC interrupt controller | | `user/usys.pl` | System call stubs |
Common Problems
| Symptom | Cause | Fix | |---------|-------|-----| | Triple fault on boot | Invalid GDT/IDT or stack | S
Read more
name: os-dev-scratch description: OS development from scratch skill for bootloader through context switching. Use when building a minimal x86-64 OS, setting up GDT/IDT/page tables, writing keyboard/serial drivers, or using QEMU for kernel boot. Activates on queries about bootloader, long mode, page tables, IDT, PIC/APIC, xv6, or x86_64-elf-gcc.
OS Development from Scratch
Purpose
Guide agents through building a minimal operating system from scratch: bootloader stages (BIOS/GRUB vs UEFI/limine), 64-bit long mode setup with GDT and page tables, IDT and interrupt handlers, PIC/APIC configuration, basic keyboard and serial drivers, physical and virtual memory managers, context switching, with xv6-RISC-V as a reference architecture.
When to Use
- Learning how an OS boots from power-on to `main()`
- Implementing protected/long mode transitions on x86-64
- Writing a physical memory allocator (bitmap) and page table manager
- Handling timer, keyboard, and page fault interrupts
- Testing with QEMU `-kernel` and cross-compiler `x86_64-elf-gcc`
- Porting concepts from xv6 to a custom x86 or RISC-V kernel
Workflow
1. Boot stages overview
BIOS path (legacy) ├── BIOS POST ├── MBR (512 bytes) → boot sector loads stage2 ├── GRUB/multiboot → loads kernel ELF └── kernel entry (_start) UEFI path (modern) ├── UEFI firmware ├── EFI bootloader (limine, systemd-boot) ├── Loads kernel + initrd from ESP └── kernel entry (handoff with memory map)
2. Toolchain setup
# Cross-compiler for bare metal brew install x86_64-elf-gcc x86_64-elf-binutils # macOS # or build from source / apt install gcc-x86-64-elf x86_64-elf-gcc --version # QEMU for testing qemu-system-x86_64 --version
Linker script essentials:
/* linker.ld */
ENTRY(_start)
SECTIONS {
. = 0x100000; /* 1MB — typical kernel load address */
.text : { *(.text .text.*) }
.rodata : { *(.rodata .rodata.*) }
.data : { *(.data .data.*) }
.bss : { *(.bss .bss.*) }
}x86_64-elf-gcc -ffreestanding -nostdlib -c kernel.c -o kernel.o x86_64-elf-ld -T linker.ld kernel.o -o kernel.elf
3. Multiboot/limine boot
# QEMU direct kernel boot (no disk) qemu-system-x86_64 \ -kernel kernel.elf \ -serial stdio \ -m 128M \ -no-reboot -no-shutdown # With limine (UEFI) qemu-system-x86_64 \ -bios /usr/share/ovmf/OVMF.fd \ -drive file=disk.img,format=raw \ -serial stdio
4. Long mode setup
Protected mode (32-bit) → enable PAE → setup 4-level page tables → enable long mode
// Minimal GDT entry (64-bit flat segments)
struct gdt_entry {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_mid;
uint8_t access;
uint8_t granularity;
uint8_t base_high;
} __attribute__((packed));
// Page table setup (4KB pages, identity map first 1GB)
uint64_t pml4[512] __attribute__((aligned(4096)));
uint64_t pdpt[512] __attribute__((aligned(4096)));
uint64_t pd[512] __attribute__((aligned(4096)));
void setup_paging(void) {
for (int i = 0; i < 512; i++)
pd[i] = (i * 0x200000) | 0x83; // 2MB huge pages
pdpt[0] = (uint64_t)pd | 0x03;
pml4[0] = (uint64_t)pdpt | 0x03;
__asm__ volatile("mov %0, %%cr3" :: "r"(pml4));
}5. IDT and interrupt handlers
struct idt_entry {
uint16_t offset_low;
uint16_t selector;
uint8_t ist;
uint8_t type_attr;
uint16_t offset_mid;
uint32_t offset_high;
uint32_t zero;
} __attribute__((packed));
// ISR stub (assembly) → common handler → dispatch by vector
void interrupt_handler(struct trap_frame *frame) {
if (frame->vector == 14) // page fault
handle_page_fault(frame->cr2, frame->error_code);
else if (frame->vector == 33) // keyboard IRQ remapped
keyboard_handler();
}# Test page fault # QEMU monitor: info registers
6. PIC and APIC
// Legacy PIC remapping (8259) // Remap IRQ 0-15 to vectors 32-47 outb(0x20, 0x11); outb(0xA0, 0x11); outb(0x21, 0x20); outb(0xA1, 0x28); // vector offsets // ... // Modern: use APIC/IOAPIC (ACPI MADT parsing) // LAPIC timer for preemption
7. Serial and keyboard drivers
// COM1 serial (0x3F8)
void serial_putc(char c) {
while ((inb(0x3F8 + 5) & 0x20) == 0);
outb(0x3F8, c);
}
// PS/2 keyboard scancode → ASCII lookup table
void keyboard_handler(void) {
uint8_t scancode = inb(0x60);
char c = scancode_to_ascii[scancode];
if (c) serial_putc(c);
outb(0x20, 0x20); // EOI to PIC
}qemu-system-x86_64 -kernel kernel.elf -serial stdio # printk output appears in terminal
8. Physical memory manager
// Bitmap allocator over usable RAM regions
// From multiboot memory map or UEFI memory map
#define PAGE_SIZE 4096
uint8_t *frame_bitmap;
uint64_t total_frames;
uint64_t alloc_frame(void) {
for (uint64_t i = 0; i < total_frames; i++) {
if (!test_bit(frame_bitmap, i)) {
set_bit(frame_bitmap, i);
return i * PAGE_SIZE;
}
}
return 0; // OOM
}9. Context switching
struct context {
uint64_t rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp;
uint64_t r8, r9, r10, r11, r12, r13, r14, r15;
uint64_t rip;
};
void switch_context(struct context *old, struct context *new);
// Assembly: save callee-saved regs to old, restore from new, ret to new->ripCooperative scheduling first; add timer IRQ preemption later.
10. xv6-RISC-V reference
git clone https://github.com/mit-pdos/xv6-riscv cd xv6-riscv && make qemu
| xv6 component | x86 equivalent | |---------------|----------------| | `kernel/vm.c` | Page table management | | `kernel/trap.c` | IDT/interrupt dispatch | | `kernel/proc.c` | Context switch, scheduler | | `kernel/plic.c` | PIC/APIC interrupt controller | | `user/usys.pl` | System call stubs |
Common Problems
| Symptom | Cause | Fix | |---------|-------|-----| | Triple fault on boot | Invalid GDT/IDT or stack | S
A curated suite of AI agent skills for systems and low-level programming — C/C++, Rust, Zig, GPU, bare-metal firmware, Linux kernel/driver development, computer architecture, compiler internals, HPC, and more.
Repo: mohitmishra786/low-level-dev-skills
Other skills on low-level-dev-skills.
- /custom-allocators
Custom allocator skill for memory allocation strategies. Use when implementing pool/slab/arena allocators, tuning jemalloc/mimalloc, writing Rust GlobalAlloc, or benchmarking allocator performance. Activates on queries about jemalloc, mimalloc, tcmalloc, arena allocator,
Open skill - /numa-programming
NUMA programming skill for multi-socket memory locality. Use when detecting NUMA topology, binding processes with numactl, using libnuma API, building NUMA-aware data structures, or measuring remote access penalties. Activates on queries about numactl, libnuma, NUMA topology,
Open skill - /af-xdp
AF_XDP skill for high-performance XDP sockets. Use when creating AF_XDP sockets, configuring UMEM and XSK rings, XDP_REDIRECT programs, copy vs zero-copy mode, or comparing with DPDK. Activates on queries about AF_XDP, xsk_umem, XDP_REDIRECT, libbpf xsk, or zero-copy XDP.
Open skill - /dpdk
DPDK skill for userspace packet I/O. Use when initializing EAL, configuring PMD drivers, using mbuf pools and rte_ring, setting up huge pages, RSS, or testpmd validation. Activates on queries about DPDK, EAL, rte_eth_rx_burst, hugepages, PMD, or testpmd.
Open skill - /io-uring
io_uring skill for Linux async I/O. Use when building high-performance servers with liburing, multi-shot operations, provided buffers, fixed files, zero-copy send, or tokio-uring. Activates on queries about io_uring, SQE/CQE, liburing, IORING_OP_PROVIDE_BUFFERS, or io_uring vs
Open skill - /adc-dac-baremetal
Bare-metal ADC and DAC skill. Use when configuring analog sampling, DMA-driven ADC, calibration, or DAC output on MCUs. Activates on queries about ADC bare-metal, sampling time, DMA ADC, or DAC channel setup.
Open skill

