一般利用一张SD卡就能进行系统的更新,以前一直不知是什么原理,最近了解了下,对了解到的内容做个记录。使用的是AM335X平台,系统是Linux,文件系统是EXT3:
1、首先需要一张分好分区的SD卡(分区方法这里不做详细介绍,一般可使用TI官方提供的create-sdcard.sh脚本可以进行分区)。其中分区1(FAT32)存放MLO、u-boot.img、uImage三个文件;分区2(EXT3)存放需要从SD卡启动的Linux文件系统。
2、AM335X上电后,根据Boot Sequence启动配置(LCD0-LCD15引脚,具体可参考TI官方的335X参考手册),从相应的存储设备启动,这里配置的是从SD卡启动。
3、AM335X上电后首先执行固化在芯片内部ROM中的代码,我们称之为BLO,BLO的功能很强大,能读取SD卡中的第一个分区(必须是FAT格式)的MLO文件,复制到AM335X内部的RAM中吞下,复制的起始地址为0x402f0400,所以编译MLO时,连接地址必须设置为0x402F0400。
4、一般的,MLO的作用是:禁用中断、关看门狗、设置栈、初始化时钟、初始化DDR3、初始化串口、最后复制S卡中boot(FAT32)分区的u-boot.img到DDR3的0x80000000(内存)处运行。MLO的最大体积可以达到109K,对于简单的逻辑程序绰绰有余了。
5、u-boot的功能主要是启动内核:u-boot从SD卡的boot(FAT32)分区复制uImage到内存运行。
6、启动Linux后挂载SD卡的rootfs分区(EXT3)的文件系统
7、文件系统是利用Busybox工具集创建的,首先内核会启动BusyBox里的Init进程。init进程首先主要对/etc/inittab文件进行解析,然后按照它的指示创建各种子进程。
8、inittab文件的内容主要以下几部分。其中系统运行后最先执行的是/etc/rc.d/rcS进程,并且它只执行一次,其它进程只有等它执行完毕后才能运行,u-boot、内核、文件系统的烧写在这里执行。
1 ::sysinit:/etc/rc.d/rcS
2 ::respawn:/sbin/getty 9600 tty1
3 ::ctrlaltdel:/sbin/reboot
4 ::shutdown:/etc/rc.d/rcS stop
5 ::restart:/sbin/init
9、接着继续看到/etc/rc.d/rcS文件,这是一个脚本文件,下面一一分析它做的工作
#!/bin/sh
# minimal startup script, will work with msh (this is best available in
# MMUless format).
mode=${1:-start} #若第一个参数存在,则mode的值为第一个参数;否则mode等于start
# load the configuration information
if [ -x /etc/rc.d/rc.conf ]
then
. /etc/rc.d/rc.conf #不管/etc/rc.d/rc.conf是否可执行,都去执行它,等价于sh /etc/rc.d/rc.conf
fi
if [ $mode = 'start' ] #若mode的值为start,
then
services=$cfg_services #那么services等于$cfg_services(这个值在/etc/rc.d/rc.conf中定义:'crond mount-proc-sys hostname depmod modules udev filesystems inetd')
else
services=$cfg_services_r #否则services等于$cfg_services_r (这个值在/etc/rc.d/rc.conf中定义:'inetd filesystems udev modules depmod hostname mount-proc-sys crond')
fi
cfg_services=${2:-$services} #若第二个参数存在,则cfg_services的值为第二个参数;否则cfg_services等于services
# run the configured sequence #按配置的顺序,启动各个进程
for i in $cfg_services
do
if [ -x /etc/rc.d/init.d/$i ] #如果进程是可执行的,那么
then
echo '/etc/rc.d/init.d/'$i $mode #打印进程信息
/etc/rc.d/init.d/$i $mode #挨个执行
fi
done
if [ $# -ge 2 ] #如果参数个数大于等于2
then
exit 0 #退出
fi
if [ $mode = 'start' ] #若mode的值为start,
then
# show all kernel log messages
if [ -x /proc/sys/kernel/printk ] #若/proc/sys/kernel/printk可执行
then
#echo 8 > /proc/sys/kernel/printk
echo 0 > /proc/sys/kernel/printk #清空/proc/sys/kernel/printk
fi
# echo set kernel halt time
echo -e '�33[9;0]' > /dev/tty0
#echo Bringing lo up
/sbin/ifconfig lo 127.0.0.1 up 2>/dev/null
/sbin/route add -net 127.0.0.0 netmask 255.0.0.0 gw 127.0.0.1 dev lo 2>/dev/null
#echo eth0 setting #网卡0设置
if [ -x /etc/eth0-setting ]
then
echo 'Found /etc/eth0-setting...'
/etc/rc.d/ifconfig-eth0 /etc/eth0-setting #在/etc/eth0-setting文件中配置IP、MASK、GATEWAY信息
fi
#echo eth1 setting #网卡1设置
if [ -x /etc/eth1-setting ]
then
echo 'Found /etc/eth1-setting...'
/etc/rc.d/ifconfig-eth1 /etc/eth1-setting #在/etc/eth1-setting文件中配置IP、MASK、GATEWAY信息
fi
#echo wlan0 setting #wlan0设置
if [ -x /etc/wlan0-setting ]
then
echo 'Found /etc/wlan0-setting...'
wpa_supplicant -B -D wext -c /etc/wpa.conf -i wlan0
wpa_cli add_network
wpa_cli enable_network 0
/etc/rc.d/ifconfig-wlan0 /etc/wlan0-setting
fi
fi
# run rc.local if present and executable
if [ -x /etc/rc.d/rc.local ] #如果/etc/rc.d/rc.local可执行
then
echo 'Found /etc/rc.d/rc.local...'
/etc/rc.d/rc.local $mode #那么,直接执行它。整个启动脚本的重点在这里,后面分析它
fi
# run startx if present and executable
if [ -x /usr/bin/startx ]
then
if [ $mode = 'start' ]
then
rm /tmp/.X0-lock
startx
fi
fi
/etc/rc.d/rcS脚本可以总结为如下的功能:
a、配置脚本运行的环境变量,文件为/etc/rc.d/rc.conf
b、运行系统服务进程:crond mount-proc-sys hostname depmod modules udev filesystems inetd
c、设置网络IP/etc/rc.d/ifconfig-eth0 /etc/eth0-setting、/etc/rc.d/ifconfig-eth1 /etc/eth1-setting
d、运行/etc/rc.d/rc.local脚本程序
10、分析/etc/rc.d/rc.local start脚本程序
#!/bin/sh
mode=${1:-start} #mode等于传入的参数1为start
if [ $mode = 'start' ] #如果mode的值等于start,那么运行以下程序
then
#set sgtl5000 volume 0~127
amixer cset numid=5 65 #猜测是设置蜂鸣器音量,还不确定
echo 'start format board mmc...'
echo 1 > /sys/class/leds/user_buzzer/brightness #打开蜂鸣器,说明开始运行此脚本更新系统
/etc/rc.d/spi.sh #运行/etc/rc.d/spi.sh脚本,把/boot/u-boot.spi.512拷贝到Flash中,以后程序从此处取出u-boot运行
以下为/etc/rc.d/spi.sh脚本内容:
#!/bin/sh
echo '****************************************************'
echo '****************************************************'
echo ''
echo ' Hua Ming spi Flashing Script - 01/01/2015'
echo ''
echo 'erase spi flash...'
flash_erase /dev/mtd0 0 8
echo 'write boot/u-boot.spi.512 ...'
flashcp /boot/u-boot.spi.512 /dev/mtd0
echo 'done'
/etc/rc.d/mmc.sh /dev/mmcblk1 #运行/etc/rc.d/mmc.sh脚本对mmc以ext3文件系统格式进行分区,分为4个区分别为sys、opt、user、Reserved。
mount /dev/mmcblk1p1 /mnt #挂接sys分区,将rfs_335x.tar.gz的文件进行解压缩,里面存放了最小根文件系统
cd /mnt
tar -xzvf ../rfs_335x.tar.gz
mount /dev/mmcblk1p2 /mnt/opt #挂接opt分区,将HmReader.tar.gz解压缩,里面存放了应用程序
tar -xzvf ../../HmReader.tar.gz
cd boot
ln -s boot.scr.mmc1 boot.scr
sync
echo 0 > /sys/class/leds/user_buzzer/brightness #蜂鸣器关闭
echo 'format finished ...' #格式化结束,更新完成
fi
/etc/rc.d/rc.local start脚本程序功能总结如下:
1、将/boot/u-boot.spi.512拷贝到Flash中,在从spi启动时启动u-boot。
2、对MMC1进行分区,分为四个区分别为sys、opt、user、Reserved
3、将最小根文件系统解压缩到sys分区
4、将应用程序解压缩到opt分区
11、对/etc/rc.d/mmc.sh脚本的分析:
#!/bin/sh
echo '****************************************************'
echo '****************************************************'
echo ''
echo ' Hua Ming Example Flashing Script - 01/01/2015'
echo ''
if [[ -z $1 ]] #如果传入脚本的参数1不存在的话
then
echo 'mmc.sh Usage:'
echo ' mmc.sh
echo ' Example: mmc.sh /dev/mmcblk0'
exit #退出脚本
fi
STARTTIME=$(date +%s) #获得当前时间
##---------Start of variables---------------------##
## Declare eMMC device name here
#DRIVE='/dev/mmcblk0'
DRIVE=$1 #DRIVE=/dev/mmcblk1
##----------End of variables-----------------------##
## Kill any partition info that might be there
dd if=/dev/zero of=$DRIVE bs=4k count=1 #猜测是清除设备原有的分区
sync
sync #马上写入设备
## Figure out how big the eMMC is in bytes
SIZE=`fdisk -l $DRIVE | grep Disk | awk '{print $5}'` #算出块设备的大小(以字节为单位)
## Translate size into segments, which traditional tools call Cylinders. eMMC is not a spinning disk.
## We are basically ignoring what FDISK and SFDISK are reporting because it is not really accurate.
## we are translating this info to something that makes more sense for eMMC.
CYLINDERS=`echo $SIZE/255/63/512 | bc` #作数学运算算出一个柱面的大小
## Check to see if the eMMC partitions have automatically loaded from the old MBR.