• Wonhyuk Yang

[Training] Arm v8 Linux kernel head.S 찍어먹기 (5)

최종 수정일: 2021년 6월 19일

Remind

지난 시간에는 임시 페이지 테이블을 만들기 위해 필요한 페이지 수를 계산해서 링커 스크립터를 통해 예약해둔 것을 확인할 수 있었는데요. 이번 시간에는 그 공간을 사용해서 임시 페이지 테이블을 만드는 과정을 살펴볼 것이에요. 이제 코드를 살펴보도록 할까요?

해당 글의 타겟 아키텍처는 aarch64이고, kernel code는 5.1버전을 다룹니다. 유의하세요!

__create_page_tables

이전 시간에 이어서 다시 __create_page_tables 함수를 보도록 할게요. 이전에 init_pg 영역의 캐시 라인을 invalidate 했었고 그 뒤에는 identity mapping 테이블을 만드는 과정이 나와요. 실제 코드는 아래와 정확히 일치하지는 않는데요, 중간 중간에 minor한 일을 해주기 때문이에요. 하지만 핵심적인 부분 변환 테이블을 만드는 과정은 아래와 같이 진행되어요.

위 코드에서 보이듯이, map_memory 매크로는 다양한 레지스터들을 인자로 받아요. 앞에서 값을 세팅한 레지스터들은 입력으로 주어지는 레지스터들이고, 값이 할당되지 않은 레지스터들(x10, x11, x12, x13, x14)는 함수 내에서 중간 계산에 사용할 레지스터들이에요.


assembly로 짜여진 map_memory 매크로를 분석하는 일은 흥미로운 일이지만, 이 글을 읽는 대부분의 독자들은 어셈블리로 짜여진 코드를 분석하는 일을 좋아하진 않을 것 같네요. 그래서, 해당 매크로를 C로 포팅한 코드를 살펴보도록 할게요 :D

해당 C 코드에서는 임시 페이지 테이블을 3단계로 구성해요. 이 매핑 테이블을 만드는 알고리즘은 이전 시간에 살펴보았던 "임시 페이지 테이블을 만들기 위해 필요한 메모리 계산" 아이디어와 동일해요. 코드는 어렵지 않으니 잠깐동안 살펴보시겠어요?


그럼 이제 같이 왜 코드가 이렇게 생겼는지 실제 예제와 함께 같이 살펴보도록 해요. 이제 커널 이미지를 매핑한다고 생각해볼까요? 그러면 _text부터 _end까지의 가상 주소를 커널 이미지가 배치된 물리 주소와 매핑시키는 테이블을 만들어야 해요. 심볼들의 가상 주소와 물리 주소는 아래와 같다고 할게요.

  • __va(_text): 0xffff000010080000

  • __va(_end): 0xffff00001179f000

  • __pa(_text): 0x40080000

여기서 __va 매크로는 심볼의 가상 주소라는 것을 표현하기 위해서 사용했어요. __pa 매크로도 마찬가지로 심볼의 물리 주소를 표현하기 위해 사용했어요

또한, 다른 옵션들은 아래와 같다고 가정할게요.

  • VA_BITS = 48

  • PAGE_SIZE = 4K

  • Page Table level = 4

이제 Page Table의 첫 번째 변환 단계인 PGD를 구성해볼게요. __va(_text)에서 __va(_end)를 변환하기 위해서 필요한 엔트리의 수는 1개에요. 그러면 다음 레벨에 필요한 페이지의 수 역시 1개가 필요하겠지요? 이제 이 엔트리에 다음 레벨 테이블의 주소를 기입하면 되요. 그러기 위해서는 다음 레벨 테이블의 주소를 기억해야겠지요? 따라서 rtbl이라는 변수로 다음 레벨 테이블의 주소를 저장해요.


이제 PGD를 다 구성했으니 PUD를 구성할 차례에요. 전과 마찬가지로 tbl을 PUD로 이동하고, rtbl을 PMD의 위치로 움직여요. 그리고 해당 테이블에 채워 넣을 엔트리의 수를 계산(early_entries)를 하고, 엔트리에 값을 채워 넣으면(populate_entries) 돼요.

마지막 레벨의 PMD는 이전 레벨과는 다르게 rtbl은 매핑할 물리 메모리의 위치로 세팅해요. 그래서 엔트리에는 매핑할 물리 메모리의 주소가 들어가게 되는 것이지요. 그러면 완성되는 과정은 아래 그림과 같아요.


이제 map_memory는 이 정도로 넘어가고, 다시 head.S로 돌아오도록 할게요. __create_page_table은 두 개의 페이지 테이블을 구성하는데요, 이 중 하나가 앞의 예제에서 본 임시 페이지 테이블이고, 다른 하나는 identity 페이지 테이블이에요.


identity 매핑은 가상 주소와 물리 주소가 동일한 매핑인데요, idmap 섹션에 있는 코드들은 identity 매핑이 되어요. 그러면 왜 굳이 identity 매핑이 필요할까요? 우리는 현재 MMU가 비활성화되어 있는 상태이고, 이제 곧 MMU를 활성화 시킬 예정이에요. 이때 MMU를 활성화하는 instruction을 실행해도 프로그램 카운터는 물리 주소를 가르키고 있어요. 따라서 MMU 활성화 직후의 코드가 정상적으로 동작하기 위해서 identity 매핑을 해놓는 것이지요. 이 부분은 나중에 mmu를 활성화할 때 더 자세히 설명하도록 할게요.


자 이제 __create_page_table을 정리하면 이 함수는 아래와 같은 C 코드로 생각해도 무방해요.

이 외에 자잘한 부분이 있지만 그렇게 핵심적인 내용은 아니에요. 그래서 그 부분은 다루지는 않도록 할게요.


정리

이번 시간에는 map_memory 함수를 통해 매핑 테이블을 구성했는데요, 이제 남은 것은 이 매핑 테이블을 사용할 수 있도록 시스템 레지스터들을 적절히 세팅하는 것이에요. 이 부분은 다음 시간에 알아보도록 할게요.


Series





조회수 200회댓글 0개

관련 게시물

전체 보기