xref: /kvmtool/hw/vesa.c (revision 947392bcaaee10d34504b40a7481d5d51f635de7)
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 ioport *ioport, 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 ioport *ioport, 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[1]			= VESA_MEM_ADDR | PCI_BASE_ADDRESS_SPACE_MEMORY,
53 };
54 
55 
56 void vesa_mmio_callback(u64 addr, u8 *data, u32 len, u8 is_write)
57 {
58 	if (!is_write)
59 		return;
60 
61 	memcpy(&videomem[addr - VESA_MEM_ADDR], data, len);
62 }
63 
64 void vesa__init(struct kvm *kvm)
65 {
66 	u8 dev, line, pin;
67 	pthread_t thread;
68 	u16 vesa_base_addr;
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 	vesa_base_addr			= ioport__register(IOPORT_EMPTY, &vesa_io_ops, IOPORT_SIZE, NULL);
76 	vesa_pci_device.bar[0]		= vesa_base_addr | PCI_BASE_ADDRESS_SPACE_IO;
77 	pci__register(&vesa_pci_device, dev);
78 
79 	kvm__register_mmio(VESA_MEM_ADDR, VESA_MEM_SIZE, &vesa_mmio_callback);
80 
81 	pthread_create(&thread, NULL, vesa__dovnc, kvm);
82 }
83 
84 /*
85  * This starts a VNC server to display the framebuffer.
86  * It's not altogether clear this belongs here rather than in kvm-run.c
87  */
88 void *vesa__dovnc(void *v)
89 {
90 	/*
91 	 * Make a fake argc and argv because the getscreen function
92 	 * seems to want it.
93 	 */
94 	char argv[1][1] = {{0}};
95 	int argc = 1;
96 
97 	rfbScreenInfoPtr server;
98 
99 	server = rfbGetScreen(&argc, (char **) argv, VESA_WIDTH, VESA_HEIGHT, 8, 3, 4);
100 	server->frameBuffer		= videomem;
101 	server->alwaysShared		= TRUE;
102 	rfbInitServer(server);
103 
104 	while (rfbIsActive(server)) {
105 		rfbMarkRectAsModified(server, 0, 0, VESA_WIDTH, VESA_HEIGHT);
106 		rfbProcessEvents(server, server->deferUpdateTime * VESA_UPDATE_TIME);
107 	}
108 	return NULL;
109 }
110