ROM부터 init까지 — 임베디드 부팅 단계의 빈자리 분석
#한 줄 요약
“부트로더는 아무것도 동작하지 않는 CPU를 커널이 동작할 수 있는 환경으로 만드는 다리입니다.” — DDR이 없고, 클럭이 안 잡혀 있고, 페리페럴이 죽어 있고, 파일 시스템이 보이지 않는 상태에서 그 모든 것을 차례로 깨우는 일을 합니다.
전원을 인가한 직후의 ARM Cortex-A 코어는 거의 아무것도 할 수 없습니다. 32KB의 on-chip SRAM 안에서 명령 한 줄씩 실행할 수 있을 뿐, DDR도 없고, MMC controller도 없고, ethernet도 없습니다. 이 상태에서 수십 MB짜리 커널 이미지를 어떻게 적재하고, 4GB의 가상 메모리를 어떻게 매핑하며, 시스템콜이 동작하는 user space까지 어떻게 도달할까요. 부트로더는 그 간격을 채우는 코드입니다.
#전원이 들어온 직후의 CPU
ARM64 CPU가 reset signal을 받으면 reset vector로 점프합니다. ARMv8의 경우 EL3(secure monitor)에서 시작하고, reset vector 주소는 SoC 설계에 따라 고정되어 있습니다.
BootROM은 SoC 출고 시 mask ROM에 굳어진 코드입니다. 우리가 수정할 수 없습니다. BootROM은 boot mode strap pin을 읽어 어디서 다음 단계를 가져올지 정한 뒤, SoC 내부 SRAM에 수십 KB짜리 작은 이미지를 적재해 점프합니다.
이 작은 이미지가 부트로더의 첫 단계입니다. U-Boot에서는 SPL(Secondary Program Loader), ARMv8 secure boot에서는 BL1·BL2라고 부릅니다.
#부트로더 없이는 안 되는 이유
“커널이 직접 동작하면 되지 않나”라는 질문에 답해야 합니다. 답은 커널 이미지가 RAM에 적재되어 있어야 동작하는데, 그 RAM이 아직 존재하지 않는다는 것입니다.
부트로더가 책임지는 일은 네 가지입니다.
#1. DDR 초기화
DDR controller는 복잡한 상태 기계입니다. PHY training, ZQ calibration, refresh interval 설정, write/read leveling을 수십 단계로 진행해야 비로소 읽고 쓸 수 있는 메모리가 됩니다. 이 시퀀스가 끝나기 전에는 0x80000000 같은 DDR 주소를 읽으면 hang합니다.
Ch 9: DRAM 초기화에서 자세히 다룹니다.
#2. 부트 미디어 추상화
커널 이미지는 eMMC, SD, NAND, SPI flash, USB, ethernet 중 어느 매체에서든 올 수 있습니다. 커널은 자신이 어디서 왔는지 모르는 채로 동작합니다. 부트로더가 해당 매체의 controller를 깨우고, 파일 시스템을 마운트하거나 raw block을 읽어, 메모리에 형태가 맞는 이미지를 펼쳐 놓습니다.
#3. 커널 로딩 ABI
ARM64 커널은 PC 주소가 어디든 동작하지만, 진입 시점에 특정 레지스터에 특정 값이 들어 있어야 합니다.
ARM64 Linux 커널 진입 요구사항은 다음과 같습니다.
x0= physical address of device tree blob (DTB)x1= 0 (reserved)x2= 0 (reserved)x3= 0 (reserved)- MMU off, D-cache off, I-cache on (optional)
- EL2 권장 (KVM 사용을 위해)
- 모든 CPU가 secondary boot 진입점에 wait
부트로더가 이 ABI를 정확히 맞춰 커널로 점프합니다. 한 가지라도 어긋나면 커널은 첫 줄도 출력하지 못하고 죽습니다.
#4. 환경 변수와 부트 정책
“어느 파티션에서 부팅할까”, “kernel command line은 무엇인가”, “rootfs는 어디 있는가”를 누군가 결정해야 합니다. 부트로더가 영구 저장소에 저장된 환경 변수를 읽어 bootargs를 조립하고, A/B 슬롯 중 어느 쪽이 활성인지를 판단합니다.
#전체 부트 체인
전원 인가부터 user space의 init까지를 한눈에 보면 다음과 같습니다.
각 단계가 다음 단계를 메모리에 적재하고 점프하는 수직 인계 구조입니다. 한 단계가 죽으면 다음 단계는 시작도 못 합니다.
#시리즈에서 다룰 보드
추상적인 설명만으로는 부트로더가 손에 잡히지 않습니다. 시리즈 전체에서 세 가지 보드를 예로 듭니다.
| 보드 | SoC | 아키텍처 | 특징 |
|---|---|---|---|
| QEMU virt | virt machine | ARMv8-A | 가상, JTAG 없이 실험에 적합 |
| BeagleBone Black | TI AM335x | ARMv7-A | 단순한 부트 흐름, SPL 학습에 좋음 |
| NXP i.MX 8M Plus EVK | i.MX 8M Plus | ARMv8-A | 실제 산업용, TF-A + U-Boot |
make qemu_arm64_defconfig로 빌드한 U-Boot은 QEMU virt에서 몇 초 안에 부팅합니다. 학습 사이클이 짧습니다. 실제 하드웨어로 가기 전에 QEMU에서 흐름을 익히는 것을 권합니다.
# QEMU virt에서 U-Boot 실행qemu-system-aarch64 -M virt -cpu cortex-a53 -nographic \ -bios u-boot.binU-Boot 2024.04 (May 19 2026 - 09:00:00 +0000)
DRAM: 128 MiBCore: 35 devices, 14 uclasses, devicetree: boardFlash: 64 MiBLoading Environment from Flash... *** Warning - bad CRC, using defaultIn: serial,usbkbdOut: serial,vidconsoleErr: serial,vidconsoleNet: eth0: virtio-net#32Hit any key to stop autoboot: 0=>이 한 줄(=>)에 도달하면 시리즈의 반은 이미 이해한 것입니다.
#아키텍처별 차이
ARMv7, ARMv8-A, RISC-V는 부트 흐름의 세부가 다릅니다. 시리즈는 ARMv8-A를 기준으로 하되, 다른 아키텍처는 차이만 짚습니다.
#ARMv7-A
- 단일 권한 모드(monitor mode가 선택)
- BL31 같은 secure monitor가 옵션
- U-Boot SPL → U-Boot Proper → Linux의 단순한 흐름이 일반적
#ARMv8-A
- EL0(user), EL1(kernel), EL2(hypervisor), EL3(secure monitor)
- 부트 시작은 EL3
- TF-A가 BL1·BL2·BL31을 담당, U-Boot은 BL33으로 동작
- PSCI를 통해 secondary CPU를 깨움
#RISC-V
- M-mode(machine), S-mode(supervisor), U-mode(user)
- OpenSBI가 M-mode firmware로 자리잡음
- U-Boot은 OpenSBI 위에서 S-mode로 동작
- ARMv8-A의 TF-A와 비슷한 위치
Ch 4: 부트 단계에서 각 단계별 책임을 정리합니다.
#부트 시간의 한계
산업용 시스템은 부트 시간이 곧 사용자 경험입니다. 자동차 인포테인먼트는 2초 안에 후방 카메라가 떠야 합니다. 산업용 HMI는 1초 안에 user space가 동작해야 합니다.
전형적인 부트 시간 분포(i.MX 8M Plus, 2GB DDR, eMMC)는 다음과 같습니다.
| 구간 | 단계 | 비고 |
|---|---|---|
| 0.0 ~ 0.3 s | BootROM + SPL | DDR training이 절반 |
| 0.3 ~ 1.0 s | U-Boot Proper | driver probe + 환경 변수 |
| 1.0 ~ 1.5 s | bootcmd 실행 | 커널 적재 |
| 1.5 ~ 3.5 s | Linux kernel | driver probe |
| 3.5 ~ 5.0 s | systemd / user space |
U-Boot Proper는 시간 도둑입니다. SPL이 커널을 직접 부트하는 Falcon Mode가 양산용 옵션입니다.
#자주 하는 오해
#”BootROM도 우리가 수정할 수 있다”
수정 불가능합니다. mask ROM입니다. SoC 출고 후에는 어떤 방법으로도 바뀌지 않습니다. fuse를 통해 동작 정책은 바꿀 수 있지만 코드 자체는 못 바꿉니다.
#”부트로더는 단일 바이너리다”
요즘 SoC의 부트로더는 항상 다단입니다. SPL → U-Boot Proper만 해도 두 단계이고, ARMv8-A는 BL1·BL2·BL31·BL33으로 네 단계가 흔합니다.
#”커널이 부트 미디어를 직접 읽을 수 있다”
커널은 RAM에 적재된 상태에서 시작합니다. 부트 미디어에서 자신을 읽어 오는 능력이 없습니다. 부트로더가 반드시 커널을 RAM에 펼쳐 놓아야 합니다.
#”DDR 초기화는 BIOS가 한다”
x86은 BIOS/UEFI가 합니다. 임베디드는 SPL의 보드 코드가 직접 합니다. NXP DDR Tool, TI K3 DDR config 같은 vendor 도구가 training parameter를 뽑아 주고, 그 값을 우리가 짠 코드가 DDR controller 레지스터에 씁니다.
#정리
- 부트로더는 아무것도 동작하지 않는 CPU를 커널이 동작할 수 있는 환경으로 만드는 다리입니다.
- BootROM은 SoC mask ROM이고 수정 불가능합니다. boot mode strap을 읽어 다음 단계를 SRAM에 적재합니다.
- 부트로더가 책임지는 일은 DDR 초기화, 부트 미디어 추상화, 커널 로딩 ABI 정합, 환경 변수와 부트 정책입니다.
- 전체 부트 체인은 BootROM → SPL → BL31 → U-Boot Proper → Linux → user space의 수직 인계 구조입니다.
- ARM64 커널은 진입 시점에 x0에 DTB physical address, MMU off, D-cache off를 요구합니다. ABI 한 줄이라도 어긋나면 커널이 시작 못 합니다.
- ARMv7-A, ARMv8-A, RISC-V는 세부가 다르지만 큰 흐름은 같습니다. 시리즈는 ARMv8-A를 기준으로 합니다.
- QEMU virt에서
qemu-system-aarch64 -bios u-boot.bin으로 몇 초 안에 학습 사이클을 돌릴 수 있습니다. - 부트 시간은 산업용에서 곧 사용자 경험입니다. U-Boot Proper를 건너뛰는 Falcon Mode가 양산 옵션입니다.
#다음 편
Ch 2: U-Boot의 위치 — Das U-Boot, TF-A, EDK II에서는 임베디드 부트로더 생태계의 분업표를 정리합니다. U-Boot이 어디서 일하고, TF-A·EDK II·LinuxBoot가 어떤 자리를 차지하는지 봅니다.
#관련 항목
Bootloader Internals · 1 of 37
- 1ROM부터 init까지 — 임베디드 부팅 단계의 빈자리 분석
- 2Das U-Boot vs TF-A vs EDK II — 임베디드 부트로더 생태계 비교
- 3U-Boot 빌드 시스템 분석 — Kconfig·Makefile·defconfig 동작 추적
- 4ARM 임베디드 부트 4단계 분해 — BL1·SPL·TPL·U-Boot Proper의 역할
- 5U-Boot Falcon Mode — SPL이 U-Boot Proper 없이 커널 직접 부팅
- 6Device Tree DTB 부트로더 처리 — 로딩 시점과 fixup 메커니즘 추적
- 7U-Boot Driver Model 내부 — uclass·driver·device 추상화 구조
- 8U-Boot 보드 초기화 시퀀스 — board_init_f와 board_init_r 분리 이유
- 9DDR Controller 프로그래밍과 PHY Training — SPL의 가장 어려운 작업
- 10임베디드 스토리지 부팅 분석 — MMC·SCSI·NAND·SPI Flash 비교
- 11임베디드 네트워크 부팅 — TFTP·PXE·BOOTP 시퀀스 분석
- 12U-Boot USB 부팅 — fastboot·UMS·USB host 메커니즘
- 13U-Boot 환경 변수와 bootcmd — 부팅 시나리오 정의하기
- 14Modern U-Boot bootflow / bootmeth — 새 추상화 레이어 분석
- 15FIT image 구조 분석 — multi-image·hash·configuration 추적
- 16U-Boot Verified Boot — RSA 서명과 public key 임베딩 흐름
- 17임베디드 A/B 부팅 이중화 — OTA 안전성을 위한 부트 슬롯 설계
- 18U-Boot의 EFI 호환 분석 — bootefi 명령과 EFI loader 동작 원리
- 19Linux Boot ABI — ARM/ARM64 커널 진입 규약 추적
- 20임베디드 펌웨어 업데이트 — RAUC vs SWUpdate 비교
- 21새 보드 U-Boot 포팅 실전 — defconfig 작성부터 첫 부팅까지
- 22부트로더 디버깅 기법 — DEBUG·JTAG·serial·post-mortem 분석
- 23SoC BootROM·eFuse·OTP — 부팅의 0단계 분석
- 24SPL·TPL 내부 해부 — 가장 작은 부트 단계의 동작 추적
- 25ARM Trusted Firmware-A 통합 — BL1·BL2·BL31·BL32·BL33 흐름
- 26DDR Training과 PHY Calibration — 보드별 파라미터 튜닝
- 27임베디드 Chain of Trust — 다단계 서명 검증의 전체 흐름
- 28임베디드 Flash Layout 설계 — partition·NAND·eMMC·UBI 비교
- 29U-Boot Distro Boot — extlinux·boot.scr 표준화 분석
- 30부트로더 CI 구축 — build matrix와 자동 부팅 테스트
- 31TF-A BL31 EL3 Runtime 분석 — PSCI·SDEI·RAS dispatcher 추적
- 32PSCI와 SMCCC ABI — ARM 표준 SMC 호출 규약 분석
- 33ARM64 Secondary Core Bring-up — PSCI CPU_ON 호출부터 EL1 진입까지
- 34U-Boot PCIe Enumeration — 부트로더가 디바이스를 찾는 흐름 분석
- 35EFI·UEFI에서 CXL 초기화 — CEDT 생성과 HDM Decoder 사전 설정
- 36부트 시 메모리 토폴로지 결정 — DDR + CXL.mem 통합 인식
- 37UEFI Secure Boot 인증서 만료 — 2011→2023 CA 롤오버와 PQC 대비
관련 글
U-Boot PCIe Enumeration — 부트로더가 디바이스를 찾는 흐름 분석
U-Boot PCIe 열거 과정 — Root Complex 초기화·Config Space scan·BAR sizing·resource 할당, CXL DVSEC 인식까지.
U-Boot Distro Boot — extlinux·boot.scr 표준화 분석
보드별 다른 부트 스크립트를 표준화 — U-Boot Distro Boot, extlinux.conf, boot.scr의 차이와 선택.
SPL·TPL 내부 해부 — 가장 작은 부트 단계의 동작 추적
SPL과 TPL의 정확한 역할, SRAM 안에 들어가는 코드 구조, DDR이 없는 환경에서 어떻게 동작하는가.
이 글을 참조하는 글 (10)
- 부트 시 메모리 토폴로지 결정 — DDR + CXL.mem 통합 인식— Bootloader Internals
- ARM64 Secondary Core Bring-up — PSCI CPU_ON 호출부터 EL1 진입까지— Bootloader Internals
- SPL·TPL 내부 해부 — 가장 작은 부트 단계의 동작 추적— Bootloader Internals
- SoC BootROM·eFuse·OTP — 부팅의 0단계 분석— Bootloader Internals
- BSP OTA와 Field Recovery — A/B 슬롯·롤백·BootCount— BSP Development
- BSP 이미지 패키징 — Flash Layout·Partition·GPT 설계— BSP Development
- BSP 부트 시간 최적화 — Bootchart·initcall_debug·Parallel Init— BSP Development
- ARM 임베디드 부트 4단계 분해 — BL1·SPL·TPL·U-Boot Proper의 역할— Bootloader Internals
- U-Boot 빌드 시스템 분석 — Kconfig·Makefile·defconfig 동작 추적— Bootloader Internals
- Das U-Boot vs TF-A vs EDK II — 임베디드 부트로더 생태계 비교— Bootloader Internals