From 415fd613a2e4eac3931cefc41593f5fda8445763 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 5 Aug 2020 01:54:13 +0300 Subject: [PATCH] Add framebuffer devices to /dev and mmap() for them --- include/sys/block/blk.h | 5 ++- include/sys/display.h | 6 +++ include/user/video.h | 1 + sys/block/blk.c | 6 +-- sys/display.c | 84 +++++++++++++++++++++++++++++++++++++ sys/kernel.c | 3 ++ sys/mem/shmem.c | 91 +++++++++++++++++++++++------------------ 7 files changed, 151 insertions(+), 45 deletions(-) diff --git a/include/sys/block/blk.h b/include/sys/block/blk.h index dfe4a06..b769559 100644 --- a/include/sys/block/blk.h +++ b/include/sys/block/blk.h @@ -18,7 +18,8 @@ struct blkdev { ssize_t (*read) (struct blkdev *blk, void *buf, size_t off, size_t count); ssize_t (*write) (struct blkdev *blk, const void *buf, size_t off, size_t count); int (*ioctl) (struct blkdev *blk, unsigned long req, void *ptr); - void *(*mmap) (struct blkdev *blk, struct ofile *fd, void *hint, size_t length, int flags); + + int (*mmap)(struct blkdev *blk, uintptr_t base, size_t page_count, int prot, int flags); void (*destroy) (struct blkdev *blk); }; @@ -29,7 +30,7 @@ void blk_sync(struct blkdev *blk); int blk_page_sync(struct blkdev *blk, uintptr_t address, uintptr_t page); void blk_sync_all(void); -void *blk_mmap(struct blkdev *blk, struct ofile *fd, void *hint, size_t length, int flags); +int blk_mmap(struct blkdev *blk, uintptr_t base, size_t page_count, int prot, int flags); ssize_t blk_read(struct blkdev *blk, void *buf, size_t off, size_t count); ssize_t blk_write(struct blkdev *blk, const void *buf, size_t off, size_t count); int blk_ioctl(struct blkdev *blk, unsigned long req, void *ptr); diff --git a/include/sys/display.h b/include/sys/display.h index dab973a..b96be77 100644 --- a/include/sys/display.h +++ b/include/sys/display.h @@ -1,4 +1,5 @@ #pragma once +#include "sys/block/blk.h" #include "sys/types.h" #include "sys/list.h" @@ -6,6 +7,7 @@ #define DISP_GRAPHIC (1 << 0) // Has a linear framebuffer #define DISP_LFB (1 << 1) +#define DISP_LOCK (1 << 2) struct display { // Text modes: set character at y, x @@ -24,6 +26,9 @@ struct display { // Private data void *data; + // If DISP_LFB + struct blkdev blk; + struct list_head list; }; @@ -31,6 +36,7 @@ extern int g_display_blink_state; void display_add(struct display *d); struct display *display_create(void); +void display_postinit(void); void display_setc(struct display *disp, uint16_t y, uint16_t x, uint16_t ch); diff --git a/include/user/video.h b/include/user/video.h index 094cdea..efc755f 100644 --- a/include/user/video.h +++ b/include/user/video.h @@ -7,4 +7,5 @@ struct ioc_vmode { uint32_t width; uint32_t height; + size_t pitch, bpp; }; diff --git a/sys/block/blk.c b/sys/block/blk.c index 89cff8d..9e271cc 100644 --- a/sys/block/blk.c +++ b/sys/block/blk.c @@ -69,12 +69,12 @@ void blk_sync_all(void) { } } -void *blk_mmap(struct blkdev *blk, struct ofile *of, void *hint, size_t length, int flags) { +int blk_mmap(struct blkdev *blk, uintptr_t base, size_t page_count, int prot, int flags) { _assert(blk); if (blk->mmap) { - return blk->mmap(blk, of, hint, length, flags); + return blk->mmap(blk, base, page_count, prot, flags); } else { - return (void *) -EINVAL; + return -EINVAL; } } diff --git a/sys/display.c b/sys/display.c index 7087c40..ff963d4 100644 --- a/sys/display.c +++ b/sys/display.c @@ -1,8 +1,16 @@ +#include "sys/mem/vmalloc.h" +#include "arch/amd64/cpu.h" +#include "sys/mem/phys.h" #include "sys/font/psf.h" #include "sys/display.h" #include "sys/console.h" +#include "sys/thread.h" #include "sys/assert.h" +#include "user/video.h" +#include "user/errno.h" +#include "user/mman.h" #include "sys/debug.h" +#include "sys/dev.h" #include int g_display_blink_state = 0; @@ -28,6 +36,57 @@ static uint32_t rgb_map[16] = { 0xFFFFFF, }; +static int display_blk_ioctl(struct blkdev *blk, unsigned long cmd, void *arg) { + struct display *disp = blk->dev_data; + _assert(disp); + + switch (cmd) { + case IOC_GETVMODE: + _assert(arg); + ((struct ioc_vmode *) arg)->width = disp->width_pixels; + ((struct ioc_vmode *) arg)->height = disp->height_pixels; + ((struct ioc_vmode *) arg)->pitch = disp->pitch; + ((struct ioc_vmode *) arg)->bpp = disp->bpp; + return 0; + default: + return -EINVAL; + } +} + +static int display_blk_mmap(struct blkdev *blk, uintptr_t base, size_t page_count, int prot, int flags) { + struct process *proc; + struct display *disp; + uintptr_t phys; + uint64_t pf; + + proc = thread_self->proc; + _assert(proc); + disp = blk->dev_data; + _assert(disp); + + if (page_count > (disp->pitch * disp->height_pixels + MM_PAGE_SIZE - 1) / MM_PAGE_SIZE) { + return -EINVAL; + } + + pf = 0; + if (prot & PROT_WRITE) { + pf |= MM_PAGE_WRITE; + } + + // TODO: prevent others (e.g. console) from writing to the memory + // while it's mapped + + phys = MM_PHYS(disp->framebuffer); + for (size_t i = 0; i < page_count; ++i) { + mm_map_single(proc->space, + base + i * MM_PAGE_SIZE, + phys + i * MM_PAGE_SIZE, + pf | MM_PAGE_USER, PU_DEVICE); + } + + return 0; +} + struct display *display_get_default(void) { if (list_empty(&displays)) { return NULL; @@ -45,8 +104,33 @@ void display_add(struct display *disp) { list_add(&disp->list, &displays); } +void display_postinit(void) { + char name[4] = "fbX"; + static char cnt = '0'; + struct display *disp; + + list_for_each_entry(disp, &displays, list) { + if (disp->flags & DISP_LFB) { + + disp->blk.flags = 0; + disp->blk.dev_data = disp; + disp->blk.read = NULL; + disp->blk.write = NULL; + disp->blk.ioctl = display_blk_ioctl; + disp->blk.mmap = display_blk_mmap; + + name[2] = cnt++; + + dev_add(DEV_CLASS_BLOCK, DEV_BLOCK_OTHER, &disp->blk, name); + } + } +} + void display_setc(struct display *disp, uint16_t y, uint16_t x, uint16_t ch) { if (disp->flags & DISP_GRAPHIC) { + if (disp->flags & DISP_LOCK) { + return; + } psf_draw(disp, y, x, ch & 0xFF, rgb_map[(ch >> 8) & 0xF], rgb_map[(ch >> 12) & 0xF]); } else { _assert(disp->setc); diff --git a/sys/kernel.c b/sys/kernel.c index 72e699f..bdfbc47 100644 --- a/sys/kernel.c +++ b/sys/kernel.c @@ -4,6 +4,7 @@ #include "drivers/usb/usb.h" #include "sys/char/tty.h" #include "sys/console.h" +#include "sys/display.h" #include "sys/assert.h" #include "sys/sched.h" #include "sys/panic.h" @@ -35,6 +36,8 @@ void main(void) { vfs_init(); tty_init(); + display_postinit(); + sysfs_populate(); syscall_init(); diff --git a/sys/mem/shmem.c b/sys/mem/shmem.c index 6d8f40c..552b953 100644 --- a/sys/mem/shmem.c +++ b/sys/mem/shmem.c @@ -15,8 +15,7 @@ #include "fs/node.h" #include "sys/mm.h" -static void *sys_mmap_anon(void *hint, size_t page_count, int prot, int flags) { - mm_space_t space; +static int sys_mmap_anon(mm_space_t space, uintptr_t base, size_t page_count, int prot, int flags) { uint64_t map_flags = MM_PAGE_USER; int map_usage = PU_PRIVATE; @@ -27,35 +26,6 @@ static void *sys_mmap_anon(void *hint, size_t page_count, int prot, int flags) { map_usage = PU_SHARED; } - _assert(thread_self && thread_self->proc); - space = thread_self->proc->space; - _assert(space); - - uintptr_t virt_base; - - if (flags & MAP_FIXED) { - virt_base = (uintptr_t) hint; - - // Treat hint as an exact address - if (virt_base == 0 || (virt_base & MM_PAGE_OFFSET_MASK)) { - return (void *) -EINVAL; - } - - // TODO: check for hint + page_count * PAGE_SIZE overflow - // Check that any of the pages in that range are already taken - for (size_t i = 0; i < page_count; ++i) { - if (mm_map_get(space, virt_base + i * MM_PAGE_SIZE, 0) != MM_NADDR) { - return (void *) -EINVAL; - } - } - // Good to proceed - } else { - virt_base = vmfind(space, 0x100000000, 0x400000000, page_count); - _assert(virt_base != MM_NADDR); - } - - kdebug("mmaping %u pages at %p\n", page_count, virt_base); - // Map pages for (size_t i = 0; i < page_count; ++i) { uintptr_t phys = mm_phys_alloc_page(); @@ -64,23 +34,67 @@ static void *sys_mmap_anon(void *hint, size_t page_count, int prot, int flags) { _assert(page); page->flags |= PG_MMAPED; - _assert(mm_map_single(space, virt_base + i * MM_PAGE_SIZE, phys, map_flags, map_usage) == 0); + _assert(mm_map_single(space, base + i * MM_PAGE_SIZE, phys, map_flags, map_usage) == 0); } - return (void *) virt_base; + return 0; +} + +static uintptr_t mmap_findmem(mm_space_t space, void *hint, size_t page_count, int flags) { + uintptr_t virt_base; + + if (flags & MAP_FIXED) { + virt_base = (uintptr_t) hint; + + // Treat hint as an exact address + if (virt_base == 0 || (virt_base & MM_PAGE_OFFSET_MASK)) { + return MM_NADDR; + } + + // TODO: check for hint + page_count * PAGE_SIZE overflow + // Check that any of the pages in that range are already taken + for (size_t i = 0; i < page_count; ++i) { + if (mm_map_get(space, virt_base + i * MM_PAGE_SIZE, 0) != MM_NADDR) { + return MM_NADDR; + } + } + // Good to proceed + } else { + virt_base = vmfind(space, 0x100000000, 0x400000000, page_count); + _assert(virt_base != MM_NADDR); + } + + return virt_base; } void *sys_mmap(void *hint, size_t length, int prot, int flags, int fd, off_t off) { size_t page_count; + mm_space_t space; + uintptr_t base; + long res; + + _assert(thread_self && thread_self->proc); + space = thread_self->proc->space; + _assert(space); if (length & MM_PAGE_OFFSET_MASK) { return (void *) -EINVAL; } page_count = length / MM_PAGE_SIZE; + // Allocate the virtual pages first + base = mmap_findmem(space, hint, page_count, flags); + + if (base == MM_NADDR) { + return (void *) -ENOMEM; + } + if (flags & MAP_ANONYMOUS) { // Anonymous mapping - return sys_mmap_anon(hint, page_count, prot, flags); + if ((res = sys_mmap_anon(space, base, page_count, prot, flags)) != 0) { + return (void *) res; + } + return (void *) base; } else { // File/device-backed mapping if (fd < 0 || fd >= THREAD_MAX_FDS) { @@ -103,13 +117,10 @@ void *sys_mmap(void *hint, size_t length, int prot, int flags, int fd, off_t off case VN_BLK: { struct blkdev *blk = vn->dev; - _assert(blk); - - if (!blk->mmap) { - return (void *) -EINVAL; + if ((res = blk_mmap(blk, base, page_count, prot, flags)) != 0) { + return (void *) res; } - - return blk->mmap(blk, of, hint, length, flags); + return (void *) base; } default: return (void *) -EINVAL;