Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1910714
  • 博文数量: 376
  • 博客积分: 2147
  • 博客等级: 大尉
  • 技术积分: 3642
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-06 10:47
文章分类

全部博文(376)

文章存档

2019年(3)

2017年(28)

2016年(15)

2015年(17)

2014年(182)

2013年(16)

2012年(115)

我的朋友

分类: LINUX

2012-02-21 11:59:51

主内存创建映射bootmem_init  

2011-03-18 12:23:19|  分类: Linux |字号 

void __init bootmem_init(void)
{
 struct meminfo *mi = &meminfo;
 unsigned long memend_pfn = 0;
 int node, initrd_node;

 /*
  * Locate which node contains the ramdisk image, if any.
  */
 initrd_node = check_initrd(mi);

 /*
  * Run through each node initialising the bootmem allocator.
  */

//遍历所有节点,为每个节点调用bootmem_init_node()完成指定节点内存的映射创建
 for_each_node(node) {
  unsigned long end_pfn = bootmem_init_node(node, mi);

  .......

}

 //bootmem_init_node():为指定节点的主内存创建映射

static unsigned long __init bootmem_init_node(int node, struct meminfo *mi) {
 unsigned long start_pfn, end_pfn, boot_pfn;
 unsigned int boot_pages;
 pg_data_t *pgdat;
 int i;

 start_pfn = -1UL;
 end_pfn = 0;

 /*
  * Calculate the pfn range, and map the memory banks for this node.
  */
 for_each_nodebank(i, mi, node) {
  struct membank *bank = &mi->bank[i];
  unsigned long start, end;

  start = bank_pfn_start(bank);
  end = bank_pfn_end(bank);

  if (start_pfn > start)
   start_pfn = start;
  if (end_pfn < end)
   end_pfn = end;

  map_memory_bank(bank);   //为指定节点类型的Bank创建映射
 }

.......

}

bootmem_init_node()遍历整个meminfo结构,为指定节点类型的Bank创建映射。为一个Bank创建映射是通过函数map_memory_bank()实现的这个函数也在arch/arm/mm/init.c中:

static inline void map_memory_bank(struct membank *bank)   //为一个Bank创建映射
{
#ifdef CONFIG_MMU
 struct map_desc map;

 map.pfn = bank_pfn_start(bank);      //要映射的物理起始页面号
 map.virtual = __phys_to_virt(bank_phys_start(bank));  //映射到的虚拟地址
 map.length = bank_phys_size(bank);   //映射长度
 map.type = MT_MEMORY;  //映射类型

 create_mapping(&map);     //为一个物理存储空间创建映射,实际上就是填充页表
#endif
}

map_memory_bank()调用create_mapping()完成映射的创建工作。create_mapping()函数用于为一个物理存储空间创建映射,实际上就是填充页表。

需要重点关注的是create_mapping()的参数map_desc结构,它在arch/arm/include/asm/mach/map.h中定义,描述了一个映射区间:

struct map_desc {
 unsigned long virtual;  //映射到的虚拟地址
 unsigned long pfn;      //要映射的物理起始页面号
 unsigned long length;   //映射长度
 unsigned int type;      //映射类型,MT_xxx,决定了相应映射页面的访问权限
};
// type的取值
/* types 0-3 are defined in asm/io.h */
#define MT_UNCACHED  4
#define MT_CACHECLEAN  5
#define MT_MINICLEAN  6
#define MT_LOW_VECTORS  7
#define MT_HIGH_VECTORS  8
#define MT_MEMORY  9
#define MT_ROM   10
#define MT_MEMORY_NONCACHED 11

其中type成员决定了页面的访问权限,这是通过全局数组struct mem_type mem_types[]实现的(type是这个数组的下标):

arch/arm/mm/mm.h

struct mem_type {
 unsigned int prot_pte;
 unsigned int prot_l1;
 unsigned int prot_sect;
 unsigned int domain;
};

arch/arm/mm/mmu.c

 static struct mem_type mem_types[] = {
 [MT_DEVICE] = {    /* Strongly ordered / ARMv6 shared device */
  .prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED |
      L_PTE_SHARED,
  .prot_l1 = PMD_TYPE_TABLE,
  .prot_sect = PROT_SECT_DEVICE | PMD_SECT_S,
  .domain  = DOMAIN_IO,
 },
 [MT_DEVICE_NONSHARED] = { /* ARMv6 non-shared device */
  .prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_NONSHARED,
  .prot_l1 = PMD_TYPE_TABLE,
  .prot_sect = PROT_SECT_DEVICE,
  .domain  = DOMAIN_IO,
 },
 [MT_DEVICE_CACHED] = {   /* ioremap_cached */
  .prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_CACHED,
  .prot_l1 = PMD_TYPE_TABLE,
  .prot_sect = PROT_SECT_DEVICE | PMD_SECT_WB,
  .domain  = DOMAIN_IO,
 }, 
 [MT_DEVICE_WC] = { /* ioremap_wc */
  .prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_WC,
  .prot_l1 = PMD_TYPE_TABLE,
  .prot_sect = PROT_SECT_DEVICE,
  .domain  = DOMAIN_IO,
 },
 [MT_UNCACHED] = {
  .prot_pte = PROT_PTE_DEVICE,
  .prot_l1 = PMD_TYPE_TABLE,
  .prot_sect = PMD_TYPE_SECT | PMD_SECT_XN,
  .domain  = DOMAIN_IO,
 },
 [MT_CACHECLEAN] = {
  .prot_sect = PMD_TYPE_SECT | PMD_SECT_XN,
  .domain    = DOMAIN_KERNEL,
 },
 [MT_MINICLEAN] = {
  .prot_sect = PMD_TYPE_SECT | PMD_SECT_XN | PMD_SECT_MINICACHE,
  .domain    = DOMAIN_KERNEL,
 },
 [MT_LOW_VECTORS] = {
  .prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
    L_PTE_EXEC,
  .prot_l1   = PMD_TYPE_TABLE,
  .domain    = DOMAIN_USER,
 },
 [MT_HIGH_VECTORS] = {
  .prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
    L_PTE_USER | L_PTE_EXEC,
  .prot_l1   = PMD_TYPE_TABLE,
  .domain    = DOMAIN_USER,
 },
 [MT_MEMORY] = {
  .prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE,
  .domain    = DOMAIN_KERNEL,
 },
 [MT_ROM] = {
  .prot_sect = PMD_TYPE_SECT,
  .domain    = DOMAIN_KERNEL,
 },
 [MT_MEMORY_NONCACHED] = {
  .prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE,
  .domain    = DOMAIN_KERNEL,
 },
};

 

再回到map_memory_bank() 代码中, 虚拟地址设成了__phys_to_virt(bank_start) 。
__phys_to_virt()和__virt_to_phys()是两个宏定义,用于在虚拟地址和物理地址之间互相转换。
这两个宏定义在arch/arm/include/asm/memory.h 中:

#define __virt_to_phys(x)  ((x) - PAGE_OFFSET + PHYS_OFFSET)
#define __phys_to_virt(x)  ((x) - PHYS_OFFSET + PAGE_OFFSET)

其 中PHYS_OFFSET 是物理内存起点, PAGE_OFFSET 是内核虚拟地址空间起点(0xC0000000, 3GB)。所以内核的虚拟地址和物理地址之间只是有一个固定的偏移量。对于AT91SAM9260EK 平台,PHYS_OFFSET 为0x20000000,有以下转换关系:
PA = VA -(0xC0000000 – 0x20000000) = VA – 0xA0000000
VA = PA + (0xC0000000 – 0x20000000) = PA + 0xA0000000
也就是说, AT91SAM9260EK 板上的64MB SDRAM 的物理地址范围是
0x20000000~0x24000000-1,映射后的虚拟地址范围是: 0xC0000000~0xC4000000-1。

阅读(1545) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~