arm的2级页表在Linux内核创建过程解析

  系统DDR的基地址为0x0,内存为1GB,所以TTB的基地址为0x4000。下面要创建虚拟地址0xfe700000到物理地址0xffff0000之间的映射,映射大小为64KB,即16页。由于物理地址不是1MB字节对齐,所以必须创建两级映射。

  用户空间/内核空间划分为2G/2G。

 

create_mapping:pgd = 0x80007f98, addr = 0xfe700000, phys =0xffff0000, next = 0xfe710000
pte =0x3fffd000, pmdval=0x3fffd801                            //这里的pte已经转成物理地址了
*pte = 0xffff045f, *(pte+(2048>>2))=0xffff045e
*pte = 0xffff145f, *(pte+(2048>>2))=0xffff145e
*pte = 0xffff245f, *(pte+(2048>>2))=0xffff245e
*pte = 0xffff345f, *(pte+(2048>>2))=0xffff345e
*pte = 0xffff445f, *(pte+(2048>>2))=0xffff445e
*pte = 0xffff545f, *(pte+(2048>>2))=0xffff545e
*pte = 0xffff645f, *(pte+(2048>>2))=0xffff645e
*pte = 0xffff745f, *(pte+(2048>>2))=0xffff745e
*pte = 0xffff845f, *(pte+(2048>>2))=0xffff845e
*pte = 0xffff945f, *(pte+(2048>>2))=0xffff945e
*pte = 0xffffa45f, *(pte+(2048>>2))=0xffffa45e
*pte = 0xffffb45f, *(pte+(2048>>2))=0xffffb45e
*pte = 0xffffc45f, *(pte+(2048>>2))=0xffffc45e
*pte = 0xffffd45f, *(pte+(2048>>2))=0xffffd45e
*pte = 0xffffe45f, *(pte+(2048>>2))=0xffffe45e
*pte = 0xfffff45f, *(pte+(2048>>2))=0xfffff45e

读取:level 1 页表
0x00007F98:  3FFFD801 3FFFDC01

读取: level 2 页表
0x3FFFD400:  FFFF045F FFFF145F FFFF245F FFFF345F
0x3FFFD410:  FFFF445F FFFF545F FFFF645F FFFF745F
0x3FFFD420:  FFFF845F FFFF945F FFFFA45F FFFFB45F
0x3FFFD430:  FFFFC45F FFFFD45F FFFFE45F FFFFF45F            /* 这个位置是linux 页表,也就是所谓的软件页表 */


0x3FFFDC00:  FFFF045E FFFF145E FFFF245E FFFF345E
0x3FFFDC10:  FFFF445E FFFF545E FFFF645E FFFF745E
0x3FFFDC20:  FFFF845E FFFF945E FFFFA45E FFFFB45E
0x3FFFDC30:  FFFFC45E FFFFD45E FFFFE45E FFFFF45E            /* 这个位置是硬件页表,是给ARM硬件MMU使用的 */


0x3FFFD000:  00000000 00000000 00000000 00000000
0x3FFFD010:  00000000 00000000 00000000 00000000
0x3FFFD020:  00000000 00000000 00000000 00000000
0x3FFFD030:  00000000 00000000 00000000 00000000            /* 这个位置是linux 页表,也就是所谓的软件页表 */


0x3FFFD800:  00000000 00000000 00000000 00000000
0x3FFFD810:  00000000 00000000 00000000 00000000
0x3FFFD820:  00000000 00000000 00000000 00000000
0x3FFFD830:  00000000 00000000 00000000 00000000           /* 这个位置是硬件页表,是给ARM硬件MMU使用的 */

static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte,
      pmdval_t prot)
{
 pmdval_t pmdval = (pte + PTE_HWTABLE_OFF) | prot;/* HW page table offset is 512*4 = 2048 */
 printk("pte =0x%x, pmdval=0x%x\n", pte, pmdval);
 pmdp[0] = __pmd(pmdval);        //第1个页表项
#ifndef CONFIG_ARM_LPAE                  //这个宏没有定义,所以1次要填充PGD的8个字节的页表
 pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t)); //第2个页表项
