现在想让某段内存"既不被分配,也不被线性映射",在 Linux 4.4 上应使用 memblock_remove()
| 对比维度 | memblock_reserve |
memblock_remove |
|---|---|---|
对 memblock.memory 的影响 |
保留在 memory 列表中 | 从 memory 列表中彻底删除 |
| 是否被线性映射 | 是,仍会建立 direct mapping | 否,不进入内核线性映射 |
| 后续访问方式 | 可用 phys_to_virt() |
必须用 ioremap() |
| 适用场景 | 需要保留但不被分配的内存(如内核镜像) | 不需要内核直接访问的内存(如固件区域) |
第一步:启动早期从 memblock 中移除(放在 setup_arch 或早期 initcall)
#include <linux/memblock.h>
#define MY_MEM_BASE 0x8600000
#define MY_MEM_SIZE 0x1000
static int __init my_board_mem_setup(void)
{
/* 从 memblock.memory 中彻底移除该区域 */
memblock_remove(MY_MEM_BASE, MY_MEM_SIZE);
pr_info("my_board: removed phys [0x%lx-0x%lx] from kernel memory\n",
(unsigned long)MY_MEM_BASE,
(unsigned long)(MY_MEM_BASE + MY_MEM_SIZE - 1));
return 0;
}
early_initcall(my_board_mem_setup);
第二步:在你的驱动中用 ioremap 映射并使用
#include <linux/io.h>
#include <linux/module.h>
#define MY_MEM_BASE 0x8600000
#define MY_MEM_SIZE 0x1000
static void __iomem *my_mem;
static int __init my_driver_init(void)
{
/* 动态创建映射,因为线性映射已不存在 */
my_mem = ioremap(MY_MEM_BASE, MY_MEM_SIZE);
if (!my_mem) {
pr_err("my_driver: ioremap failed\n");
return -ENOMEM;
}
/* 现在可以安全读写这段内存 */
writel(0xdeadbeef, my_mem);
pr_info("my_driver: read back: 0x%08x\n", readl(my_mem));
return 0;
}
static void __exit my_driver_exit(void)
{
iounmap(my_mem);
}
module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_LICENSE("GPL");
第一阶段:memblock 管理(boot-time)
此时还没有页表,也没有 struct page 数组。内核通过 memblock.memory 知道"哪些物理内存可用",通过 memblock.reserved 知道"哪些已被占用"。memblock_remove 的作用对象就是 memblock.memory------把它从"可用内存"集合中删掉。
第二阶段:paging_init 建立线性映射
paging_init 遍历 memblock.memory 中的区域,为每一个区域创建页表项,建立内核线性映射(direct mapping)。只有还在 memblock.memory 里的区域才会被映射。 被 memblock_remove 移除的区域,页表创建时根本不会看到它。
时序要求:
early_initcall(my_board_reserve_mem) ← 必须在这里完成 remove ↓ paging_init() ← 遍历 memblock.memory 建立页表 ↓ 内存分配器初始化 ← 基于 memblock.memory 构建可用内存池
如果在 paging_init 之后再调用 memblock_remove,页表已经建好,线性映射已经存在,再移除 memblock.memory 也无法撤销已建立的映射。那段物理内存仍然可以通过 phys_to_virt() 被访问,失去了"不被线性映射"的意义。
MIPS 架构的 boot_memtype 枚举定义在 arch/mips/include/asm/bootinfo.h 中,主要包含以下几类:
| 枚举值 | 含义 | 说明 |
|---|---|---|
BOOT_MEM_RAM |
可用 RAM | 内核可以分配和使用的普通内存,是最常见的类型 |
BOOT_MEM_ROM_DATA |
ROM 数据区域 | 存放只读固件数据的区域,内核通常只读访问 |
BOOT_MEM_RESERVED |
保留内存 | 已被占用或专门保留、内核不可使用的区域 |
BOOT_MEM_NOMAP |
保留但不映射 | 保留内存且不建立线性映射(较新内核版本支持) |
BOOT_MEM_NOMAP 是在 BOOT_MEM_RESERVED 基础上进一步细分的类型,与之前讨论的 memblock_mark_nomap() 语义对应,表示"内核不仅不能分配,也不应建立线性映射"。
add_memory_region函数
void __init add_memory_region(phys_addr_t start, phys_addr_t size, long type);
/* arch/mips/your_board/setup.c */
void __init prom_init_memory(void)
{
/* 注册 0x10000000 开始的 256MB 为可用 RAM */
add_memory_region(0x10000000, 0x10000000, BOOT_MEM_RAM);
/* 注册 PCI MMIO 区域为保留 */
add_memory_region(LOONGSON_PCI_MEM_START,
LOONGSON_PCI_MEM_END - LOONGSON_PCI_MEM_START,
BOOT_MEM_RESERVED);
}
5.10 内核可以使用memblock_mark_nomap/memblock_reserve 函数替换 ,并且在设备树上面加上no-map。