Computer Science
탄탄한 기반 실력을 위한
전공과 이론 지식 모음
Today I Learned!
배웠으면 기록을 해야지
TIL 사진
Flutter 사진
Flutter로 모바일까지
거꾸로캠퍼스 코딩랩 Flutter 앱개발 강사
스파르타코딩클럽 즉문즉답 튜터
카카오테크캠퍼스 3기 학습코치
프로필 사진
박성민
임베디드 세계에
발을 들인 박치기 공룡
임베디드 사진
EMBEDDED SYSTEM
임베디드 SW와 HW, 이론부터 실전까지
ALGORITHM
알고리즘 해결 전략 기록
🎓
CAU 소프트웨어학부
텔레칩스 차량용 임베디드 스쿨 3기
애플 개발자 아카데미 1기
깃허브 사진
GitHub
프로젝트 모아보기
Instagram
인스타그램 사진

Embedded System/Embedded Linux

[Embedded Linux] BeagleBone Black에서 왜 BOOT 버튼을 안눌러도 SD카드로 바로 부팅이 될까

sm_amoled 2026. 3. 14. 16:03

요약

분명 메뉴얼에는 비글본 블랙에서 BOOT 버튼을 누른 채로 켜면 SD카드로, 버튼을 안누르면 eMMC로 부팅이 된다고 되어있다. 그런데 막상 둘 다 시도해보면 모두 SD카드에 있는 OS로 부팅이 됐다. (도대체 왜)

그리고 그 이유를 찾아냈는데, 생각치 못한 곳에 있었다. 부팅 단계별로 진행을 살펴보면 다음과 같다.

먼저 1단계, 보드에 내장되어있는 ROM Boot Loader (RBL)를 실행하면서 부팅을 시작한다. RBL은 커널을 실행하기 위해 필요한 U-Boot를 실행하기 위한 준비를 위해서, MLO를 RAM에 올리고 실행할 준비를 해야한다. (ㅋㅋ) 이때 BOOT 버튼의 누름 여부에 따라 SYSBOOT 값이 달라져서 MLO 를 가져오는 위치가 달라진다. 이게 메뉴얼에 작성되어 있는대로 eMMC로부터 가져오냐, 아니면 SD카드로부터 가져오냐를 결정한다.

2단계. RAM에 올라온 Memory Loader (MLO) / 또는 Secondary Program Loader (SPL) 에 의해 U-Boot 가 실행된다. U-Boot는 eMMC와 SD카드에 각각의 버전이 들어있다. 그래서 어떤 위치에서 가져온 MLO냐에 따라서 실행되는 U-Boot가 달라지게 된다.

3단계. U-Boot가 실행된다. 이때 U-Boot 이후에 어떤 위치에서 커널을 불러오고 RootFS를 가져올 지 결정하게 된다. 문제는 여기에 있었다!!! eMMC에서 실행된 U-Boot와 SD카드에서 실행된 U-Boot 모두 다 SD카드로 부팅을 먼저 시도하고, 실패 시 eMMC에서 부팅을 시도하도록 되어있었다.

그래서 BOOT 버튼을 눌렀는지와 상관 없이 SD카드가 끼워져있으면 냅다 SD카드로 부팅이 진행되고, SD카드가 뽑혀있을 때만 eMMC로 부팅이 되는 것이였다.


들어가며

처음에는 일단 보드 SSH 연결까지 모두 뚫어두고 나서, 차근차근 부팅 과정부터 살펴봐야지! 라고 생각하고 기기 활성화를 진행하고 있었다. 기기 활성화를 뚫는 과정에서는 몰랐는데, SD카드에 OS image를 굽고나서 이걸 그냥 꽂기만 하면 eMMC로부터 부팅이 되어야하지만 내 보드는 왠걸 SD카드 부팅이 되고 있었다.

Beagleboard.org 의 공식 document 사이트에서도 위 하이라이트된 문장처럼 “그냥 부팅하면 eMMC로 부팅되고, SD카드로 부팅은 BOOT 버튼을 눌러야함” 이라고 써져있다.

그런데 나는 왜 아무것도 안눌렀는데 자꾸 SD카드에 있는 OS로 부팅이 되는거지? 심지어 SD카드를 빼고 부팅을 하면 eMMC에 담겨있는 예전 OS로 부팅이 잘 된다. eMMC가 아예 비어있는 것도 아닌데,,,

SD카드를 뽑은 채로 부팅을 시도한 결과 (Boot 버튼을 누르지 않고 전원만 공급) → 2020년 4월의 OS로 부팅됨

SD카드를 꽂은 채로 부팅을 시도한 결과 (Boot 버튼을 누르지 않고 전원만 공급) → 내가 얼마전에 SD카드에 구운 2026년 2월의 OS로 부팅됨

이 차이가 뭔지 궁금해서 막 찾아보려고 하다가 “아 이거 글감이다” 싶어서 낋여왔다. 부팅 시퀀스에 대해서 공부할 수 있는건 덤이지.


부팅 과정 파헤치기

기본적으로 컴퓨터가 부팅되기 위해서는 다음의 절차를 거쳐야 한다.

  1. 하드웨어에 전원을 인가한다.
  2. 하드웨어에 내장되어있는 1단계 부트로더인 RBL(ROM Boot Loader) 를 실행하여 2단계 부트로더를 메모리에 올린다.
  3. 2단계 부트로더인 MLO(Memory Loader) 또는 SPL(Secondary Program Loader) 를 실행하여 3단계 부트로더를 메모리(DDR)에 올린다.
  4. 3단계 부트로더 U-Boot 가 커널을 실행시킨다.
  5. 커널이 디바이스 트리, 값 초기화 등을 수행한다.
  6. 커널이 Root File System를 마운트한다.
  7. OS가 실행된다.

여기에서 누가 범인인지를 찾아보고자 파헤쳐보았다.

AM335X의 하드웨어 구조부터

BBB 보드 중앙에 박혀있는 큼지막한 보드가 AM335X SoC이다. BeagleBone Black 보드는 프로세서로 “Sitara AM3358BZCZ100 1GHz, 2000 MIPS”를 사용한다. 그래서 프로세서에 대해서 파악하고 싶다면 AM3358에 대해 찾아보아야 한다.

보드에 전원이 인가되면 프로세스 내부에 있는 ROM 에서 RBL (ROM Boot Loader)가 실행된다.

이 시점에는 아직 드라이버를 이용해 프로세서가 외부 장치를 제어할 수 없기 때문에, 내부에 있는 SRAM과 BOOT ROM만 사용할 수 있다. 그래서 여기에서 처리가 완료되어야 한다. (또는 직접 레지스터를 제어해서 접근이 가능하다)

테크니컬 레퍼런스 메뉴얼에서 이 Boot ROM에 대한 정보를 확인할 수 있다. 여기에서 48KB 공간에 RBL 코드가 들어있다.

 

테크니컬 레퍼런스에 따르면, 여기에서 48KB를 Public ROM 이라고 부르고, 여기에 실행될 코드가 들어있는 것을 확인할 수 있다.

Code 외에 각각의 항목에 대한 설명은 테크니컬 레퍼런스 매뉴얼에 5022 페이지부터 쭉 작성이 되어있다.

Booting Sequence를 살펴보자

문서에 있는 부팅 시퀀스를 살펴보면, 위쪽에 “Secure Boot”를 먼저 처리하고 초기화를 진행한 다음 ROM Booting을 진행하도록 되어있다.

다만 문서에는 이 Secure Boot에 대한 자세한 설명이 없었다. 방대한 문서 속에서 Secure Boot 라는 단어가 단 2번 등장… 구글에 검색해도 따로 정리가 되어있는 내용이 없어서 제미나이에게 물어봤는데 이거는 ‘유료 옵션’ 이라고 한다. 내가 산 범용 보드에는 단순히 General Purpose이기 때문에 보안 부팅이 생략되어서 출고가 되지만, 실제로 보안 기능이 필요한 제품에 경우에는 부팅 과정에 사용하는 MLO 등이 유효한 지 서명을 검증하고 실행하거나 보안 환경 등을 세팅하는 역할을 담당한다고 한다(TrustZone과 관련된 내용인 듯). 그래서 이건 기본 레퍼런스 메뉴얼에는 없고, 따로 계약을 통해서 기능을 추가하는 방식인 듯 생각된다.

다만 CPU 아키텍처 상 TrustZone Architecture를 가지고 있기 때문에 항상 Secure Mode로 부팅이 시작된 이후에 ROM Boot Loader로 넘어간다고 한다.

그래서, 이후에 나오는 Booting 관련된 내용은 Secure Boot이 아닌 일반 Public ROM에 들어있는 Booting 과정을 말하도록 하겠다!

이 Public ROM Boot Loader 코드는 다음의 역할을 수행한다.

  1. SYSBOOT 레지스터 읽기
  2. PLL (최소한의 클럭)을 초기화
  3. MMC 컨트롤러를 초기화 (← SD / eMMC 접근을 위함)
  4. MMC에서 FAT 파일시스템 테이블 파싱 (← MLO 파일 탐색을 위함)
  5. UART 초기화 (장치를 못찾거나 부팅 실패 시 로그 뱉기위해 초기화)
  6. CRC 검증
  7. MMC에서 MLO(Memory Loader)를 SRAM에 복사

