Add framebuffer devices to /dev and mmap() for them

This commit is contained in:
Mark
2020-08-05 01:54:13 +03:00
parent 25f0a0987f
commit 415fd613a2
7 changed files with 151 additions and 45 deletions
+3 -2
View File
@@ -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);
+6
View File
@@ -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);
+1
View File
@@ -7,4 +7,5 @@
struct ioc_vmode {
uint32_t width;
uint32_t height;
size_t pitch, bpp;
};
+3 -3
View File
@@ -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;
}
}
+84
View File
@@ -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 <stddef.h>
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);
+3
View File
@@ -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();
+51 -40
View File
@@ -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;