diff --git a/usr.sbin/bhyve/aarch64/bhyverun_machdep.c b/usr.sbin/bhyve/aarch64/bhyverun_machdep.c --- a/usr.sbin/bhyve/aarch64/bhyverun_machdep.c +++ b/usr.sbin/bhyve/aarch64/bhyverun_machdep.c @@ -107,6 +107,7 @@ " -G: start a debug server\n" " -h: help\n" " -k: key=value flat config file\n" + " -M: monitor mode\n" " -m: memory size\n" " -o: set config 'var' to 'value'\n" " -p: pin 'vcpu' to 'hostcpu'\n" @@ -125,7 +126,7 @@ const char *optstr; int c; - optstr = "hCDSWk:f:o:p:G:c:s:m:U:"; + optstr = "hCDMSWk:f:o:p:G:c:s:m:U:"; while ((c = getopt(argc, argv, optstr)) != -1) { switch (c) { case 'c': @@ -149,6 +150,9 @@ case 'm': set_config_value("memory.size", optarg); break; + case 'M': + set_config_bool("monitor", true); + break; case 'o': if (!bhyve_parse_config_option(optarg)) { errx(EX_USAGE, diff --git a/usr.sbin/bhyve/amd64/bhyverun_machdep.c b/usr.sbin/bhyve/amd64/bhyverun_machdep.c --- a/usr.sbin/bhyve/amd64/bhyverun_machdep.c +++ b/usr.sbin/bhyve/amd64/bhyverun_machdep.c @@ -90,6 +90,7 @@ " -k: key=value flat config file\n" " -K: PS2 keyboard layout\n" " -l: LPC device configuration\n" + " -M: monitor mode\n" " -m: memory size\n" " -n: NUMA domain specification\n" " -o: set config 'var' to 'value'\n" @@ -118,9 +119,9 @@ int c; #ifdef BHYVE_SNAPSHOT - optstr = "aehuwxACDHIPSWYk:f:o:p:G:c:s:m:n:l:K:U:r:"; + optstr = "aehuwxACDHIMPSWYk:f:o:p:G:c:s:m:n:l:K:U:r:"; #else - optstr = "aehuwxACDHIPSWYk:f:o:p:G:c:s:m:n:l:K:U:"; + optstr = "aehuwxACDHIMPSWYk:f:o:p:G:c:s:m:n:l:K:U:"; #endif while ((c = getopt(argc, argv, optstr)) != -1) { switch (c) { @@ -195,6 +196,9 @@ case 'm': set_config_value("memory.size", optarg); break; + case 'M': + set_config_bool("monitor", true); + break; case 'n': if (bhyve_numa_parse(optarg) != 0) errx(EX_USAGE, diff --git a/usr.sbin/bhyve/bhyve.8 b/usr.sbin/bhyve/bhyve.8 --- a/usr.sbin/bhyve/bhyve.8 +++ b/usr.sbin/bhyve/bhyve.8 @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd October 28, 2025 +.Dd November 13, 2025 .Dt BHYVE 8 .Os .Sh NAME @@ -33,7 +33,7 @@ .Nd "run a guest operating system inside a virtual machine" .Sh SYNOPSIS .Nm -.Op Fl aCDeHhPSuWwxY +.Op Fl aCDeHhMPSuWwxY .Oo .Sm off .Fl c\~ @@ -273,6 +273,13 @@ to indicate a multiple of kilobytes, megabytes, gigabytes, or terabytes. If no suffix is given, the value is assumed to be in megabytes. The default is 256M. +.It Fl M +Run the VM in +.Ql monitor +mode. +In this mode, a guest reboot does not cause the bhyve process to exit. +Instead, bhyve will restart the VM. +Once the bhyve process exits or is killed, the VM will be destroyed automatically. .Pp .It Fl n Ar id Ns Cm \&, Ns Ar size Ns Cm \&, Ns Ar cpus Ns Op Cm \&, Ns Ar domain_policy Configure guest NUMA domains. diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c --- a/usr.sbin/bhyve/bhyverun.c +++ b/usr.sbin/bhyve/bhyverun.c @@ -41,6 +41,7 @@ #ifdef BHYVE_SNAPSHOT #include #endif +#include #include @@ -688,8 +689,10 @@ { struct vmctx *ctx; int error; - bool romboot; + bool romboot, monitor; + int flags; + monitor = get_config_bool_default("monitor", false); romboot = bootrom_boot(); /* @@ -702,7 +705,10 @@ err(4, "vm_openf"); if (!romboot) errx(4, "no bootrom was configured"); - ctx = vm_openf(vmname, VMMAPI_OPEN_CREATE); + flags = VMMAPI_OPEN_CREATE; + if (monitor) + flags |= VMMAPI_OPEN_CREATE_DESTROY_ON_CLOSE; + ctx = vm_openf(vmname, flags); if (ctx == NULL) err(4, "vm_openf"); } @@ -792,7 +798,7 @@ int main(int argc, char *argv[]) { - int error; + int error, status; int max_vcpus, memflags; struct vcpu *bsp; struct vmctx *ctx; @@ -801,6 +807,7 @@ #ifdef BHYVE_SNAPSHOT struct restore_state rstate; #endif + pid_t forkpid; bhyve_init_config(); bhyve_optparse(argc, argv); @@ -859,6 +866,58 @@ } #endif + calc_mem_affinity(memsize); + 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); + error = vm_setup_memory_domains(ctx, VM_MMAP_ALL, guest_domains, + guest_ndomains); + if (error) { + fprintf(stderr, "Unable to setup memory (%d)\n", errno); + exit(BHYVE_EXIT_ERROR); + } + + set_vcpu_affinities(); + init_mem(guest_ncpus); + init_bootrom(ctx); + + if (get_config_bool_default("monitor", false)) { + while (1) { + forkpid = fork(); + if (forkpid == -1) { + EPRINTLN("Monitor mode fork failed: %s", + strerror(errno)); + exit(BHYVE_EXIT_ERROR); + } + if (forkpid == 0) + break; + while ((error = waitpid(forkpid, &status, 0)) == -1 && errno == EINTR) + ; + if (error == -1) { + EPRINTLN("Monitor mode wait failed: %s", + strerror(errno)); + exit(BHYVE_EXIT_ERROR); + } + if (WIFSIGNALED(status)) { + EPRINTLN("Child process was killed by signal %d", + WTERMSIG(status)); + exit(BHYVE_EXIT_ERROR); + } else { + status = WEXITSTATUS(status); + if (status != BHYVE_EXIT_RESET) + exit(status); + } + if (vm_reinit(ctx) != 0) { + EPRINTLN("Monitor mode reinit failed: %s", + strerror(errno)); + exit(BHYVE_EXIT_ERROR); + } + } + } + bsp = vm_vcpu_open(ctx, BSP); max_vcpus = num_vcpus_allowed(ctx, bsp); if (guest_ncpus > max_vcpus) { @@ -880,23 +939,6 @@ vcpu_info[vcpuid].vcpu = vm_vcpu_open(ctx, vcpuid); } - calc_mem_affinity(memsize); - 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); - error = vm_setup_memory_domains(ctx, VM_MMAP_ALL, guest_domains, - guest_ndomains); - if (error) { - fprintf(stderr, "Unable to setup memory (%d)\n", errno); - exit(4); - } - - set_vcpu_affinities(); - init_mem(guest_ncpus); - init_bootrom(ctx); if (bhyve_init_platform(ctx, bsp) != 0) exit(BHYVE_EXIT_ERROR); diff --git a/usr.sbin/bhyve/riscv/bhyverun_machdep.c b/usr.sbin/bhyve/riscv/bhyverun_machdep.c --- a/usr.sbin/bhyve/riscv/bhyverun_machdep.c +++ b/usr.sbin/bhyve/riscv/bhyverun_machdep.c @@ -101,6 +101,7 @@ " -D: destroy on power-off\n" " -h: help\n" " -k: key=value flat config file\n" + " -M: monitor mode\n" " -m: memory size\n" " -o: set config 'var' to 'value'\n" " -p: pin 'vcpu' to 'hostcpu'\n" @@ -119,7 +120,7 @@ const char *optstr; int c; - optstr = "hCDSWk:f:o:p:c:s:m:U:"; + optstr = "hCDMSWk:f:o:p:c:s:m:U:"; while ((c = getopt(argc, argv, optstr)) != -1) { switch (c) { case 'c': @@ -137,6 +138,9 @@ case 'k': bhyve_parse_simple_config_file(optarg); break; + case 'M': + set_config_bool("monitor", true); + break; case 'm': set_config_value("memory.size", optarg); break;