Skip to content

Instantly share code, notes, and snippets.

@proywm
Created July 4, 2018 14:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save proywm/6ca98d3e8ca001965e2c8792fcf97911 to your computer and use it in GitHub Desktop.
Save proywm/6ca98d3e8ca001965e2c8792fcf97911 to your computer and use it in GitHub Desktop.
/*
* QEMU PCI device for HYPERF
*
* Copyright (c) 2012-2015 Jiri Slaby
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "hw/pci/pci.h"
#include "hw/pci/msi.h"
#include "qemu/timer.h"
#include "qemu/main-loop.h" /* iothread mutex */
#include "qapi/visitor.h"
#include "qapi/error.h"
#include <sys/syscall.h>
#include "sysemu/kvm_int.h"
#include "sysemu/kvm.h"
#include "kvminterface.hpp"
#define HYPERF(obj) OBJECT_CHECK(HyPerfState, obj, "hyperf")
#define FACT_IRQ 0x00000001
#define DMA_IRQ 0x00000100
#define EVENT_IRQ 0xDEADBEEF
#define DMA_START 0x40000
#define DMA_SIZE 4096
#define DMA_MASK 32
#define BAR_MMIO 0
#define BAR_SHMEM 2
#define VSHMEM_SIZE 4096 /* TODO fixed for now. replace later */
typedef struct {
PCIDevice pdev;
MemoryRegion mmio; /* BAR 0 (registers) */
MemoryRegion vshmem; /* BAR 2 (shared memory) */
uint32_t vshmem_size; /* size of the shared memory region */
void *vshmem_ptr;
int OSWillProcess;
QemuThread thread;
QemuMutex thr_mutex;
QemuCond thr_cond;
bool stopping;
uint32_t addr4;
uint32_t fact;
#define HYPERF_STATUS_COMPUTING 0x01
#define HYPERF_STATUS_IRQFACT 0x80
uint32_t status;
uint32_t irq_status;
struct shmem_state {
uint32_t src;
uint32_t cnt;
uint32_t cmd;
} dma;
char dma_buf[DMA_SIZE];
// VMContextFrame dma_buf[MAXCONTEXTFRAMES];
} HyPerfState;
VMEvent *vmEvent = NULL;
static bool hyperf_msi_enabled(HyPerfState *hyperf)
{
return msi_enabled(&hyperf->pdev);
}
static void hyperf_raise_irq(HyPerfState *hyperf, uint32_t val)
{
hyperf->irq_status |= val;
if (hyperf->irq_status) {
if (hyperf_msi_enabled(hyperf)) {
msi_notify(&hyperf->pdev, 0);
} else {
pci_set_irq(&hyperf->pdev, 1);
}
}
}
static void hyperf_lower_irq(HyPerfState *hyperf, uint32_t val)
{
hyperf->irq_status &= ~val;
if (!hyperf->irq_status && !hyperf_msi_enabled(hyperf)) {
pci_set_irq(&hyperf->pdev, 0);
}
}
static uint64_t hyperf_mmio_read(void *opaque, hwaddr addr, unsigned size)
{
HyPerfState *hyperf = opaque;
uint64_t val = ~0ULL;
if (size != 4) {
return val;
}
switch (addr) {
case 0x00:
val = 0x010000edu;
break;
case 0x04:
val = hyperf->addr4;
break;
case 0x08:
qemu_mutex_lock(&hyperf->thr_mutex);
val = hyperf->fact;
qemu_mutex_unlock(&hyperf->thr_mutex);
break;
case 0x20:
val = atomic_read(&hyperf->status);
break;
case 0x24:
val = hyperf->irq_status;
break;
case 0x80:
//dma_rw(hyperf, false, &val, &hyperf->dma.src, false);
// hyperf->dma.src = val;
break;
case 0x88:
// dma_rw(hyperf, false, &val, &hyperf->dma.dst, false);
break;
case 0x90:
// dma_rw(hyperf, false, &val, &hyperf->dma.cnt, false);
// hyperf->dma.cnt = val;
break;
case 0x98:
// dma_rw(hyperf, false, &val, &hyperf->dma.cmd, false);
// hyperf->dma.cmd = val;
// hyperf_shmem_read(opaque);
break;
}
return val;
}
/* Handling guest writes to device */
static void hyperf_mmio_write(void *opaque, hwaddr addr, uint64_t val,
unsigned size)
{
HyPerfState *hyperf = opaque;
if (addr < 0x80 && size != 4) {
return;
}
if (addr >= 0x80 && size != 4 && size != 8) {
return;
}
switch (addr) {
case 0x04:
hyperf->addr4 = ~val;
break;
case 0x08:
if (atomic_read(&hyperf->status) & HYPERF_STATUS_COMPUTING) {
break;
}
/* HYPERF_STATUS_COMPUTING cannot go 0->1 concurrently, because it is only
* set in this function and it is under the iothread mutex.
*/
qemu_mutex_lock(&hyperf->thr_mutex);
hyperf->fact = val;
atomic_or(&hyperf->status, HYPERF_STATUS_COMPUTING);
qemu_cond_signal(&hyperf->thr_cond);
qemu_mutex_unlock(&hyperf->thr_mutex);
break;
case 0x20:
if (val & HYPERF_STATUS_IRQFACT) {
atomic_or(&hyperf->status, HYPERF_STATUS_IRQFACT);
} else {
atomic_and(&hyperf->status, ~HYPERF_STATUS_IRQFACT);
}
break;
case 0x60:
hyperf_raise_irq(hyperf, val);
break;
case 0x64:
hyperf_lower_irq(hyperf, val);
break;
case 0x80:
hyperf->dma.src = val;
break;
case 0x88:
break;
case 0x90:
hyperf->dma.cnt = val;
break;
case 0x98:
qemu_mutex_lock(&hyperf->thr_mutex);
hyperf->dma.cmd = val;
qemu_mutex_unlock(&hyperf->thr_mutex);
break;
}
}
static const MemoryRegionOps hyperf_mmio_ops = {
.read = hyperf_mmio_read,
.write = hyperf_mmio_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
/*
* We purposely use a thread, so that users are forced to wait for the status
* register.
*/
static void *hyperf_fact_thread(void *opaque)
{
HyPerfState *hyperf = opaque;
printf("FROM HyPerf PCI DEV: hyperf_fact_thread: Thread created >>>>>>>>>>>>> \n");
while (1) {
uint32_t val, ret = 1;
qemu_mutex_lock(&hyperf->thr_mutex);
while ((atomic_read(&hyperf->status) & HYPERF_STATUS_COMPUTING) == 0 &&
!hyperf->stopping) {
qemu_cond_wait(&hyperf->thr_cond, &hyperf->thr_mutex);
}
if (hyperf->stopping) {
qemu_mutex_unlock(&hyperf->thr_mutex);
break;
}
val = hyperf->fact;
qemu_mutex_unlock(&hyperf->thr_mutex);
while (val > 0) {
ret *= val--;
}
/*
* We should sleep for a random period here, so that students are
* forced to check the status properly.
*/
qemu_mutex_lock(&hyperf->thr_mutex);
hyperf->fact = ret;
qemu_mutex_unlock(&hyperf->thr_mutex);
atomic_and(&hyperf->status, ~HYPERF_STATUS_COMPUTING);
if (atomic_read(&hyperf->status) & HYPERF_STATUS_IRQFACT) {
qemu_mutex_lock_iothread();
hyperf_raise_irq(hyperf, FACT_IRQ);
qemu_mutex_unlock_iothread();
}
}
return NULL;
}
static void pci_hyperf_realize(PCIDevice *pdev, Error **errp)
{
HyPerfState *hyperf = DO_UPCAST(HyPerfState, pdev, pdev);
uint8_t *pci_conf = pdev->config;
pci_config_set_interrupt_pin(pci_conf, 1);
if (msi_init(pdev, 0, 1, true, false, errp)) {
return;
}
qemu_mutex_init(&hyperf->thr_mutex);
qemu_cond_init(&hyperf->thr_cond);
qemu_thread_create(&hyperf->thread, "hyperf", hyperf_fact_thread,
hyperf, QEMU_THREAD_JOINABLE);
memory_region_init_io(&hyperf->mmio, OBJECT(hyperf), &hyperf_mmio_ops, hyperf,
"hyperf-mmio", 1 << 20);
pci_register_bar(pdev, BAR_MMIO, PCI_BASE_ADDRESS_SPACE_MEMORY, &hyperf->mmio);
/* Host RAM Region that is available to guest through this device*/
hyperf->vshmem_size = VSHMEM_SIZE; //TODO replace later
memory_region_init_ram(&hyperf->vshmem, OBJECT(hyperf), "hyperf-vshmem", hyperf->vshmem_size, &error_fatal);
pci_register_bar(pdev, BAR_SHMEM, PCI_BASE_ADDRESS_SPACE_MEMORY, &hyperf->vshmem);
hyperf->vshmem_ptr = memory_region_get_ram_ptr(&hyperf->vshmem);
}
static void pci_hyperf_uninit(PCIDevice *pdev)
{
HyPerfState *hyperf = DO_UPCAST(HyPerfState, pdev, pdev);
qemu_mutex_lock(&hyperf->thr_mutex);
hyperf->stopping = true;
qemu_mutex_unlock(&hyperf->thr_mutex);
qemu_cond_signal(&hyperf->thr_cond);
qemu_thread_join(&hyperf->thread);
qemu_cond_destroy(&hyperf->thr_cond);
qemu_mutex_destroy(&hyperf->thr_mutex);
}
static void hyperf_instance_init(Object *obj)
{
HyPerfState *hyperf = HYPERF(obj);
/*----------------------------------------
* Sending the Interface to perform library
*----------------------------------------
*/
printf("FROM HyPerf PCI DEV: hyperf_instance_init: Registering my Interfaces >>>>>>>>>>>>> \n");
kvmInfo.dev = hyperf;
hyperf->OSWillProcess = 0;
kvm__register_HyPerf(&kvmInfo);
}
static void hyperf_class_init(ObjectClass *class, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(class);
k->realize = pci_hyperf_realize;
k->exit = pci_hyperf_uninit;
k->vendor_id = PCI_VENDOR_ID_QEMU;
k->device_id = 0x11e8;
k->revision = 0x10;
k->class_id = PCI_CLASS_OTHERS;
}
static void pci_hyperf_register_types(void)
{
static InterfaceInfo interfaces[] = {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ },
};
static const TypeInfo hyperf_info = {
.name = "hyperf",
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(HyPerfState),
.instance_init = hyperf_instance_init,
.class_init = hyperf_class_init,
.interfaces = interfaces,
};
type_register_static(&hyperf_info);
}
type_init(pci_hyperf_register_types)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment