Exynos4412 Uboot 移植 1----6

转载自http://blog.csdn.net/zqixiao_09/article/details/50805205

11111111111111111111111111111

Uboot 所用版本 u-boot-2013.01

      u-boot-2013.01 中有上千文件,要想了解对于某款开发板,使用哪些文件、哪些文件首先执行、可执行文件占用内存的情况,最好的方法就是阅读它的Makefile。

 

根据顶层Readme文件的说明:

可以知道如果使用开发板board/<board_name>,就先执行“make <board_name>_config”命令进行配置,然后执行“make all”, 就可以生成如下3个文件:

U-Boot.bin:二进制可执行文件,它就是可以直接烧入eMMC中的文件。

U-Boot : ELF格式的可执行文件。

U-Boot.srec : 摩托罗拉格式的可执行文件。

        对于Exynos4412开发板,这里用的其实是Fs4412,执行“make fs4412_config"、“make all"后生成的u-boot-fs4412.bin可以烧入eMMC中执行。

 

一、U-Boot 配置过程

1、在顶层Makefile中可以看到如下代码:

假定在u-boot-2013.01的根目录下编译,则其中的MKCONFIG 就是根目录下的mkconfig文件(mkconfig是shell脚本文件)

%_config是GUNmake语法层,表示的是所有以".config"结尾的文件。$(@:_config=)的结果就是将“fs4412"中的“——config”去掉,结果为fs4412。

MKCONFIG 就是执行mkconfig。

所以 “make fs4412_config"实际上就是执行如下命令:

./mkconfig fs4412 arm armv7 fs4412 samsung exynos

 

2、mkconfig 的作用

前面已经提到,mkconfig就是一shell脚本, 具体作用如下:

a -- 解析boards.cfg fs4412相关数据

这是我们自行添加的.确定开发板名称BOARD_NAME。

 

b -- 针对平台作了一系列链接,创建到平台、开发板相关的头文件的链接。

 

c --创建顶层Makefile包含的文件 include/ config.mk

inlucde / config.mk 内容如下:

 

d -- 创建开发板相关的头文件inlucde/config.h

Include/config.h导出结果如下:

 

      U-Boot 还没有类似Linux一样的可视化配置界面(比如使用 make menuconfig 来配置),要手动修改配置文件 inlucde/config/<board_name>.h 来裁剪、设置U-Boot.

配置文件中有以下两类宏。

1) 一类是选项(Options),前缀为“CONFIG”它们用于选择CPU、SOC、开发板类型,设置系统时钟、选择设备驱动等。

 

2)另一类是参数(Setting),前缀为“CFG_”,它们用于设置malloc缓冲池的大小、U-Boot 下载文件时的默认加载地址、Flash的起始地址等。

 

uboot 执行通过宏来判断:宏在头文件中定义。

#ifdef CONFIG_TEST
run_test();
#endif

某头文件

#define CONFIG_TEST

可以这样认为,“CONFIG_”除了设置一些参数外,主要用来设置U-Boot的功能、选择使用文件中的哪一部分;而“CFG_”用来 设置更细节的参数。

 

二、U-Boot 的编译、链接过程

 配uboot 编译通过Makefile来判断:

obj-y += xx.o xx.o  在编译时,只编译obj-y

        obj-$(CONFIG_XX) = xx.o xx.o 如果CONFIG_XX为y,则此文件会被编译进u-boot.bin置完后,执行“make all” 即可编译:

找第一个目标all:

上面代码是对u-boot进行格式转换,变成二进制bin格式之后,再加一些校验与4412开如平台加密信息。

依赖u-boot:

 

先总结一下U-Boot 的编译流程:

a -- 首先编译 cpu /$(CPU)/start.S,对于不同的CPU,还可能编译 cpu/$(CPU)下的其他文件;

b -- 然后,对于平台/开发板相关的每个目录、每个通用目录都使用它们个字的Makefile生成相应的库;

c -- 将a、b 步骤生成的.o .a文件按照 board / $(BOARDDIR)/config.mk文件中指定的代码段起始地址、board/$(BOARDDIR)/config.mk文件中指定的代码段起始地址、board/$(BOARDDIR)/U-Boot.lds链接脚本进行链接。

d -- 第c步得到的是ELF格式的U-Boot,后面的Makefile还会将它转换成二进制格式、S-Record格式。

 

 

22222222222222222222222222

uboot启动流程分析如下:

第一阶段:

a -- 设置cpu工作模式为SVC模式
b -- 关闭中断,mmu,cache
v -- 关看门狗
d -- 初始化内存,串口
e -- 设置栈
f -- 代码自搬移
g -- 清bss
h -- 跳c

第二阶段

a -- 初始化外设,进入超循环
b -- 超循环处理用户命令

 

可见, U-Boot 属于两阶段的Bootloader

第一阶段的文件:

arch/arm/cpu/armv7 /start.S                       平台相关,CPU工作模式设为SVC模式,关MMU,关icahce(CPU相关)

board/samsung/fs4412/lowlevel_init.S     开发板相关:关看门狗,内存初始化,时钟初始化,串口初始化(board相关,初始化最基本设备)

 

第二阶段的文件:

 

arch/arm/lib/crt0.S                     _main 函数所在处,初始化SP,为C语言准备,代码重定位,清BSS,设置R0 R1 R2 R8相应寄存器

 

arch/arm/lib/board.c                   board_init_f 函数 ,填充GD结构体,初始化外设, main_loop()函数超循环

arch/arm/cpu/armv7 /start.S 代码自搬移时会用到

针对uboot2013启动流程图如下:

 

下面是具体分析:

一、U-Boot 第一阶段代码分析

通常我们通过连接文件知晓程序入口点,入口查看 u-boot.lds

通过链接脚本可知入口为_start,位于arch/arm/cpu/armv7/start.o

 

第一阶段开始:

1、进入arch/arm/cpu/armv7/start.S

a -- 异常向量表设置

 

b -- 设置CPU处于SVC工作模式

 

d -- 协处理器 p15 的 c12 寄存器来重新定位

 

e、Bl  cpu_init_cp15(使分支预测无效,数据)

关闭数据预取功能;

DSB:多核CPU对数据处理指令

ISB:流水线清空指令;

关闭MMU,使能I-cache

NOTE:

分支预测:在流水线里,会将后面的代码优先加载到处理器中,由于是循环,会使后面加载的代码无效,故出现了分支预测技术。(统计跳的次数来选择装载循环的代码还是下面的代码)。

 

f、Bl  cpu_init_crit

 

 

2、跳到Low_level_init,位于board/samsung/fs4412/lowlevel_init.S

a、关闭看门狗

 

b、比较当前pc指针域TEXT_BASE的高8位是否一样来判断,当前代码是否在内存中

 

c、对系统时钟初始化

 

d、对内存初始化

e、对串口初始化

结束后返回 start.S

第一阶段结束,总结如下:

1 前面总结过的部分,初始化异常向量表,设置svc模式

2 配置cp15,初始化mmu cache tlb

3 板级初始化,clk,memory,uart初始化

 

 

二、第二阶段开始:

按"CTRL + ] ", 发现 _main 在两处有定义:

这里我们选择第一个Bl  _main ,跳转到arch/arm/lib/crt0.S

 

1、初始c运行环境(看注释就知道,初始化C运行环境,并调用board_init_f 函数)

功能:

初始化sp ,为支持c语言做准备;

保存128B 放GD结构体,存放全局信息,GD的地址存放在r8中;

跳转到 board_init_f 函数,其在arch/arm/lib/board.c 处定义;

 

2、跳转到arch/arm/lib/board.c

功能:

对全局信息GD结构体进行填充:

291行:mon_len 通过链接脚本可以知道存放的是uboot代码大小;

294行:fdt_blob 存放设备数地址;

303行:循环执行init_fnc_t数组的函数,作硬件初始化;

 

a -- init_fnc_t数组的函数定义

     初始化硬件

 

 

b -- Dram_init初始化成功之后,剩余代码将会对sdram空间进行规划。

 

可以看到addr的值由CONFIG_SYS_SDRAM_BASE加上ram_size。也就是到了可用sdram的顶端。

 

e--继续对gd结构体填充

如果icahe 与 dcache 是打开的,就留出 64K 的空间作为 tlb 空间,最后 addr 就是tlb 地址,4K对齐。

 

f --填充完成将信息拷贝到内存指定位置

 

 

 

 

 

 

2 -- 继续回到 _main

 

按"CTRL + O"回到跳转前的函数,即 arch/arm/lib/crt0.S

 

功能:

将 r8 指向新的 gd 地址;

代码重定位;

 对lr 的操作为了让返回时,返回的是重定位的here处

 

3 -- 代码自搬移

 

代码自搬移,防止与内核冲突,代码位于arch/arm/cpu/armv7/start.S

循环将代码搬移到指定高地址

 

这里只是将链接脚本中_image_copy_end到_start中的代码,其它段还没有操作。

 

在这里我们有疑惑就是将代码重定位到高地址,那运行的地址不就和链接地址不一样了,那运行可能不正常?这个疑惑就是.rel.dyn帮我们解决了,主要还是编译器帮我们做的工作,在链接中有如下:【参考:http://blog.csdn.net/skyflying2012/article/details/37660265】

 

 

4 -- 重定位到高地址之后,再次回到 _main(arch/arm/lib/crt0.S)

      此时回到的是刚才的重定位的 here 处

关 icache,保证数据从SDRAM中更新,更新异常向量表,因为代码被重定位了;

清BBS;

 

 

调用board_init_r主要是对外设的初始化。

R0=gd

R1=RELOCADDR

 

5 -- Main_loop 函数进入超循环(arch/arm/lib/board.c)

 

Main_loop函数主要功能是处理环境变量,解析命令

install_auto_complete();  //安装自动补全的函数,分析如下 。

getenv(bootcmd)

bootdelay(自启动)

 

如果延时大于等于零,并且没有在延时过程中接收到按键,则引导内核。

 

 

 

33333333333333333333333333333

 

Uboot添加自定义命令:uboot中的命令使用U_BOOT_CMD这个宏声明来注册进系统,链接脚本会把所有的cmd_tbl_t结构体放在相邻的地方。

UBoot版本:u-boot-2013.01 

 

一、U-Boot命令的格式

  即使是内核的启动,也是通过U-Boot命令来实现的。U-Boot中每个命令都是通过 U_BOOT_CMD 宏来定义的,格式如下:

 U_BOOT_CMD(name,maxargs,repeatable,command,"usage","help")

各项参数的意义如下:

1) -- name:命令的名字,注意,它不是一个字符串(不要用双引号括起来);

2)-- maxargs:最大的参数个数;

3)-- repeatable:命令是否可以重复,可重复是指运行一个命令后,下次敲回车即可再次运行;

4)-- command:对应的函数指针,类型为(*cmd)(struct cmd_tbl_s *, int, int, char *[]);

5) -- usage:简单的使用说明,这是个字符串;

6)-- help:较详细的使用说明,这是个字符串。

 

宏U_BOOT_CMD 在include/command.h中定义,如下所示

 

而U_BOOT_CMD 是用一个struct cmd_tbl_s 结构体定义,这个结构体仍是在include/command.h中实现:

 

      可以看出,对于每个使用U_BOOT_CMD 宏来定义的命令,就是宏 U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)将struct cmd_tbl_s这样的一个命令结构体放到U-BOOT连接脚本 board/xxx/u-boot.lds中定义的".u-boot_cmd"段所在的内存区域,即在".u_boot_list.cmd"段中定义一个 cmd_tbl_t 结构。

