<?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: Ícaro</title>
    <description>The latest articles on DEV Community by Ícaro (@icarotelesdasilva).</description>
    <link>https://dev.to/icarotelesdasilva</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%2F4123778%2F9278b1fc-d113-45a7-907e-42ab725ea57e.jpeg</url>
      <title>DEV Community: Ícaro</title>
      <link>https://dev.to/icarotelesdasilva</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/icarotelesdasilva"/>
    <language>en</language>
    <item>
      <title>Building NexisK: A Technical Deep Dive into My i386 Kernel</title>
      <dc:creator>Ícaro</dc:creator>
      <pubDate>Mon, 14 Sep 2026 02:41:12 +0000</pubDate>
      <link>https://dev.to/icarotelesdasilva/building-nexisk-a-technical-deep-dive-into-my-i386-kernel-47ai</link>
      <guid>https://dev.to/icarotelesdasilva/building-nexisk-a-technical-deep-dive-into-my-i386-kernel-47ai</guid>
      <description>&lt;p&gt;Building NexisK: Inside My i386 Kernel, Custom Bootloader and Memory Management&lt;/p&gt;

&lt;p&gt;Building an operating system kernel from scratch is not primarily about displaying text on a screen. It is about establishing a controlled execution environment on hardware that initially knows nothing about my kernel.&lt;/p&gt;

&lt;p&gt;Before my code can manage memory, receive interrupts, execute processes or provide system calls, it must first solve a more fundamental problem: how to take control of the machine reliably.&lt;/p&gt;

&lt;p&gt;That is the problem I am working on with NexisK, an experimental 32-bit x86 kernel built from scratch using C and NASM assembly.&lt;/p&gt;

&lt;p&gt;The project currently targets the i386 architecture, uses a custom BIOS bootloader, enters protected mode, discovers physical memory through BIOS E820, initializes interrupt infrastructure and contains the early foundations of physical memory management, system calls and process execution.&lt;/p&gt;

&lt;p&gt;This article explains the architecture, the decisions behind it, the current implementation and the engineering lessons I have learned while building it.&lt;/p&gt;

&lt;p&gt;The goal is not to present NexisK as a finished operating system. It is to show what exists, what works, what remains incomplete and why each subsystem matters.&lt;/p&gt;

&lt;p&gt;Introduction&lt;/p&gt;

&lt;p&gt;My current release is NexisK v0.8.9.&lt;/p&gt;

&lt;p&gt;The project is organized around a simple principle: implement the mechanisms that an operating system depends on instead of hiding them behind an existing operating-system framework.&lt;/p&gt;

&lt;p&gt;NexisK currently includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A custom two-stage BIOS bootloader.&lt;/li&gt;
&lt;li&gt;32-bit i386 protected-mode execution.&lt;/li&gt;
&lt;li&gt;Global Descriptor Table initialization.&lt;/li&gt;
&lt;li&gt;Interrupt Descriptor Table infrastructure.&lt;/li&gt;
&lt;li&gt;CPU exception handling.&lt;/li&gt;
&lt;li&gt;Programmable Interrupt Controller configuration.&lt;/li&gt;
&lt;li&gt;Programmable Interval Timer support.&lt;/li&gt;
&lt;li&gt;Keyboard and PS/2 mouse interrupt handling.&lt;/li&gt;
&lt;li&gt;VGA text output.&lt;/li&gt;
&lt;li&gt;Serial debugging output.&lt;/li&gt;
&lt;li&gt;Basic system-call infrastructure.&lt;/li&gt;
&lt;li&gt;BIOS E820 physical memory discovery.&lt;/li&gt;
&lt;li&gt;An initial Physical Memory Manager.&lt;/li&gt;
&lt;li&gt;Initial process and context-switching infrastructure.&lt;/li&gt;
&lt;li&gt;A preliminary userspace directory structure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The kernel is written primarily in C, with NASM assembly used where direct control over processor state and machine instructions is required.&lt;/p&gt;

&lt;p&gt;The repository contains 33 commits at the time of writing, and the current release tracks the development milestone v0.8.9.&lt;/p&gt;

&lt;p&gt;Source code:&lt;/p&gt;

&lt;p&gt;GitHub: NexisK&lt;/p&gt;

&lt;p&gt;Why I Built a Kernel from Scratch&lt;/p&gt;

&lt;p&gt;There are many ways to build software for a computer. Most application development starts with an operating system that already provides memory management, process isolation, drivers, filesystems and hardware abstractions.&lt;/p&gt;

&lt;p&gt;Kernel development starts before those services exist.&lt;/p&gt;

&lt;p&gt;I wanted to understand the boundary between software and hardware directly.&lt;/p&gt;

&lt;p&gt;What happens when the processor begins executing my boot code?&lt;/p&gt;

&lt;p&gt;How does the CPU move from firmware execution into protected mode?&lt;/p&gt;

&lt;p&gt;How does a kernel discover which physical memory is available?&lt;/p&gt;

&lt;p&gt;How does an interrupt reach the correct handler?&lt;/p&gt;

&lt;p&gt;How does a system call cross from a process into kernel code?&lt;/p&gt;

&lt;p&gt;These questions are connected. A failure in one layer can prevent every layer above it from working.&lt;/p&gt;

&lt;p&gt;For example, a broken GDT can prevent protected-mode execution. An incorrect IDT can cause exceptions to jump to invalid addresses. A faulty memory manager can overwrite kernel structures. A bad context switch can corrupt the execution state of a process.&lt;/p&gt;

