본문 바로가기
프로그래밍/임베디드리눅스완전정복

임베디드리눅스완전정복 | 3장 | 부트로더

by 오답노트의 주인 2025. 11. 15.

이 카테고리의 글은 <임베디드리눅스완전정복> 책을 읽고 제 나름대로 정리한 내용을 담고 있습니다. 책 내용을 그대로 서술하지 않았습니다.


이번 장은 부트로더가 무엇이고 어떤 일을 하는지와 배경지식에 대한 장이다. 내용이 다소 추상적이라서 이해하기 힘들었다. 게다가 나중에 라즈베리파이에 커널을 올릴 땐 여기서 배운 U-Boot를 쓰지 않아서 실습을 못했다.

부트로더란

시스템에 전원이 들어오면 부트로더는 어떤 일을 한다. 마침내 부트로더가 일을 다하면 커널이 실행된다. 커널이 실행되려면 기본적인 작업들이 꼭 필요하다. 부트로더는 커널이 동작할 수 있도록 만드는 기본적인 작업들을 수행한다.

'Bootloader - OSDev Wiki'

The boot loader ultimately has to:

  • Bring the kernel (and all the kernel needs to bootstrap) into memory
  • Provide the kernel with the information it needs to work correctly
  • Switch to an environment that the kernel will like
  • Transfer control to the kernel

부트로더는 대개 위 4가지 일을 한다.

컴퓨터에 전원이 인가되면 램에는 아무것도 없는 상태다. 게다가 램에 접근하도록 도와주는 장치도 초기화되지 않은 상태다. 따라서 부트로더가 커널을 직접 램으로 올려줘야 한다. 그전에, 커널이 정상적으로 실행될 수 있도록 필요한 정보들을 넘겨주어야 한다. 이 작업들을 모두 완료한 후 실행 주체를 커널로 변경한다.

ROM

기본적으로 CPU는 하드웨어적으로 부팅 시작 시 롬에 저장되어 있는 부트로더 코드를 읽고 수행하도록 설계되어 있다. 이 코드는 SoC 내부의 칩에 저장되어 있거나 메인보드의 플래시 메모리에 있다고 한다.

SPL (Secondary Program Loader)

ROM 코드의 마지막에는 SPL이 저장되어 있는 SRAM으로 점프하는 코드가 들어있다. 완전한 부트로더를 바로 실행한다면 좋겠지만, 보통 SRAM은 크기가 작으므로 SRAM에 부트로더 코드를 올릴 수는 없다. 따라서 DRAM에 부트로더 코드를 올리게 된다. SPL은 DRAM에 부트로더를 로드한 다음 TPL이 저장된 위치로 점프한다.

TPL (Tertiary Program Loader)

TPL은 부트로더 코드를 의미한다. 이제 위에서 보았던 4가지 일을 한다.

부트로더에서 커널 코드로 점프하기 전에, 커널이 필요로 하는 정보를 넘겨주어야 한다.

  • 디바이스 트리 정보들과 포인터
  • 커널 명령줄
  • 초기 램디스크의 위치와 크기(선택)

디바이스 트리는 시스템을 구성하는 하드웨어 정보를 나타낸다. 일종의 메타데이터이다.

과거에는 커널에 하드코딩 되어 있었다고 하는데, 하드웨어(페리퍼럴)가 변경될 경우 커널을 다시 컴파일 해야하는 문제점이 있었다. 그래서 커널로부터 장치 데이터를 분리함으로 유연성을 가지게 되었고, 동일한 하드웨어에 다른 커널을 올릴 경우에도 단순하게 장치 데이터를 재사용할 수도 있게 되었다.

https://docs.zephyrproject.org/latest/build/dts/intro-syntax-structure.html

내가 직접 쓸게 아니니까 문법은 그냥 참고만…

디바이스 트리 파일은 .dts확장자를 가진다. 이걸 컴파일하면 .dtb가 된다. 파일이 바이너리이므로 메모리에 올린 후 커널에게 메모리 주소를 넘겨주게 된다.

빌드하기

감사하게도 U-Boot는 GPL2+ 라이센스를 채택하고 있어서 모든 소스코드가 공개되어 있다.

git clone git@github.com/u-boot/u-boot
# 책에서는 git clone git://git.denx.de/u-boot.git을 하라고 되어 있다.
cd u-boot
git checkout v2021.01

그 다음 ARCHCROSS_COMPILE 환경변수를 설정하고 make 명령어를 통해 구성파일을 선택한다.

export ARCH=arm
export CROSS_COMPILE=arm-cortex_a8-linux-gnueabihf-
# 구성파일 목록은 configs/ 안에 모여 있다. 

make am335x_evm_defconfig
make

빌드는 금방 끝난다.

설치하기

