Tuesday 6 September 2011

FreeBSD ARM on Qemu in a VirtualBox System

After lot's of macking around, and not being happy with the ports cross compile solution, I've been finally able to get FreeBSD-ARM running in Qemu running in a VirtualBox.

Here the steps I took to success:

I've installed FreeBSD 8.2 as VirtualBox guest, as described in my previous post. I've then installed the following ports:
  • devel/subversion-freebsd
  • emulators/qemu-devel
I've then checked out the latest FreeBSD-CURRENT source from svn.freebsd.org and put it into /usr/devel/:

cd /usr/devel
svn co http://svn.freebsd.org/base/head/ .

The next step was to patch the sources in order to make them work with Qemu and the supported Gumstix-connex architecture.

The patch can be downloaded here.

It has been created following the indications found here, here, here and here.

After patching the source:
patch -p0 < gumstix-qemu.patch



the GUMSTIX FreeBSD Kernel and the ARM world can be built and installed into /usr/armqemu/armworld:
make TARGET=arm TARGET_ARCH=arm KERNCONF=GUMSTIX DESTDIR=/usr/armqemu/armworld buildworld kernel installworld distrib-dirs distribution

I've then followed the suggestions on in this message again in order to build a flash image for Qemu to pass to the -pflash command. The necessary GUMSTIX-connex uboot image can be found here.
After obtaining it run:
dd of=flash bs=1k count=16k if=/dev/zero
dd of=flash bs=1k conv=notrunc if=u-boot-connex-400-r1604.bin
dd of=flash bs=1k conv=notrunc seek=256 if=/usr/armqemu/armworld/boot/kernel/kernel.gz.tramp

Now you are ready to boot a kernel, but ther will be no root system available. The solution is to have a diskless machine, using root via NFS.

This gets a bit tricky. In VirtualBox you do already have a DHCP server, but it won't assign any addresses to interfaces not created by VirtualBox itself. So you need to install an additional DHCP server, and run it. It is safer to use a NATed interface in VirtualBox for this case, unless you want the DHCP server to offer addresses outside of VirtualBox.
I used a single VBox interface, which shows up as em0 in the FreeBSD guest, and gets address 10.0.2.15 assigned by default (The VBox default)

Next step is to install package net/isc-dhcp42-server, and to configure it editing /usr/local/etc/dhcpd.conf. I used the following subnet settings (based on this post:

subnet 10.0.2.0 netmask 255.255.255.0 {
range 10.0.2.20 10.0.2.30;
option routers 10.0.2.2;
next-server 10.0.2.15;
option root-path "10.0.2.15:/usr/armqemu/armworld";
}

The next step is to set the NFS export up in /etc/exports:
/usr/armqemu/armworld -maproot=root -network 10.0.2/24

Now we need to make sure that the once the kernel hits the init of the NFS file systems, it is still able to read from the NFS file system, so we need to set the armworld's rc.conf straight.
Edit /usr/armqemu/armworld/etc/rc.conf and add:
hostname="qemu-arm"
ifconfig_smc0="DHCP"
sshd_enable="YES"
rpcbind_enable="YES"
nfs_client_enable="YES"
rpc_lockd_enable="YES"
rpc_statd_enable="YES"

Then we need to make sure that the Qemu guest is capable of reaching the host system via the network interface. This is described in this wiki entry. In order to make it work you need to do the following:
kldload aio
kldload if_tap
kldload if_bridge
ifconfig tap0 create
ifconfig bridge0 create
ifconfig bridge0 addm tap0 em0 up
ifconfig tap0 up

In my case, tap0 would never stay up for long, unless it's used. I've read somewhere that there is a sysctl to fix that, but can't find the link anymore.
In order to make the tap bridge available on each reboot, edit /boot/loader.conf and add:
aio_load="YES"
if_tap_load="YES"
bridge_load="YES"

You also need to edit /etc/rc.conf on the server to get the tap and bridge set up properly and also to get the NFS server running.
The necessary additions to /etc/rc.conf are the following:
cloned_interfaces="tap0 bridge0"
ifconfig_bridge0="addm tap0 addm em0 up"
ifconfig_tap0="up"
rpcbind_enable="YES"
mountd_enable="YES"
mountd_flags="-r
nfs_server_enable="YES"
nfs_server_flags="-t -u -n4"
rpc_lockd_enable="YES"
rpc_statd_enable="YES"
dhcpd_enable"YES"
dhcpd_ifaces="em0"

Now the DHCP and NFS servers can be started with:
/usr/local/etc/rc.d/isc-dhcpd start
/etc/rc.d/rpcbind start
/etc/rc.d/mountd start
/etc/rc.d/nfsserver start
/etc/rc.d/lockd start
/etc/rc.d/statd start

In my case /etc/rc.d/nfsserver start wouldn't do anything, so I had to start nfsd like that:
nfsd -t -u -n4

After setting up everything it should be possible to start Qemu, boot the kernel and run FreeBSD from the NFS mounted root.
The start command (from here):
qemu-system-arm -M connex -m 289 -pflash flash -nographic -serial tcp::4000,server -net nic,macaddr=23:32:43:34:54:45,vlan=0,model=smc91c111 -net tap,vlan=0,ifname=tap0,script=no

Then in a different terminal connect to the machine via telnet:
telnet localhost 4000

Qemu will then boot the GUMSTIX u-boot first and try to load the kernel from a wrong address. At the prompt enter:
GUM> bootelf 40000

You should see the kernel boot, acquire a DHCP address (10.0.2.20) with the root-path option and then boot init from the NFS root. If everything worked, you'll end up at a login screen.

No comments:

Post a Comment