&lt;p&gt;The architecture must therefore be developed from the bottom upward.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             APPLICATIONS
                  │
                  ▼
              USERSPACE
                  │
                  ▼
             SYSTEM CALLS
                  │
                  ▼
             PROCESS MODEL
                  │
                  ▼
            VIRTUAL MEMORY
                  │
                  ▼
           PHYSICAL MEMORY
                  │
                  ▼
                CPU
                  │
                  ▼
              HARDWARE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is the reason I chose to build the kernel instead of starting with a higher-level operating-system framework.&lt;/p&gt;

&lt;p&gt;Architecture&lt;/p&gt;

&lt;p&gt;NexisK is a 32-bit x86 kernel. Its current execution environment is i386 protected mode.&lt;/p&gt;

&lt;p&gt;The architecture is divided into several subsystems:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                     NexisK
                       │
      ┌────────────────┼────────────────┐
      │                │                │
      ▼                ▼                ▼
    CPU            Interrupts         Drivers
      │                │                │
      └────────────────┼────────────────┘
                       │
                       ▼
               Memory Discovery
                       │
                       ▼
              Physical Memory
                 Management
                       │
                       ▼
                Virtual Memory
                       │
                       ▼
                  Processes
                       │
                       ▼
                System Calls
                       │
                       ▼
                   Userspace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is the intended dependency direction, not a claim that every layer is complete.&lt;/p&gt;

&lt;p&gt;The current kernel has an initial physical memory manager, while virtual memory and process management remain under development.&lt;/p&gt;

&lt;p&gt;Technical Configuration&lt;/p&gt;

&lt;p&gt;Component   Implementation&lt;br&gt;
Architecture    i386 / x86-32&lt;br&gt;
CPU mode    Protected mode&lt;br&gt;
Kernel language C&lt;br&gt;
Assembly    NASM&lt;br&gt;
Bootloader  Custom BIOS bootloader&lt;br&gt;
Boot structure  Stage 1 + Stage 2&lt;br&gt;
Memory discovery    BIOS E820&lt;br&gt;
Interrupt controller    PIC&lt;br&gt;
Timer   PIT&lt;br&gt;
Display VGA text mode&lt;br&gt;
Debugging   Serial output&lt;br&gt;
System calls    int 0x80&lt;br&gt;
Emulator    QEMU&lt;br&gt;
Build system    GNU Make&lt;br&gt;
License GPL-2.0-only&lt;/p&gt;

&lt;p&gt;The kernel is not currently a complete operating-system distribution. It does not provide a complete filesystem ecosystem, production-ready userspace or a Linux-like environment.&lt;/p&gt;

&lt;p&gt;That distinction matters because the engineering requirements of a kernel and a complete operating system are different.&lt;/p&gt;

&lt;p&gt;The Bootloader&lt;/p&gt;

&lt;p&gt;The bootloader is the first major component of NexisK.&lt;/p&gt;

&lt;p&gt;Instead of relying on GRUB or Limine, I maintain a separate custom BIOS bootloader responsible for preparing the machine and loading the kernel.&lt;/p&gt;

&lt;p&gt;The boot process is divided into two stages.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                     BIOS
                       │
                       ▼
                   Stage 1
                       │
                       ▼
                   Stage 2
                       │
        ┌──────────────┼──────────────┐
        │              │              │
        ▼              ▼              ▼
    Initialize      Boot Menu       E820
    Environment      Selection      Detection
        │              │              │
        └──────────────┼──────────────┘
                       │
                       ▼
                 Kernel Loading
                       │
                       ▼
                    NexisK
                       │
                       ▼
                      kmain
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Stage 1 is the initial boot sector. Its job is to begin execution and load Stage 2.&lt;/p&gt;

&lt;p&gt;Stage 2 handles the more substantial boot work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Boot initialization.&lt;/li&gt;
&lt;li&gt;Kernel selection.&lt;/li&gt;
&lt;li&gt;Memory map detection.&lt;/li&gt;
&lt;li&gt;Kernel loading.&lt;/li&gt;
&lt;li&gt;Transfer of control to the kernel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bootloader also generates the bootable disk image and ISO used by the project.&lt;/p&gt;

&lt;p&gt;The current disk-image layout is based on a 1.44 MB floppy-style image, which is then packaged as an El Torito bootable ISO.&lt;/p&gt;

&lt;p&gt;build/&lt;br&gt;
├── stage1.bin&lt;br&gt;
├── stage2.bin&lt;br&gt;
├── disk.img&lt;br&gt;
├── iso/&lt;br&gt;
│   └── boot.img&lt;br&gt;
└── NexisK.iso&lt;/p&gt;

&lt;p&gt;Why Separate the Bootloader from the Kernel?&lt;/p&gt;

&lt;p&gt;The bootloader and kernel solve different problems.&lt;/p&gt;

&lt;p&gt;The bootloader must understand the firmware environment, load the kernel and provide the information required for kernel initialization.&lt;/p&gt;

&lt;p&gt;The kernel must establish its own runtime environment and manage the machine after control is transferred.&lt;/p&gt;

&lt;p&gt;Keeping these components separate makes their responsibilities clearer.&lt;/p&gt;

&lt;p&gt;It also makes the bootloader reusable. A boot manager should eventually be able to load more than one kernel, expose hardware information and provide a stable interface between firmware and operating-system code.&lt;/p&gt;

&lt;p&gt;That is one of the reasons I maintain the bootloader as a separate project.&lt;/p&gt;

