U-Boot가 준비한 것을 Linux가 어떻게 받나요?
기존 booti 글은 U-Boot의 이미지 검사·배치를, head.S 글은 커널 진입 이후를 다룹니다. 여기서는 U-Boot v2026.07과 Linux v6.18.37 사이에 넘기는 데이터와 실행 상태를 연결합니다. ARM64 Image를 직접 부팅하는 경로를 기준으로 하며, EFI 진입은 별도로 구별합니다.
| 전달 항목 | U-Boot가 준비하는 쪽 | Linux가 처음 사용하는 쪽 |
|---|---|---|
| 커널 실행 위치 | images->ep | Image 헤더의 실행 명령에서 primary_entry로 진행합니다. |
| Device Tree | images->ft_addr를 x0로 전달 | preserve_boot_args가 DTB 주소를 보존합니다. |
| 커널 command line | 최종 DTB의 /chosen/bootargs | FDT 초기 스캔에서 읽습니다. 빌드의 CONFIG_CMDLINE 정책도 최종 값에 영향을 줄 수 있습니다. |
| initramfs 위치 | 최종 DTB의 linux,initrd-start/end | 커널의 초기 FDT 처리가 위치와 크기를 얻습니다. |
| 사용할 수 없는 RAM | DT의 예약 정보와 플랫폼 메모리 설정 | 초기 메모리 관리에서 보존·제외해야 할 영역을 처리합니다. |
표의 대응은 정보 전달 관계입니다. initramfs 주소를 ARM64의 x1과 x2에 직접 넘긴다는 뜻이 아닙니다.
직접 ARM64 부팅에서 x1~x3은 0으로 넘기는 예약 인자입니다. 다른 아키텍처의 부팅 레지스터 규약을 가져오면 안 됩니다. 이 버전의 ARM64 부팅 규약
분기 직전까지는 U-Boot의 주소와 상태입니다
v2026.07 · arch/arm/lib/bootm.c · 259–294행
void (*kernel_entry)(void *fdt_addr, void *res0, void *res1,
void *res2);
kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1,
void *res2))images->ep;
debug("## Transferring control to Linux (at address %lx)...\n",
(ulong) kernel_entry);
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
bootm_final(flag);
cleanup_before_linux();
if (!(flag & BOOTM_STATE_OS_FAKE_GO)) {
#ifdef CONFIG_ARMV8_PSCI
armv8_setup_psci();
#endif
do_nonsec_virt_switch();
update_os_arch_secondary_cores(images->os.arch);
#ifdef CONFIG_ARMV8_SWITCH_TO_EL1
armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0,
(u64)switch_to_el1, ES_TO_AARCH64);
#else
if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) &&
(images->os.arch == IH_ARCH_ARM))
armv8_switch_to_el2(0, (u64)gd->bd->bi_arch_number,
(u64)images->ft_addr, 0,
(u64)images->ep,
ES_TO_AARCH32);
else
armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0,
images->ep,
ES_TO_AARCH64);
#endif
}| 코드에서 볼 부분 | 이 줄이 하는 일과 다음 단계에 미치는 영향 |
|---|---|
kernel_entry = ... images->ep | 앞에서 결정한 커널의 실행 위치를 사용합니다. Image를 읽어 놓은 임시 버퍼가 반드시 최종 실행 위치인 것은 아닙니다. |
do_nonsec_virt_switch()와 EL 전환 | 지원 구성에 따라 비보안 실행과 EL 전환을 준비합니다. 부팅 시 어느 EL에서 시작했는지에 따라 실제 경로가 달라집니다. |
armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0, ...) | AArch64 경로에서 DTB 주소와 예약 인자를 다음 단계로 전달합니다. 주변의 AArch32용 분기를 같은 규약으로 읽지 않습니다. |
CONFIG_ARMV8_SWITCH_TO_EL1 | 빌드에 따라 EL1 전환 루틴을 거치는 분기가 포함될 수 있습니다. 무조건 EL2에서 커널을 실행한다는 뜻이 아닙니다. |
DTB를 옮기거나 fixup했다면 최종 주소를 넘겨야 합니다. U-Boot의 환경변수에 남은 원래 적재 주소와 images->ft_addr가 다를 수 있습니다. 커널·DTB·initramfs 구간이 서로 덮이지 않아야 하고, 커널 진입 전에 DMA가 예상치 못한 쓰기를 하지 않도록 장치 상태도 정리해야 합니다.
x0의 DTB 주소가 커널의 입력으로 남습니다
v6.18.37 · arch/arm64/kernel/head.S · 170–185행
SYM_CODE_START_LOCAL(preserve_boot_args)
mov x21, x0 // x21=FDT
adr_l x0, boot_args // record the contents of
stp x21, x1, [x0] // x0 .. x3 at kernel entry
stp x2, x3, [x0, #16]
cbnz x19, 0f // skip cache invalidation if MMU is on
dmb sy // needed before dc ivac with
// MMU off
add x1, x0, #0x20 // 4 x 8 bytes
b dcache_inval_poc // tail call
0: str_l x19, mmu_enabled_at_boot, x0
ret
SYM_CODE_END(preserve_boot_args)| 코드에서 볼 부분 | 이 줄이 하는 일과 다음 단계에 미치는 영향 |
|---|---|
mov x21, x0 | 부트로더가 넘긴 DTB 물리 주소를 x21에 복사합니다. 원래 x21 값은 덮어쓰지만 DTB 메모리 내용 자체를 복사하지는 않습니다. |
adr_l x0, boot_args | 인자 보존용 커널 메모리의 주소를 만듭니다. 이 뒤 x0는 더 이상 DTB 주소가 아닙니다. |
stp x21, x1와 stp x2, x3 | 진입 때의 네 인자를 메모리에 보존합니다. |
| MMU 상태에 따른 분기와 cache 정리 | 초기 주소 접근과 cache 가시성 조건에 맞춰 보존한 인자를 처리합니다. 분기와 barrier를 생략한 단순 포인터 복사만으로 전체 초기화가 끝나지 않습니다. |
- U-Boot의 최종 DTB
메모리에 놓인 FDT 바이트와 물리 주소 P_dtb를 준비합니다.
x0로 물리 주소 전달
- head.S
x21과 boot_args에 진입 정보를 보존합니다.
초기 커널의 DTB 처리에 주소 제공
- setup_machine_fdt / fixmap
물리 주소 P_dtb에 있는 바이트를 커널이 접근할 가상 주소로 연결합니다.
예약과 FDT 구조 스캔
- 커널의 메모리·장치 설정
RAM, 예약 구간, chosen 및 장치 정보를 사용합니다.
화살표는 주소와 정보가 다음 처리에 전달되는 방향입니다. 각 상자마다 DTB 전체를 복사하거나 새로운 DTB를 만든다는 의미는 아닙니다.
물리 주소를 받았다는 사실과 C에서 그 주소를 바로 역참조할 수 있다는 사실은 다릅니다. 매핑, 메모리 예약, DT 내용 해석을 따로 읽어야 합니다. setup_machine_fdt의 기존 설명에서 그 부분을 이어 보실 수 있습니다.
주소가 맞아도 CPU 실행 조건이 틀리면 실패합니다
| 조건 | 연동에서 확인할 내용 |
|---|---|
| 진입 EL과 보안 상태 | 일반적인 직접 부팅 규약의 비보안 EL1/EL2 조건과 실제 U-Boot 설정을 맞춥니다. |
| MMU / cache | 문서의 직접 부팅 조건을 확인합니다. head.S에 다른 진입 상태를 처리하는 코드가 있다는 이유로 임의의 매핑·cache 상태가 허용된다고 확대 해석하지 않습니다. |
| 예외 마스크와 타이머 | DAIF 및 카운터 초기 상태 등 커널 부팅 규약의 필수 조건을 확인합니다. |
| 여러 CPU의 cache 일관성 | 보조 CPU도 요구되는 coherency 환경에 들어와야 합니다. 시작 주소만 정확하다고 충분하지 않습니다. |
| EFI 경로 | PE/COFF의 EFI stub 진입과 일반 Image 진입을 구별합니다. EFI entry의 인자를 곧바로 DTB x0 규약으로 해석하지 않습니다. |
“Starting kernel ...” 뒤에 로그가 끊겨도 커널 코드가 반드시 실행됐다고 단정하지 않습니다. 실제 분기 주소, 해당 주소의 Image 내용, DTB 값과 진입 EL을 먼저 구분해서 확인합니다.
이미지 검증과 커널 진입은 별도 단계입니다
FIT의 구성 선택과 서명 검증은 “어떤 이미지를 실행하도록 허용하는가”를 결정합니다. 그다음 주소 배치와 CPU 상태 준비는 “그 이미지가 올바른 조건에서 실행되는가”를 결정합니다. 서명 검증을 통과해도 DTB 주소가 틀리면 부팅은 실패할 수 있습니다.
TF-A의 FIP와 U-Boot의 FIT는 이름이 비슷해도 같은 포맷이 아닙니다. 전체 신뢰 관계는 ROM에서 어떤 키를 신뢰하는지, TF-A와 U-Boot가 어느 이미지를 검증하는지, 실패 때 실행을 막는지까지 연결해야 합니다. OP-TEE가 있다는 사실만으로 전체 secure boot가 구성됐다고 볼 수 없습니다.
기준 소스와 확인 범위
각 코드의 링크는 위 버전의 고정 커밋을 가리킵니다. 프로젝트별 버전을 함께 명시한 것은 비교 기준이며, 이 조합을 특정 보드에서 빌드·부팅해 호환성을 검증했다는 뜻은 아닙니다. 코드는 표시한 범위의 실제 원문이며, 설명 표는 그 범위의 동작을 묶어서 읽도록 작성했습니다.