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
              params->hdr.tag = ATAG_MEM;
              params->hdr.size = tag_size (tag_mem32);
              params->u.mem.start = bd->bi_dram .start;
              params->u.mem.size = bd->bi_dram.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 。

//补充一下:
来自:http://hi.baidu.com/armfans/blog/item/306cd5035f24ff084afb514b.html
bootloader巧妙地利用函数指针及传参规范将R0:0x0,R1:机器号,R2:参数地址传递给内核.由于R0,R1比较简单,不需要再作说明.需要花点时间了解的是R2寄存器.
  R2寄存器传递的是一个指针,这个指针指向一个TAG区域.UBOOT和Linux内核之间正是通过这个扩展了的TAG区域来进行复杂参数的传递,如 command line,文件系统信息等等,用户也可以扩展这个TAG来进行更多参数的传递.TAG区域存放的地址,也就是R2的值,是在/board /yourboard/youboard.c里的board_init函数中初始化的,如在UB4020中初始化为:gd->bd->bi_boot_params = 0x30000100;,这是一个绝对地址.
  TAG区的结构比较简单,可以视为一个一个TAG的排列(数组?),每一个TAG传递一种特定类型的参数.各种系统TAG的定义可以参考./include/asm-arm/setup.h.
  下面是一个TAG区的例子:
  0x30000100 00000005 54410001 00000000 00000000
  0x30000110 00000000 0000000F 54410009 746F6F72
  0x30000120 65642F3D 61722F76 7220306D 6F632077
  0x30000130 6C6F736E 74743D65 2C305379 30303639
  0x30000140 696E6920 6C2F3D74 78756E69 EA006372
  0x30000150 00000004 54420005 30300040 00200000
  0x30000160 00000000 00000000
  我们可以看到一共有三个TAG:
  第一个TAG的长度是5个字,类型是ATAG_CORE(54410001),有三个元素,均为全零.TAG区必须以这个TAG开头.
  第二个TAG的长度是F个字,类型是ATAG_CMDLINE(54410009),这是一个字符串,是向内核传递的kernel command line
  第三个TAG的长度是4个字,类型是ATAG_INITRD2(54410005),有两个元素,第一个是start:30300040(30300000+40),第二个是size:200000(2M)
  如果说还有第四个TAG,那就是末尾的两个全零,这是TAG结束的标志.
  这些TAG是在./lib_arm/arm_linux.c中的do_bootm_linux函数中建立起来的.具体建立哪些TAG,由相应的控制宏决定.具体可以参考相应代码.例子中第一个TAG是起始TAG,如果环境变量中有bootargs,则建立第二个TAG,如果bootm有两个参数(引导文件系统),则会读取文件系统头部的必要信息,建立第三个TAG.
  内核启动后,将根据R2寄存器的值找到这些TAG,并根据TAG类型,调用相应的处理函数进行处理,从而获取内核运行的必要信息.
本文来自CSDN博客,转载请标明出处:
http://blog.csdn.net/lanmanck/archive/2009/06/24/4294685.aspx


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/253776.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

异步FIFO设计(Verilog)

FIFO(First In First Out)是异步数据传输时经常使用的存储器。该存储器的特点是数据先进先出(后进后出)。其实,多位宽数据的异步传输问题,无论是从快时钟到慢时钟域,还是从慢时钟到快时钟域&…

python中RabbitMQ的使用(路由键模糊匹配)

路由键模糊匹配 使用正则表达式进行匹配。其中“#”表示所有、全部的意思;“*”只匹配到一个词。 匹配规则: 路由键:routings [ happy.work, happy.life , happy.work.teacher, sad.work, sad.life, sad.work.teacher ] "#"&am…

数据仓库事实表分类[转]

1)在数据仓库领域有一个概念叫Transaction fact table,中文一般翻译为“事务事实表”。 事务事实表是维度建模的数据仓库中三种基本类型事实表中的一种,另外两种分别是周期快照事实表和累积快照事实表。 事务事实表与周期快照事实表、累积快…

嵌入式系统文件系统比较 jffs2, yaffs, cramfs, romfs, ramdisk, ramfs/tmpfs

Linux支持多种文件系统,包括ext2、ext3、vfat、ntfs、iso9660、jffs、romfs和nfs等,为了对各类文件系统 进行统一管理,Linux引入了虚拟文件系统VFS(Virtual File System),为各类文件系统提供一个统一的操作界面和应用编程接口。 …

Codeforces Beta Round #17 C. Balance DP

C. Balance题目链接 http://codeforces.com/contest/17/problem/C 题面 Nick likes strings very much, he likes to rotate them, sort them, rearrange characters within a string... Once he wrote a random string of characters a, b, c on a piece of paper and began t…

