固定映射的线性地址

定义

把固定的线性地址映射到指定的物理地址。

使用场景

fixmap是一段固定地址映射。kernel预留一段虚拟地址空间。因此虚拟地址是在编译的时候确定。fixmap可以用来做什么?kernel启动初期,由于此时的kernel已经运行在虚拟地址上。因此我们访问具体的物理地址是不行的,必须建立虚拟地址和物理地址的映射,然后通过虚拟地址访问才可以。例如:dtb中包含bootloader传递过来的内存信息,我们需要解析dtb,但是我们得到的是dtb的物理地址。因此访问之前必须创建映射,创建映射又需要内存。但是由于所有的内存管理子系统还没有ready。因此我们不能使用ioremap接口创建映射。为此kernel提出fixmap的解决方案。(摘自wowo科技)
fixmap addresses原理

设计接口

a. 提供接口1: 可供选择宏线性地址范围(这范围的线性地址可用于固定映射),
b . 提供接口2: 根据这范围的线性地址,定位到相应的页目录项,分配页表,并且把页表的物理地址填充到相应的页目录项中。
c. 提供接口3: 根据映射线性地址和物理地址和页访问权限,创建映射。实现原理,根据线性地址找到相应的页表项,然后把物理地址和权限合成后,写入其中的页表项中。
d. 接口4: 撤销线性地址和物理地址的映射关系。

实现

a. 接口1: 宏对应线性地址

#define __fix_to_virt(x)        (FIXADDR_TOP - ((x) << PAGE_SHIFT))
#define FIXADDR_TOP     ((unsigned long)__FIXADDR_TOP)
define __FIXADDR_TOP   0xfffff000

 53 enum fixed_addresses {                                                          
 54         FIX_HOLE,                                                               
 55         FIX_VSYSCALL,                                                           
 56 #ifdef CONFIG_X86_LOCAL_APIC                                                    
 57         FIX_APIC_BASE,  /* local (CPU) APIC) -- required for SMP or not */      
 58 #endif                                                                          
 59 #ifdef CONFIG_X86_IO_APIC                                                       
 60         FIX_IO_APIC_BASE_0,                                                     
 61         FIX_IO_APIC_BASE_END = FIX_IO_APIC_BASE_0 + MAX_IO_APICS-1,             
 62 #endif                                                                          
 63 #ifdef CONFIG_X86_VISWS_APIC                                                    
 64         FIX_CO_CPU,     /* Cobalt timer */                                      
 65         FIX_CO_APIC,    /* Cobalt APIC Redirection Table */                     
 66         FIX_LI_PCIA,    /* Lithium PCI Bridge A */                              
 67         FIX_LI_PCIB,    /* Lithium PCI Bridge B */                              
 68 #endif                                                                          
 69 #ifdef CONFIG_X86_F00F_BUG                                                      
 70         FIX_F00F_IDT,   /* Virtual mapping for IDT */                           
 71 #endif                                                                          
 72 #ifdef CONFIG_X86_CYCLONE_TIMER                                                 
 73         FIX_CYCLONE_TIMER, /*cyclone timer register*/                           
 74 #endif                                                                          
 75 #ifdef CONFIG_HIGHMEM                                                           
 76         FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */      
 77         FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,                   
 78 #endif                                                                          
 79 #ifdef CONFIG_ACPI_BOOT                                                         
 80         FIX_ACPI_BEGIN,                                                         
 81         FIX_ACPI_END = FIX_ACPI_BEGIN + FIX_ACPI_PAGES - 1,                     
 82 #endif                                                                          
 83 #ifdef CONFIG_PCI_MMCONFIG                                                      
 84         FIX_PCIE_MCFG,                                                          
 85 #endif                                                                          
 86         __end_of_permanent_fixed_addresses,                                     
 87         /* temporary boot-time mappings, used before ioremap() is functional */ 
 88 #define NR_FIX_BTMAPS   16                                                      
 89         FIX_BTMAP_END = __end_of_permanent_fixed_addresses,                     
 90         FIX_BTMAP_BEGIN = FIX_BTMAP_END + NR_FIX_BTMAPS - 1,                                                                                                                                                  
 91         FIX_WP_TEST,                                                            
 92         __end_of_fixed_addresses                                                
 93 }

