xref: /openbmc/qemu/target/i386/hvf/x86_mmu.c (revision d8e39b70625d4ba1e998439d1a077b4b978930e7)
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