Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/bhyverun.c
Show First 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | |||||
#endif | #endif | ||||
#include <machine/vmm_instruction_emul.h> | #include <machine/vmm_instruction_emul.h> | ||||
#include <vmmapi.h> | #include <vmmapi.h> | ||||
#include "bhyverun.h" | #include "bhyverun.h" | ||||
#include "acpi.h" | #include "acpi.h" | ||||
#include "atkbdc.h" | #include "atkbdc.h" | ||||
#include "bootrom.h" | #include "bootrom.h" | ||||
#include "config.h" | |||||
#include "inout.h" | #include "inout.h" | ||||
#include "debug.h" | #include "debug.h" | ||||
#include "fwctl.h" | #include "fwctl.h" | ||||
#include "gdb.h" | #include "gdb.h" | ||||
#include "ioapic.h" | #include "ioapic.h" | ||||
#include "kernemu_dev.h" | #include "kernemu_dev.h" | ||||
#include "mem.h" | #include "mem.h" | ||||
#include "mevent.h" | #include "mevent.h" | ||||
▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | static const char * const vmx_exit_reason_desc[] = { | ||||
[EXIT_REASON_PM_LOG_FULL] = "Page-modification log full", | [EXIT_REASON_PM_LOG_FULL] = "Page-modification log full", | ||||
[EXIT_REASON_XSAVES] = "XSAVES", | [EXIT_REASON_XSAVES] = "XSAVES", | ||||
[EXIT_REASON_XRSTORS] = "XRSTORS" | [EXIT_REASON_XRSTORS] = "XRSTORS" | ||||
}; | }; | ||||
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); | ||||
const char *vmname; | |||||
int guest_ncpus; | int guest_ncpus; | ||||
uint16_t cores, maxcpus, sockets, threads; | uint16_t cores, maxcpus, sockets, threads; | ||||
char *guest_uuid_str; | |||||
int raw_stdio = 0; | int raw_stdio = 0; | ||||
static int gdb_port = 0; | |||||
static int guest_vmexit_on_hlt, guest_vmexit_on_pause; | |||||
static int virtio_msix = 1; | |||||
static int x2apic_mode = 0; /* default is xAPIC */ | |||||
static int destroy_on_poweroff = 0; | |||||
static int strictio; | |||||
static int strictmsr = 1; | |||||
static int acpi; | |||||
static char *progname; | static char *progname; | ||||
static const int BSP = 0; | static const int BSP = 0; | ||||
static cpuset_t cpumask; | static cpuset_t cpumask; | ||||
static void vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip); | static void vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip); | ||||
static struct vm_exit vmexit[VM_MAXCPU]; | static struct vm_exit vmexit[VM_MAXCPU]; | ||||
Show All 19 Lines | |||||
static void | static void | ||||
usage(int code) | usage(int code) | ||||
{ | { | ||||
fprintf(stderr, | fprintf(stderr, | ||||
"Usage: %s [-aehuwxACDHPSWY]\n" | "Usage: %s [-aehuwxACDHPSWY]\n" | ||||
" %*s [-c [[cpus=]numcpus][,sockets=n][,cores=n][,threads=n]]\n" | " %*s [-c [[cpus=]numcpus][,sockets=n][,cores=n][,threads=n]]\n" | ||||
" %*s [-l <lpc>]\n" | " %*s [-k <file>] [-l <lpc>] [-m mem] [-o <var>=<value>]\n" | ||||
" %*s [-m mem] [-p vcpu:hostcpu] [-s <pci>] [-U uuid] <vm>\n" | " %*s [-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: number of cpus and/or topology specification\n" | " -c: number of cpus and/or topology specification\n" | ||||
" -C: include guest memory in core file\n" | " -C: include guest memory in core file\n" | ||||
" -D: destroy on power-off\n" | " -D: destroy on power-off\n" | ||||
" -e: exit on unhandled I/O access\n" | " -e: exit on unhandled I/O access\n" | ||||
" -h: help\n" | " -h: help\n" | ||||
" -H: vmexit from the guest on hlt\n" | " -H: vmexit from the guest on hlt\n" | ||||
" -k: key=value flat config file\n" | |||||
" -l: LPC device configuration\n" | " -l: LPC device configuration\n" | ||||
" -m: memory size in MB\n" | " -m: memory size in MB\n" | ||||
" -o: set config 'var' to 'value'\n" | |||||
" -p: pin 'vcpu' to 'hostcpu'\n" | |||||
" -P: vmexit from the guest on pause\n" | |||||
#ifdef BHYVE_SNAPSHOT | #ifdef BHYVE_SNAPSHOT | ||||
" -r: path to checkpoint file\n" | " -r: path to checkpoint file\n" | ||||
#endif | #endif | ||||
" -p: pin 'vcpu' to 'hostcpu'\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), "", (int)strlen(progname), "", | progname, (int)strlen(progname), "", (int)strlen(progname), "", | ||||
(int)strlen(progname), ""); | (int)strlen(progname), ""); | ||||
exit(code); | exit(code); | ||||
} | } | ||||
/* | /* | ||||
* XXX This parser is known to have the following issues: | * XXX This parser is known to have the following issues: | ||||
* 1. It accepts null key=value tokens ",,". | * 1. It accepts null key=value tokens ",," as setting "cpus" to an | ||||
* 2. It accepts whitespace after = and before value. | * empty string. | ||||
* 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 | * 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. | * manual page syntax specification, this results in a topology of 1 vCPU. | ||||
*/ | */ | ||||
static int | static int | ||||
topology_parse(const char *opt) | topology_parse(const char *opt) | ||||
{ | { | ||||
uint64_t ncpus; | |||||
int c, chk, n, s, t, tmp; | |||||
char *cp, *str; | char *cp, *str; | ||||
bool ns, scts; | |||||
c = 1, n = 1, s = 1, t = 1; | if (*opt == '\0') { | ||||
ns = false, scts = false; | set_config_value("sockets", "1"); | ||||
set_config_value("cores", "1"); | |||||
set_config_value("threads", "1"); | |||||
set_config_value("cpus", "1"); | |||||
return (0); | |||||
} | |||||
str = strdup(opt); | str = strdup(opt); | ||||
if (str == NULL) | if (str == NULL) | ||||
goto out; | errx(4, "Failed to allocate memory"); | ||||
while ((cp = strsep(&str, ",")) != NULL) { | while ((cp = strsep(&str, ",")) != NULL) { | ||||
if (sscanf(cp, "%i%n", &tmp, &chk) == 1) { | if (strncmp(cp, "cpus=", strlen("cpus=")) == 0) | ||||
n = tmp; | set_config_value("cpus", cp + strlen("cpus=")); | ||||
ns = true; | else if (strncmp(cp, "sockets=", strlen("sockets=")) == 0) | ||||
} else if (sscanf(cp, "cpus=%i%n", &tmp, &chk) == 1) { | set_config_value("sockets", cp + strlen("sockets=")); | ||||
n = tmp; | else if (strncmp(cp, "cores=", strlen("cores=")) == 0) | ||||
ns = true; | set_config_value("cores", cp + strlen("cores=")); | ||||
} else if (sscanf(cp, "sockets=%i%n", &tmp, &chk) == 1) { | else if (strncmp(cp, "threads=", strlen("threads=")) == 0) | ||||
s = tmp; | set_config_value("threads", cp + strlen("threads=")); | ||||
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 */ | #ifdef notyet /* Do not expose this until vmm.ko implements it */ | ||||
} else if (sscanf(cp, "maxcpus=%i%n", &tmp, &chk) == 1) { | else if (strncmp(cp, "maxcpus=", strlen("maxcpus=")) == 0) | ||||
m = tmp; | set_config_value("maxcpus", cp + strlen("maxcpus=")); | ||||
#endif | #endif | ||||
/* Skip the empty argument case from -c "" */ | else if (strchr(cp, '=') != NULL) | ||||
} else if (cp[0] == '\0') | |||||
continue; | |||||
else | |||||
goto out; | goto out; | ||||
/* Any trailing garbage causes an error */ | else | ||||
if (cp[chk] != '\0') | set_config_value("cpus", cp); | ||||
goto out; | |||||
} | } | ||||
free(str); | free(str); | ||||
str = NULL; | return (0); | ||||
out: | |||||
free(str); | |||||
return (-1); | |||||
} | |||||
static int | |||||
parse_int_value(const char *key, const char *value, int minval, int maxval) | |||||
{ | |||||
char *cp; | |||||
long lval; | |||||
errno = 0; | |||||
lval = strtol(value, &cp, 0); | |||||
if (errno != 0 || *cp != '\0' || cp == value || lval < minval || | |||||
lval > maxval) | |||||
errx(4, "Invalid value for %s: '%s'", key, value); | |||||
return (lval); | |||||
} | |||||
/* | /* | ||||
* Range check 1 <= n <= UINT16_MAX all values | * Set the sockets, cores, threads, and guest_cpus variables based on | ||||
* the configured topology. | |||||
* | |||||
* The limits of UINT16_MAX are due to the types passed to | |||||
* vm_set_topology(). vmm.ko may enforce tighter limits. | |||||
*/ | */ | ||||
if (n < 1 || s < 1 || c < 1 || t < 1 || | static void | ||||
n > UINT16_MAX || s > UINT16_MAX || c > UINT16_MAX || | calc_topolopgy(void) | ||||
t > UINT16_MAX) | { | ||||
return (-1); | const char *value; | ||||
bool explicit_cpus; | |||||
uint64_t ncpus; | |||||
/* If only the cpus was specified, use that as sockets */ | value = get_config_value("cpus"); | ||||
if (!scts) | if (value != NULL) { | ||||
s = n; | guest_ncpus = parse_int_value("cpus", value, 1, UINT16_MAX); | ||||
explicit_cpus = true; | |||||
} else { | |||||
guest_ncpus = 1; | |||||
explicit_cpus = false; | |||||
} | |||||
value = get_config_value("cores"); | |||||
if (value != NULL) | |||||
cores = parse_int_value("cores", value, 1, UINT16_MAX); | |||||
else | |||||
cores = 1; | |||||
value = get_config_value("threads"); | |||||
if (value != NULL) | |||||
threads = parse_int_value("threads", value, 1, UINT16_MAX); | |||||
else | |||||
threads = 1; | |||||
value = get_config_value("sockets"); | |||||
if (value != NULL) | |||||
sockets = parse_int_value("sockets", value, 1, UINT16_MAX); | |||||
else | |||||
sockets = guest_ncpus; | |||||
/* | /* | ||||
* Compute sockets * cores * threads avoiding overflow | * Compute sockets * cores * threads avoiding overflow. The | ||||
* The range check above insures these are 16 bit values | * range check above insures these are 16 bit values. | ||||
* If n was specified check it against computed ncpus | |||||
*/ | */ | ||||
ncpus = (uint64_t)s * c * t; | ncpus = (uint64_t)sockets * cores * threads; | ||||
if (ncpus > UINT16_MAX || (ns && n != ncpus)) | if (ncpus > UINT16_MAX) | ||||
return (-1); | errx(4, "Computed number of vCPUs too high: %ju", | ||||
(uintmax_t)ncpus); | |||||
if (explicit_cpus) { | |||||
if (guest_ncpus != ncpus) | |||||
errx(4, "Topology (%d sockets, %d cores, %d threads) " | |||||
"does not match %d vCPUs", sockets, cores, threads, | |||||
guest_ncpus); | |||||
} else | |||||
guest_ncpus = ncpus; | guest_ncpus = ncpus; | ||||
sockets = s; | |||||
cores = c; | |||||
threads = t; | |||||
return(0); | |||||
out: | |||||
free(str); | |||||
return (-1); | |||||
} | } | ||||
static int | static int | ||||
pincpu_parse(const char *opt) | pincpu_parse(const char *opt) | ||||
{ | { | ||||
const char *value; | |||||
char *newval; | |||||
char key[16]; | |||||
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); | ||||
} | } | ||||
if (vcpu < 0 || vcpu >= VM_MAXCPU) { | if (vcpu < 0 || vcpu >= VM_MAXCPU) { | ||||
fprintf(stderr, "vcpu '%d' outside valid range from 0 to %d\n", | fprintf(stderr, "vcpu '%d' outside valid range from 0 to %d\n", | ||||
vcpu, VM_MAXCPU - 1); | vcpu, VM_MAXCPU - 1); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
if (pcpu < 0 || pcpu >= CPU_SETSIZE) { | if (pcpu < 0 || pcpu >= CPU_SETSIZE) { | ||||
fprintf(stderr, "hostcpu '%d' outside valid range from " | fprintf(stderr, "hostcpu '%d' outside valid range from " | ||||
"0 to %d\n", pcpu, CPU_SETSIZE - 1); | "0 to %d\n", pcpu, CPU_SETSIZE - 1); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
if (vcpumap[vcpu] == NULL) { | snprintf(key, sizeof(key), "vcpu.%d.cpuset", vcpu); | ||||
if ((vcpumap[vcpu] = malloc(sizeof(cpuset_t))) == NULL) { | value = get_config_value(key); | ||||
perror("malloc"); | |||||
if (asprintf(&newval, "%s%s%d", value != NULL ? value : "", | |||||
value != NULL ? "," : "", pcpu) == -1) { | |||||
perror("failed to build new cpuset string"); | |||||
return (-1); | return (-1); | ||||
} | } | ||||
CPU_ZERO(vcpumap[vcpu]); | |||||
set_config_value(key, newval); | |||||
free(newval); | |||||
return (0); | |||||
} | } | ||||
static void | |||||
parse_cpuset(int vcpu, const char *list, cpuset_t *set) | |||||
{ | |||||
char *cp, *token; | |||||
int pcpu, start; | |||||
CPU_ZERO(set); | |||||
start = -1; | |||||
token = __DECONST(char *, list); | |||||
for (;;) { | |||||
pcpu = strtoul(token, &cp, 0); | |||||
if (cp == token) | |||||
errx(4, "invalid cpuset for vcpu %d: '%s'", vcpu, list); | |||||
if (pcpu < 0 || pcpu >= CPU_SETSIZE) | |||||
errx(4, "hostcpu '%d' outside valid range from 0 to %d", | |||||
pcpu, CPU_SETSIZE - 1); | |||||
switch (*cp) { | |||||
case ',': | |||||
case '\0': | |||||
if (start >= 0) { | |||||
if (start > pcpu) | |||||
errx(4, "Invalid hostcpu range %d-%d", | |||||
start, pcpu); | |||||
while (start < pcpu) { | |||||
CPU_SET(start, vcpumap[vcpu]); | |||||
start++; | |||||
} | |||||
start = -1; | |||||
} | |||||
CPU_SET(pcpu, vcpumap[vcpu]); | CPU_SET(pcpu, vcpumap[vcpu]); | ||||
return (0); | break; | ||||
case '-': | |||||
if (start >= 0) | |||||
errx(4, "invalid cpuset for vcpu %d: '%s'", | |||||
vcpu, list); | |||||
start = pcpu; | |||||
break; | |||||
default: | |||||
errx(4, "invalid cpuset for vcpu %d: '%s'", vcpu, list); | |||||
} | } | ||||
if (*cp == '\0') | |||||
break; | |||||
token = cp + 1; | |||||
} | |||||
} | |||||
static void | |||||
build_vcpumaps(void) | |||||
{ | |||||
char key[16]; | |||||
const char *value; | |||||
int vcpu; | |||||
for (vcpu = 0; vcpu < guest_ncpus; vcpu++) { | |||||
snprintf(key, sizeof(key), "vcpu.%d.cpuset", vcpu); | |||||
value = get_config_value(key); | |||||
if (value == NULL) | |||||
continue; | |||||
vcpumap[vcpu] = malloc(sizeof(cpuset_t)); | |||||
if (vcpumap[vcpu] == NULL) | |||||
err(4, "Failed to allocate cpuset for vcpu %d", vcpu); | |||||
parse_cpuset(vcpu, value, vcpumap[vcpu]); | |||||
} | |||||
} | |||||
void | void | ||||
vm_inject_fault(void *arg, int vcpu, int vector, int errcode_valid, | vm_inject_fault(void *arg, int vcpu, int vector, int errcode_valid, | ||||
int errcode) | int errcode) | ||||
{ | { | ||||
struct vmctx *ctx; | struct vmctx *ctx; | ||||
int error, restart_instruction; | int error, restart_instruction; | ||||
ctx = arg; | ctx = arg; | ||||
Show All 15 Lines | |||||
uintptr_t | uintptr_t | ||||
paddr_host2guest(struct vmctx *ctx, void *addr) | paddr_host2guest(struct vmctx *ctx, void *addr) | ||||
{ | { | ||||
return (vm_rev_map_gpa(ctx, addr)); | return (vm_rev_map_gpa(ctx, addr)); | ||||
} | } | ||||
#endif | #endif | ||||
int | int | ||||
fbsdrun_vmexit_on_pause(void) | |||||
{ | |||||
return (guest_vmexit_on_pause); | |||||
} | |||||
int | |||||
fbsdrun_vmexit_on_hlt(void) | |||||
{ | |||||
return (guest_vmexit_on_hlt); | |||||
} | |||||
int | |||||
fbsdrun_virtio_msix(void) | fbsdrun_virtio_msix(void) | ||||
{ | { | ||||
return (virtio_msix); | return (get_config_bool_default("virtio_msix", true)); | ||||
} | } | ||||
static void * | static void * | ||||
fbsdrun_start_thread(void *param) | fbsdrun_start_thread(void *param) | ||||
{ | { | ||||
char tname[MAXCOMLEN + 1]; | char tname[MAXCOMLEN + 1]; | ||||
struct mt_vmm_info *mtp; | struct mt_vmm_info *mtp; | ||||
int vcpu; | int vcpu; | ||||
mtp = param; | mtp = param; | ||||
vcpu = mtp->mt_vcpu; | vcpu = mtp->mt_vcpu; | ||||
snprintf(tname, sizeof(tname), "vcpu %d", vcpu); | snprintf(tname, sizeof(tname), "vcpu %d", vcpu); | ||||
pthread_set_name_np(mtp->mt_thr, tname); | pthread_set_name_np(mtp->mt_thr, tname); | ||||
#ifdef BHYVE_SNAPSHOT | #ifdef BHYVE_SNAPSHOT | ||||
checkpoint_cpu_add(vcpu); | checkpoint_cpu_add(vcpu); | ||||
#endif | #endif | ||||
if (gdb_port != 0) | |||||
gdb_cpu_add(vcpu); | gdb_cpu_add(vcpu); | ||||
vm_loop(mtp->mt_ctx, vcpu, vmexit[vcpu].rip); | vm_loop(mtp->mt_ctx, vcpu, vmexit[vcpu].rip); | ||||
/* not reached */ | /* not reached */ | ||||
exit(1); | exit(1); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | vmexit_inout(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) | ||||
out = !in; | out = !in; | ||||
/* Extra-special case of host notifications */ | /* Extra-special case of host notifications */ | ||||
if (out && port == GUEST_NIO_PORT) { | if (out && port == GUEST_NIO_PORT) { | ||||
error = vmexit_handle_notify(ctx, vme, pvcpu, vme->u.inout.eax); | error = vmexit_handle_notify(ctx, vme, pvcpu, vme->u.inout.eax); | ||||
return (error); | return (error); | ||||
} | } | ||||
error = emulate_inout(ctx, vcpu, vme, strictio); | error = emulate_inout(ctx, vcpu, vme); | ||||
if (error) { | if (error) { | ||||
fprintf(stderr, "Unhandled %s%c 0x%04x at 0x%lx\n", | fprintf(stderr, "Unhandled %s%c 0x%04x at 0x%lx\n", | ||||
in ? "in" : "out", | in ? "in" : "out", | ||||
bytes == 1 ? 'b' : (bytes == 2 ? 'w' : 'l'), | bytes == 1 ? 'b' : (bytes == 2 ? 'w' : 'l'), | ||||
port, vmexit->rip); | port, vmexit->rip); | ||||
return (VMEXIT_ABORT); | return (VMEXIT_ABORT); | ||||
} else { | } else { | ||||
return (VMEXIT_CONTINUE); | return (VMEXIT_CONTINUE); | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
vmexit_rdmsr(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) | vmexit_rdmsr(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) | ||||
{ | { | ||||
uint64_t val; | uint64_t val; | ||||
uint32_t eax, edx; | uint32_t eax, edx; | ||||
int error; | int error; | ||||
val = 0; | val = 0; | ||||
error = emulate_rdmsr(ctx, *pvcpu, vme->u.msr.code, &val); | error = emulate_rdmsr(ctx, *pvcpu, vme->u.msr.code, &val); | ||||
if (error != 0) { | if (error != 0) { | ||||
fprintf(stderr, "rdmsr to register %#x on vcpu %d\n", | fprintf(stderr, "rdmsr to register %#x on vcpu %d\n", | ||||
vme->u.msr.code, *pvcpu); | vme->u.msr.code, *pvcpu); | ||||
if (strictmsr) { | if (get_config_bool("x86.strictmsr")) { | ||||
vm_inject_gp(ctx, *pvcpu); | vm_inject_gp(ctx, *pvcpu); | ||||
return (VMEXIT_CONTINUE); | return (VMEXIT_CONTINUE); | ||||
} | } | ||||
} | } | ||||
eax = val; | eax = val; | ||||
error = vm_set_register(ctx, *pvcpu, VM_REG_GUEST_RAX, eax); | error = vm_set_register(ctx, *pvcpu, VM_REG_GUEST_RAX, eax); | ||||
assert(error == 0); | assert(error == 0); | ||||
Show All 9 Lines | |||||
vmexit_wrmsr(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) | vmexit_wrmsr(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) | ||||
{ | { | ||||
int error; | int error; | ||||
error = emulate_wrmsr(ctx, *pvcpu, vme->u.msr.code, vme->u.msr.wval); | error = emulate_wrmsr(ctx, *pvcpu, vme->u.msr.code, vme->u.msr.wval); | ||||
if (error != 0) { | if (error != 0) { | ||||
fprintf(stderr, "wrmsr to register %#x(%#lx) on vcpu %d\n", | fprintf(stderr, "wrmsr to register %#x(%#lx) on vcpu %d\n", | ||||
vme->u.msr.code, vme->u.msr.wval, *pvcpu); | vme->u.msr.code, vme->u.msr.wval, *pvcpu); | ||||
if (strictmsr) { | if (get_config_bool("x86.strictmsr")) { | ||||
vm_inject_gp(ctx, *pvcpu); | vm_inject_gp(ctx, *pvcpu); | ||||
return (VMEXIT_CONTINUE); | return (VMEXIT_CONTINUE); | ||||
} | } | ||||
} | } | ||||
return (VMEXIT_CONTINUE); | return (VMEXIT_CONTINUE); | ||||
} | } | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 122 Lines • ▼ Show 20 Lines | vmexit_mtrap(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) | ||||
assert(vmexit->inst_length == 0); | assert(vmexit->inst_length == 0); | ||||
stats.vmexit_mtrap++; | stats.vmexit_mtrap++; | ||||
#ifdef BHYVE_SNAPSHOT | #ifdef BHYVE_SNAPSHOT | ||||
checkpoint_cpu_suspend(*pvcpu); | checkpoint_cpu_suspend(*pvcpu); | ||||
#endif | #endif | ||||
if (gdb_port != 0) | |||||
gdb_cpu_mtrap(*pvcpu); | gdb_cpu_mtrap(*pvcpu); | ||||
#ifdef BHYVE_SNAPSHOT | #ifdef BHYVE_SNAPSHOT | ||||
checkpoint_cpu_resume(*pvcpu); | checkpoint_cpu_resume(*pvcpu); | ||||
#endif | #endif | ||||
return (VMEXIT_CONTINUE); | return (VMEXIT_CONTINUE); | ||||
} | } | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | while (!CPU_EMPTY(&cpumask)) { | ||||
pthread_cond_wait(&resetcpu_cond, &resetcpu_mtx); | pthread_cond_wait(&resetcpu_cond, &resetcpu_mtx); | ||||
} | } | ||||
pthread_mutex_unlock(&resetcpu_mtx); | pthread_mutex_unlock(&resetcpu_mtx); | ||||
switch (how) { | switch (how) { | ||||
case VM_SUSPEND_RESET: | case VM_SUSPEND_RESET: | ||||
exit(0); | exit(0); | ||||
case VM_SUSPEND_POWEROFF: | case VM_SUSPEND_POWEROFF: | ||||
if (destroy_on_poweroff) | if (get_config_bool_default("destroy_on_poweroff", false)) | ||||
vm_destroy(ctx); | vm_destroy(ctx); | ||||
exit(1); | exit(1); | ||||
case VM_SUSPEND_HALT: | case VM_SUSPEND_HALT: | ||||
exit(2); | exit(2); | ||||
case VM_SUSPEND_TRIPLEFAULT: | case VM_SUSPEND_TRIPLEFAULT: | ||||
exit(3); | exit(3); | ||||
default: | default: | ||||
fprintf(stderr, "vmexit_suspend: invalid reason %d\n", how); | fprintf(stderr, "vmexit_suspend: invalid reason %d\n", how); | ||||
exit(100); | exit(100); | ||||
} | } | ||||
return (0); /* NOTREACHED */ | return (0); /* NOTREACHED */ | ||||
} | } | ||||
static int | static int | ||||
vmexit_debug(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) | vmexit_debug(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) | ||||
{ | { | ||||
#ifdef BHYVE_SNAPSHOT | #ifdef BHYVE_SNAPSHOT | ||||
checkpoint_cpu_suspend(*pvcpu); | checkpoint_cpu_suspend(*pvcpu); | ||||
#endif | #endif | ||||
if (gdb_port != 0) | |||||
gdb_cpu_suspend(*pvcpu); | gdb_cpu_suspend(*pvcpu); | ||||
#ifdef BHYVE_SNAPSHOT | #ifdef BHYVE_SNAPSHOT | ||||
checkpoint_cpu_resume(*pvcpu); | checkpoint_cpu_resume(*pvcpu); | ||||
#endif | #endif | ||||
return (VMEXIT_CONTINUE); | return (VMEXIT_CONTINUE); | ||||
} | } | ||||
static int | static int | ||||
vmexit_breakpoint(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) | vmexit_breakpoint(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) | ||||
{ | { | ||||
if (gdb_port == 0) { | |||||
fprintf(stderr, "vm_loop: unexpected VMEXIT_DEBUG\n"); | |||||
exit(4); | |||||
} | |||||
gdb_cpu_breakpoint(*pvcpu, vmexit); | gdb_cpu_breakpoint(*pvcpu, vmexit); | ||||
return (VMEXIT_CONTINUE); | return (VMEXIT_CONTINUE); | ||||
} | } | ||||
static vmexit_handler_t handler[VM_EXITCODE_MAX] = { | static vmexit_handler_t handler[VM_EXITCODE_MAX] = { | ||||
[VM_EXITCODE_INOUT] = vmexit_inout, | [VM_EXITCODE_INOUT] = vmexit_inout, | ||||
[VM_EXITCODE_INOUT_STR] = vmexit_inout, | [VM_EXITCODE_INOUT_STR] = vmexit_inout, | ||||
[VM_EXITCODE_VMX] = vmexit_vmx, | [VM_EXITCODE_VMX] = vmexit_vmx, | ||||
▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | else | ||||
return (1); | return (1); | ||||
} | } | ||||
void | void | ||||
fbsdrun_set_capabilities(struct vmctx *ctx, int cpu) | fbsdrun_set_capabilities(struct vmctx *ctx, int cpu) | ||||
{ | { | ||||
int err, tmp; | int err, tmp; | ||||
if (fbsdrun_vmexit_on_hlt()) { | if (get_config_bool_default("x86.vmexit_on_hlt", false)) { | ||||
err = vm_get_capability(ctx, cpu, VM_CAP_HALT_EXIT, &tmp); | err = vm_get_capability(ctx, cpu, VM_CAP_HALT_EXIT, &tmp); | ||||
if (err < 0) { | if (err < 0) { | ||||
fprintf(stderr, "VM exit on HLT not supported\n"); | fprintf(stderr, "VM exit on HLT not supported\n"); | ||||
exit(4); | exit(4); | ||||
} | } | ||||
vm_set_capability(ctx, cpu, VM_CAP_HALT_EXIT, 1); | vm_set_capability(ctx, cpu, VM_CAP_HALT_EXIT, 1); | ||||
if (cpu == BSP) | if (cpu == BSP) | ||||
handler[VM_EXITCODE_HLT] = vmexit_hlt; | handler[VM_EXITCODE_HLT] = vmexit_hlt; | ||||
} | } | ||||
if (fbsdrun_vmexit_on_pause()) { | if (get_config_bool_default("x86.vmexit_on_pause", false)) { | ||||
/* | /* | ||||
* pause exit support required for this mode | * pause exit support required for this mode | ||||
*/ | */ | ||||
err = vm_get_capability(ctx, cpu, VM_CAP_PAUSE_EXIT, &tmp); | err = vm_get_capability(ctx, cpu, VM_CAP_PAUSE_EXIT, &tmp); | ||||
if (err < 0) { | if (err < 0) { | ||||
fprintf(stderr, | fprintf(stderr, | ||||
"SMP mux requested, no pause support\n"); | "SMP mux requested, no pause support\n"); | ||||
exit(4); | exit(4); | ||||
} | } | ||||
vm_set_capability(ctx, cpu, VM_CAP_PAUSE_EXIT, 1); | vm_set_capability(ctx, cpu, VM_CAP_PAUSE_EXIT, 1); | ||||
if (cpu == BSP) | if (cpu == BSP) | ||||
handler[VM_EXITCODE_PAUSE] = vmexit_pause; | handler[VM_EXITCODE_PAUSE] = vmexit_pause; | ||||
} | } | ||||
if (x2apic_mode) | if (get_config_bool_default("x86.x2apic", false)) | ||||
err = vm_set_x2apic_state(ctx, cpu, X2APIC_ENABLED); | err = vm_set_x2apic_state(ctx, cpu, X2APIC_ENABLED); | ||||
else | else | ||||
err = vm_set_x2apic_state(ctx, cpu, X2APIC_DISABLED); | err = vm_set_x2apic_state(ctx, cpu, X2APIC_DISABLED); | ||||
if (err) { | if (err) { | ||||
fprintf(stderr, "Unable to set x2apic state (%d)\n", err); | fprintf(stderr, "Unable to set x2apic state (%d)\n", err); | ||||
exit(4); | exit(4); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | spinup_vcpu(struct vmctx *ctx, int vcpu) | ||||
fbsdrun_set_capabilities(ctx, vcpu); | fbsdrun_set_capabilities(ctx, vcpu); | ||||
error = vm_set_capability(ctx, vcpu, VM_CAP_UNRESTRICTED_GUEST, 1); | error = vm_set_capability(ctx, vcpu, VM_CAP_UNRESTRICTED_GUEST, 1); | ||||
assert(error == 0); | assert(error == 0); | ||||
fbsdrun_addcpu(ctx, BSP, vcpu, rip); | fbsdrun_addcpu(ctx, BSP, vcpu, rip); | ||||
} | } | ||||
static bool | |||||
parse_config_option(const char *option) | |||||
{ | |||||
const char *value; | |||||
char *path; | |||||
value = strchr(option, '='); | |||||
if (value == NULL || value[1] == '\0') | |||||
return (false); | |||||
path = strndup(option, value - option); | |||||
if (path == NULL) | |||||
err(4, "Failed to allocate memory"); | |||||
set_config_value(path, value + 1); | |||||
return (true); | |||||
} | |||||
static void | |||||
parse_simple_config_file(const char *path) | |||||
{ | |||||
FILE *fp; | |||||
char *line, *cp; | |||||
size_t linecap; | |||||
unsigned int lineno; | |||||
fp = fopen(path, "r"); | |||||
if (fp == NULL) | |||||
err(4, "Failed to open configuration file %s", path); | |||||
line = NULL; | |||||
linecap = 0; | |||||
lineno = 1; | |||||
for (lineno = 1; getline(&line, &linecap, fp) > 0; lineno++) { | |||||
if (*line == '#' || *line == '\n') | |||||
continue; | |||||
cp = strchr(line, '\n'); | |||||
if (cp != NULL) | |||||
*cp = '\0'; | |||||
if (!parse_config_option(line)) | |||||
errx(4, "%s line %u: invalid config option '%s'", path, | |||||
lineno, line); | |||||
} | |||||
free(line); | |||||
fclose(fp); | |||||
} | |||||
static void | |||||
set_defaults(void) | |||||
{ | |||||
set_config_bool("acpi_tables", false); | |||||
set_config_value("memory.size", "256M"); | |||||
set_config_bool("x86.strictmsr", true); | |||||
} | |||||
int | int | ||||
main(int argc, char *argv[]) | main(int argc, char *argv[]) | ||||
{ | { | ||||
int c, error, err; | int c, error, err; | ||||
int max_vcpus, mptgen, memflags; | int max_vcpus, memflags; | ||||
int rtc_localtime; | |||||
bool gdb_stop; | |||||
struct vmctx *ctx; | struct vmctx *ctx; | ||||
uint64_t rip; | uint64_t rip; | ||||
size_t memsize; | size_t memsize; | ||||
const char *value, *vmname; | |||||
char *optstr; | char *optstr; | ||||
#ifdef BHYVE_SNAPSHOT | #ifdef BHYVE_SNAPSHOT | ||||
char *restore_file; | char *restore_file; | ||||
struct restore_state rstate; | struct restore_state rstate; | ||||
int vcpu; | int vcpu; | ||||
restore_file = NULL; | restore_file = NULL; | ||||
#endif | #endif | ||||
init_config(); | |||||
set_defaults(); | |||||
progname = basename(argv[0]); | progname = basename(argv[0]); | ||||
gdb_stop = false; | |||||
guest_ncpus = 1; | |||||
sockets = cores = threads = 1; | |||||
maxcpus = 0; | |||||
memsize = 256 * MB; | |||||
mptgen = 1; | |||||
rtc_localtime = 1; | |||||
memflags = 0; | |||||
#ifdef BHYVE_SNAPSHOT | #ifdef BHYVE_SNAPSHOT | ||||
optstr = "aehuwxACDHIPSWYp:G:c:s:m:l:U:r:"; | optstr = "aehuwxACDHIPSWYk:o:p:G:c:s:m:l:U:r:"; | ||||
#else | #else | ||||
optstr = "aehuwxACDHIPSWYp:G:c:s:m:l:U:"; | optstr = "aehuwxACDHIPSWYk:o:p:G:c:s:m:l:U:"; | ||||
#endif | #endif | ||||
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; | set_config_bool("x86.x2apic", false); | ||||
break; | break; | ||||
case 'A': | case 'A': | ||||
acpi = 1; | set_config_bool("acpi_tables", true); | ||||
break; | break; | ||||
case 'D': | case 'D': | ||||
destroy_on_poweroff = 1; | set_config_bool("destroy_on_poweroff", true); | ||||
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': | ||||
if (topology_parse(optarg) != 0) { | if (topology_parse(optarg) != 0) { | ||||
errx(EX_USAGE, "invalid cpu topology " | errx(EX_USAGE, "invalid cpu topology " | ||||
"'%s'", optarg); | "'%s'", optarg); | ||||
} | } | ||||
break; | break; | ||||
case 'C': | case 'C': | ||||
memflags |= VM_MEM_F_INCORE; | set_config_bool("memory.guest_in_core", true); | ||||
break; | break; | ||||
case 'G': | case 'G': | ||||
if (optarg[0] == 'w') { | if (optarg[0] == 'w') { | ||||
gdb_stop = true; | set_config_bool("gdb.wait", true); | ||||
optarg++; | optarg++; | ||||
} | } | ||||
gdb_port = atoi(optarg); | set_config_value("gdb.port", optarg); | ||||
break; | break; | ||||
case 'k': | |||||
parse_simple_config_file(optarg); | |||||
break; | |||||
case 'l': | case 'l': | ||||
if (strncmp(optarg, "help", strlen(optarg)) == 0) { | if (strncmp(optarg, "help", strlen(optarg)) == 0) { | ||||
lpc_print_supported_devices(); | lpc_print_supported_devices(); | ||||
exit(0); | exit(0); | ||||
} else if (lpc_device_parse(optarg) != 0) { | } else if (lpc_device_parse(optarg) != 0) { | ||||
errx(EX_USAGE, "invalid lpc device " | errx(EX_USAGE, "invalid lpc device " | ||||
"configuration '%s'", optarg); | "configuration '%s'", optarg); | ||||
} | } | ||||
break; | break; | ||||
#ifdef BHYVE_SNAPSHOT | #ifdef BHYVE_SNAPSHOT | ||||
case 'r': | case 'r': | ||||
restore_file = optarg; | restore_file = optarg; | ||||
break; | break; | ||||
#endif | #endif | ||||
case 's': | case 's': | ||||
if (strncmp(optarg, "help", strlen(optarg)) == 0) { | if (strncmp(optarg, "help", strlen(optarg)) == 0) { | ||||
pci_print_supported_devices(); | pci_print_supported_devices(); | ||||
exit(0); | exit(0); | ||||
} else if (pci_parse_slot(optarg) != 0) | } else if (pci_parse_slot(optarg) != 0) | ||||
exit(4); | exit(4); | ||||
else | else | ||||
break; | break; | ||||
case 'S': | case 'S': | ||||
memflags |= VM_MEM_F_WIRED; | set_config_bool("memory.wired", true); | ||||
break; | break; | ||||
case 'm': | case 'm': | ||||
error = vm_parse_memsize(optarg, &memsize); | set_config_value("memory.size", optarg); | ||||
if (error) | |||||
errx(EX_USAGE, "invalid memsize '%s'", optarg); | |||||
break; | break; | ||||
case 'o': | |||||
if (!parse_config_option(optarg)) | |||||
errx(EX_USAGE, "invalid configuration option '%s'", optarg); | |||||
break; | |||||
case 'H': | case 'H': | ||||
guest_vmexit_on_hlt = 1; | set_config_bool("x86.vmexit_on_hlt", true); | ||||
break; | break; | ||||
case 'I': | case 'I': | ||||
/* | /* | ||||
* The "-I" option was used to add an ioapic to the | * The "-I" option was used to add an ioapic to the | ||||
* virtual machine. | * virtual machine. | ||||
* | * | ||||
* An ioapic is now provided unconditionally for each | * An ioapic is now provided unconditionally for each | ||||
* virtual machine and this option is now deprecated. | * virtual machine and this option is now deprecated. | ||||
*/ | */ | ||||
break; | break; | ||||
case 'P': | case 'P': | ||||
guest_vmexit_on_pause = 1; | set_config_bool("x86.vmexit_on_pause", true); | ||||
break; | break; | ||||
case 'e': | case 'e': | ||||
strictio = 1; | set_config_bool("x86.strictio", true); | ||||
break; | break; | ||||
case 'u': | case 'u': | ||||
rtc_localtime = 0; | set_config_bool("rtc.use_localtime", false); | ||||
break; | break; | ||||
case 'U': | case 'U': | ||||
guest_uuid_str = optarg; | set_config_value("uuid", optarg); | ||||
break; | break; | ||||
case 'w': | case 'w': | ||||
strictmsr = 0; | set_config_bool("x86.strictmsr", false); | ||||
break; | break; | ||||
case 'W': | case 'W': | ||||
virtio_msix = 0; | set_config_bool("virtio_msix", false); | ||||
break; | break; | ||||
case 'x': | case 'x': | ||||
x2apic_mode = 1; | set_config_bool("x86.x2apic", true); | ||||
break; | break; | ||||
case 'Y': | case 'Y': | ||||
mptgen = 0; | set_config_bool("x86.mptable", false); | ||||
break; | break; | ||||
case 'h': | case 'h': | ||||
usage(0); | usage(0); | ||||
default: | default: | ||||
usage(1); | usage(1); | ||||
} | } | ||||
} | } | ||||
argc -= optind; | argc -= optind; | ||||
argv += optind; | argv += optind; | ||||
#ifdef BHYVE_SNAPSHOT | if (argc > 1) | ||||
if (argc > 1 || (argc == 0 && restore_file == NULL)) | |||||
usage(1); | usage(1); | ||||
#ifdef BHYVE_SNAPSHOT | |||||
if (restore_file != NULL) { | if (restore_file != NULL) { | ||||
error = load_restore_file(restore_file, &rstate); | error = load_restore_file(restore_file, &rstate); | ||||
if (error) { | if (error) { | ||||
fprintf(stderr, "Failed to read checkpoint info from " | fprintf(stderr, "Failed to read checkpoint info from " | ||||
"file: '%s'.\n", restore_file); | "file: '%s'.\n", restore_file); | ||||
exit(1); | exit(1); | ||||
} | } | ||||
vmname = lookup_vmname(&rstate); | |||||
if (vmname != NULL) | |||||
set_config_value("name", vmname); | |||||
} | } | ||||
#endif | |||||
wanpengqian_gmail.com: Setting config.dump=0 still dump the config and exit. | |||||
Done Inline ActionsHah, so it does. It is debatable if this feature should even make it into the tree, but I suppose it it does it should be a real boolean. jhb: Hah, so it does. It is debatable if this feature should even make it into the tree, but I… | |||||
if (argc == 1) { | if (argc == 1) | ||||
vmname = argv[0]; | set_config_value("name", argv[0]); | ||||
} else { | |||||
vmname = lookup_vmname(&rstate); | vmname = get_config_value("name"); | ||||
if (vmname == NULL) { | if (vmname == NULL) | ||||
fprintf(stderr, "Cannot find VM name in restore file. " | usage(1); | ||||
"Please specify one.\n"); | |||||
if (get_config_bool_default("config.dump", false)) { | |||||
dump_config(); | |||||
exit(1); | exit(1); | ||||
} | } | ||||
} | |||||
#else | |||||
if (argc != 1) | |||||
usage(1); | |||||
vmname = argv[0]; | calc_topolopgy(); | ||||
#endif | build_vcpumaps(); | ||||
value = get_config_value("memory.size"); | |||||
error = vm_parse_memsize(value, &memsize); | |||||
if (error) | |||||
errx(EX_USAGE, "invalid memsize '%s'", value); | |||||
ctx = do_open(vmname); | ctx = do_open(vmname); | ||||
#ifdef BHYVE_SNAPSHOT | #ifdef BHYVE_SNAPSHOT | ||||
if (restore_file != NULL) { | if (restore_file != NULL) { | ||||
guest_ncpus = lookup_guest_ncpus(&rstate); | guest_ncpus = lookup_guest_ncpus(&rstate); | ||||
memflags = lookup_memflags(&rstate); | memflags = lookup_memflags(&rstate); | ||||
memsize = lookup_memsize(&rstate); | memsize = lookup_memsize(&rstate); | ||||
} | } | ||||
if (guest_ncpus < 1) { | if (guest_ncpus < 1) { | ||||
fprintf(stderr, "Invalid guest vCPUs (%d)\n", guest_ncpus); | fprintf(stderr, "Invalid guest vCPUs (%d)\n", guest_ncpus); | ||||
exit(1); | exit(1); | ||||
} | } | ||||
#endif | #endif | ||||
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(4); | exit(4); | ||||
} | } | ||||
fbsdrun_set_capabilities(ctx, BSP); | fbsdrun_set_capabilities(ctx, BSP); | ||||
memflags = 0; | |||||
if (get_config_bool_default("memory.wired", false)) | |||||
memflags |= VM_MEM_F_WIRED; | |||||
if (get_config_bool_default("memory.guest_in_core", false)) | |||||
memflags |= VM_MEM_F_INCORE; | |||||
vm_set_memflags(ctx, memflags); | vm_set_memflags(ctx, memflags); | ||||
err = vm_setup_memory(ctx, memsize, VM_MMAP_ALL); | err = vm_setup_memory(ctx, memsize, VM_MMAP_ALL); | ||||
if (err) { | if (err) { | ||||
fprintf(stderr, "Unable to setup memory (%d)\n", errno); | fprintf(stderr, "Unable to setup memory (%d)\n", errno); | ||||
exit(4); | exit(4); | ||||
} | } | ||||
error = init_msr(); | error = init_msr(); | ||||
if (error) { | if (error) { | ||||
fprintf(stderr, "init_msr error %d", error); | fprintf(stderr, "init_msr error %d", error); | ||||
exit(4); | exit(4); | ||||
} | } | ||||
init_mem(); | init_mem(); | ||||
init_inout(); | init_inout(); | ||||
kernemu_dev_init(); | kernemu_dev_init(); | ||||
init_bootrom(ctx); | init_bootrom(ctx); | ||||
atkbdc_init(ctx); | atkbdc_init(ctx); | ||||
pci_irq_init(ctx); | pci_irq_init(ctx); | ||||
ioapic_init(ctx); | ioapic_init(ctx); | ||||
rtc_init(ctx, rtc_localtime); | rtc_init(ctx); | ||||
sci_init(ctx); | sci_init(ctx); | ||||
/* | /* | ||||
* Exit if a device emulation finds an error in its initilization | * Exit if a device emulation finds an error in its initilization | ||||
*/ | */ | ||||
if (init_pci(ctx) != 0) { | if (init_pci(ctx) != 0) { | ||||
perror("device emulation initialization error"); | perror("device emulation initialization error"); | ||||
exit(4); | exit(4); | ||||
} | } | ||||
/* | /* | ||||
* Initialize after PCI, to allow a bootrom file to reserve the high | * Initialize after PCI, to allow a bootrom file to reserve the high | ||||
* region. | * region. | ||||
*/ | */ | ||||
if (acpi) | if (get_config_bool("acpi_tables")) | ||||
vmgenc_init(ctx); | vmgenc_init(ctx); | ||||
if (gdb_port != 0) | value = get_config_value("gdb.port"); | ||||
init_gdb(ctx, gdb_port, gdb_stop); | if (value != NULL) | ||||
init_gdb(ctx, atoi(value), get_config_bool_default("gdb.wait", | |||||
false)); | |||||
if (lpc_bootrom()) { | if (lpc_bootrom()) { | ||||
if (vm_set_capability(ctx, BSP, VM_CAP_UNRESTRICTED_GUEST, 1)) { | if (vm_set_capability(ctx, BSP, VM_CAP_UNRESTRICTED_GUEST, 1)) { | ||||
fprintf(stderr, "ROM boot failed: unrestricted guest " | fprintf(stderr, "ROM boot failed: unrestricted guest " | ||||
"capability not available\n"); | "capability not available\n"); | ||||
exit(4); | exit(4); | ||||
} | } | ||||
error = vcpu_reset(ctx, BSP); | error = vcpu_reset(ctx, BSP); | ||||
Show All 35 Lines | |||||
#endif | #endif | ||||
error = vm_get_register(ctx, BSP, VM_REG_GUEST_RIP, &rip); | error = vm_get_register(ctx, BSP, VM_REG_GUEST_RIP, &rip); | ||||
assert(error == 0); | assert(error == 0); | ||||
/* | /* | ||||
* build the guest tables, MP etc. | * build the guest tables, MP etc. | ||||
*/ | */ | ||||
if (mptgen) { | if (get_config_bool_default("x86.mptable", true)) { | ||||
error = mptable_build(ctx, guest_ncpus); | error = mptable_build(ctx, guest_ncpus); | ||||
if (error) { | if (error) { | ||||
perror("error to build the guest tables"); | perror("error to build the guest tables"); | ||||
exit(4); | exit(4); | ||||
} | } | ||||
} | } | ||||
error = smbios_build(ctx); | error = smbios_build(ctx); | ||||
assert(error == 0); | assert(error == 0); | ||||
if (acpi) { | if (get_config_bool("acpi_tables")) { | ||||
error = acpi_build(ctx, guest_ncpus); | error = acpi_build(ctx, guest_ncpus); | ||||
assert(error == 0); | assert(error == 0); | ||||
} | } | ||||
if (lpc_bootrom()) | if (lpc_bootrom()) | ||||
fwctl_init(); | fwctl_init(); | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 56 Lines • Show Last 20 Lines |
Setting config.dump=0 still dump the config and exit.