转载自 https://blog.csdn.net/njuitjf/article/details/40745227


在 linux kernel 的代码中,经常看到 ioremap 函数。
其功能是将给定的物理地址映射为虚拟地址。
注意,此处的物理地址并不是真正内存的物理地址,而是cpu上的io memory。

可以参考芯片《Reference Manual》中断 memory map 章节。

 0x402a0000  ~  0x402a02e4似乎只是pinctrl寄存器,芯片内地址难道是从0~0x80000000 ?


   


本文主要学习 ioremap 是如何实现的。

ioremap 的定义:

[objc]  view plain  copy
  1. #define ioremap(cookie,size)  __arch_ioremap((cookie), (size), MT_DEVICE)  
  2.   
  3. #define MT_DEVICE  0  
  4.   
  5. #define __arch_ioremap   __arm_ioremap  
  6.   
  7. void __iomem *  
  8. __arm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype)  
  9. {  
  10.  return __arm_ioremap_caller(phys_addr, size, mtype,  
  11.    __builtin_return_address(0));  
  12. }  
  13.   
  14. void __iomem *__arm_ioremap_caller(unsigned long phys_addr, size_t size,  
  15.  unsigned int mtype, voidvoid *caller)  
  16. {  
  17.  unsigned long last_addr;  
  18.   unsigned long offset = phys_addr & ~PAGE_MASK;  
  19.   unsigned long pfn = __phys_to_pfn(phys_addr);  
  20.   
  21.   /* 
  22.    * Don't allow wraparound or zero size 
  23.   */  
  24.  last_addr = phys_addr + size - 1;  
  25.  if (!size || last_addr < phys_addr)  
  26.   return NULL;  
  27.   
  28.  return __arm_ioremap_pfn_caller(pfn, offset, size, mtype,  
  29.    caller);  
  30. }  
  31.   
  32. void __iomem * __arm_ioremap_pfn_caller(unsigned long pfn,  
  33.  unsigned long offset, size_t size, unsigned int mtype, voidvoid *caller)  
  34. {  
  35.  const struct mem_type *type;  
  36.  int err;  
  37.  unsigned long addr;  
  38.   struct vm_struct * area;  
  39.   
  40.  /* 
  41.   * High mappings must be supersection aligned 
  42.   */  
  43.  // 高端内存需要对齐到 supersection  
  44.  if (pfn >= 0x100000 && (__pfn_to_phys(pfn) & ~SUPERSECTION_MASK))  
  45.   return NULL;  
  46.   
  47.  /* 
  48.   * Don't allow RAM to be mapped - this causes problems with ARMv6+ 
  49.   */  
  50.  // map 的不能是 RAM ,只能是 soc 的 io memory  
  51.  /* 
  52.  int pfn_valid(unsigned long pfn) 
  53.  { 
  54.   return memblock_is_memory(pfn << PAGE_SHIFT); 
  55.  } 
  56.  */  
  57.  if (WARN_ON(pfn_valid(pfn)))  
  58.   return NULL;  
  59.   
  60.  // get_mem_type 的实现见后文  
  61.  // 从前文的定义可知, mtype 为 MT_DEVICE  
  62.  type = get_mem_type(mtype);  
  63.  if (!type)  
  64.   return NULL;  
  65.   
  66.  /* 
  67.   * Page align the mapping size, taking account of any offset. 
  68.   */  
  69.  size = PAGE_ALIGN(offset + size);  
  70.   
  71.  // get_vm_area_caller 函数的实现见后面  
  72.  area = get_vm_area_caller(size, VM_IOREMAP, caller);  
  73.   if (!area)  
  74.    return NULL;  
  75.   addr = (unsigned long)area->addr;  
  76.   
  77. #ifndef CONFIG_SMP  
  78.  if (DOMAIN_IO == 0 &&  
  79.      (((cpu_architecture() >= CPU_ARCH_ARMv6) && (get_cr() & CR_XP)) ||  
  80.         cpu_is_xsc3()) && pfn >= 0x100000 &&  
  81.         !((__pfn_to_phys(pfn) | size | addr) & ~SUPERSECTION_MASK)) {  
  82.   area->flags |= VM_ARM_SECTION_MAPPING;  
  83.   err = remap_area_supersections(addr, pfn, size, type);  
  84.  } else if (!((__pfn_to_phys(pfn) | size | addr) & ~PMD_MASK)) {  
  85.   area->flags |= VM_ARM_SECTION_MAPPING;  
  86.   err = remap_area_sections(addr, pfn, size, type);  
  87.  } else  
  88. #endif  
  89.   // ioremap_page_range 函数的实现见后文  
  90.   err = ioremap_page_range(addr, addr + size, __pfn_to_phys(pfn),  
  91.       __pgprot(type->prot_pte));  
  92.   
  93.  if (err) {  
  94.    vunmap((voidvoid *)addr);  
  95.    return NULL;  
  96.   }  
  97.   
  98.  flush_cache_vmap(addr, addr + size);  
  99.  return (void __iomem *) (offset + addr);  
  100. }  

