当前位置: 首页 > news >正文

Linux 内存管理 (3):fixmap

上一篇:Linux 内存管理 (2):memblock 子系统的建立

文章目录

  • 1. 前言
  • 2. 什么是 fixmap ?
  • 3. fixmap 实现
    • 3.1 fixmap 初始化
    • 3.2 示例
    • 3.3 内存子系统初始化后的 fixmap

1. 前言

限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。

2. 什么是 fixmap ?

fixmap 是 Linux 内核内存管理机制之一,Linux 在【内核虚拟地址空间】中预留一部分,用于临时映射物理页面

3. fixmap 实现

我们以 ARM32 架构下的 Linux 为例来说明 fixmap。Linux 通过硬编码的方式、在编译时确定了 fixmap 使用的虚拟地址区间:

/* arch/arm/include/asm/fixmap.h */#define FIXADDR_START		0xffc00000UL
#define FIXADDR_END		0xfff00000UL
#define FIXADDR_TOP		(FIXADDR_END - PAGE_SIZE)

更进一步的,按用途将 fixmap 虚拟地址区间划分为更多的子区间:

/* arch/arm/include/asm/fixmap.h */enum fixed_addresses {FIX_EARLYCON_MEM_BASE,__end_of_permanent_fixed_addresses,FIX_KMAP_BEGIN = __end_of_permanent_fixed_addresses,FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_TYPE_NR * NR_CPUS) - 1,/* Support writing RO kernel text via kprobes, jump labels, etc. */FIX_TEXT_POKE0,FIX_TEXT_POKE1,__end_of_fixmap_region,/** Share the kmap() region with early_ioremap(): this is guaranteed* not to clash since early_ioremap() is only available before* paging_init(), and kmap() only after.*//** paging_init() 前, 用于 early ioremap* paging_init() 后, 用于 kmap*/ 
#define NR_FIX_BTMAPS		32
#define FIX_BTMAPS_SLOTS	7
#define TOTAL_FIX_BTMAPS	(NR_FIX_BTMAPS * FIX_BTMAPS_SLOTS)FIX_BTMAP_END = __end_of_permanent_fixed_addresses, FIX_BTMAP_BEGIN = FIX_BTMAP_END + TOTAL_FIX_BTMAPS - 1, __end_of_early_ioremap_region
};

3.1 fixmap 初始化

start_kernel()setup_arch()early_fixmap_init() // fixmap 初始化early_ioremap_init() // early ioremap 使用 fixmap 进行映射相关的初始化

early_fixmap_init()fixmap 进行初始化:

/* arch/arm/mm/mmu.c *//** FIXMAP 初始化: * . 设定 FIXMAP 的 PTE 页表: PTE 尚未初始化, 即尚未分配物理*   内存;* . FIXMAP 函数指针 pte_offset_fixmap 设定: *   用于 [虚拟地址 ==> FIXMAP 的 PTE 页表项 虚拟地址] 的转换.*/
void __init early_fixmap_init(void)
{pmd_t *pmd;/** The early fixmap range spans multiple pmds, for which* we are not prepared:*/BUILD_BUG_ON((__fix_to_virt(__end_of_early_ioremap_region) >> PMD_SHIFT)!= FIXADDR_TOP >> PMD_SHIFT);/* 设定 FIXMAP (虚拟地址区间 [FIXADDR_TOP, FIXADDR_END)) PTE 页表为 bm_pte[] */pmd = fixmap_pmd(FIXADDR_TOP);pmd_populate_kernel(&init_mm, pmd, bm_pte);/* 设定 将 fixmap 虚拟地址转换为 PTE 页表项的接口 */pte_offset_fixmap = pte_offset_early_fixmap;
}

从上面的代码分析看到,fixmap 的 PTE 页表使用预编译到内核的 bm_pte[] 数组空间,其页表映射如下图(2 级分页示例):

image

3.2 示例

以 early console 为例,说明下 fixmap 的使用。

