Transfer of Linux device tree and parsing of device tree in kernel
When U-Boot loads the device tree into the specified location in the memory, the ARM core SoC uses the general register r2 to transfer the address of the dtb in the memory. After the kernel obtains the address, it further processes the dtb file.
#Device tree transfer
When using bootm to load the kernel image (bootz is an encapsulation and functional extension of bootm, essentially the same). The entry function for U-Boot to jump to the kernel is boot_jump_linux
/* Subcommand: GO */
static void boot_jump_linux(bootm_headers_t *images, int flag)
{
...
debug("## Transferring control to Linux (at address %08lx)" \
"...\n", (ulong) kernel_entry);
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
announce_and_cleanup(fake);
if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
r2 = (unsigned long)images->ft_addr;
else
r2 = gd->bd->bi_boot_params;
...
}
r2 is a register that stores the address of the device tree. There are two ways to obtain its value: instantiating ft_addr of the bootm_header_t data structure, and using U-Boot's board-level startup parameters as the address of the device tree.
##bootm_header_tmethod
The data structure bootm_header_t is defined as follows and is used by SoCs with various cores. Each manufacturer instantiates each member differently according to the characteristics of its own CPU.
/*
* Legacy and FIT format headers used by do_bootm() and do_bootm_<os>()
* routines.
*/
typedef struct bootm_headers {
...
char *ft_addr; /* flat dev tree address */
ulong ft_len; /* length of flat device tree */
...
} bootm_headers_t;
Using the bootm_header_t method, U-Boot needs to support device tree and non-empty files.
ft_len and ft_addr belong to bootm_header_t. When U-Boot parses the image file, these two members are instantiated. The function call stack is as follows:
do_bootz(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
-bootz_start()
--bootm_find_images(int flag, int argc, char *const argv[], ulong start,ulong size)
---boot_get_fdt(flag, argc, argv, IH_ARCH_DEFAULT, &images,&images.ft_addr, &images.ft_len);
u-boot-v2021.04/common/image-fdt.c
##gd->bd->bi_boot_params method
This is a relatively old method and is basically not used now. bi_boot_params is an address that stores kernel startup parameters, usually specified in board-level initialization.
When the code is executed here, whether r2 is the expected value can be confirmed by printing and using debugging tools.
#kernel’s parsing of the device tree
The analysis is divided into two stages. The first stage performs verification and readjustment of startup parameters; the second stage completes the decompression of the device tree, that is, changing the device tree from FDT to EDT and creating device_node.
##The first stage
The first entry related to the device tree in the kernel startup log is printed as follows, which prints out the model name of the current hardware device, "OF: fdt: Machine model: V2P-CA9"
Booting Linux on physical CPU 0x0
Linux version 5.4.124 (qemu@qemu) (gcc version 6.5.0 (Linaro GCC 6.5-2018.12)) #3 SMP Fri Jun 25 15:26:02 CST 2021
CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
OF: fdt: Machine model: V2P-CA9
This model name is defined at the head of the device tree file and defines the overall name of the current device.
// SPDX-License-Identifier: GPL-2.0
/*
* ARM Ltd. Versatile Express
*
* CoreTile Express A9x4
* Cortex-A9 MPCore (V2P-CA9)
*
* HBI-0191B
*/
/dts-v1/;
/ {
model = "V2P-CA9";
...
}
But this is not the first time the kernel handles the device tree. There have been other operations before this. The function call stack is as follows:
setup_arch(char **cmdline_p) arch/arm/kernel/setup.c
atags_vaddr = FDT_VIRT_BASE(__atags_pointer);
setup_machine_fdt(void *dt_virt) arch/arm/kernel/devtree.c
early_init_dt_verify()
of_flat_dt_match_machine() drivers/of/fdt.c
early_init_dt_scan_nodes();
__machine_arch_type = mdesc->nr;
Line 2 __atags_pointer is the address of dtb in memory. This address is obtained during the assembly phase (if the image is zImage, then it is completed during the decompression phase). Since mmu has been enabled and the 4K segment page table has been mapped when setup_arch is executed, and the device tree fdt address passed by U-Boot to the kernel belongs to the physical address, the physical address needs to be converted into a virtual address.
head-common.S
.align 2
.type __mmap_switched_data, %object
__mmap_switched_data:
.long _sdata @ r0
.long __data_loc @ r1
.long _edata_loc @ r2
.long __bss_stop @ sp (temporary stack in .bss)
.long __bss_start @ r0
.long __bss_stop @ r1
.long init_thread_union + THREAD_START_SP @ sp
.long processor_id @ r0
.long __machine_arch_type @ r1
.long __atags_pointer @ r2
The configuration of the device tree in the first stage mainly includes:
A 对dtb文件进行crc32校验,检测设备树文件是否合法early_init_dt_verify()
B early_init_dt_scan_nodes()
/* Retrieve various information from the /chosen node */
of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
/* Initialize {size,address}-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);
C 更新__machine_arch_type
D 更新chosen
The above chosen information can be checked again after the kernel is started to see what modifications have been made.
##second stage
The second stage is simply to decompress the device tree ABI file, change it from FDT to EDT, and generate the corresponding device_node node.
The function call stack at this stage is as follows:
unflatten_device_tree();
*__unflatten_device_tree()
/* First pass, scan for size */
size = unflatten_dt_nodes(blob, NULL, dad, NULL);
/* Second pass, do actual unflattening */
unflatten_dt_nodes(blob, mem, dad, mynodes);
unflatten_dt_nodes()
populate_node()
The device_nodes nodes are as follows:
After device_node is created, when the kernel creates platform_device, it registers the corresponding device based on the work completed at this stage for use by the driver code.
end
A mouthful of Linux
Follow and reply [ 1024 ] Massive Linux information will be given away
Collection of wonderful articles
Article recommendation