linux函数地址偏移,linux2.4.19下__ioremap函数中remap_area_pages虚拟地址映射建立函数...

linux2.4.19下__ioremap函数中remap_area_pages虚拟地址映射建立函数的代码分析

文章来源:http://gliethttp.cublog.cn

接续前一篇文章《linux2.4.19下__ioremap和get_vm_area的粗略理解》未完成的部分-remap_area_pages,进行进一步阐述:

虚拟内存vm技术,采用分页机制,使得进程可以使用比实际物理内存大甚多的空间;linux的地址有三种:

<1>物理地址

就是没有启用mmu之前cpu操作的地址,如:sdram的总线地址等.

<2>线性地址:

不采用分页机制,可以通过简单的加、减一个基地址方式,完成vm地址和物理地址的转换,如:物理内存sdram的虚拟地址位于3G~vmalloc_start,可以

通过virt_to_phys和phys_to_virt两个函数实现虚拟地址和sdram物理地址的直接线性转换,另一例子就是at91rm9200控制寄存器的控制,同样直接使用

AT91_IO_P2V(AT91C_BASE_SYS)将物理寄存器AT91C_BASE_SYS线性的映射成mmu下的虚拟地址(AT91_IO_P2V在include/asm-arm/arch-at91rm9200/Hardware.h中定义).

<3>逻辑地址,也被称为虚拟地址:逻辑地址和物理地址都被linux进行了分页管理,两者的页大小可以不同,但是为了管理上的方便,一般at91rm9200处理器下的linux系统将逻

辑地址和物理地址均划分大小为4k的页面,逻辑地址经过vm技术的2级(at91rm9200处理器对应的linux系统采用二级mmu映射机制)表转换后最终映射到的真正使用的物理地

址空间,at91rm9200处理器最终操作的还是总线地址,也就是物理地址,所以linux系统的工作之一就是将线性地址和逻辑地址,于是linux需要首先建立vm的2级转换表;

这之前我们先来了解一下mmu的分页机制:mmu可以对4种大小进行映射管理,

1)微页:构成1k的存储区

2)小页:构成4k的存储区

3)大页:构成64k的存储区

4)节:构成1M的存储区(arch/arm/kernel/head-armv.S启动代码中就暂用4个节映射,完成mmu的正常启动)

at91rm9200处理器采用了2级小页mmu管理方式,因此存在dir页目录4k[虚拟地址的31~20共12位],每个dir页目录项存放着第2级pte页表的基地址

dir页目录项内容如下:

bit 31 ... 10 9 ... 0

| 页表基地址 | mmu控制域 |

pte页表项内容如下:

bit 31 ... 12 11 ... 0

| 小页基地址 | mmu控制域 |

经过pte之后,pte表项内容的31~12位和虚拟地址的低12位组合成最终32位物理地址.

虚拟地址的结构:

bit 31 ... 20 19 ... 12 11 ... 0

| 第一级表索引 | 第二级表索引 | 页索引[偏移量] |

举个例子来说明2级小页mmu的结构,先假设虚拟地址为addr,那么dir目录项的索引号为:addr>>20;该dir目录索引号对应的目录项的内容31~10位作为pte的

基地址,addr的19~12位作为相对该pte基址的偏移,即:pte页表项,将pte页表项的31~12位和addr的11~0位偏移量组合成最终的物理地址,所以通过以上分析我们可以

总结出如下:dir共4k个,每个挂带1M数据,该1M数据又由256个pte分开管理,每个pte对应1个页4k,也就是1个dir页目录含有1个pte页表基地址对应256个pte页表项,1个页表项对应1个4k空间,可以用如下示意图简易的表示:

dir+0000->pte+000->4k

|->pte+001->4k

|->pte+002->4k

...

|->pte+253->4k

|->pte+254->4k

|->pte+255->4k

dir+0001->pte+000->4k

|->pte+001->4k

|->pte+002->4k

...

|->pte+253->4k

|->pte+254->4k

|->pte+255->4k

...

dir+4094->pte+000->4k

|->pte+001->4k

|->pte+002->4k

...

|->pte+253->4k

|->pte+254->4k

|->pte+255->4k

dir+4095->pte+000->4k

|->pte+001->4k

|->pte+002->4k

...

|->pte+253->4k

|->pte+254->4k

|->pte+255->4k

所以通过上述的2级小页mmu就轻松的实现了4k*256*4k=4G虚拟空间到物理空间的无缝转换.[注意:2级小页mmu结构中,dir和pte表项中的地址均为物理地址]

通过上面的mmu的转换路径分析,我们大体掌握了mmu的机理,现在转入正题,来看看__ioremap函数中remap_area_page子函数体源码:

static int

remap_area_pages(unsigned long address, unsigned long pfn,

unsigned long size, unsigned long flags)

