316 lines
8.5 KiB
C++
316 lines
8.5 KiB
C++
/*
|
|
* \brief Manager of all VM requested console functionality
|
|
* \author Markus Partheymueller
|
|
* \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>
|
|
|
|
/* 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);
|
|
|
|
extern Genode::Lock global_lock;
|
|
|
|
using Genode::env;
|
|
using Genode::Dataspace_client;
|
|
|
|
bool fb_active = true;
|
|
|
|
|
|
static unsigned mouse_value(Input::Event * ev)
|
|
{
|
|
/* bit 3 is always set */
|
|
unsigned ret = 0x8;
|
|
|
|
/* signs and movements */
|
|
int x=0, y=0;
|
|
if (ev->rx() > 0) x = 1;
|
|
if (ev->rx() < 0) x = -1;
|
|
if (ev->ry() > 0) y = 1;
|
|
if (ev->ry() < 0) y = -1;
|
|
|
|
if (x > 0)
|
|
ret |= (1 << 8);
|
|
if (x < 0)
|
|
ret |= (0xfe << 8) | (1 << 4);
|
|
if (y < 0) /* nitpicker's negative is PS2 positive */
|
|
ret |= (1 << 16);
|
|
if (y > 0)
|
|
ret |= (0xfe << 16) | (1 << 5);
|
|
|
|
/* buttons */
|
|
ret |= ((ev->code() == Input::BTN_MIDDLE ? 1 : 0) << 2);
|
|
ret |= ((ev->code() == Input::BTN_RIGHT ? 1 : 0) << 1);
|
|
ret |= ((ev->code() == Input::BTN_LEFT ? 1 : 0) << 0);
|
|
|
|
/* ps2mouse model expects 3 in the first byte */
|
|
return (ret << 8) | 0x3;
|
|
}
|
|
|
|
/*
|
|
* Console implementation
|
|
*/
|
|
|
|
/* 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()
|
|
{
|
|
Logging::printf("Hello, this is VancouverConsole.\n");
|
|
|
|
/* register host operations */
|
|
_mb.bus_console.add(this, receive_static<MessageConsole>);
|
|
_mb.bus_memregion.add(this, receive_static<MessageMemRegion>);
|
|
|
|
/* create environment for input/output */
|
|
|
|
/*
|
|
* Init sessions to the required external services
|
|
*/
|
|
enum { CONFIG_ALPHA = false };
|
|
static Framebuffer::Connection framebuffer;
|
|
static Input::Connection input;
|
|
static Timer::Connection timer;
|
|
|
|
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(_mb);
|
|
|
|
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+0x18000+j*80*2+i*2));
|
|
char colorvalue = *((char *) (_guest_fb+0x18000+j*80*2+i*2+1));
|
|
char buffer[2];
|
|
Genode::snprintf(buffer, 1, "%c", character);
|
|
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) {
|
|
|
|
/* protect against thread interference */
|
|
global_lock.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;
|
|
|
|
global_lock.unlock();
|
|
Logging::printf("Deactivated text buffer loop.\n");
|
|
}
|
|
} else unchanged = 0;
|
|
cmp_even = !cmp_even;
|
|
|
|
} else if (_regs && _guest_fb && _regs->mode != 0) {
|
|
|
|
if (!revoked) {
|
|
|
|
/* protect against thread interference */
|
|
global_lock.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;
|
|
|
|
global_lock.unlock();
|
|
}
|
|
}
|
|
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) */
|
|
unsigned mouse = mouse_value(ev);
|
|
MessageInput msg(0x10001, mouse);
|
|
_mb.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());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Vancouver_console::Vancouver_console(Motherboard &mb, Genode::size_t vm_fb_size,
|
|
Genode::Dataspace_capability fb_ds)
|
|
:
|
|
_startup_lock(Genode::Lock::LOCKED),
|
|
_vm_fb_size(vm_fb_size), _mb(mb), _fb_size(0), _pixels(0), _guest_fb(0),
|
|
_regs(0), _fb_ds(fb_ds)
|
|
{
|
|
start();
|
|
|
|
/* shake hands with console thread */
|
|
_startup_lock.lock();
|
|
}
|