U-Boot
고친 사람 github-actions[bot]
U-Boot 는 공유기 같은 임베디드 장치가 켜질 때 커널을 찾아 메모리에 올리고 실행을 넘겨줍니다. 막 켜진 보드에서 메모리와 장치를 쓸 수 있게 만드는 일부터 직접 맡습니다. 수많은 칩과 보드를 코드 하나로 받치는 오픈 소스 부트로더입니다. 켜지는 도중에 멈춰 세우고 명령을 쳐서 부팅을 손으로 다룰 수도 있습니다.
쉽고 빠른 이해
U-Boot 는 작은 보드가 켜질 때 운영체제를 불러와 실행을 넘겨줍니다. 공유기 전원을 넣으면 운영체제가 뜨기 전에 잠깐 도는 프로그램이 대개 이런 것입니다.
이런 프로그램이 필요한 까닭은 작은 보드에는 개인용 컴퓨터처럼 하드웨어를 미리 준비해 주는 프로그램이 없기 때문입니다. 칩에 새겨진 코드는 아주 작은 프로그램 하나를 올리는 데서 멈춥니다. 메모리를 쓸 수 있게 만드는 일부터 누군가 해야 합니다.
어떻게 도나:
- 칩에 새겨진 코드가 U-Boot 의 작은 앞부분을 올립니다
- 앞부분이 메모리를 쓸 수 있게 만들고 U-Boot 본체를 올립니다
- 본체가 저장 장치나 네트워크에서 운영체제를 가져와 실행을 넘깁니다
대가는 보드마다 따로 맞춰 빌드해야 한다는 것입니다. 다른 보드용으로 빌드한 것을 넣으면 그 보드는 켜지지 않습니다.
상세
이 절은 먼저 부트로더가 무엇이고 U-Boot 가 어떤 기계에서 도는지 짚습니다. 이어서 보드가 켜지는 순서를 따라가며 U-Boot 가 맡는 일을 봅니다. 그다음 콘솔에서 부팅을 다루는 법과 서명·되돌리기로 부팅을 지키는 법을 봅니다. 끝에서 백엔드 개발자가 U-Boot 를 만나는 때와 이름의 유래를 짚습니다.
부트로더는 컴퓨터를 켤 때 운영체제의 핵심인 커널을 메모리에 올리고 제어를 넘기는 프로그램입니다. 제어를 넘긴다는 것은 다음에 실행할 명령어를 커널 쪽 코드로 옮긴다는 뜻입니다. 넘긴 뒤로 U-Boot 는 다시 돌지 않습니다.
U-Boot 가 주로 올리는 커널은 리눅스 커널입니다. 다른 운영체제나 운영체제 없는 프로그램 하나를 올리는 데도 씁니다.
U-Boot 의 정식 이름은 Das U-Boot 입니다. 앞의 U 는 Universal(범용)을 뜻합니다. 한 종류의 칩이 아니라 여러 칩과 보드를 받치겠다는 이름입니다. ARM · RISC-V · MIPS 같은 여러 CPU(Central Processing Unit, 중앙 처리 장치) 계열에서 돕니다.
개인용 컴퓨터의 부트로더와 다른 점
개인용 컴퓨터와 서버에서는 메인보드의 펌웨어가 먼저 돕니다. 펌웨어는 하드웨어에 붙박이로 실려 나오는 소프트웨어입니다. 이 펌웨어가 메모리와 장치를 초기화한 뒤 부트로더를 올립니다. 초기화는 하드웨어를 쓸 수 있는 상태로 설정하는 일입니다.
이런 펌웨어의 대표가 UEFI(Unified Extensible Firmware Interface, 통합 확장 펌웨어 인터페이스)입니다. 요즘 개인용 컴퓨터와 서버는 대부분 이 펌웨어로 켜집니다.
UEFI 다음에 도는 부트로더의 대표는 GRUB(GRand Unified Bootloader)입니다. GRUB 은 이미 준비된 하드웨어 위에서 커널을 고르고 올리기만 하면 됩니다.
임베디드 보드의 두뇌는 대개 SoC(System on Chip, 시스템 온 칩)입니다. SoC 는 CPU 와 주변 장치를 칩 하나에 모은 부품입니다. 공유기나 스마트폰에 든 칩이 대개 SoC 입니다.
SoC 안에서 공장에서 새겨 넣은 작은 부팅 코드가 부트 ROM(Read-Only Memory, 읽기 전용 메모리)입니다. 부트 ROM 은 보드에 붙은 큰 메모리를 초기화하지 않습니다. 정해진 저장 장치에서 작은 프로그램 하나를 찾아 올리는 데까지만 합니다.
그래서 개인용 컴퓨터에서 펌웨어가 하던 준비를 U-Boot 가 대신 떠안습니다. 아래 표가 두 기계를 견줍니다.
| 개인용 컴퓨터 · 서버 | 임베디드 보드 | |
|---|---|---|
| 먼저 도는 것 | UEFI 같은 펌웨어 | 칩 안의 부트 ROM |
| 메모리를 초기화하는 것 | 펌웨어 | U-Boot |
| 부트로더 | GRUB 등 | U-Boot |
표의 가운데 줄이 둘을 가릅니다. U-Boot 는 펌웨어와 부트로더의 몫을 한 프로그램이 함께 집니다.
두 단계로 나눠 도는 까닭
막 켜진 보드에서 바로 쓸 수 있는 메모리는 SoC 안의 작은 SRAM(Static Random-Access Memory, 정적 램)뿐입니다. SRAM 은 켜자마자 쓸 수 있지만 크기가 아주 작습니다.
보드에 따로 붙은 큰 메모리는 DRAM(Dynamic Random-Access Memory, 동적 램)입니다. DRAM 은 속도와 신호 간격 같은 설정을 맞춰 준 뒤에야 쓸 수 있습니다. 이 설정을 맞추는 일이 DRAM 초기화입니다. 앞에서 메모리를 초기화한다고 한 일이 이것입니다.
U-Boot 본체는 SRAM 에 들어가기에 너무 큽니다. 그래서 U-Boot 는 자신을 둘로 나눠 빌드합니다. SRAM 에 들어가도록 작게 만든 앞부분이 SPL(Secondary Program Loader, 2차 프로그램 로더)입니다.
SPL 이 하는 일은 DRAM 을 초기화하고 U-Boot 본체를 DRAM 에 올리는 것입니다. 본체는 넓어진 메모리에서 나머지 일을 합니다.
flowchart TD
P["전원을 넣는다"] --> R
subgraph 칩["SoC 안"]
R["부트 ROM"]
S["SPL · SRAM 에서 돈다"]
end
subgraph 보드["보드에 붙은 DRAM"]
U["U-Boot 본체"]
K["커널"]
end
R -->|"저장 장치에서 읽어 올린다"| S
S -->|"DRAM 을 초기화하고 올린다"| U
U -->|"커널을 찾아 올리고 제어를 넘긴다"| K
U-Boot 가 SPL 과 본체 둘로 나뉘어서 부팅은 세 번 이어 올리기가 됩니다. 부트 ROM 이 SPL 을, SPL 이 본체를, 본체가 커널을 올립니다. 쓰는 메모리가 SRAM 에서 DRAM 으로 넘어가는 것이 SPL 단계입니다.
보드별 빌드
개인용 컴퓨터는 어떤 장치가 붙었는지를 펌웨어가 알아내 알려 줍니다. 임베디드 보드에는 그런 펌웨어가 없습니다. U-Boot 가 그 보드의 메모리 종류와 장치 목록을 미리 알고 있어야 합니다.
그래서 U-Boot 는 보드마다 설정 파일을 따로 두고 그 보드용으로 빌드합니다. 빌드는 보통 개발용 컴퓨터에서 합니다. 개발용 컴퓨터와 보드의 CPU 계열이 다르면 크로스 컴파일을 씁니다. 크로스 컴파일은 다른 CPU 에서 돌 실행 파일을 만드는 빌드입니다.
보드에 어떤 장치가 어느 주소에 붙었는지는 디바이스 트리에 적습니다. 디바이스 트리는 하드웨어 구성을 적은 파일입니다. U-Boot 는 이 파일로 자기가 쓸 장치를 알아냅니다. 같은 파일을 커널에도 넘겨줍니다.
대가는 이미지가 보드에 묶인다는 것입니다. 같은 칩을 쓴 보드라도 메모리가 다르면 따로 빌드해야 합니다. 맞지 않는 이미지를 넣으면 DRAM 초기화부터 실패해 보드가 켜지지 않습니다.
명령을 받는 콘솔
U-Boot 는 켜질 때 몇 초를 세며 기다립니다. 그 사이 키를 누르면 자동 부팅을 멈추고 명령을 받는 화면을 띄웁니다. 이 화면은 셸처럼 한 줄씩 명령을 받아 실행합니다.
이 화면은 보통 모니터가 아니라 시리얼 콘솔로 봅니다. 시리얼 콘솔은 보드의 직렬 통신 핀을 개발용 컴퓨터에 이어 글자를 주고받는 통로입니다.
임베디드 보드에는 화면도 키보드도 없을 때가 많습니다. 그래서 시리얼 콘솔이 보드와 이야기하는 첫 통로가 됩니다. 운영체제가 망가져 켜지지 않아도 U-Boot 까지는 여기서 닿습니다.
부팅을 정하는 환경 변수
U-Boot 는 이름과 값의 쌍을 모아 두고 부팅 방식을 거기서 읽습니다. 이 쌍 하나하나가 U-Boot 의 환경 변수입니다. 운영체제의 환경 변수와 이름이 같지만 U-Boot 가 따로 관리하는 목록입니다.
환경 변수 가운데 셋이 부팅을 정합니다.
| 변수 | 담는 것 |
|---|---|
bootdelay |
자동 부팅 전에 키 입력을 기다리는 초 |
bootcmd |
기다림이 끝나면 자동으로 실행할 명령 |
bootargs |
커널에 넘길 설정 문자열 |
setenv 로 값을 바꾸면 그 값은 이번 부팅에만 남습니다. saveenv 를 쳐야 플래시 메모리에 저장되어 다음에 켤 때도 남습니다. 플래시 메모리는 전원이 꺼져도 내용이 남는 저장 칩입니다. 임베디드 보드에서 디스크 몫을 합니다.
손으로 커널을 올려 보기
자동 부팅을 멈추고 커널을 직접 올리면 bootcmd 가 하는 일이 보입니다. 아래는 메모리 카드의 첫 파티션에서 64비트 ARM 커널을 올리는 예입니다.
=> setenv bootargs console=ttyS0 root=/dev/mmcblk0p2
=> load mmc 0:1 ${kernel_addr_r} Image
=> load mmc 0:1 ${fdt_addr_r} board.dtb
=> booti ${kernel_addr_r} - ${fdt_addr_r}
첫 줄은 커널에 넘길 설정을 정합니다. console=ttyS0 은 콘솔을 첫 번째 직렬 포트로 내라는 뜻입니다. root=/dev/mmcblk0p2 는 둘째 파티션을 루트 파일 시스템으로 쓰라는 뜻입니다.
둘째 줄과 셋째 줄은 메모리 카드 0번의 1번 파티션에서 파일을 읽어 DRAM 에 올립니다. ${kernel_addr_r} 와 ${fdt_addr_r} 는 커널과 디바이스 트리를 둘 메모리 주소를 담은 환경 변수입니다. 주소는 보드마다 다르므로 변수로 둡니다.
넷째 줄이 제어를 넘깁니다. booti 는 64비트 ARM 커널을 올리는 명령입니다. 가운데 - 는 초기 램디스크를 넘기지 않는다는 표시입니다. 초기 램디스크는 커널이 진짜 디스크를 읽기 전에 쓰는 작은 파일 모음입니다.
bootcmd 에 이 네 줄을 이어 적어 두면 켤 때마다 같은 일이 저절로 일어납니다.
커널을 가져오는 곳
U-Boot 는 커널을 여러 곳에서 가져옵니다. 보드에 붙은 플래시 메모리, 메모리 카드, USB(Universal Serial Bus) 저장 장치가 대표입니다. 메모리 카드나 USB 에서는 파일 시스템을 읽어 파일 이름으로 찾습니다.
네트워크에서 받아 올 수도 있습니다. 이때는 DHCP(Dynamic Host Configuration Protocol, 동적 호스트 구성 프로토콜)로 주소를 받습니다. 그다음 TFTP(Trivial File Transfer Protocol, 단순 파일 전송 프로토콜)로 서버에서 커널 파일을 내려받습니다. TFTP 는 인증도 디렉터리 탐색도 없이 파일 하나를 주고받는 간단한 프로토콜입니다.
네트워크로 올리기는 개발할 때 특히 쓸모가 있습니다. 개발용 컴퓨터에서 커널을 고쳐 빌드하면 보드를 껐다 켜기만 해도 새 커널로 뜹니다. 메모리 카드를 뽑아 다시 쓰는 수고가 없어집니다.
커널에 넘겨주는 것
U-Boot 는 커널과 함께 세 가지를 메모리에 올려 두고 그 위치를 알려 줍니다. 커널이 막 실행을 시작한 순간에는 스스로 알아낼 수 없는 것들입니다.
| 넘기는 것 | 어디서 오나 | 왜 필요한가 |
|---|---|---|
| 커널 명령줄 | bootargs 환경 변수 |
어느 디스크를 쓸지, 로그를 어디로 낼지 정한다 |
| 디바이스 트리 | 저장 장치의 파일 | 어떤 장치가 어느 주소에 붙었는지 알린다 |
| 초기 램디스크 | 저장 장치의 파일 · 없어도 된다 | 진짜 디스크를 읽을 드라이버를 담는다 |
U-Boot 는 디바이스 트리를 넘기기 전에 내용을 고칠 수 있습니다. 보드에 꽂힌 메모리 크기나 네트워크 장치의 하드웨어 주소처럼 켤 때 알게 되는 값을 채워 넣습니다. 그래서 디바이스 트리 파일 하나로 조금씩 다른 보드를 받칠 수 있습니다.
서명을 확인하고 넘기기
커널 파일을 누가 바꿔치기하면 장치 전체가 넘어갑니다. 운영체제가 뜬 뒤의 검사로는 이것을 못 막습니다. 검사하는 쪽이 이미 바꿔치기된 커널 위에서 돌기 때문입니다.
U-Boot 는 커널을 올리기 전에 전자 서명을 확인할 수 있습니다. 전자 서명은 파일을 만든 쪽이 자기만 가진 비밀 키로 만들어 붙이는 값입니다. 받는 쪽은 공개된 짝 키로 확인합니다. 한 글자라도 바뀌었으면 확인이 실패합니다.
서명을 붙이려면 커널과 디바이스 트리와 초기 램디스크를 한 파일로 묶습니다. 이 묶음 형식이 FIT(Flattened Image Tree, 평평한 이미지 트리) 이미지입니다. FIT 이미지는 안에 든 파일마다 해시를 적습니다. 서명은 그 해시에 붙입니다.
U-Boot 자신을 누가 검사하느냐는 칩이 정합니다. 어떤 칩은 부트 ROM 이 SPL 의 서명을 확인한 뒤에만 SPL 을 실행합니다. 이렇게 켜는 방식을 보안 부팅이라고 합니다.
보안 부팅에서는 앞 단계가 다음 단계를 확인하며 이어집니다. 이 고리를 신뢰 사슬이라고 합니다. 고리 하나라도 확인을 건너뛰면 그 뒤로 올린 것은 믿을 수 없습니다.
업데이트가 실패했을 때 되돌리기
현장에 깔린 장치에 새 운영체제를 원격으로 내려보냈는데 새 판이 켜지지 않을 수 있습니다. 사람이 찾아가 시리얼 콘솔을 꽂기 전까지 그 장치는 멈춰 있습니다. U-Boot 는 이것을 막는 방법으로 부팅 횟수 세기를 갖고 있습니다.
이 기능을 켜 두면 U-Boot 는 켤 때마다 bootcount 라는 값을 하나 올립니다. 몇 번까지 봐줄지는 bootlimit 라는 한도로 정해 둡니다. bootcount 가 이 한도를 넘으면 U-Boot 는 bootcmd 대신 altbootcmd 를 실행합니다. altbootcmd 에는 옛 판으로 켜는 명령을 적어 둡니다.
flowchart TD
A["켠다 · bootcount 를 하나 올린다"] --> B{"bootcount 가 bootlimit 를 넘었나"}
B -->|넘지 않았다| C["bootcmd · 새 판으로 켠다"]
B -->|넘었다| D["altbootcmd · 옛 판으로 켠다"]
C --> E{"운영체제가 끝까지 떴나"}
E -->|떴다| F["운영체제가 bootcount 를 0 으로 되돌린다"]
E -->|못 떴다| A
새 판이 끝까지 뜨면 운영체제 쪽 프로그램이 bootcount 를 0 으로 되돌립니다. 새 판이 도중에 멈춰 재시작이 되풀이되면 값이 쌓여 한도를 넘습니다. 그때부터 옛 판으로 켜집니다.
이 방식은 저장 장치에 운영체제를 두 벌 담아 두는 구성과 짝을 이룹니다. 한 벌을 쓰는 동안 다른 벌에 새 판을 씁니다. 새 판이 실패하면 쓰던 벌로 돌아갑니다.
백엔드 개발자가 U-Boot 를 만나는 때
백엔드 개발자가 서버에서 U-Boot 를 만날 일은 드뭅니다. 서버와 개인용 컴퓨터는 UEFI 펌웨어 위에서 GRUB 같은 부트로더를 씁니다. 가상 머신도 대개 흉내 낸 펌웨어와 GRUB 을 거칩니다. 컨테이너는 돌고 있는 커널 위에서 시작하므로 부트로더가 아예 없습니다.
U-Boot 를 만나는 것은 서버가 아니라 서버에 붙는 장치 쪽입니다. 센서를 읽어 서버로 보내는 사물인터넷 장치와 매장·공장에 놓인 작은 게이트웨이 보드는 U-Boot 로 켜지는 일이 많습니다. 이런 장치에 운영체제를 원격으로 내려보내는 시스템을 만들면 앞 소절의 되돌리기가 그 시스템의 일부가 됩니다.
장치가 원격으로 닿지 않을 때도 U-Boot 를 만납니다. 운영체제가 안 떠도 시리얼 콘솔을 꽂으면 U-Boot 콘솔까지는 들어갈 수 있습니다. 거기서 환경 변수를 보고 옛 커널로 켜거나 네트워크로 새 이미지를 받아 되살립니다.
U-Boot 는 UEFI 의 일부를 흉내 낼 수도 있습니다. 그러면 개인용 컴퓨터용 배포판의 GRUB 을 U-Boot 위에서 올릴 수 있습니다. 보드마다 다른 부팅 방식을 개인용 컴퓨터와 같은 꼴로 맞추려는 쓰임입니다.
이름의 유래
독일어로 U-Boot 는 잠수함을 뜻합니다. 「Das」는 독일어의 정관사라서 Das U-Boot 는 「그 잠수함」이 됩니다. 범용 부트로더라는 뜻의 Universal Boot 와 독일어 낱말을 겹친 말장난입니다.
관련 항목
U-Boot 앞뒤로 도는 부팅 단계
부팅 · 부트 ROM · SPL · 펌웨어 · 커널 · init · Linux
U-Boot 가 읽고 커널에 넘기는 데이터
커널 이미지 · 디바이스 트리 · 커널 명령줄 · 초기 램디스크 · FIT · 환경 변수 · 파일 시스템 · 파티션
U-Boot 와 같은 일을 하는 부트로더
부트로더 · GRUB · systemd-boot · coreboot · UEFI · BIOS
U-Boot 가 초기화하고 다루는 하드웨어
SoC · SRAM · DRAM · 플래시 메모리 · UART · 시리얼 콘솔 · USB · 이더넷
U-Boot 가 커널을 받아 오는 프로토콜
TFTP · DHCP · 네트워크 부팅 · PXE
U-Boot 가 부팅을 지키는 방식
보안 부팅 · 신뢰 사슬 · 전자 서명 · 해시 · OTA 업데이트 · A-B 파티션
U-Boot 가 도는 임베디드 기계
다른 이름: Das U-Boot · 유부트 · uboot · Universal Boot Loader