early_fixmap_init:
dtb进行映射,通过设备树文件和membloc模块让内核了解更为广阔的内存世界。Uboot将dtb拷贝到内存中,且通过传递相关参数将dtb的物理地址告知内核。但是内核必须将dtb的相关物理地址映射到虚拟地址上,通过虚拟地址间接访问dtb文件。由于此时物理地址映射并没有完成。为了解决该问题提出了fixed map机制。
内核访问DTB物理地址,先将DTB所在的物理地址映射到内核虚拟地址空间的Fixed map区域,然后通过该虚拟地址区域间接访问DTB文件。系统对dtb的大小有限制,不能大于2MB,这样要求主要是为了保证创建地址映射的时候不会分配其它的 translation table page(可以从setup_machine_fdt中有描述,必须要8字节对齐且不能超过2M)。该机制是通过early_fixmap_init函数来完成。
init/main.c中
start_kernel
--setup_arch
---early_fixmap_init
---early_ioremap_init
---setup_machine_fdt
---arm64_memblock_init
early_ioremap_init:
会调用early_ioremap_setup-->内核启动初期需要通过early_ioremap机制让设备寄存器对内存进行访问。一些硬件需要在内存管理系统运行起来之前就需要工作,内核采early_ioremap机制来映射内存给这些硬件驱动使用。并且这些硬件驱动在使用完early_ioremap的地址后需要尽快的释放掉这些内存,这样才能保证其他硬件模块继续使用。因此early_ioremap采用的是Fixed map的temporary fixmap段虚拟地址。ioremap的空间为7 * 256K的区域,保存在slot_vir[]数组中,当需要进行IO操作的时候,最终会调用到__early_ioremap函数,在该函数中去填充对应的pte entry,从而完成最终的虚拟地址和物理地址的映射。
setup_machine_fdt:
返回dtb所在的虚拟地址dt_virt
void *dt_virt = fixmap_remap_fdt(dt_phys)
fixmap_remap_fdt传入dtb所在的物理地址dt_phys返回其虚拟地址dt_virt,此时运行内核cpu已经开启了MMU。下面是其实现代码细节和调用流程:
1.//arc/arm64/mm/mmu.c
2.void *__init fixmap_remap_fdt(phys_addr_t dt_phys)
3.{
4. void *dt_virt;
5. int size;
6. //完成fdt PTE表entry的内容填写,返回fdt起始虚拟地址dt_virt和空间大小size
7. dt_virt = __fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL_RO);
8. if (!dt_virt)
9. return NULL;
10. /*把DTB所占的内存添加到memblock管理模块的reserve模块里,这样后续内存分配不会使用这段内存。在后面 11. *会使用memblock_free()把该内存释放 12. */
13. memblock_reserve(dt_phys, size);
14. return dt_virt;
13.}
1.//arc/arm64/mm/mmu.c
2.void *__init __fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot)
3.{
//为dtb提供虚拟地址的base,静态定义事先预留
15. const u64 dt_virt_base = __fix_to_virt(FIX_FDT);
16. int offset; de
17. void *dt_virt;
18.
19. BUILD_BUG_ON(MIN_FDT_ALIGN < 8);
//检查物理地址的对齐,必须MIN_FDT_ALIGN对齐
20. if (!dt_phys || dt_phys % MIN_FDT_ALIGN)
21. return NULL;
22. //检查虚拟地址的对齐,必须SZ_2M对齐
23. BUILD_BUG_ON(dt_virt_base % SZ_2M);
24. /* 25. *保证FDT所在的虚拟地址范围落在early_fixmap_init函数建立的PMD范围内以为在early_fixmap_init已经建 26. *立了PUD和PMD,不能让其额外浪费PMD内存 */
27. BUILD_BUG_ON(__fix_to_virt(FIX_FDT_END) >> SWAPPER_TABLE_SHIFT !=
28. __fix_to_virt(FIX_BTMAP_BEGIN) >> SWAPPER_TABLE_SHIFT);
29. //物理地址偏移(2M空间范围,末尾21位)
30. offset = dt_phys % SWAPPER_BLOCK_SIZE;
31. //偏移后实际的虚拟地址
32. dt_virt = (void *)dt_virt_base + offset;
33.
34. //根据提供的物理地址和虚拟地址设置页表的entry
35. create_mapping_noalloc(round_down(dt_phys, SWAPPER_BLOCK_SIZE),
dt_virt_base, SWAPPER_BLOCK_SIZE, prot);
36.
37. //根据实际的虚拟地址访问物理地址空间内容,即FDT文件内容,此处检测DTB文件首部内容是否是DTB魔数
38. if (fdt_magic(dt_virt) != FDT_MAGIC)
39. return NULL;
40. //获取dtb文件大小
41. *size = fdt_totalsize(dt_virt);
42. //DTB大小不能超过2M
43. if (*size > MAX_FDT_SIZE)
44. return NULL;
45. //如果dtb文件结尾的地址空间超过了上面建立的2M地址范围,需要紧接着再映射2M地址空间
46. if (offset + *size > SWAPPER_BLOCK_SIZE)
47. create_mapping_noalloc(round_down(dt_phys, SWAPPER_BLOCK_SIZE), dt_virt_base,
48. round_up(offset + *size, SWAPPER_BLOCK_SIZE), prot);
50. return dt_virt;
38.}
early_init_dt_scan
-----early_init_dt_verify(对dtb头进行检查) 使用crc校验 scripts/dtc/libfdt中
-----early_init_dt_scan_nodes
of_scan_flat_dt:
of_scan_flat_dt对dtb里面的所有节点进行扫描,
//向系统注册该memory node内存区域,通过memblock_add完成添加
early_init_dt_add_memory_arch(base, size);
下一章节介绍设备驱动根据dts信息如何进行内存映射。