// Copyright (c) 2018 IBM // // SPDX-License-Identifier: Apache-2.0 // package virtcontainers import ( "fmt" govmmQemu "github.com/intel/govmm/qemu" "github.com/kata-containers/runtime/virtcontainers/device/config" "github.com/kata-containers/runtime/virtcontainers/types" "time" ) type qemuS390x struct { // inherit from qemuArchBase, overwrite methods if needed qemuArchBase } const defaultQemuPath = "/usr/bin/qemu-system-s390x" const defaultQemuMachineType = QemuCCWVirtio const defaultQemuMachineOptions = "accel=kvm" const virtioSerialCCW = "virtio-serial-ccw" const qmpMigrationWaitTimeout = 5 * time.Second var qemuPaths = map[string]string{ QemuCCWVirtio: defaultQemuPath, } // Verify needed parameters var kernelParams = []Param{ {"console", "ttysclp0"}, } var kernelRootParams = commonVirtioblkKernelRootParams var ccwbridge = types.NewBridge(types.CCW, "", make(map[uint32]string, types.CCWBridgeMaxCapacity), 0) var supportedQemuMachines = []govmmQemu.Machine{ { Type: QemuCCWVirtio, Options: defaultQemuMachineOptions, }, } // MaxQemuVCPUs returns the maximum number of vCPUs supported func MaxQemuVCPUs() uint32 { // Max number of virtual Cpu defined in qemu. See // https://github.com/qemu/qemu/blob/80422b00196a7af4c6efb628fae0ad8b644e98af/target/s390x/cpu.h#L55 // #define S390_MAX_CPUS 248 return uint32(248) } func newQemuArch(config HypervisorConfig) qemuArch { machineType := config.HypervisorMachineType if machineType == "" { machineType = defaultQemuMachineType } q := &qemuS390x{ qemuArchBase{ machineType: machineType, memoryOffset: config.MemOffset, qemuPaths: qemuPaths, supportedQemuMachines: supportedQemuMachines, kernelParamsNonDebug: kernelParamsNonDebug, kernelParamsDebug: kernelParamsDebug, kernelParams: kernelParams, }, } // Set first bridge type to CCW q.Bridges = append(q.Bridges, ccwbridge) if config.ImagePath != "" { q.kernelParams = append(q.kernelParams, kernelRootParams...) q.kernelParamsNonDebug = append(q.kernelParamsNonDebug, kernelParamsSystemdNonDebug...) q.kernelParamsDebug = append(q.kernelParamsDebug, kernelParamsSystemdDebug...) } return q } func (q *qemuS390x) bridges(number uint32) { q.Bridges = genericBridges(number, q.machineType) } // appendConsole appends a console to devices. // The function has been overwriten to correctly set the driver to the CCW device func (q *qemuS390x) appendConsole(devices []govmmQemu.Device, path string) ([]govmmQemu.Device, error) { id := "serial0" addr, b, err := q.addDeviceToBridge(id, types.CCW) if err != nil { return devices, fmt.Errorf("Failed to append console %v", err) } var devno string devno, err = b.AddressFormatCCW(addr) if err != nil { return devices, fmt.Errorf("Failed to append console %v", err) } serial := govmmQemu.SerialDevice{ Driver: virtioSerialCCW, ID: id, DisableModern: q.nestedRun, DevNo: devno, } devices = append(devices, serial) console := govmmQemu.CharDevice{ Driver: govmmQemu.Console, Backend: govmmQemu.Socket, DeviceID: "console0", ID: "charconsole0", Path: path, } devices = append(devices, console) return devices, nil } func (q *qemuS390x) appendImage(devices []govmmQemu.Device, path string) ([]govmmQemu.Device, error) { drive, err := genericImage(path) if err != nil { return nil, err } drive.ShareRW = true devices, err = q.appendBlockDevice(devices, drive) if err != nil { return nil, err } return devices, nil } func (q *qemuS390x) appendBlockDevice(devices []govmmQemu.Device, drive config.BlockDrive) ([]govmmQemu.Device, error) { d, err := genericBlockDevice(drive, false) if err != nil { return devices, fmt.Errorf("Failed to append blk-dev %v", err) } addr, b, err := q.addDeviceToBridge(drive.ID, types.CCW) if err != nil { return devices, fmt.Errorf("Failed to append blk-dev %v", err) } d.DevNo, err = b.AddressFormatCCW(addr) if err != nil { return devices, fmt.Errorf("Failed to append blk-dev %v", err) } devices = append(devices, d) return devices, nil } // appendVhostUserDevice throws an error if vhost devices are tried to be used. // See issue https://github.com/kata-containers/runtime/issues/659 func (q *qemuS390x) appendVhostUserDevice(devices []govmmQemu.Device, attr config.VhostUserDeviceAttrs) ([]govmmQemu.Device, error) { return nil, fmt.Errorf("No vhost-user devices supported on s390x") } // supportGuestMemoryHotplug return false for s390x architecture. The pc-dimm backend device for s390x // is not support. PC-DIMM is not listed in the devices supported by qemu-system-s390x -device help func (q *qemuS390x) supportGuestMemoryHotplug() bool { return false } func (q *qemuS390x) appendNetwork(devices []govmmQemu.Device, endpoint Endpoint) ([]govmmQemu.Device, error) { d, err := genericNetwork(endpoint, false, false, q.networkIndex) if err != nil { return devices, fmt.Errorf("Failed to append network %v", err) } q.networkIndex++ addr, b, err := q.addDeviceToBridge(d.ID, types.CCW) if err != nil { return devices, fmt.Errorf("Failed to append network %v", err) } d.DevNo, err = b.AddressFormatCCW(addr) if err != nil { return devices, fmt.Errorf("Failed to append network %v", err) } devices = append(devices, d) return devices, nil } func (q *qemuS390x) appendRNGDevice(devices []govmmQemu.Device, rngDev config.RNGDev) ([]govmmQemu.Device, error) { addr, b, err := q.addDeviceToBridge(rngDev.ID, types.CCW) if err != nil { return devices, fmt.Errorf("Failed to append RNG-Device %v", err) } var devno string devno, err = b.AddressFormatCCW(addr) if err != nil { return devices, fmt.Errorf("Failed to append RNG-Device %v", err) } devices = append(devices, govmmQemu.RngDevice{ ID: rngDev.ID, Filename: rngDev.Filename, DevNo: devno, }, ) return devices, nil } func (q *qemuS390x) append9PVolume(devices []govmmQemu.Device, volume types.Volume) ([]govmmQemu.Device, error) { if volume.MountTag == "" || volume.HostPath == "" { return devices, nil } d := generic9PVolume(volume, false) addr, b, err := q.addDeviceToBridge(d.ID, types.CCW) if err != nil { return devices, fmt.Errorf("Failed to append 9p-Volume %v", err) } d.DevNo, err = b.AddressFormatCCW(addr) if err != nil { return devices, fmt.Errorf("Failed to append 9p-Volume %v", err) } devices = append(devices, d) return devices, nil } // appendBridges appends to devices the given bridges func (q *qemuS390x) appendBridges(devices []govmmQemu.Device) []govmmQemu.Device { return genericAppendBridges(devices, q.Bridges, q.machineType) } func (q *qemuS390x) appendSCSIController(devices []govmmQemu.Device, enableIOThreads bool) ([]govmmQemu.Device, *govmmQemu.IOThread, error) { d, t := genericSCSIController(enableIOThreads, q.nestedRun) addr, b, err := q.addDeviceToBridge(d.ID, types.CCW) if err != nil { return devices, nil, fmt.Errorf("Failed to append scsi-controller %v", err) } d.DevNo, err = b.AddressFormatCCW(addr) if err != nil { return devices, nil, fmt.Errorf("Failed to append scsi-controller %v", err) } devices = append(devices, d) return devices, t, nil } func (q *qemuS390x) appendVSock(devices []govmmQemu.Device, vsock types.VSock) ([]govmmQemu.Device, error) { var devno string id := fmt.Sprintf("vsock-%d", vsock.ContextID) addr, b, err := q.addDeviceToBridge(id, types.CCW) if err != nil { return devices, fmt.Errorf("Failed to append VSock: %v", err) } devno, err = b.AddressFormatCCW(addr) if err != nil { return devices, fmt.Errorf("Failed to append VSock: %v", err) } devices = append(devices, govmmQemu.VSOCKDevice{ ID: id, ContextID: vsock.ContextID, VHostFD: vsock.VhostFd, DisableModern: false, DevNo: devno, }, ) return devices, nil }