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