static void syborg_virtio_writel(void *opaque, target_phys_addr_t offset, uint32_t value) { SyborgVirtIOProxy *s = opaque; VirtIODevice *vdev = s->vdev; DPRINTF("writel 0x%x = 0x%x\n", (int)offset, value); if (offset >= SYBORG_VIRTIO_CONFIG) { return virtio_config_writel(vdev, offset - SYBORG_VIRTIO_CONFIG, value); } switch (offset >> 2) { case SYBORG_VIRTIO_GUEST_FEATURES: if (vdev->set_features) vdev->set_features(vdev, value); vdev->guest_features = value; break; case SYBORG_VIRTIO_QUEUE_BASE: if (value == 0) virtio_reset(vdev); else virtio_queue_set_addr(vdev, vdev->queue_sel, value); break; case SYBORG_VIRTIO_QUEUE_SEL: if (value < VIRTIO_PCI_QUEUE_MAX) vdev->queue_sel = value; break; case SYBORG_VIRTIO_QUEUE_NOTIFY: if (value < VIRTIO_PCI_QUEUE_MAX) { virtio_queue_notify(vdev, value); } break; case SYBORG_VIRTIO_STATUS: virtio_set_status(vdev, value & 0xFF); if (vdev->status == 0) virtio_reset(vdev); break; case SYBORG_VIRTIO_INT_ENABLE: s->int_enable = value; virtio_update_irq(vdev); break; case SYBORG_VIRTIO_INT_STATUS: vdev->isr &= ~value; virtio_update_irq(vdev); break; default: BADF("Bad write offset 0x%x\n", (int)offset); break; } }
static int ld_virtio_detach(device_t self, int flags) { struct ld_virtio_softc *sc = device_private(self); struct ld_softc *ld = &sc->sc_ld; bus_dma_tag_t dmat = sc->sc_virtio->sc_dmat; int r, i, qsize; qsize = sc->sc_vq.vq_num; r = ldbegindetach(ld, flags); if (r != 0) return r; virtio_reset(sc->sc_virtio); virtio_free_vq(sc->sc_virtio, &sc->sc_vq); for (i = 0; i < qsize; i++) { bus_dmamap_destroy(dmat, sc->sc_reqs[i].vr_cmdsts); bus_dmamap_destroy(dmat, sc->sc_reqs[i].vr_payload); } bus_dmamem_unmap(dmat, sc->sc_reqs, sizeof(struct virtio_blk_req) * qsize); bus_dmamem_free(dmat, &sc->sc_reqs_seg, 1); ldenddetach(ld); return 0; }
/* Reset the virtio_bus */ void virtio_bus_reset(VirtioBusState *bus) { DPRINTF("%s: reset device.\n", qbus->name); if (bus->vdev != NULL) { virtio_reset(bus->vdev); } }
static void virtio_ccw_reset(DeviceState *d) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); virtio_reset(dev->vdev); css_reset_sch(dev->sch); }
static int virtio_mmio_reset(struct vmm_emudev *edev) { struct virtio_mmio_dev *m = edev->priv; m->config.interrupt_state = 0x0; vmm_devemu_emulate_irq(m->guest, m->irq, 0); return virtio_reset(&m->dev); }
/* Reset the virtio_bus */ void virtio_bus_reset(VirtioBusState *bus) { VirtIODevice *vdev = virtio_bus_get_device(bus); DPRINTF("%s: reset device.\n", BUS(bus)->name); if (vdev != NULL) { virtio_reset(vdev); } }
static void virtio_ccw_reset(DeviceState *d) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); virtio_ccw_stop_ioeventfd(dev); virtio_reset(dev->vdev); css_reset_sch(dev->sch); dev->indicators = 0; dev->indicators2 = 0; }
int s390_virtio_hypercall(CPUS390XState *env, uint64_t mem, uint64_t hypercall) { int r = 0, i; dprintf("KVM hypercall: %ld\n", hypercall); switch (hypercall) { case KVM_S390_VIRTIO_NOTIFY: if (mem > ram_size) { VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus, mem, &i); if (dev) { virtio_queue_notify(dev->vdev, i); } else { r = -EINVAL; } } else { /* Early printk */ } break; case KVM_S390_VIRTIO_RESET: { VirtIOS390Device *dev; dev = s390_virtio_bus_find_mem(s390_bus, mem); virtio_reset(dev->vdev); stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0); s390_virtio_device_sync(dev); s390_virtio_reset_idx(dev); break; } case KVM_S390_VIRTIO_SET_STATUS: { VirtIOS390Device *dev; dev = s390_virtio_bus_find_mem(s390_bus, mem); if (dev) { s390_virtio_device_update_status(dev); } else { r = -EINVAL; } break; } default: r = -EINVAL; break; } return r; }
int s390_virtio_hypercall(CPUState *env) { int r = 0, i; target_ulong mem = env->regs[2]; dprintf("KVM hypercall: %ld\n", env->regs[1]); switch (env->regs[1]) { case KVM_S390_VIRTIO_NOTIFY: if (mem > ram_size) { VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus, mem, &i); if (dev) { virtio_queue_notify(dev->vdev, i); } else { r = -EINVAL; } } else { /* Early printk */ } break; case KVM_S390_VIRTIO_RESET: { VirtIOS390Device *dev; dev = s390_virtio_bus_find_mem(s390_bus, mem); virtio_reset(dev->vdev); s390_virtio_device_sync(dev); break; } case KVM_S390_VIRTIO_SET_STATUS: { VirtIOS390Device *dev; dev = s390_virtio_bus_find_mem(s390_bus, mem); if (dev) { s390_virtio_device_update_status(dev); } else { r = -EINVAL; } break; } default: r = -EINVAL; break; } env->regs[2] = r; return 0; }
static int s390_virtio_hcall_reset(const uint64_t *args) { uint64_t mem = args[0]; VirtIOS390Device *dev; dev = s390_virtio_bus_find_mem(s390_bus, mem); if (dev == NULL) { return -EINVAL; } virtio_reset(dev->vdev); stb_phys(&address_space_memory, dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0); s390_virtio_device_sync(dev); s390_virtio_reset_idx(dev); return 0; }
static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque; VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); DPRINTF("virtio_mmio_write offset 0x%x value 0x%" PRIx64 "\n", (int)offset, value); if (!vdev) { /* If no backend is present, we just make all registers * write-ignored. This allows us to provide transports with * no backend plugged in. */ return; } if (offset >= VIRTIO_MMIO_CONFIG) { offset -= VIRTIO_MMIO_CONFIG; switch (size) { case 1: virtio_config_writeb(vdev, offset, value); break; case 2: virtio_config_writew(vdev, offset, value); break; case 4: virtio_config_writel(vdev, offset, value); break; default: abort(); } return; } if (size != 4) { DPRINTF("wrong size access to register!\n"); return; } switch (offset) { case VIRTIO_MMIO_HOSTFEATURESSEL: proxy->host_features_sel = value; break; case VIRTIO_MMIO_GUESTFEATURES: if (!proxy->guest_features_sel) { virtio_set_features(vdev, value); } break; case VIRTIO_MMIO_GUESTFEATURESSEL: proxy->guest_features_sel = value; break; case VIRTIO_MMIO_GUESTPAGESIZE: proxy->guest_page_shift = ctz32(value); if (proxy->guest_page_shift > 31) { proxy->guest_page_shift = 0; } DPRINTF("guest page size %" PRIx64 " shift %d\n", value, proxy->guest_page_shift); break; case VIRTIO_MMIO_QUEUESEL: if (value < VIRTIO_QUEUE_MAX) { vdev->queue_sel = value; } break; case VIRTIO_MMIO_QUEUENUM: DPRINTF("mmio_queue write %d max %d\n", (int)value, VIRTQUEUE_MAX_SIZE); virtio_queue_set_num(vdev, vdev->queue_sel, value); /* Note: only call this function for legacy devices */ virtio_queue_update_rings(vdev, vdev->queue_sel); break; case VIRTIO_MMIO_QUEUEALIGN: /* Note: this is only valid for legacy devices */ virtio_queue_set_align(vdev, vdev->queue_sel, value); break; case VIRTIO_MMIO_QUEUEPFN: if (value == 0) { virtio_reset(vdev); } else { virtio_queue_set_addr(vdev, vdev->queue_sel, value << proxy->guest_page_shift); } break; case VIRTIO_MMIO_QUEUENOTIFY: if (value < VIRTIO_QUEUE_MAX) { virtio_queue_notify(vdev, value); } break; case VIRTIO_MMIO_INTERRUPTACK: atomic_and(&vdev->isr, ~value); virtio_update_irq(vdev); break; case VIRTIO_MMIO_STATUS: if (!(value & VIRTIO_CONFIG_S_DRIVER_OK)) { virtio_mmio_stop_ioeventfd(proxy); } virtio_set_status(vdev, value & 0xff); if (value & VIRTIO_CONFIG_S_DRIVER_OK) { virtio_mmio_start_ioeventfd(proxy); } if (vdev->status == 0) { virtio_reset(vdev); } break; case VIRTIO_MMIO_MAGIC: case VIRTIO_MMIO_VERSION: case VIRTIO_MMIO_DEVICEID: case VIRTIO_MMIO_VENDORID: case VIRTIO_MMIO_HOSTFEATURES: case VIRTIO_MMIO_QUEUENUMMAX: case VIRTIO_MMIO_INTERRUPTSTATUS: DPRINTF("write to readonly register\n"); break; default: DPRINTF("bad register offset\n"); } }
static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) { int ret; VqInfoBlock info; uint8_t status; VirtioFeatDesc features; void *config; hwaddr indicators; VqConfigBlock vq_config; VirtioCcwDevice *dev = sch->driver_data; bool check_len; int len; hwaddr hw_len; if (!dev) { return -EINVAL; } trace_virtio_ccw_interpret_ccw(sch->cssid, sch->ssid, sch->schid, ccw.cmd_code); check_len = !((ccw.flags & CCW_FLAG_SLI) && !(ccw.flags & CCW_FLAG_DC)); /* Look at the command. */ switch (ccw.cmd_code) { case CCW_CMD_SET_VQ: if (check_len) { if (ccw.count != sizeof(info)) { ret = -EINVAL; break; } } else if (ccw.count < sizeof(info)) { /* Can't execute command. */ ret = -EINVAL; break; } if (!ccw.cda) { ret = -EFAULT; } else { info.queue = ldq_phys(ccw.cda); info.align = ldl_phys(ccw.cda + sizeof(info.queue)); info.index = lduw_phys(ccw.cda + sizeof(info.queue) + sizeof(info.align)); info.num = lduw_phys(ccw.cda + sizeof(info.queue) + sizeof(info.align) + sizeof(info.index)); ret = virtio_ccw_set_vqs(sch, info.queue, info.align, info.index, info.num); sch->curr_status.scsw.count = 0; } break; case CCW_CMD_VDEV_RESET: virtio_reset(dev->vdev); ret = 0; break; case CCW_CMD_READ_FEAT: if (check_len) { if (ccw.count != sizeof(features)) { ret = -EINVAL; break; } } else if (ccw.count < sizeof(features)) { /* Can't execute command. */ ret = -EINVAL; break; } if (!ccw.cda) { ret = -EFAULT; } else { features.index = ldub_phys(ccw.cda + sizeof(features.features)); if (features.index < ARRAY_SIZE(dev->host_features)) { features.features = dev->host_features[features.index]; } else { /* Return zeroes if the guest supports more feature bits. */ features.features = 0; } stl_le_phys(ccw.cda, features.features); sch->curr_status.scsw.count = ccw.count - sizeof(features); ret = 0; } break; case CCW_CMD_WRITE_FEAT: if (check_len) { if (ccw.count != sizeof(features)) { ret = -EINVAL; break; } } else if (ccw.count < sizeof(features)) { /* Can't execute command. */ ret = -EINVAL; break; } if (!ccw.cda) { ret = -EFAULT; } else { features.index = ldub_phys(ccw.cda + sizeof(features.features)); features.features = ldl_le_phys(ccw.cda); if (features.index < ARRAY_SIZE(dev->host_features)) { if (dev->vdev->set_features) { dev->vdev->set_features(dev->vdev, features.features); } dev->vdev->guest_features = features.features; } else { /* * If the guest supports more feature bits, assert that it * passes us zeroes for those we don't support. */ if (features.features) { fprintf(stderr, "Guest bug: features[%i]=%x (expected 0)\n", features.index, features.features); /* XXX: do a unit check here? */ } } sch->curr_status.scsw.count = ccw.count - sizeof(features); ret = 0; } break; case CCW_CMD_READ_CONF: if (check_len) { if (ccw.count > dev->vdev->config_len) { ret = -EINVAL; break; } } len = MIN(ccw.count, dev->vdev->config_len); if (!ccw.cda) { ret = -EFAULT; } else { dev->vdev->get_config(dev->vdev, dev->vdev->config); /* XXX config space endianness */ cpu_physical_memory_write(ccw.cda, dev->vdev->config, len); sch->curr_status.scsw.count = ccw.count - len; ret = 0; } break; case CCW_CMD_WRITE_CONF: if (check_len) { if (ccw.count > dev->vdev->config_len) { ret = -EINVAL; break; } } len = MIN(ccw.count, dev->vdev->config_len); hw_len = len; if (!ccw.cda) { ret = -EFAULT; } else { config = cpu_physical_memory_map(ccw.cda, &hw_len, 0); if (!config) { ret = -EFAULT; } else { len = hw_len; /* XXX config space endianness */ memcpy(dev->vdev->config, config, len); cpu_physical_memory_unmap(config, hw_len, 0, hw_len); if (dev->vdev->set_config) { dev->vdev->set_config(dev->vdev, dev->vdev->config); } sch->curr_status.scsw.count = ccw.count - len; ret = 0; } } break; case CCW_CMD_WRITE_STATUS: if (check_len) { if (ccw.count != sizeof(status)) { ret = -EINVAL; break; } } else if (ccw.count < sizeof(status)) { /* Can't execute command. */ ret = -EINVAL; break; } if (!ccw.cda) { ret = -EFAULT; } else { status = ldub_phys(ccw.cda); virtio_set_status(dev->vdev, status); if (dev->vdev->status == 0) { virtio_reset(dev->vdev); } sch->curr_status.scsw.count = ccw.count - sizeof(status); ret = 0; } break; case CCW_CMD_SET_IND: if (check_len) { if (ccw.count != sizeof(indicators)) { ret = -EINVAL; break; } } else if (ccw.count < sizeof(indicators)) { /* Can't execute command. */ ret = -EINVAL; break; } indicators = ldq_phys(ccw.cda); if (!indicators) { ret = -EFAULT; } else { dev->indicators = indicators; sch->curr_status.scsw.count = ccw.count - sizeof(indicators); ret = 0; } break; case CCW_CMD_SET_CONF_IND: if (check_len) { if (ccw.count != sizeof(indicators)) { ret = -EINVAL; break; } } else if (ccw.count < sizeof(indicators)) { /* Can't execute command. */ ret = -EINVAL; break; } indicators = ldq_phys(ccw.cda); if (!indicators) { ret = -EFAULT; } else { dev->indicators2 = indicators; sch->curr_status.scsw.count = ccw.count - sizeof(indicators); ret = 0; } break; case CCW_CMD_READ_VQ_CONF: if (check_len) { if (ccw.count != sizeof(vq_config)) { ret = -EINVAL; break; } } else if (ccw.count < sizeof(vq_config)) { /* Can't execute command. */ ret = -EINVAL; break; } if (!ccw.cda) { ret = -EFAULT; } else { vq_config.index = lduw_phys(ccw.cda); vq_config.num_max = virtio_queue_get_num(dev->vdev, vq_config.index); stw_phys(ccw.cda + sizeof(vq_config.index), vq_config.num_max); sch->curr_status.scsw.count = ccw.count - sizeof(vq_config); ret = 0; } break; default: ret = -ENOSYS; break; } return ret; }