1
0
mirror of https://github.com/kata-containers/kata-containers.git synced 2025-05-10 17:37:37 +00:00

Merge pull request from RuoqingHe/runtime-qemu-riscv

runtime: Support and enable build on riscv64
This commit is contained in:
Xuewei Niu 2025-03-27 17:02:21 +08:00 committed by GitHub
commit 54dcf0d342
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 412 additions and 3 deletions

View File

@ -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 }}

View File

@ -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

View 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

View 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
`

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0
//
//go:build arm64 || ppc64le
//go:build arm64 || ppc64le || riscv64
package main

View 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()
}

View 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)
}

View 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)
}

View File

@ -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'"

View 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)
}

View File

@ -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

View 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
}

View 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")
}