x86_mmu.c (6893B)
1/* 2 * Copyright (C) 2016 Veertu Inc, 3 * Copyright (C) 2017 Google Inc, 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this program; if not, see <http://www.gnu.org/licenses/>. 17 */ 18 19#include "qemu/osdep.h" 20#include "panic.h" 21#include "qemu-common.h" 22#include "cpu.h" 23#include "x86.h" 24#include "x86_mmu.h" 25#include "vmcs.h" 26#include "vmx.h" 27 28#define pte_present(pte) (pte & PT_PRESENT) 29#define pte_write_access(pte) (pte & PT_WRITE) 30#define pte_user_access(pte) (pte & PT_USER) 31#define pte_exec_access(pte) (!(pte & PT_NX)) 32 33#define pte_large_page(pte) (pte & PT_PS) 34#define pte_global_access(pte) (pte & PT_GLOBAL) 35 36#define PAE_CR3_MASK (~0x1fllu) 37#define LEGACY_CR3_MASK (0xffffffff) 38 39#define LEGACY_PTE_PAGE_MASK (0xffffffffllu << 12) 40#define PAE_PTE_PAGE_MASK ((-1llu << 12) & ((1llu << 52) - 1)) 41#define PAE_PTE_LARGE_PAGE_MASK ((-1llu << (21)) & ((1llu << 52) - 1)) 42 43struct gpt_translation { 44 target_ulong gva; 45 uint64_t gpa; 46 int err_code; 47 uint64_t pte[5]; 48 bool write_access; 49 bool user_access; 50 bool exec_access; 51}; 52 53static int gpt_top_level(struct CPUState *cpu, bool pae) 54{ 55 if (!pae) { 56 return 2; 57 } 58 if (x86_is_long_mode(cpu)) { 59 return 4; 60 } 61 62 return 3; 63} 64 65static inline int gpt_entry(target_ulong addr, int level, bool pae) 66{ 67 int level_shift = pae ? 9 : 10; 68 return (addr >> (level_shift * (level - 1) + 12)) & ((1 << level_shift) - 1); 69} 70 71static inline int pte_size(bool pae) 72{ 73 return pae ? 8 : 4; 74} 75 76 77static bool get_pt_entry(struct CPUState *cpu, struct gpt_translation *pt, 78 int level, bool pae) 79{ 80 int index; 81 uint64_t pte = 0; 82 uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK; 83 uint64_t gpa = pt->pte[level] & page_mask; 84 85 if (level == 3 && !x86_is_long_mode(cpu)) { 86 gpa = pt->pte[level]; 87 } 88 89 index = gpt_entry(pt->gva, level, pae); 90 address_space_read(&address_space_memory, gpa + index * pte_size(pae), 91 MEMTXATTRS_UNSPECIFIED, &pte, pte_size(pae)); 92 93 pt->pte[level - 1] = pte; 94 95 return true; 96} 97 98/* test page table entry */ 99static bool test_pt_entry(struct CPUState *cpu, struct gpt_translation *pt, 100 int level, bool *is_large, bool pae) 101{ 102 uint64_t pte = pt->pte[level]; 103 104 if (pt->write_access) { 105 pt->err_code |= MMU_PAGE_WT; 106 } 107 if (pt->user_access) { 108 pt->err_code |= MMU_PAGE_US; 109 } 110 if (pt->exec_access) { 111 pt->err_code |= MMU_PAGE_NX; 112 } 113 114 if (!pte_present(pte)) { 115 return false; 116 } 117 118 if (pae && !x86_is_long_mode(cpu) && 2 == level) { 119 goto exit; 120 } 121 122 if (1 == level && pte_large_page(pte)) { 123 pt->err_code |= MMU_PAGE_PT; 124 *is_large = true; 125 } 126 if (!level) { 127 pt->err_code |= MMU_PAGE_PT; 128 } 129 130 uint32_t cr0 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR0); 131 /* check protection */ 132 if (cr0 & CR0_WP) { 133 if (pt->write_access && !pte_write_access(pte)) { 134 return false; 135 } 136 } 137 138 if (pt->user_access && !pte_user_access(pte)) { 139 return false; 140 } 141 142 if (pae && pt->exec_access && !pte_exec_access(pte)) { 143 return false; 144 } 145 146exit: 147 /* TODO: check reserved bits */ 148 return true; 149} 150 151static inline uint64_t pse_pte_to_page(uint64_t pte) 152{ 153 return ((pte & 0x1fe000) << 19) | (pte & 0xffc00000); 154} 155 156static inline uint64_t large_page_gpa(struct gpt_translation *pt, bool pae) 157{ 158 VM_PANIC_ON(!pte_large_page(pt->pte[1])) 159 /* 2Mb large page */ 160 if (pae) { 161 return (pt->pte[1] & PAE_PTE_LARGE_PAGE_MASK) | (pt->gva & 0x1fffff); 162 } 163 164 /* 4Mb large page */ 165 return pse_pte_to_page(pt->pte[1]) | (pt->gva & 0x3fffff); 166} 167 168 169 170static bool walk_gpt(struct CPUState *cpu, target_ulong addr, int err_code, 171 struct gpt_translation *pt, bool pae) 172{ 173 int top_level, level; 174 bool is_large = false; 175 target_ulong cr3 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR3); 176 uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK; 177 178 memset(pt, 0, sizeof(*pt)); 179 top_level = gpt_top_level(cpu, pae); 180 181 pt->pte[top_level] = pae ? (cr3 & PAE_CR3_MASK) : (cr3 & LEGACY_CR3_MASK); 182 pt->gva = addr; 183 pt->user_access = (err_code & MMU_PAGE_US); 184 pt->write_access = (err_code & MMU_PAGE_WT); 185 pt->exec_access = (err_code & MMU_PAGE_NX); 186 187 for (level = top_level; level > 0; level--) { 188 get_pt_entry(cpu, pt, level, pae); 189 190 if (!test_pt_entry(cpu, pt, level - 1, &is_large, pae)) { 191 return false; 192 } 193 194 if (is_large) { 195 break; 196 } 197 } 198 199 if (!is_large) { 200 pt->gpa = (pt->pte[0] & page_mask) | (pt->gva & 0xfff); 201 } else { 202 pt->gpa = large_page_gpa(pt, pae); 203 } 204 205 return true; 206} 207 208 209bool mmu_gva_to_gpa(struct CPUState *cpu, target_ulong gva, uint64_t *gpa) 210{ 211 bool res; 212 struct gpt_translation pt; 213 int err_code = 0; 214 215 if (!x86_is_paging_mode(cpu)) { 216 *gpa = gva; 217 return true; 218 } 219 220 res = walk_gpt(cpu, gva, err_code, &pt, x86_is_pae_enabled(cpu)); 221 if (res) { 222 *gpa = pt.gpa; 223 return true; 224 } 225 226 return false; 227} 228 229void vmx_write_mem(struct CPUState *cpu, target_ulong gva, void *data, int bytes) 230{ 231 uint64_t gpa; 232 233 while (bytes > 0) { 234 /* copy page */ 235 int copy = MIN(bytes, 0x1000 - (gva & 0xfff)); 236 237 if (!mmu_gva_to_gpa(cpu, gva, &gpa)) { 238 VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva); 239 } else { 240 address_space_write(&address_space_memory, gpa, 241 MEMTXATTRS_UNSPECIFIED, data, copy); 242 } 243 244 bytes -= copy; 245 gva += copy; 246 data += copy; 247 } 248} 249 250void vmx_read_mem(struct CPUState *cpu, void *data, target_ulong gva, int bytes) 251{ 252 uint64_t gpa; 253 254 while (bytes > 0) { 255 /* copy page */ 256 int copy = MIN(bytes, 0x1000 - (gva & 0xfff)); 257 258 if (!mmu_gva_to_gpa(cpu, gva, &gpa)) { 259 VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva); 260 } 261 address_space_read(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED, 262 data, copy); 263 264 bytes -= copy; 265 gva += copy; 266 data += copy; 267 } 268}