본문 바로가기

computer security/RISC-V

Chapter 3. Page tables

Paging hardware

1. virtual address and physical address

- virtual address : 프로그램이 사용하는 주소로, CPU가 직접 접근하지 않고 페이지 테이블을 통해 물리 주소로 변환된다.

- Physical address : 실제 RAM에 대한 주소로, DRAM 데이터를 읽고 쓰는데 사용된다.

RISC-V CPU는 항상 가상 주소를 사용하며, Sc39(39비트 가상 주소 모드)를 통해 물리 주소로 변환한다.

 

2. Sv39 : 39비트 가상 주소

Sv39는 64비트 주소 중 하위 39비트만 사용하고, 상위 25비트는 무시한다.

Sv39에서 가상 주소는 다음과 같이 나뉜다.

- 상위 27비트 : 페이지 테이블을 인덱싱하는 데 사용

- 하위 12비트 : 페이지 내 오프셋

주소 변환 과정

1) Sv39에서 페이지 테이블을 3단계 트리구조로 구성된다. 

2) 각 단계는 9비트를 사용하여 인덱싱하며, 최종적으로 512개의 페이지 테이블 엔트리(PTE) 중 하나를 선택한다.

3) 선택된 PTE에 있는 물리 페이지 번호(PPN)와 가상 주소의 하위 12비트를 결합하여 56비트 물리 주소를 생성한다.

 

단일 레벨 vs 다중 레벨 페이지 테이블

단일 레벨 페이지 테이블

RISC-V virtual and physical addresses

- 모든 가상 주소에 대해 단일 페이지 테이블로 변환할 수 있다.

- 메모리 낭비가 크다. 사용되지 않는 많은 페이지 테이블 엔트리들이 존재하기 때문이다.

 

3단계 페이지 테이블

RISC-V address translation details

- 메모리 효율적이다. 트리 구조 덕분에 가상 주소 공간의 많은 부분이 비어 있는 경우, 해당 부분에 대한 페이지 테이블을 생략할 수 있다.

 

3. PTE(Page Table Entry)의 구성

각 PTE에는 다음과 같은 정보가 포함된다 : 

PPN(Physical Page Number) : 44비트로, 실제 물리 메모리의 페이지 번호를 나타낸다.

플래그 비트 : 페이지의 속성을 나타낸다.

- PTE_V : 유효한 엔트리인지 여부

- PTE_R : 읽기 가능 여부

- PTE_W : 쓰기 가능 여부

- PTE_X : 실행 가능 여부

-PTE_U : 사용자 모드에서 접근 가능 여부

-PTE_A : 접근 플래그

- PTE_D : 수정 여부

 

4. TLB(Translation Lookaside Buffer)

Sv39에서 가상 주소를 물리 주소로 변활할 때 3단계 테이블 조회 과정은 시간이 많이 걸리기 때문에, RISC-V CPU는 변환 결과를 TLB에 캐시한다.

TLB는 최근에 변환한 가상 주소와 물리 주소를 저장하여 빠른 주소 변환을 가능하게 한다.

 

5. xv6의 메모리 매핑

 

kernel's address space

커널 주소 공간

- xv6 커널은 가상 주소 0x8000000부터 시작하여, 이 위치에 커널 코드, 데이터, 스택 등이 매핑된다.

- PHYSTOP : xv6에서 사용할 수 있는 물리 메모리의 상한이다.

- 커널은 모든 물리 메모리를 매핑하여 임의의 물리 주소에 직접 접근할 수 있다.

가상 주소와 물리 주소 매핑 예시

- 커널 코드 : R-X(읽기, 실행 가능)

- 커널 데이터 : RW-(읽기, 쓰기 가능)

- 가드 페이지 : 접근 불가능한 페이지로, 스택 오버플로우 방지를 위해 사용된다.

 

6. SATP 레지스터

RISC-V의 SATP 레지스터는 현재 CPU가 사용할 페이지 테이블의 루트 주소(물리 주소)를 저장한다.

