/* * This file is the driver for the PCI BGA device. It controls the Bochs * Graphics Adapter card on virtual machines. It creates a framebuffer device * node in the devfs mount to give access to the rest of the system. */ #include #include #include #include #include #include #include #include #include #include #include #include /* BGA Register Indicies */ enum BGAIndex { BGA_INDEX_ID, BGA_INDEX_XRES, BGA_INDEX_YRES, BGA_INDEX_BPP, BGA_INDEX_ENABLE, BGA_INDEX_BANK, BGA_INDEX_VIRT_WIDTH, BGA_INDEX_VIRTH_HEIGHT, BGA_INDEX_XOFF, BGA_INDEX_YOFF, }; /* Structure for a Video Device */ struct VideoDev { int width, height, bpp; size_t memSize; uintptr_t lfb; }; const uint16_t BGA_LFB = 0x40; /* BGA Ports */ const uint16_t BGA_IO_INDEX = 0x1CE; const uint16_t BGA_IO_DATA = 0x1CF; size_t bga_dev_write(File *file, char *buf, size_t size, off_t offset); int bga_dev_ioctl(File *file, unsigned long request, uintptr_t argp); void bga_dev_mmap(File *file, void *addr, size_t len, off_t offset); /* File Operations for the BGA device nodes */ FileOps bgaFileOps = { .write = bga_dev_write, .ioctl = bga_dev_ioctl, .mmap = bga_dev_mmap, }; static struct VideoDev bgaDev; /* Write a BGA register */ static void bga_write_register(enum BGAIndex index, uint16_t data) { outw(BGA_IO_INDEX, index); outw(BGA_IO_DATA, data); } /* Set the video mode */ static void bga_set_mode(uint32_t width, uint32_t height, uint32_t bpp) { bgaDev.width = width; bgaDev.height = height; bgaDev.bpp = bpp; bgaDev.memSize = width * height * (bpp / 8); bga_write_register(BGA_INDEX_ENABLE, 0); bga_write_register(BGA_INDEX_XRES, width); bga_write_register(BGA_INDEX_YRES, height); bga_write_register(BGA_INDEX_BPP, bpp); bga_write_register(BGA_INDEX_ENABLE, 1 | BGA_LFB); } /* Write to the BGA framebuffer */ size_t bga_dev_write(File *file, char *buf, size_t size, off_t offset) { Page *page; char *data; size_t min, count = 0; off_t indent = offset % PAGE_SIZE; off_t pgoff = PAGE_ADDR(offset + count); while (size) { min = (size < PAGE_SIZE) ? size : PAGE_SIZE; page = create_page(file->inode->pages, bgaDev.lfb + pgoff, pgoff); data = map_page(page); memcpy(data + indent, buf + count, min); remove(file->inode->pages, page); count += min; size -= min; indent = 0; } return count; } /* Control the BGA device */ int bga_dev_ioctl(File *file, unsigned long request, uintptr_t argp) { FBVarInfo *vinfo; FBFixInfo *finfo; if (!argp) return -EFAULT; switch (request) { case FBIOGET_VSCREENINFO: vinfo = (void *) argp; if (!verify_access(vinfo, sizeof(FBVarInfo), PROT_WRITE)) return -EFAULT; vinfo->xres = bgaDev.width; vinfo->yres = bgaDev.height; vinfo->bpp = bgaDev.bpp; return 0; case FBIOPUT_VSCREENINFO: vinfo = (void *) argp; if (!verify_access(vinfo, sizeof(FBVarInfo), PROT_WRITE)) return -EFAULT; bga_set_mode(vinfo->xres, vinfo->yres, vinfo->bpp); return 0; case FBIOGET_FSCREENINFO: finfo = (void *) argp; if (!verify_access(finfo, sizeof(FBFixInfo), PROT_WRITE)) return -EFAULT; finfo->fbmem = bgaDev.lfb; finfo->fbmemLen = bgaDev.memSize; return 0; default: return -EINVAL; } } /* Map the linear frame buffer into memory */ void bga_dev_mmap(File *file, void *addr, size_t len, off_t offset) { return; } /* Initialise a PCI BGA device */ void pci_init_bga(uint8_t bus, uint8_t dev, uint8_t func) { static int bgaMajor = 0; if (!bgaMajor) bgaMajor = register_driver(2, &bgaFileOps); /* Setup device */ const int width = 1280; const int height = 720; const int bpp = 32; bga_set_mode(width, height, bpp); bgaDev.lfb = pci_read_dword(bus, dev, func, 0x10) & ~0xF; mknod("/dev/fb", S_IFCHR | 0600, MKDEV(bgaMajor, 0)); uintptr_t ptr; int fd = open("/dev/fb", O_RDONLY); File *file = get(get_file_by_fd(fd)); close(fd); for (ptr = 0; ptr < bgaDev.memSize; ptr += PAGE_SIZE) create_page(file->inode->pages, bgaDev.lfb + ptr, ptr); }