这部分的内容针对大部分运行Linux的嵌入式系统来说都是通用的。关于在PC端设置TFTP和NFS服务器的内容,请参考下面这篇博客:

http://blog.163.com/thinki_cao/blog/static/83944875201411610467306
TFTP启动:
        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
CPUPC指针跳转到内存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.csdn.net/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/