【linux】U-BOOT与linux kernel通信: struct tag

 

欢迎转载,转载时需保留作者信息。

邮箱:[email protected]

博客园地址:http://www.cnblogs.com/embedded-tzp

Csdn博客地址:http://blog.csdn.net/xiayulewa

 

 

u-boot与linux通信格式

  

  

如上图,开机时执行u-boot, u-boot引导完后,就是交给linux系统了,但是linux需要一些基本信息,如内存大小,启动方式等,这就涉及到u-boot和linux通信。

而通信格式由linux规定了,以其中atag格式举例,详情查阅Documentation/arm/Setup

  

格式结构体为struct tag: 定义在 Setup.h (src\arch\arm\include\uapi\asm):147

struct tag {

struct tag_header hdr;

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;

};

 

u-boot会按照上述格式,在内存中划分一块atag参数区域,对该区域进行赋值。此处不对u-boot做讨论,后面在专门写文章。当赋值完成后,将cpu初始化成 MMU = off, D-cache = off, I-cache = dont care, r0 = 0, r1 = machine nr, r2 = atags or dtb pointer,跳转到linux代码起始处。

 

Linux atag参数解析流程

Linux代码执行后,在第一阶段(start_kernel函数之前),会验证该区域正确性(bl __vet_atags)。

进入第二阶段后(start_kernel函数之后):会开始真正解析atag参数,并赋值给相应的变量。

 

Init.h (src\include\linux) :中定义了大部分的 section,比较重要的有

#define __init __section(.init.text) __cold notrace

#define __initdata __section(.init.data)

#define __initconst __constsection(.init.rodata)

#define __exitdata __section(.exit.data)

#define __exit_call __used __section(.exitcall.exit)

 

流程:start_kernel →setup_arch→setup_machine_tags→parse_tags→parse_tag

 

在setup_arch中调用:

mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);

 

__atags_pointer定义为: unsigned int __atags_pointer __initdata;

__atags_pointer初始化: 在 head-common.S (src\arch\arm\kernel)中:

__mmap_switched:

      adr r3, __mmap_switched_data

      ldmia   r3!, {r4, r5, r6, r7}

      ……………………

ARM(   ldmia   r3, {r4, r5, r6, r7, sp})

…………………….

str  r2, [r6]                 @ Save atags pointer

……………………

      .type    __mmap_switched_data, %object

__mmap_switched_data:

      .long    __data_loc                 @ r4

      .long    _sdata                  @ r5

      .long    __bss_start                @ r6

      .long    _end                    @ r7

      .long    processor_id              @ r4

      .long    __machine_arch_type           @ r5

      .long    __atags_pointer              @ r6

 

         在函数 setup_machine_tags中有:

    if (__atags_pointer)

        tags = phys_to_virt(__atags_pointer);

    else if (mdesc->atag_offset)

        tags = (void *)(PAGE_OFFSET + mdesc->atag_offset);

得到tags = phys_to_virt(__atags_pointer); 见下图:

 

→parse_tags(tags);

 

static void __init parse_tags(const struct tag *t)

{

    for (; t->hdr.size; t = tag_next(t))

        if (!parse_tag(t))

            printk(KERN_WARNING

                "Ignoring unrecognised tag 0x%08x\n",

                t->hdr.tag);

}

static int __init parse_tag(const struct tag *tag)

{

    extern struct tagtable __tagtable_begin, __tagtable_end;

    struct tagtable *t;

 

    for (t = &__tagtable_begin; t < &__tagtable_end; t++)

        if (tag->hdr.tag == t->tag) {

            t->parse(tag);

            break;

        }

 

    return t < &__tagtable_end;

}

 

上述__tagtable_begin, __tagtable_end在src\arch\arm\kernel\vmlinux.lds

中定义:

.init.tagtable : {

  __tagtable_begin = .;

  *(.taglist.init)

  __tagtable_end = .;

 }

Setup.h (src\arch\arm\include\asm)可以看到.taglist.init段定义:

#define __tag __used __attribute__((__section__(".taglist.init")))

#define __tagtable(tag, fn) \

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

atags_parse.c (src\arch\arm\kernel) 中:有

__tagtable(ATAG_CORE, parse_tag_core);

__tagtable(ATAG_SERIAL, parse_tag_serialnr);等。

 

综上:parse_tags是依次处理每个struct tag, 对每个struct tag,扫描__tagtable_begin到__tagtable_end之间的struct tagtable,一旦对象的tag相等,则调用struct tagtable的parse, 具体为函数parse_tag_core, parse_tag_serialnr等函数。

 

u-boot命令行bootargs处理

http://blog.csdn.net/xiayulewa/article/details/45191679中有说过, u-boot可以通过

