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

hax-mem.c (10970B)


      1/*
      2 * HAX memory mapping operations
      3 *
      4 * Copyright (c) 2015-16 Intel Corporation
      5 * Copyright 2016 Google, Inc.
      6 *
      7 * This work is licensed under the terms of the GNU GPL, version 2.  See
      8 * the COPYING file in the top-level directory.
      9 */
     10
     11#include "qemu/osdep.h"
     12#include "cpu.h"
     13#include "exec/address-spaces.h"
     14#include "qemu/error-report.h"
     15
     16#include "hax-accel-ops.h"
     17#include "qemu/queue.h"
     18
     19#define DEBUG_HAX_MEM 0
     20
     21#define DPRINTF(fmt, ...) \
     22    do { \
     23        if (DEBUG_HAX_MEM) { \
     24            fprintf(stdout, fmt, ## __VA_ARGS__); \
     25        } \
     26    } while (0)
     27
     28/**
     29 * HAXMapping: describes a pending guest physical memory mapping
     30 *
     31 * @start_pa: a guest physical address marking the start of the region; must be
     32 *            page-aligned
     33 * @size: a guest physical address marking the end of the region; must be
     34 *          page-aligned
     35 * @host_va: the host virtual address of the start of the mapping
     36 * @flags: mapping parameters e.g. HAX_RAM_INFO_ROM or HAX_RAM_INFO_INVALID
     37 * @entry: additional fields for linking #HAXMapping instances together
     38 */
     39typedef struct HAXMapping {
     40    uint64_t start_pa;
     41    uint32_t size;
     42    uint64_t host_va;
     43    int flags;
     44    QTAILQ_ENTRY(HAXMapping) entry;
     45} HAXMapping;
     46
     47/*
     48 * A doubly-linked list (actually a tail queue) of the pending page mappings
     49 * for the ongoing memory transaction.
     50 *
     51 * It is used to optimize the number of page mapping updates done through the
     52 * kernel module. For example, it's effective when a driver is digging an MMIO
     53 * hole inside an existing memory mapping. It will get a deletion of the whole
     54 * region, then the addition of the 2 remaining RAM areas around the hole and
     55 * finally the memory transaction commit. During the commit, it will effectively
     56 * send to the kernel only the removal of the pages from the MMIO hole after
     57 * having computed locally the result of the deletion and additions.
     58 */
     59static QTAILQ_HEAD(, HAXMapping) mappings =
     60    QTAILQ_HEAD_INITIALIZER(mappings);
     61
     62/**
     63 * hax_mapping_dump_list: dumps @mappings to stdout (for debugging)
     64 */
     65static void hax_mapping_dump_list(void)
     66{
     67    HAXMapping *entry;
     68
     69    DPRINTF("%s updates:\n", __func__);
     70    QTAILQ_FOREACH(entry, &mappings, entry) {
     71        DPRINTF("\t%c 0x%016" PRIx64 "->0x%016" PRIx64 " VA 0x%016" PRIx64
     72                "%s\n", entry->flags & HAX_RAM_INFO_INVALID ? '-' : '+',
     73                entry->start_pa, entry->start_pa + entry->size, entry->host_va,
     74                entry->flags & HAX_RAM_INFO_ROM ? " ROM" : "");
     75    }
     76}
     77
     78static void hax_insert_mapping_before(HAXMapping *next, uint64_t start_pa,
     79                                      uint32_t size, uint64_t host_va,
     80                                      uint8_t flags)
     81{
     82    HAXMapping *entry;
     83
     84    entry = g_malloc0(sizeof(*entry));
     85    entry->start_pa = start_pa;
     86    entry->size = size;
     87    entry->host_va = host_va;
     88    entry->flags = flags;
     89    if (!next) {
     90        QTAILQ_INSERT_TAIL(&mappings, entry, entry);
     91    } else {
     92        QTAILQ_INSERT_BEFORE(next, entry, entry);
     93    }
     94}
     95
     96static bool hax_mapping_is_opposite(HAXMapping *entry, uint64_t host_va,
     97                                    uint8_t flags)
     98{
     99    /* removed then added without change for the read-only flag */
    100    bool nop_flags = (entry->flags ^ flags) == HAX_RAM_INFO_INVALID;
    101
    102    return (entry->host_va == host_va) && nop_flags;
    103}
    104
    105static void hax_update_mapping(uint64_t start_pa, uint32_t size,
    106                               uint64_t host_va, uint8_t flags)
    107{
    108    uint64_t end_pa = start_pa + size;
    109    HAXMapping *entry, *next;
    110
    111    QTAILQ_FOREACH_SAFE(entry, &mappings, entry, next) {
    112        uint32_t chunk_sz;
    113        if (start_pa >= entry->start_pa + entry->size) {
    114            continue;
    115        }
    116        if (start_pa < entry->start_pa) {
    117            chunk_sz = end_pa <= entry->start_pa ? size
    118                                                 : entry->start_pa - start_pa;
    119            hax_insert_mapping_before(entry, start_pa, chunk_sz,
    120                                      host_va, flags);
    121            start_pa += chunk_sz;
    122            host_va += chunk_sz;
    123            size -= chunk_sz;
    124        } else if (start_pa > entry->start_pa) {
    125            /* split the existing chunk at start_pa */
    126            chunk_sz = start_pa - entry->start_pa;
    127            hax_insert_mapping_before(entry, entry->start_pa, chunk_sz,
    128                                      entry->host_va, entry->flags);
    129            entry->start_pa += chunk_sz;
    130            entry->host_va += chunk_sz;
    131            entry->size -= chunk_sz;
    132        }
    133        /* now start_pa == entry->start_pa */
    134        chunk_sz = MIN(size, entry->size);
    135        if (chunk_sz) {
    136            bool nop = hax_mapping_is_opposite(entry, host_va, flags);
    137            bool partial = chunk_sz < entry->size;
    138            if (partial) {
    139                /* remove the beginning of the existing chunk */
    140                entry->start_pa += chunk_sz;
    141                entry->host_va += chunk_sz;
    142                entry->size -= chunk_sz;
    143                if (!nop) {
    144                    hax_insert_mapping_before(entry, start_pa, chunk_sz,
    145                                              host_va, flags);
    146                }
    147            } else { /* affects the full mapping entry */
    148                if (nop) { /* no change to this mapping, remove it */
    149                    QTAILQ_REMOVE(&mappings, entry, entry);
    150                    g_free(entry);
    151                } else { /* update mapping properties */
    152                    entry->host_va = host_va;
    153                    entry->flags = flags;
    154                }
    155            }
    156            start_pa += chunk_sz;
    157            host_va += chunk_sz;
    158            size -= chunk_sz;
    159        }
    160        if (!size) { /* we are done */
    161            break;
    162        }
    163    }
    164    if (size) { /* add the leftover */
    165        hax_insert_mapping_before(NULL, start_pa, size, host_va, flags);
    166    }
    167}
    168
    169static void hax_process_section(MemoryRegionSection *section, uint8_t flags)
    170{
    171    MemoryRegion *mr = section->mr;
    172    hwaddr start_pa = section->offset_within_address_space;
    173    ram_addr_t size = int128_get64(section->size);
    174    unsigned int delta;
    175    uint64_t host_va;
    176    uint32_t max_mapping_size;
    177
    178    /* We only care about RAM and ROM regions */
    179    if (!memory_region_is_ram(mr)) {
    180        if (memory_region_is_romd(mr)) {
    181            /* HAXM kernel module does not support ROMD yet  */
    182            warn_report("Ignoring ROMD region 0x%016" PRIx64 "->0x%016" PRIx64,
    183                        start_pa, start_pa + size);
    184        }
    185        return;
    186    }
    187
    188    /* Adjust start_pa and size so that they are page-aligned. (Cf
    189     * kvm_set_phys_mem() in kvm-all.c).
    190     */
    191    delta = qemu_real_host_page_size - (start_pa & ~qemu_real_host_page_mask);
    192    delta &= ~qemu_real_host_page_mask;
    193    if (delta > size) {
    194        return;
    195    }
    196    start_pa += delta;
    197    size -= delta;
    198    size &= qemu_real_host_page_mask;
    199    if (!size || (start_pa & ~qemu_real_host_page_mask)) {
    200        return;
    201    }
    202
    203    host_va = (uintptr_t)memory_region_get_ram_ptr(mr)
    204            + section->offset_within_region + delta;
    205    if (memory_region_is_rom(section->mr)) {
    206        flags |= HAX_RAM_INFO_ROM;
    207    }
    208
    209    /*
    210     * The kernel module interface uses 32-bit sizes:
    211     * https://github.com/intel/haxm/blob/master/API.md#hax_vm_ioctl_set_ram
    212     *
    213     * If the mapping size is longer than 32 bits, we can't process it in one
    214     * call into the kernel. Instead, we split the mapping into smaller ones,
    215     * and call hax_update_mapping() on each.
    216     */
    217    max_mapping_size = UINT32_MAX & qemu_real_host_page_mask;
    218    while (size > max_mapping_size) {
    219        hax_update_mapping(start_pa, max_mapping_size, host_va, flags);
    220        start_pa += max_mapping_size;
    221        size -= max_mapping_size;
    222        host_va += max_mapping_size;
    223    }
    224    /* Now size <= max_mapping_size */
    225    hax_update_mapping(start_pa, (uint32_t)size, host_va, flags);
    226}
    227
    228static void hax_region_add(MemoryListener *listener,
    229                           MemoryRegionSection *section)
    230{
    231    memory_region_ref(section->mr);
    232    hax_process_section(section, 0);
    233}
    234
    235static void hax_region_del(MemoryListener *listener,
    236                           MemoryRegionSection *section)
    237{
    238    hax_process_section(section, HAX_RAM_INFO_INVALID);
    239    memory_region_unref(section->mr);
    240}
    241
    242static void hax_transaction_begin(MemoryListener *listener)
    243{
    244    g_assert(QTAILQ_EMPTY(&mappings));
    245}
    246
    247static void hax_transaction_commit(MemoryListener *listener)
    248{
    249    if (!QTAILQ_EMPTY(&mappings)) {
    250        HAXMapping *entry, *next;
    251
    252        if (DEBUG_HAX_MEM) {
    253            hax_mapping_dump_list();
    254        }
    255        QTAILQ_FOREACH_SAFE(entry, &mappings, entry, next) {
    256            if (entry->flags & HAX_RAM_INFO_INVALID) {
    257                /* for unmapping, put the values expected by the kernel */
    258                entry->flags = HAX_RAM_INFO_INVALID;
    259                entry->host_va = 0;
    260            }
    261            if (hax_set_ram(entry->start_pa, entry->size,
    262                            entry->host_va, entry->flags)) {
    263                fprintf(stderr, "%s: Failed mapping @0x%016" PRIx64 "+0x%"
    264                        PRIx32 " flags %02x\n", __func__, entry->start_pa,
    265                        entry->size, entry->flags);
    266            }
    267            QTAILQ_REMOVE(&mappings, entry, entry);
    268            g_free(entry);
    269        }
    270    }
    271}
    272
    273/* currently we fake the dirty bitmap sync, always dirty */
    274static void hax_log_sync(MemoryListener *listener,
    275                         MemoryRegionSection *section)
    276{
    277    MemoryRegion *mr = section->mr;
    278
    279    if (!memory_region_is_ram(mr)) {
    280        /* Skip MMIO regions */
    281        return;
    282    }
    283
    284    memory_region_set_dirty(mr, 0, int128_get64(section->size));
    285}
    286
    287static MemoryListener hax_memory_listener = {
    288    .name = "hax",
    289    .begin = hax_transaction_begin,
    290    .commit = hax_transaction_commit,
    291    .region_add = hax_region_add,
    292    .region_del = hax_region_del,
    293    .log_sync = hax_log_sync,
    294    .priority = 10,
    295};
    296
    297static void hax_ram_block_added(RAMBlockNotifier *n, void *host, size_t size,
    298                                size_t max_size)
    299{
    300    /*
    301     * We must register each RAM block with the HAXM kernel module, or
    302     * hax_set_ram() will fail for any mapping into the RAM block:
    303     * https://github.com/intel/haxm/blob/master/API.md#hax_vm_ioctl_alloc_ram
    304     *
    305     * Old versions of the HAXM kernel module (< 6.2.0) used to preallocate all
    306     * host physical pages for the RAM block as part of this registration
    307     * process, hence the name hax_populate_ram().
    308     */
    309    if (hax_populate_ram((uint64_t)(uintptr_t)host, max_size) < 0) {
    310        fprintf(stderr, "HAX failed to populate RAM\n");
    311        abort();
    312    }
    313}
    314
    315static struct RAMBlockNotifier hax_ram_notifier = {
    316    .ram_block_added = hax_ram_block_added,
    317};
    318
    319void hax_memory_init(void)
    320{
    321    ram_block_notifier_add(&hax_ram_notifier);
    322    memory_listener_register(&hax_memory_listener, &address_space_memory);
    323}