arm64内存-fixmap-宏定义和初始化
arch/arm64/include/asm/fixmap.h 中的定义
enum fixed_addresses
地址类型枚举,在 101 行的函数 __set_fixmap(idx, phys) 第一个参数用到。 将一个物理地址,映射到 idx 代表的 VA 地址上面。
25/* 26 * Here we define all the compile-time 'special' virtual 27 * addresses. The point is to have a constant address at 28 * compile time, but to set the physical address only 29 * in the boot process. 30 * 31 * Each enum increment in these 'compile-time allocated' 32 * memory buffers is page-sized. Use set_fixmap(idx,phys) 33 * to associate physical memory with a fixmap index. 34 */ 35enum fixed_addresses { 36 FIX_HOLE, 37 38 /* 39 * Reserve a virtual window for the FDT that is 2 MB larger than the 40 * maximum supported size, and put it at the top of the fixmap region. 41 * The additional space ensures that any FDT that does not exceed 42 * MAX_FDT_SIZE can be mapped regardless of whether it crosses any 43 * 2 MB alignment boundaries. 44 * 45 * Keep this at the top so it remains 2 MB aligned. 46 */ 47#define FIX_FDT_SIZE (MAX_FDT_SIZE + SZ_2M) 48 FIX_FDT_END, 49 FIX_FDT = FIX_FDT_END + FIX_FDT_SIZE / PAGE_SIZE - 1, 50 51 FIX_EARLYCON_MEM_BASE, 52 FIX_TEXT_POKE0, 53
69 __end_of_permanent_fixed_addresses, 70 71 /* 72 * Temporary boot-time mappings, used by early_ioremap(), 73 * before ioremap() is functional. 74 */ 75#define NR_FIX_BTMAPS (SZ_256K / PAGE_SIZE) 76#define FIX_BTMAPS_SLOTS 7 77#define TOTAL_FIX_BTMAPS (NR_FIX_BTMAPS * FIX_BTMAPS_SLOTS) 78 79 FIX_BTMAP_END = __end_of_permanent_fixed_addresses, 80 FIX_BTMAP_BEGIN = FIX_BTMAP_END + TOTAL_FIX_BTMAPS - 1, 81 82 /* 83 * Used for kernel page table creation, so unmapped memory may be used 84 * for tables. 85 */ 86 FIX_PTE, 87 FIX_PMD, 88 FIX_PUD, 89 FIX_PGD, 90 91 __end_of_fixed_addresses 92}; 93 94#define FIXADDR_SIZE (__end_of_permanent_fixed_addresses << PAGE_SHIFT) 95#define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE)
106extern void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot);
94 行, FIXADDR_SIZE 枚举量的最大值 * 2 ^ PAGE_SHIFT ,这儿仅仅时 VA 地址空间上面的预留。
95 行,FIXADDR_START 为 FIXADDR_TOP - FIXADDR_SIZE,fixmap 起始 的 VA地址。仅仅是一个数值。
枚举值和 VA 地址关系,通过 include/asm-generic/fixmap.h 得到
给的x越大,返回的值 越小。当给定x为0时,返回 FIXADDR_TOP
21#define __fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT)) 22#define __virt_to_fix(x) ((FIXADDR_TOP - ((x)&PAGE_MASK)) >> PAGE_SHIFT)
__set_fixmap(idx, phys,pgprot_t flags)
建立映射 - idx 指定VA地址, phys 物理地址,flags 设置 属性 FIXMAP_PAGE_NORMAL /RO/NOCACHE/IO/CLEAR
取消映射 - phys 填写 0, flags 填写 FIXMAP_PAGE_CLEAR (在 include/asm-generic/fixmap.h )
中使用到 __fix_to_virt (idx ) 将 idx 转换为 v addr 。
结合 fixed_addresses 类型定义,得到 FIX_HOLE 这个enum 值 为 0,返回 FIXADDR_TOP 。 fixmap 可以访问的最大 VA地址。
FIX_FDT_END enum 值为1,转换得到的 VA地址 值 为 fixaddr_top -1* 2 ^ page_shift。
FIX_FDT ,enum 值若为 4, 则转换得到的 VA地址值 为 FIXADDR_TOP - 4* 2^PAGE_SHIFT
1243/* 1244 * Unusually, this is also called in IRQ context (ghes_iounmap_irq) so if we 1245 * ever need to use IPIs for TLB broadcasting, then we're in trouble here. 1246 */ 1247void __set_fixmap(enum fixed_addresses idx, 1248 phys_addr_t phys, pgprot_t flags) 1249{ 1250 unsigned long addr = __fix_to_virt(idx); 1251 pte_t *ptep; 1252 1253 BUG_ON(idx <= FIX_HOLE || idx >= __end_of_fixed_addresses); 1254 1255 ptep = fixmap_pte(addr); 1256 1257 if (pgprot_val(flags)) { 1258 set_pte(ptep, pfn_pte(phys >> PAGE_SHIFT, flags)); 1259 } else { 1260 pte_clear(&init_mm, addr, ptep); 1261 flush_tlb_kernel_range(addr, addr+PAGE_SIZE); 1262 } 1263}
1255 行,直接 取 VA 地址,对应的 PTE 描述项,是因为 一张 PTE 表里面,有 512个 项,__end_of_fixed_addresses 这个enum 值,不会大于 512,所以不需要修改 bm_pud 、bm_pmd 表。
1179static inline pte_t * fixmap_pte(unsigned long addr) 1180{ 1181 return &bm_pte[pte_index(addr)]; 1182} 1183
fixmap 初始化
arch/arm64/mm/mmu.c
bm_pte / bm_pmd / bm_pud 数组定义
这样定义的数组在Image 文件的 .bss 段
58 59static pte_t bm_pte[PTRS_PER_PTE] __page_aligned_bss; 60static pmd_t bm_pmd[PTRS_PER_PMD] __page_aligned_bss __maybe_unused; 61static pud_t bm_pud[PTRS_PER_PUD] __page_aligned_bss __maybe_unused;
相同的 arch/arm64/mm/mmu.c 文件中,有出生后 这三个数组的函数。
1183 1184/* 1185 * The p*d_populate functions call virt_to_phys implicitly so they can't be used 1186 * directly on kernel symbols (bm_p*d). This function is called too early to use 1187 * lm_alias so __p*d_populate functions must be used to populate with the 1188 * physical address from __pa_symbol. 1189 */ 1190void __init early_fixmap_init(void) 1191{ 1192 pgd_t *pgdp; 1193 p4d_t *p4dp, p4d; 1194 pud_t *pudp; 1195 pmd_t *pmdp; 1196 unsigned long addr = FIXADDR_START; 1197 1198 pgdp = pgd_offset_k(addr); 1199 p4dp = p4d_offset(pgdp, addr); 1200 p4d = READ_ONCE(*p4dp); 1201 if (CONFIG_PGTABLE_LEVELS > 3 && 1202 !(p4d_none(p4d) || p4d_page_paddr(p4d) == __pa_symbol(bm_pud))) { 1203 /* 1204 * We only end up here if the kernel mapping and the fixmap 1205 * share the top level pgd entry, which should only happen on 1206 * 16k/4 levels configurations. 1207 */ 1208 BUG_ON(!IS_ENABLED(CONFIG_ARM64_16K_PAGES)); 1209 pudp = pud_offset_kimg(p4dp, addr); 1210 } else { 1211 if (p4d_none(p4d)) 1212 __p4d_populate(p4dp, __pa_symbol(bm_pud), PUD_TYPE_TABLE); 1213 pudp = fixmap_pud(addr); 1214 } 1215 if (pud_none(READ_ONCE(*pudp))) 1216 __pud_populate(pudp, __pa_symbol(bm_pmd), PMD_TYPE_TABLE); 1217 pmdp = fixmap_pmd(addr); 1218 __pmd_populate(pmdp, __pa_symbol(bm_pte), PMD_TYPE_TABLE); 1219 1241} 1242
1196 行,得到 FIXADDR_START 这个 VA 地址。
1198 行, pgd_offset_k (addr ) 得到 VA 地址的 pgd 描述项的地址。
1201 ~ 1209 , 的进入条件: page level 大于3级(那就是4级)&& pgd 描述项 不为空; 或者 page level 大于3级(那就是4级)&& pgd 描述项 中填写的 phy addr 不是 bm_pud 的物理地址。
说明,fixmap 这段VA 和 kimage 这段 VA ,使用同一个 pgd 描述项。
1208 行,对这种 情况进行了 补充检查,因为这种情况,只会发生在 16k/4 levels configurations. 这中配置下。所以,如果不是CONFIG_ARM64_16K_PAGES 就 BUG_ON 报错。
使用同一个 PGD 描述项,这个描述项已经 在 head.S create_page 里面填充过了。取这个 PGD 指向的 pud 表是 kimage pud 表。所以使用
1209 行,使用 pud_offset_kimage 函数得到 v addr 的 pud 表描述项 指针 。 pud pointer.
1211 ~ 1213 , fixmap 这段 VA 和 kernel image 这段 VA,使用的不是 同一个 PGD 描述项,
1211 1212 填写,pgd 描述项 的内容,让它指向 前面定义的 bm_pud 数组。
1213 从 bm_pud 数组中,取出 v addr 对应的 pud 表的描述项 的指针。 pud pointer .
1215 ~ 1216 填写 pud 描述项内容,让它指向 bm_pmd 数组。
1217 ~ 1218 填写 pmd 描述项 内容。让它指向 bm_pte 数组。
1219 ~ 1240 之间检查内容,省略。
pmd 描述符指向 bm_pte 数组。 这个数组里面,可以放 512 个 item。 fixmap 只需要若干个,所以后面 __set_fixmap 中,只修改 bm_pte 表里面内容,不需要修改其他表内容。