Kernel: Make BochsVGADevice a BlockDevice and support mmapping it.

Currently you can only mmap the entire framebuffer.
Using this when starting up the WindowServer gets us yet another step
closer towards it moving into userspace. :^)
This commit is contained in:
Andreas Kling 2019-02-16 09:57:42 +01:00
parent 2dc7c5a7b0
commit 799177feda
Notes: sideshowbarker 2024-07-19 15:42:11 +09:00
15 changed files with 155 additions and 26 deletions

View file

@ -6,6 +6,11 @@ class BlockDevice : public Device {
public:
virtual ~BlockDevice() override;
virtual Region* mmap(Process&, LinearAddress preferred_laddr, size_t offset, size_t size) = 0;
protected:
BlockDevice(unsigned major, unsigned minor) : Device(major, minor) { }
private:
virtual bool is_block_device() const final { return true; }
};

View file

@ -1,6 +1,8 @@
#include <Kernel/BochsVGADevice.h>
#include <Kernel/IO.h>
#include <Kernel/PCI.h>
#include <Kernel/MemoryManager.h>
#include <Kernel/Process.h>
#define VBE_DISPI_IOPORT_INDEX 0x01CE
#define VBE_DISPI_IOPORT_DATA 0x01CF
@ -27,11 +29,25 @@ BochsVGADevice& BochsVGADevice::the()
}
BochsVGADevice::BochsVGADevice()
: BlockDevice(82, 413)
{
s_the = this;
m_framebuffer_address = PhysicalAddress(find_framebuffer_address());
}
Region* BochsVGADevice::mmap(Process& process, LinearAddress preferred_laddr, size_t offset, size_t size)
{
ASSERT(offset == 0);
ASSERT(size == framebuffer_size_in_bytes());
auto framebuffer_vmo = VMObject::create_framebuffer_wrapper(framebuffer_address(), framebuffer_size_in_bytes());
auto* region = process.allocate_region_with_vmo(preferred_laddr, framebuffer_size_in_bytes(), move(framebuffer_vmo), 0, "BochsVGADevice Framebuffer", true, true);
kprintf("BochsVGADevice::mmap for %s(%u) mapped region %p for fb addr %p\n",
process.name().characters(), process.pid(),
region, framebuffer_address());
ASSERT(region);
return region;
}
void BochsVGADevice::set_register(word index, word data)
{
IO::out16(VBE_DISPI_IOPORT_INDEX, index);
@ -48,6 +64,8 @@ void BochsVGADevice::set_resolution(int width, int height)
set_register(VBE_DISPI_INDEX_BPP, 32);
set_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);
set_register(VBE_DISPI_INDEX_BANK, 0);
m_framebuffer_size = { width, height };
}
void BochsVGADevice::set_y_offset(int offset)
@ -68,3 +86,23 @@ dword BochsVGADevice::find_framebuffer_address()
});
return framebuffer_address;
}
bool BochsVGADevice::can_read(Process&) const
{
ASSERT_NOT_REACHED();
}
bool BochsVGADevice::can_write(Process&) const
{
ASSERT_NOT_REACHED();
}
ssize_t BochsVGADevice::read(Process&, byte*, size_t)
{
ASSERT_NOT_REACHED();
}
ssize_t BochsVGADevice::write(Process&, const byte*, size_t)
{
ASSERT_NOT_REACHED();
}

View file

@ -2,11 +2,11 @@
#include <AK/Types.h>
#include <AK/AKString.h>
#include <SharedGraphics/Size.h>
#include <Kernel/types.h>
#include <Kernel/BlockDevice.h>
// FIXME: This should be a BlockDevice once we have BlockDevice.
class BochsVGADevice {
class BochsVGADevice final : public BlockDevice {
AK_MAKE_ETERNAL
public:
static BochsVGADevice& the();
@ -17,9 +17,22 @@ public:
void set_resolution(int width, int height);
void set_y_offset(int);
virtual Region* mmap(Process&, LinearAddress preferred_laddr, size_t offset, size_t) override;
size_t framebuffer_size_in_bytes() const { return m_framebuffer_size.area() * sizeof(dword) * 2; }
Size framebuffer_size() const { return m_framebuffer_size; }
private:
virtual const char* class_name() const override { return "BochsVGADevice"; }
virtual bool can_read(Process&) const override;
virtual bool can_write(Process&) const override;
virtual ssize_t read(Process&, byte*, size_t) override;
virtual ssize_t write(Process&, const byte*, size_t) override;
void set_register(word index, word value);
dword find_framebuffer_address();
PhysicalAddress m_framebuffer_address;
Size m_framebuffer_size;
};

View file

@ -8,4 +8,7 @@ public:
protected:
CharacterDevice(unsigned major, unsigned minor) : Device(major, minor) { }
private:
virtual bool is_character_device() const final { return true; }
};

View file

