您的位置:首页 > 运维架构 > Linux

QT210开发板的ARM Linux静态映射分析

2014-01-14 01:32 615 查看
注:内核版本为2.6.35,cpu型号为s5pv210,内容有参考网络相关文章

内核提供了一个重要的结构体struct machine_desc ,这个结构体在内核移植中起到相当重要的作用,内核通过machine_desc结构体来控制系统体系架构相关部分的初始化。machine_desc结构体的成员包含了体系架构相关部分的几个最重要的初始化函数,包括map_io,init_irq,init_machine以及phys_io ,timer成员等。

machine_desc结构体定义如下:

structmachine_desc {

/*

*Note! The first four elements are used

*by assembler code in head-armv.S

*/

unsignedint nr; /* architecture number */

unsignedint phys_io; /* start of physicalio 用来保存UART的物理地址 */

unsignedint io_pg_offst; /* byte offset for io * page tabe entry保存UART在内核空间的虚拟地址 */

constchar *name; /* architecture name */

unsignedlong ;boot_params; /* tagged list U-boot传递给kernel的启动参数地址,一般为SDRAM物理地址+0x100 */

unsignedint video_start; /* start of videoRAM */

unsignedint video_end; /* end of video RAM */

unsignedint reserve_lp0:1; /* never has lp0 */

unsignedint reserve_lp1:1; /* never has lp1 */

unsignedint reserve_lp2 :1; /* never haslp2 */

unsignedint soft_reboot :1; /* soft reboot */

void (*fixup)(struct machine_desc *,

struct tag *, char **,

struct meminfo *);

void (*map_io)(void);/*IO mapping function IO地址从物理地址到虚拟地址映射的函数指针 */

void (*init_irq)(void);/*中断初始化函数指针*/

structsys_timer *timer; /* system tick timer */

void (*init_machine)(void);

};

machine_desc结构体通过MACHINE_START宏来初始化, s5pv210 machine_desc结构体定义如下:

/* arch/arm/mach-s5/Mach-smdkv210.c*/

MACHINE_START(SMDKV210,"SMDKV210")

/*Maintainer: Kukjin Kim <kgene.kim@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 = s5pv210_init_irq,

.map_io = smdkv210_map_io,/*plat-s5p,s5p平台相关的映射*/

.init_machine = smdkv210_machine_init,

#ifdef CONFIG_S5P_HIGH_RES_TIMERS

.timer = &s5p_systimer,

#else

.timer = &s3c24xx_timer,

#endif

MACHINE_END

其中的宏MACHINE_START和MACHINE_END定义如下:

/*

* Set of macros todefine architecture features. This is built into

* a table by thelinker.

*/

#defineMACHINE_START(_type,_name) \

const struct machine_desc__mach_desc_##_type \

__attribute__((__section__(".arch.info.init")))= { \

.nr= MACH_TYPE_##_type, \

.name= _name,

#define MACHINE_END \

};

其中MACH_TYPE_##_type 为GCC扩展语法中的字符拼接标识,在预编译的时候会用真正的字符代替,比如我们这里就是MACH_TYPE_SMDKV210

MACHINE_START的使用及各个成员函数的的放置位置以及调用过程如下:

MACH_TYPE_SMDKV210这个值是目标板的类型值,定义在include/generated.h内,值为2456

/* include/generated.h*/

#define MACH_TYPE_SMDKV210 2456

由上发现,MACHINE_START主要是定义了"structmachine_desc"的类型,放在section(".arch.info.init"),是初始化数据,其所占用的内存在内核起来之后将会被释放。

这里的map_io成员即内核提供给用户的创建外设I/O资源到内核虚拟地址静态映射表的接口函数。map_io成员函数会在系统初始化过程中被调用,流程如下:

start_kernel -> setup_arch()--> paging_init()->devicemaps_init中被调用

struct machine_desc 结构体的各个成员函数在不同时期被调用:

1. .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall( ) 段里面,会自动按顺序被调用

2. init_irq在start_kernel( ) --> init_IRQ( ) -->init_arch_irq( ) 被调用

3. map_io 在 setup_arch( ) --> paging_init( )被调用

其他主要都在 setup_arch() 中用到。

用户可以在定义machine_desc结构体时指定map_io的接口函数,我们也正是这样做的。

接下来我们继续分析smdkv210_map_io的执行过程,流程如下:

smdkv210_map_io-> s5p_init_io(NULL,0, S5P_VA_CHIPID);

下面来看一下s5p_init_io函数:

/* read cpu identification code */



void __init s5p_init_io(struct map_desc*mach_desc,

intsize, void __iomem *cpuid_addr)

{

unsignedlong 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内核提供,定义如下:

/*

* Create the architecturespecific mappings

*/

void __init iotable_init(structmap_desc *io_desc, int nr)

{

inti;

for(i = 0; i nr; i++)

create_mapping(io_desc+ i);

}

由上知道,smdkv210_map_io最终调用iotable_init建立映射表。

iotable_init函数的参数有两个:一个是map_desc类型的结构体,另一个是该结构体的数量nr。这里最关键的就是struct map_desc。map_desc结构体定义如下:

/*include/asm-arm/mach/map.h */

struct map_desc {

unsignedlong virtual;/*虚拟地址*/

unsignedlong pfn;/*物理页框地址,通过宏__phys_to_pfn(phy_addr)实现,即物理地址右移12位的方式*/

unsignedlong length;

unsignedint type;

};

create_mapping( )函数就是通过map_desc提供的信息创建线性映射表的。

这样的话我们就知道了创建I/O映射表的大致流程为:只要定义相应I/O资源的map_desc结构体,并将该结构体传给iotable_init函数执行,就可以创建相应的I/O资源到内核虚拟地址空间的映射表了。

我们来看看s5pv210是怎么定义map_desc结构体的(即上面iotable_init()函数内的s5p_iodesc)。

[arch/arm/plat-s5p]

/* minimal IO mapping */

static structmap_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,

},

};

