mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-08-02 00:02:01 +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
@ -43,6 +43,11 @@ jobs:
|
|||||||
- rust
|
- rust
|
||||||
- musl-tools
|
- musl-tools
|
||||||
- protobuf-compiler
|
- protobuf-compiler
|
||||||
|
- name: runtime
|
||||||
|
path: src/runtime
|
||||||
|
needs:
|
||||||
|
- golang
|
||||||
|
- XDG_RUNTIME_DIR
|
||||||
- name: runtime-rs
|
- name: runtime-rs
|
||||||
path: src/runtime-rs
|
path: src/runtime-rs
|
||||||
needs:
|
needs:
|
||||||
@ -101,6 +106,10 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
XDG_RUNTIME_DIR=$(mktemp -d "/tmp/kata-tests-$USER.XXX" | tee >(xargs chmod 0700))
|
XDG_RUNTIME_DIR=$(mktemp -d "/tmp/kata-tests-$USER.XXX" | tee >(xargs chmod 0700))
|
||||||
echo "XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR}" >> "$GITHUB_ENV"
|
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 }}
|
- name: Running `${{ matrix.command }}` for ${{ matrix.component.name }}
|
||||||
run: |
|
run: |
|
||||||
cd ${{ matrix.component.path }}
|
cd ${{ matrix.component.path }}
|
||||||
|
@ -20,6 +20,9 @@ endif
|
|||||||
ifeq ($(ARCH),aarch64)
|
ifeq ($(ARCH),aarch64)
|
||||||
override ARCH = arm64
|
override ARCH = arm64
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(ARCH),riscv64gc)
|
||||||
|
override ARCH = riscv64
|
||||||
|
endif
|
||||||
|
|
||||||
ARCH_DIR = arch
|
ARCH_DIR = arch
|
||||||
ARCH_FILE_SUFFIX = -options.mk
|
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
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
//go:build arm64 || ppc64le
|
//go:build arm64 || ppc64le || riscv64
|
||||||
|
|
||||||
package main
|
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 -timeout X"
|
||||||
go_test_flags="-timeout ${KATA_GO_TEST_TIMEOUT:-30s}"
|
go_test_flags="-timeout ${KATA_GO_TEST_TIMEOUT:-30s}"
|
||||||
|
|
||||||
# -race flag is not supported on s390x
|
# -race flag is not supported on s390x and riscv64
|
||||||
[ "$(go env GOARCH)" != "s390x" ] && go_test_flags+=" -race"
|
[ "$(go env GOARCH)" != "s390x" ] && [ "$(go env GOARCH)" != "riscv64" ] && go_test_flags+=" -race"
|
||||||
|
|
||||||
# s390x requires special linker flags
|
# s390x requires special linker flags
|
||||||
[ "$(go env GOARCH)" = s390x ] && go_test_flags+=" -ldflags '-extldflags -Wl,--s390-pgste'"
|
[ "$(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