xref: /openbsd/usr.sbin/ldomctl/ldomctl.c (revision 3cab2bb3)
1 /*	$OpenBSD: ldomctl.c,v 1.38 2020/02/02 00:49:06 kn 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 <sys/stat.h>
22 #include <err.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <util.h>
30 
31 #include "ds.h"
32 #include "hvctl.h"
33 #include "mdstore.h"
34 #include "mdesc.h"
35 #include "ldom_util.h"
36 #include "ldomctl.h"
37 
38 extern struct ds_service pri_service;
39 
40 struct command {
41 	const char *cmd_name;
42 	void (*cmd_func)(int, char **);
43 };
44 
45 __dead void usage(void);
46 
47 struct guest_head guest_list;
48 
49 uint64_t find_guest(const char *);
50 
51 void fetch_pri(void);
52 
53 void download(int argc, char **argv);
54 void dump(int argc, char **argv);
55 void list(int argc, char **argv);
56 void list_io(int argc, char **argv);
57 void xselect(int argc, char **argv);
58 void delete(int argc, char **argv);
59 void create_vdisk(int argc, char **argv);
60 void guest_start(int argc, char **argv);
61 void guest_stop(int argc, char **argv);
62 void guest_panic(int argc, char **argv);
63 void guest_status(int argc, char **argv);
64 void guest_console(int argc, char **argv);
65 void init_system(int argc, char **argv);
66 
67 struct command commands[] = {
68 	{ "download",	download },
69 	{ "dump",	dump },
70 	{ "list",	list },
71 	{ "list-io",	list_io },
72 	{ "select",	xselect },
73 	{ "delete",	delete },
74 	{ "create-vdisk", create_vdisk },
75 	{ "start",	guest_start },
76 	{ "stop",	guest_stop },
77 	{ "panic",	guest_panic },
78 	{ "status",	guest_status },
79 	{ "console",	guest_console },
80 	{ "init-system", init_system },
81 	{ NULL,		NULL }
82 };
83 
84 void hv_open(void);
85 void hv_close(void);
86 void hv_config(void);
87 void hv_read(uint64_t, void *, size_t);
88 void hv_write(uint64_t, void *, size_t);
89 
90 int hvctl_seq = 1;
91 int hvctl_fd;
92 
93 void *hvmd_buf;
94 size_t hvmd_len;
95 struct md *hvmd;
96 uint64_t hv_mdpa;
97 uint64_t hv_membase;
98 uint64_t hv_memsize;
99 
100 extern void *pri_buf;
101 extern size_t pri_len;
102 
103 int
104 main(int argc, char **argv)
105 {
106 	struct command *cmdp;
107 
108 	if (argc < 2)
109 		usage();
110 
111 	/* Skip program name. */
112 	argv++;
113 	argc--;
114 
115 	for (cmdp = commands; cmdp->cmd_name != NULL; cmdp++)
116 		if (strcmp(argv[0], cmdp->cmd_name) == 0)
117 			break;
118 	if (cmdp->cmd_name == NULL)
119 		usage();
120 
121 	(cmdp->cmd_func)(argc, argv);
122 
123 	exit(EXIT_SUCCESS);
124 }
125 
126 __dead void
127 usage(void)
128 {
129 	fprintf(stderr, "usage:\t%1$s delete|select configuration\n"
130 	    "\t%1$s download directory\n"
131 	    "\t%1$s dump|list|list-io\n"
132 	    "\t%1$s init-system [-n] file\n"
133 	    "\t%1$s create-vdisk -s size file\n"
134 	    "\t%1$s panic|start [-c] domain\n"
135 	    "\t%1$s console|status|stop [domain]\n",
136 	    getprogname());
137 
138 	exit(EXIT_FAILURE);
139 }
140 
141 void
142 add_guest(struct md_node *node)
143 {
144 	struct guest *guest;
145 	struct md_prop *prop;
146 
147 	guest = xmalloc(sizeof(*guest));
148 
149 	if (!md_get_prop_str(hvmd, node, "name", &guest->name))
150 		goto free;
151 	if (!md_get_prop_val(hvmd, node, "gid", &guest->gid))
152 		goto free;
153 	if (!md_get_prop_val(hvmd, node, "mdpa", &guest->mdpa))
154 		goto free;
155 
156 	guest->num_cpus = 0;
157 	TAILQ_FOREACH(prop, &node->prop_list, link) {
158 		if (prop->tag == MD_PROP_ARC &&
159 		    strcmp(prop->name->str, "fwd") == 0) {
160 			if (strcmp(prop->d.arc.node->name->str, "cpu") == 0)
161 				guest->num_cpus++;
162 		}
163 	}
164 
165 	TAILQ_INSERT_TAIL(&guest_list, guest, link);
166 	return;
167 
168 free:
169 	free(guest);
170 }
171 
172 uint64_t
173 find_guest(const char *name)
174 {
175 	struct guest *guest;
176 
177 	TAILQ_FOREACH(guest, &guest_list, link) {
178 		if (strcmp(guest->name, name) == 0)
179 			return guest->gid;
180 	}
181 
182 	errx(EXIT_FAILURE, "unknown guest '%s'", name);
183 }
184 
185 void
186 fetch_pri(void)
187 {
188 	struct ds_conn *dc;
189 
190 	dc = ds_conn_open("/dev/spds", NULL);
191 	ds_conn_register_service(dc, &pri_service);
192 	while (pri_buf == NULL)
193 		ds_conn_handle(dc);
194 }
195 
196 void
197 dump(int argc, char **argv)
198 {
199 	struct guest *guest;
200 	struct md_header hdr;
201 	void *md_buf;
202 	size_t md_len;
203 	char *name;
204 	FILE *fp;
205 
206 	if (argc != 1)
207 		usage();
208 
209 	hv_config();
210 
211 	fp = fopen("hv.md", "w");
212 	if (fp == NULL)
213 		err(1, "fopen");
214 	fwrite(hvmd_buf, hvmd_len, 1, fp);
215 	fclose(fp);
216 
217 	fetch_pri();
218 
219 	fp = fopen("pri", "w");
220 	if (fp == NULL)
221 		err(1, "fopen");
222 	fwrite(pri_buf, pri_len, 1, fp);
223 	fclose(fp);
224 
225 	TAILQ_FOREACH(guest, &guest_list, link) {
226 		hv_read(guest->mdpa, &hdr, sizeof(hdr));
227 		md_len = sizeof(hdr) + hdr.node_blk_sz + hdr.name_blk_sz +
228 		    hdr.data_blk_sz;
229 		md_buf = xmalloc(md_len);
230 		hv_read(guest->mdpa, md_buf, md_len);
231 
232 		if (asprintf(&name, "%s.md", guest->name) == -1)
233 			err(1, "asprintf");
234 
235 		fp = fopen(name, "w");
236 		if (fp == NULL)
237 			err(1, "fopen");
238 		fwrite(md_buf, md_len, 1, fp);
239 		fclose(fp);
240 
241 		free(name);
242 		free(md_buf);
243 	}
244 }
245 
246 void
247 init_system(int argc, char **argv)
248 {
249 	int ch, noaction = 0;
250 
251 	while ((ch = getopt(argc, argv, "n")) != -1) {
252 		switch (ch) {
253 		case 'n':
254 			noaction = 1;
255 			break;
256 		default:
257 			usage();
258 		}
259 	}
260 	argc -= optind;
261 	argv += optind;
262 
263 	if (argc != 1)
264 		usage();
265 
266 	if (!noaction)
267 		hv_config();
268 
269 	build_config(argv[0], noaction);
270 }
271 
272 void
273 list(int argc, char **argv)
274 {
275 	struct ds_conn *dc;
276 	struct mdstore_set *set;
277 
278 	if (argc != 1)
279 		usage();
280 
281 	hv_config();
282 
283 	dc = ds_conn_open("/dev/spds", NULL);
284 	mdstore_register(dc);
285 	while (TAILQ_EMPTY(&mdstore_sets))
286 		ds_conn_handle(dc);
287 
288 	TAILQ_FOREACH(set, &mdstore_sets, link) {
289 		printf("%s", set->name);
290 		if (set->booted_set)
291 			printf(" [current]");
292 		else if (set->boot_set)
293 			printf(" [next]");
294 		printf("\n");
295 	}
296 }
297 
298 void
299 list_io(int argc, char **argv)
300 {
301 	if (argc != 1)
302 		usage();
303 
304 	list_components();
305 }
306 
307 void
308 xselect(int argc, char **argv)
309 {
310 	struct ds_conn *dc;
311 
312 	if (argc != 2)
313 		usage();
314 
315 	hv_config();
316 
317 	dc = ds_conn_open("/dev/spds", NULL);
318 	mdstore_register(dc);
319 	while (TAILQ_EMPTY(&mdstore_sets))
320 		ds_conn_handle(dc);
321 
322 	mdstore_select(dc, argv[1]);
323 }
324 
325 void
326 delete(int argc, char **argv)
327 {
328 	struct ds_conn *dc;
329 
330 	if (argc != 2)
331 		usage();
332 
333 	if (strcmp(argv[1], "factory-default") == 0)
334 		errx(1, "\"%s\" should not be deleted", argv[1]);
335 
336 	hv_config();
337 
338 	dc = ds_conn_open("/dev/spds", NULL);
339 	mdstore_register(dc);
340 	while (TAILQ_EMPTY(&mdstore_sets))
341 		ds_conn_handle(dc);
342 
343 	mdstore_delete(dc, argv[1]);
344 }
345 
346 void
347 create_vdisk(int argc, char **argv)
348 {
349 	int ch, fd, save_errno;
350 	long long imgsize;
351 	const char *imgfile_path;
352 
353 	while ((ch = getopt(argc, argv, "s:")) != -1) {
354 		switch (ch) {
355 		case 's':
356 			if (scan_scaled(optarg, &imgsize) == -1)
357 				err(1, "invalid size: %s", optarg);
358 			break;
359 		default:
360 			usage();
361 		}
362 	}
363 	argc -= optind;
364 	argv += optind;
365 
366 	if (argc != 1)
367 		usage();
368 
369 	imgfile_path = argv[0];
370 
371 	/* Refuse to overwrite an existing image */
372 	if ((fd = open(imgfile_path, O_RDWR | O_CREAT | O_TRUNC | O_EXCL,
373 	    S_IRUSR | S_IWUSR)) == -1)
374 		err(1, "open");
375 
376 	/* Extend to desired size */
377 	if (ftruncate(fd, (off_t)imgsize) == -1) {
378 		save_errno = errno;
379 		close(fd);
380 		unlink(imgfile_path);
381 		errno = save_errno;
382 		err(1, "ftruncate");
383 	}
384 
385 	close(fd);
386 }
387 
388 void
389 download(int argc, char **argv)
390 {
391 	struct ds_conn *dc;
392 
393 	if (argc != 2)
394 		usage();
395 
396 	hv_config();
397 
398 	dc = ds_conn_open("/dev/spds", NULL);
399 	mdstore_register(dc);
400 	while (TAILQ_EMPTY(&mdstore_sets))
401 		ds_conn_handle(dc);
402 
403 	mdstore_download(dc, argv[1]);
404 }
405 
406 void
407 console_exec(uint64_t gid)
408 {
409 	struct guest *guest;
410 	char console_str[8];
411 
412 	if (gid == 0)
413 		errx(1, "no console for primary domain");
414 
415 	TAILQ_FOREACH(guest, &guest_list, link) {
416 		if (guest->gid != gid)
417 			continue;
418 		snprintf(console_str, sizeof(console_str),
419 		    "ttyV%llu", guest->gid - 1);
420 
421 		closefrom(STDERR_FILENO + 1);
422 		execl(LDOMCTL_CU, LDOMCTL_CU, "-r", "-l", console_str,
423 		    (char *)NULL);
424 		err(1, "failed to open console");
425 	}
426 }
427 
428 void
429 guest_start(int argc, char **argv)
430 {
431 	struct hvctl_msg msg;
432 	ssize_t nbytes;
433 	uint64_t gid;
434 	int ch, console = 0;
435 
436 	while ((ch = getopt(argc, argv, "c")) != -1) {
437 		switch (ch) {
438 		case 'c':
439 			console = 1;
440 			break;
441 		default:
442 			usage();
443 		}
444 	}
445 	argc -= optind;
446 	argv += optind;
447 
448 	if (argc != 1)
449 		usage();
450 
451 	hv_config();
452 
453 	gid = find_guest(argv[0]);
454 
455 	/*
456 	 * Start guest domain.
457 	 */
458 	bzero(&msg, sizeof(msg));
459 	msg.hdr.op = HVCTL_OP_GUEST_START;
460 	msg.hdr.seq = hvctl_seq++;
461 	msg.msg.guestop.guestid = gid;
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 	if (console)
472 		console_exec(gid);
473 }
474 
475 void
476 guest_stop(int argc, char **argv)
477 {
478 	struct hvctl_msg msg;
479 	ssize_t nbytes;
480 
481 	if (argc != 2)
482 		usage();
483 
484 	hv_config();
485 
486 	/*
487 	 * Stop guest domain.
488 	 */
489 	bzero(&msg, sizeof(msg));
490 	msg.hdr.op = HVCTL_OP_GUEST_STOP;
491 	msg.hdr.seq = hvctl_seq++;
492 	msg.msg.guestop.guestid = find_guest(argv[1]);
493 	nbytes = write(hvctl_fd, &msg, sizeof(msg));
494 	if (nbytes != sizeof(msg))
495 		err(1, "write");
496 
497 	bzero(&msg, sizeof(msg));
498 	nbytes = read(hvctl_fd, &msg, sizeof(msg));
499 	if (nbytes != sizeof(msg))
500 		err(1, "read");
501 }
502 
503 void
504 guest_panic(int argc, char **argv)
505 {
506 	struct hvctl_msg msg;
507 	ssize_t nbytes;
508 	uint64_t gid;
509 	int ch, console = 0;
510 
511 	while ((ch = getopt(argc, argv, "c")) != -1) {
512 		switch (ch) {
513 		case 'c':
514 			console = 1;
515 			break;
516 		default:
517 			usage();
518 		}
519 	}
520 	argc -= optind;
521 	argv += optind;
522 
523 	if (argc != 1)
524 		usage();
525 
526 	hv_config();
527 
528 	gid = find_guest(argv[0]);
529 
530 	/*
531 	 * Stop guest domain.
532 	 */
533 	bzero(&msg, sizeof(msg));
534 	msg.hdr.op = HVCTL_OP_GUEST_PANIC;
535 	msg.hdr.seq = hvctl_seq++;
536 	msg.msg.guestop.guestid = gid;
537 	nbytes = write(hvctl_fd, &msg, sizeof(msg));
538 	if (nbytes != sizeof(msg))
539 		err(1, "write");
540 
541 	bzero(&msg, sizeof(msg));
542 	nbytes = read(hvctl_fd, &msg, sizeof(msg));
543 	if (nbytes != sizeof(msg))
544 		err(1, "read");
545 
546 	if (console)
547 		console_exec(gid);
548 }
549 
550 void
551 guest_status(int argc, char **argv)
552 {
553 	struct hvctl_msg msg;
554 	ssize_t nbytes;
555 	struct hvctl_rs_guest_state state;
556 	struct hvctl_rs_guest_softstate softstate;
557 	struct hvctl_rs_guest_util util;
558 	struct guest *guest;
559 	uint64_t gid = -1;
560 	uint64_t total_cycles, yielded_cycles;
561 	double utilisation = 0.0;
562 	const char *state_str;
563 	char buf[32];
564 	char console_str[8] = "-";
565 
566 	if (argc < 1 || argc > 2)
567 		usage();
568 
569 	hv_config();
570 
571 	if (argc == 2)
572 		gid = find_guest(argv[1]);
573 
574 	TAILQ_FOREACH(guest, &guest_list, link) {
575 		if (gid != -1 && guest->gid != gid)
576 			continue;
577 
578 		/*
579 		 * Request status.
580 		 */
581 		bzero(&msg, sizeof(msg));
582 		msg.hdr.op = HVCTL_OP_GET_RES_STAT;
583 		msg.hdr.seq = hvctl_seq++;
584 		msg.msg.resstat.res = HVCTL_RES_GUEST;
585 		msg.msg.resstat.resid = guest->gid;
586 		msg.msg.resstat.infoid = HVCTL_INFO_GUEST_STATE;
587 		nbytes = write(hvctl_fd, &msg, sizeof(msg));
588 		if (nbytes != sizeof(msg))
589 			err(1, "write");
590 
591 		bzero(&msg, sizeof(msg));
592 		nbytes = read(hvctl_fd, &msg, sizeof(msg));
593 		if (nbytes != sizeof(msg))
594 			err(1, "read");
595 
596 		memcpy(&state, msg.msg.resstat.data, sizeof(state));
597 		switch (state.state) {
598 		case GUEST_STATE_STOPPED:
599 			state_str = "stopped";
600 			break;
601 		case GUEST_STATE_RESETTING:
602 			state_str = "resetting";
603 			break;
604 		case GUEST_STATE_NORMAL:
605 			state_str = "running";
606 
607 			bzero(&msg, sizeof(msg));
608 			msg.hdr.op = HVCTL_OP_GET_RES_STAT;
609 			msg.hdr.seq = hvctl_seq++;
610 			msg.msg.resstat.res = HVCTL_RES_GUEST;
611 			msg.msg.resstat.resid = guest->gid;
612 			msg.msg.resstat.infoid = HVCTL_INFO_GUEST_SOFT_STATE;
613 			nbytes = write(hvctl_fd, &msg, sizeof(msg));
614 			if (nbytes != sizeof(msg))
615 				err(1, "write");
616 
617 			bzero(&msg, sizeof(msg));
618 			nbytes = read(hvctl_fd, &msg, sizeof(msg));
619 			if (nbytes != sizeof(msg))
620 				err(1, "read");
621 
622 			memcpy(&softstate, msg.msg.resstat.data,
623 			   sizeof(softstate));
624 
625 			bzero(&msg, sizeof(msg));
626 			msg.hdr.op = HVCTL_OP_GET_RES_STAT;
627 			msg.hdr.seq = hvctl_seq++;
628 			msg.msg.resstat.res = HVCTL_RES_GUEST;
629 			msg.msg.resstat.resid = guest->gid;
630 			msg.msg.resstat.infoid = HVCTL_INFO_GUEST_UTILISATION;
631 			nbytes = write(hvctl_fd, &msg, sizeof(msg));
632 			if (nbytes != sizeof(msg))
633 				err(1, "write");
634 
635 			bzero(&msg, sizeof(msg));
636 			nbytes = read(hvctl_fd, &msg, sizeof(msg));
637 			if (nbytes != sizeof(msg))
638 				err(1, "read");
639 
640 			memcpy(&util, msg.msg.resstat.data, sizeof(util));
641 
642 			total_cycles = util.active_delta * guest->num_cpus
643 			    - util.stopped_cycles;
644 			yielded_cycles = util.yielded_cycles;
645 			if (yielded_cycles <= total_cycles)
646 				utilisation = (100.0 * (total_cycles
647 				    - yielded_cycles)) / total_cycles;
648 			else
649 				utilisation = 0.0;
650 
651 			break;
652 		case GUEST_STATE_SUSPENDED:
653 			state_str = "suspended";
654 			break;
655 		case GUEST_STATE_EXITING:
656 			state_str = "exiting";
657 			break;
658 		case GUEST_STATE_UNCONFIGURED:
659 			state_str = "unconfigured";
660 			break;
661 		default:
662 			snprintf(buf, sizeof(buf), "unknown (%lld)",
663 			    state.state);
664 			state_str = buf;
665 			break;
666 		}
667 
668 		/* primary has no console */
669 		if (guest->gid != 0) {
670 			snprintf(console_str, sizeof(console_str),
671 			    "ttyV%llu", guest->gid - 1);
672 		}
673 
674 		printf("%-16s %-8s %-16s %-32s %3.0f%%\n", guest->name,
675 		    console_str, state_str, state.state == GUEST_STATE_NORMAL ?
676 		    softstate.soft_state_str : "-", utilisation);
677 	}
678 }
679 
680 void
681 guest_console(int argc, char **argv)
682 {
683 	uint64_t gid;
684 
685 	if (argc != 2)
686 		usage();
687 
688 	hv_config();
689 
690 	gid = find_guest(argv[1]);
691 
692 	console_exec(gid);
693 }
694 
695 void
696 hv_open(void)
697 {
698 	struct hvctl_msg msg;
699 	ssize_t nbytes;
700 	uint64_t code;
701 
702 	hvctl_fd = open("/dev/hvctl", O_RDWR, 0);
703 	if (hvctl_fd == -1)
704 		err(1, "cannot open /dev/hvctl");
705 
706 	/*
707 	 * Say "Hello".
708 	 */
709 	bzero(&msg, sizeof(msg));
710 	msg.hdr.op = HVCTL_OP_HELLO;
711 	msg.hdr.seq = hvctl_seq++;
712 	msg.msg.hello.major = 1;
713 	nbytes = write(hvctl_fd, &msg, sizeof(msg));
714 	if (nbytes != sizeof(msg))
715 		err(1, "write");
716 
717 	bzero(&msg, sizeof(msg));
718 	nbytes = read(hvctl_fd, &msg, sizeof(msg));
719 	if (nbytes != sizeof(msg))
720 		err(1, "read");
721 
722 	code = msg.msg.clnge.code ^ 0xbadbeef20;
723 
724 	/*
725 	 * Respond to challenge.
726 	 */
727 	bzero(&msg, sizeof(msg));
728 	msg.hdr.op = HVCTL_OP_RESPONSE;
729 	msg.hdr.seq = hvctl_seq++;
730 	msg.msg.clnge.code = code ^ 0x12cafe42a;
731 	nbytes = write(hvctl_fd, &msg, sizeof(msg));
732 	if (nbytes != sizeof(msg))
733 		err(1, "write");
734 
735 	bzero(&msg, sizeof(msg));
736 	nbytes = read(hvctl_fd, &msg, sizeof(msg));
737 	if (nbytes != sizeof(msg))
738 		err(1, "read");
739 }
740 
741 void
742 hv_close(void)
743 {
744 	close(hvctl_fd);
745 	hvctl_fd = -1;
746 }
747 
748 void
749 hv_config(void)
750 {
751 	struct hvctl_msg msg;
752 	ssize_t nbytes;
753 	struct md_header hdr;
754 	struct md_node *node;
755 	struct md_prop *prop;
756 
757 	hv_open();
758 
759 	/*
760 	 * Request config.
761 	 */
762 	bzero(&msg, sizeof(msg));
763 	msg.hdr.op = HVCTL_OP_GET_HVCONFIG;
764 	msg.hdr.seq = hvctl_seq++;
765 	nbytes = write(hvctl_fd, &msg, sizeof(msg));
766 	if (nbytes != sizeof(msg))
767 		err(1, "write");
768 
769 	bzero(&msg, sizeof(msg));
770 	nbytes = read(hvctl_fd, &msg, sizeof(msg));
771 	if (nbytes != sizeof(msg))
772 		err(1, "read");
773 
774 	hv_membase = msg.msg.hvcnf.hv_membase;
775 	hv_memsize = msg.msg.hvcnf.hv_memsize;
776 
777 	hv_mdpa = msg.msg.hvcnf.hvmdp;
778 	hv_read(hv_mdpa, &hdr, sizeof(hdr));
779 	hvmd_len = sizeof(hdr) + hdr.node_blk_sz + hdr.name_blk_sz +
780 	    hdr.data_blk_sz;
781 	hvmd_buf = xmalloc(hvmd_len);
782 	hv_read(hv_mdpa, hvmd_buf, hvmd_len);
783 
784 	hvmd = md_ingest(hvmd_buf, hvmd_len);
785 	node = md_find_node(hvmd, "guests");
786 	TAILQ_INIT(&guest_list);
787 	TAILQ_FOREACH(prop, &node->prop_list, link) {
788 		if (prop->tag == MD_PROP_ARC &&
789 		    strcmp(prop->name->str, "fwd") == 0)
790 			add_guest(prop->d.arc.node);
791 	}
792 }
793 
794 void
795 hv_read(uint64_t addr, void *buf, size_t len)
796 {
797 	struct hv_io hi;
798 
799 	hi.hi_cookie = addr;
800 	hi.hi_addr = buf;
801 	hi.hi_len = len;
802 
803 	if (ioctl(hvctl_fd, HVIOCREAD, &hi) == -1)
804 		err(1, "ioctl");
805 }
806 
807 void
808 hv_write(uint64_t addr, void *buf, size_t len)
809 {
810 	struct hv_io hi;
811 
812 	hi.hi_cookie = addr;
813 	hi.hi_addr = buf;
814 	hi.hi_len = len;
815 
816 	if (ioctl(hvctl_fd, HVIOCWRITE, &hi) == -1)
817 		err(1, "ioctl");
818 }
819