时钟切换处理(Verilog)

随着各种应用场景的限制,芯片在运行时往往需要在不同的应用下切换不同的时钟源,例如低功耗和高性能模式就分别需要低频率和高频率的时钟。两个时钟源有可能是同源且同步的,也有可能是不相关的。直接使用选择逻辑进行时钟切换大概率会导致分频…

SSH整合中,使用父action重构子类action类.(在父类中获取子类中的泛型对象)

import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type;import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven;/*** 文件名 : BaseAction.java* 提取SSH中的action类* 由于SSH的action中采用模型驱动的方法,使用泛…

用BusyBox制作Linux根文件系统

STEP 1:构建目录结构 创建根文件系统目录,主要包括以下目录 /dev /etc /lib /usr /var /proc /tmp /home /root /mnt /bin /sbin /sys #mkdir /home/rootfs #cd /home/rootfs #mkdir dev etc lib usr var proc tmp home roo…

Angular Elements 组件在非angular 页面中使用的DEMO

2019独角兽企业重金招聘Python工程师标准>>> 一、Angular Elements 介绍 Angular Elements 是伴随Angular6.0一起推出的新技术。它借助Chrome浏览器的ShadowDom API,实现一种自定义组件。 这种组件可以用Angular普通组件的开发技术进行编写,…

(转) android里,addContentView()动态增加view控件,并实现控件的顶部,中间,底部布局...

http://blog.csdn.net/bfboys/article/details/52563089转载于:https://www.cnblogs.com/zhangminghan/p/6182909.html

verilog仿真——$test$plusargs 和 $value$plusargs

VERILOG的参数可以用define和parameter的方式定义,这种方法要求我们在编译前将变量必须定义好,编译完成之后再也不能修改; 然而,有时候我们在进行仿真时,需要从外部传递参数,这个要求怎么满足呢&#xff1…

卢卡斯定理

卢卡斯定理:解决一类组合数取模问题 A、B是非负整数,p是质数。AB写成p进制:Aa[n]a[n-1]...a[0],Bb[n]b[n-1]...b[0]。 则组合数C(A,B)与C(a[n],b[n])*C(a[n-1],b[n-1])*...*C(a[0],b[0]) modp同余 即:Lucas(n,m,p)c(n%p,m%p)*Luc…

内核理解

在纯技术方面,内核是硬件与软件之间的一个中间层。其作用是将应用程序的请求传递给硬件,并充当底层的驱动程序,对系统中的各种设备和组件。内核启动init程序作为第一个进程,该进程负责进一步的系统初始化操作,并显示登…

loadrunner中对https证书的配置

1、准备好网站的证书,一般证书是cer格式; 2、因为loadrunner只支持pem格式的证书,所以要将证书转换格式,利用openssl工具;(或者直接让开发提供pem格式的证书)3、得到pem格式的证书之后&#xff…

Android 9 Pie震撼来袭 同步登陆WeTest

作者:We Test小编商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处。原文链接:wetest.qq.com/lab/view/40…WeTest 导读2018年8月7日,Google对外发布最新 Android 9.0 正式版系统,并宣布系统版本Android P 被正…

Datapath综合代码规范(Verilog)

一、一般准则 1、有符号数运算 利用类型“signed”完成有符号数运算,而不是用无符号数模拟有符号数运算。这样可以得到更好的QoR。在资源报告中检查操作数的类型和大小。 2、符号/零扩展 尽量不要手动扩展。verilog利用signed/unsigned会自动完成扩展。这样代码可…

Linux下V4L2编程小结

http://www.360doc.com/content/12/0318/16/532901_195392228.shtml :davind dm365linux移植 http://www.embedhq.org/html/jsbw/2010/0425/390.html :Linux下V4L2编程小结

百(垃)度(圾)之星初赛B hdu6114

Chess 题意:中文题 思路:其实就是在n个格子上放m个棋子(n>m)(xjb套Lucas的板子... AC代码: #include "iostream" #include "string.h" #include "stack" #include "…

variable 'xxx' unsafe in 'case'的处理

问题描述: case get(?Player_LoopTaskInfo) of{TargetCnt, TaskStar, TaskExp} ->ok;_ ->throw("not_found_loop_task_info") end 在case语句中,这样写,编译时,会提示变量unsafe,解决编译器报错的…

SDUT 3347 数据结构实验之数组三:快速转置

数据结构实验之数组三&#xff1a;快速转置 Time Limit: 1000 ms Memory Limit: 65536 KiBProblem Description 转置运算是一种最简单的矩阵运算&#xff0c;对于一个m*n的矩阵M( 1 < m < 10000,1 < n < 10000 )&#xff0c;它的转置矩阵T是一个n*m的矩阵&…