QUESTION
언제 BLS 항목이 발견·선별되고, 언제 부팅 데이터가 만들어지는가?
blspec_scan_directory()는 loader/entries/*.conf의 일반 파일을 찾고, blspec_entry_open()은 각 파일을 속성으로 읽는다. 이때 kernel/initrd/DTB의 경로를 bootm_data에 넣지는 않는다.
중복 경로·DT 호환성·설정된 machine-id를 검사한 항목만 정렬 목록에 넣는다. boot 명령이나 메뉴가 항목을 실행할 때 blspec_boot()이 경로와 옵션을 조합하고 bootm_entry()에 위임한다.
STRUCTURE
설정 속성과 bootm_data는 생성 시점이 다르다
발견과 목록화
- scanner가 파일·디렉터리·장치 입력을 탐색
- 파일 내용을 임시 DT 속성 node에 파싱
- 호환성 필터 후 정렬된
bootentry추가
선택된 항목의 실행
boot_entry()가entry.boot호출blspec_boot()이 단일 kernel/initrd/DTB 경로 구성bootm_entry()가 image 준비·handler 호출
entry.rootpath는 디렉터리 스캔에 전달된 root를 사용한다. 장치를 마운트해 스캔하면 mount root가 전달된다. 파일을 직접 지정하면 가장 깊은 /loader/entries/ 앞부분을 찾고, 없으면 mount root를 쓴다. 설정 파일의 부모 디렉터리와 무조건 결합하지 않는다.
CALL PATH
발견 → 파싱 → 필터·정렬 → 선택 → bootm
*.conf 일반 파일 또는 직접 지정한 .conf 경로blspec_compare()는 BLS provider 내부 목록의 표시·시도 순서를 정한다. 어떤 항목을 실제로 시도할지는 boot 명령의 소스 순회와 메뉴 선택이 결정한다.
STATE LEDGER
각 단계에서 확정되는 상태
| 단계 | 읽는 입력 | 그때 확정되는 상태 |
|---|---|---|
| 발견 | name 또는 mount root | 디렉터리의 loader/entries/*.conf 중 일반 파일; 직접 파일 입력은 .conf 접미사 확인 |
| 파싱 | read_file(configname) | 임시 node에 key/value 저장; 반복 options는 연결, sort-key는 별도 캐시 |
| 후보 필터 | config path·rootpath·DT·machine-id | 동일 경로 거부; DT가 비호환 또는 설정된 machine-id와 불일치하면 목록 제외 |
| 정렬 목록 | blspec_compare() | sort-key 유무/값 → machine-id → version 또는 파일명 비교; provider 목록에 삽입 |
| 항목 실행 | entry.boot callback | rootpath와 linux/initrd/DT 경로 결합, options 전달, bootm_entry 호출 |
VISUAL WALKTHROUGH
후보가 제외되는 곳과 파일 경로가 소비되는 곳
blspec_have_entry()가 같은 entry.path를 발견하면 -EEXIST.global.boot.machine_id가 설정되면 entry의 machine-id가 같아야 한다.이 필터는 커널 파일의 존재 여부를 미리 확인하는 단계가 아니다. linux가 가리키는 파일은 실행 경로에서 bootm이 열게 된다.
data.os_file은 실행 때 만들며, 이 파일이 bootm의 필수 입력이다.linux-appendroot를 bool로 검사한 뒤 bootm 준비·override·handler에 넘긴다.그림의 경로는 파일시스템 경로다. RAM 주소 배치와 커널 진입은 bootm의 이미지 handler 경계에서 결정된다.
UPSTREAM SOURCE
원본 코드
아래 코드는 Barebox v2026.06.1의 common/blspec.c에서 1-88줄을 그대로 가져온 것이다. 설명을 위해 실제 코드를 가짜 의사 코드로 바꾸지 않았다.
1// SPDX-License-Identifier: GPL-2.0-or-later
2#define pr_fmt(fmt) "blspec: " fmt
3
4#include <environment.h>
5#include <globalvar.h>
6#include <firmware.h>
7#include <malloc.h>
8#include <fcntl.h>
9#include <libfile.h>
10#include <libbb.h>
11#include <init.h>
12#include <bootm.h>
13#include <glob.h>
14#include <fs.h>
15#include <of.h>
16#include <linux/stat.h>
17#include <linux/list.h>
18#include <linux/err.h>
19#include <uapi/spec/dps.h>
20#include <boot.h>
21
22#include <bootscan.h>
23
24struct blspec_entry {
25 struct bootentry entry;
26
27 struct device_node *node;
28 struct cdev *cdev;
29 const char *rootpath;
30 /* configpath is entry.path */
31 char *sortkey;
32};
33
34/*
35 * blspec_entry_var_get - get the value of a variable
36 */
37static const char *blspec_entry_var_get(struct blspec_entry *entry, const char *name)
38{
39 const char *str;
40 int ret;
41
42 ret = of_property_read_string(entry->node, name, &str);
43
44 return ret ? NULL : str;
45}
46
47/*
48 * blspec_entry_var_set - set a variable to a value
49 */
50static int blspec_entry_var_set(struct blspec_entry *entry, const char *name,
51 const char *val)
52{
53 return of_set_property(entry->node, name, val,
54 val ? strlen(val) + 1 : 0, 1);
55}
56
57static int blspec_overlay_fixup(struct device_node *root, void *ctx)
58{
59 struct blspec_entry *entry = ctx;
60 const char *overlays;
61 char *overlay;
62 char *sep, *freep;
63
64 overlays = blspec_entry_var_get(entry, "devicetree-overlay");
65
66 sep = freep = xstrdup(overlays);
67
68 while ((overlay = strsep(&sep, " "))) {
69 char *path;
70
71 if (!*overlay)
72 continue;
73
74 path = basprintf("%s/%s", entry->rootpath, overlay);
75
76 of_overlay_apply_file(root, path, false);
77
78 free(path);
79 }
80
81 free(freep);
82
83 return 0;
84}
85
86/*
87 * blspec_boot - boot an entry
88 *
READING CONTRACT
첫 발췌와 보충 발췌의 한계
첫 발췌 L1–88은 struct blspec_entry와 속성 접근·overlay fixup까지다. 보충 발췌 L83–187은 blspec_boot()이 kernel·선택적 initrd/DTB 경로를 만들고 bootm_entry()를 호출하는 부분이다.
파일 검색·파싱·필터·정렬은 같은 태그의 common/blspec.c L202–699, 장치 mount 경계는 common/bootscan.c, 실제 항목 순회·메뉴 선택은 commands/boot.c를 함께 확인한다.
RELATED SOURCE
함께 읽어야 하는 원본 코드
첫 코드 조각만으로 동작이 완성되지 않는 경우 호출 매크로, 객체 정의와 실제 실행 목록을 같은 페이지에 묶었다. 각 조각은 같은 기준 commit에서 가져왔다.
01. BLS entry의 kernel, initrd, DTB를 bootm에 전달
common/blspec.c 83-187줄이다.
83 return 0;
84}
85
86/*
87 * blspec_boot - boot an entry
88 *
89 * This boots an entry. On success this function does not return.
90 * In case of an error the error code is returned. This function may
91 * return 0 in case of a successful dry run.
92 */
93static int blspec_boot(struct bootentry *be, int verbose, int dryrun)
94{
95 struct blspec_entry *entry = container_of(be, struct blspec_entry, entry);
96 int ret;
97 const char *abspath, *devicetree, *options, *initrd, *linuximage;
98 const char *overlays;
99 const char *appendroot;
100 char *old_fws, *fws;
101 struct bootm_data data = {};
102
103 bootm_data_init_defaults(&data);
104 data.dryrun = max_t(int, dryrun, data.dryrun);
105 data.os_file = data.oftree_file = data.initrd_file = NULL;
106 data.verbose = max(verbose, data.verbose);
107
108 devicetree = blspec_entry_var_get(entry, "devicetree");
109 initrd = blspec_entry_var_get(entry, "initrd");
110 options = blspec_entry_var_get(entry, "options");
111 linuximage = blspec_entry_var_get(entry, "linux");
112 overlays = blspec_entry_var_get(entry, "devicetree-overlay");
113
114 if (entry->rootpath)
115 abspath = entry->rootpath;
116 else
117 abspath = "";
118
119 data.os_file = basprintf("%s/%s", abspath, linuximage);
120
121 if (devicetree)
122 data.oftree_file = basprintf("%s/%s", abspath, devicetree);
123
124 if (overlays)
125 of_register_fixup(blspec_overlay_fixup, entry);
126
127 if (initrd)
128 data.initrd_file = basprintf("%s/%s", abspath, initrd);
129
130 globalvar_add_simple("linux.bootargs.dyn.bootentries", options);
131
132 appendroot = blspec_entry_var_get(entry, "linux-appendroot");
133 if (appendroot) {
134 int val;
135
136 ret = strtobool(appendroot, &val);
137 if (ret) {
138 pr_err("Invalid value \"%s\" for appendroot option\n",
139 appendroot);
140 goto err_out;
141 }
142 data.appendroot = val;
143 }
144
145 pr_info("booting %s from %s\n", blspec_entry_var_get(entry, "title"),
146 (entry->cdev && entry->cdev->dev) ?
147 dev_name(entry->cdev->dev) : "none");
148
149 of_overlay_set_basedir(abspath);
150
151 old_fws = firmware_get_searchpath();
152 if (old_fws && *old_fws)
153 fws = basprintf("%s/lib/firmware:%s", abspath, old_fws);
154 else
155 fws = basprintf("%s/lib/firmware", abspath);
156 firmware_set_searchpath(fws);
157 free(fws);
158
159 ret = bootm_entry(be, &data);
160 if (ret)
161 pr_err("Booting failed\n");
162
163 if (overlays)
164 of_unregister_fixup(blspec_overlay_fixup, entry);
165
166 of_overlay_set_basedir("/");
167 firmware_set_searchpath(old_fws);
168 free(old_fws);
169
170err_out:
171 free((char *)data.oftree_file);
172 free((char *)data.initrd_file);
173 free((char *)data.os_file);
174
175 return ret;
176}
177
178static void blspec_entry_free(struct bootentry *be)
179{
180 struct blspec_entry *entry = container_of(be, struct blspec_entry, entry);
181
182 of_delete_node(entry->node);
183 free_const(entry->rootpath);
184 free(entry->sortkey);
185 free(entry);
186}
187
DETAILS
파싱·선별·부팅을 분리해서 읽기
파일 줄을 속성으로 변환
read_file()로 파일을 읽고 줄의 첫 공백 또는 탭 앞을 이름, 뒤를 값으로 쓴다. 첫 문자가 #인 행은 건너뛴다. 반복 options는 이전 값 뒤에 새 값을 공백으로 붙인다. 반면 initrd를 반복하면 마지막 속성 값으로 덮어쓴다. 여러 initrd 파일을 연결하는 구현은 아니다.
필터는 bootm 검사와 다름
DT 속성이 없거나 none이면 호환 판정을 통과한다. 전역 machine_id가 없으면 해당 필터는 통과한다. 검사 실패 항목은 목록에 넣기 전에 해제한다.
정렬은 provider 내부에서 수행
양쪽 모두 sort-key가 있으면 그 값을 사전식 오름차순으로 비교하고, 같을 때 machine-id 오름차순, 이어 version 내림차순으로 비교한다. 여기서 버전이 같거나 없어도 파일명으로 다시 비교하지 않는다. strverscmp()는 없는 버전을 빈 문자열로 취급한다.
한쪽에만 sort-key가 있으면 그쪽이 먼저다. 양쪽 모두 없을 때만 초기값인 전체 entry.path를 버전 비교한다. 이 함수 안에는 디렉터리나 .conf 접미사를 제거하는 코드가 없다. 일반 BLS 규칙이나 소스 주석이 아니라 이 버전의 실제 분기를 기준으로 읽어야 한다.
실행 시 bootm 위임
선택한 bootentry의 callback에서 bootm_data를 만들고 linux와 선택적 initrd/DTB를 rootpath에 결합한다. bootm_entry()가 이미지 준비와 handler 호출을 맡는다.
속성 파싱 · 실제 정렬 분기 · 버전 비교의 빈 값 처리.
IMPLEMENTATION NOTES
경계에서 생기는 오해를 바로잡기
rootpath의 기준
디렉터리 스캔은 전달된 root를 사용한다. 파일 직접 스캔은 경로 속 가장 깊은 /loader/entries/ 앞부분을 쓰고, 없으면 get_mounted_path() 결과를 쓴다.
linux 필수 여부는 스캔에서 검사하지 않음
이 코드의 스캔 단계는 linux 속성의 유무나 실제 kernel 파일 존재를 검증하지 않는다. 잘못된 파일은 blspec_boot()과 bootm_entry()를 거치며 드러난다.
부팅 소스 순서와 정렬 순서는 별도
bootentry_create_from_name()은 각 provider에게 빈 목록을 주고 생성 결과를 합친다. 보통의 boot 명령은 BOOTSRC를 순서대로 탐색하고 각 항목을 시도하며, -l은 목록, -m은 메뉴를 사용한다.
OBJECT LIFETIME
속성과 경로의 소유권
| 대상 | 생성 / 소비 | 반환·해제 경계 |
|---|---|---|
| config file buffer | blspec_entry_open()의 read_file | 속성 복사 후 함수 말미에서 free |
| blspec_entry와 node | 파싱 뒤 bootentry 목록에 추가 | 필터 실패 시 즉시 해제; 목록 해제 시 release callback |
| rootpath·entry.path | 스캔 시 mount root/설정 파일 경로 저장 | 목록의 항목 해제 때 각각 blspec_entry_free와 bootentries_free에서 free_const |
| bootm_data 경로 문자열 | blspec_boot()에서 생성 | err_out에서 os/DT/initrd 문자열 free |
| overlay·firmware 설정 | bootm_entry 호출 전에 설정 | bootm_entry 반환 뒤 fixup 해제, overlay 기준 경로는 /로 설정, firmware 검색 경로는 이전 값으로 복원 |
잘못된 linux-appendroot 값 때문에 일찍 반환하면 err_out으로 바로 이동한다. 그 전에 overlay fixup을 등록했다면 해제 코드를 건너뛴다. 모든 반환 경로가 같은 정리를 수행한다고 가정하면 안 된다. overlay 기준 경로도 이전 값을 저장해 복구하는 방식이 아니다.
FAILURE PATH
목록화 실패와 부팅 실패를 구별
| 조건 | 경계 | 고정 소스의 결과 |
|---|---|---|
| 디렉터리에서 .conf 아님 / 일반 파일 아님 | 발견 | glob 대상이 아니거나 stat 검사에서 건너뜀 |
| 동일 config path / 읽기 실패 | 파싱 전·입구 | -EEXIST 또는 read_file 오류; 목록에 추가하지 않음 |
| 비호환 DT / 다른 machine-id | 후보 필터 | entry 해제 후 -ENODEV; 목록에서 제외 |
| 잘못된 linux-appendroot | blspec_boot | strtobool 오류 후 임시 경로 해제; bootm_entry 호출 전 종료. overlay fixup을 등록했다면 이 분기는 해제 코드를 건너뜀 |
| kernel 파일·handler 문제 | bootm_entry | bootm의 준비 또는 handler 오류 반환; blspec가 Booting failed 출력 |
디렉터리 스캔은 개별 config의 음수 반환을 누적하지 않고 성공 개수를 반환한다. boot -l에서 빠진 항목과 boot 때 실패한 항목을 로그에서 따로 확인한다.
EVIDENCE
소스 밖에서 확인할 증거
이 글에서는 고정 버전 소스와 웹페이지를 검증했다. Barebox를 새로 빌드하거나 보드에서 부팅하지 않았다. 아래 명령과 실험은 대상 장치에서 추가로 확인할 절차다.
| 목적 | 관찰 | 판정 |
|---|---|---|
| 후보 목록 | boot -l <BOOTSRC> | config별 항목 표시와 정렬 순서 확인 |
| 필터 | 두 .conf에 다른 machine-id 또는 DTB 사용 | global.boot.machine_id 설정 여부에 따른 목록 차이 기록 |
| 실행 경계 | boot -d -v <BOOTSRC> | 선택한 entry와 bootm 파일 경로·반환값 대조 |
| 동작 위치 | nm barebox에서 blspec_boot·blspec_scan_directory 검색 | 설정과 최적화에 따라 symbol이 남는지 확인 |
LAB
후보와 실행 단계를 따로 재현
- 01동일 경로와 두 버전의 목록 비교
loader/entries에 두 .conf를 두고 boot -l로 sort-key 유무, version, 파일명에 따른 순서를 기록한다.
- 02필터만 변경
전역 machine-id를 설정·해제하거나 DTB compatible을 바꾸고 후보 목록에서 제외되는 시점을 비교한다.
- 03경로 소비와 오류 비교
linux 파일 경로를 잘못 지정하고 목록에는 남는지, 실행 때 bootm_entry가 어떤 오류를 돌려주는지 확인한다.
PRIMARY REFERENCES