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