snifer

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

0
阅读(2312)
最近不断有人跟我说起静态映射的问题,今天就以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 */
.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)
Baidu
map