diff --git a/man/systemd-vmspawn.xml b/man/systemd-vmspawn.xml index 129f5ba141..9feb7407ca 100644 --- a/man/systemd-vmspawn.xml +++ b/man/systemd-vmspawn.xml @@ -712,6 +712,20 @@ + + + + Configures the transport to use for the VM console. Takes one of + virtio or serial. Defaults to virtio. + virtio uses a virtio-serial device, which appears as + /dev/hvc0 in the VM. serial uses a regular serial port, + which appears as /dev/ttyS0 (or /dev/ttyAMA0 on ARM) in the VM. This option only has an effect in + , , and + modes. + + + + diff --git a/src/vmspawn/vmspawn-settings.c b/src/vmspawn/vmspawn-settings.c index 776b590252..56a07b3f6f 100644 --- a/src/vmspawn/vmspawn-settings.c +++ b/src/vmspawn/vmspawn-settings.c @@ -37,3 +37,10 @@ static const char *const console_mode_table[_CONSOLE_MODE_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(console_mode, ConsoleMode); + +static const char *const console_transport_table[_CONSOLE_TRANSPORT_MAX] = { + [CONSOLE_TRANSPORT_VIRTIO] = "virtio", + [CONSOLE_TRANSPORT_SERIAL] = "serial", +}; + +DEFINE_STRING_TABLE_LOOKUP(console_transport, ConsoleTransport); diff --git a/src/vmspawn/vmspawn-settings.h b/src/vmspawn/vmspawn-settings.h index b897f148e1..83d2872535 100644 --- a/src/vmspawn/vmspawn-settings.h +++ b/src/vmspawn/vmspawn-settings.h @@ -42,6 +42,13 @@ typedef enum ConsoleMode { _CONSOLE_MODE_INVALID = -EINVAL, } ConsoleMode; +typedef enum ConsoleTransport { + CONSOLE_TRANSPORT_VIRTIO, /* virtio-serial (hvc0) */ + CONSOLE_TRANSPORT_SERIAL, /* regular serial port (ttyS0/ttyAMA0) */ + _CONSOLE_TRANSPORT_MAX, + _CONSOLE_TRANSPORT_INVALID = -EINVAL, +} ConsoleTransport; + typedef enum SettingsMask { SETTING_START_MODE = UINT64_C(1) << 0, SETTING_MACHINE_ID = UINT64_C(1) << 6, @@ -53,5 +60,6 @@ typedef enum SettingsMask { } SettingsMask; DECLARE_STRING_TABLE_LOOKUP(console_mode, ConsoleMode); +DECLARE_STRING_TABLE_LOOKUP(console_transport, ConsoleTransport); DECLARE_STRING_TABLE_LOOKUP(disk_type, DiskType); DECLARE_STRING_TABLE_LOOKUP(image_format, ImageFormat); diff --git a/src/vmspawn/vmspawn-util.h b/src/vmspawn/vmspawn-util.h index 90efd93661..9fec6641aa 100644 --- a/src/vmspawn/vmspawn-util.h +++ b/src/vmspawn/vmspawn-util.h @@ -49,6 +49,12 @@ # define QEMU_MACHINE_TYPE "none" #endif +#if defined(__arm__) || defined(__aarch64__) +# define QEMU_SERIAL_CONSOLE_NAME "ttyAMA0" +#else +# define QEMU_SERIAL_CONSOLE_NAME "ttyS0" +#endif + typedef struct OvmfConfig { char *path; char *format; diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index 1891ac8bad..85165e1af7 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -137,6 +137,7 @@ static int arg_tpm = -1; static char *arg_linux = NULL; static char **arg_initrds = NULL; static ConsoleMode arg_console_mode = CONSOLE_INTERACTIVE; +static ConsoleTransport arg_console_transport = CONSOLE_TRANSPORT_VIRTIO; static NetworkStack arg_network_stack = NETWORK_STACK_NONE; static MachineCredentialContext arg_credentials = {}; static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U; @@ -291,6 +292,8 @@ static int help(void) { "\n%3$sInput/Output:%4$s\n" " --console=MODE Console mode (interactive, native, gui, read-only\n" " or headless)\n" + " --console-transport=TRANSPORT\n" + " Console transport (virtio or serial)\n" " --background=COLOR Set ANSI color for background\n" "\n%3$sCredentials:%4$s\n" " --set-credential=ID:VALUE\n" @@ -375,6 +378,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_USER, ARG_IMAGE_FORMAT, ARG_IMAGE_DISK_TYPE, + ARG_CONSOLE_TRANSPORT, }; static const struct option options[] = { @@ -402,6 +406,7 @@ static int parse_argv(int argc, char *argv[]) { { "linux", required_argument, NULL, ARG_LINUX }, { "initrd", required_argument, NULL, ARG_INITRD }, { "console", required_argument, NULL, ARG_CONSOLE }, + { "console-transport", required_argument, NULL, ARG_CONSOLE_TRANSPORT }, { "qemu-gui", no_argument, NULL, ARG_QEMU_GUI }, /* compat option */ { "network-tap", no_argument, NULL, 'n' }, { "network-user-mode", no_argument, NULL, ARG_NETWORK_USER_MODE }, @@ -578,6 +583,13 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_CONSOLE_TRANSPORT: + arg_console_transport = console_transport_from_string(optarg); + if (arg_console_transport < 0) + return log_error_errno(arg_console_transport, "Failed to parse specified console transport: %s", optarg); + + break; + case ARG_QEMU_GUI: arg_console_mode = CONSOLE_GUI; break; @@ -2588,11 +2600,6 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { if (r < 0) return log_oom(); - r = qemu_config_section(config_file, "device", "vmspawn-virtio-serial-pci", - "driver", "virtio-serial-pci"); - if (r < 0) - return r; - /* Enable mux for native console so the QEMU monitor is accessible via Ctrl-a c */ r = qemu_config_section(config_file, "chardev", "console", "backend", "serial", @@ -2601,12 +2608,6 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { if (r < 0) return r; - r = qemu_config_section(config_file, "device", "virtconsole0", - "driver", "virtconsole", - "chardev", "console"); - if (r < 0) - return r; - if (arg_console_mode == CONSOLE_NATIVE) { r = qemu_config_section(config_file, "mon", "mon0", "chardev", "console"); @@ -2655,6 +2656,29 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { assert_not_reached(); } + if (!IN_SET(arg_console_mode, CONSOLE_GUI, CONSOLE_HEADLESS)) { + if (arg_console_transport == CONSOLE_TRANSPORT_SERIAL) { + /* Use -serial to connect the chardev to the platform's default serial + * device (e.g. isa-serial on x86, PL011 on ARM). On some platforms the + * serial device is a sysbus device that can only be connected via + * serial_hd() which is populated by -serial, not via the config file. */ + r = strv_extend_many(&cmdline, "-serial", "chardev:console"); + if (r < 0) + return log_oom(); + } else { + r = qemu_config_section(config_file, "device", "vmspawn-virtio-serial-pci", + "driver", "virtio-serial-pci"); + if (r < 0) + return r; + + r = qemu_config_section(config_file, "device", "virtconsole0", + "driver", "virtconsole", + "chardev", "console"); + if (r < 0) + return r; + } + } + r = qemu_config_section(config_file, "drive", "ovmf-code", "if", "pflash", "format", ovmf_config_format(ovmf_config), @@ -3028,7 +3052,9 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { } if (!IN_SET(arg_console_mode, CONSOLE_GUI, CONSOLE_HEADLESS)) { - r = strv_prepend(&arg_kernel_cmdline_extra, "console=hvc0"); + r = strv_prepend(&arg_kernel_cmdline_extra, + arg_console_transport == CONSOLE_TRANSPORT_SERIAL ? + "console=" QEMU_SERIAL_CONSOLE_NAME : "console=hvc0"); if (r < 0) return log_oom();