线性地址起始地址:
__end_of_fixed_addresses=FIXADDR_TOP - ((N) << PAGE_SHIFT)=0xffff f000-N<<12
一个枚举变量对应着一个页表项。

线性地址结束地址:FIXADDR_TOP=0xffff f000

b . 接口2: 根据这范围的线性地址,定位到相应的页目录项,分配页表,并且把页表的物理地址填充到相应的页目录项中。

线性地址从0xffff f000-N<<12到0xffff f000,这样只需要修改一个目录项,也就是页目录最后一项0x3ff。
一个页目录项,对应着1024个页表项,一个页表项对应着1024个页。
一个页目录项,可寻址2^22=4M

arch/i386/mm/init.c

310 static void __init pagetable_init (void)                                        
311 {                                                                               
312         unsigned long vaddr; 
341         vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;         
342         page_table_range_init(vaddr, 0, pgd_base); 
}

在341行,vaddr=0xffc00000

page_table_range_int:分配页表,并且把页表的物理地址填充到相应的页目录项中
103 static void __init page_table_range_init (unsigned long start, unsigned long end, pgd_t *pgd_base)
104 {                                                                               
105         pgd_t *pgd;                                                             
106         pud_t *pud;                                                             
107         pmd_t *pmd;                                                             
108         int pgd_idx, pmd_idx;                                                   
109         unsigned long vaddr;                                                    
110                                                                                 
111         vaddr = start;                                                          
112         pgd_idx = pgd_index(vaddr);                                             
113         pmd_idx = pmd_index(vaddr);                                             
114         pgd = pgd_base + pgd_idx;                                               
115                                                                                 
116         for ( ; (pgd_idx < PTRS_PER_PGD) && (vaddr != end); pgd++, pgd_idx++) { 
117                 if (pgd_none(*pgd))                                             
118                         one_md_table_init(pgd);                                 
119                 pud = pud_offset(pgd, vaddr);                                   
120                 pmd = pmd_offset(pud, vaddr);                                   
121                 for (; (pmd_idx < PTRS_PER_PMD) && (vaddr != end); pmd++, pmd_idx++) {
122                         if (pmd_none(*pmd))                                     
123                                 one_page_table_init(pmd);                       
124                                                                                 
125                         vaddr += PMD_SIZE;                                      
126                 }                                                                                                                            
127                 pmd_idx = 0;                                                    
128         }                                                                       
129 } 

在123行,one_page_table_init给pmd地址进行分配页表,并在页目录0x3ff项,对应项填充页表的物理地址。
在116行,PTRS_PER_PGD=0x400,pgd_idx=0x3ff,循环只执行一次。

c. 接口3:根据映射线性地址和物理地址和页访问权限,创建映射。
参数:提供固定映射的idx和物理地址,物理地址以页为单位。

131 void __set_fixmap (enum fixed_addresses idx, unsigned long phys, pgprot_t flags)
132 {                                                                               
133         unsigned long address = __fix_to_virt(idx);                             
134                                                                                 
135         if (idx >= __end_of_fixed_addresses) {                                  
136                 BUG();                                                          
137                 return;                                                         
138         }                                                                       
139         set_pte_pfn(address, phys >> PAGE_SHIFT, flags);                        
140 }  

在139行,phys右移12位,通过set_pte_pfn实现,虚拟地址和物理地址和权限关联。

d. 接口4: 撤销线性地址和物理地址的映射关系。

06 #define clear_fixmap(idx) \                                                                                                                                                                                   
107                 __set_fixmap(idx, 0, __pgprot(0)) 

调用__set_fixmap函数向其线性地址对应的页表项中写0.

测试用例


把枚举变量转换为线性地址

vaddr1 = __fix_to_virt(FIX_WP_TEST) & PMD_MASK;

##创建线性地址的页目录,页表,把页表项的内容写为0

page_table_range_init(vaddr1, 0, pgd_base);

