[라즈베리파이] u-boot 에서 kernel 부팅

커널을 바꿔가며 시험할 때 라즈베리파이 펌웨어에만 의존하면 매번 전원을 내리고 SD 카드를 빼서 파일을 갈아끼워야 한다. U-Boot 프롬프트까지 올려놨다면 그 자리에서 커널을 골라 올리고 부트 인자를 바꿔 다시 부팅할 수 있다. 이 글에서는 fatload와 booti로 커널을 직접 올리는 방법과, 같은 절차를 boot.scr·extlinux.conf로 자동화하는 방법을 정리한다. U-Boot를 빌드해 부트 파티션에 올리는 과정은 라즈베리파이 u-boot 올리기에서 다뤘다.

부팅에 필요한 파일과 로드 주소

파일역할로드 주소 변수
Image 또는 vmlinuzarm64 커널 이미지${kernel_addr_r}
bcm2710-rpi-3-b.dtb보드 디바이스 트리${fdt_addr_r}
initrd.gz초기 램디스크(선택)${ramdisk_addr_r}
U-Boot> printenv kernel_addr_r fdt_addr_r ramdisk_addr_r scriptaddr
kernel_addr_r=0x00080000
fdt_addr_r=0x05600000
ramdisk_addr_r=0x05700000
scriptaddr=0x05400000

주소를 직접 외울 필요 없이 이 변수들을 그대로 쓰면 된다. 커널이 35 MB까지 커져도 0x80000에서 0x5600000 사이에 충분히 들어간다.

fatload와 booti로 직접 부팅하기

fatload mmc 0:1 ${kernel_addr_r} Image
fatload mmc 0:1 ${fdt_addr_r} bcm2710-rpi-3-b.dtb
fatload mmc 0:1 ${ramdisk_addr_r} initrd.gz
setenv bootargs earlycon console=ttyAMA1,115200 rdinit=/init
booti ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdt_addr_r}

booti의 두 번째 인자는 램디스크주소:크기이고, initramfs를 쓰지 않으면 -로 비운다. ${filesize}에는 직전 fatload가 읽은 바이트 수가 들어가므로 램디스크를 마지막에 읽는 순서가 중요하다.

U-Boot> fatload mmc 0:1 ${kernel_addr_r} Image
35432960 bytes read in 40379 ms (856.4 KiB/s)
U-Boot> fatload mmc 0:1 ${fdt_addr_r} bcm2710-rpi-3-b.dtb
33734 bytes read in 46 ms (715.8 KiB/s)
U-Boot> fatload mmc 0:1 ${ramdisk_addr_r} initrd.gz
1005966 bytes read in 1144 ms (858.4 KiB/s)
U-Boot> setenv bootargs earlycon console=ttyAMA1,115200 rdinit=/init
U-Boot> booti ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdt_addr_r}
Moving Image from 0x80000 to 0x200000, end=0x24c0000
## Flattened Device Tree blob at 05600000
   Booting using the fdt blob at 0x5600000
Working FDT set to 5600000
   Loading Ramdisk to 1ff0a000, end 1ffff98e ... OK
   Loading Device Tree to 000000001fefe000, end 000000001ff093c5 ... OK
Working FDT set to 1fefe000

Starting kernel ...

U-Boot가 커널을 0x200000으로 옮기고 램디스크와 DTB를 메모리 상단에 재배치한 뒤 제어를 넘긴다. 여기서부터는 커널이 찍는 로그다.

[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd034]
[    0.000000] Linux version 6.8.0-1061-raspi (buildd@bos03-arm64-102) ...
[    0.000000] Machine model: Raspberry Pi 3 Model B
[    0.000000] earlycon: bcm2835aux0 at MMIO32 0x000000003f215040 (options '115200n8')
[    0.000000] printk: legacy bootconsole [bcm2835aux0] enabled
[    0.000000] Reserved memory: created CMA memory pool at 0x0000000038000000, size 64 MiB
...
[    4.172862] Run /init as init process

=== initramfs up ===
Linux 6.8.0-1061-raspi aarch64
cmdline: earlycon console=ttyAMA1,115200 rdinit=/init
rootfs: bin dev init proc root sys

BusyBox v1.36.1 (Ubuntu 1:1.36.1-6ubuntu3.1) built-in shell (ash)
Enter 'help' for a list of built-in commands.

~ #

Machine model이 fatload로 올린 DTB에서 읽힌 값이라, 이 줄이 맞게 나오면 커널·DTB 조합이 어긋나지 않았다는 뜻이다.

boot.scr로 자동화하기

매번 네 줄을 치기 번거로우면 같은 명령을 스크립트로 만들어 부트 파티션에 둔다. U-Boot는 텍스트 파일을 그대로 읽지 않고 mkimage로 헤더를 붙인 형식을 요구한다.

fatload mmc 0:1 ${kernel_addr_r} Image
fatload mmc 0:1 ${fdt_addr_r} bcm2710-rpi-3-b.dtb
fatload mmc 0:1 ${ramdisk_addr_r} initrd.gz
setenv bootargs earlycon console=ttyAMA1,115200 rdinit=/init
booti ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdt_addr_r}
mkimage -A arm64 -O linux -T script -C none \
    -n 'RPi boot script' -d boot.cmd boot.scr
