Add framebuffer devices to /dev and mmap() for them
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -7,4 +7,5 @@
|
||||
struct ioc_vmode {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
size_t pitch, bpp;
|
||||
};
|
||||
|
||||
+3
-3
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user