Index: etc/defaults/bhyve.conf =================================================================== --- etc/defaults/bhyve.conf (revision 0) +++ etc/defaults/bhyve.conf (working copy) @@ -0,0 +1,16 @@ +.include(priority=3, glob=true, try=true, prefix=true) "/etc/bhyve.conf.d/*.conf" +.include(priority=5, try=true) "/etc/bhyve.conf" +cpus = 1; +memory = 256MB; +console = "stdio"; +acpi = true; +vmexit_on_hlt = true; +vmexit_on_pause = true; +device { + slot = "0:0"; + type = "hostbridge"; +} +device { + slot = "1:0"; + type = "lpc"; +} Property changes on: etc/defaults/bhyve.conf ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: etc/mtree/BSD.root.dist =================================================================== --- etc/mtree/BSD.root.dist (revision 283903) +++ etc/mtree/BSD.root.dist (working copy) @@ -28,6 +28,8 @@ .. autofs .. + bhyve.conf.d + .. bluetooth .. casper Index: usr.sbin/bhyve/Makefile =================================================================== --- usr.sbin/bhyve/Makefile (revision 283903) +++ usr.sbin/bhyve/Makefile (working copy) @@ -14,6 +14,7 @@ bhyverun.c \ block_if.c \ consport.c \ + config.c \ dbgport.c \ inout.c \ ioapic.c \ @@ -43,6 +44,10 @@ .PATH: ${.CURDIR}/../../sys/amd64/vmm SRCS+= vmm_instruction_emul.c +#CFLAGS+=-I${.CURDIR}/../../contrib/libucl/include +CFLAGS+=-I/usr/local/include -L/usr/local/lib -lucl + +#LIBADD= vmmapi md pthread ucl LIBADD= vmmapi md pthread WARNS?= 2 Index: usr.sbin/bhyve/bhyve.8 =================================================================== --- usr.sbin/bhyve/bhyve.8 (revision 283903) +++ usr.sbin/bhyve/bhyve.8 (working copy) @@ -34,6 +34,7 @@ .Nm .Op Fl abehuwxACHPWY .Op Fl c Ar numcpus +.Op Fl f Ar bhyve.conf .Op Fl g Ar gdbport .Op Fl l Ar lpcdev Ns Op , Ns Ar conf .Op Fl m Ar size Ns Op Ar K|k|M|m|G|g|T|t @@ -41,6 +42,8 @@ .Op Fl s Ar slot,emulation Ns Op , Ns Ar conf .Op Fl U Ar uuid .Ar vmname +.Nm +.Fl f Ar bhyve.conf .Sh DESCRIPTION .Nm is a hypervisor that runs guest operating systems inside a @@ -84,6 +87,9 @@ .Nm to exit when a guest issues an access to an I/O port that is not emulated. This is intended for debug purposes. +.It Fl f Ar bhyve.conf +Read configuration from specified file. +Supersedes any flags specified before this flag. .It Fl g Ar gdbport For .Fx @@ -309,6 +315,11 @@ -l com1,/dev/nmdm0A \\ -A -H -P -m 8G .Ed +.Pp +Run a virtual machine described in 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 (revision 283903) +++ usr.sbin/bhyve/bhyverun.c (working copy) @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -51,6 +52,7 @@ #include #include +#include "config.h" #include "bhyverun.h" #include "acpi.h" #include "inout.h" @@ -67,28 +69,49 @@ #include "spinup_ap.h" #include "rtc.h" +#include + #define GUEST_NIO_PORT 0x488 /* guest upcalls via i/o port */ -#define MB (1024UL * 1024) -#define GB (1024UL * MB) - typedef int (*vmexit_handler_t)(struct vmctx *, struct vm_exit *, int *vcpu); extern int vmexit_task_switch(struct vmctx *, struct vm_exit *, int *vcpu); -char *vmname; +struct config_slot_entry sinit = { + .bnum = 0, + .snum = 0, + .fnum = 0, + .emul = NULL, + .config = NULL +}; -int guest_ncpus; -char *guest_uuid_str; +struct config_entry cinit = { + .vmname = NULL, + .vcpus = 1, + .memsize = 256 * MB, + .console = NULL, + .host_base = NULL, + .pincpu = NULL, + .lpc_device = NULL, + .uuid = NULL, + .env = NULL, + .env_count = 0, + .slots = {NULL}, + .slot_count = 0, + .gdb_port = 0, + .acpi = 0, + .dump_memory = 0, + .vmexit_on_hlt = 0, + .vmexit_on_pause = 0, + .strictio = 0, + .rtc_localtime = 1, + .strictmsr = 1, + .virtio_msix = 1, + .x2apic_mode = 0, + .mptgen = 1 +}; -static int guest_vmexit_on_hlt, guest_vmexit_on_pause; -static int virtio_msix = 1; -static int x2apic_mode = 0; /* default is xAPIC */ +struct config_entry vmconf; -static int strictio; -static int strictmsr = 1; - -static int acpi; - static char *progname; static const int BSP = 0; @@ -122,13 +145,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" @@ -207,7 +231,7 @@ fbsdrun_vmexit_on_pause(void) { - return (guest_vmexit_on_pause); + return (vmconf.vmexit_on_pause); } int @@ -214,7 +238,7 @@ fbsdrun_vmexit_on_hlt(void) { - return (guest_vmexit_on_hlt); + return (vmconf.vmexit_on_hlt); } int @@ -221,7 +245,7 @@ fbsdrun_virtio_msix(void) { - return (virtio_msix); + return (vmconf.virtio_msix); } static void * @@ -323,7 +347,7 @@ return (error); } - error = emulate_inout(ctx, vcpu, vme, strictio); + error = emulate_inout(ctx, vcpu, vme, vmconf.strictio); if (error) { fprintf(stderr, "Unhandled %s%c 0x%04x at 0x%lx\n", in ? "in" : "out", @@ -347,7 +371,7 @@ if (error != 0) { fprintf(stderr, "rdmsr to register %#x on vcpu %d\n", vme->u.msr.code, *pvcpu); - if (strictmsr) { + if (vmconf.strictmsr) { vm_inject_gp(ctx, *pvcpu); return (VMEXIT_CONTINUE); } @@ -373,7 +397,7 @@ if (error != 0) { fprintf(stderr, "wrmsr to register %#x(%#lx) on vcpu %d\n", vme->u.msr.code, vme->u.msr.wval, *pvcpu); - if (strictmsr) { + if (vmconf.strictmsr) { vm_inject_gp(ctx, *pvcpu); return (VMEXIT_CONTINUE); } @@ -687,7 +711,7 @@ handler[VM_EXITCODE_PAUSE] = vmexit_pause; } - if (x2apic_mode) + if (vmconf.x2apic_mode) err = vm_set_x2apic_state(ctx, cpu, X2APIC_ENABLED); else err = vm_set_x2apic_state(ctx, cpu, X2APIC_DISABLED); @@ -703,47 +727,54 @@ int main(int argc, char *argv[]) { - int c, error, gdb_port, err, bvmcons; - int dump_guest_memory, max_vcpus, mptgen; - int rtc_localtime; + int c, error, err, max_vcpus, bvmcons; struct vmctx *ctx; uint64_t rip; - size_t memsize; + char *temp; + int pci_err; + size_t slot; + bool new_config; + memcpy(&vmconf, &cinit, sizeof(struct config_entry)); bvmcons = 0; - dump_guest_memory = 0; progname = basename(argv[0]); - gdb_port = 0; - guest_ncpus = 1; - memsize = 256 * MB; - mptgen = 1; - rtc_localtime = 1; + temp = NULL; + new_config = false; - while ((c = getopt(argc, argv, "abehuwxACHIPWYp:g:c:s:m:l:U:")) != -1) { + if (argc == 2 && *argv[1] != '-') { + new_config = true; + load_config(NULL, argv[1], &vmconf); + } + + while ((c = getopt(argc, argv, "abehuwxACHIPWYp:f:g:c:s:m:l:U:")) != -1) { switch (c) { case 'a': - x2apic_mode = 0; + vmconf.x2apic_mode = 0; break; case 'A': - acpi = 1; + vmconf.acpi = 1; break; case 'b': bvmcons = 1; break; + case 'f': + new_config = true; + load_config(optarg, NULL, &vmconf); + 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': - guest_ncpus = atoi(optarg); + case 'c': + vmconf.vcpus = atoi(optarg); break; case 'C': - dump_guest_memory = 1; + vmconf.dump_memory = 1; break; case 'g': - gdb_port = atoi(optarg); + vmconf.gdb_port = atoi(optarg); break; case 'l': if (lpc_device_parse(optarg) != 0) { @@ -757,12 +788,12 @@ else break; case 'm': - error = vm_parse_memsize(optarg, &memsize); + error = vm_parse_memsize(optarg, &vmconf.memsize); if (error) errx(EX_USAGE, "invalid memsize '%s'", optarg); break; case 'H': - guest_vmexit_on_hlt = 1; + vmconf.vmexit_on_hlt = 1; break; case 'I': /* @@ -774,31 +805,31 @@ */ break; case 'P': - guest_vmexit_on_pause = 1; + vmconf.vmexit_on_pause = 1; break; case 'e': - strictio = 1; + vmconf.strictio = 1; break; case 'u': - rtc_localtime = 0; + vmconf.rtc_localtime = 0; break; case 'U': - guest_uuid_str = optarg; + vmconf.uuid = optarg; break; case 'w': - strictmsr = 0; + vmconf.strictmsr = 0; break; case 'W': - virtio_msix = 0; + vmconf.virtio_msix = 0; break; case 'x': - x2apic_mode = 1; + vmconf.x2apic_mode = 1; break; case 'Y': - mptgen = 0; + vmconf.mptgen = 0; break; case 'h': - usage(0); + usage(0); default: usage(1); } @@ -806,34 +837,85 @@ argc -= optind; argv += optind; - if (argc != 1) + if (new_config) { + asprintf(&temp, "%zu", vmconf.memsize); + error = vm_parse_memsize(temp, &vmconf.memsize); + if (error) + errx(EX_USAGE, "invalid memsize '%s'", optarg); + free(temp); + + asprintf(&temp, "com1,%s", vmconf.console); + if (lpc_device_parse(temp) != 0) { + errx(EX_USAGE, "invalid lpc device " + "configuration '%s'", temp); + } + free(temp); + + if (vmconf.pincpu && pincpu_parse(vmconf.pincpu) != 0) { + errx(EX_USAGE, "invalid vcpu pinning " + "configuration '%s'", vmconf.pincpu); + } + + for (slot = 0; slot < vmconf.slot_count; slot++) { + if (strcasecmp(vmconf.slots[slot]->emul, "virtio-blk") == 0 || + strcasecmp(vmconf.slots[slot]->emul, "ahci-hd") == 0) { + asprintf(&vmconf.slots[slot]->config, "%s%s%s%s", + vmconf.slots[slot]->disk.path, + (vmconf.slots[slot]->disk.nocache ? ",nocache" : ""), + (vmconf.slots[slot]->disk.sync ? ",sync" : ""), + (vmconf.slots[slot]->disk.ro ? ",ro" : "")); + } else if (strcasecmp(vmconf.slots[slot]->emul, "ahci-cd") == 0) { + asprintf(&vmconf.slots[slot]->config, "%s%s%s%s", + vmconf.slots[slot]->disk.path, + (vmconf.slots[slot]->disk.nocache ? ",nocache" : ""), + (vmconf.slots[slot]->disk.sync ? ",sync" : ""), + ",ro"); + } else if (strcasecmp(vmconf.slots[slot]->emul, "virtio-net") == 0) { + asprintf(&vmconf.slots[slot]->config, "%s%s%s", + vmconf.slots[slot]->iface.name, + (vmconf.slots[slot]->iface.mac != NULL ? ",mac=" : ""), + (vmconf.slots[slot]->iface.mac != NULL ? vmconf.slots[slot]->iface.mac : "")); + } + pci_err = pci_create_slot(vmconf.slots[slot]->bnum, + vmconf.slots[slot]->snum, + vmconf.slots[slot]->fnum, + vmconf.slots[slot]->emul, + vmconf.slots[slot]->config); + if (pci_err != 0) + errx(1, "Failed to parse PCI configuration"); + } + } + + if (!vmconf.vmname && argc > 1) + vmconf.vmname = argv[1]; + + if (!vmconf.vmname) usage(1); - vmname = argv[0]; - - ctx = vm_open(vmname); + ctx = vm_open(vmconf.vmname); if (ctx == NULL) { perror("vm_open"); exit(1); } - if (guest_ncpus < 1) { - fprintf(stderr, "Invalid guest vCPUs (%d)\n", guest_ncpus); + if (vmconf.vcpus < 1) { + fprintf(stderr, "Invalid guest vCPUs (%d)\n", vmconf.vcpus); exit(1); } max_vcpus = num_vcpus_allowed(ctx); - if (guest_ncpus > max_vcpus) { + if (vmconf.vcpus > max_vcpus) { fprintf(stderr, "%d vCPUs requested but only %d available\n", - guest_ncpus, max_vcpus); + vmconf.vcpus, max_vcpus); exit(1); } fbsdrun_set_capabilities(ctx, BSP); - if (dump_guest_memory) + if (vmconf.dump_memory) vm_set_memflags(ctx, VM_MEM_F_INCORE); - err = vm_setup_memory(ctx, memsize, VM_MMAP_ALL); + + err = vm_setup_memory(ctx, vmconf.memsize, VM_MMAP_ALL); if (err) { fprintf(stderr, "Unable to setup memory (%d)\n", err); exit(1); @@ -850,7 +932,7 @@ pci_irq_init(ctx); ioapic_init(ctx); - rtc_init(ctx, rtc_localtime); + rtc_init(ctx, vmconf.rtc_localtime); sci_init(ctx); /* @@ -859,8 +941,8 @@ if (init_pci(ctx) != 0) exit(1); - if (gdb_port != 0) - init_dbgport(gdb_port); + if (vmconf.gdb_port != 0) + init_dbgport(vmconf.gdb_port); if (bvmcons) init_bvmcons(); @@ -871,8 +953,8 @@ /* * build the guest tables, MP etc. */ - if (mptgen) { - error = mptable_build(ctx, guest_ncpus); + if (vmconf.mptgen) { + error = mptable_build(ctx, vmconf.vcpus); if (error) exit(1); } @@ -880,8 +962,8 @@ error = smbios_build(ctx); assert(error == 0); - if (acpi) { - error = acpi_build(ctx, guest_ncpus); + if (vmconf.acpi) { + error = acpi_build(ctx, vmconf.vcpus); assert(error == 0); } @@ -888,8 +970,8 @@ /* * Change the proc title to include the VM name. */ - setproctitle("%s", vmname); - + setproctitle("%s", vmconf.vmname); + /* * Add CPU 0 */ Index: usr.sbin/bhyve/bhyverun.h =================================================================== --- usr.sbin/bhyve/bhyverun.h (revision 283903) +++ usr.sbin/bhyve/bhyverun.h (working copy) @@ -39,9 +39,7 @@ #define VMEXIT_ABORT (-1) struct vmctx; -extern int guest_ncpus; -extern char *guest_uuid_str; -extern char *vmname; +extern struct config_entry vmconf; void *paddr_guest2host(struct vmctx *ctx, uintptr_t addr, size_t len); Index: usr.sbin/bhyve/config.c =================================================================== --- usr.sbin/bhyve/config.c (revision 0) +++ usr.sbin/bhyve/config.c (working copy) @@ -0,0 +1,415 @@ +/*- + * 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" + +/* + * Load the config file. + */ +void +load_config(char *in_name, char *conf_name, struct config_entry *c) +{ + struct ucl_parser *p = NULL; + + 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, "/etc/defaults/bhyve.conf")) { + if (errno != ENOENT) + errx(EXIT_FAILURE, "Parse error in file %s: %s", + in_name, ucl_parser_get_error(p)); + ucl_parser_free(p); + } + + if (in_name != NULL) { + if (!ucl_parser_add_file_priority(p, in_name, 5)) { + if (errno != ENOENT) + errx(EXIT_FAILURE, "Parse error in file %s: %s", + in_name, ucl_parser_get_error(p)); + ucl_parser_free(p); + } + } + + /* + * Load from the root of the config file. If a specific file was + * requested, this will load the config from that file. If no filename + * was provided, this will load the defaults + */ + parse_conf(NULL, c, p); + + /* If no filename was provided, look for a key matching the vmname */ + if (in_name == NULL) + parse_conf(conf_name, c, p); + + check_config(c); + + if (p != NULL) + ucl_parser_free(p); +} + +/* + * 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->console) + errx(EX_USAGE, "console not defined"); + + return (0); +} + +/* + * Parse the config file. + */ +void +parse_conf(char *conf_name, struct config_entry *c, struct ucl_parser *p) +{ + const char *config = conf_name; + ucl_object_t *root = NULL; + const ucl_object_t *obj = NULL; + const ucl_object_t *cur, *acur; + ucl_object_iter_t it = NULL, ait = NULL; + const char *key; + char *tmp; + int i; + + root = ucl_parser_get_object(p); + if (root == NULL || ucl_object_type(root) != UCL_OBJECT) + errx(EXIT_FAILURE, "Invalid configuration format.\n"); + + if (conf_name != NULL) + obj = ucl_lookup_path(root, config); + else + obj = root; + + if (obj == NULL) + errx(EXIT_FAILURE, "Could not find configuration for VM: %s", conf_name); + + 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, "host_base") == 0) + c->host_base = 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, "env") == 0) { + switch (ucl_object_type(cur)) { + case UCL_OBJECT: + if (cur->len == 0) + break; + c->env_count = cur->len; + c->env = malloc(sizeof(char *) * c->env_count); + i = 0; + ait = ucl_object_iterate_new(cur); + while ((acur = ucl_object_iterate_safe(ait, false)) != NULL) { + asprintf(&c->env[i], "%s=%s", ucl_object_key(acur), ucl_object_tostring_forced(acur)); + i++; + } + ucl_object_iterate_free(ait); + break; + default: + warnx("env must be an object containing key-value pairs"); + break; + } + } else if (strcasecmp(key, "device") == 0) { + switch (ucl_object_type(cur)) { + /* XXX: TODO: Case where machine has more than 31 devices */ + 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"); + if (tmp) + parse_pci_slot(tmp, + &c->slots[c->slot_count]->bnum, + &c->slots[c->slot_count]->snum, + &c->slots[c->slot_count]->fnum); + else + c->slots[c->slot_count]->snum = c->slot_count; + 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"); + if (tmp) + parse_pci_slot(tmp, + &c->slots[c->slot_count]->bnum, + &c->slots[c->slot_count]->snum, + &c->slots[c->slot_count]->fnum); + else + c->slots[c->slot_count]->snum = c->slot_count; + 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->slots[c->slot_count]->snum = c->slot_count; + 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)); + tmp = parse_string_safe(cur, ".slot"); + if (tmp) + parse_pci_slot(tmp, + &c->slots[c->slot_count]->bnum, + &c->slots[c->slot_count]->snum, + &c->slots[c->slot_count]->fnum); + else + c->slots[c->slot_count]->snum = c->slot_count; + tmp = parse_string_safe(cur, ".type"); + if (tmp) + c->slots[c->slot_count]->emul = tmp; + else + c->slots[c->slot_count]->emul = strdup("virtio-net"); + 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->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)); + tmp = parse_string_safe(acur, ".slot"); + if (tmp) + parse_pci_slot(tmp, + &c->slots[c->slot_count]->bnum, + &c->slots[c->slot_count]->snum, + &c->slots[c->slot_count]->fnum); + else + c->slots[c->slot_count]->snum = c->slot_count; + tmp = parse_string_safe(acur, ".type"); + if (tmp) + c->slots[c->slot_count]->emul = tmp; + else + c->slots[c->slot_count]->emul = strdup("virtio-net"); + 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->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 = strdup("virtio-net"); + c->slots[c->slot_count]->iface.name = parse_string_safe(cur, NULL); + c->slots[c->slot_count]->snum = 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)); + tmp = parse_string_safe(cur, ".slot"); + if (tmp) + parse_pci_slot(tmp, + &c->slots[c->slot_count]->bnum, + &c->slots[c->slot_count]->snum, + &c->slots[c->slot_count]->fnum); + else + c->slots[c->slot_count]->snum = c->slot_count; + tmp = parse_string_safe(cur, ".type"); + if (tmp) + c->slots[c->slot_count]->emul = tmp; + else + c->slots[c->slot_count]->emul = strdup("virtio-blk"); + c->slots[c->slot_count]->disk.path = parse_string_safe(cur, ".path"); + ucl_object_toboolean_safe(ucl_lookup_path(cur, ".boot"), &c->slots[c->slot_count]->disk.boot); + 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->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)); + tmp = parse_string_safe(acur, ".slot"); + if (tmp) + parse_pci_slot(tmp, + &c->slots[c->slot_count]->bnum, + &c->slots[c->slot_count]->snum, + &c->slots[c->slot_count]->fnum); + else + c->slots[c->slot_count]->snum = c->slot_count; + tmp = parse_string_safe(acur, ".type"); + if (tmp) + c->slots[c->slot_count]->emul = tmp; + else + c->slots[c->slot_count]->emul = strdup("virtio-blk"); + c->slots[c->slot_count]->disk.path = parse_string_safe(acur, ".path"); + ucl_object_toboolean_safe(ucl_lookup_path(acur, ".boot"), &c->slots[c->slot_count]->disk.boot); + 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->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 = strdup("virtio-blk"); + c->slots[c->slot_count]->disk.path = parse_string_safe(cur, NULL); + c->slots[c->slot_count]->snum = 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); + + if (root != NULL) + ucl_object_unref(root); + +} + +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); +} Property changes on: usr.sbin/bhyve/config.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1,2 ## +FreeBSD=%H +\ No newline at end of property \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: usr.sbin/bhyve/config.h =================================================================== --- usr.sbin/bhyve/config.h (revision 0) +++ usr.sbin/bhyve/config.h (working copy) @@ -0,0 +1,95 @@ +/*- + * 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 + +#define NDISKS 32 +#define MB (1024UL * 1024) +#define GB (1024UL * MB) + +struct config_iface_entry { + char *name; + char *mac; +}; + +struct config_disk_entry { + char *path; + bool boot; + 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 *host_base; + char *pincpu; + char *lpc_device; + char *uuid; + char **env; + size_t env_count; + struct config_slot_entry *slots[255]; + size_t 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; +}; + +extern struct config_slot_entry sinit; +extern struct config_entry cinit; + +int check_config(struct config_entry *config); +void load_config(char *in_name, char *conf_name, struct config_entry *c); +void parse_conf(char *conf_name, struct config_entry *c, struct ucl_parser *p); +bool parse_pci_slot(const char *str, int *bnum, int *snum, int *fnum); +char* parse_string_safe(const ucl_object_t *obj, char *path); Property changes on: usr.sbin/bhyve/config.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1,2 ## +FreeBSD=%H +\ No newline at end of property \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: usr.sbin/bhyve/pci_emul.c =================================================================== --- usr.sbin/bhyve/pci_emul.c (revision 283903) +++ usr.sbin/bhyve/pci_emul.c (working copy) @@ -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/bhyve/pci_emul.h =================================================================== --- usr.sbin/bhyve/pci_emul.h (revision 283903) +++ usr.sbin/bhyve/pci_emul.h (working copy) @@ -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_virtio_net.c =================================================================== --- usr.sbin/bhyve/pci_virtio_net.c (revision 283903) +++ usr.sbin/bhyve/pci_virtio_net.c (working copy) @@ -50,6 +50,7 @@ #include #include +#include "config.h" #include "bhyverun.h" #include "pci_emul.h" #include "mevent.h" @@ -621,7 +622,7 @@ */ if (!mac_provided) { snprintf(nstr, sizeof(nstr), "%d-%d-%s", pi->pi_slot, - pi->pi_func, vmname); + pi->pi_func, vmconf.vmname); MD5Init(&mdctx); MD5Update(&mdctx, nstr, strlen(nstr)); Index: usr.sbin/bhyve/smbiostbl.c =================================================================== --- usr.sbin/bhyve/smbiostbl.c (revision 283903) +++ usr.sbin/bhyve/smbiostbl.c (working copy) @@ -40,12 +40,10 @@ #include #include +#include "config.h" #include "bhyverun.h" #include "smbiostbl.h" -#define MB (1024*1024) -#define GB (1024ULL*1024*1024) - #define SMBIOS_BASE 0xF1000 /* BHYVE_ACPI_BASE - SMBIOS_BASE) */ @@ -586,11 +584,11 @@ curaddr, endaddr, n, size); type1 = (struct smbios_table_type1 *)curaddr; - if (guest_uuid_str != NULL) { + if (vmconf.uuid != NULL) { uuid_t uuid; uint32_t status; - uuid_from_string(guest_uuid_str, &uuid, &status); + uuid_from_string(vmconf.uuid, &uuid, &status); if (status != uuid_s_ok) return (-1); @@ -609,7 +607,7 @@ return (-1); MD5Init(&mdctx); - MD5Update(&mdctx, vmname, strlen(vmname)); + MD5Update(&mdctx, vmconf.vmname, strlen(vmconf.vmname)); MD5Update(&mdctx, hostname, sizeof(hostname)); MD5Final(digest, &mdctx); @@ -634,7 +632,7 @@ { int i; - for (i = 0; i < guest_ncpus; i++) { + for (i = 0; i < vmconf.vcpus; i++) { struct smbios_table_type4 *type4; char *p; int nstrings, len; Index: usr.sbin/bhyve/spinup_ap.c =================================================================== --- usr.sbin/bhyve/spinup_ap.c (revision 283903) +++ usr.sbin/bhyve/spinup_ap.c (working copy) @@ -39,6 +39,7 @@ #include #include +#include "config.h" #include "bhyverun.h" #include "spinup_ap.h" @@ -80,7 +81,7 @@ int error; assert(newcpu != 0); - assert(newcpu < guest_ncpus); + assert(newcpu < vmconf.vcpus); error = vcpu_reset(ctx, newcpu); assert(error == 0); Index: usr.sbin/bhyveload/Makefile =================================================================== --- usr.sbin/bhyveload/Makefile (revision 283903) +++ usr.sbin/bhyveload/Makefile (working copy) @@ -1,13 +1,19 @@ # $FreeBSD$ PROG= bhyveload -SRCS= bhyveload.c MAN= bhyveload.8 +SRCS= bhyveload.c \ + ${.CURDIR}/../bhyve/config.c + +#LIBADD= vmmapi ucl LIBADD= vmmapi WARNS?= 3 CFLAGS+=-I${.CURDIR}/../../sys/boot/userboot +CFLAGS+=-I${.CURDIR}/../bhyve +#CFLAGS+=-I${.CURDIR}/../../contrib/libucl/include +CFLAGS+=-I$/usr/local/include -L/usr/local/lib -lucl .include Index: usr.sbin/bhyveload/bhyveload.8 =================================================================== --- usr.sbin/bhyveload/bhyveload.8 (revision 283903) +++ usr.sbin/bhyveload/bhyveload.8 (working copy) @@ -38,9 +38,12 @@ .Op Fl c Ar cons-dev .Op Fl d Ar disk-path .Op Fl e Ar name=value +.Op Fl f Ar bhyve.conf .Op Fl h Ar host-path .Op Fl m Ar mem-size .Ar vmname +.Nm +.Fl f Ar bhyve.conf .Sh DESCRIPTION .Nm is used to load a @@ -84,6 +87,8 @@ .Pp The option may be used more than once to set more than one environment variable. +.It Fl f Ar bhyve.conf +Read configuration from specified file. .It Fl h Ar host-path The .Ar host-path @@ -132,6 +137,10 @@ .Pa /dev/nmdm1B .Pp .Dl "bhyveload -m 256MB -h /usr/images/test -c /dev/nmdm1B test-vm" +.Pp +To create a virtual machine described in 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 (revision 283903) +++ usr.sbin/bhyveload/bhyveload.c (working copy) @@ -82,21 +82,54 @@ #include +#include + #include "userboot.h" -#define MB (1024 * 1024UL) -#define GB (1024 * 1024 * 1024UL) #define BSP 0 -#define NDISKS 32 +struct config_slot_entry sinit = { + .bnum = 0, + .snum = 0, + .fnum = 0, + .emul = NULL, + .config = NULL +}; -static char *host_base; +struct config_entry cinit = { + .vmname = NULL, + .vcpus = 1, + .memsize = 256 * MB, + .console = NULL, + .host_base = NULL, + .pincpu = NULL, + .lpc_device = NULL, + .uuid = NULL, + .env = NULL, + .env_count = 0, + .slots = {NULL}, + .slot_count = 0, + .gdb_port = 0, + .acpi = 0, + .dump_memory = 0, + .vmexit_on_hlt = 0, + .vmexit_on_pause = 0, + .strictio = 0, + .rtc_localtime = 1, + .strictmsr = 1, + .virtio_msix = 1, + .x2apic_mode = 0, + .mptgen = 1 +}; + +struct config_entry vmconf; + static struct termios term, oldterm; static int disk_fd[NDISKS]; static int ndisks; static int consin_fd, consout_fd; -static char *vmname, *progname; +static char *progname; static struct vmctx *ctx; static uint64_t gdtbase, cr3, rsp; @@ -156,10 +189,10 @@ struct cb_file *cf; char path[PATH_MAX]; - if (!host_base) + if (!vmconf.host_base) return (ENOENT); - strlcpy(path, host_base, PATH_MAX); + strlcpy(path, vmconf.host_base, PATH_MAX); if (path[strlen(path) - 1] == '/') path[strlen(path) - 1] = 0; strlcat(path, filename, PATH_MAX); @@ -630,9 +663,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); } @@ -641,17 +675,26 @@ { void *h; void (*func)(struct loader_callbacks *, void *, int, int); - uint64_t mem_size; int opt, error, need_reinit; + size_t slot; + char *temp, *bootpath; + const ucl_object_t *target; + bool new_config; progname = basename(argv[0]); - - mem_size = 256 * MB; - consin_fd = STDIN_FILENO; consout_fd = STDOUT_FILENO; + temp = NULL; + bootpath = NULL; + target = NULL; + new_config = false; - while ((opt = getopt(argc, argv, "c:d:e:h:m:")) != -1) { + if (argc == 2 && *argv[1] != '-') { + new_config = true; + load_config(NULL, argv[1], &vmconf); + } + + while ((opt = getopt(argc, argv, "c:d:e:f:h:m:")) != -1) { switch (opt) { case 'c': error = altcons_open(optarg); @@ -658,23 +701,23 @@ 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': + new_config = true; + load_config(optarg, NULL, &vmconf); + break; case 'h': - host_base = optarg; + vmconf.host_base = optarg; break; - case 'm': - error = vm_parse_memsize(optarg, &mem_size); + error = vm_parse_memsize(optarg, &vmconf.memsize); if (error != 0) errx(EX_USAGE, "Invalid memsize '%s'", optarg); break; @@ -686,13 +729,62 @@ argc -= optind; argv += optind; - if (argc != 1) + if (new_config) { + error = altcons_open(vmconf.console); + if (error != 0) + errx(EX_USAGE, "Could not open console: %s", + vmconf.console); + + /* Find the boot disk */ + for (slot = 0; slot < vmconf.slot_count; slot++) { + if (strcasecmp(vmconf.slots[slot]->emul, "virtio-blk") == 0 || + strcasecmp(vmconf.slots[slot]->emul, "ahci-hd") == 0 || + strcasecmp(vmconf.slots[slot]->emul, "ahci-cd") == 0) { + if (vmconf.slots[slot]->disk.boot == true) { + bootpath = vmconf.slots[slot]->disk.path; + break; + } + } + } + if (bootpath != NULL) { + error = disk_open(bootpath); + if (error != 0) + errx(EX_USAGE, "Could not open boot disk: %s", bootpath); + } + /* Open every other disk (required for RAID etc) */ + for (slot = 0; slot < vmconf.slot_count; slot++) { + if (strcasecmp(vmconf.slots[slot]->emul, "virtio-blk") == 0 || + strcasecmp(vmconf.slots[slot]->emul, "ahci-hd") == 0 || + strcasecmp(vmconf.slots[slot]->emul, "ahci-cd") == 0) { + /* Open open the first NDISKS disks */ + if (ndisks > NDISKS) + break; + error = disk_open(vmconf.slots[slot]->disk.path); + if (error != 0) + errx(EX_USAGE, "Could not open disk: %s", vmconf.slots[slot]->disk.path); + } + } + + /* build the environment */ + for (slot = 0; slot < vmconf.env_count; slot++) { + addenv(vmconf.env[slot]); + } + + asprintf(&temp, "%zu", vmconf.memsize); + error = vm_parse_memsize(temp, &vmconf.memsize); + if (error) + errx(EX_USAGE, "invalid memsize '%s'", optarg); + free(temp); + } + + if (!vmconf.vmname && argc > 1) + vmconf.vmname = argv[1]; + + if (!vmconf.vmname) usage(); - vmname = argv[0]; - need_reinit = 0; - error = vm_create(vmname); + error = vm_create(vmconf.vmname); if (error) { if (errno != EEXIST) { perror("vm_create"); @@ -701,7 +793,7 @@ need_reinit = 1; } - ctx = vm_open(vmname); + ctx = vm_open(vmconf.vmname); if (ctx == NULL) { perror("vm_open"); exit(1); @@ -715,7 +807,7 @@ } } - error = vm_setup_memory(ctx, mem_size, VM_MMAP_ALL); + error = vm_setup_memory(ctx, vmconf.memsize, VM_MMAP_ALL); if (error) { perror("vm_setup_memory"); exit(1);