← Documents Documentation/devicetree/of_unittest.rst GitHub 원문 ↗

Linux 6.18.37 · Devicetree

Open Firmware Devicetree Unittest

OF 단위 테스트 데이터를 커널 이미지에 빌드하고 unflatten한 뒤 live tree에 연결·제거하는 child/sibling 포인터 동작을 설명합니다.

Source pathDocumentation/devicetree/of_unittest.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약과 해설

of_unittest.rst:1-224

OF 단위 테스트 데이터를 커널 이미지에 빌드하고 unflatten한 뒤 live tree에 연결·제거하는 child/sibling 포인터 동작을 설명합니다. 영어 원문 전체와 한국어 전문 번역을 함께 제공하며 함수명, symbol, source path, 코드와 원문 줄 좌표를 보존하고 세 ASCII 트리를 구조화 도식으로 다시 그립니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 =================================
4 Open Firmware Devicetree Unittest
5 =================================
6
7 Author: Gaurav Minocha <[email protected]>
8
9 1. Introduction
10 ===============
11
12 This document explains how the test data required for executing OF unittest
13 is attached to the live tree dynamically, independent of the machine's
14 architecture.
15
16 It is recommended to read the following documents before moving ahead.
17
18 (1) Documentation/devicetree/usage-model.rst
19 (2) http://www.devicetree.org/Device_Tree_Usage
20
21 OF Selftest has been designed to test the interface (include/linux/of.h)
22 provided to device driver developers to fetch the device information..etc.
23 from the unflattened device tree data structure. This interface is used by
24 most of the device drivers in various use cases.
25
26
27 2. Verbose Output (EXPECT)
28 ==========================
29
30 If unittest detects a problem it will print a warning or error message to
31 the console. Unittest also triggers warning and error messages from other
32 kernel code as a result of intentionally bad unittest data. This has led
33 to confusion as to whether the triggered messages are an expected result
34 of a test or whether there is a real problem that is independent of unittest.
35
36 'EXPECT \ : text' (begin) and 'EXPECT / : text' (end) messages have been
37 added to unittest to report that a warning or error is expected. The
38 begin is printed before triggering the warning or error, and the end is
39 printed after triggering the warning or error.
40
41 The EXPECT messages result in very noisy console messages that are difficult
42 to read. The script scripts/dtc/of_unittest_expect was created to filter
43 this verbosity and highlight mismatches between triggered warnings and
44 errors vs expected warnings and errors. More information is available
45 from 'scripts/dtc/of_unittest_expect --help'.
46
47
48 3. Test-data
49 ============
50
51 The Device Tree Source file (drivers/of/unittest-data/testcases.dts) contains
52 the test data required for executing the unit tests automated in
53 drivers/of/unittest.c. See the content of the folder::
54
55 drivers/of/unittest-data/tests-*.dtsi
56
57 for the Device Tree Source Include files (.dtsi) included in testcases.dts.
58
59 When the kernel is built with CONFIG_OF_UNITTEST enabled, then the following make
60 rule::
61
62 $(obj)/%.dtb: $(src)/%.dts FORCE
63 $(call if_changed_dep, dtc)
64
65 is used to compile the DT source file (testcases.dts) into a binary blob
66 (testcases.dtb), also referred as flattened DT.
67
68 After that, using the following rule the binary blob above is wrapped as an
69 assembly file (testcases.dtb.S)::
70
71 $(obj)/%.dtb.S: $(obj)/%.dtb
72 $(call cmd, dt_S_dtb)
73
74 The assembly file is compiled into an object file (testcases.dtb.o), and is
75 linked into the kernel image.
76
77
78 3.1. Adding the test data
79 -------------------------
80
81 Un-flattened device tree structure:
82
83 Un-flattened device tree consists of connected device_node(s) in form of a tree
84 structure described below::
85
86 // following struct members are used to construct the tree
87 struct device_node {
88 ...
89 struct device_node *parent;
90 struct device_node *child;
91 struct device_node *sibling;
92 ...
93 };
94
95 Figure 1, describes a generic structure of machine's un-flattened device tree
96 considering only child and sibling pointers. There exists another pointer,
97 ``*parent``, that is used to traverse the tree in the reverse direction. So, at
98 a particular level the child node and all the sibling nodes will have a parent
99 pointer pointing to a common node (e.g. child1, sibling2, sibling3, sibling4's
100 parent points to root node)::
101
102 root ('/')
103 |
104 child1 -> sibling2 -> sibling3 -> sibling4 -> null
105 | | | |
106 | | | null
107 | | |
108 | | child31 -> sibling32 -> null
109 | | | |
110 | | null null
111 | |
112 | child21 -> sibling22 -> sibling23 -> null
113 | | | |
114 | null null null
115 |
116 child11 -> sibling12 -> sibling13 -> sibling14 -> null
117 | | | |
118 | | | null
119 | | |
120 null null child131 -> null
121 |
122 null
123
124 Figure 1: Generic structure of un-flattened device tree
125
126
127 Before executing OF unittest, it is required to attach the test data to
128 machine's device tree (if present). So, when selftest_data_add() is called,
129 at first it reads the flattened device tree data linked into the kernel image
130 via the following kernel symbols::
131
132 __dtb_testcases_begin - address marking the start of test data blob
133 __dtb_testcases_end - address marking the end of test data blob
134
135 Secondly, it calls of_fdt_unflatten_tree() to unflatten the flattened
136 blob. And finally, if the machine's device tree (i.e. live tree) is present,
137 then it attaches the unflattened test data tree to the live tree, else it
138 attaches itself as a live device tree.
139
140 attach_node_and_children() uses of_attach_node() to attach the nodes into the
141 live tree as explained below. To explain the same, the test data tree described
142 in Figure 2 is attached to the live tree described in Figure 1::
143
144 root ('/')
145 |
146 testcase-data
147 |
148 test-child0 -> test-sibling1 -> test-sibling2 -> test-sibling3 -> null
149 | | | |
150 test-child01 null null null
151
152
153 Figure 2: Example test data tree to be attached to live tree.
154
155 According to the scenario above, the live tree is already present so it isn't
156 required to attach the root('/') node. All other nodes are attached by calling
157 of_attach_node() on each node.
158
159 In the function of_attach_node(), the new node is attached as the child of the
160 given parent in live tree. But, if parent already has a child then the new node
161 replaces the current child and turns it into its sibling. So, when the testcase
162 data node is attached to the live tree above (Figure 1), the final structure is
163 as shown in Figure 3::
164
165 root ('/')
166 |
167 testcase-data -> child1 -> sibling2 -> sibling3 -> sibling4 -> null
168 | | | | |
169 (...) | | | null
170 | | child31 -> sibling32 -> null
171 | | | |
172 | | null null
173 | |
174 | child21 -> sibling22 -> sibling23 -> null
175 | | | |
176 | null null null
177 |
178 child11 -> sibling12 -> sibling13 -> sibling14 -> null
179 | | | |
180 null null | null
181 |
182 child131 -> null
183 |
184 null
185 -----------------------------------------------------------------------
186
187 root ('/')
188 |
189 testcase-data -> child1 -> sibling2 -> sibling3 -> sibling4 -> null
190 | | | | |
191 | (...) (...) (...) null
192 |
193 test-sibling3 -> test-sibling2 -> test-sibling1 -> test-child0 -> null
194 | | | |
195 null null null test-child01
196
197
198 Figure 3: Live device tree structure after attaching the testcase-data.
199
200
201 Astute readers would have noticed that test-child0 node becomes the last
202 sibling compared to the earlier structure (Figure 2). After attaching first
203 test-child0 the test-sibling1 is attached that pushes the child node
204 (i.e. test-child0) to become a sibling and makes itself a child node,
205 as mentioned above.
206
207 If a duplicate node is found (i.e. if a node with same full_name property is
208 already present in the live tree), then the node isn't attached rather its
209 properties are updated to the live tree's node by calling the function
210 update_node_properties().
211
212
213 3.2. Removing the test data
214 ---------------------------
215
216 Once the test case execution is complete, selftest_data_remove is called in
217 order to remove the device nodes attached initially (first the leaf nodes are
218 detached and then moving up the parent nodes are removed, and eventually the
219 whole tree). selftest_data_remove() calls detach_node_and_children() that uses
220 of_detach_node() to detach the nodes from the live device tree.
221
222 To detach a node, of_detach_node() either updates the child pointer of given
223 node's parent to its sibling or attaches the previous sibling to the given
224 node's sibling, as appropriate. That is it :)
225

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

