From 52f4da0246fa1dd170bb45f0216c2cff15968f24 Mon Sep 17 00:00:00 2001
From: Ruoqing He <ruoqing@isrc.iscas.ac.cn>
Date: Mon, 29 Apr 2024 16:20:35 +0800
Subject: [PATCH] Add support for riscv64 platform

Signed-off-by: Ruoqing He <ruoqing@isrc.iscas.ac.cn>
---
 src/agent/src/linux_abi.rs                    |   3 +-
 src/runtime/Makefile                          |   3 +
 src/runtime/arch/riscv64-options.mk           |  13 ++
 .../cmd/kata-runtime/kata-check_riscv64.go    | 130 ++++++++++++++++++
 src/runtime/pkg/govmm/vmm_riscv64.go          |  12 ++
 .../factory/template/template_riscv64.go      |  14 ++
 .../virtcontainers/hypervisor_riscv64.go      |  11 ++
 src/runtime/virtcontainers/qemu_riscv64.go    |  68 +++++++++
 8 files changed, 253 insertions(+), 1 deletion(-)
 create mode 100644 src/runtime/arch/riscv64-options.mk
 create mode 100644 src/runtime/cmd/kata-runtime/kata-check_riscv64.go
 create mode 100644 src/runtime/pkg/govmm/vmm_riscv64.go
 create mode 100644 src/runtime/virtcontainers/factory/template/template_riscv64.go
 create mode 100644 src/runtime/virtcontainers/hypervisor_riscv64.go
 create mode 100644 src/runtime/virtcontainers/qemu_riscv64.go

diff --git a/src/agent/src/linux_abi.rs b/src/agent/src/linux_abi.rs
index b87da3c..fdb70be 100644
--- a/src/agent/src/linux_abi.rs
+++ b/src/agent/src/linux_abi.rs
@@ -15,7 +15,8 @@ pub const SYSFS_DIR: &str = "/sys";
     target_arch = "powerpc64",
     target_arch = "s390x",
     target_arch = "x86_64",
-    target_arch = "x86"
+    target_arch = "x86",
+    target_arch = "riscv64"
 ))]
 pub fn create_pci_root_bus_path() -> String {
     String::from("/devices/pci0000:00")
diff --git a/src/runtime/Makefile b/src/runtime/Makefile
index 33fa8f2..977dbfc 100644
--- a/src/runtime/Makefile
+++ b/src/runtime/Makefile
@@ -22,6 +22,9 @@ endif
 ifeq ($(ARCH),aarch64)
     override ARCH = arm64
 endif
+ifeq ($(ARCH),riscv64)
+    override ARCH = riscv64
+endif
 
 ARCH_DIR = arch
 ARCH_FILE_SUFFIX = -options.mk
diff --git a/src/runtime/arch/riscv64-options.mk b/src/runtime/arch/riscv64-options.mk
new file mode 100644
index 0000000..91eef18
--- /dev/null
+++ b/src/runtime/arch/riscv64-options.mk
@@ -0,0 +1,13 @@
+# Copyright (c) 2023 Xin Liu
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# riscv64 settings
+
+MACHINETYPE := virt
+KERNELPARAMS :=
+MACHINEACCELERATORS :=
+CPUFEATURES :=
+
+QEMUCMD := qemu-system-riscv64
diff --git a/src/runtime/cmd/kata-runtime/kata-check_riscv64.go b/src/runtime/cmd/kata-runtime/kata-check_riscv64.go
new file mode 100644
index 0000000..a16f869
--- /dev/null
+++ b/src/runtime/cmd/kata-runtime/kata-check_riscv64.go
@@ -0,0 +1,130 @@
+// Copyright (c) 2018 Intel Corporation
+//
+// 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 = "uarch"
+	archCPUModelField  = "isa"
+)
+
+// 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_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 {
+	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()
+}
diff --git a/src/runtime/pkg/govmm/vmm_riscv64.go b/src/runtime/pkg/govmm/vmm_riscv64.go
new file mode 100644
index 0000000..5ea2e94
--- /dev/null
+++ b/src/runtime/pkg/govmm/vmm_riscv64.go
@@ -0,0 +1,12 @@
+//
+// Copyright (c) 2018 Intel Corporation
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+
+package govmm
+
+// MaxVCPUs returns the maximum number of vCPUs supported
+func MaxVCPUs() uint32 {
+	return uint32(512)
+}
diff --git a/src/runtime/virtcontainers/factory/template/template_riscv64.go b/src/runtime/virtcontainers/factory/template/template_riscv64.go
new file mode 100644
index 0000000..abb639b
--- /dev/null
+++ b/src/runtime/virtcontainers/factory/template/template_riscv64.go
@@ -0,0 +1,14 @@
+// Copyright (c) 2023 Xin Liu
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// template implements base vm factory with vm templating.
+
+package template
+
+// templateDeviceStateSize denotes device state size when
+// mount tmpfs.
+// when bypass-shared-memory is not support like arm64,
+// creating template will occupy more space. That's why we
+// put it here.
+const templateDeviceStateSize = 8
diff --git a/src/runtime/virtcontainers/hypervisor_riscv64.go b/src/runtime/virtcontainers/hypervisor_riscv64.go
new file mode 100644
index 0000000..78d128f
--- /dev/null
+++ b/src/runtime/virtcontainers/hypervisor_riscv64.go
@@ -0,0 +1,11 @@
+// Copyright (c) 2021 Arm Ltd.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package virtcontainers
+
+// Guest protection is not supported on RISCV64.
+func availableGuestProtection() (guestProtection, error) {
+	return noneProtection, nil
+}
+
diff --git a/src/runtime/virtcontainers/qemu_riscv64.go b/src/runtime/virtcontainers/qemu_riscv64.go
new file mode 100644
index 0000000..846a5f2
--- /dev/null
+++ b/src/runtime/virtcontainers/qemu_riscv64.go
@@ -0,0 +1,68 @@
+// Copyright (c) 2023 Xin Liu
+//
+// 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 = ""
+
+var defaultGICVersion = uint32(3)
+
+var kernelParams = []Param{
+	{"numa", "off"},
+}
+
+var supportedQemuMachine = govmmQemu.Machine{
+	Type:    QemuVirt,
+	Options: defaultQemuMachineOptions,
+}
+
+// MaxQemuVCPUs returns the maximum number of vCPUs supported
+func MaxQemuVCPUs() uint32 {
+	return uint32(512)
+}
+
+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
+}
-- 
2.43.0

