이 코드는 어떤 문제를 푸나요?
부트로더가 넘긴 주소와 장치 설명은 그대로 모든 커널 기능이 사용할 수 있는 객체가 아닙니다. setup_arch는 초기 매핑, FDT 또는 ACPI, 메모리 영역과 CPU 정보를 차례로 연결합니다. 특히 임시 idmap을 제거하는 시점은 이후 실행이 정식 매핑으로 가능한지와 연결됩니다.
읽을 범위: v6.6 · arch/arm64/kernel/setup.c · setup_arch 293–392행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.
먼저 알아둘 개념
FDT와 ACPI
플랫폼의 메모리·장치·CPU 정보를 알려 주는 서로 다른 기술 방식입니다.
초기 매핑
부팅 초기에 제한된 주소로 하드웨어나 부트 정보를 읽기 위해 사용하는 임시 연결입니다.
처음 읽을 때
코드를 읽을 때 명령줄·임시 매핑·FDT 접근을 준비합니다. 그다음 어느 상태가 바뀌는지 각 줄에서 확인하세요.
더 깊이 살펴볼 때
이 경로의 호출 문맥, 잠금·인터럽트 상태, 오류 시 남는 자원을 함께 추적해 보세요. 호출된 함수가 수행하는 작업과 현재 함수가 직접 보장하는 범위를 구분하는 것이 중요합니다.
그림으로 보는 변화