Open Firmware Devicetree Unittest

1-8

이 문서는 Gaurav Minocha가 작성한 Open Firmware Devicetree 단위 테스트 설명서이며 `GPL-2.0` 라이선스가 적용됩니다.

테스트 목적과 사전 문서

9-25

이 문서는 시스템 아키텍처와 무관하게 OF 단위 테스트 실행에 필요한 테스트 데이터를 실행 중인 트리에 동적으로 연결하는 방법을 설명합니다.

계속하기 전에 `Documentation/devicetree/usage-model.rst`와 `http://www.devicetree.org/Device_Tree_Usage`를 읽는 것이 좋습니다.

OF Selftest는 장치 드라이버 개발자가 unflattened Device Tree 자료 구조에서 장치 정보 등을 가져올 때 사용하는 `include/linux/of.h` 인터페이스를 검사하도록 설계되었습니다. 이 인터페이스는 다양한 용도의 대다수 장치 드라이버에서 사용됩니다.

EXPECT를 이용한 상세 출력 구분

26-46

단위 테스트가 문제를 발견하면 콘솔에 경고나 오류 메시지를 출력합니다. 또한 의도적으로 잘못 만든 테스트 데이터 때문에 다른 커널 코드의 경고와 오류도 발생시킵니다. 이 때문에 출력된 메시지가 예상된 테스트 결과인지, 단위 테스트와 무관한 실제 문제인지 혼동할 수 있습니다.

