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