본문으로 건너뛰기
Bootloader Internals · 1/37

ROM부터 init까지 — 임베디드 부팅 단계의 빈자리 분석

· Hawk · 8분 읽기

#한 줄 요약

“부트로더는 아무것도 동작하지 않는 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 설계에 따라 고정되어 있습니다.

Reset 직후 흐름 — 전원 → POR(PMIC rail + PLL lock + CPU deassert) → Reset Vector(SoC 고정 주소) → BootROM(boot mode strap에 따라 다음 단계 적재)

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까지를 한눈에 보면 다음과 같습니다.

전체 부트 체인 — POR → BootROM → SPL/BL1+BL2 → BL31(TF-A) → U-Boot Proper/BL33 → Linux Kernel → user space, 각 단계가 다음 단계를 메모리에 적재하고 점프

각 단계가 다음 단계를 메모리에 적재하고 점프하는 수직 인계 구조입니다. 한 단계가 죽으면 다음 단계는 시작도 못 합니다.

#시리즈에서 다룰 보드

추상적인 설명만으로는 부트로더가 손에 잡히지 않습니다. 시리즈 전체에서 세 가지 보드를 예로 듭니다.

보드SoC아키텍처특징
QEMU virtvirt machineARMv8-A가상, JTAG 없이 실험에 적합
BeagleBone BlackTI AM335xARMv7-A단순한 부트 흐름, SPL 학습에 좋음
NXP i.MX 8M Plus EVKi.MX 8M PlusARMv8-A실제 산업용, TF-A + U-Boot

make qemu_arm64_defconfig로 빌드한 U-Boot은 QEMU virt에서 몇 초 안에 부팅합니다. 학습 사이클이 짧습니다. 실제 하드웨어로 가기 전에 QEMU에서 흐름을 익히는 것을 권합니다.

Terminal window
# QEMU virt에서 U-Boot 실행
qemu-system-aarch64 -M virt -cpu cortex-a53 -nographic \
-bios u-boot.bin
U-Boot 2024.04 (May 19 2026 - 09:00:00 +0000)
DRAM: 128 MiB
Core: 35 devices, 14 uclasses, devicetree: board
Flash: 64 MiB
Loading Environment from Flash... *** Warning - bad CRC, using default
In: serial,usbkbd
Out: serial,vidconsole
Err: serial,vidconsole
Net: eth0: virtio-net#32
Hit 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 sBootROM + SPLDDR training이 절반
0.3 ~ 1.0 sU-Boot Properdriver probe + 환경 변수
1.0 ~ 1.5 sbootcmd 실행커널 적재
1.5 ~ 3.5 sLinux kerneldriver probe
3.5 ~ 5.0 ssystemd / 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

  1. 1ROM부터 init까지 — 임베디드 부팅 단계의 빈자리 분석
  2. 2Das U-Boot vs TF-A vs EDK II — 임베디드 부트로더 생태계 비교
  3. 3U-Boot 빌드 시스템 분석 — Kconfig·Makefile·defconfig 동작 추적
  4. 4ARM 임베디드 부트 4단계 분해 — BL1·SPL·TPL·U-Boot Proper의 역할
  5. 5U-Boot Falcon Mode — SPL이 U-Boot Proper 없이 커널 직접 부팅
  6. 6Device Tree DTB 부트로더 처리 — 로딩 시점과 fixup 메커니즘 추적
  7. 7U-Boot Driver Model 내부 — uclass·driver·device 추상화 구조
  8. 8U-Boot 보드 초기화 시퀀스 — board_init_f와 board_init_r 분리 이유
  9. 9DDR Controller 프로그래밍과 PHY Training — SPL의 가장 어려운 작업
  10. 10임베디드 스토리지 부팅 분석 — MMC·SCSI·NAND·SPI Flash 비교
  11. 11임베디드 네트워크 부팅 — TFTP·PXE·BOOTP 시퀀스 분석
  12. 12U-Boot USB 부팅 — fastboot·UMS·USB host 메커니즘
  13. 13U-Boot 환경 변수와 bootcmd — 부팅 시나리오 정의하기
  14. 14Modern U-Boot bootflow / bootmeth — 새 추상화 레이어 분석
  15. 15FIT image 구조 분석 — multi-image·hash·configuration 추적
  16. 16U-Boot Verified Boot — RSA 서명과 public key 임베딩 흐름
  17. 17임베디드 A/B 부팅 이중화 — OTA 안전성을 위한 부트 슬롯 설계
  18. 18U-Boot의 EFI 호환 분석 — bootefi 명령과 EFI loader 동작 원리
  19. 19Linux Boot ABI — ARM/ARM64 커널 진입 규약 추적
  20. 20임베디드 펌웨어 업데이트 — RAUC vs SWUpdate 비교
  21. 21새 보드 U-Boot 포팅 실전 — defconfig 작성부터 첫 부팅까지
  22. 22부트로더 디버깅 기법 — DEBUG·JTAG·serial·post-mortem 분석
  23. 23SoC BootROM·eFuse·OTP — 부팅의 0단계 분석
  24. 24SPL·TPL 내부 해부 — 가장 작은 부트 단계의 동작 추적
  25. 25ARM Trusted Firmware-A 통합 — BL1·BL2·BL31·BL32·BL33 흐름
  26. 26DDR Training과 PHY Calibration — 보드별 파라미터 튜닝
  27. 27임베디드 Chain of Trust — 다단계 서명 검증의 전체 흐름
  28. 28임베디드 Flash Layout 설계 — partition·NAND·eMMC·UBI 비교
  29. 29U-Boot Distro Boot — extlinux·boot.scr 표준화 분석
  30. 30부트로더 CI 구축 — build matrix와 자동 부팅 테스트
  31. 31TF-A BL31 EL3 Runtime 분석 — PSCI·SDEI·RAS dispatcher 추적
  32. 32PSCI와 SMCCC ABI — ARM 표준 SMC 호출 규약 분석
  33. 33ARM64 Secondary Core Bring-up — PSCI CPU_ON 호출부터 EL1 진입까지
  34. 34U-Boot PCIe Enumeration — 부트로더가 디바이스를 찾는 흐름 분석
  35. 35EFI·UEFI에서 CXL 초기화 — CEDT 생성과 HDM Decoder 사전 설정
  36. 36부트 시 메모리 토폴로지 결정 — DDR + CXL.mem 통합 인식
  37. 37UEFI Secure Boot 인증서 만료 — 2011→2023 CA 롤오버와 PQC 대비