setup_machine_fdt() -> fixmap_remap_fdt() 부분을 분석하다가
막히는 부분이 생겨 질문드립니다ㅠㅠ
fixmap_remap_fdt() 함수를 쭉 따라가다가...
1 start_kernel init/main.c
2 setup_arch init/main.c
3 setup_machine_fdt arch/arm64/kernel/setup.c
4 fixmap_remap_fdt arch/arm64/kernel/setup.c
5 __fixmap_remap_fdt dt_virt = __fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL_RO);
6 create_mapping_noalloc 1221 create_mapping_noalloc(round_down(dt_phys, SWAPPER_BLOCK_SIZE),
7 __create_pgd_mapping 617 __create_pgd_mapping(init_mm.pgd, phys, virt, size, prot, NULL,
8 alloc_init_pud 562 alloc_init_pud(pgdp, addr, next, phys, prot, pgtable_alloc,
- alloc_init_pud() 부분 까지들어 왔습니다.
static void alloc_init_pud(pgd_t *pgdp, unsigned long addr, unsigned long end,
phys_addr_t phys, pgprot_t prot,
phys_addr_t (*pgtable_alloc)(void),
int flags)
{
unsigned long next;
pud_t *pudp;
pgd_t pgd = READ_ONCE(*pgdp);
if (pgd_none(pgd)) {
phys_addr_t pud_phys;
BUG_ON(!pgtable_alloc);
pud_phys = pgtable_alloc();
__pgd_populate(pgdp, pud_phys, PUD_TYPE_TABLE);
pgd = READ_ONCE(*pgdp);
}
BUG_ON(pgd_bad(pgd));
pudp = pud_set_fixmap_offset(pgdp, addr);
do {
pud_t old_pud = READ_ONCE(*pudp);
next = pud_addr_end(addr, end);
/*
* For 4K granule only, attempt to put down a 1GB block
*/
if (use_1G_block(addr, next, phys) &&
(flags & NO_BLOCK_MAPPINGS) == 0) {
pud_set_huge(pudp, phys, prot);
/*
* After the PUD entry has been populated once, we
* only allow updates to the permission attributes.
*/
BUG_ON(!pgattr_change_is_safe(pud_val(old_pud),
READ_ONCE(pud_val(*pudp))));
} else {
alloc_init_cont_pmd(pudp, addr, next, phys, prot,
pgtable_alloc, flags);
BUG_ON(pud_val(old_pud) != 0 &&
pud_val(old_pud) != READ_ONCE(pud_val(*pudp)));
}
phys += next - addr;
} while (pudp++, addr = next, addr != end);
pud_clear_fixmap();
}
- pud_set_fixmap_offset(pgdp, addr)을 계속 따라 가다보면..
pud_set_fixmap(pud_offset_phys(pgdp, addr)
set_fixmap_offset(FIX_PUD, addr)
__set_fixmap_offset(idx, phys, FIXMAP_PAGE_NORMAL)
#define __set_fixmap_offset(idx, phys, flags) \
({ \
unsigned long ________addr; \
__set_fixmap(idx, phys, flags); \
________addr = fix_to_virt(idx) + ((phys) & (PAGE_SIZE - 1)); \
________addr; \
})
노란색 음영의 pudp에는 __set_fixmap_offset()에서 반환되는 값인 _______addr
즉, pudp에는 FIX_PUD 페이지에서 phys의 페이지 오프셋에 해당하는 엔트리 주소가 들어간다고 생각했습니다.
매핑하는 크기가 2M이기 때문에 1G block은 사용하지 않으므로,
아래 부분에서 do-while 구문 -> if-else 구문에서 else가 실행되어
alloc_init_cont_pmd(pudp, addr, next, phys, prot,
pgtable_alloc, flags);
alloc_init_cont_pmd() 함수로 들어가게 됩니다.
여기서 페이지를 할당하는 함수포인터 인자인 pgtable_alloc = NULL 이기 때문에
pmd 페이지를 할당하지 못하고 pudp엔트리(FIX_PUD 테이블 엔트리)에 값이 들어가있어야 하는 것으로 생각했습니다.
그런데, 제가 이해하고 있기로는 FIX_PUD 테이블은 이전 함수들에서 사용한 적이 없어서
FIX_PUD 테이블 엔트리에는 값이 들어가 있지 않는데... ( = 위에서 구한 pudp 엔트리에 들어가 있는 값이 없음)
pmd 페이지도 할당하지 않는 것으로 이해를 해서 매핑이 이상하게 되는 것 같습니다.
어딘가에서 제가 잘못 생각하고 있는 부분이 있는 것 같은데...
정리를 하면...
alloc_init_pud() 함수에서
1) pudp 는 FIX_PUD가 가리키는 페이지의 엔트리 주소이다
2) FIX_PUD 페이지는 이전 함수들에서 사용한 적이 없어서 엔트리에는 값이 들어가 있지 않다.
alloc_init_cont_pmd() 함수에서
3) pudp를 읽어 값이 없으면 pgtable_alloc함수로 페이지를 할당하는데,
pgtable_alloc = NULL이어서 페이지를 할당하지도 않는다..
혹시 제가 이해하고 있는 것 중에 잘못된 부분이 있으면 지적을 부탁드립니다!
.
안녕하세요? 문c 블로그(http://jake.dothome.co.kr)의 문영일입니다.
먼저 setup_arch() 함수에서 FDT에 접근해야하는 상황입니다.
정규 매핑이 동작하지 않는 상황이라 FDT를 fixmap의
일부 영역(FIX_FDT)을 사용하여 고정매핑할 예정입니다.
FIX_FDT 가상 주소영역을 FDT가 위치한 물리 주소 영역(2M)에 매핑하기 위해
다음과 같이 총 4단계 테이블 중 마지막 PTE 테이블을 제외하고 연결합니다.
매핑 순서: PGD->PUD->PMD->2M 블럭
컴파일 타임에 준비한 static한 페이지 테이블 init_pg_dir이
이미 준비되어 있는 상황입니다.
그래서 PGD, PUD, PMD 테이블을 생성할 필요는 없습니다.
참고로 페이지 테이블을 다음 단계의 페이지 테이블과 연결 시
fixmap의 FIX_PGD, FIX_PUD, FIX_PMD, FIX_PTE 등을 임시로 사용합니다.
이 함수에서는 FIX_PUD 및 FIX_PMD를 임시로 사용하고,
연결이 끝난 후 함수를 빠져나갈 때 매핑을 해제합니다.
그림으로 표현하면 다음과 같습니다.
(위에서 아래 방향으로 메모리 주소가 증가하게 표현하였습니다)
&init_pg_dir (init_mm.pgd가 가리키는 가상 주소)
+++++++++++++++++
| PGD |
| |
| +-pgdp----+ <--- PUD 테이블 연결
| | |
| | |
| | |
+++++++++++++++++ <-------+ <---- FIX_PUD에 임시 매핑
| PUD |
| |
| +-pudp----+ <--- PMD 테이블 연결
| | |
| | |
| | |
+++++++++++++++++ <-------+ <---- FIX_PMD에 임시 매핑
| PMD |
| |
| +-pmdp----> fdt 물리 주소 (2M 블럭 매핑)
| |
| |
| |
+++++++++++++++++
정리하면
1) pudp 는 FIX_PUD가 가리키는 페이지의 엔트리 주소입니다.
-> pudp는 &init_pg_dir 테이블 다음 pud 테이블에 해당하는 한 엔트리 주소입니다.
정확한 위치는 다음과 같습니다.
pgdp = &init_pg_dir 가상 주소 + FIX_FDT 가상 주소에 대한 pgd offset
pudp = FIX_PUD 가상 주소 + FIX_FDT 가상 주소에 대한 pud offset
pmdp = FIX_PMD 가상 주소 + FIX_FDT 가상 주소에 대한 pmd offset
2) FIX_PUD 페이지는 이전 함수들에서 사용한 적이 없어서 엔트리에는 값이 들어가 있지 않다.
-> init_pg_dir의 두 번째 페이지를 pud 테이블로 사용합니다.
3) pudp를 읽어 값이 없으면 pgtable_alloc함수로 페이지를 할당하는데,
pgtable_alloc = NULL이어서 페이지를 할당하지도 않는다.
-> 컴파일 타임에 준비한 init_pg_dir 테이블 다음 pud 테이블을 활용해야 하므로,
별도의 할당 함수를 동작시키지 않습니다.
감사합니다.