内核的最终页表

在上一节中,我们可以看到,在kernel/head.S中建立了临时页表swapper_pg_dir。

背景

由内核页表所提供的最终映射必须把0xc0000000开始的线性地址转化为从0开始的物理地址。

设计

需求:把线性地址0xc0000000-0xc37FFFFF映射到物理地址0x0-0x37FFFFF。

分析:

现在页目录项有1024项,第0、1、768和769项是指向真实页表。把线性地址0xc0000000-0xc37FFFFF映射到物理地址0x0-0x37FFFFF,也就是把页目录项中第768项到第991项,分配页表内容,并且把物理地址加权限写进页表中。

步骤:

a.找到页目录项,然后分配页表,把页目录项指向对应页表。

b.把物理地址和权限写进页表项中。

代码实现

a.1 找到对应页目录项地址

        pgd_idx = pgd_index(PAGE_OFFSET);                                
        pgd = pgd_base + pgd_idx;   
        pmd = one_md_table_init(pgd);

PAGE_OFFSET是0xc0000000,则pgd_index(PAGE_OFFSET)是0x300,pgd_base=swapper_pg_dir,所以pgd是swapper_pg_dir+0x300,采用两级页表,没有上次目录和中间目录,则pmd=pgd=swapper_pg_dir+0x300。

a.2 分配页表,把页表的物理地址写进页目录项中,并返回页表的虚拟地址

因为页表有2^10=1024项,每项4个字节,则 1024×4=PAGE_SIZE大小的内存。

static pte_t * __init one_page_table_init(pmd_t *pmd)                           
{                                                                               
        if (pmd_none(*pmd)) {                                                   
                pte_t *page_table = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
                set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE));            
                if (page_table != pte_offset_kernel(pmd, 0))                    
                        BUG();                                                  
                                                                                
                return page_table;                                              
        }                                                                                                                                                
        return pte_offset_kernel(pmd, 0);                                       
}  

b 把物理地址和权限写进页表项中.

pte = one_page_table_init(pmd);
set_pte(pte, pfn_pte(pfn, PAGE_KERNEL));

从one_page_table返回页表的虚拟地址,通过set_pte(pte, pfn_pte(pfn, PAGE_KERNEL)),用相应的物理地址和权限填充页表的第1项(就是返回的页表的虚拟地址),通过循环填充页表中其他项。

建立低端内存的kernel_physical_mapping_init代码如下:

    /*                                                                              
 * This maps the physical memory to kernel virtual address space, a total       
 * of max_low_pfn pages, by creating page tables starting from address          
 * PAGE_OFFSET.                                                                 
 */                                                                                                                                                                         
static void __init kernel_physical_mapping_init(pgd_t *pgd_base)                
{                                                                               
        unsigned long pfn;                                                      
        pgd_t *pgd;                                                             
        pmd_t *pmd;                                                             
        pte_t *pte;                                                             
        int pgd_idx, pmd_idx, pte_ofs;                                          
                                                                                
        pgd_idx = pgd_index(PAGE_OFFSET);                                       
        pgd = pgd_base + pgd_idx;                                               
        pfn = 0;                                                                
                                                                                
        for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) {                      
                pmd = one_md_table_init(pgd);                                   
                if (pfn >= max_low_pfn)                                         
                        continue;                                               
                for (pmd_idx = 0; pmd_idx < PTRS_PER_PMD && pfn < max_low_pfn; pmd++, pmd_idx++) {
                        unsigned int address = pfn * PAGE_SIZE + PAGE_OFFSET;   
                                                                                
                        /* Map with big pages if possible, otherwise create normal page tables. */
                        if (cpu_has_pse) {                                      
                                unsigned int address2 = (pfn + PTRS_PER_PTE - 1) * PAGE_SIZE + PAGE_OFFSET + PAGE_SIZE-1;
                                                                                
                                if (is_kernel_text(address) || is_kernel_text(address2))
                                        set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE_EXEC));
                                else                                            
                                        set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE));
                                        pfn += PTRS_PER_PTE;                            
                        } else {                                                
                                pte = one_page_table_init(pmd);                 
                                for (pte_ofs = 0; pte_ofs < PTRS_PER_PTE && pfn < max_low_pfn; pte++, pfn++, pte_ofs++) {
                                                if (is_kernel_text(address))    
                                                        set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC));
                                                else                            
                                                        set_pte(pte, pfn_pte(pfn, PAGE_KERNEL));
                                }                                               
                        }                                                       
                }                                                               
        }                                                                       
}     

内存映射示意图

