qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

x86_mmu.c (6873B)


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