lguest: documentation I: Preparation
[deliverable/linux.git] / drivers / lguest / lguest_user.c
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
12 static 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 */
23 static 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. */
41 static 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 */
63 static 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
75 static 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. */
105 static 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
158 free_regs:
159 free_page(lg->regs_page);
160 release_guest:
161 memset(lg, 0, sizeof(*lg));
162 unlock:
163 mutex_unlock(&lguest_lock);
164 return err;
165 }
166
167 static 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
200 static 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
221 static struct file_operations lguest_fops = {
222 .owner = THIS_MODULE,
223 .release = close,
224 .write = write,
225 .read = read,
226 };
227 static struct miscdevice lguest_dev = {
228 .minor = MISC_DYNAMIC_MINOR,
229 .name = "lguest",
230 .fops = &lguest_fops,
231 };
232
233 int __init lguest_device_init(void)
234 {
235 return misc_register(&lguest_dev);
236 }
237
238 void __exit lguest_device_remove(void)
239 {
240 misc_deregister(&lguest_dev);
241 }
This page took 0.042026 seconds and 5 git commands to generate.