/* drivers/tty/serial/earlycon.c */static int __init register_earlycon(char *buf, const struct earlycon_id *match)
{...if (port->mapbase)port->membase = earlycon_map(port->mapbase, 64);...
}static void __iomem * __init earlycon_map(resource_size_t paddr, size_t size)
{void __iomem *base;
#ifdef CONFIG_FIX_EARLYCON_MEM/* 设置 fixmap FIX_EARLYCON_MEM_BASE 区间的对应的物理页面地址为 @paddr */set_fixmap_io(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK);/* 获取 fixmap FIX_EARLYCON_MEM_BASE 区间的对应页面的虚拟地址 */base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE);base += paddr & ~PAGE_MASK;
#else...
#endif...return base;
}

上面的关键是 set_fixmap_io()__fix_to_virt() 调用:

set_fixmap_io(): 设置 fixmap 区间的物理地址
__fix_to_virt(): 获取 fixmap 区间的虚拟地址

先看 set_fixmap_io() 的实现细节:

/* include/asm-generic/fixmap.h *//** Some fixmaps are for IO*/
#define set_fixmap_io(idx, phys) \__set_fixmap(idx, phys, FIXMAP_PAGE_IO)
/* arch/arm/mm/mmu.c *//** To avoid TLB flush broadcasts, this uses local_flush_tlb_kernel_range().* As a result, this can only be called with preemption disabled, as under* stop_machine().*/
/** 设定 物理地址 @phys 的 PTE 页表项,* 将物理地址到 @phys 映射到 @idx 指向的 FIXMAP 虚拟地址. */
void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot)
{unsigned long vaddr = __fix_to_virt(idx);pte_t *pte = pte_offset_fixmap(pmd_off_k(vaddr), vaddr); /* 获取虚拟地址 @vaddr 的 PTE 页表项 *//* Make sure fixmap region does not exceed available allocation. */BUILD_BUG_ON(FIXADDR_START + (__end_of_fixed_addresses * PAGE_SIZE) >FIXADDR_END);BUG_ON(idx >= __end_of_fixed_addresses);/* we only support device mappings until pgprot_kernel has been set */if (WARN_ON(pgprot_val(prot) != pgprot_val(FIXMAP_PAGE_IO) &&pgprot_val(pgprot_kernel) == 0))return;if (pgprot_val(prot))set_pte_at(NULL, vaddr, pte,pfn_pte(phys >> PAGE_SHIFT, prot));elsepte_clear(NULL, vaddr, pte);local_flush_tlb_kernel_range(vaddr, vaddr + PAGE_SIZE);
}

其中 pte_offset_fixmap 用于返回虚拟地址的 PTE 页表项,此时 pte_offset_fixmap 指向 pte_offset_early_fixmap()

static pte_t * __init pte_offset_early_fixmap(pmd_t *dir, unsigned long addr)
{return &bm_pte[pte_index(addr)];
}

再看 __fix_to_virt() 的实现细节:

/* include/asm-generic/fixmap.h */#define __fix_to_virt(x)	(FIXADDR_TOP - ((x) << PAGE_SHIFT))

3.3 内存子系统初始化后的 fixmap

fixmap 在 Linux 的不同运行阶段,会稍有不同。具体是以初始化内存子系统的 paging_init() 调用为分界,我们将 fixmap 划分为 内存子系统初始化前的 fixmap内存子系统初始化后的 fixmap,它们之间的差别在于使用了不同 PTE 页表映射 fixmap 虚拟地址区间

paging_init() 初始化内存子系统期间,重新为 fixmap 分配 PTE 页表,而不再使用 bm_pte[] 页表:

start_kernel()setup_arch()//early_fixmap_init()paging_init()
/* arch/arm/mm/mmu.c */void __init paging_init(const struct machine_desc *mdesc)
{...early_fixmap_shutdown();...
}static void __init early_fixmap_shutdown(void)
{int i;unsigned long va = fix_to_virt(__end_of_permanent_fixed_addresses - 1);pte_offset_fixmap = pte_offset_late_fixmap; /* 重新设定 FIXMAP 虚拟地址 @addr 的 PTE 页表项查找接口 */pmd_clear(fixmap_pmd(va)); /* 所有的 FIXMAP 映射只占据一个 PMD 页表项 */local_flush_tlb_kernel_page(va);/* * 对于在 early boot 阶段 已经建立 页表映射 的 FIXMAP 恒久映射, * 不再使用在 early boot 阶段 PTE 页表 bm_pte[] 进行映射, 而是* 重新 create_mapping() 重新建立它们的映射。新的映射中, 虚拟地址 * 到 物理地址 的 映射关系维持不变, 变换的是:* a. PTE 页表不再使用 bm_pte[], 而是使用重新动态分配 PTE 页表;* b. 映射的内存类型设定为 MT_DEVICE 类型。*/for (i = 0; i < __end_of_permanent_fixed_addresses; i++) {pte_t *pte;struct map_desc map;map.virtual = fix_to_virt(i);pte = pte_offset_early_fixmap(pmd_off_k(map.virtual), map.virtual);/* Only i/o device mappings are supported ATM */if (pte_none(*pte) ||(pte_val(*pte) & L_PTE_MT_MASK) != L_PTE_MT_DEV_SHARED)continue;map.pfn = pte_pfn(*pte);map.type = MT_DEVICE;map.length = PAGE_SIZE;create_mapping(&map);}
}

从上面可以看到,主要做了两点工作:

1. 重新设定 pte_offset_fixmap 为 pte_offset_late_fixmap()
2. 重新为 fixmap 分配了 PTE 页表,并在新 PTE 页表中维持了 fixmap 恒久映射区已经建立的映射

为什么要替换 PTE 页表,一方面因为 bm_pte[] 页表空间为 initdata,在内存子系统建立后会被释放掉;另一方面也是要维护统一的页表管理。

static pte_t bm_pte[PTRS_PER_PTE + PTE_HWTABLE_PTRS]__aligned(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE) __initdata;

那么,pte_offset_fixmap 指向 pte_offset_late_fixmap() 后,不再从 bm_pte[] 返回 PTE 页表项,而是从 early_fixmap_shutdown() 中新分配的 PTE 页表返回 PTE 页表项了:

static pte_t *pte_offset_late_fixmap(pmd_t *dir, unsigned long addr)
{return pte_offset_kernel(dir, addr);
}
http://www.dtcms.com/a/599021.html

相关文章:

  • 一个视频多平台发布天津网站seo策划
  • 数据管理战略|3数据管理成功的预期衡量标准|螺旋上升
  • 零碳园区的路径选择与方法论:从规划到落地的全链路实践
  • 河间做网站的电话东莞东城社保局电话
  • 晶粒 和晶体、晶格
  • 声网AI技术赋能,智能客服告别机械式应答
  • 国外网站做推广施工企业安全生产评价汇总表最终须由( )签名。
  • 潍坊建设公司网站徐州网站设计制作建设
  • 跨系统流程如何打通?选 BPM 平台认准这三点
  • 三明商城网站开发设计太仓建设网站
  • 电子销售网站报表模块如何做济南润尔网站建设技术公司
  • 域通联达网站网站建设怎样推广
  • wordpress全站启用ssl佛山网络优化推广公司
  • mips简单栈溢出
  • 青岛正规品牌网站制作策划wordpress foxpay
  • 傻瓜式网页制作网站设计制作费的税收编码
  • C语言 数组
  • 东阳实惠营销型网站建设厂家上海网站设计与开发公司
  • 公司网站制作导航西安百度seo
  • C 语言文件读写初探:打开数据之门 [特殊字符]
  • AI Agent 从入门到精通:概念、架构与实战应用
  • 官方网站优化价格免费网站模板 html
  • 免费建网站哪个好陕西网络推广网站
  • PHP中对于(并发/并行)相关概念
  • 江西网站做的好的企业移动端网站没有icp
  • 建设工程报建备案网站dedecms源码下载
  • 金富通青岛建设工程有限公司网站网站开发工程师月薪平均
  • Linux DNS域名解析服务器练习
  • 网站开发可选择方案有哪些网站开发网页设计js
  • Linux:内核地址随机化(Kaslr)