@ -35,6 +35,9 @@ public:
uid_t uid() const { return m_uid; }
uid_t gid() const { return m_gid; }
virtual bool is_block_device() const { return false; }
virtual bool is_character_device() const { return false; }
protected:
Device(unsigned major, unsigned minor) : m_major(major), m_minor(minor) { }
void set_uid(uid_t uid) { m_uid = uid; }

View file

@ -358,11 +358,16 @@ InodeMetadata Ext2FSInode::metadata() const
metadata.block_size = fs().block_size();
metadata.block_count = m_raw_inode.i_blocks;
if (::is_block_device(m_raw_inode.i_mode) || ::is_character_device(m_raw_inode.i_mode)) {
if (::is_character_device(m_raw_inode.i_mode)) {
unsigned dev = m_raw_inode.i_block[0];
metadata.major_device = (dev & 0xfff00) >> 8;
metadata.minor_device = (dev & 0xff) | ((dev >> 12) & 0xfff00);
}
if (::is_block_device(m_raw_inode.i_mode)) {
unsigned dev = m_raw_inode.i_block[1];
metadata.major_device = (dev & 0xfff00) >> 8;
metadata.minor_device = (dev & 0xff) | ((dev >> 12) & 0xfff00);
}
return metadata;
}

View file

@ -8,6 +8,8 @@
#include "TTY.h"
#include "MasterPTY.h"
#include <Kernel/Socket.h>
#include <Kernel/Process.h>
#include <Kernel/BlockDevice.h>
RetainPtr<FileDescriptor> FileDescriptor::create(RetainPtr<Inode>&& inode)
{
@ -338,3 +340,49 @@ InodeMetadata FileDescriptor::metadata() const
return m_inode->metadata();
return { };
}
bool FileDescriptor::supports_mmap() const
{
if (m_inode)
return true;
if (m_device)
return m_device->is_block_device();
return false;
}
Region* FileDescriptor::mmap(Process& process, LinearAddress laddr, size_t offset, size_t size, int prot)
{
ASSERT(supports_mmap());
if (is_block_device())
return static_cast<BlockDevice&>(*m_device).mmap(process, laddr, offset, size);
ASSERT(m_inode);
// FIXME: If PROT_EXEC, check that the underlying file system isn't mounted noexec.
auto region_name = absolute_path();
InterruptDisabler disabler;
// FIXME: Implement mapping at a client-specified address. Most of the support is already in plcae.
ASSERT(laddr.as_ptr() == nullptr);
auto* region = process.allocate_file_backed_region(LinearAddress(), size, inode(), move(region_name), prot & PROT_READ, prot & PROT_WRITE);
return region;
}
bool FileDescriptor::is_block_device() const
{
return m_device && m_device->is_block_device();
}
bool FileDescriptor::is_character_device() const
{
return m_device && m_device->is_character_device();
}
CharacterDevice* FileDescriptor::character_device()
{
return is_character_device() ? static_cast<CharacterDevice*>(device()) : nullptr;
}
const CharacterDevice* FileDescriptor::character_device() const
{
return is_character_device() ? static_cast<const CharacterDevice*>(device()) : nullptr;
}

View file

