From d8ac36b3bd4f11df79663eb1bc039044c6d947da Mon Sep 17 00:00:00 2001 From: Peter Nguyen Date: Thu, 16 Mar 2023 22:21:13 +0800 Subject: [PATCH] support ZVirtioConsoleDevice for linux vm (#364) * support ZVirtioConsoleDevice for linux vm, user can control the vm via serial port with screen command * cleanup code, fix indent * add --serial option to tart * remove serial in vmconfig, fix Serial.swift --------- Co-authored-by: peternguyen93 Co-authored-by: peter --- Sources/tart/Commands/Run.swift | 8 ++++- Sources/tart/Serial.swift | 52 +++++++++++++++++++++++++++++++++ Sources/tart/VM.swift | 32 ++++++++++++++++---- 3 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 Sources/tart/Serial.swift diff --git a/Sources/tart/Commands/Run.swift b/Sources/tart/Commands/Run.swift index c0107fd..61f5960 100644 --- a/Sources/tart/Commands/Run.swift +++ b/Sources/tart/Commands/Run.swift @@ -20,6 +20,11 @@ struct Run: AsyncParsableCommand { discussion: "Useful for integrating Tart VMs into other tools.\nUse `tart ip` in order to get an IP for SSHing or VNCing into the VM.")) var noGraphics: Bool = false + @Flag(help: ArgumentHelp( + "Open serial console in /dev/ttySXX", + discussion: "Useful for debugging Linux Kernel")) + var serial: Bool = false + @Flag(help: "Force open a UI window, even when VNC is enabled.") var graphics: Bool = false @@ -122,7 +127,8 @@ struct Run: AsyncParsableCommand { vmDir: vmDir, network: userSpecifiedNetwork(vmDir: vmDir) ?? NetworkShared(), additionalDiskAttachments: additionalDiskAttachments, - directorySharingDevices: directoryShares() + rosettaDirectoryShare() + directorySharingDevices: directoryShares() + rosettaDirectoryShare(), + serial: serial ) let vncImpl: VNC? = try { diff --git a/Sources/tart/Serial.swift b/Sources/tart/Serial.swift new file mode 100644 index 0000000..80fc12d --- /dev/null +++ b/Sources/tart/Serial.swift @@ -0,0 +1,52 @@ +// +// File.swift +// +// +// Created by Peter Nguyen on 19/12/2022. +// + +import Foundation + +func createPTY() -> Int32 +{ + var tty_fd: Int32 = -1 + var sfd: Int32 = -1 + var termios_ = termios() + let tty_path = UnsafeMutablePointer.allocate(capacity: 1024) + + var res = openpty(&tty_fd, &sfd, tty_path, nil, nil); + if(res < 0 ){ + perror("openpty error") + return -1 + } + + // close slave file descriptor + close(sfd) + + res = fcntl(tty_fd, F_GETFL) + if(res < 0){ + perror("fcntl F_GETFL error") + return res + } + + // set serial nonblocking + res = fcntl(tty_fd, F_SETFL, res | O_NONBLOCK) + if(res < 0){ + perror("fcntl F_SETFL O_NONBLOCK error") + return res + } + + // set baudrate to 115200 + tcgetattr(tty_fd, &termios_) + cfsetispeed(&termios_, speed_t(B115200)) + cfsetospeed(&termios_, speed_t(B115200)) + if(tcsetattr(tty_fd, TCSANOW, &termios_) != 0){ + perror("tcsetattr error") + return -1 + } + + print("Successfully open pty \(String(cString: tty_path))") + + tty_path.deallocate() + return tty_fd +} diff --git a/Sources/tart/VM.swift b/Sources/tart/VM.swift index a9342be..bd569d1 100644 --- a/Sources/tart/VM.swift +++ b/Sources/tart/VM.swift @@ -40,7 +40,8 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject { init(vmDir: VMDirectory, network: Network = NetworkShared(), additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment] = [], - directorySharingDevices: [VZDirectorySharingDeviceConfiguration] = [] + directorySharingDevices: [VZDirectorySharingDeviceConfiguration] = [], + serial: Bool = false ) throws { name = vmDir.name config = try VMConfig.init(fromURL: vmDir.configURL) @@ -54,7 +55,8 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject { let configuration = try Self.craftConfiguration(diskURL: vmDir.diskURL, nvramURL: vmDir.nvramURL, vmConfig: config, network: network, additionalDiskAttachments: additionalDiskAttachments, - directorySharingDevices: directorySharingDevices + directorySharingDevices: directorySharingDevices, + serial: serial ) virtualMachine = VZVirtualMachine(configuration: configuration) @@ -132,7 +134,8 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject { diskSizeGB: UInt16, network: Network = NetworkShared(), additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment] = [], - directorySharingDevices: [VZDirectorySharingDeviceConfiguration] = [] + directorySharingDevices: [VZDirectorySharingDeviceConfiguration] = [], + serial: Bool = false ) async throws { var ipswURL = ipswURL @@ -179,7 +182,8 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject { let configuration = try Self.craftConfiguration(diskURL: vmDir.diskURL, nvramURL: vmDir.nvramURL, vmConfig: config, network: network, additionalDiskAttachments: additionalDiskAttachments, - directorySharingDevices: directorySharingDevices + directorySharingDevices: directorySharingDevices, + serial: serial ) virtualMachine = VZVirtualMachine(configuration: configuration) @@ -261,7 +265,8 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject { vmConfig: VMConfig, network: Network = NetworkShared(), additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment], - directorySharingDevices: [VZDirectorySharingDeviceConfiguration] + directorySharingDevices: [VZDirectorySharingDeviceConfiguration], + serial: Bool ) throws -> VZVirtualMachineConfiguration { let configuration = VZVirtualMachineConfiguration() @@ -308,6 +313,23 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject { // Directory sharing devices configuration.directorySharingDevices = directorySharingDevices + // Serial Port + if serial { + let tty_fd = createPTY() + if(tty_fd > -1){ + let tty_read = FileHandle.init(fileDescriptor: tty_fd) + let tty_write = FileHandle.init(fileDescriptor: tty_fd) + + + configuration.serialPorts = [VZVirtioConsoleDeviceSerialPortConfiguration()] + let serialPortAttachment = VZFileHandleSerialPortAttachment( + fileHandleForReading: tty_read, + fileHandleForWriting: tty_write) + + configuration.serialPorts[0].attachment = serialPortAttachment + } + } + try configuration.validate() return configuration