for (i = 0; i < size; i++) {
- if (copy_from_user(&(event.scan_code), buf, 1)) {
+ if (copy_from_user(&(event.scan_code), buf + i, 1)) {
printk("Console Write fault\n");
return -EFAULT;
}
#include "iface-stream.h"
-// This is going to need to be a lot bigger...
-#define STREAM_BUF_SIZE 1024
+// This is probably overkill
+#define STREAM_RING_LEN 4096
static struct list_head global_streams;
-struct stream_buffer {
+
+
+struct stream_state {
char name[STREAM_NAME_LEN];
- struct ringbuf * buf;
+
+ struct ringbuf * out_ring;
int connected;
struct v3_guest * guest;
struct list_head stream_node;
+
+ struct v3_stream * v3_stream;
};
// Currently just the list of open streams
-struct vm_stream_state {
+struct vm_global_streams {
struct list_head open_streams;
};
-static struct stream_buffer * find_stream_by_name(struct v3_guest * guest, const char * name) {
- struct stream_buffer * stream = NULL;
+
+
+
+static struct stream_state * find_stream_by_name(struct v3_guest * guest, const char * name) {
+ struct stream_state * stream = NULL;
struct list_head * stream_list = NULL;
- struct vm_stream_state * vm_state = NULL;
+ struct vm_global_streams * vm_state = NULL;
if (guest == NULL) {
stream_list = &global_streams;
+#define TMP_BUF_LEN 128
+
static ssize_t stream_read(struct file * filp, char __user * buf, size_t size, loff_t * offset) {
- struct stream_buffer * stream = filp->private_data;
+ struct stream_state * stream = filp->private_data;
+ ssize_t bytes_read = 0;
+ ssize_t bytes_left = size;
+ unsigned long flags;
+ char tmp_buf[TMP_BUF_LEN];
+ ssize_t total_bytes_left = 0;
+
+ // memset(tmp_buf, 0, TMP_BUF_LEN);
+
+ while (bytes_left > 0) {
+ int tmp_len = (TMP_BUF_LEN > bytes_left) ? bytes_left : TMP_BUF_LEN;
+ int tmp_read = 0;
+
+ spin_lock_irqsave(&(stream->lock), flags);
+ tmp_read = ringbuf_read(stream->out_ring, tmp_buf, tmp_len);
+ spin_unlock_irqrestore(&(stream->lock), flags);
+
+ if (tmp_read == 0) {
+ // If userspace reads more than we have
+ break;
+ }
+
+ if (copy_to_user(buf + bytes_read, tmp_buf, tmp_read)) {
+ printk("Read Fault\n");
+ return -EFAULT;
+ }
+
+ bytes_left -= tmp_read;
+ bytes_read += tmp_read;
+ }
- wait_event_interruptible(stream->intr_queue, (ringbuf_data_len(stream->buf) != 0));
- return ringbuf_read(stream->buf, buf, size);
+ spin_lock_irqsave(&(stream->lock), flags);
+ total_bytes_left = ringbuf_data_len(stream->out_ring);
+ spin_unlock_irqrestore(&(stream->lock), flags);
+
+ if (total_bytes_left > 0) {
+ wake_up_interruptible(&(stream->intr_queue));
+ }
+
+ return bytes_read;
}
+static unsigned int
+stream_poll(struct file * filp, struct poll_table_struct * poll_tb) {
+ struct stream_state * stream = filp->private_data;
+ unsigned int mask = POLLIN | POLLRDNORM;
+ unsigned long flags;
+ int data_avail = 0;
+
+ poll_wait(filp, &(stream->intr_queue), poll_tb);
+
+ spin_lock_irqsave(&(stream->lock), flags);
+ data_avail = ringbuf_data_len(stream->out_ring);
+ spin_unlock_irqrestore(&(stream->lock), flags);
+
+ if (data_avail > 0) {
+ return mask;
+ }
+ return 0;
+
+}
+
+static ssize_t stream_write(struct file * filp, const char __user * buf, size_t size, loff_t * offset) {
+ struct stream_state * stream = filp->private_data;
+ char * kern_buf = NULL;
+ ssize_t bytes_written = 0;
+
+ kern_buf = kmalloc(size, GFP_KERNEL);
+
+ if (copy_from_user(kern_buf, buf, size)) {
+ printk("Stream Write Failed\n");
+ return -EFAULT;
+ };
+
+ bytes_written = stream->v3_stream->input(stream->v3_stream, kern_buf, size);
+
+ kfree(kern_buf);
+
+ return bytes_written;
+}
+
+
+static int stream_release(struct inode * i, struct file * filp) {
+ struct stream_state * stream = filp->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&(stream->lock), flags);
+ stream->connected = 0;
+ spin_unlock_irqrestore(&(stream->lock), flags);
+
+
+ return 0;
+
+}
static struct file_operations stream_fops = {
.read = stream_read,
- // .release = stream_close,
- // .poll = stream_poll,
+ .write = stream_write,
+ .release = stream_release,
+ .poll = stream_poll,
};
-static void * palacios_stream_open(const char * name, void * private_data) {
+static void * palacios_stream_open(struct v3_stream * v3_stream, const char * name, void * private_data) {
struct v3_guest * guest = (struct v3_guest *)private_data;
- struct stream_buffer * stream = NULL;
- struct vm_stream_state * vm_state = NULL;
+ struct stream_state * stream = NULL;
+ struct vm_global_streams * vm_state = NULL;
if (guest != NULL) {
vm_state = get_vm_ext_data(guest, "STREAM_INTERFACE");
return NULL;
}
- stream = kmalloc(sizeof(struct stream_buffer), GFP_KERNEL);
-
- stream->buf = create_ringbuf(STREAM_BUF_SIZE);
+ stream = kmalloc(sizeof(struct stream_state), GFP_KERNEL);
+ memset(stream, 0, sizeof(struct stream_state));
+
+ stream->out_ring = create_ringbuf(STREAM_RING_LEN);
+ stream->v3_stream = v3_stream;
stream->guest = guest;
+ stream->connected = 0;
strncpy(stream->name, name, STREAM_NAME_LEN - 1);
}
-static int palacios_stream_write(void * stream_ptr, char * buf, int len) {
- struct stream_buffer * stream = (struct stream_buffer *)stream_ptr;
- int ret = 0;
+static uint64_t palacios_stream_output(struct v3_stream * v3_stream, char * buf, int len) {
+ struct stream_state * stream = (struct stream_state *)v3_stream->host_stream_data;
+ int bytes_written = 0;
+ unsigned long flags;
+
- ret = ringbuf_write(stream->buf, buf, len);
+ if (stream->connected == 0) {
+ return 0;
+ }
+
+ while (bytes_written < len) {
+ spin_lock_irqsave(&(stream->lock), flags);
+ bytes_written += ringbuf_write(stream->out_ring, buf + bytes_written, len - bytes_written);
+ spin_unlock_irqrestore(&(stream->lock), flags);
- if (ret > 0) {
wake_up_interruptible(&(stream->intr_queue));
+
+ if (bytes_written < len) {
+ // not enough space in ringbuffer, activate user space to drain it
+ schedule();
+ }
}
- return ret;
+
+ return bytes_written;
}
-static void palacios_stream_close(void * stream_ptr) {
- struct stream_buffer * stream = (struct stream_buffer *)stream_ptr;
+static void palacios_stream_close(struct v3_stream * v3_stream) {
+ struct stream_state * stream = (struct stream_state *)v3_stream->host_stream_data;
- free_ringbuf(stream->buf);
+ free_ringbuf(stream->out_ring);
list_del(&(stream->stream_node));
kfree(stream);
static struct v3_stream_hooks palacios_stream_hooks = {
.open = palacios_stream_open,
- .write = palacios_stream_write,
+ .output = palacios_stream_output,
.close = palacios_stream_close,
};
static int stream_connect(struct v3_guest * guest, unsigned int cmd, unsigned long arg, void * priv_data) {
void __user * argp = (void __user *)arg;
- struct stream_buffer * stream = NULL;
+ struct stream_state * stream = NULL;
int stream_fd = 0;
char name[STREAM_NAME_LEN];
unsigned long flags = 0;
}
- stream_fd = anon_inode_getfd("v3-stream", &stream_fops, stream, 0);
+ stream_fd = anon_inode_getfd("v3-stream", &stream_fops, stream, O_RDWR);
if (stream_fd < 0) {
printk("Error creating stream inode for (%s)\n", name);
static int guest_stream_init(struct v3_guest * guest, void ** vm_data) {
- struct vm_stream_state * state = kmalloc(sizeof(struct vm_stream_state), GFP_KERNEL);
+ struct vm_global_streams * state = kmalloc(sizeof(struct vm_global_streams), GFP_KERNEL);
INIT_LIST_HEAD(&(state->open_streams));
*vm_data = state;
-
add_guest_ctrl(guest, V3_VM_STREAM_CONNECT, stream_connect, state);
return 0;
static int guest_stream_deinit(struct v3_guest * guest, void * vm_data) {
- struct vm_stream_state * state = vm_data;
+ struct vm_global_streams * state = vm_data;
if (!list_empty(&(state->open_streams))) {
printk("Error shutting down VM with open streams\n");
}
-all: v3_ctrl v3_stop v3_cons v3_mem v3_monitor v3_serial v3_net v3_user_host_dev_example v3_os_debug v3_user_keyed_stream_example v3_user_keyed_stream_file
+all: v3_ctrl v3_stop v3_cons v3_mem v3_monitor v3_stream v3_net v3_user_host_dev_example v3_os_debug v3_user_keyed_stream_example v3_user_keyed_stream_file
v3_cons : v3_cons.c v3_ctrl.h
gcc -static v3_cons.c -o v3_cons -lcurses
-v3_serial : v3_serial.c v3_ctrl.h
- gcc -static v3_serial.c -pthread -o v3_serial
+v3_stream : v3_stream.c v3_ctrl.h
+ gcc -static v3_stream.c -o v3_stream
v3_monitor : v3_cons.c v3_ctrl.h
gcc -static v3_monitor.c -o v3_monitor
clean:
- rm -f v3_ctrl v3_cons v3_mem v3_monitor v3_serial v3_user_host_dev_example v3_os_debug v3_user_keyed_stream_example v3_user_keyed_stream_file
+ rm -f v3_ctrl v3_cons v3_mem v3_monitor v3_stream v3_user_host_dev_example v3_os_debug v3_user_keyed_stream_example v3_user_keyed_stream_file
+++ /dev/null
-/*
- * V3 Console utility
- * (c) Jack lange & Lei Xia, 2010
- */
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <string.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <errno.h>
-#include<linux/unistd.h>
-#include <curses.h>
-
-
-#include "v3_ctrl.h"
-
-static int cons_fd = -1;
-static pthread_t input_handler;
-
-void *write_handler(void *val){
- char read;
- printf("Write handler active\n");
- fflush(stdout);
- while(1){
- read = getchar();
- if(write(cons_fd, &read, sizeof(char)) < 0){
- printf("WRITE ERROR");
- }
- }
-}
-
-
-int main(int argc, char* argv[]) {
- int vm_fd;
- fd_set rset;
- char *vm_dev = NULL;
- char *stream;
-
- if (argc < 2) {
- printf("Usage: ./v3_cons vm_device serial_number\n");
- return -1;
- }
-
- vm_dev = argv[1];
- stream = argv[2];
-
- vm_fd = open(vm_dev, O_RDONLY);
- if (vm_fd == -1) {
- printf("Error opening VM device: %s\n", vm_dev);
- return -1;
- }
-
- cons_fd = ioctl(vm_fd, V3_VM_SERIAL_CONNECT, stream);
-
- /* Close the file descriptor. */
- close(vm_fd);
- if (cons_fd < 0) {
- printf("Error opening stream Console\n");
- return -1;
- }
-
-
- if(pthread_create(&input_handler,0,write_handler,0)){
- perror("pthread_create");
- exit(-1);
- }
-
-
- while (1) {
- int ret;
- char cons_buf[1024];
- memset(cons_buf, 0, sizeof(cons_buf));
- int bytes_read = 0;
-
- FD_ZERO(&rset);
- FD_SET(cons_fd, &rset);
-
- ret = select(cons_fd + 1, &rset, NULL, NULL, NULL);
-
- if (ret == 1) {
- bytes_read = read(cons_fd, cons_buf, 1024);
- cons_buf[bytes_read]='\0';
- printf("%s", cons_buf);
- } else {
- printf("v3_cons ERROR: select returned %d\n", ret);
- return -1;
- }
- }
-
-
- return 0;
-}
-
-
--- /dev/null
+/*
+ * V3 Console utility
+ * (c) Jack lange & Lei Xia, 2010
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <errno.h>
+#include<linux/unistd.h>
+#include <curses.h>
+
+
+#include "v3_ctrl.h"
+
+#define BUF_LEN 1025
+#define STREAM_NAME_LEN 128
+
+int main(int argc, char* argv[]) {
+ int vm_fd;
+ fd_set rset;
+ char * vm_dev = NULL;
+ char stream[STREAM_NAME_LEN];
+ char cons_buf[BUF_LEN];
+ int stream_fd = 0;
+
+ if (argc < 2) {
+ printf("Usage: ./v3_cons vm_device serial_number\n");
+ return -1;
+ }
+
+ vm_dev = argv[1];
+
+ if (strlen(argv[2]) >= STREAM_NAME_LEN) {
+ printf("ERROR: Stream name longer than maximum size (%d)\n", STREAM_NAME_LEN);
+ return -1;
+ }
+
+ memcpy(stream, argv[2], strlen(argv[2]));
+
+ vm_fd = open(vm_dev, O_RDONLY);
+ if (vm_fd == -1) {
+ printf("Error opening VM device: %s\n", vm_dev);
+ return -1;
+ }
+
+ stream_fd = ioctl(vm_fd, V3_VM_SERIAL_CONNECT, stream);
+
+ /* Close the file descriptor. */
+ close(vm_fd);
+
+ if (stream_fd < 0) {
+ printf("Error opening stream Console\n");
+ return -1;
+ }
+
+ while (1) {
+ int ret;
+ int bytes_read = 0;
+ char in_buf[512];
+
+ memset(cons_buf, 0, BUF_LEN);
+
+
+ FD_ZERO(&rset);
+ FD_SET(stream_fd, &rset);
+ FD_SET(STDIN_FILENO, &rset);
+
+ ret = select(stream_fd + 1, &rset, NULL, NULL, NULL);
+
+ if (ret == 0) {
+ continue;
+ } else if (ret == -1) {
+ perror("Select returned error\n");
+ return -1;
+ }
+
+ if (FD_ISSET(stream_fd, &rset)) {
+
+ bytes_read = read(stream_fd, cons_buf, BUF_LEN - 1);
+
+ cons_buf[bytes_read]='\0';
+ printf("%s", cons_buf);
+ fflush(stdout);
+
+ } else if (FD_ISSET(STDIN_FILENO, &rset)) {
+ fgets(in_buf, 512, stdin);
+
+ if (write(stream_fd, in_buf, strlen(in_buf)) != strlen(in_buf)) {
+ fprintf(stderr, "Error sending input bufer\n");
+ return -1;
+ }
+ } else {
+ printf("v3_cons ERROR: select returned %d\n", ret);
+ return -1;
+ }
+
+
+ }
+
+
+ return 0;
+}
+
+
#define VIRTIO_NET_SUBDEVICE_ID 1
#define VIRTIO_BLOCK_SUBDEVICE_ID 2
+#define VIRTIO_CONSOLE_SUBDEVICE_ID 3
#define VIRTIO_BALLOON_SUBDEVICE_ID 5
#define VIRTIO_SYMBIOTIC_SUBDEVICE_ID 10
#define VIRTIO_SYMMOD_SUBDEVICE_ID 11
#define VIRTIO_VNET_SUBDEVICE_ID 12
+
#define HOST_FEATURES_PORT 0
#define GUEST_FEATURES_PORT 4
#define VRING_PG_NUM_PORT 8
+struct v3_stream {
+ void * host_stream_data;
+ void * guest_stream_data;
+ uint64_t (*input)(struct v3_stream * stream, uint8_t * buf, uint64_t len);
+};
+
+
#ifdef __V3VEE__
#include <palacios/vmm.h>
-typedef void * v3_stream_t;
+
/* VM Can be NULL */
-v3_stream_t v3_stream_open(struct v3_vm_info * vm, const char * name);
-int v3_stream_write(v3_stream_t stream, uint8_t * buf, uint32_t len);
+struct v3_stream * v3_stream_open(struct v3_vm_info * vm, const char * name,
+ uint64_t (*input)(struct v3_stream * stream, uint8_t * buf, uint64_t len),
+ void * guest_stream_data);
+
+uint64_t v3_stream_output(struct v3_stream * stream, uint8_t * buf, uint32_t len);
-void v3_stream_close(v3_stream_t stream);
+void v3_stream_close(struct v3_stream * stream);
#endif
struct v3_stream_hooks {
- void *(*open)(const char * name, void * private_data);
- int (*write)(void * stream, char * buf, int len);
- void (*close)(void * stream);
+ void *(*open)(struct v3_stream * stream, const char * name, void * host_vm_data);
+ uint64_t (*output)(struct v3_stream * stream, char * buf, int len);
+ void (*close)(struct v3_stream * stream);
};
struct v3_dev_char_ops {
/* Backend implemented functions */
- int (*write)(uint8_t * buf, uint64_t len, void * private_data);
+ uint64_t (*output)(uint8_t * buf, uint64_t len, void * private_data);
// int (*read)(uint8_t * buf, uint64_t len, void * private_data);
/* Frontend Implemented functions */
- int (*push)(struct v3_vm_info * vm, uint8_t * buf, uint64_t len, void * private_data);
+ uint64_t (*input)(struct v3_vm_info * vm, uint8_t * buf, uint64_t len, void * private_data);
};
help
Enable debugging for the Linux Virtio Symbiotic Device
+config LINUX_VIRTIO_CONSOLE
+ bool "Enable Virtio Console Device"
+ default n
+ depends on PCI
+ help
+ Enable the Virtio Console
+
+
config LINUX_VIRTIO_NET
bool "Enable Virtio Network Device"
default n
obj-$(V3_CONFIG_LINUX_VIRTIO_SYM) += lnx_virtio_sym.o
obj-$(V3_CONFIG_LINUX_VIRTIO_NET) += lnx_virtio_nic.o
obj-$(V3_CONFIG_LINUX_VIRTIO_VNET) += lnx_virtio_vnet.o
+obj-$(V3_CONFIG_LINUX_VIRTIO_CONSOLE) += lnx_virtio_console.o
obj-$(V3_CONFIG_VNET_NIC) += vnet_nic.o
obj-$(V3_CONFIG_NVRAM) += nvram.o
obj-$(V3_CONFIG_OS_DEBUG) += os_debug.o
struct stream_state {
- v3_stream_t stream;
+ struct v3_stream * stream;
struct v3_dev_char_ops char_ops;
+ struct v3_vm_info * vm;
+
void * push_fn_arg;
};
-static int serial_event_handler(struct v3_vm_info * vm,
- struct v3_serial_event * evt,
- void * private_data) {
- struct stream_state * state = (struct stream_state *)private_data;
+static uint64_t stream_input(struct v3_stream * stream, uint8_t * buf, uint64_t len) {
+ struct stream_state * state = stream->guest_stream_data;
- if (state->char_ops.push != NULL){
- state->char_ops.push(vm, evt->data, evt->len, state->push_fn_arg);
- }
+ return state->char_ops.input(state->vm, buf, len, state->push_fn_arg);
- return 0;
}
-static int stream_write(uint8_t * buf, uint64_t length, void * private_data) {
+static uint64_t stream_output(uint8_t * buf, uint64_t length, void * private_data) {
struct stream_state * state = (struct stream_state *)private_data;
-
- return v3_stream_write(state->stream, buf, length);
+
+ return v3_stream_output(state->stream, buf, length);
}
static int stream_free(struct stream_state * state) {
}
-
- state->stream = v3_stream_open(vm, stream_name);
+ state->stream = v3_stream_open(vm, stream_name, stream_input, state);
if (state->stream == NULL) {
PrintError("Could not open stream %s\n", stream_name);
return -1;
}
- state->char_ops.write = stream_write;
+ state->vm = vm;
+ state->char_ops.output = stream_output;
if (v3_dev_connect_char(vm, v3_cfg_val(frontend_cfg, "tag"),
&(state->char_ops), frontend_cfg,
return -1;
}
- v3_hook_host_event(vm, HOST_SERIAL_EVT, V3_HOST_EVENT_HANDLER(serial_event_handler), state);
+
return 0;
}
--- /dev/null
+/*
+ * 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) 2008, Jack Lange <jarusl@cs.northwestern.edu>
+ * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org>
+ * All rights reserved.
+ *
+ * Author: Jack Lange <jarusl@cs.northwestern.edu>
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
+ */
+
+#include <palacios/vmm.h>
+#include <palacios/vmm_dev_mgr.h>
+#include <devices/lnx_virtio_pci.h>
+#include <palacios/vm_guest_mem.h>
+
+#include <devices/pci.h>
+
+
+
+struct console_config {
+ uint16_t cols;
+ uint16_t rows;
+} __attribute__((packed));
+
+
+
+
+#define QUEUE_SIZE 128
+
+/* Host Feature flags */
+#define VIRTIO_CONSOLE_F_SIZE 0x1
+
+struct virtio_console_state {
+ struct console_config cons_cfg;
+ struct virtio_config virtio_cfg;
+
+ struct vm_device * pci_bus;
+ struct pci_device * pci_dev;
+
+ struct virtio_queue queue[2];
+
+ struct v3_stream * stream;
+
+
+ struct virtio_queue * cur_queue;
+
+ struct v3_vm_info * vm;
+
+ int io_range_size;
+
+ void * backend_data;
+ struct v3_dev_char_ops * ops;
+};
+
+
+struct virtio_console_state * cons_state = NULL;
+
+static int virtio_reset(struct virtio_console_state * virtio) {
+
+ memset(virtio->queue, 0, sizeof(struct virtio_queue) * 2);
+
+ virtio->cur_queue = &(virtio->queue[0]);
+
+ virtio->virtio_cfg.status = 0;
+ virtio->virtio_cfg.pci_isr = 0;
+
+ /* Console configuration */
+ // virtio->virtio_cfg.host_features = VIRTIO_NOTIFY_HOST;
+
+ // Virtio Console uses two queues
+ virtio->queue[0].queue_size = QUEUE_SIZE;
+ virtio->queue[1].queue_size = QUEUE_SIZE;
+
+
+ memset(&(virtio->cons_cfg), 0, sizeof(struct console_config));
+
+ return 0;
+}
+
+static int get_desc_count(struct virtio_queue * q, int index) {
+ struct vring_desc * tmp_desc = &(q->desc[index]);
+ int cnt = 1;
+
+ while (tmp_desc->flags & VIRTIO_NEXT_FLAG) {
+ tmp_desc = &(q->desc[tmp_desc->next]);
+ cnt++;
+ }
+
+ return cnt;
+}
+
+
+static int handle_kick(struct guest_info * core, struct virtio_console_state * virtio) {
+ struct virtio_queue * q = virtio->cur_queue;
+
+ PrintDebug("VIRTIO CONSOLE KICK: cur_index=%d (mod=%d), avail_index=%d\n",
+ q->cur_avail_idx, q->cur_avail_idx % QUEUE_SIZE, q->avail->index);
+
+ while (q->cur_avail_idx < q->avail->index) {
+ struct vring_desc * tmp_desc = NULL;
+ uint16_t desc_idx = q->avail->ring[q->cur_avail_idx % QUEUE_SIZE];
+ int desc_cnt = get_desc_count(q, desc_idx);
+ int i = 0;
+ uint32_t req_len = 0;
+
+
+ PrintDebug("Descriptor Count=%d, index=%d\n", desc_cnt, q->cur_avail_idx % QUEUE_SIZE);
+
+ for (i = 0; i < desc_cnt; i++) {
+ addr_t page_addr;
+ tmp_desc = &(q->desc[desc_idx]);
+
+
+ PrintDebug("Console output (ptr=%p) gpa=%p, len=%d, flags=%x, next=%d\n",
+ tmp_desc,
+ (void *)(addr_t)(tmp_desc->addr_gpa), tmp_desc->length,
+ tmp_desc->flags, tmp_desc->next);
+
+ if (v3_gpa_to_hva(core, tmp_desc->addr_gpa, (addr_t *)&(page_addr)) == -1) {
+ PrintError("Could not translate block header address\n");
+ return -1;
+ }
+
+ virtio->ops->output((uint8_t *)page_addr, tmp_desc->length, virtio->backend_data);
+
+ PrintDebug("Guest Console Currently Ignored\n");
+
+ req_len += tmp_desc->length;
+ desc_idx = tmp_desc->next;
+ }
+
+ q->used->ring[q->used->index % QUEUE_SIZE].id = q->avail->ring[q->cur_avail_idx % QUEUE_SIZE];
+ q->used->ring[q->used->index % QUEUE_SIZE].length = req_len; // What do we set this to????
+
+ q->used->index++;
+ q->cur_avail_idx++;
+ }
+
+ if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
+ PrintDebug("Raising IRQ %d\n", virtio->pci_dev->config_header.intr_line);
+ v3_pci_raise_irq(virtio->pci_bus, 0, virtio->pci_dev);
+ virtio->virtio_cfg.pci_isr = VIRTIO_ISR_ACTIVE;
+ }
+
+ return 0;
+}
+
+
+static uint64_t virtio_input(struct v3_vm_info * vm, uint8_t * buf, uint64_t len, void * private_data) {
+ struct virtio_console_state * cons_state = private_data;
+ struct virtio_queue * q = &(cons_state->queue[0]);
+ int xfer_len = 0;
+
+ PrintDebug("VIRTIO CONSOLE Handle Input: cur_index=%d (mod=%d), avail_index=%d\n",
+ q->cur_avail_idx, q->cur_avail_idx % QUEUE_SIZE, q->avail->index);
+
+
+ if (q->cur_avail_idx != q->avail->index) {
+ uint16_t input_idx = q->avail->ring[q->cur_avail_idx % q->queue_size];
+ struct vring_desc * input_desc = NULL;
+ uint8_t * input_buf = NULL;
+
+
+ input_desc = &(q->desc[input_idx]);
+
+ if (v3_gpa_to_hva(&(cons_state->vm->cores[0]), input_desc->addr_gpa, (addr_t *)&(input_buf)) == -1) {
+ PrintError("Could not translate receive buffer address\n");
+ return 0;
+ }
+
+ memset(input_buf, 0, input_desc->length);
+
+ xfer_len = (input_desc->length > len) ? len : input_desc->length;
+
+ memcpy(input_buf, buf, xfer_len);
+
+
+ q->used->ring[q->used->index % q->queue_size].id = q->avail->ring[q->cur_avail_idx % q->queue_size];
+ q->used->ring[q->used->index % q->queue_size].length = xfer_len;
+
+ q->used->index++;
+ q->cur_avail_idx++;
+ }
+
+
+ // say hello
+ if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
+ v3_pci_raise_irq(cons_state->pci_bus, 0, cons_state->pci_dev);
+ cons_state->virtio_cfg.pci_isr = 0x1;
+ }
+
+ return xfer_len;
+}
+
+
+static int virtio_io_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * private_data) {
+ struct virtio_console_state * virtio = (struct virtio_console_state *)private_data;
+ int port_idx = port % virtio->io_range_size;
+
+
+ PrintDebug("VIRTIO CONSOLE Write for port %d (index=%d) len=%d, value=%x\n",
+ port, port_idx, length, *(uint32_t *)src);
+
+
+
+ switch (port_idx) {
+ case GUEST_FEATURES_PORT:
+ if (length != 4) {
+ PrintError("Illegal write length for guest features\n");
+ return -1;
+ }
+
+ virtio->virtio_cfg.guest_features = *(uint32_t *)src;
+
+ break;
+ case VRING_PG_NUM_PORT:
+ if (length == 4) {
+ addr_t pfn = *(uint32_t *)src;
+ addr_t page_addr = (pfn << VIRTIO_PAGE_SHIFT);
+
+
+ virtio->cur_queue->pfn = pfn;
+
+ virtio->cur_queue->ring_desc_addr = page_addr ;
+ virtio->cur_queue->ring_avail_addr = page_addr + (QUEUE_SIZE * sizeof(struct vring_desc));
+ virtio->cur_queue->ring_used_addr = ( virtio->cur_queue->ring_avail_addr + \
+ sizeof(struct vring_avail) + \
+ (QUEUE_SIZE * sizeof(uint16_t)));
+
+ // round up to next page boundary.
+ virtio->cur_queue->ring_used_addr = (virtio->cur_queue->ring_used_addr + 0xfff) & ~0xfff;
+
+ if (v3_gpa_to_hva(core, virtio->cur_queue->ring_desc_addr, (addr_t *)&(virtio->cur_queue->desc)) == -1) {
+ PrintError("Could not translate ring descriptor address\n");
+ return -1;
+ }
+
+
+ if (v3_gpa_to_hva(core, virtio->cur_queue->ring_avail_addr, (addr_t *)&(virtio->cur_queue->avail)) == -1) {
+ PrintError("Could not translate ring available address\n");
+ return -1;
+ }
+
+
+ if (v3_gpa_to_hva(core, virtio->cur_queue->ring_used_addr, (addr_t *)&(virtio->cur_queue->used)) == -1) {
+ PrintError("Could not translate ring used address\n");
+ return -1;
+ }
+
+ PrintDebug("RingDesc_addr=%p, Avail_addr=%p, Used_addr=%p\n",
+ (void *)(virtio->cur_queue->ring_desc_addr),
+ (void *)(virtio->cur_queue->ring_avail_addr),
+ (void *)(virtio->cur_queue->ring_used_addr));
+
+ PrintDebug("RingDesc=%p, Avail=%p, Used=%p\n",
+ virtio->cur_queue->desc, virtio->cur_queue->avail, virtio->cur_queue->used);
+
+ } else {
+ PrintError("Illegal write length for page frame number\n");
+ return -1;
+ }
+ break;
+ case VRING_Q_SEL_PORT:
+ virtio->virtio_cfg.vring_queue_selector = *(uint16_t *)src;
+
+ if (virtio->virtio_cfg.vring_queue_selector > 1) {
+ PrintError("Virtio Console device only uses 2 queue, selected %d\n",
+ virtio->virtio_cfg.vring_queue_selector);
+ return -1;
+ }
+
+ virtio->cur_queue = &(virtio->queue[virtio->virtio_cfg.vring_queue_selector]);
+
+ break;
+ case VRING_Q_NOTIFY_PORT:
+ PrintDebug("Handling Kick\n");
+ if (handle_kick(core, virtio) == -1) {
+ PrintError("Could not handle Console Notification\n");
+ return -1;
+ }
+ break;
+ case VIRTIO_STATUS_PORT:
+ virtio->virtio_cfg.status = *(uint8_t *)src;
+
+ if (virtio->virtio_cfg.status == 0) {
+ PrintDebug("Resetting device\n");
+ virtio_reset(virtio);
+ }
+
+ break;
+
+ case VIRTIO_ISR_PORT:
+ virtio->virtio_cfg.pci_isr = *(uint8_t *)src;
+ break;
+ default:
+ return -1;
+ break;
+ }
+
+ return length;
+}
+
+
+static int virtio_io_read(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * private_data) {
+ struct virtio_console_state * virtio = (struct virtio_console_state *)private_data;
+ int port_idx = port % virtio->io_range_size;
+
+
+ PrintDebug("VIRTIO CONSOLE Read for port %d (index =%d), length=%d\n",
+ port, port_idx, length);
+
+ switch (port_idx) {
+ case HOST_FEATURES_PORT:
+ if (length != 4) {
+ PrintError("Illegal read length for host features\n");
+ return -1;
+ }
+
+ *(uint32_t *)dst = virtio->virtio_cfg.host_features;
+
+ break;
+ case VRING_PG_NUM_PORT:
+ if (length != 4) {
+ PrintError("Illegal read length for page frame number\n");
+ return -1;
+ }
+
+ *(uint32_t *)dst = virtio->cur_queue->pfn;
+
+ break;
+ case VRING_SIZE_PORT:
+ if (length != 2) {
+ PrintError("Illegal read length for vring size\n");
+ return -1;
+ }
+
+ *(uint16_t *)dst = virtio->cur_queue->queue_size;
+
+ break;
+
+ case VIRTIO_STATUS_PORT:
+ if (length != 1) {
+ PrintError("Illegal read length for status\n");
+ return -1;
+ }
+
+ *(uint8_t *)dst = virtio->virtio_cfg.status;
+ break;
+
+ case VIRTIO_ISR_PORT:
+ *(uint8_t *)dst = virtio->virtio_cfg.pci_isr;
+ virtio->virtio_cfg.pci_isr = 0;
+ v3_pci_lower_irq(virtio->pci_bus, 0, virtio->pci_dev);
+ break;
+
+ default:
+ if ( (port_idx >= sizeof(struct virtio_config)) &&
+ (port_idx < (sizeof(struct virtio_config) + sizeof(struct console_config))) ) {
+ int cfg_offset = port_idx - sizeof(struct virtio_config);
+ uint8_t * cfg_ptr = (uint8_t *)&(virtio->cons_cfg);
+
+ memcpy(dst, cfg_ptr + cfg_offset, length);
+
+ } else {
+ PrintError("Read of Unhandled Virtio Read\n");
+ return -1;
+ }
+
+ break;
+ }
+
+ return length;
+}
+
+
+
+
+static int connect_fn(struct v3_vm_info * vm,
+ void * frontend_data,
+ struct v3_dev_char_ops * ops,
+ v3_cfg_tree_t * cfg,
+ void * private_data,
+ void ** push_fn_arg) {
+
+ struct virtio_console_state * state = (struct virtio_console_state *)frontend_data;
+
+ state->ops = ops;
+ state->backend_data = private_data;
+
+ state->ops->input = virtio_input;
+ *push_fn_arg = state;
+
+ return 0;
+}
+
+static int virtio_free(struct virtio_console_state * virtio) {
+
+ // unregister from PCI
+
+ V3_Free(virtio);
+ return 0;
+}
+
+
+static struct v3_device_ops dev_ops = {
+ .free = (int (*)(void *))virtio_free,
+
+};
+
+
+
+
+static int virtio_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
+ struct vm_device * pci_bus = v3_find_dev(vm, v3_cfg_val(cfg, "bus"));
+ struct virtio_console_state * virtio_state = NULL;
+ struct pci_device * pci_dev = NULL;
+ char * dev_id = v3_cfg_val(cfg, "ID");
+
+ PrintDebug("Initializing VIRTIO Console device\n");
+
+ if (pci_bus == NULL) {
+ PrintError("VirtIO devices require a PCI Bus");
+ return -1;
+ }
+
+
+ virtio_state = (struct virtio_console_state *)V3_Malloc(sizeof(struct virtio_console_state));
+ memset(virtio_state, 0, sizeof(struct virtio_console_state));
+
+
+ cons_state = virtio_state;
+ cons_state->vm = vm;
+
+
+
+ struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, virtio_state);
+
+ if (dev == NULL) {
+ PrintError("Could not attach device %s\n", dev_id);
+ V3_Free(virtio_state);
+ return -1;
+ }
+
+ // PCI initialization
+ {
+ struct v3_pci_bar bars[6];
+ int num_ports = sizeof(struct virtio_config) + sizeof(struct console_config);
+ int tmp_ports = num_ports;
+ int i;
+
+
+ // This gets the number of ports, rounded up to a power of 2
+ virtio_state->io_range_size = 1; // must be a power of 2
+
+ while (tmp_ports > 0) {
+ tmp_ports >>= 1;
+ virtio_state->io_range_size <<= 1;
+ }
+
+ // this is to account for any low order bits being set in num_ports
+ // if there are none, then num_ports was already a power of 2 so we shift right to reset it
+ if ((num_ports & ((virtio_state->io_range_size >> 1) - 1)) == 0) {
+ virtio_state->io_range_size >>= 1;
+ }
+
+
+ for (i = 0; i < 6; i++) {
+ bars[i].type = PCI_BAR_NONE;
+ }
+
+ bars[0].type = PCI_BAR_IO;
+ bars[0].default_base_port = -1;
+ bars[0].num_ports = virtio_state->io_range_size;
+
+ bars[0].io_read = virtio_io_read;
+ bars[0].io_write = virtio_io_write;
+ bars[0].private_data = virtio_state;
+
+
+ pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE,
+ 0, PCI_AUTO_DEV_NUM, 0,
+ "LNX_VIRTIO_CONSOLE", bars,
+ NULL, NULL, NULL, virtio_state);
+
+ if (!pci_dev) {
+ PrintError("Could not register PCI Device\n");
+ v3_remove_device(dev);
+ return -1;
+ }
+
+ pci_dev->config_header.vendor_id = VIRTIO_VENDOR_ID;
+ pci_dev->config_header.subsystem_vendor_id = VIRTIO_SUBVENDOR_ID;
+
+
+ pci_dev->config_header.device_id = VIRTIO_CONSOLE_DEV_ID;
+ pci_dev->config_header.class = PCI_CLASS_DISPLAY;
+ pci_dev->config_header.subclass = PCI_DISPLAY_SUBCLASS_OTHER;
+
+ pci_dev->config_header.subsystem_id = VIRTIO_CONSOLE_SUBDEVICE_ID;
+
+ pci_dev->config_header.intr_pin = 1;
+
+ pci_dev->config_header.max_latency = 1; // ?? (qemu does it...)
+
+
+ virtio_state->pci_dev = pci_dev;
+ virtio_state->pci_bus = pci_bus;
+ }
+
+ virtio_reset(virtio_state);
+
+ if (v3_dev_add_char_frontend(vm, dev_id, connect_fn, (void *)virtio_state) == -1) {
+ PrintError("Could not register %s as frontend\n", dev_id);
+ v3_remove_device(dev);
+ return -1;
+ }
+
+
+ return 0;
+}
+
+
+device_register("LNX_VIRTIO_CONSOLE", virtio_init)
static struct v3_stream_hooks * stream_hooks = NULL;
// VM can be NULL
-v3_stream_t v3_stream_open(struct v3_vm_info * vm, const char * name) {
+struct v3_stream * v3_stream_open(struct v3_vm_info * vm, const char * name,
+ uint64_t (*input)(struct v3_stream * stream, uint8_t * buf, uint64_t len),
+ void * guest_stream_data) {
+ struct v3_stream * stream = NULL;
+
V3_ASSERT(stream_hooks != NULL);
V3_ASSERT(stream_hooks->open != NULL);
- return stream_hooks->open(name, vm->host_priv_data);
+ stream = V3_Malloc(sizeof(struct v3_stream *));
+
+ stream->input = input;
+ stream->guest_stream_data = guest_stream_data;
+ stream->host_stream_data = stream_hooks->open(stream, name, vm->host_priv_data);
+
+ return stream;
}
-int v3_stream_write(v3_stream_t stream, uint8_t * buf, uint32_t len) {
+uint64_t v3_stream_output(struct v3_stream * stream, uint8_t * buf, uint32_t len) {
V3_ASSERT(stream_hooks != NULL);
- V3_ASSERT(stream_hooks->write != NULL);
+ V3_ASSERT(stream_hooks->output != NULL);
- return stream_hooks->write(stream, buf, len);
+ return stream_hooks->output(stream, buf, len);
}
-void v3_stream_close(v3_stream_t stream) {
+void v3_stream_close(struct v3_stream * stream) {
V3_ASSERT(stream_hooks != NULL);
V3_ASSERT(stream_hooks->close != NULL);
- return stream_hooks->close(stream);
-}
-
+ stream_hooks->close(stream);
+ V3_Free(stream);
+}