FRIENDz

FAILURE IS THE SIGN OF SUCCESS!!

Emulator for Embedded Linux


Target Emulation and Virtual Machines
Virtualization is a mature technology that lets several operating systems share the physical resources of a machine, such that that each thinks it has exclusive use of the resources. Emulation means that a program impersonates another—or, in this case, that a processor impersonates another. Cygwin is software that emulates a POSIX system on a Windows machine.
However, when you’re emulating a processor different than the host, you have fewer options. Emulating a different processor requires software that, in effect, translates the op-codes of the emulated processor into the op-codes for the host processor.

A common practice in embedded engineering is to write code that is compiled and tested on the development host. This makes sense, because the C language is portable enough to make this possible. Although large amounts of code can be written, compiled using the native tools, and tested without the benefit of an emulator, some things, such as the following, require testing using the target processor or emulator:

• Inline assembly: This is the most obvious. Code that has inline assembly for an
ARM target won’t compile on that nice new quad core Intel 64 bit host, no matter
how much you want it to.
• Endiannesss: This describes the byte order used to store data. In a big-endian system, the high-order bytes precede the low-order bytes; little-endian is the reverse. The date 2009-01-31 is big endian, whereas 31-01-2009 is little-endian.1 If endianness isn’t agreed on, imagine the confusion with a date like 02-05-2009, where the month and day aren’t obvious. This is an example to make plain the notion that endianness, as the internal storage format for a date in a computer, is usually an integer.
• Floating point: The floating-point capacities aren’t the same for all processors and the emulator. One processor’s very large number is another processor’s overflow. Not many embedded systems need high precision, but the ones that do should take these limitations into consideration.
• Optimization: The GCC compiler is smart enough to optimize not only by refactoring and re-ordering the code but also by selecting machine instructions that execute more quickly. This means the classic speed for time optimization may be very different on the host than the target. Optimization is also likely to find bugs in the code generated by GCC
Emulation via QEMU
QEMU is a growing emulation project started by Fabrice Bellard. It’s available for Linux and Windows hosts and emulated PowerPC, ARM, MIPS, and SPARC targets. QEMU takes the approach of providing a minimal translation layer between the host and target processor.
QEMU also provides support for USB, serial, graphics, and network devices by mapping them to a real device on the host machine.
Compiling QEMU
QEMU is available in source form; the site has precompiled binaries as well. In spite of the binary distribution, this is open source software, so knowing how to compile it is important in case a patch becomes available—or just because it’s open source, and compiling from source is the right thing to do. QEMU requires GCC 3.0 in order to build. To check what version of GCC is currently installed, do the following:

$ gcc –dump-version
4.2.3
If it does present you with a 4.0 or higher version number, install the GCC 3.4 package. Don’t worry about multiple GCC installations on the system. GCC installs as gcc-<version> and creates a symlink gcc that points at the newest version. If you set the environment variable CC to gcc-3.4, that executable is used instead of the most recent gcc:

$ apt-get install gcc-3.4
$ export CC=gcc-3.4

QEMU’s build also requires some additional development libraries in order to build. Fetch these by doing the following on an Ubuntu or a Debian system:

$ sudo apt-get install libsdl-gfx1.2-dev zlib1g-dev

Start compiling QEMU by getting the source code at http://bellard.org/qemu/download.html.
The current version is 0.9.1; you can download it using wget, like so:

$ cd ~
$ wget http://bellard.org/qemu/qemu-0.9.1.tar.gz

Then, untar:
$ tar zxf qemu-0.9.1.tar.gz
Start the build process by doing a configure:

$ cd qemu-0.9.1
$ ./configure
Does this message appear? WARNING: "gcc" looks like gcc 4.x Looking for gcc 3.x gcc 3.x not found! QEMU is known to have problems when compiled with gcc 4.x It is recommended that you use gcc 3.x to build QEMU To use this compiler anyway, configure with --disable-gcc-check
This message means the GCC installed on the system isn’t a 3.x version. Check that GCC 3.4 has been installed, and make sure the environment has the CC variable set to gcc-3.4. Running configure with the –disable-gcc-check flag results in the configure step working correctly, but the compilation fails. After the configure step, typing

$ make
$ sudo make install
builds and installs QEMU.

