Beaglebone Black——理论篇beaglebone black启动——从串口获得SPL、U-BOOT,TFTP服务器获得内核,NFS服务器挂载根文件系统

          一般来讲启动一个系统所需的bootloader(SPL/MLO、u-boot.img)和根文件系统(/boot下包含内核zImage)要么是放在NAND Flash,或者是SD卡,或者是eMMC,或者是USB中,那么还有一种方式,就是所需要的这些文件全部都不在板子上,而是放在其他的电脑或者说服务器上。这样即使板子上没有eMMC也照样跑系统!而且由于根文件系统在NFS服务器上,使得板子上的系统的“硬盘空间”变得十分充足!


关于一个系统的启动流程相关参考资料

ARM-Linux嵌入式系统启动流程


内核源码的说明文档,关于NFS启动一些需要注意的

https://www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt


1、ROM-Code如何从串口获取bootloader?

          对于BBB板子来说,当没有按住boot按键时,启动顺序是MMC1、MMC0、UART0、USB0,而按住boot按键时启动顺序为SPI0、MMC0、USB0、UART0。由于板子的MMC1已经接有eMMC,所以要想让ROM-Code从串口启动,那么断电时,拔掉SD卡,然后按住boot按键,再给板子上电,如果连接上了串口调试线,此时PC机串口应该不停的收到字符CCCCCC,这就表示ROM-Code在向串口请求SPL文件(PC机的串口调试工具推荐SecureCRT)。此时用Xmoden协议将SPL文件从串口发送过去,接着串口上会显示传送进度和速度。传送完后串口会又会收到字符CCCCCC,这就表示传送过去的SPL已经运行,并向串口请求u-boot.img文件,此时用Ymoden协议将u-boot.img文件传送过去,接下来从串口可以看到u-boot已经启动,但由于没有内核和根文件系统,所以u-boot自动停止,显示出命令行交互界面#u-boot:

          至此ROM-Code和SPL任务完成,接下来控制权转交给u-boot。


2、u-boot如何从TFTP服务器获取内核镜像,从NFS服务器挂载根文件系统?

        首先要在同一局域网内的一台PC机上配置TFTP和NFS服务器,比如可以在虚拟机ubuntu12.04中配置TFTP和NFS服务器,注意,虚拟机和宿主机一定要选择桥接模式,以使虚拟机和目标板获得同一网段的IP。

相关参考:

TFTP服务器配置

NFS服务器配置

        以上配置完成后都进行都本机测试tftp localhost、mount -t nfs -o nolock localhost:/nfs/dir/ /mnt/nfs,或者用开发板来测试,以确保配置没有问题。

        准备工作都做好之后,现在u-boot#命令交互等待用户输入,该如何配置环境变量让u-boot通过TFTP协议下载内核到内存,下载到哪个位置?传递什么参数给内核来让他知道根文件系统在哪里?下面来对此进行分析:

         进入u-boot后,输入printenv,回车,得如下输出,

