u-boot总结

来源:互联网 发布:openwrt mac地址过滤 编辑:程序博客网 时间:2024/06/05 14:55

这个周末复习下u-boot,看韦东山老师的从零开始写u-boot视频是个不错的学习教程,本次总结借鉴周立功,韦东山,上课笔记,csdn上其他博客的资料汇总来的,总结可能也不是特别全面。

上篇:
本次总结的内容有:
1. u-boot的SD卡启动细节
2. 分析u-boot是如何启动内核
3. 分析几个重点:u-boot链接文件,makefile
4. 韦东山精简u-boot编写
5. u-boot常用命令

下篇:
uboot可以学习的内容还有好多
1、u-boot环境变量实现
http://blog.csdn.net/gongyuan073/article/details/47150473
2、u-boot 编译过程
http://blog.csdn.net/kevin_mr/article/details/51428535
3、u-boot 的流程、主要的数据结构、内存分配
http://blog.csdn.net/xiaoaid01/article/details/39700375
4、基于 NOR FLASH 和 NAND FLASH 启动
http://www.cnblogs.com/aaronLinux/p/5540606.html
5、u-boot 内存布局及启动过程浅析
http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=29589379&id=5571499
6u-boot-1.1.6 之 cpu/arm920t/start.s 分析
http://blog.csdn.net/yu704645129/article/details/50441751
7、u-boot之重定位
http://blog.csdn.net/funkunho/article/details/52474373
8、u-boot代码修改支持nandflash,norflash
http://blog.csdn.net/fulinus/article/details/41286211
9、u-boot添加自定义命令
http://blog.csdn.net/itxiebo/article/details/50991049
10、如何移植u-boot
http://blog.csdn.net/zqixiao_09/article/details/50818428


上篇:

1、uboot的SD卡启动细节

这里写图片描述
(1)uboot编译好之后有200多KB,超出了16KB。uboot的组织方式就是前面16KB 为BL1,剩下的部分为BL2.

(2)uboot在烧录到SD卡的时候,先截取uboot.bin的前16KB(实际脚本截取的是8KB)烧录到SD卡的block1~bolck32;然后将整个uboot烧录到SD卡的某个扇区中(譬如49扇区)

(3)实际uboot从SD卡启动时是这样的:iROM先执行,根据OMpin判断出启动设备是SD卡,然后从S卡的block1开始读取16KB(8KB)到SRAM中执行BL1,BL1执行时负责初始化DDR,并且从SD卡的49扇区开始复制整个uboot到DDR中指定位置(0x23E00000)去备用;然后BL1继续执行直到ldr pc, =main时BL1跳转到DDR上的BL2中接着执行uboot的第二阶段。

启动卡的制作:

http://blog.csdn.net/morixinguan/article/details/70196668

SD卡详细分析:

http://blog.chinaunix.net/uid-28110044-id-4006225.html


2. 分析uboot是如何启动内核

U-Boot启动过程–详细版的完全分析
http://blog.csdn.net/u013256622/article/details/41951033

Tiny4412 u-boot分析(2)u-boot启动流程
http://www.cnblogs.com/CoderTian/p/5995409.html

使用do_bootm_linux(),在/lib_arm/bootm.c定义,因为我们已经知道入口地址了,所以只需跳到入
口地址就可以启动linux内核了,但是在这之前需要做一件事————uboot传递参数给内核!!
现在来分析do_bootm_linux()这个函数:

    theKernel = (void (*)(int, int, uint))images->ep;//先是将入口地址赋值给theKernel    theKernel (0, machid, bd->bi_boot_params);//然后是调用thekernel

函数,以0,machid,bd->bi_boot_params作为参数
下面分析这三个参数:
1.machid就是uboot里设置好的板子的机器码,mini2440的是MACH_TYPE_MINI2440 (1999),内核所设置的机器码和uboot所设置的机器码必须一致才能启动内核
2.bd->bi_boot_parmas就是uboot需传递给内核的启动参数所位于的地址
3.0暂时还不知道什么作用