xv6 커널은 각 CPU마다 독립된 페이지 테이블을 사용하며, 프로세스가 변경될 때마다 SATP 레지스터를 업데이트하며 프로세스 별 가상 메모리 공간을 제공한다.

 

Kernel address space

xv6가 사용하는 커널 주소 공간의 구조와 메모리 매핑 방식에 대한 설명.

xv6는 각 프로세스마다 개별 사용자 페이지 테이블을 유지하며, 커널 전체에 대해 하나의 커널 페이지 테이블을 사용한다.

 

1. xv6 커널의 주소 공간 구성

xv6는 QEMU에서 시뮬레이션하는 RAM(물리 메모리)과 메모리 랩 I/O 디바이스를 관리한다.

QEMU에서 RAM은 물리주소 0x80000000부터 0x88000000까지 할당되며, 이 범위를 PHYSTOP으로 정의한다.

0x80000000 아래의 주소 영역은 디스크 인터페이스와 같은 메모리 맵 디바이스가 위치한다. 커널은 이 영역의 주소에 직접 read/write를 수행하여 디바이스와 통신할 수 있다.

 

2. 커널의 direct mapping

xv6 커널은 가상 주소와 물리 주소를 1:!로 매핑하여 물리 메모리와 디바이스에 직접 접근할 수 있다. 예를 들어 커널 자체는 KERNBASE = 0x80000000부터 시작하며, 이 가상 주소는 물리 주소 0x80000000과 동일하게 매핑된다. 이렇게 직접 매핑을 사용하면 커널이 메모리를 관리할 때 주소 변환 과정을 단순화할 수 있다. 

 

3. 특별한 커널 가상 주소커널 주소 공간 중 일부는 직접 매핑되지 않고 특별하게 매핑된다. 

 

트램폴린 페이지- 트램폴린 페이지는 가상 주소 공간의 가장 상단에 매핑된다.- 트램폴린 페이지는 사용자 페이지 테이블에도 같은 위치에 매핑되며, 사용자 모드에서 커널 모드로 전환할 때 중요한 역할을 한다.- 이 페이지는 동일한 물리 페이지가 커널 주소 공간의 두 위치에 매핑되는 예시이다(가상 주소 공간 상단, 직접 매핑된 주소)커널 스택- 각 프로세스마다 개별 커널 스택이 있으며, 가상 주소 공간의 높은 위치에 매핑된다.- 커널 스택 아래에는 가드 페이지가 위치하며, 이 페이지의 PTE는 유효하지 않음(PTE_V = 0)으로 설정된다.- 가드 페이지의 역할 : 만약 커널 스택이 오버플로우되면 가드 페이지로 인해 페이지 폴트 예외가 발생하고 커널이 패닉 상태에 빠진다. 가드 페이지가 없다면 스택 오버플로우 시 다른 커널 메모리 영역을 덮어쓸 수 있어 시스템 동작이 비정상적으로 변할 수 있다.

 

4. 커널 페이지 매핑과 권한 설정트램폴린 페이지와 커널 코드 페이지는 PTE_R와 PTE_X 권한으로 매핑된다.이 권한은 커널이 해당 페이지에서 코드를 읽고 실행할 수 있도록 한다.그 외의 커널 페이지(데이터, 힙, 스택)는 PTE_R와 PTE_W 권한으로 매핑된다. 가드 페이지는 PTE_V 플래그가 설정되지 않아 무효한 페이지로 처리되며, 접근시 예외가 발생한다.

 

code : creating an address space

1. 주소 공간과 페이지 테이블의 기본 개념주소 공간 : 각 프로세스는 자신만의 가상 주소 공간을 갖는다. xv6는 각 프로세스마다 별도의 페이지 테이블을 유지하여 가상 주소를 물리 주소에 매핑한다.페이지 테이블 : RISC-V에서  페이지 테이블은 3단계 트리 구조로 이루어져 있으며, 루트 페이지 테이블부터 시작하여 각 단계마다 9비트씩을 사용하여 다음 단계로 이동한다. 최종적으로 PTE에 PPN과 권한 플래그를 저장하여 가상 주소와 물리 주소를 연결한다.

 

