Tüm eğitimler
TEKNİK REHBER GÖMÜLÜ LİNUX QEMU 2026

QEMU
Embedded Emülasyon

gerçek donanım olmadan ARM, AArch64, RISC-V ve MIPS sistemleri çalıştır — CI, hata ayıklama ve hızlı prototip için.

00 QEMU nedir

QEMU (Quick Emulator), hem tam sistem hem de kullanıcı alanı emülasyonu yapabilen, açık kaynaklı bir makine öykünücüsü ve sanallaştırma aracıdır.

Full-system vs usermode emulation

QEMU iki temel modda çalışır. Full-system modunda (qemu-system-*) CPU, bellek, disk, ağ kartı ve diğer çevre birimlerinin tamamını taklit eder — sanki gerçek bir donanım üzerinde çalışıyorsunuz gibi kernel'ı boot eder. Usermode modunda (qemu-*) ise yalnızca kullanıcı alanı binary'lerini çalıştırır; sistem çağrılarını host kernel'a yönlendirir. Embedded geliştirmede genellikle full-system modu kullanılır.

ModBinaryNe taklit ederKullanım
Full-systemqemu-system-armCPU + bellek + tüm donanımKernel boot, tam sistem test
Usermode (Linux)qemu-armYalnızca CPU + syscall çevirisiCross-compile binary test, chroot
Usermode (BSD)qemu-arm-bsdBSD syscall çevirisiBSD target binary test

TCG vs KVM

QEMU, CPU emülasyonunu iki farklı yöntemle gerçekleştirir. TCG (Tiny Code Generator), misafir mimarinin makine kodunu host mimarisine çalışma anında dönüştürür (JIT derleme). Bu sayede host ile misafir farklı mimarilerde olabilir — örneğin x86-64 host üzerinde ARM kodunu çalıştırabilirsiniz. KVM ise Linux çekirdeğinin sanallaştırma altyapısını kullanır; misafir ve host aynı mimariyi paylaşırken donanım hızlanması sağlar. Embedded geliştirmede (ARM cross-emulation) TCG kullanılır.

ARM guest code → TCG frontend (ARM decode) → TCG IR → TCG backend (x86-64 codegen) → native execution

Desteklenen mimariler

MimariQEMU binaryÖrnek hedefler
ARM 32-bitqemu-system-armCortex-A8/A9, Versatile PB, BeagleBone
ARM 64-bit (AArch64)qemu-system-aarch64Cortex-A53/A72, Raspberry Pi 3/4, virt
RISC-V 32/64qemu-system-riscv64SiFive HiFive, virt, spike
MIPSqemu-system-mipsMalta, MIPS32 router SoC
x86 / x86-64qemu-system-x86_64i440FX, Q35, microvm
PowerPCqemu-system-ppcPowerNV, pseries

Embedded geliştirici için değeri

Donanım bağımsızlıkGeliştirme board'u temin edilmeden önce yazılım geliştirmeye başlanır; prototipleme hızlanır
CI entegrasyonuGitHub Actions veya GitLab CI'da ARM image'ı otomatik olarak boot edilip test edilir
Hata ayıklamaGDB stub (-s -S) ile kernel başlamadan önce breakpoint konabilir; userspace ve kernel aynı anda debug edilir
Snapshotsavevm/loadvm ile sistem durumu kaydedilir; hata tekrarlanabilir hale gelir
Güvenli denemeGerçek donanıma zarar vermeden flash yazma, partition silme gibi riskli işlemler test edilir

Bu bölümde

  • QEMU = full-system emülasyon (qemu-system-*) + usermode emülasyon (qemu-arm)
  • TCG: misafir mimarisi → JIT → host native kod; ARM emülasyonu için TCG kullanılır
  • ARM, AArch64, RISC-V, MIPS, x86, PowerPC destekli
  • CI pipeline'da board olmadan kernel boot + test: embedded CI'ın temeli

01 Kurulum ve ilk çalıştırma

QEMU'nun kurulumundan ilk ARM Linux boot'una kadar tüm adımlar — qemu-system-arm -M versatilepb ile dakikalar içinde çalışan bir sistem.

Kurulum

bash — Ubuntu 22.04 / Debian 12
# Full-system emülatörleri kur
sudo apt-get install -y \
  qemu-system-arm \
  qemu-system-aarch64 \
  qemu-system-misc \
  qemu-utils

# Usermode emülatörleri kur (cross-binary test için)
sudo apt-get install -y \
  qemu-user-static \
  binfmt-support

# Versiyon kontrolü
qemu-system-arm --version
# QEMU emulator version 8.2.2
# Copyright (c) 2003-2024 Fabrice Bellard and the QEMU Project developers

İlk çalıştırma: ARM Versatile PB

bash — ARM Linux boot
# Minimal ARM Linux sistemi indir (Buildroot önceden derlenmiş)
wget https://buildroot.org/downloads/buildroot-2024.02.tar.gz
tar xf buildroot-2024.02.tar.gz && cd buildroot-2024.02
make qemu_arm_versatile_defconfig
make -j$(nproc)

