<kernel v5.10>
다음 그림은 ARM64 아키텍처가 setup_arch() 함수내에서 준비하는 과정 중 메모리 할당자 및 매핑 시스템에 대한 전개 과정을 보여준다.
- 부트 업 순간 memblock_add() 및 memblock_reserve() 등의 API를 사용할 수 있다. 그러나 메모리에 대한 매핑이 완료되지 않은 상태에서는 memblock_alloc() 함수를 사용할 수 없다.
- paging_init()이 완료된 이후 부터는 early 메모리 할당자인 memblock을 사용하여 메모리 영역 중 빈 영역을 할당할 수 있다.
- setup_arch() 및 mm_init()이 완료된 이후 부터는 정규 메모리 할당자 들이 사용될 수 있다.
setup_arch()
arch/arm64/kernel/setup.c -1/2-
void __init __no_sanitize_address setup_arch(char **cmdline_p) { init_mm.start_code = (unsigned long) _text; init_mm.end_code = (unsigned long) _etext; init_mm.end_data = (unsigned long) _edata; init_mm.brk = (unsigned long) _end; *cmdline_p = boot_command_line; /* * 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(); /* * 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) && ((u64)_text % MIN_KIMG_ALIGN) != 0) pr_warn(FW_BUG "Kernel image misaligned at boot, please fix your bootloader!"); arm64_memblock_init(); paging_init();
ARM64 아키텍처에 초기화를 진행한다. 주로 커널 및 메모리 매핑과 reserve 영역들을 등록한다.
- 코드 라인 3~6에서 커널 코드 영역과, 데이터 영역(컴파일 타임에 초기화된 데이터 및 초기화되지 않은 데이터 영역)의 가상 주소를 지정한다.
- 코드 라인 8에서 boot_command_line을 출력 인자 cmdline_p에 대입한다.
- 코드 라인 15에서 arm64 아키텍처가 보안을 위해 커널 매핑 위치를 랜더마이즈를 함에 따라 non-global 매핑을 사용하는지 여부를 알아온다.
- 코드 라인 17에서 fixmap을 사용할 수 있도록 페이지 테이블에 매핑해둔다.
- 코드 라인 18에서 fixmap 가상 주소 영역을 사용하여 7개 디바이스까지 각 디바이스 당 최대 256K 영역을 지원하는 임시(early) 매핑을 할 수 있도록 준비한다.
- 코드 라인 20에서 디바이스 트리를 스캔하여 해당 머신에 대한 메모리 정보 및 커멘드 라인 파라메터 정보를 알아온다.
- 코드 라인 26에서 static 브랜치 사용을 준비한다.
- 코드 라인 27에서 커멘드 라인 파라메터들 중 early 파라메터에 대한 파싱 및 이에 대응하는 함수를 동작 시킨다.
- 코드 라인 34에서 현재 cpu의 DAIF 플래그를 복구한다.
- irq는 현재 시점에서 허용하지 않지만, asynchronous aborts와 fiq는 earlycon 이후로 동작될 수도 있다.
- 코드 라인 40에서 유저용 페이지 테이블을 가리키는 ttbr0는 부트 업 현재 시점까지 1:1 매핑 영역 페이지 테이블에 연결되어 사용되었다. 여기에서는 현재 cpu가 이러한 1:1 identity 매핑 테이블을 사용하지 않게 한다.
- 코드 라인 42에서 ARM64의 경우 xen 하이퍼바이저를 위해 따로 준비한 셋업 코드가 없다.
- 코드 라인 43에서 최근 ARM64 서버에서 EFI 펌웨어를 지원한다. 이의 사용을 위해 준비한다. 서버를 제외한 임베디드에서는 대부분 지원하지 않는다.
- 코드 라인 48에서 memblock 영역에 reserve할 영역을 등록해둔다.
- 코드 라인 50에서 커널 영역과 메모리 영역을 모두 정규 매핑한다.
arch/arm64/kernel/setup.c -2/2-
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_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 = __pa_symbol(empty_zero_page); #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]); } }
- 코드 라인 1~4에서 서버 등에서 받은 ACPI 테이블 데이터를 조회하여 관련 메모리 영역 등을 memblock에 reserve 한다.
- 코드 라인 6~7에서 ACPI를 사용하지 않는 시스템의 경우 DTB(FDT)형태로 로드된 디바이스 트리를 객체 형태로 바꾼다.
- 객체 형태로 바꾸기 전에는 fdt_*() API를 사용하고, 바꾼 후에는 of_*() API도 사용할 수 있다.
- 코드 라인 9에서 zone 영역을 설정하고, 초기화한다.
- 코드 라인 11에서 커널 메모리 디버그용 KASAN(Kernel Address SANitizer)을 준비한다.
- 코드 라인 13에서 memblock에 등록된 reserve 영역들과 메모리 영역을 리소스에 등록해둔다.
- 코드 라인 15에서 정규 페이징이 동작하는 경우 fixmap을 사용한 early_ioremap()을 사용할 필요가 없으므로 after_paging_init 전역 변수에 1을 대입해둔다.
- ARM64 아키텍처의 경우에는 정규 페이징이 동작하더라도 early_ioremap()의 사용을 허락한다.
- 코드 라인 17~20에서 디바이스 트리 또는 ACPI 정보를 통해 절전 관련 ops를 초기화한다.
- 코드 라인 22에서 디바이스 트리 또는 ACPI 정보를 통해 “enable-method”가 지정된 경우 cpu online/offline을 관리할 ops를 초기화한다.
- 코드 라인 23에서 디바이스 트리 또는 ACPI 정보를 통해 cpu 노드 정보를 읽어온 후 cpu의 셋업 코드를 수행한다.
- 코드 라인 24에서 mpidr 레지스터로부터 mpidr 해시를 생성해둔다.
- cpu의 suspend 진입 시 어셈블리 코드에서 빠르게 이 해시를 사용하여 cpu 로지컬 id를 분류한 후 cpu 레지스터 등의 context 정보를 저장할 때 사용한다.
- 코드 라인 27에서 KASAN에서 사용할 per-cpu prng_state를 초기화한다.
- 코드 라인 29~36에서 보안 목적으로 커널이 ttbr0를 통해 유저 영역의 메모리에 직접 접근하지 못하게한다. 이 때 sw 적인 방법을 사용하는데 ttbr0 멤버에 zero 페이지를 연결하여 유저 액세스를 금지 상태로 변경한다.
- 코드 라인 38~43에서 처음 커널의 head.S에 진입 시 boot_args[]에 저장해둔 x0~x3 레지스터 값들 중 x1 ~ x3 레지스터 값에 0이 아닌 값이 있는 경우 에러 메시지를 출력한다.