2. 주요 데이터 구조와 함수1) pagetable_t : 루트 페이지 테이블을 가리키는 포인터. xv6에서는 커널 전용 페이지 테이블과 프로세스 별 사용자 페이지 테이블을 각각 관리한다. 2) walk 함수 : 가상 주소에 해당하는 PTE를 찾는 역할을 한다. RISC-V 3단계 페이지 테이블 구조를 모방하여, 가상 주소의 상위 27비트를 9비트씩 나누어 각 단계의 PTE를 탐색한다. 3) mappages 함수 : 가상 주소 범위를 물리 주소 범위에 매핑하는 함수이다. 주어진 가상 주소 범위에 대해, 페이지 크기 간격으로 반복하면서 각각의 가상 주소에 대해 walk을 호출하여 가상 주소에 대응하는 PTE를 찾고 물리 페이지 번호와 권한 플래그를 설정하여 매핑을 완료한다. 4) kvmmap 함수 : 커널이 필요한 메모리 영역을 직접 매핑하는 함수. mappages를 호출하여 커널 코드, 데이터, 물리 메모리, 그리고 디바이스 메모리 맵을 직접 매핑한다. 

 

3. 커널 초기화 과정1) 커널 페이지 테이블 생성부팅 초기에 main 함수가 kvminit을 호출하여 커널의 페이지 테이블을 생성한다.kvminit은 kvmmake를 호출하여 루트 페이지 테이블을 위한 페이지를 할당하고, kvmmap을 호출하여 커널 코드와 데이터, 물리 메모리, 그리고 디바이스 메모리를 매핑한다.이때 가상 주소는 물리 주소와 동일하게 직접 매핑되기 때문에, 초기에는 주소 변환이 필요하지 않는다.

2) 페이지 테이블 활성화

main 함수는 kvminithar를 호출하여 

- SATP 레지스터에 루트 페이지 테이블의 물리 주소를 기록한다.

  -> 이후 CPU는 이 페이지 테이블을 사용하여 가상 주소를 물리 주소로 변환한다.

- sfence.vma 명령어를 실행하여 TLB 캐시를 초기화한다.

 

4. TLB 관리

RISC-V CPU는 페이지 테이블 엔트리를 캐싱하기 위해 TLB를 사용한다.

페이지 테이블이 변경된 후에도 CPU가 이전 TLB 캐시를 사용하면 잘못된 주소로 접근할 수 있기 때문에, xv6는 페이지 테이블을 변경할 때마다 TLB를 무효화해야 한다.

RISC-V에서는 이를 위해 sfence.vma 명령어를 사용하여 현재 CPU의 TLB 캐시를 무효화한다.

sfence.vma는 다음과 같은 경우에 호출된다.

- SATP 레지스터를 변경할 때(즉, 새로운 페이지 테이블로 전환할 때)

- 트램폴린 코드에서 사용자 모드로 전환하기 전에(사용자 페이지 테이블을 활성화할 때)

 

5. 주소 공간 식별자(ASID)

일부 RISC-V CPU는 ASID(Address Space Identifier)라는 기능을 지원하여, 특정 주소 공간에 대한 TLB 항목만 선택적으로 무효화할 수 있다.

ASID를 사용하면 TLB 전체를 무효화하지 않고도 특정 프로세스의 TLB 항목만 무효화할 수 있기 때문에 성능이 향상된다.

그러나 xv6는 이 기능을 사용하지 않는다.

 

 

Physical Memory Allocation

커널이 동적 메모리를 할당해야 하는 경우 : 페이지 테이블, 사용자 메모리, 커널 스택, 파이프 버퍼 등을 위한 메모리가 필요할 때

물리 메모리 할당 범위 : 커널의 끝부터 PHYSTOP까지의 물리 메모리를 동적 할당에 사용한다.

페이지 단위 할당 : xv6는 한 번에 4kB 단위로 메모리를 할당하고 해제한다. 

할당된 페이지 관리 방식 : free list를 이용해 관리하며, 각 페이지에 구조체(struct run)을 연결하여 연결 리스트 형태로 관리한다.

 

Physical Memory Allocator

자유 리스트 : 할당할 수 있는 물리 메모리 페이지들의 연결 리스트이다.

구조체 run : 자유 리스트의 각 페이지에 해당하는 요소로 사용된다. 자유 리스트에 포함된 페이지는 아무것도 저장되어 있지 않으므로, 페이지 자체에 struct run을 저장하여 관리한다.

초기화 과정 : kinit함수가 호출되어 커널의 끝부터 PHYSTOP까지의 모든 페이지를 자유 리스트에 추가한다. freerange 함수가 각 페이지를 4KB 정렬된 주소로 맞춘 후, kfree를 호출하여 자유 리스트에 추가한다.

kfree 동작 : 

1) 해제할 페이지의 모든 바이트를 1로 설정하여 dangling reference를 감지할 수 있도록 한다. 

2) 페이지를 struct run으로 변환한 후 자유 리스트 앞에 추가한다.

kalloc 동작 : 자유 리스트의 첫 번째 페이지를 제거하고 반환한다.

 

Process Address Space

페이지 테이블 : 각 프로세스는 별도의 페이지 테이블을 가지며, 문맥 전환 시 페이지 테이블도 함께 변경된다.

프로세스 주소 공간 구성 : 가상주소 0에서 시작하여 최대 MAXVA까지 확장될 수 있으며, 최대 256GB의 메모리를 가상으로 주소화할 수 있다.

 

Code : sbrk

sbrk는 프로세스가 메모리를 확장하거나 줄일 때 사용하는 시스템 콜이다. 

growproc 함수에 구현되어 있다.

growproc은 인자로 전달된 n 값이 양수인지 음수인지에 따라 동작한다.

- 양수일 경우 : 프로레스의 메모리를 확장하며 uvmalloc 함수를 호출하여 새로운 물리 메모리를 할당한다.

- 음수일 경우 : 프로세스의 메모리를 줄이며, uvmdealloc 함수를 호출하여 기존 메모리를 해제한다.

 

Code : exec

exec는 현재 프로세스의 사용자 주소 공간을 새로운 프로그램으로 교체하는 시스템 콜. 

실행 파일(ELF 바이너리)을 읽어와 새로운 메모리 이미지를 구성하고 기존의 메모리 이미지를 삭제한다.

동작 과정 :

1) 바이너리 열기 : 인자로 전달된 실행 파일 경로를 namei 함수를 통해 열고, ELF 헤더를 읽는다.

2) ELF 헤더 검사 : ELF 바이너리인지 확인하기 위해 ELF 헤더에 포함된 매직 넘버를 검사한다.

3) 새 페이지 테이블 생성 : proc_pagetable을 호출하여 새로운 페이지 테이블을 생성한다. 이때 페이지 테이블은 사용자 공간에 대한 매핑이 없는 초기 상태이다.

4) ELF 섹션 로드 : ELF 파일의 프로그램 섹션 헤더(proghdr)에 따라 각 섹션을 읽어와 적절한 가상 주소에 로드한다. uvmalloc을 호출하여 필요한 크기만큼 물리 메모리를 할당하고, loadseg를 이용하여 파일 내용을 메모리에 복사한다.

5) 스택 페이지 할당 및 초기화 : 한 페이지 크기의 스택을 할당하고, 프로그램 인자 문자열을 스택 상단에 복사한 뒤 그에 대한 포인터 배열을 만든다.

6) 새 메모리 이미지 커밋

 

'computer security > RISC-V' 카테고리의 다른 글

Chapter 6. Locking  (7) 2025.01.14
Chapter 5. Interrupts and device drivers  (6) 2025.01.13
Chapter 4. Traps and system calls  (4) 2025.01.09
chapter 2. Operating system organization  (7) 2025.01.06
chapter 1. operating system interfaces  (8) 2025.01.04