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 解析 设备树的节点。

 

posted @ 2022-03-26 19:21  张志伟122  阅读(765)  评论(0编辑  收藏  举报