setup_arch() – ARM64

<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이 아닌 값이 있는 경우 에러 메시지를 출력한다.

 

댓글 남기기