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

本文介绍了Linux内核中I/O内存资源的物理地址映射到核心虚地址空间的方法,通过ioremap函数实现映射,并详细解析了其内部实现过程。

摘要生成于 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;
}

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

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值