&lt;p&gt;CPU Initialization and Protected Mode&lt;/p&gt;

&lt;p&gt;NexisK targets the i386 architecture and executes in 32-bit protected mode.&lt;/p&gt;

&lt;p&gt;Protected mode changes the environment in which the kernel executes. Segment descriptors, privilege levels and interrupt handling become central parts of the system.&lt;/p&gt;

&lt;p&gt;The Global Descriptor Table provides the segment descriptors used by the CPU.&lt;/p&gt;

&lt;p&gt;The current kernel contains GDT initialization and the basic protected-mode execution environment.&lt;/p&gt;

&lt;p&gt;The general transition is:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              Real Mode
                  │
                  ▼
          Load GDT Descriptor
                  │
                  ▼
          Enable Protected Mode
                  │
                  ▼
            Far Jump
                  │
                  ▼
          32-bit Kernel Code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The GDT is not an optional decoration. The processor needs valid segment configuration to execute protected-mode code correctly.&lt;/p&gt;

&lt;p&gt;This is one of the first places where a kernel stops behaving like an ordinary C program.&lt;/p&gt;

&lt;p&gt;The compiler can generate C instructions, but it cannot independently establish the processor environment in which those instructions are valid.&lt;/p&gt;

&lt;p&gt;That work belongs to the low-level initialization code.&lt;/p&gt;

&lt;p&gt;What I Learned&lt;/p&gt;

&lt;p&gt;A kernel cannot treat CPU initialization as a single function that is called and forgotten.&lt;/p&gt;

&lt;p&gt;The processor state, descriptor tables and execution mode determine whether the rest of the kernel can run.&lt;/p&gt;

&lt;p&gt;A useful debugging strategy is to verify each transition independently:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Confirm that the bootloader executes.&lt;/li&gt;
&lt;li&gt;Confirm that Stage 2 executes.&lt;/li&gt;
&lt;li&gt;Confirm that the kernel is loaded at the expected address.&lt;/li&gt;
&lt;li&gt;Confirm that protected mode is entered.&lt;/li&gt;
&lt;li&gt;Confirm that the kernel reaches kmain.&lt;/li&gt;
&lt;li&gt;Only then begin debugging higher-level initialization.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This reduces the number of possible causes when the machine stops responding.&lt;/p&gt;

&lt;p&gt;Interrupt Architecture&lt;/p&gt;

&lt;p&gt;Interrupts are the mechanism through which the processor reacts to events that are not part of the current sequential instruction flow.&lt;/p&gt;

&lt;p&gt;NexisK contains infrastructure for CPU exceptions and hardware interrupts.&lt;/p&gt;

&lt;p&gt;The main components are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interrupt Descriptor Table.&lt;/li&gt;
&lt;li&gt;CPU exception handlers.&lt;/li&gt;
&lt;li&gt;Programmable Interrupt Controller.&lt;/li&gt;
&lt;li&gt;Programmable Interval Timer.&lt;/li&gt;
&lt;li&gt;Keyboard interrupt handler.&lt;/li&gt;
&lt;li&gt;PS/2 mouse interrupt handler.&lt;/li&gt;
&lt;li&gt;System-call interrupt vector.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The IDT associates interrupt vectors with handler entry points.&lt;/p&gt;

&lt;p&gt;The PIC is responsible for managing hardware interrupt requests.&lt;/p&gt;

&lt;p&gt;The PIT provides timer events.&lt;/p&gt;

&lt;p&gt;Together, these components establish the foundation required for scheduling, input handling and process management.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                CPU Exception
                      │
                      ▼
                   IDT Entry
                      │
                      ▼
                Exception Handler
                      │
                      ▼
                Kernel Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For hardware interrupts:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             Hardware Device
                    │
                    ▼
                   IRQ
                    │
                    ▼
                   PIC
                    │
                    ▼
                Interrupt Vector
                    │
                    ▼
                   IDT
                    │
                    ▼
                IRQ Handler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Interrupt Vector Configuration&lt;/p&gt;

&lt;p&gt;The kernel uses the standard PIC remapping layout:&lt;/p&gt;

&lt;p&gt;Master PIC IRQs: 0x20 - 0x27&lt;br&gt;
Slave PIC IRQs:  0x28 - 0x2F&lt;/p&gt;

&lt;p&gt;This separates hardware interrupt vectors from the processor exception vectors.&lt;/p&gt;

&lt;p&gt;The distinction is important because exceptions and hardware IRQs have different origins and different handling requirements.&lt;/p&gt;

&lt;p&gt;The kernel also provides serial output, which is particularly useful when debugging interrupt delivery in QEMU.&lt;/p&gt;

&lt;p&gt;VGA output is useful for seeing what is happening on the screen. Serial output is useful for recording execution progress, memory-map entries and diagnostic information without relying exclusively on the display.&lt;/p&gt;

&lt;p&gt;Memory Discovery with BIOS E820&lt;/p&gt;

&lt;p&gt;Physical memory management cannot begin until the kernel knows which physical addresses are available.&lt;/p&gt;

&lt;p&gt;NexisK uses the BIOS INT 15h, E820h interface to discover the physical memory map.&lt;/p&gt;

&lt;p&gt;The bootloader collects the memory regions and exposes the resulting map to the kernel.&lt;/p&gt;

&lt;p&gt;The kernel then processes the entries and identifies usable regions.&lt;/p&gt;

&lt;p&gt;A representative memory map reported by NexisK contains the following entries:&lt;/p&gt;