{

int error;

pgd_t * dir;

unsigned long end = address + size;

pfn -= address >> PAGE_SHIFT;

//#define PGDIR_SHIFT           20

//#define pgd_index(addr)      ((addr) >> PGDIR_SHIFT)

//#define pgd_offset(mm, addr) ((mm)->pgd+pgd_index(addr))

//dir=address对应的高12值[0~4095]

dir = pgd_offset(&init_mm, address);

flush_cache_all();

BUG_ON(address >= end);

spin_lock(&init_mm.page_table_lock);

do {

pmd_t *pmd;

//at91rm9200处理器采用2级小页mmu方式,所以pmd_alloc将直接返回dir,即:pmd = dir;

//对于采用3级页表的系统来说,pmd_alloc将会向dir目录项填入pmd的物理基地址

pmd = pmd_alloc(&init_mm, dir, address);

error = -ENOMEM;

if (!pmd)

break;

//remap_area_pmd函数将中间页pmd填入pte内容,因为采用的是2级小页mmu方式,所以pmd=dir;

if (remap_area_pmd(pmd, address, end - address,

pfn + (address >> PAGE_SHIFT), flags))

break;

error = 0;

//address+1M,如果size=5.2M,那么会连续占用接下的5个dir区域

address = (address + PGDIR_SIZE) & PGDIR_MASK;

dir++;

} while (address && (address < end));

spin_unlock(&init_mm.page_table_lock);

flush_tlb_all();

return error;

}

static inline int

remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size,

unsigned long pfn, unsigned long flags)

{

unsigned long end;

pgprot_t pgprot;

//只对address的低1M地址空间进行pte创建

address &= ~PGDIR_MASK;

end = address + size;

//#define PGDIR_SHIFT 20

//#define PGDIR_SIZE (1UL << PGDIR_SHIFT) 1级目录以1M字节为dir++自加单位

if (end > PGDIR_SIZE)

end = PGDIR_SIZE;

pfn -= address >> PAGE_SHIFT;

BUG_ON(address >= end);

pgprot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_WRITE | flags);

do {

//pte_alloc将获取256个连续pte的空间,并将地址指针指向这256个pte地址空间的最后一个地址处

pte_t * pte = pte_alloc(&init_mm, pmd, address);

if (!pte)

return -ENOMEM;

//remap_area_pte为256个连续的pte填充pfn物理地址

remap_area_pte(pte, address, end - address, pfn + (address >> PAGE_SHIFT), pgprot);

//因为是2级小页mmu方式,所以PMD_SIZE = PGD_SIZE = 1M

//又因为外层dir已经做了size总大小的处理,所以pmd这里,将只对1M空间进行pte内容填充,也就是while循环仅仅执行一次

address = (address + PMD_SIZE) & PMD_MASK;

pmd++;

} while (address && (address < end));

return 0;

}

以下为一些宏函数的出处和简单定义:

#define pmd_populate(mm,pmdp,pte) \

do { \

unsigned long __prot; \

if (mm == &init_mm) \

__prot = _PAGE_KERNEL_TABLE; \

else \

__prot = _PAGE_USER_TABLE; \

set_pmd(pmdp, __mk_pmd(pte, __prot)); \

} while (0)

//函数__mk_pmd用来把pte数值赋给pmd单元

//函数pte_alloc

static inline pmd_t __mk_pmd(pte_t *ptep, unsigned long prot)

{

unsigned long pte_ptr = (unsigned long)ptep;

pmd_t pmd;

//#define PTRS_PER_PTE 256

pte_ptr -= PTRS_PER_PTE * sizeof(void *);

pmd_val(pmd) = __virt_to_phys(pte_ptr) | prot;

return pmd;

}

include/asm-arm/cpu-multi32.h:#

include/asm-arm/cpu-single.h中有如下定义:

#define cpu_set_pgd cpu_fn(CPU_NAME,_set_pgd)

#define cpu_set_pmd cpu_fn(CPU_NAME,_set_pmd)

#define cpu_set_pte cpu_fn(CPU_NAME,_set_pte)

而cpu_fn定义如下:

#ifdef __STDC__

#define __cpu_fn(name,x) cpu_##name##x

#else

#define __cpu_fn(name,x) cpu_/**/name/**/x

#endif

#define cpu_fn(name,x) __cpu_fn(name,x)

所以最后at91rm9200处理器cpu_set_pgd,cpu_set_pmd和cpu_set_pte对应的函数原型为:

cpu_set_pgd -- cpu_arm920_set_pgd

cpu_set_pmd -- cpu_arm920_set_pmd

cpu_set_pte -- cpu_arm920_set_pte

以上三个函数位于arch/arm/mm/proc-arm920.S

include/asm-arm/proc-armv/Pgtable.h

#define PTRS_PER_PTE 256  //第二级,表示有256个表项,每个表项描述4K的二级页面

#define PTRS_PER_PMD 1    //因为at91rm9200处理器不需要该级,所以此处忽略

#define PTRS_PER_PGD 4096 //第一级,表示有4K个表项,每个表项描述1M的一级页面

include/asm-arm/pgtable.h中

extern pgd_t swapper_pg_dir[PTRS_PER_PGD];

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值