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