lguest_user.c revision d7e28ffe6c74416b54345d6004fd0964c115b12c
1/* Userspace control of the guest, via /dev/lguest. */ 2#include <linux/uaccess.h> 3#include <linux/miscdevice.h> 4#include <linux/fs.h> 5#include "lg.h" 6 7static void setup_regs(struct lguest_regs *regs, unsigned long start) 8{ 9 /* Write out stack in format lguest expects, so we can switch to it. */ 10 regs->ds = regs->es = regs->ss = __KERNEL_DS|GUEST_PL; 11 regs->cs = __KERNEL_CS|GUEST_PL; 12 regs->eflags = 0x202; /* Interrupts enabled. */ 13 regs->eip = start; 14 /* esi points to our boot information (physical address 0) */ 15} 16 17/* + addr */ 18static long user_get_dma(struct lguest *lg, const u32 __user *input) 19{ 20 unsigned long key, udma, irq; 21 22 if (get_user(key, input) != 0) 23 return -EFAULT; 24 udma = get_dma_buffer(lg, key, &irq); 25 if (!udma) 26 return -ENOENT; 27 28 /* We put irq number in udma->used_len. */ 29 lgwrite_u32(lg, udma + offsetof(struct lguest_dma, used_len), irq); 30 return udma; 31} 32 33/* To force the Guest to stop running and return to the Launcher, the 34 * Waker sets writes LHREQ_BREAK and the value "1" to /dev/lguest. The 35 * Launcher then writes LHREQ_BREAK and "0" to release the Waker. */ 36static int break_guest_out(struct lguest *lg, const u32 __user *input) 37{ 38 unsigned long on; 39 40 /* Fetch whether they're turning break on or off.. */ 41 if (get_user(on, input) != 0) 42 return -EFAULT; 43 44 if (on) { 45 lg->break_out = 1; 46 /* Pop it out (may be running on different CPU) */ 47 wake_up_process(lg->tsk); 48 /* Wait for them to reset it */ 49 return wait_event_interruptible(lg->break_wq, !lg->break_out); 50 } else { 51 lg->break_out = 0; 52 wake_up(&lg->break_wq); 53 return 0; 54 } 55} 56 57/* + irq */ 58static int user_send_irq(struct lguest *lg, const u32 __user *input) 59{ 60 u32 irq; 61 62 if (get_user(irq, input) != 0) 63 return -EFAULT; 64 if (irq >= LGUEST_IRQS) 65 return -EINVAL; 66 set_bit(irq, lg->irqs_pending); 67 return 0; 68} 69 70static ssize_t read(struct file *file, char __user *user, size_t size,loff_t*o) 71{ 72 struct lguest *lg = file->private_data; 73 74 if (!lg) 75 return -EINVAL; 76 77 /* If you're not the task which owns the guest, go away. */ 78 if (current != lg->tsk) 79 return -EPERM; 80 81 if (lg->dead) { 82 size_t len; 83 84 if (IS_ERR(lg->dead)) 85 return PTR_ERR(lg->dead); 86 87 len = min(size, strlen(lg->dead)+1); 88 if (copy_to_user(user, lg->dead, len) != 0) 89 return -EFAULT; 90 return len; 91 } 92 93 if (lg->dma_is_pending) 94 lg->dma_is_pending = 0; 95 96 return run_guest(lg, (unsigned long __user *)user); 97} 98 99/* Take: pfnlimit, pgdir, start, pageoffset. */ 100static int initialize(struct file *file, const u32 __user *input) 101{ 102 struct lguest *lg; 103 int err, i; 104 u32 args[4]; 105 106 /* We grab the Big Lguest lock, which protects the global array 107 * "lguests" and multiple simultaneous initializations. */ 108 mutex_lock(&lguest_lock); 109 110 if (file->private_data) { 111 err = -EBUSY; 112 goto unlock; 113 } 114 115 if (copy_from_user(args, input, sizeof(args)) != 0) { 116 err = -EFAULT; 117 goto unlock; 118 } 119 120 i = find_free_guest(); 121 if (i < 0) { 122 err = -ENOSPC; 123 goto unlock; 124 } 125 lg = &lguests[i]; 126 lg->guestid = i; 127 lg->pfn_limit = args[0]; 128 lg->page_offset = args[3]; 129 lg->regs_page = get_zeroed_page(GFP_KERNEL); 130 if (!lg->regs_page) { 131 err = -ENOMEM; 132 goto release_guest; 133 } 134 lg->regs = (void *)lg->regs_page + PAGE_SIZE - sizeof(*lg->regs); 135 136 err = init_guest_pagetable(lg, args[1]); 137 if (err) 138 goto free_regs; 139 140 setup_regs(lg->regs, args[2]); 141 setup_guest_gdt(lg); 142 init_clockdev(lg); 143 lg->tsk = current; 144 lg->mm = get_task_mm(lg->tsk); 145 init_waitqueue_head(&lg->break_wq); 146 lg->last_pages = NULL; 147 file->private_data = lg; 148 149 mutex_unlock(&lguest_lock); 150 151 return sizeof(args); 152 153free_regs: 154 free_page(lg->regs_page); 155release_guest: 156 memset(lg, 0, sizeof(*lg)); 157unlock: 158 mutex_unlock(&lguest_lock); 159 return err; 160} 161 162static ssize_t write(struct file *file, const char __user *input, 163 size_t size, loff_t *off) 164{ 165 struct lguest *lg = file->private_data; 166 u32 req; 167 168 if (get_user(req, input) != 0) 169 return -EFAULT; 170 input += sizeof(req); 171 172 if (req != LHREQ_INITIALIZE && !lg) 173 return -EINVAL; 174 if (lg && lg->dead) 175 return -ENOENT; 176 177 /* If you're not the task which owns the Guest, you can only break */ 178 if (lg && current != lg->tsk && req != LHREQ_BREAK) 179 return -EPERM; 180 181 switch (req) { 182 case LHREQ_INITIALIZE: 183 return initialize(file, (const u32 __user *)input); 184 case LHREQ_GETDMA: 185 return user_get_dma(lg, (const u32 __user *)input); 186 case LHREQ_IRQ: 187 return user_send_irq(lg, (const u32 __user *)input); 188 case LHREQ_BREAK: 189 return break_guest_out(lg, (const u32 __user *)input); 190 default: 191 return -EINVAL; 192 } 193} 194 195static int close(struct inode *inode, struct file *file) 196{ 197 struct lguest *lg = file->private_data; 198 199 if (!lg) 200 return 0; 201 202 mutex_lock(&lguest_lock); 203 /* Cancels the hrtimer set via LHCALL_SET_CLOCKEVENT. */ 204 hrtimer_cancel(&lg->hrt); 205 release_all_dma(lg); 206 free_guest_pagetable(lg); 207 mmput(lg->mm); 208 if (!IS_ERR(lg->dead)) 209 kfree(lg->dead); 210 free_page(lg->regs_page); 211 memset(lg, 0, sizeof(*lg)); 212 mutex_unlock(&lguest_lock); 213 return 0; 214} 215 216static struct file_operations lguest_fops = { 217 .owner = THIS_MODULE, 218 .release = close, 219 .write = write, 220 .read = read, 221}; 222static struct miscdevice lguest_dev = { 223 .minor = MISC_DYNAMIC_MINOR, 224 .name = "lguest", 225 .fops = &lguest_fops, 226}; 227 228int __init lguest_device_init(void) 229{ 230 return misc_register(&lguest_dev); 231} 232 233void __exit lguest_device_remove(void) 234{ 235 misc_deregister(&lguest_dev); 236} 237