kernel\arch\arm\mm\init.c

#ifdef CONFIG_HAVE_ARCH_PFN_VALID
int pfn_valid(unsigned long pfn)
{
return memblock_is_memory(__pfn_to_phys(pfn));
}
EXPORT_SYMBOL(pfn_valid);

int __init_memblock memblock_is_memory(phys_addr_t addr)
{
return memblock_search(&memblock.memory, addr) != -1;
}

static int __init_memblock memblock_search(struct memblock_type *type, phys_addr_t addr)
{
unsigned int left = 0, right = type->cnt;
do {
unsigned int mid = (right + left) / 2;
if (addr < type->regions[mid].base)
right = mid;
else if (addr >= (type->regions[mid].base +
  type->regions[mid].size))
left = mid + 1;
else
return mid;
} while (left < right);
return -1;
}

 



get_mem_type 函数的实现:

[objc]  view plain  copy
  1. const struct mem_type *get_mem_type(unsigned int type)  
  2. {  
  3.  return type < ARRAY_SIZE(mem_types) ? &mem_types[type] : NULL;  
  4. }  



mem_types 的定义:

[objc]  view plain  copy
  1. static struct mem_type mem_types[] = {  
  2.  [MT_DEVICE] = {    /* Strongly ordered / ARMv6 shared device */  
  3.   .prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED |  
  4.       L_PTE_SHARED,  
  5.   .prot_l1 = PMD_TYPE_TABLE,  
  6.   .prot_sect = PROT_SECT_DEVICE | PMD_SECT_S,  
  7.   .domain  = DOMAIN_IO,  
  8.  },  
  9.  [MT_DEVICE_NONSHARED] = { /* ARMv6 non-shared device */  
  10.   .prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_NONSHARED,  
  11.   .prot_l1 = PMD_TYPE_TABLE,  
  12.   .prot_sect = PROT_SECT_DEVICE,  
  13.   .domain  = DOMAIN_IO,  
  14.  },  
  15.  [MT_DEVICE_CACHED] = {   /* ioremap_cached */  
  16.   .prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_CACHED,  
  17.   .prot_l1 = PMD_TYPE_TABLE,  
  18.   .prot_sect = PROT_SECT_DEVICE | PMD_SECT_WB,  
  19.   .domain  = DOMAIN_IO,  
  20.  },   
  21.  [MT_DEVICE_WC] = { /* ioremap_wc */  
  22.   .prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_WC,  
  23.   .prot_l1 = PMD_TYPE_TABLE,  
  24.   .prot_sect = PROT_SECT_DEVICE,  
  25.   .domain  = DOMAIN_IO,  
  26.  },  
  27.  [MT_UNCACHED] = {  
  28.   .prot_pte = PROT_PTE_DEVICE,  
  29.   .prot_l1 = PMD_TYPE_TABLE,  
  30.   .prot_sect = PMD_TYPE_SECT | PMD_SECT_XN,  
  31.   .domain  = DOMAIN_IO,  
  32.  },  
  33.  [MT_CACHECLEAN] = {  
  34.   .prot_sect = PMD_TYPE_SECT | PMD_SECT_XN,  
  35.   .domain    = DOMAIN_KERNEL,  
  36.  },  
  37.  [MT_MINICLEAN] = {  
  38.   .prot_sect = PMD_TYPE_SECT | PMD_SECT_XN | PMD_SECT_MINICACHE,  
  39.   .domain    = DOMAIN_KERNEL,  
  40.  },  
  41.  [MT_LOW_VECTORS] = {  
  42.   .prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |  
  43.     L_PTE_RDONLY,  
  44.   .prot_l1   = PMD_TYPE_TABLE,  
  45.   .domain    = DOMAIN_USER,  
  46.  },  
  47.  [MT_HIGH_VECTORS] = {  
  48.   .prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |  
  49.     L_PTE_USER | L_PTE_RDONLY,  
  50.   .prot_l1   = PMD_TYPE_TABLE,  
  51.   .domain    = DOMAIN_USER,  
  52.  },  
  53.  [MT_MEMORY] = {  
  54.   .prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY,  
  55.   .prot_l1   = PMD_TYPE_TABLE,  
  56.   .prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE,  
  57.   .domain    = DOMAIN_KERNEL,  
  58.  },  
  59.  [MT_ROM] = {  
  60.   .prot_sect = PMD_TYPE_SECT,  
  61.   .domain    = DOMAIN_KERNEL,  
  62.  },  
  63.  [MT_MEMORY_NONCACHED] = {  
  64.   .prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |  
  65.     L_PTE_MT_BUFFERABLE,  
  66.   .prot_l1   = PMD_TYPE_TABLE,  
  67.   .prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE,  
  68.   .domain    = DOMAIN_KERNEL,  
  69.  },  
  70.  [MT_MEMORY_DTCM] = {  
  71.   .prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |  
  72.     L_PTE_XN,  
  73.   .prot_l1   = PMD_TYPE_TABLE,  
  74.   .prot_sect = PMD_TYPE_SECT | PMD_SECT_XN,  
  75.   .domain    = DOMAIN_KERNEL,  
  76.  },  
  77.  [MT_MEMORY_ITCM] = {  
  78.   .prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY,  
  79.   .prot_l1   = PMD_TYPE_TABLE,  
  80.   .domain    = DOMAIN_KERNEL,  
  81.  },  
  82. };  


 

get_vm_area_caller 函数的实现:

[objc]  view plain  copy
  1. struct vm_struct *get_vm_area_caller(unsigned long size, unsigned long flags,  
  2.     voidvoid *caller)  
  3. {  
  4. /* 
  5.  * Just any arbitrary offset to the start of the vmalloc VM area: the 
  6.  * current 8MB value just means that there will be a 8MB "hole" after the 
  7.  * physical memory until the kernel virtual memory starts.  That means that 
  8.  * any out-of-bounds memory accesses will hopefully be caught. 
  9.  * The vmalloc() routines leaves a hole of 4kB between each vmalloced 
  10.  * area for the same reason. ;) 
  11.  * 
  12.  * Note that platforms may override VMALLOC_START, but they must provide 
  13.  * VMALLOC_END.  VMALLOC_END defines the (exclusive) limit of this space, 
  14.  * which may not overlap IO space. 
  15.  */  
  16. /* 
  17. #ifndef VMALLOC_START 
  18. #define VMALLOC_OFFSET  (8*1024*1024) 
  19. // high_memory 在 arch/arm/mm/init.c 文件中的 bootmem_init 函数中赋值,该函数的实现见后文 
  20. #define VMALLOC_START  (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) 
  21. #endif 
  22. */  
  23. /* vmalloc ending address */  
  24. #define VMALLOC_END       0xf2000000UL  
  25.  return __get_vm_area_node(size, 1, flags, VMALLOC_START, VMALLOC_END,  
  26.       -1, GFP_KERNEL, caller);  
  27. }  
  28.   
  29. static struct vm_struct *__get_vm_area_node(unsigned long size,  
  30.   unsigned long align, unsigned long flags, unsigned long start,  
  31.   unsigned long end, int node, gfp_t gfp_mask, voidvoid *caller)  
  32. {  
  33.  static struct vmap_area *va;  
  34.  struct vm_struct *area;  
  35.   
  36.  BUG_ON(in_interrupt());  
  37.    
  38.  /* bits in flags of vmalloc's vm_struct below */  
  39.  // #define VM_IOREMAP 0x00000001 /* ioremap() and friends */  
  40.  if (flags & VM_IOREMAP) {  
  41.   int bit = fls(size);  
  42.   
  43.   if (bit > IOREMAP_MAX_ORDER)  
  44.    bit = IOREMAP_MAX_ORDER;  
  45.   else if (bit < PAGE_SHIFT)  
  46.    bit = PAGE_SHIFT;  
  47.   
  48.   align = 1ul << bit;  
  49.  }  
  50.   
  51.  size = PAGE_ALIGN(size);  
  52.  if (unlikely(!size))  
  53.   return NULL;  
  54.   
  55.  /** 
  56.   * kzalloc_node - allocate zeroed memory from a particular memory node. 
  57.   * @size: how many bytes of memory are required. 
  58.   * @flags: the type of memory to allocate (see kmalloc). 
  59.   * @node: memory node from which to allocate 
  60.   */  
  61.  // 分配一个 vm_struct 结构体  
  62.  area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);  
  63.  if (unlikely(!area))  
  64.   return NULL;  
  65.   
  66.  /* 
  67.   * We always allocate a guard page. 
  68.   */  
  69.  size += PAGE_SIZE;  
  70.   
  71.  // start 和 end 分别为 VMALLOC_START 和 VMALLOC_END  
  72.  // align 为 1  
  73.  // alloc_vmap_area 函数的注释:  
  74.  /* 
  75.   * Allocate a region of KVA of the specified size and alignment, within the 
  76.   * vstart and vend. 
  77.   */  
  78.  // 已经使用的 vm 的信息分别存在各个 vmap_area 结构体中  
  79.  // 所有的 vmap_area 结构体都在红黑树 vmap_area_root 中  
  80.  // alloc_vmap_area 函数的主要功能是,查找红黑树 vmap_area_root ,找到 start 和 end 之间满足 size 大小的未使用空间,  
  81.  // 创建一个 vmap_area 结构体,并用找到的未使用空间信息初始化该结构体,然后将该结构体插入到红黑树 vmap_area_root 中  
  82.  va = alloc_vmap_area(size, align, start, end, node, gfp_mask);  
  83.  if (IS_ERR(va)) {  
  84.   kfree(area);  
  85.   return NULL;  
  86.  }  
  87.   
  88.  /* 
  89.   * When this function is called from __vmalloc_node_range, 
  90.   * we do not add vm_struct to vmlist here to avoid 
  91.   * accessing uninitialized members of vm_struct such as 
  92.   * pages and nr_pages fields. They will be set later. 
  93.   * To distinguish it from others, we use a VM_UNLIST flag. 
  94.   */  
  95.  if (flags & VM_UNLIST)  
  96.   setup_vmalloc_vm(area, va, flags, caller);  
  97.  else  
  98.   // 看前面注释可知,上面的 if 分支只是个特殊情况,我们只分析 else 分支  
  99.   // insert_vmalloc_vm 函数的实现见后文  
  100.   insert_vmalloc_vm(area, va, flags, caller);  
  101.   
  102.  return area;  
  103. }  


 

