snifer

【原创】Linux 文件系统移植全解密以linux-2.6.35内核源码为例说明一下IO静态映射的过程

0
阅读(3617)
最近不断有人跟我说起静态映射的问题,今天就以linux-2.6.35内核源码为例说明一下IO静态映射的过程(ARM平台)。

//init/main.c
asmlinkage void __init start_kernel(void){
    ... 
    setup_arch(&command_line);
    ...
}

//arch/arm/kernel/setup.c
void __init setup_arch(char **cmdline_p){
   ... 
   paging_init(mdesc);
   ... 
}


//arch/arm/mm/mmu.c
void __init paging_init(struct machine_desc *mdesc){
   ... 
   devicemaps_init(mdesc);
   ...
}

//arch/arm/mm/mmu.c
static void __init devicemaps_init(struct machine_desc *mdesc){
   ... 
   if (mdesc->map_io)  //回调map_io
        mdesc->map_io();
   ...
}

//arch/arm/include/asm/mach/arch.h
struct machine_desc {
    /*
     * Note! The first four elements are used
     * by assembler code in head.S, head-common.S
     */
    unsigned int        nr;        /* architecture number    */
    unsigned int        phys_io;    /* start of physical io    */
    unsigned int        io_pg_offst;    /* byte offset for io page tabe entry    */

    const char        *name;        /* architecture name    */
    unsigned long        boot_params;    /* tagged list        */

    unsigned int        video_start;    /* start of video RAM    */
    unsigned int        video_end;    /* end of video RAM    */

    unsigned int        reserve_lp0 :1;    /* never has lp0    */
    unsigned int        reserve_lp1 :1;    /* never has lp1    */
    unsigned int        reserve_lp2 :1;    /* never has lp2    */
    unsigned int        soft_reboot :1;    /* soft reboot        */
    void            (*fixup)(struct machine_desc *,struct tag *, char **,struct meminfo *);
    void            (*map_io)(void);/* IO mapping function    */
    void            (*init_irq)(void);
    struct sys_timer    *timer;        /* system tick timer    */
    void            (*init_machine)(void);
};
该结构体对象初始化在对应板子的BSP文件中(这里以S5PC100平台为例)

//arch/arm/mach-s5pc100/mach-smdkc100.c
MACHINE_START(SMDKC100, "SMDKC100")
    /* Maintainer: Byungho Min <bhmin@samsung.com> */
    .phys_io    = S3C_PA_UART & 0xfff00000,
    .io_pg_offst    = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
    .boot_params    = S5P_PA_SDRAM + 0x100,
    .init_irq    = s5pc100_init_irq,
    .map_io        = smdkc100_map_io,
    .init_machine    = smdkc100_machine_init,
    .timer        = &s3c24xx_timer,
MACHINE_END

//MACHINE_START宏定义在arch/arm/include/asm/mach/arch.h
#define MACHINE_START(_type,_name)            \
static const struct machine_desc __mach_desc_##_type    \
 __used                            \
 __attribute__((__section__(".arch.info.init"))) = {    \
    .nr        = MACH_TYPE_##_type,        \
    .name        = _name,
#define MACHINE_END                \

即struct machine_desc中的域.map_io登记为smdkc100_map_io
static void __init smdkc100_map_io(void)
{
    s5p_init_io(NULL, 0, S5P_VA_CHIPID);
    ...
}

//arch/arm/plat-s5p/cpu.c
/* minimal IO mapping */
static struct map_desc s5p_iodesc[] __initdata = {
    {
        .virtual    = (unsigned long)S5P_VA_CHIPID,
        .pfn        = __phys_to_pfn(S5P_PA_CHIPID),
        .length        = SZ_4K,
        .type        = MT_DEVICE,
    }, {
        .virtual    = (unsigned long)S3C_VA_SYS,
        .pfn        = __phys_to_pfn(S5P_PA_SYSCON),
        .length        = SZ_64K,
        .type        = MT_DEVICE,
    }, {
        .virtual    = (unsigned long)S3C_VA_UART,
        .pfn        = __phys_to_pfn(S3C_PA_UART),
        .length        = SZ_4K,
        .type        = MT_DEVICE,
    }, {
        .virtual    = (unsigned long)VA_VIC0,
        .pfn        = __phys_to_pfn(S5P_PA_VIC0),
        .length        = SZ_16K,
        .type        = MT_DEVICE,
    }, {
        .virtual    = (unsigned long)VA_VIC1,
        .pfn        = __phys_to_pfn(S5P_PA_VIC1),
        .length        = SZ_16K,
        .type        = MT_DEVICE,
    }, {
        .virtual    = (unsigned long)S3C_VA_TIMER,
        .pfn        = __phys_to_pfn(S5P_PA_TIMER),
        .length        = SZ_16K,
        .type        = MT_DEVICE,
    }, {
        .virtual    = (unsigned long)S5P_VA_GPIO,
        .pfn        = __phys_to_pfn(S5P_PA_GPIO),
        .length        = SZ_4K,
        .type        = MT_DEVICE,
    },
};

/* read cpu identification code */
void __init s5p_init_io(struct map_desc *mach_desc,
            int size, void __iomem *cpuid_addr){
    unsigned long idcode;

    /* initialize the io descriptors we need for initialization */
    iotable_init(s5p_iodesc, ARRAY_SIZE(s5p_iodesc));
    if (mach_desc)
        iotable_init(mach_desc, size);

    idcode = __raw_readl(cpuid_addr);
    s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids));
}
上面的 iotable_init函数完成IO映射。

结构体static struct map_desc定义在asm/io.h中
struct map_desc {
    unsigned long virtual; //映射后的虚拟地址
    unsigned long pfn;      //被映射的物理地址所在页帧号
    unsigned long length;//被映射的IO资源长度
    unsigned int type;        //IO类型
};

这里比较难理解的是“映射后的虚拟地址virtual”,这个是自己定义的,可以修改,但是不能和已经映射的重复。
可以参看内核文档\Documentation\arm\memory.txt,其中描述如下:
VMALLOC_START    VMALLOC_END-1    vmalloc() / ioremap() space.
                Memory returned by vmalloc/ioremap will
                be dynamically placed in this region.
                VMALLOC_START may be based upon the value
                of the high_memory variable.

VMALLOC_START 定义在arch/arm/include/asm/pgtable.h中
/*
 * Just any arbitrary offset to the start of the vmalloc VM area: the
 * current 8MB value just means that there will be a 8MB "hole" after the
 * physical memory until the kernel virtual memory starts.  That means that
 * any out-of-bounds memory accesses will hopefully be caught.
 * The vmalloc() routines leaves a hole of 4kB between each vmalloced
 * area for the same reason. ;)
 *
 * Note that platforms may override VMALLOC_START, but they must provide
 * VMALLOC_END.  VMALLOC_END defines the (exclusive) limit of this space,
 * which may not overlap IO space.
 */
#ifndef VMALLOC_START
#define VMALLOC_OFFSET        (8*1024*1024)
#define VMALLOC_START        (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
#endif

S5PC100中IO映射从S3C_ADDR_BASE开始线性偏移
#define S3C_ADDR_BASE    (0xF4000000)