안녕하세요
http://jake.dothome.co.kr/fixmap/#comment-217495
링크를 통해 early_fixmap_init 을 공부하던 중 이해가 되지 않는 부분이 있어 질문을 하게 되었습니다.
제가 이해 하기로는
early_fixmap_init 함수에서
코드 라인 10~18번은 page table level 에 따른 변환 level을 처리하는 과정으로 이해 했습니다.
CONFIG_PGTABLE_LEVEL4 로 해석해보겠습니다.
10번 line에 if문 조건에 의해 CONFIG_PGTABLE_LEVEL 이 4일 경우 아래 함수를 실행하는데
pgd_none(pgd) || pgd_page_paddr(pgd) == __pa_symbol(bm_pud)
PUD TABLE 같은 경우는 이미 커널 이미지 용도로 활성화 되어있기 때문에 fixmap 시작 주소에 해당하는 pud 엔트리 포인터를 구합니다.
그리고 나머지 page table들을 활성화(?) 시키기 위해 아래 함수를 실행 합니다.
__pgd_populate(pgdp, __pa_symbol(bm_pud), PUD_TYPE_TABLE); //pgd table ->pud table 로 populate
__pud_populate(pudp, __pa_symbol(bm_pmd), PMD_TYPE_TABLE); //pud table -> pmd table로 populate
__pmd_populate(pmdp, __pa_symbol(bm_pte), PMD_TYPE_TABLE); //pmd table ->pte table로 populate
최종적으로
init_mm -> pgd table -> pud_table -> pmd_table -> pte_table
와 같이 page table이 활성화 되는 것으로 알고 있습니다.
제가 이해하고 있는게 맞다고 가정하고
CONFIG_PGTABLE_LEVEL3으로 해석 하면
if (CONFIG_PGTABLE_LEVELS > 3 && // level3이므로 if문은 무조건 거짓이 됩니다.
따라서 아래 조건문은 실행 하지 않습니다.
!(pgd_none(pgd) || pgd_page_paddr(pgd) == __pa_symbol(bm_pud)))
실행하지 않는다는 근거는
disassembly로 보았을 때 조건에 따라 분기하는 명령어가 보여서 입니다.
0xffff0000111261f8 <+44>: cbz x0, 0xffff000011126210 <early_fixmap_init+68>
0xffff000011126204 <+56>: cmp x1, x0
0xffff000011126208 <+60>: b.eq 0xffff000011126240 <early_fixmap_init+116> // b.none
즉 아래 그림처럼 3단계의 페이지 테이블을 사용하므로 중간 pud 테이블이 생략되는 과정으로 이해했습니다.
문제는 pud가 생략되면 pgd 테이블에서 pmd 테이블로 populate 하는 작업이 보이지 않는데
어떻게 그림처럼 pgd table->pmd table로 갈 수 있는지 궁금합니다.
다른 함수에 후처리를 해주는건가요? 아니면 제가 early_fixmap_init 함수를 잘못 이해하고 있는 건가요??
감사합니다.
.
안녕하세요? 문c블로그(http://jake.dothome.co.kr)의 문영일입니다.
아래 early_fixmap_init() 함수의 일부분을 코드를 보면서 설명하겠습니다.
arch/arm64/mm/mmu.c - early_fixmap_init()
810 if (CONFIG_PGTABLE_LEVELS > 3 &&
811 !(pgd_none(pgd) || pgd_page_paddr(pgd) == __pa_symbol(bm_pud))) {
812 /*
813 * We only end up here if the kernel mapping and the fixmap
814 * share the top level pgd entry, which should only happen on
815 * 16k/4 levels configurations.
816 */
817 BUG_ON(!IS_ENABLED(CONFIG_ARM64_16K_PAGES));
818 pudp = pud_offset_kimg(pgdp, addr);
819 } else {
820 if (pgd_none(pgd))
821 __pgd_populate(pgdp, __pa_symbol(bm_pud), PUD_TYPE_TABLE);
822 pudp = fixmap_pud(addr);
823 }
824 if (pud_none(READ_ONCE(*pudp)))
825 __pud_populate(pudp, __pa_symbol(bm_pmd), PMD_TYPE_TABLE);
826 pmdp = fixmap_pmd(addr);
827 __pmd_populate(pmdp, __pa_symbol(bm_pte), PMD_TYPE_TABLE);
코드 라인 820~821, pud 테이블에 매핑할 pgd 엔트리가 매핑되어 있지 않은 경우 연결하라는 코드입니다. 그러나 페이지 테이블이 3 단계 이하에서는 pud 테이블 처리를 하지 않기 때문에 pgd_none()은 바로 아래 코드와 같이 항상 false가 되면서 pud 테이블을 사용하지 않게됩니다.
include/asm-generic/pgtable-nop4d.h - pgd_none()
22 static inline int pgd_none(pgd_t pgd) { return 0; }
코드 라인 822, 이 라인이 중요합니다. population된 pud 테이블에서 주소에 해당하는 pud 엔트리를 가져오게 되어있는데, pud 페이지 테이블을 사용하지 않는 관계로 상위 테이블인 pgd 테이블에서 pgd 엔트리를 사용합니다. 다음 fixmap_pud() 함수에서 사용하는 pud_offset_kimg() 함수 코드를 보시면 pgdp 엔트리를 그대로 반환하는 것을 알 수 있습니다.
arch/arm64/mm/mmu.c - fixmap_pud()
770 static inline pud_t * fixmap_pud(unsigned long addr)
771 {
772 pgd_t *pgdp = pgd_offset_k(addr);
773 pgd_t pgd = READ_ONCE(*pgdp);
774
775 BUG_ON(pgd_none(pgd) || pgd_bad(pgd));
776
777 return pud_offset_kimg(pgdp, addr);
778 }
arch/arm64/include/asm/pgtable.h - pud_offset_kimg()
622 #define pud_offset_kimg(dir,addr) ((pud_t *)dir)
코드 라인 824~825, pmd 테이블에 매핑할 pud 엔트리가 매핑되어 있지 않은 경우 연결하라는 코드입니다. 3단계 페이지 테이블 구성의 경우 pud 엔트리 대신 pgd 엔트리가 대신합니다. 따라서 이 라인에서 3단계 페이지 테이블 구성을 사용하면 pud 테이블을 제외하고, pgd 엔트리와 pmd 테이블이 연결됩니다.
감사합니다.