1
0
Fork 0
mirror of https://gitlab.com/sortix/sortix.git synced 2023-02-13 20:55:38 -05:00

Refactor kernel graphics support.

This commit is contained in:
Jonas 'Sortie' Termansen 2014-03-25 20:28:51 +01:00
parent 8f7c72abc1
commit ee13ffa715
10 changed files with 893 additions and 1392 deletions

View file

@ -75,7 +75,6 @@ $(CPUOBJS) \
addralloc.o \ addralloc.o \
alarm.o \ alarm.o \
ata.o \ ata.o \
bga.o \
calltrace.o \ calltrace.o \
clock.o \ clock.o \
com.o \ com.o \
@ -85,7 +84,6 @@ $(CPU)/kthread.o \
crc32.o \ crc32.o \
debugger.o \ debugger.o \
descriptor.o \ descriptor.o \
dispmsg.o \
dtable.o \ dtable.o \
elf.o \ elf.o \
fcache.o \ fcache.o \
@ -96,6 +94,7 @@ fs/null.o \
fs/user.o \ fs/user.o \
fs/util.o \ fs/util.o \
fs/zero.o \ fs/zero.o \
gpu/bga/bga.o \
identity.o \ identity.o \
initrd.o \ initrd.o \
inode.o \ inode.o \

View file

@ -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 <http://www.gnu.org/licenses/>.
bga.cpp
Driver for the Bochs VBE Extensions.
*******************************************************************************/
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sortix/mman.h>
#include <sortix/kernel/addralloc.h>
#include <sortix/kernel/cpu.h>
#include <sortix/kernel/ioport.h>
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/memorymanagement.h>
#include <sortix/kernel/pci.h>
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/string.h>
#include <sortix/kernel/textbuffer.h>
#include <sortix/kernel/video.h>
#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

View file

@ -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 <http://www.gnu.org/licenses/>.
dispmsg.cpp
User-space message-based interface for video framework access.
*******************************************************************************/
#include <sys/types.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sortix/display.h>
#include <sortix/syscallnum.h>
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/string.h>
#include <sortix/kernel/syscall.h>
#include <sortix/kernel/video.h>
#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

View file

@ -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 <http://www.gnu.org/licenses/>.
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

528
kernel/gpu/bga/bga.cpp Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
gpu/bga/bga.cpp
Driver for the Bochs VBE Extensions.
*******************************************************************************/
#include <assert.h>
#include <endian.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sortix/mman.h>
#include <sortix/kernel/addralloc.h>
#include <sortix/kernel/cpu.h>
#include <sortix/kernel/ioport.h>
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/memorymanagement.h>
#include <sortix/kernel/pci.h>
#include <sortix/kernel/pci-mmio.h>
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/string.h>
#include <sortix/kernel/textbuffer.h>
#include <sortix/kernel/video.h>
#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(&current_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

View file

