들어가며
BeagleBone Black의 부팅 시퀀스를 살펴보는 과정에서 생각보다 글이 길어지는 것 같아서 시리즈로 쪼개어봤다. 이번에 살펴볼 내용은 Secondary Program Loader이다.
ㅤㅤ
우선 큰 그림부터
사용자 서비스가 올라올 때 까지 무슨 일이 일어나는지를 한 번 보자.
- 전원을 공급한다
- 보드에 내장된 ROM Boot Loader를 실행한다
- 이때 어떤 장치에서 부팅할 지 결정함
- Secondary Program Loader를 SRAM으로 옮김
- SPL으로 Jump!
- SRAM에 올라온 Secondary Program Loader를 실행한다
- RAM(메인메모리)을 초기화하는 역할
- U-Boot 이미지를 RAM에 올림
- U-Boot 로 Jump!
- RAM에 올라온 U-Boot를 실행한다
- 페리퍼럴들을 초기화
- 커널 로드 / 디바이스 트리 로드
- 커널로 Jump!
- 리눅스 커널을 시작한다
- 커널의 압축을 해제
- 하드웨어의 초기화
- 루트파일시스템 마운트
- 초기 프로세스 실행
- 사용자 서비스 시작
ㅤㅤ
여기에서 이번에는 RBL이 SRAM에 올려준 Secondary Program Loader의 동작에 대해서 살펴볼 예정이다.
Secondary Program Loader
부팅 시퀀스의 두 번째 단계에 SPL (또는 MLO—Memory LOader) 가 사용된다.
ㅤㅤ
SPL의 목적
CPU가 처음 살아났을 때 실행하는 프로그램인 ROM Boot Loader는 프로세서의 내부에 있는 ROM에서 저장되어, 프로세서 내부의 작디작은 64KB의 RAM 에다가 SPL을 올려준다. SPL이 하는 일은 그 다음 단계인 U-Boot의 실행 환경을 메모리에 올리는 일이다.
ㅤㅤ
그렇다면 RBL이 왜 U-Boot를 바로 메모리에 올리면 되는데 구차하게 SPL을 넣었냐? → U-Boot를 올리기에 프로세서 내부에 있는 RAM의 사이즈가 너무 작기 때문이다.
ㅤㅤ
그러면 RBL에서 메인 메모리를 살리면 되는거 아니냐? → RBL은 프로세서에 내장된 프로그램이지, 보드에 대한 정보는 가지고 있지 않다. 그래서 보드에 어떤 사양의, 어떻게 활성화해야 사용할 수 있는 메모리가 장착되어 있는지를 알 수 없기 때문에 + 모든 케이스를 핸들링하는 코드를 RBL에 담기에는 ROM의 사이즈가 너무 작기 때문에 RBL은 이 작업을 하지 못한다. 따라서 사용자가 수정 가능한 SPL에다가 어떤 사양의 장치인지, 어떻게 활성화 할 수 있는지를 담아서 SPL을 실행시켜 메인 메모리를 살리고, 여기에 U-Boot를 집어넣는 단계를 거친다.
ㅤㅤ
SPL이 하는 일
SPL은 다음의 작업을 주로 처리한다. 주요 임무는 역시나 U-Boot의 실행을 위한 처리.
- 메인 메모리 (DRAM) 초기화
- 부팅 장치에서
u-boot.img파일을 찾아서 메인 메모리에 복사하기 - JUMP!
ㅤㅤ
BBB의 메뉴얼에서도 SPL 단계에서는 U-Boot를 살리기 위한 initialization만 수행한다고 되어있다.

