固定映射的线性地址可以按照任意方式映射到固定的物理内存的页框,而不是线性地址减去0xC000 0000。
说明:本文分析的是2.6.11的源码
固定地址的作用:
fixmap是一段固定地址映射。kernel预留一段虚拟地址空间。因此虚拟地址是在编译的时候确定。fixmap可以用来做什么?kernel启动初期,由于此时的kernel已经运行在虚拟地址上。因此我们访问具体的物理地址是不行的,必须建立虚拟地址和物理地址的映射,然后通过虚拟地址访问才可以。例如:dtb中包含bootloader传递过来的内存信息,我们需要解析dtb,但是我们得到的是dtb的物理地址。因此访问之前必须创建映射,创建映射又需要内存。但是由于所有的内存管理子系统还没有ready。因此我们不能使用ioremap接口创建映射。为此kernel提出fixmap的解决方案。(摘自wowo科技)
fixmap addresses原理
固定映射用的那些线性地址
#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 }
也就是线性地址,0xffff f000-N<<12,知道线性地址从0xffff f000-N<<12到0xffff f000,N的大小由fixed_addresses枚举变量决定的。
创建固定映射步骤
把枚举变量转换为线性地址
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指向的一个页目录项内容,实验测试通过.