实验说明

实验主机系统为debian9。ubuntu系统安装方法相似。可以通过qemu启动bootloader,再由bootloader启动内核,也可以直接由qemu启动内核。

安装qemu虚拟机

1
apt install qemu

交叉编译器安装

1
apt install gcc-arm-linux-gnueabi

安装u-boot-tools

该工具主要为了给内核编译以及文件系统制作提供mkimage程序

1
apt-get install u-boot-tools

uboot编译(可选)

1.获取uboot源码,本实验以u-boot-2016.09.tar.bz2为例

1
wget ftp://ftp.denx.de/pub/u-boot/u-boot-2016.09.tar.bz2

2.根据qemu支持的机器配置uboot

获取qemu支持的机器列表:qemu-system-arm -machine help

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
root@debian:~/work/arm/env# qemu-system-arm -machine help
Supported machines are:
akita Sharp SL-C1000 (Akita) PDA (PXA270)
ast2500-evb Aspeed AST2500 EVB (ARM1176)
borzoi Sharp SL-C3100 (Borzoi) PDA (PXA270)
canon-a1100 Canon PowerShot A1100 IS
cheetah Palm Tungsten|E aka. Cheetah PDA (OMAP310)
collie Sharp SL-5500 (Collie) PDA (SA-1110)
connex Gumstix Connex (PXA255)
cubieboard cubietech cubieboard
highbank Calxeda Highbank (ECX-1000)
imx25-pdk ARM i.MX25 PDK board (ARM926)
integratorcp ARM Integrator/CP (ARM926EJ-S)
kzm ARM KZM Emulation Baseboard (ARM1136)
lm3s6965evb Stellaris LM3S6965EVB
lm3s811evb Stellaris LM3S811EVB
mainstone Mainstone II (PXA27x)
midway Calxeda Midway (ECX-2000)
musicpal Marvell 88w8618 / MusicPal (ARM926EJ-S)
n800 Nokia N800 tablet aka. RX-34 (OMAP2420)
n810 Nokia N810 tablet aka. RX-44 (OMAP2420)
netduino2 Netduino 2 Machine
none empty machine
nuri Samsung NURI board (Exynos4210)
palmetto-bmc OpenPOWER Palmetto BMC (ARM926EJ-S)
raspi2 Raspberry Pi 2
realview-eb ARM RealView Emulation Baseboard (ARM926EJ-S)
realview-eb-mpcore ARM RealView Emulation Baseboard (ARM11MPCore)
realview-pb-a8 ARM RealView Platform Baseboard for Cortex-A8
realview-pbx-a9 ARM RealView Platform Baseboard Explore for Cortex-A9
sabrelite Freescale i.MX6 Quad SABRE Lite Board (Cortex A9)
smdkc210 Samsung SMDKC210 board (Exynos4210)
spitz Sharp SL-C3000 (Spitz) PDA (PXA270)
sx1 Siemens SX1 (OMAP310) V2
sx1-v1 Siemens SX1 (OMAP310) V1
terrier Sharp SL-C3200 (Terrier) PDA (PXA270)
tosa Sharp SL-6000 (Tosa) PDA (PXA255)
verdex Gumstix Verdex (PXA270)
versatileab ARM Versatile/AB (ARM926EJ-S)
versatilepb ARM Versatile/PB (ARM926EJ-S)
vexpress-a15 ARM Versatile Express for Cortex-A15
vexpress-a9 ARM Versatile Express for Cortex-A9
virt-2.6 QEMU 2.6 ARM Virtual Machine
virt-2.7 QEMU 2.7 ARM Virtual Machine
virt QEMU 2.8 ARM Virtual Machine (alias of virt-2.8)
virt-2.8 QEMU 2.8 ARM Virtual Machine
xilinx-zynq-a9 Xilinx Zynq Platform Baseboard for Cortex-A9
z2 Zipit Z2 (PXA27x)

这里使用arm官方出的开发板vexpress-a9,如下配置uboot

1
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- vexpress_ca9x4_defconfig

3.编译uboot

1
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-

编译后得到的目标程序为u-boot

linux内核编译

1.获取内核,这里以linux-5.3.4.tar.xz为例

1
wget https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.3.4.tar.xz

2.配置平台和交叉编译器

为了方便修改和编译,直接修改Makefile以配置平台和编译工具链。

1
2
ARCH=arm
CROSS_COMPILE=arm-linux-gnueabi-

3.配置board

根据编译uboot阶段选用的开发板,配置内核,这里是vexpress_defconfig

1
make vexpress_defconfig

4.编译内核

在这里指定了内核的加载地址为0X60003000,在uboot引导内核的时候也需要设置该地址。

1
make LOADADDR=0X60003000 uImage -j2

内核目标文件为arch/arm/boot/uImage

5.编译设备树

1
make vexpress-v2p-ca9.dtb

设备树目标文件为arch/arm/boot/dts/vexpress-v2p-ca9.dtb

文件系统制作(busybox)

1.下载最新busybox

1
wget https://busybox.net/downloads/busybox-1.31.1.tar.bz2

