티스토리 뷰

struct mm_struct init_mm = {
    .mm_mt        = MTREE_INIT_EXT(mm_mt, MM_MT_FLAGS, init_mm.mmap_lock),
    .pgd        = swapper_pg_dir,

void __init __no_sanitize_address setup_arch(char **cmdline_p)
{
    // vmlinux.lds.S 에 있는 값을 변수로 옮김
    setup_initial_init_mm(_stext, _etext, _edata, _end);

    *cmdline_p = boot_command_line;

    // Kernel Address Space Layout Randomization
    kaslr_init();

    // fixmap __end_of_permanent_fixed_addresses ~ __end_of_fixed_addresses 구간을 가상 주소로 매핑
    early_fixmap_init();
    // 매핑한 공간중 FIX_BTMAP_BEGIN ~ FIX_BTMAP_END 구간을 ioremap 용으로 슬롯에 등록
    early_ioremap_init();

    // FDT 의 물리주소를 fixmap 에 매핑
    // Machine model: linux,dummy-virt 이런식으로 fdt 를 읽을수 있게됨.
    setup_machine_fdt(__fdt_pointer);

    ....
}

early_fixmap_init

fixmap_init 함수가 굉장히 간결해졌다.

원래는 fixmap 을 2M 페이지를 지원했었으나, FDT가 8바이트 정렬이라 2M 경계를 걸칠 수 있어서, 최대 4M 이 매핑되고 캐싱들도 동작이 이상해지기 때문에, FixMap 영역을 4K 단위로만 처리

커널은 5단계pgd > p4d > pud > pmd > pte 까지 페이지 테이블이 확장되었음

FIX_ADDR 이 사이즈가 작아서 하나만 페이지 테이블을 각각 하나씩 사용. 그래서 pmd 까지 하나쓰고 pte 만 while 돌림

// 요런식으로 pud, pmd, pte 를 boot memory 용으로 사용하기 때문에, 별도 페이지 사용하지 않음
static pte_t bm_pte[NR_BM_PTE_TABLES][PTRS_PER_PTE] __page_aligned_bss;
static pmd_t bm_pmd[PTRS_PER_PMD] __page_aligned_bss __maybe_unused;
static pud_t bm_pud[PTRS_PER_PUD] __page_aligned_bss __maybe_unused;

static void __init early_fixmap_init_pmd(pud_t *pudp, unsigned long addr,
                     unsigned long end)
{
    unsigned long next;
    pud_t pud = READ_ONCE(*pudp);
    pmd_t *pmdp;

    if (pud_none(pud))
        __pud_populate(pudp, __pa_symbol(bm_pmd),
                   PUD_TYPE_TABLE | PUD_TABLE_AF);

    pmdp = pmd_offset_kimg(pudp, addr);
    do {
        next = pmd_addr_end(addr, end);
        early_fixmap_init_pte(pmdp, addr);
    } while (pmdp++, addr = next, addr != end);
}

// FIXMAP 으로 사용할 가상주소는 미리 정해져있음
#define FIXADDR_SIZE        (__end_of_permanent_fixed_addresses << PAGE_SHIFT)
#define FIXADDR_START        (FIXADDR_TOP - FIXADDR_SIZE)
#define FIXADDR_TOT_SIZE    (__end_of_fixed_addresses << PAGE_SHIFT)
#define FIXADDR_TOT_START    (FIXADDR_TOP - FIXADDR_TOT_SIZE)

static inline pgd_t *pgd_offset_pgd(pgd_t *pgd, unsigned long address)
{
    return (pgd + pgd_index(address));
};

/*
 * a shortcut to get a pgd_t in a given mm
 */
#ifndef pgd_offset
#define pgd_offset(mm, address)        pgd_offset_pgd((mm)->pgd, (address))
#endif

#define pgd_offset_k(address)        pgd_offset(&init_mm, (address))

static void __init early_fixmap_init_pud(p4d_t *p4dp, unsigned long addr,
                     unsigned long end)
{
    p4d_t p4d = READ_ONCE(*p4dp);
    pud_t *pudp;

    if (CONFIG_PGTABLE_LEVELS > 3 && !p4d_none(p4d) &&
        p4d_page_paddr(p4d) != __pa_symbol(bm_pud)) {
        /*
         * We only end up here if the kernel mapping and the fixmap
         * share the top level pgd entry, which should only happen on
         * 16k/4 levels configurations.
         */
        BUG_ON(!IS_ENABLED(CONFIG_ARM64_16K_PAGES));
    }

    // 여기에 안찍힘. 왜일까?
    // pr_info("p4d: %016llx", p4d_val(p4d));
    if (p4d_none(p4d)) // 여기에 들어옴. pud 를 부팅용 bm_pud 로 사용함
        __p4d_populate(p4dp, __pa_symbol(bm_pud),
                   P4D_TYPE_TABLE | P4D_TABLE_AF);

    pudp = pud_offset_kimg(p4dp, addr);
    // [init_pud] pudp: 0xffff800082bfdff8
    // pr_info("[init_pud] pudp: 0x%px", pudp);
    early_fixmap_init_pmd(pudp, addr, end);
}

void __init early_fixmap_init(void)
{
    unsigned long addr = FIXADDR_TOT_START;
    unsigned long end = FIXADDR_TOP;

    pgd_t *pgdp = pgd_offset_k(addr);
    p4d_t *p4dp = p4d_offset_kimg(pgdp, addr);

   // addr: 0xffffffffff430000, end: 0xffffffffff800000, pgdp: 0xffff800081cd2078, p4dp: 0xffff800081cd2ff8
   // pr_info("addr: 0x%lx, end: 0x%lx, pgdp: 0x%px, p4dp: 0x%px", addr, end, pgdp, p4dp);

    early_fixmap_init_pud(p4dp, addr, end);
}

early_ioremap_setup

fixmap BITMAP 영역에 가상주소를 매핑

enum fixed_addresses {
    ...
    FIX_BTMAP_END = __end_of_permanent_fixed_addresses,
    FIX_BTMAP_BEGIN = FIX_BTMAP_END + TOTAL_FIX_BTMAPS - 1,