거창한 이름을 떼고 다시 한 번 정리해보면 ROM Boot Loader가 실행이 될 때

  1. SYSBOOT 레지스터 파악
  2. SYSBOOT 를 기반으로 시스템의 기본 CLK 주파수를 결정하고 활성화
    • [여기에서 Figure 26-10에 있는 Booting 영역으로 넘어감]
  3. SYSBOOT 를 기반으로 어떤 장치로부터 Memory Loader를 가져올 지 결정
  4. 해당 장치의 MMC 초기화 및 FAT 파싱 (실패 시 다음 디바이스로 다시 시도)
  5. MLO를 찾아서 SRAM에 옮기기
  6. SRAM의 MLO를 실행
  7. 만약 모두 실패했다면 UART를 활성화하고 로그 뱉기

SYSBOOT로 부팅 소스 파악

어떤 장치들을 순서대로 접근해서 MLO가 있는지 확인하고 가져올 지에 대해서는 SYSBOOT의 레지스터 값들을 기반으로 결정한다. 나머지 Bit에 대해서는 기본적으로 설정되어있는 값을 따라가고, SYSBOOT[4:0] 의 값들이 어디에서 MLO를 찾을지 장치를 결정하게 된다.

그러면 이 값들은 어디에서 가져오느냐? BeagleBone Black의 메뉴얼을 보면 아래처럼 Booting 에 사용되는 SYS_BOOT의 선 연결을 확인할 수 있다.

여기에서 microSD 카드로 부팅할 지에 대한 여부를 S2 버튼 (BOOT 버튼)으로 선택할 수 있게 되는데, 이게 바로 SYSBOOT[2] 값과 연결이 되기 때문에 적용이 되는 것이다.

  • BOOT 버튼을 안누르면 SYSBOOT[4:0] 값이 0b11100
    • MMC1으로 부팅이 먼저 진행 → 실패 시 MMC0으로 부팅 진행
  • BOOT 버튼을 누르면 SYSBOOT[4:0] 값이 0b11000
    • SPI0으로 부팅이 먼저 진행 (그런데 BBB에는 SPI EEPROM이 없음 … 무조건 실패) → MMC0으로 부팅 진행

BBB의 문서를 확인해보면, 내장 eMMC 가 MMC1과 연결이 되어있기 때문에 BOOT 버튼을 누르지 않으면 eMMC로 부팅이 진행이 된다. 그리고 microSD의 경우에는 MMC0과 연결이 되어있다고 아래처럼 작성이 되어있다.

여기에서 혹시 문제의 원인을 찾을 수 있을까

여기에 작성되어 있는 글만 보면 내가 BBB 보드에서 SD카드를 꽂고 BOOT 버튼을 누르지 않고 부팅했을 때 SD카드에 있는 이미지로 부팅이 된 이유에 의심가는게 SD카드가 MMC1로 잡혀서 그런게 아닐까 싶었다. 그런데 U-Boot 에서 mmc 목록을 출력해봤을 때, 딱히 그렇지는 않았던 것 같다.

실제로 mmc list 명령어를 통해서 MMC 목록을 확인해보면

MMC0에는 SD카드가 잡혀있고, MMC1에는 eMMC가 잡혀있는 것을 볼 수 있었다.

=> mmc list
OMAP SD/MMC: 0 (SD)
OMAP SD/MMC: 1

라고 생각하면 큰 오산이였다. 이건 U-Boot에서 MMC 번호를 매긴 것이고, 실제 U-Boot가 올라오기 전에 ROM 단계에서의 MMC 번호는 U-Boot와 완전완전 별개이다.

U-Boot 내에서 현재 이 부팅 시퀀스가

eMMC의 MLO로부터 온건지, 아니면 SD카드의 MLO로부터 온건지 살펴보기 위해

env print bootpart 라는 명령어를 입력해봤다.

=>  env print bootpart
bootpart=0:2

결과는 위처럼 나왔는데, 0번 MMC의 2번 파티션을 부팅에 참조하겠다고 등록이 되어있는 상태이다. MMC0은 SD카드를 의미하고 2번 파티션은 ext4/rootfs 가 담겨있는 위치이다. 여기에서 boot로 넘어가면 해당 파티션에 담겨있는 정보를 이용해서 커널을 살리고 rootFS를 불러오게 된다. 즉, 기본적으로 BOOT 버튼을 누르지 않았음에도 MMC0으로 부팅이 되고 있다는 것. (도대체 왜!!!)

