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

page_cache.c (4251B)


      1/*
      2 * Page cache for QEMU
      3 * The cache is base on a hash of the page address
      4 *
      5 * Copyright 2012 Red Hat, Inc. and/or its affiliates
      6 *
      7 * Authors:
      8 *  Orit Wasserman  <owasserm@redhat.com>
      9 *
     10 * This work is licensed under the terms of the GNU GPL, version 2 or later.
     11 * See the COPYING file in the top-level directory.
     12 *
     13 */
     14
     15#include "qemu/osdep.h"
     16
     17#include "qapi/qmp/qerror.h"
     18#include "qapi/error.h"
     19#include "qemu/host-utils.h"
     20#include "page_cache.h"
     21#include "trace.h"
     22
     23/* the page in cache will not be replaced in two cycles */
     24#define CACHED_PAGE_LIFETIME 2
     25
     26typedef struct CacheItem CacheItem;
     27
     28struct CacheItem {
     29    uint64_t it_addr;
     30    uint64_t it_age;
     31    uint8_t *it_data;
     32};
     33
     34struct PageCache {
     35    CacheItem *page_cache;
     36    size_t page_size;
     37    size_t max_num_items;
     38    size_t num_items;
     39};
     40
     41PageCache *cache_init(uint64_t new_size, size_t page_size, Error **errp)
     42{
     43    int64_t i;
     44    size_t num_pages = new_size / page_size;
     45    PageCache *cache;
     46
     47    if (new_size < page_size) {
     48        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
     49                   "is smaller than one target page size");
     50        return NULL;
     51    }
     52
     53    /* round down to the nearest power of 2 */
     54    if (!is_power_of_2(num_pages)) {
     55        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
     56                   "is not a power of two number of pages");
     57        return NULL;
     58    }
     59
     60    /* We prefer not to abort if there is no memory */
     61    cache = g_try_malloc(sizeof(*cache));
     62    if (!cache) {
     63        error_setg(errp, "Failed to allocate cache");
     64        return NULL;
     65    }
     66    cache->page_size = page_size;
     67    cache->num_items = 0;
     68    cache->max_num_items = num_pages;
     69
     70    trace_migration_pagecache_init(cache->max_num_items);
     71
     72    /* We prefer not to abort if there is no memory */
     73    cache->page_cache = g_try_malloc((cache->max_num_items) *
     74                                     sizeof(*cache->page_cache));
     75    if (!cache->page_cache) {
     76        error_setg(errp, "Failed to allocate page cache");
     77        g_free(cache);
     78        return NULL;
     79    }
     80
     81    for (i = 0; i < cache->max_num_items; i++) {
     82        cache->page_cache[i].it_data = NULL;
     83        cache->page_cache[i].it_age = 0;
     84        cache->page_cache[i].it_addr = -1;
     85    }
     86
     87    return cache;
     88}
     89
     90void cache_fini(PageCache *cache)
     91{
     92    int64_t i;
     93
     94    g_assert(cache);
     95    g_assert(cache->page_cache);
     96
     97    for (i = 0; i < cache->max_num_items; i++) {
     98        g_free(cache->page_cache[i].it_data);
     99    }
    100
    101    g_free(cache->page_cache);
    102    cache->page_cache = NULL;
    103    g_free(cache);
    104}
    105
    106static size_t cache_get_cache_pos(const PageCache *cache,
    107                                  uint64_t address)
    108{
    109    g_assert(cache->max_num_items);
    110    return (address / cache->page_size) & (cache->max_num_items - 1);
    111}
    112
    113static CacheItem *cache_get_by_addr(const PageCache *cache, uint64_t addr)
    114{
    115    size_t pos;
    116
    117    g_assert(cache);
    118    g_assert(cache->page_cache);
    119
    120    pos = cache_get_cache_pos(cache, addr);
    121
    122    return &cache->page_cache[pos];
    123}
    124
    125uint8_t *get_cached_data(const PageCache *cache, uint64_t addr)
    126{
    127    return cache_get_by_addr(cache, addr)->it_data;
    128}
    129
    130bool cache_is_cached(const PageCache *cache, uint64_t addr,
    131                     uint64_t current_age)
    132{
    133    CacheItem *it;
    134
    135    it = cache_get_by_addr(cache, addr);
    136
    137    if (it->it_addr == addr) {
    138        /* update the it_age when the cache hit */
    139        it->it_age = current_age;
    140        return true;
    141    }
    142    return false;
    143}
    144
    145int cache_insert(PageCache *cache, uint64_t addr, const uint8_t *pdata,
    146                 uint64_t current_age)
    147{
    148
    149    CacheItem *it;
    150
    151    /* actual update of entry */
    152    it = cache_get_by_addr(cache, addr);
    153
    154    if (it->it_data && it->it_addr != addr &&
    155        it->it_age + CACHED_PAGE_LIFETIME > current_age) {
    156        /* the cache page is fresh, don't replace it */
    157        return -1;
    158    }
    159    /* allocate page */
    160    if (!it->it_data) {
    161        it->it_data = g_try_malloc(cache->page_size);
    162        if (!it->it_data) {
    163            trace_migration_pagecache_insert();
    164            return -1;
    165        }
    166        cache->num_items++;
    167    }
    168
    169    memcpy(it->it_data, pdata, cache->page_size);
    170
    171    it->it_age = current_age;
    172    it->it_addr = addr;
    173
    174    return 0;
    175}