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