文件中的 bootmem_init 函数中赋值,该函数的实现:

[objc]  view plain  copy
  1. void __init bootmem_init(void)  
  2. {  
  3.  unsigned long min, max_low, max_high;  
  4.   
  5.  max_low = max_high = 0;  
  6.   
  7.  // 找到内存的起始地址, 低端内存的最高地址, 高端内存的最高地址  
  8.  // find_limits 函数实现见后文  
  9.  find_limits(&min, &max_low, &max_high);  
  10.   
  11.  arm_bootmem_init(min, max_low);  
  12.   
  13.  /* 
  14.   * Sparsemem tries to allocate bootmem in memory_present(), 
  15.   * so must be done after the fixed reservations 
  16.   */  
  17.  arm_memory_present();  
  18.   
  19.  /* 
  20.   * sparse_init() needs the bootmem allocator up and running. 
  21.   */  
  22.  sparse_init();  
  23.   
  24.  /* 
  25.   * Now free the memory - free_area_init_node needs 
  26.   * the sparse mem_map arrays initialized by sparse_init() 
  27.   * for memmap_init_zone(), otherwise all PFNs are invalid. 
  28.   */  
  29.  arm_bootmem_free(min, max_low, max_high);  
  30.   
  31.  // high_memory 为高端内存的起始虚拟地址  
  32.  high_memory = __va(((phys_addr_t)max_low << PAGE_SHIFT) - 1) + 1;  
  33.   
  34.  /* 
  35.   * This doesn't seem to be used by the Linux memory manager any 
  36.   * more, but is used by ll_rw_block.  If we can get rid of it, we 
  37.   * also get rid of some of the stuff above as well. 
  38.   * 
  39.   * Note: max_low_pfn and max_pfn reflect the number of _pages_ in 
  40.   * the system, not the maximum PFN. 
  41.   */  
  42.  max_low_pfn = max_low - PHYS_PFN_OFFSET;  
  43.  max_pfn = max_high - PHYS_PFN_OFFSET;  
  44. }  


 

