ARM64 Secondary Core Bring-up — PSCI CPU_ON 호출부터 EL1 진입까지
#한 줄 요약
“SMP 부트는 primary CPU가 secondary CPU 각각에 진입점을 알려 주고 한 줄씩 일으켜 세우는 순서입니다.” — BL1·BL2는 single CPU로 진행되고, BL31이 primary 한 명만 BL33으로 보냅니다. 나머지 CPU들은 Linux가 primary 위에서 동작한 뒤에야 PSCI SMC로 깨워집니다. 옛 spin-table 방식과 현재 표준 PSCI 방식이 어떻게 다른지가 핵심입니다.
Ch 32에서 PSCI CPU_ON의 ABI를 봤습니다. 이 장은 그 반대편을 봅니다. CPU_ON이 trigger된 뒤 secondary CPU가 어떤 어셈블리를 통과해 Linux의 secondary_start_kernel에 도착하는지, 그 사이에 어떤 자료구조가 초기화되는지를 따라갑니다. 비교 대상으로 옛 spin-table 방식도 함께 봅니다.
#부트 시퀀스 안에서 SMP가 일어나는 자리
ARMv8-A 부트 체인은 primary CPU 한 명만으로 진행됩니다. BL1·BL2는 single CPU 가정으로 짜여 있습니다. 나머지 CPU 코어들은 reset 직후 holding pen 또는 WFI로 묶여 있습니다.
primary가 부트의 거의 끝까지 혼자 가고, 마지막에 secondary들을 한 명씩 깨웁니다. 이 모델의 장점은 부트 초반의 race를 원천 차단하는 것입니다. DDR initialization, secure carveout setup, MMU page table build 같이 원자적 단일 흐름이 필요한 작업을 race 걱정 없이 진행할 수 있습니다.
전체 SMP bring-up 흐름을 한눈에 보면 다음과 같습니다.
#두 가지 방식 — spin-table vs PSCI
secondary CPU를 깨우는 방식은 mainline 커널 기준 두 가지입니다.
#spin-table (legacy)
ARMv8 spec 초기에 정의된 방식입니다. secondary CPU가 지정된 메모리 주소를 polling하며 WFE로 대기합니다. primary가 그 주소에 entry point를 write하고 SEV하면, secondary가 깨어나 그 주소로 점프합니다.
[Boot]Primary 진행 Secondary──────── ────────── ldr x0, [release_addr] cbz x0, wait br x0 ← 진입 wait: wfe b loop
Linux primary: str <entry>, [release_addr] sev ← secondary wakes uprelease_addr는 각 CPU마다 다른 주소고 DT에 cpu-release-addr = <0x0 0x70000000> 형태로 적힙니다. BL31이 부팅 후 secondary CPU들을 그 주소에서 WFI/WFE 상태로 둬야 합니다.
#PSCI CPU_ON (현재 표준)
primary가 SMC를 통해 BL31에 직접 깨우기를 요청합니다. BL31이 SoC reset controller를 두드려 secondary CPU의 reset을 풀고, warm boot path에서 caller가 지정한 entry로 점프시킵니다. spin-table과 달리 busy-loop polling이 필요 없고 진정한 power-down에서 깨울 수 있습니다.
| 항목 | spin-table | PSCI |
|---|---|---|
| 깨우는 메커니즘 | 메모리 polling + SEV | SMC + SoC reset controller |
| CPU의 전력 상태 | WFI/WFE (clock 그대로) | 진정한 power-down 가능 |
| HOTPLUG_CPU 지원 | 미흡 (재진입 어려움) | CPU_OFF로 완전 지원 |
| suspend-to-RAM | 미지원 | 지원 (CPU_SUSPEND) |
| 표준 | ARMv8 옛 spec | PSCI v1.0+ |
| Linux DT 표기 | enable-method = "spin-table" | enable-method = "psci" |
mainline 커널 기준 PSCI가 사실상 강제다. spin-table은 PSCI가 없는 환경(예: hypervisor 없이 EL2에서 직접 부팅하는 일부 minimal boot)이나 legacy 보드에만 남아 있습니다. 새로 만드는 모든 ARMv8 보드는 PSCI를 씁니다.
#DT 표기 — enable-method가 분기점
커널의 secondary CPU 깨우기 분기는 DT의 enable-method 속성으로 결정됩니다.
/* PSCI 방식 */cpus { cpu@0 { device_type = "cpu"; compatible = "arm,cortex-a53"; reg = <0x0>; enable-method = "psci"; }; cpu@1 { device_type = "cpu"; compatible = "arm,cortex-a53"; reg = <0x1>; enable-method = "psci"; };};
psci { compatible = "arm,psci-1.0"; method = "smc";};/* spin-table 방식 (legacy) */cpus { cpu@0 { device_type = "cpu"; compatible = "arm,cortex-a53"; reg = <0x0>; enable-method = "spin-table"; cpu-release-addr = <0x0 0x70000fff8>; }; cpu@1 { device_type = "cpu"; compatible = "arm,cortex-a53"; reg = <0x1>; enable-method = "spin-table"; cpu-release-addr = <0x0 0x70000fff0>; };};같은 cpus 노드에 enable-method가 섞여 있어도 됩니다. 일부 CPU는 PSCI, 일부는 spin-table로 깨우는 hybrid 보드가 드물게 존재합니다.
두 방식의 차이를 그림으로 비교하면 다음과 같습니다.
#Linux primary의 SMP 진입 흐름
커널이 secondary CPU를 깨우는 코드 경로를 따라갑니다. start_kernel이 마지막에 rest_init → kernel_init → kernel_init_freeable → smp_init을 호출합니다.
void __init smp_init(void){ unsigned int cpu;
/* 모든 possible CPU에 대해 cpu_up 호출 */ for_each_present_cpu(cpu) { if (num_online_cpus() >= setup_max_cpus) break; if (!cpu_online(cpu)) cpu_up(cpu); }}cpu_up이 _cpu_up → bringup_cpu → __cpu_up(arch-specific)으로 내려갑니다.
int __cpu_up(unsigned int cpu, struct task_struct *idle){ int ret; long status;
/* 1. idle task 등록 — secondary가 첫 진입할 stack */ secondary_data.task = idle; secondary_data.stack = task_stack_page(idle) + THREAD_SIZE; update_cpu_boot_status(CPU_MMU_OFF); __flush_dcache_area(&secondary_data, sizeof(secondary_data));
/* 2. enable-method별 ops 호출 */ ret = cpu_ops[cpu]->cpu_boot(cpu); if (ret) return ret;
/* 3. secondary가 online 될 때까지 wait */ wait_for_completion_timeout(&cpu_running, msecs_to_jiffies(5000));
if (!cpu_online(cpu)) return -EIO; return 0;}cpu_ops[cpu]는 enable-method에 따라 다른 ops 구조체입니다.
static const struct cpu_operations *const supported_cpu_ops[] = { &smp_spin_table_ops, &cpu_psci_ops, NULL,};PSCI 방식이면 cpu_psci_ops.cpu_boot = cpu_psci_cpu_boot가 호출되어 SMC를 발사합니다.
static int cpu_psci_cpu_boot(unsigned int cpu){ phys_addr_t pa_secondary_entry = __pa_symbol(secondary_entry); int err = psci_ops.cpu_on(cpu_logical_map(cpu), pa_secondary_entry);
if (err) pr_err("failed to boot CPU%d (%d)\n", cpu, err); return err;}secondary_entry가 secondary CPU가 첫 진입할 어셈블리 라벨입니다. PSCI CPU_ON에 physical address로 넘긴다는 점이 중요합니다.
#secondary_entry 어셈블리 — MMU off에서 시작
secondary_entry부터 __secondary_switched까지의 어셈블리가 SMP bring-up의 진짜 핵심입니다. 이 코드는 MMU off, cache off 상태에서 시작해 MMU on, cache on까지 가져갑니다.
SYM_CODE_START(secondary_entry) bl init_kernel_el /* EL1 또는 EL2 결정 */ bl set_cpu_boot_mode_flag b secondary_startupSYM_CODE_END(secondary_entry)
SYM_CODE_START_LOCAL(secondary_startup) /* * 공통 entry — secondary 모두가 통과. * stack은 still primary의 secondary_data에서 가져옴. */ mov x20, x0 /* preserve boot mode */ bl __cpu_secondary_check52bitva bl __cpu_setup /* TCR_EL1, MAIR_EL1 등 set up */ adrp x1, swapper_pg_dir /* primary가 만든 page table */ adrp x2, idmap_pg_dir bl __enable_mmu /* MMU on */ ldr x8, =__secondary_switched br x8SYM_CODE_END(secondary_startup)각 단계가 다음을 합니다.
| 단계 | 내용 |
|---|---|
init_kernel_el | EL2면 EL1으로 강하 (EL2 register init 포함) |
set_cpu_boot_mode_flag | EL1으로 시작했는지 EL2로 시작했는지 기록 |
__cpu_secondary_check52bitva | 52-bit VA 지원 일치 검증 |
__cpu_setup | TCR_EL1·MAIR_EL1·SCTLR_EL1 set up |
__enable_mmu | TTBR0·TTBR1 set, SCTLR_EL1.M·C·I bit set |
__secondary_switched | MMU on 상태로 C 코드 진입 |
MMU를 켜는 순간 primary가 만들어 둔 swapper_pg_dir가 secondary에도 보이게 됩니다. 이 page table은 모든 CPU가 공유하므로 별도 build가 필요 없습니다. __cpu_setup이 TCR_EL1의 TG0/TG1(page size), IPS(physical addr size)를 primary와 일치시켜 page table을 그대로 쓸 수 있게 만듭니다.
#__secondary_switched — C 코드로 점프
MMU가 켜진 직후의 첫 C 함수가 secondary_start_kernel입니다.
SYM_FUNC_START_LOCAL(__secondary_switched) mov x0, x20 /* boot mode */ bl set_cpu_boot_mode_flag
/* secondary_data로부터 idle task, stack 가져옴 */ adr_l x0, secondary_data ldr x1, [x0, #CPU_BOOT_STACK] /* stack */ cbz x1, __secondary_too_slow mov sp, x1 ldr x2, [x0, #CPU_BOOT_TASK] /* task_struct */ cbz x2, __secondary_too_slow msr sp_el0, x2 /* current_thread_info */
scs_load_current /* shadow call stack */ mov x29, #0 mov x30, #0 b secondary_start_kernelSYM_FUNC_END(__secondary_switched)sp_el0에 task_struct를 넣어 두는 것이 ARM64 커널의 current 매크로를 동작하게 만드는 핵심입니다. 그래서 current = (struct task_struct *) read_sysreg(sp_el0)이 어떤 CPU에서든 자기 자신의 task를 가리킵니다.
asmlinkage notrace void secondary_start_kernel(void){ u64 mpidr = read_cpuid_mpidr() & MPIDR_HWID_BITMASK; struct mm_struct *mm = &init_mm; unsigned int cpu = smp_processor_id();
/* 1. percpu offset 설정 */ set_my_cpu_offset(per_cpu_offset(cpu));
/* 2. cpu info 채우기 (cpuinfo_arm64) */ cpuinfo_store_cpu();
/* 3. mm context 활성화 */ mmgrab(mm); current->active_mm = mm;
/* 4. GIC CPU interface 초기화 */ if (cpu_ops[cpu]->cpu_postboot) cpu_ops[cpu]->cpu_postboot();
/* 5. local timer, IRQ enable */ notify_cpu_starting(cpu); store_cpu_topology(cpu); numa_add_cpu(cpu);
/* 6. online 표시 → primary가 wait에서 깨어남 */ set_cpu_online(cpu, true); complete(&cpu_running);
/* 7. local IRQ enable, idle loop 진입 */ local_daif_restore(DAIF_PROCCTX); cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);}set_cpu_online(cpu, true)이 그 CPU가 동작 가능함을 scheduler에 알리는 신호입니다. 이 줄이 실행되는 순간 scheduler가 그 CPU에 task를 배정하기 시작합니다. complete(&cpu_running)이 primary의 wait_for_completion_timeout을 깨웁니다. 그 다음 cpu_startup_entry가 idle loop로 들어가고 첫 timer interrupt가 도착하면 scheduler가 work를 배정합니다.
#percpu 자료구조 초기화
set_my_cpu_offset(per_cpu_offset(cpu))이 secondary의 percpu base register를 set up합니다. ARM64는 TPIDR_EL1를 percpu offset register로 씁니다.
static inline void set_my_cpu_offset(unsigned long off){ asm volatile("msr tpidr_el1, %0" :: "r" (off) : "memory");}
static inline unsigned long __my_cpu_offset(void){ unsigned long off; asm("mrs %0, tpidr_el1" : "=r" (off)); return off;}이 offset이 설정되기 전에 percpu variable 접근을 시도하면 primary CPU의 변수에 접근하게 되어 race가 납니다. set_my_cpu_offset 호출 직후부터 비로소 percpu access가 안전합니다.
GIC CPU interface 초기화는 cpu_postboot ops에서 합니다. PSCI ops의 경우 다음이 호출됩니다.
static void gic_cpu_init(void){ void __iomem *rbase; int i;
/* GICR (Re-distributor) base 계산 */ rbase = gic_data_rdist_sgi_base();
/* SGI/PPI enable */ for (i = 0; i < 32; i += 32) writel_relaxed(GICD_INT_DEF_PRI_X4, rbase + GIC_DIST_PRI + i);
/* CPU interface enable */ gic_cpu_sys_reg_init();}GIC CPU interface(GICC/GICR)는 CPU별로 자기 자신만 접근 가능합니다. 그래서 secondary CPU 위에서 직접 초기화해야 합니다. primary가 대신 못 합니다. 이게 secondary CPU bring-up이 단순히 reset release만으로 끝나지 않는 이유 중 하나입니다.
#HOTPLUG_CPU — CPU_OFF 흐름
echo 0 > /sys/devices/system/cpu/cpu1/online으로 CPU를 끄면 reverse 흐름이 진행됩니다.
echo 0 > .../cpu1/online │ ▼cpu_down(1) → __cpu_down → cpuhp_kick_ap │ ▼take_cpu_down (CPU1에서 실행) │ ▼__cpu_disable ├─ migrate IRQs to 다른 CPU ├─ disable local timer └─ cpu_psci_cpu_disable │ ▼cpu_die (CPU1) ├─ idle_task_exit ├─ complete(&cpu_died) └─ arch_cpu_idle_dead │ ▼ cpu_psci_cpu_die │ ▼ smc PSCI_CPU_OFF ← BL31로 trap │ ▼ [BL31] platform pwr_domain_off │ ▼ SoC reset assert → CPU1 전원 차단CPU_OFF SMC는 return하지 않는다. 호출한 CPU 자신이 power down되기 때문입니다. CPU1이 사라진 뒤 primary는 wait_for_completion(&cpu_died)에서 깨어나 hotplug list에서 CPU1을 제거합니다.
다시 켤 때는 처음 SMP bring-up과 같은 경로가 반복됩니다. PSCI CPU_ON → BL31 warm boot → secondary_entry → secondary_start_kernel. CPU별 percpu storage는 영구 할당돼 있어 재초기화만 하면 됩니다.
#측정 — 4-core boot up time
i.MX 8M Plus(4-core Cortex-A53 1.5 GHz, kernel 6.6) 측정값입니다.
[BL31 reset release → primary BL33 진입] : ~5 ms[BL33 → kernel start_kernel] : ~500 ms[start_kernel → smp_init] : ~80 ms[smp_init → 모든 CPU online] : ~1 ms [CPU1 PSCI CPU_ON SMC] : ~5 µs [CPU1 reset → secondary_start_kernel] : ~150 µs [CPU1 percpu·GIC·timer init] : ~100 µs [CPU1 ONLINE 표시] : ~300 µs (CPU1 총합) [CPU2, CPU3 동일] : ~600 µs[총 SMP bring-up time] : ~1 ms (4-core)전체 부트의 1 ms만이 SMP bring-up에 쓰입니다. 나머지는 BL31 부팅과 kernel single-CPU init이 잡아먹습니다. SMP bring-up이 빠른 이유는 PSCI fast call과 모든 CPU가 같은 page table을 공유하기 때문입니다.
[ 부트 로그에서 본 SMP 단계 ][ 0.087401] smp: Bringing up secondary CPUs ...[ 0.087512] Detected VIPT I-cache on CPU1[ 0.087602] GICv3: CPU1: found redistributor 1 region 0[ 0.087701] CPU1: Booted secondary processor 0x0000000001[ 0.087788] Detected VIPT I-cache on CPU2[ 0.087877] GICv3: CPU2: found redistributor 2 region 0[ 0.087966] CPU2: Booted secondary processor 0x0000000002[ 0.088054] Detected VIPT I-cache on CPU3[ 0.088143] GICv3: CPU3: found redistributor 3 region 0[ 0.088232] CPU3: Booted secondary processor 0x0000000003[ 0.088354] smp: Brought up 1 node, 4 CPUs[ 0.088462] SMP: Total of 4 processors activated.Booted secondary processor 사이의 timestamp 차이가 한 CPU bring-up 시간입니다. 약 100 µs 단위로 올라옵니다.
#자주 보는 함정
#Secondary CPU stack 미할당
secondary_data.stack이 NULL이면 __secondary_switched의 cbz x1, __secondary_too_slow 검사에 걸려 secondary가 조용히 hang합니다. mainline 커널은 idle thread를 미리 fork해 stack을 할당해 두지만, 새 enable-method ops를 custom으로 추가하다 secondary_data 설정을 빠뜨리면 발생합니다. 부팅 중 CPU%d: failed to come online이 뜨면 stack 또는 task struct를 의심합니다.
#GIC CPU interface 초기화 누락
cpu_postboot에서 gic_cpu_init을 부르지 않으면 SGI/PPI가 안 들어와 secondary가 timer interrupt를 못 받는다. local IRQ는 enable되지만 아무 interrupt도 도착하지 않으므로 scheduler가 task를 배정해도 idle에 머뭅니다. mainline에서는 cpu_psci_ops가 자동 처리하지만, hypervisor 환경의 paravirt SMP에서 빠뜨리는 경우가 있습니다.
#CPU_ON race — 같은 CPU 두 번 호출
primary가 cpu_up(1)을 호출한 직후 다른 thread가 다시 cpu_up(1)을 호출하면 PSCI가 ALREADY_ON 또는 ON_PENDING을 return합니다. 커널의 cpu_up은 cpu_hotplug_lock으로 보호되지만, custom ops를 짤 때 race를 신경 쓰지 않으면 secondary가 두 번 진입해 stack corruption이 납니다.
#MMU enable 전에 percpu access
set_my_cpu_offset 이전에 percpu variable에 쓰는 코드가 들어가면 primary CPU의 percpu 영역을 두드리게 됩니다. 이는 race로 primary를 망가뜨릴 수 있습니다. 특히 pr_info 같은 매크로가 내부에서 percpu printk buffer를 쓰므로 MMU enable 전에 print 호출 자체가 위험합니다. mainline은 early_printk에 special path를 두지만 custom build에서 잊기 쉽습니다.
#spin-table release_addr 정렬 누락
spin-table 방식에서 cpu-release-addr가 8-byte aligned가 아니면 ldr x0, [release_addr]이 alignment fault를 냅니다. DT 작성 시 항상 8-byte aligned 주소를 쓰고, BL31이 그 주소에 8-byte zero를 미리 깔아 둬야 합니다.
#enable-method 누락 또는 잘못
enable-method 속성이 없으면 커널이 PSCI도 spin-table도 아니라고 판단해 그 CPU를 online으로 만들지 않는다. dmesg에 CPU%d: failed in unknown state 형태의 메시지가 뜨면 DT의 enable-method를 확인합니다. 거꾸로 enable-method = "psci"인데 PSCI 노드 자체가 DT에 없으면 PSCI ops가 등록 안 돼 같은 결과가 납니다.
#정리
- SMP 부트는 primary CPU가 secondary 각각에 진입점을 알려 일으켜 세우는 순서입니다. BL1·BL2·BL31·BL33·Linux primary가 모두 single CPU로 진행됩니다.
- secondary 깨우는 방식은 두 가지입니다. spin-table은 메모리 polling + SEV, PSCI는 SMC + SoC reset controller다. 현재 표준은 PSCI다.
- DT의
cpus/cpu@N/enable-method속성이 분기점이고, PSCI 방식이면psci노드와method = "smc"가 함께 있어야 합니다. - 커널의
__cpu_up이secondary_data에 idle task와 stack을 채우고, enable-method에 맞는cpu_bootops를 호출해 PSCI CPU_ON SMC를 발사합니다. - secondary는
secondary_entry → secondary_startup → __secondary_switched → secondary_start_kernel을 통과합니다. MMU off에서 시작해 primary가 만든 swapper_pg_dir로 MMU를 켭니다. set_my_cpu_offset이 TPIDR_EL1을 채워야 percpu access가 안전해집니다. GIC CPU interface 초기화는 secondary 위에서 직접 해야 합니다.- HOTPLUG_CPU는
cpu_die가PSCI CPU_OFFSMC로 자신을 끄는 흐름이고, 다시 켤 때는 처음 bring-up과 같은 경로가 반복됩니다. - 4-core Cortex-A53 기준 전체 SMP bring-up이 약 1 ms 안에 끝납니다. 한 CPU당 reset release 후 online까지 ~300 µs다.
- 흔한 함정은 secondary stack 미할당, GIC 초기화 누락, CPU_ON race, MMU 전 percpu access, spin-table 정렬 문제, enable-method 누락 여섯 가지입니다.
#시리즈 마무리
이 장이 Bootloader Internals 시리즈의 마지막 깊이다. 시리즈는 Ch 1: 부트로더가 푸는 문제에서 시작해 BootROM부터 init까지의 전 과정을 따라왔습니다. 30장이 양산 CI까지의 보편적 흐름을 다뤘다면, 마지막 세 장(31·32·33)은 ARM bare-metal boot이 깊어질 때 반드시 마주치는 EL3 runtime과 SMP bring-up을 채웠습니다.
다음 단계로 추천할 주제는 두 가지입니다. 첫째는 Embedded Security 시리즈로 secure boot·TrustZone·TEE의 보안 모델을 더 깊이 파는 길입니다. 둘째는 Practical RTOS Internals 시리즈로 ARM Cortex-A SMP·AMP·context switch 같은 부트 이후의 runtime 동작을 따라가는 길입니다.
#관련 항목
- Ch 1: 부트로더가 푸는 문제
- Ch 4: 부트 단계 — BL1 → SPL → TPL → U-Boot Proper
- Ch 25: ARM TF-A 통합 — BL1·BL2·BL31·BL32·BL33
- Ch 31: TF-A BL31 EL3 Runtime
- Ch 32: PSCI / SMCCC ABI
- Practical RTOS Internals Ch 2-6: Cortex-A context switch
- Practical RTOS Internals Ch 4-7: SMP RTOS
- Practical RTOS Internals Ch 4-12: AMP와 OpenAMP
- Embedded Security Ch 4: TrustZone
- 원문 — Arm PSCI v1.1 specification (DEN0022)
- 원문 — Linux kernel Documentation/arm64/booting.rst
Bootloader Internals · 33 of 37
- 1ROM부터 init까지 — 임베디드 부팅 단계의 빈자리 분석
- 2Das U-Boot vs TF-A vs EDK II — 임베디드 부트로더 생태계 비교
- 3U-Boot 빌드 시스템 분석 — Kconfig·Makefile·defconfig 동작 추적
- 4ARM 임베디드 부트 4단계 분해 — BL1·SPL·TPL·U-Boot Proper의 역할
- 5U-Boot Falcon Mode — SPL이 U-Boot Proper 없이 커널 직접 부팅
- 6Device Tree DTB 부트로더 처리 — 로딩 시점과 fixup 메커니즘 추적
- 7U-Boot Driver Model 내부 — uclass·driver·device 추상화 구조
- 8U-Boot 보드 초기화 시퀀스 — board_init_f와 board_init_r 분리 이유
- 9DDR Controller 프로그래밍과 PHY Training — SPL의 가장 어려운 작업
- 10임베디드 스토리지 부팅 분석 — MMC·SCSI·NAND·SPI Flash 비교
- 11임베디드 네트워크 부팅 — TFTP·PXE·BOOTP 시퀀스 분석
- 12U-Boot USB 부팅 — fastboot·UMS·USB host 메커니즘
- 13U-Boot 환경 변수와 bootcmd — 부팅 시나리오 정의하기
- 14Modern U-Boot bootflow / bootmeth — 새 추상화 레이어 분석
- 15FIT image 구조 분석 — multi-image·hash·configuration 추적
- 16U-Boot Verified Boot — RSA 서명과 public key 임베딩 흐름
- 17임베디드 A/B 부팅 이중화 — OTA 안전성을 위한 부트 슬롯 설계
- 18U-Boot의 EFI 호환 분석 — bootefi 명령과 EFI loader 동작 원리
- 19Linux Boot ABI — ARM/ARM64 커널 진입 규약 추적
- 20임베디드 펌웨어 업데이트 — RAUC vs SWUpdate 비교
- 21새 보드 U-Boot 포팅 실전 — defconfig 작성부터 첫 부팅까지
- 22부트로더 디버깅 기법 — DEBUG·JTAG·serial·post-mortem 분석
- 23SoC BootROM·eFuse·OTP — 부팅의 0단계 분석
- 24SPL·TPL 내부 해부 — 가장 작은 부트 단계의 동작 추적
- 25ARM Trusted Firmware-A 통합 — BL1·BL2·BL31·BL32·BL33 흐름
- 26DDR Training과 PHY Calibration — 보드별 파라미터 튜닝
- 27임베디드 Chain of Trust — 다단계 서명 검증의 전체 흐름
- 28임베디드 Flash Layout 설계 — partition·NAND·eMMC·UBI 비교
- 29U-Boot Distro Boot — extlinux·boot.scr 표준화 분석
- 30부트로더 CI 구축 — build matrix와 자동 부팅 테스트
- 31TF-A BL31 EL3 Runtime 분석 — PSCI·SDEI·RAS dispatcher 추적
- 32PSCI와 SMCCC ABI — ARM 표준 SMC 호출 규약 분석
- 33ARM64 Secondary Core Bring-up — PSCI CPU_ON 호출부터 EL1 진입까지
- 34U-Boot PCIe Enumeration — 부트로더가 디바이스를 찾는 흐름 분석
- 35EFI·UEFI에서 CXL 초기화 — CEDT 생성과 HDM Decoder 사전 설정
- 36부트 시 메모리 토폴로지 결정 — DDR + CXL.mem 통합 인식
- 37UEFI Secure Boot 인증서 만료 — 2011→2023 CA 롤오버와 PQC 대비
관련 글
PSCI와 SMCCC ABI — ARM 표준 SMC 호출 규약 분석
SMCCC 호출 규약과 PSCI v1.1 ABI — function ID 구조, fast vs yielding, CPU_ON·CPU_SUSPEND, Linux PSCI driver.
TF-A BL31 EL3 Runtime 분석 — PSCI·SDEI·RAS dispatcher 추적
TF-A BL31의 EL3 runtime service 구조 — runtime_svc 등록 모델, vector entry, PSCI·SDEI·RAS dispatcher, SMC call latency.
Linux Boot ABI — ARM/ARM64 커널 진입 규약 추적
부트로더 → 커널 인계 — ARM64·RISC-V·x86 boot ABI, 인자 전달, 레지스터 상태.