단위 테스트는 경고나 오류가 예상됨을 알리기 위해 시작 메시지 `'EXPECT \ : text'`와 종료 메시지 `'EXPECT / : text'`를 사용합니다. 시작 메시지는 경고나 오류를 일으키기 전에, 종료 메시지는 일으킨 뒤에 출력합니다.

EXPECT 메시지는 콘솔 출력을 매우 장황하고 읽기 어렵게 만듭니다. `scripts/dtc/of_unittest_expect` 스크립트는 이 출력을 걸러 내고 실제로 발생한 경고·오류와 예상한 경고·오류 사이의 불일치를 강조합니다. 자세한 내용은 `scripts/dtc/of_unittest_expect --help`에서 확인할 수 있습니다.

DTS 테스트 데이터의 빌드와 링크

47-76

Device Tree Source 파일 `drivers/of/unittest-data/testcases.dts`에는 `drivers/of/unittest.c`가 자동 실행하는 단위 테스트 데이터가 들어 있습니다. `testcases.dts`에 포함되는 Device Tree Source Include 파일은 `drivers/of/unittest-data/tests-*.dtsi`에서 확인할 수 있습니다.

커널을 `CONFIG_OF_UNITTEST`가 활성화된 상태로 빌드하면 다음 Make 규칙이 `testcases.dts`를 binary blob인 `testcases.dtb`, 즉 flattened DT로 컴파일합니다.

$(obj)/%.dtb: $(src)/%.dts FORCE
        $(call if_changed_dep, dtc)

그 다음 아래 규칙은 이 binary blob을 어셈블리 파일 `testcases.dtb.S`로 감쌉니다.

$(obj)/%.dtb.S: $(obj)/%.dtb
        $(call cmd, dt_S_dtb)

어셈블리 파일은 object 파일 `testcases.dtb.o`로 컴파일되고 커널 이미지에 링크됩니다.

Unflattened Device Tree 자료 구조

77-94

unflattened Device Tree는 트리 형태로 연결된 `device_node`들로 구성됩니다. 트리를 만드는 데 사용하는 핵심 멤버는 `parent`, `child`, `sibling` 포인터입니다.

// following struct members are used to construct the tree
struct device_node {
    ...
    struct  device_node *parent;
    struct  device_node *child;
    struct  device_node *sibling;
    ...
};

일반적인 child·sibling 트리

95-126

그림 1은 child와 sibling 포인터만 고려한 시스템의 일반적인 unflattened Device Tree 구조입니다. `parent` 포인터는 반대 방향 순회에 사용되며, 같은 레벨의 child와 모든 sibling은 공통 부모를 가리킵니다. 예를 들어 `child1`, `sibling2`, `sibling3`, `sibling4`의 부모는 모두 root입니다.

그림 1: 일반적인 unflattened Device Tree
root ('/')child1
child1sibling2
sibling2sibling3
sibling3sibling4
sibling4null (root level)
child1child11
child11sibling12
sibling12sibling13
sibling13sibling14
sibling14null (child1 level)
sibling13child131
child131null (child131 child)
sibling2child21
child21sibling22
sibling22sibling23
sibling23null (sibling2 level)
sibling3child31
child31sibling32
sibling32null (sibling3 level)

수평 연결은 sibling 순서를, 아래 연결은 각 노드의 첫 child를 나타냅니다. 모든 sibling은 해당 레벨의 공통 parent를 가리킵니다.

테스트 blob을 unflatten하고 live tree에 연결

127-143

OF 단위 테스트를 실행하기 전에 테스트 데이터를 시스템의 Device Tree가 있으면 그 트리에 연결해야 합니다. `selftest_data_add()`는 먼저 커널 이미지에 링크된 flattened Device Tree 데이터를 다음 커널 심볼을 통해 읽습니다.

__dtb_testcases_begin - address marking the start of test data blob
__dtb_testcases_end   - address marking the end of test data blob

그 다음 `of_fdt_unflatten_tree()`를 호출하여 flattened blob을 unflatten합니다. 시스템의 Device Tree, 즉 live tree가 있으면 unflatten한 테스트 데이터 트리를 live tree에 연결하고, 없으면 테스트 데이터 트리 자체를 live Device Tree로 설정합니다.

`attach_node_and_children()`은 `of_attach_node()`를 사용해 노드를 live tree에 연결합니다. 다음 그림 2의 테스트 데이터 트리를 앞서 본 그림 1의 live tree에 연결하는 과정으로 이를 설명합니다.

연결할 테스트 데이터 트리

144-154
그림 2: live tree에 연결할 테스트 데이터
root ('/')testcase-data
testcase-datatest-child0
test-child0test-sibling1
test-sibling1test-sibling2
test-sibling2test-sibling3
test-sibling3null (testcase-data level)
test-child0test-child01

`testcase-data` 아래에서 `test-child0`이 첫 child이고 나머지는 sibling이며, `test-child01`은 `test-child0`의 child입니다.

of_attach_node() 적용 후의 live tree

155-200

이 시나리오에서는 live tree가 이미 있으므로 root `/` 노드를 다시 연결할 필요가 없습니다. 나머지 각 노드에는 `of_attach_node()`를 호출합니다.

`of_attach_node()`는 새 노드를 지정된 부모의 child로 연결합니다. 부모에게 이미 child가 있으면 새 노드가 현재 child를 대체하고, 기존 child는 새 노드의 sibling이 됩니다. 따라서 `testcase-data`를 그림 1의 live tree에 연결하면 다음 그림 3과 같은 구조가 됩니다.

그림 3: testcase-data 연결 후의 live Device Tree
root ('/')testcase-data
testcase-datachild1
child1sibling2
sibling2sibling3
sibling3sibling4
sibling4null (root level)
testcase-datatest-sibling3
test-sibling3test-sibling2
test-sibling2test-sibling1
test-sibling1test-child0
test-child0null (testcase-data level)
test-child0test-child01
child1child11
child11sibling12
sibling12sibling13
sibling13sibling14
sibling14null (child1 level)
sibling13child131
sibling2child21
child21sibling22
sibling22sibling23
sibling3child31
child31sibling32

root의 새 첫 child는 `testcase-data`이고 기존 `child1`부터의 트리는 그 sibling으로 유지됩니다. 테스트 child를 차례로 앞에 삽입하므로 sibling 순서는 역순이 됩니다.

sibling 순서와 중복 노드 처리

201-212

그림 2와 비교하면 그림 3에서는 `test-child0`가 마지막 sibling이 됩니다. 먼저 `test-child0`을 연결한 뒤 `test-sibling1`을 연결하면, 앞에서 설명한 규칙에 따라 기존 child인 `test-child0`가 sibling으로 밀리고 `test-sibling1`이 새 child가 됩니다. 이 과정이 반복되어 순서가 뒤집힙니다.

동일한 `full_name` 속성을 가진 노드가 live tree에 이미 있어 중복 노드가 발견되면 새 노드를 연결하지 않습니다. 대신 `update_node_properties()`를 호출하여 그 속성을 live tree의 기존 노드에 갱신합니다.

테스트 데이터 제거

213-224

테스트 케이스 실행이 끝나면 처음 연결한 장치 노드를 제거하기 위해 `selftest_data_remove()`를 호출합니다. leaf 노드를 먼저 분리한 다음 부모 방향으로 올라가며 노드를 제거하여 결국 전체 테스트 트리를 없앱니다.

`selftest_data_remove()`는 `detach_node_and_children()`을 호출하고, 이 함수는 `of_detach_node()`를 사용해 live Device Tree에서 노드를 분리합니다.

노드를 분리할 때 `of_detach_node()`는 상황에 따라 지정된 노드 부모의 child 포인터를 그 노드의 sibling으로 갱신하거나, 이전 sibling을 지정된 노드의 sibling에 연결합니다.