xref: /kvmtool/hw/vesa.c (revision de960f08060bcd9cec06bec03395c7297579799f)
1 #include "kvm/vesa.h"
2 
3 #include "kvm/virtio-pci-dev.h"
4 #include "kvm/kvm-cpu.h"
5 #include "kvm/ioport.h"
6 #include "kvm/util.h"
7 #include "kvm/irq.h"
8 #include "kvm/kvm.h"
9 #include "kvm/pci.h"
10 
11 #include <sys/types.h>
12 #include <sys/ioctl.h>
13 #include <inttypes.h>
14 #include <unistd.h>
15 
16 #include <rfb/rfb.h>
17 
18 #define VESA_QUEUE_SIZE		128
19 #define VESA_IRQ		14
20 
21 /*
22  * This "6000" value is pretty much the result of experimentation
23  * It seems that around this value, things update pretty smoothly
24  */
25 #define VESA_UPDATE_TIME	6000
26 
27 static char videomem[VESA_MEM_SIZE];
28 
29 static bool vesa_pci_io_in(struct kvm *kvm, u16 port, void *data, int size, u32 count)
30 {
31 	return true;
32 }
33 
34 static bool vesa_pci_io_out(struct kvm *kvm, u16 port, void *data, int size, u32 count)
35 {
36 	return true;
37 }
38 
39 static struct ioport_operations vesa_io_ops = {
40 	.io_in			= vesa_pci_io_in,
41 	.io_out			= vesa_pci_io_out,
42 };
43 
44 static struct pci_device_header vesa_pci_device = {
45 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
46 	.device_id		= PCI_DEVICE_ID_VESA,
47 	.header_type		= PCI_HEADER_TYPE_NORMAL,
48 	.revision_id		= 0,
49 	.class			= 0x030000,
50 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
51 	.subsys_id		= PCI_SUBSYSTEM_ID_VESA,
52 	.bar[0]			= IOPORT_VESA   | PCI_BASE_ADDRESS_SPACE_IO,
53 	.bar[1]			= VESA_MEM_ADDR | PCI_BASE_ADDRESS_SPACE_MEMORY,
54 };
55 
56 
57 void vesa_mmio_callback(u64 addr, u8 *data, u32 len, u8 is_write)
58 {
59 	if (!is_write)
60 		return;
61 
62 	memcpy(&videomem[addr - VESA_MEM_ADDR], data, len);
63 }
64 
65 void vesa__init(struct kvm *kvm)
66 {
67 	u8 dev, line, pin;
68 	pthread_t thread;
69 
70 	if (irq__register_device(PCI_DEVICE_ID_VESA, &dev, &pin, &line) < 0)
71 		return;
72 
73 	vesa_pci_device.irq_pin		= pin;
74 	vesa_pci_device.irq_line	= line;
75 
76 	pci__register(&vesa_pci_device, dev);
77 
78 	ioport__register(IOPORT_VESA, &vesa_io_ops, IOPORT_VESA_SIZE);
79 
80 	kvm__register_mmio(VESA_MEM_ADDR, VESA_MEM_SIZE, &vesa_mmio_callback);
81 
82 	pthread_create(&thread, NULL, vesa__dovnc, kvm);
83 }
84 
85 /*
86  * This starts a VNC server to display the framebuffer.
87  * It's not altogether clear this belongs here rather than in kvm-run.c
88  */
89 void *vesa__dovnc(void *v)
90 {
91 	/*
92 	 * Make a fake argc and argv because the getscreen function
93 	 * seems to want it.
94 	 */
95 	char argv[1][1] = {{0}};
96 	int argc = 1;
97 
98 	rfbScreenInfoPtr server;
99 
100 	server = rfbGetScreen(&argc, (char **) argv, VESA_WIDTH, VESA_HEIGHT, 8, 3, 4);
101 	server->frameBuffer		= videomem;
102 	server->alwaysShared		= TRUE;
103 	rfbInitServer(server);
104 
105 	while (rfbIsActive(server)) {
106 		rfbMarkRectAsModified(server, 0, 0, VESA_WIDTH, VESA_HEIGHT);
107 		rfbProcessEvents(server, server->deferUpdateTime * VESA_UPDATE_TIME);
108 	}
109 	return NULL;
110 }
111