连接脚本U-Boot.lds中有如下代码:

  当用户在u-boot的shell中输入命令时,就会在".u_boot_list.cmd"这个内存区域中查找( _u_boot_list_cmd__start - _u_boot_list_cmd__end),当该区域中某一个cmd_tbl_s命令结构体的cmd_tbl_s.name和输入的命令字符串相符时,就调用该命令结构体的cmd_tbl_s.cmd()函数

 

二、添加自定义命令

自定义命令设为"myubootcmd",不可与u-boot命令重名,

1、添加命令行配置信息

 在u-boot-2013.01/include/configs/fs4412.h(由具体开发板来配置,这里使用fs4412)中添加 #define CONFIG_CMD_MYUBOOT,如下:

 

2、编写命令行对应的源程序

  在u-boot-2013.01/common/目录下,建立相应的命令执行文件cmd_hello.c 

  注意命名的规范,必须是cmd_xxx.c才行。里面的内容也是有格式要求的,如函数的格式,必须指定参数的;还有相应结尾部分的U_BOOT_CMD定义部分,使不能缺省的。如果命令不需要跟参数,则把maxargs设置为1即可了。内容如下所示:

 

[cpp] view plain copy

  1. #include <common.h>  
  2. #include <command.h>  
  3.   
  4. #ifdef CONFIG_CMD_HELLO  
  5.   
  6. void helloword (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])   
  7. {    
  8.     printf("hello world by xiaoqiang! \n");  
  9. }  
  10.   
  11. U_BOOT_CMD(hello,1,2,helloword,  
  12.                    "hello command","xiaoqing add hello command!\n");  
  13.   
  14. #endif  

 

 

3、修改Makefile

  在common/Makefile中增加一项.


4、编译

 

5、测试

 

 

 

 

 

 

 

 

444444444444444444444444444

 bootloader 要想启动内核,可以直接跳到内核的第一个指令处,即内核的起始地址,这样便可以完成内核的启动工作了。但是要想启动内核还需要满足一些条件,如下所示:

1、cpu 寄存器设置

    * R0 = 0
    * R1 = 机器类型 id
    * R2 = 启动参数在内存中的起始地址

2、cpu 模式

    * 禁止所有中断
    * 必须为SVC(超级用户)模式

3、Cache、MMU

    * 关闭 MMU
    * 指令Cache可以开启或者关闭
    * 数据Cache必须关闭

4、设备

    * DMA 设备应当停止工作

5、PC为内核的起始地址

     

      这些需求都由 boot loader 实现,在常用的 uboot 中完成一系列的初始化后最后通过 bootm 命令加载 linux 内核。bootm 向将内核映像从各种媒介中读出,存放在指定的位置;然后设置标记列表给内核传递参数;最后跳到内核的入口点去执行。

Uboot版本:u-boot-2013.01

一、bootm命令用法介绍如下:

       在 common/cmd_bootm.c 中可以看到bootm 的定义:

可以看到 bootm 命令使调用了do_bootm 函数。

do_bootm 函数

在cmd_bootm.c 第586行可以看到do_bootm函数的定义(为方便阅读,对其中一些代码进行了删减,完整代码请阅读uboot源码):

/*******************************************************************/
/* bootm - boot application image from image in memory */
/*******************************************************************/
 
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;
 
    if (bootm_start(cmdtp, flag, argc, argv))// 获取镜像信息
        return 1;
 
    iflag = disable_interrupts(); // 关闭中断
 
    usb_stop();// 关闭usb设备
 
    ret = bootm_load_os(images.os, &load_end, 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;
    }
 
    boot_fn = boot_os[images.os.os];//获取启动参数
 
    arch_preboot_os();//启动前准备
 
    boot_fn(0, argc, argv, &images);//启动,不再返回
 
#ifdef DEBUG
    puts("\n## Control returned to monitor - resetting...\n");
#endif
    do_reset(cmdtp, flag, argc, argv);
 
    return 1;
}
该函数的实现分为 3 个部分:
a -- 首先通过 bootm_start 函数分析镜像的信息;

b -- 如果满足判定条件则进入 bootm_load_os 函数进行加载;

c -- 加载完成后就可以调用 boot_fn 开始启动。


1、bootm_start

在cmd_bootm.c 第193行可以看到bootm_start函数的定义, 主要作用是填充内核相关信息

static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    void        *os_hdr;
    int        ret;
 
    memset((void *)&images, 0, sizeof(images));
    images.verify = getenv_yesno("verify");//获取环境变量
 
    boot_start_lmb(&images);
 
    bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START, "bootm_start");
 
    /*获取镜像头,加载地址,长度 */
    os_hdr = boot_get_kernel(cmdtp, flag, argc, argv,
            &images, &images.os.image_start, &images.os.image_len);
    if (images.os.image_len == 0) {
        puts("ERROR: can't get kernel image!\n");
        return 1;
    }
 
    /*获取镜像参数*/
    switch (genimg_get_format(os_hdr)) {
    case IMAGE_FORMAT_LEGACY:
        images.os.type = image_get_type(os_hdr);//镜像类型
        images.os.comp = image_get_comp(os_hdr);//压缩类型
        images.os.os = image_get_os(os_hdr);//系统类型
 
        images.os.end = image_get_image_end(os_hdr);//镜像结束地址
        images.os.load = image_get_load(os_hdr);/加载地址
        break;
 
    /* 查询内核入口地址*/
    if (images.legacy_hdr_valid) {
        images.ep = image_get_ep(&images.legacy_hdr_os_copy);
 
    } else {
        puts("Could not find kernel entry point!\n");
        return 1;
    }
 
    if (images.os.type == IH_TYPE_KERNEL_NOLOAD) {
        images.os.load = images.os.image_start;
        images.ep += images.os.load;
    }
 
    if (((images.os.type == IH_TYPE_KERNEL) ||
         (images.os.type == IH_TYPE_KERNEL_NOLOAD) ||
         (images.os.type == IH_TYPE_MULTI)) &&
        (images.os.os == IH_OS_LINUX)) {
        /* 查询是否存在虚拟磁盘 */
        ret = boot_get_ramdisk(argc, argv, &images, IH_INITRD_ARCH,
                &images.rd_start, &images.rd_end);
        if (ret) {
            puts("Ramdisk image is corrupt or invalid\n");
            return 1;
        }
 
#if defined(CONFIG_OF_LIBFDT)
        /* 找到设备树,设备树是linux 3.XX版本特有的 */
        ret = boot_get_fdt(flag, argc, argv, &images,
                   &images.ft_addr, &images.ft_len);
        if (ret) {
            puts("Could not find a valid device tree\n");
            return 1;
        }
 
        set_working_fdt_addr(images.ft_addr);
#endif
    }
 
    images.os.start = (ulong)os_hdr;//赋值加载地址
    images.state = BOOTM_STATE_START;//更新状态
 
    return 0;
}
该函数主要进行镜像的有效性判定、校验、计算入口地址等操作,大部分工作通过 boot_get_kernel -> image_get_kernel 完成。