@ -1,6 +1,6 @@
/******************************************************************************* /*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2012. Copyright(C) Jonas 'Sortie' Termansen 2012, 2014.
This file is part of Sortix. This file is part of Sortix.
@ -17,13 +17,13 @@
You should have received a copy of the GNU General Public License along with You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>. Sortix. If not, see <http://www.gnu.org/licenses/>.
bga.h gpu/bga/bga.h
Driver for the Bochs VBE Extensions. Driver for the Bochs VBE Extensions.
*******************************************************************************/ *******************************************************************************/
#ifndef SORTIX_BGA_H #ifndef SORTIX_GPU_BGA_BGA_H
#define SORTIX_BGA_H #define SORTIX_GPU_BGA_BGA_H
namespace Sortix { namespace Sortix {
namespace BGA { namespace BGA {

View file

@ -1,6 +1,6 @@
/******************************************************************************* /*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2012. Copyright(C) Jonas 'Sortie' Termansen 2012, 2014.
This file is part of Sortix. This file is part of Sortix.
@ -32,6 +32,10 @@
__BEGIN_DECLS __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 struct dispmsg_string
{ {
size_t byte_size; // Including the terminating NUL-byte. size_t byte_size; // Including the terminating NUL-byte.

View file

@ -1,6 +1,6 @@
/******************************************************************************* /*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2012. Copyright(C) Jonas 'Sortie' Termansen 2012, 2014.
This file is part of Sortix. This file is part of Sortix.
@ -22,11 +22,14 @@
*******************************************************************************/ *******************************************************************************/
#ifndef SORTIX_VIDEO_H #ifndef INCLUDE_SORTIX_KERNEL_VIDEO_H
#define SORTIX_VIDEO_H #define INCLUDE_SORTIX_KERNEL_VIDEO_H
#include <sys/types.h> #include <sys/types.h>
#include <sortix/display.h>
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/refcount.h> #include <sortix/kernel/refcount.h>
namespace Sortix { namespace Sortix {
@ -34,43 +37,30 @@ namespace Sortix {
class TextBuffer; class TextBuffer;
class TextBufferHandle; class TextBufferHandle;
bool ReadParamString(const char* str, ...); class VideoDevice
class VideoDriver
{ {
public: public:
virtual ~VideoDriver() { } virtual ~VideoDevice() { }
virtual bool StartUp() = 0; virtual struct dispmsg_crtc_mode GetCurrentMode(uint64_t connector) const = 0;
virtual bool ShutDown() = 0; virtual bool SwitchMode(uint64_t connector, struct dispmsg_crtc_mode mode) = 0;
virtual char* GetCurrentMode() const = 0; virtual bool Supports(uint64_t connector, struct dispmsg_crtc_mode mode) const = 0;
virtual bool SwitchMode(const char* mode) = 0; virtual struct dispmsg_crtc_mode* GetModes(uint64_t connector, size_t* nummodes) const = 0;
virtual bool Supports(const char* mode) const = 0;
virtual char** GetModes(size_t* nummodes) const = 0;
virtual off_t FrameSize() 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 WriteAt(ioctx_t* ctx, off_t off, const void* buf, size_t count) = 0;
virtual ssize_t ReadAt(off_t off, 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() = 0; virtual TextBuffer* CreateTextBuffer(uint64_t connector) = 0;
}; };
} // namespace Sortix
namespace Sortix {
namespace Video { namespace Video {
void Init(Ref<TextBufferHandle> textbufhandle); void Init(Ref<TextBufferHandle> textbufhandle);
bool RegisterDriver(const char* name, VideoDriver* driver); bool RegisterDevice(const char* name, VideoDevice* device);
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);
} // namespace Video } // namespace Video
} // namespace Sortix } // namespace Sortix
#endif #endif

View file

@ -70,15 +70,14 @@
#include "alarm.h" #include "alarm.h"
#include "ata.h" #include "ata.h"
#include "bga.h"
#include "com.h" #include "com.h"
#include "dispmsg.h"
#include "elf.h" #include "elf.h"
#include "fs/full.h" #include "fs/full.h"
#include "fs/kram.h" #include "fs/kram.h"
#include "fs/null.h" #include "fs/null.h"
#include "fs/user.h" #include "fs/user.h"
#include "fs/zero.h" #include "fs/zero.h"
#include "gpu/bga/bga.h"
#include "identity.h" #include "identity.h"
#include "initrd.h" #include "initrd.h"
#include "io.h" #include "io.h"
@ -432,9 +431,6 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo)
// Initialize the scheduler. // Initialize the scheduler.
Scheduler::Init(); Scheduler::Init();
// Initialize the Display Message framework.
DisplayMessage::Init();
// Now that the base system has been loaded, it's time to go threaded. First // Now that the base system has been loaded, it's time to go threaded. First
// we create an object that represents this process. // we create an object that represents this process.
Ref<ProcessTable> ptable(new ProcessTable()); Ref<ProcessTable> ptable(new ProcessTable());

View file

@ -1,6 +1,6 @@
/******************************************************************************* /*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2012. Copyright(C) Jonas 'Sortie' Termansen 2012, 2014.
This file is part of Sortix. This file is part of Sortix.
@ -26,459 +26,406 @@
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <sortix/kernel/copy.h>
#include <sortix/kernel/kernel.h> #include <sortix/kernel/kernel.h>
#include <sortix/kernel/kthread.h> #include <sortix/kernel/kthread.h>
#include <sortix/kernel/string.h> #include <sortix/kernel/string.h>
#include <sortix/kernel/syscall.h>
#include <sortix/kernel/textbuffer.h> #include <sortix/kernel/textbuffer.h>
#include <sortix/kernel/video.h> #include <sortix/kernel/video.h>
namespace Sortix { 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 { 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; char* name;
VideoDriver* driver; VideoDevice* device;
unsigned long flags;
size_t id;
}; };
size_t numdrivers; size_t num_devices = 0;
size_t driverslen; size_t devices_length = 0;
DriverEntry* drivers; DeviceEntry* devices = NULL;
size_t nummodes;
size_t modeslen;
char** modes;
char* currentmode;
size_t currentdrvid;
bool newdrivers;
kthread_mutex_t videolock;
Ref<TextBufferHandle> textbufhandle; Ref<TextBufferHandle> textbufhandle;
void Init(Ref<TextBufferHandle> thetextbufhandle) bool RegisterDevice(const char* name, VideoDevice* device)
{ {
videolock = KTHREAD_MUTEX_INITIALIZER; ScopedLock lock(&video_lock);
textbufhandle = thetextbufhandle; if ( num_devices == devices_length )
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 )
{ {
size_t newdriverslen = driverslen ? 2 * driverslen : 8UL; size_t newdevices_length = devices_length ? 2 * devices_length : 8UL;
DriverEntry* newdrivers = new DriverEntry[newdriverslen]; DeviceEntry* newdevices = new DeviceEntry[newdevices_length];
if ( !newdrivers ) { return false; } if ( !newdevices )
memcpy(newdrivers, drivers, sizeof(*drivers) * numdrivers); return false;
delete[] drivers; drivers = newdrivers; memcpy(newdevices, devices, sizeof(*devices) * num_devices);
driverslen = driverslen; delete[] devices; devices = newdevices;
devices_length = devices_length;
} }
char* drivername = String::Clone(name); char* drivername = String::Clone(name);
if ( !drivername ) { return false; } if ( !drivername )
return false;
size_t index = numdrivers++; size_t index = num_devices++;
drivers[index].name = drivername; devices[index].name = drivername;
drivers[index].driver = driver; devices[index].device = device;
drivers[index].flags = 0;
drivers[index].id = index;
newdrivers = true;
return true; return true;
} }
static bool ExpandModesArray(size_t needed) __attribute__((unused))
static bool TransmitString(struct dispmsg_string* dest, const char* str)
{ {
size_t modesneeded = nummodes + needed; size_t size = strlen(str) + 1;
if ( modesneeded <= modeslen ) { return true; } size_t dest_size = dest->byte_size;
size_t newmodeslen = 2 * modeslen; dest->byte_size = size;
if ( newmodeslen < modesneeded ) { newmodeslen = modesneeded; } if ( dest_size < size )
char** newmodes = new char*[newmodeslen]; return errno = ERANGE, false;
if ( !newmodes ) { return false; } return CopyToUser(dest->str, str, size);
memcpy(newmodes, modes, sizeof(char*) * nummodes);
delete[] modes; modes = newmodes;
modeslen = newmodeslen;
return true;
} }
static void UpdateModes() __attribute__((unused))
static char* ReceiveString(struct dispmsg_string* src)
{ {
if ( !newdrivers ) { return; } if ( !src->byte_size )
bool allsuccess = true; return errno = EINVAL, (char*) NULL;
for ( size_t i = 0; i < numdrivers; i++ ) char* ret = new char[src->byte_size];
{ if ( !ret )
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; return NULL;
if ( !CopyFromUser(ret, src->str, src->byte_size) )
return NULL;
if ( ret[src->byte_size-1] != '\0' )
{
delete[] ret;
return errno = EINVAL, (char*) NULL;
}
return ret;
} }
static bool StartUpDriver(VideoDriver* driver, const char* drivername) static int EnumerateDevices(void* ptr, size_t size)
{ {
if ( !driver->StartUp() ) struct dispmsg_enumerate_devices msg;
{ if ( size != sizeof(msg) )
int errnum = errno; return errno = EINVAL, -1;
Log::PrintF("Error: Video driver '%s' was unable to startup\n", if ( !CopyFromUser(&msg, ptr, sizeof(msg)) )
drivername); return -1;
errno = errnum;
return false; ScopedLock lock(&video_lock);
}
return true; 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 ShutDownDriver(VideoDriver* driver, const char* drivername) static int GetDriverCount(void* ptr, size_t size)
{ {
textbufhandle->Replace(NULL); struct dispmsg_get_driver_count msg;
if ( !driver->ShutDown() ) if ( size != sizeof(msg) )
{ return errno = EINVAL, -1;
int errnum = errno; if ( !CopyFromUser(&msg, ptr, sizeof(msg)) )
Log::PrintF("Warning: Video driver '%s' did not shutdown cleanly\n", return -1;
drivername);
errno = errnum; ScopedLock lock(&video_lock);
return false;
} if ( num_devices <= msg.device )
return true; return errno = ENODEV, -1;
msg.driver_count = 1;
if ( !CopyToUser(ptr, &msg, sizeof(msg)) )
return -1;
return 0;
} }
static bool DriverModeAction(VideoDriver* driver, const char* drivername, static int GetDriverName(void* ptr, size_t size)
const char* mode, const char* action)
{ {
textbufhandle->Replace(NULL); struct dispmsg_get_driver_name msg;
if ( !driver->SwitchMode(mode) ) if ( size != sizeof(msg) )
{ return errno = EINVAL, -1;
int errnum = errno; if ( !CopyFromUser(&msg, ptr, sizeof(msg)) )
Log::PrintF("Error: Video driver '%s' could not %s mode '%s'\n", return -1;
drivername, action, mode);
errno = errnum; ScopedLock lock(&video_lock);
return false;
} if ( num_devices <= msg.device )
textbufhandle->Replace(driver->CreateTextBuffer()); return errno = ENODEV, -1;
return true;
DeviceEntry* device_entry = &devices[msg.device];
if ( !TransmitString(&msg.name, device_entry->name) )
return -1;
if ( !CopyToUser(ptr, &msg, sizeof(msg)) )
return -1;
return 0;
} }
static bool SwitchDriverMode(VideoDriver* driver, const char* drivername, static int GetDriver(void* ptr, size_t size)
const char* mode)
{ {
return DriverModeAction(driver, drivername, mode, "switch to"); 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;
} }
static int SetDriver(void* ptr, size_t size)
static bool RestoreDriverMode(VideoDriver* driver, const char* drivername,
const char* mode)
{ {
return DriverModeAction(driver, drivername, mode, "restore"); 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;
} }
// Attempts to use the specific driver and mode, if an error occurs, it will static int SetCrtcMode(void* ptr, size_t size)
// 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)
{ {
DriverEntry* prevdrvent = CurrentDriverEntry(); struct dispmsg_set_crtc_mode msg;
VideoDriver* prevdriver = prevdrvent ? prevdrvent->driver : NULL; if ( size != sizeof(msg) )
const char* prevdrivername = prevdrvent ? prevdrvent->name : NULL; return errno = EINVAL, -1;
if ( !CopyFromUser(&msg, ptr, sizeof(msg)) )
return -1;
VideoDriver* newdriver = newdrvent->driver; ScopedLock lock(&video_lock);
const char* newdrivername = newdrvent->name;
char* newcurrentmode = String::Clone(newmode); if ( num_devices <= msg.device )
if ( !newcurrentmode ) { return false; } return errno = ENODEV, -1;
if ( prevdriver == newdriver ) DeviceEntry* device_entry = &devices[msg.device];
{ VideoDevice* device = device_entry->device;
if ( !SwitchDriverMode(newdriver, newdrivername, newmode) ) if ( !device->SwitchMode(msg.connector, msg.mode) )
{ return -1;
delete[] newcurrentmode;
return false;
}
delete[] currentmode;
currentmode = newcurrentmode;
return true;
}
int errnum = 0; // TODO: This could potentially fail.
if ( msg.device == ONE_AND_ONLY_DEVICE &&
msg.connector == ONE_AND_ONLY_CONNECTOR )
textbufhandle->Replace(device->CreateTextBuffer(msg.connector));
if ( prevdriver ) { ShutDownDriver(prevdriver, prevdrivername); } // No need to respond.
char* prevmode = currentmode; currentmode = NULL; return 0;
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;
} }
char* GetCurrentMode() static int GetCrtcMode(void* ptr, size_t size)
{ {
ScopedLock lock(&videolock); struct dispmsg_get_crtc_mode msg;
UpdateModes(); if ( size != sizeof(msg) )
return String::Clone(currentmode ? currentmode : "driver=none"); 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;
} }
char** GetModes(size_t* modesnum) static int GetCrtcModes(void* ptr, size_t size)
{ {
ScopedLock lock(&videolock); struct dispmsg_get_crtc_modes msg;
UpdateModes(); if ( size != sizeof(msg) )
char** result = new char*[nummodes]; return errno = EINVAL, -1;
if ( !result ) { return NULL; } 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++ ) for ( size_t i = 0; i < nummodes; i++ )
{ {
result[i] = String::Clone(modes[i]); if ( !CopyToUser(&msg.modes[i], &modes[i], sizeof(modes[i])) )
if ( !result[i] )
{ {
for ( size_t j = 0; j < i; j++ ) delete[] modes;
return -1;
}
}
delete[] modes;
return 0;
}
static int GetMemorySize(void* ptr, size_t size)
{
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 )
{ {
delete[] result[j]; 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;
} }
delete[] result;
return NULL;
}
}
*modesnum = nummodes;
return result;
} }
bool SwitchMode(const char* mode) void Init(Ref<TextBufferHandle> thetextbufhandle)
{ {
ScopedLock lock(&videolock); textbufhandle = thetextbufhandle;
UpdateModes(); Syscall::Register(SYSCALL_DISPMSG_ISSUE, (void*) sys_dispmsg_issue);
char* drivername = NULL;
if ( !ReadParamString(mode, "driver", &drivername, NULL) ) { return false; }
if ( !strcmp(drivername, "none") )
{
DriverEntry* driverentry = CurrentDriverEntry();
if ( !driverentry )
return true;
ShutDownDriver(driverentry->driver, driverentry->name);
currentdrvid = SIZE_MAX;
return true;
}
DriverEntry* drvent = GetDriverEntry(drivername);
delete[] drivername;
if ( !drvent ) { return false; }
return DoSwitchMode(drvent, mode);
}
bool Supports(const char* mode)
{
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);
} }
} // namespace Video } // namespace Video
} // namespace Sortix } // namespace Sortix