]> rtime.felk.cvut.cz Git - jailhouse.git/blob - driver/cell.c
driver: Improve input validation to make code scanners happier
[jailhouse.git] / driver / cell.c
1 /*
2  * Jailhouse, a Linux-based partitioning hypervisor
3  *
4  * Copyright (c) Siemens AG, 2013-2015
5  *
6  * Authors:
7  *  Jan Kiszka <jan.kiszka@siemens.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2.  See
10  * the COPYING file in the top-level directory.
11  */
12
13 #include <linux/cpu.h>
14 #include <linux/slab.h>
15 #include <linux/vmalloc.h>
16
17 #include "cell.h"
18 #include "main.h"
19 #include "pci.h"
20 #include "sysfs.h"
21
22 #include <jailhouse/hypercall.h>
23
24 static LIST_HEAD(cells);
25 static struct cell *root_cell;
26 static cpumask_t offlined_cpus;
27
28 void jailhouse_cell_kobj_release(struct kobject *kobj)
29 {
30         struct cell *cell = container_of(kobj, struct cell, kobj);
31
32         jailhouse_pci_cell_cleanup(cell);
33         vfree(cell->memory_regions);
34         kfree(cell);
35 }
36
37 struct cell *jailhouse_cell_create(const struct jailhouse_cell_desc *cell_desc)
38 {
39         struct cell *cell;
40         int err;
41
42         if (cell_desc->num_memory_regions >=
43             ULONG_MAX / sizeof(struct jailhouse_memory))
44                 return ERR_PTR(-EINVAL);
45
46         cell = kzalloc(sizeof(*cell), GFP_KERNEL);
47         if (!cell)
48                 return ERR_PTR(-ENOMEM);
49
50         INIT_LIST_HEAD(&cell->entry);
51
52         bitmap_copy(cpumask_bits(&cell->cpus_assigned),
53                     jailhouse_cell_cpu_set(cell_desc),
54                     min(nr_cpumask_bits, (int)cell_desc->cpu_set_size * 8));
55
56         cell->num_memory_regions = cell_desc->num_memory_regions;
57         cell->memory_regions = vmalloc(sizeof(struct jailhouse_memory) *
58                                        cell->num_memory_regions);
59         if (!cell->memory_regions) {
60                 kfree(cell);
61                 return ERR_PTR(-ENOMEM);
62         }
63
64         memcpy(cell->memory_regions, jailhouse_cell_mem_regions(cell_desc),
65                sizeof(struct jailhouse_memory) * cell->num_memory_regions);
66
67         err = jailhouse_pci_cell_setup(cell, cell_desc);
68         if (err) {
69                 vfree(cell->memory_regions);
70                 kfree(cell);
71                 return ERR_PTR(err);
72         }
73
74         err = jailhouse_sysfs_cell_create(cell, cell_desc->name);
75         if (err)
76                 /* cleanup done by jailhouse_sysfs_cell_create */
77                 return ERR_PTR(err);
78
79         return cell;
80 }
81
82 void jailhouse_cell_register(struct cell *cell)
83 {
84         list_add_tail(&cell->entry, &cells);
85         jailhouse_sysfs_cell_register(cell);
86 }
87
88 static struct cell *find_cell(struct jailhouse_cell_id *cell_id)
89 {
90         struct cell *cell;
91
92         list_for_each_entry(cell, &cells, entry)
93                 if (cell_id->id == cell->id ||
94                     (cell_id->id == JAILHOUSE_CELL_ID_UNUSED &&
95                      strcmp(kobject_name(&cell->kobj), cell_id->name) == 0))
96                         return cell;
97         return NULL;
98 }
99
100 void jailhouse_cell_delete(struct cell *cell)
101 {
102         list_del(&cell->entry);
103         jailhouse_sysfs_cell_delete(cell);
104 }
105
106 int jailhouse_cell_prepare_root(const struct jailhouse_cell_desc *cell_desc)
107 {
108         root_cell = jailhouse_cell_create(cell_desc);
109         if (IS_ERR(root_cell))
110                 return PTR_ERR(root_cell);
111
112         cpumask_and(&root_cell->cpus_assigned, &root_cell->cpus_assigned,
113                     cpu_online_mask);
114
115         return 0;
116 }
117
118 void jailhouse_cell_register_root(void)
119 {
120         jailhouse_pci_do_all_devices(root_cell, JAILHOUSE_PCI_TYPE_IVSHMEM,
121                                      JAILHOUSE_PCI_ACTION_ADD);
122
123         root_cell->id = 0;
124         jailhouse_cell_register(root_cell);
125 }
126
127 void jailhouse_cell_delete_root(void)
128 {
129         jailhouse_cell_delete(root_cell);
130 }
131
132 void jailhouse_cell_delete_all(void)
133 {
134         struct cell *cell, *tmp;
135         unsigned int cpu;
136
137         jailhouse_pci_do_all_devices(root_cell, JAILHOUSE_PCI_TYPE_IVSHMEM,
138                                      JAILHOUSE_PCI_ACTION_DEL);
139
140         list_for_each_entry_safe(cell, tmp, &cells, entry)
141                 jailhouse_cell_delete(cell);
142
143         for_each_cpu(cpu, &offlined_cpus) {
144                 if (cpu_up(cpu) != 0)
145                         pr_err("Jailhouse: failed to bring CPU %d back "
146                                "online\n", cpu);
147                 cpu_clear(cpu, offlined_cpus);
148         }
149 }
150
151 int jailhouse_cmd_cell_create(struct jailhouse_cell_create __user *arg)
152 {
153         struct jailhouse_cell_create cell_params;
154         struct jailhouse_cell_desc *config;
155         struct jailhouse_cell_id cell_id;
156         void __user *user_config;
157         struct cell *cell;
158         unsigned int cpu;
159         int id, err = 0;
160
161         if (copy_from_user(&cell_params, arg, sizeof(cell_params)))
162                 return -EFAULT;
163
164         config = kmalloc(cell_params.config_size, GFP_KERNEL | GFP_DMA);
165         if (!config)
166                 return -ENOMEM;
167
168         user_config = (void __user *)(unsigned long)cell_params.config_address;
169         if (copy_from_user(config, user_config, cell_params.config_size)) {
170                 err = -EFAULT;
171                 goto kfree_config_out;
172         }
173         config->name[JAILHOUSE_CELL_NAME_MAXLEN] = 0;
174
175         if (mutex_lock_interruptible(&jailhouse_lock) != 0) {
176                 err = -EINTR;
177                 goto kfree_config_out;
178         }
179
180         if (!jailhouse_enabled) {
181                 err = -EINVAL;
182                 goto unlock_out;
183         }
184
185         cell_id.id = JAILHOUSE_CELL_ID_UNUSED;
186         memcpy(cell_id.name, config->name, sizeof(cell_id.name));
187         if (find_cell(&cell_id) != NULL) {
188                 err = -EEXIST;
189                 goto unlock_out;
190         }
191
192         cell = jailhouse_cell_create(config);
193         if (IS_ERR(cell)) {
194                 err = PTR_ERR(cell);
195                 goto unlock_out;
196         }
197
198         if (!cpumask_subset(&cell->cpus_assigned, &root_cell->cpus_assigned)) {
199                 err = -EBUSY;
200                 goto error_cell_delete;
201         }
202
203         for_each_cpu(cpu, &cell->cpus_assigned) {
204                 if (cpu_online(cpu)) {
205                         err = cpu_down(cpu);
206                         if (err)
207                                 goto error_cpu_online;
208                         cpu_set(cpu, offlined_cpus);
209                 }
210                 cpu_clear(cpu, root_cell->cpus_assigned);
211         }
212
213         id = jailhouse_call_arg1(JAILHOUSE_HC_CELL_CREATE, __pa(config));
214         if (id < 0) {
215                 err = id;
216                 goto error_cpu_online;
217         }
218
219         cell->id = id;
220         jailhouse_cell_register(cell);
221
222         pr_info("Created Jailhouse cell \"%s\"\n", config->name);
223
224 unlock_out:
225         mutex_unlock(&jailhouse_lock);
226
227 kfree_config_out:
228         kfree(config);
229
230         return err;
231
232 error_cpu_online:
233         for_each_cpu(cpu, &cell->cpus_assigned) {
234                 if (!cpu_online(cpu) && cpu_up(cpu) == 0)
235                         cpu_clear(cpu, offlined_cpus);
236                 cpu_set(cpu, root_cell->cpus_assigned);
237         }
238
239 error_cell_delete:
240         jailhouse_cell_delete(cell);
241         goto unlock_out;
242 }
243
244 static int cell_management_prologue(struct jailhouse_cell_id *cell_id,
245                                     struct cell **cell_ptr)
246 {
247         cell_id->name[JAILHOUSE_CELL_ID_NAMELEN] = 0;
248
249         if (mutex_lock_interruptible(&jailhouse_lock) != 0)
250                 return -EINTR;
251
252         if (!jailhouse_enabled) {
253                 mutex_unlock(&jailhouse_lock);
254                 return -EINVAL;
255         }
256
257         *cell_ptr = find_cell(cell_id);
258         if (*cell_ptr == NULL) {
259                 mutex_unlock(&jailhouse_lock);
260                 return -ENOENT;
261         }
262         return 0;
263 }
264
265 #define MEM_REQ_FLAGS   (JAILHOUSE_MEM_WRITE | JAILHOUSE_MEM_LOADABLE)
266
267 static int load_image(struct cell *cell,
268                       struct jailhouse_preload_image __user *uimage)
269 {
270         struct jailhouse_preload_image image;
271         const struct jailhouse_memory *mem;
272         unsigned int regions;
273         u64 image_offset;
274         void *image_mem;
275         int err = 0;
276
277         if (copy_from_user(&image, uimage, sizeof(image)))
278                 return -EFAULT;
279
280         mem = cell->memory_regions;
281         for (regions = cell->num_memory_regions; regions > 0; regions--) {
282                 image_offset = image.target_address - mem->virt_start;
283                 if (image.target_address >= mem->virt_start &&
284                     image_offset < mem->size) {
285                         if (image.size > mem->size - image_offset ||
286                             (mem->flags & MEM_REQ_FLAGS) != MEM_REQ_FLAGS)
287                                 return -EINVAL;
288                         break;
289                 }
290                 mem++;
291         }
292         if (regions == 0)
293                 return -EINVAL;
294
295         image_mem = jailhouse_ioremap(mem->phys_start + image_offset, 0,
296                                       image.size);
297         if (!image_mem) {
298                 pr_err("jailhouse: Unable to map cell RAM at %08llx "
299                        "for image loading\n",
300                        (unsigned long long)(mem->phys_start + image_offset));
301                 return -EBUSY;
302         }
303
304         if (copy_from_user(image_mem,
305                            (void __user *)(unsigned long)image.source_address,
306                            image.size))
307                 err = -EFAULT;
308
309         vunmap(image_mem);
310
311         return err;
312 }
313
314 int jailhouse_cmd_cell_load(struct jailhouse_cell_load __user *arg)
315 {
316         struct jailhouse_preload_image __user *image = arg->image;
317         struct jailhouse_cell_load cell_load;
318         struct cell *cell;
319         unsigned int n;
320         int err;
321
322         if (copy_from_user(&cell_load, arg, sizeof(cell_load)))
323                 return -EFAULT;
324
325         err = cell_management_prologue(&cell_load.cell_id, &cell);
326         if (err)
327                 return err;
328
329         err = jailhouse_call_arg1(JAILHOUSE_HC_CELL_SET_LOADABLE, cell->id);
330         if (err)
331                 goto unlock_out;
332
333         for (n = cell_load.num_preload_images; n > 0; n--, image++) {
334                 err = load_image(cell, image);
335                 if (err)
336                         break;
337         }
338
339 unlock_out:
340         mutex_unlock(&jailhouse_lock);
341
342         return err;
343 }
344
345 int jailhouse_cmd_cell_start(const char __user *arg)
346 {
347         struct jailhouse_cell_id cell_id;
348         struct cell *cell;
349         int err;
350
351         if (copy_from_user(&cell_id, arg, sizeof(cell_id)))
352                 return -EFAULT;
353
354         err = cell_management_prologue(&cell_id, &cell);
355         if (err)
356                 return err;
357
358         err = jailhouse_call_arg1(JAILHOUSE_HC_CELL_START, cell->id);
359
360         mutex_unlock(&jailhouse_lock);
361
362         return err;
363 }
364
365 int jailhouse_cmd_cell_destroy(const char __user *arg)
366 {
367         struct jailhouse_cell_id cell_id;
368         struct cell *cell;
369         unsigned int cpu;
370         int err;
371
372         if (copy_from_user(&cell_id, arg, sizeof(cell_id)))
373                 return -EFAULT;
374
375         err = cell_management_prologue(&cell_id, &cell);
376         if (err)
377                 return err;
378
379         err = jailhouse_call_arg1(JAILHOUSE_HC_CELL_DESTROY, cell->id);
380         if (err)
381                 goto unlock_out;
382
383         for_each_cpu(cpu, &cell->cpus_assigned) {
384                 if (cpu_isset(cpu, offlined_cpus)) {
385                         if (cpu_up(cpu) != 0)
386                                 pr_err("Jailhouse: failed to bring CPU %d "
387                                        "back online\n", cpu);
388                         cpu_clear(cpu, offlined_cpus);
389                 }
390                 cpu_set(cpu, root_cell->cpus_assigned);
391         }
392
393         pr_info("Destroyed Jailhouse cell \"%s\"\n",
394                 kobject_name(&cell->kobj));
395
396         jailhouse_cell_delete(cell);
397
398 unlock_out:
399         mutex_unlock(&jailhouse_lock);
400
401         return err;
402 }