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