arm64_memblock_init

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

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文来分析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 MEMBLOCK_ flags */
		memblock_add(base, size);
		memblock_reserve(base, size);
		}
	}

	// reserve [_text, _end)
	memblock_reserve(__pa_symbol(_text), _end - _text);

	// handle memreserve and reserved-memory in dts
	early_init_fdt_scan_reserved_mem();

	// for current project, it is 4GB
	arm64_dma_phys_limit = max_zone_dma_phys();
	high_memory = __va(memblock_end_of_DRAM() - 1) + 1;

	// Default contiguous memory area size:
	// CONFIG_CMA_SIZE_MBYTES=16
	// CONFIG_CMA_SIZE_SEL_MBYTES=y
	// [    0.000000] cma: Reserved 16 MiB at 0x00000000f2400000
	// It is stored at struct cma *dma_contiguous_default_area
	dma_contiguous_reserve(arm64_dma_phys_limit);

	memblock_allow_resize();
}
<think>嗯,我现在需要理解Linux内核中setup_arch函数的调用逻辑。首先,我应该回忆一下内核启动的大致流程。内核启动通常从arch相关的汇编代码开始,然后跳转到C语言编写的初始化函数。setup_arch好像是其中一个重要的函数,但具体在哪个阶段调用呢? 我记得在内核启动过程中,start_kernel函数是C语言入口点,而setup_arch就是被start_kernel调用的。那setup_arch的作用是什么呢?应该是负责架构相关的初始化,比如解析设备树、初始化内存、设置处理器信息等。不同的架构(比如ARM、x86)会有不同的实现,对吗? 接下来,我需要理清setup_arch的具体调用流程。首先,start_kernel在init/main.c中定义,它会调用setup_arch(&command_line)。这里的参数是命令行参数,可能用于传递启动时的配置信息。setup_arch函数应该属于arch目录下的某个文件,比如ARM架构可能在arch/arm/kernel/setup.c里。 在setup_arch内部,可能会先进行早期内存管理初始化,比如解析设备树或者ACPI表,确定物理内存布局。然后设置处理器相关的信息,比如CPU型号、频率等。接着可能会保留某些内存区域,比如内核代码、数据段,初始化页表,建立虚拟内存映射。对于支持设备树的架构,可能还会解析设备树,获取硬件信息,比如中断控制器、定时器等。 可能还有其他步骤,比如初始化系统定时器,设置中断控制器,或者处理特定平台的设置。比如ARM的machine_desc结构,用来描述特定板级的配置,这个结构可能在setup_arch中被使用到,通过匹配设备树中的兼容性字符串来选择正确的初始化函数。 不过,我可能有些细节记不太清楚。比如,setup_arch是否直接调用解析设备树的函数,还是通过其他辅助函数?设备树的解析可能是在early_init_dt_scan之类的函数中完成的,而setup_arch会调用这些函数。另外,内存管理相关的初始化可能包括调用paging_init来设置页表,而mem_init可能用来初始化内存数据结构的其他部分。 不同的架构可能有不同的步骤,比如x86可能需要处理BIOS提供的信息,而ARM则更多依赖设备树。所以setup_arch的实现会根据架构的不同而有差异。但总体而言,它的主要职责是初始化硬件相关的部分,为后续的内核启动流程做好准备。 可能需要查阅一下Linux内核源码来确认这些步骤。比如,在ARM架构中,setup_arch函数会调用setup_processor来识别CPU类型,setup_machine_fdt来查找匹配的machine_desc,然后early_init_dt_scan_nodes来解析设备树的早期信息。接着,设置内存,包括解析内存节点,调用arm_memblock_init来保留内存区域,然后调用paging_init来初始化页表,之后可能还有设备树的进一步解析,比如unflatten_device_tree。 总结一下,setup_arch的主要调用逻辑可能包括以下几个步骤: 1. 解析设备树或ACPI信息,确定硬件配置。 2. 初始化处理器相关信息,如CPU型号、特性。 3. 设置内存布局,保留内核使用的内存区域。 4. 初始化页表,启用虚拟内存。 5. 解析和处理特定于板级的配置(如machine_desc结构)。 6. 为后续的驱动初始化和子系统启动准备硬件环境。 不过,可能会有遗漏或者顺序上的不同,需要进一步确认。比如,是否先初始化处理器信息再解析设备树?或者先确定内存布局再进行页表设置?这些细节可能需要参考具体的架构实现。</think>在Linux内核启动过程中,`setup_arch` 是一个关键的架构相关初始化函数。以下是其调用逻辑的逐步解析: --- ### **1. 调用入口** - **位置**: `start_kernel` 函数(位于 `init/main.c`)。 - **触发时机**: 内核完成早期汇编初始化后,进入C语言阶段的第一个重要步骤。 - **代码示例**: ```c asmlinkage __visible void __init start_kernel(void) { ... setup_arch(&command_line); ... } ``` --- ### **2. 核心职责** `setup_arch` 负责架构和硬件相关的初始化,主要任务包括: 1. **解析硬件信息**: - 通过设备树(DT)、ACPI或BIOS获取硬件配置。 - 示例:ARM架构调用 `setup_machine_fdt()` 解析设备树。 2. **初始化内存管理**: - 调用 `early_init_dt_scan_nodes()` 扫描设备树内存节点。 - 通过 `memblock` 保留内核代码、数据段和保留内存区域。 - 最终移交内存管理给伙伴系统(Buddy System)。 3. **处理器初始化**: - 识别CPU型号(如ARM的 `setup_processor()`)。 - 配置CPU特性(如浮点支持、多核信息)。 4. **页表与虚拟内存**: - 调用 `paging_init()` 初始化页表,启用虚拟内存(MMU)。 5. **板级特定配置**: - 匹配 `machine_desc` 结构体(描述板级硬件,如设备树中的 `compatible` 字段)。 - 调用 `init_machine` 回调函数(通常指向 `of_platform_populate()`,注册平台设备)。 --- ### **3. 典型执行流程(以ARM为例)** ```plaintext setup_arch() ├─ setup_processor() // 识别CPU型号和特性 ├─ setup_machine_fdt() // 匹配设备树到machine_desc ├─ early_init_dt_scan_nodes() // 解析设备树中的内存、命令行等 ├─ arm_memblock_init() // 保留内存区域(内核、保留区等) ├─ paging_init() // 初始化页表,启用MMU ├─ unflatten_device_tree() // 展开设备树为内核数据结构 ├─ platform_init() // 调用machine_desc->init_machine() └─ ... // 其他架构相关初始化(如定时器、中断控制器) ``` --- ### **4. 关键数据结构** - **`machine_desc`**: - 描述板级硬件信息,定义在 `arch/arm/include/asm/mach/arch.h`。 - 包含函数指针(如 `init_machine`)和板级属性(如物理内存布局)。 - **`memblock`**: - 早期内存管理器,管理保留内存和可用内存区域。 - **设备树节点**: - 通过 `of_*` 函数族(如 `of_property_read_u32()`)解析硬件信息。 --- ### **5. 与其他模块的交互** - **内存管理子系统**: - `memblock` → `bootmem` → Buddy System。 - **设备驱动**: - 通过 `of_platform_populate()` 注册设备树中的设备。 - **中断子系统**: - 初始化中断控制器(如GIC)和定时器。 --- ### **6. 架构差异性** - **ARM**: - 强依赖设备树,`machine_desc` 匹配设备树的 `compatible`。 - **x86**: - 通过BIOS或UEFI获取内存映射(E820表),初始化ACPI。 - **RISC-V**: - 类似ARM,使用设备树或OpenFirmware。 --- ### **总结** `setup_arch` 是内核启动过程中架构相关初始化的核心枢纽,通过解析硬件信息、配置内存和处理器,为后续驱动加载和子系统运行奠定基础。其实现细节高度依赖目标架构和硬件平台。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值