2、bootm_load_os

      在cmd_bootm.c 第317行可以看到bootm_load_os函数的定义, 这个函数主要判断镜像是否需要解压,并且将镜像移动到加载地址:

static int bootm_load_os(image_info_t os, ulong *load_end, int boot_progress)  
{  
    uint8_t comp = os.comp;         /* 压缩格式 */  
    ulong load = os.load;           /* 加载地址 */  
    ulong blob_start = os.start;    /* 镜像起始地址 */  
    ulong blob_end = os.end;        /* 镜像结束地址 */  
    ulong image_start = os.image_start;    /* 镜像起始地址 */  
    ulong image_len = os.image_len;        /* 镜像长度 */  
    uint unc_len = CONFIG_SYS_BOOTM_LEN;   /* 镜像最大长度 */  
  
    const char *type_name = genimg_get_type_name (os.type);  /* 镜像类型 */  
  
    switch (comp) {  /* 选择解压格式 */  
    case IH_COMP_NONE:  /* 镜像没有压缩过 */  
        if (load == blob_start) {   /* 判断是否需要移动镜像 */  
            printf ("   XIP %s ... ", type_name);  
        } else {  
            printf ("   Loading %s ... ", type_name);  
  
            if (load != image_start) {  
                memmove_wd ((void *)load, (void *)image_start, image_len, CHUNKSZ);  
            }  
        }  
        *load_end = load + image_len;  
        puts("OK\n");  
        break;  
    case IH_COMP_GZIP:  /* 镜像采用 gzip 解压 */  
        printf ("   Uncompressing %s ... ", type_name);  
        if (gunzip ((void *)load, unc_len, (uchar *)image_start, &image_len) != 0) {  /* 解压 */  
            puts ("GUNZIP: uncompress, out-of-mem or overwrite error "  
                "- must RESET board to recover\n");  
            return BOOTM_ERR_RESET;  
        }  
  
        *load_end = load + image_len;  
        break;  
    ...  
    default:  
        printf ("Unimplemented compression type %d\n", comp);  
        return BOOTM_ERR_UNIMPLEMENTED;  
    }  
    puts ("OK\n");  
    debug ("   kernel loaded at 0x%08lx, end = 0x%08lx\n", load, *load_end);  
  
    if ((load < blob_end) && (*load_end > blob_start)) {  
        debug ("images.os.start = 0x%lX, images.os.end = 0x%lx\n", blob_start, blob_end);  
        debug ("images.os.load = 0x%lx, load_end = 0x%lx\n", load, *load_end);  
        return BOOTM_ERR_OVERLAP;  
    }  
  
    return 0;  
}  


3、do_bootm_linux
在bootm_load_os 执行结束后,回到do_bootm 函数,调用boot_fn 运行linux 内核;

boot_os 为函数指针数组,在cmd_bootm.c 136行有定义


可以看出 boot_fn 函数指针指向的函数是位于 arch/arm/lib/bootm.c的 do_bootm_linux,这是内核启动前最后的一个函数,该函数主要完成启动参数的初始化,并将板子设定为满足内核启动的环境,代码如下:

可以看到 do_bootm_linux 实际调用的是 boot_jump_linux 函数。

4、boot_jump_linux 

在arch/arm/lib/bootm.c 下第326行有定义

/* Subcommand: GO */
static void boot_jump_linux(bootm_headers_t *images)
{
    unsigned long machid = gd->bd->bi_arch_number;//获取机器码
    char *s;
    void (*kernel_entry)(int zero, int arch, uint params);//内核入口函数
    unsigned long r2;
 
    kernel_entry = (void (*)(int, int, uint))images->ep;
 
    s = getenv("machid");//从环境变量中获取机器码
    if (s) {
        strict_strtoul(s, 16, &machid);
        printf("Using machid 0x%lx from environment\n", machid);
    }
 
    debug("## Transferring control to Linux (at address %08lx)" \
        "...\n", (ulong) kernel_entry);
    bootstage_mark(BOOTSTAGE_ID_RUN_OS);
    announce_and_cleanup();
 
#ifdef CONFIG_OF_LIBFDT
    if (images->ft_len)
        r2 = (unsigned long)images->ft_addr;
    else
#endif
        r2 = gd->bd->bi_boot_params;//将启动参数地址赋给 r2 
 
    kernel_entry(0, machid, r2);
}

kernel_entry(0, machid, r2) 
真正将控制权交给内核, 启动内核;

满足arm架构linux内核启动时的寄存器设置条件:第一个参数为0 ;第二个参数为板子id需与内核中的id匹配,第三个参数为启动参数地址 。