U-Boot# printenv
arch=arm
autoconf=off
baudrate=115200
board=am335x
board_name=A335BNLT
board_rev=t\
ue
boot_fdt=try
bootcmd=gpio set 53; i2c mw 0x24 1 0x3e; run findfdt; setenv mmcdev 0; setenv bootpart 0:1; run mmcboot;gpio clear 56; gpio clear 55; gpio clear 54; setenv mmcdev 1; setenv bootpart 1:1; run mmcboot;run nandboot;
bootcount=2
bootdelay=1
bootenv=uEnv.txt
bootfile=zImage
bootm_size=0x10000000
bootpart=0:2
bootscript=echo Running bootscript from mmc ...; source ${loadaddr}
console=ttyO0,115200n8
cpu=armv7
device=eth0
dfu_alt_info_emmc=rawemmc mmc 0 3751936
dfu_alt_info_mmc=boot part 0 1;rootfs part 0 2;MLO fat 0 1;MLO.raw mmc 0x100 0x100;u-boot.img.raw mmc 0x300 0x400;spl-os-args.raw mmc 0x80 0x80;spl-os-image.raw mmc 0x900 0x2000;spl-os-args fat 0 1;spl-os-image fat 0 1;u-boot.img fat 0 1;uEnv.txt fat 0 1
dfu_alt_info_nand=SPL part 0 1;SPL.backup1 part 0 2;SPL.backup2 part 0 3;SPL.backup3 part 0 4;u-boot part 0 5;u-boot-spl-os part 0 6;kernel part 0 8;rootfs part 0 9
dfu_alt_info_ram=kernel ram 0x80200000 0xD80000;fdt ram 0x80F80000 0x80000;ramdisk ram 0x81000000 0x4000000
eth1addr=c8:a0:30:ac:7f:d1
ethact=cpsw
ethaddr=c8:a0:30:ac:7f:cf
fdt_addr_r=0x88000000
fdtaddr=0x88000000
fdtdir=/dtbs
fdtfile=undefined
findfdt=if test $board_name = A335BONE; then setenv fdtfile am335x-bone.dtb; setenv fdtbase am335x-bone; fi; if test $board_name = A335BNLT; then setenv fdtfile am335x-boneblack.dtb; setenv fdtbase am335x-boneblack; fi; if test $board_name = A33515BB; then setenv fdtfile am335x-evm.dtb; fi; if test $board_name = A335X_SK; then setenv fdtfile am335x-evmsk.dtb; fi; if test $fdtfile = undefined; then echo WARNING: Could not determine device tree to use; fi; 
gw_ip=192.168.1.1
importbootenv=echo Importing environment from mmc ...; env import -t -r $loadaddr $filesize
kernel_addr_r=0x82000000
loadaddr=0x82000000
loadbootenv=load mmc ${bootpart} ${loadaddr} ${bootenv}
loadbootscript=load mmc ${bootpart} ${loadaddr} ${scriptfile};
loadfdt=echo loading ${fdtdir}/${fdtfile} ...; load mmc ${bootpart} ${fdtaddr} ${fdtdir}/${fdtfile}
loadimage=load mmc ${bootpart} ${loadaddr} ${bootdir}/${bootfile}
loadramdisk=load mmc ${mmcdev} ${rdaddr} ramdisk.gz
loadrd=load mmc ${bootpart} ${rdaddr} ${bootdir}/${rdfile}; setenv rdsize ${filesize}
mmcargs=setenv bootargs console=${console} ${optargs} ${cape_disable} ${cape_enable} root=${mmcroot} rootfstype=${mmcrootfstype} ${cmdline}
mmcboot=mmc dev ${mmcdev}; if mmc rescan; then gpio set 54;echo SD/MMC found on device ${mmcdev};setenv bootpart ${mmcdev}:1; echo Checking for: /uEnv.txt ...;if test -e mmc ${bootpart} /uEnv.txt; then if run loadbootenv; then gpio set 55;echo Loaded environment from ${bootenv};run importbootenv;fi;if test -n ${cape}; then if test -e mmc ${bootpart} ${fdtdir}/${fdtbase}-${cape}.dtb; then setenv fdtfile ${fdtbase}-${cape}.dtb; fi; echo using: $fdtfile...; fi; echo Checking if uenvcmd is set ...;if test -n ${uenvcmd}; then gpio set 56; echo Running uenvcmd ...;run uenvcmd;fi;echo Checking if client_ip is set ...;if test -n ${client_ip}; then if test -n ${dtb}; then setenv fdtfile ${dtb};echo using ${fdtfile} ...;fi;gpio set 56; echo Running nfsboot ...;run nfsboot;fi;fi; echo Checking for: /${script} ...;if test -e mmc ${bootpart} /${script}; then gpio set 55;setenv scriptfile ${script};run loadbootscript;echo Loaded script from ${scriptfile};gpio set 56; run bootscript;fi; echo Checking for: /boot/${script} ...;if test -e mmc ${bootpart} /boot/${script}; then gpio set 55;setenv scriptfile /boot/${script};run loadbootscript;echo Loaded script from ${scriptfile};gpio set 56; run bootscript;fi; echo Checking for: /boot/uEnv.txt ...;for i in 1 2 3 4 5 6 7 ; do setenv mmcpart ${i};setenv bootpart ${mmcdev}:${mmcpart};if test -e mmc ${bootpart} /boot/uEnv.txt; then gpio set 55;load mmc ${bootpart} ${loadaddr} /boot/uEnv.txt;env import -t ${loadaddr} ${filesize};echo Loaded environment from /boot/uEnv.txt;if test -n ${dtb}; then setenv fdtfile ${dtb};echo Using: dtb=${fdtfile} ...;fi;echo Checking if uname_r is set in /boot/uEnv.txt...;if test -n ${uname_r}; then gpio set 56; echo Running uname_boot ...;setenv mmcroot /dev/mmcblk${mmcdev}p${mmcpart} ro;run uname_boot;fi;fi;done;fi;
mmcdev=0
mmcloados=run mmcargs; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if run loadfdt; then bootz ${loadaddr} - ${fdtaddr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi;
mmcpart=1
mmcroot=/dev/mmcblk0p2 ro
mmcrootfstype=ext4 rootwait fixrtc
mtdids=nand0=omap2-nand.0
mtdparts=mtdparts=omap2-nand.0:128k(SPL),128k(SPL.backup1),128k(SPL.backup2),128k(SPL.backup3),1792k(u-boot),128k(u-boot-spl-os),128k(u-boot-env),5m(kernel),-(rootfs)
nandargs=setenv bootargs console=${console} ${optargs} root=${nandroot} rootfstype=${nandrootfstype}
nandboot=echo Booting from nand ...; run nandargs; nand read ${fdtaddr} u-boot-spl-os; nand read ${loadaddr} kernel; bootz ${loadaddr} - ${fdtaddr}
nandroot=ubi0:rootfs rw ubi.mtd=7,2048
nandrootfstype=ubifs rootwait=1
netargs=setenv bootargs console=${console} ${optargs} root=/dev/nfs nfsroot=${serverip}:${rootpath},${nfsopts} rw ip=dhcp
netboot=echo Booting from network ...; setenv autoload no; dhcp; tftp ${loadaddr} ${bootfile}; tftp ${fdtaddr} ${fdtfile}; run netargs; bootz ${loadaddr} - ${fdtaddr}
netmask=255.255.255.0
nfs_options=,vers=3
nfsargs=setenv bootargs console=${console} ${optargs} ${cape_disable} ${cape_enable} root=/dev/nfs rw rootfstype=${nfsrootfstype} nfsroot=${nfsroot} ip=${ip} ${cmdline}
nfsboot=echo Booting from ${server_ip} ...; setenv nfsroot ${server_ip}:${root_dir}${nfs_options}; setenv ip ${client_ip}:${server_ip}:${gw_ip}:${netmask}:${hostname}:${device}:${autoconf}; setenv autoload no; setenv serverip ${server_ip}; setenv ipaddr ${client_ip}; tftp ${loadaddr} ${bootfile}; tftp ${fdtaddr} dtbs/${fdtfile}; run nfsargs; bootz ${loadaddr} - ${fdtaddr}
nfsopts=nolock
nfsrootfstype=ext4 rootwait fixrtc
partitions=uuid_disk=${uuid_gpt_disk};name=rootfs,start=2MiB,size=-,uuid=${uuid_gpt_rootfs}
ramargs=setenv bootargs console=${console} ${optargs} root=${ramroot} rootfstype=${ramrootfstype}
ramboot=echo Booting from ramdisk ...; run ramargs; bootz ${loadaddr} ${rdaddr} ${fdtaddr}
ramdisk_addr_r=0x88080000
ramroot=/dev/ram0 rw
ramrootfstype=ext2
rdaddr=0x88080000
root_dir=/home/userid/targetNFS
rootpath=/export/rootfs
script=boot.scr
scriptfile=${script}
server_ip=192.168.1.100
soc=am33xx
spiargs=setenv bootargs console=${console} ${optargs} root=${spiroot} rootfstype=${spirootfstype}
spiboot=echo Booting from spi ...; run spiargs; sf probe ${spibusno}:0; sf read ${loadaddr} ${spisrcaddr} ${spiimgsize}; bootz ${loadaddr}
spibusno=0
spiimgsize=0x362000
spiroot=/dev/mtdblock4 rw
spirootfstype=jffs2
spisrcaddr=0xe0000
static_ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::off
stderr=serial
stdin=serial
stdout=serial
uname_boot=setenv bootdir /boot; setenv bootfile vmlinuz-${uname_r}; if test -e mmc ${bootpart} ${bootdir}/${bootfile}; then echo loading ${bootdir}/${bootfile} ...; run loadimage;setenv fdtdir /boot/dtbs/${uname_r}; if test -e mmc ${bootpart} ${fdtdir}/${fdtfile}; then run loadfdt;else setenv fdtdir /usr/lib/linux-image-${uname_r}; if test -e mmc ${bootpart} ${fdtdir}/${fdtfile}; then run loadfdt;else setenv fdtdir /lib/firmware/${uname_r}/device-tree; if test -e mmc ${bootpart} ${fdtdir}/${fdtfile}; then run loadfdt;else setenv fdtdir /boot/dtb-${uname_r}; if test -e mmc ${bootpart} ${fdtdir}/${fdtfile}; then run loadfdt;else setenv fdtdir /boot/dtbs; if test -e mmc ${bootpart} ${fdtdir}/${fdtfile}; then run loadfdt;else setenv fdtdir /boot/dtb; if test -e mmc ${bootpart} ${fdtdir}/${fdtfile}; then run loadfdt;else setenv fdtdir /boot; if test -e mmc ${bootpart} ${fdtdir}/${fdtfile}; then run loadfdt;else echo; echo unable to find ${fdtfile} ...; echo booting legacy ...;run mmcargs;bootz ${loadaddr}; fi;fi;fi;fi;fi;fi;fi; setenv rdfile initrd.img-${uname_r}; if test -e mmc ${bootpart} ${bootdir}/${rdfile}; then echo loading ${bootdir}/${rdfile} ...; run loadrd;if test -n ${uuid}; then setenv mmcroot UUID=${uuid} ro;fi;run mmcargs;bootz ${loadaddr} ${rdaddr}:${rdsize} ${fdtaddr}; else run mmcargs;bootz ${loadaddr} - ${fdtaddr}; fi;fi;
usbnet_devaddr=c8:a0:30:ac:7f:d1
vendor=ti
ver=U-Boot 2014.07-00016-g329fca9 (Jul 28 2014 - 12:35:02)

Environment size: 8541/131068 bytes

既然是研究如何将网络文件系统NFS挂载为根文件系统,那么把其中59-63行的环境变量摘取出来进行分析,为了便于阅读和分析,整理成如下格式:

#定义变量nfs_options的值为,vers=3
nfs_options=,vers=3

#定义变量nfsargs为一个动作,该动作设置bootargs启动参数内容包括
#(1)console:用于让内核打印输出信息,Documentation/serial-console.txt找到相关描述
#(2)root:用于说明根文件系统是基于NFS。
#(3)rootfstype:指定根文件系统类型,此处指定的是ext4
#(4)nfsroot:指定根文件系统所在NFS服务器的IP以及服务器上的路径
#(5)ip:本目标板IP(必须的,其他可不写)、TFTP和NFS服务器IP、网关IP、子网掩码、主机名、网卡设备、

nfsargs=
setenv bootargs console=${console} ${optargs} ${cape_disable} ${cape_enable} 
                root=/dev/nfs rw 
                rootfstype=${nfsrootfstype} 
                nfsroot=${nfsroot} 
                ip=${ip} ${cmdline}

#定义变量nfsboot为一系列动作,动作之间以;隔开
nfsboot=
echo Booting from ${server_ip} ...; //回显信息“从服务器IP处启动系统...”
setenv nfsroot ${server_ip}:${root_dir}${nfs_options}; //指定根文件系统所在NFS服务器的IP以及服务器上的路径
setenv ip ${client_ip}:${server_ip}:${gw_ip}:${netmask}:${hostname}:${device}:${autoconf}; //设定IP
setenv autoload no; //仅仅对相关配置进行查询(比如本机被分配到的IP地址),而不会同时产生去通过TFTP下载任何镜像的动作。
setenv serverip ${server_ip};//设置TFTP和NFS服务器IP地址,两个服务器可以在配置同一台电脑上,一般属于这种情况,但也可以配置在不同的电脑上。 
setenv ipaddr ${client_ip};//本机目标板地址 
tftp ${loadaddr} ${bootfile}; //从TFTP服务器的目录中下载bootfile文件放到内存loadaddr处
tftp ${fdtaddr} dtbs/${fdtfile}; //从TFTP服务器的dtbs/目录下载fdtfile文件,存放到内存的fdtaddr处
run nfsargs; //执行nfsargs定义的一系列动作,解释见上。
bootz ${loadaddr} - ${fdtaddr}//bootz表示执行内存中的zImage文件,后面跟三个参数,第一个参数表示内核在内存中的地址,第二个参数表示initrd的地址,如果没有initrd就用"-"表示,第三个表示设备树二进制文件内存地址

#定义变量nfsopts的值为nolock
nfsopts=nolock

#定义变量nfsrootfstype的值为ext4 rootwait fixrtc
nfsrootfstype=ext4 rootwait fixrtc

        从以上环境变量可以得出,这几个变量均是为了nfsboot而服务的,也就是说进入u-boot命令行后,我们输入run nfsboot,那么nfsboot里面的动作(回显信息、设置环境变量、下载内核镜像、设备树二进制文件、设置启动参数、执行镜像文件)全部都会被执行。那么系统也就自动开始启动了。


        为了确保所有相关的环境变量都是正确无误的,现在u-boot下面从nfsboot入手把各个尚未知的环境变量依次打印出来,查看一下情况如何,得到结果如下,

----------------------------------------------------------------------------------------------------------------------------------------------------------

U-Boot# printenv server_ip root_dir client_ip gw_ip netmask hostname device autoconf loadaddr bootfile fdtaddr fdtfile
server_ip=192.168.1.100                //实际中我的TFTP、NFS服务器都在虚拟机中,虚拟机IP192.168.1.106
root_dir=/home/userid/targetNFS     //NFS服务器的目录为/home/username/zynfs/
## Error: "client_ip" not defined      //目标板连接在同一个局域网中,获得的IP为192.168.1.105
gw_ip=192.168.1.1                        //路由器IP为192.168.1.1,故此项无需在设置
netmask=255.255.255.0                 //子网掩码也正确,故此项无需在设置
## Error: "hostname" not defined    //主机名无需设置留空即可
device=eth0                                 //u-boot默认已经设置好,设置为目标板系统的第一块以太网网卡,故此项无需设置
autoconf=off                                //保留默认设置,故此项无需设置
loadaddr=0x82000000                   //保留默认设置,故此项无需设置
bootfile=zImage                           //保留默认设置,故此项无需设置,如果想用其他名字自己,记得到时候TFTP服务器目录下的文件名与此保持一致,否则会找不到内核
fdtaddr=0x88000000                     //保留默认设置,故此项无需设置
fdtfile=undefined                         //u-boot没有设置,需要手动设置,可以先运行run findfdt,该命令读取板子信息,根据板子信息自动设置好fdtfile。如何读取的请printenv findfdt

----------------------------------------------------------------------------------------------------------------------------------------------------------

那么根据以上分析,对需要设置的环境变量进行手动更改或者设置,同时为了确保更改生效,改完后在printenv看一下设置是否成功,如下,

U-Boot# setenv server_ip 192.168.1.106 
U-Boot# setenv client_ip 192.168.1.105
U-Boot# setenv root_dir /opt/ti-sdk-am335x-evm-07.00.00.00/targetNFS/
U-Boot# run findfdt
U-Boot# printenv server_ip client_ip root_dir fdtfile
server_ip=192.168.1.106
client_ip=192.168.1.105
root_dir=/opt/ti-sdk-am335x-evm-07.00.00.00/targetNFS/
fdtfile=am335x-boneblack.dtb

 

现在的准备工作已经万事俱备,只欠东风!接下来让u-boot来执行nfsboot的动作吧,run nfsboot。

实际的操作参见对应的实践篇。






你可能感兴趣的:(Beaglebone Black——理论篇beaglebone black启动——从串口获得SPL、U-BOOT,TFTP服务器获得内核,NFS服务器挂载根文件系统)