# Kernel + initrd ile boot (disk olmadan)
qemu-system-arm \
  -M versatilepb \
  -kernel output/images/zImage \
  -initrd output/images/rootfs.cpio.gz \
  -append "console=ttyAMA0 rdinit=/sbin/init" \
  -serial stdio \
  -nographic

# QEMU'dan çıkmak için: Ctrl+A ardından X

Temel parametreler

-M <machine>Makine tipi — versatilepb, virt, raspi3b vb. (bkz. Bölüm 02)
-kernel <file>Linux kernel image — zImage (ARM 32-bit), Image (AArch64), vmlinuz
-initrd <file>Initial RAM disk — cpio.gz veya cpio formatında rootfs
-dtb <file>Device Tree Binary — makine tanımı; bazı -M türleri dahili DTB üretir
-append <cmdline>Kernel komut satırı — console, root, init parametreleri
-nographicGrafik pencere açma; seri çıktıyı terminale yönlendir
-serial stdioİlk seri port (ttyAMA0) → standart giriş/çıkış

Seri konsol yapılandırması

bash — seri port seçenekleri
# seri → stdio (en yaygın, tek terminal)
qemu-system-arm -serial stdio -nographic ...

# seri → TCP telnet (başka terminalden bağlan)
qemu-system-arm -serial "telnet:localhost:4321,server,nowait" ...
# Bağlan: telnet localhost 4321

# İkinci seri port (loglama)
qemu-system-arm \
  -serial stdio \
  -serial "file:/tmp/uart1.log" ...

# seri → /dev/pts (pty — minicom ile bağlanmak için)
qemu-system-arm -serial pty ...
NOT

-nographic kullanıldığında QEMU monitor'e Ctrl+Alt+2 ile değil, Ctrl+A c ile geçilir. QEMU'dan çıkmak için Ctrl+A x veya monitor'de quit komutu kullanılır.

Bu bölümde

  • qemu-system-arm -M versatilepb -kernel zImage -initrd rootfs.cpio.gz: minimal boot
  • -append "console=ttyAMA0": kernel seri konsolu ttyAMA0'a yönlendirir
  • -nographic -serial stdio: grafik pencere olmadan terminalde seri çıktı
  • Çıkış: Ctrl+A x; monitor: Ctrl+A c

02 Makine tipleri ve CPU

QEMU'nun desteklediği makine tipleri, CPU modelleri, SMP ve bellek konfigürasyonu — embedded hedef için doğru makineyi seçmek.

Mevcut makine tiplerini listeleme

bash — makine listesi
# ARM 32-bit makine listesi
qemu-system-arm -M "?"
# Supported machines are:
# akita                Sharp SL-C1000 (Akita) PDA (PXA270)
# ast2500-evb          Aspeed AST2500 EVB (ARM1176)
# bcm2836              Raspberry Pi 2B (BCM2836)
# collie               Sharp SL-5500 (Collie) PDA (SA-1110)
# cubieboard           cubietech cubieboard (Cortex-A8)
# imx25-pdk            ARM i.MX25 PDK board (ARM926)
# musicpal             Marvell 88w8618 / MusicPal (ARM926EJ-S)
# netduino2            Netduino Plus 2 Machine (Cortex-M3)
# none                 empty machine
# raspi0               Raspberry Pi Zero (BCM2835)
# raspi1ap             Raspberry Pi A+ (BCM2835)
# raspi2b              Raspberry Pi 2B (BCM2836)
# realview-eb          ARM RealView Emulation Baseboard (ARM926EJ-S)
# stellaris            Luminary Micro Stellaris (Cortex-M3)
# versatilepb          ARM Versatile/PB (ARM926EJ-S)
# virt                 QEMU 2.6+ ARM Virtual Machine (cortex-a15)
# ...

# AArch64 makine listesi
qemu-system-aarch64 -M "?"
# raspi3ap             Raspberry Pi 3A+ (BCM2837)
# raspi3b              Raspberry Pi 3B (BCM2837)
# raspi4b              Raspberry Pi 4B (BCM2711)
# sbsa-ref             SBSA-REF Machine
# virt                 QEMU 2.6+ ARM Virtual Machine

CPU modeli seçimi

bash — CPU modelleri
# Mevcut CPU modellerini listele
qemu-system-aarch64 -M virt -cpu "?"
# Available CPUs:
# cortex-a35
# cortex-a53
# cortex-a55
# cortex-a57
# cortex-a72
# cortex-a76
# max  (en gelişmiş QEMU CPU — tüm özellikler etkin)

# Cortex-A53 ile virt makinesi
qemu-system-aarch64 \
  -M virt \
  -cpu cortex-a53 \
  -m 512M \
  -kernel Image \
  -nographic

# ARM 32-bit — Cortex-A9 (i.MX6 gibi SoC'lar için)
qemu-system-arm \
  -M virt \
  -cpu cortex-a9 \
  -m 256M \
  -kernel zImage \
  -nographic

SMP (Çok çekirdek) ve bellek

bash — SMP + bellek konfigürasyonu
# 4 çekirdek, 1 GB RAM
qemu-system-aarch64 \
  -M virt \
  -cpu cortex-a53 \
  -smp 4 \
  -m 1G \
  -kernel Image \
  -nographic

# SMP gelişmiş: socket/core/thread topolojisi
qemu-system-aarch64 \
  -M virt \
  -cpu cortex-a72 \
  -smp "cpus=4,sockets=1,cores=4,threads=1" \
  -m 2G \
  -kernel Image \
  -nographic

# Bellek boyutu birimleri: M (MiB), G (GiB)
# -m 256M  → 256 MiB
# -m 1G    → 1024 MiB
# -m 512   → 512 MiB (birim belirtilmezse MiB varsayılır)

virt makinesi — embedded için neden tercih edilir

Generic platformGerçek bir board'u simüle etmez — virtio cihazlarla esnek, düşük overhead
GIC (Generic Interrupt Controller)ARMv8-A standart kesme denetleyicisi — tüm modern ARM SoC'ların kullandığı model
PCI/PCIeAğ, storage ve diğer cihazlar VirtIO veya PCI üzerinden eklenir — genişletilebilir
UEFI/U-Boot uyumlu-bios parametresiyle U-Boot veya EDK2 BIOS yüklenebilir
NOT

Yeni bir projeye başlarken makine olarak virt ve CPU olarak cortex-a53 kombinasyonunu tercih edin. Bu kombinasyon, gerçek dünya ARM64 SoC'larının (i.MX8, RK3328, MT8183) donanım davranışını en iyi yansıtan ve en geniş kernel desteğine sahip seçenektir.

Bu bölümde

  • qemu-system-arm -M ? ve -cpu ?: desteklenen makine ve CPU listesi
  • -M virt -cpu cortex-a53: embedded ARM64 için önerilen kombinasyon
  • -smp 4 -m 512M: 4 çekirdek, 512 MiB RAM konfigürasyonu
  • versatilepb: ARM 32-bit geliştirme; virt: modern ARM32/ARM64

03 Storage simülasyonu

Sanal disk, SD kart ve NOR/NAND flash simülasyonu — qemu-img ile image oluşturma ve -drive parametreleri.

qemu-img ile disk image oluşturma

bash — qemu-img
# Ham (raw) disk image — 512 MB SD kart simülasyonu
qemu-img create -f raw sdcard.img 512M

# QCOW2 format — CoW (copy-on-write), sıkıştırma, snapshot desteği
qemu-img create -f qcow2 disk.qcow2 2G

# Image bilgisi görüntüle
qemu-img info sdcard.img
# image: sdcard.img
# file format: raw
# virtual size: 512 MiB (536870912 bytes)
# disk size: 512 MiB

# Raw → QCOW2 dönüştürme
qemu-img convert -f raw -O qcow2 sdcard.img sdcard.qcow2

# NOR flash için sabit boyutlu image (pflash için)
qemu-img create -f raw flash.img 64M
# Boş alanı 0xFF ile doldur (NOR flash varsayılan durumu)
dd if=/dev/zero bs=1M count=64 | tr '\000' '\377' > flash.img

Drive parametreleri

bash — -drive örnekleri
# SD kart — SD/MMC arayüzü üzerinden
qemu-system-arm \
  -M virt \
  -cpu cortex-a15 \
  -drive "file=sdcard.img,format=raw,if=sd" \
  -kernel zImage \
  -nographic

# SCSI disk — sda olarak görünür
qemu-system-arm \
  -M versatilepb \
  -drive "file=rootfs.ext2,if=scsi,format=raw" \
  -append "root=/dev/sda rw console=ttyAMA0" \
  -kernel zImage -nographic

# VirtIO block — yüksek performans, virt makinesiyle kullanılır
qemu-system-aarch64 \
  -M virt \
  -cpu cortex-a53 \
  -drive "file=disk.img,format=raw,if=virtio" \
  -append "root=/dev/vda rw console=ttyAMA0" \
  -kernel Image -nographic

# NOR Flash (pflash) — U-Boot firmware için
qemu-system-arm \
  -M virt \
  -drive "file=flash0.img,format=raw,if=pflash" \
  -drive "file=flash1.img,format=raw,if=pflash" \
  -nographic

-drive seçenekleri özeti

ParametreDeğerlerAçıklama
file=dosya yoluDisk image dosyası
format=raw, qcow2, vmdkImage formatı; raw en basit ve hızlı
if=sd, scsi, virtio, pflash, ide, noneHedef arayüz tipi
readonly=onon / offRead-only montaj — squashfs test için
cache=none, writeback, writethroughI/O cache politikası
snapshot=onon / offDeğişiklikleri image'a yazma — geçici çalışma

Partition oluşturma ve formatlama

bash — SD kart partition hazırlama
# 512 MB image oluştur
qemu-img create -f raw sdcard.img 512M

# Partition tablosu yaz (MBR)
fdisk sdcard.img << 'EOF'
o       # MBR partition tablosu oluştur
n       # Yeni partition
p       # Primary
1       # Partition 1
2048    # Başlangıç sektörü
+64M    # Boyut: 64 MB (boot)
t       # Tip değiştir
c       # W95 FAT32 (LBA)
n       # Partition 2
p
2
# varsayılan (FAT sonrası)
# varsayılan (disk sonu)
w       # Yaz ve çık
EOF

# Loop device ile bağla ve formatla
sudo losetup -fP sdcard.img
sudo mkfs.vfat -F 32 /dev/loop0p1
sudo mkfs.ext4 /dev/loop0p2
sudo losetup -d /dev/loop0
UYARI

if=pflash ile NOR flash simüle ederken image boyutu makine tipinin beklediği kapasiteyle tam eşleşmelidir. virt makinesi iki 64 MB pflash sürücü bekler. Boyut uyumsuzluğu QEMU'nun başlamamasına veya U-Boot'un çökmesine yol açar.

Bu bölümde

  • qemu-img create -f raw sdcard.img 512M: ham disk image oluşturma
  • -drive file=sdcard.img,format=raw,if=sd: SD kart simülasyonu
  • -drive file=flash.img,format=raw,if=pflash: NOR flash (U-Boot için)
  • VirtIO block (if=virtio): en yüksek performanslı sanal disk arayüzü

04 Ağ yapılandırması

QEMU'da ağ bağlantısı kurmanın dört yolu: user-mode SLIRP, TAP/bridge, macvlan ve VirtIO — her birinin kullanım senaryosu.

User-mode networking (SLIRP)

SLIRP, root yetkisi gerektirmez. QEMU dahili bir NAT yönlendirici gibi davranır; misafir internete çıkabilir ve host port yönlendirmesiyle misafirdeki servislere erişilebilir. En basit ve en yaygın yöntemdir.

bash — user-mode networking
# Basit user-mode (varsayılan — network interface olmadan)
qemu-system-aarch64 \
  -M virt -cpu cortex-a53 -m 512M \
  -netdev "user,id=net0" \
  -device "virtio-net-pci,netdev=net0" \
  -kernel Image -nographic

# Port yönlendirme: host:2222 → guest:22 (SSH)
qemu-system-aarch64 \
  -M virt -cpu cortex-a53 -m 512M \
  -netdev "user,id=net0,hostfwd=tcp::2222-:22" \
  -device "virtio-net-pci,netdev=net0" \
  -kernel Image -nographic

# Birden fazla port yönlendirme
qemu-system-aarch64 \
  -netdev "user,id=net0,\
hostfwd=tcp::2222-:22,\
hostfwd=tcp::8080-:80,\
hostfwd=udp::5353-:53" \
  -device "virtio-net-pci,netdev=net0" \
  -kernel Image -nographic

# Misafirden host'a SSH bağlantısı
ssh -p 2222 root@localhost

TAP/bridge networking

TAP modu, misafir sistemin fiziksel ağ gibi davranmasını sağlar — host ağındaki diğer makinelerle doğrudan iletişim kurabilir. Root yetkisi gerektirir.

bash — TAP bridge kurulumu
# Gerekli araçlar
sudo apt-get install -y bridge-utils uml-utilities

# Bridge oluştur (eth0 ile br0'ı bağla)
sudo ip link add name br0 type bridge
sudo ip link set br0 up
sudo ip link set eth0 master br0

# TAP interface oluştur
sudo ip tuntap add dev tap0 mode tap user $(whoami)
sudo ip link set tap0 master br0
sudo ip link set tap0 up

# QEMU TAP ile çalıştır
qemu-system-aarch64 \
  -M virt -cpu cortex-a53 -m 512M \
  -netdev "tap,id=net0,ifname=tap0,script=no,downscript=no" \
  -device "virtio-net-pci,netdev=net0,mac=52:54:00:12:34:56" \
  -kernel Image -nographic

Network device türleri

DeviceMimariPerformansNe zaman
virtio-net-pciARM virt, x86YüksekModern Linux misafir, virt makinesi
e1000Tüm PCI makinelerOrtaEski kernel, geniş uyumluluk
smc91c111versatilepbDüşükARM versatilepb makinesi
lan9118Bazı ARM makinelerOrtaSMSC LAN9118 simülasyonu
NOT

CI ortamlarında TAP/bridge kullanmak için Docker container veya VM'in ağ yetenekleri ve root izinleri gerekir. GitHub Actions gibi managed runner'larda user-mode SLIRP ile port yönlendirme pratikte daha kolaydır.

Bu bölümde

  • SLIRP (-netdev user): root gerektirmez, NAT, port yönlendirme ile SSH erişimi
  • hostfwd=tcp::2222-:22: host 2222 → misafir 22 yönlendirme
  • TAP/bridge: misafir ağ'da tam katılımcı — gerçek IP adresi alır
  • virtio-net-pci: virt makinesiyle en yüksek ağ performansı

05 Tam embedded boot zinciri

U-Boot + Device Tree + Linux Kernel + Rootfs'in QEMU üzerinde tam boot zinciri — AArch64 virt makinesiyle gerçek embedded sistemin simülasyonu.

Boot zinciri bileşenleri

QEMU -bios u-boot.bin → U-Boot → kernel Image (TFTP/SD) → DTB yükle → kernel boot → rootfs mount → /sbin/init

U-Boot derleme (AArch64 virt)

bash — U-Boot cross-compile
# Cross-toolchain kur
sudo apt-get install -y gcc-aarch64-linux-gnu

# U-Boot kaynak kodu
git clone https://source.denx.de/u-boot/u-boot.git -b v2024.04
cd u-boot

# QEMU virt AArch64 defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
  qemu_arm64_defconfig

# Derle
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
  -j$(nproc)

# Çıktı: u-boot.bin (BIOS/ROM image)
ls -lh u-boot.bin
# -rw-r--r-- 1 user user 854K u-boot.bin

Disk image hazırlama

bash — GPT + FAT32 boot partition
# 1 GB disk image oluştur
qemu-img create -f raw disk.img 1G

# GPT partition tablosu
sgdisk -n 1:2048:+64M -t 1:ef00 \
  -n 2:0:0 -t 2:8300 disk.img

# Loop device ile bağla
sudo losetup -fP disk.img
# Örnek: /dev/loop1p1 (boot), /dev/loop1p2 (rootfs)

# Partition formatla
sudo mkfs.vfat -F 32 /dev/loop1p1
sudo mkfs.ext4 /dev/loop1p2

# Boot partition'a kernel ve DTB kopyala
sudo mount /dev/loop1p1 /mnt/boot
sudo cp Image /mnt/boot/
sudo cp qemu-virt.dtb /mnt/boot/
sudo umount /mnt/boot

# Rootfs'i ikinci partition'a yaz
sudo dd if=rootfs.ext4 of=/dev/loop1p2 bs=4M status=progress
sudo losetup -d /dev/loop1

Tam boot komutu

bash — AArch64 full boot
qemu-system-aarch64 \
  -M virt \
  -cpu cortex-a53 \
  -m 1G \
  -smp 2 \
  -bios u-boot.bin \
  -drive "file=disk.img,format=raw,if=none,id=hd0" \
  -device "virtio-blk-pci,drive=hd0" \
  -netdev "user,id=net0,hostfwd=tcp::2222-:22" \
  -device "virtio-net-pci,netdev=net0" \
  -serial stdio \
  -nographic

# U-Boot'ta autoboot iptal: herhangi bir tuşa bas
# U-Boot prompt'ta:
# => fatload virtio 0:1 ${kernel_addr_r} Image
# => fatload virtio 0:1 ${fdt_addr_r} qemu-virt.dtb
# => booti ${kernel_addr_r} - ${fdt_addr_r}

U-Boot environment — otomatik boot

U-Boot — bootcmd ayarı
# U-Boot prompt'ta otomatik boot tanımla
# => setenv bootcmd 'fatload virtio 0:1 ${kernel_addr_r} Image; \
#      fatload virtio 0:1 ${fdt_addr_r} qemu-virt.dtb; \
#      setenv bootargs "root=/dev/vda2 rw console=ttyAMA0,115200"; \
#      booti ${kernel_addr_r} - ${fdt_addr_r}'
# => saveenv
# => boot

Bu bölümde

  • U-Boot derleme: ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make qemu_arm64_defconfig
  • -bios u-boot.bin: QEMU U-Boot'u firmware olarak yükler
  • -drive file=disk.img,if=none,id=hd0 + -device virtio-blk-pci,drive=hd0: VirtIO disk bağlama
  • U-Boot booti: AArch64 kernel Image + DTB yükleme ve boot komutu

06 GDB stub ile debug

QEMU'nun dahili GDB stub'ı ile kernel ve userspace programları kaynak seviyesinde hata ayıklama — -s -S parametreleri ve GDB bağlantısı.

GDB stub aktifleştirme

bash — QEMU GDB stub
# -s  : GDB stub'ı 1234 portunda dinle (kısayol: -gdb tcp::1234)
# -S  : CPU'yu boot'ta dondur (GDB'nin bağlanmasını bekle)
qemu-system-aarch64 \
  -M virt \
  -cpu cortex-a53 \
  -m 512M \
  -kernel Image \
  -initrd rootfs.cpio.gz \
  -append "console=ttyAMA0 rdinit=/sbin/init" \
  -serial stdio \
  -nographic \
  -s -S

# Özel port belirlemek için
qemu-system-aarch64 ... -gdb "tcp::5678"

GDB ile bağlanma (AArch64 kernel)

bash — GDB session
# AArch64 GDB kur
sudo apt-get install -y gdb-multiarch

# GDB'yi kernel ELF ile başlat (debug sembolleri içermeli)
gdb-multiarch vmlinux

# GDB içinde:
# (gdb) set architecture aarch64
# (gdb) target remote :1234
# Remote debugging using :1234
# 0x0000000040000000 in ?? ()

# Kernel başlangıcına breakpoint
# (gdb) break start_kernel
# Breakpoint 1 at 0xffff800010010000: file init/main.c, line 931