二、为内核设置启动参数

        Uboot 也是通过标记列表向内核传递参数,标记在源代码中定义为tag,是一个结构体,在 arch/arm/include/asm/setup.h 中定义。

tag_header 结构体定义如下:

     在一些内存标记、命令行标记的示例代码就是取自Uboot 中的 setup_memory_tags、setup_commandline_tag函数,他们都是在 arch/arm/lib/bootm.c中定义。

#if defined(CONFIG_SETUP_MEMORY_TAGS) || \
    defined(CONFIG_CMDLINE_TAG) || \
    defined(CONFIG_INITRD_TAG) || \
    defined(CONFIG_SERIAL_TAG) || \
    defined(CONFIG_REVISION_TAG)
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);
}
#endif
 
#ifdef CONFIG_SETUP_MEMORY_TAGS
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 = tag_next (params);
    }
}
#endif
 
#ifdef CONFIG_CMDLINE_TAG
static void setup_commandline_tag(bd_t *bd, char *commandline)
{
    char *p;
 
    if (!commandline)
        return;
 
    /* eat leading white space */
    for (p = commandline; *p == ' '; p++);
 
    /* skip non-existent command lines so the kernel will still
     * use its default command line.
     */
    if (*p == '\0')
        return;
 
    params->hdr.tag = ATAG_CMDLINE;
    params->hdr.size =
        (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;
 
    strcpy (params->u.cmdline.cmdline, p);
 
    params = tag_next (params);
}
#endif


一般有 setup_memory_tags、setup_commandline_tag 这两个标记就可以了,在配置文件Include/configs/fs4412.h中定义:


555555555555555555555555

Uboot 版本:u-boot-2013.01
开发板:FS_4412 平台(Exynos4412,可以根据自己的板子修改,只要是4412的过程都是一样的)

一、建立自己的平台
1、下载源码
我们可以在下面这个网站上下载最新的和以前任一版本的uboot
ftp://ftp.denx.de/pub/u-boot/
这里我们使用的是u-boot-2013.01

2、解压uboot 源码并进入目录


3、指定交叉编译工具链
a -- 打开根目录下的Makefile

b -- 添加交叉编译工具链信息


4、指定产品CPU
我们产品用的CPU 是exynos 4412
查看u-boot 源码该CPU 是否已支持
U-boot 已支持,见arch/arm/cpu/armv7/exynos/

5、指定产品BOARD
找一个最类似的board 配置修改, 这里我们参考的是board/samsung/origen/
a -- 修改board/samsung/ 板级相关文件夹,复制 board/samsung/origen/,重命名为fs4412
 

b -- 修改board/samsung/fs4412/Makefile信息

修改 origen.o 为 fs4412.o


c -- 修改include/configs/fs4412.h配置文件
这里同样是先复制 inlcude/configs/origen.h,生成 inlcude/configs/fs4412.h

打开fs4412.h,修改以下文件:
将原来的#define CONFIG_SYS_PROMPT "ORIGEN #" 改成:

将原来的#define CONFIG_IDENT_STRING for ORIGEN 改成


d -- 配置boards.cfg
打开uboot根目录下的boards.cfg,在origen后新增


6、编译u-boot
$ make distclean
$ make fs4412_config (可以在inlcude/config.mk及include/config.h下看到配置好的信息)
$ make

编译完成后生成的u-boot.bin 就是可执行的镜像文件。
烧写uboot 命令:
tftp 41000000 u-boot.bin
movi write uboot 41000000
但是该文件还不能在我们板子上运行,我们需要对u-boot 源代码进行相应的修改。


二、实现能看到串口终端信息
1、确认第一条指令有运行到(点灯法) 
a -- 在arch/arm/cpu/armv7/start.S 134 行后添加点灯程序


b --  添加三星加密方式
exynos 需要三星提供的初始引导加密后,我们的u-boot,才能被引导运行
将sdfuse_q 目录拷贝到u-boot-2013.01 源码目录下
注:sdfuse_q 三星提供的加密处理
将CodeSign4SecureBoot 目录拷贝到u-boot-2013.01 源码目录下
注:CodeSign4SecureBoot 三星提供的安全启动方式

c -- 修改Makefile
$vim Makefile
修改实现sdfuse_q 的编译

下添加

注意是tab 键缩进的,否则makefile 编译报错
注意如果执行了make distclean 需重新拷贝CodeSign4SecureBoot

d -- 拷贝编译脚本
将 build.sh 拷贝到uboot 根目录下,并加上执行权限,并执行该脚本 注:build.sh 脚本方式完成自动添加加密方式,是自己编写的

编译生成所需文件u-boot_fs4412.bin
烧写新的u-boot_fs4412.bin
复位,发现灯有点亮,说明我们的u-boot 有运行到
附:build.sh脚本文件
[cpp] view plain copy  
#!/bin/sh  
  
sec_path="CodeSign4SecureBoot/"  
CPU_JOB_NUM=$(grep processor /proc/cpuinfo | awk '{field=$NF};END{print field+1}')  
ROOT_DIR=$(pwd)  
CUR_DIR=${ROOT_DIR##*/}  
  
case "$1" in  
    clean)  
        echo make clean  
        make mrproper  
        ;;  
    *)  
              
        if [ ! -d $sec_path ]  
        then  
            echo "**********************************************"  
            echo "[ERR]please get the CodeSign4SecureBoot first"  
            echo "**********************************************"  
            return  
        fi  
          
        make fs4412_config  
          
        make -j$CPU_JOB_NUM  
          
        if [ ! -f checksum_bl2_14k.bin ]  
        then  
            echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"  
            echo "There are some error(s) while building uboot, please use command make to check."  
            echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"  
            exit 0  
        fi  
          
        cp -rf checksum_bl2_14k.bin $sec_path  
        cp -rf u-boot.bin $sec_path  
        rm checksum_bl2_14k.bin  
          
        cd $sec_path  
        cat E4412_N.bl1.SCP2G.bin bl2.bin all00_padding.bin u-boot.bin tzsw_SMDK4412_SCP_2GB.bin > u-boot-fs4412.bin  
        mv u-boot-fs4412.bin $ROOT_DIR  
          
        rm checksum_bl2_14k.bin  
        rm u-boot.bin  
  
        echo   
        echo   
        ;;  
          