setenv bootargs root=/dev/ram0 initrd=0x32000000,0x200000 rootfstype=ext2 console=ttySAC0,57600 init=/linuxrc ip=192.168.1.3

给linux传递参数,详细的过程不说,经过上述讨论,知道u-boot和linux是以atag方式通信的,实际阅读u--boot源代码的确如此,上述红色部分会以ATAG_CMDLINE的方式传递。

         在Atags_parse.c (src\arch\arm\kernel)    中有:

__tagtable(ATAG_CMDLINE, parse_tag_cmdline);

parse_tag_cmdline设置了default_command_line数组。

而在setup_machine_tags函数中,        

           char *from = default_command_line;

.............

/* parse_early_param needs a boot_command_line */

strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);

又将default_command_line拷贝到boot_command_line数组

 

 

boot_command_line数组处理流程为:

start_kernel→parse_early_param(boot_command_line已经被setup_machine_tags函数赋值)

→parse_early_options→parse_args→parse_one→handle_unknown(param, val, doing)即do_early_param函数

 

/* Check for early params. */

static int __init do_early_param(char *param, char *val, const char *unused)

{

    const struct obs_kernel_param *p;

 

    for (p = __setup_start; p < __setup_end; p++) {

        if ((p->early && parameq(param, p->str)) ||

            (strcmp(param, "console") == 0 &&

             strcmp(p->str, "earlycon") == 0)

        ) {

            if (p->setup_func(val) != 0)

                pr_warn("Malformed early option ‘%s‘\n", param);

        }

    }

    /* We accept everything at this stage. */

    return 0;

}

 

在src\arch\arm\kernel\vmlinux.lds中有:

__setup_start = .; *(.init.setup) __setup_end = .;

 

而在Init.h (src\include\linux)     中,有.init.setup段的定义:

#define __setup_param(str, unique_id, fn, early)                     \

         static const char __setup_str_##unique_id[] __initconst       \

                   __aligned(1) = str; \

         static struct obs_kernel_param __setup_##unique_id  \

                   __used __section(.init.setup)                        \

                   __attribute__((aligned((sizeof(long)))))   \

                   = { __setup_str_##unique_id, fn, early }

 

以串口处理为例:Printk.c (src\kernel)中有

 __setup("console=", console_setup);

 

 

console=xxx串口名字为何必须为ttySAC

         前面已经讨论到__setup("console=", console_setup);了,当内核参数中有console=xxx时,便会执行

console_setup→__add_preferred_console

__add_preferred_console函数中有:

    for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)

        if (strcmp(console_cmdline[i].name, name) == 0 &&

              console_cmdline[i].index == idx) {

                if (!brl_options)

                    selected_console = i;

                return 0;

    }

    .........

    c = &console_cmdline[i];

    strlcpy(c->name, name, sizeof(c->name));

    c->options = options;

 

此时console_cmdline[i].name还未初始化,为空字符串,循环结束时i = 0。最后将console=xxx中的xxx赋值给console_cmdline[0].name

 

接下来,start_kernel→console_init

 

 

void __init console_init(void)

{

    initcall_t *call;

 

    /* Setup the default TTY line discipline. */

    tty_ldisc_begin();

 

    /*

     * set up the console device so that later boot sequences can

     * inform about problems etc..

     */

    call = __con_initcall_start;

    while (call < __con_initcall_end) {

        (*call)();

        call++;

    }

}

 

在src\arch\arm\kernel\vmlinux.lds中有:

         __con_initcall_start = .; *(.con_initcall.init) __con_initcall_end = .;

在Init.h (src\include\linux)中,有.con_initcall.init段定义。

#define console_initcall(fn) \

    static initcall_t __initcall_##fn \

    __used __section(.con_initcall.init) = fn

 

在Samsung.c (src\drivers\tty\serial)中有:

         console_initcall(s3c24xx_serial_console_init);

 

    故综上:有start_kernel→console_init→s3c24xx_serial_console_init→register_console(&s3c24xx_serial_console);

其中有代码:   

