arm64内存-arm64_memblock_init
请先了解:
1、线性空间下移:https://www.cnblogs.com/zhangzhiwei122/p/16058173.html
2、整体流程:https://www.cnblogs.com/zhangzhiwei122/p/16085238.html
5.10 版本,linux/arch/arm64/mm/init.c
总体来讲,就是根据dtb,命令行参数,内核配置来 添加、删除 memblock 中的记录信息。这个搞完之后,memblock 中就记录了设备的物理内存 拥有、使用、空闲情况。
272,vabits_actual 实际使用的VA bits ,假设为 48, 则 线性空间 为 ffff000000000000 ffff7fffffffffff ,大小为 800000000000
275 解析 chosen 节点下面的 linux.usable-memory-range 属性
参考 Documentation/devicetree/bindings/chosen.txt 文档,
e.g.
/ {
chosen {
linux,usable-memory-range = <0x9 0xf0000000 0x0 0x10000000>;
};
};
对这段地址空间,调用 memblock_cap_memory_range ( base, size ) 进行处理。设置 capacity ?
270void __init arm64_memblock_init(void) 271{ 272 const s64 linear_region_size = BIT(vabits_actual - 1); 273 274 /* Handle linux,usable-memory-range property */ 275 fdt_enforce_memory_region(); 276 277 /* Remove memory above our supported physical address size */ 278 memblock_remove(1ULL << PHYS_MASK_SHIFT, ULLONG_MAX); 279 280 /* 281 * Select a suitable value for the base of physical memory. 282 */ 283 memstart_addr = round_down(memblock_start_of_DRAM(), 284 ARM64_MEMSTART_ALIGN); 285 286 /* 287 * Remove the memory that we will not be able to cover with the 288 * linear mapping. Take care not to clip the kernel which may be 289 * high in memory. 290 */ 291 memblock_remove(max_t(u64, memstart_addr + linear_region_size, 292 __pa_symbol(_end)), ULLONG_MAX); 293 if (memstart_addr + linear_region_size < memblock_end_of_DRAM()) { 294 /* ensure that memstart_addr remains sufficiently aligned */ 295 memstart_addr = round_up(memblock_end_of_DRAM() - linear_region_size, 296 ARM64_MEMSTART_ALIGN); 297 memblock_remove(0, memstart_addr); 298 } 299 300 /* 301 * If we are running with a 52-bit kernel VA config on a system that 302 * does not support it, we have to place the available physical 303 * memory in the 48-bit addressable part of the linear region, i.e., 304 * we have to move it upward. Since memstart_addr represents the 305 * physical address of PAGE_OFFSET, we have to *subtract* from it. 306 */ 307 if (IS_ENABLED(CONFIG_ARM64_VA_BITS_52) && (vabits_actual != 52)) 308 memstart_addr -= _PAGE_OFFSET(48) - _PAGE_OFFSET(52); 309 310 /* 311 * Apply the memory limit if it was set. Since the kernel may be loaded 312 * high up in memory, add back the kernel region that must be accessible 313 * via the linear mapping. 314 */ 315 if (memory_limit != PHYS_ADDR_MAX) { 316 memblock_mem_limit_remove_map(memory_limit); 317 memblock_add(__pa_symbol(_text), (u64)(_end - _text)); 318 } 319 320 if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && phys_initrd_size) { 321 /* 322 * Add back the memory we just removed if it results in the 323 * initrd to become inaccessible via the linear mapping. 324 * Otherwise, this is a no-op 325 */ 326 u64 base = phys_initrd_start & PAGE_MASK; 327 u64 size = PAGE_ALIGN(phys_initrd_start + phys_initrd_size) - base; 328 329 /* 330 * We can only add back the initrd memory if we don't end up 331 * with more memory than we can address via the linear mapping. 332 * It is up to the bootloader to position the kernel and the 333 * initrd reasonably close to each other (i.e., within 32 GB of 334 * each other) so that all granule/#levels combinations can 335 * always access both. 336 */ 337 if (WARN(base < memblock_start_of_DRAM() || 338 base + size > memblock_start_of_DRAM() + 339 linear_region_size, 340 "initrd not fully accessible via the linear mapping -- please check your bootloader ...\n")) { 341 phys_initrd_size = 0; 342 } else { 343 memblock_remove(base, size); /* clear MEMBLOCK_ flags */ 344 memblock_add(base, size); 345 memblock_reserve(base, size); 346 } 347 } 348 349 if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { 350 extern u16 memstart_offset_seed; 351 u64 range = linear_region_size - 352 (memblock_end_of_DRAM() - memblock_start_of_DRAM()); 353 354 /* 355 * If the size of the linear region exceeds, by a sufficient 356 * margin, the size of the region that the available physical 357 * memory spans, randomize the linear region as well. 358 */ 359 if (memstart_offset_seed > 0 && range >= ARM64_MEMSTART_ALIGN) { 360 range /= ARM64_MEMSTART_ALIGN; 361 memstart_addr -= ARM64_MEMSTART_ALIGN * 362 ((range * memstart_offset_seed) >> 16); 363 } 364 } 365 366 /* 367 * Register the kernel text, kernel data, initrd, and initial 368 * pagetables with memblock. 369 */ 370 memblock_reserve(__pa_symbol(_text), _end - _text); 371 if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && phys_initrd_size) { 372 /* the generic initrd code expects virtual addresses */ 373 initrd_start = __phys_to_virt(phys_initrd_start); 374 initrd_end = initrd_start + phys_initrd_size; 375 } 376 377 early_init_fdt_scan_reserved_mem(); 378 379 if (IS_ENABLED(CONFIG_ZONE_DMA)) { 380 zone_dma_bits = ARM64_ZONE_DMA_BITS; 381 arm64_dma_phys_limit = max_zone_phys(ARM64_ZONE_DMA_BITS); 382 } 383 384 if (IS_ENABLED(CONFIG_ZONE_DMA32)) 385 arm64_dma32_phys_limit = max_zone_phys(32); 386 else 387 arm64_dma32_phys_limit = PHYS_MASK + 1; 388 389 reserve_crashkernel(); 390 391 reserve_elfcorehdr(); 392 393 high_memory = __va(memblock_end_of_DRAM() - 1) + 1; 394 395 dma_contiguous_reserve(arm64_dma32_phys_limit); 396} 397
278 删除支持的 物理地址以上的区域,假设 支持物理地址 48 bits ,则 物理地址 0000 0000 0000 0000 ~ 0000 ffff ffff ffff 。 0000 ffff ffff ffff 以上的物理地址都不可访问。memblock 中不必存在。
283 行,取memblock 中 memory 类型的 regions[0].base 地址。在这个之前,内存段 已经使用 memblock_add 加入到了 memblock 中。 memstart_addr 表示物理内存地址 起始值。
以这个起始值,线性映射区域结尾 为 memstart_addr + linear_region_size 。如果 _end 值大于这个值, 则 线性区域 需要继续向后延伸到 _end 值。
291 行 - 线性映射区域 对应 物理内存 之后的,memblock 也不关心【因为memblock 只负责 内核启动过程 内存管理, 只给内核功能模块分配内存,内核分配的内存也只在线性映射区域。】 给 remove 掉。
293 行,这时再取 memblock end of DRAM , 因为 291 行,已经将 高于 线性映射区域的 remove 调了,所以,只可能取得到 _end 或者 memstart_addr + linear_region_size , 如果是 _end 地址,进入 294 行处理
保证 memstart_addr + linear region size >= _end .
301 ~ 308 行,配置VA 地址 52 bits,但是 设备不支持时的处理。暂时略。
309 ~ 319 行,如果命令和参数设置了 mem=xx 参数,限定了 使用内存大小,memory_limit 值为 设置的值。删除这个值之后的。但是,有可能把 内核镜像 装载的部分页删除了,所以再 加回来。
320 ~ 348,如果配置了 CONFIG_BLK_DEV_INITRD ,并且加载了 initrd 文件,则保留 initrd 文件占据部分的内存。
349 ~ 364 暂时略,randomize base 相关。
370 reserve 内核镜像 占据部分
371 ~ 375 处理 initrd 文件,设置变量 initrd_start initrd_end
377 处理 dtb 中 reserve memory 节点
379 ~ 387 处理 arm64_dma_phys_limit
182static phys_addr_t __init max_zone_phys(unsigned int zone_bits) 183{ 184 phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, zone_bits); 185 return min(offset + (1ULL << zone_bits), memblock_end_of_DRAM()); 186}
max zone phys - Return the maximum physical address for a zone with a given address size limit.
输入参数 为 bits , 返回值为 最大物理地址。
举例: 内存起始地址, memblock start of DRAM 0x0000 0000 1000 0000 , 给定 zone-bits 为 6 。则返回值为 0x0000 0000 1000 0400 即 6 bits 在 一个 zone ,它从 0x0000 0000 1000 0000 ~ 0x0000 0000 1000 0400
GENMASK_ULL(63, zone_bits) ; 生成一个 unsigned long long 类型的数值。 【zone_bits ~ 63) 之间的bit 被设置 为 1 。示例:GENMASK(10, 8): 0x700 。
将 start of DRAM 和 mask 进行与操作,起始地址中,小于 zone_bits 的bit 位 清零。高于 zone_bits 的bit 位保留,相当于 offset 记录了 起始地址所在的 台阶。
185 行,取min ,防止计算出来的 地址,超过 end of DRAM 物理地址。
389 391 调用同文件中的 static 函数,进行 reserve memory
393 计算high_mem 值
395 调用 kernel/dma/contiguous.c 中的函数,处理命令行,或者内核配置 的 cma 预留区域。