U-boot在开发板上移植过程详解(3)---U-boot实现源码分析(第二阶段)
2012-03-04 18:37
645 查看
转载:http://www.cnblogs.com/ljf181275034/category/340671.html
U-boot的第二阶段和bootloader所完成的功能基本上是一致的,只是顺序上有点差别。另外,u-boot在启动内核之前可以让用户决定是否进入下载模式,即进入u-boot的控制界面。
第二阶段是从lib_arm/board.c中的start_armboot函数开始的。移植u-boot的主要工作在于对硬件的初始化,驱动。这里就重点按照硬件的操作上。
(1)初始化本阶段要用到的硬件设备
这里最重要的是设置系统时钟,初始化串口,只要这两个设置好了,就可以从串口看到打印信息。board_init函数设置MPLL、改变系统时钟,它是开发板相关的函数,在board/samsung/smdk2440/smdk2440.c中实现。值得注意的是board_init函数还保存了机器类型ID,这将在调用内核的时候传递给内核。
串口的初始化函数主要是serial_init,它设置UART控制器,是CPU的相关函数,在cpu/arm920t/s3c2440/serial.c中实现。
(2)检测系统内存映射
对于特定的开发板,器内存的分布是明确的,所以可以直接设置。board/smdk2410/smdk2410.c中的dram_init函数指定了本开发板的内存起始地址为0x300
00000,大小为0x40000000.代码如下:
这些设置的参数,将在后面向内核传递参数时用到。
(3)U-boot命令实现
我们已经知道,即使是内核的启动,也是通过U-boot命令来实现的。u-boot中的每个命令都通过U-BOOT-CMD宏(在include/command.h)来定义,格式如下:
下面以bootm命令来说明,它有如下定义:
利用U_BOOT_CMD的宏展开后的命令如下
对于每个使用U_BOOT_CMD宏来定义的命令,其实都是在".u_boot_cmd"段中定义一个cmd_tbl_t结构,如下:
在u-boot的链接脚本board/smdk2410/u-boot.lds中有如下定义:
在程序中就是根据命令的名字在内存段__u_boot_cmd_start~__u_boot_cmd_end找到它的cmd_tbl_t结构,然后调用它的函数(请参考common/comm
and.c中的find_cmd函数)。
(4)引导内核的实现
U-boot也是通过标记列表向内核传递参数的。ARM Linux内核对bootloader的引导功能有一定要求,在执行内核代码前必须设置下列条件:
& 对CPU寄存器的设置为R0=0, R1=机器类型ID,R2=引导参数列表的地址
& 必须禁止中断(IRQ与FIQ)
& CPU必须处于SVC模式
& MMU必须关闭
& 数据缓存必须关闭
现在linux虽然支持两种格式的引导参数,这里主要介绍最常用的新的方式---即上面所说的标记列表的,这种方式灵活,且对参数的描述更细致。
标签列表的每个标签由标签头和标签体组成。标签头说明这个标签的大小(单位是整数不是字节)以及这个标签的类型。类型是由内核定义好的一个数字。标签头用一个结构体struct tag_header表示,如下:
在标签头之后,根据标签的类型,所需的标签体也是不同的。标签列表的结束由一个特殊的标签类型ATAG_NONE标志,它没有标签体。
比较重要的两个标签类型是ATAG_MEM(设置内存信息)和ATAG_CMDLINE(用来传递命令行参数,即U-boot的bootargs变量的内容),这里列出来,需要的请大家查看google。下面给出一些小细节:
&u-boot源码中给出了一些设置标签列表的源代码,放在文件lib_arm/armlinux.c中,方法是先定义一个全局变量static struct tag *params,其中这个结构体的类型是一个将所有标签类型组合在一起的结构体,如下所示:
所有标签的头格式都是相同的,只是标签体不同,因此用联合体的方式将它们组合在一起。下面就是设置起始标签的函数代码:
在这个函数中,首先将变量params的值设为标签列表的开始地址,然后逐个设置标签中的成员,最后params变量的值将指向下一个标签应该设置的地址。其中,tag_size是一个宏,用来得到标签的大小。最后,用于设置标签列表结束的函数如下:
对于ARM架构的CPU,都是通过lib_arm/armlinux.c中的do_bootm_linxu函数来启动内核的,方法如下:
首先,获得内核映像的入口地址:
这样theKernel就指向内核存放的地址(对于ARM架构的CPU,通常是0x30008000),这里的hdr指向内核U-boot映像头部数据的指针,而hdr->ih_ep就是内核的入口地址,最后用下述代码调用内核:
这里的bd->bi_arch_number就是前面board_init函数设置的机器类型ID, bd->bi_boot_params就是标记列表的开始地址。根据ATPCS调用约定,上述函数的三个函数分别放在寄存器R0,R1,R2中,这样就实现了内核要求的入口条件。
讲到这里,有关的U-boot的关键源码分析分析部分就完成了,下次开始就来U-boot移植的实践操作篇。
U-boot的第二阶段和bootloader所完成的功能基本上是一致的,只是顺序上有点差别。另外,u-boot在启动内核之前可以让用户决定是否进入下载模式,即进入u-boot的控制界面。
第二阶段是从lib_arm/board.c中的start_armboot函数开始的。移植u-boot的主要工作在于对硬件的初始化,驱动。这里就重点按照硬件的操作上。
(1)初始化本阶段要用到的硬件设备
这里最重要的是设置系统时钟,初始化串口,只要这两个设置好了,就可以从串口看到打印信息。board_init函数设置MPLL、改变系统时钟,它是开发板相关的函数,在board/samsung/smdk2440/smdk2440.c中实现。值得注意的是board_init函数还保存了机器类型ID,这将在调用内核的时候传递给内核。
串口的初始化函数主要是serial_init,它设置UART控制器,是CPU的相关函数,在cpu/arm920t/s3c2440/serial.c中实现。
(2)检测系统内存映射
对于特定的开发板,器内存的分布是明确的,所以可以直接设置。board/smdk2410/smdk2410.c中的dram_init函数指定了本开发板的内存起始地址为0x300
00000,大小为0x40000000.代码如下:
int dram_init(void) { //这两个值都定义在include/configs/smdk2440.h中 gd->bd->bi_dram[0] . start = PHYS_SDRAM_1; //即0x30000000 gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; //即0x04000000 return 0; } |
(3)U-boot命令实现
我们已经知道,即使是内核的启动,也是通过U-boot命令来实现的。u-boot中的每个命令都通过U-BOOT-CMD宏(在include/command.h)来定义,格式如下:
U_BOOT_CMD(name, maxargs, repeatable, command, “usage”, "help”) 各项参数说明如下: name:命令的名字,注意,它不是一个字符串(不要用双引号括起来) maxargs:最大的参数个数 repeatable:命令是否可重复,可重复是指运行一个命令后,下次敲回车即可再次运行 command:对应的函数指针,类型为(*cmd)(struct cmd_tbl_s *, int, int, char *[]) usage:简短的使用说明,这是个字符串 |
U_BOOT_CMD( bootm, CFG_MAXARGS, 1, do_bootm, "string1”, "string2" ); |
cmd_tbl_t __u_boot_cmd_boot __attribute__ ((unused, section(".u_boot_cmd"))) = { "bootm", CFG_MAXARGS, 1, do_bootm, "string1", “string2”}; |
struct cmd_tbl_s { char *name; //命名名称 int maxargs; //最大参数个数 int repeatable; //是否允许自动重复 int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); //实现函数 char *usage; //帮助信息(短) char *help; //帮助信息(长) }; typedef struct cmd_tbl_s cmd_tbl_t; |
__u_boot_cmd_start = .; .u_boot_cmd : { *(.u_boot_cmd) } __u_boot_cmd_end = .; |
and.c中的find_cmd函数)。
(4)引导内核的实现
U-boot也是通过标记列表向内核传递参数的。ARM Linux内核对bootloader的引导功能有一定要求,在执行内核代码前必须设置下列条件:
& 对CPU寄存器的设置为R0=0, R1=机器类型ID,R2=引导参数列表的地址
& 必须禁止中断(IRQ与FIQ)
& CPU必须处于SVC模式
& MMU必须关闭
& 数据缓存必须关闭
现在linux虽然支持两种格式的引导参数,这里主要介绍最常用的新的方式---即上面所说的标记列表的,这种方式灵活,且对参数的描述更细致。
标签列表的每个标签由标签头和标签体组成。标签头说明这个标签的大小(单位是整数不是字节)以及这个标签的类型。类型是由内核定义好的一个数字。标签头用一个结构体struct tag_header表示,如下:
struct tag_header{ u32 size; u32 tag; }; |
比较重要的两个标签类型是ATAG_MEM(设置内存信息)和ATAG_CMDLINE(用来传递命令行参数,即U-boot的bootargs变量的内容),这里列出来,需要的请大家查看google。下面给出一些小细节:
&u-boot源码中给出了一些设置标签列表的源代码,放在文件lib_arm/armlinux.c中,方法是先定义一个全局变量static struct tag *params,其中这个结构体的类型是一个将所有标签类型组合在一起的结构体,如下所示:
struct tag { struct tag_header hdr; union { struct tag_corecore; struct tag_mem32mem; struct tag_videotextvideotext; struct tag_ramdiskramdisk; struct tag_initrdinitrd; struct tag_serialnrserialnr; struct tag_revisionrevision; struct tag_videolfbvideolfb; struct tag_cmdlinecmdline; struct tag_acornacorn; struct tag_memclkmemclk; } u; }; |
static void setup_start_tag (bd_t *bd) { params = (struct tag *) bd->bi_boot_params; 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); } |
static void setup_end_tag (bd_t *bd) { params->hdr.tag = ATAG_NONE; params->hdr.size = 0; } |
首先,获得内核映像的入口地址:
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep); |
theKernel (0, bd->bi_arch_number, bd->bi_boot_params); |
讲到这里,有关的U-boot的关键源码分析分析部分就完成了,下次开始就来U-boot移植的实践操作篇。
相关文章推荐
- U-boot在开发板上移植过程详解(2)---U-boot实现源码分析(第一阶段)
- TQ2440 学习笔记—— 31、移植U-Boot【U-Boot 的启动过程第二阶段源码分析】
- U-boot在开发板上移植过程详解--bootloader架构分析
- (二)U-boot在开发板上移植过程详解--bootloader架构分析
- U-Boot 启动过程和源码分析(第二阶段)
- U-boot在开发板上移植过程详解--bootloader架构分析
- (二)U-boot在开发板上移植过程详解--bootloader架构分析
- U-boot在开发板上移植过程详解(1)---bootloader架构分析
- U-Boot 启动过程和源码分析(第二阶段)-main_loop分析
- (转) U-boot在开发板上移植过程详解--bootloader架构分析
- U-Boot 启动过程和源码分析(第二阶段)
- U-Boot启动过程源码分析(2)-第二阶段
- u-boot启动过程源码分析之第二阶段(S3C24XX系列)
- TQ2440 学习笔记—— 30、移植U-Boot【U-Boot 的启动过程第一阶段源码分析】
- 图解U-Boot:第二阶段源码分析
- U-boot在开发板上移植过程详解(1)-…
- u-boot源码分析 --- 启动第二阶段006
- u-boot源码分析 --- 启动第二阶段 ,基于2410 启动代码 分析
- U-Boot启动过程源码分析(1)-第一阶段
- U-boot在开发板上移植过程详解(1)-…