arm64内存-fdt的映射和setup_machine_fdt
调用顺序
arch/arm64/kernel/setup.c
setup_arch -> setup_machine_fdt
setup_machine_fdt
171static void __init setup_machine_fdt(phys_addr_t dt_phys) 172{ 173 int size; 174 void *dt_virt = fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL); 175 const char *name; 176 177 if (dt_virt) 178 memblock_reserve(dt_phys, size); 179 180 if (!dt_virt || !early_init_dt_scan(dt_virt)) { 181 pr_crit("\n" 182 "Error: invalid device tree blob at physical address %pa (virtual address 0x%p)\n" 183 "The dtb must be 8-byte aligned and must not exceed 2 MB in size\n" 184 "\nPlease check your bootloader.", 185 &dt_phys, dt_virt); 186 187 while (true) 188 cpu_relax(); 189 } 190 191 /* Early fixups are done, map the FDT as read-only now */ 192 fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL_RO); 193 194 name = of_flat_dt_get_machine_name(); 195 if (!name) 196 return; 197 198 pr_info("Machine model: %s\n", name); 199 dump_stack_set_arch_desc("%s (DT)", name); 200} 201
174 使用fixmap_remap_fdt 在 arch/arm64/mm/mmu.c, 将物理地址 dt_phys 映射到虚拟地址 dt_virt , 并且 得到 fdt 的长度 size (下面会有介绍)
177 ~ 178 如果映射成功,即 dt_virt 不为 0 , 使用memblock 模块的 reserve 函数,将 dt_phys 开始 size 大小的这块内存,在 memblock 模块中 预留下来。
180 在保证 dt_virt 不为0 的前提下面,调用 early_init_dt_scan ( dt_virt ) drivers/of/fdt.c 中, 给 fdt 模块填入输入 dt_virt ,并初始化fdt模块,后面就可以调用 fdt 模块的函数,从里面取属性了。 (下面会有介绍)
181 ~ 188 如果 early_init_dt_scan 返回false了,表示 dt_virt 指向区域的 设备树 格式有问题,报错。
192 将 dt_phys 开始 size 大小 的这块内存设置为 只读。
192 调用 fdt 模块的 函数,获取属性值, machine_name
199 给 dump stack 模块,设置 arch desc ,也就是 machine name 。
fixmap_remap_fdt
arch/arm64/mm/mmu.c
1265void *__init fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot) 1266{ 1267 const u64 dt_virt_base = __fix_to_virt(FIX_FDT); 1268 int offset; 1269 void *dt_virt; 1297 offset = dt_phys % SWAPPER_BLOCK_SIZE; 1298 dt_virt = (void *)dt_virt_base + offset; 1299 1300 /* map the first chunk so we can read the size from the header */ 1301 create_mapping_noalloc(round_down(dt_phys, SWAPPER_BLOCK_SIZE), 1302 dt_virt_base, SWAPPER_BLOCK_SIZE, prot); 1303 1304 if (fdt_magic(dt_virt) != FDT_MAGIC) 1305 return NULL; 1306 1307 *size = fdt_totalsize(dt_virt); 1308 if (*size > MAX_FDT_SIZE) 1309 return NULL; 1310 1311 if (offset + *size > SWAPPER_BLOCK_SIZE) 1312 create_mapping_noalloc(round_down(dt_phys, SWAPPER_BLOCK_SIZE), dt_virt_base, 1313 round_up(offset + *size, SWAPPER_BLOCK_SIZE), prot); 1314 1315 return dt_virt; 1316}
1267 调用 fixmap 模块的函数,得到 FIX_FDT 这个 idx 对应的 VA 地址,放在dt_virt_base 里面。
1270 ~ 1296 跳过一些参数检查
1297 计算 dt_phys 这个物理地址,以 swapper_block 大小对齐后,在一个swapper_block 中的偏移。
1298 由 dt_virt_base 和 offset 得到 dt_phys 映射后的 VA 地址。 这个地址在 1307 行,作输入给fdt模块的 fdt_totalsize 函数使用,取得 设备树 占内存大小。 1315 行返回值。
1301 使用 create_mapping_noalloc 函数,建立 dt_virt_base 到 round_down ( dt_phys, SWAPPER_BLOCK_SIZE ) 之间的 映射关系。
create_mapping_noalloc 函数 也在 arch/arm64/mm/mmu.c 中,是 static 声明,相当于一个 建立映射的 辅助函数。
这个辅助函数会 修改 已经存在 (不会新建) 的 pgd pud pmd pte 表, 表里面的表项内容,完成 phys 和 virt 之间的映射。
前提是 fixmap 模块,已经 初始化了,所以 fixmap start ~ fixmap_top 这段 VA 地址 对应的 pgd pud pmd pte 表 都已经存在了。dt_virt_base 就是处在fixmap start ~ fixmap_top 这段 VA 地址 之间。
为什么使用 本模块中的 static 辅助函数,不使用 fixmap 模块的函数?
各个软件模块 归根到底,都是为了管理 操作 设备; 各个软件模块都 都遵从 从设备读->算->写入设备 的方式,修改设备的状态即可,不必太多耦合,一个调用另外一个。
1034 ~ 1035 检查 fdt magic 标识。
1037~ 1039 取 size ,并对size 进行检查。
1311 ~ 1313 ,发现 offset + size 超过了 一个 swapper block 的大小,则需要映射后面的一个 swapper block ,这样,才能访问完整的 设备树。
early_init_dt_scan
drivers/of/fdt.c
1201bool __init early_init_dt_scan(void *params) 1202{ 1203 bool status; 1204 1205 status = early_init_dt_verify(params); 1206 if (!status) 1207 return false; 1208 1209 early_init_dt_scan_nodes(); 1210 return true; 1211}
1205 调用 early_init_dt_verify 进行 校验 检查, 检查失败,返回false
1209 调用 early_init_dt_scan_nodes 解析 设备树的节点。