github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/qemu_arch_base.go (about)

     1  // Copyright (c) 2018 Intel Corporation
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  
     6  package virtcontainers
     7  
     8  import (
     9  	"context"
    10  	"encoding/hex"
    11  	"errors"
    12  	"fmt"
    13  	"os"
    14  	"strconv"
    15  	"strings"
    16  
    17  	govmmQemu "github.com/kata-containers/govmm/qemu"
    18  
    19  	"github.com/kata-containers/runtime/virtcontainers/device/config"
    20  	"github.com/kata-containers/runtime/virtcontainers/types"
    21  	"github.com/kata-containers/runtime/virtcontainers/utils"
    22  )
    23  
    24  type qemuArch interface {
    25  	// enableNestingChecks nesting checks will be honoured
    26  	enableNestingChecks()
    27  
    28  	// disableNestingChecks nesting checks will be ignored
    29  	disableNestingChecks()
    30  
    31  	// runNested indicates if the hypervisor runs in a nested environment
    32  	runNested() bool
    33  
    34  	// enableVhostNet vhost will be enabled
    35  	enableVhostNet()
    36  
    37  	// disableVhostNet vhost will be disabled
    38  	disableVhostNet()
    39  
    40  	// machine returns the machine type
    41  	machine() (govmmQemu.Machine, error)
    42  
    43  	// qemuPath returns the path to the QEMU binary
    44  	qemuPath() (string, error)
    45  
    46  	// kernelParameters returns the kernel parameters
    47  	// if debug is true then kernel debug parameters are included
    48  	kernelParameters(debug bool) []Param
    49  
    50  	//capabilities returns the capabilities supported by QEMU
    51  	capabilities() types.Capabilities
    52  
    53  	// bridges sets the number bridges for the machine type
    54  	bridges(number uint32)
    55  
    56  	// cpuTopology returns the CPU topology for the given amount of vcpus
    57  	cpuTopology(vcpus, maxvcpus uint32) govmmQemu.SMP
    58  
    59  	// cpuModel returns the CPU model for the machine type
    60  	cpuModel() string
    61  
    62  	// memoryTopology returns the memory topology using the given amount of memoryMb and hostMemoryMb
    63  	memoryTopology(memoryMb, hostMemoryMb uint64, slots uint8) govmmQemu.Memory
    64  
    65  	// appendConsole appends a console to devices
    66  	appendConsole(devices []govmmQemu.Device, path string) ([]govmmQemu.Device, error)
    67  
    68  	// appendImage appends an image to devices
    69  	appendImage(devices []govmmQemu.Device, path string) ([]govmmQemu.Device, error)
    70  
    71  	// appendBlockImage appends an image as block device
    72  	appendBlockImage(devices []govmmQemu.Device, path string) ([]govmmQemu.Device, error)
    73  
    74  	// appendNvdimmImage appends an image as nvdimm device
    75  	appendNvdimmImage(devices []govmmQemu.Device, path string) ([]govmmQemu.Device, error)
    76  
    77  	// appendSCSIController appens a SCSI controller to devices
    78  	appendSCSIController(devices []govmmQemu.Device, enableIOThreads bool) ([]govmmQemu.Device, *govmmQemu.IOThread, error)
    79  
    80  	// appendBridges appends bridges to devices
    81  	appendBridges(devices []govmmQemu.Device) []govmmQemu.Device
    82  
    83  	// append9PVolume appends a 9P volume to devices
    84  	append9PVolume(devices []govmmQemu.Device, volume types.Volume) ([]govmmQemu.Device, error)
    85  
    86  	// appendSocket appends a socket to devices
    87  	appendSocket(devices []govmmQemu.Device, socket types.Socket) []govmmQemu.Device
    88  
    89  	// appendVSock appends a vsock PCI to devices
    90  	appendVSock(devices []govmmQemu.Device, vsock types.VSock) ([]govmmQemu.Device, error)
    91  
    92  	// appendNetwork appends a endpoint device to devices
    93  	appendNetwork(devices []govmmQemu.Device, endpoint Endpoint) ([]govmmQemu.Device, error)
    94  
    95  	// appendBlockDevice appends a block drive to devices
    96  	appendBlockDevice(devices []govmmQemu.Device, drive config.BlockDrive) ([]govmmQemu.Device, error)
    97  
    98  	// appendVhostUserDevice appends a vhost user device to devices
    99  	appendVhostUserDevice(devices []govmmQemu.Device, drive config.VhostUserDeviceAttrs) ([]govmmQemu.Device, error)
   100  
   101  	// appendVFIODevice appends a VFIO device to devices
   102  	appendVFIODevice(devices []govmmQemu.Device, vfioDevice config.VFIODev) []govmmQemu.Device
   103  
   104  	// appendRNGDevice appends a RNG device to devices
   105  	appendRNGDevice(devices []govmmQemu.Device, rngDevice config.RNGDev) ([]govmmQemu.Device, error)
   106  
   107  	// addDeviceToBridge adds devices to the bus
   108  	addDeviceToBridge(ID string, t types.Type) (string, types.Bridge, error)
   109  
   110  	// removeDeviceFromBridge removes devices to the bus
   111  	removeDeviceFromBridge(ID string) error
   112  
   113  	// getBridges grants access to Bridges
   114  	getBridges() []types.Bridge
   115  
   116  	// setBridges grants access to Bridges
   117  	setBridges(bridges []types.Bridge)
   118  
   119  	// addBridge adds a new Bridge to the list of Bridges
   120  	addBridge(types.Bridge)
   121  
   122  	// getPFlash() get pflash from configuration
   123  	getPFlash() ([]string, error)
   124  
   125  	// setPFlash() grants access to pflash
   126  	setPFlash([]string)
   127  
   128  	// handleImagePath handles the Hypervisor Config image path
   129  	handleImagePath(config HypervisorConfig)
   130  
   131  	// supportGuestMemoryHotplug returns if the guest supports memory hotplug
   132  	supportGuestMemoryHotplug() bool
   133  
   134  	// setIgnoreSharedMemoryMigrationCaps set bypass-shared-memory capability for migration
   135  	setIgnoreSharedMemoryMigrationCaps(context.Context, *govmmQemu.QMP) error
   136  
   137  	// appendPCIeRootPortDevice appends a pcie-root-port device to pcie.0 bus
   138  	appendPCIeRootPortDevice(devices []govmmQemu.Device, number uint32) []govmmQemu.Device
   139  
   140  	// append vIOMMU device
   141  	appendIOMMU(devices []govmmQemu.Device) ([]govmmQemu.Device, error)
   142  }
   143  
   144  type qemuArchBase struct {
   145  	machineType           string
   146  	memoryOffset          uint32
   147  	nestedRun             bool
   148  	vhost                 bool
   149  	disableNvdimm         bool
   150  	dax                   bool
   151  	networkIndex          int
   152  	qemuPaths             map[string]string
   153  	supportedQemuMachines []govmmQemu.Machine
   154  	kernelParamsNonDebug  []Param
   155  	kernelParamsDebug     []Param
   156  	kernelParams          []Param
   157  	Bridges               []types.Bridge
   158  	PFlash                []string
   159  }
   160  
   161  const (
   162  	defaultCores       uint32 = 1
   163  	defaultThreads     uint32 = 1
   164  	defaultCPUModel           = "host"
   165  	defaultBridgeBus          = "pcie.0"
   166  	defaultPCBridgeBus        = "pci.0"
   167  	maxDevIDSize              = 31
   168  	defaultMsize9p            = 8192
   169  	pcieRootPortPrefix        = "rp"
   170  )
   171  
   172  // This is the PCI start address assigned to the first bridge that
   173  // is added on the qemu command line. In case of x86_64, the first two PCI
   174  // addresses (0 and 1) are used by the platform while in case of ARM, address
   175  // 0 is reserved.
   176  const bridgePCIStartAddr = 2
   177  
   178  const (
   179  	// QemuPCLite is the QEMU pc-lite machine type for amd64
   180  	QemuPCLite = "pc-lite"
   181  
   182  	// QemuPC is the QEMU pc machine type for amd64
   183  	QemuPC = "pc"
   184  
   185  	// QemuQ35 is the QEMU Q35 machine type for amd64
   186  	QemuQ35 = "q35"
   187  
   188  	// QemuMicrovm is the QEMU microvm machine type for amd64
   189  	QemuMicrovm = "microvm"
   190  
   191  	// QemuVirt is the QEMU virt machine type for aarch64 or amd64
   192  	QemuVirt = "virt"
   193  
   194  	// QemuPseries is a QEMU virt machine type for ppc64le
   195  	QemuPseries = "pseries"
   196  
   197  	// QemuCCWVirtio is a QEMU virt machine type for for s390x
   198  	QemuCCWVirtio = "s390-ccw-virtio"
   199  
   200  	qmpCapMigrationIgnoreShared = "x-ignore-shared"
   201  
   202  	qemuNvdimmOption = "nvdimm"
   203  )
   204  
   205  // kernelParamsNonDebug is a list of the default kernel
   206  // parameters that will be used in standard (non-debug) mode.
   207  var kernelParamsNonDebug = []Param{
   208  	{"quiet", ""},
   209  }
   210  
   211  // kernelParamsSystemdNonDebug is a list of the default systemd related
   212  // kernel parameters that will be used in standard (non-debug) mode.
   213  var kernelParamsSystemdNonDebug = []Param{
   214  	{"systemd.show_status", "false"},
   215  }
   216  
   217  // kernelParamsDebug is a list of the default kernel
   218  // parameters that will be used in debug mode (as much boot output as
   219  // possible).
   220  var kernelParamsDebug = []Param{
   221  	{"debug", ""},
   222  }
   223  
   224  // kernelParamsSystemdDebug is a list of the default systemd related kernel
   225  // parameters that will be used in debug mode (as much boot output as
   226  // possible).
   227  var kernelParamsSystemdDebug = []Param{
   228  	{"systemd.show_status", "true"},
   229  	{"systemd.log_level", "debug"},
   230  }
   231  
   232  func (q *qemuArchBase) enableNestingChecks() {
   233  	q.nestedRun = true
   234  }
   235  
   236  func (q *qemuArchBase) disableNestingChecks() {
   237  	q.nestedRun = false
   238  }
   239  
   240  func (q *qemuArchBase) runNested() bool {
   241  	return q.nestedRun
   242  }
   243  
   244  func (q *qemuArchBase) enableVhostNet() {
   245  	q.vhost = true
   246  }
   247  
   248  func (q *qemuArchBase) disableVhostNet() {
   249  	q.vhost = false
   250  }
   251  
   252  func (q *qemuArchBase) machine() (govmmQemu.Machine, error) {
   253  	for _, m := range q.supportedQemuMachines {
   254  		if m.Type == q.machineType {
   255  			return m, nil
   256  		}
   257  	}
   258  
   259  	return govmmQemu.Machine{}, fmt.Errorf("unrecognised machine type: %v", q.machineType)
   260  }
   261  
   262  func (q *qemuArchBase) qemuPath() (string, error) {
   263  	p, ok := q.qemuPaths[q.machineType]
   264  	if !ok {
   265  		return "", fmt.Errorf("Unknown machine type: %s", q.machineType)
   266  	}
   267  
   268  	return p, nil
   269  }
   270  
   271  func (q *qemuArchBase) kernelParameters(debug bool) []Param {
   272  	params := q.kernelParams
   273  
   274  	if debug {
   275  		params = append(params, q.kernelParamsDebug...)
   276  	} else {
   277  		params = append(params, q.kernelParamsNonDebug...)
   278  	}
   279  
   280  	return params
   281  }
   282  
   283  func (q *qemuArchBase) capabilities() types.Capabilities {
   284  	var caps types.Capabilities
   285  	caps.SetBlockDeviceHotplugSupport()
   286  	caps.SetMultiQueueSupport()
   287  	caps.SetFsSharingSupport()
   288  	return caps
   289  }
   290  
   291  func (q *qemuArchBase) bridges(number uint32) {
   292  	for i := uint32(0); i < number; i++ {
   293  		q.Bridges = append(q.Bridges, types.NewBridge(types.PCI, fmt.Sprintf("%s-bridge-%d", types.PCI, i), make(map[uint32]string), 0))
   294  	}
   295  }
   296  
   297  func (q *qemuArchBase) cpuTopology(vcpus, maxvcpus uint32) govmmQemu.SMP {
   298  	smp := govmmQemu.SMP{
   299  		CPUs:    vcpus,
   300  		Sockets: maxvcpus,
   301  		Cores:   defaultCores,
   302  		Threads: defaultThreads,
   303  		MaxCPUs: maxvcpus,
   304  	}
   305  
   306  	return smp
   307  }
   308  
   309  func (q *qemuArchBase) cpuModel() string {
   310  	return defaultCPUModel
   311  }
   312  
   313  func (q *qemuArchBase) memoryTopology(memoryMb, hostMemoryMb uint64, slots uint8) govmmQemu.Memory {
   314  	memMax := fmt.Sprintf("%dM", hostMemoryMb)
   315  	mem := fmt.Sprintf("%dM", memoryMb)
   316  	memory := govmmQemu.Memory{
   317  		Size:   mem,
   318  		Slots:  slots,
   319  		MaxMem: memMax,
   320  	}
   321  
   322  	return memory
   323  }
   324  
   325  func (q *qemuArchBase) appendConsole(devices []govmmQemu.Device, path string) ([]govmmQemu.Device, error) {
   326  	serial := govmmQemu.SerialDevice{
   327  		Driver:        govmmQemu.VirtioSerial,
   328  		ID:            "serial0",
   329  		DisableModern: q.nestedRun,
   330  		MaxPorts:      uint(2),
   331  	}
   332  
   333  	devices = append(devices, serial)
   334  
   335  	console := govmmQemu.CharDevice{
   336  		Driver:   govmmQemu.Console,
   337  		Backend:  govmmQemu.Socket,
   338  		DeviceID: "console0",
   339  		ID:       "charconsole0",
   340  		Path:     path,
   341  	}
   342  
   343  	devices = append(devices, console)
   344  
   345  	return devices, nil
   346  }
   347  
   348  func genericImage(path string) (config.BlockDrive, error) {
   349  	if _, err := os.Stat(path); os.IsNotExist(err) {
   350  		return config.BlockDrive{}, err
   351  	}
   352  
   353  	randBytes, err := utils.GenerateRandomBytes(8)
   354  	if err != nil {
   355  		return config.BlockDrive{}, err
   356  	}
   357  
   358  	id := utils.MakeNameID("image", hex.EncodeToString(randBytes), maxDevIDSize)
   359  
   360  	drive := config.BlockDrive{
   361  		File:     path,
   362  		Format:   "raw",
   363  		ID:       id,
   364  		ShareRW:  true,
   365  		ReadOnly: true,
   366  	}
   367  
   368  	return drive, nil
   369  }
   370  
   371  func (q *qemuArchBase) appendNvdimmImage(devices []govmmQemu.Device, path string) ([]govmmQemu.Device, error) {
   372  	imageFile, err := os.Open(path)
   373  	if err != nil {
   374  		return nil, err
   375  	}
   376  	defer imageFile.Close()
   377  
   378  	imageStat, err := imageFile.Stat()
   379  	if err != nil {
   380  		return nil, err
   381  	}
   382  
   383  	object := govmmQemu.Object{
   384  		Driver:   govmmQemu.NVDIMM,
   385  		Type:     govmmQemu.MemoryBackendFile,
   386  		DeviceID: "nv0",
   387  		ID:       "mem0",
   388  		MemPath:  path,
   389  		Size:     (uint64)(imageStat.Size()),
   390  	}
   391  
   392  	devices = append(devices, object)
   393  
   394  	return devices, nil
   395  }
   396  
   397  func (q *qemuArchBase) appendImage(devices []govmmQemu.Device, path string) ([]govmmQemu.Device, error) {
   398  	return q.appendBlockImage(devices, path)
   399  }
   400  
   401  func (q *qemuArchBase) appendBlockImage(devices []govmmQemu.Device, path string) ([]govmmQemu.Device, error) {
   402  	drive, err := genericImage(path)
   403  	if err != nil {
   404  		return nil, err
   405  	}
   406  	devices, err = q.appendBlockDevice(devices, drive)
   407  	if err != nil {
   408  		return nil, err
   409  	}
   410  	return devices, nil
   411  }
   412  
   413  func genericSCSIController(enableIOThreads, nestedRun bool) (govmmQemu.SCSIController, *govmmQemu.IOThread) {
   414  	scsiController := govmmQemu.SCSIController{
   415  		ID:            scsiControllerID,
   416  		DisableModern: nestedRun,
   417  	}
   418  
   419  	var t *govmmQemu.IOThread
   420  
   421  	if enableIOThreads {
   422  		randBytes, _ := utils.GenerateRandomBytes(8)
   423  
   424  		t = &govmmQemu.IOThread{
   425  			ID: fmt.Sprintf("%s-%s", "iothread", hex.EncodeToString(randBytes)),
   426  		}
   427  
   428  		scsiController.IOThread = t.ID
   429  	}
   430  
   431  	return scsiController, t
   432  }
   433  
   434  func (q *qemuArchBase) appendSCSIController(devices []govmmQemu.Device, enableIOThreads bool) ([]govmmQemu.Device, *govmmQemu.IOThread, error) {
   435  	d, t := genericSCSIController(enableIOThreads, q.nestedRun)
   436  	devices = append(devices, d)
   437  	return devices, t, nil
   438  }
   439  
   440  // appendBridges appends to devices the given bridges
   441  func (q *qemuArchBase) appendBridges(devices []govmmQemu.Device) []govmmQemu.Device {
   442  	for idx, b := range q.Bridges {
   443  		if b.Type == types.CCW {
   444  			continue
   445  		}
   446  		t := govmmQemu.PCIBridge
   447  		if b.Type == types.PCIE {
   448  			t = govmmQemu.PCIEBridge
   449  		}
   450  
   451  		q.Bridges[idx].Addr = bridgePCIStartAddr + idx
   452  
   453  		devices = append(devices,
   454  			govmmQemu.BridgeDevice{
   455  				Type: t,
   456  				Bus:  defaultBridgeBus,
   457  				ID:   b.ID,
   458  				// Each bridge is required to be assigned a unique chassis id > 0
   459  				Chassis: idx + 1,
   460  				SHPC:    true,
   461  				Addr:    strconv.FormatInt(int64(q.Bridges[idx].Addr), 10),
   462  			},
   463  		)
   464  	}
   465  
   466  	return devices
   467  }
   468  
   469  func generic9PVolume(volume types.Volume, nestedRun bool) govmmQemu.FSDevice {
   470  	devID := fmt.Sprintf("extra-9p-%s", volume.MountTag)
   471  	if len(devID) > maxDevIDSize {
   472  		devID = devID[:maxDevIDSize]
   473  	}
   474  
   475  	return govmmQemu.FSDevice{
   476  		Driver:        govmmQemu.Virtio9P,
   477  		FSDriver:      govmmQemu.Local,
   478  		ID:            devID,
   479  		Path:          volume.HostPath,
   480  		MountTag:      volume.MountTag,
   481  		SecurityModel: govmmQemu.None,
   482  		DisableModern: nestedRun,
   483  		Multidev:      govmmQemu.Remap,
   484  	}
   485  }
   486  
   487  func genericAppend9PVolume(devices []govmmQemu.Device, volume types.Volume, nestedRun bool) (govmmQemu.FSDevice, error) {
   488  	d := generic9PVolume(volume, nestedRun)
   489  	return d, nil
   490  }
   491  
   492  func (q *qemuArchBase) append9PVolume(devices []govmmQemu.Device, volume types.Volume) ([]govmmQemu.Device, error) {
   493  	if volume.MountTag == "" || volume.HostPath == "" {
   494  		return devices, nil
   495  	}
   496  
   497  	d, err := genericAppend9PVolume(devices, volume, q.nestedRun)
   498  	if err != nil {
   499  		return nil, err
   500  	}
   501  
   502  	devices = append(devices, d)
   503  	return devices, nil
   504  }
   505  
   506  func (q *qemuArchBase) appendSocket(devices []govmmQemu.Device, socket types.Socket) []govmmQemu.Device {
   507  	devID := socket.ID
   508  	if len(devID) > maxDevIDSize {
   509  		devID = devID[:maxDevIDSize]
   510  	}
   511  
   512  	devices = append(devices,
   513  		govmmQemu.CharDevice{
   514  			Driver:   govmmQemu.VirtioSerialPort,
   515  			Backend:  govmmQemu.Socket,
   516  			DeviceID: socket.DeviceID,
   517  			ID:       devID,
   518  			Path:     socket.HostPath,
   519  			Name:     socket.Name,
   520  		},
   521  	)
   522  
   523  	return devices
   524  }
   525  
   526  func (q *qemuArchBase) appendVSock(devices []govmmQemu.Device, vsock types.VSock) ([]govmmQemu.Device, error) {
   527  	devices = append(devices,
   528  		govmmQemu.VSOCKDevice{
   529  			ID:            fmt.Sprintf("vsock-%d", vsock.ContextID),
   530  			ContextID:     vsock.ContextID,
   531  			VHostFD:       vsock.VhostFd,
   532  			DisableModern: q.nestedRun,
   533  		},
   534  	)
   535  
   536  	return devices, nil
   537  
   538  }
   539  
   540  func networkModelToQemuType(model NetInterworkingModel) govmmQemu.NetDeviceType {
   541  	switch model {
   542  	case NetXConnectMacVtapModel:
   543  		return govmmQemu.MACVTAP
   544  	default:
   545  		//TAP should work for most other cases
   546  		return govmmQemu.TAP
   547  	}
   548  }
   549  
   550  func genericNetwork(endpoint Endpoint, vhost, nestedRun bool, index int) (govmmQemu.NetDevice, error) {
   551  	var d govmmQemu.NetDevice
   552  	switch ep := endpoint.(type) {
   553  	case *VethEndpoint, *BridgedMacvlanEndpoint, *IPVlanEndpoint:
   554  		netPair := ep.NetworkPair()
   555  		d = govmmQemu.NetDevice{
   556  			Type:          networkModelToQemuType(netPair.NetInterworkingModel),
   557  			Driver:        govmmQemu.VirtioNet,
   558  			ID:            fmt.Sprintf("network-%d", index),
   559  			IFName:        netPair.TAPIface.Name,
   560  			MACAddress:    netPair.TAPIface.HardAddr,
   561  			DownScript:    "no",
   562  			Script:        "no",
   563  			VHost:         vhost,
   564  			DisableModern: nestedRun,
   565  			FDs:           netPair.VMFds,
   566  			VhostFDs:      netPair.VhostFds,
   567  		}
   568  	case *MacvtapEndpoint:
   569  		d = govmmQemu.NetDevice{
   570  			Type:          govmmQemu.MACVTAP,
   571  			Driver:        govmmQemu.VirtioNet,
   572  			ID:            fmt.Sprintf("network-%d", index),
   573  			IFName:        ep.Name(),
   574  			MACAddress:    ep.HardwareAddr(),
   575  			DownScript:    "no",
   576  			Script:        "no",
   577  			VHost:         vhost,
   578  			DisableModern: nestedRun,
   579  			FDs:           ep.VMFds,
   580  			VhostFDs:      ep.VhostFds,
   581  		}
   582  	case *TuntapEndpoint:
   583  		netPair := ep.NetworkPair()
   584  		d = govmmQemu.NetDevice{
   585  			Type:          govmmQemu.NetDeviceType("tap"),
   586  			Driver:        govmmQemu.VirtioNet,
   587  			ID:            fmt.Sprintf("network-%d", index),
   588  			IFName:        netPair.TAPIface.Name,
   589  			MACAddress:    netPair.TAPIface.HardAddr,
   590  			DownScript:    "no",
   591  			Script:        "no",
   592  			VHost:         vhost,
   593  			DisableModern: nestedRun,
   594  			FDs:           netPair.VMFds,
   595  			VhostFDs:      netPair.VhostFds,
   596  		}
   597  	default:
   598  		return govmmQemu.NetDevice{}, fmt.Errorf("Unknown type for endpoint")
   599  	}
   600  
   601  	return d, nil
   602  }
   603  
   604  func (q *qemuArchBase) appendNetwork(devices []govmmQemu.Device, endpoint Endpoint) ([]govmmQemu.Device, error) {
   605  	d, err := genericNetwork(endpoint, q.vhost, q.nestedRun, q.networkIndex)
   606  	if err != nil {
   607  		return devices, fmt.Errorf("Failed to append network %v", err)
   608  	}
   609  	q.networkIndex++
   610  	devices = append(devices, d)
   611  	return devices, nil
   612  }
   613  
   614  func genericBlockDevice(drive config.BlockDrive, nestedRun bool) (govmmQemu.BlockDevice, error) {
   615  	if drive.File == "" || drive.ID == "" || drive.Format == "" {
   616  		return govmmQemu.BlockDevice{}, fmt.Errorf("Empty File, ID or Format for drive %v", drive)
   617  	}
   618  
   619  	if len(drive.ID) > maxDevIDSize {
   620  		drive.ID = drive.ID[:maxDevIDSize]
   621  	}
   622  
   623  	return govmmQemu.BlockDevice{
   624  		Driver:        govmmQemu.VirtioBlock,
   625  		ID:            drive.ID,
   626  		File:          drive.File,
   627  		AIO:           govmmQemu.Threads,
   628  		Format:        govmmQemu.BlockDeviceFormat(drive.Format),
   629  		Interface:     "none",
   630  		DisableModern: nestedRun,
   631  		ShareRW:       drive.ShareRW,
   632  		ReadOnly:      drive.ReadOnly,
   633  	}, nil
   634  }
   635  
   636  func (q *qemuArchBase) appendBlockDevice(devices []govmmQemu.Device, drive config.BlockDrive) ([]govmmQemu.Device, error) {
   637  	d, err := genericBlockDevice(drive, q.nestedRun)
   638  	if err != nil {
   639  		return devices, fmt.Errorf("Failed to append block device %v", err)
   640  	}
   641  	devices = append(devices, d)
   642  	return devices, nil
   643  }
   644  
   645  func (q *qemuArchBase) appendVhostUserDevice(devices []govmmQemu.Device, attr config.VhostUserDeviceAttrs) ([]govmmQemu.Device, error) {
   646  	qemuVhostUserDevice := govmmQemu.VhostUserDevice{}
   647  
   648  	switch attr.Type {
   649  	case config.VhostUserNet:
   650  		qemuVhostUserDevice.TypeDevID = utils.MakeNameID("net", attr.DevID, maxDevIDSize)
   651  		qemuVhostUserDevice.Address = attr.MacAddress
   652  		qemuVhostUserDevice.VhostUserType = govmmQemu.VhostUserNet
   653  	case config.VhostUserSCSI:
   654  		qemuVhostUserDevice.TypeDevID = utils.MakeNameID("scsi", attr.DevID, maxDevIDSize)
   655  		qemuVhostUserDevice.VhostUserType = govmmQemu.VhostUserSCSI
   656  	case config.VhostUserBlk:
   657  		qemuVhostUserDevice.VhostUserType = govmmQemu.VhostUserBlk
   658  	case config.VhostUserFS:
   659  		qemuVhostUserDevice.TypeDevID = utils.MakeNameID("fs", attr.DevID, maxDevIDSize)
   660  		qemuVhostUserDevice.Tag = attr.Tag
   661  		qemuVhostUserDevice.CacheSize = attr.CacheSize
   662  		qemuVhostUserDevice.VhostUserType = govmmQemu.VhostUserFS
   663  	}
   664  
   665  	qemuVhostUserDevice.SocketPath = attr.SocketPath
   666  	qemuVhostUserDevice.CharDevID = utils.MakeNameID("char", attr.DevID, maxDevIDSize)
   667  
   668  	devices = append(devices, qemuVhostUserDevice)
   669  
   670  	return devices, nil
   671  }
   672  
   673  func (q *qemuArchBase) appendVFIODevice(devices []govmmQemu.Device, vfioDev config.VFIODev) []govmmQemu.Device {
   674  	if vfioDev.BDF == "" {
   675  		return devices
   676  	}
   677  
   678  	devices = append(devices,
   679  		govmmQemu.VFIODevice{
   680  			BDF:      vfioDev.BDF,
   681  			VendorID: vfioDev.VendorID,
   682  			DeviceID: vfioDev.DeviceID,
   683  			Bus:      vfioDev.Bus,
   684  		},
   685  	)
   686  
   687  	return devices
   688  }
   689  
   690  func (q *qemuArchBase) appendRNGDevice(devices []govmmQemu.Device, rngDev config.RNGDev) ([]govmmQemu.Device, error) {
   691  	devices = append(devices,
   692  		govmmQemu.RngDevice{
   693  			ID:       rngDev.ID,
   694  			Filename: rngDev.Filename,
   695  		},
   696  	)
   697  
   698  	return devices, nil
   699  }
   700  
   701  func (q *qemuArchBase) handleImagePath(config HypervisorConfig) {
   702  	if config.ImagePath != "" {
   703  		kernelRootParams := commonVirtioblkKernelRootParams
   704  		if !q.disableNvdimm {
   705  			for i := range q.supportedQemuMachines {
   706  				q.supportedQemuMachines[i].Options = strings.Join([]string{
   707  					q.supportedQemuMachines[i].Options,
   708  					qemuNvdimmOption,
   709  				}, ",")
   710  			}
   711  			if q.dax {
   712  				kernelRootParams = commonNvdimmKernelRootParams
   713  			} else {
   714  				kernelRootParams = commonNvdimmNoDAXKernelRootParams
   715  			}
   716  		}
   717  		q.kernelParams = append(q.kernelParams, kernelRootParams...)
   718  		q.kernelParamsNonDebug = append(q.kernelParamsNonDebug, kernelParamsSystemdNonDebug...)
   719  		q.kernelParamsDebug = append(q.kernelParamsDebug, kernelParamsSystemdDebug...)
   720  	}
   721  }
   722  
   723  func (q *qemuArchBase) supportGuestMemoryHotplug() bool {
   724  	return true
   725  }
   726  
   727  func (q *qemuArchBase) setIgnoreSharedMemoryMigrationCaps(ctx context.Context, qmp *govmmQemu.QMP) error {
   728  	err := qmp.ExecSetMigrationCaps(ctx, []map[string]interface{}{
   729  		{
   730  			"capability": qmpCapMigrationIgnoreShared,
   731  			"state":      true,
   732  		},
   733  	})
   734  	return err
   735  }
   736  
   737  func (q *qemuArchBase) addDeviceToBridge(ID string, t types.Type) (string, types.Bridge, error) {
   738  	var err error
   739  	var addr uint32
   740  
   741  	if len(q.Bridges) == 0 {
   742  		return "", types.Bridge{}, errors.New("failed to get available address from bridges")
   743  	}
   744  
   745  	// looking for an empty address in the bridges
   746  	for _, b := range q.Bridges {
   747  		if t != b.Type {
   748  			continue
   749  		}
   750  		addr, err = b.AddDevice(ID)
   751  		if err == nil {
   752  			switch t {
   753  			case types.CCW:
   754  				return fmt.Sprintf("%04x", addr), b, nil
   755  			case types.PCI, types.PCIE:
   756  				return fmt.Sprintf("%02x", addr), b, nil
   757  			}
   758  		}
   759  	}
   760  
   761  	return "", types.Bridge{}, fmt.Errorf("no more bridge slots available")
   762  }
   763  
   764  func (q *qemuArchBase) removeDeviceFromBridge(ID string) error {
   765  	var err error
   766  	for _, b := range q.Bridges {
   767  		err = b.RemoveDevice(ID)
   768  		if err == nil {
   769  			// device was removed correctly
   770  			return nil
   771  		}
   772  	}
   773  
   774  	return err
   775  }
   776  
   777  func (q *qemuArchBase) getBridges() []types.Bridge {
   778  	return q.Bridges
   779  }
   780  
   781  func (q *qemuArchBase) setBridges(bridges []types.Bridge) {
   782  	q.Bridges = bridges
   783  }
   784  
   785  func (q *qemuArchBase) addBridge(b types.Bridge) {
   786  	q.Bridges = append(q.Bridges, b)
   787  }
   788  
   789  // appendPCIeRootPortDevice appends to devices the given pcie-root-port
   790  func (q *qemuArchBase) appendPCIeRootPortDevice(devices []govmmQemu.Device, number uint32) []govmmQemu.Device {
   791  	return genericAppendPCIeRootPort(devices, number, q.machineType)
   792  
   793  }
   794  
   795  // appendIOMMU appends a virtual IOMMU device
   796  func (q *qemuArchBase) appendIOMMU(devices []govmmQemu.Device) ([]govmmQemu.Device, error) {
   797  	switch q.machineType {
   798  	case QemuQ35:
   799  		iommu := govmmQemu.IommuDev{
   800  			Intremap:    true,
   801  			DeviceIotlb: true,
   802  			CachingMode: true,
   803  		}
   804  
   805  		devices = append(devices, iommu)
   806  		return devices, nil
   807  	default:
   808  		return devices, fmt.Errorf("Machine Type %s does not support vIOMMU", q.machineType)
   809  	}
   810  }
   811  
   812  func (q *qemuArchBase) getPFlash() ([]string, error) {
   813  	return q.PFlash, nil
   814  }
   815  
   816  func (q *qemuArchBase) setPFlash(p []string) {
   817  	q.PFlash = p
   818  }