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