sudo cp boot.scr /mnt/boot/
Image Name:   RPi boot script
Created:      Mon Aug 17 16:22:30 2026
Image Type:   AArch64 Linux Script (uncompressed)
Data Size:    269 Bytes = 0.26 KiB = 0.00 MiB
Load Address: 00000000
Entry Point:  00000000

이제 전원을 넣으면 U-Boot가 알아서 스크립트를 찾아 실행한다.

Hit any key to stop autoboot:  0
** Booting bootflow 'mmc@7e202000.bootdev.part_1' with script
35432960 bytes read in 24204 ms (1.4 MiB/s)
33734 bytes read in 24 ms (1.3 MiB/s)
1005966 bytes read in 669 ms (1.4 MiB/s)
Moving Image from 0x80000 to 0x200000, end=0x24c0000
## Flattened Device Tree blob at 05600000
   Booting using the fdt blob at 0x5600000

Starting kernel ...

extlinux.conf로 자동화하기

boot.scr은 내용을 고칠 때마다 mkimage를 다시 돌려야 한다. 텍스트 파일 하나로 끝내려면 부트 파티션에 extlinux/extlinux.conf를 둔다.

default rpi
timeout 30
label rpi
    kernel /Image
    fdt /bcm2710-rpi-3-b.dtb
    initrd /initrd.gz
    append earlycon console=ttyAMA1,115200 rdinit=/init
** Booting bootflow 'mmc@7e202000.bootdev.part_1' with extlinux
1:	rpi
Retrieving file: /Image
Retrieving file: /initrd.gz
append: earlycon console=ttyAMA1,115200 rdinit=/init
Retrieving file: /bcm2710-rpi-3-b.dtb
Moving Image from 0x80000 to 0x200000, end=0x24c0000
## Flattened Device Tree blob at 05600000
   Booting using the fdt blob at 0x5600000

Starting kernel ...

label을 여러 개 적어두면 커널을 여러 벌 등록해두고 default만 바꿔 골라 부팅할 수 있다. 로드 주소를 직접 쓰지 않아도 U-Boot가 알아서 채운다.

세 방식 비교

방식설정 위치수정 후 추가 작업쓰기 좋은 상황
수동 fatload+booti없음(프롬프트에서 입력)없음커널·부트 인자를 바꿔가며 시험할 때
boot.scrboot.cmdmkimage 재실행U-Boot 명령을 그대로 굳힐 때
extlinux.conf텍스트 파일없음커널을 여러 벌 등록해 골라 쓸 때

주의사항

booti에 DTB 주소를 빼면 커널이 시작되지도 못하고 보드가 리셋된다. 커널이 잘못됐다고 오해하기 쉬운 증상이라 인자 개수부터 확인한다.

U-Boot> booti 0x80000
Moving Image from 0x80000 to 0x200000, end=0x24c0000
Working FDT set to 0
FDT and ATAGS support not compiled in

resetting ...
  • gzip으로 압축된 vmlinuz를 풀지 않고 그대로 올려도 booti가 Uncompressing Kernel Image to 0을 찍고 알아서 푼다. 같은 커널이 12 MB와 35 MB로 차이가 나서 로드 시간도 실측 12초 대 40초로 벌어진다.
  • 이 글의 검증은 initramfs의 셸까지다. SD 카드의 루트 파일시스템으로 부팅하려면 bootargs에 root=/dev/mmcblk0p2 rootwait를 함께 넘겨야 한다.
  • QEMU raspi3b에서는 stdio에 물리는 mini UART의 커널 드라이버가 pinctrl 문제로 probe에 실패해(bcm2835-aux-uart ... probe failed with error -22) 콘솔이 PL011(ttyAMA1)로만 잡힌다. 그래서 위 예제는 console=ttyAMA1을 쓰고 QEMU 인자도 -serial file:...과 -serial mon:stdio로 나눠 받았다. 실제 보드에서는 해당하지 않는 제약이다.
  • QEMU의 SD 에뮬레이션이 느려 35 MB 커널 fatload에 40초가 걸린다. 실제 보드에서는 이만큼 오래 걸리지 않으니 이 수치를 기준으로 삼지 말 것.
  • boot.scr과 extlinux.conf를 부트 파티션에 함께 두면 어느 쪽이 선택되는지 로그를 봐야 알 수 있다. 하나만 남겨두는 편이 헷갈리지 않는다.

마무리

커널을 올리는 명령 자체는 fatload 세 번과 booti 한 번이 전부이고, 나머지는 그 네 줄을 어디에 적어두느냐의 차이다. 시험 단계에서는 프롬프트에서 직접 치다가, 반복이 굳어지면 extlinux.conf로 옮겨두는 흐름이 편하다. 부트 인자를 바꿔가며 커널 동작을 확인하는 작업이 SD 카드를 뽑지 않고 끝난다는 것이 U-Boot를 끼워 넣는 실질적인 이유다.

참고

답글 남기기