For Windows users, a precompiled package is available at http://www.h7.dion.ne.jp/~qemuwin/.
It’s recommended that QEMU users on Windows start with this precompiled package.
Using QEMU to Emulate a Target
The QEMU maintainer has thoughtfully included ready-to-boot packages for the following processors:
• MIPS
• ARM
• X86
• ColdFire
Visit http://bellard.org/qemu/download.html and scroll to the bottom to see the current distributions available. Using the ARM distribution as an example, here is how to use one of these boot packages to quickly get a system up and running and prove that the QEMU that you built works as expected. The packages include a kernel and an initrd root file system. These systems are self-contained and immutable since the initrd file system is loaded into RAM memory at boot time and there’s no way to save the changes between boots. This has both benefits and drawbacks. The greatest benefit is that no matter what you change or delete, completely resetting the environment is just a few keystrokes away. This is also a drawback, because changes to the file system can’t be easily stored for the next power cycle without re-creating the initial RAM disk. Because you’re using these file systems to test the board, thebenefits outweigh the drawbacks. Follow these steps to download and unpack the file:
$ cd ~
$ tar xzf arm-test-0.2.tar.gz
Use these commands to start the machine without a graphical terminal:
$ cd arm-test
$ ../qemu-0.9.1/arm-softmmu/qemu-system-arm -kernel zImage.integrator-initrd arm_root.img -nographic -append "console=ttyAMA0"
A lot of parameters are passed into QEMU to make things work. Table 3-1 describes what they mean. QEMU is performing the job of a boot loader, so at the least, it needs to know what kernel to boot.
QEMU kernel boot parameters e
-kernel <kernel image>
Indicates what kernel to use. This file is a kernel image that’s been compressed.
It looks for the kernel file in the current directory if one isn’t specified.
-initrd <initial ramdisk image>
The initial RAM disk to load for the kernel. This is the only file system used by
the emulated target.
-nographic
QEMU configures a VGA device for the board if this isn't specified. The board
being booted doesn’t have graphics support, so there’s no need for QEMU to
emulate graphics.
-append <parameters> This is what appears when you add append to the end of the kernel’s command line. The kernel’s command line is similar to the parameters passed into a program. In this case, setting console=ttyAMA0 means the kernel should use the /dev/ttyAMA0 device for the console. This device is created by QEMU so the board has a pseudo-serial device to use for a terminal.
In a few seconds, the kernel boot-up message appears. Log in as root (no password is necessary), and verify that the machine is an ARM target:

# cat /proc/cpuinfo
Processor : ARM926EJ-Sid(wb) rev 5 (v5l)
BogoMIPS : 137.21
Features : swp half thumb fastmult edsp java
CPU implementer : 0x41
CPU architecture: 5TEJ
CPU variant : 0x0
CPU part : 0x926
CPU revision : 5
Cache type : write-through
Cache clean : not required
Cache lockdown : not supported
Cache format : Harvard
I size : 4096
I assoc : 4
I line length : 32
I sets : 32
D size : 65536
D assoc : 4
D line length : 32
D sets : 512
Hardware : ARM-IntegratorCP
Revision : 0000
Serial : 0000000000000000
Pretty neat! A little investigation reveals that this is a fairly basic root file system, but it does the job of starting the board and providing a prompt. At this point, you may be tempted to use the root file system here as the starting point for your project; and that isn’t an outlandish notion if the project in question only has a few additional executable files. More complex projects require enough changes to the kernel and root file system to merit a rebuild, because the amount of effort to modify or build is similar. To quit the emulator, press Ctrl+A followed by X. The machine is terminated, and control returns to the terminal prompt. There’s no confirmation to quit, so be careful not to unintentionally shut down the machine.
By creating this machine, you can test development work on the emulated host before the hardware for the project arrives, allowing activities that were once serialized to have some degree of overlap. This is very helpful in situations where the board hardware is in short supply or the project is distributed geographically and getting hardware to engineers is difficult. In addition, having QEMU run the board also shortens the iteration time for testing new root file systems and boot-up scripts.

Using QEMU to Compile under Emulation
QEMU also supports a user-space mode in which instead of running an entire virtual machine to run a program, QEMU runs a user program. To run that user-space program, you need a binary and all the libraries used by that binary. The simplest way to get this for an ARM processor is to download it from ARM directly:

$ cd ~
$ wget http://www.arm.com/linux/armbase_2.5.cramfs

If this link is no longer available, visit ARM’s distribution page at
http://www.arm.com/products/os/linux.html. After it’s downloaded, you can loop-back mount the image using this command:

$ sudo mkdir –p /mnt/arm-rfs
$ sudo mount -o loop -t cramfs ./armbase_2.5.cramfs /mnt/arm-rfs/

Loop-back mounting is a way to connect a file with a block device and then mount the device as if it were a physical block device. When it’s mounted, the /mnt/arm-rfs directory contains a file system that has the necessary libraries to run the programs in the /mnt/arm-rfs/bin directory. When QEMU attempts to run a program that has shared libraries, it needs to know the location of the shared library loader and be able to load the libraries. On a Linux system, programs that use shared libraries depend on an executable to do the final link step so the executable can run. On a Linux desktop system, that file is usually /lib/ld-linux.so.2, but
it can have any file name. When QEMU is built, it looks for these library-handling files in the /usr/gnemul/qemu-arm directory by default. In order for QEMU to find those files, copy the contents of the lib directory of /mnt/arm-rfs to that location. A copy is better than creating a symlink, because that symlink is invalid when the file system is unmounted from /mnt/arm-rfs:

$ sudo mkdir –p /usr/gnemul/qemu-arm
$ sudo cp –a /mnt/arm-rfs/lib /usr/gnemul/qemu-arm
Now that everything is in place, run a command by doing the following:
$ cd /mnt/arm-rfs
$ qemu-arm /mnt/arm-rfs/bin/ls

By taking this approach, no cross-compilation is necessary, because the code thinks it’s running on an ARM (or whatever) host. Later in the book, I cover using a native compiler running under an emulator as opposed to a cross-compiler for building a system. The examples in this section have covered ARM processors, but remember that QEMU supports more than just the ARM processor.

No comments:

Post a Comment