學習過程是參考了韋東山老師的裝置樹課程,需要購買的可以聯絡我 9折優惠。
linux uses dt data for three major purposes:
1) platform identification,
2) runtime configuration, and
3) device population.
head.s會把dtb的位置儲存在變數__atags_pointer
裡,最後呼叫start_kernel
。
從start_kernel開始分析:
start_kernel // init/main.c
setup_arch(&command_line); // arch/arm/kernel/setup.c
mdesc = setup_machine_fdt(__atags_pointer); // arch/arm/kernel/devtree.c
// 判斷是否有效的dtb, drivers/of/ftd.c
early_init_dt_verify(phys_to_virt(dt_phys)
initial_boot_params = params;
// 找到最匹配的machine_desc, drivers/of/ftd.c
mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);
while ((data = get_next_compat(&compat)))
}machine_desc = mdesc;
裝置樹只是起乙個資訊傳遞的作用,對這些資訊配置的處理,也比較簡單,即從裝置樹的dtb檔案中,把這些裝置資訊提取出來賦給核心中的某個變數即可。
函式呼叫過程分析如下:
start_kernel // init/main.c
setup_arch(&command_line); // arch/arm/kernel/setup.c
mdesc = setup_machine_fdt(__atags_pointer); // arch/arm/kernel/devtree.c
early_init_dt_scan_nodes(); // drivers/of/ftd.c
/* retrieve various information from the /chosen node */
of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
/* initialize -cells info */
of_scan_flat_dt(early_init_dt_scan_root, null);
/* setup memory, calling early_init_dt_add_memory_arch */
of_scan_flat_dt(early_init_dt_scan_memory, null);
裡面主要對三種型別的資訊進行處理,分別是:/chosen節點中 bootargs屬性,根節點的 #address-cells 和 #size-cells屬性,/memory中的 reg屬性。
1./chosen節點中bootargs屬性就是核心啟動的命令列引數,它裡面可以指定根檔案系統在**,第乙個執行的應用程式是哪乙個,指定核心的列印資訊從哪個裝置裡列印出來。
2./memory中的reg屬性指定了不同板子記憶體的大小和起始位址。
3.根節點的#address-cells和#size-cells屬性指定屬性引數的位數,比如指定前面memory中的reg屬性的位址是32位還是64位,大小是用乙個32位表示,還是兩個32位表示。
總結:a. /chosen節點中bootargs屬性的值, 存入全域性變數: boot_command_line
b. 確定根節點的這2個屬性的值: #address-cells, #size-cells
存入全域性變數: dt_root_addr_cells, dt_root_size_cells
c. 解析/memory中的reg屬性, 提取出"base, size", 最終呼叫memblock_add(base, size);
uboot把裝置樹dtb檔案隨便放到記憶體的某乙個地方就可以使用,為什麼核心執行中,他不會去覆蓋dtb所占用的那塊記憶體呢?
在裝置樹檔案中,可以使用/memreserve/
指定一塊記憶體,這塊記憶體就是保留的記憶體,核心不會占用它。即使你沒有指定這塊記憶體,當我們核心啟動時,他也會把裝置樹所占用的區域保留下來。
分析後函式呼叫過程如下:
start_kernel // init/main.c
setup_arch(&command_line); // arch/arm/kernel/setup.c
arm_memblock_init(mdesc); // arch/arm/kernel/setup.c
early_init_fdt_reserve_self();
/* reserve the dtb region */
// 把dtb所佔區域保留下來, 即呼叫: memblock_reserve
early_init_dt_reserve_memory_arch(__pa(initial_boot_params),
fdt_totalsize(initial_boot_params),
0);
early_init_fdt_scan_reserved_mem(); // 根據dtb中的memreserve資訊, 呼叫memblock_reserve
unflatten_device_tree(); // arch/arm/kernel/setup.c
__unflatten_device_tree(initial_boot_params, null, &of_root,
early_init_dt_alloc_memory_arch, false); // drivers/of/fdt.c
/* first pass, scan for size */
size = unflatten_dt_nodes(blob, null, dad, null);
/* allocate memory for the expanded device tree */
mem = dt_alloc(size + 4, __alignof__(struct device_node));
/* second pass, do actual unflattening */
unflatten_dt_nodes(blob, mem, dad, mynodes);
populate_node
np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
__alignof__(struct device_node));
np->full_name = fn = ((char *)np) + sizeof(*np);
populate_properties
pp = unflatten_dt_alloc(mem, sizeof(struct property),
__alignof__(struct property));
pp->name = (char *)pname;
pp->length = sz;
pp->value = (__be32 *)val;
可以看到,先把dtb中的memreserve資訊告訴核心,把這塊記憶體區域保留下來,不占用它。
在dts檔案裡,每個大括號代表乙個節點,比如根節點裡有個大括號,對應乙個device_node結構體;memory也有乙個大括號,也對應乙個device_node結構體。
節點裡面有各種屬性,也可能裡面還有子節點,所以它們還有一些父子關係。
根節點下的memory、chosen、led等節點是並列關係,兄弟關係。
對於父子關係、兄弟關係,在device_node結構體裡面肯定有成員來描述這些關係。
linux對塊裝置的請求處理
linux對塊裝置請求的處理是一種層次體系結構,可以分為5層 1.vfs 這層對所有檔案系統的一種封裝 這個操作分為2步 a.首先確定包含檔案的檔案系統的block size,然後計算請求的資料報含多少file block。b.呼叫跟檔案系統有關的函式來訪問檔案的inode,確定請求的資料在磁碟上的...
Linux核心 裝置樹操作常用API
核心中用下面的這個結構描述裝置樹中的乙個節點,後面的api都需要乙個device node物件作為引數傳入。include of.h struct device node struct device node 47 節點名 48 裝置型別 50 全路徑節點名 54 父節點指標 55 子節點指標 of...
小張學linux核心 七 裝置樹的使用
在很久之前的版本,uboot還需要傳machine id和引數區位址給核心,但現在只傳遞裝置樹 fdt 的位址給核心了,這些引數全在fdt中做了。uboot將裝置樹bin檔案和kernel載入到記憶體,然後將fdt位址傳給kernel,跳到kernel位址執行。fdt為平坦裝置樹,即數作為乙個乙個節...