uboot启动流程webee210启动第二阶段
2015-12-21 23:30
429 查看
又重新回到原点了,但是此时运行的环境是sdram中,好再次分析.
前面的都是相同的,但是在lowlevel_init中会有不同。
接着回到调用函数的地方。start.s中:
其中init_sequence定义如下:
@1:arch_cpu_init:
@2:
初始化了pwm4的定时器,具体细节不讲了。
@3:env_init
gd已经知道了,是一个全局的变量,gd->env_addr 赋值了默认的env 参数表,同时置位env_valid有效。好看一下这个env参数表
即gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 1;
@4:init_baudrate
初始化波特率,看一下env参数表中是否有这个命令,在 Webee210.h (include\configs) 中定义了
#define CONFIG_BAUDRATE 115200
所以gd->baudrate = 115200
@5: serial_init Serial.c (common)
dev = default_serial_console()获取到了关于默认设备的实例,
gd->have_console = 1;
打印出的log如下:
U-Boot 2011.06 (Dec 15 2015 - 22:32:20) for Webee_210_V2
U-Boot code: 33E00000 -> 33E66A30 BSS: -> 33E9CAFC
@8:print_cpuinfo
log出了一点信息,其中你想写的,还有是芯片的频率 。
@9:checkboard:没有特殊的作用,还是log
@10 dram_init 其实是初始化了
gd->ram_size = 512M
ok,假如中间有任何错误的化都会挂起等待。接着往下走
其实就有一句话被编译到了,addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size; 其中CONFIG_SYS_SDRAM_BASE 定义是0x30000000,而ram_size是536870912
ok 往下
接着往下
这个又是在为uboot预留空间,然后进行4Kb的四舍五入。
往下:
一部分预留空间给malloc使用,uboot,TLB table,board info,gd,异常退出栈(abort stack),按照webee210板子的sdram的配置,如下图:
最后还要看一下重要的数据结构gd发生了什么变化:
gd->mon_len = 0x9cd2c //uboot的长度(_start 到bss_ends)
gd->ram_size = 512M = 0x20000000 //实际的物理内存大小
gd->tlb_addr = 0x4fff0000 // TLB table 内存地址
gd->bd = bd = 0x4fe6afe0 //board info 的地址
gd->bd->bi_arch_number = 2456 //传递给linux内核的机器码
gd->irq_sp = 0x4fe6af68 //中断栈指针
此时的addr 以及addrsp 也有相对应的值了
addr = 0x4ff53000
addr_sp = 0x4fe6af58
接着往下:
gd->bd->bi_dram[0].start = 0x30000000
gd->bd->bi_dram[0].size = 0x10000000
gd->bd->bi_dram[1].start = 0x40000000
gd->bd->bi_dram[1].size = 0x10000000
而后调用显示Dram的大小的函数,打印显示DRAM:512M
gd->relocaddr = addr = 0x4ff53000
gd->start_addr_sp = addr_sp = 0x4fe6af58
gd->reloc_off = addr - _TEXT_BASE = 0x1c153000
将gd的地址拷贝到id作为参数传递给relocate_code,同时addr_sp,addr 作为参数传递给relocate_code了。
好往下走:
relocate_code是start.s 中一段代码:
这部分又将内存中的数据进行了一番拷贝将uboot的运行地址迁移到了gd->relocaddr处,继续执行。在完成搬运工作之后,回跳转到board_init_r的函数中,r5,r6 作为参数传入了这个函数中。
往下:void board_init_r(gd_t *id, ulong dest_addr) 这个函数将是uboot的最后一程,完成这个将完成自己的使命转入linux了。
gd = id;
bd = gd->bd;又重新回到了以前的状态,monitor_flash_len 实际上应该是uboot(除去bss段)的长度吧(有疑问)
gd->flags |= GD_FLG_RELOC
@2:使能caches,不必多讲
板级初始化board_init();
gd->bd->bi_arch_number = CONFIG_MACH_TYPE;
gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);
好宏定义展开
gd->bd->bi_arch_number = 2456 //传入linux的机器码
gd->bd->bi_boot_params = 0x30000000 +0x100 = 0x30000100 //启动参数的存放地址
往下:
@1:串口的初始化 注意我们的平台是S5P,所以应该可以猜到是用哪个函数了,没错是
serial_assign (default_serial_console ()->name);
其实是在根据所配置的串口选出当前的串口。
serial_assign 会根据name遍历整个serial_devices来决定当前的串口是哪一个,并将serial_current赋值,显然最后serial_current = s5p_serial0_device。
就是在给malloc分配起始地址,空间大小等
往下:
nand以及mmc的初始化应该是标配的,uboot的自带的,不在详细的讲解。
接着往下:
@1 :设置env表
env_relocate();
env_valid 在之前已经设置为1了,显然走的是env_relocate_spec。
这个env是存储在nand中的,首先会读取nandflash上的是否存在env,假如存在的话则退出,假如不存在的话会设置默认的env的环境。注意CONFIG_ENV_SIZE的大小是16KB,读取到buf中,0x15000000是nandflash存储env的起始地址,大小是16KB
分为两种情况:
@1已经存在env
读取env到buf中直接调用env_import(buf, 1);env_import(buf, 1)的作用是将env导入到env_htab中去
@2不存在env或者env不对
写入默认的env导入到env的hashtable中即env_htab中去,然后返回
默认的env是default_environment
这二者的结果都是要将env导入到env_htab中去,然后将
gd->flags |= GD_FLG_ENV_READY;
以后所有和env相关的操作都要从env_htab从去获取或者写入
好的往下:
获得IP地址,ip地址从env_htab中获取,但是并非是实际的ip地址,而是经过一定的算法得到的。具体算法如下:
接下来是比较重要的部分:
stdio_init();
@2:drv_video_init 真正的video 初始化
@2:video_fb_address是LCD framebuf的初始写地址。然后根据具体使用的位数来确定fgx以及bgx,具体的我也不清楚了。
@3:不过重要的是怎么drawlogo:
video_console_address = video_logo();
@1:画图:logo_plot 整体思路就是将对应的logo变化成16进制的数组,这里对应的数组是bmp_logo_palette,那么如何将图转变成数组好接下来讲具体步骤:
@2:转成bmp图之后将其拷贝到tools下,然后有一个bmp_logo的可执行文件,其对应的代码是bmp_logo.c,对应的指令是./bmp_logo ./logos/webee210.bmp > 1.txt,其生成的数组就在1.txt中了,将其全部替换写入Bmp_logo.h (include)
@3ok,大功告成,看看你的图片修改成功没。
@4 其实我还是遇到了一些不顺心的事就是我的图片很大,但是显示的时候发现照片数据显示不完整,所以百度了一番,发现是bmp_logo.c的问题,uboot是轻量化的启动,支持较小图片的显示,不过我还是找到了,可以参照http://blog.csdn.net/lutao614/article/details/17507951中说的,进行修改,但是我的图片好像是实在太大了,虽然可以显示全了,但是发现图片有移位的现象,像是生成的bmp数组越界之后重新来过了。这个我就不再考虑了。假如哪天显示全了我再重新写个博客。
@5最后返回了新的framebuf的地址
返回到drv_video_init的函数中,此时又新建了一个console_dev的实例化设备并将其填入到devs的链表中,同时初始化对应的函数指针。
好往下:
serial_stdio_init ();
这个函数实际上是将之前注册在serial_devices链表中的串口设备全部注册在了devs的链表中了。
好 stdio_init(); 已经结束了
回到Board.c (arch\arm\lib)
jumptable_init();
申请空间并初始化了一部分的常用函数额跳转表
_exports.h
console_init_r 其实是将stdin,stdout,stderr 的函数重定向了,重定向到了名为serial 的设备上。
往下了:
@2:board_late_init();这两个幻数搞的我有点蒙圈了,希望有人指点
接着往下:
@1:u_boot_hush_start 初始化了一个top_vars全局变量。
@2:从env中读取bootcmd,发现有定义的,"bootcmd=" CONFIG_BOOTCOMMAND
"nand read 0x30007fc0 0x100000 0x500000;bootm 0x30007fc0\0"
那么真正的是在abortboot (bootdelay)做文章。
首先延时了30ms,然后检测是否有键按下,有的话 abort=1;或者30ms之后是否有终端有消息有的话,abort = 1;然后跳入while循环,wihle循环中一直检测是否有串口数据。有的话直接跳出后返回abort=1;没有的话是abort = 0;
有两种情况:
1.倒计时完成仍然没有数据:将s的启动指令传递到parse_string_outer,指令是 nand read 0x30007fc0 0x100000 0x500000;bootm 0x30007fc0,
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
这个其实会传递执行两个命令,一个是将linux 的镜像读取到0x30007fc0,然后从0x30007fc0启动,其中要讲一下关于bootm的命令会干一件事情将我们之前的gd参数打包并传递到linux内核,内核完成解析。
因为要启动linux,所以最后调用的是do_bootm_linux。
2:倒计时中间有终端数据回来,执行menu的cmd
run_command("menu",0);
这就是一个简单的cmd的实现,具体实现我摘抄了网峰上的部分东西。
一、 制作 hello 命令
1. 创建 U-Boot 目录下的 common 目录中的 cmd_hello.c 文件,用命令:
vim common/cmd_hello.c
2. 在 cmd_hello.c 中添加下图内容,然后保存:
249#include <common.h>
#include <command.h>
#include <image.h>
#include <u-boot/zlib.h>
#include <bzlib.h>
#include <environment.h>
#include <lmb.h>
#include <linux/ctype.h>
#include <asm/byteorder.h>
#include <linux/compiler.h>
int do_hello(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
{
printf("\nHello world\n\n");
}
U_BOOT_CMD(hello, 4, 1, do_hello,
"test:hello",
"test:hello"
);
上面 U_BOOT_CMD 其实是一个宏,它在 include/command.h 头文件中被定义:
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) /
cmd_tbl_t __u_boot_cmd_##name Struct_Section =
{#name, maxargs, rep, cmd, usage, help
}
U_BOOT_CMD 中有四个参数:
name 命令的名字。
maxargs 命令执行函数中的最大参数个数。
rep 重复数。
cmd 命令执行函数名字。
usage命令用法信息。
help命令帮助信息。
也就是当我们在 U-Boot 中输入 hello,U-Boot 就会执行 do_hello 函数。
3. 打开 common 目录下的 Makefile 文件,用命令:
vim common/Makefile
4. 在 common/Makefile 文件中添加以下一行:
COBJS-y += cmd_hello.o
/* 在第 29 行添加 */
这样就能在编译 U-Boot 时,把 cmd_hello.c 编译成 cmd_hello.o,再链接到
最终生成的 bin 文件中。
5. 编译,用命令:
make webee210_config
make
把生成的 webee210-uboot.bin 烧到 SD 卡上(参考使用手册的烧写说明),然
后插到开发板,从 SD 方式启动开发板,在 U-Boot 上输入 help 命令:
可以看到,hello 命令已经存在。
好了,uboot的整体大概流程分析已经步入尾声了,还是有很多地方不懂,但是起码懂得流程后就知道哪儿有问题,到哪里去找,然后一头扎进去,借助log,应该很快就能找到原因。
不忘初心,方得始终。
前面的都是相同的,但是在lowlevel_init中会有不同。
/* when we already run in ram, we don't need to relocate U-Boot. * and actually, memory controller must be configured before U-Boot * is running in ram. */ ldr r0, =0x00ffffff bic r1, pc, r0 /* r0 <- current base addr of code */ ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */ bic r2, r2, r0 /* r0 <- current base addr of code */ cmp r1, r2 /* compare r0, r1 */ beq 1f /* r0 == r1 then skip sdram init */ /* init system clock */ bl system_clock_init /* Memory initialize */ bl mem_ctrl_asm_init 1: /* for UART */ bl uart_asm_init bl tzpc_init会判断当前运行的环境是内部的RAM还是外部的RAM,现在运行的环境是外部的RAM,那么会跳过系统时钟的初始化以及内存的初始化,会重新初始化串口,webee210的板子上串口初始化后会打印2次的ok,这一点也验证了这个地方会重新运行。
接着回到调用函数的地方。start.s中:
/* Set stackpointer in internal RAM to call board_init_f */ call_board_init_f: ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ ldr r0,=0x00000000 #if defined(CONFIG_WEBEE210) || defined(CONFIG_MINI210) adr r4, _start ldr r5,_TEXT_BASE cmp r5,r4 beq board_init_in_ram此时我们是跑在了外部的ram中,这里还会进行判断,假如是外部的RAM,直接跳转到board_init_in_ram处:
board_init_in_ram: bl board_init_f好吧,调到board_init_f中了,Board.c (arch\arm\lib)
bd_t *bd; init_fnc_t **init_fnc_ptr; gd_t *id; ulong addr, addr_sp; /* Pointer is writable since we allocated a register for it */ gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07); //gd webee 210 上的地址是 // gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07); /* compiler optimization barrier needed for GCC >= 3.4 */ __asm__ __volatile__("": : :"memory"); memset((void *)gd, 0, sizeof(gd_t)); //清0 gd->mon_len = _bss_end_ofs; //uboot的长度,bss段的结束地址 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { //初始化函数指针 if ((*init_fnc_ptr)() != 0) { hang (); } }
其中init_sequence定义如下:
init_fnc_t *init_sequence[] = { #if defined(CONFIG_ARCH_CPU_INIT) // 有定义 初始化和CPU相关的ARCH arch_cpu_init, /* basic arch cpu dependent setup */ #endif #if defined(CONFIG_BOARD_EARLY_INIT_F) //未定义 board_early_init_f, #endif timer_init, /* initialize timer */ #ifdef CONFIG_FSL_ESDHC //未定义 get_clocks, #endif env_init, /* initialize environment */ init_baudrate, /* initialze baudrate settings */ serial_init, /* serial communications setup */ console_init_f, /* stage 1 init of console */ display_banner, /* say that we are here */ #if defined(CONFIG_DISPLAY_CPUINFO) print_cpuinfo, /* display cpu info (and speed) */ #endif #if defined(CONFIG_DISPLAY_BOARDINFO) checkboard, /* display board info */ #endif #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C) //未定义 init_func_i2c, #endif dram_init, /* configure available RAM banks */ NULL, };
@1:arch_cpu_init:
#ifdef CONFIG_ARCH_CPU_INIT int arch_cpu_init(void) { s5p_set_cpu_id(); return 0; } #endifs5p_set_cpu_id函数从cpu内部读取了相关的id值到s5p_cpu_id的全局的变量。
@2:
int timer_init(void) { /* PWM Timer 4 */ pwm_init(4, MUX_DIV_2, 0); pwm_config(4, 0, 0); pwm_enable(4); return 0; }
初始化了pwm4的定时器,具体细节不讲了。
@3:env_init
int env_init(void) { //carl_Wang add #if defined(ENV_IS_EMBEDDED) || defined(CONFIG_NAND_ENV_DST) int crc1_ok = 0, crc2_ok = 0; env_t *tmp_env1; #ifdef CONFIG_ENV_OFFSET_REDUND env_t *tmp_env2; tmp_env2 = (env_t *)((ulong)env_ptr + CONFIG_ENV_SIZE); crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); #endif tmp_env1 = env_ptr; crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); if (!crc1_ok && !crc2_ok) { gd->env_addr = 0; gd->env_valid = 0; return 0; } else if (crc1_ok && !crc2_ok) { gd->env_valid = 1; } #ifdef CONFIG_ENV_OFFSET_REDUND else if (!crc1_ok && crc2_ok) { gd->env_valid = 2; } else { /* both ok - check serial */ if(tmp_env1->flags == 255 && tmp_env2->flags == 0) gd->env_valid = 2; else if(tmp_env2->flags == 255 && tmp_env1->flags == 0) gd->env_valid = 1; else if(tmp_env1->flags > tmp_env2->flags) gd->env_valid = 1; else if(tmp_env2->flags > tmp_env1->flags) gd->env_valid = 2; else /* flags are equal - almost impossible */ gd->env_valid = 1; } if (gd->env_valid == 2) env_ptr = tmp_env2; else #endif if (gd->env_valid == 1) env_ptr = tmp_env1; gd->env_addr = (ulong)env_ptr->data; #else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */ gd->env_addr = (ulong)&default_environment[0]; gd->env_valid = 1; #endif /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */ return (0); }看见了一堆的代码,但是经过查找之后,发现没用的很多,有用的是
#else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */ gd->env_addr = (ulong)&default_environment[0]; gd->env_valid = 1;
gd已经知道了,是一个全局的变量,gd->env_addr 赋值了默认的env 参数表,同时置位env_valid有效。好看一下这个env参数表
即gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 1;
const uchar default_environment[] = { #ifdef CONFIG_BOOTARGS "bootargs=" CONFIG_BOOTARGS "\0" #endif #ifdef CONFIG_BOOTCOMMAND "bootcmd=" CONFIG_BOOTCOMMAND "nand read 0x30007fc0 0x100000 0x500000;bootm 0x30007fc0\0" #endif #ifdef CONFIG_RAMBOOTCOMMAND "ramboot=" CONFIG_RAMBOOTCOMMAND "\0" #endif #ifdef CONFIG_NFSBOOTCOMMAND "nfsboot=" CONFIG_NFSBOOTCOMMAND "\0" #endif #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) "bootdelay=" MK_STR(CONFIG_BOOTDELAY) "\0" #endif #if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0) "baudrate=" MK_STR(CONFIG_BAUDRATE) "\0" #endif #ifdef CONFIG_LOADS_ECHO "loads_echo=" MK_STR(CONFIG_LOADS_ECHO) "\0" #endif #ifdef CONFIG_ETHADDR "ethaddr=" MK_STR(CONFIG_ETHADDR) "\0" #endif #ifdef CONFIG_ETH1ADDR "eth1addr=" MK_STR(CONFIG_ETH1ADDR) "\0" #endif #ifdef CONFIG_ETH2ADDR "eth2addr=" MK_STR(CONFIG_ETH2ADDR) "\0" #endif #ifdef CONFIG_ETH3ADDR "eth3addr=" MK_STR(CONFIG_ETH3ADDR) "\0" #endif #ifdef CONFIG_ETH4ADDR "eth4addr=" MK_STR(CONFIG_ETH4ADDR) "\0" #endif #ifdef CONFIG_ETH5ADDR "eth5addr=" MK_STR(CONFIG_ETH5ADDR) "\0" #endif #ifdef CONFIG_IPADDR "ipaddr=" MK_STR(CONFIG_IPADDR) "\0" #endif #ifdef CONFIG_SERVERIP "serverip=" MK_STR(CONFIG_SERVERIP) "\0" #endif #ifdef CONFIG_SYS_AUTOLOAD "autoload=" CONFIG_SYS_AUTOLOAD "\0" #endif #ifdef CONFIG_PREBOOT "preboot=" CONFIG_PREBOOT "\0" #endif #ifdef CONFIG_ROOTPATH "rootpath=" MK_STR(CONFIG_ROOTPATH) "\0" #endif #ifdef CONFIG_GATEWAYIP "gatewayip=" MK_STR(CONFIG_GATEWAYIP) "\0" #endif #ifdef CONFIG_NETMASK "netmask=" MK_STR(CONFIG_NETMASK) "\0" #endif #ifdef CONFIG_HOSTNAME "hostname=" MK_STR(CONFIG_HOSTNAME) "\0" #endif #ifdef CONFIG_BOOTFILE "bootfile=" MK_STR(CONFIG_BOOTFILE) "\0" #endif #ifdef CONFIG_LOADADDR "loadaddr=" MK_STR(CONFIG_LOADADDR) "\0" #endif #ifdef CONFIG_CLOCKS_IN_MHZ "clocks_in_mhz=1\0" #endif #if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0) "pcidelay=" MK_STR(CONFIG_PCI_BOOTDELAY) "\0" #endif #ifdef CONFIG_EXTRA_ENV_SETTINGS CONFIG_EXTRA_ENV_SETTINGS #endif "\0" };这个有点类似于linux中的devicetree机制,根据“”中的字符串可以取到后面的相应的命令或者参数。注意一下bootcmd 的指令,启动linux时会用到。。。。。
@4:init_baudrate
初始化波特率,看一下env参数表中是否有这个命令,在 Webee210.h (include\configs) 中定义了
#define CONFIG_BAUDRATE 115200
所以gd->baudrate = 115200
static int init_baudrate(void) { char tmp[64]; /* long enough for environment variables */ int i = getenv_f("baudrate", tmp, sizeof(tmp)); gd->baudrate = (i > 0) ? (int) simple_strtoul(tmp, NULL, 10) : CONFIG_BAUDRATE; return (0); }
@5: serial_init Serial.c (common)
int serial_init (void) { //carl_wang if (!(gd->flags & GD_FLG_RELOC) || !serial_current) { struct serial_device *dev = default_serial_console (); return dev->init (); } return serial_current->init (); }struct serial_device 是一个关于串行设备的一些属性以及方法的(面向对象的思想)结构体,可以使用它构成一个链表。
struct serial_device { char name[NAMESIZE]; int (*init) (void); int (*uninit) (void); void (*setbrg) (void); int (*getc) (void); int (*tstc) (void); void (*putc) (const char c); void (*puts) (const char *s); #if CONFIG_POST & CONFIG_SYS_POST_UART void (*loop) (int); #endif struct serial_device *next; };
dev = default_serial_console()获取到了关于默认设备的实例,
__weak struct serial_device *default_serial_console(void) { #if defined(CONFIG_SERIAL0) //定义了串口0 return &s5p_serial0_device; //实例化 #elif defined(CONFIG_SERIAL1) return &s5p_serial1_device; #elif defined(CONFIG_SERIAL2) return &s5p_serial2_device; #elif defined(CONFIG_SERIAL3) return &s5p_serial3_device; #else #error "CONFIG_SERIAL? missing." #endif }这里获取的是s5p_serial0_device,所以调用dev->init则是调用了serial_init_dev,dev_index则是0
/* * Initialise the serial port with the given baudrate. The settings * are always 8 data bits, no parity, 1 stop bit, no start bits. */ int serial_init_dev(const int dev_index) { //carl_wang add struct s5p_uart *const uart = s5p_get_base_uart(dev_index); /* reset and enable FIFOs, set triggers to the maximum */ writel(0, &uart->ufcon); writel(0, &uart->umcon); /* 8N1 */ writel(0x3, &uart->ulcon); /* No interrupts, no DMA, pure polling */ writel(0x245, &uart->ucon); serial_setbrg_dev(dev_index); return 0; }@6:console_init_f,实际上只是置为标位,此时
gd->have_console = 1;
/* Called before relocation - use serial functions */ int console_init_f(void) { gd->have_console = 1; #ifdef CONFIG_SILENT_CONSOLE //未定义 if (getenv("silent") != NULL) gd->flags |= GD_FLG_SILENT; #endif return 0; }@7:display_banner 哎,就是log输出点版本信息,这里还log输出了uboot的起始地址,bss 段起始地址,bss段的结束地址。
打印出的log如下:
U-Boot 2011.06 (Dec 15 2015 - 22:32:20) for Webee_210_V2
U-Boot code: 33E00000 -> 33E66A30 BSS: -> 33E9CAFC
static int display_banner(void) { printf("\n\n%s\n\n", version_string); printf("U-Boot code: %08lX -> %08lX BSS: -> %08lX\n", _TEXT_BASE, _bss_start_ofs + _TEXT_BASE, _bss_end_ofs + _TEXT_BASE); #ifdef CONFIG_MODEM_SUPPORT debug("Modem Support enabled\n"); #endif #ifdef CONFIG_USE_IRQ debug("IRQ Stack: %08lx\n", IRQ_STACK_START); debug("FIQ Stack: %08lx\n", FIQ_STACK_START); #endif return (0); }
@8:print_cpuinfo
log出了一点信息,其中你想写的,还有是芯片的频率 。
#ifdef CONFIG_DISPLAY_CPUINFO int print_cpuinfo(void) { char buf[32]; printf("\n"); printf("##############################################\n"); printf("# #\n"); printf("# i love wunana #\n"); printf("# #\n"); printf("##############################################\n\n"); //printf("CPU:\tS5P%X@%sMHz\n", // s5p_cpu_id, strmhz(buf, get_arm_clk())); printf("CPU:\tS5PV210@%sMHz\n", strmhz(buf, get_arm_clk())); return 0; } #endif
@9:checkboard:没有特殊的作用,还是log
#ifdef CONFIG_DISPLAY_BOARDINFO int checkboard(void) { //carl_wang printf("\nBoard: Webee_210_V2\n"); return (0); } #endif
@10 dram_init 其实是初始化了
gd->ram_size = 512M
int dram_init(void) { /* Since we have discontinuous RAM configuration, just put * bank1 here for relocation */ gd->ram_size = get_ram_size((long *)PHYS_SDRAM_1, PHYS_SDRAM_1_SIZE+PHYS_SDRAM_2_SIZE); return 0; }
ok,假如中间有任何错误的化都会挂起等待。接着往下走
#if defined(CONFIG_SYS_MEM_TOP_HIDE) /* * Subtract specified amount of memory to hide so that it won't * get "touched" at all by U-Boot. By fixing up gd->ram_size * the Linux kernel should now get passed the now "corrected" * memory size and won't touch it either. This should work * for arch/ppc and arch/powerpc. Only Linux board ports in * arch/powerpc with bootwrapper support, that recalculate the * memory size from the SDRAM controller setup will have to * get fixed. */ gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE; #endif addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size; #ifdef CONFIG_LOGBUFFER #ifndef CONFIG_ALT_LB_ADDR /* reserve kernel log buffer */ addr -= (LOGBUFF_RESERVE); debug("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN, addr); #endif #endif
其实就有一句话被编译到了,addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size; 其中CONFIG_SYS_SDRAM_BASE 定义是0x30000000,而ram_size是536870912
ok 往下
#ifdef CONFIG_PRAM //未定义 /* * reserve protected RAM */ i = getenv_r("pram", (char *)tmp, sizeof(tmp)); reg = (i > 0) ? simple_strtoul((const char *)tmp, NULL, 10) : CONFIG_PRAM; addr -= (reg << 10); /* size is in kB */ debug("Reserving %ldk for protected RAM at %08lx\n", reg, addr); #endif /* CONFIG_PRAM */ #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) //会编译到 /* reserve TLB table */ addr -= (4096 * 4); /* round down to next 64 kB limit */ addr &= ~(0x10000 - 1); gd->tlb_addr = addr; printf("TLB table at: %08lx\n", addr); #endif /* round down to next 4 kB limit */ addr &= ~(4096 - 1); printf("Top of RAM usable for U-Boot at: %08lx\n", addr);这个就是给TLB table分出了一些空间,然后禁止访问。
接着往下
#ifdef CONFIG_LCD //未定义 #ifdef CONFIG_FB_ADDR gd->fb_base = CONFIG_FB_ADDR; debug("Top the fb_base addr is : %08lx\n", gd->fb_base); #else /* reserve memory for LCD display (always full pages) */ addr = lcd_setmem(addr); gd->fb_base = addr; debug("Top the fb_base addr is : %08lx\n", gd->fb_base); #endif /* CONFIG_FB_ADDR */ #endif /* CONFIG_LCD */ /* * reserve memory for U-Boot code, data & bss * round down to next 4 kB limit */ addr -= gd->mon_len; addr &= ~(4096 - 1); debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);
这个又是在为uboot预留空间,然后进行4Kb的四舍五入。
往下:
#ifndef CONFIG_SPL_BUILD //会编译到 /* * reserve memory for malloc() arena */ addr_sp = addr - TOTAL_MALLOC_LEN; debug("Reserving %dk for malloc() at: %08lx\n", TOTAL_MALLOC_LEN >> 10, addr_sp); /* * (permanently) allocate a Board Info struct * and a permanent copy of the "global" data */ addr_sp -= sizeof (bd_t); bd = (bd_t *) addr_sp; gd->bd = bd; debug("Reserving %zu Bytes for Board Info at: %08lx\n", sizeof (bd_t), addr_sp); #ifdef CONFIG_MACH_TYPE gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */ #endif addr_sp -= sizeof (gd_t); id = (gd_t *) addr_sp; debug("Reserving %zu Bytes for Global Data at: %08lx\n", sizeof (gd_t), addr_sp); /* setup stackpointer for exeptions */ gd->irq_sp = addr_sp; #ifdef CONFIG_USE_IRQ addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ); debug("Reserving %zu Bytes for IRQ stack at: %08lx\n", CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp); #endif /* leave 3 words for abort-stack */ addr_sp -= 12; /* 8-byte alignment for ABI compliance */ addr_sp &= ~0x07; #else addr_sp += 128; /* leave 32 words for abort-stack */ gd->irq_sp = addr_sp; #endif
一部分预留空间给malloc使用,uboot,TLB table,board info,gd,异常退出栈(abort stack),按照webee210板子的sdram的配置,如下图:
最后还要看一下重要的数据结构gd发生了什么变化:
gd->mon_len = 0x9cd2c //uboot的长度(_start 到bss_ends)
gd->ram_size = 512M = 0x20000000 //实际的物理内存大小
gd->tlb_addr = 0x4fff0000 // TLB table 内存地址
gd->bd = bd = 0x4fe6afe0 //board info 的地址
gd->bd->bi_arch_number = 2456 //传递给linux内核的机器码
gd->irq_sp = 0x4fe6af68 //中断栈指针
此时的addr 以及addrsp 也有相对应的值了
addr = 0x4ff53000
addr_sp = 0x4fe6af58
接着往下:
#ifdef CONFIG_POST //未定义 post_bootmode_init(); post_run(NULL, POST_ROM | post_bootmode_get(0)); #endif gd->bd->bi_baudrate = gd->baudrate; /* Ram ist board specific, so move it to board code ... */ dram_init_banksize(); display_dram_config(); /* and display it */ gd->relocaddr = addr; gd->start_addr_sp = addr_sp; gd->reloc_off = addr - _TEXT_BASE; printf("relocation Offset is: %08lx\n", gd->reloc_off); // printf("relocaddr is: %08lx\n", gd->relocaddr); // printf("start_addr_sp is: %08lx\n", gd->start_addr_sp); // printf("relocation Offset is: %08lx\n", gd->reloc_off); memcpy(id, (void *)gd, sizeof(gd_t)); relocate_code(addr_sp, id, addr);dram_init_banksize(); Webee210.c (board\samsung\webee210)初始化了gd的 参数,表明起始地址,长度。这些参数都要传递到linux内核中去。
void dram_init_banksize(void) { gd->bd->bi_dram[0].start = PHYS_SDRAM_1; gd->bd->bi_dram[0].size = get_ram_size((long *)PHYS_SDRAM_1, \ PHYS_SDRAM_1_SIZE); gd->bd->bi_dram[1].start = PHYS_SDRAM_2; gd->bd->bi_dram[1].size = get_ram_size((long *)PHYS_SDRAM_2,PHYS_SDRAM_2_SIZE); }gd->bd->bi_baudrate = gd->baudrate = 115200
gd->bd->bi_dram[0].start = 0x30000000
gd->bd->bi_dram[0].size = 0x10000000
gd->bd->bi_dram[1].start = 0x40000000
gd->bd->bi_dram[1].size = 0x10000000
而后调用显示Dram的大小的函数,打印显示DRAM:512M
static int display_dram_config(void) { int i; #ifdef DEBUG puts("RAM Configuration:\n"); for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { printf("Bank #%d: %08lx ", i, gd->bd->bi_dram[i].start); print_size(gd->bd->bi_dram[i].size, "\n"); } #else ulong size = 0; for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) size += gd->bd->bi_dram[i].size; puts("DRAM: "); print_size(size, "\n"); puts(" \n\n"); #endif return (0); }最后赋值几个相关的gd的参数
gd->relocaddr = addr = 0x4ff53000
gd->start_addr_sp = addr_sp = 0x4fe6af58
gd->reloc_off = addr - _TEXT_BASE = 0x1c153000
memcpy(id, (void *)gd, sizeof(gd_t)); relocate_code(addr_sp, id, addr);
将gd的地址拷贝到id作为参数传递给relocate_code,同时addr_sp,addr 作为参数传递给relocate_code了。
好往下走:
relocate_code是start.s 中一段代码:
/*------------------------------------------------------------------------------*/ /* * void relocate_code (addr_sp, gd, addr_moni) * * This "function" does not return, instead it continues in RAM * after relocating the monitor code. * */ .globl relocate_code relocate_code: mov r4, r0 /* save addr_sp */ mov r5, r1 /* save addr of gd */ mov r6, r2 /* save addr of destination */ /* Set up the stack */ stack_setup: mov sp, r4 adr r0, _start cmp r0, r6 moveq r9, #0 /* no relocation. relocation offset(r9) = 0 */ beq clear_bss /* skip relocation */ mov r1, r6 /* r1 <- scratch for copy_loop */ ldr r3, _image_copy_end_ofs add r2, r0, r3 /* r2 <- source end address */ copy_loop: ldmia r0!, {r9-r10} /* copy from source address [r0] */ stmia r1!, {r9-r10} /* copy to target address [r1] */ cmp r0, r2 /* until source end address [r2] */ blo copy_loop ///////////////////////////////////////////////////////////////////////////////// #ifndef CONFIG_SPL_BUILD /* * fix .rel.dyn relocations */ ldr r0, _TEXT_BASE /* r0 <- Text base */ sub r9, r6, r0 /* r9 <- relocation offset */ ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */ add r10, r10, r0 /* r10 <- sym table in FLASH */ ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */ add r2, r2, r0 /* r2 <- rel dyn start in FLASH */ ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */ add r3, r3, r0 /* r3 <- rel dyn end in FLASH */ fixloop: ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */ add r0, r0, r9 /* r0 <- location to fix up in RAM */ ldr r1, [r2, #4] and r7, r1, #0xff cmp r7, #23 /* relative fixup? */ beq fixrel cmp r7, #2 /* absolute fixup? */ beq fixabs /* ignore unknown type of fixup */ b fixnext fixabs: /* */ /* absolute fix: set location to (offset) symbol value */ mov r1, r1, LSR #4 /* r1 <- symbol index in .dynsym */ add r1, r10, r1 /* r1 <- address of symbol in table */ ldr r1, [r1, #4] /* r1 <- symbol value */ add r1, r1, r9 /* r1 <- relocated sym addr */ b fixnext fixrel: /* relative fix: increase location by offset */ ldr r1, [r0] add r1, r1, r9 fixnext: str r1, [r0] add r2, r2, #8 /* each rel.dyn entry is 8 bytes */ cmp r2, r3 blo fixloop b clear_bss _rel_dyn_start_ofs: .word __rel_dyn_start - _start _rel_dyn_end_ofs: .word __rel_dyn_end - _start _dynsym_start_ofs: .word __dynsym_start - _start #endif /* #ifndef CONFIG_SPL_BUILD */ ///////////////////////////////////////////////////////////////////////////////////// clear_bss: #ifdef CONFIG_SPL_BUILD /* No relocation for SPL */ ldr r0, =__bss_start ldr r1, =__bss_end__ #else ldr r0, _bss_start_ofs ldr r1, _bss_end_ofs mov r4, r6 /* reloc addr */ add r0, r0, r4 add r1, r1, r4 #endif mov r2, #0x00000000 /* clear */ clbss_l:str r2, [r0] /* clear loop... */ add r0, r0, #4 cmp r0, r1 bne clbss_l /* * We are done. Do not return, instead branch to second part of board * initialization, now running from RAM. */ jump_2_ram: /* * If I-cache is enabled invalidate it */ #ifndef CONFIG_SYS_ICACHE_OFF mcr p15, 0, r0, c7, c5, 0 @ invalidate icache mcr p15, 0, r0, c7, c10, 4 @ DSB mcr p15, 0, r0, c7, c5, 4 @ ISB #endif ldr r0, _board_init_r_ofs adr r1, _start add lr, r0, r1 add lr, lr, r9 /* setup parameters for board_init_r */ mov r0, r5 /* gd_t */ mov r1, r6 /* dest_addr */ /* jump to it ... */ mov pc, lr _board_init_r_ofs: .word board_init_r - _start
这部分又将内存中的数据进行了一番拷贝将uboot的运行地址迁移到了gd->relocaddr处,继续执行。在完成搬运工作之后,回跳转到board_init_r的函数中,r5,r6 作为参数传入了这个函数中。
往下:void board_init_r(gd_t *id, ulong dest_addr) 这个函数将是uboot的最后一程,完成这个将完成自己的使命转入linux了。
/* ************************************************************************ * * This is the next part if the initialization sequence: we are now * running from RAM and have a "normal" C environment, i. e. global * data can be written, BSS has been cleared, the stack size in not * that critical any more, etc. * ************************************************************************ */ void board_init_r(gd_t *id, ulong dest_addr) { char *s; bd_t *bd; ulong malloc_start; #if !defined(CONFIG_SYS_NO_FLASH) ulong flash_size; #endif gd = id; bd = gd->bd; gd->flags |= GD_FLG_RELOC; /* tell others: relocation done */ monitor_flash_len = _end_ofs; //carl_wang add printf("hello board_init_r \n"); printf("hello dest_addr:%x %x\n,",(int)dest_addr,id); /* Enable caches */ enable_caches(); printf("monitor flash len: %08lX\n", monitor_flash_len); board_init(); /* Setup chipselects */ #ifdef CONFIG_SERIAL_MULTI serial_initialize(); #endif printf("Now running in RAM - U-Boot at: %08lx\n", dest_addr);@1:
gd = id;
bd = gd->bd;又重新回到了以前的状态,monitor_flash_len 实际上应该是uboot(除去bss段)的长度吧(有疑问)
.globl _end_ofs _end_ofs: .word _end - _start以下是整个uboot的段布局,可以看出_end 是在uboot中从_start到bss段之前的内容。最后告诉别人自己已经RELOC了。
gd->flags |= GD_FLG_RELOC
ENTRY(_start) SECTIONS { . = 0x00000000; . = ALIGN(4); .text : { arch/arm/cpu/armv7/start.o (.text) *(.text) } . = ALIGN(4); .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } . = ALIGN(4); .data : { *(.data) } . = ALIGN(4); . = .; __u_boot_cmd_start = .; .u_boot_cmd : { *(.u_boot_cmd) } __u_boot_cmd_end = .; . = ALIGN(4); __image_copy_end = .; .rel.dyn : { __rel_dyn_start = .; *(.rel*) __rel_dyn_end = .; } .dynsym : { __dynsym_start = .; *(.dynsym) } _end = .; .bss __rel_dyn_start (OVERLAY) : { __bss_start = .; *(.bss) . = ALIGN(4); __bss_end__ = .; } /DISCARD/ : { *(.dynstr*) } /DISCARD/ : { *(.dynamic*) } /DISCARD/ : { *(.plt*) } /DISCARD/ : { *(.interp*) } /DISCARD/ : { *(.gnu*) } }
@2:使能caches,不必多讲
板级初始化board_init();
int board_init(void) { writel(0x22222222, GPF0CON); //GPF0CON set GPF0[0:7] as HSYNC,VSYNC,VDEN,VCLK,VD[0:3] writel(0x0, GPF0PUD); //GPF0PUD set pull-up,down disable writel(0x22222222, GPF1CON); //set GPF1CON[7:0] as VD[11:4] writel(0x0, GPF1PUD); //GPF1PUD set pull-up,down disable writel(0x22222222, GPF2CON); //set GPF2CON[7:0] as VD[19:12] writel(0x0, GPF2PUD); //GPF2PUD set pull-up,down disable writel(0x00002222, GPF3CON); //set GPF3CON[3:0] as VD[23:20] writel(0x0, GPF3PUD); //GPF3PUD set pull-up,down disable //--------- S5PC110 EVT0 needs MAX drive strength---------// writel(0xffffffff, GPF0DRV); //set GPF0DRV drive strength max by WJ.KIM(09.07.17) writel(0xffffffff, GPF1DRV); //set GPF1DRV drive strength max by WJ.KIM(09.07.17) writel(0xffffffff, GPF2DRV); //set GPF2DRV drive strength max by WJ.KIM(09.07.17) writel(0x3ff, GPF3DRV); //set GPF3DRV drive strength max by WJ.KIM(09.07.17) /* Set Initial global variables */ s5pc110_gpio = (struct s5pc110_gpio *)S5PC110_GPIO_BASE; /***Modified by lk ***/ //smc9115_pre_init(); pwm_pre_init(); #ifdef CONFIG_DRIVER_DM9000 dm9000_pre_init(); #endif gd->bd->bi_arch_number = CONFIG_MACH_TYPE; gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100); return 0; }和板子的硬件相关的不要关心,只需要知道的是
gd->bd->bi_arch_number = CONFIG_MACH_TYPE;
gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);
好宏定义展开
gd->bd->bi_arch_number = 2456 //传入linux的机器码
gd->bd->bi_boot_params = 0x30000000 +0x100 = 0x30000100 //启动参数的存放地址
往下:
#ifdef CONFIG_SERIAL_MULTI serial_initialize(); #endif printf("Now running in RAM - U-Boot at: %08lx\n", dest_addr); #ifdef CONFIG_LOGBUFFER //未定义 logbuff_init_ptrs(); #endif #ifdef CONFIG_POST //未定义 post_output_backlog(); #endif /* The Malloc area is immediately below the monitor copy in DRAM */ malloc_start = dest_addr - TOTAL_MALLOC_LEN; mem_malloc_init (malloc_start, TOTAL_MALLOC_LEN);
@1:串口的初始化 注意我们的平台是S5P,所以应该可以猜到是用哪个函数了,没错是
void serial_initialize (void) { #if defined(CONFIG_8xx_CONS_SMC1) || defined(CONFIG_8xx_CONS_SMC2) serial_register (&serial_smc_device); #endif #if defined(CONFIG_8xx_CONS_SCC1) || defined(CONFIG_8xx_CONS_SCC2) \ || defined(CONFIG_8xx_CONS_SCC3) || defined(CONFIG_8xx_CONS_SCC4) serial_register (&serial_scc_device); #endif #if defined(CONFIG_SYS_NS16550_SERIAL) #if defined(CONFIG_SYS_NS16550_COM1) serial_register(&eserial1_device); #endif #if defined(CONFIG_SYS_NS16550_COM2) serial_register(&eserial2_device); #endif #if defined(CONFIG_SYS_NS16550_COM3) serial_register(&eserial3_device); #endif #if defined(CONFIG_SYS_NS16550_COM4) serial_register(&eserial4_device); #endif #endif /* CONFIG_SYS_NS16550_SERIAL */ #if defined (CONFIG_FFUART) serial_register(&serial_ffuart_device); #endif #if defined (CONFIG_BTUART) serial_register(&serial_btuart_device); #endif #if defined (CONFIG_STUART) serial_register(&serial_stuart_device); #endif #if defined(CONFIG_S3C2410) serial_register(&s3c24xx_serial0_device); serial_register(&s3c24xx_serial1_device); serial_register(&s3c24xx_serial2_device); #endif #if defined(CONFIG_S5P) //有定义 serial_register(&s5p_serial0_device); serial_register(&s5p_serial1_device); serial_register(&s5p_serial2_device); serial_register(&s5p_serial3_device); #endif #if defined(CONFIG_MPC512X) #if defined(CONFIG_SYS_PSC1) serial_register(&serial1_device); #endif #if defined(CONFIG_SYS_PSC3) serial_register(&serial3_device); #endif #if defined(CONFIG_SYS_PSC4) serial_register(&serial4_device); #endif #if defined(CONFIG_SYS_PSC6) serial_register(&serial6_device); #endif #endif #if defined(CONFIG_SYS_BFIN_UART) serial_register_bfin_uart(); #endif serial_assign (default_serial_console ()->name); }实际上调用的是下面5个函数。好看看具体在干什么
serial_register(&s5p_serial0_device); serial_register(&s5p_serial1_device); serial_register(&s5p_serial2_device); serial_register(&s5p_serial3_device);
<pre name="code" class="cpp">serial_assign (default_serial_console ()->name);
void serial_register(struct serial_device *dev) { #ifdef CONFIG_NEEDS_MANUAL_RELOC dev->init += gd->reloc_off; dev->setbrg += gd->reloc_off; dev->getc += gd->reloc_off; dev->tstc += gd->reloc_off; dev->putc += gd->reloc_off; dev->puts += gd->reloc_off; #endif dev->next = serial_devices; serial_devices = dev; }你会发现serial_devices是静态的全局变量,这个函数将serial_devices 构成了一个链表
serial_assign (default_serial_console ()->name);
其实是在根据所配置的串口选出当前的串口。
__weak struct serial_device *default_serial_console(void) { #if defined(CONFIG_SERIAL0) //有定义的 return &s5p_serial0_device; #elif defined(CONFIG_SERIAL1) return &s5p_serial1_device; #elif defined(CONFIG_SERIAL2) return &s5p_serial2_device; #elif defined(CONFIG_SERIAL3) return &s5p_serial3_device; #else #error "CONFIG_SERIAL? missing." #endif }所以返回的是s5p_serial0_device的name ->s5pser0
serial_assign 会根据name遍历整个serial_devices来决定当前的串口是哪一个,并将serial_current赋值,显然最后serial_current = s5p_serial0_device。
int serial_assign (char *name) { struct serial_device *s; for (s = serial_devices; s; s = s->next) { if (strcmp (s->name, name) == 0) { serial_current = s; return 0; } } return 1; }@2:
mem_malloc_init
就是在给malloc分配起始地址,空间大小等
往下:
#if !defined(CONFIG_SYS_NO_FLASH) // <span style="font-family: Arial, Helvetica, sans-serif;">CONFIG_SYS_NO_FLASH有定义</span> puts("Flash: "); flash_size = flash_init(); if (flash_size > 0) { # ifdef CONFIG_SYS_FLASH_CHECKSUM print_size(flash_size, ""); /* * Compute and print flash CRC if flashchecksum is set to 'y' * * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX */ s = getenv("flashchecksum"); if (s && (*s == 'y')) { printf(" CRC: %08X", crc32(0, (const unsigned char *) CONFIG_SYS_FLASH_BASE, flash_size)); } putc('\n'); # else /* !CONFIG_SYS_FLASH_CHECKSUM */ print_size(flash_size, "\n"); # endif /* CONFIG_SYS_FLASH_CHECKSUM */ } else { puts(failed); hang(); } #endif #if defined(CONFIG_CMD_NAND) puts("NAND: "); nand_init(); #endif #if defined(CONFIG_CMD_ONENAND) onenand_init(); #endif #ifdef CONFIG_GENERIC_MMC puts("MMC: "); mmc_initialize(bd); #endif #ifdef CONFIG_HAS_DATAFLASH AT91F_DataflashInit(); dataflash_print_info(); #endif这部分主要是在初始化存储器,webee210的板子支持nand以及SDcard,所以你懂得会调用
nand_init();
mmc_initialize(bd);
nand以及mmc的初始化应该是标配的,uboot的自带的,不在详细的讲解。
接着往下:
/* initialize environment */ env_relocate(); #if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI) //未定义 arm_pci_init(); #endif /* IP Address */ gd->bd->bi_ip_addr = getenv_IPaddr("ipaddr"); stdio_init(); /* get the devices list going. */ printf("carl_wang->nimei@1\r\n"); jumptable_init(); #if defined(CONFIG_API) //未定义 /* Initialize API */ api_init(); #endif //carl_wang fix console_init_r(); /* fully init console as a device */ printf("carl_wang->nimei@2\r\n"); #if defined(CONFIG_ARCH_MISC_INIT) //未定义 /* miscellaneous arch dependent initialisations */ arch_misc_init(); #endif #if defined(CONFIG_MISC_INIT_R) //未定义 /* miscellaneous platform dependent initialisations */ misc_init_r(); #endif /* set up exceptions */ interrupt_init(); //设置中断 /* enable exceptions */ enable_interrupts(); //使能中断 由于我们并未使用中断,所以为空函数
@1 :设置env表
env_relocate();
void env_relocate (void) { #if defined(CONFIG_NEEDS_MANUAL_RELOC) extern void env_reloc(void); env_reloc(); #endif if (gd->env_valid == 0) { #if defined(CONFIG_ENV_IS_NOWHERE) /* Environment not changable */ set_default_env(NULL); #else show_boot_progress (-60); set_default_env("!bad CRC"); #endif } else { env_relocate_spec (); } }
env_valid 在之前已经设置为1了,显然走的是env_relocate_spec。
void env_relocate_spec (void) { printf("env_relocate_spec\n"); #if !defined(ENV_IS_EMBEDDED) int ret; char buf[CONFIG_ENV_SIZE]; #if defined(CONFIG_ENV_OFFSET_OOB) ret = get_nand_env_oob(&nand_info[0], &nand_env_oob_offset); /* * If unable to read environment offset from NAND OOB then fall through * to the normal environment reading code below */ if (!ret) { printf("Found Environment offset in OOB..\n"); } else { set_default_env("!no env offset in OOB"); return; } #endif //ret = readenv(CONFIG_ENV_OFFSET, (u_char *)buf); ////////////////////////////////////////////////// ret = readenv(0x15000000, (u_char *)buf); ///////////////////////////////////////////////// if (ret) { printf("carl_Wang readenv fail\n"); set_default_env("!readenv() failed"); return; } env_import(buf, 1); #endif /* ! ENV_IS_EMBEDDED */ }
这个env是存储在nand中的,首先会读取nandflash上的是否存在env,假如存在的话则退出,假如不存在的话会设置默认的env的环境。注意CONFIG_ENV_SIZE的大小是16KB,读取到buf中,0x15000000是nandflash存储env的起始地址,大小是16KB
分为两种情况:
@1已经存在env
读取env到buf中直接调用env_import(buf, 1);env_import(buf, 1)的作用是将env导入到env_htab中去
/* * Check if CRC is valid and (if yes) import the environment. * Note that "buf" may or may not be aligned. */ int env_import(const char *buf, int check) { env_t *ep = (env_t *)buf; if (check) { uint32_t crc; memcpy(&crc, &ep->crc, sizeof(crc)); if (crc32(0, ep->data, ENV_SIZE) != crc) { set_default_env("!bad CRC"); return 0; } } if (himport_r(&env_htab, (char *)ep->data, ENV_SIZE, '\0', 0)) { gd->flags |= GD_FLG_ENV_READY; return 1; } error("Cannot import environment: errno = %d\n", errno); set_default_env("!import failed"); return 0; }
@2不存在env或者env不对
写入默认的env导入到env的hashtable中即env_htab中去,然后返回
默认的env是default_environment
void set_default_env(const char *s) { if (sizeof(default_environment) > ENV_SIZE) { puts("*** Error - default environment is too large\n\n"); return; } if (s) { if (*s == '!') { printf("*** Warning - %s, " "using default environment\n\n", s+1); } else { puts(s); } } else { puts("Using default environment\n\n"); } if (himport_r(&env_htab, (char *)default_environment, sizeof(default_environment), '\0', 0) == 0) { error("Environment import failed: errno = %d\n", errno); } gd->flags |= GD_FLG_ENV_READY; }
这二者的结果都是要将env导入到env_htab中去,然后将
gd->flags |= GD_FLG_ENV_READY;
以后所有和env相关的操作都要从env_htab从去获取或者写入
好的往下:
/* IP Address */ gd->bd->bi_ip_addr = getenv_IPaddr("ipaddr");
获得IP地址,ip地址从env_htab中获取,但是并非是实际的ip地址,而是经过一定的算法得到的。具体算法如下:
IPaddr_t string_to_ip(const char *s) { IPaddr_t addr; char *e; int i; if (s == NULL) return(0); printf("carl_Wang->ip:%s\n",s); for (addr=0, i=0; i<4; ++i) { ulong val = s ? simple_strtoul(s, &e, 10) : 0; addr <<= 8; addr |= (val & 0xFF); if (s) { s = (*e) ? e+1 : e; } } return (htonl(addr)); }gd->bd->bi_ip_addr = 2063640768
接下来是比较重要的部分:
stdio_init();
nt stdio_init (void) { #if defined(CONFIG_NEEDS_MANUAL_RELOC) //未定义 /* already relocated for current ARM implementation */ ulong relocation_offset = gd->reloc_off; int i; /* relocate device name pointers */ for (i = 0; i < (sizeof (stdio_names) / sizeof (char *)); ++i) { stdio_names[i] = (char *) (((ulong) stdio_names[i]) + relocation_offset); } #endif /* CONFIG_NEEDS_MANUAL_RELOC */ /* Initialize the list */ INIT_LIST_HEAD(&(devs.list)); #ifdef CONFIG_ARM_DCC_MULTI //未定义 drv_arm_dcc_init (); #endif #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C) //未定义 i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); #endif #ifdef CONFIG_LCD //未定义 drv_lcd_init (); #endif #if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE) //有定义 drv_video_init (); #endif #ifdef CONFIG_KEYBOARD //未定义 drv_keyboard_init (); #endif #ifdef CONFIG_LOGBUFFER //未定义 drv_logbuff_init (); #endif drv_system_init (); #ifdef CONFIG_SERIAL_MULTI serial_stdio_init (); #endif #ifdef CONFIG_USB_TTY //未定义 drv_usbtty_init (); #endif #ifdef CONFIG_NETCONSOLE //未定义 drv_nc_init (); #endif #ifdef CONFIG_JTAG_CONSOLE //未定义 drv_jtag_console_init (); #endif return (0); }@1:初始化devs.lists链表。devs存储设备,而且是能够构成设备的链表
@2:drv_video_init 真正的video 初始化
int drv_video_init(void) { int skip_dev_init; struct stdio_dev console_dev; /* Check if video initialization should be skipped */ if (board_video_skip()) //空函数 return 0; /* Init video chip - returns with framebuffer cleared */ skip_dev_init = (video_init() == -1); //真正初始化video的函数 #if !defined(CONFIG_VGA_AS_SINGLE_DEVICE) debug("KBD: Keyboard init ...\n"); skip_dev_init |= (VIDEO_KBD_INIT_FCT == -1); #endif if (skip_dev_init) return 0; /* Init vga device */ memset(&console_dev, 0, sizeof(console_dev)); strcpy(console_dev.name, "vga"); console_dev.ext = DEV_EXT_VIDEO; /* Video extensions */ console_dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_SYSTEM; console_dev.putc = video_putc; /* 'putc' function */ console_dev.puts = video_puts; /* 'puts' function */ console_dev.tstc = NULL; /* 'tstc' function */ console_dev.getc = NULL; /* 'getc' function */ #if !defined(CONFIG_VGA_AS_SINGLE_DEVICE) /* Also init console device */ console_dev.flags |= DEV_FLAGS_INPUT; console_dev.tstc = VIDEO_TSTC_FCT; /* 'tstc' function */ console_dev.getc = VIDEO_GETC_FCT; /* 'getc' function */ #endif /* CONFIG_VGA_AS_SINGLE_DEVICE */ if (stdio_register(&console_dev) != 0) return 0; /* Return success */ return 1; }video初始化函数
static int video_init(void) { unsigned char color8; pGD = video_hw_init(); if (pGD == NULL) return -1; video_fb_address = (void *) VIDEO_FB_ADRS; #ifdef CONFIG_VIDEO_HW_CURSOR video_init_hw_cursor(VIDEO_FONT_WIDTH, VIDEO_FONT_HEIGHT); #endif /* Init drawing pats */ switch (VIDEO_DATA_FORMAT) { case GDF__8BIT_INDEX: video_set_lut(0x01, CONSOLE_FG_COL, CONSOLE_FG_COL, CONSOLE_FG_COL); video_set_lut(0x00, CONSOLE_BG_COL, CONSOLE_BG_COL, CONSOLE_BG_COL); fgx = 0x01010101; bgx = 0x00000000; break; case GDF__8BIT_332RGB: color8 = ((CONSOLE_FG_COL & 0xe0) | ((CONSOLE_FG_COL >> 3) & 0x1c) | CONSOLE_FG_COL >> 6); fgx = (color8 << 24) | (color8 << 16) | (color8 << 8) | color8; color8 = ((CONSOLE_BG_COL & 0xe0) | ((CONSOLE_BG_COL >> 3) & 0x1c) | CONSOLE_BG_COL >> 6); bgx = (color8 << 24) | (color8 << 16) | (color8 << 8) | color8; break; case GDF_15BIT_555RGB: fgx = (((CONSOLE_FG_COL >> 3) << 26) | ((CONSOLE_FG_COL >> 3) << 21) | ((CONSOLE_FG_COL >> 3) << 16) | ((CONSOLE_FG_COL >> 3) << 10) | ((CONSOLE_FG_COL >> 3) << 5) | (CONSOLE_FG_COL >> 3)); bgx = (((CONSOLE_BG_COL >> 3) << 26) | ((CONSOLE_BG_COL >> 3) << 21) | ((CONSOLE_BG_COL >> 3) << 16) | ((CONSOLE_BG_COL >> 3) << 10) | ((CONSOLE_BG_COL >> 3) << 5) | (CONSOLE_BG_COL >> 3)); break; case GDF_16BIT_565RGB: fgx = (((CONSOLE_FG_COL >> 3) << 27) | ((CONSOLE_FG_COL >> 2) << 21) | ((CONSOLE_FG_COL >> 3) << 16) | ((CONSOLE_FG_COL >> 3) << 11) | ((CONSOLE_FG_COL >> 2) << 5) | (CONSOLE_FG_COL >> 3)); bgx = (((CONSOLE_BG_COL >> 3) << 27) | ((CONSOLE_BG_COL >> 2) << 21) | ((CONSOLE_BG_COL >> 3) << 16) | ((CONSOLE_BG_COL >> 3) << 11) | ((CONSOLE_BG_COL >> 2) << 5) | (CONSOLE_BG_COL >> 3)); break; case GDF_32BIT_X888RGB: fgx = (CONSOLE_FG_COL << 16) | (CONSOLE_FG_COL << 8) | CONSOLE_FG_COL; bgx = (CONSOLE_BG_COL << 16) | (CONSOLE_BG_COL << 8) | CONSOLE_BG_COL; break; case GDF_24BIT_888RGB: fgx = (CONSOLE_FG_COL << 24) | (CONSOLE_FG_COL << 16) | (CONSOLE_FG_COL << 8) | CONSOLE_FG_COL; bgx = (CONSOLE_BG_COL << 24) | (CONSOLE_BG_COL << 16) | (CONSOLE_BG_COL << 8) | CONSOLE_BG_COL; break; } eorx = fgx ^ bgx; #ifdef CONFIG_VIDEO_LOGO //有定义 /* Plot the logo and get start point of console */ printf("Video: Drawing the logo ...\n"); video_console_address = video_logo(); #else video_console_address = video_fb_address; #endif /* Initialize the console */ console_col = 0; console_row = 0; return 0; }@1:video_hw_init 会初始化和屏相关的参数,包括x,y等,这个内容涉及较多了,不再详细讲述了。需要注意一点,大部分的video的移植也在这部分做主要修改。返回了一个GraphicDevice的指针,这个参数基本已经包括了所有的LCD的参数了。
@2:video_fb_address是LCD framebuf的初始写地址。然后根据具体使用的位数来确定fgx以及bgx,具体的我也不清楚了。
@3:不过重要的是怎么drawlogo:
video_console_address = video_logo();
static void *video_logo(void) { char info[128]; int space, len, y_off = 0; #ifdef CONFIG_SPLASH_SCREEN //有定义 char *s; ulong addr; s = getenv("splashimage"); //env 中并没有 if (s != NULL) { int x = 0, y = 0; addr = simple_strtoul(s, NULL, 16); #ifdef CONFIG_SPLASH_SCREEN_ALIGN s = getenv("splashpos"); if (s != NULL) { if (s[0] == 'm') x = BMP_ALIGN_CENTER; else x = simple_strtol(s, NULL, 0); s = strchr(s + 1, ','); if (s != NULL) { if (s[1] == 'm') y = BMP_ALIGN_CENTER; else y = simple_strtol(s + 1, NULL, 0); } } #endif /* CONFIG_SPLASH_SCREEN_ALIGN */ if (video_display_bitmap(addr, x, y) == 0) { video_logo_height = 0; return ((void *) (video_fb_address)); } } #endif /* CONFIG_SPLASH_SCREEN */ /*change the plot of logo*/ logo_plot(video_fb_address, VIDEO_COLS, VIDEO_LOGO_X, VIDEO_LOGO_Y); #ifdef DEBUG_CFB_CONSOLE_LCD sprintf(info, " %s"," " ); #else sprintf(info, " %s", version_string); #endif space = (VIDEO_LINE_LEN / 2 - VIDEO_INFO_X) / VIDEO_FONT_WIDTH; len = strlen(info); if (len > space) { video_drawchars(VIDEO_INFO_X, VIDEO_INFO_Y, (uchar *) info, space); video_drawchars(VIDEO_INFO_X + VIDEO_FONT_WIDTH, VIDEO_INFO_Y + VIDEO_FONT_HEIGHT, (uchar *) info + space, len - space); y_off = 1; } else video_drawstring(VIDEO_INFO_X, VIDEO_INFO_Y, (uchar *) info); #ifdef CONFIG_CONSOLE_EXTRA_INFO { int i, n = ((video_logo_height - VIDEO_FONT_HEIGHT) / VIDEO_FONT_HEIGHT); for (i = 1; i < n; i++) { video_get_info_str(i, info); if (!*info) continue; len = strlen(info); if (len > space) { video_drawchars(VIDEO_INFO_X, VIDEO_INFO_Y + (i + y_off) * VIDEO_FONT_HEIGHT, (uchar *) info, space); y_off++; video_drawchars(VIDEO_INFO_X + VIDEO_FONT_WIDTH, VIDEO_INFO_Y + (i + y_off) * VIDEO_FONT_HEIGHT, (uchar *) info + space, len - space); } else { video_drawstring(VIDEO_INFO_X, VIDEO_INFO_Y + (i + y_off) * VIDEO_FONT_HEIGHT, (uchar *) info); } } } #endif return (video_fb_address + video_logo_height * VIDEO_LINE_LEN); } #endif
@1:画图:logo_plot 整体思路就是将对应的logo变化成16进制的数组,这里对应的数组是bmp_logo_palette,那么如何将图转变成数组好接下来讲具体步骤:
#ifdef CONFIG_VIDEO_LOGO void logo_plot(void *screen, int width, int x, int y) { int xcount, i; int skip = (width - VIDEO_LOGO_WIDTH) * VIDEO_PIXEL_SIZE; int ycount = video_logo_height; unsigned char r, g, b, *logo_red, *logo_blue, *logo_green; unsigned char *source; unsigned char *dest = (unsigned char *) screen + ((y * width * VIDEO_PIXEL_SIZE) + x * VIDEO_PIXEL_SIZE); printf("carl_wang->logo_plot\n"); #ifdef CONFIG_VIDEO_BMP_LOGO source = bmp_logo_bitmap; /* Allocate temporary space for computing colormap */ logo_red = malloc(BMP_LOGO_COLORS); logo_green = malloc(BMP_LOGO_COLORS); logo_blue = malloc(BMP_LOGO_COLORS); /* Compute color map */ for (i = 0; i < VIDEO_LOGO_COLORS; i++) { logo_red[i] = (bmp_logo_palette[i] & 0x0f00) >> 4; logo_green[i] = (bmp_logo_palette[i] & 0x00f0); logo_blue[i] = (bmp_logo_palette[i] & 0x000f) << 4; } #else source = linux_logo; logo_red = linux_logo_red; logo_green = linux_logo_green; logo_blue = linux_logo_blue; #endif if (VIDEO_DATA_FORMAT == GDF__8BIT_INDEX) { for (i = 0; i < VIDEO_LOGO_COLORS; i++) { video_set_lut(i + VIDEO_LOGO_LUT_OFFSET, logo_red[i], logo_green[i], logo_blue[i]); } } while (ycount--) { #if defined(VIDEO_FB_16BPP_PIXEL_SWAP) int xpos = x; #endif xcount = VIDEO_LOGO_WIDTH; while (xcount--) { r = logo_red[*source - VIDEO_LOGO_LUT_OFFSET]; g = logo_green[*source - VIDEO_LOGO_LUT_OFFSET]; b = logo_blue[*source - VIDEO_LOGO_LUT_OFFSET]; switch (VIDEO_DATA_FORMAT) { case GDF__8BIT_INDEX: *dest = *source; break; case GDF__8BIT_332RGB: *dest = ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6); break; case GDF_15BIT_555RGB: #if defined(VIDEO_FB_16BPP_PIXEL_SWAP) fill_555rgb_pswap(dest, xpos++, r, g, b); #else *(unsigned short *) dest = SWAP16((unsigned short) ( ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3))); #endif break; case GDF_16BIT_565RGB: *(unsigned short *) dest = SWAP16((unsigned short) ( ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3))); break; case GDF_32BIT_X888RGB: *(unsigned long *) dest = SWAP32((unsigned long) ( (r << 16) | (g << 8) | b)); break; case GDF_24BIT_888RGB: #ifdef VIDEO_FB_LITTLE_ENDIAN dest[0] = b; dest[1] = g; dest[2] = r; #else dest[0] = r; dest[1] = g; dest[2] = b; #endif break; } source++; dest += VIDEO_PIXEL_SIZE; } dest += skip; } #ifdef CONFIG_VIDEO_BMP_LOGO free(logo_red); free(logo_green); free(logo_blue); #endif }@1:在tools/logos下面有放置一张.jpg的图,然后webee提供了一个脚本可以将.jpg转为.bmp.好公布脚本
#!/bin/sh #install Netpbm first jpegtopnm $1 | ppmquant 31 | ppmtobmp -bpp 8 > $2
@2:转成bmp图之后将其拷贝到tools下,然后有一个bmp_logo的可执行文件,其对应的代码是bmp_logo.c,对应的指令是./bmp_logo ./logos/webee210.bmp > 1.txt,其生成的数组就在1.txt中了,将其全部替换写入Bmp_logo.h (include)
@3ok,大功告成,看看你的图片修改成功没。
@4 其实我还是遇到了一些不顺心的事就是我的图片很大,但是显示的时候发现照片数据显示不完整,所以百度了一番,发现是bmp_logo.c的问题,uboot是轻量化的启动,支持较小图片的显示,不过我还是找到了,可以参照http://blog.csdn.net/lutao614/article/details/17507951中说的,进行修改,但是我的图片好像是实在太大了,虽然可以显示全了,但是发现图片有移位的现象,像是生成的bmp数组越界之后重新来过了。这个我就不再考虑了。假如哪天显示全了我再重新写个博客。
@5最后返回了新的framebuf的地址
返回到drv_video_init的函数中,此时又新建了一个console_dev的实例化设备并将其填入到devs的链表中,同时初始化对应的函数指针。
/* Init vga device */ memset(&console_dev, 0, sizeof(console_dev)); strcpy(console_dev.name, "vga"); console_dev.ext = DEV_EXT_VIDEO; /* Video extensions */ console_dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_SYSTEM; console_dev.putc = video_putc; /* 'putc' function */ console_dev.puts = video_puts; /* 'puts' function */ console_dev.tstc = NULL; /* 'tstc' function */ console_dev.getc = NULL; /* 'getc' function */ #if !defined(CONFIG_VGA_AS_SINGLE_DEVICE) /* Also init console device */ console_dev.flags |= DEV_FLAGS_INPUT; console_dev.tstc = VIDEO_TSTC_FCT; /* 'tstc' function */ console_dev.getc = VIDEO_GETC_FCT; /* 'getc' function */ #endif /* CONFIG_VGA_AS_SINGLE_DEVICE */ if (stdio_register(&console_dev) != 0) return 0; /* Return success */ return 1;
好往下:
drv_system_init ();其实没有干什么,将一个叫做serial的dev放入到devs的链表中,并将其指针初始化
static void drv_system_init (void) { struct stdio_dev dev; memset (&dev, 0, sizeof (dev)); strcpy (dev.name, "serial"); dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; dev.putc = serial_putc; dev.puts = serial_puts; dev.getc = serial_getc; dev.tstc = serial_tstc; stdio_register (&dev); #ifdef CONFIG_SYS_DEVICE_NULLDEV //未定义 memset (&dev, 0, sizeof (dev)); strcpy (dev.name, "nulldev"); dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; dev.putc = nulldev_putc; dev.puts = nulldev_puts; dev.getc = nulldev_input; dev.tstc = nulldev_input; stdio_register (&dev); #endif }
serial_stdio_init ();
这个函数实际上是将之前注册在serial_devices链表中的串口设备全部注册在了devs的链表中了。
void serial_stdio_init (void) { struct stdio_dev dev; struct serial_device *s = serial_devices; while (s) { memset (&dev, 0, sizeof (dev)); strcpy (dev.name, s->name); dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT; dev.start = s->init; dev.stop = s->uninit; dev.putc = s->putc; dev.puts = s->puts; dev.getc = s->getc; dev.tstc = s->tstc; stdio_register (&dev); s = s->next; } }
好 stdio_init(); 已经结束了
回到Board.c (arch\arm\lib)
jumptable_init();
申请空间并初始化了一部分的常用函数额跳转表
void jumptable_init(void) { gd->jt = malloc(XF_MAX * sizeof(void *)); #include <_exports.h> }
_exports.h
EXPORT_FUNC(get_version) EXPORT_FUNC(getc) EXPORT_FUNC(tstc) EXPORT_FUNC(putc) EXPORT_FUNC(puts) EXPORT_FUNC(printf) EXPORT_FUNC(install_hdlr) EXPORT_FUNC(free_hdlr) EXPORT_FUNC(malloc) EXPORT_FUNC(free) EXPORT_FUNC(udelay) EXPORT_FUNC(get_timer) EXPORT_FUNC(vprintf) EXPORT_FUNC(do_reset) EXPORT_FUNC(getenv) EXPORT_FUNC(setenv) EXPORT_FUNC(simple_strtoul) EXPORT_FUNC(strict_strtoul) EXPORT_FUNC(simple_strtol) EXPORT_FUNC(strcmp) EXPORT_FUNC(i2c_write) EXPORT_FUNC(i2c_read) EXPORT_FUNC(spi_init) EXPORT_FUNC(spi_setup_slave) EXPORT_FUNC(spi_free_slave) EXPORT_FUNC(spi_claim_bus) EXPORT_FUNC(spi_release_bus) EXPORT_FUNC(spi_xfer)
console_init_r 其实是将stdin,stdout,stderr 的函数重定向了,重定向到了名为serial 的设备上。
int console_init_r(void) { char *stdinname, *stdoutname, *stderrname; struct stdio_dev *inputdev = NULL, *outputdev = NULL, *errdev = NULL; #ifdef CONFIG_SYS_CONSOLE_ENV_OVERWRITE int i; #endif /* CONFIG_SYS_CONSOLE_ENV_OVERWRITE */ #ifdef CONFIG_CONSOLE_MUX int iomux_err = 0; #endif /* set default handlers at first */ gd->jt[XF_getc] = serial_getc; gd->jt[XF_tstc] = serial_tstc; gd->jt[XF_putc] = serial_putc; gd->jt[XF_puts] = serial_puts; gd->jt[XF_printf] = serial_printf; /* stdin stdout and stderr are in environment */ /* scan for it */ stdinname = getenv("stdin"); stdoutname = getenv("stdout"); stderrname = getenv("stderr"); if (OVERWRITE_CONSOLE == 0) { /* if not overwritten by config switch */ inputdev = search_device(DEV_FLAGS_INPUT, stdinname); outputdev = search_device(DEV_FLAGS_OUTPUT, stdoutname); errdev = search_device(DEV_FLAGS_OUTPUT, stderrname); #ifdef CONFIG_CONSOLE_MUX iomux_err = iomux_doenv(stdin, stdinname); iomux_err += iomux_doenv(stdout, stdoutname); iomux_err += iomux_doenv(stderr, stderrname); if (!iomux_err) /* Successful, so skip all the code below. */ goto done; #endif } /* if the devices are overwritten or not found, use default device */ if (inputdev == NULL) { inputdev = search_device(DEV_FLAGS_INPUT, "serial"); } if (outputdev == NULL) { outputdev = search_device(DEV_FLAGS_OUTPUT, "serial"); } if (errdev == NULL) { errdev = search_device(DEV_FLAGS_OUTPUT, "serial"); } /* Initializes output console first */ if (outputdev != NULL) { /* need to set a console if not done above. */ console_doenv(stdout, outputdev); } if (errdev != NULL) { /* need to set a console if not done above. */ console_doenv(stderr, errdev); } if (inputdev != NULL) { /* need to set a console if not done above. */ console_doenv(stdin, inputdev); } #ifdef CONFIG_CONSOLE_MUX done: #endif gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */ stdio_print_current_devices(); #ifdef CONFIG_SYS_CONSOLE_ENV_OVERWRITE //未定义<span style="white-space:pre"> </span> /* set the environment variables (will overwrite previous env settings) */ for (i = 0; i < 3; i++) { setenv(stdio_names[i], stdio_devices[i]->name); } #endif /* CONFIG_SYS_CONSOLE_ENV_OVERWRITE */ #if 0 /* If nothing usable installed, use only the initial console */ if ((stdio_devices[stdin] == NULL) && (stdio_devices[stdout] == NULL)) return 0; #endif return 0; }@1:首先从env中获取stdin,stdout,stderr 的环境变量,但我找了半天没找到,但是全局搜索了一遍,发现有野生的。在webee21.h中定义的,哈哈,stdin,stderr是在serial上的,但是呢stdout是vga上的
#ifdef DEBUG_CFB_CONSOLE_LCD #define DEBUG_CFB_CONSOLE #define CONFIG_EXTRA_ENV_SETTINGS \ "stdin=serial\0" \ "stdout=vga\0" \ "stderr=serial\0" \ "" #define VIDEO_LOGO_X 180 #define VIDEO_LOGO_Y 18 #else #define VIDEO_LOGO_X 180 #define VIDEO_LOGO_Y 120 #endif@2:当找到对应的名称后,会调用console_doenv将以下的重新赋值
gd->jt[XF_getc] = serial_getc; gd->jt[XF_tstc] = serial_tstc; gd->jt[XF_putc] = serial_putc; gd->jt[XF_puts] = serial_puts; gd->jt[XF_printf] = serial_printf;@3:可以看到从这个函数之后,所有的log信息都打印在了LCD上了。
往下了:
/* Perform network card initialisation if necessary */ #if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96) //未定义 /* XXX: this needs to be moved to board init */ if (getenv("ethaddr")) { uchar enetaddr[6]; eth_getenv_enetaddr("ethaddr", enetaddr); smc_set_mac_addr(enetaddr); } #endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */ /* Initialize from environment */ s = getenv("loadaddr"); if (s != NULL) load_addr = simple_strtoul(s, NULL, 16); #if defined(CONFIG_CMD_NET) //未定义 s = getenv("bootfile"); if (s != NULL) copy_filename(BootFile, s, sizeof(BootFile)); #endif #ifdef BOARD_LATE_INIT board_late_init(); //暂时不知道什么意思 #endif #ifdef CONFIG_BITBANGMII bb_miiphy_init(); #endif #if defined(CONFIG_CMD_NET) #if defined(CONFIG_NET_MULTI) puts("Net: "); #endif eth_initialize(gd->bd); puts("\n "); puts("\n"); #if defined(CONFIG_RESET_PHY_R) //未定义 debug("Reset Ethernet PHY\n"); reset_phy(); #endif #endif #ifdef CONFIG_POST //未定义 post_run(NULL, POST_RAM | post_bootmode_get(0)); #endif@1:获取env中的loadaddr,虽然env中有定义,但是呢config不惯用,所以直接跳过了。
@2:board_late_init();这两个幻数搞的我有点蒙圈了,希望有人指点
int board_late_init (void) { uint *magic = (uint*)(PHYS_SDRAM_1); char boot_cmd[100]; if ((0x24564236 == magic[0]) && (0x20764316 == magic[1])) { printf("carl_Wang haha\n"); sprintf(boot_cmd, "nand erase 0 40000;nand write %08x 0 40000", PHYS_SDRAM_1 + 0x8000); magic[0] = 0; magic[1] = 0; printf("\nready for self-burning U-Boot image\n\n"); setenv("bootdelay", "0"); setenv("bootcmd", boot_cmd); } return 0; }@3:接下来就是网络的初始化
int eth_initialize(bd_t *bis) { int eth_number = 0; eth_devices = NULL; eth_current = NULL; show_boot_progress (64); #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) miiphy_init(); #endif #ifdef CONFIG_PHYLIB phy_init(); #endif /* * If board-specific initialization exists, call it. * If not, call a CPU-specific one */ if (board_eth_init != __def_eth_init) { if (board_eth_init(bis) < 0) printf("Board Net Initialization Failed\n"); } else if (cpu_eth_init != __def_eth_init) { if (cpu_eth_init(bis) < 0) printf("CPU Net Initialization Failed\n"); } else printf("Net Initialization Skipped\n"); #if defined(CONFIG_DB64360) || defined(CONFIG_CPCI750) mv6436x_eth_initialize(bis); #endif #if defined(CONFIG_DB64460) || defined(CONFIG_P3Mx) mv6446x_eth_initialize(bis); #endif if (!eth_devices) { puts ("No ethernet found.\n"); show_boot_progress (-64); } else { struct eth_device *dev = eth_devices; char *ethprime = getenv ("ethprime"); show_boot_progress (65); do { if (eth_number) puts (", "); printf("%s", dev->name); if (ethprime && strcmp (dev->name, ethprime) == 0) { eth_current = dev; puts (" [PRIME]"); } if (strchr(dev->name, ' ')) puts("\nWarning: eth device name has a space!\n"); //printf("eth_number = %d\n",eth_number); /***Modified by lk ***/ if (eth_write_hwaddr(dev, "eth", eth_number)) puts("Warning: failed to set MAC address\n"); eth_number++; dev = dev->next; } while(dev != eth_devices); eth_current_changed(); putc ('\n'); } return eth_number; }网络这方面不是太清楚,这段略过了,但是基本可以肯定的是关于网络芯片基本移植和修改的地方就在此地。
接着往下:
#if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER) //未定义 /* * Export available size of memory for Linux, * taking into account the protected RAM at top of memory */ { ulong pram; uchar memsz[32]; #ifdef CONFIG_PRAM //未定义 char *s; s = getenv("pram"); if (s != NULL) pram = simple_strtoul(s, NULL, 10); else pram = CONFIG_PRAM; #else pram = 0; #endif #ifdef CONFIG_LOGBUFFER #ifndef CONFIG_ALT_LB_ADDR /* Also take the logbuffer into account (pram is in kB) */ pram += (LOGBUFF_LEN + LOGBUFF_OVERHEAD) / 1024; #endif #endif sprintf((char *)memsz, "%ldk", (gd->ram_size / 1024) - pram); setenv("mem", (char *)memsz); } #endif /* main_loop() can return to retry autoboot, if so just run it again. */ for (;;) { main_loop(); 主函数的死循环,这个就是等待用户输入指令了。 } /* NOTREACHED - no way out of command loop except booting */ }main_loop 同时倒计时并等待用户输入
void main_loop (void) { #ifndef CONFIG_SYS_HUSH_PARSER //有定义,所以不跑 static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; int len; int rc = 1; int flag; #endif #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) //有定义启动延时 char *s; int bootdelay; #endif #ifdef CONFIG_PREBOOT //未定义 char *p; #endif #ifdef CONFIG_BOOTCOUNT_LIMIT //未定义 unsigned long bootcount = 0; unsigned long bootlimit = 0; char *bcs; char bcs_set[16]; #endif /* CONFIG_BOOTCOUNT_LIMIT */ #ifdef CONFIG_BOOTCOUNT_LIMIT //未定义 bootcount = bootcount_load(); bootcount++; bootcount_store (bootcount); sprintf (bcs_set, "%lu", bootcount); setenv ("bootcount", bcs_set); bcs = getenv ("bootlimit"); bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0; #endif /* CONFIG_BOOTCOUNT_LIMIT */ #ifdef CONFIG_MODEM_SUPPORT .//未定义 debug ("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init); if (do_mdm_init) { char *str = strdup(getenv("mdm_cmd")); setenv ("preboot", str); /* set or delete definition */ if (str != NULL) free (str); mdm_init(); /* wait for modem connection */ } #endif /* CONFIG_MODEM_SUPPORT */ #ifdef CONFIG_VERSION_VARIABLE //未定义 { setenv ("ver", version_string); /* set version variable */ } #endif /* CONFIG_VERSION_VARIABLE */ #ifdef CONFIG_SYS_HUSH_PARSER u_boot_hush_start (); #endif #if defined(CONFIG_HUSH_INIT_VAR) //未定义 hush_init_var (); #endif #ifdef CONFIG_PREBOOT //未定义 if ((p = getenv ("preboot")) != NULL) { # ifdef CONFIG_AUTOBOOT_KEYED int prev = disable_ctrlc(1); /* disable Control C checking */ # endif # ifndef CONFIG_SYS_HUSH_PARSER run_command (p, 0); # else parse_string_outer(p, FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP); # endif # ifdef CONFIG_AUTOBOOT_KEYED disable_ctrlc(prev); /* restore Control C checking */ # endif } #endif /* CONFIG_PREBOOT */ #if defined(CONFIG_UPDATE_TFTP) //未定义 update_tftp (0UL); #endif /* CONFIG_UPDATE_TFTP */ #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) s = getenv ("bootdelay"); bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; //<span style="font-family: Arial, Helvetica, sans-serif;">bootdelay = </span><span style="font-family: Arial, Helvetica, sans-serif;">CONFIG_BOOTDELAY = 5</span><span style="font-family: Arial, Helvetica, sans-serif;"> </span> debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay); # ifdef CONFIG_BOOT_RETRY_TIME //未定义 init_cmd_timeout (); # endif /* CONFIG_BOOT_RETRY_TIME */ #ifdef CONFIG_POST //未定义 if (gd->flags & GD_FLG_POSTFAIL) { s = getenv("failbootcmd"); } else #endif /* CONFIG_POST */ #ifdef CONFIG_BOOTCOUNT_LIMIT <span style="font-family: Arial, Helvetica, sans-serif;">//未定义</span> if (bootlimit && (bootcount > bootlimit)) { printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n", (unsigned)bootlimit); s = getenv ("altbootcmd"); } else #endif /* CONFIG_BOOTCOUNT_LIMIT */ s = getenv ("bootcmd"); //@2 debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); if (bootdelay >= 0 && s && !abortboot (bootdelay)) { board_video_reset(); # ifdef CONFIG_AUTOBOOT_KEYED //未定义 int prev = disable_ctrlc(1); /* disable Control C checking */ # endif # ifndef CONFIG_SYS_HUSH_PARSER run_command (s, 0); # else printf("carl_wang->%s\n",s); parse_string_outer(s, FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP); # endif # ifdef CONFIG_AUTOBOOT_KEYED disable_ctrlc(prev); /* restore Control C checking */ # endif } printf("carl_wang->%d\n",bootdelay); //run_command("sound",0); run_command("menu",0); # ifdef CONFIG_MENUKEY if (menukey == CONFIG_MENUKEY) { s = getenv("menucmd"); if (s) { # ifndef CONFIG_SYS_HUSH_PARSER run_command(s, 0); # else parse_string_outer(s, FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP); # endif } } #endif /* CONFIG_MENUKEY */ #endif /* CONFIG_BOOTDELAY */ /* * Main Loop for Monitor Command Processing */ #ifdef CONFIG_SYS_HUSH_PARSER parse_file_outer(); /* This point is never reached */ for (;;); board_video_reset(); #else for (;;) { #ifdef CONFIG_BOOT_RETRY_TIME if (rc >= 0) { /* Saw enough of a valid command to * restart the timeout. */ reset_cmd_timeout(); } #endif len = readline (CONFIG_SYS_PROMPT); flag = 0; /* assume no special flags for now */ if (len > 0) strcpy (lastcommand, console_buffer); else if (len == 0) flag |= CMD_FLAG_REPEAT; #ifdef CONFIG_BOOT_RETRY_TIME else if (len == -2) { /* -2 means timed out, retry autoboot */ puts ("\nTimed out waiting for command\n"); # ifdef CONFIG_RESET_TO_RETRY /* Reinit board to run initialization code again */ do_reset (NULL, 0, 0, NULL); # else return; /* retry autoboot */ # endif } #endif if (len == -1) puts ("<INTERRUPT>\n"); else rc = run_command (lastcommand, flag); if (rc <= 0) { /* invalid command or not repeatable, forget it */ lastcommand[0] = 0; } } #endif /*CONFIG_SYS_HUSH_PARSER*/ }
@1:u_boot_hush_start 初始化了一个top_vars全局变量。
int u_boot_hush_start(void) { if (top_vars == NULL) { top_vars = malloc(sizeof(struct variables)); top_vars->name = "HUSH_VERSION"; top_vars->value = "0.01"; top_vars->next = 0; top_vars->flg_export = 0; top_vars->flg_read_only = 1; #ifdef CONFIG_NEEDS_MANUAL_RELOC u_boot_hush_reloc(); #endif } return 0; }
@2:从env中读取bootcmd,发现有定义的,"bootcmd=" CONFIG_BOOTCOMMAND
"nand read 0x30007fc0 0x100000 0x500000;bootm 0x30007fc0\0"
s = getenv ("bootcmd"); @2 debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); if (bootdelay >= 0 && s && !abortboot (bootdelay)) { board_video_reset();
那么真正的是在abortboot (bootdelay)做文章。
static inline int abortboot(int bootdelay) { int abort = 0; #ifdef CONFIG_MENUPROMPT printf(CONFIG_MENUPROMPT); #else printf("Hit any key to stop autoboot: %2d ", bootdelay); #endif #ifdef CONFIG_UBOOT_KEY //run_command("drawstring 280 330 hit-S1-to-burn-*.img..........",0); udelay(10000); udelay(10000); udelay(10000); if(0 == check_key(0)) { abort =1 ; } #endif #if defined CONFIG_ZERO_BOOTDELAY_CHECK /* * Check if key already pressed * Don't check if bootdelay < 0 */ if (bootdelay >= 0) { if (tstc()) { /* we got a key press */ (void) getc(); /* consume input */ puts ("\b\b\b 0"); abort = 1; /* don't auto boot */ } } #endif while ((bootdelay > 0) && (!abort)) { int i; --bootdelay; /* delay 100 * 10ms */ #ifdef CONFIG_UBOOT_KEY if(0 == check_key(0)) { abort =1 ; } #endif for (i=0; !abort && i<100; ++i) { if (tstc()) { /* we got a key press */ abort = 1; /* don't auto boot */ bootdelay = 0; /* no more delay */ #ifdef CONFIG_MENUKEY menukey = getc(); #else (void) getc(); /* consume input */ # endif break; } udelay(10000); } printf("\b\b\b%2d ", bootdelay); } putc('\n'); #ifdef CONFIG_SILENT_CONSOLE if (abort) gd->flags &= ~GD_FLG_SILENT; #endif return abort; } # endif /* CONFIG_AUTOBOOT_KEYED */ #endif /* CONFIG_BOOTDELAY >= 0 */
首先延时了30ms,然后检测是否有键按下,有的话 abort=1;或者30ms之后是否有终端有消息有的话,abort = 1;然后跳入while循环,wihle循环中一直检测是否有串口数据。有的话直接跳出后返回abort=1;没有的话是abort = 0;
有两种情况:
1.倒计时完成仍然没有数据:将s的启动指令传递到parse_string_outer,指令是 nand read 0x30007fc0 0x100000 0x500000;bootm 0x30007fc0,
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
这个其实会传递执行两个命令,一个是将linux 的镜像读取到0x30007fc0,然后从0x30007fc0启动,其中要讲一下关于bootm的命令会干一件事情将我们之前的gd参数打包并传递到linux内核,内核完成解析。
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { ulong iflag; ulong load_end = 0; int ret; boot_os_fn *boot_fn; #ifdef CONFIG_NEEDS_MANUAL_RELOC static int relocated = 0; /* relocate boot function table */ if (!relocated) { int i; for (i = 0; i < ARRAY_SIZE(boot_os); i++) if (boot_os[i] != NULL) boot_os[i] += gd->reloc_off; relocated = 1; } #endif /* determine if we have a sub command */ if (argc > 1) { char *endp; simple_strtoul(argv[1], &endp, 16); /* endp pointing to NULL means that argv[1] was just a * valid number, pass it along to the normal bootm processing * * If endp is ':' or '#' assume a FIT identifier so pass * along for normal processing. * * Right now we assume the first arg should never be '-' */ if ((*endp != 0) && (*endp != ':') && (*endp != '#')) return do_bootm_subcommand(cmdtp, flag, argc, argv); } if (bootm_start(cmdtp, flag, argc, argv)) return 1; /* * We have reached the point of no return: we are going to * overwrite all exception vector code, so we cannot easily * recover from any failures any more... */ iflag = disable_interrupts(); #if defined(CONFIG_CMD_USB) /* * turn off USB to prevent the host controller from writing to the * SDRAM while Linux is booting. This could happen (at least for OHCI * controller), because the HCCA (Host Controller Communication Area) * lies within the SDRAM and the host controller writes continously to * this area (as busmaster!). The HccaFrameNumber is for example * updated every 1 ms within the HCCA structure in SDRAM! For more * details see the OpenHCI specification. */ usb_stop(); #endif ret = bootm_load_os(images.os, &load_end, 1); if (ret < 0) { if (ret == BOOTM_ERR_RESET) do_reset (cmdtp, flag, argc, argv); if (ret == BOOTM_ERR_OVERLAP) { if (images.legacy_hdr_valid) { if (image_get_type (&images.legacy_hdr_os_copy) == IH_TYPE_MULTI) puts ("WARNING: legacy format multi component " "image overwritten\n"); } else { puts ("ERROR: new format image overwritten - " "must RESET the board to recover\n"); show_boot_progress (-113); do_reset (cmdtp, flag, argc, argv); } } if (ret == BOOTM_ERR_UNIMPLEMENTED) { if (iflag) enable_interrupts(); show_boot_progress (-7); return 1; } } lmb_reserve(&images.lmb, images.os.load, (load_end - images.os.load)); if (images.os.type == IH_TYPE_STANDALONE) { if (iflag) enable_interrupts(); /* This may return when 'autostart' is 'no' */ bootm_start_standalone(iflag, argc, argv); return 0; } show_boot_progress (8); #ifdef CONFIG_SILENT_CONSOLE if (images.os.os == IH_OS_LINUX) fixup_silent_linux(); #endif boot_fn = boot_os[images.os.os]; if (boot_fn == NULL) { if (iflag) enable_interrupts(); printf ("ERROR: booting os '%s' (%d) is not supported\n", genimg_get_os_name(images.os.os), images.os.os); show_boot_progress (-8); return 1; } arch_preboot_os(); boot_fn(0, argc, argv, &images); show_boot_progress (-9); #ifdef DEBUG puts ("\n## Control returned to monitor - resetting...\n"); #endif do_reset (cmdtp, flag, argc, argv); return 1; }
因为要启动linux,所以最后调用的是do_bootm_linux。
int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images) { bd_t *bd = gd->bd; char *s; int machid = bd->bi_arch_number; void (*kernel_entry)(int zero, int arch, uint params); #ifdef CONFIG_CMDLINE_TAG char *commandline = getenv ("bootargs"); #endif if ((flag != 0) && (flag != BOOTM_STATE_OS_GO)) return 1; s = getenv ("machid"); if (s) { machid = simple_strtoul (s, NULL, 16); printf ("Using machid 0x%x from environment\n", machid); } show_boot_progress (15); #ifdef CONFIG_OF_LIBFDT if (images->ft_len) return bootm_linux_fdt(machid, images); #endif kernel_entry = (void (*)(int, int, uint))images->ep; debug ("## Transferring control to Linux (at address %08lx) ...\n", (ulong) kernel_entry); #if defined (CONFIG_SETUP_MEMORY_TAGS) || \ defined (CONFIG_CMDLINE_TAG) || \ defined (CONFIG_INITRD_TAG) || \ defined (CONFIG_SERIAL_TAG) || \ defined (CONFIG_REVISION_TAG) setup_start_tag (bd); #ifdef CONFIG_SERIAL_TAG setup_serial_tag (¶ms); #endif #ifdef CONFIG_REVISION_TAG setup_revision_tag (¶ms); #endif #ifdef CONFIG_SETUP_MEMORY_TAGS setup_memory_tags (bd); #endif #ifdef CONFIG_CMDLINE_TAG setup_commandline_tag (bd, commandline); #endif #ifdef CONFIG_INITRD_TAG if (images->rd_start && images->rd_end) setup_initrd_tag (bd, images->rd_start, images->rd_end); #endif setup_end_tag(bd); #endif announce_and_cleanup(); kernel_entry(0, machid, bd->bi_boot_params); /* does not return */ return 1; }下面是网峰写的《》
当 内 核 启 动 时 , 启 动 参 数 一 般 是 从 Bootloader 中 传 递 而 来 的 。 那 么 Bootloader 传递参数的存放地址和参数的数据结构就都是需要关心的。 由于 Bootloader 和内核不是同时启动运行的,因此 Bootloader 要向内核传 递参数只有将参数存放在一个指定的地址,然后内核再从这个地址中读取启动参 数。 在 U-Boot 中,传递参数的数据结构是以标记的形式来体现的。而参数的传 递通过标记列表的形式来实现。标记列表由 ATAG_CORE 开始,以 ATAG_NONE 标记结束。这里的 ATAG_CORE,ATAG_NONE 是各个参数的标记,本身是一 个 32 位值。标记的数据结构为 tag,它由一个 tag_header 结构体和一个联合体 组成。tag_header 结构体表示标记的长度和类型,比如是表示内存还是命令行 参数。对于不同类型的标记使用不同的联合体。对于 tag 和 tag_header 的定义 可以在网蜂提供的 U-Boot 的源码中 arch/arm/include/asm/setup.h 文件中找到, 即: struct tag { struct tag_header hdr; union { struct tag_core core; struct tag_mem32 mem; struct tag_videotext videotext; struct tag_ramdisk ramdisk; struct tag_initrd initrd; struct tag_serialnr serialnr; struct tag_revision revision; struct tag_videolfb videolfb; struct tag_cmdline cmdline; struct tag_acorn acorn; struct tag_memclk memclk; } u; 177}; 除 了 ATAG_CORE 和 ATAG_NONE 之外,其他的参数标记 还包括: ATAG_MEM,ATAG_COMDLINE 和 ATAG_INITRD2 等。每个参数标记就代表 一个参数结构体,由各个参数结构体构成了标记列表。 在网蜂提供的 U-Boot 的源码中 arch/arm/lib/bootm.c 文件中有以下代码,说 明参数是如何传递的。 static void setup_start_tag (bd_t *bd) { /* 参数存放地址 */ params = (struct tag *) bd->bi_boot_params; /* 标记 ATAG_CORE 开始 */ 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 指向下一个标记 */ params = tag_next (params); } static void setup_memory_tags (bd_t *bd) { int i; for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { /* 设置内存标记 */ params->hdr.tag = ATAG_MEM; params->hdr.size = tag_size (tag_mem32); /* 设置内存起始地址 */ params->u.mem.start = bd->bi_dram[i].start; /* 设置内存大小 */ params->u.mem.size = bd->bi_dram[i].size; /* 使 params 指向下一个标记 */ params = tag_next (params); } } static void setup_end_tag (bd_t *bd) { params->hdr.tag = ATAG_NONE; params->hdr.size = 0; } /* 标记 ATAG_NONE 结束 */ 1当设置好这些参数之后呢,会进入OS,kernel_entry(0, machid, bd->bi_boot_params);就是OS的入口地址,然后就永远不会来了。
kernel_entry(0, machid, bd->bi_boot_params);
2:倒计时中间有终端数据回来,执行menu的cmd
run_command("menu",0);
这就是一个简单的cmd的实现,具体实现我摘抄了网峰上的部分东西。
一、 制作 hello 命令
1. 创建 U-Boot 目录下的 common 目录中的 cmd_hello.c 文件,用命令:
vim common/cmd_hello.c
2. 在 cmd_hello.c 中添加下图内容,然后保存:
249#include <common.h>
#include <command.h>
#include <image.h>
#include <u-boot/zlib.h>
#include <bzlib.h>
#include <environment.h>
#include <lmb.h>
#include <linux/ctype.h>
#include <asm/byteorder.h>
#include <linux/compiler.h>
int do_hello(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
{
printf("\nHello world\n\n");
}
U_BOOT_CMD(hello, 4, 1, do_hello,
"test:hello",
"test:hello"
);
上面 U_BOOT_CMD 其实是一个宏,它在 include/command.h 头文件中被定义:
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) /
cmd_tbl_t __u_boot_cmd_##name Struct_Section =
{#name, maxargs, rep, cmd, usage, help
}
U_BOOT_CMD 中有四个参数:
name 命令的名字。
maxargs 命令执行函数中的最大参数个数。
rep 重复数。
cmd 命令执行函数名字。
usage命令用法信息。
help命令帮助信息。
也就是当我们在 U-Boot 中输入 hello,U-Boot 就会执行 do_hello 函数。
3. 打开 common 目录下的 Makefile 文件,用命令:
vim common/Makefile
4. 在 common/Makefile 文件中添加以下一行:
COBJS-y += cmd_hello.o
/* 在第 29 行添加 */
这样就能在编译 U-Boot 时,把 cmd_hello.c 编译成 cmd_hello.o,再链接到
最终生成的 bin 文件中。
5. 编译,用命令:
make webee210_config
make
把生成的 webee210-uboot.bin 烧到 SD 卡上(参考使用手册的烧写说明),然
后插到开发板,从 SD 方式启动开发板,在 U-Boot 上输入 help 命令:
可以看到,hello 命令已经存在。
好了,uboot的整体大概流程分析已经步入尾声了,还是有很多地方不懂,但是起码懂得流程后就知道哪儿有问题,到哪里去找,然后一头扎进去,借助log,应该很快就能找到原因。
不忘初心,方得始终。
相关文章推荐
- SQL关联查询
- tomact虚拟目录,虚拟主机,http请求头,相应头
- 1、Servlet 2、ServletConfig 3、ServletContext 4、HttpUrlConnection
- 蓝懿iOS 技术内容和心得 12.21
- MAC怎么获取文件路径
- SQL基础查询
- 程序员如何打造个人品牌
- HttpURLConnection详解
- 从启动的activity中获得结果
- 【程序员】项目经理如何调动组员积极性
- HttpServletResponse HttpServletRequest RequestDispatcher
- 多线程基础(五)NSThread线程通信
- xargs的理解与简单应用
- navicat cannot create file 文件名、目录名或卷标语法不正确 解决方法
- jQuery操作json
- 记录我这一年的技术之路(nodejs纯干货)
- 机器学习公开课笔记(3):Logistic回归
- 地球内层的成因
- kali 2.0更新源
- 快速幂为什么这么快