make # or make debug for reg dumping
vmm/vmm.elf kernel/kernel.bin user/orw.elf /etc/os-releaseKVM = VM implemented natively in Linux kernel.
A VM needs user memory region + virtual CPUs.
VMs created has private virtualized hardware e.g., network card, disk, graphic adapter, etc.
Steps: 1. Open KVM -> 2. Create a VM file descriptor -> 3. Set up memory for VM guest with ioctl -> 4. Create vCPU -> 5. Map memory for the vCPU -> 6. Put assembled code to user memory region -> 7. Set up vCPU registers -> 8. Run and handle exit reason
The kernel in this repository is a single-process, x86-64 guest kernel designed to run inside a KVM-based virtual machine.
The kernel is not bare-metal, since it depends on the VMM to prepare memory, CPU state, host I/O interface.
The kernel initializes its space (page tables, perms) -> Map memory for user access at SP -> Copy args (program name, args, env vars) from the user program -> Execute the program (Load binary with the args on top of the kernel stack -> Guest kernel calls to VMM with syscalls)
Linker script loads executable code at address 0x0 (top of the kernel stack), and global/static variables at 0x3000.
How CPU distinguishes between kernel-mode and user-mode -> Controlled by description privilege level (rings). The CPU has two privilege contexts inside the VM: Guest kernel mode (ring 0) and guest user mode (ring 3).
When the user programs invokes system calls, they use SYSCALL to go to guest ring 0, after that the kernel uses SYSRETQ to enter ring 3.
We use the special register efer (collection of binary switches) to enable syscall/sysenter instruction.
// Switch on Long Mode Enable and Long Mode Activated with two 1 bits
sregs->efer = 0x500; // EFER_LME | EFER_LMA
// Switch on syscall as well with LME and LMA
// 0101 0000 0000
// |0000 0000 0001
// ----------------
// 0101 0000 0001 (last bit is syscall)
sregs->efer |= 0x1; // EFER_SCEFor syscalls, we register the syscall handler via setting special registers named MSR (Model Specific Registers). After the setup, we can invoke the syscall instruction and the program will jump to the handler we registered.
Each PDE is a 64-bit value with control bits in lower positions + physical address in upper bits
[Physical Address] [Reserved] [Flags like PRESENT, RW, USER, DIRTY, etc.]
bits 63-12 bits 11-9 bits 8-0
A storage structure to organize freed memory blocks using structured lists.
The heap manager categorizes free memory chunks to distinct bins based on their sizes.
Two modes (user vs. kernel) are distinguished by the dpl (ring)
A kernel virtual address points to a location inside the kernel address space. The addresses are used to access system data and hardware.
The page tables (MMU) translate the virtual addresses to the physical ones BEFORE the processor reads/writes memory.
Standard binary format for executables, shared libs and object files on UNIX-like systems.
ELF header (overall structure) and Program header (How to load segment into memory)
ELF File Structure:
┌─────────────────────┐
│ Main Header (1) │ ← Describes file overall
├─────────────────────┤
│ Program Header (1) │ ← "Load this segment here"
│ Program Header (2) │ ← "Load this segment here"
│ Program Header (3) │ ← "Load this segment here"
├─────────────────────┤
│ Segment Data │
│ (Code, Data, etc.) │
└─────────────────────┘
↓ Loader reads program headers ↓
Memory Layout (Virtual Address Space):
┌──────────────────────┐
│ ... empty space ... │
├──────────────────────┤
│ 0x400000: Segment 1 │ ← Placed here per Program Header 1
│ (code/data) │
├──────────────────────┤
│ ... gap ... │
├──────────────────────┤
│ 0x600000: Segment 2 │ ← Placed here per Program Header 2
│ (code/data) │
├──────────────────────┤
│ ... more memory ... │
└──────────────────────┘
ASLR (Address Space Layout Randomization) is a security technique that randomizes where code, data, and stack are placed in a process's memory each time it runs.