U-boot移植之自己写一个简单的bootloader(一)

问题1、链接的时候出现引用错误,编译的时候对”//”注释方式报错
把start.s改为start.S即可
原因对于gcc编译器来说,遇到.S 的文件的时候会进行预处理(包含头文件,解析宏定义,条件编译等等),而遇到.s文件的时候就只进行编译而不会预先进行预处理

问题2、nand flash读函数不正常
实现四个字节四个字节的读取,而不是一个字节一个字节的读取,要求地址每次读完之后值加4,要求对输入的读数据长度进行除以4的操作来完成四字节的对齐读取,判断一页是否读完也要求判断是否小于2048 / 4。

1、tag

tag的类型

/* tag 列表以一个 ATAG_NONE 节点标记为结束. */
#define ATAG_NONE   0x00000000

/* tag 列表以一个 ATAG_CORE 节点标记为开始 */
#define ATAG_CORE   0x54410001

/* 允许有多个 ATAG_MEM 节点,常用于有多块不连续内存的内存设备 */
#define ATAG_MEM    0x54410002

/* VGA 文本类型显示 */
#define ATAG_VIDEOTEXT  0x54410003

/* 定义内核如何使用 ramdisk  */
#define ATAG_RAMDISK    0x54410004

/* 描述 ramdisk 镜像压缩文件所在位置 (虚拟地址) */
/*
 * this one accidentally used virtual addresses - as such,
 * its depreciated.
 */
#define ATAG_INITRD 0x54410005

/*  描述 ramdisk 镜像压缩文件所在位置 (物理地址) */
#define ATAG_INITRD2    0x54420005

/* 板子串口数目. "64 bits should be enough for everybody" */
#define ATAG_SERIAL 0x54410006

/* board revision */
#define ATAG_REVISION   0x54410007

/* initial values for vesafb-type framebuffers. see struct screen_info
 * in include/linux/tty.h
 */
#define ATAG_VIDEOLFB   0x54410008

/* command line: \0 terminated string */
#define ATAG_CMDLINE    0x54410009

/* acorn RiscPC specific information */
#define ATAG_ACORN  0x41000101

/* footbridge memory clock, see arch/arm/mach-footbridge/arch.c */
#define ATAG_MEMCLK 0x41000402

tag结构体

struct tag {
    struct tag_header hdr;  //tag 头部

    /* 共用体,里面标记tag的类型 */
    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;

        /*
         * Acorn specific
         */
        struct tag_acorn    acorn;

        /*
         * DC21285 specific
         */
        struct tag_memclk   memclk;
    } u;
};

常用的tag有关的宏定义

#define __tag __attribute__((unused, __section__(".taglist")))
#define __tagtable(tag, fn) \
static struct tagtable __tagtable_##fn __tag = { tag, fn }

/* 当前 tag 的成员所在位置 */
#define tag_member_present(tag,member)              \
    ((unsigned long)(&((struct tag *)0L)->member + 1)   \
        <= (tag)->hdr.size * 4)

/* 下一个 tag */
#define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size))
/* 求 tag 大小 */
#define tag_size(type)  ((sizeof(struct tag_header) + sizeof(struct type)) >> 2)
/* 遍历 tag 列表中的所有 tag */
#define for_each_tag(t,base)        \
    for (t = base; t->hdr.size; t = tag_next(t))

2、本bootloader的tag设置

设置tag的开始

void setup_start_tag(void)
{
    params = (struct tag *)0x30000100;  //内核约定tag地址为0x30000100

    params->hdr.tag = ATAG_CORE;        //tag的开始必须是ATAG_CORE标记,上面的tag类型中有此要求
    params->hdr.size = tag_size (tag_core); //该tag的大小,以四字节为单位

    params->u.core.flags = 0;           //tag_core的flags。此处说明这三个参数没有用到,所以设置为0
    params->u.core.pagesize = 0;        //tag_core的页大小
    params->u.core.rootdev = 0;         //tag_core的rootdev

    params = tag_next (params);         //指向下一个tag结构体
}

tag_core结构体原型

struct tag_core {
    u32 flags;      /* bit 0 = read-only */
    u32 pagesize;
    u32 rootdev;
};

设置内存tag

void setup_memory_tags(void)
{
    params->hdr.tag = ATAG_MEM;                 //tag类型
    params->hdr.size = tag_size (tag_mem32);    //tag大小,四字节为单位

    params->u.mem.start = 0x30000000;           //内存的起始地址,该板子上的SDRAM起始地址
    params->u.mem.size  = 64*1024*1024;         //内存的大小

    params = tag_next (params);                 //指向下一个tag
}

tag_mem32原型,允许设置多个该tag,用于有多块内存设备的板子

struct tag_mem32 {
    u32 size;       //内存的大小
    u32 start;      //内存的物理起始地址
};

设置命令行tag

void setup_commandline_tag(char *cmdline)
{
    int len = strlen(cmdline) + 1;                                      //tag类型

    params->hdr.tag  = ATAG_CMDLINE;                                    //tag大小,四字节为单位
    params->hdr.size = (sizeof (struct tag_header) + len + 3) >> 2;     //命令行的长度,进行4字节对齐

    strcpy (params->u.cmdline.cmdline, cmdline);                        

    params = tag_next (params);                                         //指向下一个tag
}

tag_cmdline原型,字符要以’\0’结束

struct tag_cmdline {
    char    cmdline[1];     //最小的长度
};

字符串拷贝函数

void strcpy(char *dest, char *src)
{
    while ((*dest++ = *src++) != '\0');
}

这里解释下为什么strcpy (params->u.cmdline.cmdline, cmdline);不会报错,我们都知道形如char cmdline[1];的cmdline并不是一个指针,如果把cmdline当做指针一样的进行加减操作或者取值操作就会报错,比如不可这样:cmdline++; 但是为什么上面的一个函数调用却没有出错,明明里面也进行了像指针一样的加以及取值操作。这个是因为在传入参数的时候等于说把params->u.cmdline.cmdline的值传了进去,其相当于一个偏移值,而这个偏移值就是其内部数组的地址,我们只是对传进去的值当做char类型的指针进行加以及取值操作,所以不会报错,如果使用类似下面的语句进行操作就不会报错了,效果跟上面的函数调用是类似的

char *tmp;

tmp = (char *)(params->u.cmdline.cmdline);
*tmp++ = 'h';
*tmp++ = 'e';

设置tag的结束,如上面的要求,必须用ATAG_NONE标记来说明tag参数的结束

void setup_end_tag(void)
{
    params->hdr.tag = ATAG_NONE;
    params->hdr.size = 0;
}

3、跳转执行内核启动

theKernel = (void (*)(int, int, unsigned int))0x30008000;   //内核的起始地址
theKernel(0, 362, 0x30000100);  //0:固定为0    362:芯片类型编号  0x30000100:tag参数起始地址

4、内核启动步骤

  • bootloader 对内存设备,时钟等等进行初始化
  • bootloader 拷贝内核到指定的地址去
  • bootloader 设置 tag 启动参数
  • 跳转去内核起始地址根据 bootloader 设置的参数启动内核

你可能感兴趣的:(u-boot)