/* * This file is part of the Palacios Virtual Machine Monitor developed * by the V3VEE Project with funding from the United States National * Science Foundation and the Department of Energy. * * The V3VEE Project is a joint project between Northwestern University * and the University of New Mexico. You can find out more at * http://www.v3vee.org * * Copyright (c) 2009, Robert Deloatch * Copyright (c) 2009, Steven Jaconette * Copyright (c) 2009, The V3VEE Project * All rights reserved. * * Author: Erik van der Kouwe (vdkouwe@cs.vu.nl) * * This is free software. You are permitted to use, * redistribute, and modify it as specified in the file "V3VEE_LICENSE". */ /* Interface between virtual video card and console */ #include #include #include #include #include #include #include #include #define NUM_ROWS 25 #define NUM_COLS 80 #define BYTES_PER_COL 2 #define BYTES_PER_ROW (NUM_COLS * BYTES_PER_COL) #define SCREEN_SIZE (BYTES_PER_ROW * NUM_ROWS) struct cons_state { void *tty; 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 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); } /* adjust cursor */ if (V3_TtyCursorSet(state->tty, x, y) < 0) { PrintError("V3_TtyCursorSet(0x%p, %d, %d) failed\n", state->tty, x, y); return -1; } /* done with console update */ if (V3_TtyUpdate(state->tty) < 0) { PrintError("V3_TtyUpdate(0x%p) failed\n", state->tty); return -1; } return 0; } 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]; int i; uint_t cur_x = x; uint_t cur_y = y; /* grab frame buffer */ memset(fb_buf, 0, length); v3_cons_get_fb(state->frontend_dev, fb_buf, 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 /* update current character */ if (V3_TtyCharacterSet(state->tty, cur_x, cur_y, col[0], col[1]) < 0) { PrintError("V3_TtyCursorSet(0x%p, %d, %d, %d, %d) failed\n", state->tty, 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; } /* done with console update */ if (V3_TtyUpdate(state->tty) < 0) { PrintError("V3_TtyUpdate(0x%p) failed\n", state->tty); return -1; } /* store offset to catch missing notifications */ last_offset = offset + length; return 0; } static int scroll(int rows, void *private_data) { struct vm_device *dev = (struct vm_device *)private_data; struct cons_state *state = (struct cons_state *)dev->private_data; if (rows < 0) { /* simply update the screen */ return screen_update(0, 0, SCREEN_SIZE, private_data); } if (rows > 0) { /* scroll requested number of lines*/ if (V3_TtyScroll(state->tty, rows) < 0) { PrintError("V3_TtyScroll(0x%p, %u) failed\n", state->tty, rows); return -1; } /* done with console update */ if (V3_TtyUpdate(state->tty) < 0) { PrintError("V3_TtyUpdate(0x%p) failed\n", state->tty); return -1; } last_offset = BYTES_PER_ROW * (NUM_ROWS - 1); } return 0; } static struct v3_console_ops cons_ops = { .update_screen = screen_update, .update_cursor = cursor_update, .scroll = scroll, }; static struct v3_device_ops dev_ops = { .free = NULL, .reset = NULL, .start = NULL, .stop = NULL, }; static int cons_init(struct guest_info * vm, v3_cfg_tree_t * cfg) { struct cons_state * state; v3_cfg_tree_t * frontend_cfg; const char *frontend_tag; struct vm_device * frontend; char *name, *ttypath; /* read configuration */ frontend_cfg = v3_cfg_subtree(cfg, "frontend"); V3_ASSERT(frontend_cfg); frontend_tag = v3_cfg_val(frontend_cfg, "tag"); V3_ASSERT(frontend_tag); frontend = v3_find_dev(vm, frontend_tag); V3_ASSERT(frontend); name = v3_cfg_val(cfg, "name"); /* allocate state */ state = (struct cons_state *) V3_Malloc(sizeof(struct cons_state)); V3_ASSERT(state); state->frontend_dev = frontend; ttypath = v3_cfg_val(cfg, "tty"); V3_ASSERT(ttypath); /* open tty for screen display */ state->tty = V3_TtyOpen(ttypath, TTY_OPEN_MODE_READ | TTY_OPEN_MODE_WRITE); if (!state->tty) { PrintError("Could not open tty %s\n", ttypath); V3_Free(state); return -1; } /* allocate device */ struct vm_device *dev = v3_allocate_device(name, &dev_ops, state); V3_ASSERT(dev); /* attach device to virtual machine */ if (v3_attach_device(vm, dev) == -1) { PrintError("Could not attach device %s\n", name); V3_Free(state); return -1; } /* attach to front-end display adapter */ v3_console_register_cga(frontend, &cons_ops, dev); return 0; } device_register("CURSES_CONSOLE", cons_init)