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