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


注:内核版本为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,

         }

};

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值