find_limits 函数实现:

[objc]  view plain  copy
  1. static void __init find_limits(unsigned longlong *min, unsigned longlong *max_low,  
  2.  unsigned longlong *max_high)  
  3. {  
  4.  struct meminfo *mi = &meminfo;  
  5.  int i;  
  6.   
  7.  *min = -1UL;  
  8.  *max_low = *max_high = 0;  
  9.   
  10.  for_each_bank (i, mi) {  
  11.   struct membank *bank = &mi->bank[i];  
  12.   unsigned long start, end;  
  13.   
  14.   start = bank_pfn_start(bank);  
  15.   end = bank_pfn_end(bank);  
  16.   
  17.   if (*min > start)  
  18.    *min = start;  
  19.   if (*max_high < end)  
  20.    *max_high = end;  
  21.   // 如果是高端内存,就不用更新 max_low 了  
  22.   // 参考后面的 sanity_check_meminfo 函数  
  23.   if (bank->highmem)  
  24.    continue;  
  25.   if (*max_low < end)  
  26.    *max_low = end;  
  27.  }  
  28. }  


 

sanity_check_meminfo 函数的实现:

[objc]  view plain  copy
  1. void __init sanity_check_meminfo(void)  
  2. {  
  3.  int i, j, highmem = 0;  
  4.   
  5.  for (i = 0, j = 0; i < meminfo.nr_banks; i++) {  
  6.   struct membank *bank = &meminfo.bank[j];  
  7.   *bank = meminfo.bank[i];  
  8.   
  9. #ifdef CONFIG_HIGHMEM  
  10.   // static void * __initdata vmalloc_min = (void *)(VMALLOC_END - SZ_128M);  
  11.   if (__va(bank->start) >= vmalloc_min ||  
  12.       __va(bank->start) < (voidvoid *)PAGE_OFFSET)  
  13.    highmem = 1;  
  14.   
  15.   bank->highmem = highmem;  
  16.   
  17.   ...  
  18. #else  
  19.   bank->highmem = highmem;  
  20.   
  21.   ...  
  22.  }  
  23.  ...  
  24. }  


 

回到函数 get_vm_area_caller 的实现。

insert_vmalloc_vm 函数的实现:

[objc]  view plain  copy
  1. static void insert_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va,  
  2.          unsigned long flags, voidvoid *caller)  
  3. {  
  4.  // vm(vm_struct) 结构体在函数 __get_vm_area_node 中分配  
  5.  // va(vmap_area) 结构体,在函数 __get_vm_area_node 中通过调用 alloc_vmap_area 分配  
  6.  setup_vmalloc_vm(vm, va, flags, caller);  
  7.  insert_vmalloc_vmlist(vm);  
  8. }  


 

setup_vmalloc_vm 函数的实现:

[objc]  view plain  copy
  1. static void setup_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va,  
  2.          unsigned long flags, voidvoid *caller)  
  3. {  
  4.  vm->flags = flags;  
  5.  vm->addr = (voidvoid *)va->va_start;  
  6.  vm->size = va->va_end - va->va_start;  
  7.  vm->caller = caller;  
  8.  va->vm = vm;  
  9.  va->flags |= VM_VM_AREA;  
  10. }  


 

insert_vmalloc_vmlist 函数的实现:

[objc]  view plain  copy
  1. static void insert_vmalloc_vmlist(struct vm_struct *vm)  
  2. {  
  3.  struct vm_struct *tmp, **p;  
  4.   
  5.  vm->flags &= ~VM_UNLIST;  
  6.  write_lock(&vmlist_lock);  
  7.  // 将 vm_struct 结构体插入的 vmlist 中  
  8.  for (p = &vmlist; (tmp = *p) != NULL; p = &tmp->next) {  
  9.   if (tmp->addr >= vm->addr)  
  10.    break;  
  11.  }  
  12.  vm->next = *p;  
  13.  *p = vm;  
  14.  write_unlock(&vmlist_lock);  
  15. }  


 

