169e0a03cSPaolo Bonzini /* 269e0a03cSPaolo Bonzini * Copyright (C) 2016 Veertu Inc, 369e0a03cSPaolo Bonzini * Copyright (C) 2017 Google Inc, 469e0a03cSPaolo Bonzini * 569e0a03cSPaolo Bonzini * This program is free software; you can redistribute it and/or 669e0a03cSPaolo Bonzini * modify it under the terms of the GNU Lesser General Public 769e0a03cSPaolo Bonzini * License as published by the Free Software Foundation; either 869e0a03cSPaolo Bonzini * version 2 of the License, or (at your option) any later version. 969e0a03cSPaolo Bonzini * 1069e0a03cSPaolo Bonzini * This program is distributed in the hope that it will be useful, 1169e0a03cSPaolo Bonzini * but WITHOUT ANY WARRANTY; without even the implied warranty of 1269e0a03cSPaolo Bonzini * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1369e0a03cSPaolo Bonzini * Lesser General Public License for more details. 1469e0a03cSPaolo Bonzini * 1569e0a03cSPaolo Bonzini * You should have received a copy of the GNU Lesser General Public 1669e0a03cSPaolo Bonzini * License along with this program; if not, see <http://www.gnu.org/licenses/>. 1769e0a03cSPaolo Bonzini */ 1869e0a03cSPaolo Bonzini 19*d8e39b70SMarkus Armbruster #include "qemu/osdep.h" 20*d8e39b70SMarkus Armbruster #include <memory.h> 21*d8e39b70SMarkus Armbruster #include "panic.h" 2269e0a03cSPaolo Bonzini #include "qemu-common.h" 23ff2de166SPaolo Bonzini #include "cpu.h" 2469e0a03cSPaolo Bonzini #include "x86.h" 2569e0a03cSPaolo Bonzini #include "x86_mmu.h" 2669e0a03cSPaolo Bonzini #include "vmcs.h" 2769e0a03cSPaolo Bonzini #include "vmx.h" 2869e0a03cSPaolo Bonzini #include "exec/address-spaces.h" 2969e0a03cSPaolo Bonzini 3069e0a03cSPaolo Bonzini #define pte_present(pte) (pte & PT_PRESENT) 3169e0a03cSPaolo Bonzini #define pte_write_access(pte) (pte & PT_WRITE) 3269e0a03cSPaolo Bonzini #define pte_user_access(pte) (pte & PT_USER) 3369e0a03cSPaolo Bonzini #define pte_exec_access(pte) (!(pte & PT_NX)) 3469e0a03cSPaolo Bonzini 3569e0a03cSPaolo Bonzini #define pte_large_page(pte) (pte & PT_PS) 3669e0a03cSPaolo Bonzini #define pte_global_access(pte) (pte & PT_GLOBAL) 3769e0a03cSPaolo Bonzini 3869e0a03cSPaolo Bonzini #define PAE_CR3_MASK (~0x1fllu) 3969e0a03cSPaolo Bonzini #define LEGACY_CR3_MASK (0xffffffff) 4069e0a03cSPaolo Bonzini 4169e0a03cSPaolo Bonzini #define LEGACY_PTE_PAGE_MASK (0xffffffffllu << 12) 4269e0a03cSPaolo Bonzini #define PAE_PTE_PAGE_MASK ((-1llu << 12) & ((1llu << 52) - 1)) 4369e0a03cSPaolo Bonzini #define PAE_PTE_LARGE_PAGE_MASK ((-1llu << (21)) & ((1llu << 52) - 1)) 4469e0a03cSPaolo Bonzini 4569e0a03cSPaolo Bonzini struct gpt_translation { 46ff2de166SPaolo Bonzini target_ulong gva; 47ff2de166SPaolo Bonzini uint64_t gpa; 4869e0a03cSPaolo Bonzini int err_code; 4969e0a03cSPaolo Bonzini uint64_t pte[5]; 5069e0a03cSPaolo Bonzini bool write_access; 5169e0a03cSPaolo Bonzini bool user_access; 5269e0a03cSPaolo Bonzini bool exec_access; 5369e0a03cSPaolo Bonzini }; 5469e0a03cSPaolo Bonzini 5569e0a03cSPaolo Bonzini static int gpt_top_level(struct CPUState *cpu, bool pae) 5669e0a03cSPaolo Bonzini { 5769e0a03cSPaolo Bonzini if (!pae) { 5869e0a03cSPaolo Bonzini return 2; 5969e0a03cSPaolo Bonzini } 6069e0a03cSPaolo Bonzini if (x86_is_long_mode(cpu)) { 6169e0a03cSPaolo Bonzini return 4; 6269e0a03cSPaolo Bonzini } 6369e0a03cSPaolo Bonzini 6469e0a03cSPaolo Bonzini return 3; 6569e0a03cSPaolo Bonzini } 6669e0a03cSPaolo Bonzini 67ff2de166SPaolo Bonzini static inline int gpt_entry(target_ulong addr, int level, bool pae) 6869e0a03cSPaolo Bonzini { 6969e0a03cSPaolo Bonzini int level_shift = pae ? 9 : 10; 7069e0a03cSPaolo Bonzini return (addr >> (level_shift * (level - 1) + 12)) & ((1 << level_shift) - 1); 7169e0a03cSPaolo Bonzini } 7269e0a03cSPaolo Bonzini 7369e0a03cSPaolo Bonzini static inline int pte_size(bool pae) 7469e0a03cSPaolo Bonzini { 7569e0a03cSPaolo Bonzini return pae ? 8 : 4; 7669e0a03cSPaolo Bonzini } 7769e0a03cSPaolo Bonzini 7869e0a03cSPaolo Bonzini 7969e0a03cSPaolo Bonzini static bool get_pt_entry(struct CPUState *cpu, struct gpt_translation *pt, 8069e0a03cSPaolo Bonzini int level, bool pae) 8169e0a03cSPaolo Bonzini { 8269e0a03cSPaolo Bonzini int index; 8369e0a03cSPaolo Bonzini uint64_t pte = 0; 84ff2de166SPaolo Bonzini uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK; 85ff2de166SPaolo Bonzini uint64_t gpa = pt->pte[level] & page_mask; 8669e0a03cSPaolo Bonzini 8769e0a03cSPaolo Bonzini if (level == 3 && !x86_is_long_mode(cpu)) { 8869e0a03cSPaolo Bonzini gpa = pt->pte[level]; 8969e0a03cSPaolo Bonzini } 9069e0a03cSPaolo Bonzini 9169e0a03cSPaolo Bonzini index = gpt_entry(pt->gva, level, pae); 9269e0a03cSPaolo Bonzini address_space_rw(&address_space_memory, gpa + index * pte_size(pae), 9369e0a03cSPaolo Bonzini MEMTXATTRS_UNSPECIFIED, (uint8_t *)&pte, pte_size(pae), 0); 9469e0a03cSPaolo Bonzini 9569e0a03cSPaolo Bonzini pt->pte[level - 1] = pte; 9669e0a03cSPaolo Bonzini 9769e0a03cSPaolo Bonzini return true; 9869e0a03cSPaolo Bonzini } 9969e0a03cSPaolo Bonzini 10069e0a03cSPaolo Bonzini /* test page table entry */ 10169e0a03cSPaolo Bonzini static bool test_pt_entry(struct CPUState *cpu, struct gpt_translation *pt, 10269e0a03cSPaolo Bonzini int level, bool *is_large, bool pae) 10369e0a03cSPaolo Bonzini { 10469e0a03cSPaolo Bonzini uint64_t pte = pt->pte[level]; 10569e0a03cSPaolo Bonzini 10669e0a03cSPaolo Bonzini if (pt->write_access) { 10769e0a03cSPaolo Bonzini pt->err_code |= MMU_PAGE_WT; 10869e0a03cSPaolo Bonzini } 10969e0a03cSPaolo Bonzini if (pt->user_access) { 11069e0a03cSPaolo Bonzini pt->err_code |= MMU_PAGE_US; 11169e0a03cSPaolo Bonzini } 11269e0a03cSPaolo Bonzini if (pt->exec_access) { 11369e0a03cSPaolo Bonzini pt->err_code |= MMU_PAGE_NX; 11469e0a03cSPaolo Bonzini } 11569e0a03cSPaolo Bonzini 11669e0a03cSPaolo Bonzini if (!pte_present(pte)) { 11769e0a03cSPaolo Bonzini return false; 11869e0a03cSPaolo Bonzini } 11969e0a03cSPaolo Bonzini 12069e0a03cSPaolo Bonzini if (pae && !x86_is_long_mode(cpu) && 2 == level) { 12169e0a03cSPaolo Bonzini goto exit; 12269e0a03cSPaolo Bonzini } 12369e0a03cSPaolo Bonzini 12469e0a03cSPaolo Bonzini if (1 == level && pte_large_page(pte)) { 12569e0a03cSPaolo Bonzini pt->err_code |= MMU_PAGE_PT; 12669e0a03cSPaolo Bonzini *is_large = true; 12769e0a03cSPaolo Bonzini } 12869e0a03cSPaolo Bonzini if (!level) { 12969e0a03cSPaolo Bonzini pt->err_code |= MMU_PAGE_PT; 13069e0a03cSPaolo Bonzini } 13169e0a03cSPaolo Bonzini 132ff2de166SPaolo Bonzini uint32_t cr0 = rvmcs(cpu->hvf_fd, VMCS_GUEST_CR0); 13369e0a03cSPaolo Bonzini /* check protection */ 13469e0a03cSPaolo Bonzini if (cr0 & CR0_WP) { 13569e0a03cSPaolo Bonzini if (pt->write_access && !pte_write_access(pte)) { 13669e0a03cSPaolo Bonzini return false; 13769e0a03cSPaolo Bonzini } 13869e0a03cSPaolo Bonzini } 13969e0a03cSPaolo Bonzini 14069e0a03cSPaolo Bonzini if (pt->user_access && !pte_user_access(pte)) { 14169e0a03cSPaolo Bonzini return false; 14269e0a03cSPaolo Bonzini } 14369e0a03cSPaolo Bonzini 14469e0a03cSPaolo Bonzini if (pae && pt->exec_access && !pte_exec_access(pte)) { 14569e0a03cSPaolo Bonzini return false; 14669e0a03cSPaolo Bonzini } 14769e0a03cSPaolo Bonzini 14869e0a03cSPaolo Bonzini exit: 14969e0a03cSPaolo Bonzini /* TODO: check reserved bits */ 15069e0a03cSPaolo Bonzini return true; 15169e0a03cSPaolo Bonzini } 15269e0a03cSPaolo Bonzini 15369e0a03cSPaolo Bonzini static inline uint64_t pse_pte_to_page(uint64_t pte) 15469e0a03cSPaolo Bonzini { 15569e0a03cSPaolo Bonzini return ((pte & 0x1fe000) << 19) | (pte & 0xffc00000); 15669e0a03cSPaolo Bonzini } 15769e0a03cSPaolo Bonzini 15869e0a03cSPaolo Bonzini static inline uint64_t large_page_gpa(struct gpt_translation *pt, bool pae) 15969e0a03cSPaolo Bonzini { 16069e0a03cSPaolo Bonzini VM_PANIC_ON(!pte_large_page(pt->pte[1])) 16169e0a03cSPaolo Bonzini /* 2Mb large page */ 16269e0a03cSPaolo Bonzini if (pae) { 16369e0a03cSPaolo Bonzini return (pt->pte[1] & PAE_PTE_LARGE_PAGE_MASK) | (pt->gva & 0x1fffff); 16469e0a03cSPaolo Bonzini } 16569e0a03cSPaolo Bonzini 16669e0a03cSPaolo Bonzini /* 4Mb large page */ 16769e0a03cSPaolo Bonzini return pse_pte_to_page(pt->pte[1]) | (pt->gva & 0x3fffff); 16869e0a03cSPaolo Bonzini } 16969e0a03cSPaolo Bonzini 17069e0a03cSPaolo Bonzini 17169e0a03cSPaolo Bonzini 172ff2de166SPaolo Bonzini static bool walk_gpt(struct CPUState *cpu, target_ulong addr, int err_code, 17369e0a03cSPaolo Bonzini struct gpt_translation *pt, bool pae) 17469e0a03cSPaolo Bonzini { 17569e0a03cSPaolo Bonzini int top_level, level; 17669e0a03cSPaolo Bonzini bool is_large = false; 177ff2de166SPaolo Bonzini target_ulong cr3 = rvmcs(cpu->hvf_fd, VMCS_GUEST_CR3); 178ff2de166SPaolo Bonzini uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK; 17969e0a03cSPaolo Bonzini 18069e0a03cSPaolo Bonzini memset(pt, 0, sizeof(*pt)); 18169e0a03cSPaolo Bonzini top_level = gpt_top_level(cpu, pae); 18269e0a03cSPaolo Bonzini 18369e0a03cSPaolo Bonzini pt->pte[top_level] = pae ? (cr3 & PAE_CR3_MASK) : (cr3 & LEGACY_CR3_MASK); 18469e0a03cSPaolo Bonzini pt->gva = addr; 18569e0a03cSPaolo Bonzini pt->user_access = (err_code & MMU_PAGE_US); 18669e0a03cSPaolo Bonzini pt->write_access = (err_code & MMU_PAGE_WT); 18769e0a03cSPaolo Bonzini pt->exec_access = (err_code & MMU_PAGE_NX); 18869e0a03cSPaolo Bonzini 18969e0a03cSPaolo Bonzini for (level = top_level; level > 0; level--) { 19069e0a03cSPaolo Bonzini get_pt_entry(cpu, pt, level, pae); 19169e0a03cSPaolo Bonzini 19269e0a03cSPaolo Bonzini if (!test_pt_entry(cpu, pt, level - 1, &is_large, pae)) { 19369e0a03cSPaolo Bonzini return false; 19469e0a03cSPaolo Bonzini } 19569e0a03cSPaolo Bonzini 19669e0a03cSPaolo Bonzini if (is_large) { 19769e0a03cSPaolo Bonzini break; 19869e0a03cSPaolo Bonzini } 19969e0a03cSPaolo Bonzini } 20069e0a03cSPaolo Bonzini 20169e0a03cSPaolo Bonzini if (!is_large) { 20269e0a03cSPaolo Bonzini pt->gpa = (pt->pte[0] & page_mask) | (pt->gva & 0xfff); 20369e0a03cSPaolo Bonzini } else { 20469e0a03cSPaolo Bonzini pt->gpa = large_page_gpa(pt, pae); 20569e0a03cSPaolo Bonzini } 20669e0a03cSPaolo Bonzini 20769e0a03cSPaolo Bonzini return true; 20869e0a03cSPaolo Bonzini } 20969e0a03cSPaolo Bonzini 21069e0a03cSPaolo Bonzini 211ff2de166SPaolo Bonzini bool mmu_gva_to_gpa(struct CPUState *cpu, target_ulong gva, uint64_t *gpa) 21269e0a03cSPaolo Bonzini { 21369e0a03cSPaolo Bonzini bool res; 21469e0a03cSPaolo Bonzini struct gpt_translation pt; 21569e0a03cSPaolo Bonzini int err_code = 0; 21669e0a03cSPaolo Bonzini 21769e0a03cSPaolo Bonzini if (!x86_is_paging_mode(cpu)) { 21869e0a03cSPaolo Bonzini *gpa = gva; 21969e0a03cSPaolo Bonzini return true; 22069e0a03cSPaolo Bonzini } 22169e0a03cSPaolo Bonzini 22269e0a03cSPaolo Bonzini res = walk_gpt(cpu, gva, err_code, &pt, x86_is_pae_enabled(cpu)); 22369e0a03cSPaolo Bonzini if (res) { 22469e0a03cSPaolo Bonzini *gpa = pt.gpa; 22569e0a03cSPaolo Bonzini return true; 22669e0a03cSPaolo Bonzini } 22769e0a03cSPaolo Bonzini 22869e0a03cSPaolo Bonzini return false; 22969e0a03cSPaolo Bonzini } 23069e0a03cSPaolo Bonzini 231ff2de166SPaolo Bonzini void vmx_write_mem(struct CPUState *cpu, target_ulong gva, void *data, int bytes) 23269e0a03cSPaolo Bonzini { 233ff2de166SPaolo Bonzini uint64_t gpa; 23469e0a03cSPaolo Bonzini 23569e0a03cSPaolo Bonzini while (bytes > 0) { 23669e0a03cSPaolo Bonzini /* copy page */ 23769e0a03cSPaolo Bonzini int copy = MIN(bytes, 0x1000 - (gva & 0xfff)); 23869e0a03cSPaolo Bonzini 23969e0a03cSPaolo Bonzini if (!mmu_gva_to_gpa(cpu, gva, &gpa)) { 24074682782SPaolo Bonzini VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva); 24169e0a03cSPaolo Bonzini } else { 24269e0a03cSPaolo Bonzini address_space_rw(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED, 24369e0a03cSPaolo Bonzini data, copy, 1); 24469e0a03cSPaolo Bonzini } 24569e0a03cSPaolo Bonzini 24669e0a03cSPaolo Bonzini bytes -= copy; 24769e0a03cSPaolo Bonzini gva += copy; 24869e0a03cSPaolo Bonzini data += copy; 24969e0a03cSPaolo Bonzini } 25069e0a03cSPaolo Bonzini } 25169e0a03cSPaolo Bonzini 252ff2de166SPaolo Bonzini void vmx_read_mem(struct CPUState *cpu, void *data, target_ulong gva, int bytes) 25369e0a03cSPaolo Bonzini { 254ff2de166SPaolo Bonzini uint64_t gpa; 25569e0a03cSPaolo Bonzini 25669e0a03cSPaolo Bonzini while (bytes > 0) { 25769e0a03cSPaolo Bonzini /* copy page */ 25869e0a03cSPaolo Bonzini int copy = MIN(bytes, 0x1000 - (gva & 0xfff)); 25969e0a03cSPaolo Bonzini 26069e0a03cSPaolo Bonzini if (!mmu_gva_to_gpa(cpu, gva, &gpa)) { 26174682782SPaolo Bonzini VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva); 26269e0a03cSPaolo Bonzini } 26369e0a03cSPaolo Bonzini address_space_rw(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED, 26469e0a03cSPaolo Bonzini data, copy, 0); 26569e0a03cSPaolo Bonzini 26669e0a03cSPaolo Bonzini bytes -= copy; 26769e0a03cSPaolo Bonzini gva += copy; 26869e0a03cSPaolo Bonzini data += copy; 26969e0a03cSPaolo Bonzini } 27069e0a03cSPaolo Bonzini } 271