]> rtime.felk.cvut.cz Git - jailhouse.git/blob - tools/jailhouse.c
x86: Block DMA from unlisted devices in AMD IOMMU
[jailhouse.git] / tools / jailhouse.c
1 /*
2  * Jailhouse, a Linux-based partitioning hypervisor
3  *
4  * Copyright (c) Siemens AG, 2013-2016
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 <stdbool.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <fcntl.h>
19 #include <errno.h>
20 #include <limits.h>
21 #include <libgen.h>
22 #include <sys/types.h>
23 #include <sys/ioctl.h>
24 #include <sys/stat.h>
25
26 #include <jailhouse.h>
27
28 #define JAILHOUSE_EXEC_DIR      LIBEXECDIR "/jailhouse"
29
30 enum shutdown_load_mode {LOAD, SHUTDOWN};
31
32 struct extension {
33         char *cmd, *subcmd, *help;
34 };
35
36 static const struct extension extensions[] = {
37         { "cell", "linux", "CELLCONFIG KERNEL [-i | --initrd FILE]\n"
38           "              [-c | --cmdline \"STRING\"] "
39                                         "[-w | --write-params FILE]" },
40         { "cell", "list", "" },
41         { "cell", "stats", "{ ID | [--name] NAME }" },
42         { "config", "create", "[-h] [-g] [-r ROOT] "
43           "[--mem-inmates MEM_INMATES]\n"
44           "                 [--mem-hv MEM_HV] FILE" },
45         { "config", "collect", "FILE.TAR" },
46         { NULL }
47 };
48
49 static void __attribute__((noreturn)) help(char *prog, int exit_status)
50 {
51         const struct extension *ext;
52
53         printf("Usage: %s { COMMAND | --help || --version }\n"
54                "\nAvailable commands:\n"
55                "   enable SYSCONFIG\n"
56                "   disable\n"
57                "   cell create CELLCONFIG\n"
58                "   cell load { ID | [--name] NAME } "
59                                 "{ IMAGE | { -s | --string } \"STRING\" }\n"
60                "             [-a | --address ADDRESS] ...\n"
61                "   cell start { ID | [--name] NAME }\n"
62                "   cell shutdown { ID | [--name] NAME }\n"
63                "   cell destroy { ID | [--name] NAME }\n",
64                basename(prog));
65         for (ext = extensions; ext->cmd; ext++)
66                 printf("   %s %s %s\n", ext->cmd, ext->subcmd, ext->help);
67
68         exit(exit_status);
69 }
70
71 static void call_extension_script(const char *cmd, int argc, char *argv[])
72 {
73         const struct extension *ext;
74         char new_path[PATH_MAX];
75         char script[64];
76
77         if (argc < 3)
78                 return;
79
80         for (ext = extensions; ext->cmd; ext++) {
81                 if (strcmp(ext->cmd, cmd) != 0 ||
82                     strcmp(ext->subcmd, argv[2]) != 0)
83                         continue;
84
85                 snprintf(new_path, sizeof(new_path), "PATH=%s:%s:%s",
86                         dirname(argv[0]), JAILHOUSE_EXEC_DIR,
87                         getenv("PATH") ? : "");
88                 putenv(new_path);
89
90                 snprintf(script, sizeof(script), "jailhouse-%s-%s",
91                          cmd, ext->subcmd);
92                 execvp(script, &argv[2]);
93
94                 perror("execvp");
95                 exit(1);
96         }
97 }
98
99 static int open_dev()
100 {
101         int fd;
102
103         fd = open("/dev/jailhouse", O_RDWR);
104         if (fd < 0) {
105                 perror("opening /dev/jailhouse");
106                 exit(1);
107         }
108         return fd;
109 }
110
111 static void *read_string(const char *string, size_t *size)
112 {
113         void *buffer;
114
115         *size = strlen(string) + 1;
116
117         buffer = strdup(string);
118         if (!buffer) {
119                 fprintf(stderr, "insufficient memory\n");
120                 exit(1);
121         }
122
123         return buffer;
124 }
125
126 static void *read_file(const char *name, size_t *size)
127 {
128         struct stat stat;
129         void *buffer;
130         int fd;
131
132         fd = open(name, O_RDONLY);
133         if (fd < 0) {
134                 fprintf(stderr, "opening %s: %s\n", name, strerror(errno));
135                 exit(1);
136         }
137
138         if (fstat(fd, &stat) < 0) {
139                 perror("fstat");
140                 exit(1);
141         }
142
143         buffer = malloc(stat.st_size);
144         if (!buffer) {
145                 fprintf(stderr, "insufficient memory\n");
146                 exit(1);
147         }
148
149         if (read(fd, buffer, stat.st_size) < stat.st_size) {
150                 fprintf(stderr, "reading %s: %s\n", name, strerror(errno));
151                 exit(1);
152         }
153
154         close(fd);
155
156         if (size)
157                 *size = stat.st_size;
158
159         return buffer;
160 }
161
162 static int enable(int argc, char *argv[])
163 {
164         void *config;
165         int err, fd;
166
167         if (argc != 3)
168                 help(argv[0], 1);
169
170         config = read_file(argv[2], NULL);
171
172         fd = open_dev();
173
174         err = ioctl(fd, JAILHOUSE_ENABLE, config);
175         if (err)
176                 perror("JAILHOUSE_ENABLE");
177
178         close(fd);
179         free(config);
180
181         return err;
182 }
183
184 static int cell_create(int argc, char *argv[])
185 {
186         struct jailhouse_cell_create cell_create;
187         size_t size;
188         int err, fd;
189
190         if (argc != 4)
191                 help(argv[0], 1);
192
193         cell_create.config_address = (unsigned long)read_file(argv[3], &size);
194         cell_create.config_size = size;
195
196         fd = open_dev();
197
198         err = ioctl(fd, JAILHOUSE_CELL_CREATE, &cell_create);
199         if (err)
200                 perror("JAILHOUSE_CELL_CREATE");
201
202         close(fd);
203         free((void *)(unsigned long)cell_create.config_address);
204
205         return err;
206 }
207
208 static int parse_cell_id(struct jailhouse_cell_id *cell_id, int argc,
209                          char *argv[])
210 {
211         bool use_name = false;
212         int arg_pos = 0;
213         char *endp;
214
215         memset(cell_id, 0, sizeof(*cell_id));
216
217         if (argc < 1)
218                 return 0;
219
220         if (strcmp(argv[0], "--name") == 0) {
221                 if (argc < 2)
222                         return 0;
223                 arg_pos++;
224                 use_name = true;
225         } else {
226                 errno = 0;
227                 cell_id->id = strtoll(argv[0], &endp, 0);
228                 if (errno != 0 || *endp != 0 || cell_id->id < 0)
229                         use_name = true;
230         }
231
232         if (use_name) {
233                 cell_id->id = JAILHOUSE_CELL_ID_UNUSED;
234                 /* cell_id is initialized with zeros, so leaving out the last
235                  * byte ensures that the string is always terminated. */
236                 strncpy(cell_id->name, argv[arg_pos],
237                         sizeof(cell_id->name) - 1);
238         }
239
240         return arg_pos + 1;
241 }
242
243 static bool match_opt(const char *argv, const char *short_opt,
244                       const char *long_opt)
245 {
246         return strcmp(argv, short_opt) == 0 ||
247                 strcmp(argv, long_opt) == 0;
248 }
249
250 static int cell_shutdown_load(int argc, char *argv[],
251                               enum shutdown_load_mode mode)
252 {
253         unsigned int images, id_args, arg_num, n;
254         struct jailhouse_preload_image *image;
255         struct jailhouse_cell_load *cell_load;
256         struct jailhouse_cell_id cell_id;
257         size_t size;
258         int err, fd;
259         char *endp;
260
261         id_args = parse_cell_id(&cell_id, argc - 3, &argv[3]);
262         arg_num = 3 + id_args;
263         if (id_args == 0 || (mode == SHUTDOWN && arg_num != argc) ||
264             (mode == LOAD && arg_num == argc))
265                 help(argv[0], 1);
266
267         images = 0;
268         while (arg_num < argc) {
269                 if (match_opt(argv[arg_num], "-s", "--string")) {
270                         if (arg_num + 1 >= argc)
271                                 help(argv[0], 1);
272                         arg_num++;
273                 }
274
275                 images++;
276                 arg_num++;
277
278                 if (arg_num < argc &&
279                     match_opt(argv[arg_num], "-a", "--address")) {
280                         if (arg_num + 1 >= argc)
281                                 help(argv[0], 1);
282                         arg_num += 2;
283                 }
284         }
285
286         cell_load = malloc(sizeof(*cell_load) + sizeof(*image) * images);
287         if (!cell_load) {
288                 fprintf(stderr, "insufficient memory\n");
289                 exit(1);
290         }
291         cell_load->cell_id = cell_id;
292         cell_load->num_preload_images = images;
293
294         arg_num = 3 + id_args;
295
296         for (n = 0, image = cell_load->image; n < images; n++, image++) {
297                 if (match_opt(argv[arg_num], "-s", "--string")) {
298                         arg_num++;
299                         image->source_address =
300                                 (unsigned long)read_string(argv[arg_num++],
301                                                            &size);
302                 } else {
303                         image->source_address =
304                                 (unsigned long)read_file(argv[arg_num++],
305                                                          &size);
306                 }
307                 image->size = size;
308                 image->target_address = 0;
309
310                 if (arg_num < argc &&
311                     match_opt(argv[arg_num], "-a", "--address")) {
312                         errno = 0;
313                         image->target_address =
314                                 strtoll(argv[arg_num + 1], &endp, 0);
315                         if (errno != 0 || *endp != 0)
316                                 help(argv[0], 1);
317                         arg_num += 2;
318                 }
319         }
320
321         fd = open_dev();
322
323         err = ioctl(fd, JAILHOUSE_CELL_LOAD, cell_load);
324         if (err)
325                 perror("JAILHOUSE_CELL_LOAD");
326
327         close(fd);
328         for (n = 0, image = cell_load->image; n < images; n++, image++)
329                 free((void *)(unsigned long)image->source_address);
330         free(cell_load);
331
332         return err;
333 }
334
335 static int cell_simple_cmd(int argc, char *argv[], unsigned int command)
336 {
337         struct jailhouse_cell_id cell_id;
338         int id_args, err, fd;
339
340         id_args = parse_cell_id(&cell_id, argc - 3, &argv[3]);
341         if (id_args == 0 || 3 + id_args != argc)
342                 help(argv[0], 1);
343
344         fd = open_dev();
345
346         err = ioctl(fd, command, &cell_id);
347         if (err)
348                 perror(command == JAILHOUSE_CELL_START ?
349                        "JAILHOUSE_CELL_START" :
350                        command == JAILHOUSE_CELL_DESTROY ?
351                        "JAILHOUSE_CELL_DESTROY" :
352                        "<unknown command>");
353
354         close(fd);
355
356         return err;
357 }
358
359 static int cell_management(int argc, char *argv[])
360 {
361         int err;
362
363         if (argc < 3)
364                 help(argv[0], 1);
365
366         if (strcmp(argv[2], "create") == 0) {
367                 err = cell_create(argc, argv);
368         } else if (strcmp(argv[2], "load") == 0) {
369                 err = cell_shutdown_load(argc, argv, LOAD);
370         } else if (strcmp(argv[2], "start") == 0) {
371                 err = cell_simple_cmd(argc, argv, JAILHOUSE_CELL_START);
372         } else if (strcmp(argv[2], "shutdown") == 0) {
373                 err = cell_shutdown_load(argc, argv, SHUTDOWN);
374         } else if (strcmp(argv[2], "destroy") == 0) {
375                 err = cell_simple_cmd(argc, argv, JAILHOUSE_CELL_DESTROY);
376         } else {
377                 call_extension_script("cell", argc, argv);
378                 help(argv[0], 1);
379         }
380
381         return err;
382 }
383
384 int main(int argc, char *argv[])
385 {
386         int fd;
387         int err;
388
389         if (argc < 2)
390                 help(argv[0], 1);
391
392         if (strcmp(argv[1], "enable") == 0) {
393                 err = enable(argc, argv);
394         } else if (strcmp(argv[1], "disable") == 0) {
395                 fd = open_dev();
396                 err = ioctl(fd, JAILHOUSE_DISABLE);
397                 if (err)
398                         perror("JAILHOUSE_DISABLE");
399                 close(fd);
400         } else if (strcmp(argv[1], "cell") == 0) {
401                 err = cell_management(argc, argv);
402         } else if (strcmp(argv[1], "config") == 0) {
403                 call_extension_script(argv[1], argc, argv);
404                 help(argv[0], 1);
405         } else if (strcmp(argv[1], "--version") == 0) {
406                 printf("Jailhouse management tool %s\n", JAILHOUSE_VERSION);
407                 return 0;
408         } else if (strcmp(argv[1], "--help") == 0) {
409                 help(argv[0], 0);
410         } else {
411                 help(argv[0], 1);
412         }
413
414         return err ? 1 : 0;
415 }