uboot 启动流程分析(二) — 第二阶段
来源:互联网 发布:sql临时表 编辑:程序博客网 时间:2024/05/21 22:43
在 uboot 第一阶段启动完成后将会调用 start_armboot 开始第二阶段的启动流程,这个阶段的代码由 c 语言编写,分析如下:
一、基础数据结构
第二阶段主要用到了两个数据结构即 gd_t 和 bd_t,其定义如下:
/* 全局数据结构 */typedefstructglobal_data {bd_t *bd; /* 指向板级信息结构 */unsigned long flags; /* 标记位 */unsigned long baudrate; /* 串口波特率 */unsigned long have_console; /* serial_init() was called */unsigned long env_addr; /* 环境参数地址 */unsigned long env_valid; /* 环境参数 CRC 校验有效标志 */unsigned long fb_base; /* fb 起始地址 */#ifdef CONFIG_VFDunsigned char vfd_type; /* 显示器类型(VFD代指真空荧光屏) */#endif#if 0unsigned long cpu_clk; /* cpu 频率*/unsigned long bus_clk; /* bus 频率 */phys_size_t ram_size; /* ram 大小 */unsigned long reset_status; /* reset status register at boot */#endifvoid **jt; /* 跳转函数表 */} gd_t;/* Global Data Flags */#defineGD_FLG_RELOC0x00001/* 代码已经转移到 RAM */#defineGD_FLG_DEVINIT0x00002/* 设备已经完成初始化 */#defineGD_FLG_SILENT0x00004/* 静音模式 */#defineGD_FLG_POSTFAIL0x00008/* Critical POST test failed */#defineGD_FLG_POSTSTOP0x00010/* POST seqeunce aborted */#defineGD_FLG_LOGINIT0x00020/* Log Buffer has been initialized*/#define GD_FLG_DISABLE_CONSOLE0x00040/* Disable console (in & out) *//* 定义一个寄存器变量,占用寄存器r8,作为 gd_t 的全局指针 */#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")/* 板级信息结构 */typedef struct bd_info { int bi_baudrate; /* serial console baudrate */ unsigned long bi_ip_addr; /* IP Address */ struct environment_s *bi_env; /* 板子的环境变量 */ ulong bi_arch_number; /* 板子的 id */ ulong bi_boot_params; /* 板子的启动参数 */ struct /* RAM 配置 */ { ulong start; ulong size; } bi_dram[CONFIG_NR_DRAM_BANKS];} bd_t;/************************************************************************** * * 每个环境变量以形如"name=value"的字符串存储并以'\0'结尾,环境变量的尾部以两个'\0'结束。 * 新增的环境变量都依次添加在尾部,如果删除一个环境变量需要将其后面的向前移动 * 如果替换一个环境变量需要先删除再新增。 * * 环境变量采用 32 bit CRC 校验. * ************************************************************************** *//* 我们平台定义了8kB大小的环境变量存储区,地址空间位于4M ~ 6M */#define CONFIG_ENV_IS_IN_NAND#define CONFIG_ENV_OFFSET 0x00400000 /* 4M ~ 6M */#define CONFIG_ENV_SIZE 8192 /* 8KB */#define CONFIG_ENV_RANGE 0x00200000#define ENV_SIZE (CONFIG_ENV_SIZE - ENV_HEADER_SIZE)/* 环境变量结构 */typedefstruct environment_s {uint32_t crc; /* CRC32 over data bytes */#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENTunsigned char flags; /* active/obsolete flags */#endifunsigned char data[ENV_SIZE]; /* Environment data */} env_t;
这两个类型变量记录了刚启动时的信息,还将记录作为引导内核和文件系统的参数,如 bootargs 等,并且将来还会在启动内核时,由 uboot 交由 kernel 时会有所用。
二、启动流程
1、init_sequence
start_armboot 首先为全局数据结构和板级信息结构分配内存,代码如下:
#define CONFIG_UNCONTINUOUS_MEM#define CONFIG_SYS_MALLOC_END (MDDR_BASE_ADDR + 0x00600000)#define CONFIG_SYS_MALLOC_LEN 0x00100000 /* 1MB */gd = (gd_t*)(CONFIG_SYS_MALLOC_END - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));__asm__ __volatile__("": : :"memory");memset ((void*)gd, 0, sizeof (gd_t)); /* 将全局数据清零 */gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); /* 取得板级信息数据结构的起始地址 */memset (gd->bd, 0, sizeof (bd_t)); /* 将板级信息清零 */gd->flags |= GD_FLG_RELOC; /* 标记为代码已经转移到 RAM */
可以看到 bd_t 、gd_t 以及 MALLOC 区域是紧挨着的。然后依次调用 init_sequence数组中的函数指针完成各部分的初始化,代码如下:
typedef int (init_fnc_t) (void);int print_cpuinfo (void);init_fnc_t *init_sequence[] = {#if defined(CONFIG_ARCH_CPU_INIT)arch_cpu_init, /* 与处理器架构相关的初始化 */#endifboard_init, /* 板级特殊设备初始化 */#if defined(CONFIG_USE_IRQ)interrupt_init, /* 初始化中断 */#endiftimer_init, /* 初始化定时器 */env_init, /* 初始化环境变量 */init_baudrate, /* 初始化波特率 */serial_init, /* 初始化串口 */console_init_f, /* 控制台初始化第一阶段 */display_banner, /* 打印uboot版本信息 */#if defined(CONFIG_DISPLAY_CPUINFO)print_cpuinfo, /* 打印cpu信息及各总线频率 */#endif#if defined(CONFIG_DISPLAY_BOARDINFO)checkboard, /* 打印板级信息 */#endif#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)init_func_i2c, /* 初始化i2c */#endifdram_init, /* 配置有效的内存区 */#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)arm_pci_init,#endifdisplay_dram_config, /* 打印内存区配置信息 */NULL,};for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {if ((*init_fnc_ptr)() != 0) {hang ();}}void hang (void){puts ("### ERROR ### Please RESET the board ###\n");for (;;);}
在我们平台比较重要的初始化函数有 board_init 以及 env_init,代码如下:
/* 板级特殊设备初始化 */int board_init(void){uint32_t val;uint32_t adc_vol;uint32_t adc_per_vol;uint32_t adc_per_vol_res;mmu_cache_on(memory_map); /* 初始化mmu */clock_init(); /* 初始化时钟 */calibrate_delay(); /* 延时校准 *//*enable power */xx_request_gpio(GPIO_PMU_WAKEUP);xx_set_gpio_direction(GPIO_PMU_WAKEUP, 0);xx_gpio_set(GPIO_PMU_WAKEUP,0);xx_request_gpio(GPIO_PMU_MODE);xx_set_gpio_direction(GPIO_PMU_MODE, 0);xx_gpio_set(GPIO_PMU_MODE,0);i2c_init();adc_init();pmu_init();keypad_init();/* arch number of board */gd->bd->bi_arch_number = MACH_TYPE_XXX;/* adress of boot parameters */gd->bd->bi_boot_params = CONFIG_ATAG_ADDR;return 0;}/* 初始化环境变量 */int env_init(void){gd->env_addr = (ulong)&default_environment[0]; /* 设定默认的环境变量 */gd->env_valid = 1;return (0);}
在环境变量 default_environment 中我们设置了很多参数,列表如下:
uchar default_environment[] = {/* #define CONFIG_UBOOT_OFFSET 0x00200000 */"uboot-nandoff="MK_STR(CONFIG_UBOOT_OFFSET)"\0"/* #define CONFIG_UBOOT_LADDR 0x88007e00 */"uboot-laddr="MK_STR(CONFIG_UBOOT_LADDR)"\0"/* #define CONFIG_BOOTARGS_SD "console=ttyS0,921600n8 console=ttyMTD androidboot.console=ttyS0 \ mtdparts=atxx_nd:32M(boot),2M(ttyMTD),-(system) quiet" */"bootargs_sd="CONFIG_BOOTARGS_SD"\0"/* #define CONFIG_BOOTCOMMAND_SD "fatload mmc 1 0x89807e00 kboot.img; hdcvt 89807e00; bootm 89807fc0" */"bootcmd_sd="CONFIG_BOOTCOMMAND_SD"\0"/* 各总线时钟频率 * #define CONFIG_CLK_ARM 1001000000 * #define CONFIG_CLK_AXI 312000000 * #define CONFIG_CLK_APP 104000000 * #define CONFIG_CLK_MDDR 201000000 * #define CONFIG_CLK_GCLK 312000000 * #define CONFIG_CLK_VPCLK 156000000 * #define CONFIG_CLK_VSCLK 403000000 */"clk-arm=" MK_STR(CONFIG_CLK_ARM) "\0""clk-axi=" MK_STR(CONFIG_CLK_AXI) "\0""clk-app=" MK_STR(CONFIG_CLK_APP) "\0""clk-mddr=" MK_STR(CONFIG_CLK_MDDR) "\0""clk-gclk=" MK_STR(CONFIG_CLK_GCLK) "\0""clk-vpclk=" MK_STR(CONFIG_CLK_VPCLK) "\0""clk-vsclk=" MK_STR(CONFIG_CLK_VSCLK) "\0"/* #define CONFIG_BOOTARGS "console=ttyS0,921600n8 console=ttyMTD androidboot.console=ttyS0 mtdparts=\ atxx_nd:32M(boot),2M(ttyMTD),-(system) init=/init ubi.mtd=2 root=ubi0:rootfs rootfstype=ubifs ro" */"bootargs="CONFIG_BOOTARGS"\0"/* #define CONFIG_BOOTCOMMAND "nand read 89807e00 600000 200000; hdcvt 89807e00; bootm 89807fc0" */"bootcmd="CONFIG_BOOTCOMMAND"\0""clocks_in_mhz=1\0"#ifdef CONFIG_EXTRA_ENV_SETTINGSCONFIG_EXTRA_ENV_SETTINGS#endif"\0"};我们可以在 uboot 命令行模式下输入 printenv 命令查看当前的环境变量值。
2、start_armboot
start_armboot 在接下来的流程中还做了如下操作:
void start_armboot (void){init_fnc_t **init_fnc_ptr;char *s;unsigned long addr;mem_malloc_init (CONFIG_SYS_MALLOC_END - CONFIG_SYS_MALLOC_LEN, CONFIG_SYS_MALLOC_LEN);.../* board init may have inited fb_base */if (!gd->fb_base) {/* * reserve memory for LCD display (always full pages) *//* bss_end is defined in the board-specific linker script */addr = CONFIG_SYS_MALLOC_END;lcd_setmem (addr);gd->fb_base = addr;}nand_init(); /* 初始化 NAND */env_relocate (); /* 重定位环境变量,将其从 NAND 拷贝到内存中 */serial_initialize(); /* 初始化串口 */gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr"); /* IP Address */stdio_init (); /* 初始化外设 */jumptable_init (); /* 初始化跳转函数表 */console_init_r (); /* 控制台初始化第二阶段 */misc_init_r (); /* 杂项设备初始化, eg:battery */enable_interrupts (); /* enable exceptions *//* #define CONFIG_SYS_LOAD_ADDR (MDDR_BASE_ADDR + 0x00807e00) *//* 如果存在则从环境变量中读取装载地址,其默认为 ulong load_addr = CONFIG_SYS_LOAD_ADDR; */if ((s = getenv ("loadaddr")) != NULL) {load_addr = simple_strtoul (s, NULL, 16);}/* main_loop() can return to retry autoboot, if so just run it again. */for (;;) {main_loop (); /* 进入主循环 common/main.c */}/* NOTREACHED - no way out of command loop except booting */}其中工作比较多的是 stdio_init 完成了部分外设的初始化:
int stdio_init (void){/* 初始化设备链表 */INIT_LIST_HEAD(&(devs.list));drv_lcd_init (); /* 初始化 lcd,显示 Logo */drv_system_init (); /* 初始化stdio设备系统 */serial_stdio_init (); /* 初始化串口 */return (0);}start_armboot 最终进入 main_loop 函数,首先判断用户选择的启动模式,如果是命令模式则等待输入命令然后执行,代码如下:
void main_loop (void){static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };int len;int rc = 1;int flag;char *s;int bootdelay;s = getenv ("bootdelay");bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; /* 获取超时信息 */s = getenv ("bootcmd"); /* 获取启动命令 *//* abortboot会判断用户选择的启动模式,如果是命令模式就会返回1,如果是其他模式就不再返回 */if (bootdelay >= 0 && s && !abortboot (bootdelay)) {s = getenv ("bootcmd");run_command (s, 0);}for (;;) {len = readline (CONFIG_SYS_PROMPT); /* 读取输入 */flag = 0;if (len > 0)strcpy (lastcommand, console_buffer); /* 将输入保存到历史记录中 */else if (len == 0)flag |= CMD_FLAG_REPEAT; /* 如果没有输入则重复上次 */if (len == -1)puts ("<INTERRUPT>\n");elserc = run_command (lastcommand, flag); /* 执行命令 */if (rc <= 0) { /* 执行错误的命令从历史记录中删除 */lastcommand[0] = 0;}}}
附:启动命令解析
在 uboot 进入主循环后默认会进入 nand 启动模式,会依次执行 3 个命令:
bootcmd=nand read 89807e00 600000 200000; hdcvt 89807e00; bootm 89807fc01、nand 命令
该命令用于进行各种 nand 操作,用法如下:
nand - NAND sub-systemUsage:nand info - show available NAND devicesnand device [dev] - show or set current devicenand read - addr off|partition sizenand write - addr off|partition size read/write 'size' bytes starting at offset 'off' to/from memory address 'addr', skipping bad blocks.nand bad - show bad blocksnand dump[.oob] off - dump pagenand scrub - really clean NAND erasing bad blocks (UNSAFE)nand markbad off [...] - mark bad block(s) at offset (UNSAFE)nand biterr off - make a bit error at offset (UNSAFE)在启动命令中用到了
nand read 89807e00 600000 200000;即从 nand 偏移为 0x600000 的地方读取长度为 0x200000 字节(2M)的数据到内存 0x89807e00 地址处。对于这几个参数的解释需要了解 内存地址空间 和nand 地址空间 是怎么分配的:
/* we have 1 bank of DRAM */#define CONFIG_NR_DRAM_BANKS 1#define MDDR_BASE_ADDR 0x88000000 /* 物理内存的起始地址 */#define CONFIG_SYS_MALLOC_END (MDDR_BASE_ADDR + 0x00600000) /* MALLOC 区结束地址 */#define CONFIG_SYS_MALLOC_LEN 0x00100000 /* MALLOC 区长度 1MB *//* u-boot run address */#defineCONFIG_SYS_UBOOT_BASE (MDDR_BASE_ADDR + 0x00008000) /* uboot 加载的起始地址 */#defineCONFIG_UBOOT_LADDR 0x88007e00/* default load address for reading (i.e. kernel zImage with header) */#define CONFIG_SYS_LOAD_ADDR (MDDR_BASE_ADDR + 0x00807e00) /* kernel 加载的起始地址 */#define CONFIG_SYS_KERN_ADDR (MDDR_BASE_ADDR + 0x00808000) /* kernel 入口地址 */#define CONFIG_ATAG_ADDR (MDDR_BASE_ADDR + 0x01800100) /* kernel 启动参数地址 *//* xloader 存储的起始地址为 0,长度为 2M */#defineCONFIG_XLOADER_OFFSET 0x00000000 /* 0M ~ 2M */#defineCONFIG_XLOADER_MSIZE 0x00200000/* uboot 存储的起始地址为 2M,长度为 2M */#defineCONFIG_UBOOT_OFFSET 0x00200000 /* 2M ~ 4M */#defineCONFIG_UBOOT_MSIZE 0x00200000/* 环境变量存储的起始地址为 4M,默认长度为 8k */#define CONFIG_ENV_OFFSET 0x00400000 /* 4M ~ 6M */#define CONFIG_ENV_SIZE 8192 /* 8KB */#define CONFIG_ENV_RANGE 0x00200000/* kernel 存储的起始地址为 6M,长度为 3M */#defineCONFIG_KERNEL_OFFSET 0x00600000 /* 6M ~ 9M */#defineCONFIG_KERNEL_MSIZE 0x00300000在烧录镜像到 nand 的时候程序会按照上表将 xloader、uboot、kernel 分别烧录到其对应空间,这段 nand 空间是以 1M 为单位对齐的。
2、hdcvt 命令
自动启动中用到的第二个命令是:
hdcvt 89807e00该命令将位于内存 0x89807e00 的平台镜像头转换为标准的镜像头,用法介绍如下:
Usage:hdcvt usage: hdcvt addr_from [addr_to] [body_addr]生成的标准镜像头存储起始地址为:addr_to = addr_from - sizeof(xx_image_header_t) - sizeof(image_header_t)。这里涉及到两个新的结构体struct xx_image_header 和 struct image_header:前者是平台的镜像头,后者为标准镜像头,定义如下:
/* 平台镜像头,长度512Byte,填充在kernel的加载地址与入口地址之间的区域:0x89807e00 ~ 0x89808000 */typedef struct xx_image_header {unsigned chariv[IV_SIZE];unsigned intboot_signature;unsigned intload_address; /* 加载地址 = 镜像入口 - 标准镜像头长度(64Byte) */unsigned intrun_address; /* 镜像入口 */unsigned intfirm_size; /* 镜像长度 */unsigned intnand_offset;unsigned intimage_type; /* 镜像类型 */unsigned charboard_name[16];unsigned charreserved[40];unsigned char certificate[CERT_SIZE];unsigned char signature[SIGE_SIZE];} xx_image_header_t;/* 标准镜像头,长度64Byte,填充在kernel入口地址之前:0x89807fc0 ~ 0x89808000 */typedef struct image_header {uint32_tih_magic;/* 魔数,用于检测是否存在镜像头*/uint32_tih_hcrc;/* 镜像头校验和*/uint32_tih_time;/* 镜像创建时间 */uint32_tih_size;/* 镜像大小 */uint32_tih_load;/* 镜像加载地址 */uint32_tih_ep;/* 镜像入口地址 */uint32_tih_dcrc;/* 镜像校验和 */uint8_tih_os;/* 系统类型 */uint8_tih_arch;/* 处理器类型 */uint8_tih_type;/* 镜像类型 */uint8_tih_comp;/* 压缩类型 */uint8_tih_name[IH_NMLEN];/* 镜像名称 */} image_header_t;3、bootm 命令
bootm 用于启动 linux,参数为镜像的加载地址:
bootm 89807fc0
可以看到加载地址为 0x89807fc0,这个地址在镜像头中保存,计算方法:ih_load = ih_ep - sizeof(struct image_header) 即:0x89807fc0 = 0x89808000 - 0x40。该命令执行时串口信息如下:
Booting kernel from Legacy Image at 89807fc0 ... Image Name: Linux-2.6.32.9 Image Type: ARM Linux Kernel Image (uncompressed) Data Size: 1964376 Bytes = 1.9 MB Load Address: 89807fc0 Entry Point: 89808000 Verifying Checksum ... OK XIP Kernel Image ... OKOKStarting kernel ...
- uboot 启动流程分析(二) — 第二阶段
- uboot 启动流程分析(二) — 第二阶段
- uboot - 启动流程分析【第二阶段】
- uboot第二阶段启动流程
- uboot——启动第二阶段分析
- uboot启动流程webee210启动第二阶段
- uboot启动流程分析之二
- uboot启动第二阶段详细分析(1)
- uboot启动第二阶段详细分析(2)
- uboot启动第二阶段之x-load分析
- uboot启动流程二
- Exynos4412 Uboot 移植(二)—— Uboot 启动流程分析
- Exynos4412 Uboot 移植(二)—— Uboot 启动流程分析
- Exynos4412 Uboot 移植(二)—— Uboot 启动流程分析
- Exynos4412 Uboot 移植(二)—— Uboot 启动流程分析
- uboot 启动流程分析
- Uboot启动流程分析
- Uboot启动流程分析
- 面向Web的数据编码
- wpf datagrid显示数据
- ACM中关于浮点型的精确度问题
- eclipse性能优化
- ASP.NET MVC3与ExtJS结合建站笔记(要点)
- uboot 启动流程分析(二) — 第二阶段
- Debian 软件包管理
- 关于ubuntu下Dropbox使用的问题
- OpenCV获取与设置像素点的值的几个方法
- android 调用系统的接口
- linux 内存
- SQL2005 安装时 “性能监视器计数器要求(错误)” 解决方案
- 当你输入一个网址的时候,究竟发生了什么?
- 反思总结