引用

清风徐徐 的 U-boot给kernel传参数和kernel读取参数—struct tag

U-boot会给Linux Kernel传递很多参数,如:串口,RAM,videofb等。而Linux kernel也会读取和处理这些参数。两者之间通过struct tag来传递参数。U-boot把要传递给kernel的东西保存在struct tag数据结构中,启动kernel时,把这个结构体的物理地址传给kernel;Linux kernel通过这个地址,用parse_tags分析出传递过来的参数。

本文主要以U-boot传递RAM和Linux kernel读取RAM参数为例进行说明。

1、u-boot给kernel传RAM参数

./common/cmd_bootm.c文件中,bootm命令对应的do_bootm函数,当分析uImage中信息发现OS是Linux时,调用./lib_arm/bootm.c文件中的do_bootm_linux函数来启动Linux kernel。

在do_bootm_linux函数中:

void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],

ulong addr, ulong *len_ptr, int verify)

{

......

#if defined (CONFIG_SETUP_MEMORY_TAGS) || \

defined (CONFIG_CMDLINE_TAG) || \

defined (CONFIG_INITRD_TAG) || \

defined (CONFIG_SERIAL_TAG) || \

defined (CONFIG_REVISION_TAG) || \

defined (CONFIG_LCD) || \

defined (CONFIG_VFD)

setup_start_tag (bd);      //初始化tag结构体开始

#ifdef CONFIG_SERIAL_TAG

setup_serial_tag (&params);

#endif

#ifdef CONFIG_REVISION_TAG

setup_revision_tag (&params);

#endif

#ifdef CONFIG_SETUP_MEMORY_TAGS

setup_memory_tags (bd);      //设置RAM参数

#endif

#ifdef CONFIG_CMDLINE_TAG

setup_commandline_tag (bd, commandline);

#endif

#ifdef CONFIG_INITRD_TAG

if (initrd_start && initrd_end)

setup_initrd_tag (bd, initrd_start, initrd_end);

#endif

#if defined (CONFIG_VFD) || defined (CONFIG_LCD)

setup_videolfb_tag ((gd_t *) gd);

#endif

setup_end_tag (bd);              //初始化tag结构体结束

#endif

......

......

theKernel (0, machid, bd->bi_boot_params);

//传给Kernel的参数= (struct tag *)型的bd->bi_boot_params

//bd->bi_boot_params在board_init函数中初始化如对于at91rm9200,初始化在at91rm9200dk.c的board_init中进行:bd->bi_boot_params=PHYS_SDRAM + 0x100;

//这个地址也是所有taglist的首地址,见下面的setup_start_tag函数

}

对于setup_start_tag和setup_memory_tags函数说明如下。

函数setup_start_tag也在此文件中定义,如下:

static void setup_start_tag (bd_t *bd)

{

params = (struct tag *) bd->bi_boot_params;

//初始化(struct tag *)型的全局变量params为bd->bi_boot_params的地址,之后的setup tags相关函数如下面的setup_memory_tags就把其它tag的数据放在此地址的偏移地址上。

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);

}

RAM相关参数在bootm.c中的函数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);

}                   //初始化内存相关tag

}

2、Kernel读取U-boot传递的相关参数

对于Linux Kernel,ARM平台启动时,先执行arch/arm/kernel/head.S,此文件会调用arch/arm/kernel/head-common.S中的函数,并最后调用start_kernel:

......

b     start_kernel

......

init/main.c中的start_kernel函数中会调用setup_arch函数来处理各种平台相关的动作,包括了u-boot传递过来参数的分析和保存:

start_kernel()

{

......

setup_arch(&command_line);

......

}

其中,setup_arch函数在arch/arm/kernel/setup.c文件中实现,如下:

void __init setup_arch(char **cmdline_p)

