memblock初始化过程中fdt mem处理

memblock初始化过程中fdt mem处理

导读

本部分将memblock 初始化时FDT中reserved memory的扫描处理细化整理,调用情况如下:在这里插入图片描述

code目录:

涉及到的目录 简单描述
./kernel-4.9/include/linux/of_reserved_mem.h reserved_mem 结构相关定义位置
./kernel-4.9/include/linux/of.h _OF_DECLARE宏定义位置
./kernel-4.9/drivers/of/fdt.c fdt 接口实现位置
./kernel-4.9/scripts/dtc/libfdt/fdt_ro.c fdt_get_mem_rsv实现位置
./kernel-4.9/drivers/of/of_reserved_mem.c fdt和reserved mem相关函数实现位置

1. 调用入口

这个函数是扫描reserve节点,添加到memblock管理中

/**
 * early_init_fdt_scan_reserved_mem() - create reserved memory regions
 *
 * This function grabs memory from early allocator for device exclusive use
 * defined in device tree structures. It should be called by arch specific code
 * once the early allocator (i.e. memblock) has been fully activated.
 */
void __init early_init_fdt_scan_reserved_mem(void)
{
   
   
	int n;
	u64 base, size;

	if (!initial_boot_params) return;//是否已经可以访问到FDT

	/* Process header /memreserve/ fields */
	for (n = 0; ; n++) {
   
   
	//在dts中有些vendor会定义memreserve,地址和size,会在这里读取并配置为reserved,当前平台没有配置这个
	//像这样:/memreserve/ 0x80000000 0x00010000;
		fdt_get_mem_rsv(initial_boot_params, n, &base, &size);//读取地址
		if (!size) break;
		early_init_dt_reserve_memory_arch(base, size, 0);//针对这些地址,如果是nomap则从memblock中删除,否则标记为memblock reserve
	}

	of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);//主要找到的是这个:扫描所有节点,并对每个节点调用__fdt_scan_reserved_mem
	fdt_init_reserved_mem();
}

处理的入口函数,做三件事:

  1. 对memreserve fields的地址添加处理:标记nomap的从memblock中删除,其他的添加到memblock.reserve中;
  2. 扫描设备树中的所有节点,找到reserved-memory节点及其子节点,解析其地址范围;
  3. 对于找到的需要reserved的地址空间,添加处理

这个函数是在扫描所有FDT的设备节点,并针对于每一个节点调用回调;

1.1 memreserve部分处理

从FDT中获取到base和size:fdt_get_mem_rsv(initial_boot_params, n, &base, &size)

int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
{
   
   
	FDT_CHECK_HEADER(fdt);
	*address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);//将address写入到传入的地址中
	*size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);//将size写入到传入的地址中
	return 0;
}

int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size, bool nomap)
{
   
   
	if (nomap) return memblock_remove(base, size);//标记有nomap则将其从memblock管理中删除;
	return memblock_reserve(base, size);//没有标记nomap,则添加到reserve region中
}

这里处理很简单,不做过多分析;

1.2 FDT扫描

本部分来看具体扫描FDT节点过程;

核心函数是:of_scan_flat_dt(__fdt_scan_reserved_mem, NULL),先上code:

/**
 * of_scan_flat_dt - scan flattened tree blob and call callback on each.
 * @it: callback function
 * @data: context data pointer
 *
 * This function is used to scan the flattened device-tree, it is used to extract the memory information at boot before we can unflatten the tree
 */
int __init of_scan_flat_dt(int (*it)(unsigned long node, const char *uname, int depth, void *data), void *data)
{
   
   
	const
<think>我们正在讨论设备树中如何配置和描述用于生成 `/dev/mem`的节点。首先,需要明确的是:**`/dev/mem`设备节点并不是由设备树中的某个特定节点直接生成的**。相反,`/dev/mem`是 Linux内核默认提供的一个字符设备,用于访问整个物理内存空间。它的生成与设备树中的`memory`节点有关,但并不是直接由某个设备树节点创建。 下面我将详细解释:###1.`/dev/mem`的生成机制-`/dev/mem`设备节点是由内核在初始化时自动创建的,其主设备号为1,次设备号为1。-它的功能是提供对**整个物理内存**的访问(从物理地址0开始,到实际内存结束)。-因此,它的存在不依赖于设备树中的某个特定节点,而是依赖于内核配置(`CONFIG_DEVMEM`或`CONFIG_DEVKMEM`选项)。###2.设备树中的`memory`节点虽然 `/dev/mem`不是由设备树节点直接生成,但设备树中的 `memory`节点定义了系统物理内存的布局,这间接影响了 `/dev/mem`可访问的范围。设备树中必须包含至少一个 `memory`节点来描述系统的物理内存。例如: ```dts/{#address-cells =<1>;#size-cells =<1>;memory@80000000{device_type= "memory";reg= <0x800000000x40000000>; //内存起始地址0x80000000,大小1GB};}; ```- `device_type`属性必须是`"memory"`。- `reg`属性指定了内存的起始地址和大小[^3]。 ###3. `/dev/mem`与`memory`节点的关系-内核在启动时会解析设备树中的`memory`节点,从而知道系统中有哪些物理内存区域。 -`/dev/mem`允许访问这些内存区域(以及内存映射的I/O区域),但需要注意的是:-访问未在`memory`节点中描述的内存区域(例如空洞或保留区域)可能导致错误。-访问外设寄存器(如PL的 IP核)时,这些地址通常不在 `memory`节点中,而是作为外设地址空间存在,但 `/dev/mem`仍然可以访问它们(只要地址在物理地址空间内且未被保留)。 ###4.设备树中与外设寄存器访问相关的节点虽然`/dev/mem`本身不需要在设备树中配置,但如果你想通过 `/dev/mem`访问PL(或其他外设)的寄存器,这些外设的地址信息通常会在设备树中描述(尽管这不是必须的,因为你可以直接使用物理地址)。例如,一个 GPIO控制器节点:```dtsaxi_gpio_0:gpio@41200000{compatible ="xlnx,xps-gpio-1.00.a";reg =<0x412000000x10000>;//物理地址范围//...其他属性... };``` -这个节点描述了外设寄存器的地址范围(`0x41200000`到 `0x4120FFFF`)。 -当你使用`/dev/mem`访问这个地址范围时,实际上是在操作这个GPIO控制器。 ###5.总结:设备树中如何“支持”`/dev/mem` -**不需要专门为`/dev/mem`编写节点**,因为它是内核自动创建的。-但是,设备树中的以下内容会影响`/dev/mem`的行为:1.`memory`节点:定义了可用的物理内存范围。2.外设节点(如 `gpio@41200000`):描述了外设寄存器的地址,这些地址可以通过 `/dev/mem`访问(即使它们不属于`memory`节点描述的内存区域)。 ###6.注意事项-使用 `/dev/mem`需要root权限。-生产环境中不建议使用 `/dev/mem`,因为它不安全(可能破坏系统)。替代方案包括:-使用内核驱动(通过 `ioremap`映射外设寄存器)。-使用`UIO`(用户空间 I/O)框架。###相关问题1.设备树中`memory`节点的作用是什么?2.如何通过设备树描述一个外设的寄存器地址范围?3.为什么在生产环境中不推荐使用`/dev/mem`?4.除了 `/dev/mem`,还有哪些方法可以在用户空间访问物理内存?5.设备树中的 `reserved-memory`节点对`/dev/mem`的访问有什么影响?---希望这个解释能帮助你理解设备树与`/dev/mem`的关系!</think>### 设备树中配置 `/dev/mem` 的机制解析 在 Linux 设备树中,**`/dev/mem` 设备节点不是由特定设备树节点直接生成的**,而是由内核根据设备树中的内存描述自动创建的字符设备。以下是关键配置要点: --- #### 一、核心机制:`memory` 节点 1. **必备的 `memory` 节点** 所有设备树必须包含 `memory` 节点,它定义物理内存布局: ```dts / { #address-cells = <1>; // 地址用1个32位数表示 #size-cells = <1>; // 大小用1个32位数表示 memory@80000000 { device_type = "memory"; // 固定设备类型 reg = <0x80000000 0x40000000>; // 起始地址0x80000000,大小1GB }; }; ``` - `device_type` 必须为 `"memory"`[^3] - `reg` 属性定义内存的**物理起始地址和长度**(单位字节)[^3] 2. **多区域内存配置** 对于非连续内存系统(如某些嵌入式设备): ```dts memory@0 { device_type = "memory"; reg = <0x00000000 0x20000000>; // 第一块512MB }; memory@20000000 { device_type = "memory"; reg = <0x20000000 0x20000000>; // 第二块512MB }; ``` --- #### 二、`/dev/mem` 的生成流程 1. **内核初始化过程** - 解析设备树时,内核收集所有 `device_type = "memory"` 的节点 - 根据 `reg` 属性注册物理内存区域到 `memblock` 子系统 - 启用 `CONFIG_DEVMEM` 配置后,自动创建 `/dev/mem` 设备节点 2. **用户空间访问示例** ```c int fd = open("/dev/mem", O_RDWR | O_SYNC); void *vaddr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, phys_addr); ``` - `phys_addr` 需在 `memory` 节点定义的 `reg` 范围内[^1][^3] --- #### 三、关键验证方法 1. **查看内存映射** ```bash # 确认内核识别的内存区域 cat /proc/iomem | grep -i "System RAM" # 输出示例:80000000-bfffffff : System RAM ``` 2. **检查设备树编译** ```bash # 查看dtb中的memory节点定义 fdtdump /boot/board.dtb | grep memory -A 3 ``` --- #### 四、特殊场景:保留内存区域 若需限制 `/dev/mem` 访问某些区域,使用 `reserved-memory` 节点: ```dts /reserved-memory { #address-cells = <1>; #size-cells = <1>; ranges; secure_reserved: secure@90000000 { reg = <0x90000000 0x1000000>; // 保留16MB区域 no-map; // 禁止内核映射此区域 }; }; ``` - `no-map` 属性标记的区域**无法通过 `/dev/mem` 访问**[^3] --- ### 总结:设备树与 `/dev/mem` 的关系 | **组件** | **作用** | **设备树关联** | |-------------------|------------------------------------------|-----------------------------------| | `memory` 节点 | 定义物理内存范围 | 必需,提供 `/dev/mem` 的可访问区域 | | 内核配置 | 启用 `CONFIG_DEVMEM` | 非设备树相关,需在内核编译时配置 | | `reserved-memory` | 限制对特定内存区域的访问 | 可选,增强安全性 | | 外设寄存器 | 通过 `/dev/mem` 访问(如 Zynq PL 寄存器)| 依赖 `reg` 属性定义的物理地址[^4] | > **注意**:`/dev/mem` 本身不对应具体设备树节点,其访问能力由 `memory` 节点定义的物理内存范围和内核配置共同决定。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值