2021-05-31 04:47:02 +03:00
|
|
|
/**
|
|
|
|
* @file kernel/video/lfbvideo.c
|
|
|
|
* @brief Shared linear framebuffer drivers for qemu/bochs/vbox, vmware,
|
|
|
|
* and platforms that can modeset in the bootloader.
|
2018-09-28 14:29:40 +03:00
|
|
|
*
|
2021-05-31 04:47:02 +03:00
|
|
|
* Detects a small set of video devices that can be configured with simple
|
|
|
|
* port writes and provides a runtime modesetting API for them. For other
|
|
|
|
* devices, provides framebuffer mapping and resolution querying for modes
|
|
|
|
* that have been preconfigured by the bootloader.
|
2018-03-16 15:56:19 +03:00
|
|
|
*/
|
2021-05-31 04:47:02 +03:00
|
|
|
#include <errno.h>
|
2018-03-19 05:38:11 +03:00
|
|
|
#include <kernel/types.h>
|
2021-05-31 04:47:02 +03:00
|
|
|
#include <kernel/vfs.h>
|
|
|
|
#include <kernel/printf.h>
|
2018-03-19 05:38:11 +03:00
|
|
|
#include <kernel/pci.h>
|
|
|
|
#include <kernel/video.h>
|
2021-05-31 04:47:02 +03:00
|
|
|
#include <kernel/process.h>
|
|
|
|
#include <kernel/string.h>
|
|
|
|
#include <kernel/signal.h>
|
|
|
|
#include <kernel/tokenize.h>
|
|
|
|
#include <kernel/multiboot.h>
|
|
|
|
#include <kernel/procfs.h>
|
|
|
|
#include <kernel/mmu.h>
|
|
|
|
#include <kernel/args.h>
|
2018-03-16 15:56:19 +03:00
|
|
|
|
2021-05-31 04:47:02 +03:00
|
|
|
/* FIXME: Not sure what to do with this; ifdef around it? */
|
|
|
|
#include <kernel/arch/x86_64/ports.h>
|
|
|
|
|
|
|
|
static int PREFERRED_W = 1440;
|
|
|
|
static int PREFERRED_H = 900;
|
2018-03-16 15:56:19 +03:00
|
|
|
#define PREFERRED_VY 4096
|
|
|
|
#define PREFERRED_B 32
|
|
|
|
|
2018-09-28 15:15:30 +03:00
|
|
|
/* Exported to other modules */
|
2018-03-16 15:56:19 +03:00
|
|
|
uint16_t lfb_resolution_x = 0;
|
|
|
|
uint16_t lfb_resolution_y = 0;
|
|
|
|
uint16_t lfb_resolution_b = 0;
|
|
|
|
uint32_t lfb_resolution_s = 0;
|
2018-09-28 15:15:30 +03:00
|
|
|
uint8_t * lfb_vid_memory = (uint8_t *)0xE0000000;
|
2021-05-31 04:47:02 +03:00
|
|
|
size_t lfb_memsize = 0xFF0000;
|
2018-09-28 15:15:30 +03:00
|
|
|
const char * lfb_driver_name = NULL;
|
2018-03-16 15:56:19 +03:00
|
|
|
|
2021-06-02 07:14:26 +03:00
|
|
|
uintptr_t lfb_bochs_mmio = 0;
|
|
|
|
|
2021-05-31 04:47:02 +03:00
|
|
|
fs_node_t * lfb_device = NULL;
|
|
|
|
static int lfb_init(const char * c);
|
2018-10-27 10:18:09 +03:00
|
|
|
|
2018-09-28 15:15:30 +03:00
|
|
|
/* Where to send display size change signals */
|
2018-03-16 15:56:19 +03:00
|
|
|
static pid_t display_change_recipient = 0;
|
|
|
|
|
2018-09-28 15:15:30 +03:00
|
|
|
/* Driver-specific modesetting function */
|
2021-07-19 13:50:41 +03:00
|
|
|
void (*lfb_resolution_impl)(uint16_t,uint16_t) = NULL;
|
2018-03-16 15:56:19 +03:00
|
|
|
|
2018-09-28 15:15:30 +03:00
|
|
|
/* Called by ioctl on /dev/fb0 */
|
|
|
|
void lfb_set_resolution(uint16_t x, uint16_t y) {
|
|
|
|
if (lfb_resolution_impl) {
|
|
|
|
lfb_resolution_impl(x,y);
|
|
|
|
if (display_change_recipient) {
|
2018-09-28 17:59:12 +03:00
|
|
|
send_signal(display_change_recipient, SIGWINEVENT, 1);
|
2018-09-28 15:15:30 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-03-16 15:56:19 +03:00
|
|
|
|
2021-05-31 04:47:02 +03:00
|
|
|
extern void ptr_validate(void * ptr, const char * syscall);
|
|
|
|
#define validate(o) ptr_validate(o,"ioctl")
|
|
|
|
|
2018-09-28 15:15:30 +03:00
|
|
|
/**
|
|
|
|
* Framebuffer control ioctls.
|
|
|
|
* Used by the compositor to get display sizes and by the
|
|
|
|
* resolution changer to initiate modesetting.
|
|
|
|
*/
|
2021-06-06 14:25:34 +03:00
|
|
|
static int ioctl_vid(fs_node_t * node, unsigned long request, void * argp) {
|
2018-03-16 15:56:19 +03:00
|
|
|
switch (request) {
|
|
|
|
case IO_VID_WIDTH:
|
2018-09-28 15:15:30 +03:00
|
|
|
/* Get framebuffer width */
|
2018-03-16 15:56:19 +03:00
|
|
|
validate(argp);
|
|
|
|
*((size_t *)argp) = lfb_resolution_x;
|
|
|
|
return 0;
|
|
|
|
case IO_VID_HEIGHT:
|
2018-09-28 15:15:30 +03:00
|
|
|
/* Get framebuffer height */
|
2018-03-16 15:56:19 +03:00
|
|
|
validate(argp);
|
|
|
|
*((size_t *)argp) = lfb_resolution_y;
|
|
|
|
return 0;
|
|
|
|
case IO_VID_DEPTH:
|
2018-09-28 15:15:30 +03:00
|
|
|
/* Get framebuffer bit depth */
|
2018-03-16 15:56:19 +03:00
|
|
|
validate(argp);
|
|
|
|
*((size_t *)argp) = lfb_resolution_b;
|
|
|
|
return 0;
|
2018-09-28 15:15:30 +03:00
|
|
|
case IO_VID_STRIDE:
|
|
|
|
/* Get framebuffer scanline stride */
|
|
|
|
validate(argp);
|
|
|
|
*((size_t *)argp) = lfb_resolution_s;
|
|
|
|
return 0;
|
2018-03-16 15:56:19 +03:00
|
|
|
case IO_VID_ADDR:
|
2021-06-02 07:14:26 +03:00
|
|
|
/* Map framebuffer into userspace process */
|
2018-03-16 15:56:19 +03:00
|
|
|
validate(argp);
|
2021-05-31 04:47:02 +03:00
|
|
|
{
|
|
|
|
uintptr_t lfb_user_offset;
|
|
|
|
if (*(uintptr_t*)argp == 0) {
|
|
|
|
/* Pick an address and map it */
|
|
|
|
lfb_user_offset = 0x100000000; /* at 4GiB seems good */
|
|
|
|
} else {
|
|
|
|
validate((void*)(*(uintptr_t*)argp));
|
|
|
|
lfb_user_offset = *(uintptr_t*)argp;
|
|
|
|
}
|
|
|
|
for (uintptr_t i = 0; i < lfb_memsize; i += 0x1000) {
|
|
|
|
union PML * page = mmu_get_page(lfb_user_offset + i, MMU_GET_MAKE);
|
|
|
|
mmu_frame_map_address(page,MMU_FLAG_WRITABLE|MMU_FLAG_WC,((uintptr_t)(lfb_vid_memory) & 0xFFFFFFFF) + i);
|
|
|
|
}
|
|
|
|
*((uintptr_t *)argp) = lfb_user_offset;
|
|
|
|
}
|
2018-03-16 15:56:19 +03:00
|
|
|
return 0;
|
|
|
|
case IO_VID_SIGNAL:
|
|
|
|
/* ioctl to register for a signal (vid device change? idk) on display change */
|
2021-05-31 04:47:02 +03:00
|
|
|
display_change_recipient = this_core->current_process->id;
|
2018-03-16 15:56:19 +03:00
|
|
|
return 0;
|
|
|
|
case IO_VID_SET:
|
2018-09-28 15:15:30 +03:00
|
|
|
/* Initiate mode setting */
|
2018-03-16 15:56:19 +03:00
|
|
|
validate(argp);
|
|
|
|
lfb_set_resolution(((struct vid_size *)argp)->width, ((struct vid_size *)argp)->height);
|
|
|
|
return 0;
|
2018-09-28 15:15:30 +03:00
|
|
|
case IO_VID_DRIVER:
|
|
|
|
validate(argp);
|
|
|
|
memcpy(argp, lfb_driver_name, strlen(lfb_driver_name));
|
2018-03-16 15:56:19 +03:00
|
|
|
return 0;
|
2018-10-27 10:18:09 +03:00
|
|
|
case IO_VID_REINIT:
|
2021-05-31 04:47:02 +03:00
|
|
|
if (this_core->current_process->user != 0) {
|
2018-10-27 10:18:09 +03:00
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
validate(argp);
|
|
|
|
return lfb_init(argp);
|
2018-03-16 15:56:19 +03:00
|
|
|
default:
|
2018-09-28 15:15:30 +03:00
|
|
|
return -EINVAL;
|
2018-03-16 15:56:19 +03:00
|
|
|
}
|
2021-05-31 04:47:02 +03:00
|
|
|
return -EINVAL;
|
2018-03-16 15:56:19 +03:00
|
|
|
}
|
|
|
|
|
2018-09-28 15:15:30 +03:00
|
|
|
/* Framebuffer device file initializer */
|
|
|
|
static fs_node_t * lfb_video_device_create(void /* TODO */) {
|
|
|
|
fs_node_t * fnode = malloc(sizeof(fs_node_t));
|
|
|
|
memset(fnode, 0x00, sizeof(fs_node_t));
|
2021-05-31 04:47:02 +03:00
|
|
|
snprintf(fnode->name, 100, "fb0"); /* TODO */
|
2018-10-27 10:18:09 +03:00
|
|
|
fnode->length = 0;
|
2018-09-28 15:15:30 +03:00
|
|
|
fnode->flags = FS_BLOCKDEVICE; /* Framebuffers are block devices */
|
|
|
|
fnode->mask = 0660; /* Only accessible to root user/group */
|
|
|
|
fnode->ioctl = ioctl_vid; /* control function defined above */
|
|
|
|
return fnode;
|
|
|
|
}
|
|
|
|
|
2021-06-06 14:25:34 +03:00
|
|
|
static ssize_t framebuffer_func(fs_node_t * node, off_t offset, size_t size, uint8_t * buffer) {
|
2018-09-30 10:08:56 +03:00
|
|
|
char * buf = malloc(4096);
|
|
|
|
|
|
|
|
if (lfb_driver_name) {
|
2021-05-31 04:47:02 +03:00
|
|
|
snprintf(buf, 4095,
|
2018-09-30 10:08:56 +03:00
|
|
|
"Driver:\t%s\n"
|
|
|
|
"XRes:\t%d\n"
|
|
|
|
"YRes:\t%d\n"
|
|
|
|
"BitsPerPixel:\t%d\n"
|
|
|
|
"Stride:\t%d\n"
|
2021-05-31 04:47:02 +03:00
|
|
|
"Address:\t%p\n",
|
2018-09-30 10:08:56 +03:00
|
|
|
lfb_driver_name,
|
|
|
|
lfb_resolution_x,
|
|
|
|
lfb_resolution_y,
|
|
|
|
lfb_resolution_b,
|
|
|
|
lfb_resolution_s,
|
|
|
|
lfb_vid_memory);
|
|
|
|
} else {
|
2021-05-31 04:47:02 +03:00
|
|
|
snprintf(buf, 20, "Driver:\tnone\n");
|
2018-09-30 10:08:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t _bsize = strlen(buf);
|
2021-06-06 14:25:34 +03:00
|
|
|
if ((size_t)offset > _bsize) {
|
2018-09-30 10:08:56 +03:00
|
|
|
free(buf);
|
|
|
|
return 0;
|
|
|
|
}
|
2021-06-06 14:25:34 +03:00
|
|
|
if (size > _bsize - (size_t)offset) size = _bsize - offset;
|
2018-09-30 10:08:56 +03:00
|
|
|
|
|
|
|
memcpy(buffer, buf + offset, size);
|
|
|
|
free(buf);
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct procfs_entry framebuffer_entry = {
|
|
|
|
0,
|
|
|
|
"framebuffer",
|
|
|
|
framebuffer_func,
|
|
|
|
};
|
|
|
|
|
2018-09-28 15:15:30 +03:00
|
|
|
/* Install framebuffer device */
|
|
|
|
static void finalize_graphics(const char * driver) {
|
|
|
|
lfb_driver_name = driver;
|
2021-06-10 03:35:48 +03:00
|
|
|
lfb_device = lfb_video_device_create();
|
2018-10-27 10:18:09 +03:00
|
|
|
lfb_device->length = lfb_resolution_s * lfb_resolution_y; /* Size is framebuffer size in bytes */
|
2021-06-10 03:35:48 +03:00
|
|
|
vfs_mount("/dev/fb0", lfb_device);
|
2018-09-30 10:08:56 +03:00
|
|
|
|
2021-05-31 04:47:02 +03:00
|
|
|
procfs_install(&framebuffer_entry);
|
2018-03-16 15:56:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Bochs support {{{ */
|
|
|
|
static void bochs_scan_pci(uint32_t device, uint16_t v, uint16_t d, void * extra) {
|
2021-06-02 07:14:26 +03:00
|
|
|
uintptr_t * output = extra;
|
2018-03-16 15:56:19 +03:00
|
|
|
if ((v == 0x1234 && d == 0x1111) ||
|
2018-03-19 10:04:17 +03:00
|
|
|
(v == 0x10de && d == 0x0a20)) {
|
2018-03-16 15:56:19 +03:00
|
|
|
uintptr_t t = pci_read_field(device, PCI_BAR0, 4);
|
2021-06-02 07:14:26 +03:00
|
|
|
uintptr_t m = pci_read_field(device, PCI_BAR2, 4);
|
|
|
|
|
2018-03-16 15:56:19 +03:00
|
|
|
if (t > 0) {
|
2021-06-02 07:14:26 +03:00
|
|
|
output[0] = (uintptr_t)mmu_map_from_physical(t & 0xFFFFFFF0);
|
|
|
|
output[1] = (uintptr_t)mmu_map_from_physical(m & 0xFFFFFFF0);
|
|
|
|
/* Figure out size */
|
|
|
|
pci_write_field(device, PCI_BAR0, 4, 0xFFFFFFFF);
|
|
|
|
uint32_t s = pci_read_field(device, PCI_BAR0, 4);
|
|
|
|
s = ~(s & -15) + 1;
|
|
|
|
output[2] = s;
|
|
|
|
pci_write_field(device, PCI_BAR0, 4, (uint32_t)t);
|
2018-03-16 15:56:19 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-02 07:14:26 +03:00
|
|
|
#define BOCHS_MMIO_ID 0x00
|
|
|
|
#define BOCHS_MMIO_FBWIDTH 0x02
|
|
|
|
#define BOCHS_MMIO_FBHEIGHT 0x04
|
|
|
|
#define BOCHS_MMIO_BPP 0x06
|
|
|
|
#define BOCHS_MMIO_ENABLED 0x08
|
|
|
|
#define BOCHS_MMIO_VIRTX 0x0c
|
|
|
|
#define BOCHS_MMIO_VIRTY 0x0e
|
|
|
|
|
|
|
|
static void bochs_mmio_out(int off, uint16_t val) {
|
|
|
|
*(volatile uint16_t*)(lfb_bochs_mmio + 0x500 + off) = val;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t bochs_mmio_in(int off) {
|
|
|
|
return *(volatile uint16_t*)(lfb_bochs_mmio + 0x500 + off);
|
|
|
|
}
|
|
|
|
|
2018-09-28 15:15:30 +03:00
|
|
|
static void bochs_set_resolution(uint16_t x, uint16_t y) {
|
2021-06-02 07:14:26 +03:00
|
|
|
bochs_mmio_out(BOCHS_MMIO_ENABLED, 0);
|
|
|
|
bochs_mmio_out(BOCHS_MMIO_FBWIDTH, x);
|
|
|
|
bochs_mmio_out(BOCHS_MMIO_FBHEIGHT, y);
|
|
|
|
bochs_mmio_out(BOCHS_MMIO_BPP, PREFERRED_B);
|
|
|
|
bochs_mmio_out(BOCHS_MMIO_VIRTX, x * (PREFERRED_B / 8));
|
|
|
|
bochs_mmio_out(BOCHS_MMIO_VIRTY, y);
|
2021-06-02 10:28:21 +03:00
|
|
|
bochs_mmio_out(BOCHS_MMIO_ENABLED, 0x41); /* 01h: enabled, 40h: lfb */
|
|
|
|
|
|
|
|
/* unblank vga; this should only be necessary on secondary displays */
|
|
|
|
*(volatile uint8_t*)(lfb_bochs_mmio + 0x400) = 0x20;
|
2021-06-02 07:14:26 +03:00
|
|
|
|
|
|
|
lfb_resolution_x = bochs_mmio_in(BOCHS_MMIO_FBWIDTH);
|
|
|
|
lfb_resolution_y = bochs_mmio_in(BOCHS_MMIO_FBHEIGHT);
|
|
|
|
lfb_resolution_b = bochs_mmio_in(BOCHS_MMIO_BPP);
|
|
|
|
lfb_resolution_s = lfb_resolution_x * 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void graphics_install_bochs(uint16_t resolution_x, uint16_t resolution_y) {
|
|
|
|
|
|
|
|
uintptr_t vals[3];
|
|
|
|
pci_scan(bochs_scan_pci, -1, vals);
|
|
|
|
lfb_vid_memory = (uint8_t*)vals[0];
|
|
|
|
lfb_bochs_mmio = vals[1];
|
|
|
|
lfb_memsize = vals[2];
|
|
|
|
|
|
|
|
uint16_t i = bochs_mmio_in(BOCHS_MMIO_ID);
|
|
|
|
if (i < 0xB0C0 || i > 0xB0C6) return; /* Unsupported bochs device. */
|
|
|
|
bochs_mmio_out(BOCHS_MMIO_ID, 0xB0C4); /* We speak ver. 4 */
|
|
|
|
|
|
|
|
bochs_set_resolution(resolution_x, resolution_y);
|
|
|
|
resolution_x = lfb_resolution_x; /* may have changed */
|
|
|
|
|
|
|
|
lfb_resolution_impl = &bochs_set_resolution;
|
|
|
|
|
|
|
|
if (!lfb_vid_memory) {
|
|
|
|
printf("failed to locate video memory\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
finalize_graphics("bochs");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* VirtualBox implements the portio-based interface, but not the MMIO one. */
|
|
|
|
static void vbox_scan_pci(uint32_t device, uint16_t v, uint16_t d, void * extra) {
|
|
|
|
if (v == 0x80EE && d == 0xBEEF) {
|
|
|
|
uintptr_t t = pci_read_field(device, PCI_BAR0, 4);
|
|
|
|
if (t > 0) {
|
|
|
|
*((uint8_t **)extra) = mmu_map_from_physical(t & 0xFFFFFFF0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vbox_set_resolution(uint16_t x, uint16_t y) {
|
2018-03-16 15:56:19 +03:00
|
|
|
outports(0x1CE, 0x04);
|
|
|
|
outports(0x1CF, 0x00);
|
|
|
|
outports(0x1CE, 0x01);
|
|
|
|
outports(0x1CF, x);
|
|
|
|
outports(0x1CE, 0x02);
|
|
|
|
outports(0x1CF, y);
|
|
|
|
outports(0x1CE, 0x03);
|
|
|
|
outports(0x1CF, PREFERRED_B);
|
|
|
|
outports(0x1CE, 0x07);
|
|
|
|
outports(0x1CF, PREFERRED_VY);
|
|
|
|
outports(0x1CE, 0x04);
|
|
|
|
outports(0x1CF, 0x41);
|
|
|
|
outports(0x1CE, 0x01);
|
2021-06-02 07:14:26 +03:00
|
|
|
x = inports(0x1CF);
|
2018-03-16 15:56:19 +03:00
|
|
|
lfb_resolution_x = x;
|
|
|
|
lfb_resolution_s = x * 4;
|
|
|
|
lfb_resolution_y = y;
|
2018-09-28 15:15:30 +03:00
|
|
|
lfb_resolution_b = 32;
|
2018-03-16 15:56:19 +03:00
|
|
|
}
|
|
|
|
|
2021-06-02 07:14:26 +03:00
|
|
|
static void graphics_install_vbox(uint16_t resolution_x, uint16_t resolution_y) {
|
2018-03-16 15:56:19 +03:00
|
|
|
outports(0x1CE, 0x00);
|
|
|
|
uint16_t i = inports(0x1CF);
|
|
|
|
if (i < 0xB0C0 || i > 0xB0C6) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
outports(0x1CF, 0xB0C4);
|
|
|
|
i = inports(0x1CF);
|
2021-06-02 07:14:26 +03:00
|
|
|
vbox_set_resolution(resolution_x, resolution_y);
|
2018-03-16 15:56:19 +03:00
|
|
|
resolution_x = lfb_resolution_x; /* may have changed */
|
2021-06-02 07:14:26 +03:00
|
|
|
pci_scan(vbox_scan_pci, -1, &lfb_vid_memory);
|
|
|
|
lfb_resolution_impl = &vbox_set_resolution;
|
2018-09-28 14:29:40 +03:00
|
|
|
if (!lfb_vid_memory) {
|
2021-05-31 04:47:02 +03:00
|
|
|
printf("failed to locate video memory\n");
|
2018-09-28 14:29:40 +03:00
|
|
|
return;
|
|
|
|
}
|
2018-03-16 15:56:19 +03:00
|
|
|
outports(0x1CE, 0x0a);
|
|
|
|
i = inports(0x1CF);
|
|
|
|
if (i > 1) {
|
2021-05-31 04:47:02 +03:00
|
|
|
lfb_memsize = (uint32_t)i * 64 * 1024;
|
2018-03-16 15:56:19 +03:00
|
|
|
} else {
|
2021-05-31 04:47:02 +03:00
|
|
|
lfb_memsize = inportl(0x1CF);
|
2018-03-16 15:56:19 +03:00
|
|
|
}
|
2021-06-02 07:14:26 +03:00
|
|
|
finalize_graphics("vbox");
|
2018-09-28 15:15:30 +03:00
|
|
|
}
|
2018-03-16 15:56:19 +03:00
|
|
|
|
2021-05-31 04:47:02 +03:00
|
|
|
extern struct multiboot * mboot_struct;
|
2018-03-16 15:56:19 +03:00
|
|
|
|
2021-05-31 04:47:02 +03:00
|
|
|
static void graphics_install_preset(uint16_t w, uint16_t h) {
|
2018-09-28 14:29:40 +03:00
|
|
|
/* Extract framebuffer information from multiboot */
|
2021-05-31 04:47:02 +03:00
|
|
|
lfb_vid_memory = mmu_map_from_physical(mboot_struct->framebuffer_addr);
|
|
|
|
lfb_resolution_x = mboot_struct->framebuffer_width;
|
|
|
|
lfb_resolution_y = mboot_struct->framebuffer_height;
|
|
|
|
lfb_resolution_s = mboot_struct->framebuffer_pitch;
|
2018-09-28 15:15:30 +03:00
|
|
|
lfb_resolution_b = 32;
|
2021-06-13 09:58:20 +03:00
|
|
|
|
|
|
|
/* Make sure memsize is actually big enough */
|
|
|
|
size_t minsize = lfb_resolution_s * lfb_resolution_y * 4;
|
|
|
|
if (lfb_memsize < minsize) lfb_memsize = minsize;
|
|
|
|
|
2018-09-28 15:15:30 +03:00
|
|
|
finalize_graphics("preset");
|
2018-03-16 15:56:19 +03:00
|
|
|
}
|
|
|
|
|
2021-05-31 04:47:02 +03:00
|
|
|
static void check_multiboot(void) {
|
|
|
|
if ((mboot_struct->flags & MULTIBOOT_FLAG_FB) && (mboot_struct->framebuffer_width)) {
|
|
|
|
PREFERRED_W = mboot_struct->framebuffer_width;
|
|
|
|
PREFERRED_H = mboot_struct->framebuffer_height;
|
2018-10-21 14:32:02 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-16 15:56:19 +03:00
|
|
|
#define SVGA_IO_BASE (vmware_io)
|
|
|
|
#define SVGA_IO_MUL 1
|
|
|
|
#define SVGA_INDEX_PORT 0
|
|
|
|
#define SVGA_VALUE_PORT 1
|
|
|
|
|
|
|
|
#define SVGA_REG_ID 0
|
|
|
|
#define SVGA_REG_ENABLE 1
|
|
|
|
#define SVGA_REG_WIDTH 2
|
|
|
|
#define SVGA_REG_HEIGHT 3
|
|
|
|
#define SVGA_REG_BITS_PER_PIXEL 7
|
|
|
|
#define SVGA_REG_BYTES_PER_LINE 12
|
|
|
|
#define SVGA_REG_FB_START 13
|
|
|
|
|
|
|
|
static uint32_t vmware_io = 0;
|
|
|
|
|
|
|
|
static void vmware_scan_pci(uint32_t device, uint16_t v, uint16_t d, void * extra) {
|
|
|
|
if ((v == 0x15ad && d == 0x0405)) {
|
|
|
|
uintptr_t t = pci_read_field(device, PCI_BAR0, 4);
|
|
|
|
if (t > 0) {
|
|
|
|
*((uint8_t **)extra) = (uint8_t *)(t & 0xFFFFFFF0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vmware_write(int reg, int value) {
|
|
|
|
outportl(SVGA_IO_MUL * SVGA_INDEX_PORT + SVGA_IO_BASE, reg);
|
|
|
|
outportl(SVGA_IO_MUL * SVGA_VALUE_PORT + SVGA_IO_BASE, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t vmware_read(int reg) {
|
|
|
|
outportl(SVGA_IO_MUL * SVGA_INDEX_PORT + SVGA_IO_BASE, reg);
|
|
|
|
return inportl(SVGA_IO_MUL * SVGA_VALUE_PORT + SVGA_IO_BASE);
|
|
|
|
}
|
|
|
|
|
2018-09-28 15:15:30 +03:00
|
|
|
static void vmware_set_resolution(uint16_t w, uint16_t h) {
|
2018-03-16 15:56:19 +03:00
|
|
|
vmware_write(SVGA_REG_ENABLE, 0);
|
|
|
|
vmware_write(SVGA_REG_ID, 0);
|
|
|
|
vmware_write(SVGA_REG_WIDTH, w);
|
|
|
|
vmware_write(SVGA_REG_HEIGHT, h);
|
|
|
|
vmware_write(SVGA_REG_BITS_PER_PIXEL, 32);
|
|
|
|
vmware_write(SVGA_REG_ENABLE, 1);
|
|
|
|
|
|
|
|
uint32_t bpl = vmware_read(SVGA_REG_BYTES_PER_LINE);
|
|
|
|
|
|
|
|
lfb_resolution_x = w;
|
|
|
|
lfb_resolution_s = bpl;
|
|
|
|
lfb_resolution_y = h;
|
2018-09-28 15:15:30 +03:00
|
|
|
lfb_resolution_b = 32;
|
2018-03-16 15:56:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void graphics_install_vmware(uint16_t w, uint16_t h) {
|
|
|
|
pci_scan(vmware_scan_pci, -1, &vmware_io);
|
|
|
|
|
|
|
|
if (!vmware_io) {
|
2021-05-31 04:47:02 +03:00
|
|
|
printf("vmware video, but no device found?\n");
|
2018-03-16 15:56:19 +03:00
|
|
|
return;
|
|
|
|
} else {
|
2021-05-31 04:47:02 +03:00
|
|
|
printf("vmware io base: %p\n", (void*)(uintptr_t)vmware_io);
|
2018-03-16 15:56:19 +03:00
|
|
|
}
|
|
|
|
|
2018-09-28 15:15:30 +03:00
|
|
|
vmware_set_resolution(w,h);
|
|
|
|
lfb_resolution_impl = &vmware_set_resolution;
|
2018-03-16 15:56:19 +03:00
|
|
|
|
2021-05-31 04:47:02 +03:00
|
|
|
uintptr_t fb_addr = vmware_read(SVGA_REG_FB_START);
|
|
|
|
printf("vmware fb address: %p\n", (void*)fb_addr);
|
2018-03-16 15:56:19 +03:00
|
|
|
|
2021-05-31 04:47:02 +03:00
|
|
|
size_t fb_size = vmware_read(15);
|
2018-03-16 15:56:19 +03:00
|
|
|
|
2021-05-31 04:47:02 +03:00
|
|
|
printf("vmware fb size: 0x%lx\n", fb_size);
|
2018-03-16 15:56:19 +03:00
|
|
|
|
2021-05-31 04:47:02 +03:00
|
|
|
lfb_vid_memory = mmu_map_from_physical(fb_addr);
|
2018-03-16 15:56:19 +03:00
|
|
|
|
2018-09-28 15:15:30 +03:00
|
|
|
finalize_graphics("vmware");
|
2018-03-16 15:56:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
struct disp_mode {
|
|
|
|
int16_t x;
|
|
|
|
int16_t y;
|
|
|
|
int set;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void auto_scan_pci(uint32_t device, uint16_t v, uint16_t d, void * extra) {
|
|
|
|
struct disp_mode * mode = extra;
|
|
|
|
if (mode->set) return;
|
|
|
|
if ((v == 0x1234 && d == 0x1111) ||
|
2018-03-19 10:04:17 +03:00
|
|
|
(v == 0x10de && d == 0x0a20)) {
|
2018-03-16 15:56:19 +03:00
|
|
|
mode->set = 1;
|
|
|
|
graphics_install_bochs(mode->x, mode->y);
|
2021-06-02 07:14:26 +03:00
|
|
|
} else if (v == 0x80EE && d == 0xBEEF) {
|
|
|
|
mode->set = 1;
|
|
|
|
graphics_install_vbox(mode->x, mode->y);
|
2018-03-16 15:56:19 +03:00
|
|
|
} else if ((v == 0x15ad && d == 0x0405)) {
|
|
|
|
mode->set = 1;
|
|
|
|
graphics_install_vmware(mode->x, mode->y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-10 03:35:48 +03:00
|
|
|
static fs_node_t * vga_text_device = NULL;
|
|
|
|
|
|
|
|
static int ioctl_vga(fs_node_t * node, unsigned long request, void * argp) {
|
|
|
|
switch (request) {
|
|
|
|
case IO_VID_WIDTH:
|
|
|
|
/* Get framebuffer width */
|
|
|
|
validate(argp);
|
|
|
|
*((size_t *)argp) = 80;
|
|
|
|
return 0;
|
|
|
|
case IO_VID_HEIGHT:
|
|
|
|
/* Get framebuffer height */
|
|
|
|
validate(argp);
|
|
|
|
*((size_t *)argp) = 25;
|
|
|
|
return 0;
|
|
|
|
case IO_VID_ADDR:
|
|
|
|
/* Map framebuffer into userspace process */
|
|
|
|
validate(argp);
|
|
|
|
{
|
|
|
|
uintptr_t vga_user_offset;
|
|
|
|
if (*(uintptr_t*)argp == 0) {
|
|
|
|
vga_user_offset = 0x100000000;
|
|
|
|
} else {
|
|
|
|
validate((void*)(*(uintptr_t*)argp));
|
|
|
|
vga_user_offset = *(uintptr_t*)argp;
|
|
|
|
}
|
|
|
|
for (uintptr_t i = 0; i < 0x1000; i += 0x1000) {
|
|
|
|
union PML * page = mmu_get_page(vga_user_offset + i, MMU_GET_MAKE);
|
|
|
|
mmu_frame_map_address(page,MMU_FLAG_WRITABLE|MMU_FLAG_WC,((uintptr_t)(0xB8000) & 0xFFFFFFFF) + i);
|
|
|
|
}
|
|
|
|
*((uintptr_t *)argp) = vga_user_offset;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vga_text_init(void) {
|
|
|
|
vga_text_device = calloc(sizeof(fs_node_t), 1);
|
|
|
|
snprintf(vga_text_device->name, 100, "vga0");
|
|
|
|
vga_text_device->length = 0;
|
|
|
|
vga_text_device->flags = FS_BLOCKDEVICE;
|
|
|
|
vga_text_device->mask = 0660;
|
|
|
|
vga_text_device->ioctl = ioctl_vga;
|
|
|
|
vfs_mount("/dev/vga0", vga_text_device);
|
|
|
|
}
|
|
|
|
|
2021-05-31 04:47:02 +03:00
|
|
|
static int lfb_init(const char * c) {
|
2018-10-27 10:18:09 +03:00
|
|
|
char * arg = strdup(c);
|
|
|
|
char * argv[10];
|
|
|
|
int argc = tokenize(arg, ",", argv);
|
|
|
|
|
|
|
|
uint16_t x, y;
|
|
|
|
if (argc < 3) {
|
2021-05-31 04:47:02 +03:00
|
|
|
check_multiboot();
|
2018-10-27 10:18:09 +03:00
|
|
|
x = PREFERRED_W;
|
|
|
|
y = PREFERRED_H;
|
|
|
|
} else {
|
|
|
|
x = atoi(argv[1]);
|
|
|
|
y = atoi(argv[2]);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ret_val = 0;
|
|
|
|
if (!strcmp(argv[0], "auto")) {
|
|
|
|
/* Attempt autodetection */
|
|
|
|
struct disp_mode mode = {x,y,0};
|
|
|
|
pci_scan(auto_scan_pci, -1, &mode);
|
|
|
|
if (!mode.set) {
|
|
|
|
graphics_install_preset(x,y);
|
|
|
|
}
|
|
|
|
} else if (!strcmp(argv[0], "qemu")) {
|
|
|
|
/* Bochs / Qemu Video Device */
|
|
|
|
graphics_install_bochs(x,y);
|
|
|
|
} else if (!strcmp(argv[0],"vmware")) {
|
|
|
|
/* VMware SVGA */
|
|
|
|
graphics_install_vmware(x,y);
|
|
|
|
} else if (!strcmp(argv[0],"preset")) {
|
|
|
|
/* Set by bootloader (UEFI) */
|
|
|
|
graphics_install_preset(x,y);
|
2021-06-10 03:35:48 +03:00
|
|
|
} else if (!strcmp(argv[0],"text")) {
|
|
|
|
/* VGA text mode? TODO: We should try to detect this,
|
|
|
|
* or limit it to things that are likely to have it... */
|
|
|
|
vga_text_init();
|
2018-10-27 10:18:09 +03:00
|
|
|
} else {
|
|
|
|
ret_val = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(arg);
|
|
|
|
return ret_val;
|
|
|
|
}
|
|
|
|
|
2021-05-31 04:47:02 +03:00
|
|
|
int framebuffer_initialize(void) {
|
|
|
|
lfb_init(args_present("vid") ? args_value("vid") : "auto");
|
2018-10-27 10:18:09 +03:00
|
|
|
|
2018-03-16 15:56:19 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|