&lt;p&gt;Base: 0x0000000000000000&lt;br&gt;
Size: 0x000000000009FC00&lt;br&gt;
Type: 0x00000001&lt;br&gt;
Base: 0x000000000009FC00&lt;br&gt;
Size: 0x0000000000000400&lt;br&gt;
Type: 0x00000002&lt;br&gt;
Base: 0x00000000000F0000&lt;br&gt;
Size: 0x0000000000010000&lt;br&gt;
Type: 0x00000002&lt;br&gt;
Base: 0x0000000000100000&lt;br&gt;
Size: 0x0000000007EE0000&lt;br&gt;
Type: 0x00000001&lt;br&gt;
Base: 0x0000000007FE0000&lt;br&gt;
Size: 0x0000000000020000&lt;br&gt;
Type: 0x00000002&lt;br&gt;
Base: 0x00000000FFFC0000&lt;br&gt;
Size: 0x0000000000040000&lt;br&gt;
Type: 0x00000002&lt;/p&gt;

&lt;p&gt;The two usable regions in this example are:&lt;/p&gt;

&lt;p&gt;Region 1:&lt;br&gt;
Base = 0x00000000&lt;br&gt;
Size = 0x0009FC00&lt;br&gt;
Region 2:&lt;br&gt;
Base = 0x00100000&lt;br&gt;
Size = 0x07EE0000&lt;/p&gt;

&lt;p&gt;The second region begins at the 1 MiB boundary, which is a common location for usable extended memory in BIOS-based systems.&lt;/p&gt;

&lt;p&gt;The E820 memory map is important because physical memory is not necessarily one continuous block.&lt;/p&gt;

&lt;p&gt;Firmware-reserved areas, hardware memory regions and other non-usable ranges must not be treated as allocatable RAM.&lt;/p&gt;

&lt;p&gt;A physical memory manager that simply assumes every address below the detected RAM limit is free will eventually overwrite something important.&lt;/p&gt;

&lt;p&gt;Physical Memory Manager&lt;/p&gt;

&lt;p&gt;The Physical Memory Manager is one of the most important subsystems currently being developed in NexisK.&lt;/p&gt;

&lt;p&gt;Its responsibility is to track physical pages and provide the foundation for future virtual memory management.&lt;/p&gt;

&lt;p&gt;The current implementation consumes the E820 map and identifies usable physical memory regions.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             BIOS E820
                 │
                 ▼
          Memory Map Entries
                 │
                 ▼
              pmm_init()
                 │
                 ▼
          Filter Type 1 Regions
                 │
                 ▼
           Calculate Pages
                 │
                 ▼
         Calculate Page Addresses
                 │
                 ▼
          Calculate Bitmap Index
                 │
                 ▼
         Mark Usable Pages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The PMM processes usable regions in increments of 4 KiB.&lt;/p&gt;

&lt;p&gt;That page size is important because it is the standard x86 page granularity used by the paging architecture.&lt;/p&gt;

&lt;p&gt;The current bitmap representation uses one byte per physical page.&lt;/p&gt;

&lt;p&gt;Bitmap Entry:&lt;br&gt;
0 = Free&lt;br&gt;
1 = Reserved / Occupied&lt;/p&gt;

&lt;p&gt;Each bitmap entry corresponds to one 4 KiB physical page.&lt;/p&gt;

&lt;p&gt;This is an initial representation, not the final memory-management design.&lt;/p&gt;

&lt;p&gt;Bitmap Overhead&lt;/p&gt;

&lt;p&gt;A byte-per-page bitmap is simple to understand, but it consumes more metadata than a packed bit bitmap.&lt;/p&gt;

&lt;p&gt;For example, managing 1 GiB of physical memory with 4 KiB pages requires:&lt;/p&gt;

&lt;p&gt;1 GiB / 4 KiB = 262,144 pages&lt;/p&gt;

&lt;p&gt;With one byte per page:&lt;/p&gt;

&lt;p&gt;262,144 bytes = 256 KiB&lt;/p&gt;

&lt;p&gt;With one bit per page:&lt;/p&gt;

&lt;p&gt;262,144 bits = 32 KiB&lt;/p&gt;

&lt;p&gt;The byte-per-page representation therefore uses eight times more metadata than a one-bit-per-page representation.&lt;/p&gt;

&lt;p&gt;I chose the simpler representation for the initial implementation because it makes page-state inspection and debugging straightforward.&lt;/p&gt;

&lt;p&gt;Memory efficiency matters, but correctness matters first.&lt;/p&gt;

&lt;p&gt;The current PMM still requires additional work for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complete bitmap initialization.&lt;/li&gt;
&lt;li&gt;Kernel memory reservation.&lt;/li&gt;
&lt;li&gt;Bootloader memory reservation.&lt;/li&gt;
&lt;li&gt;Bitmap memory reservation.&lt;/li&gt;
&lt;li&gt;Physical page allocation.&lt;/li&gt;
&lt;li&gt;Physical page freeing.&lt;/li&gt;
&lt;li&gt;More precise handling of memory-map boundaries.&lt;/li&gt;
&lt;li&gt;Integration with the Virtual Memory Manager.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lessons from Physical Memory Management&lt;/p&gt;

&lt;p&gt;The biggest lesson is that discovering memory and managing memory are different problems.&lt;/p&gt;

&lt;p&gt;E820 tells the kernel which regions exist and which regions are usable.&lt;/p&gt;

&lt;p&gt;The PMM must then maintain the state of individual physical pages.&lt;/p&gt;

