Embeded linux之地址映射

Posted 扑克face

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Embeded linux之地址映射相关的知识,希望对你有一定的参考价值。

一、板级文件

通常会由MACHINE_START到板级文件

MACHINE_START(Chipname, "Chipname")
  .atag_offset    = 0x100,
  .map_io      = Chipname_map_io,
  .init_early    = Chipname_init_early,
  .init_irq     = Chipname_gic_init_irq,
  .handle_irq   = gic_handle_irq,
  .timer       = &Chipname_sys_timer,
  .init_machine  = Chipname_init,
  .reserve     = Chipname_reserve,
  .restart      = Chipname_restart,
MACHINE_END

.map_io是一个函数指针,这里指定了Chipname_map_io。函数实体为:

void __init Chipname_map_io(void)

{

   ... 

}

二、内存映射

芯片IO口操作分为两类:

1.寄存器与内存统一编址,又称IO内存

2.寄存器与内存不统一编址,又称IO端口

ARM芯片基本上是统一编址,访问寄存器(包括系统寄存器、外设寄存器、IO口寄存器等)直接访问该地址即可。

其中linux支持的寄存器地址寻址方式为内存映射,即将内核内存1G的某一些地址映射给寄存器,这样操作内核虚拟内存地址就是操作寄存器。

三、映射方式

linux内核提供的映射方式有两种:

1.静态映射

使用map_desc结构体进行映射,其中map_desc结构体为:

struct map_desc {
  unsigned long virtual;  //虚拟地址
  unsigned long pfn;    //__phys_to_pfn(物理地址) , 就是物理页框号
  unsigned long length;  //长度
  unsigned int type;    //类型
};

举例,填充一个映射信息结构体,映射两块连续的地址空间,出现保留字就再映射一个,或者遇到不需要控制的地址,否则一直连续映射:

static struct map_desc Chipname_io_desc[] __initdata = {
  {
    .virtual = 0xFE000000,
    .pfn = __phys_to_pfn(0x10000000),
    .length = 0xD0000,
    .type = MT_DEVICE
  },
  {
    .virtual = 0xFE100000,
    .pfn = __phys_to_pfn(0x20000000),
    .length = 0x700000,
    .type = MT_DEVICE
  }
};

然后调用iotable_init(Chipname_io_desc, ARRAY_SIZE(Chipname_io_desc));建立映射的函数

以后直接*(volatile unsigned int *)0xFE000000就能使用了。

2.动态映射

驱动中手动映射某一个地址

#define ioremap(cookie,size)         __arm_ioremap((cookie), (size), MT_DEVICE)
#define ioremap_nocache(cookie,size)   __arm_ioremap((cookie), (size), MT_DEVICE)
#define ioremap_cached(cookie,size)     __arm_ioremap((cookie), (size), MT_DEVICE_CACHED)
#define ioremap_wc(cookie,size)       __arm_ioremap((cookie), (size), MT_DEVICE_WC)
#define iounmap               __arm_iounmap

cookie:物理地址
size:要映射的空间的大小;
返回虚拟地址
头文件io.h
以后直接*(volatile unsigned int *)虚拟地址就能使用了。
 
扩展:
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr)) //include/linux/kernel.h,ARRAY_SIZE为计算数组的一维维度,计算方法为数组大小和数组单成员大小之商

#ifdef __CHECKER__
  #define __must_be_array(arr) 0
#else
  #define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0])) //include/linux/compiler-gcc.h

#endif

#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) //include/linux/bug.h,BUILD_BUG_ON_ZERO的作用在于将返回值转化为编译错误信息。显然当内嵌函数返回值为0时,也即类型相同时,由于BUILD_BUG_ON_ZERO参数为非0而导致char[-1]而发出编译器警告。

#ifndef __same_type
# define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))  //include/linux/compiler.h,gcc编译器内嵌的函数,判断一个变量的类型是否为某指定的类型,假如是就返回1,否则返回0。这里通过判断指针和指针指向的第一个元素的指针是否是相同类型来判断是否为数组。
#endif

 

arch/arm/mm/mmu.c

void __init iotable_init(struct map_desc *io_desc, int nr)
{
  struct map_desc *md;
  struct vm_struct *vm;

  if (!nr) return;

  /*early_alloc_aligned

  *

  */

  vm = early_alloc_aligned(sizeof(*vm) * nr, __alignof__(*vm));

  for (md = io_desc; nr; md++, nr--)

  {

    create_mapping(md, false);
    vm->addr = (void *)(md->virtual & PAGE_MASK);
    vm->size = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK));
    vm->phys_addr = __pfn_to_phys(md->pfn);
    vm->flags = VM_IOREMAP | VM_ARM_STATIC_MAPPING;
    vm->flags |= VM_ARM_MTYPE(md->type);
    vm->caller = iotable_init;
    vm_area_add_early(vm++);
  }
}

 

















































以上是关于Embeded linux之地址映射的主要内容,如果未能解决你的问题,请参考以下文章

转载linux内核笔记之高端内存映射

Linux内核地址映射模型

Linux内核之内存管理完全剖析

linux内核源码解析03–启动代码分析之主内核页表创建

Linux分页机制之概述--Linux内存管理

linux内核源码分析之虚拟内存映射