# Çalıştırmaya devam et
# (gdb) continue

# Breakpoint'te durduğunda kaynak görüntüle
# (gdb) list
# (gdb) info registers
# (gdb) x/10i $pc  ← PC'deki 10 instruction

Kernel debug sembolleri etkinleştirme

bash — kernel config
# Linux kaynak dizininde
make ARCH=arm64 menuconfig

# Kernel Hacking bölümünde etkinleştir:
# CONFIG_DEBUG_INFO=y          → GDB debug sembolleri
# CONFIG_DEBUG_INFO_DWARF4=y   → DWARF 4 format (GDB uyumlu)
# CONFIG_FRAME_POINTER=y       → Fonksiyon backtrace desteği
# CONFIG_KGDB=y                → Kernel GDB stub (QEMU -s olmadan da çalışır)
# CONFIG_RANDOMIZE_BASE=n      → KASLR'ı kapat (sabit adresler için)

# Derle
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)
# vmlinux → debug semboller dahil ELF (GDB'ye verilir)
# arch/arm64/boot/Image → QEMU'ya -kernel ile verilir

QEMU monitor ile bellek inceleme

QEMU monitor — debug komutları
# Monitor'e geç: Ctrl+A c (nographic modda)

# CPU kayıt dökümü
# (qemu) info registers

# Fiziksel bellekten 10 hex word oku
# (qemu) xp /10xw 0x40200000

# Sanal bellekten oku (CPU 0)
# (qemu) x /10xg 0xffff800010010000

# CPU durumunu görüntüle
# (qemu) info cpus

# Belirli CPU'yu durdur / sürdür
# (qemu) cpu 0
# (qemu) stop
# (qemu) cont

Bu bölümde

  • -s: GDB stub port 1234; -S: CPU'yu ilk instruction'da dondur
  • gdb-multiarch vmlinuxtarget remote :1234: AArch64 kernel debug
  • CONFIG_DEBUG_INFO=y + CONFIG_RANDOMIZE_BASE=n: kernel debug için gerekli
  • QEMU monitor (xp, x): GDB olmadan fiziksel ve sanal bellek okuma

07 QEMU monitor

QEMU monitor, çalışan sanal makinenin durumunu sorgulama, sistem durumu kaydetme/yükleme ve düşük seviye hata ayıklama için etkileşimli konsoldur.

Monitor'e erişim

-nographic moddaCtrl+A c — seri konsol ve monitor arasında geçiş yapar
Grafik moddaCtrl+Alt+2 — monitor penceresine geçiş; Ctrl+Alt+1 ile geri
TCP üzerinden-monitor tcp:localhost:4444,server,nowaitnc localhost 4444
UNIX socket-monitor unix:/tmp/qemu-monitor.sock,server,nowaitsocat - /tmp/qemu-monitor.sock

Temel monitor komutları

QEMU monitor — sık kullanılan komutlar
# Yardım
(qemu) help
(qemu) help info

# CPU kayıtları
(qemu) info registers
# PC=0xffff800010543abc X0=0000000000000001 X1=ffff0000097b2c00 ...

# Bellek okuma
(qemu) xp /10xw 0x40000000   # fiziksel — 10 word hex
(qemu) xp /4xg 0x40000000    # fiziksel — 4 giant (8 byte) hex
(qemu) x /10i  0xffff800010010000 # sanal — 10 instruction disasm

# Cihaz ve IRQ durumu
(qemu) info qtree           # cihaz ağacı
(qemu) info irq             # IRQ sayaçları
(qemu) info pci             # PCI cihazları
(qemu) info block           # disk cihazları
(qemu) info network         # ağ cihazları

# CPU kontrolü
(qemu) stop                 # tüm CPU'ları durdur
(qemu) cont                 # sürdür
(qemu) system_reset        # sıcak yeniden başlat
(qemu) system_powerdown    # ACPI power-off sinyali gönder
(qemu) quit                 # QEMU'yu kapat

VM snapshot (savevm / loadvm)

QEMU monitor — snapshot
# ÖNEMLİ: snapshot için disk image QCOW2 formatında olmalı
qemu-img create -f qcow2 disk.qcow2 4G

# Sistem çalışırken snapshot al
(qemu) savevm checkpoint1
# Snapshot başarıyla kaydedildi

# Snapshot listesi
(qemu) info snapshots
# ID        TAG               VM SIZE    DATE           VM CLOCK
# 1         checkpoint1       154M       2026-01-15 14:23:01   00:01:35.000

# Önceki snapshot'a dön
(qemu) loadvm checkpoint1

# Snapshot sil
(qemu) delvm checkpoint1

# QEMU komut satırından belirli snapshot ile başlat
qemu-system-aarch64 ... -loadvm checkpoint1

QMP (QEMU Machine Protocol)

bash — QMP JSON API
# QMP üzerinden programatik kontrol
qemu-system-aarch64 \
  -M virt -cpu cortex-a53 -m 512M \
  -qmp "tcp:localhost:4445,server,nowait" \
  -kernel Image -nographic

