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
385a8e4f25SAlexandru Elisei static struct framebuffer vesafb = {
395a8e4f25SAlexandru Elisei .width = VESA_WIDTH,
405a8e4f25SAlexandru Elisei .height = VESA_HEIGHT,
415a8e4f25SAlexandru Elisei .depth = VESA_BPP,
425a8e4f25SAlexandru Elisei .mem_addr = VESA_MEM_ADDR,
435a8e4f25SAlexandru Elisei .mem_size = VESA_MEM_SIZE,
445a8e4f25SAlexandru Elisei };
455a8e4f25SAlexandru Elisei
vesa_pci_io(struct kvm_cpu * vcpu,u64 addr,u8 * data,u32 len,u8 is_write,void * ptr)46*38ae332fSAndre Przywara static void vesa_pci_io(struct kvm_cpu *vcpu, u64 addr, u8 *data, u32 len,
47*38ae332fSAndre Przywara u8 is_write, void *ptr)
485a8e4f25SAlexandru Elisei {
495a8e4f25SAlexandru Elisei }
505a8e4f25SAlexandru Elisei
vesa__bar_activate(struct kvm * kvm,struct pci_device_header * pci_hdr,int bar_num,void * data)515a8e4f25SAlexandru Elisei static int vesa__bar_activate(struct kvm *kvm, struct pci_device_header *pci_hdr,
525a8e4f25SAlexandru Elisei int bar_num, void *data)
535a8e4f25SAlexandru Elisei {
545a8e4f25SAlexandru Elisei /* We don't support remapping of the framebuffer. */
555a8e4f25SAlexandru Elisei return 0;
565a8e4f25SAlexandru Elisei }
575a8e4f25SAlexandru Elisei
vesa__bar_deactivate(struct kvm * kvm,struct pci_device_header * pci_hdr,int bar_num,void * data)585a8e4f25SAlexandru Elisei static int vesa__bar_deactivate(struct kvm *kvm, struct pci_device_header *pci_hdr,
595a8e4f25SAlexandru Elisei int bar_num, void *data)
605a8e4f25SAlexandru Elisei {
615a8e4f25SAlexandru Elisei /* We don't support remapping of the framebuffer. */
625a8e4f25SAlexandru Elisei return -EINVAL;
635a8e4f25SAlexandru Elisei }
64aba1efa5SPekka Enberg
vesa__init(struct kvm * kvm)65aba1efa5SPekka Enberg struct framebuffer *vesa__init(struct kvm *kvm)
66b025083dSJohn Floren {
67947392bcSSasha Levin u16 vesa_base_addr;
68aba1efa5SPekka Enberg char *mem;
6948d9e01aSSasha Levin int r;
70b025083dSJohn Floren
71ce2fc8f5SAlexandru Elisei BUILD_BUG_ON(!is_power_of_two(VESA_MEM_SIZE));
72ce2fc8f5SAlexandru Elisei BUILD_BUG_ON(VESA_MEM_SIZE < VESA_BPP/8 * VESA_WIDTH * VESA_HEIGHT);
73ce2fc8f5SAlexandru Elisei
745d6dd281SAlexandru Elisei vesa_base_addr = pci_get_io_port_block(PCI_IO_SIZE);
75*38ae332fSAndre Przywara r = kvm__register_pio(kvm, vesa_base_addr, PCI_IO_SIZE, vesa_pci_io,
76*38ae332fSAndre Przywara NULL);
777af40b91SSasha Levin if (r < 0)
785d6dd281SAlexandru Elisei goto out_error;
797af40b91SSasha Levin
80aa73be70SMatt Evans vesa_pci_device.bar[0] = cpu_to_le32(vesa_base_addr | PCI_BASE_ADDRESS_SPACE_IO);
81c5e3c9eeSAlexandru Elisei vesa_pci_device.bar_size[0] = PCI_IO_SIZE;
825a8e4f25SAlexandru Elisei r = pci__register_bar_regions(kvm, &vesa_pci_device, vesa__bar_activate,
835a8e4f25SAlexandru Elisei vesa__bar_deactivate, NULL);
845a8e4f25SAlexandru Elisei if (r < 0)
855a8e4f25SAlexandru Elisei goto unregister_ioport;
865a8e4f25SAlexandru Elisei
878f160708SAlexandru Elisei r = device__register(&vesa_device);
888f160708SAlexandru Elisei if (r < 0)
895d6dd281SAlexandru Elisei goto unregister_ioport;
90de960f08SPekka Enberg
9196feb589SPekka Enberg mem = mmap(NULL, VESA_MEM_SIZE, PROT_RW, MAP_ANON_NORESERVE, -1, 0);
925d6dd281SAlexandru Elisei if (mem == MAP_FAILED) {
935d6dd281SAlexandru Elisei r = -errno;
945d6dd281SAlexandru Elisei goto unregister_device;
955d6dd281SAlexandru Elisei }
96aba1efa5SPekka Enberg
975d6dd281SAlexandru Elisei r = kvm__register_dev_mem(kvm, VESA_MEM_ADDR, VESA_MEM_SIZE, mem);
985d6dd281SAlexandru Elisei if (r < 0)
995d6dd281SAlexandru Elisei goto unmap_dev;
10096feb589SPekka Enberg
1015a8e4f25SAlexandru Elisei vesafb.mem = mem;
1025a8e4f25SAlexandru Elisei vesafb.kvm = kvm;
103aba1efa5SPekka Enberg return fb__register(&vesafb);
1045d6dd281SAlexandru Elisei
1055d6dd281SAlexandru Elisei unmap_dev:
1065d6dd281SAlexandru Elisei munmap(mem, VESA_MEM_SIZE);
1075d6dd281SAlexandru Elisei unregister_device:
1085d6dd281SAlexandru Elisei device__unregister(&vesa_device);
1095d6dd281SAlexandru Elisei unregister_ioport:
110*38ae332fSAndre Przywara kvm__deregister_pio(kvm, vesa_base_addr);
1115d6dd281SAlexandru Elisei out_error:
1125d6dd281SAlexandru Elisei return ERR_PTR(r);
113b025083dSJohn Floren }
114