xref: /kvmtool/hw/vesa.c (revision 5a8e4f25dd7b32228ff214b5d5a68a27d96c9a6c)
1b025083dSJohn Floren #include "kvm/vesa.h"
2de960f08SPekka Enberg 
321ff329dSWill Deacon #include "kvm/devices.h"
4de960f08SPekka Enberg #include "kvm/virtio-pci-dev.h"
5aba1efa5SPekka Enberg #include "kvm/framebuffer.h"
6de960f08SPekka Enberg #include "kvm/kvm-cpu.h"
7b025083dSJohn Floren #include "kvm/ioport.h"
8b025083dSJohn Floren #include "kvm/util.h"
9de960f08SPekka Enberg #include "kvm/irq.h"
10b025083dSJohn Floren #include "kvm/kvm.h"
11b025083dSJohn Floren #include "kvm/pci.h"
1248d9e01aSSasha Levin 
13aa73be70SMatt Evans #include <linux/byteorder.h>
1496feb589SPekka Enberg #include <sys/mman.h>
1548d9e01aSSasha Levin #include <linux/err.h>
16b025083dSJohn Floren #include <sys/types.h>
17b025083dSJohn Floren #include <sys/ioctl.h>
18b025083dSJohn Floren #include <inttypes.h>
19b025083dSJohn Floren #include <unistd.h>
20b025083dSJohn Floren 
21b025083dSJohn Floren static struct pci_device_header vesa_pci_device = {
22aa73be70SMatt Evans 	.vendor_id	= cpu_to_le16(PCI_VENDOR_ID_REDHAT_QUMRANET),
23aa73be70SMatt Evans 	.device_id	= cpu_to_le16(PCI_DEVICE_ID_VESA),
24b025083dSJohn Floren 	.header_type	= PCI_HEADER_TYPE_NORMAL,
25b025083dSJohn Floren 	.revision_id	= 0,
26aa73be70SMatt Evans 	.class[2]	= 0x03,
27aa73be70SMatt Evans 	.subsys_vendor_id = cpu_to_le16(PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET),
28aa73be70SMatt Evans 	.subsys_id	= cpu_to_le16(PCI_SUBSYSTEM_ID_VESA),
29aa73be70SMatt Evans 	.bar[1]		= cpu_to_le32(VESA_MEM_ADDR | PCI_BASE_ADDRESS_SPACE_MEMORY),
304f045c57SSasha Levin 	.bar_size[1]	= VESA_MEM_SIZE,
31b025083dSJohn Floren };
32b025083dSJohn Floren 
3321ff329dSWill Deacon static struct device_header vesa_device = {
3421ff329dSWill Deacon 	.bus_type	= DEVICE_BUS_PCI,
3521ff329dSWill Deacon 	.data		= &vesa_pci_device,
3621ff329dSWill Deacon };
3721ff329dSWill Deacon 
38*5a8e4f25SAlexandru Elisei static struct framebuffer vesafb = {
39*5a8e4f25SAlexandru Elisei 	.width		= VESA_WIDTH,
40*5a8e4f25SAlexandru Elisei 	.height		= VESA_HEIGHT,
41*5a8e4f25SAlexandru Elisei 	.depth		= VESA_BPP,
42*5a8e4f25SAlexandru Elisei 	.mem_addr	= VESA_MEM_ADDR,
43*5a8e4f25SAlexandru Elisei 	.mem_size	= VESA_MEM_SIZE,
44*5a8e4f25SAlexandru Elisei };
45*5a8e4f25SAlexandru Elisei 
46*5a8e4f25SAlexandru Elisei static bool vesa_pci_io_in(struct ioport *ioport, struct kvm_cpu *vcpu, u16 port, void *data, int size)
47*5a8e4f25SAlexandru Elisei {
48*5a8e4f25SAlexandru Elisei 	return true;
49*5a8e4f25SAlexandru Elisei }
50*5a8e4f25SAlexandru Elisei 
51*5a8e4f25SAlexandru Elisei static bool vesa_pci_io_out(struct ioport *ioport, struct kvm_cpu *vcpu, u16 port, void *data, int size)
52*5a8e4f25SAlexandru Elisei {
53*5a8e4f25SAlexandru Elisei 	return true;
54*5a8e4f25SAlexandru Elisei }
55*5a8e4f25SAlexandru Elisei 
56*5a8e4f25SAlexandru Elisei static struct ioport_operations vesa_io_ops = {
57*5a8e4f25SAlexandru Elisei 	.io_in			= vesa_pci_io_in,
58*5a8e4f25SAlexandru Elisei 	.io_out			= vesa_pci_io_out,
59*5a8e4f25SAlexandru Elisei };
60*5a8e4f25SAlexandru Elisei 
61*5a8e4f25SAlexandru Elisei static int vesa__bar_activate(struct kvm *kvm, struct pci_device_header *pci_hdr,
62*5a8e4f25SAlexandru Elisei 			      int bar_num, void *data)
63*5a8e4f25SAlexandru Elisei {
64*5a8e4f25SAlexandru Elisei 	/* We don't support remapping of the framebuffer. */
65*5a8e4f25SAlexandru Elisei 	return 0;
66*5a8e4f25SAlexandru Elisei }
67*5a8e4f25SAlexandru Elisei 
68*5a8e4f25SAlexandru Elisei static int vesa__bar_deactivate(struct kvm *kvm, struct pci_device_header *pci_hdr,
69*5a8e4f25SAlexandru Elisei 				int bar_num, void *data)
70*5a8e4f25SAlexandru Elisei {
71*5a8e4f25SAlexandru Elisei 	/* We don't support remapping of the framebuffer. */
72*5a8e4f25SAlexandru Elisei 	return -EINVAL;
73*5a8e4f25SAlexandru Elisei }
74aba1efa5SPekka Enberg 
75aba1efa5SPekka Enberg struct framebuffer *vesa__init(struct kvm *kvm)
76b025083dSJohn Floren {
77947392bcSSasha Levin 	u16 vesa_base_addr;
78aba1efa5SPekka Enberg 	char *mem;
7948d9e01aSSasha Levin 	int r;
80b025083dSJohn Floren 
81ce2fc8f5SAlexandru Elisei 	BUILD_BUG_ON(!is_power_of_two(VESA_MEM_SIZE));
82ce2fc8f5SAlexandru Elisei 	BUILD_BUG_ON(VESA_MEM_SIZE < VESA_BPP/8 * VESA_WIDTH * VESA_HEIGHT);
83ce2fc8f5SAlexandru Elisei 
845d6dd281SAlexandru Elisei 	vesa_base_addr = pci_get_io_port_block(PCI_IO_SIZE);
855d6dd281SAlexandru Elisei 	r = ioport__register(kvm, vesa_base_addr, &vesa_io_ops, PCI_IO_SIZE, NULL);
867af40b91SSasha Levin 	if (r < 0)
875d6dd281SAlexandru Elisei 		goto out_error;
887af40b91SSasha Levin 
89aa73be70SMatt Evans 	vesa_pci_device.bar[0]		= cpu_to_le32(vesa_base_addr | PCI_BASE_ADDRESS_SPACE_IO);
90c5e3c9eeSAlexandru Elisei 	vesa_pci_device.bar_size[0]	= PCI_IO_SIZE;
91*5a8e4f25SAlexandru Elisei 	r = pci__register_bar_regions(kvm, &vesa_pci_device, vesa__bar_activate,
92*5a8e4f25SAlexandru Elisei 				      vesa__bar_deactivate, NULL);
93*5a8e4f25SAlexandru Elisei 	if (r < 0)
94*5a8e4f25SAlexandru Elisei 		goto unregister_ioport;
95*5a8e4f25SAlexandru Elisei 
968f160708SAlexandru Elisei 	r = device__register(&vesa_device);
978f160708SAlexandru Elisei 	if (r < 0)
985d6dd281SAlexandru Elisei 		goto unregister_ioport;
99de960f08SPekka Enberg 
10096feb589SPekka Enberg 	mem = mmap(NULL, VESA_MEM_SIZE, PROT_RW, MAP_ANON_NORESERVE, -1, 0);
1015d6dd281SAlexandru Elisei 	if (mem == MAP_FAILED) {
1025d6dd281SAlexandru Elisei 		r = -errno;
1035d6dd281SAlexandru Elisei 		goto unregister_device;
1045d6dd281SAlexandru Elisei 	}
105aba1efa5SPekka Enberg 
1065d6dd281SAlexandru Elisei 	r = kvm__register_dev_mem(kvm, VESA_MEM_ADDR, VESA_MEM_SIZE, mem);
1075d6dd281SAlexandru Elisei 	if (r < 0)
1085d6dd281SAlexandru Elisei 		goto unmap_dev;
10996feb589SPekka Enberg 
110*5a8e4f25SAlexandru Elisei 	vesafb.mem = mem;
111*5a8e4f25SAlexandru Elisei 	vesafb.kvm = kvm;
112aba1efa5SPekka Enberg 	return fb__register(&vesafb);
1135d6dd281SAlexandru Elisei 
1145d6dd281SAlexandru Elisei unmap_dev:
1155d6dd281SAlexandru Elisei 	munmap(mem, VESA_MEM_SIZE);
1165d6dd281SAlexandru Elisei unregister_device:
1175d6dd281SAlexandru Elisei 	device__unregister(&vesa_device);
1185d6dd281SAlexandru Elisei unregister_ioport:
1195d6dd281SAlexandru Elisei 	ioport__unregister(kvm, vesa_base_addr);
1205d6dd281SAlexandru Elisei out_error:
1215d6dd281SAlexandru Elisei 	return ERR_PTR(r);
122b025083dSJohn Floren }
123