@ -12,6 +12,8 @@
class TTY;
class MasterPTY;
class Process;
class Region;
class CharacterDevice;
class FileDescriptor : public Retainable<FileDescriptor> {
public:
@ -43,9 +45,14 @@ public:
bool is_directory() const;
bool is_character_device() const { return m_device.ptr(); }
Device* character_device() { return m_device.ptr(); }
const Device* character_device() const { return m_device.ptr(); }
bool is_device() const { return m_device.ptr(); }
Device* device() { return m_device.ptr(); }
const Device* device() const { return m_device.ptr(); }
bool is_block_device() const;
bool is_character_device() const;
CharacterDevice* character_device();
const CharacterDevice* character_device() const;
bool is_tty() const;
const TTY* tty() const;
@ -59,7 +66,8 @@ public:
Inode* inode() { return m_inode.ptr(); }
const Inode* inode() const { return m_inode.ptr(); }
bool supports_mmap() const { return m_inode && !m_device; }
bool supports_mmap() const;
Region* mmap(Process&, LinearAddress, size_t offset, size_t, int prot);
bool is_blocking() const { return m_is_blocking; }
void set_blocking(bool b) { m_is_blocking = b; }

View file

@ -30,6 +30,7 @@ struct InodeMetadata {
bool is_directory() const { return ::is_directory(mode); }
bool is_character_device() const { return ::is_character_device(mode); }
bool is_block_device() const { return ::is_block_device(mode); }
bool is_device() const { return is_character_device() || is_block_device(); }
bool is_regular_file() const { return ::is_regular_file(mode); }
bool is_fifo() const { return ::is_fifo(mode); }
bool is_symlink() const { return ::is_symlink(mode); }

View file

@ -182,12 +182,7 @@ void* Process::sys$mmap(const Syscall::SC_mmap_params* params)
return (void*)-EBADF;
if (!descriptor->supports_mmap())
return (void*)-ENODEV;
// FIXME: If PROT_EXEC, check that the underlying file system isn't mounted noexec.
auto region_name = descriptor->absolute_path();
InterruptDisabler disabler;
// FIXME: Implement mapping at a client-specified address. Most of the support is already in plcae.
ASSERT(addr == nullptr);
auto* region = allocate_file_backed_region(LinearAddress(), size, descriptor->inode(), move(region_name), prot & PROT_READ, prot & PROT_WRITE);
auto* region = descriptor->mmap(*this, LinearAddress((dword)addr), offset, size, prot);
if (!region)
return (void*)-ENOMEM;
return region->laddr().as_ptr();
@ -2240,13 +2235,6 @@ DisplayInfo Process::set_video_resolution(int width, int height)
info.height = height;
info.bpp = 32;
info.pitch = width * 4;
size_t framebuffer_size = width * height * 4 * 2;
if (!m_display_framebuffer_region) {
auto framebuffer_vmo = VMObject::create_framebuffer_wrapper(BochsVGADevice::the().framebuffer_address(), framebuffer_size);
m_display_framebuffer_region = allocate_region_with_vmo(LinearAddress(0xe0000000), framebuffer_size, move(framebuffer_vmo), 0, "framebuffer", true, true);
}
info.framebuffer = m_display_framebuffer_region->laddr().as_ptr();
BochsVGADevice::the().set_resolution(width, height);
return info;
}

View file

@ -44,7 +44,6 @@ struct DisplayInfo {
unsigned height;
unsigned bpp;
unsigned pitch;
byte* framebuffer;
};
class Process : public InlineLinkedListNode<Process>, public Weakable<Process> {
@ -295,6 +294,9 @@ public:
int gui_client_id() const { return (int)this; }
Region* allocate_region_with_vmo(LinearAddress, size_t, RetainPtr<VMObject>&&, size_t offset_in_vmo, String&& name, bool is_readable, bool is_writable);
Region* allocate_file_backed_region(LinearAddress, size_t, RetainPtr<Inode>&&, String&& name, bool is_readable, bool is_writable);
private:
friend class MemoryManager;
friend class Scheduler;
@ -366,8 +368,6 @@ private:
TTY* m_tty { nullptr };
Region* allocate_region(LinearAddress, size_t, String&& name, bool is_readable = true, bool is_writable = true, bool commit = true);
Region* allocate_file_backed_region(LinearAddress, size_t, RetainPtr<Inode>&&, String&& name, bool is_readable, bool is_writable);
Region* allocate_region_with_vmo(LinearAddress, size_t, RetainPtr<VMObject>&&, size_t offset_in_vmo, String&& name, bool is_readable, bool is_writable);
bool deallocate_region(Region& region);
Region* region_from_range(LinearAddress, size_t);

View file

@ -145,7 +145,7 @@ RetainPtr<FileDescriptor> VFS::open(const String& path, int& error, int options,
if (!inode)
return nullptr;
auto metadata = inode->metadata();
if (!(options & O_DONT_OPEN_DEVICE) && metadata.is_character_device()) {
if (!(options & O_DONT_OPEN_DEVICE) && metadata.is_device()) {
auto it = m_devices.find(encoded_device(metadata.major_device, metadata.minor_device));
if (it == m_devices.end()) {
kprintf("VFS::open: no such device %u,%u\n", metadata.major_device, metadata.minor_device);

View file

@ -86,6 +86,8 @@ VFS* vfs;
vfs->register_device(*tty2);
vfs->register_device(*tty3);
vfs->register_device(BochsVGADevice::the());
auto dev_hd0 = IDEDiskDevice::create();
auto e2fs = Ext2FS::create(dev_hd0.copy_ref());
e2fs->initialize();

View file

@ -16,6 +16,7 @@ mkdir -vp mnt/tmp
chmod 1777 mnt/tmp
mkdir -vp mnt/dev
mkdir -vp mnt/dev/pts
mknod mnt/dev/bxvga b 82 413
mknod mnt/dev/tty0 c 4 0
mknod mnt/dev/tty1 c 4 1
mknod mnt/dev/tty2 c 4 2

View file

@ -16,7 +16,21 @@ void WindowServer_main()
dbgprintf("Screen is %ux%ux%ubpp\n", info.width, info.height, info.bpp);
WSScreen screen((dword*)info.framebuffer, info.width, info.height);
int bxvga_fd = current->sys$open("/dev/bxvga", O_RDWR);
ASSERT(bxvga_fd >= 0);
Syscall::SC_mmap_params params;
memset(&params, 0, sizeof(params));
params.fd = bxvga_fd;
params.prot = PROT_READ | PROT_WRITE;
params.flags = MAP_SHARED;
params.size = info.width * info.height * sizeof(RGBA32) * 2;
params.offset = 0;
kprintf("Calling sys$mmap in WS\n");
void* framebuffer = current->sys$mmap(&params);
ASSERT(framebuffer && framebuffer != (void*)-1);
WSScreen screen((dword*)framebuffer, info.width, info.height);
WSWindowManager::the();