diff --git a/Makefile b/Makefile index a27ec3127c..1fc6155a93 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,12 @@ ifeq (,$(installing)) EXTRA_DEPS = clean endif +ifeq (uncompressed,$(KERNELTYPE)) + KERNEL_NAME = vmlinux.container +else + KERNEL_NAME = vmlinuz.container +endif + LIBEXECDIR := $(PREFIX)/libexec SHAREDIR := $(PREFIX)/share DEFAULTSDIR := $(SHAREDIR)/defaults @@ -77,7 +83,7 @@ PKGLIBDIR := $(LOCALSTATEDIR)/lib/$(PROJECT_DIR) PKGRUNDIR := $(LOCALSTATEDIR)/run/$(PROJECT_DIR) PKGLIBEXECDIR := $(LIBEXECDIR)/$(PROJECT_DIR) -KERNELPATH := $(PKGDATADIR)/vmlinuz.container +KERNELPATH := $(PKGDATADIR)/$(KERNEL_NAME) INITRDPATH := $(PKGDATADIR)/$(INITRDNAME) IMAGEPATH := $(PKGDATADIR)/$(IMAGENAME) FIRMWAREPATH := @@ -150,6 +156,7 @@ USER_VARS += INITRDNAME USER_VARS += INITRDPATH USER_VARS += MACHINETYPE USER_VARS += KERNELPATH +USER_VARS += KERNELTYPE USER_VARS += FIRMWAREPATH USER_VARS += MACHINEACCELERATORS USER_VARS += KERNELPARAMS diff --git a/arch/ppc64le-options.mk b/arch/ppc64le-options.mk new file mode 100644 index 0000000000..a309ca51db --- /dev/null +++ b/arch/ppc64le-options.mk @@ -0,0 +1,12 @@ +# Copyright (c) 2018 IBM +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Power ppc64le settings + +MACHINETYPE := pseries +KERNELPARAMS := +MACHINEACCELERATORS := +KERNELTYPE := uncompressed #This architecture must use an uncompressed kernel. +QEMUCMD := qemu-system-ppc64le diff --git a/cli/kata-check.go b/cli/kata-check.go index 3ecb67cfcf..4548df6a32 100644 --- a/cli/kata-check.go +++ b/cli/kata-check.go @@ -7,6 +7,13 @@ package main +/* +#include + +const int ioctl_KVM_CREATE_VM = KVM_CREATE_VM; +*/ +import "C" + import ( "fmt" "os" @@ -14,6 +21,7 @@ import ( "path/filepath" "regexp" "strings" + "syscall" vc "github.com/kata-containers/runtime/virtcontainers" "github.com/sirupsen/logrus" @@ -51,6 +59,11 @@ var ( modInfoCmd = "modinfo" ) +// variables rather than consts to allow tests to modify them +var ( + kvmDevice = "/dev/kvm" +) + // getCPUInfo returns details of the first CPU read from the specified cpuinfo file func getCPUInfo(cpuInfoFile string) (string, error) { text, err := getFileContents(cpuInfoFile) @@ -222,9 +235,9 @@ func checkKernelModules(modules map[string]kernelModule, handler kernelParamHand return count, nil } -// hostIsVMContainerCapable checks to see if the host is theoretically capable +// genericHostIsVMContainerCapable checks to see if the host is theoretically capable // of creating a VM container. -func hostIsVMContainerCapable(details vmContainerCapableDetails) error { +func genericHostIsVMContainerCapable(details vmContainerCapableDetails) error { cpuinfo, err := getCPUInfo(details.cpuInfoFile) if err != nil { return err @@ -293,3 +306,58 @@ var kataCheckCLICommand = cli.Command{ return nil }, } + +func genericArchKernelParamHandler(onVMM bool, fields logrus.Fields, msg string) bool { + param, ok := fields["parameter"].(string) + if !ok { + return false + } + + // This option is not required when + // already running under a hypervisor. + if param == "unrestricted_guest" && onVMM { + kataLog.WithFields(fields).Warn(kernelPropertyCorrect) + return true + } + + if param == "nested" { + kataLog.WithFields(fields).Warn(msg) + return true + } + + // don't ignore the error + return false +} + +// genericKvmIsUsable determines if it will be possible to create a full virtual machine +// by creating a minimal VM and then deleting it. +func genericKvmIsUsable() error { + flags := syscall.O_RDWR | syscall.O_CLOEXEC + + f, err := syscall.Open(kvmDevice, flags, 0) + if err != nil { + return err + } + defer syscall.Close(f) + + fieldLogger := kataLog.WithField("check-type", "full") + + fieldLogger.WithField("device", kvmDevice).Info("device available") + + vm, _, errno := syscall.Syscall(syscall.SYS_IOCTL, + uintptr(f), + uintptr(C.ioctl_KVM_CREATE_VM), + 0) + if errno != 0 { + if errno == syscall.EBUSY { + fieldLogger.WithField("reason", "another hypervisor running").Error("cannot create VM") + } + + return errno + } + defer syscall.Close(int(vm)) + + fieldLogger.WithField("feature", "create-vm").Info("feature available") + + return nil +} diff --git a/cli/kata-check_amd64.go b/cli/kata-check_amd64.go index f65eec89b9..7e44a025d4 100644 --- a/cli/kata-check_amd64.go +++ b/cli/kata-check_amd64.go @@ -5,24 +5,10 @@ package main -/* -#include - -const int ioctl_KVM_CREATE_VM = KVM_CREATE_VM; -*/ -import "C" - import ( - "syscall" - "github.com/sirupsen/logrus" ) -// variables rather than consts to allow tests to modify them -var ( - kvmDevice = "/dev/kvm" -) - // archRequiredCPUFlags maps a CPU flag value to search for and a // human-readable description of that value. var archRequiredCPUFlags = map[string]string{ @@ -66,58 +52,19 @@ var archRequiredKernelModules = map[string]kernelModule{ // 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 { - flags := syscall.O_RDWR | syscall.O_CLOEXEC - - f, err := syscall.Open(kvmDevice, flags, 0) - if err != nil { - return err - } - defer syscall.Close(f) - - fieldLogger := kataLog.WithField("check-type", "full") - - fieldLogger.WithField("device", kvmDevice).Info("device available") - - vm, _, errno := syscall.Syscall(syscall.SYS_IOCTL, - uintptr(f), - uintptr(C.ioctl_KVM_CREATE_VM), - 0) - if errno != 0 { - if errno == syscall.EBUSY { - fieldLogger.WithField("reason", "another hypervisor running").Error("cannot create VM") - } - - return errno - } - defer syscall.Close(int(vm)) - - fieldLogger.WithField("feature", "create-vm").Info("feature available") - - return nil + return genericKvmIsUsable() } func archHostCanCreateVMContainer() error { return kvmIsUsable() } -func archKernelParamHandler(onVMM bool, fields logrus.Fields, msg string) bool { - param, ok := fields["parameter"].(string) - if !ok { - return false - } - - // This option is not required when - // already running under a hypervisor. - if param == "unrestricted_guest" && onVMM { - kataLog.WithFields(fields).Warn(kernelPropertyCorrect) - return true - } - - if param == "nested" { - kataLog.WithFields(fields).Warn(msg) - return true - } +// hostIsVMContainerCapable checks to see if the host is theoretically capable +// of creating a VM container. +func hostIsVMContainerCapable(details vmContainerCapableDetails) error { + return genericHostIsVMContainerCapable(details) +} - // don't ignore the error - return false +func archKernelParamHandler(onVMM bool, fields logrus.Fields, msg string) bool { + return genericArchKernelParamHandler(onVMM, fields, msg) } diff --git a/cli/kata-check_arm64.go b/cli/kata-check_arm64.go new file mode 100644 index 0000000000..c55d80991f --- /dev/null +++ b/cli/kata-check_arm64.go @@ -0,0 +1,22 @@ +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +// 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 { + return genericHostIsVMContainerCapable(details) +} diff --git a/cli/kata-check_data_ppc64le_test.go b/cli/kata-check_data_ppc64le_test.go new file mode 100644 index 0000000000..4b25b919a5 --- /dev/null +++ b/cli/kata-check_data_ppc64le_test.go @@ -0,0 +1,114 @@ +// Copyright (c) 2018 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +const testCPUInfoTemplate = ` +processor : 0 +cpu : POWER8E (raw), altivec supported +clock : 3690.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 8 +cpu : POWER8E (raw), altivec supported +clock : 3690.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 16 +cpu : POWER8E (raw), altivec supported +clock : 2360.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 24 +cpu : POWER8E (raw), altivec supported +clock : 2061.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 32 +cpu : POWER8E (raw), altivec supported +clock : 3690.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 40 +cpu : POWER8E (raw), altivec supported +clock : 3690.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 48 +cpu : POWER8E (raw), altivec supported +clock : 3690.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 56 +cpu : POWER8E (raw), altivec supported +clock : 3690.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 64 +cpu : POWER8E (raw), altivec supported +clock : 3690.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 72 +cpu : POWER8E (raw), altivec supported +clock : 3059.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 80 +cpu : POWER8E (raw), altivec supported +clock : 2693.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 88 +cpu : POWER8E (raw), altivec supported +clock : 2061.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 96 +cpu : POWER8E (raw), altivec supported +clock : 3690.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 104 +cpu : POWER8E (raw), altivec supported +clock : 2061.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 112 +cpu : POWER8E (raw), altivec supported +clock : 2061.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 120 +cpu : POWER8E (raw), altivec supported +clock : 2061.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 128 +cpu : POWER8E (raw), altivec supported +clock : 3690.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 136 +cpu : POWER8E (raw), altivec supported +clock : 2061.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 144 +cpu : POWER8E (raw), altivec supported +clock : 2294.000000MHz +revision : 2.1 (pvr 004b 0201) + +processor : 152 +cpu : POWER8E (raw), altivec supported +clock : 2560.000000MHz +revision : 2.1 (pvr 004b 0201) + +timebase : 512000000 +platform : PowerNV +model : 8247-22L +machine : PowerNV 8247-22L +firmware : OPAL v3 +` diff --git a/cli/kata-check_ppc64le.go b/cli/kata-check_ppc64le.go new file mode 100644 index 0000000000..ce27777438 --- /dev/null +++ b/cli/kata-check_ppc64le.go @@ -0,0 +1,65 @@ +// Copyright (c) 2018 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + "github.com/sirupsen/logrus" +) + +// 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", + }, + "kvm_hv": { + desc: "Kernel-based Virtual Machine hardware virtualization", + }, +} + +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) +} + +// 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 archKernelParamHandler(onVMM bool, fields logrus.Fields, msg string) bool { + return genericArchKernelParamHandler(onVMM, fields, msg) +} diff --git a/cli/kata-env.go b/cli/kata-env.go index 52bbfacb59..d0daf3490f 100644 --- a/cli/kata-env.go +++ b/cli/kata-env.go @@ -15,6 +15,7 @@ import ( "github.com/kata-containers/runtime/virtcontainers/pkg/oci" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/urfave/cli" + runtim "runtime" ) // Semantic version for the output of the command. @@ -173,6 +174,9 @@ func getHostInfo() (HostInfo, error) { } hostVMContainerCapable := true + if runtim.GOARCH == "ppc64le" { + hostVMContainerCapable = false + } details := vmContainerCapableDetails{ cpuInfoFile: procCPUInfo, diff --git a/virtcontainers/hypervisor.go b/virtcontainers/hypervisor.go index 8b501a3f5a..44df113262 100644 --- a/virtcontainers/hypervisor.go +++ b/virtcontainers/hypervisor.go @@ -443,8 +443,8 @@ 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" { - virtLog.Debugf("Unable to know if the system is running inside a VM") + if runtime.GOARCH == "arm64" || runtime.GOARCH == "ppc64le" { + 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 e329623ac0..efb1699b72 100644 --- a/virtcontainers/qemu.go +++ b/virtcontainers/qemu.go @@ -11,6 +11,7 @@ import ( "fmt" "os" "path/filepath" + "strconv" "strings" "time" @@ -926,3 +927,82 @@ func (q *qemu) addDevice(devInfo interface{}, devType deviceType) error { func (q *qemu) getSandboxConsole(sandboxID string) (string, error) { return utils.BuildSocketPath(runStoragePath, sandboxID, defaultConsole) } + +// genericAppendBridges appends to devices the given bridges +func genericAppendBridges(devices []govmmQemu.Device, bridges []Bridge, machineType string) []govmmQemu.Device { + bus := defaultPCBridgeBus + if machineType == QemuQ35 { + bus = defaultBridgeBus + } + + for idx, b := range bridges { + t := govmmQemu.PCIBridge + if b.Type == pcieBridge { + t = govmmQemu.PCIEBridge + } + + bridges[idx].Addr = bridgePCIStartAddr + idx + + devices = append(devices, + govmmQemu.BridgeDevice{ + Type: t, + Bus: bus, + ID: b.ID, + // Each bridge is required to be assigned a unique chassis id > 0 + Chassis: (idx + 1), + SHPC: true, + Addr: strconv.FormatInt(int64(bridges[idx].Addr), 10), + }, + ) + } + + return devices +} + +func genericBridges(number uint32, machineType string) []Bridge { + var bridges []Bridge + var bt bridgeType + + switch machineType { + + case QemuQ35: + // currently only pci bridges are supported + // qemu-2.10 will introduce pcie bridges + fallthrough + case QemuPC: + bt = pciBridge + case QemuPseries: + bt = pciBridge + default: + return nil + } + + for i := uint32(0); i < number; i++ { + bridges = append(bridges, Bridge{ + Type: bt, + ID: fmt.Sprintf("%s-bridge-%d", bt, i), + Address: make(map[uint32]string), + }) + } + + return bridges +} + +func genericMemoryTopology(memoryMb, hostMemoryMb uint64) govmmQemu.Memory { + // NVDIMM device needs memory space 1024MB + // See https://github.com/clearcontainers/runtime/issues/380 + memoryOffset := 1024 + + // add 1G memory space for nvdimm device (vm guest image) + memMax := fmt.Sprintf("%dM", hostMemoryMb+uint64(memoryOffset)) + + mem := fmt.Sprintf("%dM", memoryMb) + + memory := govmmQemu.Memory{ + Size: mem, + Slots: defaultMemSlots, + MaxMem: memMax, + } + + return memory +} diff --git a/virtcontainers/qemu_amd64.go b/virtcontainers/qemu_amd64.go index 6099ade1e7..1765f9806a 100644 --- a/virtcontainers/qemu_amd64.go +++ b/virtcontainers/qemu_amd64.go @@ -6,9 +6,7 @@ package virtcontainers import ( - "fmt" "os" - "strconv" govmmQemu "github.com/intel/govmm/qemu" ) @@ -93,12 +91,7 @@ func newQemuArch(config HypervisorConfig) qemuArch { }, } - if config.ImagePath != "" { - q.kernelParams = append(q.kernelParams, kernelRootParams...) - q.kernelParamsNonDebug = append(q.kernelParamsNonDebug, kernelParamsSystemdNonDebug...) - q.kernelParamsDebug = append(q.kernelParamsDebug, kernelParamsSystemdDebug...) - } - + q.handleImagePath(config) return q } @@ -114,29 +107,7 @@ func (q *qemuAmd64) capabilities() capabilities { } func (q *qemuAmd64) bridges(number uint32) []Bridge { - var bridges []Bridge - var bt bridgeType - - switch q.machineType { - case QemuQ35: - // currently only pci bridges are supported - // qemu-2.10 will introduce pcie bridges - fallthrough - case QemuPC: - bt = pciBridge - default: - return nil - } - - for i := uint32(0); i < number; i++ { - bridges = append(bridges, Bridge{ - Type: bt, - ID: fmt.Sprintf("%s-bridge-%d", bt, i), - Address: make(map[uint32]string), - }) - } - - return bridges + return genericBridges(number, q.machineType) } func (q *qemuAmd64) cpuModel() string { @@ -148,22 +119,7 @@ func (q *qemuAmd64) cpuModel() string { } func (q *qemuAmd64) memoryTopology(memoryMb, hostMemoryMb uint64) govmmQemu.Memory { - // NVDIMM device needs memory space 1024MB - // See https://github.com/clearcontainers/runtime/issues/380 - memoryOffset := 1024 - - // add 1G memory space for nvdimm device (vm guest image) - memMax := fmt.Sprintf("%dM", hostMemoryMb+uint64(memoryOffset)) - - mem := fmt.Sprintf("%dM", memoryMb) - - memory := govmmQemu.Memory{ - Size: mem, - Slots: defaultMemSlots, - MaxMem: memMax, - } - - return memory + return genericMemoryTopology(memoryMb, hostMemoryMb) } func (q *qemuAmd64) appendImage(devices []govmmQemu.Device, path string) ([]govmmQemu.Device, error) { @@ -194,31 +150,5 @@ func (q *qemuAmd64) appendImage(devices []govmmQemu.Device, path string) ([]govm // appendBridges appends to devices the given bridges func (q *qemuAmd64) appendBridges(devices []govmmQemu.Device, bridges []Bridge) []govmmQemu.Device { - bus := defaultPCBridgeBus - if q.machineType == QemuQ35 { - bus = defaultBridgeBus - } - - for idx, b := range bridges { - t := govmmQemu.PCIBridge - if b.Type == pcieBridge { - t = govmmQemu.PCIEBridge - } - - bridges[idx].Addr = bridgePCIStartAddr + idx - - devices = append(devices, - govmmQemu.BridgeDevice{ - Type: t, - Bus: bus, - ID: b.ID, - // Each bridge is required to be assigned a unique chassis id > 0 - Chassis: (idx + 1), - SHPC: true, - Addr: strconv.FormatInt(int64(bridges[idx].Addr), 10), - }, - ) - } - - return devices + return genericAppendBridges(devices, bridges, q.machineType) } diff --git a/virtcontainers/qemu_arch_base.go b/virtcontainers/qemu_arch_base.go index 795c63cc5f..54dbc3d1e3 100644 --- a/virtcontainers/qemu_arch_base.go +++ b/virtcontainers/qemu_arch_base.go @@ -82,6 +82,9 @@ type qemuArch interface { // appendVFIODevice appends a VFIO device to devices appendVFIODevice(devices []govmmQemu.Device, vfioDevice drivers.VFIODevice) []govmmQemu.Device + + // handleImagePath handles the Hypervisor Config image path + handleImagePath(config HypervisorConfig) } type qemuArchBase struct { @@ -131,6 +134,9 @@ const ( // QemuVirt is the QEMU virt machine type for aarch64 QemuVirt = "virt" + + // QemuPseries is a QEMU virt machine type for for ppc64le + QemuPseries = "pseries" ) // kernelParamsNonDebug is a list of the default kernel @@ -495,3 +501,11 @@ func (q *qemuArchBase) appendVFIODevice(devices []govmmQemu.Device, vfioDevice d return devices } + +func (q *qemuArchBase) handleImagePath(config HypervisorConfig) { + if config.ImagePath != "" { + q.kernelParams = append(q.kernelParams, kernelRootParams...) + q.kernelParamsNonDebug = append(q.kernelParamsNonDebug, kernelParamsSystemdNonDebug...) + q.kernelParamsDebug = append(q.kernelParamsDebug, kernelParamsSystemdDebug...) + } +} diff --git a/virtcontainers/qemu_arm64_test.go b/virtcontainers/qemu_arm64_test.go new file mode 100644 index 0000000000..17aaa5f7a3 --- /dev/null +++ b/virtcontainers/qemu_arm64_test.go @@ -0,0 +1,52 @@ +// 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 TestQemuArm64CPUModel(t *testing.T) { + assert := assert.New(t) + arm64 := newTestQemu(virt) + + expectedOut := defaultCPUModel + model := arm64.cpuModel() + assert.Equal(expectedOut, model) + + arm64.enableNestingChecks() + expectedOut = defaultCPUModel + ",pmu=off" + model = arm64.cpuModel() + assert.Equal(expectedOut, model) +} + +func TestQemuArm64MemoryTopology(t *testing.T) { + assert := assert.New(t) + arm64 := newTestQemu(virt) + memoryOffset := 1024 + + hostMem := uint64(1024) + mem := uint64(120) + expectedMemory := govmmQemu.Memory{ + Size: fmt.Sprintf("%dM", mem), + Slots: defaultMemSlots, + MaxMem: fmt.Sprintf("%dM", hostMem+uint64(memoryOffset)), + } + + m := arm64.memoryTopology(mem, hostMem) + assert.Equal(expectedMemory, m) +} diff --git a/virtcontainers/qemu_ppc64le.go b/virtcontainers/qemu_ppc64le.go new file mode 100644 index 0000000000..10af79bc28 --- /dev/null +++ b/virtcontainers/qemu_ppc64le.go @@ -0,0 +1,137 @@ +// Copyright (c) 2018 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package virtcontainers + +import ( + "encoding/hex" + "os" + + govmmQemu "github.com/intel/govmm/qemu" + "github.com/kata-containers/runtime/virtcontainers/device/drivers" + "github.com/kata-containers/runtime/virtcontainers/utils" +) + +type qemuPPC64le struct { + // inherit from qemuArchBase, overwrite methods if needed + qemuArchBase +} + +const defaultQemuPath = "/usr/bin/qemu-system-ppc64le" + +const defaultQemuMachineType = QemuPseries + +const defaultQemuMachineOptions = "accel=kvm,usb=off" + +const defaultPCBridgeBus = "pci.0" + +var qemuPaths = map[string]string{ + QemuPseries: defaultQemuPath, +} + +var kernelRootParams = []Param{} + +var kernelParams = []Param{ + {"tsc", "reliable"}, + {"no_timer_check", ""}, + {"rcupdate.rcu_expedited", "1"}, + {"noreplace-smp", ""}, + {"reboot", "k"}, + {"console", "hvc0"}, + {"console", "hvc1"}, + {"iommu", "off"}, + {"cryptomgr.notests", ""}, + {"net.ifnames", "0"}, + {"pci", "lastbus=0"}, +} + +var supportedQemuMachines = []govmmQemu.Machine{ + { + Type: QemuPseries, + Options: defaultQemuMachineOptions, + }, +} + +// returns the maximum number of vCPUs supported +func MaxQemuVCPUs() uint32 { + return uint32(128) +} + +func newQemuArch(config HypervisorConfig) qemuArch { + machineType := config.HypervisorMachineType + if machineType == "" { + machineType = defaultQemuMachineType + } + + q := &qemuPPC64le{ + qemuArchBase{ + machineType: machineType, + qemuPaths: qemuPaths, + supportedQemuMachines: supportedQemuMachines, + kernelParamsNonDebug: kernelParamsNonDebug, + kernelParamsDebug: kernelParamsDebug, + kernelParams: kernelParams, + }, + } + + q.handleImagePath(config) + return q +} + +func (q *qemuPPC64le) capabilities() capabilities { + var caps capabilities + + // pseries machine type supports hotplugging drives + if q.machineType == QemuPseries { + caps.setBlockDeviceHotplugSupport() + } + + return caps +} + +func (q *qemuPPC64le) bridges(number uint32) []Bridge { + return genericBridges(number, q.machineType) +} + +func (q *qemuPPC64le) cpuModel() string { + cpuModel := defaultCPUModel + if q.nestedRun { + cpuModel += ",pmu=off" + } + return cpuModel +} + +func (q *qemuPPC64le) memoryTopology(memoryMb, hostMemoryMb uint64) govmmQemu.Memory { + + // align hostMemoryMb to 256 MB multiples + hostMemoryMb -= (hostMemoryMb % 256) + return genericMemoryTopology(memoryMb, hostMemoryMb) +} + +func (q *qemuPPC64le) appendImage(devices []govmmQemu.Device, path string) ([]govmmQemu.Device, error) { + if _, err := os.Stat(path); os.IsNotExist(err) { + return nil, err + } + + randBytes, err := utils.GenerateRandomBytes(8) + if err != nil { + return nil, err + } + + id := utils.MakeNameID("image", hex.EncodeToString(randBytes), maxDevIDSize) + + drive := drivers.Drive{ + File: path, + Format: "raw", + ID: id, + } + + return q.appendBlockDevice(devices, drive), nil +} + +// appendBridges appends to devices the given bridges +func (q *qemuPPC64le) appendBridges(devices []govmmQemu.Device, bridges []Bridge) []govmmQemu.Device { + return genericAppendBridges(devices, bridges, q.machineType) +} diff --git a/virtcontainers/qemu_ppc64le_test.go b/virtcontainers/qemu_ppc64le_test.go new file mode 100644 index 0000000000..22752b2c7c --- /dev/null +++ b/virtcontainers/qemu_ppc64le_test.go @@ -0,0 +1,52 @@ +// 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 TestQemuPPC64leCPUModel(t *testing.T) { + assert := assert.New(t) + ppc64le := newTestQemu(QemuPseries) + + expectedOut := defaultCPUModel + model := ppc64le.cpuModel() + assert.Equal(expectedOut, model) + + ppc64le.enableNestingChecks() + expectedOut = defaultCPUModel + ",pmu=off" + model = ppc64le.cpuModel() + assert.Equal(expectedOut, model) +} + +func TestQemuPPC64leMemoryTopology(t *testing.T) { + assert := assert.New(t) + ppc64le := newTestQemu(QemuPseries) + memoryOffset := 1024 + + hostMem := uint64(1024) + mem := uint64(120) + expectedMemory := govmmQemu.Memory{ + Size: fmt.Sprintf("%dM", mem), + Slots: defaultMemSlots, + MaxMem: fmt.Sprintf("%dM", hostMem+uint64(memoryOffset)), + } + + m := ppc64le.memoryTopology(mem, hostMem) + assert.Equal(expectedMemory, m) +}