xref: /openbmc/qemu/target/i386/hvf/x86_mmu.c (revision 19f703477314a5db09ffc3c0f6be9c45645f8302)
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 
19d8e39b70SMarkus Armbruster #include "qemu/osdep.h"
20d8e39b70SMarkus Armbruster #include "panic.h"
2169e0a03cSPaolo Bonzini #include "qemu-common.h"
22ff2de166SPaolo Bonzini #include "cpu.h"
2369e0a03cSPaolo Bonzini #include "x86.h"
2469e0a03cSPaolo Bonzini #include "x86_mmu.h"
2569e0a03cSPaolo Bonzini #include "vmcs.h"
2669e0a03cSPaolo Bonzini #include "vmx.h"
2769e0a03cSPaolo Bonzini #include "exec/address-spaces.h"
2869e0a03cSPaolo Bonzini 
2969e0a03cSPaolo Bonzini #define pte_present(pte) (pte & PT_PRESENT)
3069e0a03cSPaolo Bonzini #define pte_write_access(pte) (pte & PT_WRITE)
3169e0a03cSPaolo Bonzini #define pte_user_access(pte) (pte & PT_USER)
3269e0a03cSPaolo Bonzini #define pte_exec_access(pte) (!(pte & PT_NX))
3369e0a03cSPaolo Bonzini 
3469e0a03cSPaolo Bonzini #define pte_large_page(pte) (pte & PT_PS)
3569e0a03cSPaolo Bonzini #define pte_global_access(pte) (pte & PT_GLOBAL)
3669e0a03cSPaolo Bonzini 
3769e0a03cSPaolo Bonzini #define PAE_CR3_MASK                (~0x1fllu)
3869e0a03cSPaolo Bonzini #define LEGACY_CR3_MASK             (0xffffffff)
3969e0a03cSPaolo Bonzini 
4069e0a03cSPaolo Bonzini #define LEGACY_PTE_PAGE_MASK        (0xffffffffllu << 12)
4169e0a03cSPaolo Bonzini #define PAE_PTE_PAGE_MASK           ((-1llu << 12) & ((1llu << 52) - 1))
4269e0a03cSPaolo Bonzini #define PAE_PTE_LARGE_PAGE_MASK     ((-1llu << (21)) & ((1llu << 52) - 1))
4369e0a03cSPaolo Bonzini 
4469e0a03cSPaolo Bonzini struct gpt_translation {
45ff2de166SPaolo Bonzini     target_ulong  gva;
46ff2de166SPaolo Bonzini     uint64_t gpa;
4769e0a03cSPaolo Bonzini     int    err_code;
4869e0a03cSPaolo Bonzini     uint64_t pte[5];
4969e0a03cSPaolo Bonzini     bool write_access;
5069e0a03cSPaolo Bonzini     bool user_access;
5169e0a03cSPaolo Bonzini     bool exec_access;
5269e0a03cSPaolo Bonzini };
5369e0a03cSPaolo Bonzini 
5469e0a03cSPaolo Bonzini static int gpt_top_level(struct CPUState *cpu, bool pae)
5569e0a03cSPaolo Bonzini {
5669e0a03cSPaolo Bonzini     if (!pae) {
5769e0a03cSPaolo Bonzini         return 2;
5869e0a03cSPaolo Bonzini     }
5969e0a03cSPaolo Bonzini     if (x86_is_long_mode(cpu)) {
6069e0a03cSPaolo Bonzini         return 4;
6169e0a03cSPaolo Bonzini     }
6269e0a03cSPaolo Bonzini 
6369e0a03cSPaolo Bonzini     return 3;
6469e0a03cSPaolo Bonzini }
6569e0a03cSPaolo Bonzini 
66ff2de166SPaolo Bonzini static inline int gpt_entry(target_ulong addr, int level, bool pae)
6769e0a03cSPaolo Bonzini {
6869e0a03cSPaolo Bonzini     int level_shift = pae ? 9 : 10;
6969e0a03cSPaolo Bonzini     return (addr >> (level_shift * (level - 1) + 12)) & ((1 << level_shift) - 1);
7069e0a03cSPaolo Bonzini }
7169e0a03cSPaolo Bonzini 
7269e0a03cSPaolo Bonzini static inline int pte_size(bool pae)
7369e0a03cSPaolo Bonzini {
7469e0a03cSPaolo Bonzini     return pae ? 8 : 4;
7569e0a03cSPaolo Bonzini }
7669e0a03cSPaolo Bonzini 
7769e0a03cSPaolo Bonzini 
7869e0a03cSPaolo Bonzini static bool get_pt_entry(struct CPUState *cpu, struct gpt_translation *pt,
7969e0a03cSPaolo Bonzini                          int level, bool pae)
8069e0a03cSPaolo Bonzini {
8169e0a03cSPaolo Bonzini     int index;
8269e0a03cSPaolo Bonzini     uint64_t pte = 0;
83ff2de166SPaolo Bonzini     uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK;
84ff2de166SPaolo Bonzini     uint64_t gpa = pt->pte[level] & page_mask;
8569e0a03cSPaolo Bonzini 
8669e0a03cSPaolo Bonzini     if (level == 3 && !x86_is_long_mode(cpu)) {
8769e0a03cSPaolo Bonzini         gpa = pt->pte[level];
8869e0a03cSPaolo Bonzini     }
8969e0a03cSPaolo Bonzini 
9069e0a03cSPaolo Bonzini     index = gpt_entry(pt->gva, level, pae);
91*19f70347SPeter Maydell     address_space_read(&address_space_memory, gpa + index * pte_size(pae),
92*19f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, &pte, pte_size(pae));
9369e0a03cSPaolo Bonzini 
9469e0a03cSPaolo Bonzini     pt->pte[level - 1] = pte;
9569e0a03cSPaolo Bonzini 
9669e0a03cSPaolo Bonzini     return true;
9769e0a03cSPaolo Bonzini }
9869e0a03cSPaolo Bonzini 
9969e0a03cSPaolo Bonzini /* test page table entry */
10069e0a03cSPaolo Bonzini static bool test_pt_entry(struct CPUState *cpu, struct gpt_translation *pt,
10169e0a03cSPaolo Bonzini                           int level, bool *is_large, bool pae)
10269e0a03cSPaolo Bonzini {
10369e0a03cSPaolo Bonzini     uint64_t pte = pt->pte[level];
10469e0a03cSPaolo Bonzini 
10569e0a03cSPaolo Bonzini     if (pt->write_access) {
10669e0a03cSPaolo Bonzini         pt->err_code |= MMU_PAGE_WT;
10769e0a03cSPaolo Bonzini     }
10869e0a03cSPaolo Bonzini     if (pt->user_access) {
10969e0a03cSPaolo Bonzini         pt->err_code |= MMU_PAGE_US;
11069e0a03cSPaolo Bonzini     }
11169e0a03cSPaolo Bonzini     if (pt->exec_access) {
11269e0a03cSPaolo Bonzini         pt->err_code |= MMU_PAGE_NX;
11369e0a03cSPaolo Bonzini     }
11469e0a03cSPaolo Bonzini 
11569e0a03cSPaolo Bonzini     if (!pte_present(pte)) {
11669e0a03cSPaolo Bonzini         return false;
11769e0a03cSPaolo Bonzini     }
11869e0a03cSPaolo Bonzini 
11969e0a03cSPaolo Bonzini     if (pae && !x86_is_long_mode(cpu) && 2 == level) {
12069e0a03cSPaolo Bonzini         goto exit;
12169e0a03cSPaolo Bonzini     }
12269e0a03cSPaolo Bonzini 
12369e0a03cSPaolo Bonzini     if (1 == level && pte_large_page(pte)) {
12469e0a03cSPaolo Bonzini         pt->err_code |= MMU_PAGE_PT;
12569e0a03cSPaolo Bonzini         *is_large = true;
12669e0a03cSPaolo Bonzini     }
12769e0a03cSPaolo Bonzini     if (!level) {
12869e0a03cSPaolo Bonzini         pt->err_code |= MMU_PAGE_PT;
12969e0a03cSPaolo Bonzini     }
13069e0a03cSPaolo Bonzini 
131ff2de166SPaolo Bonzini     uint32_t cr0 = rvmcs(cpu->hvf_fd, VMCS_GUEST_CR0);
13269e0a03cSPaolo Bonzini     /* check protection */
13369e0a03cSPaolo Bonzini     if (cr0 & CR0_WP) {
13469e0a03cSPaolo Bonzini         if (pt->write_access && !pte_write_access(pte)) {
13569e0a03cSPaolo Bonzini             return false;
13669e0a03cSPaolo Bonzini         }
13769e0a03cSPaolo Bonzini     }
13869e0a03cSPaolo Bonzini 
13969e0a03cSPaolo Bonzini     if (pt->user_access && !pte_user_access(pte)) {
14069e0a03cSPaolo Bonzini         return false;
14169e0a03cSPaolo Bonzini     }
14269e0a03cSPaolo Bonzini 
14369e0a03cSPaolo Bonzini     if (pae && pt->exec_access && !pte_exec_access(pte)) {
14469e0a03cSPaolo Bonzini         return false;
14569e0a03cSPaolo Bonzini     }
14669e0a03cSPaolo Bonzini 
14769e0a03cSPaolo Bonzini exit:
14869e0a03cSPaolo Bonzini     /* TODO: check reserved bits */
14969e0a03cSPaolo Bonzini     return true;
15069e0a03cSPaolo Bonzini }
15169e0a03cSPaolo Bonzini 
15269e0a03cSPaolo Bonzini static inline uint64_t pse_pte_to_page(uint64_t pte)
15369e0a03cSPaolo Bonzini {
15469e0a03cSPaolo Bonzini     return ((pte & 0x1fe000) << 19) | (pte & 0xffc00000);
15569e0a03cSPaolo Bonzini }
15669e0a03cSPaolo Bonzini 
15769e0a03cSPaolo Bonzini static inline uint64_t large_page_gpa(struct gpt_translation *pt, bool pae)
15869e0a03cSPaolo Bonzini {
15969e0a03cSPaolo Bonzini     VM_PANIC_ON(!pte_large_page(pt->pte[1]))
16069e0a03cSPaolo Bonzini     /* 2Mb large page  */
16169e0a03cSPaolo Bonzini     if (pae) {
16269e0a03cSPaolo Bonzini         return (pt->pte[1] & PAE_PTE_LARGE_PAGE_MASK) | (pt->gva & 0x1fffff);
16369e0a03cSPaolo Bonzini     }
16469e0a03cSPaolo Bonzini 
16569e0a03cSPaolo Bonzini     /* 4Mb large page */
16669e0a03cSPaolo Bonzini     return pse_pte_to_page(pt->pte[1]) | (pt->gva & 0x3fffff);
16769e0a03cSPaolo Bonzini }
16869e0a03cSPaolo Bonzini 
16969e0a03cSPaolo Bonzini 
17069e0a03cSPaolo Bonzini 
171ff2de166SPaolo Bonzini static bool walk_gpt(struct CPUState *cpu, target_ulong addr, int err_code,
17269e0a03cSPaolo Bonzini                      struct gpt_translation *pt, bool pae)
17369e0a03cSPaolo Bonzini {
17469e0a03cSPaolo Bonzini     int top_level, level;
17569e0a03cSPaolo Bonzini     bool is_large = false;
176ff2de166SPaolo Bonzini     target_ulong cr3 = rvmcs(cpu->hvf_fd, VMCS_GUEST_CR3);
177ff2de166SPaolo Bonzini     uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK;
17869e0a03cSPaolo Bonzini 
17969e0a03cSPaolo Bonzini     memset(pt, 0, sizeof(*pt));
18069e0a03cSPaolo Bonzini     top_level = gpt_top_level(cpu, pae);
18169e0a03cSPaolo Bonzini 
18269e0a03cSPaolo Bonzini     pt->pte[top_level] = pae ? (cr3 & PAE_CR3_MASK) : (cr3 & LEGACY_CR3_MASK);
18369e0a03cSPaolo Bonzini     pt->gva = addr;
18469e0a03cSPaolo Bonzini     pt->user_access = (err_code & MMU_PAGE_US);
18569e0a03cSPaolo Bonzini     pt->write_access = (err_code & MMU_PAGE_WT);
18669e0a03cSPaolo Bonzini     pt->exec_access = (err_code & MMU_PAGE_NX);
18769e0a03cSPaolo Bonzini 
18869e0a03cSPaolo Bonzini     for (level = top_level; level > 0; level--) {
18969e0a03cSPaolo Bonzini         get_pt_entry(cpu, pt, level, pae);
19069e0a03cSPaolo Bonzini 
19169e0a03cSPaolo Bonzini         if (!test_pt_entry(cpu, pt, level - 1, &is_large, pae)) {
19269e0a03cSPaolo Bonzini             return false;
19369e0a03cSPaolo Bonzini         }
19469e0a03cSPaolo Bonzini 
19569e0a03cSPaolo Bonzini         if (is_large) {
19669e0a03cSPaolo Bonzini             break;
19769e0a03cSPaolo Bonzini         }
19869e0a03cSPaolo Bonzini     }
19969e0a03cSPaolo Bonzini 
20069e0a03cSPaolo Bonzini     if (!is_large) {
20169e0a03cSPaolo Bonzini         pt->gpa = (pt->pte[0] & page_mask) | (pt->gva & 0xfff);
20269e0a03cSPaolo Bonzini     } else {
20369e0a03cSPaolo Bonzini         pt->gpa = large_page_gpa(pt, pae);
20469e0a03cSPaolo Bonzini     }
20569e0a03cSPaolo Bonzini 
20669e0a03cSPaolo Bonzini     return true;
20769e0a03cSPaolo Bonzini }
20869e0a03cSPaolo Bonzini 
20969e0a03cSPaolo Bonzini 
210ff2de166SPaolo Bonzini bool mmu_gva_to_gpa(struct CPUState *cpu, target_ulong gva, uint64_t *gpa)
21169e0a03cSPaolo Bonzini {
21269e0a03cSPaolo Bonzini     bool res;
21369e0a03cSPaolo Bonzini     struct gpt_translation pt;
21469e0a03cSPaolo Bonzini     int err_code = 0;
21569e0a03cSPaolo Bonzini 
21669e0a03cSPaolo Bonzini     if (!x86_is_paging_mode(cpu)) {
21769e0a03cSPaolo Bonzini         *gpa = gva;
21869e0a03cSPaolo Bonzini         return true;
21969e0a03cSPaolo Bonzini     }
22069e0a03cSPaolo Bonzini 
22169e0a03cSPaolo Bonzini     res = walk_gpt(cpu, gva, err_code, &pt, x86_is_pae_enabled(cpu));
22269e0a03cSPaolo Bonzini     if (res) {
22369e0a03cSPaolo Bonzini         *gpa = pt.gpa;
22469e0a03cSPaolo Bonzini         return true;
22569e0a03cSPaolo Bonzini     }
22669e0a03cSPaolo Bonzini 
22769e0a03cSPaolo Bonzini     return false;
22869e0a03cSPaolo Bonzini }
22969e0a03cSPaolo Bonzini 
230ff2de166SPaolo Bonzini void vmx_write_mem(struct CPUState *cpu, target_ulong gva, void *data, int bytes)
23169e0a03cSPaolo Bonzini {
232ff2de166SPaolo Bonzini     uint64_t gpa;
23369e0a03cSPaolo Bonzini 
23469e0a03cSPaolo Bonzini     while (bytes > 0) {
23569e0a03cSPaolo Bonzini         /* copy page */
23669e0a03cSPaolo Bonzini         int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
23769e0a03cSPaolo Bonzini 
23869e0a03cSPaolo Bonzini         if (!mmu_gva_to_gpa(cpu, gva, &gpa)) {
23974682782SPaolo Bonzini             VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva);
24069e0a03cSPaolo Bonzini         } else {
241*19f70347SPeter Maydell             address_space_write(&address_space_memory, gpa,
242*19f70347SPeter Maydell                                 MEMTXATTRS_UNSPECIFIED, data, copy);
24369e0a03cSPaolo Bonzini         }
24469e0a03cSPaolo Bonzini 
24569e0a03cSPaolo Bonzini         bytes -= copy;
24669e0a03cSPaolo Bonzini         gva += copy;
24769e0a03cSPaolo Bonzini         data += copy;
24869e0a03cSPaolo Bonzini     }
24969e0a03cSPaolo Bonzini }
25069e0a03cSPaolo Bonzini 
251ff2de166SPaolo Bonzini void vmx_read_mem(struct CPUState *cpu, void *data, target_ulong gva, int bytes)
25269e0a03cSPaolo Bonzini {
253ff2de166SPaolo Bonzini     uint64_t gpa;
25469e0a03cSPaolo Bonzini 
25569e0a03cSPaolo Bonzini     while (bytes > 0) {
25669e0a03cSPaolo Bonzini         /* copy page */
25769e0a03cSPaolo Bonzini         int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
25869e0a03cSPaolo Bonzini 
25969e0a03cSPaolo Bonzini         if (!mmu_gva_to_gpa(cpu, gva, &gpa)) {
26074682782SPaolo Bonzini             VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva);
26169e0a03cSPaolo Bonzini         }
262*19f70347SPeter Maydell         address_space_read(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED,
263*19f70347SPeter Maydell                            data, copy);
26469e0a03cSPaolo Bonzini 
26569e0a03cSPaolo Bonzini         bytes -= copy;
26669e0a03cSPaolo Bonzini         gva += copy;
26769e0a03cSPaolo Bonzini         data += copy;
26869e0a03cSPaolo Bonzini     }
26969e0a03cSPaolo Bonzini }
270