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