? ? ? ? 筆者認為,Linux 操作系統(Operating System)最核心的機制是虛擬內存(Virtual Memory)。因為,操作系統主要作用是將硬件環境抽象起來,給在其中運行的應用(Applications)提供一個統一的、抽象的運行環境(Execution Environment),使得應用(Applications)專注于為用戶提供各種各樣的業務邏輯(Business Logics)。其中,虛擬內存提供了核心的支撐作用。
? ? ? ? 從下(底層,baremetal)往上看,運行的程序與外界交互,主要是與主存(Main Memory)、外圍設備(Peripheral Devices)交互(Access,Read/Write,IO)都是通過對其對應的地址(Addresses)進行讀寫操作(Read/Write Operations)來實現的。
? ? ? ? 而操作系統為了統一管理與外界的交互,使得,在操作系統上運行的應用(Applications)需要間接地(Indrectly)實現地址的訪問,也就是通過操作系統來實現交互。那么,虛擬內存就提供了這么一個機制,通過不同的頁表(Page Table)為每個應用提供一個獨立的地址空間(Address Space),然后,該地址空間的地址,通過對應的頁表,轉換成實際的物理地址(Physical Address),從而,對外界進行訪問。
? ? ? ? 這里所說的虛擬地址(Virtual Address, VA),主要指的 CPU load/store/branch 等指令所使用的地址。而物理地址(Physical Address, PA),主要指的是SoC上的總線地址,即經過總線的路由(Routing)后,指派到對應的設備上。
? ? ? ? 也就是說,在沒有虛擬地址轉換時,虛擬地址等于物理地址。
? ? ? ? 基于上述概念,以 RISCV Linux 以及 RISCV Sv39 為例,通過一系列的文章,詳細地梳理清楚 在 RISCV Sv39 上,Linux 是如何實現其虛擬內存機制的。
? ? ? ? (順便提一句,vmlinux 中的 vm,就是 virtual memory 的縮寫)
? ? ? ? 這是該系列的第一篇文章,主要講述 RISCV Sv39。
? ? ? ? RISCV Sv39 指 64 位的 RISCV CPU 中,其有效虛擬地址位數為 39 位,也就是有效位(Effective bits)為 0 - 38,另,39 - 64 的值與 bit 38 一樣。也就是 Two's Complement 的計數方式,方便地址的計算與劃分。
The RISC-V Instruction Set Manual: Volume II Privileged Architecture. P 11.4.
????????Instruction fetch addresses and load and store effective addresses,which are 64 bits, must have bits 63–39 all equal to bit 38, or else a page-fault exception will occur.
????????The 27-bit VPN is translated into a 44-bit PPN via a three-level page table, while the 12-bit page offset is untranslated.
? ? ? ? 即,根據RISCV Sv39 規定,39位有效虛擬地址被劃分為4段,高位三段為虛擬頁表索引號(Virtual Page Number),低12位為 頁表偏移量(Page Offset)。
? ? ? ? 另外,Sv39 規定,物理地址有效位數為 56 位,即 0 - 55。如下:
? ? ? ? 另外,頁表項 PTE (Page Table Entry) 的設計,如下:
? ? ? ? 這里需要注意的是,在RISCV 64 位的CPU角度上看,其訪問的地址(Virtual Address)是64位的。當 CPU 中的虛擬地址轉換模式(Virtual Address Translation Mode)被選定后,如 Sv39,那么,64位的CPU訪問地址(CPU Access Virtual Address),將裝換成虛擬地址轉換模式指定的虛擬地址,如 Sv39 虛擬地址 (Sv39 Virtual address)。其轉換規則由其轉換模式規定,如Sv39 規定,低39位CPU訪問地址有效,高位值必須等于 bit 38,類似符號擴展(sign extension)。基于,轉換模式,可以找到其對應的虛擬地址轉換模式指定的物理地址,如 Sv39 物理地址(Sv39 Physical Address),56位有效位的物理地址。然后,通過 0 擴展模式(zero extension)將其裝換成機器級(machine level)物理地址(physical address),也就是上面所說的SoC總線地址(Bus Address)。
? ? ? ? 也就是,從CPU角度出發,地址的轉換如下:
虛擬地址(Virtual Address, VA)/?CPU訪問地址(CPU Access Virtual Address)/ 邏輯地址(Logical Address )
-- 符號擴展(sign extension)--
????????==>?Sv39 虛擬地址 (Sv39 Virtual address)(VPN + Page Offset)
-- 虛擬地址轉換模式 --
????????==>?Sv39 物理地址(Sv39 Physical Address)(PPN + Page Offset)
-- 0 擴展模式(zero extension)--
????????==> 機器級(machine level)物理地址(physical address, PA)/?SoC總線地址(Bus Address)
????????通過 S mode 下的 satp 控制狀態寄存器(Control Status Register, CSR),設定虛擬地址轉換模式,如 Sv39,即物理根頁表號(Root PPN),如下:
? ? ? ? 其轉換過程如下:
? ? ? ? 其中,虛擬地址到物理地址的轉換過程,大致如下:(在不考慮PTE屬性的情況下)
#define XLEN (64) // 64 bits RISCV CPU
#define BITS_IN_BYTE (8) // 1 Byte = 8 bits
#define SATP_PPN_MASK (0x0fff'ffff'ffff)
#define PAGE_SIZE_BITS (12) // 4KB per page
#define PAGE_SIZE (1 << PAGE_SIZE_BITS)
#define PTE_SIZE (XLEN / BITS_IN_BYTE) // 8 Bytes per PTE
#define PTE_ENTRIES_PER_PAGE (PAGE_SIZE / PTE_SIZE) // 512 entries per page
#define Level_0_VA_OFFSET_BITS (PAGE_SIZE_BITS + 9 + 9)
#define Level_1_VA_OFFSET_BITS (PAGE_SIZE_BITS + 9)
#define Level_2_VA_OFFSET_BITS (PAGE_SIZE_BITS)PTE* level_0_page_table = (satp & SATP_PPN_MASK) << PAGE_SIZE_BITS
PTE* level_1_page_table = level_0_page_table[(va >> (Level_0_VA_OFFSET_BITS)) & (512 - 1)]
PTE* level_2_page_table = level_1_page_table[(va >> (Level_1_VA_OFFSET_BITS)) & (512 - 1)]
PTE* leaf_PTE = level_2_page_table[(va >> (Level_2_VA_OFFSET_BITS)) & (512 - 1)]
void * pa = leaf_PTE.ppn << Level_2_VA_OFFSET_BITS + (va & (1 << Level_2_VA_OFFSET_BITS - 1))
? ? ? ? 具體的轉換過程,請參考官方手冊。
? ? ? ? 其中,當 PTE 在 level_0_page_table 時,就是 葉表項(leaf_pte),那么該 PTE 描述的 1GB的空間(30 Bits),即
#define XLEN (64) // 64 bits RISCV CPU
#define BITS_IN_BYTE (8) // 1 Byte = 8 bits
#define SATP_PPN_MASK (0x0fff'ffff'ffff)
#define PAGE_SIZE_BITS (12) // 4KB per page
#define PAGE_SIZE (1 << PAGE_SIZE_BITS)
#define PTE_SIZE (XLEN / BITS_IN_BYTE) // 8 Bytes per PTE
#define PTE_ENTRIES_PER_PAGE (PAGE_SIZE / PTE_SIZE) // 512 entries per page
#define Level_0_VA_OFFSET_BITS (PAGE_SIZE_BITS + 9 + 9)
#define Level_1_VA_OFFSET_BITS (PAGE_SIZE_BITS + 9)
#define Level_2_VA_OFFSET_BITS (PAGE_SIZE_BITS)PTE* level_0_page_table = (satp & SATP_PPN_MASK) << PAGE_SIZE_BITS
PTE* leaf_PTE = level_0_page_table[(va >> (Level_0_VA_OFFSET_BITS)) & (512 - 1)]
void * pa = leaf_PTE.ppn << Level_0_VA_OFFSET_BITS + (va & (1 << Level_0_VA_OFFSET_BITS - 1))
? ? ? ? 同理,當 PTE 在 level_1_page_table 時,就是 葉表項(leaf_pte),那么該 PTE 描述的 2MB的空間(21 Bits),即
#define XLEN (64) // 64 bits RISCV CPU
#define BITS_IN_BYTE (8) // 1 Byte = 8 bits
#define SATP_PPN_MASK (0x0fff'ffff'ffff)
#define PAGE_SIZE_BITS (12) // 4KB per page
#define PAGE_SIZE (1 << PAGE_SIZE_BITS)
#define PTE_SIZE (XLEN / BITS_IN_BYTE) // 8 Bytes per PTE
#define PTE_ENTRIES_PER_PAGE (PAGE_SIZE / PTE_SIZE) // 512 entries per page
#define Level_0_VA_OFFSET_BITS (PAGE_SIZE_BITS + 9 + 9)
#define Level_1_VA_OFFSET_BITS (PAGE_SIZE_BITS + 9)
#define Level_2_VA_OFFSET_BITS (PAGE_SIZE_BITS)PTE* level_0_page_table = (satp & SATP_PPN_MASK) << PAGE_SIZE_BITS
PTE* level_1_page_table = level_0_page_table[(va >> (Level_0_VA_OFFSET_BITS)) & (512 - 1)]
PTE* leaf_PTE = level_1_page_table[(va >> (Level_1_VA_OFFSET_BITS)) & (512 - 1)]
void * pa = leaf_PTE.ppn << Level_1_VA_OFFSET_BITS + (va & (1 << Level_1_VA_OFFSET_BITS - 1))
? ? ? ? 那么,基于 RISCV Sv39 的虛擬地址轉換模式大致上如上所示。