palacios-dev.o \
palacios-vm.o \
palacios-mm.o \
- palacios-queue.o
+ palacios-queue.o \
+ palacios-hashtable.o
ifdef V3_CONFIG_CONSOLE
v3vee-objs += palacios-console.o
v3vee-objs += palacios-socket.o
endif
+ifdef V3_CONFIG_KEYED_STREAMS
+ v3vee-objs += palacios-keyed-stream.o
+endif
+
+ifdef V3_CONFIG_HOST_DEVICE
+ v3vee-objs += palacios-host-dev.o
+endif
+
+ifdef V3_CONFIG_GRAPHICS_CONSOLE
+ v3vee-objs += palacios-graphics-console.o
+endif
+
+
+
v3vee-objs += ../libv3vee.a
#include "palacios-inspector.h"
#endif
+#ifdef V3_CONFIG_KEYED_STREAMS
+#include "palacios-keyed-stream.h"
+#endif
+
+
MODULE_LICENSE("GPL");
int mod_allocs = 0;
INIT_LIST_HEAD(&(guest->streams));
INIT_LIST_HEAD(&(guest->files));
INIT_LIST_HEAD(&(guest->sockets));
+#ifdef V3_CONFIG_HOST_DEVICE
+ INIT_LIST_HEAD(&(guest->hostdev.devs));
+#endif
init_completion(&(guest->start_done));
init_completion(&(guest->thread_done));
- kthread_run(start_palacios_vm, guest, guest->name);
+ {
+ struct task_struct * launch_thread = NULL;
+ // At some point we're going to want to allow the user to specify a CPU mask
+ // But for now, well just launch from the local core, and rely on the global cpu mask
+
+ preempt_disable();
+ launch_thread = kthread_create(start_palacios_vm, guest, guest->name);
+
+ if (IS_ERR(launch_thread)) {
+ preempt_enable();
+ printk("Palacios error creating launch thread for vm (%s)\n", guest->name);
+ return -EFAULT;
+ }
+
+ kthread_bind(launch_thread, smp_processor_id());
+ preempt_enable();
+
+ wake_up_process(launch_thread);
+ }
wait_for_completion(&(guest->start_done));
palacios_file_init();
#endif
+#ifdef V3_CONFIG_KEYED_STREAMS
+ palacios_init_keyed_streams();
+#endif
+
#ifdef V3_CONFIG_CONSOLE
palacios_init_console();
#endif
+#ifdef V3_CONFIG_GRAPHICS_CONSOLE
+ palacios_init_graphics_console();
+#endif
+
#ifdef V3_CONFIG_EXT_INSPECTOR
palacios_init_inspector();
#endif
palacios_init_vnet();
#endif
+#ifdef V3_CONFIG_HOST_DEVICE
+ palacios_init_host_dev();
+#endif
+
return 0;
failure1:
--- /dev/null
+/*
+ * Palacios VM Graphics Console Interface (shared framebuffer between palacios and host)
+ * Copyright (c) 2011 Peter Dinda <pdinda@northwestern.edu>
+ */
+
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/poll.h>
+#include <linux/anon_inodes.h>
+#include <linux/file.h>
+#include <linux/sched.h>
+
+#include <interfaces/vmm_console.h>
+#include <palacios/vmm_host_events.h>
+
+#include "palacios.h"
+#include "palacios-graphics-console.h"
+
+
+/*
+
+ This is an implementation of the Palacios Graphics Console interface that
+ is designed to interact with a vnc server running in user space,
+ typically something based on x0vncserver.
+
+ The basic idea is that we manage a frame buffer that we share with
+ palacios. Palacios draws whatever it likes on it.
+ The user-land vncserver will send us requests for fb updates, which
+ we implement by copying the FB to it. When the user-land sends us characters, etc,
+ we deliver those immediately to palacios via the deliver_key and deliver_mouse
+ event interfaces. The end-result is that whatever the graphics system
+ in palacios renders is visible via vnc.
+
+*/
+
+static v3_graphics_console_t g_open(void * priv_data,
+ struct v3_frame_buffer_spec *desired_spec,
+ struct v3_frame_buffer_spec *actual_spec)
+{
+ struct v3_guest * guest = (struct v3_guest *)priv_data;
+ struct palacios_graphics_console *gc = (struct palacios_graphics_console *) &(guest->graphics_console);
+ uint32_t mem;
+
+ if(gc->data) {
+ printk("palacios: framebuffer already allocated - returning it\n");
+ *actual_spec=gc->spec;
+ gc->cons_refcount++;
+ gc->data_refcount++;
+ return gc;
+ }
+
+ mem = desired_spec->width * desired_spec->height * desired_spec->bytes_per_pixel;
+
+ printk("palacios: allocating %u bytes for %u by %u by %u buffer\n",
+ mem, desired_spec->width, desired_spec->height, desired_spec->bytes_per_pixel);
+
+ gc->data = kmalloc(mem,GFP_KERNEL);
+
+ if (!(gc->data)) {
+ printk("palacios: unable to allocate memory for frame buffer\n");
+ return 0;
+ }
+
+ gc->spec = *desired_spec;
+
+ *actual_spec = gc->spec;
+
+ gc->guest=guest;
+
+ gc->cons_refcount++;
+ gc->data_refcount++;
+
+ printk("palacios: allocated frame buffer\n");
+
+ return gc;
+}
+
+static void g_close(v3_graphics_console_t cons)
+{
+ struct palacios_graphics_console *gc = (struct palacios_graphics_console *) cons;
+
+ gc->cons_refcount--;
+ gc->data_refcount--;
+
+ if (gc->data_refcount<gc->cons_refcount) {
+ printk("palacios: error! data refcount is less than console refcount for graphics console\n");
+ }
+
+ if (gc->cons_refcount>0) {
+ return;
+ } else {
+ if (gc->cons_refcount<0) {
+ printk("palacios: error! refcount for graphics console is negative on close!\n");
+ }
+ if (gc->data_refcount>0) {
+ printk("palacios: error! refcount for graphics console data is positive on close - LEAKING MEMORY\n");
+ return;
+ }
+ if (gc->data) {
+ kfree(gc->data);
+ gc->data=0;
+ }
+ }
+}
+
+static void * g_get_data_read(v3_graphics_console_t cons,
+ struct v3_frame_buffer_spec *cur_spec)
+{
+ struct palacios_graphics_console *gc = (struct palacios_graphics_console *) cons;
+
+ if (gc->data_refcount<=0) {
+ printk("palacios: error! data refcount is <= 0 in get_data_read\n");
+ }
+
+ gc->data_refcount++;
+
+ *cur_spec=gc->spec;
+
+ return gc->data;
+}
+
+static void g_release_data_read(v3_graphics_console_t cons)
+{
+ struct palacios_graphics_console *gc = (struct palacios_graphics_console *) cons;
+
+ gc->data_refcount--;
+
+ if (gc->data_refcount<=0) {
+ printk("palacios: error! data refcount is <= zero in release_data_read\n");
+ }
+
+}
+
+static void *g_get_data_rw(v3_graphics_console_t cons,
+ struct v3_frame_buffer_spec *cur_spec)
+{
+ return g_get_data_read(cons,cur_spec);
+}
+
+
+static void g_release_data_rw(v3_graphics_console_t cons)
+{
+ return g_release_data_read(cons);
+}
+
+
+static int g_changed(v3_graphics_console_t cons)
+{
+ struct palacios_graphics_console *gc =
+ (struct palacios_graphics_console *) cons;
+
+#if 0
+ int rc = !(gc->num_updates % 1000);
+
+ gc->num_updates++;
+
+ return rc;
+#else
+ return 1;
+#endif
+}
+
+
+
+static int palacios_graphics_console_key(struct palacios_graphics_console *cons, uint8_t scancode)
+{
+ struct v3_keyboard_event e;
+ e.status=0;
+ e.scan_code=scancode;
+
+ v3_deliver_keyboard_event(cons->guest->v3_ctx,&e);
+
+ return 0;
+}
+
+static int palacios_graphics_console_mouse(struct palacios_graphics_console *cons, uint8_t x, uint8_t y, uint8_t buttons)
+{
+ struct v3_mouse_event e;
+ e.data[0]=x;
+ e.data[1]=y;
+ e.data[2]=buttons; // These three are completely wrong, of course - ignoring mouse for now
+
+ v3_deliver_mouse_event(cons->guest->v3_ctx,&e);
+
+ return 0;
+}
+
+static struct v3_graphics_console_hooks palacios_graphics_console_hooks =
+{
+ .open = g_open,
+ .close = g_close,
+
+ .get_data_read = g_get_data_read,
+ .release_data_read = g_release_data_read,
+ .get_data_rw = g_get_data_rw,
+ .release_data_rw = g_release_data_rw,
+
+ .changed = g_changed,
+};
+
+
+int palacios_init_graphics_console( void ) {
+
+ V3_Init_Graphics_Console(&palacios_graphics_console_hooks);
+
+ return 0;
+}
+
+
+int palacios_graphics_console_user_query(struct palacios_graphics_console *cons,
+ struct v3_fb_query_response __user *u)
+{
+ struct v3_fb_query_response q;
+
+
+ if (copy_from_user(&q,(void __user *) u, sizeof(struct v3_fb_query_response))) {
+ printk("palacios: copy from user in getting query in fb\n");
+ return -EFAULT;
+ }
+
+ switch (q.request_type) {
+ case V3_FB_SPEC:
+ // returns only the spec for the FB
+ q.spec = cons->spec;
+
+ break;
+
+ case V3_FB_UPDATE:
+ // returns whether an update is available for the region
+ // currently always true
+ q.updated = 1;
+
+ break;
+
+ case V3_FB_DATA_BOX: {
+ // Not curently implemented
+ printk("palacios: request for data in bounding box unsupported currently\n");
+ return -EFAULT;
+
+ }
+
+ break;
+
+ case V3_FB_DATA_ALL: {
+ // First let's sanity check to see if they are requesting the same
+ // spec that we have
+ if (memcmp(&(q.spec),&(cons->spec),sizeof(struct v3_frame_buffer_spec))) {
+ printk("palacios: request for data with non-matching fb spec \n");
+ return -EFAULT;
+ }
+ // Now let's indicate an update is in the pointer and copy across the data
+ if (copy_to_user(q.data,cons->data,cons->spec.width*cons->spec.height*cons->spec.bytes_per_pixel)) {
+ printk("palacios: unable to copy fb content to user\n");
+ return -EFAULT;
+ }
+ q.updated=1;
+ }
+ break;
+
+ default:
+ return -EFAULT;
+ }
+
+ // now we'll copy back any changes we made to the query/response structure
+ if (copy_to_user((void __user *) u, (void*)&q, sizeof(struct v3_fb_query_response))) {
+ printk("palacios: unable to copy fb response to user\n");
+ return -EFAULT;
+ }
+
+ return 0;
+
+}
+
+int palacios_graphics_console_user_input(struct palacios_graphics_console *cons,
+ struct v3_fb_input __user *u)
+{
+ struct v3_fb_input inp;
+ int rc=0;
+
+
+ if (copy_from_user(&inp,(void __user *) u, sizeof(struct v3_fb_input))) {
+ printk("palacios: copy from user in getting input in fb\n");
+ return -EFAULT;
+ }
+
+ if (inp.data_type==V3_FB_KEY || inp.data_type==V3_FB_BOTH) {
+ rc = palacios_graphics_console_key(cons,inp.scan_code);
+ }
+
+ if (inp.data_type==V3_FB_MOUSE || inp.data_type==V3_FB_BOTH) {
+ rc |= palacios_graphics_console_mouse(cons,inp.mouse_data[0],inp.mouse_data[1],inp.mouse_data[2]);
+ }
+
+ if (rc) {
+ return -EFAULT;
+ } else {
+ return 0;
+ }
+}
--- /dev/null
+/*
+ * Palacios VM Graphics Console Interface (shared framebuffer between palacios and host)
+ * Copyright (c) 2011 Peter Dinda <pdinda@northwestern.edu>
+ */
+
+#ifndef __PALACIOS_GRAPHICS_CONSOLE_H__
+#define __PALACIOS_GRAPHICS_CONSOLE_H__
+
+#include <interfaces/vmm_graphics_console.h>
+
+struct palacios_graphics_console {
+ // descriptor for the data in the shared frame buffer
+ struct v3_frame_buffer_spec spec;
+ // the actual shared frame buffer
+ // Note that "shared" here means shared between palacios and us
+ // This data could of course also be shared with userland
+ void *data;
+
+ struct v3_guest * guest;
+
+ int cons_refcount;
+ int data_refcount;
+
+ uint32_t num_updates;
+
+ // Currently keystrokes and mouse movements are ignored
+
+ // currently, we will not worry about locking this
+ // lock_t ...
+};
+
+
+// This is the data structure that is passed back and forth with user-land
+// ioctl
+struct v3_fb_query_response {
+ enum { V3_FB_DATA_ALL, V3_FB_DATA_BOX, V3_FB_UPDATE, V3_FB_SPEC } request_type;
+ struct v3_frame_buffer_spec spec; // in: desired spec; out: actual spec
+ uint32_t x, y, w, h; // region to copy (0s = all) in/out args
+ int updated; // whether this region has been updated or not
+ void __user *data; // user space pointer to copy data to
+};
+
+// This is what userland sends down for input events
+struct v3_fb_input {
+ enum { V3_FB_KEY, V3_FB_MOUSE, V3_FB_BOTH} data_type;
+ uint8_t scan_code;
+ uint8_t mouse_data[3];
+};
+
+
+int palacios_init_graphics_console(void);
+
+int palacios_graphics_console_user_query(struct palacios_graphics_console *cons,
+ struct v3_fb_query_response __user *fb);
+
+int palacios_graphics_console_user_input(struct palacios_graphics_console *cons,
+ struct v3_fb_input __user *in);
+
+
+#endif
--- /dev/null
+/*
+ * Palacios Hash Table
+ * (c) Lei Xia, 2011
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/preempt.h>
+#include <linux/sched.h>
+
+#include "palacios-hashtable.h"
+
+
+struct hash_entry {
+ addr_t key;
+ addr_t value;
+ uint_t hash;
+ struct hash_entry * next;
+};
+
+struct hashtable {
+ uint_t table_length;
+ struct hash_entry ** table;
+ uint_t entry_count;
+ uint_t load_limit;
+ uint_t prime_index;
+ uint_t (*hash_fn) (addr_t key);
+ int (*eq_fn) (addr_t key1, addr_t key2);
+};
+
+
+/* HASH FUNCTIONS */
+
+static inline uint_t do_hash(struct hashtable * htable, addr_t key) {
+ /* Aim to protect against poor hash functions by adding logic here
+ * - logic taken from java 1.4 hashtable source */
+ uint_t i = htable->hash_fn(key);
+ i += ~(i << 9);
+ i ^= ((i >> 14) | (i << 18)); /* >>> */
+ i += (i << 4);
+ i ^= ((i >> 10) | (i << 22)); /* >>> */
+
+ return i;
+}
+
+
+/* HASH AN UNSIGNED LONG */
+/* LINUX UNSIGHED LONG HASH FUNCTION */
+#ifdef __32BIT__
+/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */
+#define GOLDEN_RATIO_PRIME 0x9e370001UL
+//#define BITS_PER_LONG 32
+#elif defined(__64BIT__)
+/* 2^63 + 2^61 - 2^57 + 2^54 - 2^51 - 2^18 + 1 */
+#define GOLDEN_RATIO_PRIME 0x9e37fffffffc0001UL
+//#define BITS_PER_LONG 64
+#else
+#error Define GOLDEN_RATIO_PRIME for your wordsize.
+#endif
+
+ulong_t palacios_hash_long(ulong_t val, uint_t bits) {
+ ulong_t hash = val;
+
+#ifdef __PALACIOS_64BIT__
+ /* Sigh, gcc can't optimise this alone like it does for 32 bits. */
+ ulong_t n = hash;
+ n <<= 18;
+ hash -= n;
+ n <<= 33;
+ hash -= n;
+ n <<= 3;
+ hash += n;
+ n <<= 3;
+ hash -= n;
+ n <<= 4;
+ hash += n;
+ n <<= 2;
+ hash += n;
+#else
+ /* On some cpus multiply is faster, on others gcc will do shifts */
+ hash *= GOLDEN_RATIO_PRIME;
+#endif
+
+ /* High bits are more random, so use them. */
+ return hash >> (BITS_PER_LONG - bits);
+}
+
+/* HASH GENERIC MEMORY BUFFER */
+/* ELF HEADER HASH FUNCTION */
+ulong_t palacios_hash_buffer(uchar_t * msg, uint_t length) {
+ ulong_t hash = 0;
+ ulong_t temp = 0;
+ uint_t i;
+
+ for (i = 0; i < length; i++) {
+ hash = (hash << 4) + *(msg + i) + i;
+ if ((temp = (hash & 0xF0000000))) {
+ hash ^= (temp >> 24);
+ }
+ hash &= ~temp;
+ }
+ return hash;
+}
+
+/* indexFor */
+static inline uint_t indexFor(uint_t table_length, uint_t hash_value) {
+ return (hash_value % table_length);
+};
+
+#define freekey(X) kfree(X)
+
+
+static void * tmp_realloc(void * old_ptr, uint_t old_size, uint_t new_size) {
+ void * new_buf = kmalloc(new_size, GFP_KERNEL);
+
+ if (new_buf == NULL) {
+ return NULL;
+ }
+
+ memcpy(new_buf, old_ptr, old_size);
+ kfree(old_ptr);
+
+ return new_buf;
+}
+
+
+/*
+ Credit for primes table: Aaron Krowne
+ http://br.endernet.org/~akrowne/
+ http://planetmath.org/encyclopedia/GoodHashTablePrimes.html
+*/
+static const uint_t primes[] = {
+ 53, 97, 193, 389,
+ 769, 1543, 3079, 6151,
+ 12289, 24593, 49157, 98317,
+ 196613, 393241, 786433, 1572869,
+ 3145739, 6291469, 12582917, 25165843,
+ 50331653, 100663319, 201326611, 402653189,
+ 805306457, 1610612741 };
+
+
+// this assumes that the max load factor is .65
+static const uint_t load_factors[] = {
+ 35, 64, 126, 253,
+ 500, 1003, 2002, 3999,
+ 7988, 15986, 31953, 63907,
+ 127799, 255607, 511182, 1022365,
+ 2044731, 4089455, 8178897, 16357798,
+ 32715575, 65431158, 130862298, 261724573,
+ 523449198, 1046898282 };
+
+const uint_t prime_table_len = sizeof(primes) / sizeof(primes[0]);
+
+struct hashtable * palacios_create_htable(uint_t min_size,
+ uint_t (*hash_fn) (addr_t),
+ int (*eq_fn) (addr_t, addr_t)) {
+ struct hashtable * htable;
+ uint_t prime_index;
+ uint_t size = primes[0];
+
+ /* Check requested hashtable isn't too large */
+ if (min_size > (1u << 30)) {
+ return NULL;
+ }
+
+ /* Enforce size as prime */
+ for (prime_index = 0; prime_index < prime_table_len; prime_index++) {
+ if (primes[prime_index] > min_size) {
+ size = primes[prime_index];
+ break;
+ }
+ }
+
+ htable = (struct hashtable *)kmalloc(sizeof(struct hashtable), GFP_KERNEL);
+
+ if (htable == NULL) {
+ return NULL; /*oom*/
+ }
+
+ htable->table = (struct hash_entry **)kmalloc(sizeof(struct hash_entry*) * size, GFP_KERNEL);
+
+ if (htable->table == NULL) {
+ kfree(htable);
+ return NULL; /*oom*/
+ }
+
+ memset(htable->table, 0, size * sizeof(struct hash_entry *));
+
+ htable->table_length = size;
+ htable->prime_index = prime_index;
+ htable->entry_count = 0;
+ htable->hash_fn = hash_fn;
+ htable->eq_fn = eq_fn;
+ htable->load_limit = load_factors[prime_index];
+
+ return htable;
+}
+
+
+static int hashtable_expand(struct hashtable * htable) {
+ /* Double the size of the table to accomodate more entries */
+ struct hash_entry ** new_table;
+ struct hash_entry * tmp_entry;
+ struct hash_entry ** entry_ptr;
+ uint_t new_size;
+ uint_t i;
+ uint_t index;
+
+ /* Check we're not hitting max capacity */
+ if (htable->prime_index == (prime_table_len - 1)) {
+ return 0;
+ }
+
+ new_size = primes[++(htable->prime_index)];
+
+ new_table = (struct hash_entry **)kmalloc(sizeof(struct hash_entry*) * new_size, GFP_KERNEL);
+
+ if (new_table != NULL) {
+ memset(new_table, 0, new_size * sizeof(struct hash_entry *));
+ /* This algorithm is not 'stable'. ie. it reverses the list
+ * when it transfers entries between the tables */
+
+ for (i = 0; i < htable->table_length; i++) {
+
+ while ((tmp_entry = htable->table[i]) != NULL) {
+ htable->table[i] = tmp_entry->next;
+
+ index = indexFor(new_size, tmp_entry->hash);
+
+ tmp_entry->next = new_table[index];
+
+ new_table[index] = tmp_entry;
+ }
+ }
+
+ kfree(htable->table);
+
+ htable->table = new_table;
+ } else {
+ /* Plan B: realloc instead */
+
+ //new_table = (struct hash_entry **)realloc(htable->table, new_size * sizeof(struct hash_entry *));
+ new_table = (struct hash_entry **)tmp_realloc(htable->table, primes[htable->prime_index - 1],
+ new_size * sizeof(struct hash_entry *));
+
+ if (new_table == NULL) {
+ (htable->prime_index)--;
+ return 0;
+ }
+
+ htable->table = new_table;
+
+ memset(new_table[htable->table_length], 0, new_size - htable->table_length);
+
+ for (i = 0; i < htable->table_length; i++) {
+
+ for (entry_ptr = &(new_table[i]), tmp_entry = *entry_ptr;
+ tmp_entry != NULL;
+ tmp_entry = *entry_ptr) {
+
+ index = indexFor(new_size, tmp_entry->hash);
+
+ if (i == index) {
+ entry_ptr = &(tmp_entry->next);
+ } else {
+ *entry_ptr = tmp_entry->next;
+ tmp_entry->next = new_table[index];
+ new_table[index] = tmp_entry;
+ }
+ }
+ }
+ }
+
+ htable->table_length = new_size;
+
+ htable->load_limit = load_factors[htable->prime_index];
+
+ return -1;
+}
+
+uint_t palacios_htable_count(struct hashtable * htable) {
+ return htable->entry_count;
+}
+
+int palacios_htable_insert(struct hashtable * htable, addr_t key, addr_t value) {
+ /* This method allows duplicate keys - but they shouldn't be used */
+ uint_t index;
+ struct hash_entry * new_entry;
+
+ if (++(htable->entry_count) > htable->load_limit) {
+ /* Ignore the return value. If expand fails, we should
+ * still try cramming just this value into the existing table
+ * -- we may not have memory for a larger table, but one more
+ * element may be ok. Next time we insert, we'll try expanding again.*/
+ hashtable_expand(htable);
+ }
+
+ new_entry = (struct hash_entry *)kmalloc(sizeof(struct hash_entry), GFP_KERNEL);
+
+ if (new_entry == NULL) {
+ (htable->entry_count)--;
+ return 0; /*oom*/
+ }
+
+ new_entry->hash = do_hash(htable, key);
+
+ index = indexFor(htable->table_length, new_entry->hash);
+
+ new_entry->key = key;
+ new_entry->value = value;
+
+ new_entry->next = htable->table[index];
+
+ htable->table[index] = new_entry;
+
+ return -1;
+}
+
+
+int palacios_htable_change(struct hashtable * htable, addr_t key, addr_t value, int free_value) {
+ struct hash_entry * tmp_entry;
+ uint_t hash_value;
+ uint_t index;
+
+ hash_value = do_hash(htable, key);
+
+ index = indexFor(htable->table_length, hash_value);
+
+ tmp_entry = htable->table[index];
+
+ while (tmp_entry != NULL) {
+ /* Check hash value to short circuit heavier comparison */
+ if ((hash_value == tmp_entry->hash) && (htable->eq_fn(key, tmp_entry->key))) {
+
+ if (free_value) {
+ kfree((void *)(tmp_entry->value));
+ }
+
+ tmp_entry->value = value;
+ return -1;
+ }
+ tmp_entry = tmp_entry->next;
+ }
+ return 0;
+}
+
+
+
+int palacios_htable_inc(struct hashtable * htable, addr_t key, addr_t value) {
+ struct hash_entry * tmp_entry;
+ uint_t hash_value;
+ uint_t index;
+
+ hash_value = do_hash(htable, key);
+
+ index = indexFor(htable->table_length, hash_value);
+
+ tmp_entry = htable->table[index];
+
+ while (tmp_entry != NULL) {
+ /* Check hash value to short circuit heavier comparison */
+ if ((hash_value == tmp_entry->hash) && (htable->eq_fn(key, tmp_entry->key))) {
+
+ tmp_entry->value += value;
+ return -1;
+ }
+ tmp_entry = tmp_entry->next;
+ }
+ return 0;
+}
+
+
+int palacios_htable_dec(struct hashtable * htable, addr_t key, addr_t value) {
+ struct hash_entry * tmp_entry;
+ uint_t hash_value;
+ uint_t index;
+
+ hash_value = do_hash(htable, key);
+
+ index = indexFor(htable->table_length, hash_value);
+
+ tmp_entry = htable->table[index];
+
+ while (tmp_entry != NULL) {
+ /* Check hash value to short circuit heavier comparison */
+ if ((hash_value == tmp_entry->hash) && (htable->eq_fn(key, tmp_entry->key))) {
+
+ tmp_entry->value -= value;
+ return -1;
+ }
+ tmp_entry = tmp_entry->next;
+ }
+ return 0;
+}
+
+
+/* returns value associated with key */
+addr_t palacios_htable_search(struct hashtable * htable, addr_t key) {
+ struct hash_entry * cursor;
+ uint_t hash_value;
+ uint_t index;
+
+ hash_value = do_hash(htable, key);
+
+ index = indexFor(htable->table_length, hash_value);
+
+ cursor = htable->table[index];
+
+ while (cursor != NULL) {
+ /* Check hash value to short circuit heavier comparison */
+ if ((hash_value == cursor->hash) &&
+ (htable->eq_fn(key, cursor->key))) {
+ return cursor->value;
+ }
+
+ cursor = cursor->next;
+ }
+
+ return (addr_t)NULL;
+}
+
+
+/* returns value associated with key */
+addr_t palacios_htable_remove(struct hashtable * htable, addr_t key, int free_key) {
+ /* TODO: consider compacting the table when the load factor drops enough,
+ * or provide a 'compact' method. */
+
+ struct hash_entry * cursor;
+ struct hash_entry ** entry_ptr;
+ addr_t value;
+ uint_t hash_value;
+ uint_t index;
+
+ hash_value = do_hash(htable, key);
+
+ index = indexFor(htable->table_length, hash_value);
+
+ entry_ptr = &(htable->table[index]);
+ cursor = *entry_ptr;
+
+ while (cursor != NULL) {
+ /* Check hash value to short circuit heavier comparison */
+ if ((hash_value == cursor->hash) &&
+ (htable->eq_fn(key, cursor->key))) {
+
+ *entry_ptr = cursor->next;
+ htable->entry_count--;
+ value = cursor->value;
+
+ if (free_key) {
+ freekey((void *)(cursor->key));
+ }
+ kfree(cursor);
+
+ return value;
+ }
+
+ entry_ptr = &(cursor->next);
+ cursor = cursor->next;
+ }
+ return (addr_t)NULL;
+}
+
+
+/* destroy */
+void palacios_free_htable(struct hashtable * htable, int free_values, int free_keys) {
+ uint_t i;
+ struct hash_entry * cursor;
+ struct hash_entry * tmp;
+ struct hash_entry **table = htable->table;
+
+ if (free_values) {
+ for (i = 0; i < htable->table_length; i++) {
+ cursor = table[i];
+
+ while (cursor != NULL) {
+ tmp = cursor;
+ cursor = cursor->next;
+
+ if (free_keys) {
+ freekey((void *)(tmp->key));
+ }
+ kfree((void *)(tmp->value));
+ kfree(tmp);
+ }
+ }
+ } else {
+ for (i = 0; i < htable->table_length; i++) {
+ cursor = table[i];
+
+ while (cursor != NULL) {
+ struct hash_entry * tmp;
+
+ tmp = cursor;
+ cursor = cursor->next;
+
+ if (free_keys) {
+ freekey((void *)(tmp->key));
+ }
+ kfree(tmp);
+ }
+ }
+ }
+
+ kfree(htable->table);
+ kfree(htable);
+}
+
--- /dev/null
+#ifndef __PALACIOS_HASHTABLE_H__
+#define __PALACIOS_HASHTABLE_H__
+
+struct hashtable;
+
+#define __32BIT__
+
+/* Example of use:
+ *
+ * struct hashtable *h;
+ * struct some_key *k;
+ * struct some_value *v;
+ *
+ * static uint_t hash_from_key_fn( void *k );
+ * static int keys_equal_fn ( void *key1, void *key2 );
+ *
+ * h = create_hashtable(16, hash_from_key_fn, keys_equal_fn);
+ * k = (struct some_key *) malloc(sizeof(struct some_key));
+ * v = (struct some_value *) malloc(sizeof(struct some_value));
+ *
+ * (initialise k and v to suitable values)
+ *
+ * if (! hashtable_insert(h,k,v) )
+ * { exit(-1); }
+ *
+ * if (NULL == (found = hashtable_search(h,k) ))
+ * { printf("not found!"); }
+ *
+ * if (NULL == (found = hashtable_remove(h,k) ))
+ * { printf("Not found\n"); }
+ *
+ */
+
+/* Macros may be used to define type-safe(r) hashtable access functions, with
+ * methods specialized to take known key and value types as parameters.
+ *
+ * Example:
+ *
+ * Insert this at the start of your file:
+ *
+ * DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value);
+ * DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value);
+ * DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value);
+ *
+ * This defines the functions 'insert_some', 'search_some' and 'remove_some'.
+ * These operate just like hashtable_insert etc., with the same parameters,
+ * but their function signatures have 'struct some_key *' rather than
+ * 'void *', and hence can generate compile time errors if your program is
+ * supplying incorrect data as a key (and similarly for value).
+ *
+ * Note that the hash and key equality functions passed to create_hashtable
+ * still take 'void *' parameters instead of 'some key *'. This shouldn't be
+ * a difficult issue as they're only defined and passed once, and the other
+ * functions will ensure that only valid keys are supplied to them.
+ *
+ * The cost for this checking is increased code size and runtime overhead
+ * - if performance is important, it may be worth switching back to the
+ * unsafe methods once your program has been debugged with the safe methods.
+ * This just requires switching to some simple alternative defines - eg:
+ * #define insert_some hashtable_insert
+ *
+ */
+
+typedef unsigned char uchar_t;
+typedef unsigned int uint_t;
+typedef unsigned long long ullong_t;
+typedef unsigned long ulong_t;
+typedef ulong_t addr_t;
+
+
+#define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \
+ static int fnname (struct hashtable * htable, keytype key, valuetype value) { \
+ return v3_htable_insert(htable, (addr_t)key, (addr_t)value); \
+ }
+
+#define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \
+ static valuetype * fnname (struct hashtable * htable, keytype key) { \
+ return (valuetype *) (v3_htable_search(htable, (addr_t)key)); \
+ }
+
+#define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype, free_key) \
+ static valuetype * fnname (struct hashtable * htable, keytype key) { \
+ return (valuetype *) (v3_htable_remove(htable, (addr_t)key, free_key)); \
+ }
+
+
+
+
+
+/* These cannot be inlined because they are referenced as fn ptrs */
+ulong_t palacios_hash_long(ulong_t val, uint_t bits);
+ulong_t palacios_hash_buffer(uchar_t * msg, uint_t length);
+
+
+
+struct hashtable * palacios_create_htable(uint_t min_size,
+ uint_t (*hashfunction) (addr_t key),
+ int (*key_eq_fn) (addr_t key1, addr_t key2));
+
+void palacios_free_htable(struct hashtable * htable, int free_values, int free_keys);
+
+/*
+ * returns non-zero for successful insertion
+ *
+ * This function will cause the table to expand if the insertion would take
+ * the ratio of entries to table size over the maximum load factor.
+ *
+ * This function does not check for repeated insertions with a duplicate key.
+ * The value returned when using a duplicate key is undefined -- when
+ * the hashtable changes size, the order of retrieval of duplicate key
+ * entries is reversed.
+ * If in doubt, remove before insert.
+ */
+int palacios_htable_insert(struct hashtable * htable, addr_t key, addr_t value);
+
+int palacios_htable_change(struct hashtable * htable, addr_t key, addr_t value, int free_value);
+
+
+// returns the value associated with the key, or NULL if none found
+addr_t palacios_htable_search(struct hashtable * htable, addr_t key);
+
+// returns the value associated with the key, or NULL if none found
+addr_t palacios_htable_remove(struct hashtable * htable, addr_t key, int free_key);
+
+uint_t palacios_htable_count(struct hashtable * htable);
+
+// Specialty functions for a counting hashtable
+int palacios_htable_inc(struct hashtable * htable, addr_t key, addr_t value);
+int palacios_htable_dec(struct hashtable * htable, addr_t key, addr_t value);
+
+
+#endif
--- /dev/null
+#ifndef _PALACIOS_HOST_DEV_USER_H_
+#define _PALACIOS_HOST_DEV_USER_H_
+
+/*
+ * Palacios Host Device Interface + User-space interface
+ * (c) Peter Dinda, 2011
+ */
+
+
+#define V3_VM_HOST_DEV_CONNECT 512+1
+
+/* to detemine whether a host request is available, poll the fd for read */
+
+/* make a request for reading/writing guest or injecting irq */
+/* the arguemnt is a pointer to a palacios_host_dev_user_op struct */
+/* return is negative on error, positive to indicate bytes read/written or irq injected*/
+#define V3_HOST_DEV_USER_REQUEST_PUSH_IOCTL 1
+
+/* find out the size of the current host request, if one is pending */
+/* you find out if one is pending by read poll/select on the fd */
+/* the argument is a pointer to a uint64_t that will give the total size */
+/* ioctl returns 1 if a request is ready, 0 if there is no request */
+/* -EFAULT if there is a an error */
+#define V3_HOST_DEV_HOST_REQUEST_SIZE_IOCTL 2
+
+/* get the current host request, if one is available */
+/* the argument is a pointer to a palacios_host_dev_host_request_response */
+/* of the needed size */
+/* ioctl returns 1 if a request is ready+copied, 0 if there is no request */
+/* -EFAULT if there is a an error */
+#define V3_HOST_DEV_HOST_REQUEST_PULL_IOCTL 3
+
+/* write back the response to the previously pulled host request */
+/* the argument is a pointer to a palacios_host_dev_host_request_response */
+#define V3_HOST_DEV_USER_RESPONSE_PUSH_IOCTL 4
+
+
+#ifdef __KERNEL__
+#define USER __user
+#else
+#define USER
+#endif
+
+struct palacios_host_dev_user_op {
+#define PALACIOS_HOST_DEV_USER_REQUEST_READ_GUEST 1
+#define PALACIOS_HOST_DEV_USER_REQUEST_WRITE_GUEST 2
+#define PALACIOS_HOST_DEV_USER_REQUEST_IRQ_GUEST 3
+ uint32_t type; // type of operation (from the #defs above)
+ void *gpa; // physical address in guest to read or write
+ void USER *data; // user address of data that will be read or written
+ uint64_t len; // number of bytes to move
+
+ uint8_t irq; // irq to inject
+};
+
+
+struct palacios_host_dev_host_request_response {
+ // data_len must remain the first field in this structure
+ uint64_t data_len; // size of the structure + occupied data
+ uint64_t len; // size of the structure in total
+#define PALACIOS_HOST_DEV_HOST_REQUEST_READ_IO 1
+#define PALACIOS_HOST_DEV_HOST_REQUEST_WRITE_IO 2
+#define PALACIOS_HOST_DEV_HOST_REQUEST_READ_MEM 3
+#define PALACIOS_HOST_DEV_HOST_REQUEST_WRITE_MEM 4
+#define PALACIOS_HOST_DEV_HOST_REQUEST_READ_CONF 5
+#define PALACIOS_HOST_DEV_HOST_REQUEST_WRITE_CONF 6
+ uint32_t type; // one of the types given above
+ uint16_t port; // port number, for IO
+ void *gpa; // physical address in the guest for memory ops
+ uint64_t conf_addr; // address in the configuration for configuration ops
+
+ uint64_t op_len; // response: bytes read/written to the device
+ // request: bytes to read/write
+
+ uint8_t data[0]; // data (if any)
+
+} ;
+
+
+
+#endif
--- /dev/null
+/*
+ * Host device interface + user-space device interface
+ * (c) 2011 Peter Dinda
+ */
+
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/poll.h>
+#include <linux/anon_inodes.h>
+#include <linux/file.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+
+#include <interfaces/vmm_host_dev.h>
+
+#include "palacios.h"
+#include "palacios-host-dev.h"
+#include "palacios-host-dev-user.h"
+
+
+
+/*
+ There are two things in this file:
+
+
+ 1. An implementation of the Palacios host device interface that will
+ accept any URL from Palacios. Currently, the only URL type it will
+ handle is user:<name>, but it should be clear how to extend with
+ other types.
+
+ Palacios opens a host device by issuing something like this:
+
+ palacios_host_dev_open( impl="user:foo" busclass=pci, opaque )
+
+ This will attempt to rendezvous with the user space device The
+ rendevzous retry and timeout periods can be set below.
+
+ 2. An implementation of user: urls - the host interface is mapped
+ into an RPC-like interface to/from user space via a file
+ interface.
+
+ The user space gets a file descriptor like this:
+
+ int vmfd = open("/dev/v3-vmX",...);
+
+ int devfd = ioctl(vmfd,V3_HOST_DEV_CONNECT,"user:foo");
+
+ This will attempt to rendezvous with the host side.
+
+ If it returns successfully, you can now issue synchronous,
+ blocking RPCs to the guest to read/write its memory and inject
+ irqs. This means that a user->host request is handled
+ immediately, and independently of any host->user request.
+
+ struct palacios_host_dev_user_op op;
+
+ // fill out op
+ op.type = PALACIOS_HOST_DEV_USER_REQUEST_WRITE_GUEST;
+ ...
+
+ ioctl(devfd, V3_HOST_DEV_USER_REQUEST_PUSH_IOCTL, &op);
+
+ // return value is # bytes read or written; or 0 if irq injected
+ // negative value is error
+
+ The interface from the host to the user side is asynchronous
+ blocking RPC. Any host device will have at most one outstanding
+ request from palacios. The implementation here stores the
+ request until the user side is ready to accept it. The user side
+ can check if there is a request via a poll/select or an ioctl.
+ The ioctl also returns the needed size for the request structure.
+ After the user side has a request, it is expected to process it,
+ perhaps making user->host requests as described above, and then
+ return a response. Only one host->user request should be in
+ progress at any time in the user space.
+
+ What this looks like is:
+
+ poll(...devfd for read...) or select(...devfd for read...)
+
+ if (devfd is marked readable) {
+ uint64_t size;
+
+ ioctl(devfd,V3_HOST_DEV_HOST_REQUEST_SIZE_IOCTL,&size);
+ // returns 1 if there is a request, 0 if not, negative on err
+
+ struct palacios_host_dev_host_request_response *req;
+
+ req = allocate req to be at least size bytes long
+
+ ioctl(devfd,V3_HOST_DEV_HOST_REQUEST_PULL_IOCTL,req)
+ // returns 1 if there is a request, 0 if not, negative on err
+
+ // process request, perhaps using above user->host request
+ // build response structure
+ // resp.data_len == size of structure including relevant data at end
+
+ ioctl(devfd,V3_HOST_DEV_USER_RESPONSE_PUSH_IOCTL,resp);
+ // returns 0 if there is no outstanding request (user error)
+ // 1 on success, negative on error
+ }
+
+
+*/
+
+
+#define MAX_URL 256
+
+#define RENDEZVOUS_WAIT_SECS 60
+#define RENDEZVOUS_RETRY_SECS 1
+
+
+struct palacios_host_device_user {
+ spinlock_t lock;
+ int connected; // is the user space connected to this?
+ int waiting; // am I waiting for a user-space response?
+
+ int fd; // what is the user space fd?
+
+ char url[MAX_URL]; // what is the url describing the device
+
+ v3_guest_dev_t guestdev; // what is the palacios-side device
+
+ wait_queue_head_t user_wait_queue; // user space processes waiting on us (should be only one)
+ wait_queue_head_t host_wait_queue; // host threads (should only be one) waiting on user space
+
+ struct v3_guest *guest; // my guest
+ struct palacios_host_dev_host_request_response *req; // curent request
+ struct palacios_host_dev_host_request_response *resp; // curent response
+
+ struct list_head node; // for adding me to the list of hostdevs this VM has
+};
+
+
+/**************************************************************************************
+ Utility functions
+*************************************************************************************/
+
+static void palacios_host_dev_user_free(struct palacios_host_device_user *dev)
+{
+ if (dev->req) {
+ kfree(dev->req);
+ dev->req=0;
+ }
+ if (dev->resp) {
+ kfree(dev->resp);
+ dev->resp=0;
+ }
+ kfree(dev);
+}
+
+static int palacios_resize_reqresp(struct palacios_host_dev_host_request_response **r, uint64_t data_size, int copy)
+{
+ if (!*r) {
+ // allocate it
+ *r = kmalloc(sizeof(struct palacios_host_dev_host_request_response)+data_size,GFP_KERNEL);
+ if (!*r) {
+ return -1;
+ } else {
+ (*r)->len=sizeof(struct palacios_host_dev_host_request_response)+data_size;
+ return 0;
+ }
+ } else {
+ //let it go if it's big enough
+ uint64_t cur_len = (*r)->len-sizeof(struct palacios_host_dev_host_request_response);
+
+ if (data_size<=cur_len) {
+ // do nothing
+ return 0;
+ } else {
+ struct palacios_host_dev_host_request_response *new;
+
+ if (!copy) {
+ kfree(*r);
+ *r=0;
+ }
+ new = kmalloc(sizeof(struct palacios_host_dev_host_request_response)+data_size,GFP_KERNEL);
+ if (!new) {
+ return -1;
+ } else {
+ new->len=sizeof(struct palacios_host_dev_host_request_response)+data_size;
+ if (copy) {
+ memcpy(new->data,(*r)->data,(*r)->data_len-sizeof(struct palacios_host_dev_host_request_response));
+ new->data_len=(*r)->data_len;
+ kfree(*r);
+ }
+ *r=new;
+ return 0;
+ }
+ }
+ }
+}
+
+static void cycle_request_response(struct palacios_host_device_user *dev)
+{
+ // wake up user side so that polls fall through
+ wake_up_interruptible(&(dev->user_wait_queue));
+ // put us to sleep until the user side wakes us up
+ wait_event_interruptible((dev->host_wait_queue), (dev->waiting==0));
+}
+
+static void cycle_response_request(struct palacios_host_device_user *dev)
+{
+ // wake up host side
+ wake_up_interruptible(&(dev->host_wait_queue));
+}
+
+
+/*********************************************************************************************
+
+ Interface to user space
+
+ *********************************************************************************************/
+
+
+
+static unsigned int host_dev_poll(struct file * filp,
+ struct poll_table_struct * poll_tb)
+{
+
+ struct palacios_host_device_user * dev = filp->private_data;
+ unsigned long f;
+
+ if (!dev->connected) {
+ return -EFAULT;
+ }
+
+ spin_lock_irqsave(&(dev->lock),f);
+
+ if (dev->waiting) {
+ // Yes, we have a request if you want it!
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return POLLIN | POLLRDNORM;
+ }
+
+ // No request yet, so we need to wait for one to show up.
+
+ // register ourselves on the user wait queue
+ poll_wait(filp, &(dev->user_wait_queue), poll_tb);
+
+ spin_unlock_irqrestore(&(dev->lock),f);
+
+ // We will get called again when that queue is woken up
+
+ return 0;
+}
+
+
+static int host_dev_release(struct inode * i, struct file * filp)
+{
+ struct palacios_host_device_user *dev = filp->private_data;
+ unsigned long f;
+
+ printk("palacios: user side is closing host device \"%s\"\n",dev->url);
+
+ spin_lock_irqsave(&(dev->lock), f);
+ dev->connected = 0;
+ spin_unlock_irqrestore(&(dev->lock), f);
+
+ // it is the palacios->host interface's responsibility to ignore
+ // reads/writes until connected is true
+
+ return 0;
+}
+
+static int host_dev_ioctl(struct inode *ip, struct file *fp, unsigned int val, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+
+ struct palacios_host_device_user *dev = fp->private_data;
+
+ if (!dev->connected) {
+ return -EFAULT;
+ }
+
+ switch (val) {
+ case V3_HOST_DEV_USER_REQUEST_PUSH_IOCTL: {
+
+ struct palacios_host_dev_user_op op;
+
+ if (copy_from_user(&op,argp,sizeof(struct palacios_host_dev_user_op))) {
+ printk("palacios: unable to copy from user for host device \"%s\"\n",dev->url);
+ return -EFAULT;
+ }
+
+ switch (op.type) {
+ case PALACIOS_HOST_DEV_USER_REQUEST_READ_GUEST: {
+ void *temp = kmalloc(op.len,GFP_KERNEL);
+
+ if (!temp) {
+ printk("palacios: unable to allocate enough for read guest request for host device \"%s\"\n",dev->url);
+ return -EFAULT;
+ }
+
+ if (v3_host_dev_read_guest_mem(dev->guestdev,
+ dev,
+ op.gpa,
+ temp,
+ op.len) != op.len) {
+ printk("palacios: unable to read enough from guest for host device \"%s\"\n",dev->url);
+ kfree(temp);
+ return -EFAULT;
+ }
+
+ if (copy_to_user(op.data,temp,op.len)) {
+ printk("palacios: unable to copy to user for host device \"%s\"\n",dev->url);
+ kfree(temp);
+ return -EFAULT;
+ }
+
+ kfree(temp);
+
+ return op.len;
+ }
+ break;
+
+
+ case PALACIOS_HOST_DEV_USER_REQUEST_WRITE_GUEST: {
+
+ void *temp = kmalloc(op.len,GFP_KERNEL);
+
+ if (!temp) {
+ printk("palacios: unable to allocate enough for write guest request for host device \"%s\"\n",dev->url);
+ return -EFAULT;
+ }
+
+ if (copy_from_user(temp,op.data,op.len)) {
+ printk("palacios: unable to copy from user for host device \"%s\"\n",dev->url);
+ kfree(temp);
+ return -EFAULT;
+ }
+
+ if (v3_host_dev_write_guest_mem(dev->guestdev,
+ dev,
+ op.gpa,
+ temp,
+ op.len) != op.len) {
+ printk("palacios: unable to write enough to guest for host device \"%s\"\n",dev->url);
+ kfree(temp);
+ return -EFAULT;
+ }
+
+ kfree(temp);
+
+ return op.len;
+ }
+ break;
+
+ case PALACIOS_HOST_DEV_USER_REQUEST_IRQ_GUEST: {
+
+ return v3_host_dev_raise_irq(dev->guestdev, dev, op.irq);
+ }
+ break;
+
+ default:
+ printk("palacios: unknown user request to host device \"%s\"\n",dev->url);
+ return -EFAULT;
+ break;
+ }
+ }
+ break;
+
+ case V3_HOST_DEV_HOST_REQUEST_SIZE_IOCTL: {
+
+ unsigned long f;
+
+ spin_lock_irqsave(&(dev->lock),f);
+
+ if (!(dev->waiting)) {
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return 0; // no request available now
+ }
+
+ if (copy_to_user(argp,&(dev->req->data_len),sizeof(uint64_t))) {
+ printk("palacios: unable to copy to user for host device \"%s\"\n",dev->url);
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return -EFAULT; // failed to copy!
+ }
+
+ spin_unlock_irqrestore(&(dev->lock),f);
+
+ return 1; // have request for you
+
+ }
+
+ break;
+
+ case V3_HOST_DEV_HOST_REQUEST_PULL_IOCTL: {
+
+ unsigned long f;
+
+ spin_lock_irqsave(&(dev->lock),f);
+
+ if (!(dev->waiting)) {
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return 0; // no request available now
+ }
+
+ if (copy_to_user(argp,dev->req,dev->req->data_len)) {
+ printk("palacios: unable to copy to user for host device \"%s\"\n",dev->url);
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return -EFAULT; // failed to copy!
+ }
+
+ spin_unlock_irqrestore(&(dev->lock),f);
+
+ return 1; // copied request for you
+ }
+ break;
+
+ case V3_HOST_DEV_USER_RESPONSE_PUSH_IOCTL: {
+
+ unsigned long f;
+ uint64_t user_datalen;
+
+ spin_lock_irqsave(&(dev->lock),f);
+
+ if (!(dev->waiting)) {
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return 0; // no request outstanding, so we do not need a response!
+ }
+
+ if (copy_from_user(&user_datalen,argp,sizeof(uint64_t))) {
+ printk("palacios: unable to copy from user for host device \"%s\"\n",dev->url);
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return -EFAULT; // failed to copy!
+ }
+
+ if (palacios_resize_reqresp(&(dev->resp),user_datalen,0)) {
+ printk("palacios: unable to resize to accept request of size %llu from user for host device \"%s\"\n",user_datalen,dev->url);
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return -EFAULT;
+ }
+
+ if (copy_from_user(dev->resp, argp, user_datalen)) {
+ printk("palacios: unable to copy from user for host device \"%s\"\n",dev->url);
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return -EFAULT; // failed to copy!
+ }
+
+ // now have valid response!
+ dev->waiting=0;
+
+ // wake the palacios side up so that it sees it
+ cycle_response_request(dev);
+
+ spin_unlock_irqrestore(&(dev->lock),f);
+
+ return 1; // done
+ }
+ break;
+
+ default:
+ printk("palacios: unknown ioctl for host device \"%s\"\n",dev->url);
+ return -EFAULT;
+ break;
+ }
+
+}
+
+
+static struct file_operations host_dev_fops = {
+ .poll = host_dev_poll,
+ .release = host_dev_release,
+ .ioctl = host_dev_ioctl,
+};
+
+
+
+
+int connect_host_dev(struct v3_guest * guest, char *url)
+{
+ struct palacios_host_device_user *dev;
+ unsigned long f1, f2;
+ int i;
+
+ // currently only support user: types:
+ if (strncasecmp(url,"user:",5)) {
+ printk("palacios: do not currently support host devices of type in \"%s\"\n",url);
+ return -1;
+ }
+
+ printk("palacios: attempting to rendezvous with palacios side of host device \"%s\"\n",url);
+
+ // We will scan the list looking for the relevant
+ // URL. If we don't find it after a while, we give up
+
+ for (i=0;i<RENDEZVOUS_WAIT_SECS/RENDEZVOUS_RETRY_SECS;i++) {
+ spin_lock_irqsave(&(guest->hostdev.lock),f1);
+ list_for_each_entry(dev,&(guest->hostdev.devs), node) {
+ if (!strncasecmp(url,dev->url,MAX_URL)) {
+ // found it
+ spin_lock_irqsave(&(dev->lock),f2);
+ if (dev->connected) {
+ printk("palacios: device for \"%s\" is already connected!\n",url);
+ spin_unlock_irqrestore(&(dev->lock),f2);
+ spin_unlock_irqrestore(&(guest->hostdev.lock),f1);
+ return -1;
+ } else {
+ dev->fd = anon_inode_getfd("v3-hostdev", &host_dev_fops, dev, 0);
+ if (dev->fd<0) {
+ printk("palacios: cannot create fd for device \"%s\"\n",url);
+ spin_unlock_irqrestore(&(dev->lock),f2);
+ spin_unlock_irqrestore(&(guest->hostdev.lock),f1);
+ return -1;
+ }
+ dev->connected=1;
+ dev->waiting=0;
+ if (dev->req) {
+ kfree(dev->req);
+ dev->req=0;
+ }
+ if (dev->resp) {
+ kfree(dev->resp);
+ dev->resp=0;
+ }
+ printk("palacios: connected fd for device \"%s\"\n",url);
+ spin_unlock_irqrestore(&(dev->lock),f2);
+ spin_unlock_irqrestore(&(guest->hostdev.lock),f1);
+ return dev->fd;
+ }
+ spin_unlock_irqrestore(&(dev->lock),f2);
+ }
+ }
+ spin_unlock_irqrestore(&(guest->hostdev.lock),f1);
+
+ ssleep(RENDEZVOUS_RETRY_SECS);
+ }
+
+ printk("palacios: timeout waiting for connection for device \"%s\"",url);
+
+ return -1;
+
+}
+
+
+
+
+
+
+
+
+/***************************************************************************************
+
+ Following this is the implementation of the palacios->host interface
+
+**************************************************************************************/
+
+static v3_host_dev_t palacios_host_dev_open(char *url,
+ v3_bus_class_t bus,
+ v3_guest_dev_t gdev,
+ void *host_priv_data)
+{
+ struct v3_guest *guest= (struct v3_guest*)host_priv_data;
+ struct palacios_host_device_user *dev;
+ unsigned long f1,f2;
+ int i;
+
+ /*
+ I will create the device in the list and then wait
+ for the user side to attach
+ */
+
+
+ if (strncasecmp(url,"user:",5)) {
+ printk("palacios: do not currently support devices of type in \"%s\"\n",url);
+ return NULL;
+ }
+
+ // Check to see if a device of this url already exists, which would be ugly
+ spin_lock_irqsave(&(guest->hostdev.lock),f1);
+ list_for_each_entry(dev,&(guest->hostdev.devs), node) {
+ if (!strncasecmp(url,dev->url,MAX_URL)) {
+ // found it
+ spin_unlock_irqrestore(&(guest->hostdev.lock),f1);
+ printk("palacios: a host device with url \"%s\" already exists in the guest!\n",url);
+ return NULL;
+ }
+ }
+ spin_unlock_irqrestore(&(guest->hostdev.lock),f1);
+
+
+ printk("palacios: creating host device \"%s\"\n",url);
+
+ dev = kmalloc(sizeof(struct palacios_host_device_user),GFP_KERNEL);
+
+ if (!dev) {
+ printk("palacios: cannot allocate for host device \"%s\"\n",url);
+ return NULL;
+ }
+
+ memset(dev,0,sizeof(struct palacios_host_device_user));
+
+ strncpy(dev->url,url,MAX_URL);
+
+ dev->guestdev=gdev;
+
+ dev->guest=guest;
+
+ spin_lock_init(&(dev->lock));
+
+ init_waitqueue_head(&(dev->user_wait_queue));
+ init_waitqueue_head(&(dev->host_wait_queue));
+
+ printk("palacios: attempting to rendezvous with user side of host device \"%s\"\n",url);
+
+ // Insert ourselves into the list
+ spin_lock_irqsave(&(guest->hostdev.lock),f1);
+ list_add(&(dev->node),&(guest->hostdev.devs));
+ spin_unlock_irqrestore(&(guest->hostdev.lock),f1);
+
+
+ // Now wait until we are noticed!
+ for (i=0;i<RENDEZVOUS_WAIT_SECS/RENDEZVOUS_RETRY_SECS;i++) {
+ spin_lock_irqsave(&(dev->lock),f2);
+ if (dev->connected){
+ printk("palacios: connection with user side established for host device \"%s\" fd=%d\n",dev->url,dev->fd);
+ spin_unlock_irqrestore(&(dev->lock),f2);
+ return dev;
+ }
+ spin_unlock_irqrestore(&(dev->lock),f2);
+ ssleep(RENDEZVOUS_RETRY_SECS);
+ }
+
+ printk("palacios: timeout waiting for user side to connect to host device \"%s\"",url);
+
+ // get us out of the list
+ spin_lock_irqsave(&(guest->hostdev.lock),f1);
+ list_del(&(dev->node));
+ spin_unlock_irqrestore(&(guest->hostdev.lock),f1);
+
+ palacios_host_dev_user_free(dev);
+
+ return NULL;
+}
+
+static int palacios_host_dev_close(v3_host_dev_t hostdev)
+{
+ unsigned long f1, f2;
+
+ struct palacios_host_device_user *dev = (struct palacios_host_device_user *) hostdev;
+
+ printk("palacios: closing host device \"%s\"\n",dev->url);
+
+ spin_lock_irqsave(&(dev->guest->hostdev.lock),f1);
+
+ spin_lock_irqsave(&(dev->lock),f2);
+
+ if (dev->connected) {
+ dev->connected=0;
+ // After this, any user side request will return -EFAULT
+ }
+
+ list_del(&(dev->node));
+
+ spin_unlock_irqrestore(&(dev->lock),f2);
+ spin_unlock_irqrestore(&(dev->guest->hostdev.lock),f1);
+
+ palacios_host_dev_user_free(dev);
+
+ return 0;
+}
+
+
+
+
+static uint64_t palacios_host_dev_read_io(v3_host_dev_t hostdev,
+ uint16_t port,
+ void *dest,
+ uint64_t len)
+{
+ struct palacios_host_device_user *dev = (struct palacios_host_device_user *)hostdev;
+ unsigned long f;
+ uint64_t op_len;
+
+ spin_lock_irqsave(&(dev->lock),f);
+
+ if (dev->waiting) {
+ printk("palacios: guest issued i/o read request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected);
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return 0;
+ }
+
+ if (!dev->connected) {
+ printk("palacios: ignoring request as user side is not connected for host device \"%s\"\n",dev->url);
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return 0;
+ }
+
+ // resize request and response in case they will need it
+ palacios_resize_reqresp(&(dev->req),0,0); // request doesn't carry data
+
+ dev->req->type=PALACIOS_HOST_DEV_HOST_REQUEST_READ_IO;
+ dev->req->port=port;
+ dev->req->op_len=len;
+ dev->req->gpa=0;
+ dev->req->conf_addr=0;
+ dev->req->data_len=sizeof(struct palacios_host_dev_host_request_response);
+
+ dev->waiting=1;
+
+ spin_unlock_irqrestore(&(dev->lock),f);
+
+ // hand over to the user space and wait for it to respond
+ cycle_request_response(dev);
+
+ // We're back! So now we'll hand the response back to Palacios
+
+ spin_lock_irqsave(&(dev->lock),f);
+
+ op_len = dev->resp->op_len < len ? dev->resp->op_len : len ;
+
+ memcpy(dest,dev->resp->data, op_len);
+
+ spin_unlock_irqrestore(&(dev->lock),f);
+
+ return op_len;
+}
+
+static uint64_t palacios_host_dev_read_mem(v3_host_dev_t hostdev,
+ void * gpa,
+ void *dest,
+ uint64_t len)
+{
+ struct palacios_host_device_user *dev = (struct palacios_host_device_user *)hostdev;
+ unsigned long f;
+ uint64_t op_len;
+
+ spin_lock_irqsave(&(dev->lock),f);
+
+ if (dev->waiting) {
+ printk("palacios: guest issued memory read request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected);
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return 0;
+ }
+ if (!dev->connected) {
+ printk("palacios: ignoring request as user side is not connected for host device \"%s\"\n",dev->url);
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return 0;
+ }
+
+ // resize request and response in case they will need it
+ palacios_resize_reqresp(&(dev->req),0,0); // request doesn't carry data
+
+ dev->req->type=PALACIOS_HOST_DEV_HOST_REQUEST_READ_MEM;
+ dev->req->port=0;
+ dev->req->op_len=len;
+ dev->req->gpa=gpa;
+ dev->req->conf_addr=0;
+ dev->req->data_len=sizeof(struct palacios_host_dev_host_request_response);
+
+ dev->waiting=1;
+
+ spin_unlock_irqrestore(&(dev->lock),f);
+
+ // hand over to the user space and wait for it to respond
+ cycle_request_response(dev);
+
+ // We're back! So now we'll hand the response back to Palacios
+
+ spin_lock_irqsave(&(dev->lock),f);
+
+ op_len = dev->resp->op_len < len ? dev->resp->op_len : len ;
+
+ memcpy(dest,dev->resp->data, op_len);
+
+ spin_unlock_irqrestore(&(dev->lock),f);
+
+ return op_len;
+}
+
+static uint64_t palacios_host_dev_read_conf(v3_host_dev_t hostdev,
+ uint64_t offset,
+ void *dest,
+ uint64_t len)
+{
+ struct palacios_host_device_user *dev = (struct palacios_host_device_user *)hostdev;
+ unsigned long f;
+ uint64_t op_len;
+
+ spin_lock_irqsave(&(dev->lock),f);
+
+ if (dev->waiting) {
+ printk("palacios: guest issued config read request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected);
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return 0;
+ }
+ if (!dev->connected) {
+ printk("palacios: ignoring request as user side is not connected for host device \"%s\"\n",dev->url);
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return 0;
+ }
+
+ // resize request and response in case they will need it
+ palacios_resize_reqresp(&(dev->req),0,0); // request doesn't carry data
+
+ dev->req->type=PALACIOS_HOST_DEV_HOST_REQUEST_READ_CONF;
+ dev->req->port=0;
+ dev->req->op_len=len;
+ dev->req->gpa=0;
+ dev->req->conf_addr=offset;
+ dev->req->data_len=sizeof(struct palacios_host_dev_host_request_response);
+
+ dev->waiting=1;
+
+ spin_unlock_irqrestore(&(dev->lock),f);
+
+ // hand over to the user space and wait for it to respond
+ cycle_request_response(dev);
+
+ // We're back! So now we'll hand the response back to Palacios
+
+ spin_lock_irqsave(&(dev->lock),f);
+
+ op_len = dev->resp->op_len < len ? dev->resp->op_len : len ;
+
+ memcpy(dest,dev->resp->data, op_len);
+
+ spin_unlock_irqrestore(&(dev->lock),f);
+
+ return op_len;
+}
+
+
+static uint64_t palacios_host_dev_write_io(v3_host_dev_t hostdev,
+ uint16_t port,
+ void *src,
+ uint64_t len)
+{
+ struct palacios_host_device_user *dev = (struct palacios_host_device_user *)hostdev;
+ unsigned long f;
+ uint64_t op_len;
+
+ spin_lock_irqsave(&(dev->lock),f);
+
+ if (dev->waiting) {
+ printk("palacios: guest issued i/o write request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected);
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return 0;
+ }
+ if (!dev->connected) {
+ printk("palacios: ignoring request as user side is not connected for host device \"%s\"\n",dev->url);
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return 0;
+ }
+
+ // resize request and response in case they will need it
+ palacios_resize_reqresp(&(dev->req),len,0); // make room for data
+
+ dev->req->type=PALACIOS_HOST_DEV_HOST_REQUEST_WRITE_IO;
+ dev->req->port=port;
+ dev->req->op_len=len;
+ dev->req->gpa=0;
+ dev->req->conf_addr=0;
+ dev->req->data_len=sizeof(struct palacios_host_dev_host_request_response)+len;
+
+ memcpy(dev->req->data,src,len);
+
+ dev->waiting=1;
+
+ spin_unlock_irqrestore(&(dev->lock),f);
+
+ // hand over to the user space and wait for it to respond
+ cycle_request_response(dev);
+
+ // We're back! So now we'll hand the response back to Palacios
+
+ spin_lock_irqsave(&(dev->lock),f);
+
+ op_len = dev->resp->op_len < len ? dev->resp->op_len : len ;
+
+ spin_unlock_irqrestore(&(dev->lock),f);
+
+ return op_len;
+}
+
+
+static uint64_t palacios_host_dev_write_mem(v3_host_dev_t hostdev,
+ void * gpa,
+ void *src,
+ uint64_t len)
+{
+ struct palacios_host_device_user *dev = (struct palacios_host_device_user *)hostdev;
+ unsigned long f;
+ uint64_t op_len;
+
+ spin_lock_irqsave(&(dev->lock),f);
+
+ if (dev->waiting) {
+ printk("palacios: guest issued memory write request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected);
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return 0;
+ }
+ if (!dev->connected) {
+ printk("palacios: ignoring request as user side is not connected for host device \"%s\"\n",dev->url);
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return 0;
+ }
+
+ // resize request and response in case they will need it
+ palacios_resize_reqresp(&(dev->req),len,0); // make room for data
+
+ dev->req->type=PALACIOS_HOST_DEV_HOST_REQUEST_WRITE_MEM;
+ dev->req->port=0;
+ dev->req->op_len=len;
+ dev->req->gpa=gpa;
+ dev->req->conf_addr=0;
+ dev->req->data_len=sizeof(struct palacios_host_dev_host_request_response)+len;
+
+ memcpy(dev->req->data,src,len);
+
+ dev->waiting=1;
+
+ spin_unlock_irqrestore(&(dev->lock),f);
+
+ // hand over to the user space and wait for it to respond
+ cycle_request_response(dev);
+
+ // We're back! So now we'll hand the response back to Palacios
+
+ spin_lock_irqsave(&(dev->lock),f);
+
+ op_len= dev->resp->op_len < len ? dev->resp->op_len : len ;
+
+ spin_unlock_irqrestore(&(dev->lock),f);
+
+ return op_len;
+}
+
+
+static int palacios_host_dev_ack_irq(v3_host_dev_t hostdev, uint8_t irq)
+{
+ // we don't care
+ return 0;
+}
+
+
+
+static uint64_t palacios_host_dev_write_conf(v3_host_dev_t hostdev,
+ uint64_t offset,
+ void *src,
+ uint64_t len)
+{
+ struct palacios_host_device_user *dev = (struct palacios_host_device_user *)hostdev;
+ unsigned long f;
+ uint64_t op_len;
+
+ spin_lock_irqsave(&(dev->lock),f);
+
+ if (dev->waiting) {
+ printk("palacios: guest issued config write request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected);
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return 0;
+ }
+ if (!dev->connected) {
+ printk("palacios: ignoring request as user side is not connected for host device \"%s\"\n",dev->url);
+ spin_unlock_irqrestore(&(dev->lock),f);
+ return 0;
+ }
+
+ // resize request and response in case they will need it
+ palacios_resize_reqresp(&(dev->req),len,0); // make room for data
+
+ dev->req->type=PALACIOS_HOST_DEV_HOST_REQUEST_WRITE_CONF;
+ dev->req->port=0;
+ dev->req->op_len=len;
+ dev->req->gpa=0;
+ dev->req->conf_addr=offset;
+ dev->req->data_len=sizeof(struct palacios_host_dev_host_request_response)+len;
+
+ memcpy(dev->req->data,src,len);
+
+ dev->waiting=1;
+
+ spin_unlock_irqrestore(&(dev->lock),f);
+
+ // hand over to the user space and wait for it to respond
+ cycle_request_response(dev);
+
+ // We're back! So now we'll hand the response back to Palacios
+
+ spin_lock_irqsave(&(dev->lock),f);
+
+ op_len = dev->resp->op_len < len ? dev->resp->op_len : len ;
+
+ spin_unlock_irqrestore(&(dev->lock),f);
+
+ return op_len;
+ }
+
+
+
+
+
+
+static struct v3_host_dev_hooks palacios_host_dev_hooks = {
+ .open = palacios_host_dev_open,
+ .close = palacios_host_dev_close,
+ .read_io = palacios_host_dev_read_io,
+ .write_io = palacios_host_dev_write_io,
+ .read_mem = palacios_host_dev_read_mem,
+ .write_mem = palacios_host_dev_write_mem,
+ .read_config = palacios_host_dev_read_conf,
+ .write_config = palacios_host_dev_write_conf,
+ .ack_irq = palacios_host_dev_ack_irq,
+};
+
+
+
+int palacios_init_host_dev( void ) {
+ V3_Init_Host_Device_Support(&palacios_host_dev_hooks);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Palacios Host Device Interface + User-space interface
+ * (c) Peter Dinda, 2011
+ */
+
+#ifndef __PALACIOS_HOST_DEV_H__
+#define __PALACIOS_HOST_DEV_H__
+
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include "palacios-host-dev-user.h"
+
+struct v3_guest;
+
+/*
+ This is the collection of host devices that
+ a single guest has
+*/
+struct palacios_host_dev {
+ spinlock_t lock;
+ struct list_head devs;
+};
+
+
+
+int connect_host_dev(struct v3_guest * guest, char *url);
+
+int palacios_init_host_dev( void );
+
+
+#endif
--- /dev/null
+#include "palacios.h"
+#include "palacios-keyed-stream.h"
+#include "palacios-hashtable.h"
+
+#define sint64_t int64_t
+#include <interfaces/vmm_keyed_stream.h>
+
+/*
+ Streams are stored in a hash table
+ The values for this hash table are hash tables associted with
+ each stream. A keyed stream for a "mem:" stream is
+ an instance of the structure given here
+*/
+
+#define DEF_NUM_STREAMS 16
+#define DEF_NUM_KEYS 128
+#define DEF_SIZE 128
+
+struct mem_stream {
+ char *data;
+ uint32_t size;
+ uint32_t data_max;
+ uint32_t ptr;
+};
+
+static struct mem_stream *create_mem_stream(void)
+{
+ struct mem_stream *m = kmalloc(sizeof(struct mem_stream),GFP_KERNEL);
+
+ if (!m) {
+ return 0;
+ }
+
+ m->data = kmalloc(DEF_SIZE,GFP_KERNEL);
+
+ if (!m->data) {
+ kfree(m);
+ return 0;
+ }
+
+ m->size=DEF_SIZE;
+ m->ptr=0;
+ m->data_max=0;
+
+ return m;
+}
+
+static void destroy_mem_stream(struct mem_stream *m)
+{
+ if (m) {
+ if (m->data) {
+ kfree(m->data);
+ }
+ m->data=0;
+ kfree(m);
+ }
+}
+
+static int expand_mem_stream(struct mem_stream *m, uint32_t new_size)
+{
+ void *data = kmalloc(new_size,GFP_KERNEL);
+ uint32_t nc;
+
+ if (!data) {
+ return -1;
+ }
+
+ nc = (new_size<m->data_max) ? new_size : m->data_max;
+
+ memcpy(data,m->data,nc);
+
+ kfree(m->data);
+
+ m->data=data;
+ m->size=new_size;
+ if (m->size<m->data_max) {
+ m->data_max=m->size;
+ }
+
+ return 0;
+}
+
+static uint32_t write_mem_stream(struct mem_stream *m,
+ void *data,
+ uint32_t len)
+{
+ if ((m->ptr + len) > m->size) {
+ if (expand_mem_stream(m,m->ptr + len)) {
+ return 0;
+ }
+ }
+ memcpy(m->data+m->ptr,data,len);
+ m->ptr+=len;
+ m->data_max=m->ptr;
+
+ return len;
+
+}
+
+static uint32_t read_mem_stream(struct mem_stream *m,
+ void *data,
+ uint32_t len)
+{
+ if ((m->ptr + len) > m->data_max) {
+ return 0;
+ }
+ memcpy(data,m->data+m->ptr,len);
+ m->ptr+=len;
+
+ return len;
+
+}
+
+
+static void reset_mem_stream(struct mem_stream *m)
+{
+ m->ptr=0;
+}
+
+
+static inline uint_t hash_func(addr_t key)
+{
+ return palacios_hash_buffer((uchar_t*)key,strlen((uchar_t*)key));
+}
+
+static inline int hash_comp(addr_t k1, addr_t k2)
+{
+ return strcasecmp((char*)k1,(char*)k2)==0;
+}
+
+
+// This stores all the streams
+static struct hashtable *streams=0;
+
+
+static v3_keyed_stream_t open_stream(char *url,
+ v3_keyed_stream_open_t ot)
+{
+ if (strncasecmp(url,"mem:",4)) {
+ printk("Only in-memory streams are currently supported\n");
+ return 0;
+ }
+
+ switch (ot) {
+ case V3_KS_RD_ONLY:
+ case V3_KS_WR_ONLY:
+ return (v3_keyed_stream_t) palacios_htable_search(streams,(addr_t)(url+4));
+ break;
+ case V3_KS_WR_ONLY_CREATE: {
+ struct hashtable *s = (struct hashtable *) palacios_htable_search(streams,(addr_t)(url+4));
+
+ if (!s) {
+ s = palacios_create_htable(DEF_NUM_KEYS,hash_func,hash_comp);
+ if (!s) {
+ printk("Cannot allocate in-memory keyed stream %s\n",url);
+ return 0;
+ }
+ if (!palacios_htable_insert(streams,(addr_t)(url+4),(addr_t)s)) {
+ printk("Cannot insert in-memory keyed stream %s\n",url);
+ return 0;
+ }
+ }
+
+ return s;
+
+ }
+
+ break;
+ }
+
+ return 0;
+
+}
+
+
+static void close_stream(v3_keyed_stream_t stream)
+{
+ // nothing to do
+ return;
+}
+
+static v3_keyed_stream_key_t open_key(v3_keyed_stream_t stream,
+ char *key)
+{
+ struct hashtable *s = (struct hashtable *) stream;
+
+ struct mem_stream *m;
+
+ m = (struct mem_stream *) palacios_htable_search(s,(addr_t)key);
+
+ if (!m) {
+ m = create_mem_stream();
+
+ if (!m) {
+ printk("Cannot allocate keyed stream for key %s\n",key);
+ return 0;
+ }
+
+ if (!palacios_htable_insert(s,(addr_t)key,(addr_t)m)) {
+ printk("Cannot insert keyed stream for key %s\n",key);
+ destroy_mem_stream(m);
+ return 0;
+ }
+ }
+
+ reset_mem_stream(m);
+ return m;
+
+}
+
+static void close_key(v3_keyed_stream_t stream,
+ v3_keyed_stream_key_t key)
+{
+ // nothing to do
+ return;
+}
+
+static sint64_t write_key(v3_keyed_stream_t stream,
+ v3_keyed_stream_key_t key,
+ void *buf,
+ sint64_t len)
+{
+ struct mem_stream *m = (struct mem_stream *) key;
+ uint32_t mylen;
+ uint32_t writelen;
+
+ if (len<0) {
+ return len;
+ }
+
+ mylen = (uint32_t) len;
+
+ writelen=write_mem_stream(m,buf,mylen);
+
+ if (writelen!=mylen) {
+ printk("Failed to write all data for key\n");
+ return -1;
+ } else {
+ return (sint64_t)writelen;
+ }
+}
+
+static sint64_t read_key(v3_keyed_stream_t stream,
+ v3_keyed_stream_key_t key,
+ void *buf,
+ sint64_t len)
+{
+ struct mem_stream *m = (struct mem_stream *) key;
+ uint32_t mylen;
+ uint32_t readlen;
+
+ if (len<0) {
+ return len;
+ }
+
+ mylen = (uint32_t) len;
+
+ readlen=read_mem_stream(m,buf,mylen);
+
+ if (readlen!=mylen) {
+ printk("Failed to read all data for key\n");
+ return -1;
+ } else {
+ return (sint64_t)readlen;
+ }
+}
+
+static struct v3_keyed_stream_hooks hooks = {
+ .open = open_stream,
+ .close = close_stream,
+ .open_key = open_key,
+ .close_key = close_key,
+ .read_key = read_key,
+ .write_key = write_key
+};
+
+
+int palacios_init_keyed_streams()
+{
+ streams = palacios_create_htable(DEF_NUM_STREAMS,hash_func,hash_comp);
+
+ if (!streams) {
+ printk("Failed to allocated stream pool\n");
+ return -1;
+ }
+
+ V3_Init_Keyed_Streams(&hooks);
+
+ return 0;
+
+}
+
+int palacios_deinit_keyed_streams()
+{
+ printk("DEINIT OF PALACIOS KEYED STREAMS NOT IMPLEMENTED - WE HAVE JUST LEAKED MEMORY!\n");
+ return -1;
+}
--- /dev/null
+/*
+ * Palacios VM Keyed Stream Interface (for checkpoint/restore)
+ * Copyright (c) 2011 Peter Dinda <pdinda@northwestern.edu>
+ */
+
+#ifndef __PALACIOS_KEYED_STREAM_H__
+#define __PALACIOS_KEYED_STREAM_H__
+
+
+
+int palacios_init_keyed_streams(void);
+int palacios_deinit_keyed_streams(void);
+
+
+#endif
#include "palacios-inspector.h"
#endif
+#ifdef V3_CONFIG_GRAPHICS_CONSOLE
+#include "palacios-graphics-console.h"
+#endif
+
+#ifdef V3_CONFIG_HOST_DEVICE
+#include "palacios-host-dev.h"
+#endif
extern struct class * v3_class;
#define STREAM_NAME_LEN 128
break;
}
+ case V3_VM_HOST_DEV_CONNECT: {
+#ifdef V3_CONFIG_HOST_DEV
+ if (copy_from_user(host_dev_url, argp, HOST_DEV_URL_LEN)) {
+ printk("copy from user error getting url for host device connect...\n");
+ return -EFAULT;
+ }
+
+ return connect_host_dev(guest,host_dev_url);
+#else
+ printk("palacios: Host device support not available\n");
+ return -EFAULT;
+#endif
+ break;
+ }
+
+ case V3_VM_FB_INPUT:
+#ifdef V3_CONFIG_GRAPHICS_CONSOLE
+ return palacios_graphics_console_user_input(&(guest->graphics_console),
+ (struct v3_fb_input __user *) arg) ;
+#else
+ return -EFAULT;
+#endif
+ break;
+
+ case V3_VM_FB_QUERY:
+#ifdef V3_CONFIG_GRAPHICS_CONSOLE
+ return palacios_graphics_console_user_query(&(guest->graphics_console),
+ (struct v3_fb_query_response __user *) arg);
+#else
+ return -EFAULT;
+#endif
+ break;
+
+
default:
printk("\tUnhandled\n");
return -EINVAL;
#include "palacios-console.h"
#endif
+#ifdef V3_CONFIG_GRAPHICS_CONSOLE
+#include "palacios-graphics-console.h"
+#endif
+
+#ifdef V3_CONFIG_HOST_DEVICE
+#include "palacios-host-dev.h"
+#endif
+
+
/* Global Control IOCTLs */
#define V3_START_GUEST 10
#define V3_ADD_MEMORY 50
#define V3_VM_STREAM_CONNECT 21
#define V3_VM_STOP 22
+#define V3_VM_FB_INPUT 256+1
+#define V3_VM_FB_QUERY 256+2
+
+#define V3_VM_HOST_DEV_CONNECT 512+1
+
+
struct v3_guest_img {
unsigned long long size;
void * guest_data;
struct palacios_console console;
#endif
+#ifdef V3_CONFIG_CONSOLE
+ struct palacios_graphics_console graphics_console;
+#endif
+
+#ifdef V3_CONFIG_HOST_DEVICE
+ struct palacios_host_dev hostdev;
+#endif
+
+
struct completion start_done;
struct completion thread_done;
-all: v3_ctrl v3_stop v3_cons v3_mem v3_monitor v3_serial v3_net
+all: v3_ctrl v3_stop v3_cons v3_mem v3_monitor v3_serial v3_net v3_user_host_dev_example
+
v3_ctrl : v3_ctrl.c v3_ctrl.h
v3_net : v3_net.c v3_ctrl.h
gcc -static v3_net.c -o v3_net
+v3_user_host_dev_example: v3_user_host_dev_example.c v3_user_host_dev.h v3_user_host_dev.c
+ gcc -static -I../linux_module v3_user_host_dev_example.c v3_user_host_dev.c -o v3_user_host_dev_example
+
clean:
rm -f v3_ctrl v3_cons v3_mem v3_monitor v3_serial v3_net
--- /dev/null
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <malloc.h>
+
+#include "v3_user_host_dev.h"
+
+
+int v3_user_host_dev_rendezvous(char *vmdev, char *url)
+{
+ int vmfd;
+ int devfd;
+
+ if ((vmfd=open(vmdev,O_RDWR))<0) {
+ return -1;
+ }
+
+ devfd = ioctl(vmfd,V3_VM_HOST_DEV_CONNECT,url);
+
+ close(vmfd);
+
+ return devfd;
+
+}
+int v3_user_host_dev_depart(int devfd)
+{
+ return close(devfd);
+}
+
+
+int v3_user_host_dev_have_request(int devfd)
+{
+ uint64_t len;
+
+ return ioctl(devfd,V3_HOST_DEV_HOST_REQUEST_SIZE_IOCTL,&len)==1;
+}
+
+int v3_user_host_dev_pull_request(int devfd, struct palacios_host_dev_host_request_response **req)
+{
+ uint64_t len;
+ int rc;
+
+ rc=ioctl(devfd,V3_HOST_DEV_HOST_REQUEST_SIZE_IOCTL,&len);
+
+ if (rc<=0) {
+ return -1;
+ } else {
+ struct palacios_host_dev_host_request_response *r = malloc(len);
+
+ rc=ioctl(devfd, V3_HOST_DEV_HOST_REQUEST_PULL_IOCTL,r);
+
+ if (rc<=0) {
+ free(r);
+ return -1;
+ } else {
+ *req=r;
+ return 0;
+ }
+ }
+}
+
+
+int v3_user_host_dev_push_response(int devfd, struct palacios_host_dev_host_request_response *resp)
+{
+ int rc;
+
+ rc=ioctl(devfd, V3_HOST_DEV_USER_RESPONSE_PUSH_IOCTL,resp);
+
+ if (rc<=0) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+
+
+static uint64_t do_user(int devfd, struct palacios_host_dev_user_op *op)
+{
+ return ioctl(devfd, V3_HOST_DEV_USER_REQUEST_PUSH_IOCTL,op);
+}
+
+uint64_t v3_user_host_dev_read_guest_mem(int devfd, void *gpa, void *dest, uint64_t len)
+{
+ struct palacios_host_dev_user_op op;
+
+ op.type= PALACIOS_HOST_DEV_USER_REQUEST_READ_GUEST;
+ op.gpa=gpa;
+ op.data=dest;
+ op.len=len;
+ op.irq=0;
+
+ return do_user(devfd,&op);
+}
+
+uint64_t v3_user_host_dev_write_guest_mem(int devfd, void *gpa, void *src, uint64_t len)
+{
+ struct palacios_host_dev_user_op op;
+
+ op.type= PALACIOS_HOST_DEV_USER_REQUEST_WRITE_GUEST;
+ op.gpa=gpa;
+ op.data=src;
+ op.len=len;
+ op.irq=0;
+
+ return do_user(devfd,&op);
+}
+
+int v3_user_host_dev_inject_irq(int devfd, uint8_t irq)
+{
+ struct palacios_host_dev_user_op op;
+
+ op.type= PALACIOS_HOST_DEV_USER_REQUEST_IRQ_GUEST;
+ op.gpa=0;
+ op.data=0;
+ op.len=0;
+ op.irq=irq;
+
+ return do_user(devfd,&op);
+}
+
+
+
--- /dev/null
+#ifndef _V3_USER_HOST_DEV_
+#define _V3_USER_HOST_DEV_
+
+#include <stdint.h>
+#include "palacios-host-dev-user.h"
+
+int v3_user_host_dev_rendezvous(char *vmdev, char *url); // returns devfd for use in poll/select
+int v3_user_host_dev_depart(int devfd);
+
+int v3_user_host_dev_have_request(int devfd);
+int v3_user_host_dev_pull_request(int devfd, struct palacios_host_dev_host_request_response **req);
+int v3_user_host_dev_push_response(int devfd, struct palacios_host_dev_host_request_response *resp);
+
+uint64_t v3_user_host_dev_read_guest_mem(int devfd, void *gpa, void *dest, uint64_t len);
+uint64_t v3_user_host_dev_write_guest_mem(int devfd, void *gpa, void *src, uint64_t len);
+int v3_user_host_dev_inject_guest_irq(int devfd, uint8_t irq);
+
+#endif
+
--- /dev/null
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/select.h>
+#include <malloc.h>
+
+#include "v3_user_host_dev.h"
+
+void usage()
+{
+ fprintf(stderr,"v3_host_dev_example /dev/v3-vm0 user:mydev busywait|select\n");
+}
+
+
+int do_work(struct palacios_host_dev_host_request_response *req,
+ struct palacios_host_dev_host_request_response **resp)
+{
+ uint64_t datasize;
+
+ //
+ //
+ // Process request here, perhaps calling these functions:
+ //
+ // uint64_t v3_user_host_dev_read_guest_mem(int devfd, void *gpa, void *dest, uint64_t len);
+ // uint64_t v3_user_host_dev_write_guest_mem(int devfd, void *gpa, void *src, uint64_t len);
+ // int v3_user_host_dev_inject_guest_irq(int devfd, uint8_t irq);
+ //
+ // determine datasize - # bytes to include in response
+ //
+ // now built a response
+ *resp = malloc(sizeof(struct palacios_host_dev_host_request_response) + datasize);
+ (*resp)->data_len = sizeof(struct palacios_host_dev_host_request_response) + datasize;
+
+ //
+ // Fill out the fields of the response - notice that there are six kinds of things to response to:
+ // - read/write device port
+ // - read/write device mem
+ // - read/write device configuration space
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int devfd;
+ int mode=0;
+ char *vm, *url;
+
+ if (argc!=4) {
+ usage();
+ exit(-1);
+ }
+
+ vm=argv[1];
+ url=argv[2];
+ mode = argv[3][0]=='s';
+
+ fprintf(stderr,"Attempting to rendezvous with host device %s on vm %s\n", url, vm);
+
+ if ((devfd = v3_user_host_dev_rendezvous(vm,url))<0) {
+ perror("failed to rendezvous");
+ exit(-1);
+ }
+
+ fprintf(stderr,"Rendezvous succeeded, I will now operate in %s mode\n", mode==0 ? "busywait" : "select");
+
+ if (mode==0) {
+ //busywait
+
+ struct palacios_host_dev_host_request_response *req;
+ struct palacios_host_dev_host_request_response *resp;
+ uint64_t datasize;
+
+ while (1) {
+ while (!(v3_user_host_dev_have_request(devfd))) {
+ }
+ v3_user_host_dev_pull_request(devfd, &req);
+
+ do_work(req, &resp);
+
+ v3_user_host_dev_push_response(devfd, resp);
+
+ free(resp);
+ free(req);
+ }
+ } else {
+
+ struct palacios_host_dev_host_request_response *req;
+ struct palacios_host_dev_host_request_response *resp;
+ uint64_t datasize;
+ fd_set readset;
+ int rc;
+
+ // select-based operation so that you can wait for multiple things
+
+ while (1) {
+ FD_ZERO(&readset);
+ FD_SET(devfd,&readset);
+
+ rc = select(devfd+1, &readset, 0, 0, 0); // pick whatever you want to select on, just include devfd
+
+ if (rc>0) {
+ if (FD_ISSET(devfd,&readset)) {
+ // a request is read for us!
+ v3_user_host_dev_pull_request(devfd, &req);
+
+ do_work(req, &resp);
+
+ v3_user_host_dev_push_response(devfd, resp);
+
+ free(resp);
+ free(req);
+ }
+ }
+ }
+ }
+
+ v3_user_host_dev_depart(devfd);
+
+ return 0;
+
+}
v3_core_operating_mode_t core_run_state;
/* the logical cpu on which this core runs */
- uint32_t cpu_id;
+ uint32_t pcpu_id;
+
+ /* The virtual core # of this cpu (what the guest sees this core as) */
+ uint32_t vcpu_id;
};
#include <palacios/vmm_types.h>
+#include <palacios/vmm_queue.h>
+#include <palacios/vmm_lock.h>
+
+/* The locking in this file is nasty.
+ * There are 3 different locking approaches that are taken, depending on the APIC operation
+ * 1. Queue locks. Actual irq insertions are done via queueing irq ops at the dest apic.
+ * The destination apic's core is responsible for draining the queue, and actually
+ * setting the vector table.
+ * 2. State locks. This is a standard lock taken when internal apic state is read/written.
+ * When an irq's destination is determined this lock is taken to examine the apic's
+ * addressability.
+ * 3. VM barrier lock. This is taken when actual VM core state is changed (via SIPI).
+ */
+
#ifndef V3_CONFIG_DEBUG_APIC
struct v3_timer * timer;
+ v3_lock_t state_lock;
+ struct v3_queue irq_queue;
+
uint32_t eoi;
apic->spec_eoi.val = 0x00000000;
+ v3_init_queue(&(apic->irq_queue));
+
+
}
static int read_apic_msr(struct guest_info * core, uint_t msr, v3_msr_t * dst, void * priv_data) {
struct apic_dev_state * apic_dev = (struct apic_dev_state *)priv_data;
- struct apic_state * apic = &(apic_dev->apics[core->cpu_id]);
+ struct apic_state * apic = &(apic_dev->apics[core->vcpu_id]);
- PrintDebug("apic %u: core %u: MSR read\n", apic->lapic_id.val, core->cpu_id);
+ PrintDebug("apic %u: core %u: MSR read\n", apic->lapic_id.val, core->vcpu_id);
dst->value = apic->base_addr;
static int write_apic_msr(struct guest_info * core, uint_t msr, v3_msr_t src, void * priv_data) {
struct apic_dev_state * apic_dev = (struct apic_dev_state *)priv_data;
- struct apic_state * apic = &(apic_dev->apics[core->cpu_id]);
- struct v3_mem_region * old_reg = v3_get_mem_region(core->vm_info, core->cpu_id, apic->base_addr);
+ struct apic_state * apic = &(apic_dev->apics[core->vcpu_id]);
+ struct v3_mem_region * old_reg = v3_get_mem_region(core->vm_info, core->vcpu_id, apic->base_addr);
- PrintDebug("apic %u: core %u: MSR write\n", apic->lapic_id.val, core->cpu_id);
+ PrintDebug("apic %u: core %u: MSR write\n", apic->lapic_id.val, core->vcpu_id);
if (old_reg == NULL) {
// uh oh...
PrintError("apic %u: core %u: APIC Base address region does not exit...\n",
- apic->lapic_id.val, core->cpu_id);
+ apic->lapic_id.val, core->vcpu_id);
return -1;
}
apic->base_addr = src.value;
- if (v3_hook_full_mem(core->vm_info, core->cpu_id, apic->base_addr,
+ if (v3_hook_full_mem(core->vm_info, core->vcpu_id, apic->base_addr,
apic->base_addr + PAGE_SIZE_4KB,
apic_read, apic_write, apic_dev) == -1) {
PrintError("apic %u: core %u: Could not hook new APIC Base address\n",
- apic->lapic_id.val, core->cpu_id);
+ apic->lapic_id.val, core->vcpu_id);
return -1;
}
// irq_num is the bit offset into a 256 bit buffer...
-// return values
-// -1 = error
-// 0 = OK, no interrupt needed now
-// 1 = OK, interrupt needed now
static int activate_apic_irq(struct apic_state * apic, uint32_t irq_num) {
int major_offset = (irq_num & ~0x00000007) >> 3;
int minor_offset = irq_num & 0x00000007;
uint8_t flag = 0x1 << minor_offset;
- if (irq_num <= 15 || irq_num>255) {
+ if (irq_num <= 15 || irq_num > 255) {
PrintError("apic %u: core %d: Attempting to raise an invalid interrupt: %d\n",
- apic->lapic_id.val, apic->core->cpu_id, irq_num);
+ apic->lapic_id.val, apic->core->vcpu_id, irq_num);
return -1;
}
- PrintDebug("apic %u: core %d: Raising APIC IRQ %d\n", apic->lapic_id.val, apic->core->cpu_id, irq_num);
+ PrintDebug("apic %u: core %d: Raising APIC IRQ %d\n", apic->lapic_id.val, apic->core->vcpu_id, irq_num);
if (*req_location & flag) {
PrintDebug("Interrupt %d coallescing\n", irq_num);
return 1;
} else {
PrintDebug("apic %u: core %d: Interrupt not enabled... %.2x\n",
- apic->lapic_id.val, apic->core->cpu_id,*en_location);
+ apic->lapic_id.val, apic->core->vcpu_id, *en_location);
}
return 0;
((mda & 0x0f) & (dst_apic->log_dst.dst_log_id & 0x0f)) ) { /* I am in the set) */
PrintDebug("apic %u core %u: accepting clustered IRQ (mda 0x%x == log_dst 0x%x)\n",
- dst_apic->lapic_id.val, dst_core->cpu_id, mda,
+ dst_apic->lapic_id.val, dst_core->vcpu_id, mda,
dst_apic->log_dst.dst_log_id);
return 1;
} else {
PrintDebug("apic %u core %u: rejecting clustered IRQ (mda 0x%x != log_dst 0x%x)\n",
- dst_apic->lapic_id.val, dst_core->cpu_id, mda,
+ dst_apic->lapic_id.val, dst_core->vcpu_id, mda,
dst_apic->log_dst.dst_log_id);
return 0;
}
if (dst_apic->log_dst.dst_log_id & mda) { // I am in the set
PrintDebug("apic %u core %u: accepting flat IRQ (mda 0x%x == log_dst 0x%x)\n",
- dst_apic->lapic_id.val, dst_core->cpu_id, mda,
+ dst_apic->lapic_id.val, dst_core->vcpu_id, mda,
dst_apic->log_dst.dst_log_id);
return 1;
} else {
PrintDebug("apic %u core %u: rejecting flat IRQ (mda 0x%x != log_dst 0x%x)\n",
- dst_apic->lapic_id.val, dst_core->cpu_id, mda,
+ dst_apic->lapic_id.val, dst_core->vcpu_id, mda,
dst_apic->log_dst.dst_log_id);
return 0;
}
} else {
PrintError("apic %u core %u: invalid destination format register value 0x%x for logical mode delivery.\n",
- dst_apic->lapic_id.val, dst_core->cpu_id, dst_apic->dst_fmt.model);
+ dst_apic->lapic_id.val, dst_core->vcpu_id, dst_apic->dst_fmt.model);
return -1;
}
}
int do_xcall;
- PrintDebug("delivering IRQ %d to core %u\n", vector, dst_core->cpu_id);
+ PrintDebug("delivering IRQ %d to core %u\n", vector, dst_core->vcpu_id);
do_xcall = activate_apic_irq(dst_apic, vector);
- if (do_xcall < 0) {
- PrintError("Failed to activate apic irq!\n");
- return -1;
- }
- if (do_xcall && (dst_apic != src_apic)) {
- // Assume core # is same as logical processor for now
- // TODO FIX THIS FIX THIS
- // THERE SHOULD BE: guestapicid->virtualapicid map,
- // cpu_id->logical processor map
- // host maitains logical proc->phsysical proc
+
+ if (dst_apic != src_apic) {
PrintDebug(" non-local core with new interrupt, forcing it to exit now\n");
#ifdef V3_CONFIG_MULTITHREAD_OS
- v3_interrupt_cpu(dst_core->vm_info, dst_core->cpu_id, 0);
-#else
- V3_ASSERT(0);
+ v3_interrupt_cpu(dst_core->vm_info, dst_core->pcpu_id, 0);
#endif
}
}
case APIC_INIT_DELIVERY: {
- PrintDebug(" INIT delivery to core %u\n", dst_core->cpu_id);
+ PrintDebug(" INIT delivery to core %u\n", dst_core->vcpu_id);
// TODO: any APIC reset on dest core (shouldn't be needed, but not sure...)
// Sanity check
if (dst_apic->ipi_state != INIT_ST) {
PrintError(" Warning: core %u is not in INIT state (mode = %d), ignored (assuming this is the deassert)\n",
- dst_core->cpu_id, dst_apic->ipi_state);
+ dst_core->vcpu_id, dst_apic->ipi_state);
// Only a warning, since INIT INIT SIPI is common
break;
}
// Sanity check
if (dst_apic->ipi_state != SIPI) {
PrintError(" core %u is not in SIPI state (mode = %d), ignored!\n",
- dst_core->cpu_id, dst_apic->ipi_state);
+ dst_core->vcpu_id, dst_apic->ipi_state);
break;
}
dst_core->segments.cs.base = vector << 12;
PrintDebug(" SIPI delivery (0x%x -> 0x%x:0x0) to core %u\n",
- vector, dst_core->segments.cs.selector, dst_core->cpu_id);
+ vector, dst_core->segments.cs.selector, dst_core->vcpu_id);
// Maybe need to adjust the APIC?
// We transition the target core to SIPI state
{
int i;
- if (icr->dst >0 && icr->dst < apic_dev->num_apics) {
+ if ( (icr->dst > 0) && (icr->dst < apic_dev->num_apics) ) {
// see if it simply is the core id
if (apic_dev->apics[icr->dst].lapic_id.val == icr->dst) {
return &(apic_dev->apics[icr->dst]);
}
}
- for (i=0;i<apic_dev->num_apics;i++) {
+ for (i = 0; i < apic_dev->num_apics; i++) {
if (apic_dev->apics[i].lapic_id.val == icr->dst) {
return &(apic_dev->apics[i]);
}
case APIC_SHORTHAND_NONE: // no shorthand
if (icr->dst_mode == APIC_DEST_PHYSICAL) {
- dest_apic=find_physical_apic(apic_dev,icr);
+ dest_apic = find_physical_apic(apic_dev, icr);
- if (dest_apic==NULL) {
+ if (dest_apic == NULL) {
PrintError("apic: Attempted send to unregistered apic id=%u\n", icr->dst);
return -1;
}
}
- V3_Print("apic: done\n");
-
+ PrintDebug("apic: done\n");
} else if (icr->dst_mode == APIC_DEST_LOGICAL) {
- if (icr->del_mode!=APIC_LOWEST_DELIVERY ) {
+ if (icr->del_mode != APIC_LOWEST_DELIVERY) {
+ int i;
+ uint8_t mda = icr->dst;
+
// logical, but not lowest priority
// we immediately trigger
// fixed, smi, reserved, nmi, init, sipi, etc
- int i;
-
- uint8_t mda = icr->dst;
+
for (i = 0; i < apic_dev->num_apics; i++) {
+ int del_flag = 0;
dest_apic = &(apic_dev->apics[i]);
- int del_flag = should_deliver_ipi(dest_apic->core, dest_apic, mda);
+ del_flag = should_deliver_ipi(dest_apic->core, dest_apic, mda);
if (del_flag == -1) {
+
PrintError("apic: Error checking delivery mode\n");
return -1;
} else if (del_flag == 1) {
+
if (deliver_ipi(src_apic, dest_apic,
icr->vec, icr->del_mode) == -1) {
PrintError("apic: Error: Could not deliver IPI\n");
}
}
} else { //APIC_LOWEST_DELIVERY
- // logical, lowest priority
int i;
struct apic_state * cur_best_apic = NULL;
uint8_t mda = icr->dst;
+ // logical, lowest priority
+
for (i = 0; i < apic_dev->num_apics; i++) {
int del_flag = 0;
return -1;
} else if (del_flag == 1) {
// update priority for lowest priority scan
- if (!cur_best_apic) {
+ if (cur_best_apic == 0) {
cur_best_apic = dest_apic;
} else if (dest_apic->task_prio.val < cur_best_apic->task_prio.val) {
cur_best_apic = dest_apic;
}
} else if (icr->dst_mode == APIC_DEST_LOGICAL) { /* logical delivery */
PrintError("apic: use of logical delivery in self (untested)\n");
+
if (deliver_ipi(src_apic, src_apic, icr->vec, icr->del_mode) == -1) {
PrintError("apic: Could not deliver IPI to self (logical)\n");
return -1;
// External function, expected to acquire lock on apic
static int apic_read(struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data) {
struct apic_dev_state * apic_dev = (struct apic_dev_state *)(priv_data);
- struct apic_state * apic = &(apic_dev->apics[core->cpu_id]);
+ struct apic_state * apic = &(apic_dev->apics[core->vcpu_id]);
addr_t reg_addr = guest_addr - apic->base_addr;
struct apic_msr * msr = (struct apic_msr *)&(apic->base_addr_msr.value);
uint32_t val = 0;
PrintDebug("apic %u: core %u: at %p: Read apic address space (%p)\n",
- apic->lapic_id.val, core->cpu_id, apic, (void *)guest_addr);
+ apic->lapic_id.val, core->vcpu_id, apic, (void *)guest_addr);
if (msr->apic_enable == 0) {
PrintError("apic %u: core %u: Read from APIC address space with disabled APIC, apic msr=0x%llx\n",
- apic->lapic_id.val, core->cpu_id, apic->base_addr_msr.value);
+ apic->lapic_id.val, core->vcpu_id, apic->base_addr_msr.value);
return -1;
}
default:
PrintError("apic %u: core %u: Read from Unhandled APIC Register: %x (getting zero)\n",
- apic->lapic_id.val, core->cpu_id, (uint32_t)reg_addr);
+ apic->lapic_id.val, core->vcpu_id, (uint32_t)reg_addr);
return -1;
}
} else {
PrintError("apic %u: core %u: Invalid apic read length (%d)\n",
- apic->lapic_id.val, core->cpu_id, length);
+ apic->lapic_id.val, core->vcpu_id, length);
return -1;
}
PrintDebug("apic %u: core %u: Read finished (val=%x)\n",
- apic->lapic_id.val, core->cpu_id, *(uint32_t *)dst);
+ apic->lapic_id.val, core->vcpu_id, *(uint32_t *)dst);
return length;
}
*/
static int apic_write(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data) {
struct apic_dev_state * apic_dev = (struct apic_dev_state *)(priv_data);
- struct apic_state * apic = &(apic_dev->apics[core->cpu_id]);
+ struct apic_state * apic = &(apic_dev->apics[core->vcpu_id]);
addr_t reg_addr = guest_addr - apic->base_addr;
struct apic_msr * msr = (struct apic_msr *)&(apic->base_addr_msr.value);
uint32_t op_val = *(uint32_t *)src;
PrintDebug("apic %u: core %u: at %p and priv_data is at %p\n",
- apic->lapic_id.val, core->cpu_id, apic, priv_data);
+ apic->lapic_id.val, core->vcpu_id, apic, priv_data);
PrintDebug("apic %u: core %u: write to address space (%p) (val=%x)\n",
- apic->lapic_id.val, core->cpu_id, (void *)guest_addr, *(uint32_t *)src);
+ apic->lapic_id.val, core->vcpu_id, (void *)guest_addr, *(uint32_t *)src);
if (msr->apic_enable == 0) {
PrintError("apic %u: core %u: Write to APIC address space with disabled APIC, apic msr=0x%llx\n",
- apic->lapic_id.val, core->cpu_id, apic->base_addr_msr.value);
+ apic->lapic_id.val, core->vcpu_id, apic->base_addr_msr.value);
return -1;
}
if (length != 4) {
PrintError("apic %u: core %u: Invalid apic write length (%d)\n",
- apic->lapic_id.val, length, core->cpu_id);
+ apic->lapic_id.val, length, core->vcpu_id);
return -1;
}
case EXT_APIC_FEATURE_OFFSET:
PrintError("apic %u: core %u: Attempting to write to read only register %p (error)\n",
- apic->lapic_id.val, core->cpu_id, (void *)reg_addr);
+ apic->lapic_id.val, core->vcpu_id, (void *)reg_addr);
break;
// Data registers
case APIC_ID_OFFSET:
//V3_Print("apic %u: core %u: my id is being changed to %u\n",
- // apic->lapic_id.val, core->cpu_id, op_val);
+ // apic->lapic_id.val, core->vcpu_id, op_val);
apic->lapic_id.val = op_val;
break;
break;
case LDR_OFFSET:
PrintDebug("apic %u: core %u: setting log_dst.val to 0x%x\n",
- apic->lapic_id.val, core->cpu_id, op_val);
+ apic->lapic_id.val, core->vcpu_id, op_val);
apic->log_dst.val = op_val;
break;
case DFR_OFFSET:
tmp_icr = apic->int_cmd;
// V3_Print("apic %u: core %u: sending cmd 0x%llx to apic %u\n",
- // apic->lapic_id.val, core->cpu_id,
+ // apic->lapic_id.val, core->vcpu_id,
// apic->int_cmd.val, apic->int_cmd.dst);
if (route_ipi(apic_dev, apic, &tmp_icr) == -1) {
PrintError("IPI Routing failure\n");
return -1;
}
+
break;
}
case INT_CMD_HI_OFFSET: {
apic->int_cmd.hi = op_val;
- V3_Print("apic %u: core %u: writing command high=0x%x\n", apic->lapic_id.val, core->cpu_id,apic->int_cmd.hi);
+ V3_Print("apic %u: core %u: writing command high=0x%x\n", apic->lapic_id.val, core->vcpu_id,apic->int_cmd.hi);
break;
}
case SEOI_OFFSET:
default:
PrintError("apic %u: core %u: Write to Unhandled APIC Register: %x (ignored)\n",
- apic->lapic_id.val, core->cpu_id, (uint32_t)reg_addr);
+ apic->lapic_id.val, core->vcpu_id, (uint32_t)reg_addr);
return -1;
}
- PrintDebug("apic %u: core %u: Write finished\n", apic->lapic_id.val, core->cpu_id);
+ PrintDebug("apic %u: core %u: Write finished\n", apic->lapic_id.val, core->vcpu_id);
return length;
static int apic_intr_pending(struct guest_info * core, void * private_data) {
struct apic_dev_state * apic_dev = (struct apic_dev_state *)(private_data);
- struct apic_state * apic = &(apic_dev->apics[core->cpu_id]);
+ struct apic_state * apic = &(apic_dev->apics[core->vcpu_id]);
+
+ // drain irq QUEUE
+
int req_irq = get_highest_irr(apic);
int svc_irq = get_highest_isr(apic);
- // PrintDebug("apic %u: core %u: req_irq=%d, svc_irq=%d\n",apic->lapic_id.val,info->cpu_id,req_irq,svc_irq);
+ // PrintDebug("apic %u: core %u: req_irq=%d, svc_irq=%d\n",apic->lapic_id.val,info->vcpu_id,req_irq,svc_irq);
if ((req_irq >= 0) &&
(req_irq > svc_irq)) {
static int apic_get_intr_number(struct guest_info * core, void * private_data) {
struct apic_dev_state * apic_dev = (struct apic_dev_state *)(private_data);
- struct apic_state * apic = &(apic_dev->apics[core->cpu_id]);
+ struct apic_state * apic = &(apic_dev->apics[core->vcpu_id]);
int req_irq = get_highest_irr(apic);
int svc_irq = get_highest_isr(apic);
static int apic_begin_irq(struct guest_info * core, void * private_data, int irq) {
struct apic_dev_state * apic_dev = (struct apic_dev_state *)(private_data);
- struct apic_state * apic = &(apic_dev->apics[core->cpu_id]);
+ struct apic_state * apic = &(apic_dev->apics[core->vcpu_id]);
int major_offset = (irq & ~0x00000007) >> 3;
int minor_offset = irq & 0x00000007;
uint8_t *req_location = apic->int_req_reg + major_offset;
} else {
// do nothing...
//PrintDebug("apic %u: core %u: begin irq for %d ignored since I don't own it\n",
- // apic->lapic_id.val, core->cpu_id, irq);
+ // apic->lapic_id.val, core->vcpu_id, irq);
}
return 0;
uint64_t cpu_cycles, uint64_t cpu_freq,
void * priv_data) {
struct apic_dev_state * apic_dev = (struct apic_dev_state *)(priv_data);
- struct apic_state * apic = &(apic_dev->apics[core->cpu_id]);
+ struct apic_state * apic = &(apic_dev->apics[core->vcpu_id]);
// The 32 bit GCC runtime is a pile of shit
#ifdef __V3_64BIT__
if ((apic->tmr_init_cnt == 0) ||
( (apic->tmr_vec_tbl.tmr_mode == APIC_TMR_ONESHOT) &&
(apic->tmr_cur_cnt == 0))) {
- //PrintDebug("apic %u: core %u: APIC timer not yet initialized\n",apic->lapic_id.val,info->cpu_id);
+ //PrintDebug("apic %u: core %u: APIC timer not yet initialized\n",apic->lapic_id.val,info->vcpu_id);
return;
}
break;
default:
PrintError("apic %u: core %u: Invalid Timer Divider configuration\n",
- apic->lapic_id.val, core->cpu_id);
+ apic->lapic_id.val, core->vcpu_id);
return;
}
// raise irq
PrintDebug("apic %u: core %u: Raising APIC Timer interrupt (periodic=%d) (icnt=%d) (div=%d)\n",
- apic->lapic_id.val, core->cpu_id,
+ apic->lapic_id.val, core->vcpu_id,
apic->tmr_vec_tbl.tmr_mode, apic->tmr_init_cnt, shift_num);
if (apic_intr_pending(core, priv_data)) {
PrintDebug("apic %u: core %u: Overriding pending IRQ %d\n",
- apic->lapic_id.val, core->cpu_id,
+ apic->lapic_id.val, core->vcpu_id,
apic_get_intr_number(core, priv_data));
}
if (activate_internal_irq(apic, APIC_TMR_INT) == -1) {
PrintError("apic %u: core %u: Could not raise Timer interrupt\n",
- apic->lapic_id.val, core->cpu_id);
+ apic->lapic_id.val, core->vcpu_id);
}
if (apic->tmr_vec_tbl.tmr_mode == APIC_TMR_PERIODIC) {
return -1;
}
- v3_hook_full_mem(vm, core->cpu_id, apic->base_addr, apic->base_addr + PAGE_SIZE_4KB, apic_read, apic_write, apic_dev);
+ v3_hook_full_mem(vm, core->vcpu_id, apic->base_addr, apic->base_addr + PAGE_SIZE_4KB, apic_read, apic_write, apic_dev);
PrintDebug("apic %u: (setup device): done, my id is %u\n", i, apic->lapic_id.val);
}
#endif
// rotate data right
if (ror) {
- data = (data>>ror) | data<<(8-ror);
+ data = (data>>ror) | (data<<(8-ror));
}
#if DEBUG_DEEP_MEM
uint8_t data = ((uint8_t *)src)[i];
if (ror) {
- data = (data>>ror) | data<<(8-ror);
+ data = (data>>ror) | (data<<(8-ror));
}
uint8_t bm = vga->vga_graphics_controller.vga_bit_mask & data;
struct v3_inspector_state * vm_state = priv_data;
char core_name[50];
- snprintf(core_name, 50, "core.%d", core->cpu_id);
+ snprintf(core_name, 50, "core.%d", core->vcpu_id);
{
struct v3_mtree * core_node = v3_mtree_create_subtree(&(vm_state->state_tree), core_name);
if (khz) {
time_state->guest_cpu_freq = atoi(khz);
PrintDebug("Core %d CPU frequency requested at %d khz.\n",
- info->cpu_id, time_state->guest_cpu_freq);
+ info->pcpu_id, time_state->guest_cpu_freq);
}
if ( (khz == NULL) ||
pte32_t * shadow_pte = (pte32_t *)&(shadow_pt[PTE32_INDEX(fault_addr)]);
addr_t guest_pa = BASE_TO_PAGE_ADDR((addr_t)(guest_pte->page_base_addr)) + PAGE_OFFSET(fault_addr);
- struct v3_mem_region * shdw_reg = v3_get_mem_region(core->vm_info, core->cpu_id, guest_pa);
+ struct v3_mem_region * shdw_reg = v3_get_mem_region(core->vm_info, core->vcpu_id, guest_pa);
if (shdw_reg == NULL) {
// Inject a machine check in the guest
PrintDebug("Handling 4MB fault (guest_fault_pa=%p) (error_code=%x)\n", (void *)guest_fault_pa, *(uint_t*)&error_code);
PrintDebug("ShadowPT=%p, LargeGuestPDE=%p\n", shadow_pt, large_guest_pde);
- struct v3_mem_region * shdw_reg = v3_get_mem_region(core->vm_info, core->cpu_id, guest_fault_pa);
+ struct v3_mem_region * shdw_reg = v3_get_mem_region(core->vm_info, core->vcpu_id, guest_fault_pa);
if (shdw_reg == NULL) {
pte32_t * shadow_pte = (pte32_t *)&(shadow_pt[PTE32_INDEX(fault_addr)]);
addr_t guest_pa = BASE_TO_PAGE_ADDR((addr_t)(guest_pte->page_base_addr)) + PAGE_OFFSET(fault_addr);
- struct v3_mem_region * shdw_reg = v3_get_mem_region(core->vm_info, core->cpu_id, guest_pa);
+ struct v3_mem_region * shdw_reg = v3_get_mem_region(core->vm_info, core->vcpu_id, guest_pa);
if (shdw_reg == NULL) {
// Inject a machine check in the guest
if ((shdw_reg->host_type == SHDW_REGION_ALLOCATED) ||
(shdw_reg->host_type == SHDW_REGION_WRITE_HOOK)) {
- addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, core->cpu_id, guest_pa);
+ addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, core->vcpu_id, guest_pa);
shadow_pte->page_base_addr = PAGE_BASE_ADDR(shadow_pa);
PrintDebug("Handling 4MB fault (guest_fault_pa=%p) (error_code=%x)\n", (void *)guest_fault_pa, *(uint_t*)&error_code);
PrintDebug("ShadowPT=%p, LargeGuestPDE=%p\n", shadow_pt, large_guest_pde);
- struct v3_mem_region * shdw_reg = v3_get_mem_region(core->vm_info, core->cpu_id, guest_fault_pa);
+ struct v3_mem_region * shdw_reg = v3_get_mem_region(core->vm_info, core->vcpu_id, guest_fault_pa);
if (shdw_reg == NULL) {
if ((shdw_reg->host_type == SHDW_REGION_ALLOCATED) ||
(shdw_reg->host_type == SHDW_REGION_WRITE_HOOK)) {
- addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, core->cpu_id, guest_fault_pa);
+ addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, core->vcpu_id, guest_fault_pa);
shadow_pte->page_base_addr = PAGE_BASE_ADDR(shadow_pa);
PrintDebug("Handling PTE fault\n");
- struct v3_mem_region * shdw_reg = v3_get_mem_region(core->vm_info, core->cpu_id, guest_pa);
+ struct v3_mem_region * shdw_reg = v3_get_mem_region(core->vm_info, core->vcpu_id, guest_pa);
int inherited_ar_user = ((inherited_ar & PT_USER_MASK) == PT_USER_MASK) ? 1 : 0;
int inherited_ar_writable = ((inherited_ar & PT_WRITABLE_MASK) == PT_WRITABLE_MASK) ? 1 : 0;
- addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, core->cpu_id, guest_pa);
+ addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, core->vcpu_id, guest_pa);
shadow_pte->page_base_addr = PAGE_BASE_ADDR(shadow_pa);
PrintDebug("Handling 2MB fault (guest_fault_pa=%p) (error_code=%x)\n", (void *)guest_fault_pa, *(uint_t*)&error_code);
PrintDebug("ShadowPT=%p, LargeGuestPDE=%p\n", shadow_pt, large_guest_pde);
- struct v3_mem_region * shdw_reg = v3_get_mem_region(core->vm_info, core->cpu_id, guest_fault_pa);
+ struct v3_mem_region * shdw_reg = v3_get_mem_region(core->vm_info, core->vcpu_id, guest_fault_pa);
int fixed = 0;
int write_pt = 0;
if ((shdw_reg->host_type == SHDW_REGION_ALLOCATED) ||
(shdw_reg->host_type == SHDW_REGION_WRITE_HOOK)) {
- addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, core->cpu_id, guest_fault_pa);
+ addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, core->vcpu_id, guest_fault_pa);
shadow_pte->page_base_addr = PAGE_BASE_ADDR(shadow_pa);
pte32_t * shadow_pte = (pte32_t *)&(shadow_pt[PTE32_INDEX(fault_addr)]);
addr_t guest_pa = BASE_TO_PAGE_ADDR((addr_t)(guest_pte->page_base_addr)) + PAGE_OFFSET(fault_addr);
- struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->cpu_id, guest_pa);
+ struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->vcpu_id, guest_pa);
if (shdw_reg == NULL) {
// Inject a machine check in the guest
PrintDebug("Handling 4MB fault (guest_fault_pa=%p) (error_code=%x)\n", (void *)guest_fault_pa, *(uint_t*)&error_code);
PrintDebug("ShadowPT=%p, LargeGuestPDE=%p\n", shadow_pt, large_guest_pde);
- struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->cpu_id, guest_fault_pa);
+ struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->vcpu_id, guest_fault_pa);
if (shdw_reg == NULL) {
PrintDebug("Handling PTE fault\n");
- struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->cpu_id, guest_pa);
+ struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->vcpu_id, guest_pa);
PrintDebug("Handling 2MB fault (guest_fault_pa=%p) (error_code=%x)\n", (void *)guest_fault_pa, *(uint_t*)&error_code);
PrintDebug("ShadowPT=%p, LargeGuestPDE=%p\n", shadow_pt, large_guest_pde);
- struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->cpu_id, guest_fault_pa);
+ struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->vcpu_id, guest_fault_pa);
if (shdw_reg == NULL) {
pte32_t * shadow_pte = (pte32_t *)&(shadow_pt[PTE32_INDEX(fault_addr)]);
addr_t guest_pa = BASE_TO_PAGE_ADDR((addr_t)(guest_pte->page_base_addr)) + PAGE_OFFSET(fault_addr);
- struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->cpu_id, guest_pa);
+ struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->vcpu_id, guest_pa);
if (shdw_reg == NULL) {
// Inject a machine check in the guest
PrintDebug("Handling 4MB fault (guest_fault_pa=%p) (error_code=%x)\n", (void *)guest_fault_pa, *(uint_t*)&error_code);
PrintDebug("ShadowPT=%p, LargeGuestPDE=%p\n", shadow_pt, large_guest_pde);
- struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->cpu_id, guest_fault_pa);
+ struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->vcpu_id, guest_fault_pa);
if (shdw_reg == NULL) {
PrintDebug("Handling 4MB fault with large page (guest_fault_pa=%p) (error_code=%x)\n", (void *)guest_fault_pa, *(uint_t*)&error_code);
PrintDebug("LargeShadowPDE=%p, LargeGuestPDE=%p\n", large_shadow_pde, large_guest_pde);
- struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->cpu_id, guest_fault_pa);
+ struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->vcpu_id, guest_fault_pa);
if (shdw_reg == NULL) {
PrintDebug("Handling PTE fault\n");
- struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->cpu_id, guest_pa);
+ struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->vcpu_id, guest_pa);
PrintDebug("Handling 2MB fault with large page (guest_fault_pa=%p) (error_code=%x)\n", (void *)guest_fault_pa, *(uint_t*)&error_code);
PrintDebug("LargeShadowPDE=%p, LargeGuestPDE=%p\n", large_shadow_pde, large_guest_pde);
- struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->cpu_id, guest_fault_pa);
+ struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->vcpu_id, guest_fault_pa);
if (shdw_reg == NULL) {
// Inject a machine check in the guest
PrintDebug("Handling 2MB fault (guest_fault_pa=%p) (error_code=%x)\n", (void *)guest_fault_pa, *(uint_t*)&error_code);
PrintDebug("ShadowPT=%p, LargeGuestPDE=%p\n", shadow_pt, large_guest_pde);
- struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->cpu_id, guest_fault_pa);
+ struct v3_mem_region * shdw_reg = v3_get_mem_region(info->vm_info, info->vcpu_id, guest_fault_pa);
if (shdw_reg == NULL) {
// vmcb_saved_state_t * guest_state = GET_VMCB_SAVE_STATE_AREA((vmcb_t*)(info->vmm_data));
// vmcb_ctrl_t * guest_ctrl = GET_VMCB_CTRL_AREA((vmcb_t*)(info->vmm_data));
- PrintDebug("Starting SVM core %u\n", info->cpu_id);
+ PrintDebug("Starting SVM core %u (on logical core %u)\n", info->vcpu_id, info->pcpu_id);
- if (info->cpu_id == 0) {
+ if (info->vcpu_id == 0) {
info->core_run_state = CORE_RUNNING;
info->vm_info->run_state = VM_RUNNING;
} else {
- PrintDebug("SVM core %u: Waiting for core initialization\n", info->cpu_id);
+ PrintDebug("SVM core %u (on %u): Waiting for core initialization\n", info->vcpu_id, info->pcpu_id);
while (info->core_run_state == CORE_STOPPED) {
v3_yield(info);
- //PrintDebug("SVM core %u: still waiting for INIT\n",info->cpu_id);
+ //PrintDebug("SVM core %u: still waiting for INIT\n", info->vcpu_id);
}
- PrintDebug("SVM core %u initialized\n", info->cpu_id);
+ PrintDebug("SVM core %u(on %u) initialized\n", info->vcpu_id, info->pcpu_id);
}
- PrintDebug("SVM core %u: I am starting at CS=0x%x (base=0x%p, limit=0x%x), RIP=0x%p\n",
- info->cpu_id, info->segments.cs.selector, (void *)(info->segments.cs.base),
+ PrintDebug("SVM core %u(on %u): I am starting at CS=0x%x (base=0x%p, limit=0x%x), RIP=0x%p\n",
+ info->vcpu_id, info->pcpu_id,
+ info->segments.cs.selector, (void *)(info->segments.cs.base),
info->segments.cs.limit, (void *)(info->rip));
- PrintDebug("SVM core %u: Launching SVM VM (vmcb=%p)\n", info->cpu_id, (void *)info->vmm_data);
+ PrintDebug("SVM core %u: Launching SVM VM (vmcb=%p) (on cpu %u)\n",
+ info->vcpu_id, (void *)info->vmm_data, info->pcpu_id);
//PrintDebugVMCB((vmcb_t*)(info->vmm_data));
v3_start_time(info);
info->vm_info->run_state = VM_ERROR;
- V3_Print("SVM core %u: SVM ERROR!!\n", info->cpu_id);
+ V3_Print("SVM core %u: SVM ERROR!!\n", info->vcpu_id);
v3_print_guest_state(info);
- V3_Print("SVM core %u: SVM Exit Code: %p\n", info->cpu_id, (void *)(addr_t)guest_ctrl->exit_code);
+ V3_Print("SVM core %u: SVM Exit Code: %p\n", info->vcpu_id, (void *)(addr_t)guest_ctrl->exit_code);
- V3_Print("SVM core %u: exit_info1 low = 0x%.8x\n", info->cpu_id, *(uint_t*)&(guest_ctrl->exit_info1));
- V3_Print("SVM core %u: exit_info1 high = 0x%.8x\n", info->cpu_id, *(uint_t *)(((uchar_t *)&(guest_ctrl->exit_info1)) + 4));
+ V3_Print("SVM core %u: exit_info1 low = 0x%.8x\n", info->vcpu_id, *(uint_t*)&(guest_ctrl->exit_info1));
+ V3_Print("SVM core %u: exit_info1 high = 0x%.8x\n", info->vcpu_id, *(uint_t *)(((uchar_t *)&(guest_ctrl->exit_info1)) + 4));
- V3_Print("SVM core %u: exit_info2 low = 0x%.8x\n", info->cpu_id, *(uint_t*)&(guest_ctrl->exit_info2));
- V3_Print("SVM core %u: exit_info2 high = 0x%.8x\n", info->cpu_id, *(uint_t *)(((uchar_t *)&(guest_ctrl->exit_info2)) + 4));
+ V3_Print("SVM core %u: exit_info2 low = 0x%.8x\n", info->vcpu_id, *(uint_t*)&(guest_ctrl->exit_info2));
+ V3_Print("SVM core %u: exit_info2 high = 0x%.8x\n", info->vcpu_id, *(uint_t *)(((uchar_t *)&(guest_ctrl->exit_info2)) + 4));
linear_addr = get_addr_linear(info, info->rip, &(info->segments.cs));
v3_gva_to_hva(info, linear_addr, &host_addr);
}
- V3_Print("SVM core %u: Host Address of rip = 0x%p\n", info->cpu_id, (void *)host_addr);
+ V3_Print("SVM core %u: Host Address of rip = 0x%p\n", info->vcpu_id, (void *)host_addr);
- V3_Print("SVM core %u: Instr (15 bytes) at %p:\n", info->cpu_id, (void *)host_addr);
+ V3_Print("SVM core %u: Instr (15 bytes) at %p:\n", info->vcpu_id, (void *)host_addr);
v3_dump_mem((uint8_t *)host_addr, 15);
v3_print_stack(info);
}
-/*
+
if ((info->num_exits % 5000) == 0) {
V3_Print("SVM Exit number %d\n", (uint32_t)info->num_exits);
+ v3_print_guest_state(info);
}
-*/
+
}
}
int v3_gpa_to_hpa(struct guest_info * info, addr_t gpa, addr_t * hpa) {
- struct v3_mem_region * reg = v3_get_mem_region(info->vm_info, info->cpu_id, gpa);
+ struct v3_mem_region * reg = v3_get_mem_region(info->vm_info, info->vcpu_id, gpa);
if (reg == NULL) {
PrintError("In GPA->HPA: Could not find address in shadow map (addr=%p) (NULL REGION)\n",
struct guest_info * core = (struct guest_info *)p;
- PrintDebug("virtual core %u: in start_core (RIP=%p)\n",
- core->cpu_id, (void *)(addr_t)core->rip);
+ PrintDebug("virtual core %u (on logical core %u): in start_core (RIP=%p)\n",
+ core->vcpu_id, core->pcpu_id, (void *)(addr_t)core->rip);
switch (v3_cpu_types[0]) {
#ifdef V3_CONFIG_SVM
i--; // We reset the logical core idx. Not strictly necessary I guess...
} else {
- /* This assumes that the core 0 thread has been mapped to physical core 0 */
if (i == V3_Get_CPU()) {
// We skip the local CPU because it is reserved for vcore 0
continue;
core_idx, start_core, core, core->exec_name);
// TODO: actually manage these threads instead of just launching them
+ core->pcpu_id = core_idx;
core_thread = V3_CREATE_THREAD_ON_CPU(core_idx, start_core, core, core->exec_name);
if (core_thread == NULL) {
sprintf(vm->cores[0].exec_name, "%s", vm->name);
+ vm->cores[0].pcpu_id = V3_Get_CPU();
+
if (start_core(&(vm->cores[0])) != 0) {
PrintError("Error starting VM core 0\n");
v3_stop_vm(vm);
for (i = 0; i < vm->num_cores; i++) {
struct guest_info * info = &(vm->cores[i]);
- info->cpu_id = i;
+ info->vcpu_id = i;
info->vm_info = vm;
info->core_cfg_data = per_core_cfg;
int pde_index = PDE32_INDEX(fault_addr);
int pte_index = PTE32_INDEX(fault_addr);
- struct v3_mem_region * region = v3_get_mem_region(info->vm_info, info->cpu_id, fault_addr);
+ struct v3_mem_region * region = v3_get_mem_region(info->vm_info, info->vcpu_id, fault_addr);
if (region == NULL) {
PrintError("Invalid region in passthrough page fault 32, addr=%p\n",
int pde_index = PDE32PAE_INDEX(fault_addr);
int pte_index = PTE32PAE_INDEX(fault_addr);
- struct v3_mem_region * region = v3_get_mem_region(info->vm_info, info->cpu_id, fault_addr);
+ struct v3_mem_region * region = v3_get_mem_region(info->vm_info, info->vcpu_id, fault_addr);
if (region == NULL) {
PrintError("Invalid region in passthrough page fault 32PAE, addr=%p\n",
int pde_index = PDE64_INDEX(fault_addr);
int pte_index = PTE64_INDEX(fault_addr);
- struct v3_mem_region * region = v3_get_mem_region(core->vm_info, core->cpu_id, fault_addr);
+ struct v3_mem_region * region = v3_get_mem_region(core->vm_info, core->vcpu_id, fault_addr);
int page_size = PAGE_SIZE_4KB;
if (region == NULL) {
if ((ext->impl) && (ext->impl->core_init)) {
if (ext->impl->core_init(core, ext->priv_data) == -1) {
PrintError("Error configuring per core extension %s on core %d\n",
- ext->impl->name, core->cpu_id);
+ ext->impl->name, core->vcpu_id);
return -1;
}
}
pg_start = PAGE_ADDR_4MB(page_addr);
pg_end = (pg_start + PAGE_SIZE_4MB);
- reg = get_overlapping_region(core->vm_info, core->cpu_id, pg_start, pg_end);
+ reg = get_overlapping_region(core->vm_info, core->vcpu_id, pg_start, pg_end);
if ((reg) && ((reg->host_addr % PAGE_SIZE_4MB) == 0)) {
page_size = PAGE_SIZE_4MB;
pg_start = PAGE_ADDR_2MB(page_addr);
pg_end = (pg_start + PAGE_SIZE_2MB);
- reg = get_overlapping_region(core->vm_info, core->cpu_id, pg_start, pg_end);
+ reg = get_overlapping_region(core->vm_info, core->vcpu_id, pg_start, pg_end);
if ((reg) && ((reg->host_addr % PAGE_SIZE_2MB) == 0)) {
page_size = PAGE_SIZE_2MB;
pg_start = PAGE_ADDR_1GB(page_addr);
pg_end = (pg_start + PAGE_SIZE_1GB);
- reg = get_overlapping_region(core->vm_info, core->cpu_id, pg_start, pg_end);
+ reg = get_overlapping_region(core->vm_info, core->vcpu_id, pg_start, pg_end);
if ((reg) && ((reg->host_addr % PAGE_SIZE_1GB) == 0)) {
page_size = PAGE_SIZE_1GB;
pg_start = PAGE_ADDR_2MB(page_addr);
pg_end = (pg_start + PAGE_SIZE_2MB);
- reg = get_overlapping_region(core->vm_info, core->cpu_id, pg_start, pg_end);
+ reg = get_overlapping_region(core->vm_info, core->vcpu_id, pg_start, pg_end);
if ((reg) && ((reg->host_addr % PAGE_SIZE_2MB) == 0)) {
page_size = PAGE_SIZE_2MB;
src_reg = reg;
} else {
// Note that this should only trigger for string operations
- src_reg = v3_get_mem_region(core->vm_info, core->cpu_id, src_mem_op_gpa);
+ src_reg = v3_get_mem_region(core->vm_info, core->vcpu_id, src_mem_op_gpa);
}
if (src_reg == NULL) {
// We don't check whether the region is a hook here because it doesn't yet matter.
// These hva calculations will be true regardless
if (src_reg->flags.alloced == 0) {
- src_mem_op_hva = (addr_t)(hooks->hook_hvas_1 + (PAGE_SIZE * core->cpu_id));
+ src_mem_op_hva = (addr_t)(hooks->hook_hvas_1 + (PAGE_SIZE * core->vcpu_id));
} else {
// We already have the region so we can do the conversion ourselves
src_mem_op_hva = (addr_t)V3_VAddr((void *)((src_mem_op_gpa - src_reg->guest_start) + src_reg->host_addr));
dst_reg = reg;
} else {
// Note that this should only trigger for string operations
- dst_reg = v3_get_mem_region(core->vm_info, core->cpu_id, dst_mem_op_gpa);
+ dst_reg = v3_get_mem_region(core->vm_info, core->vcpu_id, dst_mem_op_gpa);
}
if (dst_reg == NULL) {
// We don't check whether the region is a hook here because it doesn't yet matter.
// These hva calculations will be true regardless
if (dst_reg->flags.alloced == 0) {
- dst_mem_op_hva = (addr_t)(hooks->hook_hvas_2 + (PAGE_SIZE * core->cpu_id));
+ dst_mem_op_hva = (addr_t)(hooks->hook_hvas_2 + (PAGE_SIZE * core->vcpu_id));
} else {
// We already have the region so we can do the conversion ourselves
dst_mem_op_hva = (addr_t)V3_VAddr((void *)((dst_mem_op_gpa - dst_reg->guest_start) + dst_reg->host_addr));
if (global_state->active == 1) {
// unmap page
- struct v3_mem_region * old_reg = v3_get_mem_region(core->vm_info, core->cpu_id,
+ struct v3_mem_region * old_reg = v3_get_mem_region(core->vm_info, core->vcpu_id,
(addr_t)global_state->global_guest_pa);
if (old_reg == NULL) {
if (local_state->active == 1) {
// unmap page
- struct v3_mem_region * old_reg = v3_get_mem_region(core->vm_info, core->cpu_id,
+ struct v3_mem_region * old_reg = v3_get_mem_region(core->vm_info, core->vcpu_id,
(addr_t)local_state->local_guest_pa);
if (old_reg == NULL) {
local_state->active = 1;
// map page
- v3_add_shadow_mem(core->vm_info, core->cpu_id, (addr_t)local_state->local_guest_pa,
+ v3_add_shadow_mem(core->vm_info, core->vcpu_id, (addr_t)local_state->local_guest_pa,
(addr_t)(local_state->local_guest_pa + PAGE_SIZE_4KB - 1),
local_state->local_page_pa);
} else {
state->local_page = (struct v3_symspy_local_page *)V3_VAddr((void *)state->local_page_pa);
memset(state->local_page, 0, PAGE_SIZE_4KB);
- snprintf((uint8_t *)&(state->local_page->magic), 8, "V3V.%d", core->cpu_id);
+ snprintf((uint8_t *)&(state->local_page->magic), 8, "V3V.%d", core->vcpu_id);
return 0;
}
struct exit_event * evt = NULL;
struct rb_node * node = v3_rb_first(&(core->core_telem.exit_root));
- V3_Print("Exit information for Core %d\n", core->cpu_id);
+ V3_Print("Exit information for Core %d\n", core->vcpu_id);
if (!node) {
V3_Print("No information yet for this core\n");
if (khz) {
time_state->guest_cpu_freq = atoi(khz);
- PrintDebug("Core %d CPU frequency requested at %d khz.\n",
- info->cpu_id, time_state->guest_cpu_freq);
+ PrintDebug("Logical Core %d (vcpu=%d) CPU frequency requested at %d khz.\n",
+ info->pcpu_id, info->vcpu_id, time_state->guest_cpu_freq);
}
if ( (khz == NULL) ||
time_state->guest_cpu_freq = time_state->host_cpu_freq;
}
- PrintDebug("Core %d CPU frequency set to %d KHz (host CPU frequency = %d KHz).\n",
- info->cpu_id,
+ PrintDebug("Logical Core %d (vcpu=%d) CPU frequency set to %d KHz (host CPU frequency = %d KHz).\n",
+ info->pcpu_id, info->vcpu_id,
time_state->guest_cpu_freq,
time_state->host_cpu_freq);
v3_vmxassist_init(core, vmx_state);
} else if ((core->shdw_pg_mode == NESTED_PAGING) &&
- (v3_cpu_types[core->cpu_id] == V3_VMX_EPT_CPU)) {
+ (v3_cpu_types[core->pcpu_id] == V3_VMX_EPT_CPU)) {
#define CR0_PE 0x00000001
#define CR0_PG 0x80000000
}
} else if ((core->shdw_pg_mode == NESTED_PAGING) &&
- (v3_cpu_types[core->cpu_id] == V3_VMX_EPT_UG_CPU)) {
+ (v3_cpu_types[core->pcpu_id] == V3_VMX_EPT_UG_CPU)) {
int i = 0;
// For now we will assume that unrestricted guest mode is assured w/ EPT
int v3_start_vmx_guest(struct guest_info * info) {
- PrintDebug("Starting VMX core %u\n", info->cpu_id);
+ PrintDebug("Starting VMX core %u\n", info->vcpu_id);
- if (info->cpu_id == 0) {
+ if (info->vcpu_id == 0) {
info->core_run_state = CORE_RUNNING;
info->vm_info->run_state = VM_RUNNING;
} else {
- PrintDebug("VMX core %u: Waiting for core initialization\n", info->cpu_id);
+ PrintDebug("VMX core %u: Waiting for core initialization\n", info->vcpu_id);
while (info->core_run_state == CORE_STOPPED) {
v3_yield(info);
- //PrintDebug("VMX core %u: still waiting for INIT\n",info->cpu_id);
+ //PrintDebug("VMX core %u: still waiting for INIT\n",info->vcpu_id);
}
- PrintDebug("VMX core %u initialized\n", info->cpu_id);
+ PrintDebug("VMX core %u initialized\n", info->vcpu_id);
}
PrintDebug("VMX core %u: I am starting at CS=0x%x (base=0x%p, limit=0x%x), RIP=0x%p\n",
- info->cpu_id, info->segments.cs.selector, (void *)(info->segments.cs.base),
+ info->vcpu_id, info->segments.cs.selector, (void *)(info->segments.cs.base),
info->segments.cs.limit, (void *)(info->rip));
- PrintDebug("VMX core %u: Launching VMX VM\n", info->cpu_id);
+ PrintDebug("VMX core %u: Launching VMX VM on logical core %u\n", info->vcpu_id, info->pcpu_id);
v3_start_time(info);
PrintDebug("VMXON pointer: 0x%p\n", (void *)host_vmcs_ptrs[cpu_id]);
if (vmx_on(host_vmcs_ptrs[cpu_id]) == VMX_SUCCESS) {
- PrintDebug("VMX Enabled\n");
+ V3_Print("VMX Enabled\n");
} else {
PrintError("VMX initialization failure\n");
return;
int pde_index = PDE64_INDEX(fault_addr);
int pte_index = PTE64_INDEX(fault_addr);
- struct v3_mem_region * region = v3_get_mem_region(core->vm_info, core->cpu_id, fault_addr);
+ struct v3_mem_region * region = v3_get_mem_region(core->vm_info, core->vcpu_id, fault_addr);
int page_size = PAGE_SIZE_4KB;
obj-$(V3_CONFIG_VNET) += vnet_core.o vnet_host.o vnet_hashtable.o
+obj-y += null.o
--- /dev/null
+/** \file
+ * Do nothing module.
+ *
+ * This file only exists to appease the kbuild gods.
+ */
+