From: Jack Lange Date: Thu, 10 Mar 2011 19:28:33 +0000 (-0600) Subject: moved linux module to main Palacios tree X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=commitdiff_plain;h=260bb3e805ebc92ae294c3c2b36d027ba8bca488;p=palacios.git moved linux module to main Palacios tree --- diff --git a/linux_module/Makefile b/linux_module/Makefile new file mode 100644 index 0000000..55eb199 --- /dev/null +++ b/linux_module/Makefile @@ -0,0 +1,33 @@ +KERNELDIR := ../../linux-2.6.32.y/ +CONFIG_PALACIOS_PATH := /home/jarusl/palacios + + + + +v3vee-objs := palacios.o palacios-dev.o palacios-vm.o palacios-file.o palacios-stream.o palacios-console.o palacios-mm.o palacios-serial.o palacios-queue.o palacios-ringbuffer.o +#palacios-socket.o +#palacios-vnet.o palacios-packet.o + +v3vee-objs += ../../palacios/libv3vee.a + + +obj-m := v3vee.o + + +LDFLAGS = --whole-archive --script=$(PWD)/link.cmd + + +EXTRA_CFLAGS := -I$(CONFIG_PALACIOS_PATH)/palacios/include/ -DMODULE=1 -D__KERNEL__=1 + + + +all: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules + + +clean: + $(MAKE) -C $(KERNELDIR) M=$(PWD) clean + +install: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install /sbin/ -ae + diff --git a/linux_module/link.cmd b/linux_module/link.cmd new file mode 100644 index 0000000..6c014d8 --- /dev/null +++ b/linux_module/link.cmd @@ -0,0 +1,33 @@ +SECTIONS +{ + _v3_devices : + { + __start__v3_devices = .; + *(_v3_devices); + __stop__v3_devices = .; + + } + +/* _v3_capsules : + { + __start__v3_capsules = .; + *(_v3_capsules); + __stop__v3_capsules = .; + } +*/ + _v3_shdw_pg_impls : + { + __start__v3_shdw_pg_impls = .; + *(_v3_shdw_pg_impls); + __stop__v3_shdw_pg_impls = .; + + } + _v3_extensions : + { + __start__v3_extensions = .; + *(_v3_extensions); + __stop__v3_extensions = .; + + } +} + diff --git a/linux_module/palacios-console.c b/linux_module/palacios-console.c new file mode 100644 index 0000000..cc6d80e --- /dev/null +++ b/linux_module/palacios-console.c @@ -0,0 +1,376 @@ +/* + * VM Console + * (c) Jack Lange, 2010 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "palacios.h" +#include "palacios-console.h" +#include "palacios-queue.h" + +typedef enum { CONSOLE_CURS_SET = 1, + CONSOLE_CHAR_SET = 2, + CONSOLE_SCROLL = 3, + CONSOLE_UPDATE = 4, + CONSOLE_RESOLUTION = 5} console_op_t; + + + +struct cursor_msg { + int x; + int y; +} __attribute__((packed)); + +struct character_msg { + int x; + int y; + char c; + unsigned char style; +} __attribute__((packed)); + +struct scroll_msg { + int lines; +} __attribute__((packed)); + + +struct resolution_msg { + int cols; + int rows; +} __attribute__((packed)); + +struct cons_msg { + unsigned char op; + union { + struct cursor_msg cursor; + struct character_msg character; + struct scroll_msg scroll; + struct resolution_msg resolution; + }; +} __attribute__((packed)); + + +/* This is overkill...*/ +#define CONSOLE_QUEUE_LEN 8096 + + +static ssize_t +console_read(struct file * filp, char __user * buf, size_t size, loff_t * offset) { + struct palacios_console * cons = filp->private_data; + struct cons_msg * msg = NULL; + unsigned long flags; + int entries = 0; + + if (cons->open == 0) { + return 0; + } + + + if (size < sizeof(struct cons_msg)) { + printk("Invalid Read operation size: %lu\n", size); + return -EFAULT; + } + + msg = dequeue(cons->queue); + + if (msg == NULL) { + printk("ERROR: Null console message\n"); + return -EFAULT; + } + + if (copy_to_user(buf, msg, size)) { + printk("Read Fault\n"); + return -EFAULT; + } + + + kfree(msg); + + spin_lock_irqsave(&(cons->queue->lock), flags); + entries = cons->queue->num_entries; + spin_unlock_irqrestore(&(cons->queue->lock), flags); + + if (entries > 0) { + wake_up_interruptible(&(cons->intr_queue)); + } + + // printk("Read from console\n"); + return size; +} + + +static ssize_t +console_write(struct file * filp, const char __user * buf, size_t size, loff_t * offset) { + struct palacios_console * cons = filp->private_data; + int i = 0; + struct v3_keyboard_event event = {0, 0}; + + if (cons->open == 0) { + return 0; + } + + + for (i = 0; i < size; i++) { + if (copy_from_user(&(event.scan_code), buf, 1)) { + printk("Console Write fault\n"); + return -EFAULT; + } + + v3_deliver_keyboard_event(cons->guest->v3_ctx, &event); + } + + return size; +} + +static unsigned int +console_poll(struct file * filp, struct poll_table_struct * poll_tb) { + struct palacios_console * cons = filp->private_data; + unsigned int mask = POLLIN | POLLRDNORM; + unsigned long flags; + int entries = 0; + + // printk("Console=%p (guest=%s)\n", cons, cons->guest->name); + + + poll_wait(filp, &(cons->intr_queue), poll_tb); + + spin_lock_irqsave(&(cons->queue->lock), flags); + entries = cons->queue->num_entries; + spin_unlock_irqrestore(&(cons->queue->lock), flags); + + if (entries > 0) { + // printk("Returning from POLL\n"); + return mask; + } + + return 0; +} + + +static int console_release(struct inode * i, struct file * filp) { + struct palacios_console * cons = filp->private_data; + struct cons_msg * msg = NULL; + unsigned long flags; + + printk("Releasing the Console File desc\n"); + + spin_lock_irqsave(&(cons->queue->lock), flags); + cons->connected = 0; + spin_unlock_irqrestore(&(cons->queue->lock), flags); + + while ((msg = dequeue(cons->queue))) { + kfree(msg); + } + + return 0; +} + + +static struct file_operations cons_fops = { + .read = console_read, + .write = console_write, + .poll = console_poll, + .release = console_release, +}; + + + +int connect_console(struct v3_guest * guest) { + struct palacios_console * cons = &(guest->console); + int cons_fd = 0; + unsigned long flags; + + if (cons->open == 0) { + printk("Attempted to connect to unopened console\n"); + return -1; + } + + spin_lock_irqsave(&(cons->lock), flags); + + cons_fd = anon_inode_getfd("v3-cons", &cons_fops, cons, 0); + + if (cons_fd < 0) { + printk("Error creating console inode\n"); + return cons_fd; + } + + cons->connected = 1; + + v3_deliver_console_event(guest->v3_ctx, NULL); + spin_unlock_irqrestore(&(cons->lock), flags); + + printk("Console connected\n"); + + return cons_fd; +} + + + +static void * palacios_tty_open(void * private_data, unsigned int width, unsigned int height) { + struct v3_guest * guest = (struct v3_guest *)private_data; + struct palacios_console * cons = &(guest->console); + + printk("Guest initialized virtual console (Guest=%s)\n", guest->name); + + if (guest == NULL) { + printk("ERROR: Cannot open a console on a NULL guest\n"); + return NULL; + } + + if (cons->open == 1) { + printk("Console already open\n"); + return NULL; + } + + + cons->width = width; + cons->height = height; + + cons->queue = create_queue(CONSOLE_QUEUE_LEN); + spin_lock_init(&(cons->lock)); + init_waitqueue_head(&(cons->intr_queue)); + + cons->guest = guest; + + cons->open = 1; + cons->connected = 0; + + + return cons; +} + +static int post_msg(struct palacios_console * cons, struct cons_msg * msg) { + // printk("Posting Console message\n"); + + while (enqueue(cons->queue, msg) == -1) { + wake_up_interruptible(&(cons->intr_queue)); + schedule(); + } + + wake_up_interruptible(&(cons->intr_queue)); + + return 0; +} + + +static int palacios_tty_cursor_set(void * console, int x, int y) { + struct palacios_console * cons = (struct palacios_console *)console; + struct cons_msg * msg = NULL; + + if (cons->connected == 0) { + return 0; + } + + msg = kmalloc(sizeof(struct cons_msg), GFP_KERNEL); + + msg->op = CONSOLE_CURS_SET; + msg->cursor.x = x; + msg->cursor.y = y; + + return post_msg(cons, msg); +} + +static int palacios_tty_character_set(void * console, int x, int y, char c, unsigned char style) { + struct palacios_console * cons = (struct palacios_console *) console; + struct cons_msg * msg = NULL; + + if (cons->connected == 0) { + return 0; + } + + msg = kmalloc(sizeof(struct cons_msg), GFP_KERNEL); + + msg->op = CONSOLE_CHAR_SET; + msg->character.x = x; + msg->character.y = y; + msg->character.c = c; + msg->character.style = style; + + return post_msg(cons, msg); +} + +static int palacios_tty_scroll(void * console, int lines) { + struct palacios_console * cons = (struct palacios_console *) console; + struct cons_msg * msg = NULL; + + + if (cons->connected == 0) { + return 0; + } + + msg = kmalloc(sizeof(struct cons_msg), GFP_KERNEL); + + msg->op = CONSOLE_SCROLL; + msg->scroll.lines = lines; + + return post_msg(cons, msg); +} + +static int palacios_set_text_resolution(void * console, int cols, int rows) { + struct palacios_console * cons = (struct palacios_console *)console; + struct cons_msg * msg = NULL; + + if (cons->connected == 0) { + return 0; + } + + msg = kmalloc(sizeof(struct cons_msg), GFP_KERNEL); + + msg->op = CONSOLE_RESOLUTION; + msg->resolution.cols = cols; + msg->resolution.rows = rows; + + return post_msg(cons, msg); +} + +static int palacios_tty_update(void * console) { + struct palacios_console * cons = (struct palacios_console *) console; + struct cons_msg * msg = NULL; + + if (cons->connected == 0) { + return 0; + } + + msg = kmalloc(sizeof(struct cons_msg), GFP_KERNEL); + + msg->op = CONSOLE_UPDATE; + + return post_msg(cons, msg); +} + +static void palacios_tty_close(void * console) { + struct palacios_console * cons = (struct palacios_console *) console; + + cons->open = 0; +} + + + +static struct v3_console_hooks palacios_console_hooks = { + .open = palacios_tty_open, + .set_cursor = palacios_tty_cursor_set, + .set_character = palacios_tty_character_set, + .scroll = palacios_tty_scroll, + .set_text_resolution = palacios_set_text_resolution, + .update = palacios_tty_update, + .close = palacios_tty_close, +}; + + + +int palacios_init_console( void ) { + V3_Init_Console(&palacios_console_hooks); + + return 0; +} diff --git a/linux_module/palacios-console.h b/linux_module/palacios-console.h new file mode 100644 index 0000000..9e443e5 --- /dev/null +++ b/linux_module/palacios-console.h @@ -0,0 +1,39 @@ +/* + * Palacios VM Stream Console interface + * (c) Jack Lange, 2010 + */ + +#ifndef __PALACIOS_CONSOLE_H__ +#define __PALACIOS_CONSOLE_H__ + + +#include +#include + + +struct palacios_console { + struct gen_queue * queue; + spinlock_t lock; + + int open; + int connected; + + wait_queue_head_t intr_queue; + + unsigned int width; + unsigned int height; + + struct v3_guest * guest; +}; + + + +struct v3_guest; + + +int connect_console(struct v3_guest * guest); + +int palacios_init_console( void ); + + +#endif diff --git a/linux_module/palacios-dev.c b/linux_module/palacios-dev.c new file mode 100644 index 0000000..585e807 --- /dev/null +++ b/linux_module/palacios-dev.c @@ -0,0 +1,269 @@ +/* + Palacios main control interface + (c) Jack Lange, 2010 + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "palacios.h" +#include "palacios-mm.h" +#include "palacios-vm.h" +#include "palacios-stream.h" +#include "palacios-file.h" +#include "palacios-serial.h" + +MODULE_LICENSE("GPL"); + + +static int v3_major_num = 0; + +static u8 v3_minor_map[MAX_VMS / 8] = {[0 ... (MAX_VMS / 8) - 1] = 0}; + + +struct class * v3_class = NULL; +static struct cdev ctrl_dev; + +void * v3_base_addr = NULL; +unsigned int v3_pages = 0; + +static int register_vm( void ) { + int i, j = 0; + int avail = 0; + + for (i = 0; i < sizeof(v3_minor_map); i++) { + if (v3_minor_map[i] != 0xff) { + for (j = 0; j < 8; j++) { + if (!v3_minor_map[i] & (0x1 << j)) { + avail = 1; + v3_minor_map[i] |= (0x1 << j); + break; + } + } + + if (avail == 1) { + break; + } + } + } + + if (avail == 0) { + return -1; + } + + return (i * 8) + j; +} + + + +static long v3_dev_ioctl(struct file * filp, + unsigned int ioctl, unsigned long arg) { + void __user * argp = (void __user *)arg; + printk("V3 IOCTL %d\n", ioctl); + + + switch (ioctl) { + case V3_START_GUEST:{ + struct v3_guest_img user_image; + struct v3_guest * guest = kmalloc(sizeof(struct v3_guest), GFP_KERNEL); + int vm_minor = 0; + + if (IS_ERR(guest)) { + printk("Error allocating Kernel guest_image\n"); + return -EFAULT; + } + + memset(guest, 0, sizeof(struct v3_guest)); + + printk("Starting V3 Guest...\n"); + + vm_minor = register_vm(); + + if (vm_minor == -1) { + printk("Too many VMs are currently running\n"); + return -EFAULT; + } + + guest->vm_dev = MKDEV(v3_major_num, vm_minor); + + if (copy_from_user(&user_image, argp, sizeof(struct v3_guest_img))) { + printk("copy from user error getting guest image...\n"); + return -EFAULT; + } + + guest->img_size = user_image.size; + + printk("Allocating kernel memory for guest image (%llu bytes)\n", user_image.size); + guest->img = kmalloc(guest->img_size, GFP_KERNEL); + + if (IS_ERR(guest->img)) { + printk("Error: Could not allocate space for guest image\n"); + return -EFAULT; + } + + if (copy_from_user(guest->img, user_image.guest_data, guest->img_size)) { + printk("Error loading guest data\n"); + return -EFAULT; + } + + strncpy(guest->name, user_image.name, 127); + + printk("Launching VM\n"); + + INIT_LIST_HEAD(&(guest->streams)); + INIT_LIST_HEAD(&(guest->files)); + INIT_LIST_HEAD(&(guest->sockets)); + init_completion(&(guest->thread_done)); + + kthread_run(start_palacios_vm, guest, guest->name); + + wait_for_completion(&(guest->thread_done)); + + return guest->vm_dev; + break; + } + case V3_ADD_MEMORY: { + struct v3_mem_region mem; + + memset(&mem, 0, sizeof(struct v3_mem_region)); + + if (copy_from_user(&mem, argp, sizeof(struct v3_mem_region))) { + printk("copy from user error getting mem_region...\n"); + return -EFAULT; + } + + printk("Adding %llu pages to Palacios memory\n", mem.num_pages); + + if (add_palacios_memory(mem.base_addr, mem.num_pages) == -1) { + printk("Error adding memory to Palacios\n"); + return -EFAULT; + } + + // Mem test... + /* + { + void * vaddr = __va(alloc_palacios_pgs(131072, 4096)); + memset(vaddr, 0xfe492fe2, mem.num_pages * 4096); + } + */ + + break; + } + default: + printk("\tUnhandled\n"); + return -EINVAL; + } + + return 0; +} + + + +static struct file_operations v3_ctrl_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = v3_dev_ioctl, + .compat_ioctl = v3_dev_ioctl, +}; + + +extern unsigned int v3_pages; +extern void * v3_base_addr; + +static int __init v3_init(void) { + dev_t dev = MKDEV(0, 0); // We dynamicallly assign the major number + int ret = 0; + + + palacios_init_mm(); + + v3_class = class_create(THIS_MODULE, "vms"); + if (IS_ERR(v3_class)) { + printk("Failed to register V3 VM device class\n"); + return PTR_ERR(v3_class); + } + + printk("intializing V3 Control device\n"); + + ret = alloc_chrdev_region(&dev, 0, MAX_VMS + 1, "v3vee"); + + if (ret < 0) { + printk("Error registering device region for V3 devices\n"); + goto failure2; + } + + v3_major_num = MAJOR(dev); + + dev = MKDEV(v3_major_num, MAX_VMS + 1); + + + printk("Creating V3 Control device: Major %d, Minor %d\n", v3_major_num, MINOR(dev)); + cdev_init(&ctrl_dev, &v3_ctrl_fops); + ctrl_dev.owner = THIS_MODULE; + ctrl_dev.ops = &v3_ctrl_fops; + cdev_add(&ctrl_dev, dev, 1); + + device_create(v3_class, NULL, dev, NULL, "v3vee"); + + if (ret != 0) { + printk("Error adding v3 control device\n"); + goto failure1; + } + + if ((v3_pages > 0) && (v3_base_addr != NULL)) { + add_palacios_memory(__pa(v3_base_addr), v3_pages); + } + + // Initialize Palacios + + palacios_vmm_init(); + + palacios_init_stream(); + palacios_file_init(); + palacios_init_console(); + + + + return 0; + + failure1: + unregister_chrdev_region(MKDEV(v3_major_num, 0), MAX_VMS + 1); + failure2: + class_destroy(v3_class); + + return ret; +} + +static void __exit v3_exit(void) { + extern u32 pg_allocs; + extern u32 pg_frees; + extern u32 mallocs; + extern u32 frees; + + printk("Removing V3 Control device\n"); + + palacios_vmm_exit(); + + + printk("Palacios Mallocs = %d, Frees = %d\n", mallocs, frees); + printk("Palacios Page Allocs = %d, Page Frees = %d\n", pg_allocs, pg_frees); + + + unregister_chrdev_region(MKDEV(v3_major_num, 0), MAX_VMS + 1); +} + + + +module_init(v3_init); +module_exit(v3_exit); diff --git a/linux_module/palacios-file.c b/linux_module/palacios-file.c new file mode 100644 index 0000000..d26fcb2 --- /dev/null +++ b/linux_module/palacios-file.c @@ -0,0 +1,158 @@ +/* Palacios file interface + * (c) Jack Lange, 2010 + */ + + +#include +#include +#include +#include +#include + +#include "palacios.h" + +#include + +static struct list_head global_files; + +struct palacios_file { + struct file * filp; + + char * path; + int mode; + + spinlock_t lock; + + struct v3_guest * guest; + + + struct list_head file_node; +}; + + + +static void * palacios_file_open(const char * path, int mode, void * private_data) { + struct v3_guest * guest = (struct v3_guest *)private_data; + struct palacios_file * pfile = NULL; + + pfile = kmalloc(sizeof(struct palacios_file), GFP_KERNEL); + memset(pfile, 0, sizeof(struct palacios_file)); + + if ((mode & FILE_OPEN_MODE_READ) && (mode & FILE_OPEN_MODE_WRITE)) { + pfile->mode = O_RDWR; + } else if (mode & FILE_OPEN_MODE_READ) { + pfile->mode = O_RDONLY; + } else if (mode & FILE_OPEN_MODE_WRITE) { + pfile->mode = O_WRONLY; + } + + pfile->filp = filp_open(path, pfile->mode, 0); + + if (pfile->filp == NULL) { + printk("Cannot open file: %s\n", path); + return NULL; + } + + pfile->path = kmalloc(strlen(path) + 1, GFP_KERNEL); + strncpy(pfile->path, path, strlen(path)); + pfile->guest = guest; + + spin_lock_init(&(pfile->lock)); + + if (guest == NULL) { + list_add(&(pfile->file_node), &(global_files)); + } else { + list_add(&(pfile->file_node), &(guest->files)); + } + + + return pfile; +} + +static int palacios_file_close(void * file_ptr) { + struct palacios_file * pfile = (struct palacios_file *)file_ptr; + + filp_close(pfile->filp, NULL); + + list_del(&(pfile->file_node)); + + kfree(pfile); + + return 0; +} + +static long long palacios_file_size(void * file_ptr) { + struct palacios_file * pfile = (struct palacios_file *)file_ptr; + struct file * filp = pfile->filp; + struct kstat s; + int ret; + + ret = vfs_getattr(filp->f_path.mnt, filp->f_path.dentry, &s); + + if (ret != 0) { + printk("Failed to fstat file\n"); + return -1; + } + + return s.size; +} + +static long long palacios_file_read(void * file_ptr, void * buffer, long long length, long long offset){ + struct palacios_file * pfile = (struct palacios_file *)file_ptr; + struct file * filp = pfile->filp; + ssize_t ret; + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(get_ds()); + + ret = vfs_read(filp, buffer, length, &offset); + + set_fs(old_fs); + + if (ret <= 0) { + printk("sys_read of %p for %lld bytes failed\n", filp, length); + } + + return ret; +} + + +static long long palacios_file_write(void * file_ptr, void * buffer, long long length, long long offset) { + struct palacios_file * pfile = (struct palacios_file *)file_ptr; + struct file * filp = pfile->filp; + mm_segment_t old_fs; + ssize_t ret; + + old_fs = get_fs(); + set_fs(get_ds()); + + ret = vfs_write(filp, buffer, length, &offset); + + set_fs(old_fs); + + + if (ret <= 0) { + printk("sys_write failed\n"); + } + + return ret; +} + + +static struct v3_file_hooks palacios_file_hooks = { + .open = palacios_file_open, + .close = palacios_file_close, + .read = palacios_file_read, + .write = palacios_file_write, + .size = palacios_file_size, +}; + + +int palacios_file_init( void ) { + INIT_LIST_HEAD(&(global_files)); + + V3_Init_File(&palacios_file_hooks); + + return 0; +} diff --git a/linux_module/palacios-file.h b/linux_module/palacios-file.h new file mode 100644 index 0000000..7e2180c --- /dev/null +++ b/linux_module/palacios-file.h @@ -0,0 +1,6 @@ +#ifndef __PALACIOS_FILE_H__ +#define __PALACISO_FILE_H__ + +int palacios_file_init(void); + +#endif diff --git a/linux_module/palacios-mm.c b/linux_module/palacios-mm.c new file mode 100644 index 0000000..a6505e6 --- /dev/null +++ b/linux_module/palacios-mm.c @@ -0,0 +1,194 @@ +/* Palacios memory manager + * (c) Jack Lange, 2010 + */ + +#include +#include +#include +#include +#include +//static struct list_head pools; + + +struct mempool { + uintptr_t base_addr; + u64 num_pages; + + u8 * bitmap; +}; + + +static struct mempool pool; + +static inline int get_page_bit(int index) { + int major = index / 8; + int minor = index % 8; + + return (pool.bitmap[major] & (0x1 << minor)); +} + +static inline void set_page_bit(int index) { + int major = index / 8; + int minor = index % 8; + + pool.bitmap[major] |= (0x1 << minor); +} + +static inline void clear_page_bit(int index) { + int major = index / 8; + int minor = index % 8; + + pool.bitmap[major] &= ~(0x1 << minor); +} + + + +static uintptr_t alloc_contig_pgs(u64 num_pages, u32 alignment) { + int step = 1; + int i = 0; + int start = 0; + + printk("Allocating %llu pages (align=%lu)\n", + num_pages, (unsigned long)alignment); + + if (pool.bitmap == NULL) { + printk("ERROR: Attempting to allocate from non initialized memory\n"); + return 0; + } + + if (alignment > 0) { + step = alignment / 4096; + } + + // Start the search at the correct alignment + if (pool.base_addr % alignment) { + start = ((alignment - (pool.base_addr % alignment)) >> 12); + } + + printk("\t Start idx %d (base_addr=%llu)\n", start, (u64)pool.base_addr); + + for (i = start; i < (pool.num_pages - num_pages); i += step) { + if (get_page_bit(i) == 0) { + int j = 0; + int collision = 0; + + for (j = i; (j - i) < num_pages; j++) { + if (get_page_bit(j) == 1) { + collision = 1; + break; + } + } + + if (collision == 1) { + break; + } + + for (j = i; (j - i) < num_pages; j++) { + set_page_bit(j); + } + + return pool.base_addr + (i * 4096); + } + } + + return 0; +} + + +// alignment is in bytes +uintptr_t alloc_palacios_pgs(u64 num_pages, u32 alignment) { + uintptr_t addr = 0; + + if ((num_pages < 12)) { + struct page * pgs = NULL; + int order = get_order(num_pages * PAGE_SIZE); + + pgs = alloc_pages(GFP_KERNEL, order); + + WARN(!pgs, "Could not allocate pages\n"); + + printk("%llu pages (order=%d) aquired from alloc_pages\n", + num_pages, order); + + addr = page_to_pfn(pgs) << PAGE_SHIFT; + } else { + printk("Allocating %llu pages from bitmap allocator\n", num_pages); + //addr = pool.base_addr; + addr = alloc_contig_pgs(num_pages, alignment); + } + + + printk("Returning from alloc addr=%p, vaddr=%p\n", (void *)addr, __va(addr)); + return addr; +} + + + +void free_palacios_pgs(uintptr_t pg_addr, int num_pages) { + printk("Freeing Memory page %p\n", (void *)pg_addr); + + if ((pg_addr >= pool.base_addr) && + (pg_addr < pool.base_addr + (4096 * pool.num_pages))) { + int pg_idx = (pg_addr - pool.base_addr) / 4096; + int i = 0; + + if ((pg_idx + num_pages) > pool.num_pages) { + printk("Freeing memory bounds exceeded\n"); + return; + } + + for (i = 0; i < num_pages; i++) { + WARN(get_page_bit(pg_idx + i) == 0, "Trying to free unallocated page\n"); + + clear_page_bit(pg_idx + i); + } + } else { + __free_pages(pfn_to_page(pg_addr >> PAGE_SHIFT), get_order(num_pages * PAGE_SIZE)); + } +} + + +int add_palacios_memory(uintptr_t base_addr, u64 num_pages) { + /* JRL: OK.... so this is horrible, terrible and if anyone else did it I would yell at them. + * But... the fact that you can do this in C is so ridiculous that I can't help myself. + * Note that we're repurposing "true" to be 1 here + */ + + int bitmap_size = (num_pages / 8) + ((num_pages % 8) > 0); + + if (pool.num_pages != 0) { + printk("ERROR: Memory has already been added\n"); + return -1; + } + + printk("Managing %dMB of memory starting at %llu (%lluMB)\n", + (unsigned int)(num_pages * 4096) / (1024 * 1024), + (unsigned long long)base_addr, + (unsigned long long)(base_addr / (1024 * 1024))); + + + pool.bitmap = kmalloc(bitmap_size, GFP_KERNEL); + + if (IS_ERR(pool.bitmap)) { + printk("Error allocating Palacios MM bitmap\n"); + return -1; + } + + memset(pool.bitmap, 0, bitmap_size); + + pool.base_addr = base_addr; + pool.num_pages = num_pages; + + return 0; +} + + + +int palacios_init_mm( void ) { + // INIT_LIST_HEAD(&(pools)); + pool.base_addr = 0; + pool.num_pages = 0; + pool.bitmap = NULL; + + return 0; +} diff --git a/linux_module/palacios-mm.h b/linux_module/palacios-mm.h new file mode 100644 index 0000000..940467c --- /dev/null +++ b/linux_module/palacios-mm.h @@ -0,0 +1,21 @@ +/* Palacios memory manager + * (c) Jack Lange, 2010 + */ + +#ifndef PALACIOS_MM_H +#define PALACIOS_MM_H + + + +uintptr_t alloc_palacios_pgs(u64 num_pages, u32 alignment); +void free_palacios_pg(uintptr_t base_addr); +void free_palacios_pgs(uintptr_t base_addr, u64 num_pages); + + +int add_palacios_memory(uintptr_t base_addr, u64 num_pages); +int remove_palacios_memory(uintptr_t base_addr, u64 num_pages); +int palacios_init_mm( void ); + + + +#endif diff --git a/linux_module/palacios-packet.c b/linux_module/palacios-packet.c new file mode 100644 index 0000000..f6f4c2c --- /dev/null +++ b/linux_module/palacios-packet.c @@ -0,0 +1,204 @@ +/* + * VM Raw Packet + * (c) Lei Xia, 2010 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "palacios.h" +#include "palacios-packet.h" + +//#define DEBUG_PALACIOS_PACKET + +static struct socket * raw_sock; + +static int packet_inited = 0; + +static int init_raw_socket (const char * eth_dev){ + int err; + struct sockaddr_ll sock_addr; + struct ifreq if_req; + int dev_idx; + + err = sock_create(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL), &raw_sock); + if (err < 0) { + printk(KERN_WARNING "Could not create a PF_PACKET Socket, err %d\n", err); + return -1; + } + + if(eth_dev == NULL){ + return 0; + } + + memset(&if_req, 0, sizeof(if_req)); + strncpy(if_req.ifr_name, eth_dev, sizeof(if_req.ifr_name)); + + err = raw_sock->ops->ioctl(raw_sock, SIOCGIFINDEX, (long)&if_req); + if(err < 0){ + printk(KERN_WARNING "Palacios Packet: Unable to get index for device %s, error %d\n", if_req.ifr_name, err); + dev_idx = 2; /* default "eth0" */ + } + else{ + dev_idx = if_req.ifr_ifindex; + } + + printk(KERN_INFO "Palacios Packet: bind to device index: %d\n", dev_idx); + + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sll_family = PF_PACKET; + sock_addr.sll_protocol = htons(ETH_P_ALL); + sock_addr.sll_ifindex = dev_idx; + + err = raw_sock->ops->bind(raw_sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)); + if (err < 0){ + printk(KERN_WARNING "Error binding raw packet to device %s, %d\n", eth_dev, err); + return -1; + } + + printk(KERN_INFO "Bind palacios raw packet interface to device %s\n", eth_dev); + + return 0; +} + + +static int +palacios_packet_send(const char * pkt, unsigned int len, void * private_data) { + struct msghdr msg; + struct iovec iov; + mm_segment_t oldfs; + int size = 0; + +#ifdef DEBUG_PALACIOS_PACKET + { + printk("Palacios Packet: send pkt to NIC (size: %d)\n", + len); + //print_hex_dump(NULL, "pkt_data: ", 0, 20, 20, pkt, len, 0); + } +#endif + + + iov.iov_base = (void *)pkt; + iov.iov_len = (__kernel_size_t)len; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_flags = 0; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + size = sock_sendmsg(raw_sock, &msg, len); + set_fs(oldfs); + + return size; +} + + +static struct v3_packet_hooks palacios_packet_hooks = { + .send = palacios_packet_send, +}; + + +static int +recv_pkt(char * pkt, int len) { + struct msghdr msg; + struct iovec iov; + mm_segment_t oldfs; + int size = 0; + + if (raw_sock == NULL) { + return -1; + } + + iov.iov_base = pkt; + iov.iov_len = len; + + msg.msg_flags = 0; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + size = sock_recvmsg(raw_sock, &msg, len, msg.msg_flags); + set_fs(oldfs); + + return size; +} + + +void +send_raw_packet_to_palacios(char * pkt, + int len, + struct v3_vm_info * vm) { + struct v3_packet_event event; + + event.pkt = kmalloc(len, GFP_KERNEL); + memcpy(event.pkt, pkt, len); + event.size = len; + + v3_deliver_packet_event(vm, &event); +} + +static int packet_server(void * arg) { + char pkt[ETHERNET_PACKET_LEN]; + int size; + + printk("Palacios Raw Packet Bridge: Staring receiving server\n"); + + while (!kthread_should_stop()) { + size = recv_pkt(pkt, ETHERNET_PACKET_LEN); + if (size < 0) { + printk(KERN_WARNING "Palacios Packet Socket receive error\n"); + break; + } + +#ifdef DEBUG_PALACIOS_PACKET + { + printk("Palacios Packet: receive pkt from NIC (size: %d)\n", + size); + //print_hex_dump(NULL, "pkt_data: ", 0, 20, 20, pkt, size, 0); + } +#endif + + send_raw_packet_to_palacios(pkt, size, NULL); + } + + return 0; +} + + +int palacios_init_packet(const char * eth_dev) { + + if(packet_inited == 0){ + packet_inited = 1; + init_raw_socket(eth_dev); + V3_Init_Packet(&palacios_packet_hooks); + + kthread_run(packet_server, NULL, "raw-packet-server"); + } + + return 0; +} + diff --git a/linux_module/palacios-packet.h b/linux_module/palacios-packet.h new file mode 100644 index 0000000..476f473 --- /dev/null +++ b/linux_module/palacios-packet.h @@ -0,0 +1,11 @@ +/* + * Palacios VM Raw Packet interface + * (c) Lei Xia, 2010 + */ + +#ifndef __PALACIOS_PACKET_H__ +#define __PALACIOS_PACKET_H__ + +int palacios_init_packet(const char * eth_dev); + +#endif diff --git a/linux_module/palacios-queue.c b/linux_module/palacios-queue.c new file mode 100644 index 0000000..3e0249b --- /dev/null +++ b/linux_module/palacios-queue.c @@ -0,0 +1,81 @@ +/* + * This file is part of the Palacios Virtual Machine Monitor developed + * by the V3VEE Project with funding from the United States National + * Science Foundation and the Department of Energy. + * + * The V3VEE Project is a joint project between Northwestern University + * and the University of New Mexico. You can find out more at + * http://www.v3vee.org + * + * Copyright (c) 2008, Jack Lange + * Copyright (c) 2008, The V3VEE Project + * All rights reserved. + * + * Author: Jack Lange + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "V3VEE_LICENSE". + */ + +#include + +#include "palacios-queue.h" + +void init_queue(struct gen_queue * queue, unsigned int max_entries) { + queue->num_entries = 0; + queue->max_entries = max_entries; + + INIT_LIST_HEAD(&(queue->entries)); + spin_lock_init(&(queue->lock)); +} + +struct gen_queue * create_queue(unsigned int max_entries) { + struct gen_queue * tmp_queue = kmalloc(sizeof(struct gen_queue), GFP_KERNEL); + init_queue(tmp_queue, max_entries); + return tmp_queue; +} + +int enqueue(struct gen_queue * queue, void * entry) { + struct queue_entry * q_entry = NULL; + unsigned long flags; + + if (queue->num_entries >= queue->max_entries) { + return -1; + } + + q_entry = kmalloc(sizeof(struct queue_entry), GFP_KERNEL); + + spin_lock_irqsave(&(queue->lock), flags); + + q_entry->entry = entry; + list_add_tail(&(q_entry->node), &(queue->entries)); + queue->num_entries++; + + spin_unlock_irqrestore(&(queue->lock), flags); + + return 0; +} + + +void * dequeue(struct gen_queue * queue) { + void * entry_val = 0; + unsigned long flags; + + spin_lock_irqsave(&(queue->lock), flags); + + if (!list_empty(&(queue->entries))) { + struct list_head * q_entry = queue->entries.next; + struct queue_entry * tmp_entry = list_entry(q_entry, struct queue_entry, node); + + entry_val = tmp_entry->entry; + list_del(q_entry); + kfree(tmp_entry); + + queue->num_entries--; + + } + + spin_unlock_irqrestore(&(queue->lock), flags); + + return entry_val; +} diff --git a/linux_module/palacios-queue.h b/linux_module/palacios-queue.h new file mode 100644 index 0000000..f35f05e --- /dev/null +++ b/linux_module/palacios-queue.h @@ -0,0 +1,55 @@ +/* + * This file is part of the Palacios Virtual Machine Monitor developed + * by the V3VEE Project with funding from the United States National + * Science Foundation and the Department of Energy. + * + * The V3VEE Project is a joint project between Northwestern University + * and the University of New Mexico. You can find out more at + * http://www.v3vee.org + * + * Copyright (c) 2008, Jack Lange + * Copyright (c) 2008, The V3VEE Project + * All rights reserved. + * + * Author: Jack Lange + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "V3VEE_LICENSE". + */ + + +#ifndef __PALACIOS_QUEUE_H__ +#define __PALACIOS_QUEUE_H__ + + + +#include +#include + + + +struct queue_entry { + void * entry; + struct list_head node; +}; + + +struct gen_queue { + unsigned int num_entries; + unsigned int max_entries; + struct list_head entries; + spinlock_t lock; +}; + + +struct gen_queue * create_queue(unsigned int max_entries); +void init_queue(struct gen_queue * queue, unsigned int max_entries); + +int enqueue(struct gen_queue * queue, void * entry); +void * dequeue(struct gen_queue * queue); + + + + + +#endif diff --git a/linux_module/palacios-ringbuf.h b/linux_module/palacios-ringbuf.h new file mode 100644 index 0000000..aca8ba8 --- /dev/null +++ b/linux_module/palacios-ringbuf.h @@ -0,0 +1,14 @@ +/* Ringbuffer implementation for Palacios + */ + +#ifndef PALACIOS_RINGBUF_H +#define PALACIOS_RINGBUF_H + +extern struct v3_ringbuf * v3_create_ringbuf(unsigned int size); +extern void v3_free_ringbuf(struct v3_ringbuf * ring); +extern int v3_ringbuf_read(struct v3_ringbuf * ring, unsigned char * dst, unsigned int len); +extern int v3_ringbuf_write(struct v3_ringbuf * ring, unsigned char * src, unsigned int len); + + + +#endif diff --git a/linux_module/palacios-ringbuffer.c b/linux_module/palacios-ringbuffer.c new file mode 100644 index 0000000..2ab5b50 --- /dev/null +++ b/linux_module/palacios-ringbuffer.c @@ -0,0 +1,178 @@ +/* + * Ringbuffer Routines for VM + * (c) Lei Xia, 2010 + */ +#include +#include +#include + +#include "palacios.h" +#include "palacios-ringbuffer.h" + +void init_ringbuf(struct ringbuf * ring, unsigned int size) { + ring->buf = kmalloc(size, GFP_KERNEL); + ring->size = size; + + ring->start = 0; + ring->end = 0; + ring->current_len = 0; +} + +struct ringbuf * create_ringbuf(unsigned int size) { + struct ringbuf * ring = (struct ringbuf *)kmalloc(sizeof(struct ringbuf), GFP_KERNEL); + init_ringbuf(ring, size); + + return ring; +} + +void free_ringbuf(struct ringbuf * ring) { + kfree(ring->buf); + kfree(ring); +} + +static inline unsigned char * get_read_ptr(struct ringbuf * ring) { + return (unsigned char *)(ring->buf + ring->start); +} + + +static inline unsigned char * get_write_ptr(struct ringbuf * ring) { + return (unsigned char *)(ring->buf + ring->end); +} + +static inline int get_read_section_size(struct ringbuf * ring) { + return ring->size - ring->start; +} + + +static inline int get_write_section_size(struct ringbuf * ring) { + return ring->size - ring->end; +} + + +static inline int is_read_loop(struct ringbuf * ring, unsigned int len) { + if ((ring->start >= ring->end) && (ring->current_len > 0)) { + // end is past the end of the buffer + if (get_read_section_size(ring) < len) { + return 1; + } + } + return 0; +} + + +static inline int is_write_loop(struct ringbuf * ring, unsigned int len) { + if ((ring->end >= ring->start) && (ring->current_len < ring->size)) { + // end is past the end of the buffer + if (get_write_section_size(ring) < len) { + return 1; + } + } + return 0; +} + + +static inline int ringbuf_avail_space(struct ringbuf * ring) { + return ring->size - ring->current_len; +} + + +int ringbuf_data_len(struct ringbuf * ring) { + return ring->current_len; +} + + +static inline int ringbuf_capacity(struct ringbuf * ring) { + return ring->size; +} + + +int ringbuf_read(struct ringbuf * ring, unsigned char * dst, unsigned int len) { + int read_len = 0; + int ring_data_len = ring->current_len; + + read_len = (len > ring_data_len) ? ring_data_len : len; + + if (is_read_loop(ring, read_len)) { + int section_len = get_read_section_size(ring); + + memcpy(dst, get_read_ptr(ring), section_len); + memcpy(dst + section_len, ring->buf, read_len - section_len); + + ring->start = read_len - section_len; + } else { + memcpy(dst, get_read_ptr(ring), read_len); + + ring->start += read_len; + } + + ring->current_len -= read_len; + + return read_len; +} + + +#if 0 + +static int ringbuf_peek(struct ringbuf * ring, unsigned char * dst, unsigned int len) { + int read_len = 0; + int ring_data_len = ring->current_len; + + read_len = (len > ring_data_len) ? ring_data_len : len; + + if (is_read_loop(ring, read_len)) { + int section_len = get_read_section_size(ring); + + memcpy(dst, get_read_ptr(ring), section_len); + memcpy(dst + section_len, ring->buf, read_len - section_len); + } else { + memcpy(dst, get_read_ptr(ring), read_len); + } + + return read_len; +} + + +static int ringbuf_delete(struct ringbuf * ring, unsigned int len) { + int del_len = 0; + int ring_data_len = ring->current_len; + + del_len = (len > ring_data_len) ? ring_data_len : len; + + if (is_read_loop(ring, del_len)) { + int section_len = get_read_section_size(ring); + ring->start = del_len - section_len; + } else { + ring->start += del_len; + } + + ring->current_len -= del_len; + return del_len; +} +#endif + +int ringbuf_write(struct ringbuf * ring, unsigned char * src, unsigned int len) { + int write_len = 0; + int ring_avail_space = ring->size - ring->current_len; + + write_len = (len > ring_avail_space) ? ring_avail_space : len; + + if (is_write_loop(ring, write_len)) { + int section_len = get_write_section_size(ring); + + memcpy(get_write_ptr(ring), src, section_len); + ring->end = 0; + + memcpy(get_write_ptr(ring), src + section_len, write_len - section_len); + + ring->end += write_len - section_len; + } else { + memcpy(get_write_ptr(ring), src, write_len); + + ring->end += write_len; + } + + ring->current_len += write_len; + + return write_len; +} + diff --git a/linux_module/palacios-ringbuffer.h b/linux_module/palacios-ringbuffer.h new file mode 100644 index 0000000..eb39025 --- /dev/null +++ b/linux_module/palacios-ringbuffer.h @@ -0,0 +1,26 @@ +/* + * Ringbuffer Routines for VM + * (c) Lei Xia, 2010 + */ + +#ifndef __PALACIOS_RING_BUFFER_H__ +#define __PALACIOS_RING_BUFFER_H__ + +struct ringbuf { + unsigned char * buf; + unsigned int size; + + unsigned int start; + unsigned int end; + unsigned int current_len; +}; + + +struct ringbuf * create_ringbuf(unsigned int size); +void free_ringbuf(struct ringbuf * ring); +int ringbuf_read(struct ringbuf * ring, unsigned char * dst, unsigned int len); +int ringbuf_write(struct ringbuf * ring, unsigned char * src, unsigned int len); +int ringbuf_data_len(struct ringbuf * ring); + +#endif + diff --git a/linux_module/palacios-serial.c b/linux_module/palacios-serial.c new file mode 100644 index 0000000..355f4d4 --- /dev/null +++ b/linux_module/palacios-serial.c @@ -0,0 +1,132 @@ +/* + * VM Serial Controls + * (c) Lei Xia, 2010 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "palacios.h" +#include "palacios-stream.h" + + +void +send_serial_input_to_palacios( unsigned char *input, + unsigned int len, + struct v3_vm_info * vm ) { + struct v3_serial_event event; + + if (len > 128) { + len = 128; + } + + memcpy(event.data, input, len); + event.len = len; + + v3_deliver_serial_event(vm, &event); +} + +static ssize_t +serial_read(struct file * filp, char __user * buf, size_t size, loff_t * offset) { + + int len = 0; + char temp[128]; + struct stream_buffer * stream = filp->private_data; + + memset(temp, 0, 128); + + if (size > 128) { + size = 128; + } + + len = stream_dequeue(stream, temp, size); + + if (copy_to_user(buf, temp, len)) { + printk("Read fault\n"); + return -EFAULT; + } + + printk("Returning %d bytes\n", len); + + return len; +} + +static ssize_t +serial_write(struct file * filp, const char __user * buf, size_t size, loff_t * offset) { + char temp[128]; + struct stream_buffer * stream = filp->private_data; + struct v3_vm_info * vm; + + memset(temp, 0, 128); + + if (size > 128) { + size = 128; + } + + if (copy_from_user(temp, buf, size)) { + printk("Write fault\n"); + return -EFAULT; + } + + vm = stream->guest->v3_ctx; + send_serial_input_to_palacios(temp, size, vm); + + return size; +} + + +static unsigned int +serial_poll(struct file * filp, struct poll_table_struct * poll_tb) { + unsigned int mask = 0; + struct stream_buffer *stream = filp->private_data; + + poll_wait(filp, &(stream->intr_queue), poll_tb); + + if(stream_datalen(stream) > 0){ + mask = POLLIN | POLLRDNORM; + } + + printk("polling v3 serial\n"); + + return mask; +} + +static struct file_operations v3_cons_fops = { + .read = serial_read, + .write = serial_write, + .poll = serial_poll, +}; + + +int open_serial(char * name) { + int cons_fd; + void *stream; + + printk("open path: %s\n", name); + + stream = find_stream_by_name(NULL, name); + + if (stream == NULL) { + return -1; + } + + cons_fd = anon_inode_getfd("v3-cons", &v3_cons_fops, stream, 0); + + if (cons_fd < 0) { + printk("Error creating serial inode\n"); + return cons_fd; + } + + printk("Returning new serial fd\n"); + + return cons_fd; +} diff --git a/linux_module/palacios-serial.h b/linux_module/palacios-serial.h new file mode 100644 index 0000000..e9fa8a0 --- /dev/null +++ b/linux_module/palacios-serial.h @@ -0,0 +1,11 @@ +/* + * Palacios VM Stream Serial interface + * (c) Jack Lange, 2010 + */ + +#ifndef __PALACIOS_SERIAL_H__ +#define __PALACIOS_SERIAL_H__ + +int open_serial(char * name); + +#endif diff --git a/linux_module/palacios-socket.c b/linux_module/palacios-socket.c new file mode 100644 index 0000000..d76c5c6 --- /dev/null +++ b/linux_module/palacios-socket.c @@ -0,0 +1,438 @@ +/* + * This file is part of the Palacios Virtual Machine Monitor developed + * by the V3VEE Project with funding from the United States National + * Science Foundation and the Department of Energy. + * + * The V3VEE Project is a joint project between Northwestern University + * and the University of New Mexico. You can find out more at + * http://www.v3vee.org + * + * Copyright (c) 2010, Lei Xia + * Copyright (c) 2010, The V3VEE Project + * All rights reserved. + * + * This is free software. You are permitted to use, redistribute, + * and modify it under the terms of the GNU General Public License + * Version 2 (GPLv2). The accompanying COPYING file contains the + * full text of the license. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "palacios.h" + + +struct palacios_socket { + struct socket * sock; + + struct v3_guest * guest; + struct list_head sock_node; +}; + +static struct list_head global_sockets; + +//ignore the arguments given here currently +static void * +palacios_tcp_socket( + const int bufsize, + const int nodelay, + const int nonblocking, + void * private_data +) +{ + struct v3_guest * guest = (struct v3_guest *)private_data; + struct palacios_socket * sock = NULL; + int err; + + sock = kmalloc(sizeof(struct palacios_socket), GFP_KERNEL); + memset(sock, 0, sizeof(struct palacios_socket)); + + err = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, &(sock->sock)); + + if (err < 0) { + kfree(sock); + return NULL; + } + + sock->guest = guest; + + if (guest == NULL) { + list_add(&(sock->sock_node), &global_sockets); + } else { + list_add(&(sock->sock_node), &(guest->sockets)); + } + + return sock; +} + +//ignore the arguments given here currently +static void * +palacios_udp_socket( + const int bufsize, + const int nonblocking, + void * private_data +) +{ + struct v3_guest * guest = (struct v3_guest *)private_data; + struct palacios_socket * sock = NULL; + int err; + + sock = kmalloc(sizeof(struct palacios_socket), GFP_KERNEL); + memset(sock, 0, sizeof(struct palacios_socket)); + + err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &(sock->sock)) ; + + if (err < 0){ + kfree(sock); + return NULL; + } + + + sock->guest = guest; + + if (guest == NULL) { + list_add(&(sock->sock_node), &global_sockets); + } else { + list_add(&(sock->sock_node), &(guest->sockets)); + } + + return sock; +} + + +static void +palacios_close(void * sock_ptr) +{ + struct palacios_socket * sock = (struct palacios_socket *)sock_ptr; + + if (sock != NULL) { + sock->sock->ops->release(sock->sock); + + list_del(&(sock->sock_node)); + kfree(sock); + } +} + +static int +palacios_bind_socket( + const void * sock_ptr, + const int port +) +{ + struct sockaddr_in addr; + struct palacios_socket * sock = (struct palacios_socket *)sock_ptr; + + if (sock == NULL) { + return -1; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = INADDR_ANY; + + return sock->sock->ops->bind(sock->sock, (struct sockaddr *)&addr, sizeof(addr)); +} + +static int +palacios_listen( + const void * sock_ptr, + int backlog +) +{ + struct palacios_socket * sock = (struct palacios_socket *)sock_ptr; + + if (sock == NULL) { + return -1; + } + + return sock->sock->ops->listen(sock->sock, backlog); +} + +static void * +palacios_accept( + const void * sock_ptr, + unsigned int * remote_ip, + unsigned int * port +) +{ + struct palacios_socket * sock = (struct palacios_socket *)sock_ptr; + struct palacios_socket * newsock = NULL; + int err; + + if (sock == NULL) { + return NULL; + } + + newsock = kmalloc(sizeof(struct palacios_socket), GFP_KERNEL); + + err = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, &(newsock->sock)); + + if (err < 0) { + kfree(newsock); + return NULL; + } + + newsock->sock->type = sock->sock->type; + newsock->sock->ops = sock->sock->ops; + + err = newsock->sock->ops->accept(sock->sock, newsock->sock, 0); + + if (err < 0){ + kfree(newsock); + return NULL; + } + + //TODO: How do we get ip & port? + + + newsock->guest = sock->guest; + + if (sock->guest == NULL) { + list_add(&(newsock->sock_node), &global_sockets); + } else { + list_add(&(newsock->sock_node), &(sock->guest->sockets)); + } + + return newsock; +} + +static int +palacios_select( + struct v3_sock_set * rset, + struct v3_sock_set * wset, + struct v3_sock_set * eset, + struct v3_timeval tv) +{ + //TODO: + + return 0; +} + +static int +palacios_connect_to_ip( + const void * sock_ptr, + const int hostip, + const int port +) +{ + struct sockaddr_in client; + struct palacios_socket * sock = (struct palacios_socket *)sock_ptr; + + if (sock == NULL) { + return -1; + } + + client.sin_family = AF_INET; + client.sin_port = htons(port); + client.sin_addr.s_addr = htonl(hostip); + + return sock->sock->ops->connect(sock->sock, (struct sockaddr *)&client, sizeof(client), 0); +} + +static int +palacios_send( + const void * sock_ptr, + const char * buf, + const int len +) +{ + struct palacios_socket * sock = (struct palacios_socket *)sock_ptr; + struct msghdr msg; + mm_segment_t oldfs; + struct iovec iov; + int err = 0; + + if (sock == NULL) { + return -1; + } + + msg.msg_flags = MSG_NOSIGNAL;//0/*MSG_DONTWAIT*/;; + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + iov.iov_base = (char *)buf; + iov.iov_len = (size_t)len; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sock_sendmsg(sock->sock, &msg, (size_t)len); + + set_fs(oldfs); + + return err; +} + +static int +palacios_recv( + const void * sock_ptr, + char * buf, + const int len +) +{ + + struct palacios_socket * sock = (struct palacios_socket *)sock_ptr; + struct msghdr msg; + mm_segment_t oldfs; + struct iovec iov; + int err; + + if (sock == NULL) { + return -1; + } + + msg.msg_flags = 0; + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + iov.iov_base = (void *)&buf[0]; + iov.iov_len = (size_t)len; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sock_recvmsg(sock->sock, &msg, (size_t)len, 0/*MSG_DONTWAIT*/); + + set_fs(oldfs); + + return err; +} + +static int +palacios_sendto_ip( + const void * sock_ptr, + const int ip_addr, + const int port, + const char * buf, + const int len +) +{ + struct palacios_socket * sock = (struct palacios_socket *)sock_ptr; + struct msghdr msg; + mm_segment_t oldfs; + struct iovec iov; + struct sockaddr_in dst; + int err = 0; + + if (sock == NULL) { + return -1; + } + + dst.sin_family = AF_INET; + dst.sin_port = htons(port); + dst.sin_addr.s_addr = htonl(ip_addr); + + msg.msg_flags = MSG_NOSIGNAL;//0/*MSG_DONTWAIT*/;; + msg.msg_name = &dst; + msg.msg_namelen = sizeof(struct sockaddr_in); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + iov.iov_base = (char *)buf; + iov.iov_len = (size_t)len; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sock_sendmsg(sock->sock, &msg, (size_t)len); + + set_fs(oldfs); + + return err; +} + + +// TODO: +static int +palacios_recvfrom_ip( + const void * sock_ptr, + const int ip_addr, + const int port, + char * buf, + const int len +) +{ + struct palacios_socket * sock = (struct palacios_socket *)sock_ptr; + struct sockaddr_in src; + int alen; + struct msghdr msg; + mm_segment_t oldfs; + struct iovec iov; + int err; + + if (sock == NULL) { + return -1; + } + + src.sin_family = AF_INET; + src.sin_port = htons(port); + src.sin_addr.s_addr = htonl(ip_addr); + alen = sizeof(src); + + + msg.msg_flags = 0; + msg.msg_name = &src; + msg.msg_namelen = sizeof(struct sockaddr_in); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + iov.iov_base = (void *)&buf[0]; + iov.iov_len = (size_t)len; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sock_recvmsg(sock->sock, &msg, (size_t)len, 0/*MSG_DONTWAIT*/); + + set_fs(oldfs); + + return err; +} + +struct v3_socket_hooks palacios_sock_hooks = { + .tcp_socket = palacios_tcp_socket, + .udp_socket = palacios_udp_socket, + .close = palacios_close, + .bind = palacios_bind_socket, + .listen = palacios_listen, + .accept = palacios_accept, + .select = palacios_select, + .connect_to_ip = palacios_connect_to_ip, + .connect_to_host = NULL, + .send = palacios_send, + .recv = palacios_recv, + .sendto_host = NULL, + .sendto_ip = palacios_sendto_ip, + .recvfrom_host = NULL, + .recvfrom_ip = palacios_recvfrom_ip, +}; + +int palacios_socket_init( void ) { + V3_Init_Sockets(&palacios_sock_hooks); + INIT_LIST_HEAD(&global_sockets); + return 0; +} diff --git a/linux_module/palacios-stream.c b/linux_module/palacios-stream.c new file mode 100644 index 0000000..815a151 --- /dev/null +++ b/linux_module/palacios-stream.c @@ -0,0 +1,122 @@ + +/* + * VM specific Controls + * (c) Lei Xia, 2010 + */ +#include +#include +#include + +#include +#include "palacios-stream.h" +#include "palacios-ringbuf.h" + +static struct list_head global_streams; + +int stream_enqueue(struct stream_buffer * stream, char * buf, int len) { + int bytes = 0; + + bytes = ringbuf_write(stream->buf, buf, len); + + return bytes; +} + + +int stream_dequeue(struct stream_buffer * stream, char * buf, int len) { + int bytes = 0; + + bytes = ringbuf_read(stream->buf, buf, len); + + return bytes; +} + +int stream_datalen(struct stream_buffer * stream){ + return ringbuf_data_len(stream->buf); +} + + +struct stream_buffer * find_stream_by_name(struct v3_guest * guest, const char * name) { + struct stream_buffer * stream = NULL; + struct list_head * stream_list = NULL; + + if (guest == NULL) { + stream_list = &global_streams; + } else { + stream_list = &(guest->streams); + } + + list_for_each_entry(stream, stream_list, stream_node) { + if (strncmp(stream->name, name, STREAM_NAME_LEN) == 0) { + return stream; + } + } + + return NULL; +} + + +static void * palacios_stream_open(const char * name, void * private_data) { + struct v3_guest * guest = (struct v3_guest *)private_data; + struct stream_buffer * stream = NULL; + + if (find_stream_by_name(guest, name) != NULL) { + printk("Stream already exists\n"); + return NULL; + } + + stream = kmalloc(sizeof(struct stream_buffer), GFP_KERNEL); + + stream->buf = create_ringbuf(STREAM_BUF_SIZE); + stream->guest = guest; + + strncpy(stream->name, name, STREAM_NAME_LEN - 1); + + init_waitqueue_head(&(stream->intr_queue)); + spin_lock_init(&(stream->lock)); + + if (guest == NULL) { + list_add(&(stream->stream_node), &(global_streams)); + } else { + list_add(&(stream->stream_node), &(guest->streams)); + } + + return stream; +} + + +static int palacios_stream_write(void * stream_ptr, char * buf, int len) { + struct stream_buffer * stream = (struct stream_buffer *)stream_ptr; + int ret = 0; + + ret = stream_enqueue(stream, buf, len); + + if (ret > 0) { + wake_up_interruptible(&(stream->intr_queue)); + } + + return ret; +} + + +static void palacios_stream_close(void * stream_ptr) { + struct stream_buffer * stream = (struct stream_buffer *)stream_ptr; + + free_ringbuf(stream->buf); + list_del(&(stream->stream_node)); + kfree(stream); + +} + +struct v3_stream_hooks palacios_stream_hooks = { + .open = palacios_stream_open, + .write = palacios_stream_write, + .close = palacios_stream_close, +}; + + +void palacios_init_stream() { + INIT_LIST_HEAD(&(global_streams)); + V3_Init_Stream(&palacios_stream_hooks); +} + + diff --git a/linux_module/palacios-stream.h b/linux_module/palacios-stream.h new file mode 100644 index 0000000..7e9279f --- /dev/null +++ b/linux_module/palacios-stream.h @@ -0,0 +1,37 @@ +/* + * Palacios Stream interface + * (c) Lei Xia, 2010 + */ + +#ifndef __PALACIOS_STREAM_H__ +#define __PALACIOS_STREAM_H__ + +#include +#include +#include "palacios.h" +#include "palacios-ringbuffer.h" + +#define STREAM_BUF_SIZE 1024 +#define STREAM_NAME_LEN 128 + +struct stream_buffer { + char name[STREAM_NAME_LEN]; + struct ringbuf * buf; + + wait_queue_head_t intr_queue; + spinlock_t lock; + + struct v3_guest * guest; + struct list_head stream_node; +}; + + +void palacios_init_stream(void); +int stream_enqueue(struct stream_buffer * stream, char * buf, int len); +int stream_dequeue(struct stream_buffer * stream, char * buf, int len); +int stream_datalen(struct stream_buffer * stream); + +struct stream_buffer * find_stream_by_name(struct v3_guest * guest, const char * name); + +#endif + diff --git a/linux_module/palacios-vm.c b/linux_module/palacios-vm.c new file mode 100644 index 0000000..cd3ba2c --- /dev/null +++ b/linux_module/palacios-vm.c @@ -0,0 +1,177 @@ +/* + * VM specific Controls + * (c) Jack Lange, 2010 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#include + +#include "palacios.h" +#include "palacios-console.h" +#include "palacios-serial.h" +#include "palacios-vm.h" + +extern struct class * v3_class; +#define STREAM_NAME_LEN 128 + +static long v3_vm_ioctl(struct file * filp, + unsigned int ioctl, unsigned long arg) { + void __user * argp = (void __user *)arg; + char path_name[STREAM_NAME_LEN]; + + struct v3_guest * guest = filp->private_data; + + printk("V3 IOCTL %d\n", ioctl); + + switch (ioctl) { + + case V3_VM_CONSOLE_CONNECT: { + return connect_console(guest); + break; + } + case V3_VM_SERIAL_CONNECT: { + if (copy_from_user(path_name, argp, STREAM_NAME_LEN)) { + printk("copy from user error getting guest image...\n"); + return -EFAULT; + } + + return open_serial(path_name); + break; + } + case V3_VM_STOP: { + printk("Stopping VM\n"); + stop_palacios_vm(guest); + break; + } + default: + printk("\tUnhandled\n"); + return -EINVAL; + } + + return 0; +} + +static int v3_vm_open(struct inode * inode, struct file * filp) { + struct v3_guest * guest = container_of(inode->i_cdev, struct v3_guest, cdev); + filp->private_data = guest; + return 0; +} + + +static ssize_t v3_vm_read(struct file * filp, char __user * buf, size_t size, loff_t * offset) { + + return 0; +} + + +static ssize_t v3_vm_write(struct file * filp, const char __user * buf, size_t size, loff_t * offset) { + + + return 0; +} + + +static struct file_operations v3_vm_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = v3_vm_ioctl, + .compat_ioctl = v3_vm_ioctl, + .open = v3_vm_open, + .read = v3_vm_read, + .write = v3_vm_write, +}; + + + +extern int vm_running; +extern u32 pg_allocs; +extern u32 pg_frees; +extern u32 mallocs; +extern u32 frees; + + +int start_palacios_vm(void * arg) { + struct v3_guest * guest = (struct v3_guest *)arg; + int err; + + lock_kernel(); + daemonize(guest->name); +// allow_signal(SIGKILL); + unlock_kernel(); + + + + + guest->v3_ctx = v3_create_vm(guest->img, (void *)guest, guest->name); + + if (guest->v3_ctx == NULL) { + printk("palacios: failed to create vm\n"); + return -1; + } + + printk("Creating VM device: Major %d, Minor %d\n", MAJOR(guest->vm_dev), MINOR(guest->vm_dev)); + + cdev_init(&(guest->cdev), &v3_vm_fops); + + guest->cdev.owner = THIS_MODULE; + guest->cdev.ops = &v3_vm_fops; + + + printk("Adding VM device\n"); + err = cdev_add(&(guest->cdev), guest->vm_dev, 1); + + if (err) { + printk("Fails to add cdev\n"); + return -1; + } + + if (device_create(v3_class, NULL, guest->vm_dev, guest, "v3-vm%d", MINOR(guest->vm_dev)) == NULL){ + printk("Fails to create device\n"); + return -1; + } + + complete(&(guest->thread_done)); + + + printk("palacios: launching vm\n"); + + if (v3_start_vm(guest->v3_ctx, 0xffffffff) < 0) { + printk("palacios: launch of vm failed\n"); + return -1; + } + + complete(&(guest->thread_done)); + + + printk("palacios: vm completed. returning.\n"); + + return 0; +} + + + + +int stop_palacios_vm(struct v3_guest * guest) { + + v3_stop_vm(guest->v3_ctx); + + wait_for_completion(&(guest->thread_done)); + + v3_free_vm(guest->v3_ctx); + + + return 0; +} diff --git a/linux_module/palacios-vm.h b/linux_module/palacios-vm.h new file mode 100644 index 0000000..e0c5f74 --- /dev/null +++ b/linux_module/palacios-vm.h @@ -0,0 +1,14 @@ +/* + * Palacios VM interface + * (c) Jack Lange, 2010 + */ + +#ifndef __PALACIOS_VM_H__ +#define __PALACIOS_VM_H__ + +#include "palacios.h" + +int start_palacios_vm(void * arg); +int stop_palacios_vm(struct v3_guest * guest); + +#endif diff --git a/linux_module/palacios-vnet.c b/linux_module/palacios-vnet.c new file mode 100644 index 0000000..ad51ee5 --- /dev/null +++ b/linux_module/palacios-vnet.c @@ -0,0 +1,976 @@ +/* + Palacios VNET interface + (c) Lei Xia, 2010 + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "palacios-vnet.h" + +//#define DEBUG_VNET_BRIGE + +#define VNET_UDP_PORT 9000 + +struct vnet_route { + struct v3_vnet_route route; + + int route_idx; + + struct list_head node; +}; + + +struct vnet_link { + uint32_t dst_ip; + uint16_t dst_port; + + struct socket * sock; + struct sockaddr_in sock_addr; + + int link_idx; + + struct list_head node; +}; + +struct palacios_vnet_state { + uint32_t num_routes; + uint32_t num_links; + + struct list_head route_list; + struct list_head link_list; + + struct socket * serv_sock; + struct sockaddr_in serv_addr; + + /* The thread recving pkts from sockets. */ + struct task_struct * serv_thread; + spinlock_t lock; + + unsigned long pkt_sent, pkt_recv, pkt_drop, pkt_udp_recv, pkt_udp_send; +}; + + +static struct palacios_vnet_state vnet_state; + + +struct vnet_link * find_link_by_ip(uint32_t ip) { + struct vnet_link * link = NULL; + + list_for_each_entry(link, &(vnet_state.link_list), node) { + + if (link->dst_ip == ip) { + return link; + } + } + + return NULL; +} + +struct vnet_link * find_link_by_idx(int idx) { + struct vnet_link * link = NULL; + + list_for_each_entry(link, &(vnet_state.link_list), node) { + + if (link->link_idx == idx) { + return link; + } + } + return NULL; +} + +struct vnet_route * find_route_by_idx(int idx) { + struct vnet_route * route = NULL; + + list_for_each_entry(route, &(vnet_state.route_list), node) { + + if (route->route_idx == idx) { + return route; + } + } + + return NULL; +} + + +static int parse_mac_str(char * str, uint8_t * qual, uint8_t * mac) { + char * token; + + printk("Parsing MAC (%s)\n", str); + + if (strstr(str, "-")) { + token = strsep(&str, "-"); + + if (strnicmp("not", token, strlen("not")) == 0) { + *qual = MAC_NOT; + } else { + printk("Invalid MAC String token (%s)\n", token); + return -1; + } + } + + if (!strstr(str, ":")) { + if (strnicmp("any", str, strlen("any")) == 0) { + printk("qual = any\n"); + *qual = MAC_ANY; + } else if (strnicmp("none", str, strlen("none")) == 0) { + printk("qual = None\n"); + *qual = MAC_NONE; + } else { + printk("Invalid MAC Qual token (%s)\n", str); + return -1; + } + + } else { + int i = 0; + + *qual = MAC_ADDR; + + for (i = 0; i < 6; i++) { + token = strsep(&str, ":"); + mac[i] = simple_strtol(token, &token, 16); + } + } + + return 0; +} + +static int parse_route_str(char * str, struct v3_vnet_route * route) { + char * token = NULL; + struct vnet_link *link = NULL; + + // src MAC + token = strsep(&str, " "); + + if (!token) { + return -1; + } + + parse_mac_str(token, &(route->src_mac_qual), route->src_mac); + + // dst MAC + token = strsep(&str, " "); + + if (!token) { + return -1; + } + + parse_mac_str(token, &(route->dst_mac_qual), route->dst_mac); + + // dst LINK type + token = strsep(&str, " "); + + if (!token) { + return -1; + } + + if (strnicmp("interface", token, strlen("interface")) == 0) { + route->dst_type = LINK_INTERFACE; + printk("DST type = INTERFACE\n"); + } else if (strnicmp("edge", token, strlen("edge")) == 0) { + route->dst_type = LINK_EDGE; + printk("DST type = EDGE\n"); + } else { + printk("Invalid Destination Link Type (%s)\n", token); + return -1; + } + + + // dst link ID + token = strsep(&str, " "); + + if (!token) { + return -1; + } + + printk("dst link ID=%s\n", token); + + // Figure out link ID here + if (route->dst_type == LINK_EDGE) { + uint32_t link_ip; + + + // Figure out Link Here + if (in4_pton(token, strlen(token), (uint8_t *)&(link_ip), '\0', NULL) != 1) { + printk("Invalid Dst IP address (%s)\n", token); + return -EFAULT; + } + + + printk("link_ip = %d\n", link_ip); + link = find_link_by_ip(link_ip); + if (link != NULL){ + route->dst_id = link->link_idx; + }else{ + printk("can not find dst link %s\n", token); + return -1; + } + } else { + printk("Unsupported dst link type\n"); + return -1; + } + + // src LINK + token = strsep(&str, " "); + + printk("SRC type = %s\n", token); + + if (!token) { + return -1; + } + + if (strnicmp("interface", token, strlen("interface")) == 0) { + route->src_type = LINK_INTERFACE; + printk("SRC type = INTERFACE\n"); + } else if (strnicmp("edge", token, strlen("edge")) == 0) { + route->src_type = LINK_EDGE; + printk("SRC type = EDGE\n"); + } else if (strnicmp("any", token, strlen("any")) == 0) { + route->src_type = LINK_ANY; + printk("SRC type = ANY\n"); + } else { + printk("Invalid Src link type (%s)\n", token); + return -1; + } + + + if (route->src_type == LINK_ANY) { + route->src_id = (uint32_t)-1; + } else if (route->src_type == LINK_EDGE) { + uint32_t src_ip; + token = strsep(&str, " "); + + if (!token) { + return -1; + } + + // Figure out Link Here + if (in4_pton(token, strlen(token), (uint8_t *)&(src_ip), '\0', NULL) != 1) { + printk("Invalid SRC IP address (%s)\n", token); + return -EFAULT; + } + + link = find_link_by_ip(src_ip); + if (link != NULL){ + route->src_id = link->link_idx; + }else{ + printk("can not find src link %s\n", token); + return -1; + } + } else { + printk("Invalid link type\n"); + return -1; + } + + + return 0; +} + + + + +static void * route_seq_start(struct seq_file * s, loff_t * pos) { + struct vnet_route * route_iter = NULL; + loff_t i = 0; + + + if (*pos >= vnet_state.num_routes) { + return NULL; + } + + list_for_each_entry(route_iter, &(vnet_state.route_list), node) { + + if (i == *pos) { + break; + } + + i++; + } + + return route_iter; +} + + +static void * link_seq_start(struct seq_file * s, loff_t * pos) { + struct vnet_link * link_iter = NULL; + loff_t i = 0; + + + if (*pos >= vnet_state.num_links) { + return NULL; + } + + list_for_each_entry(link_iter, &(vnet_state.link_list), node) { + + if (i == *pos) { + break; + } + + i++; + } + + return link_iter; +} + + + +static void * route_seq_next(struct seq_file * s, void * v, loff_t * pos) { + struct vnet_route * route_iter = NULL; + + route_iter = list_entry(((struct vnet_route *)v)->node.next, struct vnet_route, node); + + // Check if the list has looped + if (&(route_iter->node) == &(vnet_state.route_list)) { + return NULL; + } + + *pos += 1; + + return route_iter; +} + + +static void * link_seq_next(struct seq_file * s, void * v, loff_t * pos) { + struct vnet_link * link_iter = NULL; + + + link_iter = list_entry(((struct vnet_link *)v)->node.next, struct vnet_link, node); + + // Check if the list has looped + if (&(link_iter->node) == &(vnet_state.link_list)) { + return NULL; + } + + *pos += 1; + + return link_iter; +} + + +static void route_seq_stop(struct seq_file * s, void * v) { + printk("route_seq_stop\n"); + + return; +} + + +static void link_seq_stop(struct seq_file * s, void * v) { + printk("link_seq_stop\n"); + + return; +} + +static int route_seq_show(struct seq_file * s, void * v) { + struct vnet_route * route_iter = v; + struct v3_vnet_route * route = &(route_iter->route); + + + seq_printf(s, "%d:\t", route_iter->route_idx); + + switch (route->src_mac_qual) { + case MAC_ANY: + seq_printf(s, "any "); + break; + case MAC_NONE: + seq_printf(s, "none "); + break; + case MAC_NOT: + seq_printf(s, "not-%x:%x:%x:%x:%x:%x ", + route->src_mac[0], route->src_mac[1], route->src_mac[2], + route->src_mac[3], route->src_mac[4], route->src_mac[5]); + break; + default: + seq_printf(s, "%x:%x:%x:%x:%x:%x ", + route->src_mac[0], route->src_mac[1], route->src_mac[2], + route->src_mac[3], route->src_mac[4], route->src_mac[5]); + break; + } + + switch (route->dst_mac_qual) { + case MAC_ANY: + seq_printf(s, "any "); + break; + case MAC_NONE: + seq_printf(s, "none "); + break; + case MAC_NOT: + seq_printf(s, "not-%x:%x:%x:%x:%x:%x ", + route->src_mac[0], route->src_mac[1], route->src_mac[2], + route->src_mac[3], route->src_mac[4], route->src_mac[5]); + break; + default: + seq_printf(s, "%x:%x:%x:%x:%x:%x ", + route->src_mac[0], route->src_mac[1], route->src_mac[2], + route->src_mac[3], route->src_mac[4], route->src_mac[5]); + break; + } + + + switch (route->dst_type) { + case LINK_EDGE: { + struct vnet_link * link = (struct vnet_link *)find_link_by_idx(route->dst_id); + seq_printf(s, "EDGE %pI4", &link->dst_ip); + break; + } + case LINK_INTERFACE: { + seq_printf(s, "INTERFACE "); + seq_printf(s, "%d ", route->dst_id); + break; + } + default: + seq_printf(s, "Invalid Dst Link Type (%d) ", route->dst_type); + break; + } + + + + + + switch (route->src_type) { + case LINK_EDGE: { + struct vnet_link * link = (struct vnet_link *)find_link_by_idx(route->src_id); + seq_printf(s, "EDGE %pI4", &link->dst_ip); + break; + } + case LINK_INTERFACE: { + seq_printf(s, "INTERFACE %d", route->src_id); + break; + } + case LINK_ANY: + seq_printf(s, "ANY"); + break; + default: + seq_printf(s, "Invalid Src Link Type (%d) ", route->src_type); + break; + } + + + seq_printf(s, "\n"); + + return 0; +} + + +static int link_seq_show(struct seq_file * s, void * v) { + struct vnet_link * link_iter = v; + + seq_printf(s, "%d:\t%pI4\t%d\n", + link_iter->link_idx, + &link_iter->dst_ip, + link_iter->dst_port); + + return 0; +} + + +static struct seq_operations route_seq_ops = { + .start = route_seq_start, + .next = route_seq_next, + .stop = route_seq_stop, + .show = route_seq_show +}; + + +static struct seq_operations link_seq_ops = { + .start = link_seq_start, + .next = link_seq_next, + .stop = link_seq_stop, + .show = link_seq_show +}; + + +static int route_open(struct inode * inode, struct file * file) { + return seq_open(file, &route_seq_ops); +} + + +static int link_open(struct inode * inode, struct file * file) { + return seq_open(file, &link_seq_ops); +} + +static int inject_route(struct vnet_route * route) { + v3_vnet_add_route(route->route); + + printk("Palacios-vnet: One route added to VNET core\n"); + + return 0; +} + +static ssize_t +route_write(struct file * file, + const char * buf, + size_t size, + loff_t * ppos) { + char route_buf[256]; + char * buf_iter = NULL; + char * line_str = route_buf; + char * token = NULL; + + if (size >= 256) { + return -EFAULT; + } + + if (copy_from_user(route_buf, buf, size)) { + return -EFAULT; + } + + printk("Route written: %s\n", route_buf); + + while ((buf_iter = strsep(&line_str, "\r\n"))) { + + token = strsep(&buf_iter, " "); + if (!token) { + return -EFAULT; + } + + if (strnicmp("ADD", token, strlen("ADD")) == 0) { + struct vnet_route * new_route = NULL; + new_route = kmalloc(sizeof(struct vnet_route), GFP_KERNEL); + + if (!new_route) { + return -ENOMEM; + } + + memset(new_route, 0, sizeof(struct vnet_route)); + + if (parse_route_str(buf_iter, &(new_route->route)) == -1) { + kfree(new_route); + return -EFAULT; + } + + if (inject_route(new_route) != 0) { + return -EFAULT; + } + } else if (strnicmp("DEL", token, strlen("DEL")) == 0) { + printk("I should delete the route here\n"); + } else { + printk("Invalid Route command string\n"); + } + } + + return size; +} + + +static int create_link(struct vnet_link * link) { + int err; + unsigned long flags; + + if ( (err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &link->sock)) < 0) { + printk("Could not create socket\n"); + return -1; + } + + memset(&link->sock_addr, 0, sizeof(struct sockaddr)); + + link->sock_addr.sin_family = AF_INET; + link->sock_addr.sin_addr.s_addr = link->dst_ip; + link->sock_addr.sin_port = htons(link->dst_port); + + if ((err = link->sock->ops->connect(link->sock, (struct sockaddr *)&(link->sock_addr), sizeof(struct sockaddr), 0) < 0)) { + printk("Could not connect to remote host\n"); + return -1; + } + + // We use the file pointer because we are in the kernel + // This is only used to assigned File Descriptors for user space, so it is available here + // link->sock->file = link; + + spin_lock_irqsave(&(vnet_state.lock), flags); + list_add(&(link->node), &(vnet_state.link_list)); + link->link_idx = vnet_state.num_links++; + spin_unlock_irqrestore(&(vnet_state.lock), flags); + + printk("VNET Bridge: Link created, ip %d, port: %d, idx: %d, link: %p\n", + link->dst_ip, + link->dst_port, + link->link_idx, + link); + + return 0; +} + +static ssize_t +link_write(struct file * file, const char * buf, size_t size, loff_t * ppos) { + char link_buf[256]; + char * link_iter = NULL; + char * line_str = link_buf; + char * token = NULL; + + if (size >= 256) { + return -EFAULT; + } + + if (copy_from_user(link_buf, buf, size)) { + return -EFAULT; + } + + while ((link_iter = strsep(&line_str, "\r\n"))) { + printk("Link written: %s\n", link_buf); + + token = strsep(&link_iter, " "); + + if (!token) { + return -EFAULT; + } + + if (strnicmp("ADD", token, strlen("ADD")) == 0) { + struct vnet_link * new_link = NULL; + char * ip_str = NULL; + uint32_t ip; + + ip_str = strsep(&link_iter, " "); + + if ((!ip_str) || (!link_iter)) { + printk("Missing fields in ADD Link command\n"); + return -EFAULT; + } + + if (in4_pton(ip_str, strlen(ip_str), (uint8_t *)&(ip), '\0', NULL) != 1) { + printk("Invalid Dst IP address (%s)\n", ip_str); + return -EFAULT; + } + + new_link = kmalloc(sizeof(struct vnet_link), GFP_KERNEL); + + if (!new_link) { + return -ENOMEM; + } + + memset(new_link, 0, sizeof(struct vnet_link)); + + new_link->dst_ip = ip; + new_link->dst_port = simple_strtol(link_iter, &link_iter, 10); + + if (create_link(new_link) != 0) { + printk("Could not create link\n"); + kfree(new_link); + return -EFAULT; + } + + } else if (strnicmp("DEL", token, strlen("DEL")) == 0) { + printk("Link deletion not supported\n"); + } else { + printk("Invalid Link command string\n"); + } + } + + return size; +} + + +static struct file_operations route_fops = { + .owner = THIS_MODULE, + .open = route_open, + .read = seq_read, + .write = route_write, + .llseek = seq_lseek, + .release = seq_release +}; + + +static struct file_operations link_fops = { + .owner = THIS_MODULE, + .open = link_open, + .read = seq_read, + .write = link_write, + .llseek = seq_lseek, + .release = seq_release +}; + + +static int init_proc_files(void) { + struct proc_dir_entry * route_entry = NULL; + struct proc_dir_entry * link_entry = NULL; + struct proc_dir_entry * vnet_root = NULL; + + + vnet_root = proc_mkdir("vnet", NULL); + if (vnet_root == NULL) { + return -1; + } + + route_entry = create_proc_entry("routes", 0, vnet_root); + + if (route_entry == NULL) { + remove_proc_entry("vnet", NULL); + return -1; + } + + route_entry->proc_fops = &route_fops; + + + link_entry = create_proc_entry("links", 0, vnet_root); + + if (link_entry == NULL) { + remove_proc_entry("routes", vnet_root); + remove_proc_entry("vnet", NULL); + return -1; + } + + link_entry->proc_fops = &link_fops; + + return 0; + +} + + + +static int +udp_send(struct socket * sock, + struct sockaddr_in * addr, + unsigned char * buf, int len) { + struct msghdr msg; + struct iovec iov; + mm_segment_t oldfs; + int size = 0; + + + if (sock->sk == NULL) { + return 0; + } + + iov.iov_base = buf; + iov.iov_len = len; + + msg.msg_flags = 0; + msg.msg_name = addr; + msg.msg_namelen = sizeof(struct sockaddr_in); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + size = sock_sendmsg(sock, &msg, len); + set_fs(oldfs); + + return size; +} + + + +static int +udp_recv(struct socket * sock, + struct sockaddr_in * addr, + unsigned char * buf, int len) { + struct msghdr msg; + struct iovec iov; + mm_segment_t oldfs; + int size = 0; + + if (sock->sk == NULL) { + return 0; + } + + iov.iov_base = buf; + iov.iov_len = len; + + msg.msg_flags = 0; + msg.msg_name = addr; + msg.msg_namelen = sizeof(struct sockaddr_in); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + size = sock_recvmsg(sock, &msg, len, msg.msg_flags); + + set_fs(oldfs); + + return size; +} + +//send packets from Network to VNET core +static int +send_to_palacios(unsigned char * buf, + int len, + int link_id){ + struct v3_vnet_pkt pkt; + pkt.size = len; + pkt.src_type = LINK_EDGE; + pkt.src_id = link_id; + memcpy(pkt.header, buf, ETHERNET_HEADER_LEN); + pkt.data = buf; + +#ifdef DEBUG_VNET_BRIGE + { + printk("VNET Lnx Bridge: send pkt to VNET core (size: %d, src_id: %d, src_type: %d)\n", + pkt.size, pkt.src_id, pkt.src_type); + + print_hex_dump(NULL, "pkt_data: ", 0, 20, 20, pkt.data, pkt.size, 0); + } +#endif + + return v3_vnet_send_pkt(&pkt, NULL);; +} + + +//send packet from VNET core to Network +static int +bridge_send_pkt(struct v3_vm_info * vm, + struct v3_vnet_pkt * pkt, + void * private_data) { + struct vnet_link * link; + + #ifdef DEBUG_VNET_BRIGE + { + printk("VNET Lnx Host Bridge: packet received from VNET Core ... len: %d, pkt size: %d, link: %d\n", + len, + pkt->size, + pkt->dst_id); + + print_hex_dump(NULL, "pkt_data: ", 0, 20, 20, pkt->data, pkt->size, 0); + } + #endif + + vnet_state.pkt_recv ++; + + link = find_link_by_idx(pkt->dst_id); + if (link != NULL) { + udp_send(link->sock, &(link->sock_addr), pkt->data, pkt->size); + vnet_state.pkt_udp_send ++; + } else { + printk("VNET Bridge Linux Host: wrong dst link, idx: %d, discards the packet\n", pkt->dst_id); + vnet_state.pkt_drop ++; + } + + return 0; +} + + +static void +poll_pkt(struct v3_vm_info * vm, + void * private_data) { + + +} + + + +static int init_vnet_serv(void) { + + if (sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &vnet_state.serv_sock) < 0) { + printk("Could not create socket\n"); + return -1; + } + + memset(&vnet_state.serv_addr, 0, sizeof(struct sockaddr)); + + vnet_state.serv_addr.sin_family = AF_INET; + vnet_state.serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); + vnet_state.serv_addr.sin_port = htons(VNET_UDP_PORT); + + if (vnet_state.serv_sock->ops->bind(vnet_state.serv_sock, (struct sockaddr *)&(vnet_state.serv_addr), sizeof(struct sockaddr)) < 0) { + printk("Could not bind VNET server socket to port %d\n", VNET_UDP_PORT); + return -1; + } + + printk("VNET server bind to port: %d\n", VNET_UDP_PORT); + + return 0; +} + +static int vnet_server(void * arg) { + unsigned char pkt[ETHERNET_PACKET_LEN]; + struct sockaddr_in pkt_addr; + struct vnet_link *link = NULL; + int len; + int link_id; + + printk("Palacios VNET Bridge: UDP receiving server ..... \n"); + + while (!kthread_should_stop()) { + + len = udp_recv(vnet_state.serv_sock, &pkt_addr, pkt, ETHERNET_PACKET_LEN); + if(len < 0) { + printk("Receive error: Could not get packet, error %d\n", len); + continue; + } + + link = find_link_by_ip(ntohl(pkt_addr.sin_addr.s_addr)); + if (link != NULL){ + link_id= link->link_idx; + } + else { + link_id= 0; + } + + vnet_state.pkt_udp_recv ++; + + send_to_palacios(pkt, len, link_id); + } + + return 0; +} + +#if 0 +static int profiling(void *args) { + static unsigned long long last_time=0; + unsigned long long cur_time=0; + set_user_nice(current, MAX_PRIO-1); + + while (!kthread_should_stop()) { + rdtscll(cur_time); + if((cur_time - last_time) > 50000000000) { + last_time = cur_time; + printk("Palacios Linux VNET Bridge - profiling: sent: %ld, rxed: %ld, dropped: %ld, upd send: %ld, udp recv: %ld\n", + vnet_state.pkt_sent, + vnet_state.pkt_recv, + vnet_state.pkt_drop, + vnet_state.pkt_udp_send, + vnet_state.pkt_udp_recv); + } + schedule(); + } + + return 0; +} +#endif + +int palacios_init_vnet(void) { + struct v3_vnet_bridge_ops bridge_ops; + + memset(&vnet_state, 0, sizeof(struct palacios_vnet_state)); + + INIT_LIST_HEAD(&(vnet_state.link_list)); + INIT_LIST_HEAD(&(vnet_state.route_list)); + spin_lock_init(&(vnet_state.lock)); + + init_proc_files(); + if(init_vnet_serv() < 0){ + printk("Failure to initiate VNET server\n"); + return -1; + } + + vnet_state.serv_thread = kthread_run(vnet_server, NULL, "vnet-server"); + + //kthread_run(profiling, NULL, "Profiling"); + + bridge_ops.input = bridge_send_pkt; + bridge_ops.poll = poll_pkt; + + v3_vnet_add_bridge(NULL, &bridge_ops, HOST_LNX_BRIDGE, NULL); + + printk("Palacios VNET Linux Bridge initiated\n"); + + return 0; +} + diff --git a/linux_module/palacios-vnet.h b/linux_module/palacios-vnet.h new file mode 100644 index 0000000..98f8dce --- /dev/null +++ b/linux_module/palacios-vnet.h @@ -0,0 +1,14 @@ +/* + * Palacios VNET Linux Bridge + * (c) Lei Xia, 2010 + */ + +#ifndef __PALACIOS_VNET_BRIDGE_H__ +#define __PALACIOS_VNET_BRIDGE_H__ + +#include + +int palacios_init_vnet(void); + +#endif + diff --git a/linux_module/palacios.c b/linux_module/palacios.c new file mode 100644 index 0000000..7a3238e --- /dev/null +++ b/linux_module/palacios.c @@ -0,0 +1,482 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include "palacios.h" + + + + +#include "palacios-mm.h" + + +u32 pg_allocs = 0; +u32 pg_frees = 0; +u32 mallocs = 0; +u32 frees = 0; + + +static struct v3_vm_info * irq_to_guest_map[256]; + + +extern unsigned int cpu_khz; + + +/** + * Prints a message to the console. + */ +static void palacios_print(const char * fmt, ...) { + va_list ap; + va_start(ap, fmt); + vprintk(fmt, ap); + va_end(ap); + + return; +} + + + +/* + * Allocates a contiguous region of pages of the requested size. + * Returns the physical address of the first page in the region. + */ +static void * palacios_allocate_pages(int num_pages, unsigned int alignment) { + void * pg_addr = NULL; + + pg_addr = (void *)alloc_palacios_pgs(num_pages, alignment); + pg_allocs += num_pages; + + return pg_addr; +} + + +/** + * Frees a page previously allocated via palacios_allocate_page(). + * Note that palacios_allocate_page() can allocate multiple pages with + * a single call while palacios_free_page() only frees a single page. + */ + +static void palacios_free_pages(void * page_paddr, int num_pages) { + pg_frees += num_pages; + free_palacios_pgs((uintptr_t)page_paddr, num_pages); +} + + +/** + * Allocates 'size' bytes of kernel memory. + * Returns the kernel virtual address of the memory allocated. + */ +static void * +palacios_alloc(unsigned int size) { + void * addr = NULL; + + addr = kmalloc(size, GFP_KERNEL); + mallocs++; + + return addr; +} + +/** + * Frees memory that was previously allocated by palacios_alloc(). + */ +static void +palacios_free( + void * addr +) +{ + frees++; + kfree(addr); + return; +} + +/** + * Converts a kernel virtual address to the corresponding physical address. + */ +static void * +palacios_vaddr_to_paddr( + void * vaddr +) +{ + return (void*) __pa(vaddr); + +} + +/** + * Converts a physical address to the corresponding kernel virtual address. + */ +static void * +palacios_paddr_to_vaddr( + void * paddr +) +{ + return __va(paddr); +} + +/** + * Runs a function on the specified CPU. + */ + +// For now, do call only on local CPU +static void +palacios_xcall( + int cpu_id, + void (*fn)(void *arg), + void * arg +) +{ + printk("palacios_xcall: Doing 'xcall' to local cpu\n"); + fn(arg); + return; +} + +struct lnx_thread_arg { + int (*fn)(void * arg); + void * arg; + char * name; +}; + +static int lnx_thread_target(void * arg) { + struct lnx_thread_arg * thread_info = (struct lnx_thread_arg *)arg; + + /* + lock_kernel(); + printk("Daemonizing new Palacios thread (name=%s)\n", thread_info->name); + + daemonize(thread_info->name); + unlock_kernel(); + allow_signal(SIGKILL); + */ + + + thread_info->fn(thread_info->arg); + + kfree(thread_info); + // handle cleanup + + return 0; +} + +/** + * Creates a kernel thread. + */ +static void +palacios_start_kernel_thread( + int (*fn) (void * arg), + void * arg, + char * thread_name) { + + struct lnx_thread_arg * thread_info = kmalloc(sizeof(struct lnx_thread_arg), GFP_KERNEL); + + thread_info->fn = fn; + thread_info->arg = arg; + thread_info->name = thread_name; + + kthread_run( lnx_thread_target, thread_info, thread_name ); + return; +} + + +/** + * Starts a kernel thread on the specified CPU. + */ +static void * +palacios_start_thread_on_cpu(int cpu_id, + int (*fn)(void * arg), + void * arg, + char * thread_name ) { + struct task_struct * thread = NULL; + struct lnx_thread_arg * thread_info = kmalloc(sizeof(struct lnx_thread_arg), GFP_KERNEL); + + thread_info->fn = fn; + thread_info->arg = arg; + thread_info->name = thread_name; + + thread = kthread_run( lnx_thread_target, thread_info, thread_name ); + + if (IS_ERR(thread)) { + printk("Palacios error creating thread: %s\n", thread_name); + return NULL; + } + + return thread; +} + +/** + * Returns the CPU ID that the caller is running on. + */ +static unsigned int +palacios_get_cpu(void) +{ +#if 1 + return 0; + // return smp_processor_id(); + // id = get_cpu(); put_cpu(id); + // return this_cpu; +#else + struct cpumask mask; + unsigned int set; + + if(sched_getaffinity(0,&mask)<0){ + panic("sched_getaffinity failed"); + return -1; + } + set = cpumask_first(&mask); + printk("***mask.bits: %d",set); + return set; +#endif +} + +/** + * Interrupts the physical CPU corresponding to the specified logical guest cpu. + * + * NOTE: + * This is dependent on the implementation of xcall_reschedule(). Currently + * xcall_reschedule does not explicitly call schedule() on the destination CPU, + * but instead relies on the return to user space to handle it. Because + * palacios is a kernel thread schedule will not be called, which is correct. + * If it ever changes to induce side effects, we'll need to figure something + * else out... + */ +static void +palacios_interrupt_cpu( + struct v3_vm_info * vm, + int cpu_id, + int vector +) +{ + // panic("palacios_interrupt_cpu"); + // printk("Faking interruption of target CPU by not doing anything since there is only one CPU\n"); + return; +} + +/** + * Dispatches an interrupt to Palacios for handling. + */ +static void +palacios_dispatch_interrupt( int vector, void * dev, struct pt_regs * regs ) { + struct v3_interrupt intr = { + .irq = vector, + .error = regs->orig_ax, + .should_ack = 1, + }; + + if (irq_to_guest_map[vector]) { + v3_deliver_irq(irq_to_guest_map[vector], &intr); + } + +} + +/** + * Instructs the kernel to forward the specified IRQ to Palacios. + */ +static int +palacios_hook_interrupt(struct v3_vm_info * vm, + unsigned int vector ) { + printk("hooking vector %d\n", vector); + + if (irq_to_guest_map[vector]) { + printk(KERN_WARNING + "%s: Interrupt vector %u is already hooked.\n", + __func__, vector); + return -1; + } + + printk(KERN_DEBUG + "%s: Hooking interrupt vector %u to vm %p.\n", + __func__, vector, vm); + + irq_to_guest_map[vector] = vm; + + /* + * NOTE: Normally PCI devices are supposed to be level sensitive, + * but we need them to be edge sensitive so that they are + * properly latched by Palacios. Leaving them as level + * sensitive would lead to an interrupt storm. + */ + //ioapic_set_trigger_for_vector(vector, ioapic_edge_sensitive); + + //set_idtvec_handler(vector, palacios_dispatch_interrupt); + if (vector < 32) { + panic("unexpected vector for hooking\n"); + } else { + int device_id = 0; + + int flag = 0; + int error; + + printk("hooking vector: %d\n", vector); + + if (vector == 32) { + flag = IRQF_TIMER; + } else { + flag = IRQF_SHARED; + } + + error = request_irq((vector - 32), + (void *)palacios_dispatch_interrupt, + flag, + "interrupt_for_palacios", + &device_id); + + if (error) { + printk("error code for request_irq is %d\n", error); + panic("request vector %d failed",vector); + } + } + + return 0; +} + +/** + * Acknowledges an interrupt. + */ +static int +palacios_ack_interrupt( + int vector +) +{ + ack_APIC_irq(); + printk("Pretending to ack interrupt, vector=%d\n",vector); + return 0; +} + +/** + * Returns the CPU frequency in kilohertz. + */ +static unsigned int +palacios_get_cpu_khz(void) +{ + printk("cpu_khz is %u\n",cpu_khz); + if (cpu_khz==0) { + printk("faking cpu_khz to 1000000\n"); + return 1000000; + } else { + return cpu_khz; + } + //return 1000000; +} + +/** + * Yield the CPU so other host OS tasks can run. + */ +static void +palacios_yield_cpu(void) +{ + schedule(); + return; +} + + + +/** + * Allocates a mutex. + * Returns NULL on failure. + */ +static void * +palacios_mutex_alloc(void) +{ + spinlock_t *lock = kmalloc(sizeof(spinlock_t), GFP_KERNEL); + if (lock) + spin_lock_init(lock); + return lock; +} + +/** + * Frees a mutex. + */ +static void +palacios_mutex_free( + void * mutex +) +{ + kfree(mutex); +} + +/** + * Locks a mutex. + */ +static void +palacios_mutex_lock( + void * mutex, + int must_spin +) +{ + spin_lock((spinlock_t*)mutex); +} + +/** + * Unlocks a mutex. + */ +static void +palacios_mutex_unlock( + void * mutex +) +{ + spin_unlock((spinlock_t*)mutex); +} + +/** + * Structure used by the Palacios hypervisor to interface with the host kernel. + */ +static struct v3_os_hooks palacios_os_hooks = { + .print = palacios_print, + .allocate_pages = palacios_allocate_pages, + .free_pages = palacios_free_pages, + .malloc = palacios_alloc, + .free = palacios_free, + .vaddr_to_paddr = palacios_vaddr_to_paddr, + .paddr_to_vaddr = palacios_paddr_to_vaddr, + .hook_interrupt = palacios_hook_interrupt, + .ack_irq = palacios_ack_interrupt, + .get_cpu_khz = palacios_get_cpu_khz, + .start_kernel_thread = palacios_start_kernel_thread, + .yield_cpu = palacios_yield_cpu, + .mutex_alloc = palacios_mutex_alloc, + .mutex_free = palacios_mutex_free, + .mutex_lock = palacios_mutex_lock, + .mutex_unlock = palacios_mutex_unlock, + .get_cpu = palacios_get_cpu, + .interrupt_cpu = palacios_interrupt_cpu, + .call_on_cpu = palacios_xcall, + .start_thread_on_cpu = palacios_start_thread_on_cpu, +}; + + + + +int palacios_vmm_init( void ) +{ + + memset(irq_to_guest_map, 0, sizeof(struct v3_vm_info *) * 256); + + printk("palacios_init starting - calling init_v3\n"); + + Init_V3(&palacios_os_hooks, 1); + + + return 0; + +} + + +int palacios_vmm_exit( void ) { + + Shutdown_V3(); + + return 0; +} diff --git a/linux_module/palacios.h b/linux_module/palacios.h new file mode 100644 index 0000000..3ee850a --- /dev/null +++ b/linux_module/palacios.h @@ -0,0 +1,68 @@ +#ifndef _PALACIOS_H +#define _PALACIOS_H + +#include +#include +#include + +#include "palacios-console.h" + +/* Global Control IOCTLs */ +#define V3_START_GUEST 10 +#define V3_ADD_MEMORY 50 + +/* VM Specific IOCTLs */ +#define V3_VM_CONSOLE_CONNECT 20 +#define V3_VM_SERIAL_CONNECT 21 +#define V3_VM_STOP 22 + +struct v3_guest_img { + unsigned long long size; + void * guest_data; + char name[128]; +}; + +struct v3_mem_region { + unsigned long long base_addr; + unsigned long long num_pages; +}; + + + +struct v3_guest { + void * v3_ctx; + + void * img; + u32 img_size; + + char name[128]; + + struct list_head files; + struct list_head streams; + struct list_head sockets; + + struct palacios_console console; + + struct completion thread_done; + + dev_t vm_dev; + struct cdev cdev; +}; + +// For now MAX_VMS must be a multiple of 8 +// This is due to the minor number bitmap +#define MAX_VMS 32 + + + + + +extern void send_key_to_palacios(unsigned char status, unsigned char scan_code); + + +int palacios_vmm_init( void ); +int palacios_vmm_exit( void ); + + + +#endif