123说明:
1)内核空间的线性地址0xc0000000-0xc37FFFFF映射到物理地址0x0-0x37FFFFF。
2)用户空间的线性地址0x0-0x3ff000映射到物理地址0x0-0x3ff000.

PSE的打开

在arch/i386/kernel/cpu/common.c中

264                         cpuid(0x00000001, &tfms, &junk, &excap, &capability);   
265                         c->x86_capability[0] = capability;
140 static inline void cpuid(int op, int *eax, int *ebx, int *ecx, int *edx)                                                                                                                                      
141 {                                                                               
142         __asm__("cpuid"                                                         
143                 : "=a" (*eax),                                                  
144                   "=b" (*ebx),                                                  
145                   "=c" (*ecx),                                                  
146                   "=d" (*edx)                                                   
147                 : "0" (op), "c"(0));                                            
148 }   

就是通过调用,cpuid汇编,然后把capabilty的值保存到edx中,然后c->x86_capability[0] 等于edx,里面有了包含CPU是否支持PSE。

PSE的打开与关闭

CR4中的PSE位默认是0.

打开PSE功能。

  1. 置位PDE中的PS
  2. 置位CR4的PSE

置位PDE中的PS位的动作在arch/i386/mm/init.c中

 if (cpu_has_pse) {                                      
 unsigned int address2 = (pfn + PTRS_PER_PTE - 1) * PAGE_SIZE + PAGE_OFFSET + PAGE_SIZE-1;
                                                                                
                                 if (is_kernel_text(address) || is_kernel_text(address2))
                                 {                                               
                                         set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE_EXEC));
                                }                                               
                                 else                                            
                                 {                                                                                                                                                                             
                                        set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE));
                               }                                               
                                 pfn += PTRS_PER_PTE;                            
                        }                                    

set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE))可以看到置位PDE中的PS位。

置位CR4的PSE在pagetable_init中

342         if (cpu_has_pge) {                                                      
343                 set_in_cr4(X86_CR4_PGE);                                        
344                 __PAGE_KERNEL |= _PAGE_GLOBAL;                                  
345                 __PAGE_KERNEL_EXEC |= _PAGE_GLOBAL;                             
346         }  

PSE的关闭
1.
可以通过disable_pse=1,然后
在arch/i386/kernel/cpu/common.c的early_cpu_init中添加下面语句:

529 void __init early_cpu_init(void)                                                
530 {  
...................

540         early_cpu_detect();                                                     
541                                                                                                                                                  
542         if(disable_pse)                                                         
543             clear_bit(X86_FEATURE_PSE, boot_cpu_data.x86_capability);           
544                                                                                 
545 #ifdef CONFIG_DEBUG_PAGEALLOC                                                   
546         /* pse is not compatible with on-the-fly unmapping,                     
547          * disable it even if the cpus claim to support it.                     
548          */                                                                     
549         clear_bit(X86_FEATURE_PSE, boot_cpu_data.x86_capability);               
550         disable_pse = 1;                                                        
551 #endif 

.....
}

2.或者打开kernel_hacking----------->Page alloc debugging
这样就关闭了PSE

        if (disable_pse)                                                                                                                                                                                      
                 clear_bit(X86_FEATURE_PSE, c->x86_capability);   

这样清空了x86_capability中的86_FEATURE_PSE。CR4中的PSE默认是0,PDE也没置位PS。

MMU单元是按大页(4M)或者页(4K)转换线性地址,主要根据:
1)PDE中的PS
2)CR4的PSE是否打开

关于地址的一些说明

内核页表
设置好CR3,然后硬件MMU单元会把线性地址转化为物理地址。
1)开启分页后,汇编还是c用的地址都是线性地址,都会经过MMU单元转化为物理地址。
2)CR3寄存器里,页目录,和页表中存放的都是物理地址,因为MMU会用到CR3页表,页必须都是物理地址。

movl $swapper_pg_dir-__PAGE_OFFSET,%eax
movl %eax,%cr3          /* set the page table pointer.. */

所以:
pte_t *page_table = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE));
注意:set_pmd中,pmd是虚拟地址,__pa(page_table)是物理地址。

问题:
根据框图,用户空间和内核空间的地址空间是多少?
内核的页表项,是768和769项?
这他们能寻址:
1100 0000 00 00 0000 0000 00000 0000 0000—1100 0000 00 11 1111 1111 1111 1111 1111
也就是0xC00 0000-0xC03 fffff
1100 0000 01 00 0000 0000 00000 0000 0000—1100 0000 01 11 1111 1111 1111 1111 1111
也就是0xC04 0000-0xC07f ffff
也就是:内核的地址0xC00 0000- 0xC03 ffff,映射到物理地址0-0x7f ffff,也是能寻址8M

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值