{

struct tag *tags = (struct tag *)&init_tags;

struct machine_desc *mdesc;

char *from = default_command_line;

setup_processor();

mdesc = setup_machine(machine_arch_type);

machine_name = mdesc->name;

if (mdesc->soft_reboot)

reboot_setup("s");

if (__atags_pointer)

//指向各种tag起始位置的指针,定义如下:

//unsigned int __atags_pointer  __initdata;

//此指针指向__initdata段,各种tag的信息保存在这个段中。

tags = phys_to_virt(__atags_pointer);

else if (mdesc->boot_params)

tags = phys_to_virt(mdesc->boot_params);

if (tags->hdr.tag != ATAG_CORE)

convert_to_tag_list(tags);

if (tags->hdr.tag != ATAG_CORE)

tags = (struct tag *)&init_tags;

if (mdesc->fixup)

mdesc->fixup(mdesc, tags, &from, &meminfo);

if (tags->hdr.tag == ATAG_CORE) {

if (meminfo.nr_banks != 0)

squash_mem_tags(tags);

save_atags(tags);

parse_tags(tags);

//处理各种tags,其中包括了RAM参数的处理。

//这个函数处理如下tags:

__tagtable(ATAG_MEM, parse_tag_mem32);

__tagtable(ATAG_VIDEOTEXT, parse_tag_videotext);

__tagtable(ATAG_RAMDISK, parse_tag_ramdisk);

__tagtable(ATAG_SERIAL, parse_tag_serialnr);

__tagtable(ATAG_REVISION, parse_tag_revision);

__tagtable(ATAG_CMDLINE, parse_tag_cmdline);

}

init_mm.start_code = (unsigned long) &_text;

init_mm.end_code   = (unsigned long) &_etext;

init_mm.end_data   = (unsigned long) &_edata;

init_mm.brk       = (unsigned long) &_end;

memcpy(boot_command_line, from, COMMAND_LINE_SIZE);

boot_command_line[COMMAND_LINE_SIZE-1] = '\0';

parse_cmdline(cmdline_p, from);  //处理编译内核时指定的cmdline或u-boot传递的cmdline

paging_init(&meminfo, mdesc);

request_standard_resources(&meminfo, mdesc);

#ifdef CONFIG_SMP

smp_init_cpus();

#endif

cpu_init();

init_arch_irq = mdesc->init_irq;

system_timer = mdesc->timer;

init_machine = mdesc->init_machine;

#ifdef CONFIG_VT

#if defined(CONFIG_VGA_CONSOLE)

conswitchp = &vga_con;

#elif defined(CONFIG_DUMMY_CONSOLE)

conswitchp = &dummy_con;

#endif

#endif

early_trap_init();

}

对于处理RAM的tag,调用了parse_tag_mem32函数:

static int __init parse_tag_mem32(const struct tag *tag)

{

......

arm_add_memory(tag->u.mem.start, tag->u.mem.size);

......

}

__tagtable(ATAG_MEM, parse_tag_mem32);

上述的arm_add_memory函数定义如下:

static void __init arm_add_memory(unsigned long start, unsigned long size)

{

struct membank *bank;

size -= start & ~PAGE_MASK;

bank = &meminfo.bank[meminfo.nr_banks++];

bank->start = PAGE_ALIGN(start);

bank->size  = size & PAGE_MASK;

bank->node  = PHYS_TO_NID(start);

}

如上可见,parse_tag_mem32函数调用arm_add_memory函数把RAM的start和size等参数保存到了meminfo结构的meminfo结构体中。最后,在setup_arch中执行下面语句:

paging_init(&meminfo, mdesc);

对有MMU的平台上调用arch/arm/mm/nommu.c中的paging_init,否则调用arch/arm/mm/mmu.c中的paging_init函数。这里暂不分析mmu.c中的paging_init函数。

3、关于U-boot中的bd和gd

U-boot中有一个用来保存很多有用信息的全局结构体--gd_t(global data缩写),其中包括了bd变量,可以说gd_t结构体包括了u-boot中所有重要全局变量。最后传递给内核的参数,都是从gd和bd中来的,如上述的setup_memory_tags函数作用就是用bd中的值来初始化RAM相应的tag。

对于ARM平台这个结构体的定义大致如下:

include/asm-arm/global_data.h

typedef    struct      global_data {

bd_t        *bd;

unsigned long  flags;

unsigned long  baudrate;

unsigned long  have_console; /* serial_init() was called */

unsigned long  reloc_off;       /* Relocation Offset */

unsigned long  env_addr;       /* Address  of Environment struct */

unsigned long  env_valid;       /* Checksum of Environment valid? */

unsigned long  fb_base;  /* base address of frame buffer */

void        **jt;        /* jump table */

} gd_t;

在U-boot中使用gd结构之前要用先用宏DECLARE_GLOBAL_DATA_PTR来声明。这个宏的定义如下:

include/asm-arm/global_data.h

#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")

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

说明:本文的版本为U-boot-1.3.4、Linux-2.6.28,平台是ARM。

