When using gem5 in full-system mode, you have to have a disk image with the operating system and all of your data on it. This is just like having a physical disk in a physical machine. In this post, I'm going to walk through how to create a new disk and install a (semi-)current version of Ubuntu on the disk. By the end of this post, you should be able to create your own disk with whatever extra data and applications you want.
This post assumes that you have already checked out a version of gem5 and can build and run gem5 in full-system mode. The Learning gem5 documentation is a good place to start. This post uses the x86 ISA for gem5, and is mostly applicable to other ISAs. More details on setting up ARM systems can be found on the gem5 wiki: http://gem5.org/Ubuntu_Disk_Image_for_ARM_Full_System.
In the future, this post may be folded into Learning gem5.
Creating a blank disk image
The first step is to create a blank disk image (usually a .img file). Luckily, the gem5 developers have already made this easy with a tool that is simple to use. To create a blank disk image, which is formatted with ext2 by default, simply run the following.
> util/gem5img.py init ubuntu-14.04.img 4096
This command creates a new image, called "ubuntu-14.04.img" that is 4096 MB. This command may require you to enter the sudo password, if you don't have permission to create loopback devices. You should never run commands as the root user that you don't understand! You should look at the file util/gem5img.py and ensure that it isn't going to do anything malicious to your computer!
We will be using util/gem5img.py heavily throughout this post, so you may want to understand it better. If you just run util/gem5img.py, it displays all of the possible commands.
Usage: %s [command] <command arguments>
where [command] is one of
init: Create an image with an empty file system.
mount: Mount the first partition in the disk image.
umount: Unmount the first partition in the disk image.
new: File creation part of "init".
partition: Partition part of "init".
format: Formatting part of "init".
Watch for orphaned loopback devices and delete them with
losetup -d. Mounted images will belong to root, so you may need
to use sudo to modify their contents
Copying root files to the disk
Now that we have created a blank disk, we need to populate it with all of the OS files. Ubuntu distributes a set of files explicitly for this purpose. You can find the Ubuntu core distribution for 14.04 at http://cdimage.ubuntu.com/ubuntu-core/releases/14.04/release/ Since I am simulating an x86 machine, I chose the file ubuntu-core-14.04-core-amd64.tar.gz. Download whatever image is appropriate for the system you are simulating.
Next, we need to mount the blank disk and copy all of the files onto the disk.
../../util/gem5img.py mount ubuntu-14.04.img mnt
sudo tar xzvf ubuntu-core-14.04-core-amd64.tar.gz -C mnt
The next step is to copy a few required files from your working system onto the disk so we can chroot into the new disk. We need to copy /etc/resolv.conf onto the new disk.
sudo cp /etc/resolv.conf mnt/etc/
Setting up gem5-specific files
Create a serial terminal
By default, gem5 uses the serial port to allow communication from the host system to the simulated system. To use this, we need to create a serial tty. Since Ubuntu uses upstart to control the init process, we need to add a file to /etc/init which will initialize our terminal. Also, in this file, we will add some code to detect if there was a script passed to the simulated system. If there is a script, we will execute the script instead of creating a terminal.
Put the following code into a file called /etc/init/tty-gem5.conf
# ttyS0 - getty
# This service maintains a getty on ttyS0 from the point the system is
# started until it is shut down again, unless there is a script passed to gem5.
# If there is a script, the script is executed then simulation is stopped.
start on stopped rc RUNLEVEL=
stop on runlevel [!12345]
# Create the serial tty if it doesn't already exist
if [ ! -c /dev/ttyS0 ]
mknod /dev/ttyS0 -m 660 /dev/ttyS0 c 4 64
# Try to read in the script from the host system
/sbin/m5 readfile > /tmp/script
chmod 755 /tmp/script
if [ -s /tmp/script ]
# If there is a script, execute the script and then exit the simulation
exec su root -c '/tmp/script' # gives script full privileges as root user in multi-user mode
# If there is no script, login the root user and drop to a console
# Use m5term to connect to this console
exec /sbin/getty --autologin root -8 38400 ttyS0
We also need to set up the localhost loopback device if we are going to use any applications that use it. To do this, we need to add the following to the /etc/hosts file.
::1 localhost ip6-localhost ip6-loopback
Next, we need to create an entry in /etc/fstab for each partition we want to be able to access from the simulated system. Only one partition is absolutely required (/); however, you may want to add additional partitions, like a swap partition.
The following should appear in the file /etc/fstab.
# /etc/fstab: static file system information.
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
# <file system> <mount point> <type> <options> <dump> <pass>
/dev/hda1 / ext3 noatime 0 1
Copy the m5 binary to the disk
gem5 comes with an extra binary application that executes pseudo-instructions to allow the simulated system to interact with the host system. To build this binary, run make -f Makefile.<isa> in the gem5/m5 directory, where <isa> is the ISA that you are simulating (e.g., x86). After this, you should have an m5 binary file. Copy this file to /sbin on your newly created disk.
After updating the disk with all of the gem5-specific files, unless you are going on to add more applications or copying additional files, unmount the disk image.
> util/gem5img.py umount mnt
Install new applications
The easiest way to install new applications on to your disk, is to use chroot. This program logically changes the root directory ("/") to a different directory, mnt in this case. Before you can change the root, you first have to set up the special directories in your new root. To do this, we use mount -o bind.
> sudo /bin/mount -o bind /sys mnt/sys
> sudo /bin/mount -o bind /dev mnt/dev
> sudo /bin/mount -o bind /proc mnt/proc
After binding those directories, you can now chroot:
> sudo /usr/sbin/chroot mnt /bin/bash
At this point you will see a root prompt and you will be in the / directory of your new disk.
You should update your repository information.
> apt-get update
You may want to add the universe repositories to your list with the following commands. Note: The first command is require in 14.04.
> apt-get install software-properties-common
> add-apt-repository universe
> apt-get update
Now, you are able to install any applications you could install on a native Ubuntu machine via apt-get.
Remember, after you exit you need to unmount all of the directories we used bind on.
> sudo /bin/umount mnt/sys
> sudo /bin/umount mnt/proc
> sudo /bin/umount mnt/dev