在s5p_init_io()函数中,除了iotable_init()以为,还会在最后调用,s3c_init_cpu

(cpu->map_io)(mach_desc, size);

而CPU的这个map_io在arch/arm/plat-s5p/cpu.c里面定义如下:

static struct cpu_table cpu_ids[] __initdata= {

{

.idcode = 0x56440100,

.idmask = 0xffffff00,

.map_io = s5p6440_map_io,

.init_clocks = s5p6440_init_clocks,

.init_uarts = s5p6440_init_uarts,

.init = s5p6440_init,

.name = name_s5p6440,

},{

.idcode = 0x36442000,

.idmask = 0xffffff00,

.map_io = s5p6442_map_io,

.init_clocks = s5p6442_init_clocks,

.init_uarts = s5p6442_init_uarts,

.init = s5p6442_init,

.name = name_s5p6442,

},{

.idcode = 0x43100000,

.idmask = 0xfffff000,

.map_io = s5pc100_map_io,

.init_clocks = s5pc100_init_clocks,

.init_uarts = s5pc100_init_uarts,

.init = s5pc100_init,

.name = name_s5pc100,

},{

.idcode = 0x43110000,

.idmask = 0xfffff000,

.map_io = s5pv210_map_io,/*cpu相关的映射*/

.init_clocks = s5pv210_init_clocks,

.init_uarts = s5pv210_init_uarts,

.init = s5pv210_init,

.name = name_s5pv210,

},

};

再查看s5pv210_map_io (),函数代码如下:

void __init s5pv210_map_io(void)

{

#ifdef CONFIG_S3C_DEV_ADC

s3c_device_adc.name = "s3c64xx-adc";

#endif



iotable_init(s5pv210_iodesc, ARRAY_SIZE(s5pv210_iodesc));



/*initialise device information early */

s5pv210_default_sdhci0();

s5pv210_default_sdhci1();

s5pv210_default_sdhci2();



/*the i2c devices are directly compatible with s3c2440 */

s3c_i2c0_setname("s3c2440-i2c");

s3c_i2c1_setname("s3c2440-i2c");

s3c_i2c2_setname("s3c2440-i2c");

}

接下来看结构s5pv210_iodesc [arch/arm/mach-s5pv20/cpu.c],代码如下,

/* Initial IO mappings */



static struct map_desc s5pv210_iodesc[]__initdata = {

{

.virtual = (unsigned long)S5P_VA_SYSTIMER,

.pfn = __phys_to_pfn(S5PV210_PA_SYSTIMER),

.length = SZ_1M,

.type = MT_DEVICE,

},{

.virtual = (unsigned long)VA_VIC2,

.pfn = __phys_to_pfn(S5PV210_PA_VIC2),

.length = SZ_16K,

.type = MT_DEVICE,

},{

.virtual = (unsigned long)VA_VIC3,

.pfn = __phys_to_pfn(S5PV210_PA_VIC3),

.length = SZ_16K,

.type = MT_DEVICE,

},{

.virtual = (unsigned long)S5P_VA_SROMC,

.pfn = __phys_to_pfn(S5PV210_PA_SROMC),

.length = SZ_4K,

.type = MT_DEVICE,

},{

.virtual = (unsigned long)S3C_VA_WATCHDOG,

.pfn = __phys_to_pfn(S5P_PA_WDT),

.length = SZ_4K,

.type = MT_DEVICE,

},{

.virtual = (unsigned long)S3C_VA_OTG,

.pfn = __phys_to_pfn(S5PV210_PA_OTG),

.length = SZ_1M,

.type = MT_DEVICE,

},{

.virtual = (unsigned long)S3C_VA_OTGSFR,

.pfn = __phys_to_pfn(S5PV210_PA_OTGSFR),

.length = SZ_1M,

.type = MT_DEVICE,

},

#if defined(CONFIG_HRT_RTC)

{

.virtual = (unsigned long)S5P_VA_RTC,

.pfn = __phys_to_pfn(S5PV210_PA_RTC),

.length = SZ_4K,

.type = MT_DEVICE,

},

#endif

{

.virtual = (unsigned long)S5P_VA_DMC0,

.pfn = __phys_to_pfn(S5P_PA_DMC0),

.length = SZ_4K,

.type = MT_DEVICE,

},{

.virtual = (unsigned long)S5P_VA_DMC1,

.pfn = __phys_to_pfn(S5P_PA_DMC1),

.length = SZ_4K,

.type = MT_DEVICE,

},{

.virtual = (unsigned long)S5P_VA_AUDSS,

.pfn = __phys_to_pfn(S5PV210_PA_AUDSS),

.length = SZ_1M,

.type = MT_DEVICE,

},{

.virtual = (unsigned long)S5P_VA_BUS_AXI_DSYS,

.pfn =__phys_to_pfn(S5PV210_PA_BUS_AXI_DSYS),

.length = SZ_4K,

.type = MT_DEVICE,

}

};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: