Setting Up an Embedded HTTP Environment on QEMU-ARM with Access Examples

Those engaged in embedded development know that devices must interact with the outside world via HTTP, such as for data transmission and remote control. Recently, I set up a lightweight HTTP service on a QEMU-emulated ARM Linux system using the httpd included with BusyBox, and I also enabled access from the host. I encountered several pitfalls during the process, and now I am sharing my experiences with everyone.

1. Core Objectives

1. Install and start httpd in QEMU-ARM: This step requires ensuring that the root filesystem includes httpd; BusyBox may not have it integrated by default, so you need to compile it yourself.2. Configure the network to allow the host to access ARM: Use TAP networking, ensuring that the IP addresses of the host and ARM are set correctly, and the subnet mask is also accurate.3. Create HTML pages for testing: Validate the service step by step, from static pages to dynamic CGI scripts.

2. Step 1: Prepare the Root Filesystem with httpd

BusyBox’s httpd is quite suitable for embedded scenarios, but when I first set it up, I forgot to select httpd in the Networking Utilities during the BusyBox compilation. As a result, after starting QEMU, I couldn’t find the command. I later recompiled, enteredmake menuconfig, checked httpd, and then copied the new rootfs to the SD card image, which resolved the issue.

Verify if httpd exists:

httpd –help # Display help to confirm it works

3. Step 2: Configure TAP Networking (Lessons Learned)

4. Start QEMU with network parameters:

qemu-system-arm \

-M vexpress-a9 \

-kernel arch/arm/boot/zImage \

-dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb \

-sd rootfs.ext4 \

-append “root=/dev/mmcblk0 rw console=ttyAMA0 ip=192.168.1.101” \

-serial stdio \

-net nic -net tap,ifname=tap0,script=no,downscript=no

I initially used user-mode networking, which allowed the host to ping the ARM, but the browser could not access port 80 at all. After switching to a TAP interface, I also had to manually assign an IP to tap0:

sudo ifconfig tap0 192.168.1.100 netmask 255.255.255.0

5. Test connectivity: ARM terminal:ping 192.168.1.100 Host terminal:ping 192.168.1.101Both must be reachable; once I mistakenly set the subnet mask to 255.255.0.0, and neither side could ping, which took me a while to figure out.

4. Step 3: Start the HTTP Service (Permission Pitfalls)

6. Create the /www directory and write a test page:

mkdir -p /www

echo “<h1>QEMU-ARM HTTP Test</h1><p>Hello from Embedded System!</p>” > /www/index.html

I initially did not set permissions for /www, and while httpd started without errors, the browser returned a 403 Forbidden error. Later, Ichmod 755 /www to fix it.

7. Start the service:

httpd -h /www -p 80 # Use netstat -tln to check if port 80 is listening

5. Step 4: Test Access from the Host

8. Access via browser:Enterhttp://192.168.1.101, and the page should display normally. I tested with Firefox and Chrome, both worked fine, but Safari occasionally caches old pages, requiring aCtrl+Shift+R to force refresh.9. Validate with curl:

curl http://192.168.1.101

This outputs the HTML content; this step generally does not fail, but be aware of the ARM firewall. I previously had ufw enabled, blocking port 80, and it worked fine only after I disabled it.

6. Advanced: CGI Scripts (Importance of Script Headers)

10. Enable CGI support:

httpd -h /www -p 80 -c “**.cgi”

11. Write a CPU information script:

cat > /www/cpuinfo.cgi << EOF

#!/bin/sh

echo “Content-Type: text/plain”

echo “”

cat /proc/cpuinfo

EOF

chmod +x /www/cpuinfo.cgi

I initially forgot to include#!/bin/sh, which caused the browser to download the script directly; I also omitted theContent-Type and the empty line, resulting in garbled output.

12. Test access:

curl http://192.168.1.101/cpuinfo.cgi

This outputs the ARM CPU information, indicating that CGI is functioning correctly.

7. Autostart on Boot (Startup Order Issues)

Edit/etc/init.d/rcS to add:

echo “httpd -h /www -p 80 &” >> /etc/init.d/rcS

chmod +x /etc/init.d/rcS

Initially, the system failed to start the service because the /www directory was not yet mounted. Later, I addedsleep 3 before the command to allow the system to stabilize before starting, which worked.

Common Problem Solutions

13. Host cannot access: Check if port 80 is listening on ARM (netstat -tln) Disable the host firewall (sudo ufw disable)14. CGI script not executing: Ensure the script has execute permissions (chmod +x) The script header must include#!/bin/sh, and the response header must includeContent-Type and an empty line15. Page not updating: Browser caching issues, force refresh Delete/www/index.html and regenerate

Conclusion

From compiling BusyBox to configuring the network and debugging CGI scripts, each step can present challenges, such as incorrect subnet masks, insufficient permissions, and missing script headers. However, by following the steps and checking logs (/var/log/messages), issues can be resolved. Now, every time I start QEMU, the HTTP service runs automatically, and I can access it by entering the IP in the host browser, greatly improving development efficiency.

Leave a Comment