In the previous post, we built the physical frame allocator required to construct page tables dynamically. Until now, the kernel has relied on an identity mapping, with virtual addresses equal to physical addresses, which was enough to enable the MMU and continue execution with address translation enabled. The next step is to map the kernel […]
In the previous post, we laid the hardware foundation for our virtual memory subsystem: configuring the MMU, setting up MAIR attributes, and establishing an identity mapping to ensure our kernel survived the transition to address translation. While we successfully enabled translation without crashing, our current translation tables are backed by statically reserved storage. To build […]
In the previous post, we moved from structural parsing to semantic interpretation of the Device Tree, allowing our kernel to dynamically discover peripheral addresses and interrupt lines. But while we have successfully purged hardcoded addresses from the assembly in favor of dynamic discovery, we are still operating in a flat, unprotected physical address space. To […]
In the previous post, we built a structural parser that transformed the raw Device Tree Blob (DTB) into a searchable table of PlatformDevice entries. While we can now traverse the tree and see property names like reg or compatible, these properties are still just raw, big-endian byte buffers. Our kernel knows a device exists, but […]
In my previous post, I built a minimal bootloader to load the kernel and pass the address of the Device Tree Blob (DTB) via the x0 register. Up until now, my kernel has been “cheating” – I hardcoded peripheral addresses like the UART (0x09000000) and the GIC (0x08000000) directly into the source code. While this […]
As part of continuing the work on my AArch64 kernel, I’ve reached the point where it makes sense to introduce a minimal loader. Until now, I’ve been running the kernel directly, but going forward, I want a more realistic setup — one where a small bootloader is responsible for loading the kernel image, passing the […]
In the previous post, we stopped at the point where AFL++ had produced instrumented binaries with PCGUARD and LTO. In this installment, we pick up the story at runtime: tracing the execution of those binaries, observing how coverage signals are collected, and following how afl-fuzz consumes that feedback to discover new paths. This is where […]
In the previous post, we laid out the theoretical map of AFL++’s instrumentation modes, from the classic edge coverage to modern LLVM-based techniques. With that foundation in place, it’s time to move from theory to practice. This article focuses on the compilation process with afl-cc: how LTO and PCGUARD instrumentation are inserted into the binary, […]
AFL++ has earned its reputation as a top-tier fuzzer thanks to its clever use of code coverage to guide the search for bugs. This series takes a deep dive into that process. In this first post, we’ll explore the instrumentation modes AFL++ uses to generate coverage—from the classic AFL edge hash to modern LLVM-based approaches. […]
In the previous posts, we successfully set up our development environment, wrote a minimal kernel, and initialized the UART to get a “Hello, world!” message printing to a serial console. This was a critical milestone, proving that our code can boot and run correctly within the QEMU emulator on the virt AArch64 platform. However, our […]