Index: share/examples/bhyve/guest.conf.sample =================================================================== --- /dev/null +++ share/examples/bhyve/guest.conf.sample @@ -0,0 +1,14 @@ +# +# Example: bhyve guest configuration file. +# +# Note: The variable bootdisk must be set with the disk that was +# selected in the installation process. +# +name = FreeBSD_Guest +memory = 2048M +vcpus = 2 +bootdisk = "/path/disk1.img" +disk = "/path/disk1.img,/path/disk2.img" +isofile = /path/FreeBSD-bootonly.iso +iface = "tap1,tap2" +console = "stdio" 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 @@ -67,6 +67,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) @@ -697,6 +700,10 @@ struct vmctx *ctx; uint64_t rip; size_t memsize; + struct config_entry cb; + char temp[STR_SIZE]; + char *multiface, *multidisk = NULL; + int slot = 0; bvmcons = 0; dump_guest_memory = 0; @@ -707,8 +714,61 @@ mptgen = 1; rtc_localtime = 1; - while ((c = getopt(argc, argv, "abehuwxACHIPWYp:g:c:s:m:l:U:")) != -1) { + while ((c = getopt(argc, argv, "abehuwxACHIPWYp:g:c:s:m:l:U:f:")) != -1) { switch (c) { + case 'f': + cb = parse_conf(optarg); + guest_ncpus = cb.vcpus; + vm_parse_memsize(cb.memsize, &memsize); + vmname = cb.vmname; + acpi = 1; + guest_vmexit_on_hlt = 1; + guest_vmexit_on_pause = 1; + gdb_port = 0; + + snprintf(temp, sizeof(temp), "%d:0,hostbridge", slot); + pci_parse_slot(temp); + slot += 1; + + memset(temp, '\0', STR_SIZE); + snprintf(temp, sizeof(temp), "%d:0,lpc", slot); + pci_parse_slot(temp); + slot += 1; + + while ((multiface = strsep(&cb.iface, ",")) != NULL) { + memset(temp, '\0', STR_SIZE); + snprintf(temp, sizeof(temp), "%d:0,virtio-net,%s", + slot, multiface); + pci_parse_slot(temp); + slot += 1; + } + + if (cb.isofile) { + memset(temp, '\0', STR_SIZE); + snprintf(temp, sizeof(temp), "%d:0,virtio-blk,%s", + slot, cb.isofile); + pci_parse_slot(temp); + slot += 1; + } + + memset(temp, '\0', STR_SIZE); + snprintf(temp, sizeof(temp), "%d:0,virtio-blk,%s", + slot, cb.bootdisk); + pci_parse_slot(temp); + slot += 1; + + while ((multidisk = strsep(&cb.vdisk, ",")) != NULL) { + memset(temp, '\0', STR_SIZE); + snprintf(temp, sizeof(temp), "%d:0,virtio-blk,%s", + slot, multidisk); + pci_parse_slot(temp); + slot += 1; + } + + memset(temp, '\0', STR_SIZE); + snprintf(temp, sizeof(temp), "com1,%s", cb.console); + lpc_device_parse(temp); + break; case 'a': x2apic_mode = 0; break; @@ -794,10 +854,8 @@ argc -= optind; argv += optind; - if (argc != 1) - usage(1); - - vmname = argv[0]; + if (!vmname) + vmname = argv[0]; ctx = vm_open(vmname); if (ctx == NULL) { @@ -821,8 +879,9 @@ if (dump_guest_memory) vm_set_memflags(ctx, VM_MEM_F_INCORE); + err = vm_setup_memory(ctx, memsize, VM_MMAP_ALL); - if (err) { + if (err) { fprintf(stderr, "Unable to setup memory (%d)\n", err); exit(1); } @@ -874,11 +933,6 @@ } /* - * Change the proc title to include the VM name. - */ - setproctitle("%s", vmname); - - /* * Add CPU 0 */ fbsdrun_addcpu(ctx, BSP, BSP, rip); Index: usr.sbin/bhyve/config.h =================================================================== --- /dev/null +++ usr.sbin/bhyve/config.h @@ -0,0 +1,43 @@ +/*- + * 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. + * + */ + +#define STR_SIZE 254 + +struct config_entry { + char *vmname; + char *memsize; + int vcpus; + char *iface; + char *console; + char *bootdisk; + char *vdisk; + char *isofile; +}; + +struct config_entry parse_conf(char *config_file); +int load_config(const char *fname); +int check_config(struct config_entry *config); Index: usr.sbin/bhyve/config.c =================================================================== --- /dev/null +++ usr.sbin/bhyve/config.c @@ -0,0 +1,157 @@ +/*- + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +struct config_entry cinit = { + NULL, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +/* + * 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->iface) + errx(EX_USAGE, "iface not defined"); + 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; + ucl_object_iter_t it = NULL; + const char *key, *value; + + if (load_config(config) == 1) { + errx(EXIT_FAILURE, "No config file found: %s", config); + exit (1); + } + + p = ucl_parser_new(0); + if (p == NULL) + errx(1, "Cant allocate p"); + + if (!ucl_parser_add_file(p, config)) { + if (errno != ENOENT) + errx(EXIT_FAILURE, "No config file found " + "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)); + c = &cinit; + while ((cur = ucl_iterate_object(obj, &it, true))) { + key = ucl_object_key(cur); + value = ucl_object_tostring_forced(cur); + if (strcasecmp(key, "name") == 0) + c->vmname = strdup(value); + else if (strcasecmp(key, "memory") == 0) + c->memsize = strdup(value); + else if (strcasecmp(key, "vcpus") == 0) + c->vcpus = atoi(strdup(value)); + else if (strcasecmp(key, "bootdisk") == 0) + c->bootdisk = strdup(value); + else if (strcasecmp(key, "iface") == 0) + c->iface = strdup(value); + else if (strcasecmp(key, "console") == 0) + c->console = strdup(value); + else if (strcasecmp(key, "disk") == 0) + c->vdisk = strdup(value); + else if (strcasecmp(key, "isofile") == 0) + c->isofile = strdup(value); + } + + check_config(c); + + if (p != NULL) + ucl_parser_free(p); + if (obj != NULL) + ucl_object_unref(obj); + + return *c; +} Index: usr.sbin/bhyveload/Makefile =================================================================== --- usr.sbin/bhyveload/Makefile +++ usr.sbin/bhyveload/Makefile @@ -1,13 +1,18 @@ # $FreeBSD$ PROG= bhyveload -SRCS= bhyveload.c MAN= bhyveload.8 -LIBADD= vmmapi +SRCS= bhyveload.c \ + ${.CURDIR}/../bhyve/config.c + +LIBADD= vmmapi ucl WARNS?= 3 +CFLAGS+=-g 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 @@ -82,6 +82,8 @@ #include +#include + #include "userboot.h" #define MB (1024 * 1024UL) @@ -643,7 +645,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 +653,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:h:m:f:")) != -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 +705,8 @@ argc -= optind; argv += optind; - if (argc != 1) - usage(); - - vmname = argv[0]; + if (!vmname) + vmname = argv[0]; need_reinit = 0; error = vm_create(vmname);