#include #include #include #include #include #include #define VBE_DISPI_INDEX_ID 0x00u #define VBE_DISPI_INDEX_XRES 0x01u #define VBE_DISPI_INDEX_YRES 0x02u #define VBE_DISPI_INDEX_BPP 0x03u #define VBE_DISPI_INDEX_ENABLE 0x04u #define VBE_DISPI_INDEX_BANK 0x05u #define VBE_DISPI_INDEX_VIRT_WIDTH 0x06u #define VBE_DISPI_INDEX_VIRT_HEIGHT 0x07u #define VBE_DISPI_INDEX_X_OFFSET 0x08u #define VBE_DISPI_INDEX_Y_OFFSET 0x09u #define VBE_DISPI_LFB_ENABLED 0x40 static struct pci_driver *qemufb_driver = NULL; static struct pci_device_id qemufb_device_ids[] = { PCI_DEVICE_ID(0x1234, 0x1111), PCI_DEVICE_ID_INVALID, }; static inline uint16_t dispi_mmio_offset(int index) { return (0x500 + (index << 1)) >> 1; } static kern_status_t qemufb_set_varinfo(struct device *dev, const struct framebuffer_varinfo *varinfo) { struct framebuffer_device *fbdev = FRAMEBUFFER_DEVICE(dev); uint32_t mmio_base = pci_device_read_field(dev, PCI_REG_BAR2, 4); uint16_t *mmio = vm_phys_to_virt(mmio_base); mmio[dispi_mmio_offset(VBE_DISPI_INDEX_ENABLE)] = 0; mmio[dispi_mmio_offset(VBE_DISPI_INDEX_XRES)] = varinfo->fb_xres; mmio[dispi_mmio_offset(VBE_DISPI_INDEX_YRES)] = varinfo->fb_yres; mmio[dispi_mmio_offset(VBE_DISPI_INDEX_BPP)] = varinfo->fb_bpp; mmio[dispi_mmio_offset(VBE_DISPI_INDEX_ENABLE)] = 1 | VBE_DISPI_LFB_ENABLED; if (mmio[dispi_mmio_offset(VBE_DISPI_INDEX_XRES)] != varinfo->fb_xres || mmio[dispi_mmio_offset(VBE_DISPI_INDEX_YRES)] != varinfo->fb_yres || mmio[dispi_mmio_offset(VBE_DISPI_INDEX_BPP)] != varinfo->fb_bpp) { return KERN_UNSUPPORTED; } fbdev->fb_fixedinfo.fb_baseptr = pci_device_read_field(dev, PCI_REG_BAR0, 4) & ~(VM_PAGE_SIZE - 1); return KERN_OK; } static struct framebuffer_device_ops qemufb_ops = { .set_varinfo = qemufb_set_varinfo, }; static kern_status_t qemufb_probe(struct pci_driver *driver, struct device *dev) { struct framebuffer_device *fb = framebuffer_device_from_generic(dev); (void)fb; snprintf(dev->dev_name, sizeof dev->dev_name, "qemufb"); fb->fb_ops = &qemufb_ops; uint32_t mmio_base = pci_device_read_field(dev, PCI_REG_BAR2, 4); uint16_t *mmio = vm_phys_to_virt(mmio_base); struct framebuffer_varinfo *varinfo = &fb->fb_varinfo; struct framebuffer_fixedinfo *fixedinfo = &fb->fb_fixedinfo; varinfo->fb_flags = FB_MODE_RGB; varinfo->fb_xres = mmio[dispi_mmio_offset(VBE_DISPI_INDEX_XRES)]; varinfo->fb_yres = mmio[dispi_mmio_offset(VBE_DISPI_INDEX_YRES)]; varinfo->fb_bpp = mmio[dispi_mmio_offset(VBE_DISPI_INDEX_BPP)]; varinfo->fb_stride = varinfo->fb_bpp / 8; fixedinfo->fb_baseptr = pci_device_read_field(dev, PCI_REG_BAR0, 4) & (VM_PAGE_SIZE - 1); if (!varinfo->fb_xres) { /* no mode data. assume that we're in VGA text mode */ varinfo->fb_xres = 640; varinfo->fb_yres = 400; varinfo->fb_bpp = 16; varinfo->fb_flags = FB_MODE_VGATEXT; varinfo->fb_xcells = 80; varinfo->fb_ycells = 25; varinfo->fb_stride = 80 * 25 * 2; fixedinfo->fb_baseptr = 0xb8000; } return device_register(dev, pci_driver_base(qemufb_driver), NULL); } static kern_status_t online(struct kext *self) { printk("qemufb: registering PCI driver"); qemufb_driver = pci_driver_create(self, "qemufb", qemufb_device_ids); if (!qemufb_driver) { return KERN_NO_MEMORY; } qemufb_driver->probe = qemufb_probe; pci_driver_register(qemufb_driver); return KERN_OK; } DEFINE_KEXT("net.doorstuck.socks.qemufb", online, NULL, PCI_SUBSYSTEM_KEXT_ID);