ioremap_page_range 函数的实现:

[objc]  view plain  copy
  1. int ioremap_page_range(unsigned long addr,  
  2.          unsigned long end, phys_addr_t phys_addr, pgprot_t prot)  
  3. {  
  4.  pgd_t *pgd;  
  5.  unsigned long start;  
  6.  unsigned long next;  
  7.  int err;  
  8.   
  9.  BUG_ON(addr >= end);  
  10.   
  11.  start = addr;  
  12.  phys_addr -= addr;  
  13.  pgd = pgd_offset_k(addr);  
  14.  do {  
  15.   next = pgd_addr_end(addr, end);  
  16.   err = ioremap_pud_range(pgd, addr, next, phys_addr+addr, prot);  
  17.   if (err)  
  18.    break;  
  19.  } while (pgd++, addr = next, addr != end);  
  20.   
  21.  flush_cache_vmap(start, end);  
  22.   
  23.  return err;  
  24. }  
  25.   
  26. static inline int ioremap_pud_range(pgd_t *pgd, unsigned long addr,  
  27.   unsigned long end, phys_addr_t phys_addr, pgprot_t prot)  
  28. {  
  29.  pud_t *pud;  
  30.  unsigned long next;  
  31.   
  32.  phys_addr -= addr;  
  33.  pud = pud_alloc(&init_mm, pgd, addr);  
  34.  if (!pud)  
  35.   return -ENOMEM;  
  36.  do {  
  37.   next = pud_addr_end(addr, end);  
  38.   if (ioremap_pmd_range(pud, addr, next, phys_addr + addr, prot))  
  39.    return -ENOMEM;  
  40.  } while (pud++, addr = next, addr != end);  
  41.  return 0;  
  42. }  
  43.   
  44. static inline int ioremap_pmd_range(pud_t *pud, unsigned long addr,  
  45.   unsigned long end, phys_addr_t phys_addr, pgprot_t prot)  
  46. {  
  47.  pmd_t *pmd;  
  48.  unsigned long next;  
  49.   
  50.  phys_addr -= addr;  
  51.  pmd = pmd_alloc(&init_mm, pud, addr);  
  52.  if (!pmd)  
  53.   return -ENOMEM;  
  54.  do {  
  55.   next = pmd_addr_end(addr, end);  
  56.   if (ioremap_pte_range(pmd, addr, next, phys_addr + addr, prot))  
  57.    return -ENOMEM;  
  58.  } while (pmd++, addr = next, addr != end);  
  59.  return 0;  
  60. }  
  61.   
  62. static int ioremap_pte_range(pmd_t *pmd, unsigned long addr,  
  63.   unsigned long end, phys_addr_t phys_addr, pgprot_t prot)  
  64. {  
  65.  pte_t *pte;  
  66.  u64 pfn;  
  67.   
  68.  pfn = phys_addr >> PAGE_SHIFT;  
  69.  pte = pte_alloc_kernel(pmd, addr);  
  70.  if (!pte)  
  71.   return -ENOMEM;  
  72.  do {  
  73.   BUG_ON(!pte_none(*pte));  
  74.   set_pte_at(&init_mm, addr, pte, pfn_pte(pfn, prot));  
  75.   pfn++;  
  76.  } while (pte++, addr += PAGE_SIZE, addr != end);  
  77.  return 0;  
  78. }  


 

上面几个函数的功能,是建立 linux 4级页表。
linux 4级页表参考:
http://larmbr.me/2014/01/19/the-evolution-of-4-level-page-talbe-in-linux/

总价一下。
ioremap中首先做了一些检查,其中一项检查是要处理的物理地址是不是 RAM ,因为 ioremap 只处理 soc 的 io memory ,不处理 RAM 。
分配一个 vm_struct 结构体。
之后分配一个 vmap_area 结构体,并查找红黑树 vmap_area_root 找到合适的 hole 。
然后初始化 vm_struct 结构体和 vmap_area 结构体的一些成员。
最后建立 linux 的4级内存页表。
4级即: PGD -> PUD -> PMD -> PTE


Logo

更多推荐