xref: /kvmtool/hw/vesa.c (revision e342083c127d150f36289a0dbcfe153f15bbd73b)
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 #include "kvm/i8042.h"
11 
12 #include <sys/types.h>
13 #include <sys/ioctl.h>
14 #include <inttypes.h>
15 #include <unistd.h>
16 
17 #include <rfb/rfb.h>
18 
19 #define VESA_QUEUE_SIZE		128
20 #define VESA_IRQ		14
21 
22 /*
23  * This "6000" value is pretty much the result of experimentation
24  * It seems that around this value, things update pretty smoothly
25  */
26 #define VESA_UPDATE_TIME	6000
27 
28 static char videomem[VESA_MEM_SIZE];
29 
30 static bool vesa_pci_io_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count)
31 {
32 	return true;
33 }
34 
35 static bool vesa_pci_io_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count)
36 {
37 	return true;
38 }
39 
40 static struct ioport_operations vesa_io_ops = {
41 	.io_in			= vesa_pci_io_in,
42 	.io_out			= vesa_pci_io_out,
43 };
44 
45 static struct pci_device_header vesa_pci_device = {
46 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
47 	.device_id		= PCI_DEVICE_ID_VESA,
48 	.header_type		= PCI_HEADER_TYPE_NORMAL,
49 	.revision_id		= 0,
50 	.class			= 0x030000,
51 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
52 	.subsys_id		= PCI_SUBSYSTEM_ID_VESA,
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 	u16 vesa_base_addr;
70 
71 	if (irq__register_device(PCI_DEVICE_ID_VESA, &dev, &pin, &line) < 0)
72 		return;
73 
74 	vesa_pci_device.irq_pin		= pin;
75 	vesa_pci_device.irq_line	= line;
76 	vesa_base_addr			= ioport__register(IOPORT_EMPTY, &vesa_io_ops, IOPORT_SIZE, NULL);
77 	vesa_pci_device.bar[0]		= vesa_base_addr | PCI_BASE_ADDRESS_SPACE_IO;
78 	pci__register(&vesa_pci_device, dev);
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 	server->kbdAddEvent		= kbd_handle_key;
104 	server->ptrAddEvent		= kbd_handle_ptr;
105 	rfbInitServer(server);
106 
107 	while (rfbIsActive(server)) {
108 		rfbMarkRectAsModified(server, 0, 0, VESA_WIDTH, VESA_HEIGHT);
109 		rfbProcessEvents(server, server->deferUpdateTime * VESA_UPDATE_TIME);
110 	}
111 	return NULL;
112 }
113