QEMU ARM + NFS Debugging: Creating an Efficient Embedded Development Environment

Hello everyone, I am a programmer who loves to share. I am happy to share my experiences and understanding from my work.

-begin-

In embedded development, frequently flashing images for debugging is inefficient. However, using QEMU to simulate an ARM environment in conjunction with NFS (Network File System) allows the program to read files directly from the host in a virtual machine, eliminating the need to re-flash after modifying code, significantly improving debugging speed. Today, I will discuss how to set up this environment and common pitfalls during debugging.

1. Environment Setup Steps (Taking ARM Cortex-A9 as an Example)

  1. Prepare Tools
    • Install QEMU:
      sudo apt-get install qemu-system-arm

      (must support ARM architecture)

    • Compile the ARM cross-toolchain: It is recommended to use Linaro or Buildroot to generate arm-linux-gnueabihf-gcc
    • Prepare the ARM kernel and root filesystem: You can compile it using Buildroot or use official images (such as debian-armhf)
  2. Configure NFS Service (Host Side)
    • Install NFS server:
      sudo apt-get install nfs-kernel-server
    • Edit the configuration file /etc/exports to add the shared directory:
      /home/yourname/arm_nfs 192.168.7.0/24(rw,sync,no_root_squash,no_subtree_check)

      (192.168.7.0/24 is the QEMU virtual network segment, rw means read-write)

    • Restart the NFS service:
      sudo /etc/init.d/nfs-kernel-server restart
  3. Configure QEMU Network
    • When starting QEMU, add network parameters using a tap device bridge:
      qemu-system-arm -M vexpress-a9 -kernel zImage -dtb vexpress-v2p-ca9.dtb 
      -nographic -net nic -net tap,ifname=tap0,script=no,downscript=no 
      -append "root=/dev/nfs nfsroot=192.168.7.1:/home/yourname/arm_nfs rw ip=192.168.7.10:192.168.7.1:192.168.7.1:255.255.255.0::eth0:off"
    • nfsroot: Host IP and NFS shared directory
    • ip: QEMU virtual machine IP: Host IP: Gateway: Subnet Mask:: Network Card: off
  4. Verify NFS Mount
    • After starting QEMU, execute mount in the virtual terminal. If you see 192.168.7.1:/home/yourname/arm_nfs on / type nfs ..., it indicates that the mount was successful.

2. Debugging Practice: Running Programs in NFS

  1. Compile the Program: Use the cross-toolchain to compile the test program (e.g., hello.c):
    arm-linux-gnueabihf-gcc hello.c -o hello

    Place the generated hello in the host’s /home/yourname/arm_nfs directory.

  2. Run in QEMU: Directly execute ./hello in the virtual terminal without copying; after modifying the code, recompiling will allow immediate execution, saving the flashing step.
  3. GDB Remote Debugging
    • Add the -g option when compiling the program:
      arm-linux-gnueabihf-gcc -g hello.c -o hello
    • Add -s -S parameters when starting QEMU (-s means listening on port 1234, -S means pausing and waiting for GDB connection)
    • Start GDB on the host:
      arm-linux-gnueabihf-gdb hello

      (gdb) target remote localhost:1234 # Connect to QEMU
      (gdb) break main
      (gdb) continue to set breakpoints and view variables just like debugging a local program.

3. Common Issues and Solutions

  1. NFS Mount Failure
    • Check: ping 192.168.7.1 (ping the host from QEMU). If it does not connect, check the virtual network configuration.
    • Solution: Ensure the permissions of /etc/exports are correct, disable the host firewall (or open the NFS port).
  2. Program Runs with “Permission Denied”
    • Reason: The NFS configuration lacks no_root_squash, causing the root user in QEMU to be mapped as a normal user.
    • Solution: Add the no_root_squash parameter in /etc/exports and restart the NFS service.
  3. GDB Connection Timeout
    • Check if QEMU was started with the -s parameter and whether the network between the host and QEMU is connected.
    • Ensure the program was compiled with -g, and that the GDB version matches the cross-toolchain.

4. Summary of Advantages

  • Development Efficiency: Directly compile after modifying code, with real-time synchronization via NFS, eliminating the need to flash images.
  • Convenient Debugging: Combined with GDB remote debugging, supports breakpoints, variable viewing, and other features.
  • Resource Sharing: The host and QEMU share files, making it easy to view logs and transfer data.

This environment is particularly suitable for early functional debugging of ARM programs, especially when there is no development board available. QEMU + NFS can quickly validate code logic. However, it is important to note that the hardware simulated by QEMU may differ from the actual development board, and final testing should still be conducted on physical hardware.

-end-

If this article has helped you, please like, share, and follow. Thank you very much!

Leave a Comment