mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-05-10 17:37:37 +00:00
Merge pull request #11056 from RuoqingHe/runtime-qemu-riscv
runtime: Support and enable build on riscv64
This commit is contained in:
commit
54dcf0d342
.github/workflows
src/runtime
@ -43,6 +43,11 @@ jobs:
|
||||
- rust
|
||||
- musl-tools
|
||||
- protobuf-compiler
|
||||
- name: runtime
|
||||
path: src/runtime
|
||||
needs:
|
||||
- golang
|
||||
- XDG_RUNTIME_DIR
|
||||
- name: runtime-rs
|
||||
path: src/runtime-rs
|
||||
needs:
|
||||
@ -101,6 +106,10 @@ jobs:
|
||||
run: |
|
||||
XDG_RUNTIME_DIR=$(mktemp -d "/tmp/kata-tests-$USER.XXX" | tee >(xargs chmod 0700))
|
||||
echo "XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR}" >> "$GITHUB_ENV"
|
||||
- name: Skip tests that depend on virtualization capable runners when needed
|
||||
if: inputs.instance == 'riscv-builder'
|
||||
run: |
|
||||
echo "GITHUB_RUNNER_CI_NON_VIRT=true" >> "$GITHUB_ENV"
|
||||
- name: Running `${{ matrix.command }}` for ${{ matrix.component.name }}
|
||||
run: |
|
||||
cd ${{ matrix.component.path }}
|
||||
|
@ -20,6 +20,9 @@ endif
|
||||
ifeq ($(ARCH),aarch64)
|
||||
override ARCH = arm64
|
||||
endif
|
||||
ifeq ($(ARCH),riscv64gc)
|
||||
override ARCH = riscv64
|
||||
endif
|
||||
|
||||
ARCH_DIR = arch
|
||||
ARCH_FILE_SUFFIX = -options.mk
|
||||
|
13
src/runtime/arch/riscv64-options.mk
Normal file
13
src/runtime/arch/riscv64-options.mk
Normal file
@ -0,0 +1,13 @@
|
||||
# Copyright (c) 2024 Institute of Software, CAS.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
# riscv64 settings
|
||||
|
||||
MACHINETYPE := virt
|
||||
KERNELPARAMS :=
|
||||
MACHINEACCELERATORS :=
|
||||
CPUFEATURES :=
|
||||
|
||||
QEMUCMD := qemu-system-riscv64
|
26
src/runtime/cmd/kata-runtime/kata-check_data_riscv64_test.go
Normal file
26
src/runtime/cmd/kata-runtime/kata-check_data_riscv64_test.go
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2025 Institute of Software, CAS.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
const testCPUInfoTemplate = `
|
||||
processor : 0
|
||||
hart : 1
|
||||
isa : rv64imafdch_zicbom_zicboz_zicntr_zicsr_zifencei_zihintntl_zihintpause_zihpm_zawrs_zfa_zca_zcd_zba_zbb_zbc_zbs_smaia_ssaia_sstc
|
||||
mmu : sv48
|
||||
mvendorid : 0x0
|
||||
marchid : 0x0
|
||||
mimpid : 0x0
|
||||
hart isa : rv64imafdch_zicbom_zicboz_zicntr_zicsr_zifencei_zihintntl_zihintpause_zihpm_zawrs_zfa_zca_zcd_zba_zbb_zbc_zbs_smaia_ssaia_sstc
|
||||
|
||||
processor : 1
|
||||
hart : 0
|
||||
isa : rv64imafdch_zicbom_zicboz_zicntr_zicsr_zifencei_zihintntl_zihintpause_zihpm_zawrs_zfa_zca_zcd_zba_zbb_zbc_zbs_smaia_ssaia_sstc
|
||||
mmu : sv48
|
||||
mvendorid : 0x0
|
||||
marchid : 0x0
|
||||
mimpid : 0x0
|
||||
hart isa : rv64imafdch_zicbom_zicboz_zicntr_zicsr_zifencei_zihintntl_zihintpause_zihpm_zawrs_zfa_zca_zcd_zba_zbb_zbc_zbs_smaia_ssaia_sstc
|
||||
`
|
@ -3,7 +3,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
//go:build arm64 || ppc64le
|
||||
//go:build arm64 || ppc64le || riscv64
|
||||
|
||||
package main
|
||||
|
||||
|
141
src/runtime/cmd/kata-runtime/kata-check_riscv64.go
Normal file
141
src/runtime/cmd/kata-runtime/kata-check_riscv64.go
Normal file
@ -0,0 +1,141 @@
|
||||
// Copyright (c) 2024 Institute of Software, CAS.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
cpuFlagsTag = genericCPUFlagsTag
|
||||
archCPUVendorField = "mvenderid"
|
||||
archCPUModelField = "marchid"
|
||||
)
|
||||
|
||||
// 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",
|
||||
required: true,
|
||||
},
|
||||
"vhost": {
|
||||
desc: "Host kernel accelerator for virtio",
|
||||
required: true,
|
||||
},
|
||||
"vhost_net": {
|
||||
desc: "Host kernel accelerator for virtio network",
|
||||
required: true,
|
||||
},
|
||||
"vhost_vsock": {
|
||||
desc: "Host Support for Linux VM Sockets",
|
||||
required: false,
|
||||
},
|
||||
}
|
||||
|
||||
func setCPUtype(hypervisorType vc.HypervisorType) 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(hypervisorType vc.HypervisorType) error {
|
||||
if hypervisorType == "remote" {
|
||||
return nil
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
func getRiscv64CPUDetails() (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])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
vendor = "Unknown"
|
||||
}
|
||||
if archCPUModelField != "" {
|
||||
if strings.HasPrefix(line, prefixModel) {
|
||||
fields := strings.Split(line, ":")
|
||||
if len(fields) > 1 {
|
||||
model = strings.TrimSpace(fields[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if vendor == "" {
|
||||
return "", "", fmt.Errorf("cannot find vendor field in file %v", procCPUInfo)
|
||||
}
|
||||
|
||||
if model == "" {
|
||||
return "", "", fmt.Errorf("Error in parsing cpu model from %v", procCPUInfo)
|
||||
}
|
||||
|
||||
return vendor, model, nil
|
||||
}
|
||||
|
||||
func getCPUDetails() (string, string, error) {
|
||||
if vendor, model, err := genericGetCPUDetails(); err == nil {
|
||||
return vendor, model, nil
|
||||
}
|
||||
return getRiscv64CPUDetails()
|
||||
}
|
106
src/runtime/cmd/kata-runtime/kata-check_riscv64_test.go
Normal file
106
src/runtime/cmd/kata-runtime/kata-check_riscv64_test.go
Normal file
@ -0,0 +1,106 @@
|
||||
// Copyright (c) 2025 Institute of Software, CAS.
|
||||
// Copyright (c) 2018 ARM Limited
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func setupCheckHostIsVMContainerCapable(assert *assert.Assertions, cpuInfoFile string, cpuData []testCPUData, moduleData []testModuleData) {
|
||||
createModules(assert, cpuInfoFile, moduleData)
|
||||
err := makeCPUInfoFile(cpuInfoFile, "", "")
|
||||
assert.NoError(err)
|
||||
}
|
||||
|
||||
func TestCCCheckCLIFunction(t *testing.T) {
|
||||
if os.Getenv("GITHUB_RUNNER_CI_NON_VIRT") == "true" {
|
||||
t.Skip("Skipping the test as the GitHub self hosted runners for RISC-V do not support Virtualization")
|
||||
}
|
||||
|
||||
var cpuData []testCPUData
|
||||
moduleData := []testModuleData{
|
||||
{filepath.Join(sysModuleDir, "kvm"), "", true},
|
||||
{filepath.Join(sysModuleDir, "vhost"), "", true},
|
||||
{filepath.Join(sysModuleDir, "vhost_net"), "", true},
|
||||
}
|
||||
|
||||
genericCheckCLIFunction(t, cpuData, moduleData)
|
||||
}
|
||||
|
||||
func TestGetCPUDetails(t *testing.T) {
|
||||
type testData struct {
|
||||
contents string
|
||||
expectedNormalizeVendor string
|
||||
expectedNormalizeModel string
|
||||
expectError bool
|
||||
}
|
||||
|
||||
validVendorName := "0x0"
|
||||
validModelName := "0x0"
|
||||
validVendor := fmt.Sprintf(`%s : %s`, archCPUVendorField, validVendorName)
|
||||
validModel := fmt.Sprintf(`%s : %s`, archCPUModelField, validModelName)
|
||||
|
||||
validContents := fmt.Sprintf(`
|
||||
a : b
|
||||
%s
|
||||
foo : bar
|
||||
%s
|
||||
`, validVendor, validModel)
|
||||
|
||||
data := []testData{
|
||||
{"", "", "", true},
|
||||
{"invalid", "", "", true},
|
||||
{archCPUVendorField, "", "", true},
|
||||
{archCPUModelField, "", "", true},
|
||||
{"", validVendorName, "", true},
|
||||
{"", "", validModelName, true},
|
||||
{validContents, validVendorName, validModelName, false},
|
||||
}
|
||||
|
||||
tmpdir := t.TempDir()
|
||||
|
||||
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.expectedNormalizeVendor, vendor)
|
||||
assert.Equal(t, d.expectedNormalizeModel, model)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetCPUtype(t *testing.T) {
|
||||
testSetCPUTypeGeneric(t)
|
||||
}
|
13
src/runtime/cmd/kata-runtime/kata-env_riscv64_test.go
Normal file
13
src/runtime/cmd/kata-runtime/kata-env_riscv64_test.go
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright (c) 2025 Institute of Software, CAS.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
func getExpectedHostDetails(tmpdir string) (HostInfo, error) {
|
||||
expectedVendor := "0x0"
|
||||
expectedModel := "0x0"
|
||||
expectedVMContainerCapable := true
|
||||
return genericGetExpectedHostDetails(tmpdir, expectedVendor, expectedModel, expectedVMContainerCapable)
|
||||
}
|
@ -22,8 +22,8 @@ if [ -z "$go_test_flags" ]; then
|
||||
# "go test -timeout X"
|
||||
go_test_flags="-timeout ${KATA_GO_TEST_TIMEOUT:-30s}"
|
||||
|
||||
# -race flag is not supported on s390x
|
||||
[ "$(go env GOARCH)" != "s390x" ] && go_test_flags+=" -race"
|
||||
# -race flag is not supported on s390x and riscv64
|
||||
[ "$(go env GOARCH)" != "s390x" ] && [ "$(go env GOARCH)" != "riscv64" ] && go_test_flags+=" -race"
|
||||
|
||||
# s390x requires special linker flags
|
||||
[ "$(go env GOARCH)" = s390x ] && go_test_flags+=" -ldflags '-extldflags -Wl,--s390-pgste'"
|
||||
|
12
src/runtime/pkg/govmm/vmm_riscv64.go
Normal file
12
src/runtime/pkg/govmm/vmm_riscv64.go
Normal file
@ -0,0 +1,12 @@
|
||||
//
|
||||
// Copyright (c) 2024 Institute of Software, CAS.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package govmm
|
||||
|
||||
// MaxVCPUs returns the maximum number of vCPUs supported
|
||||
func MaxVCPUs() uint32 {
|
||||
return uint32(512)
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2024 Institute of Software, CAS.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// template implements base vm factory with vm templating.
|
||||
|
||||
package template
|
||||
|
||||
// templateDeviceStateSize denotes device state size when
|
||||
// mount tmpfs.
|
||||
const templateDeviceStateSize = 8
|
10
src/runtime/virtcontainers/hypervisor_linux_riscv64.go
Normal file
10
src/runtime/virtcontainers/hypervisor_linux_riscv64.go
Normal file
@ -0,0 +1,10 @@
|
||||
// Copyright (c) 2024 Institute of Software, CAS.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package virtcontainers
|
||||
|
||||
// Guest protection is not available on RISC-V.
|
||||
func availableGuestProtection() (guestProtection, error) {
|
||||
return noneProtection, nil
|
||||
}
|
65
src/runtime/virtcontainers/qemu_riscv64.go
Normal file
65
src/runtime/virtcontainers/qemu_riscv64.go
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2024 Institute of Software, CAS.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package virtcontainers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
govmmQemu "github.com/kata-containers/kata-containers/src/runtime/pkg/govmm/qemu"
|
||||
)
|
||||
|
||||
type qemuRiscv64 struct {
|
||||
// inherit from qemuArchBase, overwrite methods if needed
|
||||
qemuArchBase
|
||||
}
|
||||
|
||||
const defaultQemuPath = "/usr/bin/qemu-system-riscv64"
|
||||
|
||||
const defaultQemuMachineType = QemuVirt
|
||||
|
||||
const qmpMigrationWaitTimeout = 10 * time.Second
|
||||
|
||||
const defaultQemuMachineOptions = "accel=kvm,usb=off"
|
||||
|
||||
var kernelParams = []Param{
|
||||
{"numa", "off"},
|
||||
}
|
||||
|
||||
var supportedQemuMachine = govmmQemu.Machine{
|
||||
Type: QemuVirt,
|
||||
Options: defaultQemuMachineOptions,
|
||||
}
|
||||
|
||||
func newQemuArch(config HypervisorConfig) (qemuArch, error) {
|
||||
machineType := config.HypervisorMachineType
|
||||
if machineType == "" {
|
||||
machineType = defaultQemuMachineType
|
||||
}
|
||||
|
||||
if machineType != defaultQemuMachineType {
|
||||
return nil, fmt.Errorf("unrecognised machinetype: %v", machineType)
|
||||
}
|
||||
|
||||
q := &qemuRiscv64{
|
||||
qemuArchBase{
|
||||
qemuMachine: supportedQemuMachine,
|
||||
qemuExePath: defaultQemuPath,
|
||||
memoryOffset: config.MemOffset,
|
||||
kernelParamsNonDebug: kernelParamsNonDebug,
|
||||
kernelParamsDebug: kernelParamsDebug,
|
||||
kernelParams: kernelParams,
|
||||
},
|
||||
}
|
||||
|
||||
q.handleImagePath(config)
|
||||
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (q *qemuRiscv64) appendIOMMU(devices []govmmQemu.Device) ([]govmmQemu.Device, error) {
|
||||
return devices, fmt.Errorf("riscv64 does not support appending a vIOMMU")
|
||||
}
|
Loading…
Reference in New Issue
Block a user