From d5fb32c2b9221d9a36814ecb6439e22d19eded92 Mon Sep 17 00:00:00 2001 From: Alice Frosi Date: Mon, 6 Aug 2018 15:06:01 +0200 Subject: [PATCH] s390x: add support for s390x The PR adds the support for s390x. The VirtioNetPCI has been changed to VirtioNet. The generalization allows to set the VirtioNet to the correct CCW device for s390x. Fixes: #666 Co-authored-by: Yash D Jain ydjainopensource@gmail.com Signed-off-by: Alice Frosi --- arch/s390x-options.mk | 12 ++ cli/kata-check_data_s390x_test.go | 21 ++ cli/kata-check_s390x.go | 125 +++++++++++ cli/kata-check_s390x_test.go | 285 ++++++++++++++++++++++++++ cli/kata-env_s390x_test.go | 91 ++++++++ cli/utils_s390x.go | 12 ++ versions.yaml | 6 + virtcontainers/hypervisor.go | 2 +- virtcontainers/qemu.go | 12 +- virtcontainers/qemu_arch_base.go | 7 +- virtcontainers/qemu_arch_base_test.go | 4 +- virtcontainers/qemu_s390x.go | 120 +++++++++++ virtcontainers/qemu_s390x_test.go | 53 +++++ 13 files changed, 743 insertions(+), 7 deletions(-) create mode 100644 arch/s390x-options.mk create mode 100644 cli/kata-check_data_s390x_test.go create mode 100644 cli/kata-check_s390x.go create mode 100644 cli/kata-check_s390x_test.go create mode 100644 cli/kata-env_s390x_test.go create mode 100644 cli/utils_s390x.go create mode 100644 virtcontainers/qemu_s390x.go create mode 100644 virtcontainers/qemu_s390x_test.go diff --git a/arch/s390x-options.mk b/arch/s390x-options.mk new file mode 100644 index 0000000000..3256e46288 --- /dev/null +++ b/arch/s390x-options.mk @@ -0,0 +1,12 @@ +# Copyright (c) 2018 IBM +# +# SPDX-License-Identifier: Apache-2.0 +# + +# s390x settings + +MACHINETYPE := s390-ccw-virtio +KERNELPARAMS := +MACHINEACCELERATORS := + +QEMUCMD := qemu-system-s390x diff --git a/cli/kata-check_data_s390x_test.go b/cli/kata-check_data_s390x_test.go new file mode 100644 index 0000000000..258f1e1b0c --- /dev/null +++ b/cli/kata-check_data_s390x_test.go @@ -0,0 +1,21 @@ +// Copyright (c) 2018 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +const testCPUInfoTemplate = ` +vendor_id : IBM/S390 +# processors : 4 +bogomips per cpu: 20325.00 +max thread id : 0 +features : esan3 zarch stfle msa ldisp eimm dfp edat etf3eh highgprs te vx sie +cache0 : level=1 type=Data scope=Private size=128K line_size=256 associativity=8 +cache1 : level=1 type=Instruction scope=Private size=96K line_size=256 associativity=6 +cache2 : level=2 type=Data scope=Private size=2048K line_size=256 associativity=8 +cache3 : level=2 type=Instruction scope=Private size=2048K line_size=256 associativity=8 +cache4 : level=3 type=Unified scope=Shared size=65536K line_size=256 associativity=16 +cache5 : level=4 type=Unified scope=Shared size=491520K line_size=256 associativity=30 +processor 0: version = FF, identification = FFFFFF, machine = 2964 +` diff --git a/cli/kata-check_s390x.go b/cli/kata-check_s390x.go new file mode 100644 index 0000000000..bb940615d0 --- /dev/null +++ b/cli/kata-check_s390x.go @@ -0,0 +1,125 @@ +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + "github.com/sirupsen/logrus" + "strings" +) + +const ( + cpuFlagsTag = genericCPUFlagsTag + archCPUVendorField = genericCPUVendorField + // On s390x the cpu model is indicated by the field machine. + // Example: + // processor 0: version = FF, identification = 3FEC87, machine = 2964 + archCPUModelField = "machine" +) + +// archRequiredCPUFlags maps a CPU flag value to search for and a +// human-readable description of that value. +var archRequiredCPUFlags = map[string]string{} + +// archRequiredCPUAttribs maps a CPU (non-CPU flag) attribute value to search for +// and a human-readable description of that value. +var archRequiredCPUAttribs = map[string]string{} + +// archRequiredKernelModules maps a required module name to a human-readable +// description of the modules functionality and an optional list of +// required module parameters. +var archRequiredKernelModules = map[string]kernelModule{ + "kvm": { + desc: "Kernel-based Virtual Machine", + }, +} + +func setCPUtype() error { + return nil +} + +// kvmIsUsable determines if it will be possible to create a full virtual machine +// by creating a minimal VM and then deleting it. +func kvmIsUsable() error { + return genericKvmIsUsable() +} + +func archHostCanCreateVMContainer() error { + return kvmIsUsable() +} + +// hostIsVMContainerCapable checks to see if the host is theoretically capable +// of creating a VM container. +func hostIsVMContainerCapable(details vmContainerCapableDetails) error { + + _, err := getCPUInfo(details.cpuInfoFile) + if err != nil { + return err + } + + count, err := checkKernelModules(details.requiredKernelModules, archKernelParamHandler) + if err != nil { + return err + } + + if count == 0 { + return nil + } + + return fmt.Errorf("ERROR: %s", failMessage) + +} + +func archKernelParamHandler(onVMM bool, fields logrus.Fields, msg string) bool { + return genericArchKernelParamHandler(onVMM, fields, msg) +} + +// getS390xCPUDetails returns the cpu information +func getS390xCPUDetails() (vendor, model string, err error) { + prefixModel := "processor" + cpuinfo, err := getCPUInfo(procCPUInfo) + if err != nil { + return "", "", err + } + + lines := strings.Split(cpuinfo, "\n") + + for _, line := range lines { + if archCPUVendorField != "" { + if strings.HasPrefix(line, archCPUVendorField) { + fields := strings.Split(line, ":") + if len(fields) > 1 { + vendor = strings.TrimSpace(fields[1]) + } + } + } + if archCPUModelField != "" { + if strings.HasPrefix(line, prefixModel) { + fields := strings.Split(strings.TrimSpace(line), ",") + cpuModel := strings.Split(fields[2], "=") + model = strings.TrimSpace(cpuModel[1]) + } + } + } + + if vendor == "" { + return "", "", fmt.Errorf("cannot find vendor field in file %v", procCPUInfo) + } + + // model name is optional + if model == "" { + return "", "", fmt.Errorf("Error in parsing cpu model from %v", procCPUInfo) + } + + return vendor, model, nil +} + +func getCPUDetails() (vendor, model string, err error) { + if vendor, model, err := genericGetCPUDetails(); err == nil { + return vendor, model, nil + } + return getS390xCPUDetails() +} diff --git a/cli/kata-check_s390x_test.go b/cli/kata-check_s390x_test.go new file mode 100644 index 0000000000..f7e8fed99e --- /dev/null +++ b/cli/kata-check_s390x_test.go @@ -0,0 +1,285 @@ +// Copyright (c) 2018 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "testing" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" +) + +func setupCheckHostIsVMContainerCapable(assert *assert.Assertions, cpuInfoFile string, cpuData []testCPUData, moduleData []testModuleData) { + createModules(assert, cpuInfoFile, moduleData) + + // all the modules files have now been created, so deal with the + // cpuinfo data. + for _, d := range cpuData { + err := makeCPUInfoFile(cpuInfoFile, d.vendorID, d.flags) + assert.NoError(err) + + details := vmContainerCapableDetails{ + cpuInfoFile: cpuInfoFile, + requiredCPUFlags: archRequiredCPUFlags, + requiredCPUAttribs: archRequiredCPUAttribs, + requiredKernelModules: archRequiredKernelModules, + } + + err = hostIsVMContainerCapable(details) + if d.expectError { + assert.Error(err) + } else { + assert.NoError(err) + } + } +} + +func TestCCCheckCLIFunction(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + savedSysModuleDir := sysModuleDir + savedProcCPUInfo := procCPUInfo + + cpuInfoFile := filepath.Join(dir, "cpuinfo") + + // XXX: override + sysModuleDir = filepath.Join(dir, "sys/module") + procCPUInfo = cpuInfoFile + + defer func() { + sysModuleDir = savedSysModuleDir + procCPUInfo = savedProcCPUInfo + }() + + err = os.MkdirAll(sysModuleDir, testDirMode) + if err != nil { + t.Fatal(err) + } + + cpuData := []testCPUData{ + {"", "", false}, + } + + moduleData := []testModuleData{ + {filepath.Join(sysModuleDir, "kvm"), false, "Y"}, + } + + devNull, err := os.OpenFile(os.DevNull, os.O_WRONLY, 0666) + assert.NoError(err) + defer devNull.Close() + + savedLogOutput := kataLog.Logger.Out + + // discard normal output + kataLog.Logger.Out = devNull + + defer func() { + kataLog.Logger.Out = savedLogOutput + }() + + setupCheckHostIsVMContainerCapable(assert, cpuInfoFile, cpuData, moduleData) + + ctx := createCLIContext(nil) + ctx.App.Name = "foo" + + // create buffer to save logger output + buf := &bytes.Buffer{} + + // capture output this time + kataLog.Logger.Out = buf + + fn, ok := kataCheckCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + err = fn(ctx) + assert.NoError(err) + + output := buf.String() + + for _, m := range moduleData { + name := path.Base(m.path) + assert.True(findAnchoredString(output, name)) + } +} + +func TestArchKernelParamHandler(t *testing.T) { + assert := assert.New(t) + + type testData struct { + onVMM bool + fields logrus.Fields + msg string + expectIgnore bool + } + + data := []testData{ + {true, logrus.Fields{}, "", false}, + {false, logrus.Fields{}, "", false}, + + { + false, + logrus.Fields{ + // wrong type + "parameter": 123, + }, + "foo", + false, + }, + + { + false, + logrus.Fields{ + "parameter": "unrestricted_guest", + }, + "", + false, + }, + + { + true, + logrus.Fields{ + "parameter": "unrestricted_guest", + }, + "", + true, + }, + + { + false, + logrus.Fields{ + "parameter": "nested", + }, + "", + true, + }, + } + + for i, d := range data { + result := archKernelParamHandler(d.onVMM, d.fields, d.msg) + if d.expectIgnore { + assert.True(result, "test %d (%+v)", i, d) + } else { + assert.False(result, "test %d (%+v)", i, d) + } + } +} + +type TestDataa struct { + contents string + expectedVendor string + expectedModel string + expectError bool +} + +func TestKvmIsUsable(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + savedKvmDevice := kvmDevice + fakeKVMDevice := filepath.Join(dir, "kvm") + kvmDevice = fakeKVMDevice + + defer func() { + kvmDevice = savedKvmDevice + }() + + err = kvmIsUsable() + assert.Error(err) + + err = createEmptyFile(fakeKVMDevice) + assert.NoError(err) + + err = kvmIsUsable() + assert.Error(err) +} + +func TestGetCPUDetails(t *testing.T) { + type testData struct { + contents string + expectedVendor string + expectedModel string + expectError bool + } + + const validVendorName = "a vendor" + validVendor := fmt.Sprintf(`%s : %s`, archCPUVendorField, validVendorName) + + const validModelName = "some CPU model" + validModel := fmt.Sprintf(`processor 0: version = 00, identification = XXXXX, %s = %s`, archCPUModelField, validModelName) + + validContents := fmt.Sprintf(` +a : b +%s +foo : bar +%s +`, validVendor, validModel) + + data := []testData{ + {"", "", "", true}, + {"invalid", "", "", true}, + {archCPUVendorField, "", "", true}, + {validVendor, "", "", true}, + {validModel, "", "", true}, + {validContents, validVendorName, validModelName, false}, + } + + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + savedProcCPUInfo := procCPUInfo + + testProcCPUInfo := filepath.Join(tmpdir, "cpuinfo") + + // override + procCPUInfo = testProcCPUInfo + + defer func() { + procCPUInfo = savedProcCPUInfo + }() + + _, _, err = getCPUDetails() + // ENOENT + assert.Error(t, err) + assert.True(t, os.IsNotExist(err)) + + for _, d := range data { + err := createFile(procCPUInfo, d.contents) + assert.NoError(t, err) + + vendor, model, err := getCPUDetails() + + if d.expectError { + assert.Error(t, err, fmt.Sprintf("%+v", d)) + continue + } else { + assert.NoError(t, err, fmt.Sprintf("%+v", d)) + assert.Equal(t, d.expectedVendor, vendor) + assert.Equal(t, d.expectedModel, model) + } + } + +} diff --git a/cli/kata-env_s390x_test.go b/cli/kata-env_s390x_test.go new file mode 100644 index 0000000000..071b7bced4 --- /dev/null +++ b/cli/kata-env_s390x_test.go @@ -0,0 +1,91 @@ +// Copyright (c) 2018 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + vcUtils "github.com/kata-containers/runtime/virtcontainers/utils" + "path/filepath" + goruntime "runtime" +) + +func getExpectedHostDetails(tmpdir string) (HostInfo, error) { + type filesToCreate struct { + file string + contents string + } + + const expectedKernelVersion = "99.1" + const expectedArch = goruntime.GOARCH + + expectedDistro := DistroInfo{ + Name: "Foo", + Version: "42", + } + + expectedCPU := CPUInfo{ + Vendor: "moi", + Model: "awesome XI", + } + + expectedHostDetails := HostInfo{ + Kernel: expectedKernelVersion, + Architecture: expectedArch, + Distro: expectedDistro, + CPU: expectedCPU, + VMContainerCapable: true, + SupportVSocks: vcUtils.SupportsVsocks(), + } + + testProcCPUInfo := filepath.Join(tmpdir, "cpuinfo") + testOSRelease := filepath.Join(tmpdir, "os-release") + + // XXX: This file is *NOT* created by this function on purpose + // (to ensure the only file checked by the tests is + // testOSRelease). osReleaseClr handling is tested in + // utils_test.go. + testOSReleaseClr := filepath.Join(tmpdir, "os-release-clr") + + testProcVersion := filepath.Join(tmpdir, "proc-version") + + // override + procVersion = testProcVersion + osRelease = testOSRelease + osReleaseClr = testOSReleaseClr + procCPUInfo = testProcCPUInfo + + procVersionContents := fmt.Sprintf("Linux version %s a b c", + expectedKernelVersion) + + osReleaseContents := fmt.Sprintf(` +NAME="%s" +VERSION_ID="%s" +`, expectedDistro.Name, expectedDistro.Version) + + procCPUInfoContents := fmt.Sprintf(` +%s : %s +processor 0: version = 00, identification = 3929E7, %s = %s +`, + archCPUVendorField, + expectedCPU.Vendor, + archCPUModelField, + expectedCPU.Model) + + data := []filesToCreate{ + {procVersion, procVersionContents}, + {osRelease, osReleaseContents}, + {procCPUInfo, procCPUInfoContents}, + } + + for _, d := range data { + err := createFile(d.file, d.contents) + if err != nil { + return HostInfo{}, err + } + } + + return expectedHostDetails, nil +} diff --git a/cli/utils_s390x.go b/cli/utils_s390x.go new file mode 100644 index 0000000000..d2db30905d --- /dev/null +++ b/cli/utils_s390x.go @@ -0,0 +1,12 @@ +// +build s390x + +// Copyright (c) 2018 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +func archConvertStatFs(cgroupFsType int) uint32 { + return uint32(cgroupFsType) +} diff --git a/versions.yaml b/versions.yaml index 11c94f5fdd..70df24634e 100644 --- a/versions.yaml +++ b/versions.yaml @@ -83,6 +83,9 @@ assets: ppc64le: name: "centos" version: "latest" + s390x: + name: "ubuntu" + version: "latest" x86_64: name: &default-image-name "clearlinux" version: "20640" @@ -101,6 +104,9 @@ assets: ppc64le: name: *default-initrd-name version: *default-initrd-version + s390x: + name: *default-initrd-name + version: *default-initrd-version x86_64: name: *default-initrd-name version: *default-initrd-version diff --git a/virtcontainers/hypervisor.go b/virtcontainers/hypervisor.go index 33d21aa8f5..8c82ae1702 100644 --- a/virtcontainers/hypervisor.go +++ b/virtcontainers/hypervisor.go @@ -514,7 +514,7 @@ func getHostMemorySizeKb(memInfoPath string) (uint64, error) { // RunningOnVMM checks if the system is running inside a VM. func RunningOnVMM(cpuInfoPath string) (bool, error) { - if runtime.GOARCH == "arm64" || runtime.GOARCH == "ppc64le" { + if runtime.GOARCH == "arm64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x" { virtLog.Info("Unable to know if the system is running inside a VM") return false, nil } diff --git a/virtcontainers/qemu.go b/virtcontainers/qemu.go index e73e936991..a6e986dd5a 100644 --- a/virtcontainers/qemu.go +++ b/virtcontainers/qemu.go @@ -10,6 +10,7 @@ import ( "fmt" "os" "path/filepath" + "runtime" "strconv" "strings" "time" @@ -634,7 +635,6 @@ func (q *qemu) stopSandbox() error { if err != nil { q.Logger().WithError(err).Error("Fail to clean up vm directory") } - return nil } @@ -892,7 +892,13 @@ func (q *qemu) hotplugNetDevice(endpoint Endpoint, op operation) error { endpoint.SetPciAddr(pciAddr) devID := "virtio-" + tap.ID - if err = q.qmpMonitorCh.qmp.ExecuteNetPCIDeviceAdd(q.qmpMonitorCh.ctx, tap.Name, devID, endpoint.HardwareAddr(), addr, bridge.ID, romFile, int(q.config.NumVCPUs)); err != nil { + + if runtime.GOARCH == "s390x" { + err = q.qmpMonitorCh.qmp.ExecuteNetCCWDeviceAdd(q.qmpMonitorCh.ctx, tap.Name, devID, endpoint.HardwareAddr(), addr, bridge.ID, int(q.config.NumVCPUs)) + } else { + err = q.qmpMonitorCh.qmp.ExecuteNetPCIDeviceAdd(q.qmpMonitorCh.ctx, tap.Name, devID, endpoint.HardwareAddr(), addr, bridge.ID, romFile, int(q.config.NumVCPUs)) + } + if err != nil { return err } } else { @@ -1288,6 +1294,8 @@ func genericBridges(number uint32, machineType string) []Bridge { bt = pcieBridge case QemuPseries: bt = pciBridge + case QemuCCWVirtio: + bt = pciBridge default: return nil } diff --git a/virtcontainers/qemu_arch_base.go b/virtcontainers/qemu_arch_base.go index a0f8f84e78..f9880ca398 100644 --- a/virtcontainers/qemu_arch_base.go +++ b/virtcontainers/qemu_arch_base.go @@ -148,6 +148,9 @@ const ( // QemuPseries is a QEMU virt machine type for ppc64le QemuPseries = "pseries" + + // QemuCCWVirtio is a QEMU virt machine type for for s390x + QemuCCWVirtio = "s390-ccw-virtio" ) // kernelParamsNonDebug is a list of the default kernel @@ -449,7 +452,7 @@ func (q *qemuArchBase) appendNetwork(devices []govmmQemu.Device, endpoint Endpoi devices = append(devices, govmmQemu.NetDevice{ Type: networkModelToQemuType(netPair.NetInterworkingModel), - Driver: govmmQemu.VirtioNetPCI, + Driver: govmmQemu.VirtioNet, ID: fmt.Sprintf("network-%d", q.networkIndex), IFName: netPair.TAPIface.Name, MACAddress: netPair.TAPIface.HardAddr, @@ -466,7 +469,7 @@ func (q *qemuArchBase) appendNetwork(devices []govmmQemu.Device, endpoint Endpoi devices = append(devices, govmmQemu.NetDevice{ Type: govmmQemu.MACVTAP, - Driver: govmmQemu.VirtioNetPCI, + Driver: govmmQemu.VirtioNet, ID: fmt.Sprintf("network-%d", q.networkIndex), IFName: ep.Name(), MACAddress: ep.HardwareAddr(), diff --git a/virtcontainers/qemu_arch_base_test.go b/virtcontainers/qemu_arch_base_test.go index dcca194ea5..47c218533a 100644 --- a/virtcontainers/qemu_arch_base_test.go +++ b/virtcontainers/qemu_arch_base_test.go @@ -471,7 +471,7 @@ func TestQemuArchBaseAppendNetwork(t *testing.T) { expectedOut := []govmmQemu.Device{ govmmQemu.NetDevice{ Type: networkModelToQemuType(macvlanEp.NetPair.NetInterworkingModel), - Driver: govmmQemu.VirtioNetPCI, + Driver: govmmQemu.VirtioNet, ID: fmt.Sprintf("network-%d", 0), IFName: macvlanEp.NetPair.TAPIface.Name, MACAddress: macvlanEp.NetPair.TAPIface.HardAddr, @@ -482,7 +482,7 @@ func TestQemuArchBaseAppendNetwork(t *testing.T) { }, govmmQemu.NetDevice{ Type: govmmQemu.MACVTAP, - Driver: govmmQemu.VirtioNetPCI, + Driver: govmmQemu.VirtioNet, ID: fmt.Sprintf("network-%d", 1), IFName: macvtapEp.Name(), MACAddress: macvtapEp.HardwareAddr(), diff --git a/virtcontainers/qemu_s390x.go b/virtcontainers/qemu_s390x.go new file mode 100644 index 0000000000..700c85b640 --- /dev/null +++ b/virtcontainers/qemu_s390x.go @@ -0,0 +1,120 @@ +// Copyright (c) 2018 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package virtcontainers + +import ( + govmmQemu "github.com/intel/govmm/qemu" + "github.com/kata-containers/runtime/virtcontainers/device/config" + "github.com/sirupsen/logrus" +) + +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 defaultPCBridgeBus = "pci.0" + +const VirtioSerialCCW = "virtio-serial-ccw" + +var qemuPaths = map[string]string{ + QemuCCWVirtio: defaultQemuPath, +} + +var kernelRootParams = []Param{} + +// Verify needed parameters +var kernelParams = []Param{ + {"console", "ttysclp0"}, +} + +var supportedQemuMachines = []govmmQemu.Machine{ + { + Type: QemuCCWVirtio, + Options: defaultQemuMachineOptions, + }, +} + +// MaxQemuVCPUs returns the maximum number of vCPUs supported +func MaxQemuVCPUs() uint32 { + return uint32(255) +} + +func newQemuArch(config HypervisorConfig) qemuArch { + machineType := config.HypervisorMachineType + if machineType == "" { + machineType = defaultQemuMachineType + } + + q := &qemuS390x{ + qemuArchBase{ + machineType: machineType, + qemuPaths: qemuPaths, + supportedQemuMachines: supportedQemuMachines, + kernelParamsNonDebug: kernelParamsNonDebug, + kernelParamsDebug: kernelParamsDebug, + kernelParams: kernelParams, + }, + } + + if config.ImagePath != "" { + q.kernelParams = append(q.kernelParams, kernelRootParams...) + q.kernelParamsNonDebug = append(q.kernelParamsNonDebug, kernelParamsSystemdNonDebug...) + q.kernelParamsDebug = append(q.kernelParamsDebug, kernelParamsSystemdDebug...) + } + + return q +} + +// appendBridges appends to devices the given bridges +func (q *qemuS390x) appendBridges(devices []govmmQemu.Device, bridges []Bridge) []govmmQemu.Device { + return genericAppendBridges(devices, bridges, 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 { + serial := govmmQemu.SerialDevice{ + Driver: VirtioSerialCCW, + ID: "serial0", + DisableModern: q.nestedRun, + } + + devices = append(devices, serial) + + var console govmmQemu.CharDevice + + console = govmmQemu.CharDevice{ + Driver: govmmQemu.Console, + Backend: govmmQemu.Socket, + DeviceID: "console0", + ID: "charconsole0", + Path: path, + } + + devices = append(devices, console) + + return devices +} + +// 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 { + logrus.Fatalln("No vhost-user devices supported on s390x") + return nil +} + +// 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 +} diff --git a/virtcontainers/qemu_s390x_test.go b/virtcontainers/qemu_s390x_test.go new file mode 100644 index 0000000000..c9d344ac20 --- /dev/null +++ b/virtcontainers/qemu_s390x_test.go @@ -0,0 +1,53 @@ +// Copyright (c) 2018 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package virtcontainers + +import ( + "fmt" + "testing" + + govmmQemu "github.com/intel/govmm/qemu" + "github.com/stretchr/testify/assert" +) + +func newTestQemu(machineType string) qemuArch { + config := HypervisorConfig{ + HypervisorMachineType: machineType, + } + return newQemuArch(config) +} + +func TestQemuS390xCPUModel(t *testing.T) { + assert := assert.New(t) + s390x := newTestQemu(QemuCCWVirtio) + + expectedOut := defaultCPUModel + model := s390x.cpuModel() + assert.Equal(expectedOut, model) + + s390x.enableNestingChecks() + expectedOut = defaultCPUModel + ",pmu=off" + model = s390x.cpuModel() + assert.Equal(expectedOut, model) +} + +func TestQemuS390xMemoryTopology(t *testing.T) { + assert := assert.New(t) + s390x := newTestQemu(QemuCCWVirtio) + memoryOffset := 1024 + + hostMem := uint64(1024) + mem := uint64(120) + slots := uint8(10) + expectedMemory := govmmQemu.Memory{ + Size: fmt.Sprintf("%dM", mem), + Slots: slots, + MaxMem: fmt.Sprintf("%dM", hostMem+uint64(memoryOffset)), + } + + m := s390x.memoryTopology(mem, hostMem, slots) + assert.Equal(expectedMemory, m) +}