&lt;p&gt;That means it must know which pages are free, which are reserved and which are currently allocated.&lt;/p&gt;

&lt;p&gt;The PMM cannot safely allocate memory until its own metadata is protected.&lt;/p&gt;

&lt;p&gt;This creates a dependency:&lt;/p&gt;

&lt;p&gt;Memory Discovery&lt;br&gt;
      │&lt;br&gt;
      ▼&lt;br&gt;
PMM Metadata&lt;br&gt;
      │&lt;br&gt;
      ▼&lt;br&gt;
Reserve Kernel and Boot Memory&lt;br&gt;
      │&lt;br&gt;
      ▼&lt;br&gt;
Physical Page Allocation&lt;br&gt;
      │&lt;br&gt;
      ▼&lt;br&gt;
Virtual Memory&lt;/p&gt;

&lt;p&gt;The metadata used to manage memory is itself stored in memory. The kernel must therefore reserve and protect its own bookkeeping structures.&lt;/p&gt;

&lt;p&gt;This is one of the first places where operating-system development becomes a problem of managing the resources used to manage resources.&lt;/p&gt;

&lt;p&gt;Virtual Memory and Paging&lt;/p&gt;

&lt;p&gt;The Virtual Memory Manager is planned to be built on top of the physical memory manager.&lt;/p&gt;

&lt;p&gt;The current VMM is not considered complete.&lt;/p&gt;

&lt;p&gt;The intended functionality includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Paging.&lt;/li&gt;
&lt;li&gt;Virtual address mapping.&lt;/li&gt;
&lt;li&gt;Page fault handling.&lt;/li&gt;
&lt;li&gt;Dynamic page mapping.&lt;/li&gt;
&lt;li&gt;Kernel/user memory permissions.&lt;/li&gt;
&lt;li&gt;Per-process address spaces.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The relationship between physical and virtual memory is fundamental.&lt;/p&gt;

&lt;p&gt;The PMM tracks physical pages.&lt;/p&gt;

&lt;p&gt;The VMM controls how virtual addresses map to those physical pages.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             Virtual Address
                    │
                    ▼
                Page Tables
                    │
                    ▼
             Physical Address
                    │
                    ▼
              Physical Memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Without a working physical memory allocator, the VMM cannot reliably create new mappings.&lt;/p&gt;

&lt;p&gt;Without a working VMM, process isolation and independent address spaces are difficult to implement correctly.&lt;/p&gt;

&lt;p&gt;This is why I am treating memory management as a staged subsystem instead of trying to implement every feature at once.&lt;/p&gt;

&lt;p&gt;Process Infrastructure&lt;/p&gt;

&lt;p&gt;NexisK contains initial process-related infrastructure and context-switching groundwork.&lt;/p&gt;

&lt;p&gt;This is not yet a complete multitasking system.&lt;/p&gt;

&lt;p&gt;The intended process subsystem includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Process creation.&lt;/li&gt;
&lt;li&gt;Process destruction.&lt;/li&gt;
&lt;li&gt;PID management.&lt;/li&gt;
&lt;li&gt;Process address spaces.&lt;/li&gt;
&lt;li&gt;Context switching.&lt;/li&gt;
&lt;li&gt;Scheduler.&lt;/li&gt;
&lt;li&gt;Preemptive multitasking.&lt;/li&gt;
&lt;li&gt;Process isolation.&lt;/li&gt;
&lt;li&gt;Userspace execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A process is more than a function call.&lt;/p&gt;

&lt;p&gt;A process requires an execution context, a stack, an address space and a mechanism for switching between execution contexts.&lt;/p&gt;

&lt;p&gt;A simplified process transition looks like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          Current Process
                │
                ▼
          Save CPU Context
                │
                ▼
         Select Next Process
                │
                ▼
          Restore CPU Context
                │
                ▼
           Next Process
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The context-switching infrastructure is only one part of the complete process model.&lt;/p&gt;

&lt;p&gt;A scheduler must decide when a process runs. The memory subsystem must provide the required address-space behavior. The interrupt subsystem must provide a mechanism for preemption or other scheduling events.&lt;/p&gt;

&lt;p&gt;This is another example of why kernel subsystems cannot be developed as completely isolated features.&lt;/p&gt;

&lt;p&gt;They have interfaces, but their correctness depends on the behavior of the other components.&lt;/p&gt;

&lt;p&gt;System Calls&lt;/p&gt;

&lt;p&gt;NexisK contains a basic system-call mechanism using the int 0x80 instruction.&lt;/p&gt;

&lt;p&gt;The syscall number is passed through the EAX register.&lt;/p&gt;

&lt;p&gt;The current interface is intentionally minimal and is primarily used to validate the system-call path.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              Execution Context
                     │
                     ▼
                  int 0x80
                     │
                     ▼
                  IDT[0x80]
                     │
                     ▼
               Syscall Handler
                     │
                     ▼
                Kernel Syscall
                     │
                     ▼
                    iret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The system-call mechanism establishes the basic path from an execution context into kernel code.&lt;/p&gt;

&lt;p&gt;The interface will need to evolve alongside process management, privilege levels and userspace support.&lt;/p&gt;

&lt;p&gt;A production-quality syscall ABI requires much more than a single interrupt vector.&lt;/p&gt;

&lt;p&gt;It needs defined calling conventions, argument validation, return values, error handling and a stable interface between user programs and the kernel.&lt;/p&gt;

&lt;p&gt;The current implementation is the beginning of that path, not a complete syscall subsystem.&lt;/p&gt;

