static void bcm2835_mbox_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { BCM2835MboxState *s = opaque; hwaddr childaddr; uint8_t ch; offset &= 0xff; switch (offset) { case MAIL0_SENDER: break; case MAIL0_CONFIG: s->mbox[0].config &= ~ARM_MC_IHAVEDATAIRQEN; s->mbox[0].config |= value & ARM_MC_IHAVEDATAIRQEN; break; case 0xa0 ... 0xac: /* MAIL1_WRITE */ if (s->mbox[1].status & ARM_MS_FULL) { /* Mailbox full */ qemu_log_mask(LOG_GUEST_ERROR, "%s: mailbox full\n", __func__); } else { ch = value & 0xf; if (ch < MBOX_CHAN_COUNT) { childaddr = ch << MBOX_AS_CHAN_SHIFT; if (ldl_le_phys(&s->mbox_as, childaddr + MBOX_AS_PENDING)) { /* Child busy, push delayed. Push it in the arm->vc mbox */ mbox_push(&s->mbox[1], value); } else { /* Push it directly to the child device */ stl_le_phys(&s->mbox_as, childaddr, value); } } else { /* Invalid channel number */ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid channel %u\n", __func__, ch); } } break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", __func__, offset); return; } bcm2835_mbox_update(s); }
/* Send an MSI-X message */ void msix_notify(PCIDevice *dev, unsigned vector) { MSIMessage msg; //fprintf(stdout, "qemu:msix: msix_notify \n"); if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector]) return; if (msix_is_masked(dev, vector)) { msix_set_pending(dev, vector); return; } msg = msix_get_message(dev, vector); stl_le_phys(msg.address, msg.data); }
/* Send an MSI-X message */ void msix_notify(PCIDevice *dev, unsigned vector) { uint8_t *table_entry = dev->msix_table_page + vector * PCI_MSIX_ENTRY_SIZE; uint64_t address; uint32_t data; if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector]) return; if (msix_is_masked(dev, vector)) { msix_set_pending(dev, vector); return; } address = pci_get_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR); data = pci_get_long(table_entry + PCI_MSIX_ENTRY_DATA); stl_le_phys(address, data); }
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; }
static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) { uint32_t tag; uint32_t bufsize; uint32_t tot_len; size_t resplen; uint32_t tmp; int n; uint32_t offset, length, color; uint32_t xres, yres, xoffset, yoffset, bpp, pixo, alpha; uint32_t *newxres = NULL, *newyres = NULL, *newxoffset = NULL, *newyoffset = NULL, *newbpp = NULL, *newpixo = NULL, *newalpha = NULL; value &= ~0xf; s->addr = value; tot_len = ldl_le_phys(&s->dma_as, value); /* @(addr + 4) : Buffer response code */ value = s->addr + 8; while (value + 8 <= s->addr + tot_len) { tag = ldl_le_phys(&s->dma_as, value); bufsize = ldl_le_phys(&s->dma_as, value + 4); /* @(value + 8) : Request/response indicator */ resplen = 0; switch (tag) { case 0x00000000: /* End tag */ break; case 0x00000001: /* Get firmware revision */ stl_le_phys(&s->dma_as, value + 12, 346337); resplen = 4; break; case 0x00010001: /* Get board model */ qemu_log_mask(LOG_UNIMP, "bcm2835_property: %x get board model NYI\n", tag); resplen = 4; break; case 0x00010002: /* Get board revision */ stl_le_phys(&s->dma_as, value + 12, s->board_rev); resplen = 4; break; case 0x00010003: /* Get board MAC address */ resplen = sizeof(s->macaddr.a); dma_memory_write(&s->dma_as, value + 12, s->macaddr.a, resplen); break; case 0x00010004: /* Get board serial */ qemu_log_mask(LOG_UNIMP, "bcm2835_property: %x get board serial NYI\n", tag); resplen = 8; break; case 0x00010005: /* Get ARM memory */ /* base */ stl_le_phys(&s->dma_as, value + 12, 0); /* size */ stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_base); resplen = 8; break; case 0x00010006: /* Get VC memory */ /* base */ stl_le_phys(&s->dma_as, value + 12, s->fbdev->vcram_base); /* size */ stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_size); resplen = 8; break; case 0x00028001: /* Set power state */ /* Assume that whatever device they asked for exists, * and we'll just claim we set it to the desired state */ tmp = ldl_le_phys(&s->dma_as, value + 16); stl_le_phys(&s->dma_as, value + 16, (tmp & 1)); resplen = 8; break; /* Clocks */ case 0x00030001: /* Get clock state */ stl_le_phys(&s->dma_as, value + 16, 0x1); resplen = 8; break; case 0x00038001: /* Set clock state */ qemu_log_mask(LOG_UNIMP, "bcm2835_property: %x set clock state NYI\n", tag); resplen = 8; break; case 0x00030002: /* Get clock rate */ case 0x00030004: /* Get max clock rate */ case 0x00030007: /* Get min clock rate */ switch (ldl_le_phys(&s->dma_as, value + 12)) { case 1: /* EMMC */ stl_le_phys(&s->dma_as, value + 16, 50000000); break; case 2: /* UART */ stl_le_phys(&s->dma_as, value + 16, 3000000); break; default: stl_le_phys(&s->dma_as, value + 16, 700000000); break; } resplen = 8; break; case 0x00038002: /* Set clock rate */ case 0x00038004: /* Set max clock rate */ case 0x00038007: /* Set min clock rate */ qemu_log_mask(LOG_UNIMP, "bcm2835_property: %x set clock rates NYI\n", tag); resplen = 8; break; /* Temperature */ case 0x00030006: /* Get temperature */ stl_le_phys(&s->dma_as, value + 16, 25000); resplen = 8; break; case 0x0003000A: /* Get max temperature */ stl_le_phys(&s->dma_as, value + 16, 99000); resplen = 8; break; /* Frame buffer */ case 0x00040001: /* Allocate buffer */ stl_le_phys(&s->dma_as, value + 12, s->fbdev->base); stl_le_phys(&s->dma_as, value + 16, s->fbdev->size); resplen = 8; break; case 0x00048001: /* Release buffer */ resplen = 0; break; case 0x00040002: /* Blank screen */ resplen = 4; break; case 0x00040003: /* Get display width/height */ case 0x00040004: stl_le_phys(&s->dma_as, value + 12, s->fbdev->xres); stl_le_phys(&s->dma_as, value + 16, s->fbdev->yres); resplen = 8; break; case 0x00044003: /* Test display width/height */ case 0x00044004: resplen = 8; break; case 0x00048003: /* Set display width/height */ case 0x00048004: xres = ldl_le_phys(&s->dma_as, value + 12); newxres = &xres; yres = ldl_le_phys(&s->dma_as, value + 16); newyres = &yres; resplen = 8; break; case 0x00040005: /* Get depth */ stl_le_phys(&s->dma_as, value + 12, s->fbdev->bpp); resplen = 4; break; case 0x00044005: /* Test depth */ resplen = 4; break; case 0x00048005: /* Set depth */ bpp = ldl_le_phys(&s->dma_as, value + 12); newbpp = &bpp; resplen = 4; break; case 0x00040006: /* Get pixel order */ stl_le_phys(&s->dma_as, value + 12, s->fbdev->pixo); resplen = 4; break; case 0x00044006: /* Test pixel order */ resplen = 4; break; case 0x00048006: /* Set pixel order */ pixo = ldl_le_phys(&s->dma_as, value + 12); newpixo = &pixo; resplen = 4; break; case 0x00040007: /* Get alpha */ stl_le_phys(&s->dma_as, value + 12, s->fbdev->alpha); resplen = 4; break; case 0x00044007: /* Test pixel alpha */ resplen = 4; break; case 0x00048007: /* Set alpha */ alpha = ldl_le_phys(&s->dma_as, value + 12); newalpha = α resplen = 4; break; case 0x00040008: /* Get pitch */ stl_le_phys(&s->dma_as, value + 12, s->fbdev->pitch); resplen = 4; break; case 0x00040009: /* Get virtual offset */ stl_le_phys(&s->dma_as, value + 12, s->fbdev->xoffset); stl_le_phys(&s->dma_as, value + 16, s->fbdev->yoffset); resplen = 8; break; case 0x00044009: /* Test virtual offset */ resplen = 8; break; case 0x00048009: /* Set virtual offset */ xoffset = ldl_le_phys(&s->dma_as, value + 12); newxoffset = &xoffset; yoffset = ldl_le_phys(&s->dma_as, value + 16); newyoffset = &yoffset; resplen = 8; break; case 0x0004000a: /* Get/Test/Set overscan */ case 0x0004400a: case 0x0004800a: stl_le_phys(&s->dma_as, value + 12, 0); stl_le_phys(&s->dma_as, value + 16, 0); stl_le_phys(&s->dma_as, value + 20, 0); stl_le_phys(&s->dma_as, value + 24, 0); resplen = 16; break; case 0x0004800b: /* Set palette */ offset = ldl_le_phys(&s->dma_as, value + 12); length = ldl_le_phys(&s->dma_as, value + 16); n = 0; while (n < length - offset) { color = ldl_le_phys(&s->dma_as, value + 20 + (n << 2)); stl_le_phys(&s->dma_as, s->fbdev->vcram_base + ((offset + n) << 2), color); n++; } stl_le_phys(&s->dma_as, value + 12, 0); resplen = 4; break; case 0x00060001: /* Get DMA channels */ /* channels 2-5 */ stl_le_phys(&s->dma_as, value + 12, 0x003C); resplen = 4; break; case 0x00050001: /* Get command line */ resplen = 0; break; default: qemu_log_mask(LOG_GUEST_ERROR, "bcm2835_property: unhandled tag %08x\n", tag); break; } if (tag == 0) { break; } stl_le_phys(&s->dma_as, value + 8, (1 << 31) | resplen); value += bufsize + 12; } /* Reconfigure framebuffer if required */ if (newxres || newyres || newxoffset || newyoffset || newbpp || newpixo || newalpha) { bcm2835_fb_reconfigure(s->fbdev, newxres, newyres, newxoffset, newyoffset, newbpp, newpixo, newalpha); } /* Buffer response code */ stl_le_phys(&s->dma_as, s->addr + 4, (1 << 31)); }