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

edu.c (11494B)


      1/*
      2 * QEMU educational PCI device
      3 *
      4 * Copyright (c) 2012-2015 Jiri Slaby
      5 *
      6 * Permission is hereby granted, free of charge, to any person obtaining a
      7 * copy of this software and associated documentation files (the "Software"),
      8 * to deal in the Software without restriction, including without limitation
      9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10 * and/or sell copies of the Software, and to permit persons to whom the
     11 * Software is furnished to do so, subject to the following conditions:
     12 *
     13 * The above copyright notice and this permission notice shall be included in
     14 * all copies or substantial portions of the Software.
     15 *
     16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     22 * DEALINGS IN THE SOFTWARE.
     23 */
     24
     25#include "qemu/osdep.h"
     26#include "qemu/units.h"
     27#include "hw/pci/pci.h"
     28#include "hw/hw.h"
     29#include "hw/pci/msi.h"
     30#include "qemu/timer.h"
     31#include "qom/object.h"
     32#include "qemu/main-loop.h" /* iothread mutex */
     33#include "qemu/module.h"
     34#include "qapi/visitor.h"
     35
     36#define TYPE_PCI_EDU_DEVICE "edu"
     37typedef struct EduState EduState;
     38DECLARE_INSTANCE_CHECKER(EduState, EDU,
     39                         TYPE_PCI_EDU_DEVICE)
     40
     41#define FACT_IRQ        0x00000001
     42#define DMA_IRQ         0x00000100
     43
     44#define DMA_START       0x40000
     45#define DMA_SIZE        4096
     46
     47struct EduState {
     48    PCIDevice pdev;
     49    MemoryRegion mmio;
     50
     51    QemuThread thread;
     52    QemuMutex thr_mutex;
     53    QemuCond thr_cond;
     54    bool stopping;
     55
     56    uint32_t addr4;
     57    uint32_t fact;
     58#define EDU_STATUS_COMPUTING    0x01
     59#define EDU_STATUS_IRQFACT      0x80
     60    uint32_t status;
     61
     62    uint32_t irq_status;
     63
     64#define EDU_DMA_RUN             0x1
     65#define EDU_DMA_DIR(cmd)        (((cmd) & 0x2) >> 1)
     66# define EDU_DMA_FROM_PCI       0
     67# define EDU_DMA_TO_PCI         1
     68#define EDU_DMA_IRQ             0x4
     69    struct dma_state {
     70        dma_addr_t src;
     71        dma_addr_t dst;
     72        dma_addr_t cnt;
     73        dma_addr_t cmd;
     74    } dma;
     75    QEMUTimer dma_timer;
     76    char dma_buf[DMA_SIZE];
     77    uint64_t dma_mask;
     78};
     79
     80static bool edu_msi_enabled(EduState *edu)
     81{
     82    return msi_enabled(&edu->pdev);
     83}
     84
     85static void edu_raise_irq(EduState *edu, uint32_t val)
     86{
     87    edu->irq_status |= val;
     88    if (edu->irq_status) {
     89        if (edu_msi_enabled(edu)) {
     90            msi_notify(&edu->pdev, 0);
     91        } else {
     92            pci_set_irq(&edu->pdev, 1);
     93        }
     94    }
     95}
     96
     97static void edu_lower_irq(EduState *edu, uint32_t val)
     98{
     99    edu->irq_status &= ~val;
    100
    101    if (!edu->irq_status && !edu_msi_enabled(edu)) {
    102        pci_set_irq(&edu->pdev, 0);
    103    }
    104}
    105
    106static bool within(uint64_t addr, uint64_t start, uint64_t end)
    107{
    108    return start <= addr && addr < end;
    109}
    110
    111static void edu_check_range(uint64_t addr, uint64_t size1, uint64_t start,
    112                uint64_t size2)
    113{
    114    uint64_t end1 = addr + size1;
    115    uint64_t end2 = start + size2;
    116
    117    if (within(addr, start, end2) &&
    118            end1 > addr && within(end1, start, end2)) {
    119        return;
    120    }
    121
    122    hw_error("EDU: DMA range 0x%016"PRIx64"-0x%016"PRIx64
    123             " out of bounds (0x%016"PRIx64"-0x%016"PRIx64")!",
    124            addr, end1 - 1, start, end2 - 1);
    125}
    126
    127static dma_addr_t edu_clamp_addr(const EduState *edu, dma_addr_t addr)
    128{
    129    dma_addr_t res = addr & edu->dma_mask;
    130
    131    if (addr != res) {
    132        printf("EDU: clamping DMA %#.16"PRIx64" to %#.16"PRIx64"!\n", addr, res);
    133    }
    134
    135    return res;
    136}
    137
    138static void edu_dma_timer(void *opaque)
    139{
    140    EduState *edu = opaque;
    141    bool raise_irq = false;
    142
    143    if (!(edu->dma.cmd & EDU_DMA_RUN)) {
    144        return;
    145    }
    146
    147    if (EDU_DMA_DIR(edu->dma.cmd) == EDU_DMA_FROM_PCI) {
    148        uint64_t dst = edu->dma.dst;
    149        edu_check_range(dst, edu->dma.cnt, DMA_START, DMA_SIZE);
    150        dst -= DMA_START;
    151        pci_dma_read(&edu->pdev, edu_clamp_addr(edu, edu->dma.src),
    152                edu->dma_buf + dst, edu->dma.cnt);
    153    } else {
    154        uint64_t src = edu->dma.src;
    155        edu_check_range(src, edu->dma.cnt, DMA_START, DMA_SIZE);
    156        src -= DMA_START;
    157        pci_dma_write(&edu->pdev, edu_clamp_addr(edu, edu->dma.dst),
    158                edu->dma_buf + src, edu->dma.cnt);
    159    }
    160
    161    edu->dma.cmd &= ~EDU_DMA_RUN;
    162    if (edu->dma.cmd & EDU_DMA_IRQ) {
    163        raise_irq = true;
    164    }
    165
    166    if (raise_irq) {
    167        edu_raise_irq(edu, DMA_IRQ);
    168    }
    169}
    170
    171static void dma_rw(EduState *edu, bool write, dma_addr_t *val, dma_addr_t *dma,
    172                bool timer)
    173{
    174    if (write && (edu->dma.cmd & EDU_DMA_RUN)) {
    175        return;
    176    }
    177
    178    if (write) {
    179        *dma = *val;
    180    } else {
    181        *val = *dma;
    182    }
    183
    184    if (timer) {
    185        timer_mod(&edu->dma_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 100);
    186    }
    187}
    188
    189static uint64_t edu_mmio_read(void *opaque, hwaddr addr, unsigned size)
    190{
    191    EduState *edu = opaque;
    192    uint64_t val = ~0ULL;
    193
    194    if (addr < 0x80 && size != 4) {
    195        return val;
    196    }
    197
    198    if (addr >= 0x80 && size != 4 && size != 8) {
    199        return val;
    200    }
    201
    202    switch (addr) {
    203    case 0x00:
    204        val = 0x010000edu;
    205        break;
    206    case 0x04:
    207        val = edu->addr4;
    208        break;
    209    case 0x08:
    210        qemu_mutex_lock(&edu->thr_mutex);
    211        val = edu->fact;
    212        qemu_mutex_unlock(&edu->thr_mutex);
    213        break;
    214    case 0x20:
    215        val = qatomic_read(&edu->status);
    216        break;
    217    case 0x24:
    218        val = edu->irq_status;
    219        break;
    220    case 0x80:
    221        dma_rw(edu, false, &val, &edu->dma.src, false);
    222        break;
    223    case 0x88:
    224        dma_rw(edu, false, &val, &edu->dma.dst, false);
    225        break;
    226    case 0x90:
    227        dma_rw(edu, false, &val, &edu->dma.cnt, false);
    228        break;
    229    case 0x98:
    230        dma_rw(edu, false, &val, &edu->dma.cmd, false);
    231        break;
    232    }
    233
    234    return val;
    235}
    236
    237static void edu_mmio_write(void *opaque, hwaddr addr, uint64_t val,
    238                unsigned size)
    239{
    240    EduState *edu = opaque;
    241
    242    if (addr < 0x80 && size != 4) {
    243        return;
    244    }
    245
    246    if (addr >= 0x80 && size != 4 && size != 8) {
    247        return;
    248    }
    249
    250    switch (addr) {
    251    case 0x04:
    252        edu->addr4 = ~val;
    253        break;
    254    case 0x08:
    255        if (qatomic_read(&edu->status) & EDU_STATUS_COMPUTING) {
    256            break;
    257        }
    258        /* EDU_STATUS_COMPUTING cannot go 0->1 concurrently, because it is only
    259         * set in this function and it is under the iothread mutex.
    260         */
    261        qemu_mutex_lock(&edu->thr_mutex);
    262        edu->fact = val;
    263        qatomic_or(&edu->status, EDU_STATUS_COMPUTING);
    264        qemu_cond_signal(&edu->thr_cond);
    265        qemu_mutex_unlock(&edu->thr_mutex);
    266        break;
    267    case 0x20:
    268        if (val & EDU_STATUS_IRQFACT) {
    269            qatomic_or(&edu->status, EDU_STATUS_IRQFACT);
    270        } else {
    271            qatomic_and(&edu->status, ~EDU_STATUS_IRQFACT);
    272        }
    273        break;
    274    case 0x60:
    275        edu_raise_irq(edu, val);
    276        break;
    277    case 0x64:
    278        edu_lower_irq(edu, val);
    279        break;
    280    case 0x80:
    281        dma_rw(edu, true, &val, &edu->dma.src, false);
    282        break;
    283    case 0x88:
    284        dma_rw(edu, true, &val, &edu->dma.dst, false);
    285        break;
    286    case 0x90:
    287        dma_rw(edu, true, &val, &edu->dma.cnt, false);
    288        break;
    289    case 0x98:
    290        if (!(val & EDU_DMA_RUN)) {
    291            break;
    292        }
    293        dma_rw(edu, true, &val, &edu->dma.cmd, true);
    294        break;
    295    }
    296}
    297
    298static const MemoryRegionOps edu_mmio_ops = {
    299    .read = edu_mmio_read,
    300    .write = edu_mmio_write,
    301    .endianness = DEVICE_NATIVE_ENDIAN,
    302    .valid = {
    303        .min_access_size = 4,
    304        .max_access_size = 8,
    305    },
    306    .impl = {
    307        .min_access_size = 4,
    308        .max_access_size = 8,
    309    },
    310
    311};
    312
    313/*
    314 * We purposely use a thread, so that users are forced to wait for the status
    315 * register.
    316 */
    317static void *edu_fact_thread(void *opaque)
    318{
    319    EduState *edu = opaque;
    320
    321    while (1) {
    322        uint32_t val, ret = 1;
    323
    324        qemu_mutex_lock(&edu->thr_mutex);
    325        while ((qatomic_read(&edu->status) & EDU_STATUS_COMPUTING) == 0 &&
    326                        !edu->stopping) {
    327            qemu_cond_wait(&edu->thr_cond, &edu->thr_mutex);
    328        }
    329
    330        if (edu->stopping) {
    331            qemu_mutex_unlock(&edu->thr_mutex);
    332            break;
    333        }
    334
    335        val = edu->fact;
    336        qemu_mutex_unlock(&edu->thr_mutex);
    337
    338        while (val > 0) {
    339            ret *= val--;
    340        }
    341
    342        /*
    343         * We should sleep for a random period here, so that students are
    344         * forced to check the status properly.
    345         */
    346
    347        qemu_mutex_lock(&edu->thr_mutex);
    348        edu->fact = ret;
    349        qemu_mutex_unlock(&edu->thr_mutex);
    350        qatomic_and(&edu->status, ~EDU_STATUS_COMPUTING);
    351
    352        if (qatomic_read(&edu->status) & EDU_STATUS_IRQFACT) {
    353            qemu_mutex_lock_iothread();
    354            edu_raise_irq(edu, FACT_IRQ);
    355            qemu_mutex_unlock_iothread();
    356        }
    357    }
    358
    359    return NULL;
    360}
    361
    362static void pci_edu_realize(PCIDevice *pdev, Error **errp)
    363{
    364    EduState *edu = EDU(pdev);
    365    uint8_t *pci_conf = pdev->config;
    366
    367    pci_config_set_interrupt_pin(pci_conf, 1);
    368
    369    if (msi_init(pdev, 0, 1, true, false, errp)) {
    370        return;
    371    }
    372
    373    timer_init_ms(&edu->dma_timer, QEMU_CLOCK_VIRTUAL, edu_dma_timer, edu);
    374
    375    qemu_mutex_init(&edu->thr_mutex);
    376    qemu_cond_init(&edu->thr_cond);
    377    qemu_thread_create(&edu->thread, "edu", edu_fact_thread,
    378                       edu, QEMU_THREAD_JOINABLE);
    379
    380    memory_region_init_io(&edu->mmio, OBJECT(edu), &edu_mmio_ops, edu,
    381                    "edu-mmio", 1 * MiB);
    382    pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &edu->mmio);
    383}
    384
    385static void pci_edu_uninit(PCIDevice *pdev)
    386{
    387    EduState *edu = EDU(pdev);
    388
    389    qemu_mutex_lock(&edu->thr_mutex);
    390    edu->stopping = true;
    391    qemu_mutex_unlock(&edu->thr_mutex);
    392    qemu_cond_signal(&edu->thr_cond);
    393    qemu_thread_join(&edu->thread);
    394
    395    qemu_cond_destroy(&edu->thr_cond);
    396    qemu_mutex_destroy(&edu->thr_mutex);
    397
    398    timer_del(&edu->dma_timer);
    399    msi_uninit(pdev);
    400}
    401
    402static void edu_instance_init(Object *obj)
    403{
    404    EduState *edu = EDU(obj);
    405
    406    edu->dma_mask = (1UL << 28) - 1;
    407    object_property_add_uint64_ptr(obj, "dma_mask",
    408                                   &edu->dma_mask, OBJ_PROP_FLAG_READWRITE);
    409}
    410
    411static void edu_class_init(ObjectClass *class, void *data)
    412{
    413    DeviceClass *dc = DEVICE_CLASS(class);
    414    PCIDeviceClass *k = PCI_DEVICE_CLASS(class);
    415
    416    k->realize = pci_edu_realize;
    417    k->exit = pci_edu_uninit;
    418    k->vendor_id = PCI_VENDOR_ID_QEMU;
    419    k->device_id = 0x11e8;
    420    k->revision = 0x10;
    421    k->class_id = PCI_CLASS_OTHERS;
    422    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
    423}
    424
    425static void pci_edu_register_types(void)
    426{
    427    static InterfaceInfo interfaces[] = {
    428        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
    429        { },
    430    };
    431    static const TypeInfo edu_info = {
    432        .name          = TYPE_PCI_EDU_DEVICE,
    433        .parent        = TYPE_PCI_DEVICE,
    434        .instance_size = sizeof(EduState),
    435        .instance_init = edu_instance_init,
    436        .class_init    = edu_class_init,
    437        .interfaces = interfaces,
    438    };
    439
    440    type_register_static(&edu_info);
    441}
    442type_init(pci_edu_register_types)