#endif
 flush_pmd_entry(pmdp);
}


static pte_t * __init early_pte_alloc(pmd_t *pmd, unsigned long addr, unsigned long prot)
{
 if (pmd_none(*pmd)) {
  pte_t *pte = early_alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE);
  __pmd_populate(pmd, __pa(pte), prot);
 }
 BUG_ON(pmd_bad(*pmd));
 return pte_offset_kernel(pmd, addr);
}

static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,
      unsigned long end, unsigned long pfn,
      const struct mem_type *type)
{
 pte_t *pte = early_pte_alloc(pmd, addr, type->prot_l1);
 do {
  set_pte_ext(pte, pfn_pte(pfn, __pgprot(type->prot_pte)), 0);
  printk("*pte = 0x%x, *(pte+(2048>>2))=0x%x\n", *pte, *(pte + (2048>>2)));
  pfn++;
 } while (pte++, addr += PAGE_SIZE, addr != end);
}


void __init create_mapping(struct map_desc *md)
{
 unsigned long addr, length, end;
 phys_addr_t phys;
 const struct mem_type *type;
 pgd_t *pgd;

 if (md->virtual != vectors_base() && md->virtual < TASK_SIZE) {
  printk(KERN_WARNING "BUG: not creating mapping for 0x%08llx"
         " at 0x%08lx in user region\n",
         (long long)__pfn_to_phys((u64)md->pfn), md->virtual);
  return;
 }

 if ((md->type == MT_DEVICE || md->type == MT_ROM) &&
     md->virtual >= PAGE_OFFSET &&
     (md->virtual < VMALLOC_START || md->virtual >= VMALLOC_END)) {
  printk(KERN_WARNING "BUG: mapping for 0x%08llx"
         " at 0x%08lx out of vmalloc space\n",
         (long long)__pfn_to_phys((u64)md->pfn), md->virtual);
 }

 type = &mem_types[md->type];

#ifndef CONFIG_ARM_LPAE
 /*
  * Catch 36-bit addresses
  */
 if (md->pfn >= 0x100000) {
  create_36bit_mapping(md, type);
  return;
 }
#endif

 addr = md->virtual & PAGE_MASK;
 phys = __pfn_to_phys(md->pfn);
 length = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK));

 if (type->prot_l1 == 0 && ((addr | phys | length) & ~SECTION_MASK)) {
  printk(KERN_WARNING "BUG: map for 0x%08llx at 0x%08lx can not "
         "be mapped using pages, ignoring.\n",
         (long long)__pfn_to_phys(md->pfn), addr);
  return;
 }

 pgd = pgd_offset_k(addr);
 end = addr + length;
 do {
  unsigned long next = pgd_addr_end(addr, end);
  //if(phys == 0xffff0000)
   printk("%s:pgd = 0x%x, addr = 0x%x, phys =0x%x, next = 0x%x\n", __func__, (u32)pgd, addr, phys, next);
  alloc_init_pud(pgd, addr, next, phys, type);

  phys += next - addr;
  addr = next;
 } while (pgd++, addr != end);
}

 

一级页表的内容是:3FFFD801 3FFFDC01

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

按照linux的方式映射:

pgd_index = (0xfe7 >> 1) = 0x7f3
所以0xfe600000和0xfe700000组成的2MB空间是放到一个pgd[2]数组里的。0xfe6对应的是pgd[0],0xfe7对应的是pgd[1]。

一级页表的在TTB中的TTB_offset = 0x7f3 * 8 = 0x3f98
所以1级页表的地址为0x4000+0x3f98 = 0x7f98

所以二级页表的地址计算过程为:
第1个表项0x3fffd801 ----> 0xfe600000虚拟地址

0x3fffd| 1|000 0000  0001
   hex    bin   bin  bin 
  bit31   bit11  |bit10 bit9     ......           bit2 bit1  bit0