# QMP ile bağlan
nc localhost 4445

# Handshake: {"execute": "qmp_capabilities"}
# Sistem durdur: {"execute": "stop"}
# CPU bilgisi: {"execute": "query-cpus-fast"}
# Güç kapat: {"execute": "system_powerdown"}
# Snapshot: {"execute": "savevm", "arguments": {"name": "snap1"}}

Bu bölümde

  • Monitor erişimi: Ctrl+A c (nographic) veya -monitor tcp:localhost:4444
  • info registers, xp /10xw addr: CPU ve bellek inceleme
  • savevm/loadvm: QCOW2 disk üzerinde VM snapshot
  • QMP: JSON tabanlı programatik makine kontrolü — CI ve otomasyon için

08 Buildroot + QEMU entegrasyonu

Buildroot'un yerleşik QEMU desteği — qemu_arm_versatile_defconfig, otomatik üretilen start-qemu.sh ve CI için kullanımı.

QEMU defconfig'leri

bash — Buildroot QEMU defconfig listesi
# QEMU için hazır konfigürasyonlar
make list-defconfigs | grep qemu
# qemu_aarch64_virt_defconfig         - AArch64 virt makinesi
# qemu_arm_versatile_defconfig        - ARM 32-bit versatilepb
# qemu_arm_vexpress_defconfig         - ARM Versatile Express
# qemu_mips32r2_malta_defconfig       - MIPS32 Malta
# qemu_mips32r2el_malta_defconfig     - MIPS32 LE Malta
# qemu_riscv32_virt_defconfig         - RISC-V 32-bit
# qemu_riscv64_virt_defconfig         - RISC-V 64-bit
# qemu_x86_defconfig                  - x86 32-bit
# qemu_x86_64_defconfig               - x86 64-bit

# AArch64 virt ile build
make qemu_aarch64_virt_defconfig
make -j$(nproc)

start-qemu.sh analizi

bash — Buildroot'un ürettiği start-qemu.sh
#!/bin/sh
# output/images/start-qemu.sh — Buildroot tarafından otomatik üretilir

BINARIES_DIR="${0%/*}/"
cd "${BINARIES_DIR}"

if [ "${1}" = "serial-only" ] ; then
    EXTRA_ARGS='-nographic'
else
    EXTRA_ARGS=''
fi

# Temel QEMU komutu
exec qemu-system-aarch64 \
    -M virt \
    -cpu cortex-a53 \
    -smp 1 \
    -m 1024 \
    -kernel Image \
    -initrd rootfs.cpio.gz \
    -append "rootwait root=/dev/mem console=ttyAMA0,115200" \
    -netdev "user,id=net0" \
    -device "virtio-net-device,netdev=net0" \
    ${EXTRA_ARGS}

# Çalıştırma:
# ./start-qemu.sh              → grafik mod
# ./start-qemu.sh serial-only  → nographic, CI için ideal

Buildroot post-build scripti ile özelleştirme

bash — board/myproject/post-build.sh
#!/bin/bash
# BR2_ROOTFS_POST_BUILD_SCRIPT ile çağrılır
TARGET_DIR="$1"

# Basit test scripti rootfs'e ekle
cat > "${TARGET_DIR}/usr/bin/run-tests.sh" << 'TESTEOF'
#!/bin/sh
echo "[TEST] Kernel version: $(uname -r)"
echo "[TEST] CPU info: $(grep 'model name' /proc/cpuinfo | head -1)"
echo "[TEST] Memory: $(free -m | awk '/Mem/{print $2}') MB"
ls /dev/mmcblk0 2>/dev/null && echo "[TEST] SD card: PASS" || echo "[TEST] SD card: SKIP"
echo "[TEST] DONE"
TESTEOF
chmod +x "${TARGET_DIR}/usr/bin/run-tests.sh"

Bu bölümde

  • Buildroot: 9 ayrı QEMU defconfig (ARM, AArch64, MIPS, RISC-V, x86)
  • output/images/start-qemu.sh serial-only: CI için headless boot
  • post-build.sh ile rootfs'e test scripti enjekte etme
  • BR2_ROOTFS_POST_BUILD_SCRIPT: Buildroot build sonrası hook

09 Pratik: CI pipeline'da QEMU

GitHub Actions ve GitLab CI'da cross-compile → QEMU boot → seri çıktıdan pass/fail tespiti — tam otomatik embedded CI pipeline.

Strateji

push → cross-compile (x86-64 host) → QEMU ARM boot → seri çıktı logla → PASS/FAIL → pipeline sonucu

GitHub Actions — ARM QEMU CI

.github/workflows/embedded-ci.yml
name: Embedded CI — QEMU ARM64

on:
  push:
    branches: [main, develop]
  pull_request:

