[커널 17차] 48주차

2021.08.01 00:51

ㅇㅇㅇ 조회 수:168

memmap_init_zone()

- 각 노드의 각 존에 할당된 페이지들에 대한 struct page를 초기화한다
- 전역 변수 highest_memmap_pfn보다 큰 end_pfn 값이 들어오면 업데이트한다
- ZONE_DEVICE의 경우 인수 altmap = NULL이므로 그냥 리턴한다
- start pfn부터 end pfn까지 루프를 돌면서 아래를 처리한다

1) context가 MEMINIT_EARLY인 경우
- 함수 overlap_memmap_init()로 movable zone안의 mirror memblock 영역들은 skip하도록 한다
- defer_init()는 CONFIG_DEFERRED_STRUCT_PAGE_INIT는 꺼져 있어서 빈 함수이다

2) __init_single_page() 함수로 pfn에 해당하는 struct page를 초기화한다
3) context가 MEMINIT_HOTPLUG이면 __SetPageReserved()로 page를 reverse 처리한다

4) pageblock 단위의 첫 pfn마다 set_pageblock_migratetype() 함수로 페이지 속성을 MIGRATE_MOVABLE로 변경한다
- cond_resched()로 현재 thread가 preempt_count = 0 (preemptible)인 경우에 새로운 프로세스로 preempt한다
- https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=sysganda&logNo=30175845113

5) pfn을 increment한다


overlap_memmap_init()
- mirrored_kernelcore가 설정되고 zone이 ZONE_MOVABLE인 경우에 mirrored memblock을 skip하는 함수임
- 결과적으로 normal zone의 경우 spanned page 전체에 대해 page가 초기화되고, movable zone의 경우 mirror가 아닌 경우에 한해 page가 초기화되어 movable zone에서 mirror가 아닌 memblock 영역은 처음에 normal zone으로 초기화되었다가 다음에 movable zone으로 다시 초기화되는 것으로 보인다

 

set_pageblock_migratetype(page, flags, pfn, mask)
- set_pfnblock_flags_mask() 함수로 pfn에 해당하는 mem_section->usage->pageblock_flags의 비트맵의 mask 영역을 flag로 cmpxchg로 업데이트한다
- 각 subsection (pageblock) 별로 flag 비트 배열을 관리
- 만약 page_group_by_mobility_disabled가 1이고 migratetype < MIGRATE_PCPTYPES이면 migratetype을 MIGRATE_UNMOVABLE로 변경한다
- page_group_by_mobility_disabled가 1인 경우는 pageblock 수가 4개 미만인 경우이다 (8MB)

 

ARM64인 경우 WANT_PAGE_VIRTUAL은 정의되어 있지 않다
- SPARCE, ARC 등에서 정의됨


free_area_init() 계속
- free_area_init_node(nid) 이후 부터
- 노드에 메모리가 있는 경우 (pgdat->node_present_pages), node_states[N_MEMORY] 비트맵에 해당 노드 비트를 1로 세팅한다
- check_for_memory()로 노드의 메모리 상태 비트맵을 업데이트한다


check_for_memory()
- CONFIG_HIGHMEM이 설정되어 있으면 zone_movable보다 낮은 zone들 중 하나라도 메모리가 있으면 해당 노드를 HIGH_MEMORY가 있는 노드로 설정 (비트맵 node_state[N_HIGH_MEMORY]에 노드 비트를 1로 설정)
- 마찬가지로 zone_movable보다 낮은 zone들 중 하나라도 메모리가 있으면 해당 노드를 NORMAL_MEMORY가 있는 노드로 설정 (비트맵 node_state[N_NORMAL_MEMORY]에 노드 비트를 1로 설정)


bootmem_init() 계속
- zone_sizes_init() 이후로 계속
- memblock_dump_all()로 현재 memblock 정보를 모두 커널 로그에 출력한다


early_memblock()
- early_param("memblock", early_memblock)에 설정되어 있음
- cmdline에 "debug"가 있으면 memblock_debug 변수를 1로 설정

 

memblock_dump_all()
- memblock_debug가 1이면 __memblock_dump_all()로 모든 memblock 정보를 커널 로그로 출력한다

 

__memblock_dump_all()
- memblock_dump() 함수로 memblock의 memory/reserved 영역을 모두 커널 로그로 덤프한다

 

memblock_dump()
- memblock의 모든 region에 대해 type name, index, base, end, size, node 관련 정보, flag를 커널 로그에 출력한다

 

==============================================