❯ ls -lt        
-rw-rw-r--   1 user user 1557644 12월  2 22:20 u-boot-dtb.img
-rw-rw-r--   1 user user   89880 12월  2 22:20 u-boot.dtb
drwxrwxr-x  15 user user    4096 12월  2 22:20 spl
-rw-rw-r--   1 user user  109576 12월  2 22:20 MLO.byteswap
-rw-rw-r--   1 user user  109576 12월  2 22:20 MLO
-rw-rw-r--   1 user user  124374 12월  2 22:20 System.map
-rw-rw-r--   1 user user  251739 12월  2 22:20 u-boot.sym
-rw-rw-r--   1 user user  710248 12월  2 22:20 u-boot.bin
-rw-rw-r--   1 user user  710248 12월  2 22:20 u-boot-dtb.bin
-rwxrwxr-x   1 user user 1861172 12월  2 22:20 u-boot.srec
-rw-rw-r--   1 user user 1557644 12월  2 22:20 u-boot.img
drwxrwxr-x   3 user user    4096 12월  2 22:20 dts
-rwxrwxr-x   1 user user  620368 12월  2 22:20 u-boot-nodtb.bin
-rw-rw-r--   1 user user 1334769 12월  2 22:20 u-boot.map
-rwxrwxr-x   1 user user 6800668 12월  2 22:20 u-boot
-rw-rw-r--   1 user user    1329 12월  2 22:20 u-boot.lds
(...)

위와 같이 여러 파일들이 생성된다.

파티션과 볼륨 생성

비글본블랙에는 SPL역할을 할 MLO와 부트로더 바이너리인 u-boot.img 두 개를 FAT32 타입인 파티션에 넣어야 한다.

먼저 파티션을 생성하기 위해, sd카드를 리더기에 꽂은 후 우분투 PC에 꽂았다. 그다음 fdisk를 사용해서 파티션을 생성해준다. 나의 경우 /dev/sdc1장치로 잡혀있어서 아래 명령어를 입력했다.

만약 sd카드가 시스템에 마운트 되어 있다면 언마운트해야한다.

❯ sudo fdisk /dev/sdc

Welcome to fdisk (util-linux 2.37.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): d
Selected partition 1
Partition 1 has been deleted.

Command (m for help): n
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (1-4, default 1): 1
First sector (2048-122265599, default 2048): 
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-122265599, default 122265599): +512M

Created a new partition 1 of type 'Linux' and of size 512 MiB.
Partition #1 contains a vfat signature.

Do you want to remove the signature? [Y]es/[N]o: n

Command (m for help): t

Selected partition 1
Hex code or alias (type L to list all): c
Changed type of partition 'Linux' to 'W95 FAT32 (LBA)'.

Command (m for help): a
Selected partition 1
The bootable flag on partition 1 is enabled now.

