1df48b8331
To ease the creation of custom virtual machine monitors on top of NOVA, this patch moves generic utilities from vancouver resp. seoul to the public include location 'ports/include/vmm'. As a nice side effect, this change simplifies 'vancouver/main.cc'. Issue #949
361 lines
10 KiB
C++
361 lines
10 KiB
C++
/*
|
|
* \brief Manager of all VM requested console functionality
|
|
* \author Markus Partheymueller
|
|
* \author Norman Feske
|
|
* \date 2012-07-31
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2011-2013 Genode Labs GmbH
|
|
* Copyright (C) 2012 Intel Corporation
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU General Public License version 2.
|
|
*
|
|
* The code is partially based on the Vancouver VMM, which is distributed
|
|
* under the terms of the GNU General Public License version 2.
|
|
*
|
|
* Modifications by Intel Corporation are contributed under the terms and
|
|
* conditions of the GNU General Public License version 2.
|
|
*/
|
|
|
|
/* local includes */
|
|
#include <console.h>
|
|
#include <keyboard.h>
|
|
|
|
/* Genode includes */
|
|
#include <base/snprintf.h>
|
|
#include <util/register.h>
|
|
|
|
/* nitpicker graphics backend */
|
|
#include <nitpicker_gfx/chunky_canvas.h>
|
|
#include <nitpicker_gfx/pixel_rgb565.h>
|
|
#include <nitpicker_gfx/font.h>
|
|
|
|
extern char _binary_mono_tff_start;
|
|
Font default_font(&_binary_mono_tff_start);
|
|
|
|
|
|
using Genode::env;
|
|
using Genode::Dataspace_client;
|
|
|
|
bool fb_active = true;
|
|
|
|
|
|
/**
|
|
* Layout of PS/2 mouse packet
|
|
*/
|
|
struct Ps2_mouse_packet : Genode::Register<32>
|
|
{
|
|
struct Packet_size : Bitfield<0, 3> { };
|
|
struct Left_button : Bitfield<8, 1> { };
|
|
struct Middle_button : Bitfield<9, 1> { };
|
|
struct Right_button : Bitfield<10, 1> { };
|
|
struct Rx_high : Bitfield<12, 1> { };
|
|
struct Ry_high : Bitfield<13, 1> { };
|
|
struct Rx_low : Bitfield<16, 8> { };
|
|
struct Ry_low : Bitfield<24, 8> { };
|
|
};
|
|
|
|
|
|
static bool is_mouse_event(Input::Event const *ev)
|
|
{
|
|
using Input::Event;
|
|
if (ev->type() == Event::PRESS || ev->type() == Event::RELEASE) {
|
|
if (ev->code() == Input::BTN_LEFT) return true;
|
|
if (ev->code() == Input::BTN_MIDDLE) return true;
|
|
if (ev->code() == Input::BTN_RIGHT) return true;
|
|
}
|
|
|
|
if (ev->type() == Event::MOTION)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Convert Genode::Input event to PS/2 packet
|
|
*
|
|
* This function updates _left, _middle, and _right as a side effect.
|
|
*/
|
|
unsigned Vancouver_console::_input_to_ps2mouse(Input::Event const *ev)
|
|
{
|
|
/* track state of mouse buttons */
|
|
using Input::Event;
|
|
if (ev->type() == Event::PRESS || ev->type() == Event::RELEASE) {
|
|
bool const pressed = ev->type() == Event::PRESS;
|
|
if (ev->code() == Input::BTN_LEFT) _left = pressed;
|
|
if (ev->code() == Input::BTN_MIDDLE) _middle = pressed;
|
|
if (ev->code() == Input::BTN_RIGHT) _right = pressed;
|
|
}
|
|
|
|
/* clamp relative motion vector to bounds */
|
|
int const boundary = 200;
|
|
int const rx = min(boundary, max(-boundary, ev->rx()));
|
|
int const ry = -min(boundary, max(-boundary, ev->ry()));
|
|
|
|
/* assemble PS/2 packet */
|
|
Ps2_mouse_packet::access_t packet = 0;
|
|
Ps2_mouse_packet::Packet_size::set (packet, 3);
|
|
Ps2_mouse_packet::Left_button::set (packet, _left);
|
|
Ps2_mouse_packet::Middle_button::set(packet, _middle);
|
|
Ps2_mouse_packet::Right_button::set (packet, _right);
|
|
Ps2_mouse_packet::Rx_high::set (packet, (rx >> 8) & 1);
|
|
Ps2_mouse_packet::Ry_high::set (packet, (ry >> 8) & 1);
|
|
Ps2_mouse_packet::Rx_low::set (packet, rx & 0xff);
|
|
Ps2_mouse_packet::Ry_low::set (packet, ry & 0xff);
|
|
|
|
return packet;
|
|
}
|
|
|
|
|
|
/* bus callbacks */
|
|
|
|
bool Vancouver_console::receive(MessageConsole &msg)
|
|
{
|
|
if (msg.type == MessageConsole::TYPE_ALLOC_VIEW) {
|
|
_guest_fb = msg.ptr;
|
|
|
|
if (msg.size < _fb_size)
|
|
_fb_size = msg.size;
|
|
|
|
_regs = msg.regs;
|
|
|
|
msg.view = 0;
|
|
} else if (msg.type == MessageConsole::TYPE_SWITCH_VIEW) {
|
|
/* XXX: For now, we only have one view. */
|
|
} else if (msg.type == MessageConsole::TYPE_GET_MODEINFO) {
|
|
|
|
/*
|
|
* We supply two modes to the guest, text mode and one
|
|
* configured graphics mode 16-bit.
|
|
*/
|
|
if (msg.index == 0) {
|
|
msg.info->_vesa_mode = 3;
|
|
msg.info->attr = 0x1;
|
|
msg.info->resolution[0] = 80;
|
|
msg.info->resolution[1] = 25;
|
|
msg.info->bytes_per_scanline = 80*2;
|
|
msg.info->bytes_scanline = 80*2;
|
|
msg.info->bpp = 4;
|
|
msg.info->phys_base = 0xb8000;
|
|
msg.info->_phys_size = 0x8000;
|
|
return true;
|
|
|
|
} else if (msg.index == 1) {
|
|
|
|
/*
|
|
* It's important to set the _vesa_mode field, otherwise the
|
|
* device model is going to ignore this mode.
|
|
*/
|
|
msg.info->_vesa_mode = 0x114;
|
|
msg.info->attr = 0x39f;
|
|
msg.info->resolution[0] = _fb_mode.width();
|
|
msg.info->resolution[1] = _fb_mode.height();
|
|
msg.info->bytes_per_scanline = _fb_mode.width()*2;
|
|
msg.info->bytes_scanline = _fb_mode.width()*2;
|
|
msg.info->bpp = 16;
|
|
msg.info->vbe1[0] = 0x5; /* red mask size */
|
|
msg.info->vbe1[1] = 0xb; /* red field position */
|
|
msg.info->vbe1[2] = 0x6; /* green mask size */
|
|
msg.info->vbe1[3] = 0x5; /* green field position */
|
|
msg.info->vbe1[4] = 0x5; /* blue mask size */
|
|
msg.info->vbe1[5] = 0x0; /* blue field position */
|
|
msg.info->vbe1[6] = 0x0; /* reserved mask size */
|
|
msg.info->vbe1[7] = 0x0; /* reserved field position */
|
|
msg.info->vbe1[8] = 0x0; /* direct color mode info */
|
|
msg.info->phys_base = 0xe0000000;
|
|
msg.info->_phys_size = _fb_mode.width()*_fb_mode.height()*2;
|
|
return true;
|
|
} else return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool Vancouver_console::receive(MessageMemRegion &msg)
|
|
{
|
|
if (msg.page >= 0xb8 && msg.page <= 0xbf) {
|
|
|
|
/* we had a fault in the text framebuffer */
|
|
if (!fb_active) fb_active = true;
|
|
Logging::printf("Reactivating text buffer loop.\n");
|
|
}
|
|
}
|
|
|
|
|
|
void Vancouver_console::entry()
|
|
{
|
|
/*
|
|
* Init sessions to the required external services
|
|
*/
|
|
enum { CONFIG_ALPHA = false };
|
|
|
|
static Input::Connection input;
|
|
static Timer::Connection timer;
|
|
Framebuffer::Connection *framebuffer = 0;
|
|
|
|
try {
|
|
framebuffer = new (env()->heap()) Framebuffer::Connection();
|
|
} catch (...) {
|
|
PERR("Headless mode - no framebuffer session available");
|
|
_startup_lock.unlock();
|
|
return;
|
|
}
|
|
|
|
Genode::Dataspace_capability ev_ds_cap = input.dataspace();
|
|
|
|
Input::Event *ev_buf = static_cast<Input::Event *>
|
|
(env()->rm_session()->attach(ev_ds_cap));
|
|
|
|
_fb_size = Dataspace_client(framebuffer->dataspace()).size();
|
|
_fb_mode = framebuffer->mode();
|
|
|
|
_pixels = env()->rm_session()->attach(framebuffer->dataspace());
|
|
|
|
Chunky_canvas<Pixel_rgb565> canvas((Pixel_rgb565 *) _pixels,
|
|
Area(_fb_mode.width(),
|
|
_fb_mode.height()));
|
|
|
|
/*
|
|
* Handle input events
|
|
*/
|
|
unsigned long count = 0;
|
|
bool revoked = false;
|
|
Vancouver_keyboard vkeyb(_motherboard);
|
|
|
|
Genode::uint64_t checksum1 = 0;
|
|
Genode::uint64_t checksum2 = 0;
|
|
unsigned unchanged = 0;
|
|
bool cmp_even = 1;
|
|
|
|
_startup_lock.unlock();
|
|
|
|
while (1) {
|
|
while (!input.is_pending()) {
|
|
|
|
/* transfer text buffer content into chunky canvas */
|
|
if (_regs && ++count % 10 == 0 && _regs->mode == 0
|
|
&& _guest_fb && !revoked && fb_active) {
|
|
|
|
memset(_pixels, 0, _fb_size);
|
|
if (cmp_even) checksum1 = 0;
|
|
else checksum2 = 0;
|
|
for (int j=0; j<25; j++) {
|
|
for (int i=0; i<80; i++) {
|
|
Point where(i*8, j*15);
|
|
char character = *((char *) (_guest_fb +(_regs->offset << 1) +j*80*2+i*2));
|
|
char colorvalue = *((char *) (_guest_fb+(_regs->offset << 1)+j*80*2+i*2+1));
|
|
char buffer[2]; buffer[0] = character; buffer[1] = 0;
|
|
char fg = colorvalue & 0xf;
|
|
if (fg == 0x8) fg = 0x7;
|
|
unsigned lum = ((fg & 0x8) >> 3)*127;
|
|
Color color(((fg & 0x4) >> 2)*127+lum, /* R+luminosity */
|
|
((fg & 0x2) >> 1)*127+lum, /* G+luminosity */
|
|
(fg & 0x1)*127+lum /* B+luminosity */);
|
|
|
|
canvas.draw_string(where, default_font, color, buffer);
|
|
|
|
/* Checksum for comparing */
|
|
if (cmp_even) checksum1 += character;
|
|
else checksum2 += character;
|
|
}
|
|
}
|
|
/* compare checksums to detect idle buffer */
|
|
if (checksum1 == checksum2) {
|
|
unchanged++;
|
|
|
|
/* if we copy the same data 10 times, unmap the text buffer from guest */
|
|
if (unchanged == 10) {
|
|
|
|
Genode::Lock::Guard guard(_console_lock);
|
|
|
|
env()->rm_session()->detach((void *)_guest_fb);
|
|
env()->rm_session()->attach_at(_fb_ds, (Genode::addr_t)_guest_fb);
|
|
unchanged = 0;
|
|
fb_active = false;
|
|
|
|
Logging::printf("Deactivated text buffer loop.\n");
|
|
}
|
|
} else {
|
|
unchanged = 0;
|
|
framebuffer->refresh(0, 0, _fb_mode.width(), _fb_mode.height());
|
|
}
|
|
|
|
cmp_even = !cmp_even;
|
|
} else if (_regs && _guest_fb && _regs->mode != 0) {
|
|
|
|
if (!revoked) {
|
|
|
|
Genode::Lock::Guard guard(_console_lock);
|
|
|
|
env()->rm_session()->detach((void *)_guest_fb);
|
|
env()->rm_session()->attach_at(framebuffer->dataspace(),
|
|
(Genode::addr_t)_guest_fb);
|
|
|
|
/* if the VGA model expects a larger FB, pad to that size. */
|
|
if (_fb_size < _vm_fb_size) {
|
|
Genode::Ram_dataspace_capability _backup =
|
|
Genode::env()->ram_session()->alloc(_vm_fb_size-_fb_size);
|
|
|
|
env()->rm_session()->attach_at(_backup,
|
|
(Genode::addr_t) (_guest_fb+_fb_size));
|
|
}
|
|
|
|
revoked = true;
|
|
}
|
|
framebuffer->refresh(0, 0, _fb_mode.width(), _fb_mode.height());
|
|
}
|
|
|
|
timer.msleep(10);
|
|
}
|
|
|
|
for (int i = 0, num_ev = input.flush(); i < num_ev; i++) {
|
|
Input::Event *ev = &ev_buf[i];
|
|
|
|
/* update mouse model (PS2) */
|
|
if (is_mouse_event(ev)) {
|
|
MessageInput msg(0x10001, _input_to_ps2mouse(ev));
|
|
_motherboard()->bus_input.send(msg);
|
|
}
|
|
|
|
if (ev->type() == Input::Event::PRESS) {
|
|
if (ev->code() <= 0xee) {
|
|
vkeyb.handle_keycode_press(ev->code());
|
|
}
|
|
}
|
|
if (ev->type() == Input::Event::RELEASE) {
|
|
if (ev->code() <= 0xee) { /* keyboard event */
|
|
vkeyb.handle_keycode_release(ev->code());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Vancouver_console::register_host_operations(Motherboard &motherboard)
|
|
{
|
|
motherboard.bus_console. add(this, receive_static<MessageConsole>);
|
|
motherboard.bus_memregion.add(this, receive_static<MessageMemRegion>);
|
|
}
|
|
|
|
|
|
Vancouver_console::Vancouver_console(Synced_motherboard &mb,
|
|
Genode::size_t vm_fb_size,
|
|
Genode::Dataspace_capability fb_ds)
|
|
:
|
|
Thread("vmm_console"),
|
|
_startup_lock(Genode::Lock::LOCKED),
|
|
_vm_fb_size(vm_fb_size), _motherboard(mb),
|
|
_fb_size(0), _pixels(0), _guest_fb(0),
|
|
_regs(0), _fb_ds(fb_ds),
|
|
_left(false), _middle(false), _right(false)
|
|
{
|
|
start();
|
|
|
|
/* shake hands with console thread */
|
|
_startup_lock.lock();
|
|
}
|