라고 생각하는 것 역시 오산이였다. bootpart는 다음 번에 커널을 어디에서 가져올 지를 결정하는 것이고, 어떤 MLO로부터 이 U-Boot가 불러와졌는지를 보여주는 녀석은 아니였다.

이 U-Boot는 어떤 MLO로부터 실행된거지 알 수 있는 방법이 없나?

그리고 결정적인 단서를 찾아냈다.

이게 BOOT 버튼을 누르지 않은 채로 부팅이 된 뒤에 U-Boot에서 내뱉은 가장 첫번째 줄이다. 메뉴얼에 따르면 이렇게 부팅을 하면 eMMC의 U-Boot로 부팅이 되어야 한다. 여기에 보면 2019년 4월 버전의 U-Boo이며 MMC2로부터 부팅 시도중 이라는 로그를 확인할 수 있다.

U-Boot SPL 2019.04-00002-g31a8ae0206 (May 13 2020 - 09:26:17 -0500)
Trying to boot from MMC2

그리고 이게 BOOT 버튼을 누른 채로 부팅이 된 뒤에 U-Boot에서 내뱉은 가장 첫번째 줄이다. 이렇게 하면 SD카드에 들어있는 U-Boot를 불러오게 된다. 실제로 2022년 4월 버전으로 되어있으며 (일단 U-Boot 버전이 변경되었다는 것부터 신났음) MMC1로부터 부팅중이라는 메시지도 볼 수 있다!!

U-Boot SPL 2022.04-g5509547b (Jan 22 2026 - 19:56:08 +0000)
Trying to boot from MMC1

근데 앞서 봤던 내용들에서는 MMC0이 SD카드, MMC1이 eMMC를 의미하는 거였는데 MMC1, MMC2 라는 또 생소한 넘버링이 등장해서 의아햇다. 이 부분에 대해서는 U-Boot의 소스코드 쪽을 열어서 확인이 필요했다.

우선 U-BOOT의 소스코드에서 “Trying to boot from” 이라는 텍스트를 찾아서 어디에 박혀있는지 살펴봤다. common/spl 에 들어있었다. (이게 Secondary Program Loader인 듯. 캬)

// common/spl/spl.c

