嵌入式


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-&gt;bi_boot_params)函数启动内核,theKernel函数的三个参数通过寄存器来传参,第1个参数0存放在R0中,第2个参数机器码存放在R1中,第3个参数bd-&gt;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 ---&gt; Memory Technology Device (MTD) support ---&gt; Command line partition table parsing</code></strong></font></p> </blockquote></li> </ul> <p>mtdparts的格式如下:</p> <pre><code>mtdparts=&lt;mtddef&gt;[;&lt;mtddef] &lt;mtddef&gt; := &lt;mtd-id&gt;:&lt;partdef&gt;[,&lt;partdef&gt;] &lt;partdef&gt; := &lt;size&gt;[@offset][&lt;name&gt;][ro] &lt;mtd-id&gt; := unique id used in mapping driver/device &lt;size&gt; := standard linux memsize OR "-" to denote all remaining space &lt;name&gt; := (NAME)</code></pre> <p>mtdparts使用的设置格式:</p> <pre><code>mtdparts=mtd-id:&lt;size1&gt;@&lt;offset1&gt;(&lt;name1&gt;),&lt;size2&gt;@&lt;offset2&gt;(&lt;name2&gt;)</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>

页面列表

ITEM_HTML