xref: /openbmc/qemu/target/i386/hvf/x86_mmu.c (revision 72b88908d12ee9347d13539c7dd9a252625158d1)
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
88af82b8eSChetan Pant  * version 2.1 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"
21ff2de166SPaolo Bonzini #include "cpu.h"
2269e0a03cSPaolo Bonzini #include "x86.h"
2369e0a03cSPaolo Bonzini #include "x86_mmu.h"
2469e0a03cSPaolo Bonzini #include "vmcs.h"
2569e0a03cSPaolo Bonzini #include "vmx.h"
2669e0a03cSPaolo Bonzini 
2769e0a03cSPaolo Bonzini #define pte_present(pte) (pte & PT_PRESENT)
2869e0a03cSPaolo Bonzini #define pte_write_access(pte) (pte & PT_WRITE)
2969e0a03cSPaolo Bonzini #define pte_user_access(pte) (pte & PT_USER)
3069e0a03cSPaolo Bonzini #define pte_exec_access(pte) (!(pte & PT_NX))
3169e0a03cSPaolo Bonzini 
3269e0a03cSPaolo Bonzini #define pte_large_page(pte) (pte & PT_PS)
3369e0a03cSPaolo Bonzini #define pte_global_access(pte) (pte & PT_GLOBAL)
3469e0a03cSPaolo Bonzini 
3569e0a03cSPaolo Bonzini #define PAE_CR3_MASK                (~0x1fllu)
3669e0a03cSPaolo Bonzini #define LEGACY_CR3_MASK             (0xffffffff)
3769e0a03cSPaolo Bonzini 
3869e0a03cSPaolo Bonzini #define LEGACY_PTE_PAGE_MASK        (0xffffffffllu << 12)
3969e0a03cSPaolo Bonzini #define PAE_PTE_PAGE_MASK           ((-1llu << 12) & ((1llu << 52) - 1))
4069e0a03cSPaolo Bonzini #define PAE_PTE_LARGE_PAGE_MASK     ((-1llu << (21)) & ((1llu << 52) - 1))
41*654076bcSAlexander Graf #define PAE_PTE_SUPER_PAGE_MASK     ((-1llu << (30)) & ((1llu << 52) - 1))
4269e0a03cSPaolo Bonzini 
4369e0a03cSPaolo Bonzini struct gpt_translation {
44ff2de166SPaolo Bonzini     target_ulong  gva;
45ff2de166SPaolo Bonzini     uint64_t gpa;
4669e0a03cSPaolo Bonzini     int    err_code;
4769e0a03cSPaolo Bonzini     uint64_t pte[5];
4869e0a03cSPaolo Bonzini     bool write_access;
4969e0a03cSPaolo Bonzini     bool user_access;
5069e0a03cSPaolo Bonzini     bool exec_access;
5169e0a03cSPaolo Bonzini };
5269e0a03cSPaolo Bonzini 
gpt_top_level(CPUState * cpu,bool pae)53f8436a16SPhilippe Mathieu-Daudé static int gpt_top_level(CPUState *cpu, bool pae)
5469e0a03cSPaolo Bonzini {
5569e0a03cSPaolo Bonzini     if (!pae) {
5669e0a03cSPaolo Bonzini         return 2;
5769e0a03cSPaolo Bonzini     }
5869e0a03cSPaolo Bonzini     if (x86_is_long_mode(cpu)) {
5969e0a03cSPaolo Bonzini         return 4;
6069e0a03cSPaolo Bonzini     }
6169e0a03cSPaolo Bonzini 
6269e0a03cSPaolo Bonzini     return 3;
6369e0a03cSPaolo Bonzini }
6469e0a03cSPaolo Bonzini 
gpt_entry(target_ulong addr,int level,bool pae)65ff2de166SPaolo Bonzini static inline int gpt_entry(target_ulong addr, int level, bool pae)
6669e0a03cSPaolo Bonzini {
6769e0a03cSPaolo Bonzini     int level_shift = pae ? 9 : 10;
6869e0a03cSPaolo Bonzini     return (addr >> (level_shift * (level - 1) + 12)) & ((1 << level_shift) - 1);
6969e0a03cSPaolo Bonzini }
7069e0a03cSPaolo Bonzini 
pte_size(bool pae)7169e0a03cSPaolo Bonzini static inline int pte_size(bool pae)
7269e0a03cSPaolo Bonzini {
7369e0a03cSPaolo Bonzini     return pae ? 8 : 4;
7469e0a03cSPaolo Bonzini }
7569e0a03cSPaolo Bonzini 
7669e0a03cSPaolo Bonzini 
get_pt_entry(CPUState * cpu,struct gpt_translation * pt,int level,bool pae)77f8436a16SPhilippe Mathieu-Daudé static bool get_pt_entry(CPUState *cpu, struct gpt_translation *pt,
7869e0a03cSPaolo Bonzini                          int level, bool pae)
7969e0a03cSPaolo Bonzini {
8069e0a03cSPaolo Bonzini     int index;
8169e0a03cSPaolo Bonzini     uint64_t pte = 0;
82ff2de166SPaolo Bonzini     uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK;
83ff2de166SPaolo Bonzini     uint64_t gpa = pt->pte[level] & page_mask;
8469e0a03cSPaolo Bonzini 
8569e0a03cSPaolo Bonzini     if (level == 3 && !x86_is_long_mode(cpu)) {
8669e0a03cSPaolo Bonzini         gpa = pt->pte[level];
8769e0a03cSPaolo Bonzini     }
8869e0a03cSPaolo Bonzini 
8969e0a03cSPaolo Bonzini     index = gpt_entry(pt->gva, level, pae);
9019f70347SPeter Maydell     address_space_read(&address_space_memory, gpa + index * pte_size(pae),
9119f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, &pte, pte_size(pae));
9269e0a03cSPaolo Bonzini 
9369e0a03cSPaolo Bonzini     pt->pte[level - 1] = pte;
9469e0a03cSPaolo Bonzini 
9569e0a03cSPaolo Bonzini     return true;
9669e0a03cSPaolo Bonzini }
9769e0a03cSPaolo Bonzini 
9869e0a03cSPaolo Bonzini /* test page table entry */
test_pt_entry(CPUState * cpu,struct gpt_translation * pt,int level,int * largeness,bool pae)99f8436a16SPhilippe Mathieu-Daudé static bool test_pt_entry(CPUState *cpu, struct gpt_translation *pt,
100*654076bcSAlexander Graf                           int level, int *largeness, bool pae)
10169e0a03cSPaolo Bonzini {
10269e0a03cSPaolo Bonzini     uint64_t pte = pt->pte[level];
10369e0a03cSPaolo Bonzini 
10469e0a03cSPaolo Bonzini     if (pt->write_access) {
10569e0a03cSPaolo Bonzini         pt->err_code |= MMU_PAGE_WT;
10669e0a03cSPaolo Bonzini     }
10769e0a03cSPaolo Bonzini     if (pt->user_access) {
10869e0a03cSPaolo Bonzini         pt->err_code |= MMU_PAGE_US;
10969e0a03cSPaolo Bonzini     }
11069e0a03cSPaolo Bonzini     if (pt->exec_access) {
11169e0a03cSPaolo Bonzini         pt->err_code |= MMU_PAGE_NX;
11269e0a03cSPaolo Bonzini     }
11369e0a03cSPaolo Bonzini 
11469e0a03cSPaolo Bonzini     if (!pte_present(pte)) {
11569e0a03cSPaolo Bonzini         return false;
11669e0a03cSPaolo Bonzini     }
11769e0a03cSPaolo Bonzini 
11869e0a03cSPaolo Bonzini     if (pae && !x86_is_long_mode(cpu) && 2 == level) {
11969e0a03cSPaolo Bonzini         goto exit;
12069e0a03cSPaolo Bonzini     }
12169e0a03cSPaolo Bonzini 
122*654076bcSAlexander Graf     if (level && pte_large_page(pte)) {
12369e0a03cSPaolo Bonzini         pt->err_code |= MMU_PAGE_PT;
124*654076bcSAlexander Graf         *largeness = level;
12569e0a03cSPaolo Bonzini     }
12669e0a03cSPaolo Bonzini     if (!level) {
12769e0a03cSPaolo Bonzini         pt->err_code |= MMU_PAGE_PT;
12869e0a03cSPaolo Bonzini     }
12969e0a03cSPaolo Bonzini 
1303b295bcbSPhilippe Mathieu-Daudé     uint32_t cr0 = rvmcs(cpu->accel->fd, VMCS_GUEST_CR0);
13169e0a03cSPaolo Bonzini     /* check protection */
132704afe34SCameron Esfahani     if (cr0 & CR0_WP_MASK) {
13369e0a03cSPaolo Bonzini         if (pt->write_access && !pte_write_access(pte)) {
13469e0a03cSPaolo Bonzini             return false;
13569e0a03cSPaolo Bonzini         }
13669e0a03cSPaolo Bonzini     }
13769e0a03cSPaolo Bonzini 
13869e0a03cSPaolo Bonzini     if (pt->user_access && !pte_user_access(pte)) {
13969e0a03cSPaolo Bonzini         return false;
14069e0a03cSPaolo Bonzini     }
14169e0a03cSPaolo Bonzini 
14269e0a03cSPaolo Bonzini     if (pae && pt->exec_access && !pte_exec_access(pte)) {
14369e0a03cSPaolo Bonzini         return false;
14469e0a03cSPaolo Bonzini     }
14569e0a03cSPaolo Bonzini 
14669e0a03cSPaolo Bonzini exit:
14769e0a03cSPaolo Bonzini     /* TODO: check reserved bits */
14869e0a03cSPaolo Bonzini     return true;
14969e0a03cSPaolo Bonzini }
15069e0a03cSPaolo Bonzini 
pse_pte_to_page(uint64_t pte)15169e0a03cSPaolo Bonzini static inline uint64_t pse_pte_to_page(uint64_t pte)
15269e0a03cSPaolo Bonzini {
15369e0a03cSPaolo Bonzini     return ((pte & 0x1fe000) << 19) | (pte & 0xffc00000);
15469e0a03cSPaolo Bonzini }
15569e0a03cSPaolo Bonzini 
large_page_gpa(struct gpt_translation * pt,bool pae,int largeness)156*654076bcSAlexander Graf static inline uint64_t large_page_gpa(struct gpt_translation *pt, bool pae,
157*654076bcSAlexander Graf                                       int largeness)
15869e0a03cSPaolo Bonzini {
159*654076bcSAlexander Graf     VM_PANIC_ON(!pte_large_page(pt->pte[largeness]))
160*654076bcSAlexander Graf 
161*654076bcSAlexander Graf     /* 1Gib large page  */
162*654076bcSAlexander Graf     if (pae && largeness == 2) {
163*654076bcSAlexander Graf         return (pt->pte[2] & PAE_PTE_SUPER_PAGE_MASK) | (pt->gva & 0x3fffffff);
164*654076bcSAlexander Graf     }
165*654076bcSAlexander Graf 
166*654076bcSAlexander Graf     VM_PANIC_ON(largeness != 1)
167*654076bcSAlexander Graf 
16869e0a03cSPaolo Bonzini     /* 2Mb large page  */
16969e0a03cSPaolo Bonzini     if (pae) {
17069e0a03cSPaolo Bonzini         return (pt->pte[1] & PAE_PTE_LARGE_PAGE_MASK) | (pt->gva & 0x1fffff);
17169e0a03cSPaolo Bonzini     }
17269e0a03cSPaolo Bonzini 
17369e0a03cSPaolo Bonzini     /* 4Mb large page */
17469e0a03cSPaolo Bonzini     return pse_pte_to_page(pt->pte[1]) | (pt->gva & 0x3fffff);
17569e0a03cSPaolo Bonzini }
17669e0a03cSPaolo Bonzini 
17769e0a03cSPaolo Bonzini 
17869e0a03cSPaolo Bonzini 
walk_gpt(CPUState * cpu,target_ulong addr,int err_code,struct gpt_translation * pt,bool pae)179f8436a16SPhilippe Mathieu-Daudé static bool walk_gpt(CPUState *cpu, target_ulong addr, int err_code,
18069e0a03cSPaolo Bonzini                      struct gpt_translation *pt, bool pae)
18169e0a03cSPaolo Bonzini {
18269e0a03cSPaolo Bonzini     int top_level, level;
183*654076bcSAlexander Graf     int largeness = 0;
1843b295bcbSPhilippe Mathieu-Daudé     target_ulong cr3 = rvmcs(cpu->accel->fd, VMCS_GUEST_CR3);
185ff2de166SPaolo Bonzini     uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK;
18669e0a03cSPaolo Bonzini 
18769e0a03cSPaolo Bonzini     memset(pt, 0, sizeof(*pt));
18869e0a03cSPaolo Bonzini     top_level = gpt_top_level(cpu, pae);
18969e0a03cSPaolo Bonzini 
19069e0a03cSPaolo Bonzini     pt->pte[top_level] = pae ? (cr3 & PAE_CR3_MASK) : (cr3 & LEGACY_CR3_MASK);
19169e0a03cSPaolo Bonzini     pt->gva = addr;
19269e0a03cSPaolo Bonzini     pt->user_access = (err_code & MMU_PAGE_US);
19369e0a03cSPaolo Bonzini     pt->write_access = (err_code & MMU_PAGE_WT);
19469e0a03cSPaolo Bonzini     pt->exec_access = (err_code & MMU_PAGE_NX);
19569e0a03cSPaolo Bonzini 
19669e0a03cSPaolo Bonzini     for (level = top_level; level > 0; level--) {
19769e0a03cSPaolo Bonzini         get_pt_entry(cpu, pt, level, pae);
19869e0a03cSPaolo Bonzini 
199*654076bcSAlexander Graf         if (!test_pt_entry(cpu, pt, level - 1, &largeness, pae)) {
20069e0a03cSPaolo Bonzini             return false;
20169e0a03cSPaolo Bonzini         }
20269e0a03cSPaolo Bonzini 
203*654076bcSAlexander Graf         if (largeness) {
20469e0a03cSPaolo Bonzini             break;
20569e0a03cSPaolo Bonzini         }
20669e0a03cSPaolo Bonzini     }
20769e0a03cSPaolo Bonzini 
208*654076bcSAlexander Graf     if (!largeness) {
20969e0a03cSPaolo Bonzini         pt->gpa = (pt->pte[0] & page_mask) | (pt->gva & 0xfff);
21069e0a03cSPaolo Bonzini     } else {
211*654076bcSAlexander Graf         pt->gpa = large_page_gpa(pt, pae, largeness);
21269e0a03cSPaolo Bonzini     }
21369e0a03cSPaolo Bonzini 
21469e0a03cSPaolo Bonzini     return true;
21569e0a03cSPaolo Bonzini }
21669e0a03cSPaolo Bonzini 
21769e0a03cSPaolo Bonzini 
mmu_gva_to_gpa(CPUState * cpu,target_ulong gva,uint64_t * gpa)218f8436a16SPhilippe Mathieu-Daudé bool mmu_gva_to_gpa(CPUState *cpu, target_ulong gva, uint64_t *gpa)
21969e0a03cSPaolo Bonzini {
22069e0a03cSPaolo Bonzini     bool res;
22169e0a03cSPaolo Bonzini     struct gpt_translation pt;
22269e0a03cSPaolo Bonzini     int err_code = 0;
22369e0a03cSPaolo Bonzini 
22469e0a03cSPaolo Bonzini     if (!x86_is_paging_mode(cpu)) {
22569e0a03cSPaolo Bonzini         *gpa = gva;
22669e0a03cSPaolo Bonzini         return true;
22769e0a03cSPaolo Bonzini     }
22869e0a03cSPaolo Bonzini 
22969e0a03cSPaolo Bonzini     res = walk_gpt(cpu, gva, err_code, &pt, x86_is_pae_enabled(cpu));
23069e0a03cSPaolo Bonzini     if (res) {
23169e0a03cSPaolo Bonzini         *gpa = pt.gpa;
23269e0a03cSPaolo Bonzini         return true;
23369e0a03cSPaolo Bonzini     }
23469e0a03cSPaolo Bonzini 
23569e0a03cSPaolo Bonzini     return false;
23669e0a03cSPaolo Bonzini }
23769e0a03cSPaolo Bonzini 
vmx_write_mem(CPUState * cpu,target_ulong gva,void * data,int bytes)238f8436a16SPhilippe Mathieu-Daudé void vmx_write_mem(CPUState *cpu, target_ulong gva, void *data, int bytes)
23969e0a03cSPaolo Bonzini {
240ff2de166SPaolo Bonzini     uint64_t gpa;
24169e0a03cSPaolo Bonzini 
24269e0a03cSPaolo Bonzini     while (bytes > 0) {
24369e0a03cSPaolo Bonzini         /* copy page */
24469e0a03cSPaolo Bonzini         int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
24569e0a03cSPaolo Bonzini 
24669e0a03cSPaolo Bonzini         if (!mmu_gva_to_gpa(cpu, gva, &gpa)) {
24774682782SPaolo Bonzini             VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva);
24869e0a03cSPaolo Bonzini         } else {
24919f70347SPeter Maydell             address_space_write(&address_space_memory, gpa,
25019f70347SPeter Maydell                                 MEMTXATTRS_UNSPECIFIED, data, copy);
25169e0a03cSPaolo Bonzini         }
25269e0a03cSPaolo Bonzini 
25369e0a03cSPaolo Bonzini         bytes -= copy;
25469e0a03cSPaolo Bonzini         gva += copy;
25569e0a03cSPaolo Bonzini         data += copy;
25669e0a03cSPaolo Bonzini     }
25769e0a03cSPaolo Bonzini }
25869e0a03cSPaolo Bonzini 
vmx_read_mem(CPUState * cpu,void * data,target_ulong gva,int bytes)259f8436a16SPhilippe Mathieu-Daudé void vmx_read_mem(CPUState *cpu, void *data, target_ulong gva, int bytes)
26069e0a03cSPaolo Bonzini {
261ff2de166SPaolo Bonzini     uint64_t gpa;
26269e0a03cSPaolo Bonzini 
26369e0a03cSPaolo Bonzini     while (bytes > 0) {
26469e0a03cSPaolo Bonzini         /* copy page */
26569e0a03cSPaolo Bonzini         int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
26669e0a03cSPaolo Bonzini 
26769e0a03cSPaolo Bonzini         if (!mmu_gva_to_gpa(cpu, gva, &gpa)) {
26874682782SPaolo Bonzini             VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva);
26969e0a03cSPaolo Bonzini         }
27019f70347SPeter Maydell         address_space_read(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED,
27119f70347SPeter Maydell                            data, copy);
27269e0a03cSPaolo Bonzini 
27369e0a03cSPaolo Bonzini         bytes -= copy;
27469e0a03cSPaolo Bonzini         gva += copy;
27569e0a03cSPaolo Bonzini         data += copy;
27669e0a03cSPaolo Bonzini     }
27769e0a03cSPaolo Bonzini }
278