jobs:
  build-and-test:
    runs-on: ubuntu-22.04

    steps:
      # 1. Bağımlılıklar
      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y \
            gcc-aarch64-linux-gnu \
            qemu-system-aarch64 \
            expect

      # 2. Kaynak kodu kontrol et
      - uses: actions/checkout@v4

      # 3. Cross-compile
      - name: Cross-compile application
        run: |
          aarch64-linux-gnu-gcc \
            -static -O2 \
            -o app_arm64 \
            src/main.c
          aarch64-linux-gnu-strip app_arm64

      # 4. Kernel + rootfs indir (önceden derlenmiş)
      - name: Download test rootfs
        run: |
          wget -q https://example.com/ci/rootfs-aarch64.cpio.gz
          wget -q https://example.com/ci/Image-aarch64

      # 5. App'ı rootfs'e enjekte et
      - name: Inject app into rootfs
        run: |
          mkdir -p rootfs_inject/usr/bin
          cp app_arm64 rootfs_inject/usr/bin/app
          cat > rootfs_inject/etc/init.d/S99test << 'EOF'
          #!/bin/sh
          /usr/bin/app && echo "APP_TEST_PASS" || echo "APP_TEST_FAIL"
          poweroff -f
          EOF
          chmod +x rootfs_inject/etc/init.d/S99test
          cd rootfs_inject
          find . | cpio -H newc -o | gzip -9 > ../app-rootfs.cpio.gz

      # 6. QEMU boot + çıktı yakala
      - name: Boot and test
        run: |
          timeout 120 qemu-system-aarch64 \
            -M virt \
            -cpu cortex-a53 \
            -m 512M \
            -smp 2 \
            -kernel Image-aarch64 \
            -initrd app-rootfs.cpio.gz \
            -append "console=ttyAMA0 rdinit=/sbin/init" \
            -serial file:serial.log \
            -nographic || true

          cat serial.log
          grep -q "APP_TEST_PASS" serial.log && \
            echo "TEST PASSED" || \
            (echo "TEST FAILED"; exit 1)

GitLab CI — YAML

.gitlab-ci.yml
stages:
  - build
  - test

variables:
  CROSS: "aarch64-linux-gnu-"
  ARCH: "arm64"

build:arm64:
  stage: build
  image: ubuntu:22.04
  before_script:
    - apt-get update -qq
    - apt-get install -y -qq gcc-aarch64-linux-gnu make
  script:
    - make CROSS_COMPILE=${CROSS} ARCH=${ARCH} all
  artifacts:
    paths: [build/app_arm64]
    expire_in: 1 hour

test:qemu-aarch64:
  stage: test
  image: ubuntu:22.04
  dependencies: [build:arm64]
  before_script:
    - apt-get update -qq
    - apt-get install -y -qq qemu-system-aarch64
  script:
    - |
      timeout 120 qemu-system-aarch64 \
        -M virt -cpu cortex-a53 -m 512M \
        -kernel ${CI_PROJECT_DIR}/ci/Image \
        -initrd ${CI_PROJECT_DIR}/ci/rootfs.cpio.gz \
        -append "console=ttyAMA0 rdinit=/sbin/init" \
        -serial file:qemu.log -nographic || true
      grep -q "TESTS_PASSED" qemu.log
  artifacts:
    paths: [qemu.log]
    when: always

Expect ile interaktif test

bash — expect script
#!/usr/bin/expect -f
# test-qemu.exp — login + komut çalıştır + sonucu doğrula

set timeout 120

# QEMU'yu başlat
spawn qemu-system-aarch64 \
    -M virt -cpu cortex-a53 -m 512M \
    -kernel Image -initrd rootfs.cpio.gz \
    -append "console=ttyAMA0 rdinit=/sbin/init" \
    -nographic

# Login prompt'u bekle
expect "login:"
send "root\r"

# Shell prompt'u bekle
expect "# "

# Uygulamayı çalıştır
send "/usr/bin/app --test\r"

# Başarı çıktısını bekle (60 saniye içinde)
expect {
    "TEST_OK"  { puts "PASS"; exit 0 }
    "TEST_FAIL" { puts "FAIL"; exit 1 }
    timeout     { puts "TIMEOUT"; exit 2 }
}
NOT

CI'da QEMU timeout değerini dikkatli ayarlayın. Boot + init + test süresi genellikle 30–60 saniye arasındadır. timeout 120 ile 2 dakika üst sınırı güvenli bir başlangıç noktasıdır. Yavaş disk I/O gerektiren testler için bu süreyi artırın.

UYARI

GitHub Actions shared runner'larında KVM desteği yoktur — TCG modu kullanılır. ARM64 emülasyonu x86-64 native çalışmaya göre 5–15x yavaştır. Uzun çalışan testler için self-hosted runner veya ARM runner tercih edin. Test süresini minimize etmek için rootfs'i küçük tutun ve gereksiz init servislerini devre dışı bırakın.

Bu bölümde

  • GitHub Actions: cross-compile + rootfs enjekte + QEMU boot + serial.log grep ile pass/fail
  • GitLab CI: iki aşamalı pipeline — build artifact → test job
  • expect: interaktif login + komut çalıştırma + çıktı doğrulama
  • TCG'de ARM64 emülasyonu 5–15x yavaş — timeout ve rootfs boyutunu buna göre ayarla