.dts파일은 컴파일 후, HexFile로 생성된다.
해당 HexFile을 Linux Kernel이 Parsing 하면 아래그림과 같이 Tree 구조를 가진다.

Linux Source Code 흐름을 따라가보면 아래 그림과 같다.

동일한 내용을 텍스트로 작성.
setup_arch(&command_line)
setup_machine_fdt(__atags_pointer)
| 인수로 전달받은 디바이스 트리(fdt)가 물리 주소에 있는지 검색
| 해당 machine을 찾고 machine_desc구조체 포인터로 알아온다
| 디바이스 트리로부터 초기 부트업 과정에 미리(early) 설정해야 정보를 얻어온다
| 초기 정보에는 커맨드 라인 정보나 메모리 정보가 있다
early_init_dt_verify(phys_to_virt(dt_phys))
| 전역변수 initiali_boot_params의 값에 device tree의 가상주소 저장
| fdt magic value 및 CRC를 체크하여 유효성 검사
of_flat_dt_match_machine(mdesc_best, arch_get_next_mach) : 디바이스 트리 항목들의 compatible속성을 읽어들인다
arch_get next_mach_(&compat))
| 다음 항목에 대하여 compatible string을 반환한다
| 디바이스 트리의 모든 항목들에 대하여 Loop를 돈다
| bootarg와 device tree의 모든 항목들에 대하여 compatible을 비교하는 이 루틴은 MULTIPATFORM을 지원하기 위함이다
| 당사의 제품의 경우 하나의 platform이 올라가기 때문에 루프한번을 돌고 compatile value를 얻어온다
of flat_dt_match(dt_root, compat)
of_fdt_match(initial_boot_params, node, compat)
| machine을 찾지 못하여 machine_desc 구조체 포인터를 알아오지 못한 경우, machine table을 덤프하고 종료
| 만약 firmware가 kernel level에서의 device tree조작을 요청한 경우, 즉, machine_dexc->dt_fixup 함수 포인터에 값이 저장되어 있는 경우, 해당 함수를 수행한다
early_init_dt_scan_nodes()
| 본 함수에서는 of_scan_flat_dt( ) 함수를 인자를 다르게 하여 3번 호출한다
| of_scan_flat_dt( )는 해당하는 node를 찾아서 call back 함수를 호출하는 역할을 한다
of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line)
| fdt_next_node( ) 함수를 통해서 dtb blob을 순휘한다
kbasename(pathp)
| device tree는 파일처럼 root에서 시작되는 경로로 이루어져 있는데, 경로의 마지막 이름을 얻어온다
it(offset, pathp,depth, data)
| 콜백 함수 early_init_dt_scan_chosen(unsigned long node, const char *uname, int depth,void *data)를 실행한다 (dtb의bootarg를 읽어와 cmdline 설정)
| 현재 찾는 노드는 chosen이 맞는가 문자열 비교를 통하여 확인한다
| chosen 노드는 bootarg와 bootmode가 설정되어 있는 노드이다
of_get_flat_dt_prop(node, "bootargs", &l)
fdt_getprop(initial_boot_params, node, name, size)
fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp)
| property name string을 비교하여 fdt_property 구조체 포인터 반환
| fdt_porperty->data 반환
| 전역변수 boot_command_lene에 chosen node의 bootargs property의 data 저장
| CONFIG_CMDLINE 정의 & CONFIG_CMDLINE_FORCE 비정의 -> config파일의 CMDLINE 사용
of_scan_flat_dt(early_init_dt_scan_root, NULL)
| 콜백 함수 early_init_dt_scan_root(unsigned long node, const char *uname, int depth, void *data) 실행
| root node (top level)의 address-cell 크기와 size-cell 크기를 알아와서 전역변수 dt-root_size_cells, dt_root_addr_cells에 각각 저장
| cell size의 값은 1이 4byte를 나타낸다
of_scan_flat_dt(early_init_dt_scan_memory, NULL)
| 콜백 함수 early_init_dt_scan_memory(unsigned long node, const char *uname, intdepth, void *data) 실행
of_get_flat_dt_prop(node, "device_type", NULL)
| node name이 "device_type인 node의 fdt_property->data를 반환한다
| node의 property가 memory가 아니라면 함수를 종료한다
| node 내의 각각의 cell을 모두 스캔하여 dt_mem_next_cell(dt_root_addr-cells, ®), dt_mem_next_cell(dt_root_ssize-cells,®) 두개의 함수를 이용하여 지정된 cell size만큰 base와 size를 read한다
early_init_dt_add_memory_arch(base, size)
| memblock에 해당 base와 size를 PAGE_ALIGN 하여 추가한다
memblock_add_region(base, size, MAX_NUMNODES, 0)
memblock_add_range(_rgn, base, size, nid, flags)
| 전역변수 memblock->memory->region[i]에 메모리 구역을 추가한다
| type이 동일한 영역들을 merging하거나, memblock entry갯수가 모자란 경우를 위해 적어도 두번 이상 loop를 동면서 merging을 수행한다
┖memblock_double_array(type, obase, size)
| first round의 경우 기존 memblock들과 끼워 넣을memblock들의 합이 최대 관리 개수를 넘어가는 경우에 한해 memblock 영역의 엔트리 수를 두 배 더 크게 넓히기 위해 memblock_double_array( ) 함수를 호출
| 이 떄 충분한 엔트리 개수가 준비될 때 까지 반복한다.
if/else
┖memblock_merge_regions(type)
| second round에서 끼워 넣은 memblock들에 대해 주변 memblock들과 인접하고 flag타입이 동일한 memblock들을 memvlock_merge_regions( ) 함수를 사용하여 merge한다.
| 만약 multi platform인 경우에는 인식한 architecture의 갯수를 전역변수 __machine_arch_type에 기록한다
| machine_desc를 반환하여 setup_arch( )의 나머지 부분에 계속해서 사용하여 초기화를 진행한다
......
parse_early_param()
| 전역 변수 boot_command_line의 내용을 tmp_cmdline에 복사
parse_early_options(tmp_cmdline)
| 파라미터 블록, 개수, 범위가 지정되는 경우 그 파라미터 범위에 해당하는 토큰과 매치되는 경우 해당 파라미터에 값을 대입
| 파라미터 블록, 개수 및 범위가 0으로 전달되는 경우 각 토큰을 파싱하게 되면 param과 val 값을 가지고 항상 unknown hanler인 do_early_param() 함수가 호출
do early param(char *oaram, char *val, const char *unused)
| 다음 조건에 해당되는 early 커널 파라미터를 발견하면 @val 값 인수를 가지고 해당 커널 파라미터에 등록된 함수를 호출
| 요청한 커맨드 라인 파라미터가 early 커널 셋업 파라미터와 매치된 경우
| 요청한 커맨드 라인 파라미터가 "console"로 시작한 경우
| early_param("earlycon")으로 등록한 셋업 함수
early_mem(char *p)
arm_add_memory(start, size)
memblock_add(start, size)
| bootarg애 있는 값 또한 memblock에 추가한다
......
arm_memblock_init(mdesc)
......
early_init_fdt_scan_reserved_mem()
early_init_dt_reserve_memory_arch(__pa(initial_voot_params), fdt_totalsize(initial_boot_params),0)
| DTB Blob이 저장되어 있는 영역을 전역변수 memblock->reserved->region[i] 추가한다
| DTB 시작위치에서 fdt_header->off_mem_rsvmap 만큼 떨어진 필드에 reservation이 필요한 메모리 영역이 시작위치(32bit), 크기(32bit) 순서대로 위치해 있다. 해당 pair list의 마지막은 size가 0으로 저장되어 있다.
fdt_get_mem_rsv(initial_boot_params, n, &base, &size)
| pair list를 돌면서 base와 size를 얻어온다
early_init_dt_reserve_memory_
reserved mem 초기화 함수를 등록할 수 있다
#define RESERVEDMEM_OF_DECLARE(name, compat, init) _OF_DECLARE(reservedmem, name, compat, init reserved_of_init_fn)
예를 들어 아래와 같이 사용한 경우
RESERVEDMEM_OF_DECLARE(cma, "shared-dma-pool", rmem_cma_setup)
RESERVEDMEM_OF_DECLARE를 통해서 __of_table_cma 이름의 of_device_id 구조체가 __reservedmem_of_table에 등록된다.
디바이스명(compat)은 "shared-cma-pool"이다.
이 디바이스의 초기화 함수는 rmem_cma_setup() 함수이다.
참고 및 출처:
http://jake.dothome.co.kr/arm_memblock_init/
arm_memblock_init()
<kernel v4.0> reserve memblock 영역에 다음 영역들을 등록한다. 커널 영역 (XIP 커널인 경우 코드를 제외한 커널 영역) initrd 영역 페이지 테이블 영역 아키텍처 머신이 지정하는 reserve 영역 DTB 영역 및 DT
jake.dothome.co.kr
http://jake.dothome.co.kr/setup_machine_fdt/
setup_machine_fdt()
<kernel v5.0> 머신 설정 시스템을 설정하는 방법은 다음과 같이 두 가지로 나뉜다. Legacy 머신 디스크립터를 사용하여 아키텍처 또는 머신 specific한 코드를 사용한다. 기존 ARM32를 사용한 대부분의
jake.dothome.co.kr
https://www.cnblogs.com/downey-blog/p/10485596.html
linux设备驱动程序-设备树(1)-dtb转换成device_node - 牧野星辰 - 博客园
linux设备驱动程序 设备树(1) dtb转换成device_node 本设备树解析基于arm平台 从start_kernel开始 linux最底层的初始化部分在HEAD.s中,这是汇编代码,我们暂且不作过多讨论,在head.s完成部分初始化之后
www.cnblogs.com
http://jake.dothome.co.kr/unflatten_device_tree/
unflatten_device_tree()
<kernel v5.10> 디바이스 트리(FDT) -> Expanded 포맷으로 변환 device_node와 property 구조체를 사용하여 트리 구조로 각 노드와 속성을 연결한다. 기존에 사용하던 DTB 바이너리들도 문자열등을 그대로 사용
jake.dothome.co.kr
'Kernel > 理解' 카테고리의 다른 글
| Scatter Gather List (0) | 2024.04.10 |
|---|---|
| Block IO 분석 (0) | 2024.03.24 |
| Device Tree 문법 (0) | 2024.02.02 |
| ZRAM 분석 (0) | 2024.02.02 |
| ELF 실행 & execve (0) | 2024.02.02 |