From ec956306a8beb49f73740fa9654596567a864426 Mon Sep 17 00:00:00 2001 From: Matheus Date: Mon, 10 Jul 2023 21:15:40 -0300 Subject: [PATCH] fb: expose framebuffer devices to userspace --- kernel-src/arch/x86-64/main.c | 2 + kernel-src/include/kernel/devfs.h | 1 + kernel-src/include/kernel/fb.h | 6 + kernel-src/io/fb.c | 233 ++++++++++++++++++++++++++++++ 4 files changed, 242 insertions(+) create mode 100644 kernel-src/include/kernel/fb.h diff --git a/kernel-src/arch/x86-64/main.c b/kernel-src/arch/x86-64/main.c index a6ebd121..1c0e7a5a 100644 --- a/kernel-src/arch/x86-64/main.c +++ b/kernel-src/arch/x86-64/main.c @@ -24,6 +24,7 @@ #include #include #include +#include static cpu_t bsp_cpu; @@ -66,6 +67,7 @@ void kernel_entry() { pseudodevices_init(); keyboard_init(); arch_ps2_init(); + fb_init(); console_init(); logging_sethook(console_putc); diff --git a/kernel-src/include/kernel/devfs.h b/kernel-src/include/kernel/devfs.h index 94d86f6e..14b52833 100644 --- a/kernel-src/include/kernel/devfs.h +++ b/kernel-src/include/kernel/devfs.h @@ -9,6 +9,7 @@ #define DEV_MAJOR_FULL 2 #define DEV_MAJOR_ZERO 3 #define DEV_MAJOR_CONSOLE 4 +#define DEV_MAJOR_FB 5 typedef struct { int (*open)(int minor, int flags); diff --git a/kernel-src/include/kernel/fb.h b/kernel-src/include/kernel/fb.h new file mode 100644 index 00000000..610e7e51 --- /dev/null +++ b/kernel-src/include/kernel/fb.h @@ -0,0 +1,6 @@ +#ifndef _FB_H +#define _FB_h + +void fb_init(); + +#endif diff --git a/kernel-src/io/fb.c b/kernel-src/io/fb.c index 96231d73..4bdcdb92 100644 --- a/kernel-src/io/fb.c +++ b/kernel-src/io/fb.c @@ -1,6 +1,239 @@ #include +#include +#include +#include +#include +#include +#include +#include + +typedef struct bitfield_t { + uint32_t offset; + uint32_t length; + uint32_t msb_right; +} bitfield_t; + +typedef struct { + uint32_t xres; + uint32_t yres; + uint32_t xres_virtual; + uint32_t yres_virtual; + uint32_t xoffset; + uint32_t yoffset; + uint32_t bits_per_pixel; + uint32_t grayscale; + bitfield_t red; + bitfield_t green; + bitfield_t blue; + bitfield_t transp; + uint32_t nonstd; + uint32_t activate; + uint32_t height; + uint32_t width; + uint32_t accel_flags; + uint32_t pixclock; + uint32_t left_margin; + uint32_t right_margin; + uint32_t upper_margin; + uint32_t lower_margin; + uint32_t hsync_len; + uint32_t vsync_len; + uint32_t sync; + uint32_t vmode; + uint32_t rotate; + uint32_t colorspace; + uint32_t reserved[4]; +} varinfo_t; + +typedef struct { + char id[16]; + unsigned long smem_start; + uint32_t smem_len; + uint32_t type; + uint32_t type_aux; + uint32_t visual; + uint16_t xpanstep; + uint16_t ypanstep; + uint16_t ywrapstep; + uint32_t line_length; + unsigned long mmio_start; + uint32_t mmio_len; + uint32_t accel; + uint16_t capabilities; + uint16_t reserved[2]; +} fixinfo_t; + +#define FB_VISUAL_TRUECOLOR 2 +#define FB_TYPE_PACKED_PIXELS 0 volatile struct limine_framebuffer_request fb_liminereq = { .id = LIMINE_FRAMEBUFFER_REQUEST, .revision = 0 }; + +static size_t count; +static struct limine_framebuffer **fbs; +static fixinfo_t *fixinfos; +static varinfo_t *varinfos; + +static int read(int minor, void *buffer, size_t size, uintmax_t offset, int flags, size_t *readc) { + if (minor >= count) + return ENODEV; + + uintmax_t end = fbs[minor]->pitch * fbs[minor]->height; + *readc = 0; + + if (offset >= end) + return 0; + + if(size + offset > end) + size = end - offset; + + memcpy(buffer, (void *)((uintptr_t)fbs[minor]->address + offset), size); + *readc = size; + + return 0; +} + +static int write(int minor, void *buffer, size_t size, uintmax_t offset, int flags, size_t *writec) { + if (minor >= count) + return ENODEV; + + uintmax_t end = fbs[minor]->pitch * fbs[minor]->height; + *writec = 0; + + if (offset >= end) + return 0; + + if(size + offset > end) + size = end - offset; + + memcpy((void *)((uintptr_t)fbs[minor]->address + offset), buffer, size); + *writec = size; + + return 0; +} + +static int mmap(int minor, void *addr, uintmax_t offset, int flags) { + if (minor >= count) + return ENODEV; + + uintmax_t end = fbs[minor]->pitch * fbs[minor]->height; + __assert(offset < end); + + void *paddr = NULL; + + if (flags & V_FFLAGS_SHARED) { + // make sure offset is ALWAYS page aligned when doing a shared mapping + __assert((offset % PAGE_SIZE) == 0); + return arch_mmu_map(_cpu()->vmmctx->pagetable, FROM_HHDM((void *)((uintptr_t)fbs[minor]->address + offset)), addr, vnodeflagstommuflags(flags)) ? 0 : ENOMEM; + } else { + size_t size = offset + PAGE_SIZE < end ? PAGE_SIZE : end - offset; + paddr = pmm_alloc(1, PMM_SECTION_DEFAULT); + if (paddr == NULL) + return ENOMEM; + + memcpy(MAKE_HHDM(paddr), (void *)((uintptr_t)fbs[minor]->address + offset), size); + + if (arch_mmu_map(_cpu()->vmmctx->pagetable, paddr, addr, vnodeflagstommuflags(flags)) == false) { + pmm_free(paddr, 1); + return ENOMEM; + } + + return 0; + } +} + +static int munmap(int minor, void *addr, uintmax_t offset, int flags) { + void *phys = arch_mmu_getphysical(_cpu()->vmmctx->pagetable, addr); + arch_mmu_unmap(_cpu()->vmmctx->pagetable, addr); + if ((flags & V_FFLAGS_SHARED) == 0) { + pmm_free(phys, 1); + } + + return 0; +} + +#define FBIOGET_VSCREENINFO 0x4600 +#define FBIOPUT_VSCREENINFO 0x4601 +#define FBIOGET_FSCREENINFO 0x4602 +#define FBIOBLANK 0x4611 + +static int ioctl(int minor, unsigned long request, void *arg, int *result){ + if(minor >= count) + return ENODEV; + + switch (request) { + case FBIOGET_FSCREENINFO: + *(fixinfo_t *)arg = fixinfos[minor]; + break; + case FBIOGET_VSCREENINFO: + *(varinfo_t *)arg = varinfos[minor]; + break; + default: + return ENOTTY; + } + + return 0; +} + +static devops_t ops = { + .read = read, + .write = write, + .mmap = mmap, + .munmap = munmap, + .ioctl = ioctl +}; + +void fb_init() { + if (fb_liminereq.response == NULL || fb_liminereq.response->framebuffer_count == 0) { + printf("fb: no framebuffer devices\n"); + return; + } + + fbs = fb_liminereq.response->framebuffers; + count = fb_liminereq.response->framebuffer_count; + printf("fb: %d framebuffer device%c\n", count, count > 1 ? 's' : ' '); + + fixinfos = alloc(count * sizeof(fixinfo_t)); + varinfos = alloc(count * sizeof(varinfo_t)); + __assert(fixinfos && varinfos); + + for (int i = 0; i < count; ++i) { + struct limine_framebuffer* fb = fbs[i]; + + varinfo_t* vi = &varinfos[i]; + vi->xres = fb->width; + vi->yres = fb->height; + vi->xres_virtual = fb->width; + vi->yres_virtual = fb->height; + vi->bits_per_pixel = fb->bpp; + vi->red.msb_right = 1; + vi->green.msb_right = 1; + vi->blue.msb_right = 1; + vi->transp.msb_right = 1; + vi->red.offset = fb->red_mask_shift; + vi->red.length = fb->red_mask_size; + vi->blue.offset = fb->blue_mask_shift; + vi->blue.length = fb->blue_mask_size; + vi->green.offset = fb->green_mask_shift; + vi->green.length = fb->green_mask_size; + vi->height = -1; + vi->width = -1; + + fixinfo_t* fi = &fixinfos[i]; + strcpy(fi->id, "LIMINE FB"); + fi->smem_len = fb->pitch * fb->height; + fi->type = FB_TYPE_PACKED_PIXELS; + fi->visual = FB_VISUAL_TRUECOLOR; + fi->line_length = fb->pitch; + fi->mmio_len = fb->pitch * fb->height; + + char name[6]; + snprintf(name, 6, "fb%d", i); + + __assert(devfs_register(&ops, name, V_TYPE_CHDEV, DEV_MAJOR_FB, i, 0644) == 0); + + printf("%s: %dx%d %d bpp\n", name, fb->width, fb->height, fb->bpp); + } +}