ZedBoard上首先从SD卡加载BOOT.bin之后即完成FSBL初始化,然后跳转到SSBL,也就是U-BOOT接管CPU。DIGILENT OOB提供的U-BOOT镜像启动以后可以看到下面这些环境变量:
zed-boot> printenv
baudrate=115200
bootcmd=run modeboot
bootdelay=3
ethact=zynq_gem
ethaddr=00:0a:35:00:01:22
ipaddr=192.168.1.10
jtagboot=echo TFTPing Linux to RAM...;tftp 0x8000 zImage;tftp 0x1000000 devicetree.dtb;tftp 0x800000 ramdisk8M.image.gz;go 0x8000
kernel_size=0x140000
modeboot=run sdboot
qspiboot=sf probe 0 0 0;sf read 0x8000 0x100000 0x2c0000;sf read 0x1000000 0x3c0000 0x40000;sf read 0x800000 0x400000 0x800000;go 0x8000
ramdisk_size=0x200000
sdboot=echo Copying Linux from SD to RAM...;mmcinfo;fatload mmc 0 0x8000 zImage;fatload mmc 0 0x1000000 devicetree_ramdisk.dtb;fatload mmc 0 0x800000 ramdisk8M.image.gz;go 0x8000
sdboot_linaro=echo Copying Linux from SD to RAM...;mmcinfo;fatload mmc 0 0x8000 zImage;fatload mmc 0 0x1000000 devicetree_linaro.dtb;go 0x8000
serverip=192.168.1.50
stderr=serial
stdin=serial
stdout=serial
Environment size: 861/65532 bytes
其中的加粗字体就是需要关注的环境变量,启动是从bootcmd环境变量开始:bootcmd->run modeboot->run sdboot
看到这里就可以看到默认情况下是从SD卡启动,下面简要分析一下操作顺序:
echo Copying Linux from SD to RAM...;
打印echo后面的文字
mmcinfo
;
显示mmc信息
fatload mmc
0
0x8000
zImage
;
加载fat文件系统根目录下的zImage文件到内存0x8000开始处
fatload mmc
0
0x1000000
devicetree_ramdisk
.
dtb
;
加载fat文件系统根目录下的devicetree_ramdisk.dtb文件到内存0x1000000
fatload
mmc
0
0x800000
ramdisk8M
.
image
.
gz
;
加载fat文件系统根目录下的
ramdisk8M
.
image
.
gz
文件到内存0x800000
go
0x8000
CPU的PC指针跳转到内存0x8000开始执行(也就是开始内核起始地址)
相应的如果要从tftp启动的话,对应的就是jtagboot启动方式了:
echo
TFTPing
Linux
to RAM
...;
tftp
0x8000
zImage
;
tftp
0x1000000
devicetree
.
dtb
;
tftp
0x800000
ramdisk8M
.
image
.
gz
;
go
0x8000
注意:这里的内容与sdboot基本相似,唯有一个地方需要注意,也就是devicetree.dtb文件名与上述的文件名不同
我们将DIGILENT OOB的sd镜像中的内核,设备树和ramdisk文件放入pc端的tftp下载目录下就可以运行了。
tftp-hpa有一个问题,就是每次开机使用之前都需要重启一下服务:
sudo service tftpd-hpa restart
重启完毕后还需要配置ZedBoard的IP,从而保证板子与PC机在同一个网段,这里我在U-Boot中的配置是:
zed
-
boot
>
setenv serverip
10.10
.
143.230
zed
-
boot
>
setenv ipaddr
10.10
.
143.101
zed
-
boot
>
setenv netmask
255.255
.
255.0
zed
-
boot
>
ping
10.10
.
143.230
Trying
to
set
up GEM link
...
Phy
ID
:
01410DD1
Resetting
PHY
...
PHY reset complete
.
Waiting
for
PHY to complete
auto
-
negotiation
...
Link
is
now at
100Mbps
!
Using
zynq_gem device
host
10.10
.
143.230
is
alive
(
说到这里不得不吐槽一下默认的UBOOT镜像竟然不支持saveenv命令!)
如果ping通以后就可以执行jtagboot:(这里的ping操作是先复位网卡物理层IC然后再连接,再ping,与之前TI DM365的UBOOT差别比较大)
zed-boot>run jtagboot
TFTPing Linux to RAM...
Using zynq_gem device
TFTP from server 10.10.143.230; our IP address is 10.10.143.101
Filename 'zImage'.
Load address: 0x8000
Loading: #################################################################
#################################################################
######################################################
done
Bytes transferred = 2695640 (2921d8 hex)
Using zynq_gem device
TFTP from server 10.10.143.230; our IP address is 10.10.143.101
Filename 'devicetree.dtb'.
Load address: 0x1000000
Loading: #
done
Bytes transferred = 6161 (1811 hex)
Using zynq_gem device
TFTP from server 10.10.143.230; our IP address is 10.10.143.101
Filename 'ramdisk8M.image.gz'.
Load address: 0x800000
Loading: #################################################################
#################################################################
#################################################################
#########################################################
done
Bytes transferred = 3694108 (385e1c hex)
## Starting application at 0x00008000 ...
Uncompressing Linux... done, booting the kernel.
[ 0.000000] Booting Linux on physical CPU 0
到这里就基本上算是tftp启动成功了。
NFS启动:
nfs启动需要修改启动参数,在以前启动参数都是在U-Boot中以bootargs环境变量的形式传递给内核的,现在由于ZedBoard启用了设备树,所以启动参数都是从设备树中传递给Linux内核,我们先看一下设备树中默认的启动参数:
从SD卡启动Linaro:
bootargs = "console=ttyPS0,115200 root=/dev/mmcblk0p2 rw earlyprintk rootfstype=ext4 rootwait devtmpfs.mount=1";
console
指定控制台的设备以及波特了
root
指定挂载的根文件系统,这里是/dev/mmcblk0p2,当时在创建linaro的sd卡镜像时会特别提示需要在SD卡中创建两个分区,第一个分区是FAT文件系统,存放内核,设备树,BOOT.bin等,而第二个就是存放Linaro文件系统了,这里也就是对应的mmcblk0p2设备。
rootfstype
制定跟文件系统的类型,这里是ext4
rw
rw参数告诉内核以读写方式加载根文件系统。 ro参数告诉内核以只读方式加载根文件系统,以便进行文件系统完整性检查,比如运行fsck;
earlyprintk
在console设备注册前(也就是printk注册之前)提供对打印函数的支持,这个之前就可以使用early_printk()函数来代替printk()函数
rootwait
让内核等待所有设备都被初始化完成后,再去执行root文件系统的挂载工作。这样可以避免根文件系统驱动初始化成功之前就挂载根文件系统。详细的说明可以参考http://blog.youkuaiyun.com/liujixin8/article/details/5704991
devtmpfs.mount
是否挂载devtmpfs,1是挂载,0是不挂载。Devtmpfs lets the kernel create a tmpfs very early at kernel initialization, before any driver core device is registered. Every device with a major/minor will have a device node created in this tmpfs instance. After the rootfs is mounted by the kernel, the populated tmpfs is mounted at /dev. In initramfs, it can be moved to the manually mounted root filesystem before /sbin/init is executed.相关的信息自己Google去吧。
从SD卡启动ramdisk:
bootargs = "console=ttyPS0,115200 root=/dev/ram rw initrd=0x800000,8M earlyprintk rootwait rootfstype=ext4 devtmpfs.mount=0";
这里重点讲与启动Linaro不一样的地方:
root=/dev/ram
基于ram根文件系统
initrd=0x800000,8M
这里指的是init ramdisk,用于在真正的挂载根文件系统之前进行初始化的ram文件系统,内存中起始地址是0x800000,大小是8MB(其实这正对应着UBOOT中的启动参数:fatload mmc 0 0x800000 ramdisk8M.image.gz) 在 《Embedded Linux Primer: A Practical Real-World Approach》Christopher Hallinan (译本《嵌入式Linux基础教程》)中有较为详细的介绍。
注:完整的内核参数说明请参考内核源代码中的Documentation/kernel-parameters.txt文档
解释完上面的意思之后就要修改设备树中的启动参数了,修改后的参数如下:
bootargs = "console=ttyPS0,115200 root=/dev/nfs rw nfsroot=10.10.143.230:/zynq/rootfs ip=10.10.143.101:10.10.143.230:10.10.143.20:255.255.255.0::eth0:off earlyprintk rootwait devtmpfs.mount=0";
其中主要修改的地方:
root=/dev/nfs
制定/dev/nfs设备为根文件系统
nfsroot=10.10.143.230:/zynq/rootfs
nfs根文件系统的目录:10.10.143.230服务器上的/zynq/rootfs目录
ip=10.10.143.101:10.10.143.230:10.10.143.20:255.255.255.0::eth0:off
这里的参数最终被内核读取启动之后打印如下信息:
[ 1.430000] IP-Config: Complete:
[ 1.430000] device=eth0, addr=10.10.143.101, mask=255.255.255.0, gw=10.10.143.20
[ 1.440000] host=10.10.143.101, domain=, nis-domain=(none)
[ 1.440000] bootserver=10.10.143.230, rootserver=10.10.143.230, rootpath=
修改完毕之后编译设备树:
./linux-digilent/scripts/dtc/dtc -I dts -O dtb -o devicetree_ramdisk.dtb devicetree_ramdisk.dts
覆盖SD卡目录下默认的设备树文件即可,下面还要开启内核对NFS的支持,可以参考下面这篇文章:
http://blog.163.com/thinki_cao/blog/static/83944875201372611250687
内核编译成功之后覆盖默认SD卡内核镜像。启动ZedBoard的时候按照sdboot默认参数启动即可,如果在启动参数中看到这条指令即可说明nfs挂载rootfs成功:
…………………………………………
[
1.450000
]
No
soundcards found
.
[ 1.460000] VFS: Mounted root (nfs filesystem) on device 0:11.
[
1.470000
]
Freeing
init memory
:
152K
……………………………………
其实如果挂载失败的话就直接Kernel Panic了。另外我们还需要在PC端的nfs目录挂载处放上原来ramdisk的文件系统,记得要解压。这里面还有一个问题,就是init进程启动以后会重新分配IP,所以我们需要注释掉etc/init.d/rcS脚本中的
ifconfig eth0 down
ifconfig eth0
192.168
.
1.10
up
否则你就会出现:
………………
[ 1.390000] VFS: Mounted root (nfs filesystem) on device 0:11.
[ 1.400000] devtmpfs: mounted
[ 1.400000] Freeing init memory: 152K
Starting rcS...
++ Mounting filesystem
++ Setting up mdev
++ Configure static IP 192.168.1.10
[ 11.020000] nfs: server 10.10.143.230 not responding, still trying
原因很简单,修改了IP,nfs server当然不响应了。如果在启动过程中虽然成功挂载了nfs,但是却出现了下面这些:
mdev: can't create 'ram0': Operation not permitted
……………………
mdev: can't create 'loop0': Operation not permitted
……………………
mdev: can't create 'ram10': Operation not permitted
……………………
那么在PC端口的NFS服务配置中还需要加入no_root_squash参数,即/etc/exports文件中的配置如下:
/zynq/rootfs *(rw,sync,no_subtree_check,no_root_squash)
链接:http://serverfault.com/questions/212178/chown-on-a-mounted-nfs-partition-gives-operation-not-permitted中解释了这个字段的含义:
By default the root_squash export option is turned on,
therefore NFS does not allow a root user from the client to perform operations as root on the server, instead mapping it to the user/group id specified by anonuid and anongid options (default=65534). This is configurable in /etc/exports together with other export options.
这里mdev创建/dev目录下的设备节点就是root用户才能有权限进行的操作,所以不能squash root权限: - )
利用TFTP和NFS能够非常方便地开发和调试Linux系统,所以本文对于其他的运行Linux嵌入式系统一样具有通用性!
最后要感谢小辉辉提供的详细介绍:http://blog.163.com/thinki_cao/blog/static/83944875201439112133825/