QEMUConstant.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. //
  2. // Copyright © 2022 osy. All rights reserved.
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. //
  16. import Foundation
  17. import Metal
  18. // MARK: QEMUConstant protocol
  19. /// A QEMU constant is a enum that can be generated externally
  20. protocol QEMUConstant: Codable, RawRepresentable, CaseIterable where RawValue == String, AllCases == [Self] {
  21. static var allRawValues: [String] { get }
  22. static var allPrettyValues: [String] { get }
  23. static var shownPrettyValues: [String] { get }
  24. var prettyValue: String { get }
  25. var rawValue: String { get }
  26. var isHidden: Bool { get }
  27. init?(rawValue: String)
  28. }
  29. extension QEMUConstant where Self: CaseIterable, AllCases == [Self] {
  30. static var allRawValues: [String] {
  31. allCases.map { value in value.rawValue }
  32. }
  33. static var allPrettyValues: [String] {
  34. allCases.map { value in value.prettyValue }
  35. }
  36. static var shownPrettyValues: [String] {
  37. allCases.compactMap { value in value.isHidden ? nil : value.prettyValue }
  38. }
  39. var isHidden: Bool {
  40. false
  41. }
  42. }
  43. extension QEMUConstant where Self: RawRepresentable, RawValue == String {
  44. init(from decoder: Decoder) throws {
  45. let rawValue = try String(from: decoder)
  46. guard let representedValue = Self.init(rawValue: rawValue) else {
  47. throw UTMConfigurationError.invalidConfigurationValue(rawValue)
  48. }
  49. self = representedValue
  50. }
  51. func encode(to encoder: Encoder) throws {
  52. try rawValue.encode(to: encoder)
  53. }
  54. }
  55. protocol QEMUDefaultConstant: QEMUConstant {
  56. static var `default`: Self { get }
  57. }
  58. extension Optional where Wrapped: QEMUDefaultConstant {
  59. var _bound: Wrapped? {
  60. get {
  61. return self
  62. }
  63. set {
  64. self = newValue
  65. }
  66. }
  67. var bound: Wrapped {
  68. get {
  69. return _bound ?? Wrapped.default
  70. }
  71. set {
  72. _bound = newValue
  73. }
  74. }
  75. }
  76. /// Type erasure for a QEMU constant useful for serialization/deserialization
  77. struct AnyQEMUConstant: QEMUConstant, RawRepresentable {
  78. static var allRawValues: [String] { [] }
  79. static var allPrettyValues: [String] { [] }
  80. static var allCases: [AnyQEMUConstant] { [] }
  81. var prettyValue: String { rawValue }
  82. let rawValue: String
  83. init<C>(_ base: C) where C : QEMUConstant {
  84. self.rawValue = base.rawValue
  85. }
  86. init?(rawValue: String) {
  87. self.rawValue = rawValue
  88. }
  89. }
  90. extension QEMUConstant {
  91. func asAnyQEMUConstant() -> AnyQEMUConstant {
  92. return AnyQEMUConstant(self)
  93. }
  94. }
  95. extension AnyQEMUConstant: QEMUDefaultConstant {
  96. static var `default`: AnyQEMUConstant {
  97. AnyQEMUConstant(rawValue: "default")!
  98. }
  99. }
  100. // MARK: Enhanced type checking for generated constants
  101. protocol QEMUTarget: QEMUDefaultConstant {}
  102. extension AnyQEMUConstant: QEMUTarget {}
  103. protocol QEMUCPU: QEMUDefaultConstant {}
  104. extension AnyQEMUConstant: QEMUCPU {}
  105. protocol QEMUCPUFlag: QEMUConstant {}
  106. extension AnyQEMUConstant: QEMUCPUFlag {}
  107. protocol QEMUDisplayDevice: QEMUConstant {}
  108. extension AnyQEMUConstant: QEMUDisplayDevice {}
  109. protocol QEMUNetworkDevice: QEMUConstant {}
  110. extension AnyQEMUConstant: QEMUNetworkDevice {}
  111. protocol QEMUSoundDevice: QEMUConstant {}
  112. extension AnyQEMUConstant: QEMUSoundDevice {}
  113. protocol QEMUSerialDevice: QEMUConstant {}
  114. extension AnyQEMUConstant: QEMUSerialDevice {}
  115. // MARK: Display constants
  116. enum QEMUScaler: String, CaseIterable, QEMUConstant {
  117. case linear = "Linear"
  118. case nearest = "Nearest"
  119. var prettyValue: String {
  120. switch self {
  121. case .linear: return NSLocalizedString("Linear", comment: "UTMQemuConstants")
  122. case .nearest: return NSLocalizedString("Nearest Neighbor", comment: "UTMQemuConstants")
  123. }
  124. }
  125. var metalSamplerMinMagFilter: MTLSamplerMinMagFilter {
  126. switch self {
  127. case .linear: return .linear
  128. case .nearest: return .nearest
  129. }
  130. }
  131. }
  132. // MARK: USB constants
  133. enum QEMUUSBBus: String, CaseIterable, QEMUConstant {
  134. case disabled = "Disabled"
  135. case usb2_0 = "2.0"
  136. case usb3_0 = "3.0"
  137. var prettyValue: String {
  138. switch self {
  139. case .disabled: return NSLocalizedString("Disabled", comment: "UTMQemuConstants")
  140. case .usb2_0: return NSLocalizedString("USB 2.0", comment: "UTMQemuConstants")
  141. case .usb3_0: return NSLocalizedString("USB 3.0 (XHCI)", comment: "UTMQemuConstants")
  142. }
  143. }
  144. }
  145. // MARK: Network constants
  146. enum QEMUNetworkMode: String, CaseIterable, QEMUConstant {
  147. case emulated = "Emulated"
  148. case shared = "Shared"
  149. case host = "Host"
  150. case bridged = "Bridged"
  151. var prettyValue: String {
  152. switch self {
  153. case .emulated: return NSLocalizedString("Emulated VLAN", comment: "UTMQemuConstants")
  154. case .shared: return NSLocalizedString("Shared Network", comment: "UTMQemuConstants")
  155. case .host: return NSLocalizedString("Host Only", comment: "UTMQemuConstants")
  156. case .bridged: return NSLocalizedString("Bridged (Advanced)", comment: "UTMQemuConstants")
  157. }
  158. }
  159. }
  160. enum QEMUNetworkProtocol: String, CaseIterable, QEMUConstant {
  161. case tcp = "TCP"
  162. case udp = "UDP"
  163. var prettyValue: String {
  164. switch self {
  165. case .tcp: return NSLocalizedString("TCP", comment: "UTMQemuConstants")
  166. case .udp: return NSLocalizedString("UDP", comment: "UTMQemuConstants")
  167. }
  168. }
  169. }
  170. // MARK: Serial constants
  171. enum QEMUTerminalTheme: String, CaseIterable, QEMUDefaultConstant {
  172. case `default` = "Default"
  173. var prettyValue: String {
  174. switch self {
  175. case .`default`: return NSLocalizedString("Default", comment: "UTMQemuConstants")
  176. }
  177. }
  178. }
  179. struct QEMUTerminalFont: QEMUConstant {
  180. #if os(macOS)
  181. static var allRawValues: [String] = {
  182. NSFontManager.shared.availableFontNames(with: .fixedPitchFontMask) ?? []
  183. }()
  184. static var allPrettyValues: [String] = {
  185. allRawValues.map { name in
  186. NSFont(name: name, size: 1)?.displayName ?? name
  187. }
  188. }()
  189. #else
  190. static var allRawValues: [String] = {
  191. UIFont.familyNames.flatMap { family -> [String] in
  192. guard let font = UIFont(name: family, size: 1) else {
  193. return []
  194. }
  195. if font.fontDescriptor.symbolicTraits.contains(.traitMonoSpace) {
  196. return UIFont.fontNames(forFamilyName: family)
  197. } else {
  198. return []
  199. }
  200. }
  201. }()
  202. static var allPrettyValues: [String] = {
  203. allRawValues.map { name in
  204. guard let font = UIFont(name: name, size: 1) else {
  205. return name
  206. }
  207. let traits = font.fontDescriptor.symbolicTraits
  208. let description: String
  209. if traits.isSuperset(of: [.traitItalic, .traitBold]) {
  210. description = NSLocalizedString("Italic, Bold", comment: "UTMQemuConstants")
  211. } else if traits.contains(.traitItalic) {
  212. description = NSLocalizedString("Italic", comment: "UTMQemuConstants")
  213. } else if traits.contains(.traitBold) {
  214. description = NSLocalizedString("Bold", comment: "UTMQemuConstants")
  215. } else {
  216. description = NSLocalizedString("Regular", comment: "UTMQemuConstants")
  217. }
  218. return String.localizedStringWithFormat(NSLocalizedString("%@ (%@)", comment: "QEMUConstant"), font.familyName, description)
  219. }
  220. }()
  221. #endif
  222. static var allCases: [QEMUTerminalFont] {
  223. Self.allRawValues.map { Self(rawValue: $0) }
  224. }
  225. var prettyValue: String {
  226. guard let index = Self.allRawValues.firstIndex(of: rawValue) else {
  227. return rawValue
  228. }
  229. return Self.allPrettyValues[index]
  230. }
  231. let rawValue: String
  232. }
  233. enum QEMUSerialMode: String, CaseIterable, QEMUConstant {
  234. case builtin = "Terminal"
  235. case tcpClient = "TcpClient"
  236. case tcpServer = "TcpServer"
  237. #if os(macOS)
  238. case ptty = "Ptty"
  239. #endif
  240. var prettyValue: String {
  241. switch self {
  242. case .builtin: return NSLocalizedString("Built-in Terminal", comment: "UTMQemuConstants")
  243. case .tcpClient: return NSLocalizedString("TCP Client Connection", comment: "UTMQemuConstants")
  244. case .tcpServer: return NSLocalizedString("TCP Server Connection", comment: "UTMQemuConstants")
  245. #if os(macOS)
  246. case .ptty: return NSLocalizedString("Pseudo-TTY Device", comment: "UTMQemuConstants")
  247. #endif
  248. }
  249. }
  250. }
  251. enum QEMUSerialTarget: String, CaseIterable, QEMUConstant {
  252. case autoDevice = "Auto"
  253. case manualDevice = "Manual"
  254. case gdb = "GDB"
  255. case monitor = "Monitor"
  256. var prettyValue: String {
  257. switch self {
  258. case .autoDevice: return NSLocalizedString("Automatic Serial Device (max 4)", comment: "UTMQemuConstants")
  259. case .manualDevice: return NSLocalizedString("Manual Serial Device (advanced)", comment: "UTMQemuConstants")
  260. case .gdb: return NSLocalizedString("GDB Debug Stub", comment: "UTMQemuConstants")
  261. case .monitor: return NSLocalizedString("QEMU Monitor (HMP)", comment: "UTMQemuConstants")
  262. }
  263. }
  264. }
  265. // MARK: Drive constants
  266. enum QEMUDriveImageType: String, CaseIterable, QEMUConstant {
  267. case none = "None"
  268. case disk = "Disk"
  269. case cd = "CD"
  270. case bios = "BIOS"
  271. case linuxKernel = "LinuxKernel"
  272. case linuxInitrd = "LinuxInitrd"
  273. case linuxDtb = "LinuxDTB"
  274. var prettyValue: String {
  275. switch self {
  276. case .none: return NSLocalizedString("None", comment: "UTMQemuConstants")
  277. case .disk: return NSLocalizedString("Disk Image", comment: "UTMQemuConstants")
  278. case .cd: return NSLocalizedString("CD/DVD (ISO) Image", comment: "UTMQemuConstants")
  279. case .bios: return NSLocalizedString("BIOS", comment: "UTMQemuConstants")
  280. case .linuxKernel: return NSLocalizedString("Linux Kernel", comment: "UTMQemuConstants")
  281. case .linuxInitrd: return NSLocalizedString("Linux RAM Disk", comment: "UTMQemuConstants")
  282. case .linuxDtb: return NSLocalizedString("Linux Device Tree Binary", comment: "UTMQemuConstants")
  283. }
  284. }
  285. }
  286. enum QEMUDriveInterface: String, CaseIterable, QEMUConstant {
  287. case none = "None"
  288. case ide = "IDE"
  289. case scsi = "SCSI"
  290. case sd = "SD"
  291. case mtd = "MTD"
  292. case floppy = "Floppy"
  293. case pflash = "PFlash"
  294. case virtio = "VirtIO"
  295. case nvme = "NVMe"
  296. case usb = "USB"
  297. var prettyValue: String {
  298. switch self {
  299. case .none: return NSLocalizedString("None (Advanced)", comment: "UTMQemuConstants")
  300. case .ide: return NSLocalizedString("IDE", comment: "UTMQemuConstants")
  301. case .scsi: return NSLocalizedString("SCSI", comment: "UTMQemuConstants")
  302. case .sd: return NSLocalizedString("SD Card", comment: "UTMQemuConstants")
  303. case .mtd: return NSLocalizedString("MTD (NAND/NOR)", comment: "UTMQemuConstants")
  304. case .floppy: return NSLocalizedString("Floppy", comment: "UTMQemuConstants")
  305. case .pflash: return NSLocalizedString("PC System Flash", comment: "UTMQemuConstants")
  306. case .virtio: return NSLocalizedString("VirtIO", comment: "UTMQemuConstants")
  307. case .nvme: return NSLocalizedString("NVMe", comment: "UTMQemuConstants")
  308. case .usb: return NSLocalizedString("USB", comment: "UTMQemuConstants")
  309. }
  310. }
  311. }
  312. // MARK: Sharing constants
  313. enum QEMUFileShareMode: String, CaseIterable, QEMUConstant {
  314. case none = "None"
  315. case webdav = "WebDAV"
  316. case virtfs = "VirtFS"
  317. var prettyValue: String {
  318. switch self {
  319. case .none: return NSLocalizedString("None", comment: "UTMQemuConstants")
  320. case .webdav: return NSLocalizedString("SPICE WebDAV", comment: "UTMQemuConstants")
  321. case .virtfs: return NSLocalizedString("VirtFS", comment: "UTMQemuConstants")
  322. }
  323. }
  324. }
  325. // MARK: File names
  326. enum QEMUPackageFileName: String {
  327. case images = "Images"
  328. case debugLog = "debug.log"
  329. case efiVariables = "efi_vars.fd"
  330. case tpmData = "tpmdata"
  331. case vmState = "vmstate"
  332. }
  333. // MARK: Supported features
  334. extension QEMUArchitecture {
  335. var hasAgentSupport: Bool {
  336. switch self {
  337. case .avr: return false
  338. case .m68k: return false
  339. case .microblaze, .microblazeel: return false
  340. case .rx: return false
  341. case .sparc, .sparc64: return false
  342. case .tricore: return false
  343. default: return true
  344. }
  345. }
  346. var hasSharingSupport: Bool {
  347. switch self {
  348. case .sparc, .sparc64: return false
  349. default: return true
  350. }
  351. }
  352. var hasUsbSupport: Bool {
  353. switch self {
  354. case .s390x: return false
  355. case .sparc, .sparc64: return false
  356. default: return true
  357. }
  358. }
  359. var hasHypervisorSupport: Bool {
  360. guard UTMCapabilities.current.contains(.hasHypervisorSupport) else {
  361. return false
  362. }
  363. if UTMCapabilities.current.contains(.isAarch64) {
  364. return self == .aarch64
  365. } else if UTMCapabilities.current.contains(.isX86_64) {
  366. return self == .x86_64
  367. } else {
  368. return false
  369. }
  370. }
  371. /// TSO is supported on jailbroken iOS devices with Hypervisor support
  372. var hasTSOSupport: Bool {
  373. #if os(iOS) || os(visionOS)
  374. return hasHypervisorSupport
  375. #else
  376. if #available(macOS 15, *) {
  377. return true
  378. } else {
  379. return false
  380. }
  381. #endif
  382. }
  383. var hasSecureBootSupport: Bool {
  384. switch self {
  385. case .x86_64, .i386: return true
  386. case .aarch64: return true
  387. default: return false
  388. }
  389. }
  390. }
  391. extension QEMUTarget {
  392. var hasUsbSupport: Bool {
  393. switch self.rawValue {
  394. case "isapc": return false
  395. default: return true
  396. }
  397. }
  398. var hasAgentSupport: Bool {
  399. switch self.rawValue {
  400. case "isapc": return false
  401. default: return true
  402. }
  403. }
  404. var hasSecureBootSupport: Bool {
  405. switch self.rawValue {
  406. case "microvm": return false
  407. default: return true
  408. }
  409. }
  410. }
  411. #if WITH_QEMU_TCI
  412. /// TCI build has a reduced set of supported architectures due to size of binaries.
  413. extension QEMUArchitecture {
  414. var isHidden: Bool {
  415. switch self {
  416. case .arm: return false
  417. case .aarch64: return false
  418. case .i386: return false
  419. case .ppc: return false
  420. case .ppc64: return false
  421. case .riscv32: return false
  422. case .riscv64: return false
  423. case .x86_64: return false
  424. default: return true
  425. }
  426. }
  427. }
  428. #endif