&lt;p&gt;Kernel I/O and Drivers&lt;/p&gt;

&lt;p&gt;The kernel currently provides basic low-level I/O through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VGA text output.&lt;/li&gt;
&lt;li&gt;Serial output.&lt;/li&gt;
&lt;li&gt;Keyboard input.&lt;/li&gt;
&lt;li&gt;PS/2 mouse input.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These drivers are small, but they are essential for making the kernel observable.&lt;/p&gt;

&lt;p&gt;A kernel without reliable output is difficult to debug.&lt;/p&gt;

&lt;p&gt;A kernel without input cannot meaningfully interact with a user.&lt;/p&gt;

&lt;p&gt;The VGA driver provides visible text output in the traditional text-mode environment.&lt;/p&gt;

&lt;p&gt;The serial driver provides diagnostic output that can be captured externally.&lt;/p&gt;

&lt;p&gt;The keyboard and mouse handlers provide the first layer of device interaction.&lt;/p&gt;

&lt;p&gt;The current hardware support is deliberately limited. Storage drivers, filesystem support and additional device drivers remain future work.&lt;/p&gt;

&lt;p&gt;Real Hardware Testing&lt;/p&gt;

&lt;p&gt;QEMU is extremely useful for kernel development, but it is not a replacement for physical hardware.&lt;/p&gt;

&lt;p&gt;An emulator provides a controlled environment. It allows me to reproduce boot behavior, inspect execution and collect diagnostic information.&lt;/p&gt;

&lt;p&gt;NexisK uses QEMU as its primary emulation environment and also includes real hardware boot validation in its development process.&lt;/p&gt;

&lt;p&gt;The build system provides:&lt;/p&gt;

&lt;p&gt;make&lt;/p&gt;

&lt;p&gt;to build the kernel and bootable image.&lt;/p&gt;

&lt;p&gt;To run the kernel in QEMU:&lt;/p&gt;

&lt;p&gt;make run&lt;/p&gt;

&lt;p&gt;For deeper QEMU diagnostics:&lt;/p&gt;

&lt;p&gt;make dev&lt;/p&gt;

&lt;p&gt;The debug configuration writes additional information to:&lt;/p&gt;

&lt;p&gt;build/qemu.log&lt;/p&gt;

&lt;p&gt;This is useful for investigating:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU resets.&lt;/li&gt;
&lt;li&gt;Interrupt behavior.&lt;/li&gt;
&lt;li&gt;Guest errors.&lt;/li&gt;
&lt;li&gt;Unimplemented instructions.&lt;/li&gt;
&lt;li&gt;MMU activity.&lt;/li&gt;
&lt;li&gt;Protected-mode execution.&lt;/li&gt;
&lt;li&gt;Kernel execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why Hardware Testing Matters&lt;/p&gt;

&lt;p&gt;A kernel can behave correctly in an emulator and still fail on a physical machine.&lt;/p&gt;

&lt;p&gt;Firmware implementations differ.&lt;/p&gt;

&lt;p&gt;Hardware initialization differs.&lt;/p&gt;

&lt;p&gt;Memory maps differ.&lt;/p&gt;

&lt;p&gt;Devices differ.&lt;/p&gt;

&lt;p&gt;The boot path may encounter assumptions that were never exposed in the emulator.&lt;/p&gt;

&lt;p&gt;For NexisK, this matters particularly because the bootloader interacts directly with BIOS services and hardware.&lt;/p&gt;

&lt;p&gt;Real hardware testing is therefore not merely a demonstration that the kernel boots. It is a way to discover assumptions about the machine that are not visible in a controlled emulated environment.&lt;/p&gt;

&lt;p&gt;The current project documentation records real hardware boot validation, but it does not provide a complete compatibility matrix across different machines.&lt;/p&gt;

&lt;p&gt;I do not consider the current bootloader universally compatible with all x86 hardware.&lt;/p&gt;

&lt;p&gt;Performance&lt;/p&gt;

&lt;p&gt;NexisK is currently a kernel-development project, not a performance benchmark suite.&lt;/p&gt;

&lt;p&gt;The repository does not currently provide measured syscall latency, interrupt latency, context-switch timing, memory-allocation throughput or boot-time benchmarks.&lt;/p&gt;

&lt;p&gt;I will not invent those numbers.&lt;/p&gt;

&lt;p&gt;The current measurable implementation details are architectural:&lt;/p&gt;

&lt;p&gt;Metric  Current Value&lt;br&gt;
Target architecture i386&lt;br&gt;
CPU mode    32-bit protected mode&lt;br&gt;
Page size   4 KiB&lt;br&gt;
PMM bitmap entry size   1 byte&lt;br&gt;
Boot stages 2&lt;br&gt;
Syscall vector  0x80&lt;br&gt;
Master PIC vector range 0x20–0x27&lt;br&gt;
Slave PIC vector range  0x28–0x2F&lt;br&gt;
Current release v0.8.9&lt;br&gt;
Repository commits  33&lt;/p&gt;

&lt;p&gt;These values describe the implementation. They are not performance results.&lt;/p&gt;

&lt;p&gt;A meaningful performance evaluation will require a defined workload and a stable implementation.&lt;/p&gt;

&lt;p&gt;For example, a future PMM benchmark could measure the time required to allocate and free a fixed number of physical pages.&lt;/p&gt;

&lt;p&gt;A syscall benchmark could measure the cost of entering the kernel through int 0x80 and returning through iret.&lt;/p&gt;

