mmu.c (2523B)
1 typedef unsigned long u64; 2 typedef unsigned int u32; 3 typedef unsigned char u8; 4 5 #include "arch.h" 6 7 #define PTE_V 0x001UL 8 #define PTE_R 0x002UL 9 #define PTE_W 0x004UL 10 #define PTE_X 0x008UL 11 #define PTE_U 0x010UL 12 #define PTE_G 0x020UL 13 #define PTE_A 0x040UL 14 #define PTE_D 0x080UL 15 16 #define KFLAGS (PTE_V | PTE_R | PTE_W | PTE_X | PTE_A | PTE_D) 17 #define DFLAGS (PTE_V | PTE_R | PTE_W | PTE_A | PTE_D) 18 #define UFLAGS (PTE_V | PTE_R | PTE_W | PTE_X | PTE_U | PTE_A | PTE_D) 19 20 __attribute__((aligned(4096))) static u64 l2_root[512]; 21 __attribute__((aligned(4096))) static u64 l1_user[512]; 22 23 extern void riscv_write_satp(u64 v); 24 extern void riscv_set_sum(void); 25 26 static u64 pte(u64 pa, u64 flags) { 27 return (pa >> 2) | flags; 28 } 29 30 static u64 pool_pa(int which) { 31 return which ? ARCH_USER_POOL_B_PA : ARCH_USER_POOL_A_PA; 32 } 33 34 static void fill_user_l1(int which) { 35 for (int i = 0; i < 512; i++) { 36 u64 pa = (u64)i * 0x200000UL; 37 l1_user[i] = pte(pa, DFLAGS); 38 } 39 u64 base = pool_pa(which); 40 for (int i = ARCH_USER_POOL_FIRST_SLOT; i <= ARCH_USER_POOL_LAST_SLOT; i++) { 41 u64 pa = base + (u64)(i - ARCH_USER_POOL_FIRST_SLOT) * 0x200000UL; 42 l1_user[i] = pte(pa, UFLAGS); 43 } 44 } 45 46 void arch_setup_mmu(void) { 47 for (int i = 0; i < 512; i++) l2_root[i] = 0; 48 fill_user_l1(0); 49 50 l2_root[0] = pte((u64)l1_user, PTE_V); 51 l2_root[1] = pte(0x40000000UL, DFLAGS); 52 /* VA == PA identity map for kernel-side DRAM, slot per 1 GiB. 53 * Covers PA 0x80000000 .. 0x1ffffffff (6 GiB), enough for QEMU 54 * virt configurations up to ~6 GiB of RAM. The DTB lives near 55 * the top of physical RAM (e.g. 0x13fe00000 with -m 3072M); we 56 * read it via VA = dtb_phys, so it must fall inside this map. */ 57 l2_root[2] = pte(0x80000000UL, KFLAGS); 58 l2_root[3] = pte(0xc0000000UL, KFLAGS); 59 l2_root[4] = pte(0x100000000UL, KFLAGS); 60 l2_root[5] = pte(0x140000000UL, KFLAGS); 61 l2_root[6] = pte(0x180000000UL, KFLAGS); 62 l2_root[7] = pte(0x1c0000000UL, KFLAGS); 63 /* Device alias: VA ARCH_DEVICE_ALIAS_BASE + PA → PA, used by 64 * arch_mmio_ptr / arch_console_putc to reach UART + virtio-mmio 65 * regs whose low-PA addresses overlap the user pool L1 slots. 66 * Lives well above the direct-map kernel window. */ 67 l2_root[8] = pte(0x00000000UL, DFLAGS); 68 69 riscv_set_sum(); 70 riscv_write_satp(((u64)8 << 60) | ((u64)l2_root >> 12)); 71 } 72 73 void arch_swap_user_pool(int which) { 74 fill_user_l1(which); 75 riscv_write_satp(((u64)8 << 60) | ((u64)l2_root >> 12)); 76 }