xref: /openbsd-src/usr.sbin/ldomctl/ldomctl.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /*	$OpenBSD: ldomctl.c,v 1.19 2012/12/09 20:24:53 kettenis Exp $	*/
2 
3 /*
4  * Copyright (c) 2012 Mark Kettenis
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 #include <err.h>
22 #include <fcntl.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 #include "ds.h"
29 #include "hvctl.h"
30 #include "mdstore.h"
31 #include "mdesc.h"
32 #include "util.h"
33 #include "ldomctl.h"
34 
35 extern struct ds_service pri_service;
36 
37 struct command {
38 	const char *cmd_name;
39 	void (*cmd_func)(int, char **);
40 };
41 
42 __dead void usage(void);
43 
44 struct guest_head guest_list;
45 
46 uint64_t find_guest(const char *);
47 
48 void fetch_pri(void);
49 
50 void download(int argc, char **argv);
51 void dump(int argc, char **argv);
52 void list(int argc, char **argv);
53 void xselect(int argc, char **argv);
54 void delete(int argc, char **argv);
55 void guest_start(int argc, char **argv);
56 void guest_stop(int argc, char **argv);
57 void guest_panic(int argc, char **argv);
58 void guest_status(int argc, char **argv);
59 void init_system(int argc, char **argv);
60 
61 struct command commands[] = {
62 	{ "download",	download },
63 	{ "dump",	dump },
64 	{ "list",	list },
65 	{ "select",	xselect },
66 	{ "delete",	delete },
67 	{ "start",	guest_start },
68 	{ "stop",	guest_stop },
69 	{ "panic",	guest_panic },
70 	{ "status",	guest_status },
71 	{ "init-system", init_system },
72 	{ NULL,		NULL }
73 };
74 
75 void hv_open(void);
76 void hv_close(void);
77 void hv_read(uint64_t, void *, size_t);
78 void hv_write(uint64_t, void *, size_t);
79 
80 int hvctl_seq = 1;
81 int hvctl_fd;
82 
83 void *hvmd_buf;
84 size_t hvmd_len;
85 struct md *hvmd;
86 uint64_t hv_mdpa;
87 uint64_t hv_membase;
88 uint64_t hv_memsize;
89 
90 extern void *pri_buf;
91 extern size_t pri_len;
92 
93 int
94 main(int argc, char **argv)
95 {
96 	struct command *cmdp;
97 	struct hvctl_msg msg;
98 	ssize_t nbytes;
99 	struct md_header hdr;
100 	struct md_node *node;
101 	struct md_prop *prop;
102 
103 	if (argc < 2)
104 		usage();
105 
106 	/* Skip program name. */
107 	argv++;
108 	argc--;
109 
110 	for (cmdp = commands; cmdp->cmd_name != NULL; cmdp++)
111 		if (strcmp(argv[0], cmdp->cmd_name) == 0)
112 			break;
113 	if (cmdp->cmd_name == NULL)
114 		usage();
115 
116 	hv_open();
117 
118 	/*
119 	 * Request config.
120 	 */
121 	bzero(&msg, sizeof(msg));
122 	msg.hdr.op = HVCTL_OP_GET_HVCONFIG;
123 	msg.hdr.seq = hvctl_seq++;
124 	nbytes = write(hvctl_fd, &msg, sizeof(msg));
125 	if (nbytes != sizeof(msg))
126 		err(1, "write");
127 
128 	bzero(&msg, sizeof(msg));
129 	nbytes = read(hvctl_fd, &msg, sizeof(msg));
130 	if (nbytes != sizeof(msg))
131 		err(1, "read");
132 
133 	hv_membase = msg.msg.hvcnf.hv_membase;
134 	hv_memsize = msg.msg.hvcnf.hv_memsize;
135 
136 	hv_mdpa = msg.msg.hvcnf.hvmdp;
137 	hv_read(hv_mdpa, &hdr, sizeof(hdr));
138 	hvmd_len = sizeof(hdr) + hdr.node_blk_sz + hdr.name_blk_sz +
139 	    hdr.data_blk_sz;
140 	hvmd_buf = xmalloc(hvmd_len);
141 	hv_read(hv_mdpa, hvmd_buf, hvmd_len);
142 
143 	hvmd = md_ingest(hvmd_buf, hvmd_len);
144 	node = md_find_node(hvmd, "guests");
145 	TAILQ_INIT(&guest_list);
146 	TAILQ_FOREACH(prop, &node->prop_list, link) {
147 		if (prop->tag == MD_PROP_ARC &&
148 		    strcmp(prop->name->str, "fwd") == 0)
149 			add_guest(prop->d.arc.node);
150 	}
151 
152 	(cmdp->cmd_func)(argc, argv);
153 
154 	exit(EXIT_SUCCESS);
155 }
156 
157 void
158 usage(void)
159 {
160 	extern char *__progname;
161 
162 	fprintf(stderr, "usage: %s start|stop|panic domain\n", __progname);
163 	fprintf(stderr, "       %s status [domain]\n", __progname);
164 	exit(EXIT_FAILURE);
165 }
166 
167 void
168 add_guest(struct md_node *node)
169 {
170 	struct guest *guest;
171 	struct md_prop *prop;
172 
173 	guest = xmalloc (sizeof(*guest));
174 
175 	if (!md_get_prop_str(hvmd, node, "name", &guest->name))
176 		goto free;
177 	if (!md_get_prop_val(hvmd, node, "gid", &guest->gid))
178 		goto free;
179 	if (!md_get_prop_val(hvmd, node, "mdpa", &guest->mdpa))
180 		goto free;
181 
182 	guest->num_cpus = 0;
183 	TAILQ_FOREACH(prop, &node->prop_list, link) {
184 		if (prop->tag == MD_PROP_ARC &&
185 		    strcmp(prop->name->str, "fwd") == 0) {
186 			if (strcmp(prop->d.arc.node->name->str, "cpu") == 0)
187 				guest->num_cpus++;
188 		}
189 	}
190 
191 	TAILQ_INSERT_TAIL(&guest_list, guest, link);
192 	return;
193 
194 free:
195 	free(guest);
196 }
197 
198 uint64_t
199 find_guest(const char *name)
200 {
201 	struct guest *guest;
202 
203 	TAILQ_FOREACH(guest, &guest_list, link) {
204 		if (strcmp(guest->name, name) == 0)
205 			return guest->gid;
206 	}
207 
208 	errx(EXIT_FAILURE, "unknown guest '%s'", name);
209 }
210 
211 void
212 fetch_pri(void)
213 {
214 	struct ds_conn *dc;
215 
216 	dc = ds_conn_open("/dev/spds", NULL);
217 	ds_conn_register_service(dc, &pri_service);
218 	while (pri_buf == NULL)
219 		ds_conn_handle(dc);
220 }
221 
222 void
223 dump(int argc, char **argv)
224 {
225 	struct guest *guest;
226 	struct md_header hdr;
227 	void *md_buf;
228 	size_t md_len;
229 	char *name;
230 	FILE *fp;
231 
232 	if (argc != 1)
233 		usage();
234 
235 	fp = fopen("hv.md", "w");
236 	if (fp == NULL)
237 		err(1, "fopen");
238 	fwrite(hvmd_buf, hvmd_len, 1, fp);
239 	fclose(fp);
240 
241 	fetch_pri();
242 
243 	fp = fopen("pri", "w");
244 	if (fp == NULL)
245 		err(1, "fopen");
246 	fwrite(pri_buf, pri_len, 1, fp);
247 	fclose(fp);
248 
249 	TAILQ_FOREACH(guest, &guest_list, link) {
250 		hv_read(guest->mdpa, &hdr, sizeof(hdr));
251 		md_len = sizeof(hdr) + hdr.node_blk_sz + hdr.name_blk_sz +
252 		    hdr.data_blk_sz;
253 		md_buf = xmalloc(md_len);
254 		hv_read(guest->mdpa, md_buf, md_len);
255 
256 		if (asprintf(&name, "%s.md", guest->name) == -1)
257 			err(1, "asprintf");
258 
259 		fp = fopen(name, "w");
260 		if (fp == NULL)
261 			err(1, "fopen");
262 		fwrite(md_buf, md_len, 1, fp);
263 		fclose(fp);
264 
265 		free(name);
266 		free(md_buf);
267 	}
268 }
269 
270 void
271 init_system(int argc, char **argv)
272 {
273 	if (argc != 2)
274 		usage();
275 
276 	build_config(argv[1]);
277 }
278 
279 void
280 list(int argc, char **argv)
281 {
282 	struct ds_conn *dc;
283 	struct mdstore_set *set;
284 
285 	dc = ds_conn_open("/dev/spds", NULL);
286 	ds_conn_register_service(dc, &mdstore_service);
287 	while (TAILQ_EMPTY(&mdstore_sets))
288 		ds_conn_handle(dc);
289 
290 	TAILQ_FOREACH(set, &mdstore_sets, link) {
291 		printf("%s", set->name);
292 		if (set->booted_set)
293 			printf(" [current]");
294 		else if (set->boot_set)
295 			printf(" [next]");
296 		printf("\n");
297 	}
298 }
299 
300 void
301 xselect(int argc, char **argv)
302 {
303 	struct ds_conn *dc;
304 
305 	if (argc < 2)
306 		usage();
307 
308 	dc = ds_conn_open("/dev/spds", NULL);
309 	ds_conn_register_service(dc, &mdstore_service);
310 	while (TAILQ_EMPTY(&mdstore_sets))
311 		ds_conn_handle(dc);
312 
313 	mdstore_select(dc, argv[1]);
314 }
315 
316 void
317 delete(int argc, char **argv)
318 {
319 	struct ds_conn *dc;
320 
321 	if (argc < 2)
322 		usage();
323 
324 	if (strcmp(argv[1], "factory-default") == 0)
325 		errx(1, "\"%s\" should not be deleted", argv[1]);
326 
327 	dc = ds_conn_open("/dev/spds", NULL);
328 	ds_conn_register_service(dc, &mdstore_service);
329 	while (TAILQ_EMPTY(&mdstore_sets))
330 		ds_conn_handle(dc);
331 
332 	mdstore_delete(dc, argv[1]);
333 }
334 
335 void
336 download(int argc, char **argv)
337 {
338 	struct ds_conn *dc;
339 
340 	if (argc < 2)
341 		usage();
342 
343 	dc = ds_conn_open("/dev/spds", NULL);
344 	ds_conn_register_service(dc, &mdstore_service);
345 	while (TAILQ_EMPTY(&mdstore_sets))
346 		ds_conn_handle(dc);
347 
348 	mdstore_download(dc, argv[1]);
349 }
350 
351 void
352 guest_start(int argc, char **argv)
353 {
354 	struct hvctl_msg msg;
355 	ssize_t nbytes;
356 
357 	if (argc < 2)
358 		usage();
359 
360 	/*
361 	 * Start guest domain.
362 	 */
363 	bzero(&msg, sizeof(msg));
364 	msg.hdr.op = HVCTL_OP_GUEST_START;
365 	msg.hdr.seq = hvctl_seq++;
366 	msg.msg.guestop.guestid = find_guest(argv[1]);
367 	nbytes = write(hvctl_fd, &msg, sizeof(msg));
368 	if (nbytes != sizeof(msg))
369 		err(1, "write");
370 
371 	bzero(&msg, sizeof(msg));
372 	nbytes = read(hvctl_fd, &msg, sizeof(msg));
373 	if (nbytes != sizeof(msg))
374 		err(1, "read");
375 }
376 
377 void
378 guest_stop(int argc, char **argv)
379 {
380 	struct hvctl_msg msg;
381 	ssize_t nbytes;
382 
383 	if (argc < 2)
384 		usage();
385 
386 	/*
387 	 * Stop guest domain.
388 	 */
389 	bzero(&msg, sizeof(msg));
390 	msg.hdr.op = HVCTL_OP_GUEST_STOP;
391 	msg.hdr.seq = hvctl_seq++;
392 	msg.msg.guestop.guestid = find_guest(argv[1]);
393 	nbytes = write(hvctl_fd, &msg, sizeof(msg));
394 	if (nbytes != sizeof(msg))
395 		err(1, "write");
396 
397 	bzero(&msg, sizeof(msg));
398 	nbytes = read(hvctl_fd, &msg, sizeof(msg));
399 	if (nbytes != sizeof(msg))
400 		err(1, "read");
401 }
402 
403 void
404 guest_panic(int argc, char **argv)
405 {
406 	struct hvctl_msg msg;
407 	ssize_t nbytes;
408 
409 	if (argc < 2)
410 		usage();
411 
412 	/*
413 	 * Stop guest domain.
414 	 */
415 	bzero(&msg, sizeof(msg));
416 	msg.hdr.op = HVCTL_OP_GUEST_PANIC;
417 	msg.hdr.seq = hvctl_seq++;
418 	msg.msg.guestop.guestid = find_guest(argv[1]);
419 	nbytes = write(hvctl_fd, &msg, sizeof(msg));
420 	if (nbytes != sizeof(msg))
421 		err(1, "write");
422 
423 	bzero(&msg, sizeof(msg));
424 	nbytes = read(hvctl_fd, &msg, sizeof(msg));
425 	if (nbytes != sizeof(msg))
426 		err(1, "read");
427 }
428 
429 void
430 guest_status(int argc, char **argv)
431 {
432 	struct hvctl_msg msg;
433 	ssize_t nbytes;
434 	struct hvctl_rs_guest_state state;
435 	struct hvctl_rs_guest_softstate softstate;
436 	struct hvctl_rs_guest_util util;
437 	struct guest *guest;
438 	uint64_t gid = -1;
439 	uint64_t total_cycles, yielded_cycles;
440 	double utilisation = 0.0;
441 	const char *state_str;
442 	char buf[64];
443 
444 	if (argc < 1 || argc > 2)
445 		usage();
446 	if (argc == 2)
447 		gid = find_guest(argv[1]);
448 
449 	TAILQ_FOREACH(guest, &guest_list, link) {
450 		if (gid != -1 && guest->gid != gid)
451 			continue;
452 
453 		/*
454 		 * Request status.
455 		 */
456 		bzero(&msg, sizeof(msg));
457 		msg.hdr.op = HVCTL_OP_GET_RES_STAT;
458 		msg.hdr.seq = hvctl_seq++;
459 		msg.msg.resstat.res = HVCTL_RES_GUEST;
460 		msg.msg.resstat.resid = guest->gid;
461 		msg.msg.resstat.infoid = HVCTL_INFO_GUEST_STATE;
462 		nbytes = write(hvctl_fd, &msg, sizeof(msg));
463 		if (nbytes != sizeof(msg))
464 			err(1, "write");
465 
466 		bzero(&msg, sizeof(msg));
467 		nbytes = read(hvctl_fd, &msg, sizeof(msg));
468 		if (nbytes != sizeof(msg))
469 			err(1, "read");
470 
471 		memcpy(&state, msg.msg.resstat.data, sizeof(state));
472 		switch (state.state) {
473 		case GUEST_STATE_STOPPED:
474 			state_str = "stopped";
475 			break;
476 		case GUEST_STATE_RESETTING:
477 			state_str = "resetting";
478 			break;
479 		case GUEST_STATE_NORMAL:
480 			state_str = "running";
481 
482 			bzero(&msg, sizeof(msg));
483 			msg.hdr.op = HVCTL_OP_GET_RES_STAT;
484 			msg.hdr.seq = hvctl_seq++;
485 			msg.msg.resstat.res = HVCTL_RES_GUEST;
486 			msg.msg.resstat.resid = guest->gid;
487 			msg.msg.resstat.infoid = HVCTL_INFO_GUEST_SOFT_STATE;
488 			nbytes = write(hvctl_fd, &msg, sizeof(msg));
489 			if (nbytes != sizeof(msg))
490 				err(1, "write");
491 
492 			bzero(&msg, sizeof(msg));
493 			nbytes = read(hvctl_fd, &msg, sizeof(msg));
494 			if (nbytes != sizeof(msg))
495 				err(1, "read");
496 
497 			memcpy(&softstate, msg.msg.resstat.data,
498 			   sizeof(softstate));
499 
500 			bzero(&msg, sizeof(msg));
501 			msg.hdr.op = HVCTL_OP_GET_RES_STAT;
502 			msg.hdr.seq = hvctl_seq++;
503 			msg.msg.resstat.res = HVCTL_RES_GUEST;
504 			msg.msg.resstat.resid = guest->gid;
505 			msg.msg.resstat.infoid = HVCTL_INFO_GUEST_UTILISATION;
506 			nbytes = write(hvctl_fd, &msg, sizeof(msg));
507 			if (nbytes != sizeof(msg))
508 				err(1, "write");
509 
510 			bzero(&msg, sizeof(msg));
511 			nbytes = read(hvctl_fd, &msg, sizeof(msg));
512 			if (nbytes != sizeof(msg))
513 				err(1, "read");
514 
515 			memcpy(&util, msg.msg.resstat.data, sizeof(util));
516 
517 			total_cycles = util.active_delta * guest->num_cpus
518 			    - util.stopped_cycles;
519 			yielded_cycles = util.yielded_cycles;
520 			if (yielded_cycles <= total_cycles)
521 				utilisation = (100.0 * (total_cycles
522 				    - yielded_cycles)) / total_cycles;
523 			else
524 				utilisation = 0.0;
525 
526 			break;
527 		case GUEST_STATE_SUSPENDED:
528 			state_str = "suspended";
529 			break;
530 		case GUEST_STATE_EXITING:
531 			state_str = "exiting";
532 			break;
533 		case GUEST_STATE_UNCONFIGURED:
534 			state_str = "unconfigured";
535 			break;
536 		default:
537 			snprintf(buf, sizeof(buf), "unknown (%lld)",
538 			    state.state);
539 			state_str = buf;
540 			break;
541 		}
542 
543 		if (state.state != GUEST_STATE_NORMAL)
544 			printf("%-16s  %-16s\n", guest->name, state_str);
545 		else
546 			printf("%-16s  %-16s  %-32s  %3.0f%%\n", guest->name,
547 			       state_str, softstate.soft_state_str,
548 			       utilisation);
549 	}
550 }
551 
552 void
553 hv_open(void)
554 {
555 	struct hvctl_msg msg;
556 	ssize_t nbytes;
557 	uint64_t code;
558 
559 	hvctl_fd = open("/dev/hvctl", O_RDWR, 0);
560 	if (hvctl_fd == -1)
561 		err(1, "cannot open /dev/hvctl");
562 
563 	/*
564 	 * Say "Hello".
565 	 */
566 	bzero(&msg, sizeof(msg));
567 	msg.hdr.op = HVCTL_OP_HELLO;
568 	msg.hdr.seq = hvctl_seq++;
569 	msg.msg.hello.major = 1;
570 	nbytes = write(hvctl_fd, &msg, sizeof(msg));
571 	if (nbytes != sizeof(msg))
572 		err(1, "write");
573 
574 	bzero(&msg, sizeof(msg));
575 	nbytes = read(hvctl_fd, &msg, sizeof(msg));
576 	if (nbytes != sizeof(msg))
577 		err(1, "read");
578 
579 	code = msg.msg.clnge.code ^ 0xbadbeef20;
580 
581 	/*
582 	 * Respond to challenge.
583 	 */
584 	bzero(&msg, sizeof(msg));
585 	msg.hdr.op = HVCTL_OP_RESPONSE;
586 	msg.hdr.seq = hvctl_seq++;
587 	msg.msg.clnge.code = code ^ 0x12cafe42a;
588 	nbytes = write(hvctl_fd, &msg, sizeof(msg));
589 	if (nbytes != sizeof(msg))
590 		err(1, "write");
591 
592 	bzero(&msg, sizeof(msg));
593 	nbytes = read(hvctl_fd, &msg, sizeof(msg));
594 	if (nbytes != sizeof(msg))
595 		err(1, "read");
596 }
597 
598 void
599 hv_close(void)
600 {
601 	close(hvctl_fd);
602 	hvctl_fd = -1;
603 }
604 
605 void
606 hv_read(uint64_t addr, void *buf, size_t len)
607 {
608 	struct hv_io hi;
609 
610 	hi.hi_cookie = addr;
611 	hi.hi_addr = buf;
612 	hi.hi_len = len;
613 
614 	if (ioctl(hvctl_fd, HVIOCREAD, &hi) == -1)
615 		err(1, "ioctl");
616 }
617 
618 void
619 hv_write(uint64_t addr, void *buf, size_t len)
620 {
621 	struct hv_io hi;
622 
623 	hi.hi_cookie = addr;
624 	hi.hi_addr = buf;
625 	hi.hi_len = len;
626 
627 	if (ioctl(hvctl_fd, HVIOCWRITE, &hi) == -1)
628 		err(1, "ioctl");
629 }
630