&lt;p&gt;A context-switch benchmark could measure the time required to save and restore process state.&lt;/p&gt;

&lt;p&gt;Those measurements would become meaningful only after the relevant subsystems are complete enough to benchmark.&lt;/p&gt;

&lt;p&gt;Development Lessons Learned&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Bootloader Complexity Is Not the Same as Kernel Complexity&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A kernel can have a working C entry point and still be unable to boot reliably.&lt;/p&gt;

&lt;p&gt;The bootloader must establish the environment in which the kernel executes.&lt;/p&gt;

&lt;p&gt;That includes loading the kernel correctly, preserving required information and transferring control at the correct processor state.&lt;/p&gt;

&lt;p&gt;The bootloader deserves its own design and testing.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Memory Management Must Be Developed Incrementally&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The PMM cannot be treated as a single feature.&lt;/p&gt;

&lt;p&gt;Memory discovery, page tracking, reservation, allocation and freeing are separate responsibilities.&lt;/p&gt;

&lt;p&gt;Trying to implement all of them simultaneously makes debugging much harder.&lt;/p&gt;

&lt;p&gt;The current byte-per-page bitmap is intentionally simple. It allows the initial implementation to focus on page-state tracking before optimizing metadata representation.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hardware Abstraction Must Not Hide the Hardware Too Early&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The current kernel uses explicit architecture-specific code.&lt;/p&gt;

&lt;p&gt;That is appropriate for an i386 kernel because the processor’s descriptor tables, interrupt vectors and I/O mechanisms are directly relevant to the implementation.&lt;/p&gt;

&lt;p&gt;A large abstraction layer introduced too early can hide the exact state that needs to be debugged.&lt;/p&gt;

&lt;p&gt;I prefer to establish correct low-level mechanisms first and abstract them only when the interfaces are understood.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A Directory Is Not an Implementation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;NexisK includes:&lt;/p&gt;

&lt;p&gt;userspace/&lt;br&gt;
├── init/&lt;br&gt;
└── shell/&lt;/p&gt;

&lt;p&gt;These directories establish the intended separation between kernel code and future userspace programs.&lt;/p&gt;

&lt;p&gt;They do not mean that Ring 3 execution, executable loading or a complete shell already exist.&lt;/p&gt;

&lt;p&gt;The same principle applies to process management and virtual memory.&lt;/p&gt;

&lt;p&gt;A subsystem should be described according to what it actually implements, not according to what its directory structure suggests.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;QEMU and Real Hardware Solve Different Problems&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;QEMU is valuable for repeatable testing and diagnostics.&lt;/p&gt;

&lt;p&gt;Physical hardware exposes firmware and compatibility assumptions.&lt;/p&gt;

&lt;p&gt;Neither environment completely replaces the other.&lt;/p&gt;

&lt;p&gt;A useful development process uses emulation for rapid iteration and hardware testing for validation against real machines.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Kernel Development Is Dependency Management&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The kernel architecture is a dependency graph.&lt;/p&gt;

&lt;p&gt;The bootloader provides the environment.&lt;/p&gt;

&lt;p&gt;The CPU subsystem establishes execution.&lt;/p&gt;

&lt;p&gt;The interrupt subsystem provides event handling.&lt;/p&gt;

&lt;p&gt;The memory subsystem provides allocation and address translation.&lt;/p&gt;

&lt;p&gt;The process subsystem depends on memory and interrupt behavior.&lt;/p&gt;

&lt;p&gt;System calls depend on the execution and privilege model.&lt;/p&gt;

&lt;p&gt;Userspace depends on all of them.&lt;/p&gt;

&lt;p&gt;Understanding these dependencies prevents implementing higher-level features on unstable foundations.&lt;/p&gt;

&lt;p&gt;Future Development&lt;/p&gt;

&lt;p&gt;The next stages of NexisK development are centered on completing the existing architectural foundations.&lt;/p&gt;

&lt;p&gt;Bootloader&lt;/p&gt;

&lt;p&gt;The bootloader needs more robust disk access, improved hardware compatibility and eventually UEFI support.&lt;/p&gt;

&lt;p&gt;The current design is based on the traditional BIOS environment.&lt;/p&gt;

&lt;p&gt;A future x86-64 transition will also require changes to the boot architecture.&lt;/p&gt;

&lt;p&gt;Memory Management&lt;/p&gt;

&lt;p&gt;The PMM needs complete bitmap initialization, proper reservation handling, physical page allocation and freeing.&lt;/p&gt;

&lt;p&gt;The VMM needs paging, page fault handling and dynamic mapping.&lt;/p&gt;

&lt;p&gt;These are prerequisites for a reliable process model.&lt;/p&gt;

&lt;p&gt;Privilege Levels&lt;/p&gt;

&lt;p&gt;The kernel currently has protected-mode infrastructure and initial work toward privilege-level support.&lt;/p&gt;

&lt;p&gt;Full Ring 3 execution and kernel/user separation remain under development.&lt;/p&gt;

&lt;p&gt;The final privilege model must provide a reliable boundary between kernel code and userspace code.&lt;/p&gt;

&lt;p&gt;Processes and Scheduling&lt;/p&gt;

&lt;p&gt;The process subsystem needs a complete lifecycle, address spaces, context switching and scheduling.&lt;/p&gt;

&lt;p&gt;Preemptive multitasking will require coordination between the timer, interrupt handling, process state and memory management.&lt;/p&gt;

&lt;p&gt;Userspace&lt;/p&gt;

