/* Interface between virtual video card and console */
#include <palacios/vmm.h>
-#include <palacios/vmm_console.h>
+#include <interfaces/vmm_console.h>
#include <palacios/vmm_dev_mgr.h>
#include <palacios/vmm_sprintf.h>
#include <palacios/vmm_host_events.h>
#include <devices/console.h>
-#define NUM_ROWS 25
-#define NUM_COLS 80
-#define BYTES_PER_COL 2
-#define BYTES_PER_ROW (NUM_COLS * BYTES_PER_COL)
+#ifndef DEBUG_CURSES_CONS
+#undef PrintDebug
+#define PrintDebug(fmt, args...)
+#endif
-#define SCREEN_SIZE (BYTES_PER_ROW * NUM_ROWS)
+#define BYTES_PER_COL 2
struct cons_state {
v3_console_t cons;
+ int rows;
+ int cols;
+ uint8_t * framebuf;
struct vm_device * frontend_dev;
};
static int screen_update(uint_t x, uint_t y, uint_t length, void *private_data);
-static uint_t last_offset;
+static int screen_update_all(void * private_data) {
+ struct vm_device *dev = (struct vm_device *) private_data;
+ struct cons_state *state = (struct cons_state *)dev->private_data;
+ uint_t screen_size;
+
+ screen_size = state->cols * state->rows * BYTES_PER_COL;
+ return screen_update(0, 0, screen_size, private_data);
+}
static int cursor_update(uint_t x, uint_t y, void *private_data)
{
struct vm_device *dev = (struct vm_device *) private_data;
struct cons_state *state = (struct cons_state *) dev->private_data;
- uint_t offset = (x * BYTES_PER_COL) + (y * BYTES_PER_ROW);
- uint_t last_x, last_y;
-
- /* unfortunately Palacios sometimes misses some writes,
- * but if they are accompanied by a cursor move we may be able to
- * detect this
- */
- if (offset < last_offset) last_offset = 0;
-
- if (offset > last_offset) {
- last_x = (last_offset % BYTES_PER_ROW) / BYTES_PER_COL;
- last_y = last_offset / BYTES_PER_ROW;
- screen_update(last_x, last_y, offset - last_offset, private_data);
- }
+ uint_t offset;
+
+ PrintDebug(VM_NONE, VCORE_NONE, "cursor_update(%d, %d, %p)\n", x, y, private_data);
+
+ /* avoid out-of-range coordinates */
+ if (x < 0) x = 0;
+ if (y < 0) y = 0;
+ if (x >= state->cols) x = state->cols - 1;
+ if (y >= state->rows) y = state->rows - 1;
+ offset = (x + y * state->cols) * BYTES_PER_COL;
/* adjust cursor */
if (v3_console_set_cursor(state->cons, x, y) < 0) {
- PrintError("set cursor (0x%p, %d, %d) failed\n", state->cons, x, y);
+ PrintError(VM_NONE, VCORE_NONE, "set cursor (0x%p, %d, %d) failed\n", state->cons, x, y);
return -1;
}
/* done with console update */
if (v3_console_update(state->cons) < 0) {
- PrintError("console update (0x%p) failed\n", state->cons);
+ PrintError(VM_NONE, VCORE_NONE, "console update (0x%p) failed\n", state->cons);
return -1;
}
}
static int screen_update(uint_t x, uint_t y, uint_t length, void * private_data) {
- struct vm_device *dev = (struct vm_device *)private_data;
- struct cons_state *state = (struct cons_state *)dev->private_data;
- uint_t offset = (x * BYTES_PER_COL) + (y * BYTES_PER_ROW);
- uint8_t fb_buf[length];
+ struct vm_device * dev = (struct vm_device *)private_data;
+ struct cons_state * state = (struct cons_state *)dev->private_data;
+ uint_t offset = (x + y * state->cols) * BYTES_PER_COL;
int i;
uint_t cur_x = x;
uint_t cur_y = y;
+ if (length > (state->rows * state->cols * BYTES_PER_COL)) {
+ PrintError(VM_NONE, VCORE_NONE, "Screen update larger than curses framebuffer\n");
+ return 0;
+ }
+
+ PrintDebug(VM_NONE, VCORE_NONE, "screen_update(%d, %d, %d, %p)\n", x, y, length, private_data);
+
/* grab frame buffer */
- memset(fb_buf, 0, length);
- v3_cons_get_fb(state->frontend_dev, fb_buf, offset, length);
+ v3_cons_get_fb(state->frontend_dev, state->framebuf, offset, length);
/* update the screen */
for (i = 0; i < length; i += 2) {
uint_t col_index = i;
uint8_t col[2];
- col[0] = fb_buf[col_index]; // Character
- col[1] = fb_buf[col_index + 1]; // Attribute
+ col[0] = state->framebuf[col_index]; // Character
+ col[1] = state->framebuf[col_index + 1]; // Attribute
/* update current character */
if (v3_console_set_char(state->cons, cur_x, cur_y, col[0], col[1]) < 0) {
- PrintError("set cursor (0x%p, %d, %d, %d, %d) failed\n",
+ PrintError(VM_NONE, VCORE_NONE, "set cursor (0x%p, %d, %d, %d, %d) failed\n",
state->cons, cur_x, cur_y, col[1], col[0]);
return -1;
}
// CAUTION: the order of these statements is critical
// cur_y depends on the previous value of cur_x
- cur_y = cur_y + ((cur_x + 1) / NUM_COLS);
- cur_x = (cur_x + 1) % NUM_COLS;
+ cur_y = cur_y + ((cur_x + 1) / state->cols);
+ cur_x = (cur_x + 1) % state->cols;
}
/* done with console update */
if (v3_console_update(state->cons) < 0) {
- PrintError("console update(0x%p) failed\n", state->cons);
+ PrintError(VM_NONE, VCORE_NONE, "console update(0x%p) failed\n", state->cons);
return -1;
}
- /* store offset to catch missing notifications */
- last_offset = offset + length;
-
return 0;
}
struct vm_device *dev = (struct vm_device *)private_data;
struct cons_state *state = (struct cons_state *)dev->private_data;
+ PrintDebug(VM_NONE, VCORE_NONE, "scroll(%d, %p)\n", rows, private_data);
+
if (rows < 0) {
/* simply update the screen */
- return screen_update(0, 0, SCREEN_SIZE, private_data);
+ return screen_update_all(private_data);
}
if (rows > 0) {
/* scroll requested number of lines*/
if (v3_console_scroll(state->cons, rows) < 0) {
- PrintError("console scroll (0x%p, %u) failed\n", state->cons, rows);
+ PrintError(VM_NONE, VCORE_NONE, "console scroll (0x%p, %u) failed\n", state->cons, rows);
return -1;
}
/* done with console update */
if (v3_console_update(state->cons) < 0) {
- PrintError("console update (0x%p) failed\n", state->cons);
+ PrintError(VM_NONE, VCORE_NONE, "console update (0x%p) failed\n", state->cons);
return -1;
}
-
- last_offset = BYTES_PER_ROW * (NUM_ROWS - 1);
}
return 0;
}
+static int set_text_resolution(int cols, int rows, void * private_data) {
+ struct vm_device *dev = (struct vm_device *)private_data;
+ struct cons_state *state = (struct cons_state *)dev->private_data;
+
+ PrintDebug(VM_NONE, VCORE_NONE, "set_text_resolution(%d, %d, %p)\n", cols, rows, private_data);
+
+ /* store resolution for internal use */
+ V3_ASSERT(VM_NONE, VCORE_NONE, cols >= 1);
+ V3_ASSERT(VM_NONE, VCORE_NONE, rows >= 1);
+ state->cols = cols;
+ state->rows = rows;
+
+ /* set notification regarding resolution change */
+ if (v3_console_set_text_resolution(state->cons, cols, rows) < 0) {
+ PrintError(VM_NONE, VCORE_NONE, "console set_text_resolution (0x%p, %u, %u) failed\n", state->cons, cols, rows);
+ return -1;
+ }
+
+ /* update the screen */
+ return screen_update_all(private_data);
+}
+
+static int cons_free(struct cons_state * state) {
+ v3_console_close(state->cons);
+
+ // remove host event
+
+ V3_Free(state);
+
+ return 0;
+}
static int console_event_handler(struct v3_vm_info * vm,
struct v3_console_event * evt,
void * priv_data) {
- screen_update(0, 0, SCREEN_SIZE, priv_data);
-
- return 0;
+ return screen_update_all(priv_data);
}
static struct v3_console_ops cons_ops = {
.update_screen = screen_update,
.update_cursor = cursor_update,
.scroll = scroll,
+ .set_text_resolution = set_text_resolution,
};
static struct v3_device_ops dev_ops = {
- .free = NULL,
- .reset = NULL,
- .start = NULL,
- .stop = NULL,
+ .free = (int (*)(void *))cons_free,
};
static int cons_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg)
{
struct cons_state * state = NULL;
- v3_cfg_tree_t * frontend_cfg = v3_cfg_subtree(cfg, "frontend");
- const char * frontend_tag = v3_cfg_val(frontend_cfg, "tag");
- struct vm_device * frontend = v3_find_dev(vm, frontend_tag);
+ v3_cfg_tree_t * frontend_cfg;
+ const char * frontend_tag;
+ struct vm_device * frontend;
char * dev_id = v3_cfg_val(cfg, "ID");
/* read configuration */
- V3_ASSERT(frontend_cfg);
- V3_ASSERT(frontend_tag);
- V3_ASSERT(frontend);
+ frontend_cfg = v3_cfg_subtree(cfg, "frontend");
+ if (!frontend_cfg) {
+ PrintError(vm, VCORE_NONE, "No frontend specification for curses console.\n");
+ return -1;
+ }
+ frontend_tag = v3_cfg_val(frontend_cfg, "tag");
+ if (!frontend_tag) {
+ PrintError(vm, VCORE_NONE, "No frontend device tag specified for curses console.\n");
+ return -1;
+ }
+
+ frontend = v3_find_dev(vm, frontend_tag);
+ if (!frontend) {
+ PrintError(vm, VCORE_NONE, "Could not find frontend device %s for curses console.\n",
+ frontend_tag);
+ return -1;
+ }
/* allocate state */
state = (struct cons_state *)V3_Malloc(sizeof(struct cons_state));
- V3_ASSERT(state);
+
+ if (!state) {
+ PrintError(vm, VCORE_NONE, "Cannot allocate curses state\n");
+ V3_Free(state);
+ return -1;
+ }
state->frontend_dev = frontend;
+ state->cols = 80;
+ state->rows = 25;
+ state->framebuf = V3_Malloc(state->cols * state->rows * BYTES_PER_COL);
+
+ if (!state->framebuf) {
+ PrintError(vm, VCORE_NONE, "Cannot allocate frame buffer\n");
+ V3_Free(state);
+ return -1;
+ }
/* open tty for screen display */
- state->cons = v3_console_open(vm, NUM_COLS, NUM_ROWS);
+ state->cons = v3_console_open(vm, state->cols, state->rows);
if (!state->cons) {
- PrintError("Could not open console\n");
+ PrintError(vm, VCORE_NONE, "Could not open console\n");
+ V3_Free(state->framebuf);
V3_Free(state);
return -1;
}
/* allocate device */
- struct vm_device * dev = v3_allocate_device(dev_id, &dev_ops, state);
- V3_ASSERT(dev);
+ struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, state);
- /* attach device to virtual machine */
- if (v3_attach_device(vm, dev) == -1) {
- PrintError("Could not attach device %s\n", dev_id);
+ if (dev == NULL) {
+ PrintError(vm, VCORE_NONE, "Could not attach device %s\n", dev_id);
+ V3_Free(state->framebuf);
V3_Free(state);
return -1;
}