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.