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