Saturday, May 26, 2012

Bootloader與Kernel間參數傳遞機制




Tag list 被用來在 bootloader  linux kernel 之間傳遞參數,這裡分析一下相關的數據結構,主要包括兩個部分: Tag list  Tag parser list 
先來看 Tag list 
這個 list 是在 bootloader 中填充的,其數據結構定義在 bootloader  linux kernel 中均有定義,是一致的。我們來看 linux kernel 中的定義:

top/arch/arm/include/asm/setup.h
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;
} ;
struct tag_header {
    __u32 size;
    __u32 tag;
} ;

其中 tag 的取值如下,暫且稱之為 tag type :

# define ATAG_CORE            0x54410001
# define ATAG_MEM            0x54410002
# define ATAG_VIDEOTEXT        0x54410003
# define ATAG_RAMDISK        0x54410004
# define ATAG_INITRD            0x54410005
# define ATAG_INITRD2        0x54420005
# define ATAG_SERIAL            0x54410006
# define ATAG_REVISION        0x54410007
# define ATAG_VIDEOLFB        0x54410008
# define ATAG_CMDLINE        0x54410009
# define ATAG_ACORN            0x41000101
# define ATAG_MEMCLK        0x41000402

其數據結構用圖形表示就是:
 
其實就是一個鏈表,通過 Tag size 以及當前 tag 的位置來定位下一個 tag 的位置。而且第一個 tag 的類型必然是 ATAG_CORE 

參數就是按照這個結構進行傳遞的,那麼 kernel 是如何進行解析的呢?
我們來看 tag parser list:
同樣是在 top/arch/arm/include/asm/setup.h ,有如下定義:

top/arch/arm/include/asm/setup.h
struct tagtable {
    __u32 tag;
    int ( * parse) ( const struct tag * ) ;
} ;
# define __tag __used __attribute__( ( __section__( ".taglist.init" ) ) )
# define __tagtable( tag, fn) /
static struct tagtable __tagtable_# # fn __tag = { tag, fn }

從上面知道, tag parser list 存在於 .taglist.init 段,他們的定義將通過宏 __tagtable(tag, fn) 的形式給出,比如在 top/arch/arm/kernel/setup.c 中:

top/arch/arm/kernel/setup.c
__tagtable( ATAG_CORE, parse_tag_core) ;
__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) ;

通過這樣的定義,每個 tag table 的表項就自 動連接在了一起,而且存在於同一個段中。如圖所示:
可以看到,所有支持的 tag parser 都列在這 裡了。
 kernel 中,將針對 tag list 中的每一項在這個 tag parser list 中進行查找,如果有 對應的處理項,則調用解析函數,於是就完成了參數的傳遞以及解析!

注意:
 top/arch/arm/kernel/head-common.s 中會對從 bootloader 傳遞過來的 tag list 進行合法性判斷:
以標號 __vet_atags 開始的一段處理就要是 判斷 tag list 的第一項是否是 ATAG_CORE ,同時判斷長度是否越界!

Tuesday, May 08, 2012

schedule_delayed_work()用法

資料來源: schedule_delayed_work()用法

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/net.h>
#include <net/sock.h>
#include <linux/in.h>
#include <linux/types.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/skbuff.h>
#include <linux/string.h>
#include <asm-generic/unaligned.h>
#include <linux/sysctl.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <asm/checksum.h>
#include <linux/ip.h>
#include <linux/workqueue.h>

#define err(msg) printk(KERN_INFO "%s failed.\n", msg)

static void defense_work_handler(struct work_struct *work);

static DECLARE_DELAYED_WORK(defense_work, defense_work_handler);

static void defense_work_handler(struct work_struct *work)
{
    printk(KERN_INFO "defense_work_handler function.\n");
}

static int __init main_init(void)
{
    schedule_delayed_work(&defense_work, 3 * HZ);

    return 0;
}

static void __exit main_exit(void)
{
    cancel_delayed_work_sync(&defense_work);
}

module_init(main_init);
module_exit(main_exit);
MODULE_LICENSE("GPL");