Changeset View
Changeset View
Standalone View
Standalone View
head/usr.sbin/bhyve/bhyverun.c
Show First 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | |||||
#include <libgen.h> | #include <libgen.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <assert.h> | #include <assert.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <pthread.h> | #include <pthread.h> | ||||
#include <pthread_np.h> | #include <pthread_np.h> | ||||
#include <sysexits.h> | #include <sysexits.h> | ||||
#include <stdbool.h> | #include <stdbool.h> | ||||
#include <stdint.h> | |||||
#include <machine/vmm.h> | #include <machine/vmm.h> | ||||
#ifndef WITHOUT_CAPSICUM | #ifndef WITHOUT_CAPSICUM | ||||
#include <machine/vmm_dev.h> | #include <machine/vmm_dev.h> | ||||
#endif | #endif | ||||
#include <vmmapi.h> | #include <vmmapi.h> | ||||
#include "bhyverun.h" | #include "bhyverun.h" | ||||
Show All 20 Lines | |||||
#define GB (1024UL * MB) | #define GB (1024UL * MB) | ||||
typedef int (*vmexit_handler_t)(struct vmctx *, struct vm_exit *, int *vcpu); | typedef int (*vmexit_handler_t)(struct vmctx *, struct vm_exit *, int *vcpu); | ||||
extern int vmexit_task_switch(struct vmctx *, struct vm_exit *, int *vcpu); | extern int vmexit_task_switch(struct vmctx *, struct vm_exit *, int *vcpu); | ||||
char *vmname; | char *vmname; | ||||
int guest_ncpus; | int guest_ncpus; | ||||
uint16_t cores, maxcpus, sockets, threads; | |||||
char *guest_uuid_str; | char *guest_uuid_str; | ||||
static int guest_vmexit_on_hlt, guest_vmexit_on_pause; | static int guest_vmexit_on_hlt, guest_vmexit_on_pause; | ||||
static int virtio_msix = 1; | static int virtio_msix = 1; | ||||
static int x2apic_mode = 0; /* default is xAPIC */ | static int x2apic_mode = 0; /* default is xAPIC */ | ||||
static int strictio; | static int strictio; | ||||
static int strictmsr = 1; | static int strictmsr = 1; | ||||
Show All 28 Lines | |||||
static cpuset_t *vcpumap[VM_MAXCPU] = { NULL }; | static cpuset_t *vcpumap[VM_MAXCPU] = { NULL }; | ||||
static void | static void | ||||
usage(int code) | usage(int code) | ||||
{ | { | ||||
fprintf(stderr, | fprintf(stderr, | ||||
"Usage: %s [-abehuwxACHPSWY] [-c vcpus] [-g <gdb port>] [-l <lpc>]\n" | "Usage: %s [-abehuwxACHPSWY]\n" | ||||
" %*s [-c [[cpus=]numcpus][,sockets=n][,cores=n][,threads=n]]\n" | |||||
" %*s [-g <gdb port>] [-l <lpc>]\n" | |||||
" %*s [-m mem] [-p vcpu:hostcpu] [-s <pci>] [-U uuid] <vm>\n" | " %*s [-m mem] [-p vcpu:hostcpu] [-s <pci>] [-U uuid] <vm>\n" | ||||
" -a: local apic is in xAPIC mode (deprecated)\n" | " -a: local apic is in xAPIC mode (deprecated)\n" | ||||
" -A: create ACPI tables\n" | " -A: create ACPI tables\n" | ||||
" -c: # cpus (default 1)\n" | " -c: number of cpus and/or topology specification" | ||||
" -C: include guest memory in core file\n" | " -C: include guest memory in core file\n" | ||||
" -e: exit on unhandled I/O access\n" | " -e: exit on unhandled I/O access\n" | ||||
" -g: gdb port\n" | " -g: gdb port\n" | ||||
" -h: help\n" | " -h: help\n" | ||||
" -H: vmexit from the guest on hlt\n" | " -H: vmexit from the guest on hlt\n" | ||||
" -l: LPC device configuration\n" | " -l: LPC device configuration\n" | ||||
" -m: memory size in MB\n" | " -m: memory size in MB\n" | ||||
" -p: pin 'vcpu' to 'hostcpu'\n" | " -p: pin 'vcpu' to 'hostcpu'\n" | ||||
" -P: vmexit from the guest on pause\n" | " -P: vmexit from the guest on pause\n" | ||||
" -s: <slot,driver,configinfo> PCI slot config\n" | " -s: <slot,driver,configinfo> PCI slot config\n" | ||||
" -S: guest memory cannot be swapped\n" | " -S: guest memory cannot be swapped\n" | ||||
" -u: RTC keeps UTC time\n" | " -u: RTC keeps UTC time\n" | ||||
" -U: uuid\n" | " -U: uuid\n" | ||||
" -w: ignore unimplemented MSRs\n" | " -w: ignore unimplemented MSRs\n" | ||||
" -W: force virtio to use single-vector MSI\n" | " -W: force virtio to use single-vector MSI\n" | ||||
" -x: local apic is in x2APIC mode\n" | " -x: local apic is in x2APIC mode\n" | ||||
" -Y: disable MPtable generation\n", | " -Y: disable MPtable generation\n", | ||||
progname, (int)strlen(progname), ""); | progname, (int)strlen(progname), "", (int)strlen(progname), "", | ||||
(int)strlen(progname), ""); | |||||
exit(code); | exit(code); | ||||
} | } | ||||
/* | |||||
* XXX This parser is known to have the following issues: | |||||
* 1. It accepts null key=value tokens ",,". | |||||
* 2. It accepts whitespace after = and before value. | |||||
* 3. Values out of range of INT are silently wrapped. | |||||
* 4. It doesn't check non-final values. | |||||
* 5. The apparently bogus limits of UINT16_MAX are for future expansion. | |||||
* | |||||
* The acceptance of a null specification ('-c ""') is by design to match the | |||||
* manual page syntax specification, this results in a topology of 1 vCPU. | |||||
*/ | |||||
static int | static int | ||||
topology_parse(const char *opt) | |||||
{ | |||||
uint64_t ncpus; | |||||
int c, chk, n, s, t, tmp; | |||||
char *cp, *str; | |||||
bool ns, scts; | |||||
c = 1, n = 1, s = 1, t = 1; | |||||
ns = false, scts = false; | |||||
str = strdup(opt); | |||||
while ((cp = strsep(&str, ",")) != NULL) { | |||||
if (sscanf(cp, "%i%n", &tmp, &chk) == 1) { | |||||
n = tmp; | |||||
ns = true; | |||||
} else if (sscanf(cp, "cpus=%i%n", &tmp, &chk) == 1) { | |||||
n = tmp; | |||||
ns = true; | |||||
} else if (sscanf(cp, "sockets=%i%n", &tmp, &chk) == 1) { | |||||
s = tmp; | |||||
scts = true; | |||||
} else if (sscanf(cp, "cores=%i%n", &tmp, &chk) == 1) { | |||||
c = tmp; | |||||
scts = true; | |||||
} else if (sscanf(cp, "threads=%i%n", &tmp, &chk) == 1) { | |||||
t = tmp; | |||||
scts = true; | |||||
#ifdef notyet /* Do not expose this until vmm.ko implements it */ | |||||
} else if (sscanf(cp, "maxcpus=%i%n", &tmp, &chk) == 1) { | |||||
m = tmp; | |||||
#endif | |||||
/* Skip the empty argument case from -c "" */ | |||||
} else if (cp[0] == '\0') | |||||
continue; | |||||
else | |||||
return (-1); | |||||
/* Any trailing garbage causes an error */ | |||||
if (cp[chk] != '\0') | |||||
return (-1); | |||||
} | |||||
/* | |||||
* Range check 1 <= n <= UINT16_MAX all values | |||||
*/ | |||||
if (n < 1 || s < 1 || c < 1 || t < 1 || | |||||
n > UINT16_MAX || s > UINT16_MAX || c > UINT16_MAX || | |||||
t > UINT16_MAX) | |||||
return (-1); | |||||
/* If only the cpus was specified, use that as sockets */ | |||||
if (!scts) | |||||
s = n; | |||||
/* | |||||
* Compute sockets * cores * threads avoiding overflow | |||||
* The range check above insures these are 16 bit values | |||||
* If n was specified check it against computed ncpus | |||||
*/ | |||||
ncpus = (uint64_t)s * c * t; | |||||
if (ncpus > UINT16_MAX || (ns && n != ncpus)) | |||||
return (-1); | |||||
guest_ncpus = ncpus; | |||||
sockets = s; | |||||
cores = c; | |||||
threads = t; | |||||
return(0); | |||||
} | |||||
static int | |||||
pincpu_parse(const char *opt) | pincpu_parse(const char *opt) | ||||
{ | { | ||||
int vcpu, pcpu; | int vcpu, pcpu; | ||||
if (sscanf(opt, "%d:%d", &vcpu, &pcpu) != 2) { | if (sscanf(opt, "%d:%d", &vcpu, &pcpu) != 2) { | ||||
fprintf(stderr, "invalid format: %s\n", opt); | fprintf(stderr, "invalid format: %s\n", opt); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 602 Lines • ▼ Show 20 Lines | #endif | ||||
if (reinit) { | if (reinit) { | ||||
error = vm_reinit(ctx); | error = vm_reinit(ctx); | ||||
if (error) { | if (error) { | ||||
perror("vm_reinit"); | perror("vm_reinit"); | ||||
exit(1); | exit(1); | ||||
} | } | ||||
} | } | ||||
error = vm_set_topology(ctx, sockets, cores, threads, maxcpus); | |||||
if (error) | |||||
errx(EX_OSERR, "vm_set_topology"); | |||||
return (ctx); | return (ctx); | ||||
} | } | ||||
int | int | ||||
main(int argc, char *argv[]) | main(int argc, char *argv[]) | ||||
{ | { | ||||
int c, error, gdb_port, err, bvmcons; | int c, error, gdb_port, err, bvmcons; | ||||
int max_vcpus, mptgen, memflags; | int max_vcpus, mptgen, memflags; | ||||
int rtc_localtime; | int rtc_localtime; | ||||
struct vmctx *ctx; | struct vmctx *ctx; | ||||
uint64_t rip; | uint64_t rip; | ||||
size_t memsize; | size_t memsize; | ||||
char *optstr; | char *optstr; | ||||
bvmcons = 0; | bvmcons = 0; | ||||
progname = basename(argv[0]); | progname = basename(argv[0]); | ||||
gdb_port = 0; | gdb_port = 0; | ||||
guest_ncpus = 1; | guest_ncpus = 1; | ||||
sockets = cores = threads = 1; | |||||
maxcpus = 0; | |||||
memsize = 256 * MB; | memsize = 256 * MB; | ||||
mptgen = 1; | mptgen = 1; | ||||
rtc_localtime = 1; | rtc_localtime = 1; | ||||
memflags = 0; | memflags = 0; | ||||
optstr = "abehuwxACHIPSWYp:g:c:s:m:l:U:"; | optstr = "abehuwxACHIPSWYp:g:c:s:m:l:U:"; | ||||
while ((c = getopt(argc, argv, optstr)) != -1) { | while ((c = getopt(argc, argv, optstr)) != -1) { | ||||
switch (c) { | switch (c) { | ||||
case 'a': | case 'a': | ||||
x2apic_mode = 0; | x2apic_mode = 0; | ||||
break; | break; | ||||
case 'A': | case 'A': | ||||
acpi = 1; | acpi = 1; | ||||
break; | break; | ||||
case 'b': | case 'b': | ||||
bvmcons = 1; | bvmcons = 1; | ||||
break; | break; | ||||
case 'p': | case 'p': | ||||
if (pincpu_parse(optarg) != 0) { | if (pincpu_parse(optarg) != 0) { | ||||
errx(EX_USAGE, "invalid vcpu pinning " | errx(EX_USAGE, "invalid vcpu pinning " | ||||
"configuration '%s'", optarg); | "configuration '%s'", optarg); | ||||
} | } | ||||
break; | break; | ||||
case 'c': | case 'c': | ||||
guest_ncpus = atoi(optarg); | if (topology_parse(optarg) != 0) { | ||||
errx(EX_USAGE, "invalid cpu topology " | |||||
"'%s'", optarg); | |||||
} | |||||
break; | break; | ||||
case 'C': | case 'C': | ||||
memflags |= VM_MEM_F_INCORE; | memflags |= VM_MEM_F_INCORE; | ||||
break; | break; | ||||
case 'g': | case 'g': | ||||
gdb_port = atoi(optarg); | gdb_port = atoi(optarg); | ||||
break; | break; | ||||
case 'l': | case 'l': | ||||
▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | main(int argc, char *argv[]) | ||||
argc -= optind; | argc -= optind; | ||||
argv += optind; | argv += optind; | ||||
if (argc != 1) | if (argc != 1) | ||||
usage(1); | usage(1); | ||||
vmname = argv[0]; | vmname = argv[0]; | ||||
ctx = do_open(vmname); | ctx = do_open(vmname); | ||||
if (guest_ncpus < 1) { | |||||
fprintf(stderr, "Invalid guest vCPUs (%d)\n", guest_ncpus); | |||||
exit(1); | |||||
} | |||||
max_vcpus = num_vcpus_allowed(ctx); | max_vcpus = num_vcpus_allowed(ctx); | ||||
if (guest_ncpus > max_vcpus) { | if (guest_ncpus > max_vcpus) { | ||||
fprintf(stderr, "%d vCPUs requested but only %d available\n", | fprintf(stderr, "%d vCPUs requested but only %d available\n", | ||||
guest_ncpus, max_vcpus); | guest_ncpus, max_vcpus); | ||||
exit(1); | exit(1); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 96 Lines • Show Last 20 Lines |