第2阶段——编写uboot之启动内核和制作Makefile(2)

目标:

1   添加头文件setup.h和serial.h

2   写main函数  

    2.1 帮内核设置串口0, (内核启动会打印出启动信息)

    2.2把内核读入到SDRAM

    2.3设置参数(参考u-boot-1.1.6 /lib_arm/armlinux.C中do_bootm_linux()函数)

    2.4跳转运行内核(参考u-boot-1.1.6/lib_arm/armlinux.C中do_bootm_linux()函数)  

3 写tag参数函数

   3.1 setup_start_tag (void);

   3.2 setup_memory_tags(void);

   3.3 setup_commandline_tag (char *cmdline);

   3.4 setup_end_tag (void);

 4 写makefile文件

1 添加头文件setup.h和serial.h

1.1     将lcd裸板程序中串口uart0初始化文件serial.c复制到my_bootloader目录中.并修改serial.c

1.2      因为TAG结构体定义是存在u-boot-1.1.6/include/asm-arm/setup.h中,所以设置TAG参数需要用到这个文件,将setup.h复制到my_bootloader目录中.

1.2.1  修改setup.h文件

 删除以下不需要的代码:

#define __tag __attribute__((unused, __section__(".taglist")))

#define __tagtable(tag, fn) \

static struct tagtable __tagtable_##fn __tag = { tag, fn }


#define tag_member_present(tag,member)                                \

         ((unsigned long)(&((struct tag *)0L)->member + 1)  \

                   <= (tag)->hdr.size * 4)

添加以下代码:

#define  u32  unsigned  long
#define  u16  unsigned  int
#define  u8   unsigned  char

2. 新建my_bootloader/boor.c,用于存放main函数(main:由start.S跳转过来的).

main函数代码如下:

void main(void)

{

void (*theKernel)(int zero, int arch, unsigned int params); 

/*定义一个函数指针theKernel,其中第一个参数zero:0           */

/* arch:机器ID ,由于芯片类型很多,内核为了辨别芯片而定义的机器ID,其中2440芯片的ID号是362,*/

/* params :tag参数位置,这里我们的tag起始地址=0x30000100*/

  /*1 初 始 化 串 口 0 , 使 内 核 能 打 印 信 息  */

  uart0_init();                                             //调用serial.h头文件里的uart0_init()
  puts(“uart0 init OK\r\n”);                                //打印uart0初始化  

  /*2从  nand   flash  里 把 内 核 复 制 到  SDRAM  中 */

  puts(“copy  kernel  from  nand\r\n”);                  //打印内核复制
  nand_read((0x60000+64),0X30008000,0X200000);              //烧写2MB,多烧写点避免出错       

/*

0x60000+64:表示内核在nand(存储)地址上位置,

0X30008000:内核在sdram(运行)地址上位置

0X200000:内核长度2MB

因为Flash上存的内核格式是:uImage(64B头部(header)  + 真正的内核 )

在uboot界面中输入mtd命令可以看到:

              kernel分区位于 nand的0X00060000~0x00260000

所以在nand中真正的内核地址=0x60000+64,

在uboot界面中输入boot命令可以看到:

            Data Size:    1848656 Bytes =1.8 MB

            Load Address: 30008000

所以内核目的地址=0X30008000

长度=1.8MB

*/

  /*3 设  置  T  A  G  参  数        */

 puts(“set  boot  params\r\n”);                  //打印设置参数信息
 setup_start_tag (void);                      //在0X30000100地址保存start_tag数据,
 setup_memory_tags (void);                      //保存memory_tag数据,让内核知道内存多大
 setup_commandline_tag (“boottargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0”);  
 /*保存命令行bootargs参数,让内核知道根文件系统位置在/dev/mtdblock3,指定开机运行第一个脚本/linuxrc,指定打印串口0*/
 setup_end_tag (void);                            //初始化tag结构体结束

 
 /*  4 跳 转 执 行          */

   puts(“boot   kernel\r\n”);                      //打印启动内核
   theKernel = (void (*)(int, int, unsigend int))0x30008000;
    // 设置theKernel地址=0x30008000,用于后面启动内核
   theKernel(0,362,0x300000100);        //362:机器ID,  0x300000100: params(tag)地址
/*传递参数跳转执行到0x30008000启动内核,           */
/*相当于: mov r0,#0                             */
/*ldr r1,=362                                  */
/*ldr r2,= 0x300000100                         */
/*mov pc,#0x30008000                           */
  puts(“kernel  ERROR\r\n”);                  //打印内核启动出错

}

3.创建TAG参数函数(使main函数调用)

设置tag参数函数代码如下

#include “setup.h”              

 

static struct tag *params;               //定义个tag结构体变量params指针

setup_start_tag (void)                    //开始tag
{
 params = (struct tag *) 0x30000100;      //tag起始地址等于0X30000100
params->hdr.tag = ATAG_CORE;        //头部常量tag=0x54410001
params->hdr.size = tag_size (tag_core);    //size=5,

 params->u.core.flags = 0;
 params->u.core.pagesize = 0;
 params->u.core.rootdev = 0;
   params = tag_next (params);     //parmas=( struct tag *)((u32 *)parmas+ params->hdr.size)  
}

// setup_start_tag (bd)保存tag参数如下:
setup_memory_tags (void)                //内存tag
{

int i;
params->hdr.tag = ATAG_MEM;               //头部常量tag=0x54410002
params->hdr.size = tag_size (tag_mem32);       //size=4
params->u.mem.start = 0x30000000;            //SDRAM起始地址
params->u.mem.size = 0x4000000;             //SDRAM内存大小64M
params = tag_next (params);                  //指向下个tag
}  

