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: \0 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 ...\n", 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\n");  
            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\n");  
            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\n");  
                SHOW_BOOT_PROGRESS (-12);  
                do_reset (cmdtp, flag, argc, argv);  
            }  
            printf ("OK\n");  
        }  
  
        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\n");  
            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\n");  
    }  
#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) ...\n",  
           (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 ("\nStarting kernel ...\n\n");打印信息  
  
#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 == '\0')  
        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;  
}  

 



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM