arm64_memblock_init

本文详细解析ARM64架构下memblock_init函数的工作原理,重点介绍如何初始化内存区块,包括移除no-map区域、预留关键内存区,如kernel、ramdisk及设备树节点,同时解析dts文件中的memreserve和reserved-memory属性。

本文来分析arm64_memblock_init函数的代码
这个函数的主要作用是初始化memblock。关于memory的区间范围,已经在setup_machine_fdt函数中添加进来。这里主要是remove掉no-map的区域,并reserve一些关键区域:如kernel镜像、dtb块、ramdisk镜像和device tree中的一些需要reserve的节点。
查看代码发现,有对ramdisk和kernel进行reserve,没有dtb,因为在setup_machine_fdt中已经完成了。
除此之外,就是要从dts中解析memreserve and reserved-memory,进行memblock reserve和memblock remove(no-map的不归kernel管)
还申请了一个公共的CMA区域:16MB
之后memblock就算初始化完成,可以进行resize了

void __init arm64_memblock_init(void)
{
	// RAM的起始物理地址,1GB对齐
	// For [0x10000000, 0xffffffff], memstart_addr = 0x0
	// For [0x40000000, 0xffffffff], memstart_addr = 0x40000000
	// For [0x80000000, 0xffffffff], memstart_addr = 0x80000000
	memstart_addr = round_down(memblock_start_of_DRAM(),
				   ARM64_MEMSTART_ALIGN);

	// reserve [initrd_start, initrd_end)
	if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && initrd_start) {
		u64 base = initrd_start & PAGE_MASK;
		u64 size = PAGE_ALIGN(initrd_end) - base;
		memblock_remove(base, size); /* clear MEMBLO
<think>好的,我现在需要帮助用户解决`of_reserved_mem_device_init`失败的问题,并且这个问题可能与`dma_alloc_coherent`分配指定区域空间有关。首先,我得回忆一下之前提供的解决方案,并结合用户现在提供的四个引用来分析可能的原因和解决方法。 用户提到`of_reserved_mem_device_init`失败,这可能涉及设备树配置、驱动代码中的初始化步骤,或者预留内存区域的问题。根据引用[2],设备树中的CMA配置示例显示需要正确设置`compatible`为`shared-dma-pool`,并且分配范围、大小等参数要正确。用户可能在设备树中没有正确配置预留内存,导致初始化失败。例如,`reg`属性的地址和大小是否正确?是否有冲突的内存区域? 另外,引用[1]提到通过调整`CONSISTENT_DMA_SIZE`来解决分配失败的问题。如果预留的内存区域大小超过了内核默认的DMA区域限制,可能需要调整这个参数。比如,用户可能在设备树中预留了较大的内存,但内核配置的DMA区域较小,导致`of_reserved_mem_device_init`无法正确初始化。 引用[4]提到在Kernel 4.9上`dma_alloc_coherent`失败,可能需要检查CMA的配置或内核版本差异。用户是否在较新的内核版本上遇到了问题?新版本的内核可能对DMA内存管理有不同要求,例如CMA的默认设置或设备树绑定方式的变化。 引用[3]澄清了关于DMA ZONE和`dma_alloc_coherent`的误解,指出该函数分配的内存是否物理连续取决于架构和配置。如果用户试图在非连续的区域分配,可能导致失败。需要确认预留的内存区域是否物理连续,以及驱动是否正确设置了DMA掩码和操作。 接下来,我需要结合这些信息,逐步排查可能导致`of_reserved_mem_device_init`失败的原因: 1. **设备树配置错误**:检查预留内存的节点是否正确,包括地址、大小、兼容性属性等。是否有拼写错误或格式问题?例如,`compatible`应为`shared-dma-pool`,`reg`属性是否正确指定了起始地址和大小? 2. **内存区域冲突或不可用**:预留的内存是否已被其他系统部分占用?比如,是否与内核或其他驱动程序的内存区域重叠?可以使用`dmesg`日志查看是否有冲突提示。 3. **内核配置限制**:检查`CONFIG_DMA_CMA`是否启用,以及CMA区域的大小是否足够。如果预留的内存超过CMA默认大小,可能需要通过内核启动参数调整,如`cma=256M`。 4. **驱动代码问题**:确认驱动中调用`of_reserved_mem_device_init`的位置和参数是否正确。是否在设备探测的早期阶段调用?是否检查了返回值并处理了错误? 5. **DMA掩码设置**:设备是否设置了正确的DMA掩码?如果设备只能访问特定地址范围,需通过`dma_set_coherent_mask`设置,否则即使预留了内存,也可能无法使用。 6. **内核版本差异**:用户是否在较新的内核版本上遇到问题?比如,从3.14升级到4.9,可能需要适配新的API或设备树绑定方式。引用[4]中提到的问题可能与内核版本变化有关,需检查API变更或CMA管理的调整。 7. **物理内存连续性**:虽然`dma_alloc_coherent`通常要求物理连续,但某些架构可能支持非连续内存。需要确认预留区域是否真的连续,是否存在内存碎片问题。 8. **调试工具使用**:如引用[1]提到的,使用ftrace和printk跟踪`dma_alloc_coherent`的调用,查看分配失败的具体原因。同时,检查系统日志`dmesg`是否有相关错误信息,如内存不足或地址越界。 综合这些可能性,用户需要逐步验证设备树配置、内核配置、驱动代码的正确性,并利用调试工具定位具体问题。例如,首先检查设备树是否有语法错误,确认预留内存区域是否被正确识别;然后确保驱动正确初始化和绑定预留内存;接着检查DMA掩码和内核配置是否支持所需的内存区域;最后,通过日志和跟踪工具分析失败的具体原因,进行针对性调整。</think>### **of_reserved_mem_device_init失败与DMA指定区域分配问题的解决方案** `of_reserved_mem_device_init`失败通常与**设备树配置错误**或**预留内存管理机制不兼容**相关,同时可能涉及`dma_alloc_coherent`对指定物理地址区域的分配限制。以下是系统性排查与解决方案: --- #### **一、设备树配置验证** ##### 1. **检查预留内存节点语法** - **关键属性**: - `compatible`必须为`"shared-dma-pool"`(引用[2]) - `reg`需明确指定物理地址和大小(例如`<0x80000000 0x10000000>`表示从0x80000000开始的256MB) - `no-map`或`reusable`属性需根据用途选择(若需长期保留则用`no-map`) - **常见错误示例**: ```dts // 错误:缺少compatible或reg格式错误 reserved-memory { my_region@80000000 { reg = <0x80000000>; // 缺少大小参数 }; }; ``` ##### 2. **验证内存区域可用性** - **步骤**: 1. 在内核启动参数中添加`memmap=256M$0x80000000`,显式保留该区域(若未通过设备树生效) 2. 使用`cat /proc/iomem`检查预留区域是否被标记为`reserved`或`CMA` 3. 确保地址范围不与内核或其他外设(如GPU、PCIe BAR)冲突 --- #### **二、驱动代码适配** ##### 1. **正确调用初始化函数** - **调用时机**:在`probe`函数中尽早调用`of_reserved_mem_device_init`,确保在DMA操作前完成绑定: ```c static int my_driver_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; // 步骤:绑定预留内存 if (of_reserved_mem_device_init(dev)) { dev_err(dev, "Failed to bind reserved memory\n"); return -ENODEV; } // 后续DMA分配操作 void *vaddr = dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL); // ... } ``` ##### 2. **处理DMA掩码限制** - **强制DMA地址范围**:若设备仅支持特定物理地址(如32位设备需低4GB),需在驱动中设置掩码: ```c if (dma_set_coherent_mask(dev, DMA_BIT_MASK(32))) { dev_err(dev, "DMA mask not supported\n"); return -EIO; } ``` 未正确设置掩码会导致`dma_alloc_coherent`无法从预留区域分配内存(引用[3])。 --- #### **三、内核配置与调试** ##### 1. **CMA配置调整** - **扩大CMA区域**:若预留内存区域属于CMA池,需确保内核配置足够: - 修改内核启动参数:`cma=512M` - 检查`/proc/meminfo`中的`CmaTotal`值是否匹配 - **内核编译选项**: - 启用`CONFIG_DMA_CMA`和`CONFIG_CMA` - 若使用设备树预留,确认`CONFIG_OF_RESERVED_MEM`已启用 ##### 2. **调试工具使用** - **追踪分配失败原因**: - 使用`dmesg`查看内核日志,搜索`reserved mem`或`CMA`相关错误 - 通过ftrace跟踪`of_reserved_mem_device_init`调用链(引用[1]): ```bash echo function > /sys/kernel/tracing/current_tracer echo of_reserved_mem_device_init > /sys/kernel/tracing/set_ftrace_filter cat /sys/kernel/tracing/trace ``` ##### 3. **物理连续性验证** - **检查内存连续性**: 若预留区域存在物理碎片,可能导致`dma_alloc_coherent`失败。通过`memblock=debug`内核参数输出内存布局: ```bash dmesg | grep memblock ``` --- #### **四、典型错误场景与修复** | 错误现象 | 可能原因 | 解决方案 | |-----------------------------------|-----------------------------------|--------------------------------------------------------------------------| | `of_reserved_mem_device_init`返回`-ENODEV` | 设备树节点未正确关联到设备 | 在设备树中添加`memory-region = <&my_dma_reserved>`到设备节点 | | 分配时返回`NULL`且无内核报错 | DMA掩码未覆盖预留区域地址 | 调用`dma_set_coherent_mask`设置掩码 | | 内核日志提示`CMA allocation failed` | CMA区域不足 | 调整内核启动参数`cma=size`或重新编译内核增大`CONFIG_CMA_SIZE_MBYTES` | | 设备树解析失败 | 地址格式错误或范围越界 | 使用`dtc`编译工具检查设备树语法:`dtc -I dts -O dtb -o my.dtb my.dts` | --- #### **五、代码修正示例** **设备树修正**: ```dts // 正确配置示例 reserved-memory { #address-cells = <1>; #size-cells = <1>; ranges; my_dma_reserved: dma_region@80000000 { compatible = "shared-dma-pool"; reg = <0x80000000 0x10000000>; // 256MB no-map; }; }; // 在设备节点中关联预留区域 my_device { compatible = "vendor,my-device"; memory-region = <&my_dma_reserved>; }; ``` **驱动代码修正**: ```c // 补充DMA掩码设置 static int my_driver_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; // 绑定预留内存 if (of_reserved_mem_device_init(dev)) { dev_err(dev, "Reserved memory binding failed\n"); return -ENODEV; } // 设置DMA掩码 if (dma_set_coherent_mask(dev, DMA_BIT_MASK(32))) { dev_err(dev, "Failed to set DMA mask\n"); of_reserved_mem_device_release(dev); // 释放预留内存绑定 return -EINVAL; } // 分配DMA内存 void *vaddr = dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL); if (!vaddr) { dev_err(dev, "DMA allocation failed\n"); of_reserved_mem_device_release(dev); return -ENOMEM; } return 0; } ``` --- ### **相关问题** 1. **如何调试设备树中的内存预留冲突?** 使用`dtc`编译检查语法,结合`/proc/iomem`和内核启动参数`memmap`验证实际内存布局[^2]。 2. **`dma_alloc_coherent`在ARM与x86架构下的行为差异?** ARM架构依赖CMA且默认DMA区域较小,x86通常无物理连续限制但需注意DMA掩码[^3]。 3. **预留内存是否必须通过设备树配置?** 否,可通过内核启动参数`memmap`预留,但设备树更便于动态绑定[^2][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值