// setup_memory_tag s(bd)保存tag参数如下:
int strlen(char  *str)          //uboot不依赖任何库,所以需要自己写strlen函数
{
   int  i=0;
   while(str[i])
   {
    i++;
   }
return i;
}

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

 

setup_commandline_tag (char  *cmdline)     //命令行tag
/**cmdline :指向命令行参数                                                             */
/*一般为:“boottargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0”     */
{
 int len=strlen(cmdline)+1;                       //计算cmdline长度,并加上结束符
params->hdr.tag = ATAG_CMDLINE;             //头部常量tag=0x54410009
params->hdr.size =(sizeof (struct tag_header) +len+3) >> 2;    /*size=(字符串长度+头部长度) >>2 */
/*“+3”表示:按4字节对齐,比如当总长度=(1,2,3,4)时,size=(总长度+3)>>2=1,实现4字节对齐         */ 
strcpy (params->u.cmdline.cmdline, cmdline);      //复制形参字符串到params->u.cmdline.cmdline
params = tag_next (params);                    //执行下个tag
}

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

4 写makefile文件

4.1 首先将lcd裸板程序里的makefile复制到my_bootloader目录中,并修改.

备注:在makefile中‘=’与‘:=’的区别:                                

‘=’:无关位置的等于(比如:”x=a  y=$(x)  x=b”,那么y的值永远等于最后的值,等于 b ,而不是a)                                                     

‘:=’:有关位置的等于(比如:”x:=a  y:=$(x)  x:=b”,那么y的值取决于当时位置的值,等于 a ,而不是b)   

代码如下:

CC      = arm-linux-gcc               //定义CC变量=arm-linux-gcc,简化书写,编译命令,(*.C,*.S)文件生成*.O文件

LD      = arm-linux-ld                 //连接命令,将多个*.O文件生成 boot.elf       

AR      = arm-linux-ar                 //库管理命令,这里没有用到 

OBJCOPY = arm-linux-objcopy            //复制/格式转换命令, boot.elf生成boot.dis

OBJDUMP = arm-linux-objdump           //反汇编命令,boot.bin生成boot.dis

 

CFLAGS           := -Wall -O2         //GCC编译参数,-Wall:显示所有错误和警告, -O2:采用2级编译优化

CPPFLAGS     := -nostdinc -fno-builtin       

//添加头文件参数,-nostdinc忽略缺省目录, -fno-builtin不连接系统标准启动文件和标准库文件(表示不用自带的strlen()等库函数)


objs := start.o init.o boot.o                     //定义objs变量,包含生成boot.bin目标文件需要的依赖文件


boot.bin: $(objs)                      //执行生成目标文件,首先是先满足objs所有依赖文件都拥有,才执行

         ${LD} -Tuboot.lds -o boot_elf $^

         ${OBJCOPY} -O binary -S boot_elf $@

         ${OBJDUMP} -D -m arm boot_elf > boot.dis

        
%.o:%.c                                //%通配符。生成xxx.o文件先要找到xxx.c文件

         ${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<   //-c编译不连接。$@表示目标文件   $<表示第一个依赖文件
 

%.o:%.S

         ${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

clean:

         rm -f *.bin *.elf *.dis *.o

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏coder修行路

Beego 框架学习(一)

 Beego官网本身已经整理的非常详细了,但是作为一个学习者,我还是决定自己好好整理一下,这样在后面使用的时候自己对每部分才能非常熟悉,即使忘记了,也可以迅速定...

3818
来自专栏程序员互动联盟

linux设备驱动第三篇:如何写一个简单的字符设备驱动

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动。本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符...

33714
来自专栏张戈的专栏

Linux基础知识之xargs命令

昨天在给服务器做年终“大扫除”整理时,发现有个目录下因为文件过多而删除失败,最终使用 xargs 才搞定,于是顺便来记录下。 在执行某些命令时,当 Linux ...

34612
来自专栏python学习路

三、scrapy后续 LinkExtractorsrules Logging发送POST请求内置设置参考手册

CrawlSpiders 通过下面的命令可以快速创建 CrawlSpider模板 的代码: scrapy genspider -t crawl tencent ...

3584
来自专栏pydata

java 开发常用错误集锦

1.org.apache.catalina.deply.WebXml addServer错误: Maven工程中用到的tomcat的catalina.jar,...

852
来自专栏Python中文社区

用Python实现微信接口(一)

專 欄 ❈爱撒谎的男孩,Python中文社区专栏作者 博客:https://chenjiabing666.github.io ❈ 安装 sudo pip in...

2406
来自专栏向治洪

JNI基础

JNI基础 将java中的字符串转换成C中字符串的工具方法 char* Jstring2CStr(JNIEnv* env, ...

18510
来自专栏技术博文

浅谈开启magic_quotes_gpc后的sql注入攻与防

通过启用php.ini配置文件中的相关选项,就可以将大部分想利用SQL注入漏洞的骇客拒绝于门外。        开启magic_quotes_gpc=on之后,...

3445
来自专栏阮一峰的网络日志

Node.js 命令行程序开发教程

一种编程语言是否易用,很大程度上,取决于开发命令行程序的能力。 Node.js 作为目前最热门的开发工具之一,怎样使用它开发命令行程序,是 Web 开发者应该掌...

3756
来自专栏猿人谷

使用bash编写Linux shell脚本--复合命令

除了最简单的脚本,你很少想要执行每一个命令。执行一组命令或者重复执行一组命令若干次比执行单个命令更加有助。复合命令是将命令封装在一组其他命令中。 从可读性来说,...

27310

扫码关注云+社区