0x3fffd|    1               0   0 0 0 0 0     0   0    0     0    即 0x3fffd800  就是上面的硬件二级物理页表地址。

第2个表项0x3fffdc01 ----> 0xfe700000虚拟地址

0x3fffd| 1|100 0000  0001
   hex     bin  bin   bin
  bit31  bit11 |bit10  bit9     ......           bit2  bit1 bit0
0x3fffd|   1          1     0   0 0 0 0 0     0   0     0      0  即 0x3fffdc00  就是上面的硬件二级物理页表地址。

所以从linux页表角度来看,bit31-bit11为2级页表的基地址,而bit10-bit2为二级页表的索引(偏移)。而bit10-bit2来自虚拟地址(MVA)的bit20-bit12。

虚拟地址0xfe600000: bit20-bit12 是0b0 0000 0000

虚拟地址0xfe700000: bit20-bit12 是0b1 0000 0000

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

下面是从硬件角度做页表转换:


虚拟地址0xfe700000
 
pgd_index = 0xfe7

TTB_offset = 0xfe7 * 4 = 0x3f9c
所以1级页表的地址为0x4000+0x3f9c = 0x7f9c,所以硬件找到的1级表项内容是3FFFDC01

下面是找2级页表的过程
0x3fffd| 11|00 0000  0001
   hex     bin  bin   bin
  bit31   bit10  bit9 ......     bit2 bit1 bit0
0x3fffd|  1  1    |0   0 0 0 0 0   0   0    0    0  即 0x3fffdc00  就是上面的硬件二级物理页表地址。

假定MMU要访问第1个1MB空间,即虚拟地址为0xfe60000,即pgd_index = 0xfe6
TTB_offset = 0xfe6 * 4 = 0x3f98

0x3fffd| 10|00 0000  0001
   hex    bin   bin  bin 
  bit31    bit10 bit9 ......        bit2    bit1    bit0
0x3fffd| 1  0    |0   0 0 0 0 0   0   0    0    0  即 0x3fffd800  就是上面的硬件二级物理页表地址。
bit9...bit2 来自虚拟机地址的bit19~bit12。

虚拟地址0xfe600000: bit19-bit12 是0b0 0000 0000

虚拟地址0xfe700000: bit19-bit12 是0b0 0000 0000

 

---------------------------------------------------------------------------------------------------------------------------------------------------

总结:

                                                                       bit21    bit20
pg_index = 0xfe7-->0b 1111  1110    0 1   1           1           xxxx xxxx xxxx xxxx xxxx

对于linux来说实际是以2MB为单位映射的,pgd一定是8个字节对齐的,比如这里的0x7f98而不是0x7f9c。bit21为0则映射到这两2MB第1MB空间,而bit21为1的话则映射到这2MB第1MB空间,我们举的这个例子,是映射到第2个1MB空间。我们知道硬件的二级页表本来是256项,即256*4KB = 1MB空间,现在明显Linux把它改成512项, 512*4KB = 2MB。所以linux是这样处理的,通过定义1级pgd为8个字节,即有两个表项,2级页表pte有512个表项,L1的第1个表项,指向L2的前256个表项,L1的第2个表项,指向L2的后256个表项,
总体来看和硬件是保持一致的。

pmdp[0] = __pmd(pmdval);        //第1个页表项, 对应第1个1MB空间  (bit21 = 0)
pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t)); //第2个页表项, 跳过256个表项,即跳过1MB地址空间,进入到第2个1MB空间 (bit21 = 1)
一次性把L1的pgd的2个表项都填充完毕,linux的映射是以2MB空间为单位映射的。

bit21在linux中只是前256个表项还是后256个表项。

#define PTRS_PER_PTE  512
#define pte_index(addr)  (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))

最后2级页表的布局:

0x3fffd000--> Linux page table         offset: 0
               256个表项(1st 1MB)
0x3fffd400--> Linux page table       
               256个表项(2st 1MB)
0x3fffd800--> HW page table            offset: 2048
               256个表项(1st 1MB)
0x3fffdc00--> HW page table
               256个表项(2st 1MB)

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值