1. 부트 정보 접근
명령줄·임시 매핑·FDT 접근을 준비합니다.
화살표는 초기화 순서입니다.
2. 메모리와 플랫폼 정리
memblock과 페이지 테이블, ACPI 또는 DT를 준비합니다.
표준 커널 객체로 해석하는 단계입니다.
3. CPU와 펌웨어 연결
PSCI 등 플랫폼 호출과 CPU 열거 정보를 설정합니다.
모든 보조 CPU를 이 함수에서 즉시 실행하는 뜻은 아닙니다.
setup_arch를 한 줄씩 읽기
줄 번호는 v6.6 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.
void __init __no_sanitize_address setup_arch(char **cmdline_p)
{
setup_initial_init_mm(_stext, _etext, _edata, _end);
*cmdline_p = boot_command_line;
kaslr_init();
/*
* If know now we are going to need KPTI then use non-global
* mappings from the start, avoiding the cost of rewriting
* everything later.
*/
arm64_use_ng_mappings = kaslr_requires_kpti();
early_fixmap_init();
early_ioremap_init();
setup_machine_fdt(__fdt_pointer);
/*
* Initialise the static keys early as they may be enabled by the
* cpufeature code and early parameters.
*/
jump_label_init();
parse_early_param();
dynamic_scs_init();
/*
* Unmask asynchronous aborts and fiq after bringing up possible
* earlycon. (Report possible System Errors once we can report this
* occurred).
*/
local_daif_restore(DAIF_PROCCTX_NOIRQ);
/*
* TTBR0 is only used for the identity mapping at this stage. Make it
* point to zero page to avoid speculatively fetching new entries.
*/
cpu_uninstall_idmap();
xen_early_init();
efi_init();
if (!efi_enabled(EFI_BOOT)) {
if ((u64)_text % MIN_KIMG_ALIGN)
pr_warn(FW_BUG "Kernel image misaligned at boot, please fix your bootloader!");
WARN_TAINT(mmu_enabled_at_boot, TAINT_FIRMWARE_WORKAROUND,
FW_BUG "Booted with MMU enabled!");
}
arm64_memblock_init();
paging_init();
acpi_table_upgrade();
/* Parse the ACPI tables for possible boot-time configuration */
acpi_boot_table_init();
if (acpi_disabled)
unflatten_device_tree();
bootmem_init();
kasan_init();
request_standard_resources();
early_ioremap_reset();
if (acpi_disabled)
psci_dt_init();
else
psci_acpi_init();
init_bootcpu_ops();
smp_init_cpus();
smp_build_mpidr_hash();
/* Init percpu seeds for random tags after cpus are set up. */
kasan_init_sw_tags();
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
/*
* Make sure init_thread_info.ttbr0 always generates translation
* faults in case uaccess_enable() is inadvertently called by the init
* thread.
*/
init_task.thread_info.ttbr0 = phys_to_ttbr(__pa_symbol(reserved_pg_dir));
#endif
if (boot_args[1] || boot_args[2] || boot_args[3]) {
pr_err("WARNING: x1-x3 nonzero in violation of boot protocol:\n"
"\tx1: %016llx\n\tx2: %016llx\n\tx3: %016llx\n"
"This indicates a broken bootloader or old kernel\n",
boot_args[1], boot_args[2], boot_args[3]);
}
}void __init __no_sanitize_address setup_arch(char **cmdline_p)arm64 플랫폼과 메모리 기반을 부팅 중 준비하는 함수입니다. __init은 초기화 코드 분류, __no_sanitize_address는 이 함수의 주소 검사 계측 제외를 나타냅니다. char **를 통해 호출자의 명령줄 포인터를 돌려줍니다.
setup_initial_init_mm(_stext, _etext, _edata, _end);초기 커널 mm에 코드·데이터·이미지 끝 주소를 기록합니다.
*cmdline_p = boot_command_line;호출자에게 부트 명령줄 포인터를 돌려줍니다. char **를 통해 호출자의 포인터 값을 변경합니다.
kaslr_init();커널 주소 무작위화 관련 초기 상태를 정리합니다.
arm64_use_ng_mappings = kaslr_requires_kpti();KASLR과 커널 페이지 테이블 격리 요구에 따라 비전역 매핑을 사용할지를 기록합니다. 이후 페이지 테이블 구성에서 이 결정을 사용합니다.
early_fixmap_init();부트 정보와 하드웨어에 접근할 초기 고정 매핑 체계를 준비합니다.
early_ioremap_init();부팅 중 물리 영역을 임시로 매핑하여 읽는 기반을 준비합니다.
setup_machine_fdt(__fdt_pointer);부트로더의 평탄한 장치 트리 주소를 해석하여 플랫폼 정보를 준비합니다.
jump_label_init();static branch에 필요한 패치 정보를 초기화합니다. 조건마다 일반 분기를 계속 실행하지 않도록 코드를 선택할 기반입니다.
parse_early_param();일반 인자 처리보다 먼저 효과가 필요한 early parameter를 해석합니다.
dynamic_scs_init();구성에 맞춰 동적 shadow call stack 관련 코드 처리를 준비합니다.
local_daif_restore(DAIF_PROCCTX_NOIRQ);프로세스 문맥에서 IRQ를 허용하지 않는 DAIF 상태를 적용합니다. 디버그·SError·IRQ·FIQ의 마스크를 정의된 조합으로 바꾸는 것이며, 일반 IRQ를 지금 활성화하는 호출이 아닙니다.
cpu_uninstall_idmap();부팅 전환에 사용한 identity mapping 상태를 정리합니다. 이후 정식 커널 매핑으로 실행 가능해야 합니다.
xen_early_init();Xen 실행 환경에서 필요한 이른 가상화 상태를 준비합니다.
efi_init();EFI가 전달한 시스템 테이블과 메모리 정보를 초기화합니다.
if (!efi_enabled(EFI_BOOT)) {EFI로 부팅하지 않은 경우에 적용할 ARM64 부트 규약 검사를 시작합니다.
if ((u64)_text % MIN_KIMG_ALIGN)커널 이미지 시작 주소가 요구한 정렬 단위의 배수인지 나머지 연산으로 확인합니다.
pr_warn(FW_BUG "Kernel image misaligned at boot, please fix your bootloader!");부트로더가 커널 이미지를 요구한 경계에 맞추지 않았음을 경고합니다. 주소를 이 자리에서 다시 정렬하는 동작은 없으므로 부트로더 배치를 수정해야 한다는 진단입니다.
WARN_TAINT(mmu_enabled_at_boot, TAINT_FIRMWARE_WORKAROUND,비EFI 부팅에서 부트 시 MMU가 켜져 있었다면 경고하고 펌웨어 우회와 관련된 taint를 기록합니다. 바로 위 이미지 정렬 검사와 별개의 부트 규약 검사입니다.
FW_BUG "Booted with MMU enabled!");앞줄 WARN_TAINT에 전달할 진단 문구입니다. 현재 paging_init이 완료되었는지를 말하는 것이 아니라 부트로더가 넘긴 최초 MMU 상태를 지적합니다.
arm64_memblock_init();ARM64가 사용할 초기 물리 메모리 범위와 예약 영역을 memblock에 정리합니다.
paging_init();커널이 지속적으로 사용할 페이지 테이블과 메모리 매핑을 준비합니다.
acpi_table_upgrade();제공된 ACPI 테이블 교체 정보가 있으면 반영합니다.
acpi_boot_table_init();부팅에 필요한 ACPI 테이블 해석 기반을 준비합니다.
if (acpi_disabled)ACPI를 사용하지 않는 구성을 검사하여 DT 기반의 대체 경로를 선택합니다.
unflatten_device_tree();평탄한 FDT 바이트 구조를 커널의 device_node 구조로 만듭니다.
bootmem_init();초기 메모리 구역과 페이지 관리의 아키텍처별 초기화를 수행합니다.
kasan_init();KASAN(Kernel Address Sanitizer)은 범위를 벗어난 메모리 접근과 해제 후 사용을 찾아내는 검사 기능입니다. arm64의 이 호출은 Generic·Software Tags 모드에서 실제 메모리를 설명할 shadow 영역과 검사 상태를 준비합니다. Hardware Tags 모드만 쓰거나 해당 소프트웨어 모드가 없으면 이 함수는 빈 구현이며 태그 모드의 추가 준비도 별도 경로에 있습니다.
request_standard_resources();커널 이미지 등 표준 메모리 영역을 자원 관리 체계에 등록합니다.
early_ioremap_reset();초기 임시 매핑 단계의 상태를 정리합니다.
if (acpi_disabled)ACPI를 사용하지 않는 구성을 검사하여 DT 기반의 대체 경로를 선택합니다.
psci_dt_init();DT가 기술한 PSCI 펌웨어 호출 방식을 연결합니다.
else앞 조건이 성립하지 않은 경로를 선택합니다. 앞쪽 처리와 아래 처리를 모두 수행하는 것이 아닙니다.
psci_acpi_init();ACPI 정보에서 PSCI 펌웨어 호출 방식을 연결합니다.
init_bootcpu_ops();부트 CPU와 관련된 CPU 동작 함수를 준비합니다.
smp_init_cpus();플랫폼에 있는 CPU들을 열거하고 SMP 관리 정보에 반영합니다.
smp_build_mpidr_hash();CPU 하드웨어 식별자로 CPU별 저장 상태를 찾을 수 있도록 해시 구성을 준비합니다.
kasan_init_sw_tags();Software Tags KASAN은 포인터와 메모리의 태그를 비교하여 잘못된 접근을 찾는 arm64의 소프트웨어 검사 방식입니다. shadow 메모리 준비와 별도로 필요한 태그 상태를 초기화하며, 이 모드를 빌드하지 않은 구성에서는 빈 구현으로 빠집니다.
#ifdef CONFIG_ARM64_SW_TTBR0_PANPAN(Privileged Access Never)을 TTBR0 전환으로 구현하여 커널의 무분별한 사용자 메모리 접근을 막는 구성입니다. 정상 사용자 접근 함수가 잠시 올바른 TTBR0를 복원하며, 여기서는 초기 태스크에 사용할 예약 테이블 값을 저장합니다. 이 선택은 전처리 단계에서 이루어지며, CPU가 실행 중 이 줄의 조건을 검사하지 않습니다.
init_task.thread_info.ttbr0 = phys_to_ttbr(__pa_symbol(reserved_pg_dir));소프트웨어 PAN 경로에서 사용할 예약 페이지 테이블의 물리 주소를 TTBR 형식으로 바꿔 초기 태스크에 저장합니다.
#endif소프트웨어 PAN용 초기 TTBR0 값을 기록하는 구간을 마칩니다. 다음 부트 인자 검사는 이 PAN 설정과 별개의 공통 진단입니다.
if (boot_args[1] || boot_args[2] || boot_args[3]) {부트 규약상 0이어야 할 x1–x3 인자가 남아 있는지 검사합니다.
pr_err("WARNING: x1-x3 nonzero in violation of boot protocol:\n"커널 진입 시 0이어야 하는 x1–x3에 값이 남아 부트 규약을 어겼음을 보고하기 시작합니다. 보존해 둔 boot_args를 사용하므로 현재 레지스터 내용을 새로 읽는 출력이 아닙니다. 로그 출력 자체가 오류 상태를 복구하지는 않으며, 실제 중단·복구 처리는 뒤의 코드가 담당합니다.
"\tx1: %016llx\n\tx2: %016llx\n\tx3: %016llx\n"각 부트 인자 값을 16자리 16진수로 보여 주는 형식 문자열입니다. 어느 인자가 규약을 어겼는지 실제 비트 패턴을 확인할 수 있게 합니다. 앞줄에서 시작한 호출이나 식에 이어지는 부분이므로, 이 줄만으로 별도의 함수 호출이 생기지는 않습니다.
"This indicates a broken bootloader or old kernel\n",진단 메시지에 가능한 전달 경로의 문제를 안내합니다. 이 문자열 출력이 부트로더의 구체적인 고장 원인을 자동 판정하는 것은 아닙니다. 앞줄에서 시작한 호출이나 식에 이어지는 부분이므로, 이 줄만으로 별도의 함수 호출이 생기지는 않습니다.
boot_args[1], boot_args[2], boot_args[3]);초기 진입에서 저장한 x1·x2·x3 값을 순서대로 로그에 전달합니다. 첫 DTB 인자 x0는 이 0 검사 대상과 구분됩니다. 앞줄에서 시작한 호출이나 식에 이어지는 부분이므로, 이 줄만으로 별도의 함수 호출이 생기지는 않습니다.
함께 생각해 볼 질문
FDT 주소만 있으면 바로 일반 DT API를 쓸 수 있나요?
평탄한 바이트 구조를 읽는 초기 단계와 노드 객체를 만드는 단계를 구분해야 합니다.
cpu_uninstall_idmap은 MMU를 끄나요?
부팅 전환에 사용한 identity mapping과 연결 상태를 정리하는 동작이며 단순히 MMU 전체를 끈다는 뜻은 아닙니다.
비 EFI 부팅 검사가 별도로 있는 이유는 무엇인가요?
사용한 부팅 경로마다 전달 상태에 대한 전제가 달라 해당 규약을 확인하기 때문입니다.
출처와 읽은 범위
Linux stable v6.6 · arch/arm64/kernel/setup.c
해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고