/*

     *  See if this console matches one we selected on

     *  the command line.

     */

    for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0];

            i++) {

        if (strcmp(console_cmdline[i].name, newcon->name) != 0)

            continue;

……………..

由此可见,只有s3c24xx_serial_console.name和console_cmdline[i].name的名字一样,console才能被正常初始化。

static struct console s3c24xx_serial_console = {

    .name      = S3C24XX_SERIAL_NAME,

    ……….

#define S3C24XX_SERIAL_NAME        "ttySAC"

 

综上:console=xxx串口名字必须为ttySAC,使用方式为下面红色部分标注的:

setenv bootargs root=/dev/ram0 initrd=0x32000000,0x200000 rootfstype=ext2 console=ttySAC0,57600 init=/linuxrc ip=192.168.1.3

时间: 2024-10-06 18:16:30

【linux】U-BOOT与linux kernel通信: struct tag的相关文章

Linux 线程与进程,以及通信

http://blog.chinaunix.net/uid-25324849-id-3110075.html 部分转自:http://blog.chinaunix.net/uid-20620288-id-3025213.html 1.首先要明确进程和线程的含义: 进程(Process)是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位.与程序相比,程序只是一组指令的有序集合,它本身没有任何运行的含义,只是一个静态实体.进程是程序在某个数据集上的执行,

linux用户态和内核态通信之netlink机制【转】

本文转载自:http://blog.csdn.net/zcabcd123/article/details/8272360 这是一篇学习笔记,主要是对<Linux 系统内核空间与用户空间通信的实现与分析>中的源码imp2的分析.其中的源码,可以到以下URL下载: http://www-128.ibm.com/developerworks/cn/Linux/l-netlink/imp2.tar.gz [size=3]参考文档[/size] <linux 系统内核空间与用户空间通信的实现与分析

Linux下c开发 之 线程通信(转)

Linux下c开发 之 线程通信(转) 1.Linux“线程” 进程与线程之间是有区别的,不过Linux内核只提供了轻量进程的支持,未实现线程模型.Linux是一种“多进程单线程”的操作系统.Linux本身只有进程的概念,而其所谓的“线程”本质上在内核里仍然是进程. 大家知道,进程是资源分配的单位,同一进程中的多个线程共享该进程的资源(如作为共享内存的全局变量).Linux中所谓的“线程”只是在被创建时clone了父进程的资源,因此clone出来的进程表现为“线程”,这一点一定要弄清楚.因此,L

windows下QT前台和linux下后台程序通过socket通信

通常情况下,linux下的后台程序不需要GUI进行展示,而前台程序往往有个界面,方便和用户的交互.本文所演示的例 子,是QT 程序和后台linux进程(C语言)交互,通过socket传输的内容是结构体.因为QT本身是跨平台的框架,因此以后前端程序移植到其它平台依然能很好 的运行. 结构体的定义如下: struct Test              {                      int a;                      char b;              };

Linux/UNIX之进程间的通信(2)

进程间的通信(2) 有三种IPC我们称为XSI IPC,即消息队列.信号量以及共享存储器,它们之间有很多相似之处. 标识符和键 每个内核的IPC结构(消息队列.信号量或共享存储段)都用一个非负整数的标识符加以引用.例如,为了对一个消息队列发送或取消息,只需要知道其队列标识符.与文件描述符不同,IPC标识符不是小的整数.当一个IPC结构被创建,以后被删除时,与这种结果相关的标识符连续加1,知道达到一个整型数的最大值,然后又回到0. 标识符是IPC对象的内部名.为使多个合作进程能够在同一IPC对象上

Linux 更新后异常(kernel 版本 3.17) VMware player

前几天手欠把linux的内和从3.16 升级到 3.17,结果就悲剧了,VMware不能正常启动了,一直报一个错误. 我这里的linux版本为:fedora20,其他发行版本也一样: VMware player版本: VMware-Player-6.0.4-2249910.x86_64.bundle 错误信息大概为: before you can run vmware several modules must be compiled and loaded into the running ker

分析Linux 0.11中的kernel部分的makefile文件

## 在UltraEdit下注释# # ## if you want the ram-disk device, define this to be the# size in blocks.#RAMDISK = #-DRAMDISK=512 ##8086汇编编译器和连接器. -0生成8086目标程序;-a生成与gas和gld部分兼容的代码???zzz#AS86 =as86 -0 -a LD86 =ld86 -0 ##GNU汇编编译器和连接器#AS =gas   LD =gld ##GNU连接器gl

Linux中 /boot 目录介绍 【转载】

Linux中 /boot 目录介绍 转自:点击打开链接 一./boot/目录中的文件和目录 Linux系统在本地启动时,目录/boot/非常重要,其中的文件和目录有: (1)系统Kernel的配置文件: (2)启动管理程序GRUB的目录,里面存放的都是GRUB在启动时所需要的画面.配置及各阶段(stage1, stage1.5, stage 2)的文件.见下图. (3)Initrd文件,是系统启动时的模块供应的主要来源: (4)System.map文件时系统Kernel中的变量对应表: (5)v

recover Windows 7 boot partition overwritten by Linux Mint boot loader

My Notebook installed with dual boot OS: Linux Mint 16 and Windows 7 During Linux Mint 17 overwritting version 16, I selected the windows boot partition as the mint boot loader device by mistake, so windows 7 failed to start after the installation. t