uboot移植
<ul>
<li>
<p>U-Boot支持的主要功能:</p>
<ul>
<li>
<p>系统引导支持NFS挂载、RAMDISK(压缩或非压缩)形式的根文件系统;支持NFS挂载、从FLASH中引导压缩或非压缩系统内核;</p>
</li>
<li>
<p>基本辅助功能强大的操作系统接口功能;可灵活设置、传递多个关键参数给操作系统,适合系统在不同开发阶段的调试要求与产品发布,尤以Linux支持最为强劲;支持目标板环境参数多种存储方式,如FLASH、NVRAM、EEPROM;</p>
</li>
<li>
<p>CRC32校验可校验FLASH中内核、RAMDISK镜像文件是否完好;</p>
</li>
<li>
<p>设备驱动串口、SDRAM、FLASH、以太网、LCD、NVRAM、EEPROM、键盘、USB、PCMCIA、PCI、RTC等驱动支持;</p>
</li>
<li>
<p>上电自检功能SDRAM、FLASH大小自动检测;SDRAM故障检测;CPU型号;</p>
</li>
<li>特殊功能XIP内核引导</li>
</ul>
</li>
</ul>
<h3>下载模式</h3>
<p>板子与主机间传输文件时,可以使用串口的xmodem/ymodem/zmodem协议,还可以使用网络通过tftp、nfs协议来传输,以及USB下载等方法。</p>
<h3>uboot常用命令</h3>
<p><img src="https://www.showdoc.cc/server/api/common/visitfile/sign/02a1e7345a000a169cb9baf3f911f718?showdoc=.jpg" alt="" /></p>
<ol>
<li>help 或 ?</li>
<li>printenv</li>
<li>setenv envname value
如果没有value,则表示删除env环境变量</li>
<li>bootcmd 自动启动执行命令,uboot开机后会自动倒计时,在倒计时结束前如果没有外部按键打断自动计时,uboot将自动执行bootcmd变量保存的命令。</li>
<li>串口传输命令
<pre><code>loadb - load binary file over serial line (kermit mode)
loadx - load binary file over serial line (xmodem mode)
loady - load binary file over serial line (ymodem mode)</code></pre></li>
<li>
<p>网络命令</p>
<pre><code>ping hostname
nfs - boot image via network using NFS protocol
tftpboot- boot image via network using TFTP protocol
bootp - boot image via network using BOOTP/TFTP protocol</code></pre>
</li>
<li>
<p>NandFlash操作命令
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/a39caaf202cf813800553bd9f9bea161?showdoc=.jpg" alt="" /></p>
</li>
<li>
<p>内存、寄存器操作命令
nm 修改内存值 (指定地址)<br />
格式: nm [.b, .w, .l] address</p>
<p>mm 修改内存值(地址自动加一)
格式: mm [.b, .w, .l] address </p>
<p>md 显示内存值
格式: md [.b, .w, .l] address [# of objects] </p>
<p>mw 用指定的数据填充内存
格式: mw [.b, .w, .l] address value [count]</p>
<p>cp 内存的拷贝(包括内存与Nor Flash间的数据拷贝)
格式:cp [.b, .w, .l] source target count</p>
</li>
<li>
<p>USB操作命令
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/9898346ac354a4fc9cfaa03ca637283f?showdoc=.jpg" alt="" /></p>
</li>
<li>
<p>SD/MMC操作命令
mmc init [dev] - 初始化MMC子系统
mmc device [dev] - 查看和设置当前设备</p>
</li>
<li>uboot在Flash中的分区</li>
</ol>
<p><img src="https://www.showdoc.cc/server/api/common/visitfile/sign/77b8474bbef786edb0f4c73428e54ac8?showdoc=.jpg" alt="" /></p>
<h3>找到对应的参考开发板</h3>
<p>我们现在的开发板cpu型号是mx6g2c,那么可参考<code>include/configs/mx6g2c.h</code>,对应的<code>board</code>在<code>board/freescale/mx6g2c</code>目录下</p>
<h3>删除无用文件(可选)</h3>
<ul>
<li>arch下保留arm</li>
<li>arm下保留armv7</li>
<li>board下保留freescale</li>
<li>freescale保留mx6g2c和common</li>
</ul>
<p><strong><font color=red>上面对移植uboot作用不大,需要进一步深入</font></strong></p>
<h3>uboot环境变量</h3>
<p>Uboot环境变量的设计逻辑是在启动过程中将env从静态存储器中读出放到RAM中,之后在uboot下对env的操作(如printenv editenv setenv)都是对RAM中env的操作,只有在执行saveenv时才会将RAM中的env重新写入静态存储器中。基于这种设计逻辑,2014.4版本uboot实现了saveenv这个保存env到静态存储器的命令,而没有实现读取env到RAM的命令。</p>
<p>u-boot 的 env 按 name=value”\0”的方式存储,在所有env 的最后以”\0\0”表示整个 env 的结束。新的name=value 对总是被添加到 env 数据块的末尾,当删除一个 name=value 对时,后面的环境变量将前移,对一个已经存在的环境变量的修改实际上先删除再插入。 </p>
<p>uboot中env的整个架构可以分为3层:</p>
<p>(1) 命令层,如saveenv,setenv editenv这些命令的实现,还有如启动时调用的env_relocate函数。</p>
<p>(2) 中间封装层,利用不同静态存储器特性封装出命令层需要使用的一些通用函数,如env_init,env_relocate_spec,saveenv这些函数。实现文件在common/env_xxx.c</p>
<p>(3) 驱动层,实现不同静态存储器的读写擦等操作,这些是uboot下不同子系统都必须的</p>
<h3>uboot启动流程</h3>
<h4>BL1</h4>
<p>uboot的BL1阶段代码通常放在start.s文件中,用汇编语言实现,其主要代码功能如下:</p>
<ol>
<li>指定uboot的入口。在链接脚本uboot.lds中指定uboot的入口为start.S中的_start。</li>
<li>设置异常向量(exception vector)</li>
<li>关闭IRQ、FIQ,设置SVC模式</li>
<li>关闭L1 cache、设置L2 cache、关闭MMU</li>
<li>根据OM引脚确定启动方式</li>
<li>在SoC内部SRAM中设置栈</li>
<li>lowlevel_init(主要初始化系统时钟、SDRAM初始化、串口初始化等)</li>
<li>设置开发板供电锁存</li>
<li>设置SDRAM中的栈</li>
<li>将uboot从SD卡拷贝到SDRAM中</li>
<li>设置并开启MMU</li>
<li>通过对SDRAM整体使用规划,在SDRAM中合适的地方设置栈</li>
<li>清除bss段,远跳转到start_armboot执行,BL1阶段执行完</li>
</ol>
<h4>BL2阶段</h4>
<p>start_armboot函数位于lib_arm/board.c中,是C语言开始的函数,也是BL2阶段代码中C语言的主函数,同时还是整个u-boot(armboot)的主函数,BL2阶段的主要功能如下:</p>
<ol>
<li>规划uboot的内存使用</li>
<li>遍历调用函数指针数组init_sequence中的初始化函数</li>
<li>初始化uboot的堆管理器mem_malloc_init</li>
<li>初始化SMDKV210开发板的SD/MMC控制器mmc_initialize</li>
<li>环境变量重定位env_relocate</li>
<li>将环境变量中网卡地址赋值给全局变量的开发板变量</li>
<li>开发板硬件设备的初始化devices_init</li>
<li>跳转表jumptable_init</li>
<li>控制台初始化console_init_r</li>
<li>网卡芯片初始化eth_initialize</li>
<li>uboot进入主循环main_loop</li>
</ol>
<p><img src="https://www.showdoc.cc/server/api/common/visitfile/sign/d0fe64bd7f5b3365c8def562964fb911?showdoc=.jpg" alt="" /></p>
<h3>分区</h3>
<p><img src="https://www.showdoc.cc/server/api/common/visitfile/sign/602456fa469a2ca4ef521190db78f718?showdoc=.jpg" alt="" /></p>
<p><img src="https://www.showdoc.cc/server/api/common/visitfile/sign/0c2f06715ecd6f7a63990477b2e4397d?showdoc=.jpg" alt="" /></p>
<h3>内核格式</h3>
<p>Linux krenel经过编译后会生成名称为vmlinux或vmlinuz的ELF格式文件,嵌入式系统在部署时烧录的文件格式需要用objcopy工具去制作成烧录镜像格式文件Image。但由于Image太大,因此linux kernel项目对Image进行了压缩,并且在image压缩后的文件的前端附加了一部分解压缩代码,构成压缩镜像格式zImage。同时,uboot自身为了支持linux kernel的启动,在zImage压缩镜像的基础上增加了64字节的头信息,使用uboot自带的mkimage工具生成了uboot自身的压缩镜像格式uImage。在SMDK210的uboot版本中是同时支持zImage和uImage两种压缩镜像格式的。</p>
<h3>uboot的传参机制</h3>
<ol>
<li>
<p>uboot使用tag方式传参,tag是一种数据结构,与linux kernel中的tag是相同的数据结构。tag结构体包含tag_header和tag_xxxx成员,tag_header结构体包含tag的大小和类型编码,kernel接收到tag参数后根据头信息中类型编码确定tag的类型。
起始tag是ATAG_CORE类型,结束tag是ATAG_NONE类型,其他类型的tag是有效信息,环境变量bootargs传递给CONFIG_CMDLINE_TAG类型tag。环境变量mtdpart存放的启动设备分区表信息传递给ATAG_MTDPART类型tag。
uboot通过调用theKernel (0, machid, bd->bi_boot_params)函数启动内核,theKernel函数的三个参数通过寄存器来传参,第1个参数0存放在R0中,第2个参数机器码存放在R1中,第3个参数bd->bi_boot_params(传参tag的首地址)存放在R2中。</p>
</li>
<li>
<p>bootargs参数解读</p>
<ul>
<li>
<p>root</p>
<p>用来指定rootfs的位置, 常见的情况有:</p>
<p>root=/dev/ram rw </p>
<p>root=/dev/ram0 rw</p>
<p>root=/dev/mtdx rw</p>
<p>root=/dev/mtdblockx rw</p>
<p>root=/dev/nfs,在文件系统为基于nfs的文件系统的时候使用。指定root=/dev/nfs后,需要指定nfsroot=serverip:nfs_dir。</p>
</li>
<li>rootfstype
和root配合使用,一般如果根文件系统是ext2,选项可有可无,如果是jffs2,squashfs等文件系统的话,就需要rootfstype指明文件系统的类型,不然无法挂载根分区。</li>
<li>console
console=tty<n> 使用虚拟串口终端设备 <n></li>
</ul>
<p>console=ttyS<n>[,options] 使用特定的串口<n>,options可以是这样的形式bbbbpnx,这里bbbb是指串口的波特率,p是奇偶校验位,n是指的bits。</p>
<p>console=ttySAC<n>[,options] 同上面。</p>
<ul>
<li>
<p>mem
mem=xxM 指定内存的大小,非必须选项</p>
</li>
<li>
<p>ramdisk_size
ramdisk_size=xxxxx 推荐
ramdisk 驱动,创建的ramdisk的size,默认情况下是4M</p>
</li>
<li>
<p>initrd, noinitrd
如果没有使用ramdisk启动系统,则需要使用noinitrd参数。如果使用了ramdisk启动系统,就需要指定initrd=r_addr,size, r_addr表示initrd在内存中的位置,size表示initrd的大小</p>
</li>
<li>
<p>init
init指定内核启动后,进入系统中运行的第一个脚本,一般init=/linuxrc, 或者init=/etc/preinit,preinit的内容一般是创建console,null设备节点,运行init程序,挂载一些文件系统等等操作。</p>
</li>
<li>mtdparts
<pre><code>mtdparts=fc000000.nor_flash:1920k(linux),128k(fdt),20M(ramdisk),4M(jffs2),38272k(user),256k(env),384k(uboot)</code></pre>
<blockquote>
<p><font color=blue><code>mtdparts</code>参数需要内核中的mtd驱动支持,即内核配置时需要选上<strong><code>Device Drivers ---> Memory Technology Device (MTD) support ---> Command line partition table parsing</code></strong></font></p>
</blockquote></li>
</ul>
<p>mtdparts的格式如下:</p>
<pre><code>mtdparts=<mtddef>[;<mtddef]
<mtddef> := <mtd-id>:<partdef>[,<partdef>]
<partdef> := <size>[@offset][<name>][ro]
<mtd-id> := unique id used in mapping driver/device
<size> := standard linux memsize OR "-" to denote all remaining space
<name> := (NAME)</code></pre>
<p>mtdparts使用的设置格式:</p>
<pre><code>mtdparts=mtd-id:<size1>@<offset1>(<name1>),<size2>@<offset2>(<name2>)</code></pre>
<p>mtd-id 必须跟当前平台的flash的mtd-id一致,否则整个mtdparts会失效;size设置的时候可以为实际的size(xxM,xxk,xx),也可以用'-'表示剩余的所有空间。</p>
<pre><code>mtdparts=sc1200:- → 只有一个分区
mtdparts=sc1200:256k(ARMboot)ro,-(root) → 有两个分区</code></pre>
<ul>
<li><strong>ip</strong>
指定系统启动后网卡的ip地址,如果使用了基于nfs的文件系统,必须要指定ip参数。设置ip有两种方法:
<pre><code>ip = ip addr
ip=ip addr:server ip addr:gateway:netmask::which netcard:off</code></pre>
<blockquote>
<p>which netcard 是指开发板上的网卡,而不是主机上的网卡。</p>
</blockquote></li>
</ul>
</li>
</ol>
<p>几种常见的bootargs的使用设置如下:
假设文件系统是ramdisk,且直接就在内存中,bootargs的设置如下:</p>
<pre><code>setenv bootargs ‘initrd=0x32000000,0xa00000 root=/dev/ram0 console=ttySAC0 mem=64M init=/linuxrc’</code></pre>
<p>假设文件系统是ramdisk,且在flash中,bootargs的设置如下:</p>
<pre><code>setenv bootargs ‘mem=32M console=ttyS0,115200 root=/dev/ram rw init=/linuxrc’</code></pre>
<p>需要在bootm命令中指定ramdisk在flash中的地址,如<code>bootm kernel_addr ramdisk_addr (fdt_addr)</code></p>
<p>假设文件系统是jffs2类型的,且在flash中,bootargs的设置应该如下</p>
<pre><code>setenv bootargs ‘mem=32M console=ttyS0,115200 noinitrd root=/dev/mtdblock2 rw rootfstype=jffs2 init=/linuxrc’</code></pre>
<p>假设文件系统是基于nfs的,bootargs的设置应该如下</p>
<pre><code>setenv bootargs ‘noinitrd mem=64M console=ttySAC0 root=/dev/nfs nfsroot=192.168.0.3:/nfs ip=192.168.0.5:192.168.0.3:192.168.0.3:255.255.255.0::eth0:off’</code></pre>
<p>或者</p>
<pre><code>setenv bootargs ‘noinitrd mem=64M console=ttySAC0 root=/dev/nfs nfsroot=192.168.0.3:/nfs ip=192.168.0.5’</code></pre>
<blockquote>
<p>bootargs参数的设置极其灵活,需要根据平台灵活设置。</p>
</blockquote>
<h3>uboot命令体系</h3>
<p>uboot命令体系代码放在uboot/common中,包括cmd_xxx.c、command.c 、main.c源码文件。uboot实现命令体系的方法是每一个uboot命令对应一个函数,与shell的实现是一致的。
boot命令体系没有采用数组、链表来实现,而是每一个命令对应一个cmd_tbl_t命令类型结构体,通过对cmd_tbl_t命令类型结构体的段属性设置,将命令集存储在了程序中的自定义段.u_boot_cmd中,程序在链接阶段会将命令集分配在程序中的自定义段。链接脚本命令集自定义段如下:</p>
<pre><code>__u_boot_cmd_start = .;//命令集段起始地址
.u_boot_cmd : { *(.u_boot_cmd) }//命令集中的命令
__u_boot_cmd_end = .;//命令集段的结束地址</code></pre>
<h2><strong>执行流程</strong></h2>
<p><code>uboot</code>在启动进入<code>BL2</code>阶段后最终执行在<code>main_loop</code>函数,如果在自动倒计时时没有按下字符键,<code>uboot</code>将自动启动<code>kernel</code>;如果按下了字符键,<code>uboot</code>将进入人机交互命令行的主循环,执行读取、解析、执行命令。
<code>main_loop</code>函数中会先执行<code>getenv ("bootcmd")</code>,如果<code>bootcmd</code>环境变量设置的是启动<code>kenel</code>的命令,则在自动倒计时结束后如果没有字符输入,则<code>uboot</code>会自动执行<code>bootcmd</code>的命令,默认即执行启动<code>kernel</code>。如果自动倒计时结束前有字符输入,则进入命令行提示符状态阻塞等待用户输入命令。<code>readline</code>函数读取用户输入命令,进而通过<code>run_command</code>函数解析、运行命令。<code>run_command</code>函数会将接收的命令用<code>parse_line</code>函数解析,主要是将接收的命令字符串根据空格、分号分割成几部分,利用<code>find_cmd</code>函数遍历查找命令集,看uboot中是否有输入的命令,如果没有输入的命令,打印提示符。如果有当前输入的命令,调用当前输入命令的命令结构体的函数指针成员cmd执行命令对应的函数。</p>
<h3>uboot移植</h3>
<p><a href="http://blog.51cto.com/9291927/1793136">http://blog.51cto.com/9291927/1793136</a></p>