cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

x86_mmu.c (6893B)


      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}