103 static void __init page_table_range_init (unsigned long start, unsigned long end, pgd_t *pgd_base)
104 {                                                                               
105         pgd_t *pgd;                                                             
106         pud_t *pud;                                                             
107         pmd_t *pmd;                                                             
108         int pgd_idx, pmd_idx;                                                   
109         unsigned long vaddr;                                                    
110                                                                                 
111         vaddr = start;                                                          
112         pgd_idx = pgd_index(vaddr);                                             
113         pmd_idx = pmd_index(vaddr);                                             
114         pgd = pgd_base + pgd_idx;                                               
115         printk(KERN_ERR "tom page_table_range_init1 =%x %x\n",start,end);       
116         printk(KERN_ERR "tom page_table_range_init =%x %x %x %x\n",pgd_idx,pmd_idx,pgd,pgd_base);
117         //tom page_table_range_init =3ff 0 c03cbffc c03cb000                    
118         //tom page_table_range_init =3fe 0 c03cbff8 c03cb000                    
119         for ( ; (pgd_idx < PTRS_PER_PGD) && (vaddr != end); pgd++, pgd_idx++) { 
120                 if (pgd_none(*pgd))          //页目录项已经存在,所以不需要床架页目录项目                                   
121                 {                                                               
123                         one_md_table_init(pgd);                                 
124                 }                                                               
125                 pud = pud_offset(pgd, vaddr);                                   
126                 pmd = pmd_offset(pud, vaddr);                                   
127                 printk(KERN_ERR "tom page_table_range_init12 =%x %x\n",pud,pmd);
128                 for (; (pmd_idx < PTRS_PER_PMD) && (vaddr != end); pmd++, pmd_idx++) {
129                         if (pmd_none(*pmd))        
       //因为页目录项是第1023项,没有初始化,896MB,只初始化到第 991项。                           
130                         {                                                       
131                                 one_page_table_init(pmd);      //初始化     
132                         printk(KERN_ERR "tom page_table_range_init13 =%x\n",pmd);
133                         }                                                       
134                                                                                 
135                         printk(KERN_ERR "tom page_table_range_init14 =%x\n",pmd);
136                         vaddr += PMD_SIZE;                                      
137                 }                                                                                                                                                                                             
138                 pmd_idx = 0;                                                    
139         }                                                                       
140 }    

把相应的线性地址的页表的置,固定相应的物理地址

__set_fixmap(FIX_WP_TEST, __pa(&swapper_pg_dir), PAGE_READONLY);

131 void __set_fixmap (enum fixed_addresses idx, unsigned long phys, pgprot_t flags)
132 {                                                                               
133         unsigned long address = __fix_to_virt(idx);                             
134                                                                                 
135         if (idx >= __end_of_fixed_addresses) {                                  
136                 BUG();                                                          
137                 return;                                                         
138         }                                                                       
139         set_pte_pfn(address, phys >> PAGE_SHIFT, flags);                        
140 }  

撤销这个映射

06 #define clear_fixmap(idx) \                                                                                                                                                                                   
107                 __set_fixmap(idx, 0, __pgprot(0)) 

就是把相应页表项的内容设置为0

例子

360         //Add by tom                                                            
361         vaddr1 = __fix_to_virt(FIX_WP_TEST) & PMD_MASK;                         
                                
364         printk(KERN_ERR "tom FIX_WP_TEST value before=%x %x\n",*(unsigned int *)swapper_pg_dir, swapper_pg_dir);
365         __set_fixmap(FIX_WP_TEST, __pa(&swapper_pg_dir), PAGE_READONLY);        
366         /*printk(KERN_ERR "tom FIX_WP_TEST value=%x\n",*(unsigned int *)vaddr1);*/
367         printk(KERN_ERR "tom FIX_WP_TEST value=%x %x\n",*(unsigned int *)__fix_to_virt(FIX_WP_TEST),*(unsigned int *)swapper_pg_dir);

把FIX_WP_TEST对应的线性地址固定到swapper_pg_dir的物理地址,这样访问FIX_WP_TEST的第一个地址,就是swapper_pg_dir指向的一个页目录项内容,实验测试通过.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值