qemu/target/i386/hvf/x86_mmu.c
<<
>>
Prefs
   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
  42struct 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
  52static 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
  64static 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
  70static inline int pte_size(bool pae)
  71{
  72    return pae ? 8 : 4;
  73}
  74
  75
  76static 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 */
  98static 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    
 145exit:
 146    /* TODO: check reserved bits */
 147    return true;
 148}
 149
 150static inline uint64_t pse_pte_to_page(uint64_t pte)
 151{
 152    return ((pte & 0x1fe000) << 19) | (pte & 0xffc00000);
 153}
 154
 155static 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
 169static 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
 208bool 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
 228void 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
 249void 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}
 268