esac  

2、实现串口输出
修改lowlevel_init.S 文件
$vim board/samsung/fs4412/lowlevel_init.S
a -- 添加临时栈

lowlevel_init:
后添加


b --  添加关闭看门狗代码

beq wakeup_reset
后添加


c -- 添加串口初始化代码
在uart_asm_init: 的
str r1, [r0, #EXYNOS4_GPIO_A1_CON_OFFSET]
后添加

注释掉trustzone 初始化
注释掉
bl uart_asm_init
下的
bl tzpc_init

重新编译u-boot
$ ./build.sh
烧写新的u-boot_fs4412.bin
复位会看到串口信息

三、网卡移植
1、添加网络初始化代码
$vim board/samsung/fs4412/fs4412.c
在struct exynos4_gpio_part2 *gpio2; 后添加
[cpp] view plain copy  
#ifdef CONFIG_DRIVER_DM9000  
#define EXYNOS4412_SROMC_BASE 0X12570000  
  
#define DM9000_Tacs     (0x1)   // 0clk         address set-up  
#define DM9000_Tcos     (0x1)   // 4clk         chip selection set-up  
#define DM9000_Tacc     (0x5)   // 14clk        access cycle  
#define DM9000_Tcoh     (0x1)   // 1clk         chip selection hold  
#define DM9000_Tah      (0xC)   // 4clk         address holding time  
#define DM9000_Tacp     (0x9)   // 6clk         page mode access cycle  
#define DM9000_PMC      (0x1)   // normal(1data)page mode configuration  
  
  
struct exynos_sromc {  
    unsigned int bw;  
    unsigned int bc[6];  
};  
  
/* 
 *  s5p_config_sromc() - select the proper SROMC Bank and configure the 
 *  band width control and bank control registers 
 *  srom_bank    - SROM 
 *  srom_bw_conf  - SMC Band witdh reg configuration value 
 *  srom_bc_conf  - SMC Bank Control reg configuration value 
 */  
  
void exynos_config_sromc(u32 srom_bank, u32 srom_bw_conf, u32 srom_bc_conf)  
{  
    unsigned int tmp;  
    struct exynos_sromc *srom = (struct exynos_sromc *)(EXYNOS4412_SROMC_BASE);  
  
    /* Configure SMC_BW register to handle proper SROMC 
     * bank */  
    tmp = srom->bw;  
    tmp &= ~(0xF << (srom_bank * 4));  
    tmp |= srom_bw_conf;  
    srom->bw = tmp;  
  
    /* Configure SMC_BC 
     * register */  
    srom->bc[srom_bank] = srom_bc_conf;  
}  
static void dm9000aep_pre_init(void)  
{  
    unsigned int tmp;  
    unsigned char smc_bank_num = 1;  
    unsigned int     smc_bw_conf=0;  
    unsigned int     smc_bc_conf=0;  
  
    /* gpio configuration */  
    writel(0x00220020, 0x11000000 + 0x120);  
    writel(0x00002222, 0x11000000 + 0x140);  
    /* 16 Bit bus width */  
    writel(0x22222222, 0x11000000 + 0x180);  
    writel(0x0000FFFF, 0x11000000 + 0x188);  
    writel(0x22222222, 0x11000000 + 0x1C0);  
    writel(0x0000FFFF, 0x11000000 + 0x1C8);  
    writel(0x22222222, 0x11000000 + 0x1E0);  
    writel(0x0000FFFF, 0x11000000 + 0x1E8);                
    smc_bw_conf &= ~(0xf<<4);  
    smc_bw_conf |= (1<<7) | (1<<6) | (1<<5) | (1<<4);  
    smc_bc_conf = ((DM9000_Tacs << 28)  
            | (DM9000_Tcos << 24)  
            | (DM9000_Tacc << 16)  
            | (DM9000_Tcoh << 12)  
            | (DM9000_Tah << 8)  
            | (DM9000_Tacp << 4)  
            | (DM9000_PMC));  
    exynos_config_sromc(smc_bank_num,smc_bw_conf,smc_bc_conf);  
}  
#endif  

在gd->bd->bi_boot_params = (PHYS_SDRAM_1 + 0x100UL); 后添加
[cpp] view plain copy  
#ifdef CONFIG_DRIVER_DM9000  
    dm9000aep_pre_init();  
#endif  
在文件末尾添加
[cpp] view plain copy  
#ifdef CONFIG_CMD_NET  
int board_eth_init(bd_t *bis)  
{  
    int rc = 0;  
#ifdef CONFIG_DRIVER_DM9000  
    rc = dm9000_initialize(bis);  
#endif  
    return rc;  
}  
#endif  


2、修改配置文件添加网络相关配置
$ vim include/configs/fs4412.h
修改
#undef CONFIG_CMD_PING

#def ine CONFIG_CMD_PING
修改
#undef CONFIG_CMD_NET

#def ine CONFIG_CMD_NET


在文件末尾
#endif /* __CONFIG_H */
前面添加


3、重新编译u-boot
$ ./build.sh
烧写新的u-boot_fs4412.bin
复位后
# ping 192.168.9.120

四、FLASH 移植(EMMC)
1、初始化EMMC
$cp movi.c arch/arm/cpu/armv7/exynos/
$vim arch/arm/cpu/armv7/exynos/Makefile
在pinmux.o 后添加movi.o


修改板级文件
$vim board/samsung/fs4412/fs4412.c

#include <asm/arch/mmc.h>
后面添加
#include <asm/arch/clk.h>
#include "origen_setup.h"



#ifdef CONFIG_GENERIC_MMC
后面添加
[cpp] view plain copy  
u32 sclk_mmc4;  /*clock source for emmc controller*/  
#define __REGMY(x) (*((volatile u32 *)(x)))  
#define CLK_SRC_FSYS  __REGMY(EXYNOS4_CLOCK_BASE + CLK_SRC_FSYS_OFFSET)  
#define CLK_DIV_FSYS3 __REGMY(EXYNOS4_CLOCK_BASE + CLK_DIV_FSYS3_OFFSET)  
  
int emmc_init()  
{  
    u32 tmp;  
    u32 clock;  
    u32 i;  
    /* setup_hsmmc_clock */  
    /* MMC4 clock src = SCLKMPLL */  
    tmp = CLK_SRC_FSYS & ~(0x000f0000);  
    CLK_SRC_FSYS = tmp | 0x00060000;  
    /* MMC4 clock div */  
    tmp = CLK_DIV_FSYS3 & ~(0x0000ff0f);  
    clock = get_pll_clk(MPLL)/1000000;  
  
    for(i=0 ; i<=0xf; i++)  {  
        sclk_mmc4=(clock/(i+1));  
  
        if(sclk_mmc4 <= 160) //200  
        {  
            CLK_DIV_FSYS3 = tmp | (i<<0);  
            break;  
        }  
    }  
    emmcdbg("[mjdbg] sclk_mmc4:%d MHZ; mmc_ratio: %d\n",sclk_mmc4,i);  
    sclk_mmc4 *= 1000000;  
  
    /* 
     * MMC4 EMMC GPIO CONFIG 
     * 
     * GPK0[0]  SD_4_CLK 
     * GPK0[1]  SD_4_CMD 
     * GPK0[2]  SD_4_CDn 
     * GPK0[3:6]    SD_4_DATA[0:3] 
     */  
    writel(readl(0x11000048)&~(0xf),0x11000048); //SD_4_CLK/SD_4_CMD pull-down enable  
    writel(readl(0x11000040)&~(0xff),0x11000040);//cdn set to be output  
  
    writel(readl(0x11000048)&~(3<<4),0x11000048); //cdn pull-down disable  
    writel(readl(0x11000044)&~(1<<2),0x11000044); //cdn output 0 to shutdown the emmc power  
    writel(readl(0x11000040)&~(0xf<<8)|(1<<8),0x11000040);//cdn set to be output  
    udelay(100*1000);  
    writel(readl(0x11000044)|(1<<2),0x11000044); //cdn output 1  
  
  
    writel(0x03333133, 0x11000040);  
  
    writel(0x00003FF0, 0x11000048);  
    writel(0x00002AAA, 0x1100004C);  
  
#ifdef CONFIG_EMMC_8Bit  
    writel(0x04444000, 0x11000060);  
    writel(0x00003FC0, 0x11000068);  
    writel(0x00002AAA, 0x1100006C);  
#endif  
  
#ifdef USE_MMC4  
    smdk_s5p_mshc_init();  
#endif   
}  

将int board_mmc_init(bd_t *bis)函数内容改写为
[cpp] view plain copy  
int board_mmc_init(bd_t *bis)  
{  
    int i, err;  
#ifdef CONFIG_EMMC  
    err = emmc_init();  
#endif  
    return err;  
}  


在末尾添加
[cpp] view plain copy  
#ifdef CONFIG_BOARD_LATE_INIT  
#include <movi.h>  
int  chk_bootdev(void)//mj for boot device check  
{  
    char run_cmd[100];  
    struct mmc *mmc;  
    int boot_dev = 0;  
    int cmp_off = 0x10;  
    ulong  start_blk, blkcnt;  
  
    mmc = find_mmc_device(0);  
  
    if (mmc == NULL)  
    {  
        printf("There is no eMMC card, Booting device is SD card\n");  
        boot_dev = 1;  
        return boot_dev;  
    }  
    start_blk = (24*1024/MOVI_BLKSIZE);  
    blkcnt = 0x10;  
  
    sprintf(run_cmd,"emmc open 0");  
    run_command(run_cmd, 0);  
  
    sprintf(run_cmd,"mmc read 0 %lx %lx %lx",CFG_PHY_KERNEL_BASE,start_blk,blkcnt);  
    run_command(run_cmd, 0);  
  
    sprintf(run_cmd,"emmc close 0");  
    run_command(run_cmd, 0);  
  
    return 0;  
}  


2、添加相关命令
$ cp cmd_movi.c common/
$ cp cmd_mmc.c common/
$ cp cmd_mmc_fdisk.c common/
修改Makefile
$ vim common/Makefile

COBJS-$(CONFIG_CMD_MMC) += cmd_mmc.o
后添加
COBJS-$(CONFIG_CMD_MMC) += cmd_mmc_fdisk.o
COBJS-$(CONFIG_CMD_MOVINAND) += cmd_movi.o


添加驱动
$ cp mmc.c drivers/mmc/
$ cp s5p_mshc.c drivers/mmc/
$ cp mmc.h include/
$ cp movi.h include/
$ cp s5p_mshc.h include/
修改Makefile
$vim drivers/mmc/Makefile
添加
COBJS-$(CONFIG_S5P_MSHC) += s5p_mshc.o


3、添加EMMC 相关配置
$vim include/configs/fs4412.h
添加
[cpp] view plain copy  
#define CONFIG_EVT1     1       /* EVT1 */  
#ifdef CONFIG_EVT1  
#define CONFIG_EMMC44_CH4 //eMMC44_CH4 (OMPIN[5:1] = 4)  
  
#ifdef CONFIG_SDMMC_CH2  
#define CONFIG_S3C_HSMMC  
#undef DEBUG_S3C_HSMMC  
#define USE_MMC2    
#endif  
  
#ifdef CONFIG_EMMC44_CH4  
#define CONFIG_S5P_MSHC  
#define CONFIG_EMMC             1                 
#define USE_MMC4    
/* #define CONFIG_EMMC_8Bit */  
#define CONFIG_EMMC_EMERGENCY  
/* 
 * #define emmcdbg(fmt,args...) printf(fmt ,##args)   
 * #define emmcdbg(fmt,args...)  
 */  
 #define emmcdbg(fmt,args...)   
#endif  
  
#endif /*end CONFIG_EVT1*/  
#define CONFIG_CMD_MOVINAND  
#define CONFIG_CLK_1000_400_200  
#define CFG_PHY_UBOOT_BASE      CONFIG_SYS_SDRAM_BASE + 0x3e00000  
#define CFG_PHY_KERNEL_BASE     CONFIG_SYS_SDRAM_BASE + 0x8000  
  
#define BOOT_MMCSD      0x3  
#define BOOT_EMMC43     0x6  
#define BOOT_EMMC441    0x7  
#define CONFIG_BOARD_LATE_INIT  


4、重新编译u-boot
$ ./build.sh
烧写新的u-boot_fs4412.bin
复位后
# mmcinfo
--------------------- 
 

 

6666666666666666666666666666666666666

一、gd结构体的定义与使用

gd_t 和 bd_t 是u-boot中两个重要的数据结构,在初始化操作很多都要靠这两个数据结构来保存或传递。

gd_t 定义在/u-boot-2013.01/arch/arm/include/asm/global_data.h

bd_t 定义在 ./include/asm-arm/u-boot.h
 

1、gd_t : global data数据结构定义

位于文件/u-boot-2013.01/arch/arm/include/asm/global_data.h 中。其成员主要是一些全局的系统初始化参数。

当使用gd_t 时需用宏定义进行声明DECLARE_GLOBAL_DATA_PTR

从这个宏的定义可以看出,gd是一个保存在ARM的r8寄存器中的gd_t结构体的指针。指定占用寄存器R8

 

[cpp] view plain copy

  1. typedef struct  global_data {  
  2.     bd_t        *bd;//struct board_info指针,保存开发板信息  
  3.     unsigned long   flags;//指示标志,如设备已经初始化标志等  
  4.     unsigned int    baudrate;//串口波特率  
  5.     unsigned long   have_console;   //串口初始化标志  
  6. #ifdef CONFIG_PRE_CONSOLE_BUFFER  
  7.     unsigned long   precon_buf_idx; /* Pre-Console buffer index */  
  8. #endif  
  9.     unsigned long   env_addr;   /* Address  of Environment struct */  
  10.     unsigned long   env_valid;  /* Checksum of Environment valid? */  
  11.     unsigned long   fb_base;    /* base address of frame buffer */  
  12. #ifdef CONFIG_FSL_ESDHC  
  13.     unsigned long   sdhc_clk;  
  14. #endif  
  15. #ifdef CONFIG_AT91FAMILY  
  16.     /* "static data" needed by at91's clock.c */  
  17.     unsigned long   cpu_clk_rate_hz;  
  18.     unsigned long   main_clk_rate_hz;  
  19.     unsigned long   mck_rate_hz;  
  20.     unsigned long   plla_rate_hz;  
  21.     unsigned long   pllb_rate_hz;  
  22.     unsigned long   at91_pllb_usb_init;  
  23. #endif  
  24. #ifdef CONFIG_ARM  
  25.     /* "static data" needed by most of timer.c on ARM platforms */  
  26.     unsigned long   timer_rate_hz;  
  27.     unsigned long   tbl;  
  28.     unsigned long   tbu;  
  29.     unsigned long long  timer_reset_value;  
  30.     unsigned long   lastinc;  
  31. #endif  
  32. #ifdef CONFIG_IXP425  
  33.     unsigned long   timestamp;  
  34. #endif  
  35.     unsigned long   relocaddr;  /* Start address of U-Boot in RAM */  
  36.     phys_size_t ram_size;   /* RAM size */  
  37.     unsigned long   mon_len;    /* monitor len */  
  38.     unsigned long   irq_sp;     /* irq stack pointer */  
  39.     unsigned long   start_addr_sp;  /* start_addr_stackpointer */  
  40.     unsigned long   reloc_off;  
  41. #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))  
  42.     unsigned long   tlb_addr;  
  43.     unsigned long   tlb_size;  
  44. #endif  
  45.     const void  *fdt_blob;  /* Our device tree, NULL if none */  
  46.     void        **jt;       /* jump table */  
  47.     char        env_buf[32];    /* buffer for getenv() before reloc. */  
  48. #if defined(CONFIG_POST) || defined(CONFIG_LOGBUFFER)  
  49.     unsigned long   post_log_word; /* Record POST activities */  
  50.     unsigned long   post_log_res; /* success of POST test */  
  51.     unsigned long   post_init_f_time; /* When post_init_f started */  
  52. #endif  
  53. } gd_t;  


 

 

2.、bd_t :board info数据结构定义

位于文件u-boot-2013.01/arch/arm/include/asm/u-boot.h。保存板子参数。

 

[cpp] view plain copy

  1. typedef struct bd_info {  
  2.     unsigned int    bi_baudrate;    /* 串口波特率 */  
  3.     ulong           bi_arch_number; /* 开发板机器ID */  
  4.     ulong           bi_boot_params; /* 启动参数 */  
  5.     unsigned long   bi_arm_freq; /* arm frequency */  
  6.     unsigned long   bi_dsp_freq; /* dsp core frequency */  
  7.     unsigned long   bi_ddr_freq; /* ddr frequency */  
  8.     struct              /* RAM configuration */  
  9.     {  
  10.     ulong start;  
  11.     ulong size;  
  12.     }           bi_dram[CONFIG_NR_DRAM_BANKS];  
  13. } bd_t;  

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值