引用 U-boot给kernel传参数和kernel读取参数—struct tag的更多相关文章

  1. spring boot mapper层传参数是用main的arg0(第一个参数),arg1(第二个参数)

    spring boot mapper层传参数是用main的arg0(第一个参数),arg1(第二个参数) 大于三个参数,用map传递 public interface FrontMapper{ //= ...

  2. spring boot 参数传递(spring boot 参数传数 arg0 每一个参数 arg0#{arg0},arg1 #{arg1})

    spring boot 参数传数 arg0 每一个参数 arg0#{arg0},arg1  #{arg1} @Select("select * from sys_user where nam ...

  3. linux内核可以接受的参数 | Linux kernel启动参数 | 通过grub给内核传递参数

    在Linux中,给kernel传递参数以控制其行为总共有三种方法: 1.build kernel之时的各个configuration选项. 2.当kernel启动之时,可以参数在kernel被GRUB ...

  4. C#方法的六种参数,值参数、引用参数、输出参数、参数数组、命名参数、可选参数

    方法的参数有六种,分别是值参数.引用参数.输出参数.参数数组.命名参数.可选参数. 值参数 值参数是方法的默认类型,通过复制实参的值到形参的方式把数据传递到方法,方法被调用时,系统作两步操作: 在栈中 ...

  5. spring boot文件上传、下载

    主题:Spring boot 文件上传(多文件上传)[从零开始学Spring Boot]http://www.iteye.com/topic/1143595 Spring MVC实现文件下载http: ...

  6. Spring Boot应用上传文件时报错

    问题描述 Spring Boot应用(使用默认的嵌入式Tomcat)在上传文件时,偶尔会出现上传失败的情况,后台报错日志信息如下:"The temporary upload location ...

  7. C# WebApi 根据实体类检查传参或字典检查参数

    根据实体类或字典检查传参,是否缺少参数并返回缺少参数 值类型必须声明可空 /// <summary> /// 根据 Dictionary<string, string> 得到实 ...

  8. Spring框架学习笔记(7)——Spring Boot 实现上传和下载

    最近忙着都没时间写博客了,做了个项目,实现了下载功能,没用到上传,写这篇文章也是顺便参考学习了如何实现上传,上传和下载做一篇笔记吧 下载 主要有下面的两种方式: 通过ResponseEntity实现 ...

  9. [iOS 多线程 & 网络 - 2.6] - 使用POST上传JSON数据 & 多值参数

    A.上传JSON 1.思路: 必须使用POST方法才能上传大量JSON数据 设置请求头:设置Content-Type 设置请求体,JSON实际相当于字典,可以用NSDictionary NSJSONS ...

随机推荐

  1. 10,随机等概率的输出m个不重复的数

    今天看到一段代码,可以从0.....n-1中随机等概率的输出m个不重复的数(n远远大于m).遂记录下来. 首先,产生随机数,不免要用到srand,rand函数.先简单介绍下两个函数. 1,void s ...

  2. HTTP使用BASIC认证的原理及实现方法 (转载)

    转自:http://blog.itpub.net/23071790/viewspace-709367 一.   BASIC认证概述 在HTTP协议进行通信的过程中,HTTP协议定义了基本认证过程以允许 ...

  3. RFID电子标签制造封装工艺和可靠性研究

    一.目的和意义 电子标签已经成为RFID工业的主要焦点 实现低成本.大批量.高可靠性地制造电子标签是推广RFID产品应用的关键技术之一 针对RFID标签制造中核心的封装工艺开展研究,以各向异性导 ...

  4. 一劳永逸让windows 64位操作系统 禁止强制驱动签名

    如何让WINDOWS7 64位直接加载“禁用强制驱动程序签名”方式启动  Windows Client 论坛 > Windows 7 问题 0 登录进行投票 因为开发需要,要装一台设备的驱动,但 ...

  5. Spring-data-redis: 分布式队列

    Redis中list数据结构,具有"双端队列"的特性,同时redis具有持久数据的能力,因此redis实现分布式队列是非常安全可靠的.它类似于JMS中的"Queue&qu ...

  6. golang ODBC 访问access数据库(问题解决之心理路程)

    最近项目需要,需要操作access,以前是用VC++ OLE访问,网络用ACE库,感觉很庞大...决定用go试试 网上用的最多的就是这个https://github.com/weigj/go-odbc ...

  7. Mysql 创建联合主键

    Mysql 创建联合主键2008年01月11日 星期五 下午 5:21使用primary key (fieldlist)      比如:   create table mytable (       ...

  8. C#中的虚方法和抽象方法(Thirteenth Day)

    今天在云和学院学了很多,我这次只能先总结一下C#中的虚方法和抽象的运用. 理论: 虚方法: •用virtual修饰的方法叫做虚方法 •虚方法可以在子类中通过override关键字来重写 •常见的虚方法 ...

  9. IOS开发之格式化日期时间的使用 && 编程中常见问题

    今天在做一个有关时间的一些开发的时候,遇见了一写问题,反正来说既是很简单的问题,但毕竟用了我一些时间去调错误,遂记录之. 本来是想用 NSDateFormat 来转换一下服务器返回的时间,然后在客户端 ...

  10. Java 取得当前日期之后N天的日期 zz

    链接地址:http://blog.sina.com.cn/s/blog_483486840100vrjn.html public static String afterNDay(int n){    ...