From ee13ffa715311cb494c3daaf90c5aecdb7d9dfa6 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Tue, 25 Mar 2014 20:28:51 +0100 Subject: [PATCH] Refactor kernel graphics support. --- kernel/Makefile | 3 +- kernel/bga.cpp | 585 ---------------------- kernel/dispmsg.cpp | 342 ------------- kernel/dispmsg.h | 36 -- kernel/gpu/bga/bga.cpp | 528 +++++++++++++++++++ kernel/{ => gpu/bga}/bga.h | 8 +- kernel/include/sortix/display.h | 6 +- kernel/include/sortix/kernel/video.h | 48 +- kernel/kernel.cpp | 6 +- kernel/video.cpp | 723 +++++++++++++-------------- 10 files changed, 893 insertions(+), 1392 deletions(-) delete mode 100644 kernel/bga.cpp delete mode 100644 kernel/dispmsg.cpp delete mode 100644 kernel/dispmsg.h create mode 100644 kernel/gpu/bga/bga.cpp rename kernel/{ => gpu/bga}/bga.h (88%) diff --git a/kernel/Makefile b/kernel/Makefile index e6182b23..a6a528ec 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -75,7 +75,6 @@ $(CPUOBJS) \ addralloc.o \ alarm.o \ ata.o \ -bga.o \ calltrace.o \ clock.o \ com.o \ @@ -85,7 +84,6 @@ $(CPU)/kthread.o \ crc32.o \ debugger.o \ descriptor.o \ -dispmsg.o \ dtable.o \ elf.o \ fcache.o \ @@ -96,6 +94,7 @@ fs/null.o \ fs/user.o \ fs/util.o \ fs/zero.o \ +gpu/bga/bga.o \ identity.o \ initrd.o \ inode.o \ diff --git a/kernel/bga.cpp b/kernel/bga.cpp deleted file mode 100644 index 98b2989e..00000000 --- a/kernel/bga.cpp +++ /dev/null @@ -1,585 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2012. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - bga.cpp - Driver for the Bochs VBE Extensions. - -*******************************************************************************/ - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "x86-family/memorymanagement.h" -#include "lfbtextbuffer.h" -#include "bga.h" - -namespace Sortix { -namespace BGA { - -const bool TEST_RES_BY_TRYING = false; - -const uint16_t VBE_DISPI_INDEX_ID = 0; -const uint16_t VBE_DISPI_INDEX_XRES = 1; -const uint16_t VBE_DISPI_INDEX_YRES = 2; -const uint16_t VBE_DISPI_INDEX_BPP = 3; -const uint16_t VBE_DISPI_INDEX_ENABLE = 4; -const uint16_t VBE_DISPI_INDEX_BANK = 5; -const uint16_t VBE_DISPI_INDEX_VIRT_WIDTH = 6; -const uint16_t VBE_DISPI_INDEX_VIRT_HEIGHT = 7; -const uint16_t VBE_DISPI_INDEX_X_OFFSET = 8; -const uint16_t VBE_DISPI_INDEX_Y_OFFSET = 9; - -const uint16_t VBE_DISPI_IOPORT_INDEX = 0x01CE; -const uint16_t VBE_DISPI_IOPORT_DATA = 0x01CF; - -const uint16_t VBE_DISPI_BPP_4 = 0x04; -const uint16_t VBE_DISPI_BPP_8 = 0x08; -const uint16_t VBE_DISPI_BPP_15 = 0x0F; -const uint16_t VBE_DISPI_BPP_16 = 0x10; -const uint16_t VBE_DISPI_BPP_24 = 0x18; -const uint16_t VBE_DISPI_BPP_32 = 0x20; - -const uint16_t VBE_DISPI_DISABLED = 0x00; -const uint16_t VBE_DISPI_ENABLED = 0x01; -const uint16_t VBE_DISPI_GETCAPS = 0x02; -const uint16_t VBE_DISPI_8BIT_DAC = 0x20; -const uint16_t VBE_DISPI_LFB_ENABLED = 0x40; -const uint16_t VBE_DISPI_NOCLEARMEM = 0x80; - -const uint16_t VBE_MIN_SUP_VERSION = 0xB0C0; -const uint16_t VBE_MIN_POS_VERSION = 0xB0C0; -const uint16_t VBE_MAX_POS_VERSION = 0xB0CF; - -const size_t VBE_BANK_SIZE = 64UL * 1024UL; -volatile uint8_t* const VBE_VIDEO_MEM = (volatile uint8_t*) 0xA0000; - -static addr_t ParseDevBar0(uint32_t devaddr) -{ - pcibar_t bar = PCI::GetBAR(devaddr, 0); - return bar.addr(); -} - -addr_t DetectBGAFramebuffer() -{ - uint32_t devaddr; - pcifind_t pcifind; - - // Search for the bochs BGA device and compatible. - memset(&pcifind, 255, sizeof(pcifind)); - pcifind.vendorid = 0x1234; - pcifind.deviceid = 0x1111; - if ( (devaddr = PCI::SearchForDevices(pcifind, 0)) ) - return ParseDevBar0(devaddr); - - // Search for a generic VGA compatible device. - memset(&pcifind, 255, sizeof(pcifind)); - pcifind.classid = 0x03; - pcifind.subclassid = 0x00; - pcifind.progif = 0x00; - if ( (devaddr = PCI::SearchForDevices(pcifind)) ) - return ParseDevBar0(devaddr); - - return 0; -} - -uint16_t version; -uint16_t maxbpp; -uint16_t maxxres; -uint16_t maxyres; - -uint16_t curbpp; -uint16_t curxres; -uint16_t curyres; -uint16_t curbank; - -addr_t bgaframebuffer; - -void WriteRegister(uint16_t index, uint16_t value) -{ - outport16(VBE_DISPI_IOPORT_INDEX, index); - outport16(VBE_DISPI_IOPORT_DATA, value); -} - -uint16_t ReadRegister(uint16_t index) -{ - outport16(VBE_DISPI_IOPORT_INDEX, index); - return inport16(VBE_DISPI_IOPORT_DATA); -} - -uint16_t GetCapability(uint16_t index) -{ - uint16_t wasenabled = ReadRegister(VBE_DISPI_INDEX_ENABLE); - WriteRegister(VBE_DISPI_INDEX_ENABLE, wasenabled | VBE_DISPI_GETCAPS); - uint16_t cap = ReadRegister(index); - WriteRegister(VBE_DISPI_INDEX_ENABLE, wasenabled); - return cap; -} - -bool SetVideoMode(uint16_t width, uint16_t height, uint16_t depth, bool keep) -{ - bool uselinear = true; - WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED); - WriteRegister(VBE_DISPI_INDEX_XRES, width); - WriteRegister(VBE_DISPI_INDEX_YRES, height); - WriteRegister(VBE_DISPI_INDEX_BPP, depth); - WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | - (uselinear ? VBE_DISPI_LFB_ENABLED : 0) | - (keep ? VBE_DISPI_NOCLEARMEM : 0)); - curbpp = depth; - curxres = width; - curyres = height; - return true; -} - -bool IsStandardResolution(uint16_t width, uint16_t height, uint16_t depth) -{ - if ( depth != VBE_DISPI_BPP_32 ) { return false; } - if ( width == 800 && height == 600 ) { return true; } - if ( width == 1024 && height == 768 ) { return true; } - if ( width == 1280 && height == 720 ) { return true; } - if ( width == 1280 && height == 1024 ) { return true; } - if ( width == 1600 && height == 900 ) { return true; } - if ( width == 1920 && height == 1080 ) { return true; } - return false; -} - -// TODO: Need a better method of detecting available/desired resolutions. -bool SupportsResolution(uint16_t width, uint16_t height, uint16_t depth) -{ - if ( !width || !height || !depth ) { return false; } - if ( maxxres < width || maxyres < height || maxbpp < depth ) return false; - if ( width % 8U ) { return false; } - uint16_t wasenabled = ReadRegister(VBE_DISPI_INDEX_ENABLE); - if ( width == curxres && height == curyres && depth == curbpp) return true; - if ( !TEST_RES_BY_TRYING ) { return true; } - SetVideoMode(width, height, depth, true); - uint16_t newxres = ReadRegister(VBE_DISPI_INDEX_XRES); - uint16_t newyres = ReadRegister(VBE_DISPI_INDEX_XRES); - uint16_t newbpp = ReadRegister(VBE_DISPI_INDEX_BPP); - bool result = newxres != curxres || newyres != curyres || newbpp != curbpp; - SetVideoMode(curxres, curyres, curbpp, true); - WriteRegister(VBE_DISPI_INDEX_ENABLE, wasenabled); - return result; -} - -class BGADriver : public VideoDriver -{ -public: - BGADriver(); - virtual ~BGADriver(); - -public: - virtual bool StartUp(); - virtual bool ShutDown(); - virtual char* GetCurrentMode() const; - virtual bool SwitchMode(const char* mode); - virtual bool Supports(const char* mode) const; - virtual char** GetModes(size_t* nummodes) const; - virtual off_t FrameSize() const; - virtual ssize_t WriteAt(off_t off, const void* buf, size_t count); - virtual ssize_t ReadAt(off_t off, void* buf, size_t count); - virtual TextBuffer* CreateTextBuffer(); - -private: - bool MapVideoMemory(size_t size); - bool MapVideoMemoryRange(addr_t mapat, size_t from, size_t to); - bool IncreaseVirtual(size_t new_size); - bool DetectModes() const; - -private: - mutable size_t nummodes; - mutable char** modes; - char* curmode; - size_t lfbmapped; - size_t framesize; - addralloc_t addr_allocation; - -}; - -BGADriver::BGADriver() -{ - nummodes = 0; - modes = NULL; - curmode = NULL; - lfbmapped = 0; - framesize = 0; - memset(&addr_allocation, 0, sizeof(addr_allocation)); -} - -BGADriver::~BGADriver() -{ - MapVideoMemory(0); - FreeKernelAddress(&addr_allocation); - for ( size_t i = 0; i < nummodes; i++ ) - { - delete[] modes[i]; - } - delete[] modes; - delete[] curmode; -} - -bool BGADriver::MapVideoMemoryRange(addr_t mapat, size_t from, size_t to) -{ - addr_t phys = bgaframebuffer; - const addr_t mtype = Memory::PAT_WC; - for ( size_t i = from; i < to; i += Page::Size() ) - if ( !Memory::MapPAT(phys+i, mapat+i, PROT_KWRITE | PROT_KREAD, mtype) ) - { - Log::PrintF("Error: Insufficient memory to map BGA framebuffer " - "onto kernel address space: needed 0x%zx bytes but " - "only 0x%zx was available at this point.\n", to, i); - for ( size_t n = from; n < i; n += Page::Size() ) - Memory::Unmap(mapat + n); - return false; - } - return true; -} - -bool BGADriver::IncreaseVirtual(size_t new_size) -{ - new_size = Page::AlignUp(new_size); - assert(addr_allocation.size < new_size); - - addralloc_t new_addralloc; - if ( !AllocateKernelAddress(&new_addralloc, new_size) ) - { - Log::PrintF("Error: Insufficient virtual address space for BGA " - "frame of size 0x%zx bytes, only 0x%zx was available.\n", - new_size, addr_allocation.size); - return false; - } - - addr_t old_mapat = addr_allocation.from; - addr_t new_mapat = new_addralloc.from; - - if ( !MapVideoMemoryRange(new_mapat, 0, new_size) ) - { - FreeKernelAddress(&addr_allocation); - return false; - } - - for ( size_t i = 0; i < lfbmapped; i += Page::Size() ) - Memory::Unmap(old_mapat + i); - - FreeKernelAddress(&addr_allocation); - - lfbmapped = new_size; - addr_allocation = new_addralloc; - - Memory::Flush(); - return true; -} - -bool BGADriver::MapVideoMemory(size_t size) -{ - size = Page::AlignUp(size); - - if ( size == lfbmapped ) - return true; - - if ( addr_allocation.size < size ) - return IncreaseVirtual(size); - - addr_t mapat = addr_allocation.from; - for ( size_t i = size; i < lfbmapped; i+= Page::Size() ) - Memory::Unmap(mapat + i); - - lfbmapped = size; - Memory::Flush(); - - if ( !size ) - FreeKernelAddress(&addr_allocation); - - return true; -} - -bool BGADriver::StartUp() -{ - if ( !modes && !DetectModes() ) { return false; } - return true; -} - -bool BGADriver::ShutDown() -{ - MapVideoMemory(0); - if ( curmode ) - { - delete[] curmode; curmode = NULL; - errno = ENOSYS; - return false; // TODO: Return to VGA Text Mode. - } - return true; -} - -char* BGADriver::GetCurrentMode() const -{ - if ( !curmode ) { errno = EINVAL; return NULL; } - return String::Clone(curmode); -} - -bool BGADriver::SwitchMode(const char* mode) -{ - bool result = false; - char* modeclone = String::Clone(mode); - if ( !modeclone ) - return NULL; - char* xstr = NULL; - char* ystr = NULL; - char* bppstr = NULL; - if ( !ReadParamString(mode, "width", &xstr, "height", &ystr, - "bpp", &bppstr, "STOP") ) - { - delete[] modeclone; - return false; - } - uint16_t xres = xstr ? atoi(xstr) : 0; - uint16_t yres = ystr ? atoi(ystr) : 0; - uint16_t bpp = bppstr ? atoi(bppstr) : 32; - size_t newframesize = (size_t) xres * (size_t) yres * (size_t) bpp/8UL; - - // If the current resolution uses more memory than the new one, keep it - // around in case setting the video mode failed. - if ( MapVideoMemory(newframesize < lfbmapped ? lfbmapped : newframesize) && - SetVideoMode(xres, yres, bpp, false) ) - { - delete[] curmode; - curmode = modeclone; - modeclone = NULL; - // We can now truncate the amount of memory to what we really need. - MapVideoMemory(framesize = newframesize); - result = true; - } - - delete[] xstr; - delete[] ystr; - delete[] bppstr; - delete[] modeclone; - return result; -} - -bool BGADriver::Supports(const char* mode) const -{ - char* xstr = NULL; - char* ystr = NULL; - char* bppstr = NULL; - if ( !ReadParamString(mode, "width", &xstr, "height", &ystr, - "bpp", &bppstr, NULL, NULL) ) { return false; } - uint16_t xres = xstr ? atoi(xstr) : 0; - uint16_t yres = ystr ? atoi(ystr) : 0; - uint16_t bpp = bppstr ? atoi(bppstr) : 0; - bool result = SupportsResolution(xres, yres, bpp); - delete[] xstr; - delete[] ystr; - delete[] bppstr; - return result; -} - -char** BGADriver::GetModes(size_t* retnum) const -{ - if ( !modes && !DetectModes() ) { return NULL; } - char** result = new char*[nummodes]; - if ( !result ) { return NULL; } - for ( size_t i = 0; i < nummodes; i++ ) - { - result[i] = String::Clone(modes[i]); - if ( !result[i] ) - { - for ( size_t n = 0; n < i; i++ ) { delete[] result[n]; } - delete[] result; - return NULL; - } - } - *retnum = nummodes; - return result; -} - -off_t BGADriver::FrameSize() const -{ - return curxres * curyres * (curbpp / 8UL); -} - -ssize_t BGADriver::WriteAt(off_t off, const void* buf, size_t count) -{ - uint8_t* frame = (uint8_t*) addr_allocation.from; - if ( (off_t) framesize <= off ) - return 0; - if ( framesize < off + count ) - count = framesize - off; - memcpy(frame + off, buf, count); - return count; -} - -ssize_t BGADriver::ReadAt(off_t off, void* buf, size_t count) -{ - const uint8_t* frame = (const uint8_t*) addr_allocation.from; - if ( (off_t) framesize <= off ) - return 0; - if ( framesize < off + count ) - count = framesize - off; - memcpy(buf, frame + off, count); - return count; -} - -bool BGADriver::DetectModes() const -{ - nummodes = 0; - unsigned bpp = VBE_DISPI_BPP_32; - for ( unsigned w = 0; w < maxxres; w += 4U ) - { - for ( unsigned h = 0; h < maxyres; h += 4UL ) - { - if ( !IsStandardResolution(w, h, bpp) ) { continue; } - if ( !SupportsResolution(w, h, bpp) ) { continue; } - nummodes++; - } - } - modes = new char*[nummodes]; - if ( !modes ) { return false; } - memset(modes, 0, sizeof(char*) * nummodes); - size_t curmodeid = 0; - for ( unsigned w = 0; w < maxxres; w += 4U ) - { - for ( unsigned h = 0; h < maxyres; h += 4UL ) - { - if ( !IsStandardResolution(w, h, bpp) ) { continue; } - if ( !SupportsResolution(w, h, bpp) ) { continue; } - char bppstr[64]; - char xresstr[64]; - char yresstr[64]; - snprintf(bppstr, 64, "%u", bpp); - snprintf(xresstr, 64, "%u", w); - snprintf(yresstr, 64, "%u", h); - char* modestr = String::Combine(6, "width=", xresstr, ",height=", - yresstr, ",bpp=", bppstr); - if ( !modestr ) { return false; } - modes[curmodeid++] = modestr; - } - } - return true; -} - -TextBuffer* BGADriver::CreateTextBuffer() -{ - uint8_t* lfb = (uint8_t*) addr_allocation.from; - uint32_t lfbformat = curbpp; - size_t scansize = curxres * curbpp / 8UL; - return CreateLFBTextBuffer(lfb, lfbformat, curxres, curyres, scansize); -} - -static uint16_t ProbeBGAVersion() -{ - // First see if the register is in the legal range. - uint16_t ver = ReadRegister(VBE_DISPI_INDEX_ID); - if ( ver < VBE_MIN_POS_VERSION ) - return 0; - if ( ver > VBE_MAX_POS_VERSION ) - return 0; - - // The bootloader or BIOS may have set the current version to less than what - // really is supported. If we a version number to the register, we can read - // it back only if it is supported. - - // If the register accepts an invalid version number, don't trust it and we - // may be in danger if an unrelated type of register is using this IO port. - // Since that is unlikely, just assume we got a real BGA device. - WriteRegister(VBE_DISPI_INDEX_ID, 0xFFFF); - if ( ReadRegister(VBE_DISPI_INDEX_ID) == 0xFFFF ) - { - WriteRegister(VBE_DISPI_INDEX_ID, ver); - Log::PrintF("Warning: Found what appears to be BGA hardware, but it " - "behaves differently when attempting to scan what version " - "it conforms to. "); - if ( ver < VBE_MIN_SUP_VERSION ) - { - Log::PrintF("The hardware is by default set to an old unsupported " - "version, this driver will not use it.\n"); - return 0; - } - Log::PrintF("The driver will use this hardware (even though it may not " - "be BGA hardware) and bad things may happen.\n"); - return ver; - } - - // Attempt to query all possible version ids. - for ( uint16_t i = ver; i < VBE_MAX_POS_VERSION; i++ ) - { - WriteRegister(VBE_DISPI_INDEX_ID, i); - if ( ReadRegister(VBE_DISPI_INDEX_ID) == i ) - ver = i; - } - - return ver; -} - -void Init() -{ - if ( !(version = ProbeBGAVersion()) ) - return; - - curbpp = 0; - curxres = 0; - curyres = 0; - curbank = 0xFFFF; - - maxbpp = GetCapability(VBE_DISPI_INDEX_BPP); - maxxres = GetCapability(VBE_DISPI_INDEX_XRES); - maxyres = GetCapability(VBE_DISPI_INDEX_YRES); - - if ( !(bgaframebuffer = DetectBGAFramebuffer()) ) - { - Log::PrintF("BGA support detected but no PCI device could be found " - "determines the location of the framebuffer. Rather than " - "guessing it is at 0xE0000000, this driver shuts down to " - "avoid corrupting possible memory there.\n"); - return; - } - - BGADriver* bgadriver = new BGADriver; - if ( !bgadriver ) - { - Log::PrintF("Unable to allocate BGA driver, but hardware present\n"); - return; - } - - if ( !Video::RegisterDriver("bga", bgadriver) ) - { - Log::PrintF("Unable to register BGA driver, but hardware present\n"); - delete bgadriver; - return; - } -} - -} // namespace BGA -} // namespace Sortix diff --git a/kernel/dispmsg.cpp b/kernel/dispmsg.cpp deleted file mode 100644 index dc54eada..00000000 --- a/kernel/dispmsg.cpp +++ /dev/null @@ -1,342 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2012. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - dispmsg.cpp - User-space message-based interface for video framework access. - -*******************************************************************************/ - -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include "dispmsg.h" - -namespace Sortix { -namespace DisplayMessage { - -const uint64_t ONE_AND_ONLY_DEVICE = 0; -const uint64_t ONE_AND_ONLY_CONNECTOR = 0; - -__attribute__((unused)) -static char* StringOfCrtcMode(struct dispmsg_crtc_mode mode) -{ - char bppstr[sizeof(mode.fb_format) * 3]; - char xresstr[sizeof(mode.view_xres) * 3]; - char yresstr[sizeof(mode.view_yres) * 3]; - char magicstr[sizeof(mode.magic) * 3]; - snprintf(bppstr, sizeof(bppstr), "%ju", (uintmax_t) mode.fb_format); - snprintf(xresstr, sizeof(xresstr), "%ju", (uintmax_t) mode.view_xres); - snprintf(yresstr, sizeof(yresstr), "%ju", (uintmax_t) mode.view_yres); - snprintf(magicstr, sizeof(magicstr), "%ju", (uintmax_t) mode.magic); - char* drivername = Video::GetDriverName(mode.driver_index); - if ( !drivername ) - return NULL; - char* ret = String::Combine(10, - "driver=", drivername, "," - "bpp=", bppstr, "," - "width=", xresstr, "," - "height=", yresstr, "," - "modeid=", magicstr); - delete[] drivername; - return ret; -} - -__attribute__((unused)) -struct dispmsg_crtc_mode CrtcModeOfString(const char* string, uint64_t driverid) -{ - char* xstr = NULL; - char* ystr = NULL; - char* bppstr = NULL; - char* modeidstr = NULL; - char* unsupportedstr = NULL; - struct dispmsg_crtc_mode ret; - ret.control = 0; - if ( !strcmp(string, "driver=none") ) - return ret; - if ( !ReadParamString(string, - "width", &xstr, - "height", &ystr, - "bpp", &bppstr, - "modeid", &modeidstr, - "unsupported", &unsupportedstr, - NULL, NULL) ) - return ret; - ret.driver_index = driverid; - ret.view_xres = xstr ? atol(xstr) : 0; - delete[] xstr; - ret.view_yres = ystr ? atol(ystr) : 0; - delete[] ystr; - ret.fb_format = bppstr ? atol(bppstr) : 0; - delete[] bppstr; - ret.magic = modeidstr ? atoll(modeidstr) : 0; - delete[] modeidstr; - if ( !unsupportedstr ) - ret.control = 1; - delete[] unsupportedstr; - return ret; -} - -__attribute__((unused)) -static bool TransmitString(struct dispmsg_string* dest, const char* str) -{ - size_t size = strlen(str) + 1; - size_t dest_size = dest->byte_size; - dest->byte_size = size; - if ( dest_size < size ) - return errno = ERANGE, false; - strcpy(dest->str, str); - return true; -} - -__attribute__((unused)) -static char* ReceiveString(struct dispmsg_string* src) -{ - if ( !src->byte_size ) -bad_input: - return errno = EINVAL, (char*) NULL; - char* ret = new char[src->byte_size]; - if ( !ret ) - return NULL; - memcpy(ret, src->str, src->byte_size); - if ( ret[src->byte_size-1] != '\0' ) { delete[] ret; goto bad_input; } - return ret; -} - -static int EnumerateDevices(void* ptr, size_t size) -{ - if ( size != sizeof(struct dispmsg_enumerate_devices) ) - return errno = EINVAL, -1; - struct dispmsg_enumerate_devices* msg = - (struct dispmsg_enumerate_devices*) ptr; - // TODO: HACK: Only one device is currently supported by our backend. - size_t num_devices = 1; - if ( msg->devices_length < num_devices ) - { - msg->devices_length = num_devices; - return errno = ERANGE, -1; - } - msg->devices[0] = ONE_AND_ONLY_DEVICE; - return 0; -} - -static int GetDriverCount(void* ptr, size_t size) -{ - if ( size != sizeof(struct dispmsg_get_driver_count) ) - return errno = EINVAL, -1; - struct dispmsg_get_driver_count* msg = - (struct dispmsg_get_driver_count*) ptr; - if ( msg->device != ONE_AND_ONLY_DEVICE ) - return errno = EINVAL, -1; - msg->driver_count = Video::GetNumDrivers(); - return 0; -} - -static int GetDriverName(void* ptr, size_t size) -{ - if ( size != sizeof(struct dispmsg_get_driver_name) ) - return errno = EINVAL, -1; - struct dispmsg_get_driver_name* msg = - (struct dispmsg_get_driver_name* ) ptr; - if ( msg->device != ONE_AND_ONLY_DEVICE ) - return errno = EINVAL, -1; - char* name = Video::GetDriverName(msg->driver_index); - if ( !name ) - return -1; - bool success = TransmitString(&msg->name, name); - delete[] name; - return success ? 0 : -1; -} - -static int GetDriver(void* ptr, size_t size) -{ - if ( size != sizeof(struct dispmsg_get_driver) ) - return errno = EINVAL, -1; - struct dispmsg_get_driver* msg = (struct dispmsg_get_driver*) ptr; - if ( msg->device != ONE_AND_ONLY_DEVICE ) - return errno = EINVAL, -1; - msg->driver_index = Video::GetCurrentDriverIndex(); - return 0; -} - -static int SetDriver(void* ptr, size_t size) -{ - if ( size != sizeof(struct dispmsg_set_driver) ) - return errno = EINVAL, -1; - struct dispmsg_set_driver* msg = (struct dispmsg_set_driver*) ptr; - if ( msg->device != ONE_AND_ONLY_DEVICE ) - return errno = EINVAL, -1; - return errno = ENOSYS, -1; -} - -static int SetCrtcMode(void* ptr, size_t size) -{ - if ( size != sizeof(struct dispmsg_set_crtc_mode) ) - return errno = EINVAL, -1; - struct dispmsg_set_crtc_mode* msg = (struct dispmsg_set_crtc_mode*) ptr; - if ( msg->device != ONE_AND_ONLY_DEVICE ) - return errno = EINVAL, -1; - if ( msg->connector != ONE_AND_ONLY_CONNECTOR ) - return errno = EINVAL, -1; - char* modestr = StringOfCrtcMode(msg->mode); - if ( !modestr ) - return -1; - bool success = Video::SwitchMode(modestr); - delete[] modestr; - return success ? 0 : -1; -} - -static int GetCrtcMode(void* ptr, size_t size) -{ - if ( size != sizeof(struct dispmsg_get_crtc_mode) ) - return errno = EINVAL, -1; - struct dispmsg_get_crtc_mode* msg = (struct dispmsg_get_crtc_mode*) ptr; - if ( msg->device != ONE_AND_ONLY_DEVICE ) - return errno = EINVAL, -1; - if ( msg->connector != ONE_AND_ONLY_CONNECTOR ) - return errno = EINVAL, -1; - char* modestr = Video::GetCurrentMode(); - if ( !modestr ) - return -1; - msg->mode = CrtcModeOfString(modestr, Video::GetCurrentDriverIndex()); - delete[] modestr; - return 0; -} - -static int GetCrtcModes(void* ptr, size_t size) -{ - if ( size != sizeof(struct dispmsg_get_crtc_modes) ) - return errno = EINVAL, -1; - struct dispmsg_get_crtc_modes* msg = (struct dispmsg_get_crtc_modes*) ptr; - if ( msg->device != ONE_AND_ONLY_DEVICE ) - return errno = EINVAL, -1; - if ( msg->connector != ONE_AND_ONLY_CONNECTOR ) - return errno = EINVAL, -1; - size_t nummodes; - char** modes = Video::GetModes(&nummodes); - if ( !modes ) - return -1; - size_t dest_length = msg->modes_length; - - int ret; - if ( nummodes <= dest_length ) - { - ret = 0; - for ( size_t i = 0; i < nummodes; i++ ) - { - const char* modestr = modes[i]; - uint64_t driver_index = Video::LookupDriverIndexOfMode(modestr); - msg->modes[i] = CrtcModeOfString(modestr, driver_index); - } - } - else - { - errno = ERANGE; - ret = -1; - } - msg->modes_length = nummodes; - for ( size_t i = 0; i < nummodes; i++ ) - delete[] modes[i]; - delete[] modes; - return ret; -} - -static int GetMemorySize(void* ptr, size_t size) -{ - if ( size != sizeof(struct dispmsg_get_memory_size) ) - return errno = EINVAL, -1; - struct dispmsg_get_memory_size* msg = - (struct dispmsg_get_memory_size*) ptr; - msg->memory_size = Video::FrameSize(); - return 0; -} - -static int WriteMemory(void* ptr, size_t size) -{ - if ( size != sizeof(struct dispmsg_write_memory) ) - return errno = EINVAL, -1; - struct dispmsg_write_memory* msg = (struct dispmsg_write_memory*) ptr; - if ( msg->device != ONE_AND_ONLY_DEVICE ) - return errno = EINVAL, -1; - if ( OFF_MAX < msg->offset ) - return errno = EOVERFLOW, -1; - off_t offset = msg->offset; - if ( Video::WriteAt(offset, msg->src, msg->size) < 0 ) - return -1; - return 0; -} - -static int ReadMemory(void* ptr, size_t size) -{ - if ( size != sizeof(struct dispmsg_read_memory) ) - return errno = EINVAL, -1; - struct dispmsg_read_memory* msg = (struct dispmsg_read_memory*) ptr; - if ( msg->device != ONE_AND_ONLY_DEVICE ) - return errno = EINVAL, -1; - if ( OFF_MAX < msg->offset ) - return errno = EOVERFLOW, -1; - off_t offset = msg->offset; - if ( Video::ReadAt(offset, msg->dst, msg->size) < 0 ) - return -1; - return 0; -} - -// TODO: Secure this system call against bad user-space pointers. -static int sys_dispmsg_issue(void* ptr, size_t size) -{ - struct dispmsg_header* hdr = (struct dispmsg_header*) ptr; - if ( size < sizeof(*hdr) ) - return errno = EINVAL, -1; - switch ( hdr->msgid ) - { - case DISPMSG_ENUMERATE_DEVICES: return EnumerateDevices(ptr, size); - case DISPMSG_GET_DRIVER_COUNT: return GetDriverCount(ptr, size); - case DISPMSG_GET_DRIVER_NAME: return GetDriverName(ptr, size); - case DISPMSG_GET_DRIVER: return GetDriver(ptr, size); - case DISPMSG_SET_DRIVER: return SetDriver(ptr, size); - case DISPMSG_SET_CRTC_MODE: return SetCrtcMode(ptr, size); - case DISPMSG_GET_CRTC_MODE: return GetCrtcMode(ptr, size); - case DISPMSG_GET_CRTC_MODES: return GetCrtcModes(ptr, size); - case DISPMSG_GET_MEMORY_SIZE: return GetMemorySize(ptr, size); - case DISPMSG_WRITE_MEMORY: return WriteMemory(ptr, size); - case DISPMSG_READ_MEMORY: return ReadMemory(ptr, size); - default: - return errno = ENOSYS, -1; - } -} - -void Init() -{ - Syscall::Register(SYSCALL_DISPMSG_ISSUE, (void*) sys_dispmsg_issue); -} - -} // namespace DisplayMessage -} // namespace Sortix diff --git a/kernel/dispmsg.h b/kernel/dispmsg.h deleted file mode 100644 index fd999513..00000000 --- a/kernel/dispmsg.h +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2012. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - dispmsg.h - User-space message-based interface for video framework access. - -*******************************************************************************/ - -#ifndef SORTIX_DISPMSG_H -#define SORTIX_DISPMSG_H - -namespace Sortix { -namespace DisplayMessage { - -void Init(); - -} // namespace DisplayMessage -} // namespace Sortix - -#endif diff --git a/kernel/gpu/bga/bga.cpp b/kernel/gpu/bga/bga.cpp new file mode 100644 index 00000000..e618f1ff --- /dev/null +++ b/kernel/gpu/bga/bga.cpp @@ -0,0 +1,528 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012, 2014. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + gpu/bga/bga.cpp + Driver for the Bochs VBE Extensions. + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "x86-family/memorymanagement.h" +#include "lfbtextbuffer.h" +#include "bga.h" + +namespace Sortix { +namespace BGA { + +const uint16_t VBE_DISPI_INDEX_ID = 0; +const uint16_t VBE_DISPI_INDEX_XRES = 1; +const uint16_t VBE_DISPI_INDEX_YRES = 2; +const uint16_t VBE_DISPI_INDEX_BPP = 3; +const uint16_t VBE_DISPI_INDEX_ENABLE = 4; +const uint16_t VBE_DISPI_INDEX_BANK = 5; +const uint16_t VBE_DISPI_INDEX_VIRT_WIDTH = 6; +const uint16_t VBE_DISPI_INDEX_VIRT_HEIGHT = 7; +const uint16_t VBE_DISPI_INDEX_X_OFFSET = 8; +const uint16_t VBE_DISPI_INDEX_Y_OFFSET = 9; +const uint16_t VBE_DISPI_NUM_REGISTERS = 10; + +#if defined(__i386__) || defined(__x86_64__) +const uint16_t VBE_DISPI_IOPORT_INDEX = 0x01CE; +const uint16_t VBE_DISPI_IOPORT_DATA = 0x01CF; +#endif + +const uint16_t VBE_DISPI_BPP_4 = 0x04; +const uint16_t VBE_DISPI_BPP_8 = 0x08; +const uint16_t VBE_DISPI_BPP_15 = 0x0F; +const uint16_t VBE_DISPI_BPP_16 = 0x10; +const uint16_t VBE_DISPI_BPP_24 = 0x18; +const uint16_t VBE_DISPI_BPP_32 = 0x20; + +const uint16_t VBE_DISPI_DISABLED = 0x00; +const uint16_t VBE_DISPI_ENABLED = 0x01; +const uint16_t VBE_DISPI_GETCAPS = 0x02; +const uint16_t VBE_DISPI_8BIT_DAC = 0x20; +const uint16_t VBE_DISPI_LFB_ENABLED = 0x40; +const uint16_t VBE_DISPI_NOCLEARMEM = 0x80; + +const uint16_t VBE_MIN_SUP_VERSION = 0xB0C0; +const uint16_t VBE_MIN_POS_VERSION = 0xB0C0; +const uint16_t VBE_MAX_POS_VERSION = 0xB0CF; + +static bool IsStandardResolution(uint16_t width, uint16_t height, uint16_t depth) +{ + if ( depth != VBE_DISPI_BPP_32 ) { return false; } + if ( width == 720 && height == 400 ) { return true; } + if ( width == 800 && height == 600 ) { return true; } + if ( width == 1024 && height == 768 ) { return true; } + if ( width == 1280 && height == 720 ) { return true; } + if ( width == 1280 && height == 1024 ) { return true; } + if ( width == 1600 && height == 900 ) { return true; } + if ( width == 1920 && height == 1080 ) { return true; } + return false; +} + +class BGADevice : public VideoDevice +{ +public: + BGADevice(uint32_t devaddr, addralloc_t fb_alloc, addralloc_t mmio_alloc); + virtual ~BGADevice(); + +public: + virtual struct dispmsg_crtc_mode GetCurrentMode(uint64_t connector) const; + virtual bool SwitchMode(uint64_t connector, struct dispmsg_crtc_mode mode); + virtual bool Supports(uint64_t connector, struct dispmsg_crtc_mode mode) const; + virtual struct dispmsg_crtc_mode* GetModes(uint64_t connector, size_t* num_modes) const; + virtual off_t FrameSize() const; + virtual ssize_t WriteAt(ioctx_t* ctx, off_t off, const void* buf, size_t count); + virtual ssize_t ReadAt(ioctx_t* ctx, off_t off, void* buf, size_t count); + virtual TextBuffer* CreateTextBuffer(uint64_t connector); + +public: + bool Initialize(); + +private: + bool DetectModes() const; + uint16_t WriteRegister(uint16_t index, uint16_t value); + uint16_t ReadRegister(uint16_t index); + uint16_t GetCapability(uint16_t index); + bool SetVideoMode(uint16_t width, uint16_t height, uint16_t depth, bool keep); + bool SupportsResolution(uint16_t width, uint16_t height, uint16_t depth); + +private: + mutable size_t num_modes; + mutable struct dispmsg_crtc_mode* modes; + struct dispmsg_crtc_mode current_mode; + addralloc_t fb_alloc; + addralloc_t mmio_alloc; + uint32_t devaddr; + uint16_t version; + uint16_t maxbpp; + uint16_t maxxres; + uint16_t maxyres; + +}; + +BGADevice::BGADevice(uint32_t devaddr, addralloc_t fb_alloc, addralloc_t mmio_alloc) : + fb_alloc(fb_alloc), mmio_alloc(mmio_alloc), devaddr(devaddr) +{ + num_modes = 0; + modes = NULL; + memset(¤t_mode, 0, sizeof(current_mode)); + version = 0; + maxbpp = 0; + maxxres = 0; + maxyres = 0; +} + +BGADevice::~BGADevice() +{ + UnmapPCIBar(&fb_alloc); + UnmapPCIBar(&mmio_alloc); + delete[] modes; +} + +uint16_t BGADevice::WriteRegister(uint16_t index, uint16_t value) +{ + assert(index < VBE_DISPI_NUM_REGISTERS); +#if defined(__i386__) || defined(__x86_64__) + if ( mmio_alloc.size == 0 ) + { + outport16(VBE_DISPI_IOPORT_INDEX, index); + return outport16(VBE_DISPI_IOPORT_DATA, value); + } +#endif + volatile little_uint16_t* regs = + (volatile little_uint16_t*) (mmio_alloc.from + 0x500); + return regs[index] = value; +} + +uint16_t BGADevice::ReadRegister(uint16_t index) +{ +#if defined(__i386__) || defined(__x86_64__) + if ( mmio_alloc.size == 0 ) + { + outport16(VBE_DISPI_IOPORT_INDEX, index); + return inport16(VBE_DISPI_IOPORT_DATA); + } +#endif + assert(index < VBE_DISPI_NUM_REGISTERS); + volatile little_uint16_t* regs = + (volatile little_uint16_t*) (mmio_alloc.from + 0x500); + return regs[index]; +} + +uint16_t BGADevice::GetCapability(uint16_t index) +{ + uint16_t was_enabled = ReadRegister(VBE_DISPI_INDEX_ENABLE); + WriteRegister(VBE_DISPI_INDEX_ENABLE, was_enabled | VBE_DISPI_GETCAPS); + uint16_t cap = ReadRegister(index); + WriteRegister(VBE_DISPI_INDEX_ENABLE, was_enabled); + return cap; +} + +bool BGADevice::Initialize() +{ + if ( (version = ReadRegister(VBE_DISPI_INDEX_ID)) < VBE_MIN_SUP_VERSION ) + { + Log::PrintF("[BGA device @ PCI:0x%X] Hardware version 0x%X is too old, " + "minimum version supported is 0x%X\n", + devaddr, version, VBE_MIN_SUP_VERSION); + return false; + } + + maxbpp = GetCapability(VBE_DISPI_INDEX_BPP); + maxxres = GetCapability(VBE_DISPI_INDEX_XRES); + maxyres = GetCapability(VBE_DISPI_INDEX_YRES); + + return true; +} + +bool BGADevice::SetVideoMode(uint16_t width, uint16_t height, uint16_t depth, bool keep) +{ + bool uselinear = true; + WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED); + WriteRegister(VBE_DISPI_INDEX_XRES, width); + WriteRegister(VBE_DISPI_INDEX_YRES, height); + WriteRegister(VBE_DISPI_INDEX_BPP, depth); + WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | + (uselinear ? VBE_DISPI_LFB_ENABLED : 0) | + (keep ? VBE_DISPI_NOCLEARMEM : 0)); + // TODO: How do we verify the video mode was *actually* set? + return true; +} + +// TODO: Need a better method of detecting available/desired resolutions. +bool BGADevice::SupportsResolution(uint16_t width, uint16_t height, uint16_t depth) +{ + if ( !width || !height || !depth ) + return false; + if ( maxxres < width || maxyres < height || maxbpp < depth ) + return false; + // TODO: Is this actually a restriction? + if ( width % 8U ) + return false; + // TODO: Can we determine this more closely in advance? Perhaps if the + // framebuffer we will be using is larger than video memory? + return true; +} + +struct dispmsg_crtc_mode BGADevice::GetCurrentMode(uint64_t connector) const +{ + if ( connector != 0 ) + { + errno = EINVAL; + struct dispmsg_crtc_mode mode; + memset(&mode, 0, sizeof(mode)); + return mode; + } + + return current_mode; +} + +bool BGADevice::SwitchMode(uint64_t connector, struct dispmsg_crtc_mode mode) +{ + if ( !Supports(connector, mode) ) + return false; + + if ( connector != 0 ) + return errno = EINVAL, false; + + size_t new_framesize = (size_t) mode.view_xres * + (size_t) mode.view_yres * + ((size_t) mode.fb_format + 7) / 8UL; + // TODO: Use a better error code than ENOSPC? + if ( fb_alloc.size < new_framesize ) + return errno = ENOSPC, false; + + if ( !SetVideoMode(mode.view_xres, mode.view_yres, mode.fb_format, false) ) + return false; + + current_mode = mode; + + return true; +} + +bool BGADevice::Supports(uint64_t connector, struct dispmsg_crtc_mode mode) const +{ + if ( connector != 0 ) + return errno = EINVAL, false; + + if ( mode.control & DISPMSG_CONTROL_VGA ) + return errno = EINVAL, false; + if ( !(mode.control & DISPMSG_CONTROL_VALID) ) + return errno = EINVAL, false; + + if ( UINT16_MAX < mode.view_xres ) + return errno = EINVAL, false; + if ( UINT16_MAX < mode.view_yres ) + return errno = EINVAL, false; + // TODO: This is wrong, list the right values as above. + if ( mode.fb_format != VBE_DISPI_BPP_4 && + mode.fb_format != VBE_DISPI_BPP_8 && + mode.fb_format != VBE_DISPI_BPP_15 && + mode.fb_format != VBE_DISPI_BPP_16 && + mode.fb_format != VBE_DISPI_BPP_24 && + mode.fb_format != VBE_DISPI_BPP_32 ) + return errno = EINVAL, false; + + // TODO: This is disabled because its support needs to be verified, see the + // framebuffer size calculation above? + if ( mode.fb_format != VBE_DISPI_BPP_32 ) + return errno = ENOSYS, false; + + return ((BGADevice*) this)->SupportsResolution(mode.view_xres, mode.view_yres, mode.fb_format); +} + +struct dispmsg_crtc_mode* BGADevice::GetModes(uint64_t connector, size_t* retnum) const +{ + if ( connector != 0 ) + return errno = EINVAL, (struct dispmsg_crtc_mode*) NULL; + + if ( !modes && !DetectModes() ) + return NULL; + struct dispmsg_crtc_mode* result = new struct dispmsg_crtc_mode[num_modes]; + if ( !result ) + return NULL; + for ( size_t i = 0; i < num_modes; i++ ) + result[i] = modes[i]; + *retnum = num_modes; + return result; +} + +off_t BGADevice::FrameSize() const +{ + return (off_t) fb_alloc.size; +} + +ssize_t BGADevice::WriteAt(ioctx_t* ctx, off_t off, const void* buf, size_t count) +{ + uint8_t* frame = (uint8_t*) fb_alloc.from; + if ( (off_t) fb_alloc.size <= off ) + return 0; + if ( fb_alloc.size < off + count ) + count = fb_alloc.size - off; + if ( !ctx->copy_from_src(frame + off, buf, count) ) + return -1; + return count; +} + +ssize_t BGADevice::ReadAt(ioctx_t* ctx, off_t off, void* buf, size_t count) +{ + const uint8_t* frame = (const uint8_t*) fb_alloc.from; + if ( (off_t) fb_alloc.size <= off ) + return 0; + if ( fb_alloc.size < off + count ) + count = fb_alloc.size - off; + if ( !ctx->copy_to_dest(buf, frame + off, count) ) + return -1; + return count; +} + +bool BGADevice::DetectModes() const +{ + num_modes = 0; + unsigned bpp = VBE_DISPI_BPP_32; + for ( unsigned w = 0; w < maxxres; w += 4U ) + { + for ( unsigned h = 0; h < maxyres; h += 4UL ) + { + if ( !IsStandardResolution(w, h, bpp) ) + continue; + if ( !((BGADevice*) this)->SupportsResolution(w, h, bpp) ) + continue; + num_modes++; + } + } + num_modes++; + modes = new struct dispmsg_crtc_mode[num_modes]; + if ( !modes ) + return false; + memset(modes, 0, sizeof(char*) * num_modes); + size_t current_mode_id = 0; + for ( unsigned w = 0; w < maxxres; w += 4U ) + { + for ( unsigned h = 0; h < maxyres; h += 4UL ) + { + if ( !IsStandardResolution(w, h, bpp) ) + continue; + if ( !((BGADevice*) this)->SupportsResolution(w, h, bpp) ) + continue; + struct dispmsg_crtc_mode mode; + memset(&mode, 0, sizeof(mode)); + mode.view_xres = w; + mode.view_yres = h; + mode.fb_format = bpp; + mode.control = DISPMSG_CONTROL_VALID; + modes[current_mode_id++] = mode; + } + } + + struct dispmsg_crtc_mode any_mode; + memset(&any_mode, 0, sizeof(any_mode)); + any_mode.view_xres = 0; + any_mode.view_yres = 0; + any_mode.fb_format = 0; + any_mode.control = DISPMSG_CONTROL_OTHER_RESOLUTIONS; + modes[num_modes-1] = any_mode; + + return true; +} + +TextBuffer* BGADevice::CreateTextBuffer(uint64_t connector) +{ + if ( connector != 0 ) + return errno = EINVAL, (TextBuffer*) NULL; + + uint8_t* lfb = (uint8_t*) fb_alloc.from; + uint32_t lfbformat = current_mode.fb_format; + size_t scansize = current_mode.view_xres * current_mode.fb_format / 8UL; + return CreateLFBTextBuffer(lfb, lfbformat, current_mode.view_xres, current_mode.view_yres, scansize); +} + +static void TryInitializeDevice(uint32_t devaddr) +{ + pciid_t id = PCI::GetDeviceId(devaddr); + + bool is_qemu_bga = id.vendorid == 0x1234 && id.deviceid == 0x1111; + bool is_vbox_bga = id.vendorid == 0x80EE && id.deviceid == 0xBEEF; + + (void) is_qemu_bga; + (void) is_vbox_bga; + + pcibar_t fb_bar; + pcibar_t mmio_bar; + addralloc_t fb_alloc; + addralloc_t mmio_alloc; + bool has_mmio = false; + bool fallback_ioport = false; + + fb_bar = PCI::GetBAR(devaddr, 0); + if ( !MapPCIBAR(&fb_alloc, fb_bar, MAP_PCI_BAR_WRITE_COMBINE) ) + { + Log::PrintF("[BGA device @ PCI:0x%X] Framebuffer could not be mapped: %s\n", + devaddr, strerror(errno)); + return; + } + + if ( is_qemu_bga ) + mmio_bar = PCI::GetBAR(devaddr, 2); + + if ( is_qemu_bga && mmio_bar.is_mmio() && 4096 <= mmio_bar.size() ) + { + has_mmio = true; + + if ( !MapPCIBAR(&mmio_alloc, mmio_bar, MAP_PCI_BAR_WRITE_COMBINE) ) + { + Log::PrintF("[BGA device @ PCI:0x%X] Memory-mapped registers could not be mapped: %s\n", + devaddr, strerror(errno)); + UnmapPCIBar(&fb_alloc); + return; + } + } + + else + { + // This device doesn't come with its own set of registers, so we have to + // assume that the global BGA io port registers are available and that + // only a single such device is present (since two concurrent devices) + // could not exist then. This is only available on x86-family systems. +#if defined(__i386__) || defined(__x86_64__) + fallback_ioport = true; +#endif + } + + if ( !has_mmio && !fallback_ioport ) + { + Log::PrintF("[BGA device @ PCI:0x%X] Device provides no registers.\n", + devaddr); + UnmapPCIBar(&fb_alloc); + return; + } + + if ( fallback_ioport ) + memset(&mmio_alloc, 0, sizeof(mmio_alloc)); + + BGADevice* bga_device = new BGADevice(devaddr, fb_alloc, mmio_alloc); + if ( !bga_device ) + { + Log::PrintF("[BGA device @ PCI:0x%X] Unable to allocate driver structure: %s\n", + devaddr, strerror(errno)); + UnmapPCIBar(&mmio_alloc); + UnmapPCIBar(&fb_alloc); + return; + } + + if ( !bga_device->Initialize() ) + { + delete bga_device; + return; + } + + if ( !Video::RegisterDevice("bga", bga_device) ) + { + Log::PrintF("[BGA device @ PCI:0x%X] Unable to register device: %s\n", + devaddr, strerror(errno)); + delete bga_device; + return; + } +} + +void Init() +{ + pcifind_t bga_pcifind; + memset(&bga_pcifind, 255, sizeof(bga_pcifind)); + bga_pcifind.vendorid = 0x1234; + bga_pcifind.deviceid = 0x1111; + + uint32_t devaddr = 0; + while ( (devaddr = PCI::SearchForDevices(bga_pcifind, devaddr)) ) + TryInitializeDevice(devaddr); + + memset(&bga_pcifind, 255, sizeof(bga_pcifind)); + bga_pcifind.vendorid = 0x80EE; + bga_pcifind.deviceid = 0xBEEF; + + devaddr = 0; + while ( (devaddr = PCI::SearchForDevices(bga_pcifind, devaddr)) ) + TryInitializeDevice(devaddr); +} + +} // namespace BGA +} // namespace Sortix diff --git a/kernel/bga.h b/kernel/gpu/bga/bga.h similarity index 88% rename from kernel/bga.h rename to kernel/gpu/bga/bga.h index bbcc1a6e..4d5fa482 100644 --- a/kernel/bga.h +++ b/kernel/gpu/bga/bga.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2014. This file is part of Sortix. @@ -17,13 +17,13 @@ You should have received a copy of the GNU General Public License along with Sortix. If not, see . - bga.h + gpu/bga/bga.h Driver for the Bochs VBE Extensions. *******************************************************************************/ -#ifndef SORTIX_BGA_H -#define SORTIX_BGA_H +#ifndef SORTIX_GPU_BGA_BGA_H +#define SORTIX_GPU_BGA_BGA_H namespace Sortix { namespace BGA { diff --git a/kernel/include/sortix/display.h b/kernel/include/sortix/display.h index 9db62ef9..38b43342 100644 --- a/kernel/include/sortix/display.h +++ b/kernel/include/sortix/display.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2014. This file is part of Sortix. @@ -32,6 +32,10 @@ __BEGIN_DECLS +const uint32_t DISPMSG_CONTROL_VALID = 1 << 0; +const uint32_t DISPMSG_CONTROL_VGA = 1 << 1; +const uint32_t DISPMSG_CONTROL_OTHER_RESOLUTIONS = 1 << 2; + struct dispmsg_string { size_t byte_size; // Including the terminating NUL-byte. diff --git a/kernel/include/sortix/kernel/video.h b/kernel/include/sortix/kernel/video.h index 30a70164..d26ae2ff 100644 --- a/kernel/include/sortix/kernel/video.h +++ b/kernel/include/sortix/kernel/video.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2014. This file is part of Sortix. @@ -22,11 +22,14 @@ *******************************************************************************/ -#ifndef SORTIX_VIDEO_H -#define SORTIX_VIDEO_H +#ifndef INCLUDE_SORTIX_KERNEL_VIDEO_H +#define INCLUDE_SORTIX_KERNEL_VIDEO_H #include +#include + +#include #include namespace Sortix { @@ -34,43 +37,30 @@ namespace Sortix { class TextBuffer; class TextBufferHandle; -bool ReadParamString(const char* str, ...); - -class VideoDriver +class VideoDevice { public: - virtual ~VideoDriver() { } - virtual bool StartUp() = 0; - virtual bool ShutDown() = 0; - virtual char* GetCurrentMode() const = 0; - virtual bool SwitchMode(const char* mode) = 0; - virtual bool Supports(const char* mode) const = 0; - virtual char** GetModes(size_t* nummodes) const = 0; + virtual ~VideoDevice() { } + virtual struct dispmsg_crtc_mode GetCurrentMode(uint64_t connector) const = 0; + virtual bool SwitchMode(uint64_t connector, struct dispmsg_crtc_mode mode) = 0; + virtual bool Supports(uint64_t connector, struct dispmsg_crtc_mode mode) const = 0; + virtual struct dispmsg_crtc_mode* GetModes(uint64_t connector, size_t* nummodes) const = 0; virtual off_t FrameSize() const = 0; - virtual ssize_t WriteAt(off_t off, const void* buf, size_t count) = 0; - virtual ssize_t ReadAt(off_t off, void* buf, size_t count) = 0; - virtual TextBuffer* CreateTextBuffer() = 0; + virtual ssize_t WriteAt(ioctx_t* ctx, off_t off, const void* buf, size_t count) = 0; + virtual ssize_t ReadAt(ioctx_t* ctx, off_t off, void* buf, size_t count) = 0; + virtual TextBuffer* CreateTextBuffer(uint64_t connector) = 0; }; +} // namespace Sortix + +namespace Sortix { namespace Video { void Init(Ref textbufhandle); -bool RegisterDriver(const char* name, VideoDriver* driver); -char* GetCurrentMode(); -char* GetDriverName(size_t index); -size_t GetCurrentDriverIndex(); -size_t GetNumDrivers(); -size_t LookupDriverIndexOfMode(const char* mode); -bool Supports(const char* mode); -bool SwitchMode(const char* mode); -char** GetModes(size_t* modesnum); -off_t FrameSize(); -ssize_t WriteAt(off_t off, const void* buf, size_t count); -ssize_t ReadAt(off_t off, void* buf, size_t count); +bool RegisterDevice(const char* name, VideoDevice* device); } // namespace Video - } // namespace Sortix #endif diff --git a/kernel/kernel.cpp b/kernel/kernel.cpp index 2d1eedff..3f5cfc41 100644 --- a/kernel/kernel.cpp +++ b/kernel/kernel.cpp @@ -70,15 +70,14 @@ #include "alarm.h" #include "ata.h" -#include "bga.h" #include "com.h" -#include "dispmsg.h" #include "elf.h" #include "fs/full.h" #include "fs/kram.h" #include "fs/null.h" #include "fs/user.h" #include "fs/zero.h" +#include "gpu/bga/bga.h" #include "identity.h" #include "initrd.h" #include "io.h" @@ -432,9 +431,6 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) // Initialize the scheduler. Scheduler::Init(); - // Initialize the Display Message framework. - DisplayMessage::Init(); - // Now that the base system has been loaded, it's time to go threaded. First // we create an object that represents this process. Ref ptable(new ProcessTable()); diff --git a/kernel/video.cpp b/kernel/video.cpp index d2044a91..4f88a690 100644 --- a/kernel/video.cpp +++ b/kernel/video.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2014. This file is part of Sortix. @@ -26,459 +26,406 @@ #include #include +#include #include #include #include +#include #include #include namespace Sortix { - -bool ReadParamString(const char* str, ...) -{ - if ( strchr(str, '\n') ) { errno = EINVAL; } - const char* keyname; - va_list args; - while ( *str ) - { - size_t varlen = strcspn(str, ","); - if ( !varlen ) { str++; continue; } - size_t namelen = strcspn(str, "="); - if ( !namelen ) { errno = EINVAL; goto cleanup; } - if ( !str[namelen] ) { errno = EINVAL; goto cleanup; } - if ( varlen < namelen ) { errno = EINVAL; goto cleanup; } - size_t valuelen = varlen - 1 /*=*/ - namelen; - char* name = String::Substring(str, 0, namelen); - if ( !name ) { goto cleanup; } - char* value = String::Substring(str, namelen+1, valuelen); - if ( !value ) { delete[] name; goto cleanup; } - va_start(args, str); - while ( (keyname = va_arg(args, const char*)) ) - { - if ( strcmp(keyname, "STOP") == 0 ) - break; - char** nameptr = va_arg(args, char**); - if ( strcmp(keyname, name) ) { continue; } - *nameptr = value; - break; - } - va_end(args); - if ( !keyname ) { delete[] value; } - delete[] name; - str += varlen; - str += strspn(str, ","); - } - return true; - -cleanup: - va_start(args, str); - while ( (keyname = va_arg(args, const char*)) ) - { - char** nameptr = va_arg(args, char**); - delete[] *nameptr; *nameptr = NULL; - } - va_end(args); - return false; -} - namespace Video { -const unsigned long DRIVER_GOT_MODES = (1UL<<0UL); +const uint64_t ONE_AND_ONLY_DEVICE = 0; +const uint64_t ONE_AND_ONLY_CONNECTOR = 0; -struct DriverEntry +kthread_mutex_t video_lock = KTHREAD_MUTEX_INITIALIZER; + +struct DeviceEntry { char* name; - VideoDriver* driver; - unsigned long flags; - size_t id; + VideoDevice* device; }; -size_t numdrivers; -size_t driverslen; -DriverEntry* drivers; +size_t num_devices = 0; +size_t devices_length = 0; +DeviceEntry* devices = NULL; -size_t nummodes; -size_t modeslen; -char** modes; - -char* currentmode; -size_t currentdrvid; -bool newdrivers; - -kthread_mutex_t videolock; Ref textbufhandle; -void Init(Ref thetextbufhandle) +bool RegisterDevice(const char* name, VideoDevice* device) { - videolock = KTHREAD_MUTEX_INITIALIZER; - textbufhandle = thetextbufhandle; - numdrivers = driverslen = 0; - drivers = NULL; - nummodes = modeslen = 0; - modes = NULL; - currentdrvid = SIZE_MAX; - newdrivers = false; - currentmode = NULL; -} - -size_t GetCurrentDriverIndex() -{ - ScopedLock lock(&videolock); - return currentdrvid; -} - -size_t GetNumDrivers() -{ - ScopedLock lock(&videolock); - return numdrivers; -} - -char* GetDriverName(size_t index) -{ - ScopedLock lock(&videolock); - if ( numdrivers <= index || !drivers[index].name ) - return String::Clone("none"); - return String::Clone(drivers[index].name); -} - -static DriverEntry* CurrentDriverEntry() -{ - if ( currentdrvid == SIZE_MAX ) { return NULL; } - return drivers + currentdrvid; -} - -bool RegisterDriver(const char* name, VideoDriver* driver) -{ - ScopedLock lock(&videolock); - if ( numdrivers == driverslen ) + ScopedLock lock(&video_lock); + if ( num_devices == devices_length ) { - size_t newdriverslen = driverslen ? 2 * driverslen : 8UL; - DriverEntry* newdrivers = new DriverEntry[newdriverslen]; - if ( !newdrivers ) { return false; } - memcpy(newdrivers, drivers, sizeof(*drivers) * numdrivers); - delete[] drivers; drivers = newdrivers; - driverslen = driverslen; + size_t newdevices_length = devices_length ? 2 * devices_length : 8UL; + DeviceEntry* newdevices = new DeviceEntry[newdevices_length]; + if ( !newdevices ) + return false; + memcpy(newdevices, devices, sizeof(*devices) * num_devices); + delete[] devices; devices = newdevices; + devices_length = devices_length; } char* drivername = String::Clone(name); - if ( !drivername ) { return false; } - - size_t index = numdrivers++; - drivers[index].name = drivername; - drivers[index].driver = driver; - drivers[index].flags = 0; - drivers[index].id = index; - newdrivers = true; - return true; -} - -static bool ExpandModesArray(size_t needed) -{ - size_t modesneeded = nummodes + needed; - if ( modesneeded <= modeslen ) { return true; } - size_t newmodeslen = 2 * modeslen; - if ( newmodeslen < modesneeded ) { newmodeslen = modesneeded; } - char** newmodes = new char*[newmodeslen]; - if ( !newmodes ) { return false; } - memcpy(newmodes, modes, sizeof(char*) * nummodes); - delete[] modes; modes = newmodes; - modeslen = newmodeslen; - return true; -} - -static void UpdateModes() -{ - if ( !newdrivers ) { return; } - bool allsuccess = true; - for ( size_t i = 0; i < numdrivers; i++ ) - { - bool success = false; - if ( drivers[i].flags & DRIVER_GOT_MODES ) { continue; } - const char* drivername = drivers[i].name; - VideoDriver* driver = drivers[i].driver; - size_t prevnummodes = nummodes; - size_t drvnummodes = 0; - char** drvmodes = driver->GetModes(&drvnummodes); - if ( !drvmodes ) { goto cleanup_error; } - if ( !ExpandModesArray(drvnummodes) ) { goto cleanup_drvmodes; } - for ( size_t n = 0; n < drvnummodes; n++ ) - { - char* modestr = String::Combine(4, "driver=", drivername, - ",", drvmodes[n]); - if ( !modestr ) { goto cleanup_newmodes; } - modes[nummodes++] = modestr; - } - success = true; - drivers[i].flags |= DRIVER_GOT_MODES; -cleanup_newmodes: - for ( size_t i = prevnummodes; !success && i < nummodes; i++ ) - delete[] modes[i]; - if ( !success ) { nummodes = prevnummodes; } -cleanup_drvmodes: - for ( size_t n = 0; n < drvnummodes; n++ ) { delete[] drvmodes[n]; } - delete[] drvmodes; -cleanup_error: - allsuccess &= success; - } - newdrivers = !allsuccess; -} - -static DriverEntry* GetDriverEntry(const char* drivername) -{ - for ( size_t i = 0; i < numdrivers; i++ ) - { - if ( !strcmp(drivername, drivers[i].name) ) - { - return drivers + i; - } - } - errno = ENODRV; - return NULL; -} - -static bool StartUpDriver(VideoDriver* driver, const char* drivername) -{ - if ( !driver->StartUp() ) - { - int errnum = errno; - Log::PrintF("Error: Video driver '%s' was unable to startup\n", - drivername); - errno = errnum; + if ( !drivername ) return false; - } + + size_t index = num_devices++; + devices[index].name = drivername; + devices[index].device = device; return true; } -static bool ShutDownDriver(VideoDriver* driver, const char* drivername) +__attribute__((unused)) +static bool TransmitString(struct dispmsg_string* dest, const char* str) { - textbufhandle->Replace(NULL); - if ( !driver->ShutDown() ) - { - int errnum = errno; - Log::PrintF("Warning: Video driver '%s' did not shutdown cleanly\n", - drivername); - errno = errnum; - return false; - } - return true; + size_t size = strlen(str) + 1; + size_t dest_size = dest->byte_size; + dest->byte_size = size; + if ( dest_size < size ) + return errno = ERANGE, false; + return CopyToUser(dest->str, str, size); } -static bool DriverModeAction(VideoDriver* driver, const char* drivername, - const char* mode, const char* action) +__attribute__((unused)) +static char* ReceiveString(struct dispmsg_string* src) { - textbufhandle->Replace(NULL); - if ( !driver->SwitchMode(mode) ) + if ( !src->byte_size ) + return errno = EINVAL, (char*) NULL; + char* ret = new char[src->byte_size]; + if ( !ret ) + return NULL; + if ( !CopyFromUser(ret, src->str, src->byte_size) ) + return NULL; + if ( ret[src->byte_size-1] != '\0' ) { - int errnum = errno; - Log::PrintF("Error: Video driver '%s' could not %s mode '%s'\n", - drivername, action, mode); - errno = errnum; - return false; + delete[] ret; + return errno = EINVAL, (char*) NULL; } - textbufhandle->Replace(driver->CreateTextBuffer()); - return true; + return ret; } -static bool SwitchDriverMode(VideoDriver* driver, const char* drivername, - const char* mode) +static int EnumerateDevices(void* ptr, size_t size) { - return DriverModeAction(driver, drivername, mode, "switch to"); + struct dispmsg_enumerate_devices msg; + if ( size != sizeof(msg) ) + return errno = EINVAL, -1; + if ( !CopyFromUser(&msg, ptr, sizeof(msg)) ) + return -1; + + ScopedLock lock(&video_lock); + + size_t requested_num_devices = msg.devices_length; + msg.devices_length = num_devices; + + if ( !CopyToUser(ptr, &msg, sizeof(msg)) ) + return -1; + + if ( requested_num_devices < num_devices ) + return errno = ERANGE, -1; + + for ( uint64_t i = 0; i < num_devices; i++ ) + if ( !CopyToUser(&msg.devices[i], &i, sizeof(i)) ) + return -1; + + return 0; } - -static bool RestoreDriverMode(VideoDriver* driver, const char* drivername, - const char* mode) +static int GetDriverCount(void* ptr, size_t size) { - return DriverModeAction(driver, drivername, mode, "restore"); + struct dispmsg_get_driver_count msg; + if ( size != sizeof(msg) ) + return errno = EINVAL, -1; + if ( !CopyFromUser(&msg, ptr, sizeof(msg)) ) + return -1; + + ScopedLock lock(&video_lock); + + if ( num_devices <= msg.device ) + return errno = ENODEV, -1; + + msg.driver_count = 1; + + if ( !CopyToUser(ptr, &msg, sizeof(msg)) ) + return -1; + + return 0; } -// Attempts to use the specific driver and mode, if an error occurs, it will -// attempt to reload the previous driver and mode. If that fails, we are kinda -// screwed and the video adapter is left in an undefined state. -static bool DoSwitchMode(DriverEntry* newdrvent, const char* newmode) +static int GetDriverName(void* ptr, size_t size) { - DriverEntry* prevdrvent = CurrentDriverEntry(); - VideoDriver* prevdriver = prevdrvent ? prevdrvent->driver : NULL; - const char* prevdrivername = prevdrvent ? prevdrvent->name : NULL; + struct dispmsg_get_driver_name msg; + if ( size != sizeof(msg) ) + return errno = EINVAL, -1; + if ( !CopyFromUser(&msg, ptr, sizeof(msg)) ) + return -1; - VideoDriver* newdriver = newdrvent->driver; - const char* newdrivername = newdrvent->name; + ScopedLock lock(&video_lock); - char* newcurrentmode = String::Clone(newmode); - if ( !newcurrentmode ) { return false; } + if ( num_devices <= msg.device ) + return errno = ENODEV, -1; - if ( prevdriver == newdriver ) - { - if ( !SwitchDriverMode(newdriver, newdrivername, newmode) ) - { - delete[] newcurrentmode; - return false; - } - delete[] currentmode; - currentmode = newcurrentmode; - return true; - } + DeviceEntry* device_entry = &devices[msg.device]; + if ( !TransmitString(&msg.name, device_entry->name) ) + return -1; - int errnum = 0; + if ( !CopyToUser(ptr, &msg, sizeof(msg)) ) + return -1; - if ( prevdriver ) { ShutDownDriver(prevdriver, prevdrivername); } - - char* prevmode = currentmode; currentmode = NULL; - currentdrvid = SIZE_MAX; - - if ( !StartUpDriver(newdriver, newdrivername) ) - { - errnum = errno; - goto restore_prev_driver; - } - - currentdrvid = newdrvent->id; - - if ( !SwitchDriverMode(newdriver, newdrivername, newmode) ) - { - errnum = errno; - ShutDownDriver(newdriver, newdrivername); - currentdrvid = SIZE_MAX; - goto restore_prev_driver; - } - - currentmode = newcurrentmode; - delete[] prevmode; - - return true; - -restore_prev_driver: - delete[] newcurrentmode; - if ( !prevdriver ) { goto error_out; } - if ( !StartUpDriver(prevdriver, prevdrivername) ) { goto error_out; } - - currentdrvid = prevdrvent->id; - - if ( !RestoreDriverMode(prevdriver, prevdrivername, prevmode) ) - { - ShutDownDriver(prevdriver, prevdrivername); - currentdrvid = SIZE_MAX; - goto error_out; - } - - Log::PrintF("Successfully restored video driver '%s' mode '%s'\n", - prevdrivername, prevmode); - -error_out: - if ( currentdrvid == SIZE_MAX ) - Log::PrintF("Warning: Could not fall back upon a video driver\n"); - errno = errnum; // Return the original error, not the last one. - return false; + return 0; } -char* GetCurrentMode() +static int GetDriver(void* ptr, size_t size) { - ScopedLock lock(&videolock); - UpdateModes(); - return String::Clone(currentmode ? currentmode : "driver=none"); + struct dispmsg_get_driver msg; + if ( size != sizeof(msg) ) + return errno = EINVAL, -1; + if ( !CopyFromUser(&msg, ptr, sizeof(msg)) ) + return -1; + + ScopedLock lock(&video_lock); + + if ( num_devices <= msg.device ) + return errno = ENODEV, -1; + + msg.driver_index = 0; + + if ( !CopyToUser(ptr, &msg, sizeof(msg)) ) + return -1; + + return 0; } -char** GetModes(size_t* modesnum) +static int SetDriver(void* ptr, size_t size) { - ScopedLock lock(&videolock); - UpdateModes(); - char** result = new char*[nummodes]; - if ( !result ) { return NULL; } + struct dispmsg_set_driver msg; + if ( size != sizeof(msg) ) + return errno = EINVAL, -1; + if ( !CopyFromUser(&msg, ptr, sizeof(msg)) ) + return -1; + + ScopedLock lock(&video_lock); + + if ( num_devices <= msg.device ) + return errno = ENODEV, -1; + + if ( msg.driver_index != 0 ) + return errno = EINVAL, -1; + + if ( !CopyToUser(ptr, &msg, sizeof(msg)) ) + return -1; + + return 0; +} + +static int SetCrtcMode(void* ptr, size_t size) +{ + struct dispmsg_set_crtc_mode msg; + if ( size != sizeof(msg) ) + return errno = EINVAL, -1; + if ( !CopyFromUser(&msg, ptr, sizeof(msg)) ) + return -1; + + ScopedLock lock(&video_lock); + + if ( num_devices <= msg.device ) + return errno = ENODEV, -1; + + DeviceEntry* device_entry = &devices[msg.device]; + VideoDevice* device = device_entry->device; + if ( !device->SwitchMode(msg.connector, msg.mode) ) + return -1; + + // TODO: This could potentially fail. + if ( msg.device == ONE_AND_ONLY_DEVICE && + msg.connector == ONE_AND_ONLY_CONNECTOR ) + textbufhandle->Replace(device->CreateTextBuffer(msg.connector)); + + // No need to respond. + + return 0; +} + +static int GetCrtcMode(void* ptr, size_t size) +{ + struct dispmsg_get_crtc_mode msg; + if ( size != sizeof(msg) ) + return errno = EINVAL, -1; + if ( !CopyFromUser(&msg, ptr, sizeof(msg)) ) + return -1; + + ScopedLock lock(&video_lock); + + if ( num_devices <= msg.device ) + return errno = ENODEV, -1; + + DeviceEntry* device_entry = &devices[msg.device]; + VideoDevice* device = device_entry->device; + + // TODO: There is no real way to detect failure here. + errno = 0; + struct dispmsg_crtc_mode mode = device->GetCurrentMode(msg.connector); + if ( !(mode.control & DISPMSG_CONTROL_VALID) && errno != 0 ) + return -1; + + msg.mode = mode; + + if ( !CopyToUser(ptr, &msg, sizeof(msg)) ) + return -1; + + return 0; +} + +static int GetCrtcModes(void* ptr, size_t size) +{ + struct dispmsg_get_crtc_modes msg; + if ( size != sizeof(msg) ) + return errno = EINVAL, -1; + if ( !CopyFromUser(&msg, ptr, sizeof(msg)) ) + return -1; + + ScopedLock lock(&video_lock); + + if ( num_devices <= msg.device ) + return errno = ENODEV, -1; + + DeviceEntry* device_entry = &devices[msg.device]; + VideoDevice* device = device_entry->device; + + size_t nummodes; + struct dispmsg_crtc_mode* modes = device->GetModes(msg.connector, &nummodes); + if ( !modes ) + return -1; + + size_t requested_modes = msg.modes_length; + msg.modes_length = nummodes; + + if ( !CopyToUser(ptr, &msg, sizeof(msg)) ) + return -1; + + if ( requested_modes < nummodes ) + { + delete[] modes; + return errno = ERANGE, -1; + } + for ( size_t i = 0; i < nummodes; i++ ) { - result[i] = String::Clone(modes[i]); - if ( !result[i] ) + if ( !CopyToUser(&msg.modes[i], &modes[i], sizeof(modes[i])) ) { - for ( size_t j = 0; j < i; j++ ) - { - delete[] result[j]; - } - delete[] result; - return NULL; + delete[] modes; + return -1; } } - *modesnum = nummodes; - return result; + + delete[] modes; + + + return 0; } -bool SwitchMode(const char* mode) +static int GetMemorySize(void* ptr, size_t size) { - ScopedLock lock(&videolock); - UpdateModes(); - char* drivername = NULL; - if ( !ReadParamString(mode, "driver", &drivername, NULL) ) { return false; } - if ( !strcmp(drivername, "none") ) + struct dispmsg_get_memory_size msg; + if ( size != sizeof(msg) ) + return errno = EINVAL, -1; + if ( !CopyFromUser(&msg, ptr, sizeof(msg)) ) + return -1; + + ScopedLock lock(&video_lock); + + if ( num_devices <= msg.device ) + return errno = ENODEV, -1; + + DeviceEntry* device_entry = &devices[msg.device]; + VideoDevice* device = device_entry->device; + + msg.memory_size = device->FrameSize(); + + if ( !CopyToUser(ptr, &msg, sizeof(msg)) ) + return -1; + + return 0; +} + +static int WriteMemory(void* ptr, size_t size) +{ + struct dispmsg_write_memory msg; + if ( size != sizeof(msg) ) + return errno = EINVAL, -1; + if ( !CopyFromUser(&msg, ptr, sizeof(msg)) ) + return -1; + + ScopedLock lock(&video_lock); + + if ( num_devices <= msg.device ) + return errno = ENODEV, -1; + + DeviceEntry* device_entry = &devices[msg.device]; + VideoDevice* device = device_entry->device; + + ioctx_t ctx; SetupUserIOCtx(&ctx); + if ( device->WriteAt(&ctx, msg.offset, msg.src, msg.size) < 0 ) + return -1; + + return 0; +} + +static int ReadMemory(void* ptr, size_t size) +{ + struct dispmsg_read_memory msg; + if ( size != sizeof(msg) ) + return errno = EINVAL, -1; + if ( !CopyFromUser(&msg, ptr, sizeof(msg)) ) + return -1; + + ScopedLock lock(&video_lock); + + if ( num_devices <= msg.device ) + return errno = ENODEV, -1; + + DeviceEntry* device_entry = &devices[msg.device]; + VideoDevice* device = device_entry->device; + + ioctx_t ctx; SetupUserIOCtx(&ctx); + if ( device->ReadAt(&ctx, msg.offset, msg.dst, msg.size) < 0 ) + return -1; + + return 0; +} + +static int sys_dispmsg_issue(void* ptr, size_t size) +{ + struct dispmsg_header hdr; + if ( size < sizeof(hdr) ) + return errno = EINVAL, -1; + if ( !CopyFromUser(&hdr, ptr, sizeof(hdr)) ) + return -1; + switch ( hdr.msgid ) { - DriverEntry* driverentry = CurrentDriverEntry(); - if ( !driverentry ) - return true; - ShutDownDriver(driverentry->driver, driverentry->name); - currentdrvid = SIZE_MAX; - return true; + case DISPMSG_ENUMERATE_DEVICES: return EnumerateDevices(ptr, size); + case DISPMSG_GET_DRIVER_COUNT: return GetDriverCount(ptr, size); + case DISPMSG_GET_DRIVER_NAME: return GetDriverName(ptr, size); + case DISPMSG_GET_DRIVER: return GetDriver(ptr, size); + case DISPMSG_SET_DRIVER: return SetDriver(ptr, size); + case DISPMSG_SET_CRTC_MODE: return SetCrtcMode(ptr, size); + case DISPMSG_GET_CRTC_MODE: return GetCrtcMode(ptr, size); + case DISPMSG_GET_CRTC_MODES: return GetCrtcModes(ptr, size); + case DISPMSG_GET_MEMORY_SIZE: return GetMemorySize(ptr, size); + case DISPMSG_WRITE_MEMORY: return WriteMemory(ptr, size); + case DISPMSG_READ_MEMORY: return ReadMemory(ptr, size); + default: + return errno = ENOSYS, -1; } - DriverEntry* drvent = GetDriverEntry(drivername); - delete[] drivername; - if ( !drvent ) { return false; } - return DoSwitchMode(drvent, mode); } -bool Supports(const char* mode) +void Init(Ref thetextbufhandle) { - ScopedLock lock(&videolock); - UpdateModes(); - char* drivername = NULL; - if ( !ReadParamString(mode, "driver", &drivername, NULL) ) { return false; } - DriverEntry* drvent = GetDriverEntry(drivername); - delete[] drivername; - if ( !drvent ) { return false; } - return drvent->driver->Supports(mode); -} - -size_t LookupDriverIndexOfMode(const char* mode) -{ - const char* needle = "driver="; - size_t needlelen = strlen(needle); - while ( *mode ) - { - if ( !strncmp(mode, needle, needlelen) ) - { - const char* name = mode + needlelen; - size_t namelen = strcspn(name, ","); - ScopedLock lock(&videolock); - for ( size_t i = 0; i < numdrivers; i++ ) - if ( !strncmp(drivers[i].name, name, namelen) ) - return i; - return SIZE_MAX; - } - mode += strcspn(mode, ",") + 1; - } - return SIZE_MAX; -} - -off_t FrameSize() -{ - ScopedLock lock(&videolock); - DriverEntry* drvent = CurrentDriverEntry(); - if ( !drvent ) { errno = EINVAL; return -1; } - return drvent->driver->FrameSize(); -} - -ssize_t WriteAt(off_t off, const void* buf, size_t count) -{ - ScopedLock lock(&videolock); - DriverEntry* drvent = CurrentDriverEntry(); - if ( !drvent ) { errno = EINVAL; return -1; } - return drvent->driver->WriteAt(off, buf, count); -} - -ssize_t ReadAt(off_t off, void* buf, size_t count) -{ - ScopedLock lock(&videolock); - DriverEntry* drvent = CurrentDriverEntry(); - if ( !drvent ) { errno = EINVAL; return -1; } - return drvent->driver->ReadAt(off, buf, count); + textbufhandle = thetextbufhandle; + Syscall::Register(SYSCALL_DISPMSG_ISSUE, (void*) sys_dispmsg_issue); } } // namespace Video - } // namespace Sortix