zoukankan      html  css  js  c++  java
  • linux下lk和kernel层通信方式[2]

    U-Boot与Linux内核的交互

    说明:本文所使用的U-Boot的版本是1.1.6,平台是S3C2440。

    目录

    一、简介
    1.1标记列表
    二、设置标记存放的地址
    2.1相关的结构体定义
    2.2标记存放地址的设定
    三、标记的设置
    3.1设置标记ATAG_CORE
    3.2设置内存标记ATAG_MEM
    3.3设置命令行标记ATAG_CMDLINE
    3.4设置ATAG_NONE

    一、简介

    U-Boot与Linux内核的交互是单向的,U-Boot将各类参数传递给内核。由于他们不能同时运行,传递办法只能有一个个:U-Boot将参数放在某个约定的地方之后,在启动内核,内核启动后从这个地方获得参数。

    1.1标记列表

    除了约定好参数存放的地方外,还要规定参数的结构。Linux2.4.x以后的内核都以标记列表(tagged list)的形式来传递参数。标记就是一种数据结构;标记列表就是挨着存放的多个标记。标记列表以标记ATAG_CORE开始,以ATAGE_NONE结束。

    标记的数据结构为tag,它是偶一个tag_header结构和一个联合体(union)组成。tag_header结构体表示标记的类型及长度,比如是表示内存还是表示命令行参数等。对于不同类型的标记使用不同的联合体,比如表示内存=时使用tag_men32,表示命令行时使用tag_cmdline。其定定义在include/asm-arm/setup.c文件中。

    /*  
     * The new way of passing information: a list of tagged entries  
     */  
      
    /* The list ends with an ATAG_NONE node. */  
    #define ATAG_NONE   0x00000000  
      
    struct tag_header {  
        u32 size;  
        u32 tag;  
    };  
      
    /* The list must start with an ATAG_CORE node */  
    #define ATAG_CORE   0x54410001  
      
    struct tag_core {  
        u32 flags;      /* bit 0 = read-only */  
        u32 pagesize;  
        u32 rootdev;  
    };  
      
    /* it is allowed to have multiple ATAG_MEM nodes */  
    #define ATAG_MEM    0x54410002  
      
    struct tag_mem32 {  
        u32 size;  
        u32 start;  /* physical start address */  
    };  
      
    /* VGA text type displays */  
    #define ATAG_VIDEOTEXT  0x54410003  
      
    struct tag_videotext {  
        u8      x;  
        u8      y;  
        u16     video_page;  
        u8      video_mode;  
        u8      video_cols;  
        u16     video_ega_bx;  
        u8      video_lines;  
        u8      video_isvga;  
        u16     video_points;  
    };  
      
    /* describes how the ramdisk will be used in kernel */  
    #define ATAG_RAMDISK    0x54410004  
      
    struct tag_ramdisk {  
        u32 flags;  /* bit 0 = load, bit 1 = prompt */  
        u32 size;   /* decompressed ramdisk size in _kilo_ bytes */  
        u32 start;  /* starting block of floppy-based RAM disk image */  
    };  
      
    /* describes where the compressed ramdisk image lives (virtual address) */  
    /*  
     * this one accidentally used virtual addresses - as such,  
     * its depreciated.  
     */  
    #define ATAG_INITRD 0x54410005  
      
    /* describes where the compressed ramdisk image lives (physical address) */  
    #define ATAG_INITRD2    0x54420005  
      
    struct tag_initrd {  
        u32 start;  /* physical start address */  
        u32 size;   /* size of compressed ramdisk image in bytes */  
    };  
      
    /* board serial number. "64 bits should be enough for everybody" */  
    #define ATAG_SERIAL 0x54410006  
      
    struct tag_serialnr {  
        u32 low;  
        u32 high;  
    };  
      
    /* board revision */  
    #define ATAG_REVISION   0x54410007  
      
    struct tag_revision {  
        u32 rev;  
    };  
      
    /* initial values for vesafb-type framebuffers. see struct screen_info  
     * in include/linux/tty.h  
     */  
    #define ATAG_VIDEOLFB   0x54410008  
      
    struct tag_videolfb {  
        u16     lfb_width;  
        u16     lfb_height;  
        u16     lfb_depth;  
        u16     lfb_linelength;  
        u32     lfb_base;  
        u32     lfb_size;  
        u8      red_size;  
        u8      red_pos;  
        u8      green_size;  
        u8      green_pos;  
        u8      blue_size;  
        u8      blue_pos;  
        u8      rsvd_size;  
        u8      rsvd_pos;  
    };  
      
    /* command line:  terminated string */  
    #define ATAG_CMDLINE    0x54410009  
      
    struct tag_cmdline {  
        char    cmdline[1]; /* this is the minimum size */  
    };  
      
    /* acorn RiscPC specific information */  
    #define ATAG_ACORN  0x41000101  
      
    struct tag_acorn {  
        u32 memc_control_reg;  
        u32 vram_pages;  
        u8 sounddefault;  
        u8 adfsdrives;  
    };  
      
    /* footbridge memory clock, see arch/arm/mach-footbridge/arch.c */  
    #define ATAG_MEMCLK 0x41000402  
      
    struct tag_memclk {  
        u32 fmemclk;  
    };  
      
    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;  
    };  
      
    #define tag_next(t)<span style="white-space:pre">   </span>((struct tag *)((u32 *)(t) + (t)->hdr.size))  
    #define tag_size(type)<span style="white-space:pre">    </span>((sizeof(struct tag_header) + sizeof(struct type)) >> 2) //???  

    二、设置标记存放的地址

    2.1相关的结构体定义

    结构体bd中保存了标记存放的地址。bd结构体是gd结构体的一项,我们先看gd结构体,其定义在include/asm-arm/global_data.h文件中:

    typedef struct  global_data {  
        bd_t        *bd;//开发板相关参数 ,结构体变量,参考u-boot.h   
        unsigned long   flags;//指示标志,如设备已经初始化标志等  
        unsigned long   baudrate;//串行口通讯速率  
        unsigned long   have_console;  
        /* serial_init() was called 如果执行了该函数,则设置为1 */  
        unsigned long   reloc_off;    
        /*   
         *Relocation Offset 重定位偏移,就是实际定向的位置与编译连接时指定的位置之差,一般为0  
            */  
        unsigned long   env_addr;   /* 环境参数地址*/  
        unsigned long   env_valid;  /* 环境参数CRC检验有效标志*/  
        unsigned long   fb_base;    /*帧缓冲区基地址*/  
    #ifdef CONFIG_VFD  
        unsigned char   vfd_type;   /* 显示类型*/  
    #endif  
    #if 0  
        unsigned long   cpu_clk;    /*cpu时钟*/  
        unsigned long   bus_clk;    //总线时钟  
        unsigned long   ram_size;   /* RAM size */  
        unsigned long   reset_status;   /* reset status register at boot */  
    #endif  
        void        **jt;   /* jump table 跳转表,用来登记"函数调用地址"*/  
    } gd_t;  

    接来下我们来看一下bd结构体,这个结构体定义在include/asm-arm/u-boot.h文件中:

    typedef struct bd_info {  
        int         bi_baudrate;    /* 串口波特率*/  
        unsigned long   bi_ip_addr; /*  IP 地址*/  
        unsigned char   bi_enetaddr[6]; /* MAC地址*/  
        struct environment_s           *bi_env;  
        ulong           bi_arch_number; /*  板子的id*/  
        ulong           bi_boot_params; /* 启动参数*/  
        struct              /* RAM 配置*/  
        {  
        ulong start;  
        ulong size;  
        }bi_dram[CONFIG_NR_DRAM_BANKS];  
    #ifdef CONFIG_HAS_ETH1  
        /* second onboard ethernet port */  
        unsigned char   bi_enet1addr[6];  
    #endif  
    } bd_t;  

    2.2标记存放地址的设定

    在board/smdk2410/smdk2410.c的board_init 函数设置了bi_boot_params 参数:

    int board_init (void)  
    {  
        S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();//获取时钟和电源配置寄存器的第一个寄存器的地址,寄存器的地上是连续的  
        S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();//获取GPIO配置寄存器的第一个寄存器的地址  
      
        /* to reduce PLL lock time, adjust the LOCKTIME register */  
        clk_power->LOCKTIME = 0xFFFFFF;  
      
        /* configure MPLL */  
        clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);  
      
        /* some delay between MPLL and UPLL */  
        delay (4000);  
      
        /* configure UPLL */  
        clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV);  
      
        /* some delay between MPLL and UPLL */  
        delay (8000);  
      
        /* set up the I/O ports */  
        gpio->GPACON = 0x007FFFFF;  
        gpio->GPBCON = 0x00044555;  
        gpio->GPBUP = 0x000007FF;  
        gpio->GPCCON = 0xAAAAAAAA;  
        gpio->GPCUP = 0x0000FFFF;  
        gpio->GPDCON = 0xAAAAAAAA;  
        gpio->GPDUP = 0x0000FFFF;  
        gpio->GPECON = 0xAAAAAAAA;  
        gpio->GPEUP = 0x0000FFFF;  
        gpio->GPFCON = 0x000055AA;  
        gpio->GPFUP = 0x000000FF;  
        gpio->GPGCON = 0xFF95FFBA;  
        gpio->GPGUP = 0x0000FFFF;  
        gpio->GPHCON = 0x002AFAAA;  
        gpio->GPHUP = 0x000007FF;  
      
        /* arch number of SMDK2410-Board */  
        gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;  
      
        /* adress of boot parameters */  
        gd->bd->bi_boot_params = 0x30000100;  
      
        icache_enable();  //调用cpu/arm920t/cpu.c中的函数  
        dcache_enable();  
      
        return 0;  
    }  

    三、标记的设置

    U-Boot通过bootm命令引导Linux内核,bootm命令对吼调用do_bootm_linux函数来引导内核。在do_bootm_linux函数就设置了标记,该函数的定义在lib_arm/armlinux.c中:

    void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],  
                 ulong addr, ulong *len_ptr, int verify)  
    {  
        ulong len = 0, checksum;  
        ulong initrd_start, initrd_end;  
        ulong data;  
        void (*theKernel)(int zero, int arch, uint params);  
        image_header_t *hdr = &header;  
        bd_t *bd = gd->bd;  
      
    #ifdef CONFIG_CMDLINE_TAG  
        char *commandline = getenv ("bootargs");  
    #endif  
      
        theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);  
        设置kernal加载地址  
      
        /*  
         * Check if there is an initrd image  
         */  
        用户自定义了initrd之后需要加载进来,整个过程需要进行头部以及整个数据内部校,类似于内核的加载校验,这里省略了。  
    initial RAM disk  Linux初始 RAM磁盘(initrd)是在系统引导过程中挂载的一个临时根文件系统,用来支持两阶段的引导过程。initrd文件中包含了各种可执行程序和驱动程序,它们可以用来挂载实际的根文件系统,然后再将这个 initrd RAM 磁盘卸载,并释放内存。在很多嵌入式Linux 系统中,initrd 就是最终的根文件系统。  
        if (argc >= 3) {  
            SHOW_BOOT_PROGRESS (9);  
      
            addr = simple_strtoul (argv[2], NULL, 16);  
      
            printf ("## Loading Ramdisk Image at %08lx ...
    ", addr);  
      
            /* Copy header so we can blank CRC field for re-calculation */  
    #ifdef CONFIG_HAS_DATAFLASH  
            if (addr_dataflash (addr)) {  
                read_dataflash (addr, sizeof (image_header_t),  
                        (char *) &header);  
            } else  
    #endif  
                memcpy (&header, (char *) addr,  
                    sizeof (image_header_t));  
      
            if (ntohl (hdr->ih_magic) != IH_MAGIC) {  
                printf ("Bad Magic Number
    ");  
                SHOW_BOOT_PROGRESS (-10);  
                do_reset (cmdtp, flag, argc, argv);  
            }  
      
            data = (ulong) & header;  
            len = sizeof (image_header_t);  
      
            checksum = ntohl (hdr->ih_hcrc);  
            hdr->ih_hcrc = 0;  
      
            if (crc32 (0, (unsigned char *) data, len) != checksum) {  
                printf ("Bad Header Checksum
    ");  
                SHOW_BOOT_PROGRESS (-11);  
                do_reset (cmdtp, flag, argc, argv);  
            }  
      
            SHOW_BOOT_PROGRESS (10);  
      
            print_image_hdr (hdr);  
      
            data = addr + sizeof (image_header_t);  
            len = ntohl (hdr->ih_size);  
      
    #ifdef CONFIG_HAS_DATAFLASH  
            if (addr_dataflash (addr)) {  
                read_dataflash (data, len, (char *) CFG_LOAD_ADDR);  
                data = CFG_LOAD_ADDR;  
            }  
    #endif  
      
            if (verify) {  
                ulong csum = 0;  
      
                printf ("   Verifying Checksum ... ");  
                csum = crc32 (0, (unsigned char *) data, len);  
                if (csum != ntohl (hdr->ih_dcrc)) {  
                    printf ("Bad Data CRC
    ");  
                    SHOW_BOOT_PROGRESS (-12);  
                    do_reset (cmdtp, flag, argc, argv);  
                }  
                printf ("OK
    ");  
            }  
      
            SHOW_BOOT_PROGRESS (11);  
      
            if ((hdr->ih_os != IH_OS_LINUX) ||  
                (hdr->ih_arch != IH_CPU_ARM) ||  
                (hdr->ih_type != IH_TYPE_RAMDISK)) {  
                printf ("No Linux ARM Ramdisk Image
    ");  
                SHOW_BOOT_PROGRESS (-13);  
                do_reset (cmdtp, flag, argc, argv);  
            }  
      
    #if defined(CONFIG_B2) || defined(CONFIG_EVB4510) || defined(CONFIG_ARMADILLO)  
            /*  
             *we need to copy the ramdisk to SRAM to let Linux boot  
             */  
            memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);  
            data = ntohl(hdr->ih_load);  
    #endif /* CONFIG_B2 || CONFIG_EVB4510 */  
      
            /*  
             * Now check if we have a multifile image  
             */  
        } else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) {  
            ulong tail = ntohl (len_ptr[0]) % 4;  
            int i;  
      
            SHOW_BOOT_PROGRESS (13);  
      
            /* skip kernel length and terminator */  
            data = (ulong) (&len_ptr[2]);  
            /* skip any additional image length fields */  
            for (i = 1; len_ptr[i]; ++i)  
                data += 4;  
            /* add kernel length, and align */  
            data += ntohl (len_ptr[0]);  
            if (tail) {  
                data += 4 - tail;  
            }  
      
            len = ntohl (len_ptr[1]);  
      
        } else {  
            /*  
             * no initrd image  
             */  
            SHOW_BOOT_PROGRESS (14);  
      
            len = data = 0;  
        }  
      
    #ifdef  DEBUG  
        if (!data) {  
            printf ("No initrd
    ");  
        }  
    #endif  
      
        if (data) {  
            initrd_start = data;  
            initrd_end = initrd_start + len;  
        } else {  
            initrd_start = 0;  
            initrd_end = 0;  
        }  
      
        SHOW_BOOT_PROGRESS (15);  
      
        debug ("## Transferring control to Linux (at address %08lx) ...
    ",  
               (ulong) theKernel);  
      
    #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,用于传递参数给Linux  
    #ifdef CONFIG_SERIAL_TAG  
        setup_serial_tag (¶ms);  
    #endif  
    #ifdef CONFIG_REVISION_TAG  
        setup_revision_tag (¶ms);  
    #endif  
    #ifdef CONFIG_SETUP_MEMORY_TAGS  
        setup_memory_tags (bd);  
    #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);  
    #endif  
      
        /* we assume that the kernel is in place */  
        printf ("
    Starting kernel ...
    
    ");打印信息  
      
    #ifdef CONFIG_USB_DEVICE  
        {  
            extern void udc_disconnect (void);  
            udc_disconnect ();  
        }  
    #endif  
      
        cleanup_before_linux ();启动之前先做一些清理工作cpu/arm920t/cpu.c  
      
    调用内核需要传递的参数如下:  
    R0:必须为0  
    R1:机器类型ID,本机为ARM(bd->bi_arch_number)  
    R2:启动参数列表在内存中的位置(bd->bi_boot_params)  
        theKernel (0, bd->bi_arch_number, bd->bi_boot_params);  
    }  
     

     

    3.1设置标记ATAG_CORE

    标记列表以标记ATAG_CORE开始

    static void setup_start_tag (bd_t *bd)  
    {  
        params = (struct tag *) bd->bi_boot_params;  
      
        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);//指向当前标记的末尾  
    }  
     

    3.2设置内存标记ATAG_MEM

    在board/smdk2410/smdk2410.c的dram_init函数设置了bd的bi_dram结构体:

    int dram_init (void)  
    {  
        gd->bd->bi_dram[0].start = PHYS_SDRAM_1;  
        gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;  
      
        return 0;  
    }  

    下面是这边内存标记的结构体:

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

    3.3设置命令行标记ATAG_CMDLINE

    命令行就是一个字符串,用来控制内核的一些行为。比如“root=/dev/mtdblock2 init=/linuxrc console=ttySAC0 ”表示根文件系统在MTD2分区上系统启动后执行的第一个程序为/linuxrc,控制台是ttySAC0 。

    static void setup_commandline_tag (bd_t *bd, char *commandline)  
    {  
        char *p;  
      
        if (!commandline)  
            return;  
      
        /* eat leading white space */  
        for (p = commandline; *p == ' '; p++);  
      
        /* skip non-existent command lines so the kernel will still  
         * use its default command line.  
         */  
        if (*p == '')  
            return;  
      
        params->hdr.tag = ATAG_CMDLINE;  
        params->hdr.size =  
            (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;  
      
        strcpy (params->u.cmdline.cmdline, p);  
      
        params = tag_next (params);  
    }  
     

    3.4设置ATAG_NONE

    标记列表以标记ATAG_NONE介绍。

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

  • 相关阅读:
    day36 08-Hibernate抓取策略:批量抓取
    day36 07-Hibernate抓取策略:many-to-one上的抓取策略
    day36 06-Hibernate抓取策略:set集合上的抓取策略
    day36 05-Hibernate检索方式:离线条件查询
    day36 04-Hibernate检索方式:多表连接查询
    day36 03-Hibernate检索方式:排序、参数绑定、投影查询
    【收集】11款Linux数据恢复工具
    【收集】11款Linux数据恢复工具
    7个免费的Linux FTP客户端工具
    7个免费的Linux FTP客户端工具
  • 原文地址:https://www.cnblogs.com/Ph-one/p/5580931.html
Copyright © 2011-2022 走看看