&lt;p&gt;The initial userspace directory structure exists, but the actual execution environment remains to be implemented.&lt;/p&gt;

&lt;p&gt;Future work includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Executable loading.&lt;/li&gt;
&lt;li&gt;Initial init process.&lt;/li&gt;
&lt;li&gt;Userspace system-call API.&lt;/li&gt;
&lt;li&gt;Shell execution.&lt;/li&gt;
&lt;li&gt;User-space programs.&lt;/li&gt;
&lt;li&gt;Process isolation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Storage&lt;/p&gt;

&lt;p&gt;Storage support is not currently available as a complete kernel subsystem.&lt;/p&gt;

&lt;p&gt;Future work includes disk abstraction, disk drivers, LBA support, filesystem abstraction and file operations.&lt;/p&gt;

&lt;p&gt;Get Started&lt;/p&gt;

&lt;p&gt;NexisK is currently developed and tested primarily on Linux.&lt;/p&gt;

&lt;p&gt;The required tools are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GCC.&lt;/li&gt;
&lt;li&gt;NASM.&lt;/li&gt;
&lt;li&gt;GNU Make.&lt;/li&gt;
&lt;li&gt;GNU ld.&lt;/li&gt;
&lt;li&gt;QEMU.&lt;/li&gt;
&lt;li&gt;genisoimage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On Debian or Ubuntu:&lt;/p&gt;

&lt;p&gt;sudo apt update&lt;br&gt;
sudo apt install \&lt;br&gt;
    build-essential \&lt;br&gt;
    gcc \&lt;br&gt;
    nasm \&lt;br&gt;
    make \&lt;br&gt;
    binutils \&lt;br&gt;
    qemu-system-x86 \&lt;br&gt;
    genisoimage&lt;/p&gt;

&lt;p&gt;Clone the repository:&lt;/p&gt;

&lt;p&gt;git clone &lt;a href="https://github.com/icarotelesdasilva/NexisK.git" rel="noopener noreferrer"&gt;https://github.com/icarotelesdasilva/NexisK.git&lt;/a&gt;&lt;br&gt;
cd NexisK&lt;/p&gt;

&lt;p&gt;Build the kernel and bootable ISO:&lt;/p&gt;

&lt;p&gt;make&lt;/p&gt;

&lt;p&gt;The generated files are placed in:&lt;/p&gt;

&lt;p&gt;build/&lt;/p&gt;

&lt;p&gt;The main bootable image is:&lt;/p&gt;

&lt;p&gt;build/NexisK.iso&lt;/p&gt;

&lt;p&gt;Run the kernel with QEMU:&lt;/p&gt;

&lt;p&gt;make run&lt;/p&gt;

&lt;p&gt;For deeper debugging:&lt;/p&gt;

&lt;p&gt;make dev&lt;/p&gt;

&lt;p&gt;To remove generated build artifacts:&lt;/p&gt;

&lt;p&gt;make clean&lt;/p&gt;

&lt;p&gt;A clean build can then be produced with:&lt;/p&gt;

&lt;p&gt;make&lt;/p&gt;

&lt;p&gt;Conclusion&lt;/p&gt;

&lt;p&gt;NexisK is an ongoing effort to build a 32-bit x86 kernel from the lowest levels upward.&lt;/p&gt;

&lt;p&gt;The project currently has a custom BIOS bootloader, protected-mode execution, interrupt infrastructure, hardware input, serial debugging, BIOS E820 memory discovery and an initial physical memory manager.&lt;/p&gt;

&lt;p&gt;It also contains the early foundations of system calls, process management and userspace architecture.&lt;/p&gt;

&lt;p&gt;The most important part of the project is not the number of features. It is the relationship between them.&lt;/p&gt;

&lt;p&gt;The bootloader must load the kernel correctly.&lt;/p&gt;

&lt;p&gt;The CPU must execute in a valid environment.&lt;/p&gt;

&lt;p&gt;The interrupt subsystem must deliver events reliably.&lt;/p&gt;

&lt;p&gt;The memory manager must track physical pages without corrupting kernel state.&lt;/p&gt;

&lt;p&gt;The process subsystem must preserve execution contexts.&lt;/p&gt;

&lt;p&gt;The syscall interface must establish a controlled path into the kernel.&lt;/p&gt;

&lt;p&gt;Each layer depends on the previous one.&lt;/p&gt;

&lt;p&gt;That is what makes kernel development difficult, and that is also what makes it useful.&lt;/p&gt;

&lt;p&gt;I am building NexisK to understand these mechanisms directly, with the implementation exposed instead of hidden behind a finished operating system.&lt;/p&gt;

&lt;p&gt;The project is still evolving. Its current limitations are part of the engineering work, not something to conceal.&lt;/p&gt;

&lt;p&gt;If you are interested in operating-system development, x86 architecture, C, assembly programming or bare-metal systems programming, the source code is available.&lt;/p&gt;

&lt;p&gt;Explore the project, inspect the implementation and follow the development.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: NexisK&lt;/li&gt;
&lt;li&gt;GitHub profile: icarotelesdasilva&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are working on your own kernel, I would be interested in comparing implementation approaches, discussing memory management or examining how different kernels handle the hardware/software boundary.&lt;/p&gt;

&lt;p&gt;The source is open. The architecture is still being built.&lt;/p&gt;

&lt;p&gt;And the next layer is always waiting.&lt;/p&gt;

</description>
      <category>osdev</category>
      <category>kernel</category>
      <category>x86</category>
      <category>c</category>
    </item>
  </channel>
</rss>
