@@ -21,7 +21,7 @@ obj-y += virtex_ml507.o
# PowerPC OpenPIC
obj-y += openpic.o
obj-$(CONFIG_FDT) += ../device_tree.o
-
+obj-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o qxl-pci.o qxl-virtio.o
# Xilinx PPC peripherals
obj-y += xilinx_ethlite.o
@@ -1253,6 +1253,7 @@ static int qxl_init_common(QXLDevice *qxl)
qemu_mutex_init(&qxl->async_lock);
qxl->current_async = QXL_UNDEFINED_IO;
qxl->pci.guest_bug = 0;
+ qxl->transport = QXL_TRANSPORT_PCI;
switch (qxl->pci.revision) {
case 1: /* spice 0.4 -- qxl-1 */
@@ -24,15 +24,19 @@
static void qxl_blit(QXLDevice *qxl, QXLRect *rect)
{
uint8_t *src;
- uint8_t *dst = qxl->pci.vga.ds->surface->data;
+ uint8_t *dst = (qxl->transport == QXL_TRANSPORT_PCI) ?
+ qxl->pci.vga.ds->surface->data : qxl->ssd.ds->surface->data;
int len, i;
- if (is_buffer_shared(qxl->pci.vga.ds->surface)) {
+ if (qxl->transport == QXL_TRANSPORT_PCI &&
+ is_buffer_shared(qxl->pci.vga.ds->surface)) {
return;
}
if (!qxl->guest_primary.data) {
trace_qxl_render_blit_guest_primary_initialized();
- qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->pci.vga.vram);
+ qxl->guest_primary.data = (qxl->transport == QXL_TRANSPORT_PCI) ?
+ memory_region_get_ram_ptr(&qxl->pci.vga.vram) :
+ qxl->virtio.ram_ptr;
}
trace_qxl_render_blit(qxl->guest_primary.qxl_stride,
rect->left, rect->right, rect->top, rect->bottom);
@@ -97,14 +101,15 @@ static void qxl_set_rect_to_surface(QXLDevice *qxl, QXLRect *area)
static void qxl_render_update_area_unlocked(QXLDevice *qxl)
{
- VGACommonState *vga = &qxl->pci.vga;
int i;
- DisplaySurface *surface = vga->ds->surface;
+ DisplayState *ds = (qxl->transport == QXL_TRANSPORT_PCI) ?
+ qxl->pci.vga.ds : qxl->ssd.ds;
+ DisplaySurface *surface = ds->surface;
if (qxl->guest_primary.resized) {
qxl->guest_primary.resized = 0;
- qxl->guest_primary.data =
- memory_region_get_ram_ptr(&qxl->pci.vga.vram);
+ qxl->guest_primary.data = (qxl->transport == QXL_TRANSPORT_PCI) ?
+ memory_region_get_ram_ptr(&qxl->pci.vga.vram) : qxl->virtio.ram_ptr;
qxl_set_rect_to_surface(qxl, &qxl->dirty[0]);
qxl->num_dirty_rects = 1;
trace_qxl_render_guest_primary_resized(
@@ -117,25 +122,25 @@ static void qxl_render_update_area_unlocked(QXLDevice *qxl)
if (surface->width != qxl->guest_primary.surface.width ||
surface->height != qxl->guest_primary.surface.height) {
if (qxl->guest_primary.qxl_stride > 0) {
- qemu_free_displaysurface(vga->ds);
+ qemu_free_displaysurface(ds);
qemu_create_displaysurface_from(qxl->guest_primary.surface.width,
qxl->guest_primary.surface.height,
qxl->guest_primary.bits_pp,
qxl->guest_primary.abs_stride,
qxl->guest_primary.data);
} else {
- qemu_resize_displaysurface(vga->ds,
+ qemu_resize_displaysurface(ds,
qxl->guest_primary.surface.width,
qxl->guest_primary.surface.height);
}
- dpy_resize(vga->ds);
+ dpy_resize(ds);
}
for (i = 0; i < qxl->num_dirty_rects; i++) {
if (qemu_spice_rect_is_empty(qxl->dirty+i)) {
break;
}
qxl_blit(qxl, qxl->dirty+i);
- dpy_update(vga->ds,
+ dpy_update(ds,
qxl->dirty[i].left, qxl->dirty[i].top,
qxl->dirty[i].right - qxl->dirty[i].left,
qxl->dirty[i].bottom - qxl->dirty[i].top);
@@ -155,7 +160,7 @@ void qxl_render_update(QXLDevice *qxl)
qemu_mutex_lock(&qxl->ssd.lock);
- if (!runstate_is_running() || !qxl->guest_primary.commands) {
+ if (qxl->transport == QXL_TRANSPORT_PCI && ((!runstate_is_running() || !qxl->guest_primary.commands))) {
qxl_render_update_area_unlocked(qxl);
qemu_mutex_unlock(&qxl->ssd.lock);
return;
@@ -231,7 +236,9 @@ fail:
/* called from spice server thread context only */
int qxl_render_cursor(QXLDevice *qxl, QXLCommandExt *ext)
{
- QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
+ QXLCursorCmd *cmd = (qxl->transport == QXL_TRANSPORT_PCI) ?
+ qxl_phys2virt(qxl, ext->cmd.data, ext->group_id) :
+ (QXLCursorCmd *)ext->cmd.data;
QXLCursor *cursor;
QEMUCursor *c;
@@ -250,7 +257,9 @@ int qxl_render_cursor(QXLDevice *qxl, QXLCommandExt *ext)
}
switch (cmd->type) {
case QXL_CURSOR_SET:
- cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id);
+ cursor = (qxl->transport == QXL_TRANSPORT_PCI) ?
+ qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id) :
+ (QXLCursor *)cmd->u.set.shape;
if (!cursor) {
return 1;
}
@@ -23,21 +23,599 @@
*/
#include "pci.h"
-#include "virtio.h"
+#include "qxl.h"
+#include "qxl-virtio.h"
#include "virtio-pci.h"
+extern uint64_t virtio_qxl_ram;
+static QXLDevice *qxl0;
+
+typedef struct QXLBridgeReq
+{
+ QXLDevice *dev;
+ VirtQueueElement elem;
+ struct vbr_proto_hdr *hdr;
+ void *payload;
+ uint8_t *status;
+ QEMUIOVector qiov;
+} QXLBridgeReq;
+
+QXLDevMemSlot slot = {
+ .slot_group_id = 0,
+ .slot_id = 0,
+ .generation = 0,
+ .virt_start = 0,
+ .virt_end = ~0,
+ .addr_delta = 0,
+ .qxl_ram_size = ~0,
+};
+
+void *offset_to_virt(QXLDevice *qxl, QXLPHYSICAL pqxl, int group_id)
+{
+ void *addr;
+ int mappable_len = qxl->virtio.config.ramsize +
+ qxl->virtio.config.vramsize +
+ qxl->virtio.config.romsize;
+
+ if (pqxl < mappable_len) {
+ addr = (void *)(qxl->virtio.ram_ptr + pqxl);
+ return addr;
+ } else {
+ dprint(qxl, 1, "Error mapping offset %llu\n",
+ (long long unsigned) pqxl);
+ /* For some reason some offsets are tried to be remapped twice.
+ * When this happens we fall here and should just return the offset
+ * as it were (i.e, already mapped)
+ */
+ return (void *)pqxl;
+ }
+}
+
+/*******************************************************************************
+ * Spice Display Interface callbacks
+ ******************************************************************************/
+/* called from spice server thread context only */
+static void qxl_map_command(QXLDevice *qxl, struct QXLCommandExt *ext)
+{
+ switch (ext->cmd.type) {
+ case QXL_CMD_SURFACE:
+ {
+ QXLSurfaceCmd *cmd = offset_to_virt(qxl,
+ ext->cmd.data,
+ ext->group_id);
+ char *hostaddr;
+
+ ext->cmd.data = (QXLPHYSICAL)cmd;
+
+ if (cmd->type == QXL_SURFACE_CMD_DESTROY) {
+ break;
+ }
+
+ hostaddr = offset_to_virt(qxl,
+ cmd->u.surface_create.data,
+ ext->group_id);
+ cmd->u.surface_create.data = (QXLPHYSICAL)hostaddr;
+ break;
+ }
+ case QXL_CMD_CURSOR:
+ {
+ QXLCursorCmd *cmd = offset_to_virt(qxl,
+ ext->cmd.data,
+ ext->group_id);
+ char *hostaddr;
+
+ ext->cmd.data = (QXLPHYSICAL)cmd;
+
+ if (cmd->type != QXL_CURSOR_SET) {
+ break;
+ }
+
+ hostaddr = offset_to_virt(qxl, cmd->u.set.shape, ext->group_id);
+ cmd->u.set.shape = (QXLPHYSICAL)hostaddr;
+ break;
+ }
+ case QXL_CMD_DRAW:
+ {
+ struct QXLDrawable *draw = offset_to_virt(qxl,
+ ext->cmd.data,
+ ext->group_id);
+ char *hostaddr;
+ QXLPHYSICAL addr;
+ QXLImage *image;
+ QXLDataChunk *chunk, *prev;
+
+ ext->cmd.data = (QXLPHYSICAL)draw;
+
+ if (draw->type != QXL_DRAW_COPY) {
+ break;
+ }
+
+ hostaddr = offset_to_virt(qxl,
+ draw->u.copy.src_bitmap,
+ ext->group_id);
+ draw->u.copy.src_bitmap = (QXLPHYSICAL)hostaddr;
+ image = (void *)draw->u.copy.src_bitmap;
+
+ if (image->descriptor.type == SPICE_IMAGE_TYPE_SURFACE) {
+ break;
+ }
+
+ hostaddr = offset_to_virt(qxl,
+ image->bitmap.data,
+ ext->group_id);
+ image->bitmap.data = (QXLPHYSICAL)hostaddr;
+
+ if (image->bitmap.flags & QXL_BITMAP_DIRECT) {
+ break;
+ }
+
+ addr = image->bitmap.data;
+ prev = 0;
+ while (addr) {
+ chunk = offset_to_virt(qxl, addr, ext->group_id);
+ if (prev) {
+ prev->next_chunk = (QXLPHYSICAL)chunk;
+ }
+ chunk->prev_chunk = (QXLPHYSICAL)prev;
+ prev = chunk;
+ addr = chunk->next_chunk;
+ }
+ prev->next_chunk = 0;
+ break;
+ }
+ }
+}
+
+QXLInterface qxl_interface = {
+ .base.type = SPICE_INTERFACE_QXL,
+ .base.description = "virtio qxl gpu",
+ .base.major_version = SPICE_INTERFACE_QXL_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_QXL_MINOR,
+
+ .attache_worker = interface_attach_worker,
+ .set_compression_level = interface_set_compression_level,
+ .set_mm_time = interface_set_mm_time,
+ .get_init_info = interface_get_init_info,
+
+ /* the callbacks below are called from spice server thead context */
+ .get_command = interface_get_command,
+ .req_cmd_notification = interface_req_cmd_notification,
+ .release_resource = interface_release_resource,
+ .get_cursor_command = interface_get_cursor_command,
+ .req_cursor_notification = interface_req_cursor_notification,
+ .notify_update = interface_notify_update,
+ .flush_resources = interface_flush_resources,
+ .async_complete = interface_async_complete,
+ .update_area_complete = interface_update_area_complete,
+};
+
+static void setup_virtioqxl_mem(QXLDevice *qxl)
+{
+
+ struct virtioqxl_config *cfg;
+ int total_size;
+
+ cfg = &qxl->virtio.config;
+ cfg->configsize = sizeof(*cfg);
+ cfg->ramsize = qxl->vram_size;
+ cfg->vramsize = qxl->vram_size;
+ cfg->romsize = qxl_rom_size();
+ total_size = qxl->virtio.config.ramsize +
+ qxl->virtio.config.vramsize +
+ qxl->virtio.config.romsize;
+
+ qxl->virtio.ram_ptr = g_malloc(total_size);
+ memset(qxl->virtio.ram_ptr,0x0,total_size);
+ qxl->virtio.vram_ptr = qxl->virtio.ram_ptr + qxl->virtio.config.ramsize;
+ qxl->virtio.rom_ptr = qxl->virtio.ram_ptr +
+ qxl->virtio.config.ramsize +
+ qxl->virtio.config.vramsize;
+
+ init_qxl_rom(qxl);
+ init_qxl_ram(qxl);
+}
+
+static void create_primary_surface(QXLDevice *qxl)
+{
+ QXLDevSurfaceCreate surface = { 0, };
+ QXLSurfaceCreate *sc;
+
+ if (qxl->virtio.primary_created) {
+ return;
+ }
+
+ qxl->virtio.primary_created = 1;
+ qxl->guest_primary.surface = qxl->ram->create_surface;
+ sc = &qxl->guest_primary.surface;
+
+ surface.width = sc->width = le32_to_cpu(sc->width);
+ surface.height = sc->height = le32_to_cpu(sc->height);
+ surface.stride = sc->stride = le32_to_cpu(sc->stride);
+ surface.format = sc->format = le32_to_cpu(sc->format);
+ surface.position = sc->position = le32_to_cpu(sc->position);
+ surface.mouse_mode = true;
+ surface.flags = sc->flags = le32_to_cpu(sc->flags);
+ surface.type = sc->type = le32_to_cpu(sc->type);
+ sc->mem = le64_to_cpu(sc->mem);
+ surface.mem = (uint64_t)offset_to_virt(qxl,sc->mem,0);
+ surface.group_id = MEMSLOT_GROUP_HOST;
+
+ qxl->ssd.worker->create_primary_surface(qxl->ssd.worker, 0, &surface);
+ qxl_render_resize(qxl);
+}
+
+static void ioport_write(QXLDevice *qxl,
+ target_phys_addr_t addr,
+ uint64_t val,
+ unsigned size)
+{
+
+ uint32_t io_port = addr;
+
+ switch (io_port) {
+ case QXL_IO_RESET:
+ qxl->ssd.worker->reset_cursor(qxl->ssd.worker);
+ qxl->ssd.worker->reset_image_cache(qxl->ssd.worker);
+ qxl->ssd.worker->destroy_surfaces(qxl->ssd.worker);
+ qxl->ssd.worker->reset_memslots(qxl->ssd.worker);
+ qxl->ssd.worker->add_memslot(qxl->ssd.worker, &slot);
+ init_qxl_ram(qxl);
+ qxl->virtio.primary_created = 0;
+ break;
+ case QXL_IO_MEMSLOT_ADD:
+ case QXL_IO_MEMSLOT_DEL:
+ break;
+ case QXL_IO_CREATE_PRIMARY:
+ create_primary_surface(qxl);
+ break;
+ case QXL_IO_UPDATE_AREA:
+ {
+ if (!qxl->virtio.primary_created) {
+ return;
+ }
+ QXLRect rect = qxl->ram->update_area;
+ rect.top= le32_to_cpu(qxl->ram->update_area.top);
+ rect.bottom = le32_to_cpu(qxl->ram->update_area.bottom);
+ rect.left = le32_to_cpu(qxl->ram->update_area.left);
+ rect.right = le32_to_cpu(qxl->ram->update_area.right);
+ uint32_t sid = le32_to_cpu(qxl->ram->update_surface);
+
+ qxl->ssd.worker->update_area(qxl->ssd.worker, sid, &rect, NULL,
+ 0, 1);
+ break;
+
+ }
+ case QXL_IO_NOTIFY_CURSOR:
+ case QXL_IO_NOTIFY_CMD:
+ qxl->ssd.worker->wakeup(qxl->ssd.worker);
+ break;
+ case QXL_IO_NOTIFY_OOM:
+ qxl->ssd.worker->oom(qxl->ssd.worker);
+ break;
+ default:
+ fprintf(stderr,"Not handled IO port %d\n",io_port);
+ break;
+ }
+}
+
+/*******************************************************************************
+ * Virtio Transport Stuff
+ ******************************************************************************/
+static inline QXLDevice *to_virtio_qxl(VirtIODevice *vdev)
+{
+ return (QXLDevice *)vdev;
+}
+
+static void virtio_qxl_update_config(VirtIODevice *vdev, uint8_t *config)
+{
+}
+
+static uint32_t virtio_qxl_get_features(VirtIODevice *vdev,
+ uint32_t requested_features)
+{
+ return requested_features;
+}
+
+static void virtio_qxl_reset(VirtIODevice *vdev)
+{
+}
+
+static QXLBridgeReq *qxl_alloc_request(QXLDevice *s)
+{
+ QXLBridgeReq *req = g_malloc(sizeof(*req));
+ req->dev = s;
+ req->qiov.size = 0;
+ return req;
+}
+
+static QXLBridgeReq *qxl_get_request(QXLDevice *s)
+{
+ QXLBridgeReq *req = qxl_alloc_request(s);
+
+ if (req != NULL) {
+ if (!virtqueue_pop(s->virtio.vq, &req->elem)) {
+ g_free(req);
+ return NULL;
+ }
+ }
+
+ return req;
+}
+
+static void do_qxl_request(QXLBridgeReq *req)
+{
+ int i;
+ QXLDevice *qxl = req->dev;
+
+ req->hdr = req->elem.out_sg[0].iov_base;
+ req->status = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base;
+
+ switch (req->hdr->function) {
+ case VIRTIOQXL_GETCFG:
+ {
+ struct virtioqxl_config *cfg;
+ cfg = req->payload = req->elem.in_sg[0].iov_base;
+
+ qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1],
+ req->elem.out_num - 1);
+
+ cfg->configsize = sizeof(*cfg);
+ cfg->romsize = qxl->virtio.config.romsize;
+ cfg->vramsize = qxl->virtio.config.vramsize;
+ cfg->ramsize = qxl->virtio.config.ramsize;
+ break;
+ }
+ case VIRTIOQXL_GET_RAM:
+ {
+ int data_len = 0;
+ uint8_t *start, *p;
+ VirtQueueElement *e = &req->elem;
+
+ p = start = qxl->virtio.ram_ptr + req->hdr->param;
+
+ for (i = 0; i < (e->in_num - 1); i++) {
+ memcpy(e->in_sg[i].iov_base, p, e->in_sg[i].iov_len);
+ p += e->in_sg[i].iov_len;
+ data_len += e->in_sg[i].iov_len;
+ }
+ break;
+ }
+ case VIRTIOQXL_SET_RAM:
+ {
+ int data_len = 0;
+ uint8_t *start, *p;
+ VirtQueueElement *e = &req->elem;
+
+ p = start = qxl->virtio.ram_ptr + req->hdr->param;
+
+ for (i = 1; i < e->out_num; i++) {
+ memcpy(p, e->out_sg[i].iov_base, e->out_sg[i].iov_len);
+ p += e->out_sg[i].iov_len;
+ data_len += e->out_sg[i].iov_len;
+ }
+ break;
+ }
+ case VIRTIOQXL_GET_ROM:
+ {
+ int data_len = 0;
+ uint8_t *p = qxl->virtio.rom_ptr;
+ VirtQueueElement *e = &req->elem;
+
+ for (i = 0; i < (e->in_num - 1); i++) {
+ memcpy(e->in_sg[i].iov_base, p, e->in_sg[i].iov_len);
+ p += e->in_sg[i].iov_len;
+ data_len += e->in_sg[i].iov_len;
+ }
+ break;
+ }
+ case VIRTIOQXL_IOPORT_WRITE:
+ {
+ struct iowrite_cmd *cmd;
+ cmd = req->payload = req->elem.out_sg[1].iov_base;
+
+ ioport_write(qxl, cmd->port, cmd->arg, 0);
+ break;
+ }
+ default:
+ break;
+ }
+ req->status = VIRTIOQXL_STATUS_DONE;
+}
+
+static void complete_qxl_request(QXLBridgeReq *req)
+{
+ QXLDevice *s = req->dev;
+
+ virtqueue_push(s->virtio.vq, &req->elem, req->qiov.size);
+ virtio_notify(&s->virtio.vdev, s->virtio.vq);
+}
+
+static void virtio_qxl_handle_request(VirtIODevice *vdev, VirtQueue *vq)
+{
+ QXLBridgeReq *req;
+ QXLDevice *s = to_virtio_qxl(vdev);
+
+ while ((req = qxl_get_request(s))) {
+ do_qxl_request(req);
+ complete_qxl_request(req);
+ g_free(req);
+ }
+}
+
+
+
+int interface_get_command_virtio(QXLDevice *qxl, struct QXLCommandExt *ext)
+{
+ QXLCommandRing *ring;
+ QXLCommand *cmd;
+ int notify ATTRIBUTE_UNUSED;
+
+ /*
+ * before a consumer can use a command, a primary surface must be created
+ */
+ if (!qxl->virtio.primary_created) {
+ return false;
+ }
+
+ ring = &qxl->ram->cmd_ring;
+
+ if (SPICE_RING_IS_EMPTY(ring)) {
+ return false;
+ }
+
+ SPICE_RING_CONS_ITEM(qxl, ring, cmd);
+ ext->cmd = *cmd;
+ ext->group_id = 0;
+ ext->flags = 0;
+ SPICE_RING_POP(ring, notify);
+ qxl_map_command(qxl, ext);
+ qxl->guest_primary.commands++;
+
+ return true;
+}
+
+int interface_get_cursor_command_virtio(QXLDevice *qxl, struct QXLCommandExt *ext)
+{
+ int notify ATTRIBUTE_UNUSED;
+ QXLCursorRing *ring;
+ QXLCommand *cmd;
+
+ /*
+ * before a consumer can use a command, a primary surface must be created
+ */
+ if (!qxl->virtio.primary_created) {
+ return false;
+ }
+
+ ring = &qxl->ram->cursor_ring;
+
+ if (SPICE_RING_IS_EMPTY(ring)) {
+ return false;
+ }
+
+ SPICE_RING_CONS_ITEM(qxl, ring, cmd);
+ ext->cmd = *cmd;
+ ext->group_id = 0;
+ ext->flags = 0;
+ SPICE_RING_POP(ring, notify);
+ qxl_map_command(qxl, ext);
+ qxl->guest_primary.commands++;
+
+ if (qxl->id == 0) {
+ qxl_render_cursor(qxl, ext);
+ }
+
+ return true;
+}
+
+static void qxl_hw_update(void *opaque)
+{
+ QXLDevice *qxl = opaque;
+ if (!qxl->virtio.primary_created) {
+ return;
+ }
+ qxl_render_update(qxl);
+}
+
+static void qxl_hw_invalidate(void *opaque)
+{
+}
+
+static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch)
+{
+}
+
+static void qxl_hw_text_update(void *opaque, console_ch_t *chardata)
+{
+}
+
+static void qxl_init_ramsize(QXLDevice *qxl, uint32_t ram_min_mb)
+{
+ qxl->vram_size = (virtio_qxl_ram > ram_min_mb) ?
+ virtio_qxl_ram * 1024 * 1024 :
+ ram_min_mb * 1024 * 1024;;
+
+ qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1);
+}
+
+static void display_update(struct DisplayState *ds, int x, int y, int w, int h)
+{
+}
+
+static void display_resize(struct DisplayState *ds)
+{
+}
+
+static void display_refresh(struct DisplayState *ds)
+{
+ if (!qxl0->virtio.primary_created) {
+ return;
+ }
+ qemu_mutex_lock(&qxl0->ssd.lock);
+ qemu_spice_cursor_refresh_unlocked(&qxl0->ssd);
+ qemu_mutex_unlock(&qxl0->ssd.lock);
+}
+
+static DisplayChangeListener display_listener = {
+ .dpy_update = display_update,
+ .dpy_resize = display_resize,
+ .dpy_refresh = display_refresh,
+};
+
VirtIODevice *virtio_qxl_init(DeviceState *dev)
{
- return NULL;
+ QXLDevice *qxl;
+ static int virtio_fb_id = 0;
+
+ qxl = (QXLDevice *)virtio_common_init("virtio-qxl",
+ VIRTIO_ID_QXL, 0, sizeof(QXLDevice));
+
+ qxl->virtio.vdev.get_config = virtio_qxl_update_config;
+ qxl->virtio.vdev.get_features = virtio_qxl_get_features;
+ qxl->virtio.vdev.reset = virtio_qxl_reset;
+ qxl->virtio.vq = virtio_add_queue(&qxl->virtio.vdev,
+ 256,
+ virtio_qxl_handle_request);
+ qxl->virtio.qdev = dev;
+
+ qxl->transport = QXL_TRANSPORT_VIRTIO;
+ qxl_init_ramsize(qxl, 32);
+ setup_virtioqxl_mem(qxl);
+ qxl->ssd.ds = graphic_console_init(qxl_hw_update,
+ qxl_hw_invalidate,
+ qxl_hw_screen_dump,
+ qxl_hw_text_update,
+ qxl);
+
+ qemu_spice_display_init_common(&qxl->ssd, qxl->ssd.ds);
+ register_displaychangelistener(qxl->ssd.ds, &display_listener);
+ qxl->ssd.qxl.base.sif = &qxl_interface.base;
+ qemu_spice_add_interface(&qxl->ssd.qxl.base);
+ qxl->ssd.qxl.id = qxl->id;
+
+ qxl0 = qxl;
+ register_savevm(dev, "virtio-qxl", virtio_fb_id++,
+ 2, NULL, NULL, qxl);
+
+ qxl->update_area_bh = qemu_bh_new(qxl_render_update_area_bh, qxl);
+ return &qxl->virtio.vdev;
}
void virtio_qxl_exit(VirtIODevice *vdev)
{
- return;
+ QXLDevice *qxl = to_virtio_qxl(vdev);
+ unregister_savevm(qxl->virtio.qdev, "virtio-qxl", qxl);
}
-
//setting up virtio device
+static Property qxl_properties[] = {
+ DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+ DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static int virtio_qxl_init_pci(PCIDevice *pci_dev)
{
VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
@@ -75,7 +653,7 @@ static void virtio_qxl_class_init(ObjectClass *klass, void *data)
k->revision = VIRTIO_PCI_ABI_VERSION;
k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
dc->reset = virtio_pci_reset;
-// dc->props = qxl_properties; //we will use the same used by qxl-pci
+ dc->props = qxl_properties;
}
static TypeInfo virtio_qxl_info = {
new file mode 100644
@@ -0,0 +1,61 @@
+/*
+ * The Virtio QXL device is a bridge by means of a virtualized transport
+ * (VirtIO) enabling the usage of QXL driver in non-PCI architectures
+ * for communication between the guest and the Spice server.
+ *
+ * (C) Copyright IBM Corp. 2012
+ *
+ * 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.
+ */
+
+#ifndef QXL_VIRTIO_H
+#define QXL_VIRTIO_H
+
+#define VIRTIO_ID_QXL 6
+
+enum {
+ VIRTIOQXL_GETCFG,
+ VIRTIOQXL_IOPORT_WRITE,
+ VIRTIOQXL_GET_RAM,
+ VIRTIOQXL_SET_RAM,
+ VIRTIOQXL_GET_VRAM,
+ VIRTIOQXL_SET_VRAM,
+ VIRTIOQXL_GET_ROM,
+ VIRTIOQXL_SET_ROM,
+ VIRTIOQXL_CMD_RANGE
+};
+
+enum {
+ VIRTIOQXL_STATUS_DONE,
+ VIRTIOQXL_STATUS_ERROR,
+ VIRTIOQXL_STATUS_RANGE
+};
+
+struct iowrite_cmd {
+ uint32_t port;
+ uint32_t arg;
+};
+
+struct vbr_proto_hdr {
+ uint32_t function;
+ uint32_t flags;
+ uint32_t param;
+};
+
+#endif
@@ -154,30 +154,39 @@ ram_addr_t qxl_rom_size(void)
void init_qxl_rom(QXLDevice *d)
{
QXLRom *rom;
- rom = memory_region_get_ram_ptr(&d->pci.rom_bar);
+ rom = (d->transport == QXL_TRANSPORT_PCI) ?
+ memory_region_get_ram_ptr(&d->pci.rom_bar) :
+ (QXLRom *)d->virtio.rom_ptr;
QXLModes *modes = (QXLModes *)(rom + 1);
uint32_t ram_header_size;
uint32_t surface0_area_size;
uint32_t num_pages;
uint32_t fb;
int i, n;
+ uint32_t romsize = (d->transport == QXL_TRANSPORT_PCI) ?
+ d->pci.rom_size : d->virtio.config.romsize;
+ uint32_t vramsize = (d->transport == QXL_TRANSPORT_PCI) ?
+ d->pci.vga.vram_size : d->virtio.config.ramsize;
- memset(rom, 0, d->pci.rom_size);
+ memset(rom, 0, romsize);
rom->magic = cpu_to_le32(QXL_ROM_MAGIC);
- rom->id = cpu_to_le32(d->id);
- rom->log_level = cpu_to_le32(d->pci.guestdebug);
+ rom->id = (d->transport == QXL_TRANSPORT_PCI) ?
+ cpu_to_le32(d->id) : 0;
+ rom->log_level = (d->transport == QXL_TRANSPORT_PCI) ?
+ cpu_to_le32(d->pci.guestdebug) : 0;
rom->modes_offset = cpu_to_le32(sizeof(QXLRom));
rom->slot_gen_bits = MEMSLOT_GENERATION_BITS;
rom->slot_id_bits = MEMSLOT_SLOT_BITS;
- rom->slots_start = 1;
+ rom->slots_start = (d->transport == QXL_TRANSPORT_PCI) ?
+ 1 : 0;
rom->slots_end = NUM_MEMSLOTS - 1;
rom->n_surfaces = cpu_to_le32(NUM_SURFACES);
for (i = 0, n = 0; i < ARRAY_SIZE(qxl_modes); i++) {
fb = qxl_modes[i].y_res * qxl_modes[i].stride;
- if (fb > d->pci.vgamem_size) {
+ if (fb > vramsize) {
continue;
}
modes->modes[n].id = cpu_to_le32(i);
@@ -193,8 +202,9 @@ void init_qxl_rom(QXLDevice *d)
modes->n_modes = cpu_to_le32(n);
ram_header_size = ALIGN(sizeof(QXLRam), 4096);
- surface0_area_size = ALIGN(d->pci.vgamem_size, 4096);
- num_pages = d->pci.vga.vram_size;
+ surface0_area_size = (d->transport == QXL_TRANSPORT_PCI) ?
+ ALIGN(d->pci.vgamem_size, 4096) : ALIGN(fb, 4096);
+ num_pages = vramsize;
num_pages -= ram_header_size;
num_pages -= surface0_area_size;
num_pages = num_pages / TARGET_PAGE_SIZE;
@@ -203,7 +213,7 @@ void init_qxl_rom(QXLDevice *d)
rom->surface0_area_size = cpu_to_le32(surface0_area_size);
rom->pages_offset = cpu_to_le32(surface0_area_size);
rom->num_pages = cpu_to_le32(num_pages);
- rom->ram_header_offset = cpu_to_le32(d->pci.vga.vram_size - ram_header_size);
+ rom->ram_header_offset = cpu_to_le32(vramsize - ram_header_size);
d->shadow_rom = *rom;
d->rom = rom;
@@ -215,7 +225,8 @@ void init_qxl_ram(QXLDevice *d)
uint8_t *buf;
uint64_t *item;
- buf = d->pci.vga.vram_ptr;
+ buf = (d->transport == QXL_TRANSPORT_PCI) ?
+ d->pci.vga.vram_ptr : d->virtio.ram_ptr;
d->ram = (QXLRam *)(buf + le32_to_cpu(d->shadow_rom.ram_header_offset));
d->ram->magic = cpu_to_le32(QXL_RAM_MAGIC);
d->ram->int_pending = cpu_to_le32(0);
@@ -224,10 +235,12 @@ void init_qxl_ram(QXLDevice *d)
SPICE_RING_INIT(&d->ram->cmd_ring);
SPICE_RING_INIT(&d->ram->cursor_ring);
SPICE_RING_INIT(&d->ram->release_ring);
- SPICE_RING_PROD_ITEM(d, &d->ram->release_ring, item);
- assert(item);
- *item = 0;
- qxl_ring_set_dirty(d);
+ if (d->transport == QXL_TRANSPORT_PCI) {
+ SPICE_RING_PROD_ITEM(d, &d->ram->release_ring, item);
+ assert(item);
+ *item = 0;
+ qxl_ring_set_dirty(d);
+ }
}
/* spice display interface callbacks */
@@ -238,6 +251,10 @@ void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
qxl->ssd.worker = qxl_worker;
trace_qxl_interface_attach_worker(qxl->id);
+ if (qxl->transport == QXL_TRANSPORT_VIRTIO) {
+ qxl->ssd.running = true;
+ qxl_worker->start(qxl_worker);
+ }
}
void interface_set_compression_level(QXLInstance *sin, int level)
@@ -246,7 +263,9 @@ void interface_set_compression_level(QXLInstance *sin, int level)
trace_qxl_interface_set_compression_level(qxl->id, level);
qxl->shadow_rom.compression_level = cpu_to_le32(level);
- qxl_rom_set_dirty(qxl);
+ if (qxl->transport == QXL_TRANSPORT_PCI) {
+ qxl_rom_set_dirty(qxl);
+ }
qxl->rom->compression_level = cpu_to_le32(level);
}
@@ -256,7 +275,9 @@ void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
trace_qxl_interface_set_mm_time(qxl->id, mm_time);
qxl->shadow_rom.mm_clock = cpu_to_le32(mm_time);
- qxl_rom_set_dirty(qxl);
+ if (qxl->transport == QXL_TRANSPORT_PCI) {
+ qxl_rom_set_dirty(qxl);
+ }
qxl->rom->mm_clock = cpu_to_le32(mm_time);
}
@@ -265,10 +286,14 @@ void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
trace_qxl_interface_get_init_info(qxl->id);
- info->memslot_gen_bits = MEMSLOT_GENERATION_BITS;
- info->memslot_id_bits = MEMSLOT_SLOT_BITS;
- info->num_memslots = NUM_MEMSLOTS;
- info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
+ info->memslot_gen_bits = (qxl->transport == QXL_TRANSPORT_PCI) ?
+ MEMSLOT_GENERATION_BITS : 1;
+ info->memslot_id_bits = (qxl->transport == QXL_TRANSPORT_PCI) ?
+ MEMSLOT_SLOT_BITS : 1;
+ info->num_memslots = (qxl->transport == QXL_TRANSPORT_PCI) ?
+ NUM_MEMSLOTS : 1;
+ info->num_memslots_groups = (qxl->transport == QXL_TRANSPORT_PCI) ?
+ NUM_MEMSLOTS_GROUPS : 1;
info->internal_groupslot_id = 0;
info->qxl_ram_size = le32_to_cpu(qxl->shadow_rom.num_pages) << TARGET_PAGE_BITS;
info->n_surfaces = NUM_SURFACES;
@@ -277,8 +302,11 @@ void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
{
QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
-
- return interface_get_command_pci(qxl, ext);
+ if (qxl->transport == QXL_TRANSPORT_PCI) {
+ return interface_get_command_pci(qxl, ext);
+ } else {
+ return interface_get_command_virtio(qxl, ext);
+ }
}
/* called from spice server thread context only */
@@ -287,6 +315,10 @@ int interface_req_cmd_notification(QXLInstance *sin)
QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
int wait = 1;
+ if (qxl->transport == QXL_TRANSPORT_VIRTIO) {
+ return true;
+ }
+
trace_qxl_ring_command_req_notification(qxl->id);
switch (qxl->pci.mode) {
case QXL_MODE_COMPAT:
@@ -310,10 +342,17 @@ void interface_release_resource(QXLInstance *sin,
QXLReleaseRing *ring;
uint64_t *item, id;
- if (ext.group_id == MEMSLOT_GROUP_HOST) {
- /* host group -> vga mode update request */
- qemu_spice_destroy_update(&qxl->ssd, (void *)(intptr_t)ext.info->id);
+
+ if (qxl->transport == QXL_TRANSPORT_PCI) {
+ if (ext.group_id == MEMSLOT_GROUP_HOST) {
+ /* host group -> vga mode update request */
+ qemu_spice_destroy_update(&qxl->ssd,
+ (void *)(intptr_t)ext.info->id);
+ }
return;
+ } else { //QXL_TRANSPORT_VIRTIO
+ if (ext.info->id == 0)
+ return;
}
/*
@@ -329,15 +368,19 @@ void interface_release_resource(QXLInstance *sin,
/* stick head into the ring */
id = ext.info->id;
ext.info->next = 0;
- qxl_ram_set_dirty(qxl, &ext.info->next);
*item = id;
- qxl_ring_set_dirty(qxl);
+ if (qxl->transport == QXL_TRANSPORT_PCI) {
+ qxl_ram_set_dirty(qxl, &ext.info->next);
+ qxl_ring_set_dirty(qxl);
+ }
} else {
/* append item to the list */
qxl->last_release->next = ext.info->id;
ext.info->next = 0;
- qxl_ram_set_dirty(qxl, &qxl->last_release->next);
- qxl_ram_set_dirty(qxl, &ext.info->next);
+ if (qxl->transport == QXL_TRANSPORT_PCI) {
+ qxl_ram_set_dirty(qxl, &qxl->last_release->next);
+ qxl_ram_set_dirty(qxl, &ext.info->next);
+ }
}
qxl->last_release = ext.info;
qxl->num_free_res++;
@@ -350,7 +393,11 @@ int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext)
{
QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
- return interface_get_cursor_command_pci(qxl, ext);
+ if (qxl->transport == QXL_TRANSPORT_PCI) {
+ return interface_get_cursor_command_pci(qxl, ext);
+ } else {
+ return interface_get_cursor_command_virtio(qxl, ext);
+ }
}
/* called from spice server thread context only */
@@ -359,6 +406,10 @@ int interface_req_cursor_notification(QXLInstance *sin)
QXLDevice *qxl = container_of(sin, QXLDevice, ssd.qxl);
int wait = 1;
+ if (qxl->transport == QXL_TRANSPORT_VIRTIO) {
+ return true;
+ }
+
trace_qxl_ring_cursor_req_notification(qxl->id);
switch (qxl->pci.mode) {
case QXL_MODE_COMPAT:
@@ -4,6 +4,7 @@
#include "hw.h"
#include "pci.h"
#include "vga_int.h"
+#include "virtio.h"
#include "qemu-thread.h"
#include "ui/qemu-spice.h"
@@ -16,6 +17,10 @@ enum qxl_mode {
QXL_MODE_NATIVE,
};
+enum qxl_transport {
+ QXL_TRANSPORT_PCI,
+ QXL_TRANSPORT_VIRTIO,
+};
#ifndef QXL_VRAM64_RANGE_INDEX
#define QXL_VRAM64_RANGE_INDEX 4
#endif
@@ -24,6 +29,8 @@ enum qxl_mode {
#define QXL_NUM_DIRTY_RECTS 64
+#define ATTRIBUTE_UNUSED __attribute__((unused))
+
typedef struct PCIQXLDevice {
PCIDevice pci;
int generation;
@@ -54,15 +61,41 @@ typedef struct PCIQXLDevice {
uint32_t vgamem_size_mb;
} PCIQXLDevice;
+typedef struct VirtQXLDevice {
+ VirtIODevice vdev;
+ VirtQueue *vq;
+ DeviceState *qdev;
+ DisplayState *ds;
+
+ bool primary_created;
+
+ uint8_t *ram_ptr;
+ uint8_t *vram_ptr;
+ uint8_t *rom_ptr;
+
+ /* all configs that are shared with virtio driver should be here */
+ struct virtioqxl_config {
+ uint32_t configsize;
+ uint32_t ramsize;
+ uint32_t vramsize;
+ uint32_t romsize;
+ uint32_t virtiomem[0];
+ } config;
+} VirtQXLDevice;
+
#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V10
typedef struct QXLDevice {
- PCIQXLDevice pci;
+ union {
+ PCIQXLDevice pci;
+ VirtQXLDevice virtio;
+ };
SimpleSpiceDisplay ssd;
int id;
uint32_t cmdflags;
uint32_t debug;
+ enum qxl_transport transport;
int32_t num_memslots;
int32_t num_surfaces;
@@ -156,9 +189,11 @@ typedef struct QXLDevice {
uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r); \
typeof(&(r)->items[prod]) m_item = &(r)->items[prod]; \
if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \
- qxl_set_guest_bug(qxl, "SPICE_RING_PROD_ITEM indices mismatch " \
- "! %p <= %p < %p", (uint8_t *)start, \
- (uint8_t *)m_item, (uint8_t *)end); \
+ if (qxl->transport == QXL_TRANSPORT_PCI) { \
+ qxl_set_guest_bug(qxl, "SPICE_RING_PROD_ITEM indices mismatch " \
+ "! %p <= %p < %p", (uint8_t *)start, \
+ (uint8_t *)m_item, (uint8_t *)end); \
+ } \
ret = NULL; \
} else { \
ret = &m_item->el; \
@@ -172,9 +207,11 @@ typedef struct QXLDevice {
uint32_t cons = (r)->cons & SPICE_RING_INDEX_MASK(r); \
typeof(&(r)->items[cons]) m_item = &(r)->items[cons]; \
if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \
- qxl_set_guest_bug(qxl, "SPICE_RING_CONS_ITEM indices mismatch " \
- "! %p <= %p < %p", (uint8_t *)start, \
- (uint8_t *)m_item, (uint8_t *)end); \
+ if (qxl->transport == QXL_TRANSPORT_PCI) { \
+ qxl_set_guest_bug(qxl, "SPICE_RING_CONS_ITEM indices mismatch " \
+ "! %p <= %p < %p", (uint8_t *)start, \
+ (uint8_t *)m_item, (uint8_t *)end); \
+ } \
ret = NULL; \
} else { \
ret = &m_item->el; \
@@ -237,6 +274,11 @@ int interface_get_command_pci(QXLDevice *qxl, struct QXLCommandExt *ext);
int interface_get_cursor_command_pci(QXLDevice *qxl, struct QXLCommandExt *ext);
const char *qxl_mode_to_string(int mode);
+/* qxl-virtio.c */
+int interface_get_command_virtio(QXLDevice *qxl, struct QXLCommandExt *ext);
+int interface_get_cursor_command_virtio(QXLDevice *qxl, struct QXLCommandExt *ext);
+void *offset_to_virt(QXLDevice *qxl, QXLPHYSICAL pqxl, int group_id);
+
/* called from spice server thread context only */
static inline void qxl_push_free_res(QXLDevice *d, int flush)
{
@@ -260,13 +302,15 @@ static inline void qxl_push_free_res(QXLDevice *d, int flush)
}
SPICE_RING_PUSH(ring, notify);
- trace_qxl_ring_res_push(d->id, qxl_mode_to_string(d->pci.mode),
- d->guest_surfaces.count, d->num_free_res,
- d->last_release, notify ? "yes" : "no");
- trace_qxl_ring_res_push_rest(d->id, ring->prod - ring->cons,
- ring->num_items, ring->prod, ring->cons);
- if (notify) {
- qxl_send_events(d, QXL_INTERRUPT_DISPLAY);
+ if (d->transport == QXL_TRANSPORT_PCI) {
+ trace_qxl_ring_res_push(d->id, qxl_mode_to_string(d->pci.mode),
+ d->guest_surfaces.count, d->num_free_res,
+ d->last_release, notify ? "yes" : "no");
+ trace_qxl_ring_res_push_rest(d->id, ring->prod - ring->cons,
+ ring->num_items, ring->prod, ring->cons);
+ if (notify) {
+ qxl_send_events(d, QXL_INTERRUPT_DISPLAY);
+ }
}
SPICE_RING_PROD_ITEM(d, ring, item);
if (!item) {
@@ -275,7 +319,9 @@ static inline void qxl_push_free_res(QXLDevice *d, int flush)
*item = 0;
d->num_free_res = 0;
d->last_release = NULL;
- qxl_ring_set_dirty(d);
+ if (d->transport == QXL_TRANSPORT_PCI) {
+ qxl_ring_set_dirty(d);
+ }
}
static inline uint32_t msb_mask(uint32_t val)
@@ -769,7 +769,8 @@ static void ppc_spapr_init(ram_addr_t ram_size,
if (usb_enabled) {
pci_create_simple(QLIST_FIRST(&spapr->phbs)->host_state.bus,
-1, "pci-ohci");
- if (spapr->has_graphics) {
+ if (spapr->has_graphics || virtio_qxl_enabled) {
+ printf("Starting USB inputs\n");
usbdevice_create("keyboard");
usbdevice_create("mouse");
}
@@ -77,7 +77,7 @@ static inline target_phys_addr_t vring_align(target_phys_addr_t addr,
typedef struct VirtQueue VirtQueue;
-#define VIRTQUEUE_MAX_SIZE 1024
+#define VIRTQUEUE_MAX_SIZE 8192
typedef struct VirtQueueElement
{
@@ -645,12 +645,13 @@ QemuOptsList qemu_boot_opts = {
QemuOptsList qemu_virtio_qxl_opts = {
.name = "virtio-qxl",
+ .implied_opt_name = "ram_size_mb",
.head = QTAILQ_HEAD_INITIALIZER(qemu_virtio_qxl_opts.head),
.desc = {
- /*
- * no elements => accepty any
- * sanity checking will happen later when setting device properties
- */
+ {
+ .name = "ram_size_mb",
+ .type = QEMU_OPT_NUMBER,
+ },
{ /* end of list */}
},
};
@@ -571,7 +571,7 @@ DEFHEADING()
DEFHEADING(QXL VirtIO option:)
-DEF("virtio-qxl", 0, QEMU_OPTION_virtio_qxl, "-virtio-qxl\n",
+DEF("virtio-qxl", HAS_ARG, QEMU_OPTION_virtio_qxl, "-virtio-qxl ram_size_mb=XXX\n",
QEMU_ARCH_ALL)
DEFHEADING()
@@ -120,6 +120,7 @@ extern int win2k_install_hack;
extern int alt_grab;
extern int ctrl_grab;
extern int usb_enabled;
+extern int virtio_qxl_enabled;
extern int smp_cpus;
extern int max_cpus;
extern int cursor_hide;
@@ -234,6 +234,7 @@ uint8_t *boot_splash_filedata;
int boot_splash_filedata_size;
uint8_t qemu_extra_params_fw[2];
int virtio_qxl_enabled = 0;
+uint64_t virtio_qxl_ram = 0;
typedef struct FWBootEntry FWBootEntry;
@@ -2958,11 +2959,18 @@ int main(int argc, char **argv, char **envp)
virtio_qxl_enabled = 1;
vga_model = "none";
default_vga = 0;
- device = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
+ device = qemu_opts_create(qemu_find_opts("device"),
+ NULL,
+ 0,
NULL);
qemu_opt_set(device, "driver", "virtio-qxl-pci");
- break;
+ olist = qemu_find_opts("virtio-qxl");
+ opts = qemu_opts_parse(olist, optarg, 1);
+ virtio_qxl_ram = qemu_opt_get_number(opts,
+ "ram_size_mb",
+ 64);
+ break;
}
case QEMU_OPTION_serial:
add_device_config(DEV_SERIAL, optarg);