Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


added virtio console, finished the stream interface implementation, added v3_stream...
Jack Lange [Wed, 17 Aug 2011 18:23:46 +0000 (14:23 -0400)]
13 files changed:
linux_module/iface-console.c
linux_module/iface-stream.c
linux_usr/Makefile
linux_usr/v3_serial.c [deleted file]
linux_usr/v3_stream.c [new file with mode: 0644]
palacios/include/devices/lnx_virtio_pci.h
palacios/include/interfaces/vmm_stream.h
palacios/include/palacios/vmm_dev_mgr.h
palacios/src/devices/Kconfig
palacios/src/devices/Makefile
palacios/src/devices/char_stream.c
palacios/src/devices/lnx_virtio_console.c [new file with mode: 0644]
palacios/src/interfaces/vmm_stream.c

index 3e82d4c..9e13546 100644 (file)
@@ -140,7 +140,7 @@ console_write(struct file * filp, const char __user * buf, size_t size, loff_t *
 
 
     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;
        }
index 36582d9..9cb89e5 100644 (file)
 #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;
 
@@ -40,19 +43,24 @@ struct stream_buffer {
 
     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;
@@ -78,28 +86,119 @@ static struct stream_buffer * find_stream_by_name(struct v3_guest * guest, const
 
 
 
+#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");
@@ -115,10 +214,13 @@ static void * palacios_stream_open(const char * name, void * private_data) {
        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);
 
@@ -135,24 +237,38 @@ static void * palacios_stream_open(const char * name, void * private_data) {
 }
 
 
-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);
 
@@ -160,7 +276,7 @@ static void palacios_stream_close(void * stream_ptr) {
 
 static struct v3_stream_hooks palacios_stream_hooks = {
     .open = palacios_stream_open,
-    .write = palacios_stream_write,
+    .output = palacios_stream_output,
     .close = palacios_stream_close,
 };
 
@@ -188,7 +304,7 @@ static int stream_deinit( void ) {
 
 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;
@@ -221,7 +337,7 @@ static int stream_connect(struct v3_guest * guest, unsigned int cmd, unsigned lo
     }
 
     
-    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);
@@ -235,12 +351,11 @@ static int stream_connect(struct v3_guest * guest, unsigned int cmd, unsigned lo
 
 
 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;
@@ -248,7 +363,7 @@ static int guest_stream_init(struct v3_guest * guest, void ** vm_data) {
 
 
 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");
     }
index 1af1d8d..64b4672 100644 (file)
@@ -1,4 +1,4 @@
-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
 
 
 
@@ -15,8 +15,8 @@ v3_mem : v3_mem.c v3_ctrl.h
 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
@@ -38,4 +38,4 @@ v3_user_keyed_stream_file: v3_user_keyed_stream_file.c v3_user_keyed_stream.h v3
 
 
 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
diff --git a/linux_usr/v3_serial.c b/linux_usr/v3_serial.c
deleted file mode 100644 (file)
index 2f66c7f..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/* 
- * 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; 
-}
-
-
diff --git a/linux_usr/v3_stream.c b/linux_usr/v3_stream.c
new file mode 100644 (file)
index 0000000..0a1f46a
--- /dev/null
@@ -0,0 +1,113 @@
+/* 
+ * 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; 
+}
+
+
index 5379479..e48df2c 100644 (file)
 
 #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
index 513cf73..e9c49c7 100644 (file)
 
 
 
+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);
 };
 
 
index 87f62fc..792aa0c 100644 (file)
@@ -202,11 +202,11 @@ struct v3_dev_console_ops {
 
 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);
 };
 
 
index 575223b..758f2bd 100644 (file)
@@ -153,6 +153,14 @@ config DEBUG_VIRTIO_SYM
        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
index 0ab7f7f..1154059 100644 (file)
@@ -15,6 +15,7 @@ obj-$(V3_CONFIG_LINUX_VIRTIO_BLOCK) += lnx_virtio_blk.o
 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
index f0c762d..2bde181 100644 (file)
 
 
 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) {
@@ -93,8 +90,7 @@ static int stream_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
     }
 
 
-
-    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);
@@ -102,7 +98,8 @@ static int stream_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
        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, 
@@ -113,7 +110,7 @@ static int stream_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
        return -1;
     }
 
-    v3_hook_host_event(vm, HOST_SERIAL_EVT, V3_HOST_EVENT_HANDLER(serial_event_handler), state);
+
 
     return 0;
 }
diff --git a/palacios/src/devices/lnx_virtio_console.c b/palacios/src/devices/lnx_virtio_console.c
new file mode 100644 (file)
index 0000000..f7f2a11
--- /dev/null
@@ -0,0 +1,532 @@
+/* 
+ * 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)
index 66ce081..377a545 100644 (file)
 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);
+}