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 "cpu.h" 22 #include "x86.h" 23 #include "x86_mmu.h" 24 #include "vmcs.h" 25 #include "vmx.h" 26 27 #define pte_present(pte) (pte & PT_PRESENT) 28 #define pte_write_access(pte) (pte & PT_WRITE) 29 #define pte_user_access(pte) (pte & PT_USER) 30 #define pte_exec_access(pte) (!(pte & PT_NX)) 31 32 #define pte_large_page(pte) (pte & PT_PS) 33 #define pte_global_access(pte) (pte & PT_GLOBAL) 34 35 #define PAE_CR3_MASK (~0x1fllu) 36 #define LEGACY_CR3_MASK (0xffffffff) 37 38 #define LEGACY_PTE_PAGE_MASK (0xffffffffllu << 12) 39 #define PAE_PTE_PAGE_MASK ((-1llu << 12) & ((1llu << 52) - 1)) 40 #define PAE_PTE_LARGE_PAGE_MASK ((-1llu << (21)) & ((1llu << 52) - 1)) 41 42 struct gpt_translation { 43 target_ulong gva; 44 uint64_t gpa; 45 int err_code; 46 uint64_t pte[5]; 47 bool write_access; 48 bool user_access; 49 bool exec_access; 50 }; 51 52 static int gpt_top_level(CPUState *cpu, bool pae) 53 { 54 if (!pae) { 55 return 2; 56 } 57 if (x86_is_long_mode(cpu)) { 58 return 4; 59 } 60 61 return 3; 62 } 63 64 static inline int gpt_entry(target_ulong addr, int level, bool pae) 65 { 66 int level_shift = pae ? 9 : 10; 67 return (addr >> (level_shift * (level - 1) + 12)) & ((1 << level_shift) - 1); 68 } 69 70 static inline int pte_size(bool pae) 71 { 72 return pae ? 8 : 4; 73 } 74 75 76 static bool get_pt_entry(CPUState *cpu, struct gpt_translation *pt, 77 int level, bool pae) 78 { 79 int index; 80 uint64_t pte = 0; 81 uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK; 82 uint64_t gpa = pt->pte[level] & page_mask; 83 84 if (level == 3 && !x86_is_long_mode(cpu)) { 85 gpa = pt->pte[level]; 86 } 87 88 index = gpt_entry(pt->gva, level, pae); 89 address_space_read(&address_space_memory, gpa + index * pte_size(pae), 90 MEMTXATTRS_UNSPECIFIED, &pte, pte_size(pae)); 91 92 pt->pte[level - 1] = pte; 93 94 return true; 95 } 96 97 /* test page table entry */ 98 static bool test_pt_entry(CPUState *cpu, struct gpt_translation *pt, 99 int level, bool *is_large, bool pae) 100 { 101 uint64_t pte = pt->pte[level]; 102 103 if (pt->write_access) { 104 pt->err_code |= MMU_PAGE_WT; 105 } 106 if (pt->user_access) { 107 pt->err_code |= MMU_PAGE_US; 108 } 109 if (pt->exec_access) { 110 pt->err_code |= MMU_PAGE_NX; 111 } 112 113 if (!pte_present(pte)) { 114 return false; 115 } 116 117 if (pae && !x86_is_long_mode(cpu) && 2 == level) { 118 goto exit; 119 } 120 121 if (1 == level && pte_large_page(pte)) { 122 pt->err_code |= MMU_PAGE_PT; 123 *is_large = true; 124 } 125 if (!level) { 126 pt->err_code |= MMU_PAGE_PT; 127 } 128 129 uint32_t cr0 = rvmcs(cpu->accel->fd, VMCS_GUEST_CR0); 130 /* check protection */ 131 if (cr0 & CR0_WP_MASK) { 132 if (pt->write_access && !pte_write_access(pte)) { 133 return false; 134 } 135 } 136 137 if (pt->user_access && !pte_user_access(pte)) { 138 return false; 139 } 140 141 if (pae && pt->exec_access && !pte_exec_access(pte)) { 142 return false; 143 } 144 145 exit: 146 /* TODO: check reserved bits */ 147 return true; 148 } 149 150 static inline uint64_t pse_pte_to_page(uint64_t pte) 151 { 152 return ((pte & 0x1fe000) << 19) | (pte & 0xffc00000); 153 } 154 155 static inline uint64_t large_page_gpa(struct gpt_translation *pt, bool pae) 156 { 157 VM_PANIC_ON(!pte_large_page(pt->pte[1])) 158 /* 2Mb large page */ 159 if (pae) { 160 return (pt->pte[1] & PAE_PTE_LARGE_PAGE_MASK) | (pt->gva & 0x1fffff); 161 } 162 163 /* 4Mb large page */ 164 return pse_pte_to_page(pt->pte[1]) | (pt->gva & 0x3fffff); 165 } 166 167 168 169 static bool walk_gpt(CPUState *cpu, target_ulong addr, int err_code, 170 struct gpt_translation *pt, bool pae) 171 { 172 int top_level, level; 173 bool is_large = false; 174 target_ulong cr3 = rvmcs(cpu->accel->fd, VMCS_GUEST_CR3); 175 uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK; 176 177 memset(pt, 0, sizeof(*pt)); 178 top_level = gpt_top_level(cpu, pae); 179 180 pt->pte[top_level] = pae ? (cr3 & PAE_CR3_MASK) : (cr3 & LEGACY_CR3_MASK); 181 pt->gva = addr; 182 pt->user_access = (err_code & MMU_PAGE_US); 183 pt->write_access = (err_code & MMU_PAGE_WT); 184 pt->exec_access = (err_code & MMU_PAGE_NX); 185 186 for (level = top_level; level > 0; level--) { 187 get_pt_entry(cpu, pt, level, pae); 188 189 if (!test_pt_entry(cpu, pt, level - 1, &is_large, pae)) { 190 return false; 191 } 192 193 if (is_large) { 194 break; 195 } 196 } 197 198 if (!is_large) { 199 pt->gpa = (pt->pte[0] & page_mask) | (pt->gva & 0xfff); 200 } else { 201 pt->gpa = large_page_gpa(pt, pae); 202 } 203 204 return true; 205 } 206 207 208 bool mmu_gva_to_gpa(CPUState *cpu, target_ulong gva, uint64_t *gpa) 209 { 210 bool res; 211 struct gpt_translation pt; 212 int err_code = 0; 213 214 if (!x86_is_paging_mode(cpu)) { 215 *gpa = gva; 216 return true; 217 } 218 219 res = walk_gpt(cpu, gva, err_code, &pt, x86_is_pae_enabled(cpu)); 220 if (res) { 221 *gpa = pt.gpa; 222 return true; 223 } 224 225 return false; 226 } 227 228 void vmx_write_mem(CPUState *cpu, target_ulong gva, void *data, int bytes) 229 { 230 uint64_t gpa; 231 232 while (bytes > 0) { 233 /* copy page */ 234 int copy = MIN(bytes, 0x1000 - (gva & 0xfff)); 235 236 if (!mmu_gva_to_gpa(cpu, gva, &gpa)) { 237 VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva); 238 } else { 239 address_space_write(&address_space_memory, gpa, 240 MEMTXATTRS_UNSPECIFIED, data, copy); 241 } 242 243 bytes -= copy; 244 gva += copy; 245 data += copy; 246 } 247 } 248 249 void vmx_read_mem(CPUState *cpu, void *data, target_ulong gva, int bytes) 250 { 251 uint64_t gpa; 252 253 while (bytes > 0) { 254 /* copy page */ 255 int copy = MIN(bytes, 0x1000 - (gva & 0xfff)); 256 257 if (!mmu_gva_to_gpa(cpu, gva, &gpa)) { 258 VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva); 259 } 260 address_space_read(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED, 261 data, copy); 262 263 bytes -= copy; 264 gva += copy; 265 data += copy; 266 } 267 } 268