Command (m for help): n
Partition type
   p   primary (1 primary, 0 extended, 3 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (2-4, default 2): 2
First sector (1050624-122265599, default 1050624): 
Last sector, +/-sectors or +/-size{K,M,G,T,P} (1050624-122265599, default 122265599): 

Created a new partition 2 of type 'Linux' and of size 57.8 GiB.

Command (m for help): t
Partition number (1,2, default 2): 2
Hex code or alias (type L to list all): 83

Changed type of partition 'Linux' to 'Linux'.

Command (m for help): p
Disk /dev/sdc: 58.3 GiB, 62599987200 bytes, 122265600 sectors
Disk model: STORAGE DEVICE  
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xcb149a1c

Device     Boot   Start       End   Sectors  Size Id Type
/dev/sdc1  *       2048   1050623   1048576  512M  c W95 FAT32 (LBA)
/dev/sdc2       1050624 122265599 121214976 57.8G 83 Linux

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

이렇게 하고 lsblk를 입력하면 아래처럼 나온다.

❯ lsblk -f
NAME        FSTYPE   FSVER LABEL                UUID                                 FSAVAIL FSUSE% MOUNTPOINTS
loop0       squashfs 4.0                                                                   0   100% /snap/bare/5
loop1       squashfs 4.0                                                                   0   100% /snap/code/211
loop2       squashfs 4.0                                                                   0   100% /snap/code/213
loop3       squashfs 4.0                                                                   0   100% /snap/core20/2669
loop4       squashfs 4.0                                                                   0   100% /snap/core20/2682
loop5       squashfs 4.0                                                                   0   100% /snap/core22/2139
loop6       squashfs 4.0                                                                   0   100% /snap/core22/2163
loop7       squashfs 4.0                                                                   0   100% /snap/firefox/7298
loop8       squashfs 4.0                                                                   0   100% /snap/firefox/7423
loop9       squashfs 4.0                                                                   0   100% /snap/gnome-42-2204/176
loop10      squashfs 4.0                                                                   0   100% /snap/gnome-42-2204/226
loop11      squashfs 4.0                                                                   0   100% /snap/gtk-common-themes/1535
loop12      squashfs 4.0                                                                   0   100% /snap/snap-store/1113
loop13      squashfs 4.0                                                                   0   100% /snap/snap-store/1216
loop14      squashfs 4.0                                                                   0   100% /snap/snapd/21759
loop15      squashfs 4.0                                                                   0   100% /snap/snapd/25577
loop16      squashfs 4.0                                                                   0   100% /snap/snapd-desktop-integration/178
loop17      squashfs 4.0                                                                   0   100% /snap/snapd-desktop-integration/315
sda                                                                                                 
├─sda1                                                                                              
├─sda2      ntfs           백업디스크           3C9C7E739C7E2792                                    
└─sda3      ntfs           WDC WD80EAZZ-00BKLB0 DCA080B2A08094A4                                    
sdb                                                                                                 
└─sdb1      ntfs           백업디스크2          224AD6D64AD6A637                                    
sdc                                                                                                 
├─sdc1      vfat     FAT32 boot                 6B4A-64DE                                           
└─sdc2      ext4     1.0   rootfs               ee2730e5-f8d0-4b0a-9f5d-da533af5a81d                
(생략)

파티션은 생성되었지만 볼륨이 없기 때문에 볼륨을 생성해준다.

sudo mkfs.vfat -F 32 -n boot /dev/sdc1
sudo mkfs.ext4 -L rootfs /dev/sdc2

볼륨 생성 후 다시 리더기를 뺏다가 꽂으면 제대로 마운트된다.

cp MLO /media/user/boot
cp u-boot.img /media/user/boot

그 다음 두 파일을 차례대로 sd카드에 복사한 후 비글본블랙에 꽂고 부팅하면 된다.

비글본블랙이 sd카드로 바로 부팅하지 않고 먼저 eMMC에 접근하여 부팅하므로, 책에서는 S2버튼을 꾹 누르고 전원을 연결하라고 되어 있다.
근데 나는 모르고 일단 부팅한다음 eMMC를 통해 부팅된 커널에서 블럭을 싹 0으로 밀어버렸다...
https://forum.beagleboard.org/t/bbb-not-booting-from-sd-card/35461/8

부팅하기

USB to TTL을 비글본블랙의 J1 커넥터의 1(GND),4(RX),5(TX)에 연결하면 우분투에서 ttyUSB0과 같이 장치가 잡힌다. 먼저 터미널을 열어서 연결된 장치를 통해 시리얼 통신을 시작하고, 비글본블랙에 전원을 연결하면 아래처럼 쫘르륵 뜬다.

U-Boot SPL 2025.10 (Dec 03 2025 - 00:02:09 +0900)
Trying to boot from MMC1


U-Boot 2025.10 (Dec 03 2025 - 00:02:09 +0900)

CPU  : AM335X-GP rev 2.1
Model: TI AM335x BeagleBone Black
DRAM:  512 MiB
Core:  161 devices, 18 uclasses, devicetree: separate
WDT:   Started wdt@44e35000 with servicing every 1000ms (60s timeout)
NAND:  0 MiB
MMC:   OMAP SD/MMC: 0, OMAP SD/MMC: 1
Loading Environment from FAT... Unable to read "uboot.env" from mmc0:1... 
<ethaddr> not set. Validating first E-fuse MAC
Net:   eth2: ethernet@4a100000using musb-hdrc, OUT ep1out IN ep1in STATUS ep2in
MAC de:ad:be:ef:00:01
HOST MAC de:ad:be:ef:00:00
(생략)

sd카드를 보드에 꽂고 전원을 연결하면 U-boot는 곧바로 정해진 절차에 따라 부팅을 시도한다. autoboot를 막으려면 2초정도 주어진 시간에 아무 tx나 보내면 중단하고 일종의 쉘같은걸 띄워준다.

U-Boot SPL 2025.10 (Dec 03 2025 - 00:02:09 +0900)
Trying to boot from MMC1


U-Boot 2025.10 (Dec 03 2025 - 00:02:09 +0900)

CPU  : AM335X-GP rev 2.1
Model: TI AM335x BeagleBone Black
DRAM:  512 MiB
Core:  161 devices, 18 uclasses, devicetree: separate
WDT:   Started wdt@44e35000 with servicing every 1000ms (60s timeout)
NAND:  0 MiB
MMC:   OMAP SD/MMC: 0, OMAP SD/MMC: 1
Loading Environment from FAT... Unable to read "uboot.env" from mmc0:1... 
<ethaddr> not set. Validating first E-fuse MAC
Net:   eth2: ethernet@4a100000using musb-hdrc, OUT ep1out IN ep1in STATUS ep2in
MAC de:ad:be:ef:00:01
HOST MAC de:ad:be:ef:00:00
RNDIS ready
, eth3: usb_ether
Hit any key to stop autoboot:  2 
Hit any key to stop autoboot: 0[K
=> 

이제 다음 게시글에서 커널을 빌드하고 위에서 본 쉘 비슷한 인터페이스를 통해 메모리에 커널을 올리면 된다.

라즈베리파이 5는?

내부적으로 uboot가 하는 작업들을 할 수 있는 펌웨어가 EEPROM에 있다. 따라서 그냥 빌드한 커널이랑 필요한 몇가지 파일들을 sd카드에 담고 적절한 환경변수를 넣으면 알아서 부팅한다. 라즈베리파이5에서는 사실 이 장에서 배운 내용이 사실 필요없다.

댓글