diff --git a/usr.sbin/bhyvectl/Makefile b/usr.sbin/bhyvectl/Makefile --- a/usr.sbin/bhyvectl/Makefile +++ b/usr.sbin/bhyvectl/Makefile @@ -14,8 +14,9 @@ CFLAGS+= -I${.CURDIR} -I${SRCTOP}/sys/amd64/vmm +LIBADD+= bhyve nv + .if ${MK_BHYVE_SNAPSHOT} != "no" -LIBADD+= nv CFLAGS+= -DBHYVE_SNAPSHOT # usr.sbin/bhyve/snapshot.h needs ucl header diff --git a/usr.sbin/bhyvectl/bhyvectl.8 b/usr.sbin/bhyvectl/bhyvectl.8 --- a/usr.sbin/bhyvectl/bhyvectl.8 +++ b/usr.sbin/bhyvectl/bhyvectl.8 @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 8, 2025 +.Dd November 30, 2025 .Dt BHYVECTL 8 .Os .Sh NAME @@ -42,6 +42,8 @@ .Op Fl -force-poweroff .Op Fl -checkpoint= Ns Ar .Op Fl -suspend= Ns Ar +.Op Fl -pci-add= Ns Ar emulation Ns Op Cm \&, Ns Ar conf +.Op Fl -pci-remove= Ns Ar slot .Sh DESCRIPTION The .Nm @@ -77,6 +79,32 @@ .Fl -checkpoint . The virtual machine will terminate after the snapshot has been saved. +.It Fl -pci-add= Ns Ar emulation Ns Op Cm \&, Ns Ar conf +Add an emulated PCI device to a running virtual machine. +The +.Ar emulation +and +.Ar conf +arguments follow the same format as the +.Xr bhyve 8 +.Fl s +flag. +See the +.Xr bhyve 8 +.Sx "PCI EMULATION" +section for available options for the +.Ar emulation +and +.Ar conf +arguments. +.It Fl -pci-remove= Ns Ar slot +Request removal of a previously added emulated PCI device. +See the +.Xr bhyve 8 +.Sx "IPC COMMANDS" +section for available formats for the +.Ar slot +argument .El .Pp .Em Note : @@ -98,6 +126,14 @@ .Pp Running VMs will be visible in .Pa /dev/vmm/ . +.Pp +Add an 'e1000' PCI device to the 'fbsd16' VM: +.Pp +.Dl bhyvectl --vm=fbsd10 --pci-add=e1000,tap0 +.Pp +Remove a previously hotplugged PCI device from the 'fbsd16' VM: +.Pp +.Dl bhyvectl --vm=fbsd10 --pci-remove=0:3:0 .Sh COMPATIBILITY The snapshot file format is not yet stable and is subject to future changes. Backwards compatibility support for the current snapshot file format is not diff --git a/usr.sbin/bhyvectl/bhyvectl.c b/usr.sbin/bhyvectl/bhyvectl.c --- a/usr.sbin/bhyvectl/bhyvectl.c +++ b/usr.sbin/bhyvectl/bhyvectl.c @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -46,6 +47,7 @@ #include #include +#include #include #include #include @@ -86,6 +88,8 @@ SET_CHECKPOINT_FILE, SET_SUSPEND_FILE, #endif + PCI_ADD, + PCI_REMOVE, OPT_LAST, }; @@ -134,6 +138,8 @@ { "get-debug-cpus", NO_ARG, &get_debug_cpus, 1 }, { "get-suspended-cpus", NO_ARG, &get_suspended_cpus, 1 }, { "get-cpu-topology", NO_ARG, &get_cpu_topology, 1 }, + { "pci-add", REQ_ARG, 0, PCI_ADD }, + { "pci-remove", REQ_ARG, 0, PCI_REMOVE }, #ifdef BHYVE_SNAPSHOT { "checkpoint", REQ_ARG, 0, SET_CHECKPOINT_FILE}, { "suspend", REQ_ARG, 0, SET_SUSPEND_FILE}, @@ -302,6 +308,57 @@ return (err); } +static int +pci_request_add(const char *vmname, const char *opt) +{ + nvlist_t *nvl; + char *device, *config, *str, *cp; + + nvl = nvlist_create(0); + nvlist_add_string(nvl, "cmd", "pci_add"); + + config = NULL; + str = strdup(opt); + device = str; + if ((cp = strchr(str, ',')) != NULL) { + *cp = '\0'; + config = cp + 1; + } + + if (pci_parse_config(nvl, device, config) != 0) { + fprintf(stderr, "%s: failed to parse device config: %s\n", + __func__, nvlist_get_string(nvl, "error")); + free(str); + return (-1); + } + + if (pci_init_fds(nvl, device) != 0) { + fprintf(stderr, + "%s: failed to initialize device descriptors: %s\n", + __func__, nvlist_get_string(nvl, "error")); + free(str); + return (-1); + } + + nvlist_add_string(nvl, "device", device); + assert(nvlist_error(nvl) == 0); + free(str); + + return (ipc_send_message(vmname, nvl)); +} + +static int +pci_request_remove(const char *vmname, const char *bdf) +{ + nvlist_t *nvl; + + nvl = nvlist_create(0); + nvlist_add_string(nvl, "cmd", "pci_remove"); + nvlist_add_string(nvl, "slot", bdf); + + return (ipc_send_message(vmname, nvl)); +} + #ifdef BHYVE_SNAPSHOT static int open_directory(const char *file) @@ -351,12 +408,16 @@ #ifdef BHYVE_SNAPSHOT char *checkpoint_file = NULL; #endif + const char *pci_devname; + const char *pci_bdf; opts = setup_options(); action_opts = 0; vcpuid = 0; vmname = NULL; + pci_devname = NULL; + pci_bdf = NULL; progname = basename(argv[0]); while ((ch = getopt_long(argc, argv, "", opts, NULL)) != -1) { @@ -395,6 +456,12 @@ vm_suspend_opt = (ch == SET_SUSPEND_FILE); break; #endif + case PCI_ADD: + pci_devname = optarg; + break; + case PCI_REMOVE: + pci_bdf = optarg; + break; default: usage(opts); } @@ -540,6 +607,12 @@ error = snapshot_request(vmname, checkpoint_file, vm_suspend_opt); #endif + if (!error && pci_devname != NULL) + error = pci_request_add(vmname, pci_devname); + + if (!error && pci_bdf != NULL) + error = pci_request_remove(vmname, pci_bdf); + if (error) printf("errno = %d\n", errno);