static int boot_from_devices(struct spl_image_info *spl_image,
                 u32 spl_boot_list[], int count)
{
    struct spl_image_loader *drv =
        ll_entry_start(struct spl_image_loader, spl_image_loader);
    const int n_ents =
        ll_entry_count(struct spl_image_loader, spl_image_loader);
    int ret = -ENODEV;
    int i;

    for (i = 0; i < count && spl_boot_list[i] != BOOT_DEVICE_NONE; i++) {
        struct spl_image_loader *loader;
        int bootdev = spl_boot_list[i];

        if (CONFIG_IS_ENABLED(SHOW_ERRORS))
            ret = -ENXIO;
        for (loader = drv; loader != drv + n_ents; loader++) {
            if (loader && bootdev != loader->boot_device)
                continue;
            if (!CONFIG_IS_ENABLED(SILENT_CONSOLE)) {
                **printf("Trying to boot from %s\n", spl_loader_name(loader));**
            }
            ...

대충 쭈르륵 살펴보면 boot_from_devices 라는 함수에서 이 출력을 뱉고있는데, 만약 해당 부팅의 loader와 부팅 리스트의 항목이 일치한다면 name을 출력하도록 로직이 작성되어있다.

이 이름들은 spl.h 에서 아래처럼 정의를 해두고 있다. 이 시점에 MMC0 이라는 이름은 없고, MMC1과 MMC2라는 이름이 있는걸 볼 수 있다.

arch/arm/include/asm/arch-omap5/spl.h

#define BOOT_DEVICE_NONE    0x00
#define BOOT_DEVICE_XIP        0x01
#define BOOT_DEVICE_XIPWAIT    0x02
#define BOOT_DEVICE_NAND    0x03
#define BOOT_DEVICE_ONENAND    0x04
#define BOOT_DEVICE_MMC1    0x05
#define BOOT_DEVICE_MMC2    0x06
#define BOOT_DEVICE_MMC2_2    0x07
#define BOOT_DEVICE_SATA    0x09
#define BOOT_DEVICE_SPI        0x0A

#define MMC_BOOT_DEVICES_START    BOOT_DEVICE_MMC1
#define MMC_BOOT_DEVICES_END    BOOT_DEVICE_MMC2_2

그리고 spl_mmc.c 라는 파일을 살펴보면, BOOT_DEVICE_MMC1 이라는 놈한테 MMC1 이라는 이름을 달아주고, BOOT_DEVICE_MMC2 에게는 MMC2 라는 이름을 달아주는 것도 확인할 수 있다. 이렇게 붙여준 이름이 이후에 spl_loader_name 함수에서 튀어나오게 되는 것!

common/spl/spl_mmc.c

SPL_LOAD_IMAGE_METHOD("MMC1", 0, BOOT_DEVICE_MMC1, spl_mmc_load_image);
SPL_LOAD_IMAGE_METHOD("MMC2", 0, BOOT_DEVICE_MMC2, spl_mmc_load_image);
SPL_LOAD_IMAGE_METHOD("MMC2_2", 0, BOOT_DEVICE_MMC2_2, spl_mmc_load_image);

SD카드와 eMMC에게 MMC0, MMC1 이라는 이름을 붙여주는 것은 이후에 U-Boot 에서 따로 이름을 붙여주는 것이다. board/ti/... 의 경로의 파일에서 U-Boot 쪽 로직을 확인할 수 있다.

즉, MMC1 에서 부팅이 되었다고 나오는 경우에는 eMMC 에서 MLO를 통해 U-Boot가 실행된 것이고, MMC2에서 부팅이 되었다고 나오는 경우에는 SD카드에서 MLO를 통해 U-Boot가 실행된 것이다. 오호라. 일단 여기까지는 분리가 되어서 실행된 게 맞다.

그러면 왜 둘 다 SD 카드의 OS 이미지로 실행이 된거냐

여기에 대해서는 boot_targets 라는 속성을 보고 파악할 수 있었다. boot_targets 는 부팅을 시도할 대상의 순서를 쭉 나열한 것이다. 그리고 mmc list는 아래처럼 되어있다. MMC0 이 SD 카드라고 딱 표시가 되어있다. 이거는 eMMC의 U-Boot, SD카드의 U-Boot 모두 동일했다.

여기를 보면 mmc0mmc1... 이렇게 순서로 부팅을 하도록 등록이 되어있는 것을 확인할 수 있다. 즉, mmc0 으로 부팅을 시도하고, 만약 여기에 실패하면 mmc1 로 부팅을 실패하고, 이런 순서로 진행이 되도록 되어있다.

즉, eMMC로부터 MLO가 불려와져서 U-Boot를 실행했다고 하더라도, boot_targets 의 순서가 SD카드가 더 높았고, SD카드가 장착이 되어있는 상태라면 무조건 SD카드로부터 먼저 부팅을 시도하기 때문에 버튼을 누르든 말든 결과적으로는 SD카드의 이미지로 부팅이 되었던 것이다!!

따라서

BBB 보드의 메뉴얼에 작성되어있는 내용은 절반 정도 사실이였다.

  • BOOT 버튼 (S2 버튼) 을 누르지 않은 채로 전원을 공급하면 eMMC 로 부팅이 된다.
  • BOOT 버튼을 누른 채로 전원을 공급하면 SD카드로 부팅이 된다.

그런데 내가 마주한 결과는

  • 두 방식 모두 결국에는 SD 카드에 있는 OS 이미지로 부팅이 되었다.
  • 그리고 SD카드를 뽑았을 때만 eMMC에 있는 OS 이미지로 부팅이 되었다는 점.

그 이유는 까보니 다음과 같다.

  1. BOOT 버튼을 눌렀을 때, 누르지 않았을 때 서로 다른 MLO(SPL)로부터 U-Boot를 불러온다. 그래서 U-Boot 버전이 다른 것은 확인을 했음.
  2. 근데 막상 까보니 부팅을 시도할 때 참조하는 파티션은 두 경로 모두 SD카드를 우선적으로 시도하고, 실패한 경우에 eMMC를 시도하도록 되어있었다.
  3. 그래서 SD카드가 연결되어 있는 경우에는 BOOT 버튼을 누르든 말든 SD카드로 부팅이 되었고, SD카드를 빼고 부팅을 하려는 경우에만 SD카드 부팅에 실패해서 eMMC로 부팅이 되는 것이였다.

아주,,, 내가 열어보지 못했던 심연을 계속 들춰보면서 탐색했던 것 같다. UART 시리얼로 로그를 뽑아볼 수 있는게 U-Boot 부팅 로그부터라서, MLO 단계부터 찾아보는게 꽤나 쉽지 않았던 것 같다…

320x100