Exemplo n.º 1
0
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);
}
Exemplo n.º 2
0
/* 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);
}
Exemplo n.º 3
0
/* 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);
}
Exemplo n.º 4
0
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;
}
Exemplo n.º 5
0
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 = &alpha;
            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));
}