Buildroot U-Boot 통합 — 빌드·env·fw_env 흐름
#한 줄 요약
“U-Boot도 한 트리 안에 둡니다.” — kernel·rootfs와 같은 toolchain·같은 git 트리에서 빌드돼야 시그니처·DTB·env offset이 어긋나지 않습니다. Buildroot는 U-Boot를 그냥 또 하나의 패키지처럼 다룹니다.
#왜 U-Boot까지 Buildroot가 빌드하는가
U-Boot를 별도 트리에서 빌드해 손으로 SD 카드에 옮기는 방식은 첫 prototype에서는 빠릅니다. 다만 양산이 가까워질수록 사고가 잦아집니다. toolchain이 다르면 SPL이 main U-Boot를 찾지 못하고, env offset이 다르면 fw_setenv가 엉뚱한 영역을 덮어씁니다.
Buildroot는 같은 트리·같은 toolchain·같은 commit으로 U-Boot까지 만듭니다. 산출물은 output/images/에 다른 산출물(zImage, rootfs.ext4)과 나란히 놓입니다. 한 트리를 clone하면 부트로더부터 application까지 완전히 재현 가능한 시스템이 됩니다.
#BR2_TARGET_UBOOT 옵션 한눈에
Bootloaders 메뉴 아래 U-Boot를 켜면 다음 트리가 열립니다. 자주 쓰는 것만 정리합니다.
| 옵션 | 의미 |
|---|---|
BR2_TARGET_UBOOT=y | U-Boot 빌드 활성화 |
BR2_TARGET_UBOOT_LATEST_VERSION=y | Buildroot가 기본 버전을 따라감 |
BR2_TARGET_UBOOT_CUSTOM_VERSION=y | 특정 버전 고정 (예: 2024.04) |
BR2_TARGET_UBOOT_CUSTOM_TARBALL=y | 외부 tarball |
BR2_TARGET_UBOOT_CUSTOM_GIT=y | 외부 git remote |
BR2_TARGET_UBOOT_BOARD_DEFCONFIG | tree 안의 defconfig 이름 |
BR2_TARGET_UBOOT_USE_CUSTOM_CONFIG=y | 외부 .config 파일 사용 |
BR2_TARGET_UBOOT_CUSTOM_FRAGMENT_FILES | 추가 fragment(여러 개 가능) |
BR2_TARGET_UBOOT_PATCH | 적용할 patch 파일 / 디렉터리 |
BR2_TARGET_UBOOT_FORMAT_* | 산출물 형식 (bin, img, kwb, imx 등) |
BR2_TARGET_UBOOT_SPL=y | SPL을 함께 빌드 |
BR2_TARGET_UBOOT_SPL_NAME | SPL 산출물 이름 (MLO, u-boot-spl.bin) |
BR2_TARGET_UBOOT_BOOT_SCRIPT=y | boot.scr 자동 생성 |
BR2_TARGET_UBOOT_BOOT_SCRIPT_SOURCE | boot.cmd의 경로 |
대표적인 설정은 다음과 같습니다.
BR2_TARGET_UBOOT=yBR2_TARGET_UBOOT_CUSTOM_VERSION=yBR2_TARGET_UBOOT_CUSTOM_VERSION_VALUE="2024.04"BR2_TARGET_UBOOT_BOARD_DEFCONFIG="am335x_evm"BR2_TARGET_UBOOT_FORMAT_BIN=yBR2_TARGET_UBOOT_FORMAT_IMG=yBR2_TARGET_UBOOT_SPL=yBR2_TARGET_UBOOT_SPL_NAME="MLO u-boot-spl.bin"BR2_TARGET_UBOOT_BOOT_SCRIPT=yBR2_TARGET_UBOOT_BOOT_SCRIPT_SOURCE="board/myboard/boot.cmd"각 항목은 다음 절에서 풀어 설명합니다.
#Source location 4가지
Source 토글로 어디서 U-Boot 소스를 가져올지를 정합니다. 네 가지 선택지가 있습니다.
BR2_TARGET_UBOOT_LATEST_VERSION=y # Buildroot가 권장하는 mainline 버전BR2_TARGET_UBOOT_CUSTOM_VERSION=y # 직접 명시BR2_TARGET_UBOOT_CUSTOM_TARBALL=y # 외부 tarball URLBR2_TARGET_UBOOT_CUSTOM_GIT=y # 외부 git remote각 선택의 사용 기준은 다음과 같습니다.
| 선택 | 적합한 경우 |
|---|---|
| Latest | 학습·prototyping·mainline 보드 |
| Custom version | ”2024.04”처럼 특정 mainline 버전을 고정 |
| Custom tarball | vendor가 mirror로 배포하는 고정 tarball |
| Custom git | vendor fork (NXP/TI/Xilinx의 u-boot-fslc, ti-u-boot 등) |
vendor fork는 보통 git을 씁니다.
BR2_TARGET_UBOOT_CUSTOM_GIT=yBR2_TARGET_UBOOT_CUSTOM_REPO_URL="https://github.com/nxp-imx/uboot-imx"BR2_TARGET_UBOOT_CUSTOM_REPO_VERSION="lf_v2024.04"vendor-specific DDR PHY init, eMMC tuning, secure boot ROM API 같은 코드가 fork 안에 들어 있습니다. 가능하면 mainline을 쓰되 기능이 빠진 부분만 fork를 쓰는 게 장기 유지보수에 유리합니다.
#defconfig — 시작점 선택
U-Boot config 시스템도 Linux와 똑같이 defconfig + fragments를 씁니다. 트리 안의 defconfig를 쓰는 가장 일반적인 방식.
BR2_TARGET_UBOOT_BOARD_DEFCONFIG="rpi_4"이렇게 두면 빌드 시 make rpi_4_defconfig가 실행되고, 같은 트리(configs/rpi_4_defconfig)의 파일이 baseline이 됩니다.
기존 defconfig가 맞지 않으면 완전히 외부의 .config를 던질 수 있습니다.
BR2_TARGET_UBOOT_USE_CUSTOM_CONFIG=yBR2_TARGET_UBOOT_CUSTOM_CONFIG_FILE="board/myboard/uboot.config"또는 baseline은 트리 defconfig를 쓰되 덧붙이는 fragment만 외부에서 줄 수 있습니다. Ch 12의 kernel customize와 같은 패턴입니다.
BR2_TARGET_UBOOT_BOARD_DEFCONFIG="rpi_4"BR2_TARGET_UBOOT_CUSTOM_FRAGMENT_FILES="board/myboard/uboot-extra.config"uboot-extra.config의 내용은 평범한 Kconfig 단편입니다.
CONFIG_BOOTDELAY=1CONFIG_USE_BOOTCOMMAND=yCONFIG_BOOTCOMMAND="run distro_bootcmd"CONFIG_ENV_IS_IN_MMC=yCONFIG_SYS_MMC_ENV_DEV=0CONFIG_ENV_OFFSET=0xC0000CONFIG_ENV_SIZE=0x2000CONFIG_CMD_FS_GENERIC=ydefconfig 자체를 고치지 않고 fragment로만 덧붙이는 게 유지보수에 좋습니다. U-Boot 버전을 올려 defconfig가 바뀌어도 fragment는 그대로 적용됩니다.
#산출물 — bin / img / SPL / FIT
U-Boot는 보드·SoC ROM 부트 방식에 따라 여러 형식을 산출합니다. BR2_TARGET_UBOOT_FORMAT_*로 어떤 것을 만들지 고릅니다.
| 산출물 | 용도 |
|---|---|
u-boot.bin | 가장 단순한 raw binary. 직접 jump 시작 주소가 정확히 맞을 때 |
u-boot.img | U-Boot legacy header가 붙은 형식. 일부 보드의 SPL이 이걸 찾음 |
MLO / u-boot-spl.bin | SPL (Secondary Program Loader) — TI AM335x 등의 ROM이 요구 |
SPL | NXP i.MX, Rockchip 등의 SPL |
u-boot.itb | FIT image — secure boot 시그니처 포함 가능 |
u-boot.kwb | Marvell Kirkwood/Armada |
u-boot.imx | NXP i.MX legacy IVT header |
u-boot.sb | Freescale/NXP Vybrid |
산출물은 모두 output/images/에 떨어집니다.
$ ls output/images/MLO u-boot.img u-boot.binboot.scr Image sun50i-h6-orangepi-3.dtbrootfs.ext4 rootfs.tar복수의 산출물을 동시에 켤 수 있고, post-image script가 이들을 모아 SD 카드 이미지를 만드는 게 일반적입니다. SD 카드 이미지 생성은 Ch 15에서 다룹니다.
#boot.scr와 uEnv.txt — boot 명령 자동 생성
U-Boot는 보드 부팅 시 어떤 명령을 실행할지를 SD 카드의 boot.scr 또는 uEnv.txt에서 읽습니다. 이걸 직접 작성하는 게 깔끔합니다.
boot.scr은 서명된 binary script입니다. Buildroot가 자동으로 만들어 줍니다.
BR2_TARGET_UBOOT_BOOT_SCRIPT=yBR2_TARGET_UBOOT_BOOT_SCRIPT_SOURCE="board/myboard/boot.cmd"boot.cmd는 사람이 읽는 평문 스크립트입니다.
setenv bootargs "console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait"load mmc 0:1 ${kernel_addr_r} Imageload mmc 0:1 ${fdt_addr_r} sun50i-h6-orangepi-3.dtbbooti ${kernel_addr_r} - ${fdt_addr_r}빌드 시 Buildroot가 mkimage로 변환해 output/images/boot.scr를 만듭니다. SD 카드의 boot partition에 넣어 두면 U-Boot가 자동으로 실행합니다.
uEnv.txt는 서명 없는 평문 env입니다. Buildroot가 자동 생성하지는 않지만 post-image script로 넣어 두는 경우가 많습니다.
bootdelay=1bootcmd=load mmc 0:1 ${kernel_addr_r} Image; load mmc 0:1 ${fdt_addr_r} board.dtb; booti ${kernel_addr_r} - ${fdt_addr_r}bootargs=console=ttyS0,115200 root=/dev/mmcblk0p2 rootwaitboot.scr은 서명·CRC가 들어가 불의의 텍스트 손상을 막아 줍니다. 양산은 boot.scr을, 디버깅·prototyping은 uEnv.txt를 쓰는 게 일반적입니다.
booti는 ARM64, bootm은 uImage, bootz는 zImage용입니다. 보드 architecture에 맞게 선택하면 됩니다.
#env 위치 — 플래시·eMMC·SD·UBI
U-Boot env는 재부팅 사이에 살아남아야 할 변수 집합입니다. 어디에 저장할지는 CONFIG_ENV_IS_IN_* 옵션 하나로 정합니다.
| 옵션 | 저장소 | 짝지을 옵션 |
|---|---|---|
CONFIG_ENV_IS_IN_MMC | eMMC / SD | CONFIG_SYS_MMC_ENV_DEV, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE |
CONFIG_ENV_IS_IN_SPI_FLASH | SPI NOR | CONFIG_ENV_OFFSET, CONFIG_ENV_SECT_SIZE |
CONFIG_ENV_IS_IN_NAND | raw NAND | CONFIG_ENV_OFFSET, CONFIG_ENV_RANGE |
CONFIG_ENV_IS_IN_UBI | UBI volume | CONFIG_ENV_UBI_PART, CONFIG_ENV_UBI_VOLUME |
CONFIG_ENV_IS_IN_FAT | FAT 파일 | CONFIG_ENV_FAT_INTERFACE, CONFIG_ENV_FAT_DEVICE_AND_PART, CONFIG_ENV_FAT_FILE |
CONFIG_ENV_IS_NOWHERE | 휘발성 | 매 부팅마다 default 사용 |
가장 흔한 eMMC 예.
CONFIG_ENV_IS_IN_MMC=yCONFIG_SYS_MMC_ENV_DEV=0 # mmc 0번 디바이스CONFIG_SYS_MMC_ENV_PART=1 # boot partition 1CONFIG_ENV_OFFSET=0xC0000 # boot partition 안에서 768KB offsetCONFIG_ENV_SIZE=0x2000 # 8KBCONFIG_ENV_OFFSET_REDUND=0xC2000 # 이중화 (선택)이 값들은 *나중에 userspace fw_env.config*와 정확히 일치해야 합니다. 한 글자만 어긋나면 사고가 납니다.
#fw_env.config — userspace에서 env 접근
런타임에 application이 fw_setenv kernel_version 6.6.0처럼 U-Boot env를 갱신해야 하는 경우가 흔합니다. OTA 업데이트, A/B 슬롯 전환, factory reset flag 등이 예입니다. libubootenv 또는 BusyBox의 fw_printenv/fw_setenv가 이걸 처리합니다.
Buildroot에서 켜는 방법.
BR2_PACKAGE_LIBUBOOTENV=y # 권장 — newer, FIT 지원# 또는BR2_PACKAGE_UBOOT_TOOLS_FW_PRINTENV=y # legacy런타임에 어디를 읽을지를 알려 주는 게 /etc/fw_env.config입니다. 파일 한 줄당 한 device를 정의합니다.
# <device> <offset> <env-size> <sector-size> <num-sectors>/dev/mmcblk0boot1 0xC0000 0x2000 0x200/dev/mmcblk0boot1 0xC2000 0x2000 0x200각 필드의 의미는 다음과 같습니다.
| 필드 | 의미 |
|---|---|
<device> | env가 저장된 block device 경로 |
<offset> | device 안에서의 byte offset (U-Boot의 CONFIG_ENV_OFFSET과 일치) |
<env-size> | env 영역 크기 (CONFIG_ENV_SIZE와 일치) |
<sector-size> | erase block 크기. MMC/SD는 보통 0x200(512), NOR은 0x10000(64KB) |
<num-sectors> | NAND에서만. 보통 생략 |
두 줄을 적으면 primary + redundant 이중화입니다. U-Boot가 CONFIG_ENV_OFFSET_REDUND를 쓰는 경우 반드시 두 줄을 적어야 합니다. 한 줄이면 redundant 영역이 갱신 안 되어 오래된 env로 부팅하는 사고가 납니다.
런타임 사용 예.
$ fw_printenv bootcmdbootcmd=run distro_bootcmd
$ fw_setenv slot B$ fw_setenv bootcount 0$ fw_setenv -- bootargs "console=ttyS0,115200 root=/dev/mmcblk0p3 rw"fw_setenv NAME (값 없이)으로 삭제, fw_setenv NAME VALUE로 갱신입니다. --는 값에 -가 들어가는 경우의 escape입니다.
#SPL과 main U-Boot의 분업
대부분의 modern SoC는 두 단계 부팅을 합니다. SoC ROM이 작은 SPL을 SRAM에 로드해 실행하고, SPL이 DRAM을 초기화한 뒤 main U-Boot를 DRAM에 로드합니다.
[SoC ROM] ↓ load 32 ~ 128 KB[SPL — SRAM] - DDR PHY init - clock tree setup - basic console ↓ load 500 KB ~ 1 MB[main U-Boot — DRAM] - 전체 device 트리 - filesystem, network, USB - bootcmd 실행 ↓[Linux kernel]각 단계의 책임은 다음과 같습니다.
| 단계 | 책임 |
|---|---|
| SoC ROM | 보드 출하 시 mask programmed. SD/eMMC/SPI에서 SPL 로드 |
| SPL | DDR init, clock init, console UART, main U-Boot 로드 |
| main U-Boot | env, filesystem, ethernet, USB, FIT, bootcmd |
| Linux | 진짜 시스템 |
Buildroot로 SPL을 함께 빌드하려면 다음 두 옵션이 필요합니다.
BR2_TARGET_UBOOT_SPL=yBR2_TARGET_UBOOT_SPL_NAME="MLO u-boot-spl.bin"BR2_TARGET_UBOOT_SPL_NAME은 어떤 산출물 이름을 SPL로 인식할지 알려 주는 힌트입니다. 보드마다 이름이 다릅니다. TI는 MLO, NXP i.MX는 u-boot-spl.bin, Rockchip은 idbloader.img입니다. 보드 README나 Documentation/board/<vendor>/<board>.rst에서 확인합니다.
#흔한 실수
U-Boot 통합에서 가장 자주 만나는 문제 다섯 가지입니다.
env offset/size mismatch. U-Boot CONFIG_ENV_OFFSET=0xC0000인데 fw_env.config에 0x100000이라고 적은 경우. fw_printenv가 읽으면 CRC error로 default를 돌려주고, 쓰면 엉뚱한 영역(보통 rootfs 일부)을 덮어씁니다. 변경한 쪽에서 반드시 두 파일을 같이 갱신해야 합니다.
FIT signature 불일치. secure boot 환경에서 FIT image (u-boot.itb 또는 fitImage)는 U-Boot의 embedded public key로 검증됩니다. 빌드 시 사용한 keys/ 디렉터리와 U-Boot DTB에 embed된 key가 다르면 “signature missing” 또는 *“hash error”*로 부팅 실패합니다.
DTB가 잘못 임베디드. U-Boot의 u-boot.dtb와 kernel DTB는 다른 파일입니다. 같다고 가정하면 U-Boot가 보드 인식 못 함 또는 kernel이 peripheral을 못 찾는 사고가 납니다. 두 개를 분리해서 관리하는 게 안전합니다.
BR2_TARGET_UBOOT_SPL 미설정. AM335x에 BR2_TARGET_UBOOT_SPL=y를 안 켜면 u-boot.bin만 만들어집니다. SD 카드에 굽고 부팅해도 ROM이 SPL을 못 찾아 콘솔에 아무 것도 안 나옵니다. 보드 부팅이 SoC ROM 단계에서 멈춰 보이면 SPL 관련 옵션을 먼저 의심합니다.
mkimage 부재로 boot.scr 생성 실패. BR2_TARGET_UBOOT_BOOT_SCRIPT=y가 켜져 있으면 Buildroot가 자동으로 host의 mkimage를 빌드합니다. host build가 실패하면 boot.scr이 안 만들어지고 조용히 skip되는 경우가 있습니다. output/host/bin/mkimage가 존재하는지 확인하는 게 빠른 sanity check입니다.
env partition 권한. Linux에서 /dev/mmcblk0boot1은 보통 read-only입니다. fw_setenv가 쓰기 실패하면 /sys/block/mmcblk0boot1/force_ro에 0을 써서 해제하거나 부팅 시 udev rule로 자동 처리합니다.
#정리
- U-Boot를 Buildroot 트리 안에 두면 toolchain·DTB·env offset이 자동으로 정렬됩니다.
- Source는 latest / version / tarball / git 네 가지. vendor fork는 보통 git을 씁니다.
- Defconfig는 트리 내 defconfig + 외부 fragment 조합이 유지보수에 유리합니다.
- 산출물은
u-boot.bin/u-boot.img/ SPL / FIT 등 보드별로 다양합니다. ROM이 요구하는 형식을 정확히 켜야 합니다. boot.scr은 서명·CRC가 들어간 boot script. 양산용.uEnv.txt는 평문, 디버깅용입니다.- env 저장 위치는
CONFIG_ENV_IS_IN_*하나로 결정됩니다. eMMC·NOR·NAND·UBI·FAT·휘발성 6가지가 있습니다. - 런타임 접근은
fw_env.config+fw_printenv/fw_setenv. U-Boot 설정과 글자 단위로 일치해야 합니다. - SPL은 DDR/clock 초기화와 main U-Boot 로드를 책임집니다.
BR2_TARGET_UBOOT_SPL=y와 보드별 SPL 이름이 짝지어야 합니다.
#다음 장 예고
다음 편은 Ch 14: 빌드 캐싱 — ccache, BR2_CCACHE, per-package directories. 30 ~ 50분짜리 toolchain·U-Boot·kernel 재빌드를 분 단위로 줄이는 캐싱 전략을 다룹니다.
#관련 항목
- Ch 7: 보드 customize — board 디렉터리·post-image — boot.cmd, fw_env.config 배치
- Ch 11: Toolchain 선택 — internal vs external — U-Boot와 kernel이 공유할 toolchain
- Ch 12: Linux 커널 customize — defconfig fragment와 DTS — fragment 패턴은 U-Boot와 동일
- Ch 16: OTA 업데이트 — SWUpdate·RAUC·Mender 통합 — A/B 슬롯 env 변수 관리
- Bootloader 시리즈 Ch 18: U-Boot env in flash — env 저장소와 redundant 동작 원리
- 원문 — Buildroot Manual §17: Customizing the bootloader
- 원문 — U-Boot Documentation
Buildroot Practical · 13 of 20
- 1Buildroot가 푸는 문제 — Yocto와의 핵심 차이 분석
- 2Buildroot 디렉터리 구조 분해 — board·configs·dl·output
- 3Buildroot Kconfig 설정 — menuconfig와 defconfig 작성
- 4Buildroot 첫 빌드 — QEMU에서 동작하는 시스템 만들기
- 5Buildroot 패키지 시스템 분석 — .mk와 Config.in 동작 추적
- 6Buildroot 외부 트리 — BR2_EXTERNAL 구성과 활용
- 7Buildroot 보드 Customize — overlay·post-build·post-image 흐름
- 8Buildroot 출력 파일시스템 — initramfs·squashfs·ext4·cpio 선택
- 9Buildroot 새 패키지 작성 — autotools·cmake·python 통합
- 10Buildroot 실전 — BeagleBone Black 시스템 처음부터 끝까지
- 11Buildroot Toolchain 선택 — Internal vs External 비교
- 12Buildroot 커널 Customize — defconfig fragment와 DTS 통합
- 13Buildroot U-Boot 통합 — 빌드·env·fw_env 흐름
- 14Buildroot 빌드 캐싱 분석 — dl·ccache·per-package
- 15Buildroot post-build·post-image 심화 — rootfs 최종 수정 흐름
- 16Buildroot OTA 이미지 업데이트 — RAUC·swupdate 통합
- 17Buildroot SDK 생성·배포 — make sdk와 application 워크플로
- 18Buildroot Security·CVE 추적 — pkg-stats와 Reproducible Builds
- 19Buildroot CI/CD 구축 — Container Build와 Cache 공유
- 20Buildroot → Yocto 마이그레이션 — 언제·어떻게 옮길까
관련 글
Buildroot → Yocto 마이그레이션 — 언제·어떻게 옮길까
Buildroot가 한계에 도달하는 신호와 Yocto/OE로 점진 이전하는 패턴, meta-buildroot 같은 hybrid 옵션.
Buildroot CI/CD 구축 — Container Build와 Cache 공유
GitLab/GitHub Actions에서 Buildroot 트리를 컨테이너로 빌드하고 dl·ccache를 팀이 공유하는 패턴.
Buildroot Security·CVE 추적 — pkg-stats와 Reproducible Builds
Buildroot의 CVE 추적·legal info 산출·SBOM·reproducible build로 보안과 컴플라이언스를 관리하는 패턴.