    /*
     * Used for kernel page table creation, so unmapped memory may be used
     * for tables.
     */
    FIX_PTE,
    FIX_PMD,
    FIX_PUD,
    FIX_P4D,
    FIX_PGD,

    __end_of_fixed_addresses
};

void __init early_ioremap_setup(void)
{
    int i;

    for (i = 0; i < FIX_BTMAPS_SLOTS; i++) {
        WARN_ON_ONCE(prev_map[i]);
        slot_virt[i] = __fix_to_virt(FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*i);
    }
}

pgtable

include/linux/pgtable.h

#ifndef pgd_index
/* Must be a compile-time constant, so implement it as a macro */
#define pgd_index(a)  (((a) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1))
#endif

static inline pgd_t *pgd_offset_pgd(pgd_t *pgd, unsigned long address)
{
    return (pgd + pgd_index(address));
};

/*
 * a shortcut to get a pgd_t in a given mm
 */
#ifndef pgd_offset
#define pgd_offset(mm, address)        pgd_offset_pgd((mm)->pgd, (address))
#endif

/*
 * a shortcut which implies the use of the kernel's pgd, instead
 * of a process's
 */
#define pgd_offset_k(address)        pgd_offset(&init_mm, (address))

fixmap_remap_fdt

// 실제 FDT(Device Tree Blob)의 물리 주소를 해당 가상 주소에 매핑
void *__init fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot)
{
    // FDT 용으로 정의된 fixmap 의 가상주소
    const u64 dt_virt_base = __fix_to_virt(FIX_FDT);
    phys_addr_t dt_phys_base;
    int offset;
    void *dt_virt;

    /*
     * Check whether the physical FDT address is set and meets the minimum
     * alignment requirement. Since we are relying on MIN_FDT_ALIGN to be
     * at least 8 bytes so that we can always access the magic and size
     * fields of the FDT header after mapping the first chunk, double check
     * here if that is indeed the case.
     */
    BUILD_BUG_ON(MIN_FDT_ALIGN < 8);
    if (!dt_phys || dt_phys % MIN_FDT_ALIGN)
        return NULL;

    dt_phys_base = round_down(dt_phys, PAGE_SIZE);
    offset = dt_phys % PAGE_SIZE;
    dt_virt = (void *)dt_virt_base + offset;

    // fdt 가 있는곳에 가상주소를 찾아내서 매핑
    /* map the first chunk so we can read the size from the header */
    create_mapping_noalloc(dt_phys_base, dt_virt_base, PAGE_SIZE, prot);

    if (fdt_magic(dt_virt) != FDT_MAGIC)
        return NULL;

    *size = fdt_totalsize(dt_virt);
    if (*size > MAX_FDT_SIZE)
        return NULL;

    if (offset + *size > PAGE_SIZE) {
        create_mapping_noalloc(dt_phys_base, dt_virt_base,
                       offset + *size, prot);
    }

    return dt_virt;
}

'개발 > arm64 linux 6 분석' 카테고리의 다른 글

early_param (setup_arch)  (0) 2025.05.31
jump_label (setup_arch)  (0) 2025.05.31
start_kernel 1  (0) 2025.05.24
proc.S __primary_switch  (0) 2025.05.17
proc.S __cpu_setup  (0) 2025.05.10
댓글