qemu.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package qemu
  15. import (
  16. "fmt"
  17. "path"
  18. "path/filepath"
  19. "strings"
  20. "sync"
  21. "yunion.io/x/log"
  22. "yunion.io/x/pkg/errors"
  23. "yunion.io/x/pkg/utils"
  24. "yunion.io/x/onecloud/pkg/util/fileutils2"
  25. "yunion.io/x/onecloud/pkg/util/procutils"
  26. )
  27. type Version string
  28. const (
  29. Version_4_2_0 Version = "4.2.0"
  30. Version_4_0_1 Version = "4.0.1"
  31. Version_2_12_1 Version = "2.12.1"
  32. Version_9_0_1 Version = "9.0.1"
  33. Version_10_0_3 Version = "10.0.3"
  34. Version_10_0_7 Version = "10.0.7"
  35. )
  36. type Arch string
  37. const (
  38. Arch_x86_64 Arch = "x86_64"
  39. Arch_aarch64 Arch = "aarch64"
  40. Arch_riscv64 Arch = "riscv64"
  41. )
  42. func (a Arch) IsX86() bool {
  43. return a == Arch_x86_64
  44. }
  45. const (
  46. OS_NAME_LINUX = "Linux"
  47. OS_NAME_WINDOWS = "Windows"
  48. OS_NAME_MACOS = "macOS"
  49. OS_NAME_ANDROID = "Android"
  50. OS_NAME_VMWARE = "VMWare"
  51. OS_NAME_CIRROS = "Cirros"
  52. OS_NAME_OPENWRT = "OpenWrt"
  53. MODE_READLINE = "readline"
  54. MODE_CONTROL = "control"
  55. DISK_DRIVER_VIRTIO = "virtio"
  56. DISK_DRIVER_SCSI = "scsi"
  57. DISK_DRIVER_PVSCSI = "pvscsi"
  58. DISK_DRIVER_IDE = "ide"
  59. DISK_DRIVER_SATA = "sata"
  60. BIOS_UEFI = "UEFI"
  61. )
  62. type QemuCommand interface {
  63. GetVersion() Version
  64. GetArch() Arch
  65. GetOptions() QemuOptions
  66. }
  67. type CPUOption struct {
  68. EnableKVM bool
  69. IsKVMSupport bool
  70. HostCPUPassthrough bool
  71. IsCPUIntel bool
  72. IsCPUAMD bool
  73. EnableNested bool
  74. IsolatedDeviceCPU string
  75. }
  76. type QemuOptions interface {
  77. IsArm() bool
  78. Log(enable bool) string
  79. RTC() string
  80. FreezeCPU() string
  81. Daemonize() string
  82. Nodefaults() string
  83. Nodefconfig() string
  84. NoHpet() (bool, string)
  85. Global() string
  86. KeyboardLayoutLanguage(lang string) string
  87. Name(name string) string
  88. UUID(enable bool, uuid string) string
  89. MemPath(sizeMB uint64, p string) string
  90. MemDev(sizeMB uint64) string
  91. MemFd(sizeMB uint64) string
  92. Boot(order *string, enableMenu bool) string
  93. BIOS(ovmfPath, ovmfVarsPath, homedir string) (string, error)
  94. Device(devStr string) string
  95. Drive(driveStr string) string
  96. Chardev(backend string, id string, name string) string
  97. MonitorChardev(id string, port uint, host string) string
  98. Mon(chardev string, id string, mode string) string
  99. Object(typeName string, props map[string]string) string
  100. Pidfile(file string) string
  101. USB() string
  102. VNC(port uint, usePasswd bool) string
  103. Initrd(initrdPath string) string
  104. Kernel(kernelPath string) string
  105. ScsiDeviceId(serial string, driver string) string
  106. }
  107. var (
  108. cmdDrivers = new(sync.Map)
  109. )
  110. func getCommandKey(version Version, arch Arch) string {
  111. return fmt.Sprintf("%s-%s", version, arch)
  112. }
  113. func RegisterCmd(cmds ...QemuCommand) {
  114. for _, cmd := range cmds {
  115. _, ok := GetCommand(cmd.GetVersion(), cmd.GetArch())
  116. if ok {
  117. log.Fatalf("cmd 'version %s' 'arch %s' already registered!", cmd.GetVersion(), cmd.GetArch())
  118. }
  119. cmdDrivers.Store(getCommandKey(cmd.GetVersion(), cmd.GetArch()), cmd)
  120. }
  121. }
  122. func GetCommand(version Version, arch Arch) (QemuCommand, bool) {
  123. key := getCommandKey(version, arch)
  124. val, ok := cmdDrivers.Load(key)
  125. if !ok {
  126. return nil, false
  127. }
  128. return val.(QemuCommand), true
  129. }
  130. func EnsureGetCommand(version Version, arch Arch) QemuCommand {
  131. cmd, ok := GetCommand(version, arch)
  132. if !ok {
  133. log.Fatalf("cmd %s %s not registered!", version, arch)
  134. }
  135. return cmd
  136. }
  137. type baseCommand struct {
  138. version Version
  139. arch Arch
  140. options QemuOptions
  141. }
  142. func newBaseCommand(version Version, arch Arch, opts QemuOptions) QemuCommand {
  143. return &baseCommand{
  144. version: version,
  145. arch: arch,
  146. options: opts,
  147. }
  148. }
  149. func (c *baseCommand) GetVersion() Version {
  150. return c.version
  151. }
  152. func (c *baseCommand) GetArch() Arch {
  153. return c.arch
  154. }
  155. func (c *baseCommand) GetOptions() QemuOptions {
  156. return c.options
  157. }
  158. type baseOptions struct {
  159. arch Arch
  160. }
  161. func newBaseOptions(arch Arch) *baseOptions {
  162. return &baseOptions{
  163. arch: arch,
  164. }
  165. }
  166. func (o baseOptions) IsArm() bool {
  167. return o.arch == Arch_aarch64
  168. }
  169. func (o baseOptions) Log(enable bool) string {
  170. if !enable {
  171. return ""
  172. }
  173. return "-d all"
  174. }
  175. func (o baseOptions) RTC() string {
  176. return "-rtc base=utc,clock=host,driftfix=none"
  177. }
  178. func (o baseOptions) Daemonize() string {
  179. return "-daemonize"
  180. }
  181. func (o baseOptions) FreezeCPU() string {
  182. return "-S"
  183. }
  184. func (o baseOptions) Nodefaults() string {
  185. return "-nodefaults"
  186. }
  187. func (o baseOptions) NoHpet() (bool, string) {
  188. return false, "-no-hpet"
  189. }
  190. func (o baseOptions) Nodefconfig() string {
  191. return "-nodefconfig"
  192. }
  193. func (o baseOptions) ScsiDeviceId(serial string, driver string) string {
  194. if !utils.IsInStringArray(driver, []string{DISK_DRIVER_SCSI, DISK_DRIVER_PVSCSI}) {
  195. return ""
  196. }
  197. return fmt.Sprintf(",device_id=%s", serial[:20])
  198. }
  199. /*
  200. @subsection -no-kvm-pit-reinjection (since 1.3.0)
  201. The “-no-kvm-pit-reinjection” argument is now a
  202. synonym for setting “-global kvm-pit.lost_tick_policy=discard”.
  203. */
  204. func (o baseOptions) Global() string {
  205. return "-global kvm-pit.lost_tick_policy=discard"
  206. }
  207. func (o baseOptions) KeyboardLayoutLanguage(lang string) string {
  208. return "-k " + lang
  209. }
  210. func (o baseOptions) Name(name string) string {
  211. return fmt.Sprintf(`-name '%s',debug-threads=on`, strings.ReplaceAll(name, " ", "_"))
  212. }
  213. func (o baseOptions) UUID(enable bool, uuid string) string {
  214. if !enable {
  215. return ""
  216. }
  217. return "-uuid " + uuid
  218. }
  219. func (o baseOptions) MemPrealloc() string {
  220. return "-mem-prealloc"
  221. }
  222. func (o baseOptions) MemPath(sizeMB uint64, p string) string {
  223. return fmt.Sprintf("-object memory-backend-file,id=mem,size=%dM,mem-path=%s,share=on,prealloc=on -numa node,memdev=mem", sizeMB, p)
  224. }
  225. func (o baseOptions) MemDev(sizeMB uint64) string {
  226. return fmt.Sprintf("-object memory-backend-ram,id=mem,size=%dM -numa node,memdev=mem", sizeMB)
  227. }
  228. func (o baseOptions) MemFd(sizeMB uint64) string {
  229. return fmt.Sprintf("-object memory-backend-memfd,id=mem,size=%dM,share=on,prealloc=on -numa node,memdev=mem", sizeMB)
  230. }
  231. func (o baseOptions) Boot(order *string, enableMenu bool) string {
  232. opts := []string{}
  233. if order != nil {
  234. opts = append(opts, *order)
  235. }
  236. if enableMenu {
  237. opts = append(opts, "menu=on")
  238. }
  239. if len(opts) == 0 {
  240. return ""
  241. }
  242. return fmt.Sprintf("-boot %s", strings.Join(opts, ","))
  243. }
  244. func (o baseOptions) BIOS(ovmfPath, ovmfVarsPath, homedir string) (string, error) {
  245. ovmfVarsName := "OVMF_VARS.fd"
  246. if ovmfVarsPath != "" {
  247. ovmfVarsName = filepath.Base(ovmfVarsPath)
  248. }
  249. guestOvmfVarsPath := path.Join(homedir, ovmfVarsName)
  250. if !fileutils2.Exists(guestOvmfVarsPath) {
  251. sourceOvmfVarsPath := ovmfPath
  252. if ovmfVarsPath != "" && fileutils2.Exists(ovmfVarsPath) {
  253. sourceOvmfVarsPath = ovmfVarsPath
  254. }
  255. err := procutils.NewRemoteCommandAsFarAsPossible("cp", "-f", sourceOvmfVarsPath, guestOvmfVarsPath).Run()
  256. if err != nil {
  257. return "", errors.Wrap(err, "failed copy ovmf vars")
  258. }
  259. }
  260. return fmt.Sprintf(
  261. "-drive if=pflash,format=raw,unit=0,file=%s,readonly=on -drive if=pflash,format=raw,unit=1,file=%s",
  262. ovmfPath, guestOvmfVarsPath,
  263. ), nil
  264. }
  265. func (o baseOptions) Device(devStr string) string {
  266. return "-device " + devStr
  267. }
  268. func (o baseOptions) Drive(driveStr string) string {
  269. return "-drive " + driveStr
  270. }
  271. func (o baseOptions) Chardev(backend string, id string, name string) string {
  272. opt := fmt.Sprintf("-chardev %s,id=%s", backend, id)
  273. if name != "" {
  274. opt = fmt.Sprintf("%s,name=%s", opt, name)
  275. }
  276. return opt
  277. }
  278. func (o baseOptions) MonitorChardev(id string, port uint, host string) string {
  279. opt := o.Chardev("socket", id, "")
  280. return fmt.Sprintf("%s,port=%d,host=%s,nodelay,server,nowait", opt, port, host)
  281. }
  282. func (o baseOptions) Mon(chardev string, id string, mode string) string {
  283. return fmt.Sprintf("-mon chardev=%s,id=%s,mode=%s", chardev, id, mode)
  284. }
  285. func (o baseOptions) Object(typeName string, props map[string]string) string {
  286. propStrs := []string{}
  287. for k, v := range props {
  288. propStrs = append(propStrs, fmt.Sprintf("%s=%s", k, v))
  289. }
  290. opt := fmt.Sprintf("-object %s", typeName)
  291. if len(propStrs) > 0 {
  292. opt = opt + "," + strings.Join(propStrs, ",")
  293. }
  294. return opt
  295. }
  296. func (o baseOptions) Pidfile(file string) string {
  297. return "-pidfile " + file
  298. }
  299. func (o baseOptions) USB() string {
  300. return "-usb"
  301. }
  302. func (o baseOptions) Initrd(initrdPath string) string {
  303. return "-initrd " + initrdPath
  304. }
  305. func (o baseOptions) Kernel(kernelPath string) string {
  306. return "-kernel " + kernelPath
  307. }
  308. func (o baseOptions) VNC(port uint, usePasswd bool) string {
  309. opt := fmt.Sprintf("-vnc :%d", port)
  310. if usePasswd {
  311. opt += ",password"
  312. }
  313. return opt
  314. }
  315. type baseOptions_x86_64 struct {
  316. *baseOptions
  317. }
  318. func newBaseOptions_x86_64() *baseOptions_x86_64 {
  319. return &baseOptions_x86_64{
  320. baseOptions: newBaseOptions(Arch_x86_64),
  321. }
  322. }
  323. // qemu version grate or equal 8.0.0
  324. type baseOptions_ge_800_x86_64 struct {
  325. }
  326. func (o baseOptions_ge_800_x86_64) NoHpet() (bool, string) {
  327. return true, "hpet=off"
  328. }
  329. func newBaseOptionsGE800_x86_64() *baseOptions_ge_800_x86_64 {
  330. return &baseOptions_ge_800_x86_64{}
  331. }
  332. // qemu version grate or equal 3.1.0
  333. type baseOptions_ge_310 struct {
  334. }
  335. func (o baseOptions_ge_310) Nodefconfig() string {
  336. return "-no-user-config"
  337. }
  338. func newBaseOptionsGE310() *baseOptions_ge_310 {
  339. return &baseOptions_ge_310{}
  340. }
  341. type baseOptions_aarch64 struct {
  342. *baseOptions
  343. }
  344. func newBaseOptions_aarch64() *baseOptions_aarch64 {
  345. return &baseOptions_aarch64{
  346. baseOptions: newBaseOptions(Arch_aarch64),
  347. }
  348. }
  349. func (o baseOptions_aarch64) Global() string {
  350. return ""
  351. }
  352. func (o baseOptions_aarch64) NoHpet() (bool, string) {
  353. return false, ""
  354. }
  355. type baseOptions_riscv64 struct {
  356. *baseOptions
  357. }
  358. func newBaseOptions_riscv64() *baseOptions_riscv64 {
  359. return &baseOptions_riscv64{
  360. baseOptions: newBaseOptions(Arch_riscv64),
  361. }
  362. }
  363. func (o baseOptions_riscv64) Global() string {
  364. return ""
  365. }
  366. func (o baseOptions_riscv64) NoHpet() (bool, string) {
  367. return false, ""
  368. }