函数 ioremap 物理地址到内核映射空间的映射函数

ioremap函数用于将I/O内存的物理地址映射到内核虚拟地址空间,确保内核能直接访问高地址。通过get_vm_area获取虚拟地址空间,再使用remap_area_pages进行页面映射。在映射过程中涉及到了内核内存管理的分页机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 源代码:

/*
 * Remap an arbitrary physical address space into the kernel virtual
 * address space. Needed when the kernel wants to access high addresses
 * directly.
 *
 * NOTE! We need to allow non-page-aligned mappings too: we will obviously
 * have to convert them into an offset in a page-aligned mapping, but the
 * caller shouldn't need to know that small detail.
 *
 * 'flags' are the extra L_PTE_ flags that you want to specify for this
 * mapping.  See include/asm-arm/proc-armv/pgtable.h for more information.
 */
void * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags)
{
 void * addr;
 struct vm_struct * area;
 unsigned long offset, last_addr;

 /* Don't allow wraparound or zero size */
 last_addr = phys_addr + size - 1;
 if (!size || last_addr < phys_addr)
  return NULL;

 /*
  * Mappings have to be page-aligned
  */
 offset = phys_addr & ~PAGE_MASK;
 phys_addr &= PAGE_MASK;
 size = PAGE_ALIGN(last_addr) - phys_addr;

 /*
  * Ok, go for it..
  */
 area = get_vm_area(size, VM_IOREMAP);
 if (!area)
  return NULL;
 addr = area->addr;
 if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr, size, flags)) {
  vfree(addr);
  return NULL;
 }
 return (void *) (offset + (char *)addr);
}

功能就是将I/O内存资源的物理地址映射到核心虚地址空间(3GB-4GB).

1、返回映射后的虚拟地址(get_vm_area获取

2、对获取的虚拟空间进行页面映射(remap_area_pages

下面是相关的函数:

struct vm_struct * get_vm_area(unsigned long size, unsigned long flags)
{
 unsigned long addr, next;
 struct vm_struct **p, *tmp, *area;

 area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
 if (!area)
  return NULL;

 size += PAGE_SIZE;
 if (!size) {
  kfree (area);
  return NULL;
 }

 addr = VMALLOC_START;
 write_lock(&vmlist_lock);
 for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
  if ((size + addr) < addr)
   goto out;
  if (size + addr <= (unsigned long) tmp->addr)
   break;
  next = tmp->size + (unsigned long) tmp->addr;
  if (next > addr)
   addr = next;
  if (addr > VMALLOC_END-size)
   goto out;
 }
 area->flags = flags;
 area->addr = (void *)addr;
 area->size = size;
 area->next = *p;
 *p = area;
 write_unlock(&vmlist_lock);
 return area;

out:
 write_unlock(&vmlist_lock);
 kfree(area);
 return NULL;
}

static int remap_area_pages(unsigned long address, unsigned long phys_addr,
     unsigned long size, unsigned long flags)
{
 int error;
 pgd_t * dir;
 unsigned long end = address + size;

 phys_addr -= address;
 dir = pgd_offset_k(address);
 flush_cache_all();
 if (address >= end)
  BUG();
 spin_lock(&init_mm.page_table_lock);
 do {
  pmd_t *pmd;
  pmd = pmd_alloc(&init_mm, dir, address);
  error = -ENOMEM;
  if (!pmd)
   break;
  if (remap_area_pmd(pmd, address, end - address,
      phys_addr + address, flags))
   break;
  error = 0;
  address = (address + PGDIR_SIZE) & PGDIR_MASK;
  dir++;
 } while (address && (address < end));
 spin_unlock(&init_mm.page_table_lock);
 flush_tlb_all();
 return error;
}

其中的一些映射关系参考内存管理的分页管理

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值