ㅤㅤ
소스코드를 까보자
SPL은 U-Boot를 실행하기 위해서 있기 때문에, U-Boot의 빌드에 SPL이 함께 튀어나오게 된다. 그래서 U-Boot의 코드에 SPL의 소스코드가 함께 있다. 매우 웰컴한 점은, U-Boot가 오픈소스이기 때문에 SPL의 코드도 함께 볼 수 있다. 굿. 근데 막상 호기롭게 소스코드를 열어봤지만, 어디에서부터 어떻게 읽어가야 할 지 감이 잘 안온다. 흑쓰따스… 역시나 로우레벨은 어려워.
ㅤㅤ
SPL의 확인을 위해서는 어떤 파일들을 봐야하냐
4개의 레이어를 봐야한다. 뭔데 레이어가 4개나 되냐?! 라고 생각했지만, 찾아보니 맞는 말이였다. + 이게 U-boot 시점에도
- CPU 레이어 - ARM Cortex-A8의 CPU 정보를 알아야 명령어 및 초기화 세팅이 가능함
- SoC 레이어 - SITARA의 AM3358 프로세서의 정보를 알아야 PIN, CLK 등의 주변 장치를 이용 가능
- Board 레이어 - 어떤 사양의 DRAM이 장착되어있는지 알아야 용량, 접근 방식을 이용 가능
- SPL 레이어 - SPL의 실제 코드
ㅤㅤ
소스코드를 한 번 살펴보기 위해서 요 부분은 클로드의 도움을 받았다. 내가 SPL의 동작을 한 번 살펴보고 싶다고 했을 때, 이 경로로 코드를 살펴보는 것을 추천했다.
- cpu의
start.S→ 가장 처음에 실행되는 코드, CPU 초기화 spl.c→ 보드 초기화- SoC의
board.c→ DDR 초기화 spl.c→ U-boot를 DDR에 올리기
ㅤㅤ
1. start.S : CPU를 초기화하자
소스코드는 여기에서 확인할 수 있다.
ㅤㅤ
먼저 CPU의 초기화가 필요하다. 보드 초기화가 덜 된 상태에서 갑자기 Interrupt가 발생해 Jump 하거나 시스템콜 등 supervisor의 권한이 필요한 명령어의 수행을 위해 모드를 변경해줘야 한다.
나름 중간에 불필요하거나 필수가 아닌 포인트는 생략하고, 핵심적인 부분만 트리밍 해서 담아보려고 했다. 실수로 중요한 부분을 지웠을 수도 있다…
arch/arm/cpu/armv7/start.S
/*************************************************************************
*
* Startup Code (reset vector)
*
* Do important init only if we don't start from memory!
* Setup memory and board specific bits prior to relocation.
* Relocate armboot to ram. Setup stack.
*
*************************************************************************/
.globl reset
.globl save_boot_params_ret
.type save_boot_params_ret,%function
[여기에서부터 RBL의 실행 흐름이 SPL에게로 넘어온다]
reset:
/* Allow the board to save important registers */
b save_boot_params
save_boot_params_ret:
<중간 생략>
/*
* disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
* except if in HYP mode already
*/
[FIQ, IRQ 등의 인터럽트를 끄고, 슈퍼바이저모드로 진입함]
mrs r0, cpsr
and r1, r0, #0x1f @ mask mode bits
teq r1, #0x1a @ test for HYP mode
bicne r0, r0, #0x1f @ clear all mode bits
orrne r0, r0, #0x13 @ set SVC mode
orr r0, r0, #0xc0 @ disable FIQ and IRQ
msr cpsr,r0
<중간 생략>
#ifdef CONFIG_CPU_V7A
[CPU의 동작을 제어하는 System Control Register를 설정하는 함수]
bl cpu_init_cp15
#endif
#if !CONFIG_IS_ENABLED(SKIP_LOWLEVEL_INIT_ONLY)
[SoC 외부 하드웨어의 레지스터를 설정하는 함수]
bl cpu_init_crit
#endif
[보드 초기화로 넘어감]
bl _main
ㅤㅤ
위 과정에서 System Control Register의 제어를 담당하는 cpu_init_cp15는 다음처럼 구성이 되어있다. 여기에서는 혹시 캐시된 명령어가 있는지, 캐시된 데이터가 있는지를 확인하고 이를 지우면서 CPU를 초기화하는 역할을 수행한다.
/*************************************************************************
*
* cpu_init_cp15
*
* Setup CP15 registers (cache, MMU, TLBs). The I-cache is turned on unless
* CONFIG_SYS_ICACHE_OFF is defined.
*
*************************************************************************/
ENTRY(cpu_init_cp15)
/* ================================================
* 1. TLB / I-Cache / Branch Predictor 무효화
* - 이전 부팅 잔재 데이터 제거
* - 깨끗한 상태에서 시작하기 위함
* ================================================ */
mov r0, #0
mcr p15, 0, r0, c8, c7, 0 @ TLB 전체 무효화
mcr p15, 0, r0, c7, c5, 0 @ I-Cache 무효화
mcr p15, 0, r0, c7, c5, 6 @ Branch Predictor 무효화
dsb @ 메모리 배리어 (위 명령 완료 보장)
isb @ 명령어 배리어 (파이프라인 flush)
/* ================================================
* 2. SCTLR (c1, c0, 0) 설정
* - CPU 동작 모드의 핵심 제어 레지스터
* ================================================ */
mrc p15, 0, r0, c1, c0, 0 @ SCTLR 읽기
bic r0, r0, #0x00002000 @ bit13 (V) clear : 벡터 테이블 위치 고정 (0x00000000)
bic r0, r0, #0x00000007 @ bit2 (C) clear : D-Cache OFF
@ bit1 (A) clear : 일단 정렬예외 OFF
@ bit0 (M) clear : MMU OFF
orr r0, r0, #0x00000002 @ bit1 (A) set : 정렬예외 다시 ON
@ → 잘못된 메모리 접근 즉시 감지
orr r0, r0, #0x00000800 @ bit11 (Z) set : Branch Target Buffer ON
@ → 분기 예측기는 켜둠 (성능)
orr r0, r0, #0x00001000 @ bit12 (I) set : I-Cache ON
@ → CONFIG_SYS_ICACHE_OFF 시 bic으로 OFF
mcr p15, 0, r0, c1, c0, 0 @ SCTLR 쓰기 (설정 반영)
/* ================================================
* 3. CPU Variant/Revision 읽기
* - ERRATA(하드웨어 버그 픽스) 적용 대상 판별용
* - 특정 CPU 버전에만 존재하는 버그를
* 소프트웨어로 우회하기 위해 버전 확인
* ================================================ */
mrc p15, 0, r1, c0, c0, 0 @ MIDR (Main ID Register) 읽기
mov r3, r1, lsr #20 @ variant 필드 추출 (bit[23:20])
and r3, r3, #0xf @ r3 = CPU variant
and r4, r1, #0xf @ r4 = CPU revision
mov r2, r3, lsl #4
orr r2, r4, r2 @ r2 = variant[7:4] | revision[3:0]
@ → 이후 ERRATA 조건 분기에 사용
mov pc, r5 @ 호출자로 복귀
ENDPROC(cpu_init_cp15)
ㅤㅤ
cpu_init_crit 는 SoC에 연결된 외부 보드의 하드웨어에 대한 레지스터를 설정하는 함수이다. 따라서 외부의 구성에 따라서 코드 구현이 달라져야 한다. 이를 CPU 단에서는 알 수 없기 때문에 아래처럼 lowlevel_init 이라는 함수로 점프해서 처리하도록 되어있다. 요 녀석은 am335x 보드에 대한 코드인 board.c 에 들어있다. 다만, BBB 보드에서는 SKIP_LOWLEVEL_INIT 가 정의되어 있어서 lowlevel_init 함수를 통한 설정 초기화를 생략하고, _main 함수 쪽에서 처리한다.
ㅤㅤ
이 시점까지 코드가 실행되고 나면 아직 CPU 단에 대해서만 초기화가 진행된 상태이다. 프로세서 내에 있는 SRAM 메모리를 이용해 코드가 실행되고 있는 중이고, 메인 메모리인 DDR3는 아직 초기화가 되어있지 않다.
ㅤㅤ
2. _main : C언어 실행 환경으로 넘어가기
소스코드는 여기에서 확인할 수 있다.
ㅤㅤ
위 reset 함수의 마지막에 _main 으로 jump 하는 명령어를 볼 수 있다. 그러면 crt0.S 라는 어셈블리 파일로 이동해서 _main 함수를 실행하게 된다. crt0은 C Runtime 0 (0번째) 라는 의미로, C 실행 환경을 구성하는 절차를 담고있다. C언어 환경이 구성되고, DDR3를 초기화 한 다음 U-Boot를 로드해서 실행하는 과정이 이 _main 에 담겨있다.
ㅤㅤ
crt0.S 파일에서 상단을 살펴보면 이 파일에서 처리하는 일을 아주 친절하고 상세하게 작성해뒀다.
/*
* This file handles the target-independent stages of the U-Boot
* start-up where a C runtime environment is needed. Its entry point
* is _main and is branched into from the target's start.S file.
*
* _main execution sequence is:
*
* 1. Set up initial environment for calling board_init_f().
* This environment only provides a stack and a place to store
* the GD ('global data') structure, both located in some readily
* available RAM (SRAM, locked cache...). In this context, VARIABLE
* global data, initialized or not (BSS), are UNAVAILABLE; only
* CONSTANT initialized data are available. GD should be zeroed
* before board_init_f() is called.
*
* 2. Call board_init_f(). This function prepares the hardware for
* execution from system RAM (DRAM, DDR...) As system RAM may not
* be available yet, , board_init_f() must use the current GD to
* store any data which must be passed on to later stages. These
* data include the relocation destination, the future stack, and
* the future GD location.
*
* 3. Set up intermediate environment where the stack and GD are the
* ones allocated by board_init_f() in system RAM, but BSS and
* initialized non-const data are still not available.
*
* 4a.For U-Boot proper (not SPL), call relocate_code(). This function
* relocates U-Boot from its current location into the relocation
* destination computed by board_init_f().
*
* 4b.For SPL, board_init_f() just returns (to crt0). There is no
* code relocation in SPL.
*
* 5. Set up final environment for calling board_init_r(). This
* environment has BSS (initialized to 0), initialized non-const
* data (initialized to their intended value), and stack in system
* RAM (for SPL moving the stack and GD into RAM is optional - see
* CONFIG_SPL_STACK_R). GD has retained values set by board_init_f().
*
* 6. For U-Boot proper (not SPL), some CPUs have some work left to do
* at this point regarding memory, so call c_runtime_cpu_setup.
*
* 7. Branch to board_init_r().
*
* For more information see 'Board Initialisation Flow in README.
*/
board_init_f()함수를 실행하기 위한 환경을 초기화한다. 이때 메인 메모리(DRAM)를 아직 사용할 수 없는 상태라, 프로세서 내부 메모리(SRAM, L1 캐시 등)에 스택이나 상수 등을 할당한다.board_init_f()함수를 호출한다. 이 함수에서는 시스템 호출을 위한 메인메모리 하드웨어 초기화를 담당한다. 여기에 새로 사용할 스택, 상수 위치 등을 확인한다.- SRAM에 담겨있는 스택, 상수, 전역 데이터 등을 초기화된 DRAM으로 이동한다.
- (SPL을 거쳐서 U-boot로 가기 때문에)
_main의 실행 흐름으로 돌아간다. board_init_r()함수를 위한 환경을 준비하기 위해, BSS을 0으로 초기화하고 변수 데이터에 값을 집어넣는다.board_init_r()함수를 호출한다. 이 함수에서 각종 디바이스와 네트워크를 초기화하고 커널 부팅을 시작하러 떠난다.
ㅤㅤ
즉, board_init_f 함수에서 메인 메모리를 초기화한다. 해당 함수 실행 이전에는 꼭 필요한 상수들만 잠시 SRAM에 올리고, 활성화 이후에는 이걸 다시 메인 메모리로 이동시킨다. board_init_r 함수의 실행 전에 변수 값들을 메인 메모리에 넣어주고, 해당 함수에서 이제 보드의 다른 부분들을 활성화하게 된다.
ㅤㅤ
코드를 잠깐 정리해달라 요청했다. 여기에서도 if로 감싸져서 분기되는 중간 로직들은 쭉쭉 생략하고 간다.
// arch/arm/lib/crt0.S
ENTRY(_main)
/* ================================================
* 0. 아키텍처 초기화 (옵션)
* - 일부 아키텍처에서만 사용
* - BBB(AM335x)는 해당 없음
* ================================================ */
#if CONFIG_IS_ENABLED(ARCH_VERY_EARLY_INIT)
bl arch_very_early_init
#endif
/* ================================================
* 1. C 런타임 환경 초기화
* - 스택 포인터(SP) 설정
* - gd(global data) 포인터 설정
* - BSS 영역 클리어
* → 이 이후부터 C 함수 호출 가능
* ================================================ */
[실행 환경에 대한 초기화 코드 생략]
/* ================================================
* 2. SPL 1차 초기화
* - am33xx/board.c의 board_init_f() 호출
* - 내부에서:
* hw_data_init() 하드웨어 데이터 포인터 초기화
* early_system_init() WDT/UART/Clock 초기화
* board_early_init_f() prcm_init/핀맵 설정
* sdram_init() DDR3 초기화
* ================================================ */
mov r0, #0
bl board_init_f
/* ================================================
* 3. SPL 스택을 SRAM → DDR3로 이전 (옵션)
* - sdram_init() 완료 후 DDR3 사용 가능
* - 더 넓은 DDR3 스택으로 전환
* - r0 = 0 이면 스택 이전 없이 그대로 진행
* ================================================ */
#ifdef CONFIG_XPL_BUILD
bl spl_relocate_stack_gd /* 새 스택 주소 반환 */
cmp r0, #0
movne sp, r0 /* r0 != 0 이면 SP를 DDR3로 전환 */
movne r9, r0 /* gd 포인터도 DDR3로 전환 */
#endif
/* ================================================
* 4. SPL 2차 초기화 → U-Boot로 Jump
* - board_init_r() 호출
* - 내부에서:
* spl_boot_device() 부팅 디바이스 감지
* spl_load_image() u-boot.img 로드
* jump_to_image_no_args() U-Boot로 Jump
* - 이 함수는 리턴하지 않음 (__noreturn)
* ================================================ */
mov r0, r9 /* r0 = gd 포인터 */
ldr r1, [r9, #GD_RELOCADDR] /* r1 = 재배치 주소 */
ldr pc, =board_init_r /* board_init_r()로 Jump */
/* 여기서 SPL 종료, U-Boot 시작 */
ENDPROC(_main)
ㅤㅤ
3. board_init_f : 메인메모리의 초기화
앞서 설명을 요약해봤던 것처럼, board_init_f 함수에서는 메인메모리의 초기화가 이루어진다. 그래서 메인 메모리가 올라간 보드의 초기화 부분을 찾기 위해서 board.c 파일을 찾아 열어봤다. 아래처럼 하드웨어 정보를 확인하고, sdram_init() 함수를 통해서 메인메모리를 활성화하는 과정을 거치는 것을 볼 수 있다!
// arch/arm/mach-omap2/am33xx/board.c
#ifdef CONFIG_XPL_BUILD
void board_init_f(ulong dummy)
{
hw_data_init();
early_system_init();
board_early_init_f();
sdram_init();
/* dram_init must store complete ramsize in gd->ram_size */
gd->ram_size = get_ram_size(
(void *)CFG_SYS_SDRAM_BASE,
CFG_MAX_RAM_BANK_SIZE);
}
#endif
ㅤㅤ
여기에서 메인메모리 활성화를 담당할 것만 같은 “아주 수상해보이는” 함수가 있다. sdram_init() 를 실행하면 DRAM의 초기화가 일어날 것만 같아서, 요 녀석의 소스코드를 살펴봤다. 이 코드는 보드에 관련된 내용이기 때문에 일부러 TI (Texas Instrument)의 폴더로 들어가서 찾아봤다. 관련이 없어보이는 녀석들은 … 으로 생략해버렸다. 여기 코드 내에서 beagleblack 이라는 아주 반가운 키워드를 확인할 수 있었다.
// board/ti/am335x/board.c
void sdram_init(void)
{
if (board_is_evm_sk()) {
/*
* EVM SK 1.2A and later use gpio0_7 to enable DDR3.
* This is safe enough to do on older revs.
*/
gpio_request(GPIO_DDR_VTT_EN, "ddr_vtt_en");
gpio_direction_output(GPIO_DDR_VTT_EN, 1);
}
if (board_is_icev2()) {
gpio_request(ICE_GPIO_DDR_VTT_EN, "ddr_vtt_en");
gpio_direction_output(ICE_GPIO_DDR_VTT_EN, 1);
}
if (board_is_evm_sk()) ...;
else if (board_is_pb() || board_is_bone_lt())
config_ddr(400, &ioregs_bonelt,
&ddr3_beagleblack_data,
&ddr3_beagleblack_cmd_ctrl_data,
&ddr3_beagleblack_emif_reg_data, 0);
else if (board_is_evm_15_or_later()) ...;
else if (board_is_icev2()) ...;
else if (board_is_gp_evm()) ...;
else ...;
}
// 요 beagleblack의 정보는 이렇게 정리가 되어있었다.
static const struct ddr_data ddr3_beagleblack_data = {
.datardsratio0 = MT41K256M16HA125E_RD_DQS,
.datawdsratio0 = MT41K256M16HA125E_WR_DQS,
.datafwsratio0 = MT41K256M16HA125E_PHY_FIFO_WE,
.datawrsratio0 = MT41K256M16HA125E_PHY_WR_DATA,
};
ㅤㅤ
즉, 보드에 박혀있는 하드웨어의 초기화를 위해서 요렇게 보드에서 실제로 사용하려는 사양과 방식에 대해서 입력을 해줘야한다. 비글본의 메뉴얼을 보면 MT41K256M16HA125 라는 제품에 대한 정보가 코드와 동일하게 박혀있는 것을 확인할 수 있다. 내가 사용하는 보드에서는 Micron의 제품을 사용하는 것으로 보인다.

ㅤㅤ
이 파라미터들을 가지고 config_ddr 이라는 함수에서 하드웨어를 초기화하고 있는데, 요 함수는 레지스터의 값들을 세팅하는 역할을 한다(고 한다). 아무래도 이전에 프로젝트 하면서 봤던 센서 모듈들을 제어하는 방식이랑 비슷하게 레지스터들을 세팅해서 통신을 준비하는 작업을 하는게 아닐런지.
ㅤㅤ
4. board_init_r : U-Boot를 로드
요 함수는 공용 함수인 spl.c 에 있는 파일을 사용하는 것으로 생각되어서 가져왔다. 코드가 생각보다 길고 if문으로 분기처리되는게 많아서 클로드로 주석치고 정리를 한 번 해달라고 했다.
// common/spl/spl.c
void board_init_r(gd_t *dummy1, ulong dummy2)
{
/* 1. 구조체 및 변수 초기화 */
struct spl_image_info spl_image; // 다음 단계로 넘길 이미지 정보를 담는 구조체
spl_jump_to_image_t jumper = &jump_to_image; // 기본 점프 함수 설정
// 스택 포인터 등 기본 시스템 데이터(GD) 설정
spl_set_bd();
/* 2. 메모리 할당 및 시스템 타이머 초기화 */
// 동적 메모리 할당(malloc)을 위한 힙 영역 준비
if (IS_ENABLED(CONFIG_SPL_SYS_MALLOC)) {
mem_malloc_init(SPL_SYS_MALLOC_START, SPL_SYS_MALLOC_SIZE);
}
// 시스템 타이머 가동 (각종 딜레이 및 타임아웃 계산용)
timer_init();
/* 3. 드라이버 모델(DM) 및 보드 전용 초기화 */
// 디바이스 드라이버 체계 초기화
if (!(gd->flags & GD_FLG_SPL_INIT)) {
if (spl_init()) hang();
}
// 보드 수준의 추가 하드웨어 초기화 (이전에 확인한 I2C, MUX 설정 등 호출 가능)
if (CONFIG_IS_ENABLED(BOARD_INIT))
spl_board_init();
/* 4. 부팅 장치 탐색 및 이미지 로드 (가장 중요) */
// 부팅 순서 결정 (SD카드, eMMC, NAND 등 어디서 불러올지)
board_boot_order(spl_boot_list);
// 결정된 장치들로부터 다음 단계 이미지(U-Boot Proper 등)를 RAM으로 읽어옴
ret = boot_from_devices(&spl_image, spl_boot_list, ARRAY_SIZE(spl_boot_list));
if (ret) hang(); // 로드 실패 시 시스템 중단
/* 5. 로드된 이미지의 타입 분석 (OS 종류 판별) */
os = spl_image.os;
if (os == IH_OS_U_BOOT) {
// 일반적인 경우: U-Boot 본체로 이동 준비
} else if (os == IH_OS_LINUX) {
// 'Falcon Mode': U-Boot를 거치지 않고 바로 리눅스 커널로 점프 준비
jumper = &jump_to_image_linux;
}
// ... (필요 시 OP-TEE, ATF 등의 보안 펌웨어 처리)
/* 6. 최종 점프 수행 */
debug("Jumping to next image...\n");
// 최종적으로 하드웨어 상태를 점검하고 다음 단계로 CPU 실행 권한을 넘김
spl_board_prepare_for_boot();
// 지정된 주소(DRAM 내 이미지 위치)로 프로그램 카운터(PC) 이동
jumper(&spl_image);
}
ㅤㅤ
board_init_r 함수에서 하는 일을 다시 한 번 나열해보면
- 구조체와 변수 초기화
- 힙 영역 준비 및 시스템 타이머 가동
- 디바이스 드라이버 시스템 및 보드의 하드웨어 주변장치 초기화
- 부팅 장치 탐색 및 이미지 로드
- 로드한 이미지에 따라서 U-Boot 또는 커널(U-Boot 거치지 않는 경우)로 점프 준비
- 이미지로 실행 흐름 전달
ㅤㅤ
그래서 SPL이 뭘 한다고?
SPL이 하는 일을 결국 DRAM의 초기화 + U-Boot 이미지를 RAM에 올리기이다. 실제로 소스코드에서도 이를 확인할 수 있었다.
ㅤㅤ
다만 이 과정이 역시나 마법처럼 이뤄지는 것은 아니고, [ 프로세서의 SRAM을 기반으로 필요한 변수를 잠시 초기화 → DRAM에 레지스터 값 세팅 → DRAM으로 정해진 주소에 SRAM 의 데이터 옮기기 → 기타 타이머, SPI 등의 하드웨어 초기화 → DRAM에 U-Boot 이미지 올리기 → JUMP로 실행흐름 전달 ]의 과정이 엘레강스하게 이뤄지고 있다.
ㅤㅤ
다만 마지막 부분에 가니 디바이스 드라이버 시스템에 대한 초기화 및 주변장치 초기화에 대한 키워드가 튀어나왔는데, 요 부분은 U-Boot의 부팅 단계에서 진행이 되는 것으로 알고있었어서, 실제로 동작이 어떻게 진행되는지나 차이점이 뭔지는 다음 U-Boot 정리에서 한 번 살펴봐야할 것 같다.
'Embedded System > Embedded Linux' 카테고리의 다른 글
| [Embedded Linux] BeagleBone Black의 부팅과정 (1) - ROM Boot Loader (1) | 2026.03.15 |
|---|---|
| [Embedded Linux] BeagleBone Black에서 왜 BOOT 버튼을 안눌러도 SD카드로 바로 부팅이 될까 (0) | 2026.03.14 |
| [Embedded Linux] M1 Mac에서 비글본 블랙 SSH 연결하기 (0) | 2026.03.09 |
| [Embedded Linux] M1 Mac에서의 비글본 블랙 부팅하기 (0) | 2026.03.09 |
| [Embedded Linux] 독학용 리눅스 개발 보드 고르기, BeagleBone Black (1) | 2026.02.24 |