top of page

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

  • 작성자 사진: Wonhyuk Yang
    Wonhyuk Yang
  • 2021년 5월 31일
  • 2분 분량

최종 수정일: 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) 돼요.

ree

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

ree

이제 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





댓글


© 2024 by WHOAMI

bottom of page