가상 메모리: Sv32부터 Sv57까지
프로그램이 쓰는 주소는 진짜 메모리 칩의 주소가 아니다. 하트는 load·store·명령어 인출을 할 때마다 가상 주소를 물리 주소로 바꾸는데, 그 변환표가 메모리에 놓인 페이지 테이블이다. 이 장에서는 RISC-V 특권 규격이 정한 페이지 기반 가상 메모리 Sv32·Sv39·Sv48·Sv57을 비트 단위로 해부하고, 규격의 변환 알고리즘을 시뮬레이터로 한 단계씩 따라간다.
- 가상 메모리가 주는 격리·요구 페이징·공유를 그림으로 설명한다.
- satp의 MODE·ASID·PPN 필드와 Sv32/Sv39/Sv48/Sv57의 주소 분할, 정규형 주소 규칙을 읽는다.
- PTE의 V·R·W·X·U·G·A·D 비트와 N·PBMT 비트의 뜻, 잎과 포인터의 차이를 안다.
- 규격의 주소 변환 알고리즘을 따라가며 물리 주소나 페이지 폴트를 직접 계산한다.
- 슈퍼페이지·Svnapot, SUM·MXR, Svade·Svadu가 변환 결과를 어떻게 바꾸는지 안다.
- TLB와 ASID, SFENCE.VMA의 rs1/rs2 조합, Svinval·Svvptc·Svpbmt의 역할을 이해한다.
왜 가상 메모리인가
운영체제 위에서 도는 프로그램은 저마다 “0번지부터 시작하는 넓은 메모리를 혼자 쓴다”고 믿는다. 실제로는 수십 개의 프로세스가 한 개의 DRAM을 나눠 쓰고 있다. 이 착각을 만들어 주는 장치가 가상 메모리(virtual memory)다. 하트가 내놓는 모든 주소는 가상 주소(VA)이고, 하드웨어가 이를 물리 주소(PA)로 바꿔서 메모리에 접근한다.
변환은 바이트 단위가 아니라 페이지(page) 단위로 한다. RISC-V의 기본 페이지는 4 KiB다. 가상 주소 공간을 4 KiB 조각(페이지)으로, 물리 메모리를 같은 크기의 조각(프레임(frame))으로 자르고, “가상 페이지 몇 번은 물리 프레임 몇 번”이라는 대응표를 프로세스마다 하나씩 둔다.
| 얻는 것 | 어떻게 | 예 |
|---|---|---|
| 격리 | 프로세스마다 다른 페이지 테이블. 남의 프레임은 아예 표에 없다. | 버그 난 프로그램이 커널이나 다른 프로그램을 덮어쓰지 못한다 |
| 요구 페이징 | V=0인 항목에 접근하면 페이지 폴트 → OS가 그때 프레임을 채운다 | 실행 파일을 다 읽지 않고 시작, 스왑, 메모리 초과 할당 |
| 공유 | 여러 표가 같은 프레임을 가리킨다 | 공유 라이브러리, 공유 메모리, fork 뒤 copy-on-write |
| 보호 | 페이지마다 읽기·쓰기·실행·사용자 권한 비트 | 코드 영역 쓰기 금지, 데이터 영역 실행 금지(W^X) |
| 연속된 착각 | 흩어진 프레임을 연속된 가상 주소로 보여 준다 | 큰 배열을 할당할 때 물리적으로 붙은 메모리가 필요 없다 |
규격의 해설은 “오랜 숙고 끝에 RV32와 RV64 모두 관례적인 4 KiB 페이지로 정했다”고 적는다. 큰 페이지는 TLB 한 칸이 덮는 범위를 넓히지만 내부 단편화로 메모리를 낭비한다. 기존 운영체제와 장치 드라이버를 옮기기 쉽게 4 KiB를 기본으로 두고, 큰 페이지가 필요할 때는 슈퍼페이지(이 장 뒤쪽)로 해결한다.
satp: 변환을 켜는 스위치
가상 메모리를 켜고 끄는 것은 S 모드 CSR 하나, satp(Supervisor Address Translation and Protection), CSR 주소 0x180다. 이 레지스터에는 세 가지가 들어 있다. 어떤 변환 방식을 쓸지(MODE), 지금 주소 공간의 이름표(ASID), 그리고 루트 페이지 테이블의 물리 주소를 4 KiB로 나눈 값(PPN)이다.
| SXLEN | MODE 값 | 이름 | 뜻 |
|---|---|---|---|
| 32 | 0 | Bare | 변환·보호 없음 (VA = PA) |
| 32 | 1 | Sv32 | 32비트 가상 주소, 2단계 페이지 테이블 |
| 64 | 0 | Bare | 변환·보호 없음 |
| 64 | 1–7 | — | 표준용 예약 |
| 64 | 8 | Sv39 | 39비트 가상 주소, 3단계 |
| 64 | 9 | Sv48 | 48비트 가상 주소, 4단계 |
| 64 | 10 | Sv57 | 57비트 가상 주소, 5단계 |
| 64 | 11 | Sv64 | 예약 (앞으로 정의할 64비트 방식) |
| 64 | 12–13 | — | 표준용 예약 |
| 64 | 14–15 | — | 사용자 정의(custom)용 |
satp에는 소프트웨어가 알아 두면 편리한 성질이 몇 가지 있다.
- 지원하지 않는 MODE를 쓰면 쓰기 전체가 무시된다. 어느 필드도 바뀌지 않는다. 그래서 커널은 Sv57을 써 보고 다시 읽어서, 값이 남아 있으면 그 모드를 지원한다고 판단할 수 있다.
- ASID 비트 수(ASIDLEN)는 구현마다 다르고 0일 수도 있다. ASID 필드에 모두 1을 쓰고 다시 읽어 보면 몇 비트가 구현되었는지 알 수 있다. 최대값(ASIDMAX)은 Sv32에서 9, Sv39·Sv48·Sv57에서 16이다.
- satp는 실효 특권 모드가 S나 U일 때만 “활성”이다. M 모드의 접근은 변환되지 않는다(단,
mstatus.MPRV=1이면 M 모드의 load/store가 MPP 모드의 권한으로 변환된다). - satp를 써도 TLB가 비워지지 않는다. 페이지 테이블을 고쳤거나 ASID를 재사용한다면
sfence.vma가 따로 필요하다. 반대로 MODE를 Bare↔다른 모드로 바꾸거나 ASID를 바꾸는 것 자체는 즉시 효력이 있다.
문맥 전환 때 ASID와 루트 테이블을 따로따로 바꾸면, 그 사이의 짧은 순간에 “새 ASID + 옛 테이블”이나 “옛 ASID + 새 테이블” 조합으로 변환이 일어나 남의 주소 공간 항목이 TLB에 섞일 수 있다. 한 CSR에 두면 csrw satp 한 번으로 둘이 원자적으로 바뀐다.
가상 주소의 모양: Sv32·Sv39·Sv48·Sv57
4 KiB 페이지에서 주소의 아래 12비트는 페이지 안의 위치, 즉 페이지 오프셋(page offset)이고 변환 없이 그대로 물리 주소로 넘어간다. 나머지 위쪽 비트가 가상 페이지 번호(VPN)다. VPN을 한 번에 큰 표로 찾지 않고 여러 조각으로 나눠, 조각 하나가 페이지 테이블 한 단계의 색인이 되도록 한다. 이것이 다단계(radix-tree) 페이지 테이블이다.
조각의 크기는 “페이지 테이블 하나는 정확히 한 페이지”라는 원칙에서 나온다. Sv39의 PTE는 8바이트이므로 4096 ÷ 8 = 512개 = 29, 그래서 VPN 조각은 9비트다. Sv32의 PTE는 4바이트라서 한 표에 1024개 = 210, 조각이 10비트다.
| 방식 | VA 비트 | 단계 | VPN 조각 | PTE | PA 비트 | 지원 페이지 크기 | 주소 공간 |
|---|---|---|---|---|---|---|---|
| Sv32 | 32 | 2 | 10 + 10 | 4 B | 34 | 4 KiB, 4 MiB | 4 GiB |
| Sv39 | 39 | 3 | 9 × 3 | 8 B | 56 | 4 KiB, 2 MiB, 1 GiB | 512 GiB |
| Sv48 | 48 | 4 | 9 × 4 | 8 B | 56 | + 512 GiB | 256 TiB |
| Sv57 | 57 | 5 | 9 × 5 | 8 B | 56 | + 256 TiB | 128 PiB |
정규형 주소와 가운데 “구멍”
RV64의 레지스터는 64비트지만 Sv39가 실제로 쓰는 가상 주소는 39비트뿐이다. 규격은 명령어 인출 주소와 load/store 유효 주소의 비트 63–39가 모두 비트 38과 같아야 하며, 그렇지 않으면 페이지 폴트가 난다고 정한다(Sv48은 63–48이 비트 47과, Sv57은 63–57이 비트 56과 같아야 한다). 이런 주소를 흔히 정규형 주소(canonical address)라 부른다.
결과적으로 쓸 수 있는 주소는 64비트 공간의 맨 아래 절반 조각과 맨 위 절반 조각이 되고, 그 사이는 거대한 구멍이다. 운영체제는 보통 아래쪽을 사용자 공간, 위쪽(최상위 비트가 1)을 커널 공간으로 쓴다. 최상위 비트 하나만 보고 사용자 주소인지 커널 주소인지 바로 가를 수 있어 편리하다. 규격 해설도 이것이 0 확장이 아니라 “굳어진 관례”를 따른 선택이라고 설명한다.
Sv48을 구현하면 Sv39도 반드시 구현해야 하고, Sv57을 구현하면 Sv48도 구현해야 한다. 더 넓은 하드웨어는 사실상 공짜로 좁은 방식을 지원할 수 있기 때문이다. 리눅스는 부팅할 때 하드웨어가 지원하는 가장 넓은 방식을 고르고, 커널 인자 no5lvl·no4lvl로 낮출 수 있다.
가상 주소 형식 탐색기
0x00007fffffffffff(Sv48 아래쪽 절반의 끝)를 넣고 Sv39와 Sv48에서 판정이 어떻게 달라지는지 확인해 보자. 거꾸로 Sv39에서 정규형인 주소는 Sv48·Sv57에서도 언제나 정규형이다.페이지 테이블 항목(PTE)
페이지 테이블의 한 칸이 페이지 테이블 항목(PTE)이다. 하위 10비트는 상태와 권한 비트이고, 그 위가 물리 페이지 번호(PPN)다. Sv39·Sv48·Sv57의 PTE는 아래 10비트와 위쪽 10비트(63–54)의 뜻이 모두 같고, 가운데 PPN을 몇 조각으로 나눠 보느냐만 다르다.
| 비트 | 이름 | 뜻 |
|---|---|---|
| 0 | V Valid | 1이면 유효. 0이면 나머지 비트는 하드웨어가 보지 않으므로 OS가 자유롭게 쓴다(예: 스왑 위치 기록). |
| 1–3 | R W X | 읽기·쓰기·실행 권한. 셋 다 0이면 이 PTE는 잎이 아니라 다음 단계 표를 가리키는 포인터다. |
| 4 | U User | U 모드가 접근할 수 있는 페이지. S 모드는 SUM=1일 때만 load/store할 수 있고, 실행은 언제나 금지. |
| 5 | G Global | 모든 주소 공간에 똑같이 있는 매핑(커널 영역 등). TLB가 ASID와 상관없이 공유할 수 있다. 포인터 PTE에 G가 있으면 그 아래 매핑이 모두 전역이다. |
| 6 | A Accessed | 마지막으로 지운 뒤 읽기·쓰기·인출이 있었음. 페이지 교체 정책의 힌트. |
| 7 | D Dirty | 마지막으로 지운 뒤 쓰기가 있었음. 내보낼 때 디스크에 다시 써야 하는지 알려 준다. |
| 8–9 | RSW | 감독자 소프트웨어용. 하드웨어는 무시한다. |
| 10–53 | PPN | 잎이면 데이터 페이지의, 포인터면 다음 단계 표의 물리 페이지 번호(44비트). |
| 54–60 | 예약 | 0이어야 한다. Svrsw60t59b 확장이 있으면 60–59는 소프트웨어용이 된다. |
| 61–62 | PBMT | Svpbmt: 페이지 단위 메모리 유형(PMA·NC·IO). 잎 PTE에서만 의미가 있다. |
| 63 | N | Svnapot: 연속된 64 KiB 영역의 일부임을 표시. |
R·W·X 조합과 잎/포인터
| X | W | R | 뜻 |
|---|---|---|---|
| 0 | 0 | 0 | 다음 단계 페이지 테이블을 가리키는 포인터 |
| 0 | 0 | 1 | 읽기 전용 페이지 |
| 0 | 1 | 0 | 예약 쓰기만 되는 페이지는 없다 |
| 0 | 1 | 1 | 읽기·쓰기 페이지 |
| 1 | 0 | 0 | 실행 전용 페이지 |
| 1 | 0 | 1 | 읽기·실행 페이지 |
| 1 | 1 | 0 | 예약 |
| 1 | 1 | 1 | 읽기·쓰기·실행 페이지 |
“The permission bits, R, W, and X, indicate whether the page is readable, writable, and executable, respectively. When all three are zero, the PTE is a pointer to the next level of the page table; otherwise, it is a leaf PTE. Writable pages must also be marked readable; the contrary combinations are reserved for future use.”
Volume II, Sv32: Addressing and Memory Protection — Sv39 이후의 방식도 “비트 9–0은 Sv32와 같은 뜻”이라며 이 정의를 그대로 물려받는다.
페이지 테이블 워크: 규격의 변환 알고리즘
TLB에 답이 없으면 하드웨어(페이지 워커)가 메모리의 페이지 테이블을 직접 따라 내려간다. 이를 페이지 테이블 워크(page-table walk)라 한다. Sv39에서는 satp가 가리키는 루트 표에서 VPN[2]로 한 칸을 고르고, 그 칸이 가리키는 표에서 VPN[1]로, 다시 VPN[0]으로 고른다.
규격은 이 과정을 알고리즘으로 정확히 적어 두었다. Sv32를 기준으로 쓰고, Sv39·Sv48·Sv57은 LEVELS와 PTESIZE만 바꾸면 된다(Sv39: LEVELS=3, PTESIZE=8). 아래는 Sv39에 맞춰 옮긴 것이다.
- a = satp.ppn × 4096, i = 2로 시작한다. (실효 특권 모드가 S나 U여야 한다.)
- pte = 메모리[a + va.vpn[i] × 8]를 읽는다. 이 읽기가 PMA·PMP 검사에 걸리면 원래 접근 종류의 접근 폴트(access fault)다.
- V=0이거나, R=0인데 W=1이거나, 예약 비트·예약 인코딩이 켜져 있으면 페이지 폴트.
- R=1 또는 X=1이면 잎이므로 5단계로. 아니면 포인터다: i = i − 1. i < 0이면 페이지 폴트. 아니면 a = pte.ppn × 4096으로 놓고 2단계로 돌아간다.
- 잎에 도착했다. i > 0인데 pte.ppn[i−1:0] ≠ 0이면 정렬되지 않은 슈퍼페이지 → 페이지 폴트.
- 현재 특권 모드와 SUM·MXR을 고려해 U 비트가 이 접근을 허락하는지 본다. 아니면 페이지 폴트.
- 그림자 스택(Zicfiss) 규칙상 R·W·X 조합이 허락되지 않으면 접근 폴트.
- R·W·X 비트가 이 접근을 허락하는지 본다. 아니면 페이지 폴트.
- A=0이거나, 쓰기인데 D=0이면: Svade라면 페이지 폴트, 아니면 PTE를 원자적으로 비교·갱신해 A(쓰기면 D도)를 1로 만든다.
- 성공. pa.pgoff = va.pgoff. i > 0이면 슈퍼페이지이므로 pa.ppn[i−1:0] = va.vpn[i−1:0], 그리고 pa.ppn[2:i] = pte.ppn[2:i].
“페이지 테이블이 이 접근을 허락하지 않는다”면 페이지 폴트(12·13·15)다. 반면 PTE를 읽으러 간 물리 주소나 최종 물리 주소가 PMP·PMA 검사에 걸리면 접근 폴트(1·5·7)다. 운영체제는 앞의 것만 고칠 수 있다. 뒤의 것은 M 모드가 친 울타리(20장)에 부딪힌 것이다.
Sv39 페이지 테이블 워크
슈퍼페이지와 Svnapot
알고리즘 4단계를 다시 보자. 잎은 꼭 레벨 0에서만 나오는 것이 아니다. 어느 단계의 PTE든 잎이 될 수 있다. Sv39에서 레벨 1에서 잎이 나오면 VPN[0]과 오프셋을 합친 21비트가 통째로 오프셋이 되어 2 MiB짜리 메가페이지(megapage)가 되고, 레벨 2에서 나오면 30비트 오프셋의 1 GiB 기가페이지(gigapage)가 된다. Sv48은 512 GiB 테라페이지, Sv57은 256 TiB 페타페이지까지 지원한다.
슈퍼페이지의 가장 큰 이득은 TLB 도달 범위다. TLB 한 칸이 4 KiB 대신 2 MiB나 1 GiB를 덮으므로, 커다란 데이터를 다루는 프로그램이나 커널의 물리 메모리 직접 매핑(linear map)에서 TLB 미스가 크게 준다. 워크도 짧아진다. 리눅스의 transparent huge page와 hugetlbfs가 이 기능을 쓴다.
Svnapot: 64 KiB 연속 페이지
2 MiB는 너무 크고 4 KiB는 너무 작을 때가 있다. Svnapot(NAPOT Translation Contiguity, v1.0)은 레벨 0의 PTE 16개가 64 KiB의 연속된 영역을 함께 매핑한다고 표시하는 방법이다. PTE의 N 비트(63)를 1로 하고, pte.ppn[0]의 아래 4비트를 1000으로 둔다. 현재 표준이 정한 크기는 64 KiB(napot_bits = 4) 하나뿐이고, 레벨 0이 아니거나 다른 패턴이면 예약 인코딩이라 페이지 폴트가 난다.
구현은 NAPOT 영역을 한 TLB 칸으로 처리하면서 16개 PTE 중 아무것이나 참고할 수 있다. 그래서 A·D 비트가 16개 사이에서 서로 다를 수 있다. 운영체제는 한 페이지가 접근되었는지(또는 수정되었는지) 알려면 16개 별칭을 모두 확인해야 한다. 영역을 바꿀 때는 먼저 16개를 모두 무효로 만들고 SFENCE.VMA를 한 다음 새 값을 쓴다.
권한 검사와 페이지 폴트
잎을 찾았다고 끝이 아니다. 알고리즘 5–9단계는 이 접근이 허락되는지 차례로 검사한다. 어느 하나라도 통과하지 못하면 원래 접근 종류에 맞는 페이지 폴트가 나고, stval(또는 mtval)에 문제의 가상 주소가 담긴다.
| 접근 종류 | 페이지 폴트 (scause) | 접근 폴트 | 이 페이지 폴트를 내는 명령 |
|---|---|---|---|
| 명령어 인출 | 12 Instruction page fault | 1 | X=0 페이지에서 실행, S 모드가 U=1 페이지 실행 |
| load | 13 Load page fault | 5 | load, LR, 캐시 블록 관리(CMO) 명령 |
| store·AMO | 15 Store/AMO page fault | 7 | store, SC, AMO, cbo.zero |
SUM과 MXR: sstatus의 두 스위치
sstatus의 두 비트가 권한 검사를 바꾼다. SUM(permit Supervisor User Memory access, 비트 18)이 0이면 S 모드가 U=1 페이지에 load/store하는 순간 폴트가 난다. 커널은 평소에 SUM=0으로 두고, copy_from_user처럼 사용자 메모리를 일부러 만지는 짧은 구간에서만 SUM을 켠다. 실수로 사용자 포인터를 따라가는 버그를 잡아 주는 안전장치다. MXR(Make eXecutable Readable, 비트 19)이 1이면 X=1 페이지도 읽을 수 있다. 실행 전용 페이지에서 명령어를 읽어 에뮬레이션해야 하는 트랩 처리기에 쓴다.
| 모드 | 페이지 U | 읽기·쓰기 | 실행 |
|---|---|---|---|
| U | 1 | R/W 비트대로 | X 비트대로 |
| U | 0 | 폴트 | 폴트 |
| S | 0 | R/W 비트대로 | X 비트대로 |
| S | 1 | SUM=1이면 R/W 비트대로, SUM=0이면 폴트 | 항상 폴트 |
SUM·MXR을 바꾸는 것은 SFENCE.VMA 없이 즉시 효력이 있다. TLB 항목에는 PTE의 권한 비트가 그대로 들어 있고, 검사는 매 접근마다 현재 SUM·MXR 값으로 하기 때문이다.
A·D 비트는 누가 켜나: Svade와 Svadu
운영체제는 A 비트로 “최근에 쓰인 페이지”를, D 비트로 “디스크에 다시 써야 할 페이지”를 가려낸다. 이 비트를 켜는 방법은 두 가지다.
| 방식 | A=0 또는 (쓰기·D=0)을 만나면 | 장단점 |
|---|---|---|
| Svade | 페이지 폴트. OS 처리기가 비트를 켜고 명령을 다시 실행한다. | 하드웨어가 단순. 처음 접근·처음 쓰기마다 트랩 비용 |
| Svadu (v1.0) | 하드웨어가 PTE를 원자적으로 비교·갱신해 A(와 D)를 켠다. | 트랩 없음. 페이지 워커가 메모리에 원자적으로 써야 한다 |
Svadu가 있으면 menvcfg.ADUE(비트 61)로 하드웨어 갱신을 켜고 끈다. ADUE=0이면 Svade처럼 동작한다. A 비트는 추측 실행 때문에 켜져도 되지만, D 비트는 실제로 실행된 store 때문에만, 그리고 프로그램 순서대로 켜져야 한다. 하드웨어가 A·D를 끄는 일은 없다. 스왑이나 메모리 매핑 입출력처럼 A·D가 필요 없는 페이지라면 처음부터 둘 다 1로 두는 것이 빠르다.
TLB, ASID, 그리고 SFENCE.VMA
load 하나마다 메모리를 세 번 더 읽는다면 컴퓨터는 몇 배 느려질 것이다. 그래서 하트는 최근 변환 결과를 TLB(Translation Lookaside Buffer)에 저장한다. 규격은 TLB라는 말 대신 주소 변환 캐시(address-translation cache)라는 추상적 이름을 쓰고, 그 크기와 구조는 정하지 않는다. 대신 이 캐시가 메모리의 페이지 테이블과 자동으로 일치하지 않는다(incoherent)는 점, 그리고 소프트웨어가 SFENCE.VMA로 동기화해야 한다는 점을 정한다.
SFENCE.VMA의 네 가지 모양
sfence.vma rs1, rs2(인코딩 0001001 rs2 rs1 000 00000 1110011)는 “앞서 이 하트에 보인 페이지 테이블 쓰기”를 “뒤따르는 변환을 위한 암묵적 읽기”보다 앞에 오도록 순서를 맞추고, 관련된 변환 캐시 항목을 무효화한다. rs1은 가상 주소, rs2는 ASID를 지정하며 레지스터 번호가 x0인지에 따라 범위가 달라진다.
| rs1 | rs2 | 범위 | 전역(G) 항목 | 쓰는 때 |
|---|---|---|---|---|
| x0 | x0 | 모든 주소 공간의 모든 단계 | 포함 | 전역 매핑을 바꾼 경우, 대규모 변경 |
| x0 | ASID | 그 ASID의 모든 단계 | 제외 | ASID 재사용, 포인터(비잎) PTE 변경 |
| VA | x0 | 그 VA의 잎 PTE, 모든 주소 공간 | 포함 | 전역 매핑의 잎 하나 변경 |
| VA | ASID | 그 VA의 잎 PTE, 그 ASID만 | 제외 | 사용자 페이지 하나의 잎 변경 (가장 흔함) |
규격이 꼽는 대표적인 규칙은 다음과 같다.
- 잎 PTE를 바꾸면 rs1에 그 페이지 안의 VA를 준다. 경로에 G=1인 PTE가 있었다면 rs2는 x0, 아니면 해당 ASID.
- 포인터 PTE를 바꾸면 rs1=x0으로 한다(그 아래 수많은 VA가 영향을 받으므로).
- ASID 없이(또는 항상 ASID 0으로) 운영한다면 satp를 쓸 때마다 rs1=x0, rs2=“값이 0인 x0 아닌 레지스터”로 sfence.vma를 한다. 전역 항목은 남겨 두기 위해서다.
- 권한을 늘리거나 무효→유효로 바꾸는 경우는 게으르게 처리해도 된다. 옛 값이 쓰이면 페이지 폴트가 나고, 그때 처리기에서 sfence.vma를 하면 된다.
- SFENCE.VMA는 자기 하트만 동기화한다. 다른 하트에는 IPI로 알리고 각자 실행하게 해야 한다. 이를 TLB 슛다운이라 하며, RISC-V에서는 보통 SBI의 RFENCE 확장(22장)을 이용한다.
- rs1의 값이 올바른 가상 주소가 아니면 아무 일도 하지 않는다(예외도 없다). 구현은 언제든지 더 넓게(예: 항상 전체) 무효화해도 된다.
TLB와 ASID: 두 프로세스가 번갈아 달릴 때
| 칸 | V | ASID | G | 가상 페이지 | PPN | 최근 사용 |
|---|
0x10000, a1 = 현재 ASID)Sv 확장들: Svpbmt·Svinval·Svvptc와 친구들
특권 규격의 감독자 장에는 기본 Sv 방식 외에도 작은 확장들이 모여 있다. 응용 프로세서 프로파일 RVA23S64는 이 중 상당수를 필수로 요구한다(23장).
| 확장 | 버전 | 하는 일 | RVA23S64 |
|---|---|---|---|
| Sv39 | — | 39비트 3단계 변환 | 필수 |
| Sv48 · Sv57 | — | 48·57비트 변환 | 선택 |
| Svnapot | 1.0 | 64 KiB NAPOT 연속 매핑 (N 비트) | 필수 |
| Svpbmt | 1.0 | PTE로 PMA 메모리 유형 덮어쓰기 (PBMT) | 필수 |
| Svinval | 1.0 | SFENCE.VMA를 무효화와 순서 맞추기로 쪼갬 | 필수 |
| Svade | — | A/D가 꺼져 있으면 페이지 폴트 | 필수 |
| Svadu | 1.0 | 하드웨어가 A/D를 원자적으로 갱신 | 선택 |
| Svvptc | 1.0 | 무효→유효 PTE 변경 뒤 SFENCE.VMA 생략 가능 | 선택 |
| Svrsw60t59b | 1.0 | PTE 비트 60–59를 소프트웨어용으로 | — |
Svpbmt: 페이지마다 메모리 유형 정하기
메모리 영역이 캐시 가능한지, 입출력 장치인지 같은 성질(PMA)은 원래 물리 주소마다 고정이다. Svpbmt은 잎 PTE의 비트 62–61(PBMT)로 이를 덮어쓴다. 예를 들어 그래픽 프레임 버퍼를 캐시하지 않되 쓰기 결합은 허용하는 NC로 매핑할 수 있다. menvcfg.PBMTE(비트 62)가 1일 때만 쓸 수 있고, 0이면 Svpbmt가 없는 것처럼 동작한다.
| 이름 | PBMT | 요청하는 메모리 속성 |
|---|---|---|
| PMA | 0 | 덮어쓰지 않음. 물리 주소의 PMA를 그대로 |
| NC | 1 | 캐시 불가, 멱등(idempotent), 약한 순서(RVWMO), 주 메모리 |
| IO | 2 | 캐시 불가, 비멱등, 강한 순서(I/O 순서), 입출력 |
| — | 3 | 예약 (잎에 쓰면 페이지 폴트) |
포인터 PTE의 비트 62–61은 예약이다. 같은 물리 페이지를 캐시 가능 속성과 불가 속성으로 동시에 매핑하면 일관성이 깨질 수 있으므로, 속성을 바꿔 접근하기 전에는 fence iorw,iorw → cbo.flush → fence iorw,iorw 같은 순서가 필요하다고 규격은 설명한다.
Svinval: 무효화를 묶어서 빠르게
범위가 넓은 매핑을 해제할 때 SFENCE.VMA를 페이지마다 실행하면, 매번 “앞선 쓰기 정렬 + 무효화 + 뒤따르는 변환 정렬”을 다 하느라 파이프라인이 거듭 멈춘다. Svinval은 이것을 세 명령으로 쪼갠다.
| 명령 | 인코딩 | 하는 일 |
|---|---|---|
| sfence.w.inval | 0001100 00000 00000 000 00000 1110011 | 앞선 store를 뒤따르는 SINVAL.VMA보다 앞에 정렬 |
| sinval.vma rs1, rs2 | 0001011 rs2 rs1 000 00000 1110011 | 같은 rs1·rs2의 SFENCE.VMA가 지울 항목을 무효화 (순서 보장은 없음) |
| sfence.inval.ir | 0001100 00001 00000 000 00000 1110011 | 앞선 SINVAL.VMA를 뒤따르는 암묵적 변환 읽기보다 앞에 정렬 |
Svinval은 하이퍼바이저 확장이 있으면 hinval.vvma·hinval.gvma도 함께 제공한다(21장). SINVAL.VMA는 SFENCE.VMA와 같은 권한을 요구하므로 mstatus.TVM=1이면 S 모드에서 불법 명령 예외가 나지만, 두 순서 명령은 부작용이 없어 TVM과 상관없이 허용된다.
Svvptc와 Svrsw60t59b
Svvptc가 있으면 PTE의 V 비트를 0→1로 바꾼 store는 “유한한 시간 안에” 이 하트의 변환에 보이게 된다. 그래서 새 매핑을 만들 때마다 하던 SFENCE.VMA를 생략할 수 있다. 가끔 쓸데없는 페이지 폴트가 한 번 더 날 수 있지만, 명령을 아끼는 이득이 더 크다는 판단이다. 유효→무효, 권한 축소 같은 다른 변경에는 여전히 펜스가 필요하다. Svrsw60t59b는 원래 예약이라 0이어야 했던 PTE 비트 60–59를 운영체제가 메타데이터 저장용으로 쓸 수 있게 풀어 준다.
핵심 정리
- 가상 메모리는 페이지(기본 4 KiB) 단위로 VA를 PA로 바꾸며 격리·요구 페이징·공유·보호를 제공한다.
satp(0x180)는 MODE(RV64: Bare 0, Sv39 8, Sv48 9, Sv57 10 / RV32: Sv32 1), ASID, 루트 표의 PPN을 담는다. 지원하지 않는 MODE를 쓰면 쓰기 전체가 무시된다.- 페이지 테이블 하나는 정확히 한 페이지다. Sv39는 8바이트 PTE 512개이므로 VPN 조각이 9비트이고, Sv32는 4바이트 PTE 1024개라서 10비트다. Sv39 이상은 VA의 위쪽 비트가 부호 확장된 정규형이어야 한다.
- PTE의 R=W=X=0은 포인터, 하나라도 1이면 잎이다. W=1·R=0은 예약 조합이다. 어느 단계든 잎이 될 수 있어 메가·기가·테라·페타페이지가 생기며, 물리 쪽 정렬이 안 맞으면 페이지 폴트다.
- 권한 검사는 U 비트(SUM 고려), R·W·X(MXR 고려), A·D(Svade는 폴트, Svadu는 하드웨어 갱신) 순이며, 실패하면 접근 종류에 따라 12·13·15번 페이지 폴트가 난다.
- TLB는 ASID와 G 비트로 주소 공간을 구별한다. 페이지 테이블을 고친 뒤에는 rs1(VA)·rs2(ASID)로 범위를 고른
sfence.vma로 자기 하트를 동기화하고, 다른 하트에는 IPI로 알린다. - Svnapot(64 KiB 연속), Svpbmt(PMA·NC·IO), Svinval(무효화 묶음), Svvptc(무효→유효 펜스 생략)는 성능과 장치 매핑을 돕는 작은 확장이다.
확인 퀴즈
Sv39의 페이지 테이블 하나에 들어 있는 PTE 수와 PTE 하나의 크기는?
V=1이고 R=W=X=0인 PTE는 무엇을 뜻하나?
Sv39에서 가상 주소 0x0000004000000000에 load하면?
Sv39 워크가 레벨 1에서 잎 PTE를 만났는데 pte.ppn[0] ≠ 0이다. 결과는?
S 모드에서 SUM=1로 두고 U=1 페이지의 명령어를 실행하려 하면?
쓰기 권한(W=0)이 없는 페이지에 AMO 명령을 실행했다. scause는?
ASID 3인 프로세스의 사용자 페이지 하나(전역 아님)의 잎 PTE를 고쳤다. 가장 알맞은 명령은?
Svnapot으로 64 KiB 영역을 표시할 때 레벨 0 PTE의 ppn[0] 아래 4비트는?
x xxxx 1000이면 64 KiB 연속 영역이다(napot_bits = 4). 다른 패턴은 지금은 예약이라 페이지 폴트가 난다.더 읽을거리
- The RISC-V Instruction Set Manual, Volume II: Privileged Architecture — Supervisor-Level ISA (v20260120) — satp, SFENCE.VMA, Sv32/39/48/57, Svnapot, Svpbmt, Svinval, Svadu, Svvptc, Svrsw60t59b
- Virtual Address Translation Process — 이 장 시뮬레이터가 따른 변환 알고리즘
- Volume II, Machine-Level ISA — menvcfg의 PBMTE·ADUE, mstatus의 MPRV·SUM·MXR
- RVA23 Profiles v1.0 — RVA23S64의 필수·선택 Sv 확장
- 이 책의 18장(트랩), 20장(PMA·PMP), 21장(2단계 변환)