2.配置busybox交叉编译工具前缀:make menuconfig

1
Setting->cross compile prefix 配置为arm-linux-gnueabi-

3.编译和安装:make && make install

安装完成后在其顶层目录生成_install目录。将其拷贝为rootfs目录。

1
cp _install ../rootfs

4.在rootfs中创建其它需要的目录

1
mkdir mkdir mnt tmp var  sys proc etc lib dev  sbin root home

5.在dev目录下创建console和null文件

1
2
mknod  -m 666 dev/console c 5 1
mknod -m 666 dev/null c 1 3

6.添加配置文件以及修改配置文件

将busybox的examples/bootfloppy/etc中的所有文件拷贝到rootfs/etc中。

1
cp busybox-1.31.1/examples/bootfloppy/etc/* rootfs/etc -arf

修改rootfs/etc/fstab内容为:

1
2
3
proc          /proc           proc       defaults              0  0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0

修改rootfs/etc/profile内容为:

1
2
3
4
5
6
PS1='[\u@\h \W]\# '
HOSTNAME=`/bin/hostname`
export USER LOGNAME PS1
PATH=/bin:/sbin:/usr/bin:/usr/sbin
LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH
export PATH LD_LIBRARY_PATH

修改rootfs/etc/inittab内容为:

1
2
3
4
5
6
7
::sysinit:/etc/init.d/rcS
#::respawn:-/bin/sh ###不需要登陆
::respawn:-/bin/login ###需要登陆
#console::askfirst:-/bin/sh
#tty2::askfirst:-/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r

修改rootfs/etc/init.d/rcS内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/bin/mount -n -t ramfs ramfs /var

/bin/mkdir -p /var/tmp
/bin/mkdir -p /var/modules
/bin/mkdir -p /var/run
/bin/mkdir -p /var/log
/bin/mount -a
/bin/hostname -F /etc/hostname

/bin/mount -t devtmpfs none /dev
/bin/mkdir -p /dev/pts
/bin/mkdir -p /dev/shm
#/bin/mknod /dev/pts/0 c 136 0
/bin/mount -n -t devpts none /dev/pts -o mode=0622
#echo /sbin/mdev > /proc/sys/kernel/hotplug
#/sbin/mdev -s

/sbin/ifconfig lo 127.0.0.1 netmask 255.0.0.0
/sbin/ifconfig eth0 up

创建rootfs/etc/hostname文件

1
2
touch rootfs/etc/hostname
echo busybox > rootfs/etc/hostname

创建rootfs/passwd文件

1
2
3
4
5
touch rootfs/etc/passwd
echo root::0:0:root:/root:/bin/ash > rootfs/etc/passwd
echo bin:*:1:1:bin:/bin: >> rootfs/etc/passwd
echo daemon:*:2:2:daemon:/sbin: >> rootfs/etc/passwd
echo nobody:*:1:99:99:Nobody:/: >> rootfs/etc/passwd

7.将所使用的编译器自带的动态库拷贝到rootfs/lib目录中。

1
cp /usr/arm-linux-gnueabi/lib/* rootfs/lib/ -arf

如果在busybox配置中选择了Settings->build Options ->Build busybox as a static binary,那么此处不需要拷贝动态库文件,但是要添加自己的程序到新做的文件系统中,则需要静态编译或者同事添加动态库。

文件系统目标目录文件为rootfs

制作ramdisk文件系统镜像(可选)

系统可以从内存中加载,也可以从其它介质中加载,如emmc,nandflash等,如果不需要从内存加载系统,则不需要制作ramdisk系统镜像。

1.安装genext2fs

1
apt install genext2fs

2.将rootfs制作成ext2镜像格式

1
genext2fs -b 32768 -d rootfs ramdisk

镜像大小为32768kb,即32M,生成的镜像名为ramdisk

3.压缩ramdisk镜像为ramdisk.gz

1
gzip -v9 -f ramdisk

4.制作uboot格式的ramdisk镜像

1
mkimage -n 'uboot ext2 ramdisk rootfs' -A arm -O linux -T ramdisk -C gzip -d ramdisk.gz ramdisk.img

最终生成ramdisk.img

5.内核配置支持

要支持ramdisk系统,linux内核需要以下配置项的支持

  • General setup->Initial RAM filesystem and RAM disk(initramfs/initrd) support

  • Device Drivers->Block device->RAM block device support以及Default RAM disk size kbytes的值修改为di2步中ramdisk的大小,即32768

  • File systems->Second extended fs support

系统加载和启动

系统的存储介质可以是emmc,nandflash,sd卡,网络等。

通过SD卡启动ramdisk系统(可选)

1.制作一个虚拟SD卡,大小为512M,文件系统格式为ext2

1
2
dd if=/dev/zero of=virtsd.ext2 bs=1M count=512
mkfs.ext2 virtsd.ext2

制作好的目标sd卡是virtsd.ext2

2.将目标程序拷贝到sd卡

将内核,设备树以及ramdisk文件系统拷贝到sd卡中

1
2
3
4
5
6
mount -t ext2 virtsd.ext2 /mnt -o loop
cp linux-5.3.4/arch/arm/boot/uImage /mnt
cp linux-5.3.4/arch/arm/boot/dts/vexpress-v2p-ca9.dtb /mnt
cp ramdisk.img /mnt
sync
umount /mnt

3.通过qume虚拟机启动uboot

启动qemu虚拟机,设定机器为vexpress-a9 加载程序为u-boot sd卡为virtsd.ext2 内存为256M,不需要图形界面

1
qemu-system-arm -M vexpress-a9 -kernel u-boot -sd virtsd.ext2 -nographic -m 256M

待uboot启动进入倒计时的时候按任意键进入uboot操作界面,然后设置uboot启动参数

4.启动内核

在uboot操作界面中设置启动参数设置如下:

1
暂时不需要设置

将内核,设备树以及ramdisk文件系统从sd卡读到内存中:

1
ext2load mmc 0 60003000 uImage;ext2load mmc 0 61000000 vexpress-v2p-ca9.dtb;ext2load mmc 0 62000000 ramdisk.img

启动系统

1
bootm 60003000 62000000 61000000

如果一切正常的话,就会成功进入到busybox系统。

直接从SD卡启动系统内核(可选)

1.制作一个虚拟SD卡,大小为512M,文件系统格式为ext2

1
2
dd if=/dev/zero of=virtsd.ext2 bs=1M count=512
mkfs.ext2 virtsd.ext2

制作好的目标sd卡是virtsd.ext2

2.将整个文件系统目录拷贝到sd卡中

1
2
3
4
mount -t ext2 virtsd.ext2 /mnt -o loop
cp rootfs/* /mnt -arf
sync
umount /mnt

3.通过qemu虚拟机启动内核,设定机器为vexpress-a9 加载程序为内核zImage, sd卡为virtsd.ext2 内存为256M,不需要图形界面,设定根文件系统存储设备为/dev/mmcblk0

1
qemu-system-arm -M vexpress-a9 -m 512M -kernel zImage -dtb vexpress-v2p-ca9.dtb -nographic -append "root=/dev/mmcblk0 rw console=ttyAMA0" -sd virtsd.ext2

退出qemu虚拟机

1.直接退出:先按ctrl+a,然后松开,按x键退出虚拟机

2.系统正常关机:在虚拟机shell中执行poweroff命令即可

网络配置

qemu有多种网络模式,在这里以TUN/TAP设备虚拟网卡设置为例说明其配置过程。

先决条件

基于TUN/TAP设备的配置需要宿主机插入tun模块的支持,如果没有该模块的支持则不能顺利实施网桥模式的设置,此外,为了配置虚拟机访问外网,还需要内核开启iptables的支持,并且需要用户空间iptables管理工具。

可以通过下面的命令查看是否加载了tun模块,如果没有加载,可以先网络搜索加载方法先将其加载:

1
lsmod | grep tun

如果加载了tun模块,该命令将得到类似下面的输出:

1
tun                    28672  2

同时,需要确定tun模块的设备文件是否已经创建,其路径是/dev/net/tun。

1
2
ls /dev/net/
tun

如果没有该设备文件,可以通过下面的命令创建该设备文件:

1
2
mkdir /dev/net
mknod /dev/net/tun c 10 200

配置网络

宿主机虚拟网卡配置

在主机中创建/etc/qemu-ifup文件,该文件用来在qemu虚拟机启动的时候创建一个虚拟网卡,文件内容如下:

1
2
#!/bin/bash
ifconfig $1 172.0.0.1

然后启动虚拟机,并传入网络参数-net nic,vlan=0 -net tap,vlan=0,结合直接从SD卡启动系统内核小节的启动参数,加入网络参数后的完整的启动命令如下:

1
qemu-system-arm -M vexpress-a9 -m 512M -kernel zImage -dtb vexpress-v2p-ca9.dtb -nographic -append "root=/dev/mmcblk0 rw console=ttyAMA0" -sd virtsd.ext2 -net nic,vlan=0 -net tap,vlan=0

启动虚拟机后同时在宿主机上创建了tap0虚拟网卡,其ip地址为172.0.0.1

虚拟机网络配置

在虚拟机中配置eth0网卡和宿主机虚拟网卡tap0在同一网段:

1
ifconfig eth0 172.0.0.2 netmask 255.255.0.0

此时,虚拟机即可和宿主机上的tap0网卡互通,可以通过ping命令测试。

配置虚拟机的默认网关地址为宿主机tap0的地址:

1
route add default gw 172.0.0.1

此时,可以通过虚拟机访问到宿主机eth0的网段了。可以通过ping命令测试。

虚拟机外网访问配置

首先通过下面的命令在宿主机上开启路由转发功能:

1
echo 1 > /proc/sys/net/ipv4/ip_forward

然后添加nat转换规则:

1
iptables -t nat -A POSTROUTING -o eth0 -s 172.0.0.0/24 -j MASQUERADE

此时即可通过虚拟机访问互联网,可以通过ping一些公网的ip地址进行测试。