qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

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"
     37 typedef struct EduState EduState;
     38 DECLARE_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 
     47 struct 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 
     80 static bool edu_msi_enabled(EduState *edu)
     81 {
     82     return msi_enabled(&edu->pdev);
     83 }
     84 
     85 static 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 
     97 static 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 
    106 static bool within(uint64_t addr, uint64_t start, uint64_t end)
    107 {
    108     return start <= addr && addr < end;
    109 }
    110 
    111 static 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 
    127 static 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 
    138 static 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 
    171 static 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 
    189 static 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 
    237 static 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 
    298 static 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  */
    317 static 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 
    362 static 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 
    385 static 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 
    402 static 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 
    411 static 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 
    425 static 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 }
    442 type_init(pci_edu_register_types)