Index: share/examples/bhyve/guest.conf.sample =================================================================== --- /dev/null +++ share/examples/bhyve/guest.conf.sample @@ -0,0 +1,26 @@ +name = "vm"; +cpus = 2; +memory = 1024MB; +console = "/dev/nmdm0001A"; +device [ + { + slot = "0:0"; + type = "hostbridge"; + } + { + slot = "1:0"; + type = "lpc"; + } +] +acpi = true; +vmexit_on_hlt = true; +vmexit_on_pause = true; +disk { + type = "virtio-blk"; + path = "/path/to/image"; +} +network { + type = "virtio-net"; + name = "tap0"; +} + Index: usr.sbin/bhyve/Makefile =================================================================== --- usr.sbin/bhyve/Makefile +++ usr.sbin/bhyve/Makefile @@ -14,6 +14,7 @@ bhyverun.c \ block_if.c \ consport.c \ + config.c \ dbgport.c \ inout.c \ ioapic.c \ @@ -43,7 +44,8 @@ .PATH: ${.CURDIR}/../../sys/amd64/vmm SRCS+= vmm_instruction_emul.c -LIBADD= vmmapi md pthread +CFLAGS+=-I${.CURDIR}/../../contrib/libucl/include +LIBADD= vmmapi md pthread ucl WARNS?= 2 Index: usr.sbin/bhyve/bhyve.8 =================================================================== --- usr.sbin/bhyve/bhyve.8 +++ usr.sbin/bhyve/bhyve.8 @@ -41,6 +41,8 @@ .Op Fl s Ar slot,emulation Ns Op , Ns Ar conf .Op Fl U Ar uuid .Ar vmname +.Nm +.Op Fl f Ar guest.conf .Sh DESCRIPTION .Nm is a hypervisor that runs guest operating systems inside a @@ -260,6 +262,8 @@ Alphanumeric name of the guest. This should be the same as that created by .Xr bhyveload 8 . +.It Fl f +Use a configuration file. .El .Sh EXAMPLES The guest operating system must have been loaded with @@ -308,6 +312,11 @@ -l com1,/dev/nmdm0A \\ -A -H -P -m 8G .Ed +.Pp +To create a virtual machine using a configuration file: +.Bd -literal -offset ident +bhyve -f guest.conf +.Ed .Sh SEE ALSO .Xr bhyve 4 , .Xr nmdm 4 , Index: usr.sbin/bhyve/bhyverun.c =================================================================== --- usr.sbin/bhyve/bhyverun.c +++ usr.sbin/bhyve/bhyverun.c @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -67,6 +68,9 @@ #include "spinup_ap.h" #include "rtc.h" +#include "config.h" +#include + #define GUEST_NIO_PORT 0x488 /* guest upcalls via i/o port */ #define MB (1024UL * 1024) @@ -122,13 +126,14 @@ { fprintf(stderr, - "Usage: %s [-abehuwxACHPWY] [-c vcpus] [-g ] [-l ]\n" - " %*s [-m mem] [-p vcpu:hostcpu] [-s ] [-U uuid] \n" + "Usage: %s [-abehuwxACHPWY] [-c vcpus] [-f ] [-g ]\n" + " %*s [-l ] [-m mem] [-p vcpu:hostcpu] [-s ] [-U uuid] \n" " -a: local apic is in xAPIC mode (deprecated)\n" " -A: create ACPI tables\n" " -c: # cpus (default 1)\n" " -C: include guest memory in core file\n" " -e: exit on unhandled I/O access\n" + " -f: UCL config file to read\n" " -g: gdb port\n" " -h: help\n" " -H: vmexit from the guest on hlt\n" @@ -697,6 +702,9 @@ struct vmctx *ctx; uint64_t rip; size_t memsize; + struct config_entry *cb; + char *temp; + int slot, pci_err; bvmcons = 0; dump_guest_memory = 0; @@ -706,8 +714,9 @@ memsize = 256 * MB; mptgen = 1; rtc_localtime = 1; + temp = NULL; - while ((c = getopt(argc, argv, "abehuwxACHIPWYp:g:c:s:m:l:U:")) != -1) { + while ((c = getopt(argc, argv, "abehuwxACHIPWYp:f:g:c:s:m:l:U:")) != -1) { switch (c) { case 'a': x2apic_mode = 0; @@ -718,13 +727,74 @@ case 'b': bvmcons = 1; break; + case 'f': + cb = parse_conf(optarg); + + vmname = cb->vmname; + guest_ncpus = cb->vcpus; + + asprintf(&temp, "%zu", cb->memsize); + vm_parse_memsize(temp, &memsize); + free(temp); + + asprintf(&temp, "com1,%s", cb->console); + lpc_device_parse(temp); + free(temp); + + if (cb->pincpu && pincpu_parse(cb->pincpu) != 0) { + errx(EX_USAGE, "invalid vcpu pinning " + "configuration '%s'", cb->pincpu); + } + + guest_uuid_str = cb->uuid; + gdb_port = cb->gdb_port; + acpi = cb->acpi; + dump_guest_memory = cb->dump_memory; + guest_vmexit_on_hlt = cb->vmexit_on_hlt; + guest_vmexit_on_pause = cb->vmexit_on_pause; + strictio = cb->strictio; + rtc_localtime = cb->rtc_localtime; + strictmsr = cb->strictmsr; + virtio_msix = cb->virtio_msix; + x2apic_mode = cb->x2apic_mode; + mptgen = cb->mptgen; + + for (slot = 0; slot < cb->slot_count; slot++) { + if (strcasecmp(cb->slots[slot]->emul, "virtio-blk") == 0 || + strcasecmp(cb->slots[slot]->emul, "ahci-hd") == 0) { + asprintf(&cb->slots[slot]->config, "%s%s%s%s", + cb->slots[slot]->disk.path, + (cb->slots[slot]->disk.nocache ? ",nocache" : ""), + (cb->slots[slot]->disk.sync ? ",sync" : ""), + (cb->slots[slot]->disk.ro ? ",ro" : "")); + } else if (strcasecmp(cb->slots[slot]->emul, "ahci-cd") == 0) { + asprintf(&cb->slots[slot]->config, "%s%s%s%s", + cb->slots[slot]->disk.path, + (cb->slots[slot]->disk.nocache ? ",nocache" : ""), + (cb->slots[slot]->disk.sync ? ",sync" : ""), + ",ro"); + } else if (strcasecmp(cb->slots[slot]->emul, "virtio-net") == 0) { + asprintf(&cb->slots[slot]->config, "%s%s%s", + cb->slots[slot]->iface.name, + (cb->slots[slot]->iface.mac != NULL ? "," : ""), + (cb->slots[slot]->iface.mac != NULL ? cb->slots[slot]->iface.mac : "")); + } + pci_err = pci_create_slot(cb->slots[slot]->bnum, + cb->slots[slot]->snum, + cb->slots[slot]->fnum, + cb->slots[slot]->emul, + cb->slots[slot]->config); + if (pci_err != 0) + errx(1, "Failed to parse PCI configuration"); + } + break; case 'p': - if (pincpu_parse(optarg) != 0) { - errx(EX_USAGE, "invalid vcpu pinning " - "configuration '%s'", optarg); - } + if (pincpu_parse(optarg) != 0) { + errx(EX_USAGE, "invalid vcpu pinning " + "configuration '%s'", optarg); + } break; - case 'c': + case 'c': guest_ncpus = atoi(optarg); break; case 'C': @@ -786,7 +856,7 @@ mptgen = 0; break; case 'h': - usage(0); + usage(0); default: usage(1); } @@ -794,11 +864,12 @@ argc -= optind; argv += optind; - if (argc != 1) + if (!vmname && argc > 0) + vmname = argv[0]; + + if (!vmname) usage(1); - vmname = argv[0]; - ctx = vm_open(vmname); if (ctx == NULL) { perror("vm_open"); @@ -821,6 +892,7 @@ if (dump_guest_memory) vm_set_memflags(ctx, VM_MEM_F_INCORE); + err = vm_setup_memory(ctx, memsize, VM_MMAP_ALL); if (err) { fprintf(stderr, "Unable to setup memory (%d)\n", err); @@ -876,8 +948,8 @@ /* * Change the proc title to include the VM name. */ - setproctitle("%s", vmname); - + setproctitle("%s", vmname); + /* * Add CPU 0 */ Index: usr.sbin/bhyve/config.h =================================================================== --- /dev/null +++ usr.sbin/bhyve/config.h @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 2015 Marcelo Araujo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include + +#include + +#define STR_SIZE 254 +#define NDISKS 32 + +struct config_iface_entry { + char *name; + char *mac; +}; + +struct config_disk_entry { + char *path; + bool nocache; + bool sync; + bool ro; +}; + +struct config_slot_entry { + int bnum; + int snum; + int fnum; + char *emul; + char *config; + union { + struct config_iface_entry iface; + struct config_disk_entry disk; + }; +}; + +struct config_entry { + char *vmname; + int vcpus; + size_t memsize; + char *console; + char *bootdisk; /* do not need? */ + char *pincpu; + char *lpc_device; + char *uuid; + struct config_slot_entry *slots[255]; + int slot_count; + int gdb_port; + bool acpi; + bool dump_memory; + bool vmexit_on_hlt; + bool vmexit_on_pause; + bool strictio; + bool rtc_localtime; + bool strictmsr; + bool virtio_msix; + bool x2apic_mode; + bool mptgen; +}; + +struct config_entry* parse_conf(char *config_file); +int load_config(const char *fname); +int check_config(struct config_entry *config); +char* parse_string_safe(const ucl_object_t *obj, char *path); +bool parse_pci_slot(const char *str, int *bnum, int *snum, int *fnum); + +void ucl_obj_dump(const ucl_object_t *obj, unsigned int shift); Index: usr.sbin/bhyve/config.c =================================================================== --- /dev/null +++ usr.sbin/bhyve/config.c @@ -0,0 +1,429 @@ +/*- + * Copyright (c) 2015 Marcelo Araujo + * Copyright (c) 2015 Allan Jude + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +struct config_slot_entry sinit = { + 0, + 0, + 0, + NULL, + NULL +}; + +struct config_entry cinit = { + NULL, + 0, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + {NULL}, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; + +/* + * Check if the config file exist. + */ +int +load_config(const char *fname) +{ + FILE *file; + if ((file = fopen(fname, "r"))) { + fclose(file); + return 0; + } + + errx(EX_USAGE, "Config file does not exist: %s", fname); + return 1; +} + +/* + * Check if the basic variables are setting in + * the config file. + */ +int +check_config(struct config_entry *config) +{ + if (!config->vmname) + errx(EX_USAGE, "name not defined"); + else if (!config->memsize) + errx(EX_USAGE, "memory not defined"); + else if (!config->vcpus) + errx(EX_USAGE, "vcpus not defined"); + /* + else if (!config->bootdisk) + errx(EX_USAGE, "no boot disk found"); + */ + else if (!config->console) + errx(EX_USAGE, "console not defined"); + + return (0); +} + +/* + * Parse the config file. + */ +struct config_entry * +parse_conf(char *config_file) +{ + struct config_entry *c = NULL; + const char *config = config_file; + struct ucl_parser *p = NULL; + ucl_object_t *obj = NULL; + const ucl_object_t *cur, *acur; + ucl_object_iter_t it = NULL, ait = NULL; + const char *key; + char *tmp; + + if (load_config(config) == 1) + errx(EXIT_FAILURE, "No config file found: %s", config); + + p = ucl_parser_new(UCL_PARSER_KEY_LOWERCASE | + UCL_PARSER_NO_IMPLICIT_ARRAYS); + if (p == NULL) + errx(1, "Could not allocate ucl parser"); + + if (!ucl_parser_add_file(p, config)) { + if (errno != ENOENT) + errx(EXIT_FAILURE, "Parse error in file %s: %s", + config, ucl_parser_get_error(p)); + ucl_parser_free(p); + } + + obj = ucl_parser_get_object(p); + if (obj->type != UCL_OBJECT) + warnx("Invalid configuration format.\n"); + + c = malloc(sizeof(struct config_entry)); + memcpy(c, &cinit, sizeof(struct config_entry)); + + it = ucl_object_iterate_new(obj); + while ((cur = ucl_object_iterate_safe(it, true)) != NULL) { + key = ucl_object_key(cur); + if (strcasecmp(key, "name") == 0) + c->vmname = parse_string_safe(cur, NULL); + else if (strcasecmp(key, "cpus") == 0) { + if (!ucl_object_toint_safe(cur, (long *)&c->vcpus)) + errx(EXIT_FAILURE, "Invalid number of CPUs specified"); + } else if (strcasecmp(key, "memory") == 0) { + if (!ucl_object_toint_safe(cur, (size_t *)&c->memsize)) + errx(EXIT_FAILURE, "Invalid amount of memory specified"); + } else if (strcasecmp(key, "console") == 0) + c->console = parse_string_safe(cur, NULL); + else if (strcasecmp(key, "bootdisk") == 0) + c->bootdisk = parse_string_safe(cur, NULL); + else if (strcasecmp(key, "pincpu") == 0) + c->pincpu = parse_string_safe(cur, NULL); + else if (strcasecmp(key, "lpc") == 0) + c->lpc_device = parse_string_safe(cur, NULL); + else if (strcasecmp(key, "uuid") == 0) + c->uuid = parse_string_safe(cur, NULL); + else if (strcasecmp(key, "device") == 0) { + switch (ucl_object_type(cur)) { + case UCL_OBJECT: + c->slots[c->slot_count] = malloc(sizeof(struct config_slot_entry)); + memcpy(c->slots[c->slot_count], &sinit, sizeof(struct config_slot_entry)); + tmp = parse_string_safe(cur, ".slot"); + parse_pci_slot(tmp, + &c->slots[c->slot_count]->bnum, + &c->slots[c->slot_count]->snum, + &c->slots[c->slot_count]->fnum); + c->slots[c->slot_count]->emul = parse_string_safe(cur, ".type"); + c->slots[c->slot_count]->config = parse_string_safe(cur, ".config"); + free(tmp); + c->slot_count++; + break; + case UCL_ARRAY: + ait = ucl_object_iterate_new(cur); + while ((acur = ucl_object_iterate_safe(ait, true)) != NULL) { + c->slots[c->slot_count] = malloc(sizeof(struct config_slot_entry)); + memcpy(c->slots[c->slot_count], &sinit, sizeof(struct config_slot_entry)); + memcpy(c->slots[c->slot_count], &sinit, sizeof(struct config_slot_entry)); + tmp = parse_string_safe(acur, ".slot"); + parse_pci_slot(tmp, + &c->slots[c->slot_count]->bnum, + &c->slots[c->slot_count]->snum, + &c->slots[c->slot_count]->fnum); + c->slots[c->slot_count]->emul = parse_string_safe(acur, ".type"); + c->slots[c->slot_count]->config = parse_string_safe(acur, ".config"); + free(tmp); + c->slot_count++; + } + ucl_object_iterate_free(ait); + break; + case UCL_STRING: + c->slots[c->slot_count] = malloc(sizeof(struct config_slot_entry)); + memcpy(c->slots[c->slot_count], &sinit, sizeof(struct config_slot_entry)); + c->slots[c->slot_count]->emul = parse_string_safe(cur, NULL); + c->slot_count++; + break; + default: + warnx("Error parsing disk contain"); + } + } else if (strcasecmp(key, "network") == 0) { + switch (ucl_object_type(cur)) { + case UCL_OBJECT: + c->slots[c->slot_count] = malloc(sizeof(struct config_slot_entry)); + memcpy(c->slots[c->slot_count], &sinit, sizeof(struct config_slot_entry)); + c->slots[c->slot_count]->emul = parse_string_safe(cur, ".type"); + c->slots[c->slot_count]->iface.name = parse_string_safe(cur, ".name"); + c->slots[c->slot_count]->iface.mac = parse_string_safe(cur, ".mac"); + c->slots[c->slot_count]->bnum = c->slot_count; + c->slot_count++; + break; + case UCL_ARRAY: + ait = ucl_object_iterate_new(cur); + while ((acur = ucl_object_iterate_safe(ait, true)) != NULL) { + c->slots[c->slot_count] = malloc(sizeof(struct config_slot_entry)); + memcpy(c->slots[c->slot_count], &sinit, sizeof(struct config_slot_entry)); + c->slots[c->slot_count]->emul = parse_string_safe(acur, ".type"); + c->slots[c->slot_count]->iface.name = parse_string_safe(acur, ".name"); + c->slots[c->slot_count]->iface.mac = parse_string_safe(acur, ".mac"); + c->slots[c->slot_count]->bnum = c->slot_count; + c->slot_count++; + } + ucl_object_iterate_free(ait); + break; + case UCL_STRING: + c->slots[c->slot_count] = malloc(sizeof(struct config_slot_entry)); + memcpy(c->slots[c->slot_count], &sinit, sizeof(struct config_slot_entry)); + c->slots[c->slot_count]->iface.name = parse_string_safe(cur, NULL); + c->slots[c->slot_count]->bnum = c->slot_count; + c->slot_count++; + break; + default: + warnx("Error parsing disk contain"); + } + } else if (strcasecmp(key, "disk") == 0) { + switch (ucl_object_type(cur)) { + case UCL_OBJECT: + c->slots[c->slot_count] = malloc(sizeof(struct config_slot_entry)); + memcpy(c->slots[c->slot_count], &sinit, sizeof(struct config_slot_entry)); + c->slots[c->slot_count]->emul = parse_string_safe(cur, ".type"); + c->slots[c->slot_count]->disk.path = parse_string_safe(cur, ".path"); + ucl_object_toboolean_safe(ucl_lookup_path(cur, ".nocache"), &c->slots[c->slot_count]->disk.nocache); + ucl_object_toboolean_safe(ucl_lookup_path(cur, ".sync"), &c->slots[c->slot_count]->disk.sync); + ucl_object_toboolean_safe(ucl_lookup_path(cur, ".readonly"), &c->slots[c->slot_count]->disk.ro); + c->slots[c->slot_count]->bnum = c->slot_count; + c->slot_count++; + break; + case UCL_ARRAY: + ait = ucl_object_iterate_new(cur); + while ((acur = ucl_object_iterate_safe(ait, true)) != NULL) { + c->slots[c->slot_count] = malloc(sizeof(struct config_slot_entry)); + memcpy(c->slots[c->slot_count], &sinit, sizeof(struct config_slot_entry)); + c->slots[c->slot_count]->emul = parse_string_safe(acur, ".type"); + c->slots[c->slot_count]->disk.path = parse_string_safe(acur, ".path"); + ucl_object_toboolean_safe(ucl_lookup_path(acur, ".nocache"), &c->slots[c->slot_count]->disk.nocache); + ucl_object_toboolean_safe(ucl_lookup_path(acur, ".sync"), &c->slots[c->slot_count]->disk.sync); + ucl_object_toboolean_safe(ucl_lookup_path(acur, ".readonly"), &c->slots[c->slot_count]->disk.ro); + c->slots[c->slot_count]->bnum = c->slot_count; + c->slot_count++; + } + ucl_object_iterate_free(ait); + break; + case UCL_STRING: + c->slots[c->slot_count] = malloc(sizeof(struct config_slot_entry)); + memcpy(c->slots[c->slot_count], &sinit, sizeof(struct config_slot_entry)); + c->slots[c->slot_count]->disk.path = parse_string_safe(cur, NULL); + c->slots[c->slot_count]->bnum = c->slot_count; + c->slot_count++; + break; + default: + warnx("Error parsing disk contain"); + } + } else if (strcasecmp(key, "gdbport") == 0) { + if (!ucl_object_toint_safe(cur, (long *)&c->gdb_port)) + warnx("Invalid GDB port specified"); + } else if (strcasecmp(key, "acpi") == 0) + ucl_object_toboolean_safe(cur, &c->acpi); + else if (strcasecmp(key, "dump_memory") == 0) + ucl_object_toboolean_safe(cur, &c->dump_memory); + else if (strcasecmp(key, "vmexit_on_hlt") == 0) + ucl_object_toboolean_safe(cur, &c->vmexit_on_hlt); + else if (strcasecmp(key, "vmexit_on_pause") == 0) + ucl_object_toboolean_safe(cur, &c->vmexit_on_pause); + else if (strcasecmp(key, "strictio") == 0) + ucl_object_toboolean_safe(cur, &c->strictio); + else if (strcasecmp(key, "rtc_localtime") == 0) + ucl_object_toboolean_safe(cur, &c->rtc_localtime); + else if (strcasecmp(key, "strictmsr") == 0) + ucl_object_toboolean_safe(cur, &c->strictmsr); + else if (strcasecmp(key, "virtio_msix") == 0) + ucl_object_toboolean_safe(cur, &c->virtio_msix); + else if (strcasecmp(key, "x2apic_mode") == 0) + ucl_object_toboolean_safe(cur, &c->x2apic_mode); + else if (strcasecmp(key, "mptgen") == 0) + ucl_object_toboolean_safe(cur, &c->mptgen); + } + ucl_object_iterate_free(it); + + check_config(c); + + if (p != NULL) + ucl_parser_free(p); + if (obj != NULL) + ucl_object_unref(obj); + + return c; +} + +char * +parse_string_safe(const ucl_object_t *obj, char *path) +{ + const ucl_object_t *target = NULL; + char *str = NULL; + + if (path != NULL) + target = ucl_lookup_path(obj, path); + else + target = obj; + + if (target == NULL) + return NULL; + + str = (char *)ucl_object_tostring(target); + if (str != NULL) + return strdup(str); + + return (str); +} + +bool +parse_pci_slot(const char *str, int *bnum, int *snum, int *fnum) +{ + if (str == NULL) + return (false); + /* :: */ + if (sscanf(str, "%d:%d:%d", bnum, snum, fnum) != 3) { + *bnum = 0; + /* : */ + if (sscanf(str, "%d:%d", snum, fnum) != 2) { + *fnum = 0; + /* */ + if (sscanf(str, "%d", snum) != 1) { + *snum = -1; + return (false); + } + } + } + return (true); +} + +void +ucl_obj_dump(const ucl_object_t *obj, unsigned int shift) +{ + int num = shift * 2 + 5; + char *pre = (char *) malloc (num * sizeof(char)); + const ucl_object_t *cur, *tmp; + ucl_object_iter_t it = NULL, it_obj = NULL; + + pre[--num] = 0x00; + while (num--) + pre[num] = 0x20; + + tmp = obj; + + while ((obj = ucl_iterate_object (tmp, &it, false))) { + printf ("%sucl object address: %p\n", pre + 4, obj); + if (obj->key != NULL) { + printf ("%skey: \"%s\"\n", pre, ucl_object_key (obj)); + } + printf ("%sref: %u\n", pre, obj->ref); + printf ("%slen: %u\n", pre, obj->len); + printf ("%sprev: %p\n", pre, obj->prev); + printf ("%snext: %p\n", pre, obj->next); + if (ucl_object_type(obj) == UCL_OBJECT) { + printf ("%stype: UCL_OBJECT\n", pre); + printf ("%svalue: %p\n", pre, obj->value.ov); + it_obj = NULL; + while ((cur = ucl_iterate_object (obj, &it_obj, true))) { + ucl_obj_dump (cur, shift + 2); + } + } + else if (ucl_object_type(obj) == UCL_ARRAY) { + printf ("%stype: UCL_ARRAY\n", pre); + printf ("%svalue: %p\n", pre, obj->value.av); + it_obj = NULL; + while ((cur = ucl_iterate_object (obj, &it_obj, true))) { + ucl_obj_dump (cur, shift + 2); + } + } + else if (ucl_object_type(obj) == UCL_INT) { + printf ("%stype: UCL_INT\n", pre); + printf ("%svalue: %jd\n", pre, (intmax_t)ucl_object_toint (obj)); + } + else if (ucl_object_type(obj) == UCL_FLOAT) { + printf ("%stype: UCL_FLOAT\n", pre); + printf ("%svalue: %f\n", pre, ucl_object_todouble (obj)); + } + else if (ucl_object_type(obj) == UCL_STRING) { + printf ("%stype: UCL_STRING\n", pre); + printf ("%svalue: \"%s\"\n", pre, ucl_object_tostring (obj)); + } + else if (ucl_object_type(obj) == UCL_BOOLEAN) { + printf ("%stype: UCL_BOOLEAN\n", pre); + printf ("%svalue: %s\n", pre, ucl_object_tostring_forced (obj)); + } + else if (ucl_object_type(obj) == UCL_TIME) { + printf ("%stype: UCL_TIME\n", pre); + printf ("%svalue: %f\n", pre, ucl_object_todouble (obj)); + } + else if (ucl_object_type(obj) == UCL_USERDATA) { + printf ("%stype: UCL_USERDATA\n", pre); + printf ("%svalue: %p\n", pre, obj->value.ud); + } + } + + free (pre); +} \ No newline at end of file Index: usr.sbin/bhyve/pci_emul.h =================================================================== --- usr.sbin/bhyve/pci_emul.h +++ usr.sbin/bhyve/pci_emul.h @@ -227,6 +227,7 @@ int pci_msix_pba_bar(struct pci_devinst *pi); int pci_msi_msgnum(struct pci_devinst *pi); int pci_parse_slot(char *opt); +int pci_create_slot(int bnum, int snum, int fnum, char *emul, char *config); void pci_populate_msicap(struct msicap *cap, int msgs, int nextptr); int pci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum); int pci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size, Index: usr.sbin/bhyve/pci_emul.c =================================================================== --- usr.sbin/bhyve/pci_emul.c +++ usr.sbin/bhyve/pci_emul.c @@ -166,8 +166,6 @@ int pci_parse_slot(char *opt) { - struct businfo *bi; - struct slotinfo *si; char *emul, *config, *str, *cp; int error, bnum, snum, fnum; @@ -200,6 +198,26 @@ } } + error = pci_create_slot(bnum, snum, fnum, emul, config); + +done: + if (error) + free(str); + + return (error); +} + +int +pci_create_slot(int bnum, int snum, int fnum, char *emul, char *config) +{ + struct businfo *bi; + struct slotinfo *si; + char *opt; + int error; + + error = -1; + asprintf(&opt, "%d:%d:%d,%s,%s", bnum, snum, fnum, emul, config); + if (bnum < 0 || bnum >= MAXBUSES || snum < 0 || snum >= MAXSLOTS || fnum < 0 || fnum >= MAXFUNCS) { pci_parse_slot_usage(opt); @@ -229,8 +247,7 @@ si->si_funcs[fnum].fi_param = config; done: - if (error) - free(str); + free(opt); return (error); } Index: usr.sbin/bhyveload/Makefile =================================================================== --- usr.sbin/bhyveload/Makefile +++ usr.sbin/bhyveload/Makefile @@ -1,13 +1,17 @@ # $FreeBSD$ PROG= bhyveload -SRCS= bhyveload.c MAN= bhyveload.8 -LIBADD= vmmapi +SRCS= bhyveload.c \ + ${.CURDIR}/../bhyve/config.c + +LIBADD= vmmapi ucl WARNS?= 3 CFLAGS+=-I${.CURDIR}/../../sys/boot/userboot +CFLAGS+=-I${.CURDIR}/../bhyve +CFLAGS+=-I${.CURDIR}/../../contrib/libucl/include .include Index: usr.sbin/bhyveload/bhyveload.8 =================================================================== --- usr.sbin/bhyveload/bhyveload.8 +++ usr.sbin/bhyveload/bhyveload.8 @@ -41,6 +41,8 @@ .Op Fl h Ar host-path .Op Fl m Ar mem-size .Ar vmname +.Nm +.Op Fl f Ar guest.conf .Sh DESCRIPTION .Nm is used to load a @@ -111,6 +113,11 @@ The default value of .Ar mem-size is 256M. +.It Fl f Ar guest.conf +The +.Ar guest.conf +is the configuration file that contain all parameters +necessary to load the guest. .El .Sh EXAMPLES To create a virtual machine named @@ -132,6 +139,10 @@ .Pa /dev/nmdm1B .Pp .Dl "bhyveload -m 256MB -h /usr/images/test -c /dev/nmdm1B test-vm" +.Pp +To create a virtual machine using a configuration file: +.Pp +.Dl "bhyveload -f guest.conf" .Sh SEE ALSO .Xr bhyve 4 , .Xr nmdm 4 , Index: usr.sbin/bhyveload/bhyveload.c =================================================================== --- usr.sbin/bhyveload/bhyveload.c +++ usr.sbin/bhyveload/bhyveload.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2011 NetApp, Inc. + * Copyright (c) 2015 Allan Jude * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -82,6 +83,8 @@ #include +#include + #include "userboot.h" #define MB (1024 * 1024UL) @@ -630,9 +633,10 @@ fprintf(stderr, "usage: %s [-c ] [-d ] [-e ]\n" - " %*s [-h ] [-m mem-size] \n", + " %*s [-f [-h ] [-m mem-size]\n" + " %*s \n", progname, - (int)strlen(progname), ""); + (int)strlen(progname), (int)strlen(progname), ""); exit(1); } @@ -643,7 +647,7 @@ void (*func)(struct loader_callbacks *, void *, int, int); uint64_t mem_size; int opt, error, need_reinit; - + struct config_entry config; progname = basename(argv[0]); mem_size = 256 * MB; @@ -651,28 +655,45 @@ consin_fd = STDIN_FILENO; consout_fd = STDOUT_FILENO; - while ((opt = getopt(argc, argv, "c:d:e:h:m:")) != -1) { + while ((opt = getopt(argc, argv, "c:d:e:f:h:m:")) != -1) { switch (opt) { case 'c': error = altcons_open(optarg); if (error != 0) errx(EX_USAGE, "Could not open '%s'", optarg); break; - case 'd': error = disk_open(optarg); if (error != 0) errx(EX_USAGE, "Could not open '%s'", optarg); break; - case 'e': addenv(optarg); break; - + case 'f': + if (load_config(optarg) == 0) { + config = parse_conf(optarg); + error = altcons_open(config.console); + if (error != 0) + errx(EX_USAGE, "Could not open console"); + if (config.isofile) + error = disk_open(config.isofile); + else + error = disk_open(config.bootdisk); + if (error != 0 ) + errx(EX_USAGE, "Could not open disk"); + error = vm_parse_memsize(config.memsize, &mem_size); + if (error != 0) + errx(EX_USAGE, "Invalid memsize"); + vmname = config.vmname; + } else { + errx(EX_USAGE, "Error to parse config file: %s", optarg); + exit (1); + } + break; case 'h': host_base = optarg; break; - case 'm': error = vm_parse_memsize(optarg, &mem_size); if (error != 0) @@ -686,10 +707,11 @@ argc -= optind; argv += optind; - if (argc != 1) - usage(); + if (!vmname && argc > 0) + vmname = argv[0]; - vmname = argv[0]; + if (!vmname) + usage(); need_reinit = 0; error = vm_create(vmname);