ASAN 개요
- 아래 코드를 변형해서 체크

 

int *a = ??;
*a = 1;

 

- 위 코드를 아래와 같이 변형

 

if (!address_is_ok(a)) {
    ERROR;
}
*a = 1;

 

- 위에서 address check를 위해 shadow memory가 필요하다
- address check를 위해서 shadow memory base + address(a) >> 3을 체크
- 위 영역에는 address(a)의 valid 여부와 속성을 기록한다
- 에를 들어 redzone이면 접근 불가이고 redzone의 특성이 기록된다
- left redzone, mid redzone, right redzone, heap overflow, heap underflow, heap use after free, ...
- 잘못된 주소에 접근하면 위와 같이 shadow memory를 체크해서 invalid함을 체크하고 그 사유는 redzone의 값을 읽어서 출력해준다
- 따라서 shadow memory가 1/8이 필요하다

 

================================================

 

kasan_init()
- 커널 이미지, 리니어 매핑 영역 중 실제로 memblock 물리 메모리가 존재하는 가상 주소에 대해서 shadow memory를 memblock으로 할당하고 swapper_pg_dir MMU table에 반영시킨다
- 나머지 영역, PAGE_END부터 module영역 전까지, module 영역 다음부터 커널 이미지 전까지, 커널 이미지 다음부터 shadow 메모리 영역 끝 부분에 대해서는 그냥 kasan_early_shadow_p4d/pud/pmd/pte 및 kasan_early_shadow_page만을 사용해서 돌려막기식으로 MMU 매핑테이블을 대충 만들어서 swapper_pg_dir에 반영한다
- init_task.kasan_depth = 0으로 만들고 종료한다

 

- 먼저 tmp_pg_dir 주소 영역에 swapper_pg_dir를 복사하고 dsb 배리어로 store가 완전히 끝났음을 보장한다
- 이후 cpu_replace_ttbr1()로 tmp_pg_dir 매핑으로 TTBR1을 변경한다 (기존 swapper_pg_dir과 동일)
- 이제부터 swapper_pg_dir을 KASAN shadown memory가 추가된 매핑으로 변경하는 작업을 실행한다
- 여기서부터 하는 작업은 swapper_pg_dir을 변경시키는 작업이다
- clear_pgds()로 KASAN SHADOW 영역의 pgd를 모두 삭제한다
- kasan_map_populate()로 커널 이미지 영역에 대한 shadow memory를 할당하고 MMU table을 만든다
- kasan_populate_early_shadow()로 PAGE_END (KASAN SHADOW 시작 주소)부터 module shadow start 전까지 shadow memory 및 MMU table을 만든다
- kasan_populate_early_shadow()로 커널 이미지 이후부터 KASAN SHADOW 끝까지 shadow memory 및 MMU table을 만든다
- 커널 이미지 시작 주소가 모듈 마지막 주소보다 크면 kasan_populate_early_shadow()로 module shadow end부터 커널 이미지 시작 전까지 shadow memory 및 MMU table을 만든다
- 각 memblock.memory 영역에 대해서 kasan_map_populate()로 shadow memory를 할당하고 MMU table을 만든다
- 각 kasan_early_shadow_pte에 kasan_early_shadow_page 물리 주소를 페이지 attribute와 함께 기록한다
- kasan_early_shadow_page를 0으로 memset한다
- cpu_replace_ttbr1()로 swapper_pg_dir로 TTBR1을 변경한다
- init_task.kasan_depth = 0로 변경하고 리턴한다

 

 

kasan_mem_to_shadow(vaddr)
- 입력받은 가상주소에 해당하는 shadow 가상 주소를 출력하는 함수

 

kasan_map_populate(start_addr, end_addr, nid)
- 시작주소부터 끝 주소까지 shadow memory용 매핑 테이블을 생성한다
- 테이블용 메모리와 shadow memory 모두 memblock을 호출하여 새로 할당받는다

 

kasan_populate_early_shadow(start_addr, end_addr)
- 시작주소부터 끝 주소까지 shadow memory용 매핑 테이블을 생성한다
- 제대로 테이블을 만드는 것이 아니라 전역변수 kasan_early_shadow_p4d/pud/pmd/pte 및 kasan_early_shadow_page를 사용해서 중복 매핑하여 엉성하게 매핑만 해놓는 방식이다
- 첫 주소(unligned인 경우) 및 마지막 주소(일부 남는 영역)인 경우는 새로 memblock으로 매핑받기도 한다

XE Login