mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
Add VirtualBox Guest Additions.
This commit is contained in:
parent
e7c5d032d1
commit
ce54be34da
5 changed files with 670 additions and 10 deletions
|
@ -59,6 +59,7 @@ ifdef X86FAMILY
|
|||
x86-family/pat.o \
|
||||
x86-family/float.o \
|
||||
x86-family/ps2.o \
|
||||
x86-family/vbox.o \
|
||||
x86-family/x86-family.o
|
||||
endif
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
#include "x86-family/memorymanagement.h"
|
||||
#include "x86-family/vbox.h"
|
||||
#endif
|
||||
|
||||
#include "lfbtextbuffer.h"
|
||||
|
@ -126,6 +127,9 @@ private:
|
|||
bool SupportsResolution(uint16_t width, uint16_t height, uint16_t depth);
|
||||
|
||||
private:
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
VBox::GuestAdditions* guest_additions;
|
||||
#endif
|
||||
size_t num_modes;
|
||||
struct dispmsg_crtc_mode* modes;
|
||||
struct dispmsg_crtc_mode current_mode;
|
||||
|
@ -142,6 +146,9 @@ private:
|
|||
BGADevice::BGADevice(uint32_t devaddr, addralloc_t fb_alloc, addralloc_t mmio_alloc) :
|
||||
fb_alloc(fb_alloc), mmio_alloc(mmio_alloc), devaddr(devaddr)
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
guest_additions = NULL;
|
||||
#endif
|
||||
num_modes = 0;
|
||||
modes = NULL;
|
||||
memset(¤t_mode, 0, sizeof(current_mode));
|
||||
|
@ -153,6 +160,10 @@ BGADevice::BGADevice(uint32_t devaddr, addralloc_t fb_alloc, addralloc_t mmio_al
|
|||
|
||||
BGADevice::~BGADevice()
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
if ( guest_additions )
|
||||
guest_additions->UnregisterVideoDevice(device_index);
|
||||
#endif
|
||||
UnmapPCIBar(&fb_alloc);
|
||||
UnmapPCIBar(&mmio_alloc);
|
||||
delete[] modes;
|
||||
|
@ -211,6 +222,31 @@ bool BGADevice::Initialize()
|
|||
maxxres = GetCapability(VBE_DISPI_INDEX_XRES);
|
||||
maxyres = GetCapability(VBE_DISPI_INDEX_YRES);
|
||||
|
||||
if ( !Video::RegisterDevice("bga", this) )
|
||||
{
|
||||
Log::PrintF("[BGA device @ PCI:0x%X] Unable to register device: %s\n",
|
||||
devaddr, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
guest_additions = VBox::GetGuestAdditions();
|
||||
if ( !guest_additions )
|
||||
return false;
|
||||
if ( !guest_additions->RegisterVideoDevice(device_index) )
|
||||
{
|
||||
guest_additions = NULL;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
Video::ConfigureDevice(this);
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
if ( guest_additions )
|
||||
guest_additions->ReadyVideoDevice(device_index);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -259,6 +295,16 @@ bool BGADevice::GetDefaultMode(uint64_t connector,
|
|||
uint32_t xres;
|
||||
uint32_t yres;
|
||||
uint32_t bpp;
|
||||
#if 0
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
if ( guest_additions &&
|
||||
guest_additions->GetBestVideoMode(connector, &xres, &yres, &bpp) )
|
||||
{
|
||||
good = true;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#endif
|
||||
if ( connector == 0 && Log::fallback_framebuffer &&
|
||||
SupportsResolution(Log::fallback_framebuffer_width,
|
||||
Log::fallback_framebuffer_height,
|
||||
|
@ -312,6 +358,10 @@ bool BGADevice::GetCurrentMode(uint64_t connector,
|
|||
if ( connector != 0 )
|
||||
return false;
|
||||
*mode = current_mode;
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
if ( guest_additions )
|
||||
mode->control |= DISPMSG_CONTROL_VM_AUTO_SCALE;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -460,6 +510,11 @@ bool BGADevice::DetectModes()
|
|||
any_mode.view_yres = 0;
|
||||
any_mode.fb_format = 0;
|
||||
any_mode.control = DISPMSG_CONTROL_OTHER_RESOLUTIONS;
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
if ( guest_additions )
|
||||
any_mode.control |= DISPMSG_CONTROL_VM_AUTO_SCALE;
|
||||
#endif
|
||||
|
||||
modes[num_modes-1] = any_mode;
|
||||
|
||||
return true;
|
||||
|
@ -558,16 +613,6 @@ static void TryInitializeDevice(uint32_t devaddr)
|
|||
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;
|
||||
}
|
||||
|
||||
Video::ConfigureDevice(bga_device);
|
||||
}
|
||||
|
||||
void Init()
|
||||
|
|
|
@ -94,6 +94,7 @@
|
|||
#include "x86-family/float.h"
|
||||
#include "x86-family/gdt.h"
|
||||
#include "x86-family/ps2.h"
|
||||
#include "x86-family/vbox.h"
|
||||
#endif
|
||||
|
||||
// Keep the stack size aligned with $CPU/boot.s
|
||||
|
@ -566,6 +567,11 @@ static void BootThread(void* /*user*/)
|
|||
// Search for PCI devices and load their drivers.
|
||||
PCI::Init();
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
// Initialize the VirtualBox Guest Additions.
|
||||
VBox::Init();
|
||||
#endif
|
||||
|
||||
// Initialize AHCI devices.
|
||||
AHCI::Init("/dev", slashdev);
|
||||
|
||||
|
|
560
kernel/x86-family/vbox.cpp
Normal file
560
kernel/x86-family/vbox.cpp
Normal file
|
@ -0,0 +1,560 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* x86-family/vbox.cpp
|
||||
* VirtualBox Guest Additions.
|
||||
*/
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <endian.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <sortix/kernel/addralloc.h>
|
||||
#include <sortix/kernel/clock.h>
|
||||
#include <sortix/kernel/interrupt.h>
|
||||
#include <sortix/kernel/ioport.h>
|
||||
#include <sortix/kernel/kernel.h>
|
||||
#include <sortix/kernel/kthread.h>
|
||||
#include <sortix/kernel/memorymanagement.h>
|
||||
#include <sortix/kernel/pci.h>
|
||||
#include <sortix/kernel/pci-mmio.h>
|
||||
#include <sortix/kernel/time.h>
|
||||
#include <sortix/kernel/video.h>
|
||||
|
||||
#include "vbox.h"
|
||||
|
||||
namespace Sortix {
|
||||
namespace VBox {
|
||||
|
||||
#define VBOX_VMMDEV_VERSION 0x00010003
|
||||
#define VBOX_REQUEST_HEADER_VERSION 0x10001
|
||||
|
||||
#define VBOX_EVENT_MOUSE_CAPABILITIES_CHANGED (1U << 0)
|
||||
#define VBOX_EVENT_HGCM (1U << 1)
|
||||
#define VBOX_EVENT_DISPLAY_CHANGE_REQUEST (1U << 2)
|
||||
#define VBOX_EVENT_JUDGE_CREDENTIALS (1U << 3)
|
||||
#define VBOX_EVENT_RESTORED (1U << 4)
|
||||
#define VBOX_EVENT_SEAMLESS_MODE_CHANGE_REQUEST (1U << 5)
|
||||
#define VBOX_EVENT_BALLOON_CHANGE_REQUEST (1U << 6)
|
||||
#define VBOX_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST (1U << 7)
|
||||
#define VBOX_EVENT_VRDP (1U << 8)
|
||||
#define VBOX_EVENT_MOUSE_POSITION_CHANGED (1U << 9)
|
||||
#define VBOX_EVENT_CPU_HOTPLUG (1U << 10)
|
||||
|
||||
struct registers
|
||||
{
|
||||
uint32_t size;
|
||||
uint32_t version;
|
||||
uint32_t host_events;
|
||||
uint32_t guest_event_mask;
|
||||
};
|
||||
|
||||
struct vbox_header
|
||||
{
|
||||
uint32_t size;
|
||||
uint32_t version;
|
||||
uint32_t request_type;
|
||||
int32_t rc;
|
||||
uint32_t reserved[2];
|
||||
};
|
||||
|
||||
#define VBOX_REQUEST_GET_HOST_VERSION 4
|
||||
struct vbox_host_version
|
||||
{
|
||||
struct vbox_header hdr;
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
uint32_t build;
|
||||
uint32_t revision;
|
||||
uint32_t features;
|
||||
};
|
||||
|
||||
#define VBOX_REQUEST_GET_HOST_TIME 10
|
||||
struct vbox_host_time
|
||||
{
|
||||
struct vbox_header hdr;
|
||||
uint64_t time;
|
||||
};
|
||||
|
||||
#define VBOX_REQUEST_ACK_EVENTS 41
|
||||
struct vbox_ack_events
|
||||
{
|
||||
struct vbox_header hdr;
|
||||
uint32_t events;
|
||||
};
|
||||
|
||||
#define VBOX_REQUEST_GUEST_INFO 50
|
||||
struct vbox_guest_info
|
||||
{
|
||||
struct vbox_header hdr;
|
||||
uint32_t version;
|
||||
uint32_t ostype;
|
||||
};
|
||||
|
||||
#define VBOX_REQUEST_GET_DISPLAY_CHANGE2 54
|
||||
struct vbox_get_display_change2
|
||||
{
|
||||
struct vbox_header hdr;
|
||||
uint32_t xres;
|
||||
uint32_t yres;
|
||||
uint32_t bpp;
|
||||
uint32_t eventack;
|
||||
uint32_t display;
|
||||
};
|
||||
|
||||
#define VBOX_REQUEST_SET_GUEST_CAPS2 56
|
||||
struct vbox_guest_caps2
|
||||
{
|
||||
struct vbox_header hdr;
|
||||
uint32_t caps_or;
|
||||
uint32_t caps_not;
|
||||
};
|
||||
#define VBOX_GUEST_SUPPORTS_SEAMLESS (1U << 0)
|
||||
#define VBOX_GUEST_SUPPORTS_GUEST_HOST_WINDOW_MAPPING (1U << 1)
|
||||
#define VBOX_GUEST_SUPPORTS_GRAPHICS (1U << 2)
|
||||
|
||||
#define VBOX_REQUEST_VIDEO_MODE_SUPPORTED2 57
|
||||
struct vbox_video_mode_supported2
|
||||
{
|
||||
struct vbox_header hdr;
|
||||
uint32_t display;
|
||||
uint32_t xres;
|
||||
uint32_t yres;
|
||||
uint32_t bpp;
|
||||
bool is_supported;
|
||||
};
|
||||
|
||||
class VBoxDevice : public GuestAdditions
|
||||
{
|
||||
public:
|
||||
VBoxDevice(uint32_t devaddr);
|
||||
virtual ~VBoxDevice();
|
||||
|
||||
public:
|
||||
virtual bool IsSupportedVideoMode(uint32_t display, uint32_t xres,
|
||||
uint32_t yres, uint32_t bpp);
|
||||
virtual bool GetBestVideoMode(uint32_t display, uint32_t* xres,
|
||||
uint32_t* yres, uint32_t* bpp);
|
||||
virtual bool RegisterVideoDevice(uint64_t device_id);
|
||||
virtual void ReadyVideoDevice(uint64_t device_id);
|
||||
virtual void UnregisterVideoDevice(uint64_t device_id);
|
||||
|
||||
public:
|
||||
bool Initialize();
|
||||
void OnInterrupt();
|
||||
void InterruptWork(void* payload, size_t size);
|
||||
|
||||
private:
|
||||
void Request(void* bufptr, size_t size);
|
||||
void RequestIRQ(void* bufptr, size_t size);
|
||||
void ReportCapabilities();
|
||||
__attribute__((format(printf, 2, 3)))
|
||||
void LogF(const char* format, ...);
|
||||
|
||||
private:
|
||||
struct vbox_host_version vbox_version;
|
||||
struct interrupt_handler interrupt_registration;
|
||||
kthread_mutex_t buffer_lock;
|
||||
uint64_t video_device;
|
||||
volatile struct registers* regs;
|
||||
volatile unsigned char* buffer1;
|
||||
volatile unsigned char* buffer2;
|
||||
addr_t buffer1_frame;
|
||||
addr_t buffer2_frame;
|
||||
uint32_t devaddr;
|
||||
uint32_t capabilities;
|
||||
uint32_t listening_events;
|
||||
addralloc_t mmio_alloc;
|
||||
addralloc_t buffer1_alloc;
|
||||
addralloc_t buffer2_alloc;
|
||||
uint16_t port;
|
||||
unsigned char interrupt_index;
|
||||
bool has_mmio_alloc;
|
||||
bool has_buffer1_alloc;
|
||||
bool has_buffer1_mapped;
|
||||
bool has_buffer2_alloc;
|
||||
bool has_buffer2_mapped;
|
||||
bool has_interrupt_registered;
|
||||
bool has_video_device;
|
||||
|
||||
};
|
||||
|
||||
VBoxDevice::VBoxDevice(uint32_t devaddr)
|
||||
{
|
||||
this->buffer_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||
this->devaddr = devaddr;
|
||||
this->buffer1_frame = 0;
|
||||
this->buffer2_frame = 0;
|
||||
this->has_mmio_alloc = false;
|
||||
this->has_buffer1_alloc = false;
|
||||
this->has_buffer1_mapped = false;
|
||||
this->has_buffer2_alloc = false;
|
||||
this->has_buffer2_mapped = false;
|
||||
this->has_interrupt_registered = false;
|
||||
this->has_video_device = false;
|
||||
}
|
||||
|
||||
VBoxDevice::~VBoxDevice()
|
||||
{
|
||||
if ( has_interrupt_registered )
|
||||
Interrupt::UnregisterHandler(interrupt_index, &interrupt_registration);
|
||||
if ( has_buffer2_mapped )
|
||||
Memory::Unmap(buffer2_alloc.from);
|
||||
if ( has_buffer1_mapped )
|
||||
Memory::Unmap(buffer1_alloc.from);
|
||||
if ( has_buffer2_alloc )
|
||||
FreeKernelAddress(&buffer2_alloc);
|
||||
if ( has_buffer1_alloc )
|
||||
FreeKernelAddress(&buffer1_alloc);
|
||||
if ( buffer2_frame )
|
||||
Page::Put(buffer2_frame, PAGE_USAGE_DRIVER);
|
||||
if ( buffer1_frame )
|
||||
Page::Put(buffer1_frame, PAGE_USAGE_DRIVER);
|
||||
if ( has_mmio_alloc )
|
||||
UnmapPCIBar(&mmio_alloc);
|
||||
}
|
||||
|
||||
void VBoxDevice__OnInterrupt(struct interrupt_context*, void* context)
|
||||
{
|
||||
((VBoxDevice*) context)->OnInterrupt();
|
||||
}
|
||||
|
||||
void VBoxDevice__InterruptWork(void* context, void* payload, size_t size)
|
||||
{
|
||||
((VBoxDevice*) context)->InterruptWork(payload, size);
|
||||
}
|
||||
|
||||
bool VBoxDevice::Initialize()
|
||||
{
|
||||
ScopedLock lock(&buffer_lock);
|
||||
interrupt_index = PCI::SetupInterruptLine(devaddr);
|
||||
if ( !interrupt_index )
|
||||
{
|
||||
LogF("error: cannot determine interrupt line");
|
||||
return errno = EINVAL, false;
|
||||
}
|
||||
pcibar_t port_bar = PCI::GetBAR(devaddr, 0);
|
||||
if ( !port_bar.is_iospace() )
|
||||
{
|
||||
LogF("error: BAR 0 is invalid");
|
||||
return false;
|
||||
}
|
||||
pcibar_t mmio_bar = PCI::GetBAR(devaddr, 1);
|
||||
if ( !(mmio_bar.is_mmio() && 4096 <= mmio_bar.size()) )
|
||||
{
|
||||
LogF("error: BAR 1 is invalid");
|
||||
return false;
|
||||
}
|
||||
port = port_bar.ioaddr();
|
||||
if ( !MapPCIBAR(&mmio_alloc, mmio_bar, 0) )
|
||||
{
|
||||
LogF("error: failed to memory map BAR 1: %m");
|
||||
return false;
|
||||
}
|
||||
has_mmio_alloc = true;
|
||||
regs = (volatile struct registers*) mmio_alloc.from;
|
||||
if ( !(buffer1_frame = Page::Get32Bit(PAGE_USAGE_DRIVER)) )
|
||||
{
|
||||
LogF("error: buffer page allocation failure");
|
||||
return false;
|
||||
}
|
||||
if ( !(buffer2_frame = Page::Get32Bit(PAGE_USAGE_DRIVER)) )
|
||||
{
|
||||
LogF("error: buffer page allocation failure");
|
||||
return false;
|
||||
}
|
||||
if ( !AllocateKernelAddress(&buffer1_alloc, Page::Size()) )
|
||||
{
|
||||
LogF("error: buffer page virtual address allocation failure");
|
||||
return false;
|
||||
}
|
||||
has_buffer1_alloc = true;
|
||||
if ( !AllocateKernelAddress(&buffer2_alloc, Page::Size()) )
|
||||
{
|
||||
LogF("error: buffer page virtual address allocation failure");
|
||||
return false;
|
||||
}
|
||||
has_buffer2_alloc = true;
|
||||
int prot = PROT_KREAD | PROT_KWRITE;
|
||||
if ( !Memory::Map(buffer1_frame, buffer1_alloc.from, prot) )
|
||||
{
|
||||
LogF("error: buffer page virtual mapping failure");
|
||||
return false;
|
||||
}
|
||||
has_buffer1_mapped = true;
|
||||
buffer1 = (volatile unsigned char*) buffer1_alloc.from;
|
||||
if ( !Memory::Map(buffer2_frame, buffer2_alloc.from, prot) )
|
||||
{
|
||||
LogF("error: buffer page virtual mapping failure");
|
||||
return false;
|
||||
}
|
||||
has_buffer2_mapped = true;
|
||||
buffer2 = (volatile unsigned char*) buffer2_alloc.from;
|
||||
|
||||
memset(&vbox_version, 0, sizeof(vbox_version));
|
||||
vbox_version.hdr.size = sizeof(vbox_version);
|
||||
vbox_version.hdr.version = VBOX_REQUEST_HEADER_VERSION;
|
||||
vbox_version.hdr.request_type = VBOX_REQUEST_GET_HOST_VERSION;
|
||||
vbox_version.hdr.rc = -1;
|
||||
Request(&vbox_version, sizeof(vbox_version));
|
||||
if ( vbox_version.hdr.rc != 0 )
|
||||
{
|
||||
LogF("error: REQUEST_GET_HOST_VERSION failed with rc = %i\n",
|
||||
vbox_version.hdr.rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct vbox_guest_info guest_info;
|
||||
memset(&guest_info, 0, sizeof(guest_info));
|
||||
guest_info.hdr.size = sizeof(guest_info);
|
||||
guest_info.hdr.version = VBOX_REQUEST_HEADER_VERSION;
|
||||
guest_info.hdr.request_type = VBOX_REQUEST_GUEST_INFO;
|
||||
guest_info.version = VBOX_VMMDEV_VERSION;
|
||||
guest_info.ostype = 0;
|
||||
Request(&guest_info, sizeof(guest_info));
|
||||
|
||||
struct vbox_host_time host_time;
|
||||
memset(&host_time, 0, sizeof(host_time));
|
||||
host_time.hdr.size = sizeof(host_time);
|
||||
host_time.hdr.version = VBOX_REQUEST_HEADER_VERSION;
|
||||
host_time.hdr.request_type = VBOX_REQUEST_GET_HOST_TIME;
|
||||
Request(&host_time, sizeof(host_time));
|
||||
if ( host_time.hdr.rc == 0 )
|
||||
{
|
||||
struct timespec realtime;
|
||||
realtime.tv_sec = host_time.time / 1000;
|
||||
realtime.tv_nsec = (host_time.time % 1000) * 1000000L;
|
||||
Time::GetClock(CLOCK_REALTIME)->Set(&realtime, NULL);
|
||||
}
|
||||
|
||||
capabilities = 0;
|
||||
listening_events = 0;
|
||||
|
||||
ReportCapabilities();
|
||||
|
||||
interrupt_registration.handler = VBoxDevice__OnInterrupt;
|
||||
interrupt_registration.context = this;
|
||||
Interrupt::RegisterHandler(interrupt_index, &interrupt_registration);
|
||||
has_interrupt_registered = true;
|
||||
|
||||
regs->guest_event_mask = listening_events;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VBoxDevice::OnInterrupt()
|
||||
{
|
||||
uint32_t host_events = regs->host_events;
|
||||
if ( !host_events )
|
||||
return;
|
||||
|
||||
regs->guest_event_mask = 0;
|
||||
|
||||
// TODO: There's no protection that this interrupt handler isn't clobbering
|
||||
// the buffer because we don't have the lock.
|
||||
|
||||
struct vbox_ack_events ack_events;
|
||||
memset(&ack_events, 0, sizeof(ack_events));
|
||||
ack_events.hdr.size = sizeof(ack_events);
|
||||
ack_events.hdr.version = VBOX_REQUEST_HEADER_VERSION;
|
||||
ack_events.hdr.request_type = VBOX_REQUEST_ACK_EVENTS;
|
||||
ack_events.events = host_events; // TODO: Or?
|
||||
RequestIRQ(&ack_events, sizeof(ack_events));
|
||||
|
||||
Interrupt::ScheduleWork(VBoxDevice__InterruptWork, this,
|
||||
&host_events, sizeof(host_events));
|
||||
}
|
||||
|
||||
void VBoxDevice::InterruptWork(void* payload, size_t /*size*/)
|
||||
{
|
||||
ScopedLock lock(&buffer_lock);
|
||||
uint32_t host_events;
|
||||
memcpy(&host_events, payload, sizeof(host_events));
|
||||
|
||||
if ( host_events & VBOX_EVENT_DISPLAY_CHANGE_REQUEST )
|
||||
{
|
||||
struct vbox_get_display_change2 get_display_change;
|
||||
memset(&get_display_change, 0, sizeof(get_display_change));
|
||||
get_display_change.hdr.size = sizeof(get_display_change);
|
||||
get_display_change.hdr.version = VBOX_REQUEST_HEADER_VERSION;
|
||||
get_display_change.hdr.request_type = VBOX_REQUEST_GET_DISPLAY_CHANGE2;
|
||||
get_display_change.eventack = VBOX_EVENT_DISPLAY_CHANGE_REQUEST;
|
||||
Request(&get_display_change, sizeof(get_display_change));
|
||||
uint32_t display = get_display_change.display;
|
||||
uint32_t bpp = get_display_change.bpp;
|
||||
uint32_t xres = get_display_change.xres;
|
||||
uint32_t yres = get_display_change.yres;
|
||||
if ( has_video_device )
|
||||
Video::ResizeDisplay(video_device, display, xres, yres, bpp);
|
||||
}
|
||||
|
||||
regs->guest_event_mask = listening_events;
|
||||
}
|
||||
|
||||
void VBoxDevice::Request(void* bufptr, size_t size)
|
||||
{
|
||||
assert(size <= buffer1_alloc.size);
|
||||
unsigned char* buf = (unsigned char*) bufptr;
|
||||
for ( size_t i = 0; i < size; i++ )
|
||||
buffer1[i] = buf[i];
|
||||
outport32(port, buffer1_frame);
|
||||
for ( size_t i = 0; i < size; i++ )
|
||||
buf[i] = buffer1[i];
|
||||
}
|
||||
|
||||
void VBoxDevice::RequestIRQ(void* bufptr, size_t size)
|
||||
{
|
||||
assert(size <= buffer1_alloc.size);
|
||||
unsigned char* buf = (unsigned char*) bufptr;
|
||||
for ( size_t i = 0; i < size; i++ )
|
||||
buffer1[i] = buf[i];
|
||||
outport32(port, buffer1_frame);
|
||||
for ( size_t i = 0; i < size; i++ )
|
||||
buf[i] = buffer1[i];
|
||||
}
|
||||
|
||||
void VBoxDevice::ReportCapabilities()
|
||||
{
|
||||
struct vbox_guest_caps2 guest_caps;
|
||||
memset(&guest_caps, 0, sizeof(guest_caps));
|
||||
guest_caps.hdr.size = sizeof(guest_caps);
|
||||
guest_caps.hdr.version = VBOX_REQUEST_HEADER_VERSION;
|
||||
guest_caps.hdr.request_type = VBOX_REQUEST_SET_GUEST_CAPS2;
|
||||
guest_caps.caps_or = capabilities;
|
||||
guest_caps.caps_not = ~capabilities;
|
||||
Request(&guest_caps, sizeof(guest_caps));
|
||||
}
|
||||
|
||||
void VBoxDevice::LogF(const char* format, ...)
|
||||
{
|
||||
// TODO: Print this line in an atomic manner.
|
||||
Log::PrintF("vbox: pci 0x%X: ", devaddr);
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
Log::PrintFV(format, ap);
|
||||
va_end(ap);
|
||||
Log::PrintF("\n");
|
||||
}
|
||||
|
||||
bool VBoxDevice::IsSupportedVideoMode(uint32_t display, uint32_t xres,
|
||||
uint32_t yres, uint32_t bpp)
|
||||
{
|
||||
ScopedLock lock(&buffer_lock);
|
||||
struct vbox_video_mode_supported2 video_mode_supported;
|
||||
memset(&video_mode_supported, 0, sizeof(video_mode_supported));
|
||||
video_mode_supported.hdr.size = sizeof(video_mode_supported);
|
||||
video_mode_supported.hdr.version = VBOX_REQUEST_HEADER_VERSION;
|
||||
video_mode_supported.hdr.request_type = VBOX_REQUEST_VIDEO_MODE_SUPPORTED2;
|
||||
video_mode_supported.display = display;
|
||||
video_mode_supported.xres = xres;
|
||||
video_mode_supported.yres = yres;
|
||||
video_mode_supported.bpp = bpp;
|
||||
Request(&video_mode_supported, sizeof(video_mode_supported));
|
||||
if ( video_mode_supported.hdr.rc != 0 )
|
||||
return false;
|
||||
return video_mode_supported.is_supported;
|
||||
}
|
||||
|
||||
bool VBoxDevice::GetBestVideoMode(uint32_t display, uint32_t* xres_ptr,
|
||||
uint32_t* yres_ptr, uint32_t* bpp_ptr)
|
||||
{
|
||||
uint32_t bpp = 32;
|
||||
uint32_t xres = 1;
|
||||
uint32_t yres = 1;
|
||||
if ( !IsSupportedVideoMode(display, xres, yres, bpp) )
|
||||
{
|
||||
LogF("unsupported %ux%u\n", xres, yres);
|
||||
return false;
|
||||
}
|
||||
while ( IsSupportedVideoMode(display, xres + 1, yres, bpp) )
|
||||
xres++;
|
||||
while ( IsSupportedVideoMode(display, xres, yres + 1, bpp) )
|
||||
yres++;
|
||||
*xres_ptr = xres;
|
||||
*yres_ptr = yres;
|
||||
*bpp_ptr = bpp;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VBoxDevice::RegisterVideoDevice(uint64_t device_id)
|
||||
{
|
||||
ScopedLock lock(&buffer_lock);
|
||||
if ( has_video_device )
|
||||
return errno = EINVAL, false;
|
||||
video_device = device_id;
|
||||
has_video_device = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void VBoxDevice::ReadyVideoDevice(uint64_t device_id)
|
||||
{
|
||||
ScopedLock lock(&buffer_lock);
|
||||
if ( !has_video_device || device_id != video_device )
|
||||
return;
|
||||
capabilities |= VBOX_GUEST_SUPPORTS_GRAPHICS;
|
||||
ReportCapabilities();
|
||||
listening_events |= VBOX_EVENT_DISPLAY_CHANGE_REQUEST;
|
||||
regs->guest_event_mask = listening_events;
|
||||
}
|
||||
|
||||
void VBoxDevice::UnregisterVideoDevice(uint64_t device_id)
|
||||
{
|
||||
ScopedLock lock(&buffer_lock);
|
||||
if ( !has_video_device || device_id != video_device )
|
||||
return;
|
||||
has_video_device = false;
|
||||
listening_events &= ~VBOX_EVENT_DISPLAY_CHANGE_REQUEST;
|
||||
regs->guest_event_mask = listening_events;
|
||||
capabilities &= ~VBOX_GUEST_SUPPORTS_GRAPHICS;
|
||||
ReportCapabilities();
|
||||
}
|
||||
|
||||
static VBoxDevice* vbox;
|
||||
|
||||
GuestAdditions* GetGuestAdditions()
|
||||
{
|
||||
return vbox;
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
pcifind_t pcifind;
|
||||
memset(&pcifind, 255, sizeof(pcifind));
|
||||
pcifind.vendorid = 0x80EE;
|
||||
pcifind.deviceid = 0xCAFE;
|
||||
|
||||
uint32_t devaddr = PCI::SearchForDevices(pcifind, 0);
|
||||
if ( !devaddr )
|
||||
return;
|
||||
|
||||
vbox = new VBoxDevice(devaddr);
|
||||
if ( !vbox )
|
||||
Panic("Failed to allocate virtualbox guest additions driver");
|
||||
|
||||
if ( !vbox->Initialize() )
|
||||
{
|
||||
delete vbox;
|
||||
vbox = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VBox
|
||||
} // namespace Sortix
|
48
kernel/x86-family/vbox.h
Normal file
48
kernel/x86-family/vbox.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* x86-family/vbox.h
|
||||
* VirtualBox Guest Additions.
|
||||
*/
|
||||
|
||||
#ifndef SORTIX_X86_FAMILY_VBOX_H
|
||||
#define SORTIX_X86_FAMILY_VBOX_H
|
||||
|
||||
namespace Sortix {
|
||||
namespace VBox {
|
||||
|
||||
class GuestAdditions
|
||||
{
|
||||
public:
|
||||
virtual ~GuestAdditions() { }
|
||||
|
||||
public:
|
||||
virtual bool IsSupportedVideoMode(uint32_t display, uint32_t xres,
|
||||
uint32_t yres, uint32_t bpp) = 0;
|
||||
virtual bool GetBestVideoMode(uint32_t display, uint32_t* xres,
|
||||
uint32_t* yres, uint32_t* bpp) = 0;
|
||||
virtual bool RegisterVideoDevice(uint64_t device_id) = 0;
|
||||
virtual void ReadyVideoDevice(uint64_t device_id) = 0;
|
||||
virtual void UnregisterVideoDevice(uint64_t device_id) = 0;
|
||||
|
||||
};
|
||||
|
||||
void Init();
|
||||
GuestAdditions* GetGuestAdditions();
|
||||
|
||||
} // namespace VBox
|
||||
} // namespace Sortix
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue