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 解析 裝置樹的節點。