那么uboot传给内核的启动参数是在哪里设置的呢?
其实就是在调用 theKernel (0, machid, bd->bi_boot_params);前面的一小段代码里设置的,下面我截取了部分片段:

setup_start_tag (bd);setup_revision_tag (&params);setup_memory_tags (bd);setup_commandline_tag (bd, commandline);setup_initrd_tag (bd, images->rd_start, images->rd_end);setup_videolfb_tag ((gd_t *) gd);setup_end_tag (bd);

每一个启动参数对应一个tag结构体,所谓的设置传递参数其实就是初始化这些tag的值,想了解这个结构体以及这些tag的值是如何设置的请看韦东山的书关于uboot移植章节!
下面我们看一下setup_start_tag(bd)这个函数先:

static void setup_start_tag (bd_t *bd){    params = (struct tag *) bd->bi_boot_params;   //在board.c中有一句gd->bd->bi_boot_params = 0x30000100,这里设置了参数存放的位置    params->hdr.tag = ATAG_CORE;    params->hdr.size = tag_size (tag_core);    params->u.core.flags = 0;    params->u.core.pagesize = 0;    params->u.core.rootdev = 0;    params = tag_next (params);}

我们再来看下

setup_commandline_tag (bd, commandline);这个函数:static void setup_commandline_tag (bd_t *bd, char *commandline){// commandline就是我们的bootargs    char *p;    if (!commandline)        return;    for (p = commandline; *p == ' '; p++);    if (*p == '\0')        return;    params->hdr.tag = ATAG_CMDLINE;    params->hdr.size =        (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;    strcpy (params->u.cmdline.cmdline, p);    params = tag_next (params);}

Linux内核启动时就会去读取这些tag参数


3、分析几个重点:uboot链接文件,makefile

makdfile
http://www.cnblogs.com/amanlikethis/p/3419858.html

uboot链接文件
对于.lds 文件,它定义了整个程序编译之后的连接过程,决定了一个可执行程序的各个段的存储位置。虽然现在我还没怎么用它,但感觉还是挺重要的,有必要了解一下。
定义入口。由于一个可执行的Image必须有一个入口点,并且只能有一个全局入口,通常这个入口放在ROM(Flash)的0x0地址,因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本来完成。
/board/samsung/smdkc110/**u-boot.lds
链接文件:给链接器使用的一个文件,该文件告诉链接器我们编译好的目标文件如何链接:
1、链接的地址
2、程序的段(.text代码段、.data数据段、.bss未初始化段)如何链接
3、程序的入口
这里写图片描述

secname和 contents是必须的,其他的都是可选的。下面挑几个常用的看看:1、 secname:段名2、 contents:决定哪些内容放在本段,可以是整个目标文件,也可以是目标文件中的某段(代码段、数据段等)3、 start:本段连接(运行)的地址,如果没有使用 AT(ldadr),本段存储的地址也是 start。 GNU 网站上说 start 可以用任意一种描述地址的符号来描述。4AT(ldadr):定义本段存储(加载)的地址。

4. 韦东山精简uboot编写

这里写图片描述

最简单的bootloader的编写步骤:1. 初始化硬件:关看门狗、设置时钟、设置SDRAM、初始化NAND FLASH2. 如果bootloader比较大,要把它重定位到SDRAM3. 把内核从NAND FLASH读到SDRAM4. 设置"要传给内核的参数"5. 跳转执行内核int main(void){    void (*theKernel)(int zero, int arch, unsigned int params);    volatile unsigned int *p = (volatile unsigned int *)0x30008000;    /* 0. 帮内核设置串口: 内核启动的开始部分会从串口打印一些信息,但是内核一开始没有初始化串口 */    uart0_init();    /* 1. 从NAND FLASH里把内核读入内存 */    puts("Copy kernel from nand\n\r");    nand_read(0x60000+64, (unsigned char *)0x30008000, 0x200000);    puthex(0x1234ABCD);    puts("\n\r");    puthex(*p);    puts("\n\r");    /* 2. 设置参数 */    puts("Set boot params\n\r");    setup_start_tag();    setup_memory_tags();    setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0");    setup_end_tag();    /* 3. 跳转执行 */    puts("Boot kernel\n\r");    theKernel = (void (*)(int, int, unsigned int))0x30008000;    theKernel(0, 362, 0x30000100);      /*      *  mov r0, #0     *  ldr r1, =362     *  ldr r2, =0x30000100     *  mov pc, #0x30008000      */    puts("Error!\n\r");    /* 如果一切正常, 不会执行到这里 */    return -1;}

5. uboot常用命令

这里写图片描述
举例:
setenv ipaddr 192.168.1.3
setenv serverip 192.168.1.2
setenv gatewayip 192.168.1.1
saveenv

bdinfo

GEC210 # bdinfoarch_number = 0x00000998  //机器码boot_params = 0x30000100  //存放启动参数的地址DRAM bank   = 0x00000000-> start    = 0x30000000-> size     = 0x10000000DRAM bank   = 0x00000001-> start    = 0x40000000-> size     = 0x10000000ethaddr     = 00:40:5C:26:0A:5Bip_addr     = 192.168.1.3baudrate    = 115200 bps  //uart0的速率
1)arch_number = 0x00000998 uboot针对不同的硬件平台有唯一的一个机器码(软件设定,用户定义)。    kernel针对不同的硬件平台有唯一的一个机器码    uboot启动后,会将机器码传给kernel,kernel将uboot传过来的机器码与自身的机器码做对比,如果二者一致,kernel才可以正常启动。2)boot_params = 0x30000100    uboot在加载kernel之前,会将kernel的启动参数存放在该地址下;kernel在启动过程中,会从该地址下拿到启动参数。

printenv

GEC210 # printenvbootargs=root=/dev/mtdblock4 rootfstype=yaffs2 init=/init console=ttySAC0,115200bootcmd=nand read 0x30008000 0x600000 0x500000 ;bootm 0x30008000baudrate=115200ethaddr=00:40:5c:26:0a:5bnetmask=255.255.0.0ipaddr=192.168.1.3serverip=192.168.1.2gatewayip=192.168.1.1bootdelay=5stdin=serialstdout=serialstderr=serial

启动命令–bootcmd

bootcmd=nand read 0x30008000 0x600000 0x500000 ;bootm 0x30008000

(1)nand read 0x30008000 0x600000 0x500000 从nand
flash的0x600000地址开始,读取0x500000大小的内容(5MB,kernel),读到内存的0x30008000地址下
去哪里找到内核,并完成将内核拷贝到内存

(2)bootm 0x30008000
在0x30008000地址上,启动kernel

告诉uboot,去哪里找内核,并启动内核。

如何设置启动命令:

setenv bootcmd ‘nand read 0x30008000 0x600000 0x500000; bootm
0x30008000’

启动参数 –bootargs

这里写图片描述

1)root=/dev/mtdblock4    rootfs是在nand flash的第4个分区中。我们要去该位置上挂载rootfs。   /dev/mtdblock4 --->nand flash第四个分区的设备文件,应用程序通过设备文件访问驱动。2)rootfstype=yaffs2    rootfs文件系统的格式(3)init=/sbin/init    init是linux启动的第一个进程,也是所有进程的父进程,PID=1,也是唯一的一个由linux内核发起的进程。    告诉内核,init进程做什么工作。    /sbin/init ---》处理busybox    什么是busybox  --->shell命令4)console=ttySAC0,115200    设置linux内核的控制台(stdinstdoutstderr)为串口0(UART0),串口0的波特率是115200.

下篇:
待补充。