查看“第一课--OpenWRT uboot介绍”的源代码
←
第一课--OpenWRT uboot介绍
跳转至:
导航
、
搜索
因为以下原因,您没有权限编辑本页:
您所请求的操作仅限于该用户组的用户使用:
用户
您可以查看与复制此页面的源代码。
{| style="width: 800px;" |- | 关于uboot的介绍,在网上有很多blog,并且很多文章都详细介绍了uboot的启动过程,这些启动过程大多是基于ARM core的S3C2440. Uboot的作用简单概括就是设置硬件的初始状态,并引导kernel。其实在系统上电之后,uboot被运行之前还有一端代码,一般称为bootrom。 这部分内容对我们开发人员来说是不可见的,我们也不用关心。 我们只需要知道uboot是我们开始设计系统的第一步就可以了。 本篇教程主要基于microWRT介绍uboot的启动。MicroWRT所用过的芯片MTK7620A是一款MIPS 架构的芯片, 所以首先我们就要介绍mips处理器的架构。 ==MIPS 处理器架构== ===地址空间映射=== MIPS 地址空间,这里说的是CPU的寻址空间,不是内存空间。内存只是映射在一部分地址空间上而已。 每个芯片的地址空间映射是很重要的,这些信息都会从datasheet中获得。Mips处理器的地址空间一般分为4段 (Kuseg、Kseg0、Kseg1、Kseg2),其中: Kseg0 (0x80000000 ~ 0x9fffffff) 为缓存段,直接映射在物理地址段上。在 CPU 复位时,缓存未被初始化,只有 Kseg1 能够被直接访问。 kseg1: 虚拟空间0xA000 0000 - 0xBFFF FFFF(512M): 这些地址通过把最高3位清零的方法来映射到相应的物理地址上, 与kseg0映射的物理地址一样。但kseg1是非cache存取的。kseg1是唯一的在系统重启时能正常工作的地址空间。 这也是为什么重新启动时的入口向量是0xBFC0 0000。这个向量相应的物理地址是0x1FC0 0000。 你将使用这段地址空间去存取你的初始化ROM。大多数人在这段空间使用I/O寄存器。 kseg2: 虚拟空间0xC000 0000 - 0xFFFF FFFF (1G): 这段地址空间只能在核心态下使用并且要经过MMU的转换。 在MMU设置好之前,不能存取这段区域。除非你在写一个真正的操作系统,一般来说你不需要使用这段地址空间。 综上可以看到,MIPS32 CPU下面的不经过MMU转换的内存窗口只有kseg0和kseg1 的512M的大小,而且这两个内存窗口映射到同一个512M的物理地址空间。 其余的3G虚拟地址空间需要经过MMU转换成物理地址,这个转换规则是由CPU 厂商实现的。换 句话说,在MIPS32 CPU下面访问高于512M的物理地址空间,必须通过MMU地址转换, 在 CPU 复位时,缓存未被初始化,只有 Kseg1 能够被直接访问。 基于MIPS的这种特殊设计,在移植uboot的时候,往往要对cache进行一些特殊设置。 ===microWRT地址映射=== 1. 物理段: 内存、Flash、IO寄存器等都被映射在物理段上访问时需要通过宏 KSEG0ADDR(_addr) 来通过 Kseg0 访问或 KSEG1ADDR(_addr) 来通过 Kseg1 访问。 物理地址一般不能直接访问,都需要通过 Kseg0 或 Kseg1 来访问。 2. 主要的映射范围: DDR:0~0x0fffffff (这一段直接映射物理内存,一般通过带缓存的 Kseg0 来访问,以便加快速度) I/O空间:0x10000000 ~ 0x1dffffff (这一段直接控制硬件,必须通过 Kseg1 来访问) SPI Flash 空间:0x1f000000 ~ 0x1fffffff (这一段映射闪存的前 16MB 数据) ==microWRT Uboot 启动分析== uboot的入口函数,我们可以从板子的lds文件里找到。一般都是start.s, 每个板子都有自己的lds文件,其主要用来说明编译生成的命令, 以及运行过程中用到的数据放置的位置。下面是个lds的例子。 OUTPUT_FORMAT(“elf32-tradbigmips”, “elf32-tradbigmips”, “elf32-tradbigmips”) /* 这里是生成格式为elf。大端,mips */ OUTPUT_ARCH(mips) /* 平台为mips */ ENTRY(_start) /* 入口点为_start */ SECTIONS { . = 0×00000000; . = ALIGN(4); .text : /* 这个是程序存放的地方 */ { *(.text) } . = ALIGN(4); /* 表示以4字节对齐 */ .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } . = ALIGN(4); .data : { *(.data) } . = .; _gp = ALIGN(16) + 0×7ff0; .got : { __got_start = .; /* 表示该处地址的值给__got_start */ *(.got) __got_end = .; } .sdata : { *(.sdata) } .u_boot_cmd : { __u_boot_cmd_start = .; *(.u_boot_cmd) __u_boot_cmd_end = .; } uboot_end_data = .; num_got_entries = (__got_end – __got_start) >> 2; . = ALIGN(4); .sbss (NOLOAD) : { *(.sbss) } .bss (NOLOAD) : { *(.bss) . = ALIGN(4); } uboot_end = .; } 通过分析lds,我们就可以知道uboot的入口是start.s 文件,下面我就就来分析这个文件。限于篇幅的限制,下面给出了uboot运行的一个大概流程,没有对源码做详细分析。 首先从_start开始。前面是128个字(不是字节),是留给异常入口点的。 1. 最前面两个分别是硬复位和软复位,这两个都跳到reset处。 2. 下面就是清一些CP0(协处理器0,mips对CPU的控制都是通过它实现的)的一些主要位。 3. 然后是关闭cache 4. 下面这个比较有意思。为什么还非要跳一下呢?这样就可以知道代码的位置,而不是标号值。比如可能在RAM中或ROM中。 bal 1f nop .word _gp 1: lw gp, 0(ra) 5. 这里执行lowlevel_init。这是第一个需要我们自己定义的函数。由于没有初始化堆栈,这里只能用汇编。我们看到在jalr后跟了个nop,这就是分支延迟槽,在这里什么也没有执行。 6. 下面执行了mips_cache_reset,它会来清理数据和指令的cache,并设置为正确的值。然后就可以打开cache了。 7. 由于我们的内存可能还没有始初化(有些人会有lowlevel_init中初始化,但有的人没有这样做)。但我们使用C函数的话,就需要堆栈,所以需要一个内存空间。于是这里执行了mips_cache_lock,将cache的地址锁定,就是将cache当内存用了。然后我们将堆栈的地址设定在我们锁定的cache的最高地址(因为堆栈是向下生长的)。这时我们就可以用C函数了,当然你还用不了malloc,也不可以太多的浪费堆栈。 8. 这里就跑到C的初始化函数中去了--board_init_f。 对于mips,board_init_f在lib_mips/board.c下。在board_init_f()函数中,主要完成了一些功能初始化,和划分RAM。 再看一下都初始化了什么功能。初始化的函数都在init_fnc_t *init_sequence[]里。有些不同版本的uboot,会直接在上级函数里调用这些函数。 init_fnc_t *init_sequence[] = { board_early_init_f, /* 一些必要,需在之前做的初始化,如想使用需定义CONFIG_BOARD_EARLY_INIT_F */ timer_init, /* 初始化时钟计数,cp0的 */ env_init, /* 环境变量保存在flash中 */ #ifdef CONFIG_INCA_IP incaip_set_cpuclk, /* 根据cpuclk环境变量设定CPU主频 */ #endif init_baudrate, /* 根据baudrate环境变量设定gd->baudrate */ serial_init, /* 设定串口速率,需要我们自己写(包括其它serial的) */ console_init_f, /* 设置gd->have_console=1,有CONFIG_SILENT_CONSOLE则查看silent */ display_banner, /* 打印uboot信息 */ checkboard, /* 检测板子,可以在这打印设备信息,需要我们自己写 */ init_func_ram, /* 设置gd->ram_size,initdram需要我们自己写 */ NULL, /* 最后这个空必须留着,检查结束 */ }; 最后这个函数调用了relocate_code (addr_sp, id, addr)。注意,这个函数,准确的说不是函数(因为不能返回),是不返回的。 现在我们又回到start.S中了。我们可以看到,这里和C语言传递参数是用a0,a1,a2。relocate_code的工作就是将代码搬移到RAM中执行。这里做的工作是: 1. 移动gp指针 2. 复制代码到RAM中 3. 刷新一下cache 4. 跳到RAM代码当中去(in_ram) in_ram的主要工作是:更新GOT;清空BSS段;最后跳到board_init_r。我们可以看到board_init_r最后一个参数是在分支延迟槽中赋值的。 这其实这里主要说一下GOTs(global offset tables)这个东东,这是uboot能跳转到不同空间运行的原理。uboot编译时用到了PIC(position-independent code)(也可以说成position-independent executable (PIE))。这个其实是很早之前,在没有MMU的年代引进来的东西。为了在没有MMU时,不同进程也能同时运行,就需要他们的运行地址可以改变。GOTs用来保存所有的全局变量地址,所以我们只要改变GOTs的值就可以了。gp就是指向GOTs位置的指针。这个功能需要在gcc编译时指定-fpic。然后就像我们看到的,我们只要改变GOTs里的值,加上地址偏移就可以了。 下面再看一下board_init_r。这里的工作: 1. 复制cmd段的信息过来。这里只复制了cmd,name,usage,usage。帮助信息的字符串还在flash中。 2. 然后是初始化malloc功能。注意这里env有malloc的方式分配到了空间,并复制到RAM。 3. 再就是stdio,串口,入口函数,以及全局变最根据env的初始化了。 4. 再接着就是网络的初始化。eth_initialize(gd->bd)。对于mips,如果设了CONFIG_NET_MULTI。我们需要自己写board_eth_init和cpu_eth_init两个函数。 只有前者返回值小于0时,我们才需要执行后者。 5. 最后进入main_loop()。 在这个函数中,主要是通过bootm 命令来启动kernel的。具体内容如下: main_loop() { s = getenv ("bootcmd"); debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); run_command (s, 0); } 执行上面的bootcmd,第一步就是copy kernel 到 RAM 中。这里涉及到nand的读写 do_nand() 完成kernle的copy后,就接着执行bootm,实际的执行顺序为bootm -->do_bootm-->do_bootm_linux do_bootm() --> 在文件cmd_bootm.c 中 { bootm_start() --> 读取头部获得加载地址和入口地址 do_bootm_linux() --> 启动,当然要首先设置启动参数,然后跳到入口地址 bootm.c 设置参数 setup_start_tag (bd); setup_serial_tag (¶ms); setup_revision_tag (¶ms); setup_memory_tags (bd); --> 这个是在start_armboot 的dram_init() 里设定好的。 setup_commandline_tag (bd, commandline); --> commandline 就是bootargs if (images->rd_start && images->rd_end) setup_initrd_tag (bd, images->rd_start, images->rd_end); setup_videolfb_tag ((gd_t *) gd); setup_end_tag (bd); 启动内核 theKernel (0, machid, bd->bi_boot_params); --> 启动kernel后一去不复返了。 } |}
返回至
第一课--OpenWRT uboot介绍
。
导航菜单
个人工具
创建账户
登录
名字空间
页面
讨论
变种
视图
阅读
查看源代码
查看历史
更多
搜索
Welcome
首页
创客大赛
大赛详情
3D打印
安装月球车
图形化编程
操控月球车
升级月球车
编程工具下载
软件下载
Arduino
Processing
Mixly
Scratch
模块套件
Microduino 102
mCookie 102
mCookie 202
mCookie 302
IBC
其他
应用套件
四轴飞行器
平衡车
小车CUBE
音乐播放器
刷卡音乐播放器
wifi气象站
彩虹音乐触摸灯
分贝检测仪
迎门汇报
LED点阵时钟
LED点阵屏幕
硬件
mCookie
Sensor
Microduino
MicroWrt
MicroNux
MicroRobot-Core
MicroRobot-CoreESP
ideaBoard
ideaBox
MicroMV
MicroAI
帮助
常见问题
帮助
工具
链入页面
相关更改
特殊页面
页面信息