2 * This file is part of the Palacios Virtual Machine Monitor developed
3 * by the V3VEE Project with funding from the United States National
4 * Science Foundation and the Department of Energy.
6 * The V3VEE Project is a joint project between Northwestern University
7 * and the University of New Mexico. You can find out more at
10 * Copyright (c) 2009, Robert Deloatch <rtdeloatch@gmail.com>
11 * Copyright (c) 2009, Steven Jaconette <stevenjaconette2007@u.northwestern.edu>
12 * Copyright (c) 2009, The V3VEE Project <http://www.v3vee.org>
13 * All rights reserved.
15 * Author: Robdert Deloatch <rtdeloatch@gmail.com>
16 * Steven Jaconette <stevenjaconette2007@u.northwestern.edu>
18 * This is free software. You are permitted to use,
19 * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
22 /* Interface between virtual video card and client apps */
24 #include <palacios/vmm.h>
25 #include <palacios/vmm_dev_mgr.h>
26 #include <palacios/vmm_sprintf.h>
27 #include <palacios/vmm_host_events.h>
28 #include <palacios/vmm_lock.h>
29 #include <palacios/vmm_string.h>
30 #include <interfaces/vmm_socket.h>
32 #include <devices/console.h>
37 #define BYTES_PER_ROW (NUM_COLS * 2)
38 #define BYTES_PER_COL 2
41 #define SCREEN_SIZE 4000
43 #define NO_KEY { 0, 0 }
44 #define ESC_CHAR ((uint8_t)0x1b)
45 #define CR_CHAR ((uint8_t)0x0d)
47 #define ASCII_CTRL_CODE 0x1d
62 struct vm_device * frontend_dev;
71 static const struct key_code ascii_to_key_code[] = { // ASCII Value Serves as Index
72 NO_KEY, NO_KEY, NO_KEY, NO_KEY, // 0x00 - 0x03
73 NO_KEY, NO_KEY, NO_KEY, NO_KEY, // 0x04 - 0x07
74 { 0x0E, 0 }, { 0x0F, 0 }, { 0x1C, 0 }, NO_KEY, // 0x08 - 0x0B
75 NO_KEY, { 0x1C, 0 }, NO_KEY, NO_KEY, // 0x0C - 0x0F
76 NO_KEY, NO_KEY, NO_KEY, NO_KEY, // 0x10 - 0x13
77 NO_KEY, NO_KEY, NO_KEY, NO_KEY, // 0x14 - 0x17
78 NO_KEY, NO_KEY, NO_KEY, { 0x01, 0 }, // 0x18 - 0x1B
79 NO_KEY, NO_KEY, NO_KEY, NO_KEY, // 0x1C - 0x1F
80 { 0x39, 0 }, { 0x02, 1 }, { 0x28, 1 }, { 0x04, 1 }, // 0x20 - 0x23
81 { 0x05, 1 }, { 0x06, 1 }, { 0x08, 1 }, { 0x28, 0 }, // 0x24 - 0x27
82 { 0x0A, 1 }, { 0x0B, 1 }, { 0x09, 1 }, { 0x0D, 1 }, // 0x28 - 0x2B
83 { 0x33, 0 }, { 0x0C, 0 }, { 0x34, 0 }, { 0x35, 0 }, // 0x2C - 0x2F
84 { 0x0B, 0 }, { 0x02, 0 }, { 0x03, 0 }, { 0x04, 0 }, // 0x30 - 0x33
85 { 0x05, 0 }, { 0x06, 0 }, { 0x07, 0 }, { 0x08, 0 }, // 0x34 - 0x37
86 { 0x09, 0 }, { 0x0A, 0 }, { 0x27, 1 }, { 0x27, 0 }, // 0x38 - 0x3B
87 { 0x33, 1 }, { 0x0D, 0 }, { 0x34, 1 }, { 0x35, 1 }, // 0x3C - 0x3F
88 { 0x03, 1 }, { 0x1E, 1 }, { 0x30, 1 }, { 0x2E, 1 }, // 0x40 - 0x43
89 { 0x20, 1 }, { 0x12, 1 }, { 0x21, 1 }, { 0x22, 1 }, // 0x44 - 0x47
90 { 0x23, 1 }, { 0x17, 1 }, { 0x24, 1 }, { 0x25, 1 }, // 0x48 - 0x4B
91 { 0x26, 1 }, { 0x32, 1 }, { 0x31, 1 }, { 0x18, 1 }, // 0x4C - 0x4F
92 { 0x19, 1 }, { 0x10, 1 }, { 0x13, 1 }, { 0x1F, 1 }, // 0x50 - 0x53
93 { 0x14, 1 }, { 0x16, 1 }, { 0x2F, 1 }, { 0x11, 1 }, // 0x54 - 0x57
94 { 0x2D, 1 }, { 0x15, 1 }, { 0x2C, 1 }, { 0x1A, 0 }, // 0x58 - 0x5B
95 { 0x2B, 0 }, { 0x1B, 0 }, { 0x07, 1 }, { 0x0C, 1 }, // 0x5C - 0x5F
96 { 0x29, 0 }, { 0x1E, 0 }, { 0x30, 0 }, { 0x2E, 0 }, // 0x60 - 0x63
97 { 0x20, 0 }, { 0x12, 0 }, { 0x21, 0 }, { 0x22, 0 }, // 0x64 - 0x67
98 { 0x23, 0 }, { 0x17, 0 }, { 0x24, 0 }, { 0x25, 0 }, // 0x68 - 0x6B
99 { 0x26, 0 }, { 0x32, 0 }, { 0x31, 0 }, { 0x18, 0 }, // 0x6C - 0x6F
100 { 0x19, 0 }, { 0x10, 0 }, { 0x13, 0 }, { 0x1F, 0 }, // 0x70 - 0x73
101 { 0x14, 0 }, { 0x16, 0 }, { 0x2F, 0 }, { 0x11, 0 }, // 0x74 - 0x77
102 { 0x2D, 0 }, { 0x15, 0 }, { 0x2C, 0 }, { 0x1A, 1 }, // 0x78 - 0x7B
103 { 0x2B, 1 }, { 0x1B, 1 }, { 0x29, 1 }, { 0x0E, 0 } // 0x7C - 0x7F
108 static int deliver_scan_code(struct cons_state * state, struct key_code * key) {
109 struct v3_keyboard_event key_event;
110 struct v3_keyboard_event key_shift;
111 uint_t cap = key->capital;
113 key_event.status = 0;
114 key_event.scan_code = (uint8_t)key->scan_code;
116 PrintDebug("Scan code: 0x%x\n", key_event.scan_code);
120 key_shift.status = 0;
121 key_shift.scan_code = (uint8_t)0x2A;
123 if (v3_deliver_keyboard_event(state->vm, &key_shift) == -1) {
124 PrintError("Video: Error delivering key event\n");
130 if (v3_deliver_keyboard_event(state->vm, &key_event) == -1) {
131 PrintError("Video: Error delivering key event\n");
136 key_event.scan_code = key_event.scan_code | 0x80;
138 if (v3_deliver_keyboard_event(state->vm, &key_event) == -1) {
139 PrintError("Video: Error delivering key event\n");
145 key_shift.scan_code = 0x2A | 0x80;
147 if (v3_deliver_keyboard_event(state->vm, &key_shift) == -1) {
148 PrintError("Video: Error delivering key event\n");
153 PrintDebug("Finished with Key delivery\n");
160 static int recv_all(int socket, char * buf, int length) {
163 PrintDebug("Reading %d bytes\n", length - bytes_read);
164 while (bytes_read < length) {
165 int tmp_bytes = V3_Recv(socket, buf + bytes_read, length - bytes_read);
166 PrintDebug("Received %d bytes\n", tmp_bytes);
168 if (tmp_bytes == 0) {
169 PrintError("Connection Closed unexpectedly\n");
171 } else if (tmp_bytes == -1) {
172 PrintError("Socket Error in for V3_RECV\n");
176 bytes_read += tmp_bytes;
183 static int send_all(const int sock, const char * buf, const int len){
184 int bytes_left = len;
186 while (bytes_left != 0) {
189 if ((written = V3_Send(sock, buf + (len - bytes_left), bytes_left)) == -1) {
193 bytes_left -= written;
198 // Translate attribute color into terminal escape sequence color
199 static const uint8_t fg_color_map[] = {
200 30, 34, 32, 36, 31, 35, 33, 37, 90, 94, 92, 96, 91, 95, 93, 97
203 static const uint8_t bg_color_map[] = {
204 40, 44, 42, 46, 41, 45, 43, 47, 100, 104, 102, 106, 101, 105, 103, 107
208 #define INT_TO_CHAR(index, buf, val) \
210 uint8_t base = '0'; \
211 if ((val) >= 100) buf[(index)++] = base + ((val) / 100); \
212 if ((val) >= 10) buf[(index)++] = base + ((val) / 10); \
213 buf[(index)++] = base + ((val) % 10); \
217 static int send_update(struct cons_state * state, uint8_t x, uint8_t y, uint8_t attrib, uint8_t val) {
218 uint8_t fg_color = fg_color_map[(attrib & 0x0f) % 16];
219 uint8_t bg_color = bg_color_map[(attrib & 0xf0) % 16];
229 INT_TO_CHAR(i, buf, y + 1);
233 INT_TO_CHAR(i, buf, x + 1);
243 INT_TO_CHAR(i, buf, fg_color);
247 INT_TO_CHAR(i, buf, bg_color);
255 INT_TO_CHAR(i, buf, y + 1);
257 INT_TO_CHAR(i, buf, x + 1);
261 PrintDebug("printing value '%c'\n", val);
263 if (state->connected) {
267 ret = send_all(state->client_fd, buf, 32);
270 PrintDebug("Sendall latency=%d cycles\n", (uint32_t)(end - start));
278 static int cursor_update(uint_t x, uint_t y, void * private_data) {
279 struct cons_state * state = (struct cons_state *)private_data;
282 addr_t irq_state = 0;
289 INT_TO_CHAR(i, buf, y + 1);
291 INT_TO_CHAR(i, buf, x + 1);
295 irq_state = v3_lock_irqsave(state->cons_lock);
297 if (state->connected) {
298 ret = send_all(state->client_fd, buf, 16);
301 v3_unlock_irqrestore(state->cons_lock, irq_state);
307 static int screen_update(uint_t x, uint_t y, uint_t length, void * private_data) {
308 struct cons_state * state = (struct cons_state *)private_data;
309 uint_t offset = (x * BYTES_PER_COL) + (y * BYTES_PER_ROW);
310 uint8_t fb_buf[length];
314 addr_t irq_state = 0;
317 memset(fb_buf, 0, length);
321 irq_state = v3_lock_irqsave(state->cons_lock);
323 v3_cons_get_fb(state->frontend_dev, fb_buf, offset, length);
325 v3_unlock_irqrestore(state->cons_lock, irq_state);
328 for (i = 0; i < length; i += 2) {
329 uint_t col_index = i;
332 col[0] = fb_buf[col_index]; // Character
333 col[1] = fb_buf[col_index + 1]; // Attribute
335 irq_state = v3_lock_irqsave(state->cons_lock);
337 if (send_update(state, cur_x, cur_y, col[1], col[0]) == -1) {
338 PrintError("Could not send attribute to telnet session\n");
343 v3_unlock_irqrestore(state->cons_lock, irq_state);
346 // CAUTION: the order of these statements is critical
347 // cur_y depends on the previous value of cur_x
348 cur_y = cur_y + ((cur_x + 1) / NUM_COLS);
349 cur_x = (cur_x + 1) % NUM_COLS;
356 static int scroll(int rows, void * private_data) {
357 struct cons_state * state = (struct cons_state *)private_data;
358 addr_t irq_state = 0;
364 irq_state = v3_lock_irqsave(state->cons_lock);
366 for (i = rows; i > 0; i--) {
367 uint8_t message[2] = { ESC_CHAR, 'D' };
369 if (state->connected) {
370 if (send_all(state->client_fd, message, sizeof(message)) == -1) {
371 PrintError("Could not send scroll command\n");
378 v3_unlock_irqrestore(state->cons_lock, irq_state);
380 } else if (rows < 0) {
381 ret = screen_update(0, 0, SCREEN_SIZE, private_data);
389 static struct v3_console_ops cons_ops = {
390 .update_screen = screen_update,
391 .update_cursor = cursor_update,
393 .set_text_resolution = NULL,
396 static int cons_free(struct cons_state * state) {
405 static struct v3_device_ops dev_ops = {
406 .free = (int (*)(void *))cons_free,
411 static int key_handler( struct cons_state * state, uint8_t ascii) {
412 PrintDebug("Character recieved: 0x%x\n", ascii);
416 const struct key_code * key = &(ascii_to_key_code[ascii]);
418 if (deliver_scan_code(state, (struct key_code *)key) == -1) {
419 PrintError("Could not deliver scan code to vm\n");
423 } else if (ascii == ESC_CHAR) { // Escape Key
424 // This means that another 2 characters are pending
425 // receive it and deliver accordingly
426 char esc_seq[2] = {0, 0};
428 int recv = recv_all(state->client_fd, esc_seq, 2);
431 PrintError("Video: Error getting key from network\n");
433 } else if (recv == 0) {
434 PrintDebug("Video: Client Disconnected\n");
439 if (esc_seq[0] != '[') {
440 PrintDebug("Ignoring non handled escape sequence (codes = %d %d)\n",
441 esc_seq[0], esc_seq[1]);
446 if (esc_seq[1] == 'A') { // UP ARROW
447 struct key_code up = { 0x48, 0 };
448 deliver_scan_code(state, &up);
449 } else if (esc_seq[1] == 'B') { // DOWN ARROW
450 struct key_code down = { 0x50, 0 };
451 deliver_scan_code(state, &down);
452 } else if (esc_seq[1] == 'C') { // RIGHT ARROW
453 struct key_code right = { 0x4D, 0 };
454 deliver_scan_code(state, &right);
455 } else if (esc_seq[1] == 'D') { // LEFT ARROW
456 struct key_code left = { 0x4B, 0 };
457 deliver_scan_code(state, &left);
460 PrintError("Invalid character received from network (%c) (code=%d)\n",
468 static int cons_server(void * arg) {
469 struct cons_state * state = (struct cons_state *)arg;
471 state->server_fd = V3_Create_TCP_Socket();
474 PrintDebug("Video: Socket File Descriptor: %d\n", state->server_fd);
476 if (V3_Bind_Socket(state->server_fd, state->port) == -1) {
477 PrintError("Video: Failed to bind to socket %d\n", state->port);
480 if (V3_Listen_Socket(state->server_fd, 8) == -1) {
481 PrintError("Video: Failed to listen with socket %d\n", state->server_fd);
486 uint32_t client_port;
487 uint8_t ascii_code = 0;
490 if ((state->client_fd = V3_Accept_Socket(state->server_fd, &client_ip, &client_port)) == -1) {
491 PrintError("Video: Failed to accept connection on port %d\n", client_port);
493 PrintDebug("Accepted Telnet Console connection\n");
494 state->connected = 1;
496 screen_update(0, 0, SCREEN_SIZE, state);
499 recv = recv_all(state->client_fd, &ascii_code, sizeof(ascii_code));
501 PrintDebug("Telnet console Received %d bytes\n", recv);
504 PrintError("Video: Error getting key from network\n");
506 } else if (recv == 0) {
507 PrintDebug("Video: Client Disconnected\n");
511 if (key_handler(state, ascii_code) == -1) {
512 PrintError("Error in key handler\n");
517 state->connected = 0;
518 V3_Close_Socket(state->client_fd);
525 static int cons_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
526 struct cons_state * state = (struct cons_state *)V3_Malloc(sizeof(struct cons_state));
527 v3_cfg_tree_t * frontend_cfg = v3_cfg_subtree(cfg, "frontend");
528 struct vm_device * frontend = v3_find_dev(vm, v3_cfg_val(frontend_cfg, "tag"));
529 char * dev_id = v3_cfg_val(cfg, "ID");
532 state->server_fd = 0;
533 state->client_fd = 0;
534 state->frontend_dev = frontend;
535 state->port = atoi(v3_cfg_val(cfg, "port"));
536 v3_lock_init(&(state->cons_lock));
539 struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, state);
542 PrintError("Could not attach device %s\n", dev_id);
548 v3_console_register_cga(frontend, &cons_ops, state);
550 V3_CREATE_THREAD(cons_server, state, "Telnet Console Network Server");
557 device_register("TELNET_CONSOLE", cons_init)