[PATCH][V2] ARM64 SMCCC firmware API tests
Colin King
colin.king at canonical.com
Mon Aug 23 21:42:59 UTC 2021
From: Colin Ian King <colin.king at canonical.com>
Add SMCCC firmware API test. This initial release adds tests to
exercise the API in the following ways:
1. Test the API version (and check conduit number)
2. Test the API function ID features
3. Test the bus get segment information
Currently the PCI_READ and PCI_WRITE API calls are not yet
being tested, these will be tested in later releases of the
SMCCC test.
For more information about ARM SMCCC firmware APIs, please
refer to:
https://developer.arm.com/documentation/den0115/latest
Signed-off-by: Colin Ian King <colin.king at canonical.com>
---
V2: Fix copyright years
Fix kernel version 5.12+
---
debian/control | 9 +
debian/fwts-smccc-dkms.dkms | 6 +
debian/rules | 9 +-
smccc_test/Makefile | 29 ++++
smccc_test/smccc_test.c | 244 ++++++++++++++++++++++++++
smccc_test/smccc_test.h | 44 +++++
src/Makefile.am | 2 +
src/pci/smccc/smccc.c | 333 ++++++++++++++++++++++++++++++++++++
8 files changed, 673 insertions(+), 3 deletions(-)
create mode 100644 debian/fwts-smccc-dkms.dkms
create mode 100644 smccc_test/Makefile
create mode 100644 smccc_test/smccc_test.c
create mode 100644 smccc_test/smccc_test.h
create mode 100644 src/pci/smccc/smccc.c
diff --git a/debian/control b/debian/control
index 9325c96e..7d6213ba 100644
--- a/debian/control
+++ b/debian/control
@@ -86,3 +86,12 @@ Depends: ${misc:Depends},
Description: Firmware Test Suite UEFI Runtime Service kernel driver
This package provides the efi_runtime kernel driver in DKMS format,
which is required for accessing UEFI Runtime Services.
+
+Package: fwts-smccc-dkms
+Architecture: arm64
+Priority: optional
+Depends: ${misc:Depends},
+ dkms
+Description: Firmware Test Suite SMCCC firmware kernel driver
+ This package provides the ARM64 SMCCC kernel driver in DKMS format,
+ which is required for accessing the ARM64 SMCCC firmware API.
diff --git a/debian/fwts-smccc-dkms.dkms b/debian/fwts-smccc-dkms.dkms
new file mode 100644
index 00000000..4a1a87cd
--- /dev/null
+++ b/debian/fwts-smccc-dkms.dkms
@@ -0,0 +1,6 @@
+PACKAGE_NAME="fwts-smccc-dkms"
+PACKAGE_VERSION="#MODULE_VERSION#"
+MAKE[0]="KVER=$kernelver make"
+BUILT_MODULE_NAME[0]="smccc_test"
+DEST_MODULE_LOCATION[0]="/updates"
+AUTOINSTALL="yes"
diff --git a/debian/rules b/debian/rules
index c24df00f..01b952af 100755
--- a/debian/rules
+++ b/debian/rules
@@ -9,11 +9,14 @@ DEBVERS := $(shell dpkg-parsechangelog | grep ^Version: | cut -d' ' -f2 \
VERSION := $(shell echo '$(DEBVERS)' | sed -e 's/[+-].*//' -e 's/~//g')
-DKMS_SRC_DIR := $(CURDIR)/debian/fwts-efi-runtime-dkms/usr/src/fwts-efi-runtime-dkms-$(VERSION)
+DKMS_EFI_RUNTIME_SRC_DIR := $(CURDIR)/debian/fwts-efi-runtime-dkms/usr/src/fwts-efi-runtime-dkms-$(VERSION)
+DKMS_SMCCC_SRC_DIR := $(CURDIR)/debian/fwts-smccc-dkms/usr/src/fwts-smccc-dkms-$(VERSION)
override_dh_auto_install:
- install -d $(DKMS_SRC_DIR)
- cp -a efi_runtime/* $(DKMS_SRC_DIR)
+ install -d $(DKMS_EFI_RUNTIME_SRC_DIR)
+ cp -a efi_runtime/* $(DKMS_EFI_RUNTIME_SRC_DIR)
+ install -d $(DKMS_SMCCC_SRC_DIR)
+ cp -a smccc_test/* $(DKMS_SMCCC_SRC_DIR)
dh_auto_install
override_dh_dkms:
diff --git a/smccc_test/Makefile b/smccc_test/Makefile
new file mode 100644
index 00000000..f2faf76b
--- /dev/null
+++ b/smccc_test/Makefile
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2021 Canonical, Ltd.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+KVER ?= `uname -r`
+KBUILD_MODPOST_WARN=y
+obj-m += smccc_test.o
+all:
+ make -C /lib/modules/$(KVER)/build M=`pwd` modules
+
+install:
+ make -C /lib/modules/$(KVER)/build M=`pwd` modules_install
+
+clean:
+ make -C /lib/modules/$(KVER)/build M=`pwd` clean
diff --git a/smccc_test/smccc_test.c b/smccc_test/smccc_test.c
new file mode 100644
index 00000000..563e3c08
--- /dev/null
+++ b/smccc_test/smccc_test.c
@@ -0,0 +1,244 @@
+/*
+ * SMCCC test driver
+ *
+ * Copyright(C) 2021 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <linux/version.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+/*
+ * ARM SMCCC kernel test driver
+ * https://developer.arm.com/documentation/den0115/latest
+ */
+
+#if defined(__aarch64__) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
+#include <linux/arm-smccc.h>
+#endif
+
+#include "smccc_test.h"
+
+#define MODULE_NAME "smccc_test"
+#define SMCCC_TEST_VERSION (0x00000001)
+
+MODULE_AUTHOR("Colin Ian King");
+MODULE_DESCRIPTION("SMCCC Test Driver");
+MODULE_LICENSE("GPL");
+
+#if defined(__aarch64__) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
+
+/* PCI ECAM conduit (defined by ARM DEN0115A) */
+#define SMCCC_PCI_CALL_VAL(val) \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ ARM_SMCCC_OWNER_STANDARD, val)
+
+#ifndef SMCCC_PCI_VERSION
+#define SMCCC_PCI_VERSION SMCCC_PCI_CALL_VAL(0x0130)
+#endif
+
+#ifndef SMCCC_PCI_FEATURES
+#define SMCCC_PCI_FEATURES SMCCC_PCI_CALL_VAL(0x131)
+#endif
+
+#ifndef SMCCC_PCI_READ
+#define SMCCC_PCI_READ SMCCC_PCI_CALL_VAL(0x132)
+#endif
+
+#ifndef SMCCC_PCI_WRITE
+#define SMCCC_PCI_WRITE SMCCC_PCI_CALL_VAL(0x133)
+#endif
+
+#ifndef SMCCC_PCI_SEG_INFO
+#define SMCCC_PCI_SEG_INFO SMCCC_PCI_CALL_VAL(0x134)
+#endif
+
+/*
+ * smccc_test_copy_to_user()
+ * copy arm_res a* registers to user space test_arg w array
+ */
+static int smccc_test_copy_to_user(unsigned long arg, struct arm_smccc_res *arm_res, int conduit)
+{
+ struct smccc_test_arg test_arg = { }, __user *test_arg_user;
+
+ test_arg_user = (struct smccc_test_arg __user *)arg;
+
+ test_arg.size = sizeof(test_arg);
+ test_arg.conduit = conduit;
+ test_arg.w[0] = arm_res->a0;
+ test_arg.w[1] = arm_res->a1;
+ test_arg.w[2] = arm_res->a2;
+ test_arg.w[3] = arm_res->a3;
+
+ if (copy_to_user(test_arg_user, &test_arg, sizeof(*test_arg_user)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * smccc_test_copy_from_user()
+ * copy user space test_arg data to test_arg
+ */
+static int smccc_test_copy_from_user(struct smccc_test_arg *test_arg, unsigned long arg)
+{
+ struct smccc_test_arg __user *user;
+
+ user = (struct smccc_test_arg __user *)arg;
+ return copy_from_user(test_arg, user, sizeof(*test_arg));
+}
+
+static long smccc_test_pci_version(unsigned long arg)
+{
+ struct arm_smccc_res arm_res = { };
+ int conduit;
+
+ conduit = arm_smccc_1_1_invoke(SMCCC_PCI_VERSION, 0, 0, 0, 0, 0, 0, 0, &arm_res);
+
+ return smccc_test_copy_to_user(arg, &arm_res, conduit);
+}
+
+static long smccc_test_pci_features(unsigned long arg)
+{
+ struct arm_smccc_res arm_res = { };
+ struct smccc_test_arg test_arg;
+ int ret, conduit;
+
+ ret = smccc_test_copy_from_user(&test_arg, arg);
+ if (ret)
+ return ret;
+ conduit = arm_smccc_1_1_invoke(SMCCC_PCI_FEATURES, test_arg.w[0], 0, 0, 0, 0, 0, 0, &arm_res);
+
+ return smccc_test_copy_to_user(arg, &arm_res, conduit);
+}
+
+static long smccc_test_pci_get_seg_info(unsigned long arg)
+{
+ struct arm_smccc_res arm_res = { };
+ struct smccc_test_arg test_arg;
+ int ret, conduit;
+
+ ret = smccc_test_copy_from_user(&test_arg, arg);
+ if (ret)
+ return ret;
+
+ conduit = arm_smccc_1_1_invoke(SMCCC_PCI_SEG_INFO, test_arg.w[1], 0, 0, 0, 0, 0, 0, &arm_res);
+
+ return smccc_test_copy_to_user(arg, &arm_res, conduit);
+}
+
+static long smccc_test_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ u32 size;
+ u32 __user *user_size = (u32 __user *)arg;
+
+ if (get_user(size, user_size))
+ return -EFAULT;
+ if (size != sizeof(struct smccc_test_arg))
+ return -EINVAL;
+
+ switch (cmd) {
+ case SMCCC_TEST_PCI_VERSION:
+ return smccc_test_pci_version(arg);
+ case SMCCC_TEST_PCI_FEATURES:
+ return smccc_test_pci_features(arg);
+ case SMCCC_TEST_PCI_READ:
+ /* TODO */
+ return -ENOTSUPP;
+ case SMCCC_TEST_PCI_WRITE:
+ /* TODO */
+ return -ENOTSUPP;
+ case SMCCC_TEST_PCI_GET_SEG_INFO:
+ return smccc_test_pci_get_seg_info(arg);
+ default:
+ break;
+ }
+
+ return -ENOTTY;
+}
+
+static int smccc_test_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int smccc_test_close(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static const struct file_operations smccc_test_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = smccc_test_ioctl,
+ .open = smccc_test_open,
+ .release = smccc_test_close,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice smccc_test_dev = {
+ MISC_DYNAMIC_MINOR,
+ "smccc_test",
+ &smccc_test_fops
+};
+
+static int __init smccc_test_init(void)
+{
+ int ret;
+
+ ret = arm_smccc_get_version();
+ pr_info(MODULE_NAME ": ARM SMCCC version %d.%d.%d\n",
+ (ret >> 16) & 0xff, (ret >> 8) & 0xff, ret & 0xff);
+
+ ret = misc_register(&smccc_test_dev);
+ if (ret) {
+ pr_err(MODULE_NAME ": can't misc_register on minor=%d\n",
+ MISC_DYNAMIC_MINOR);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit smccc_test_exit(void)
+{
+ misc_deregister(&smccc_test_dev);
+}
+
+#else
+
+static int __init smccc_test_init(void)
+{
+ pr_info(MODULE_NAME ": ARM SMCCC not supported on this kernel and architecture\n",
+
+ return -ENODEV;
+}
+
+static void __exit smccc_test_exit(void)
+{
+}
+
+#endif
+
+module_init(smccc_test_init);
+module_exit(smccc_test_exit);
diff --git a/smccc_test/smccc_test.h b/smccc_test/smccc_test.h
new file mode 100644
index 00000000..355f50d3
--- /dev/null
+++ b/smccc_test/smccc_test.h
@@ -0,0 +1,44 @@
+/*
+ * SMCCC test driver
+ *
+ * Copyright(C) 2021 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+#ifndef _SMCCC_TEST_H_
+#define _SMCCC_TEST_H_
+
+#include <linux/types.h>
+
+struct smccc_test_arg {
+ __u32 size;
+ __u32 conduit;
+ __u32 w[8];
+};
+
+#define SMCCC_TEST_PCI_VERSION \
+ _IOWR('p', 0x01, struct smccc_test_arg)
+#define SMCCC_TEST_PCI_FEATURES \
+ _IOWR('p', 0x02, struct smccc_test_arg)
+#define SMCCC_TEST_PCI_READ \
+ _IOWR('p', 0x03, struct smccc_test_arg)
+#define SMCCC_TEST_PCI_WRITE \
+ _IOWR('p', 0x04, struct smccc_test_arg)
+#define SMCCC_TEST_PCI_GET_SEG_INFO \
+ _IOWR('p', 0x05, struct smccc_test_arg)
+
+#endif
+
diff --git a/src/Makefile.am b/src/Makefile.am
index f8066aff..9a26af86 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -11,6 +11,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/src/acpica/source/include \
-I$(top_srcdir)/src/acpica/source/compiler \
-I$(top_srcdir)/efi_runtime \
+ -I$(top_srcdir)/smccc_test \
-pthread `pkg-config --cflags glib-2.0 gio-2.0` \
-Wall -Werror -Wextra \
-Wno-address-of-packed-member \
@@ -186,6 +187,7 @@ fwts_SOURCES = main.c \
pci/aspm/aspm.c \
pci/crs/crs.c \
pci/maxreadreq/maxreadreq.c \
+ pci/smccc/smccc.c \
tpm/tpmevlog/tpmevlog.c \
tpm/tpmevlogdump/tpmevlogdump.c \
uefi/csm/csm.c \
diff --git a/src/pci/smccc/smccc.c b/src/pci/smccc/smccc.c
new file mode 100644
index 00000000..a94d0b39
--- /dev/null
+++ b/src/pci/smccc/smccc.c
@@ -0,0 +1,333 @@
+/*
+ *
+ * Copyright (C) 2021 Canonical
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include "fwts.h"
+
+#ifdef FWTS_ARCH_AARCH64
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#include "smccc_test.h"
+
+/*
+ * ARM SMCCC tests,
+ * https://developer.arm.com/documentation/den0115/latest
+ */
+
+/* SMCCC conduit types */
+enum {
+ FWTS_SMCCC_CONDUIT_NONE,
+ FWTS_SMCCC_CONDUIT_SMC,
+ FWTS_SMCCC_CONDUIT_HVC,
+};
+
+/* SMCCC API function ids */
+#define PCI_VERSION (0x84000130)
+#define PCI_FEATURES (0x84000131)
+#define PCI_READ (0x84000132)
+#define PCI_WRITE (0x84000133)
+#define PCI_GET_SEG_INFO (0x84000134)
+
+/* SMCCC API id to name mapping */
+typedef struct {
+ const uint32_t pci_func_id;
+ const char *pci_func_id_name;
+ bool implemented;
+} pci_func_id_t;
+
+static pci_func_id_t pci_func_ids[] = {
+ { PCI_VERSION, "PCI_VERSION", false },
+ { PCI_FEATURES, "PCI_FEATURES", false },
+ { PCI_READ, "PCI_READ", false },
+ { PCI_WRITE, "PCI_WRITE", false },
+ { PCI_GET_SEG_INFO, "PCI_GET_SEG_INFO", false },
+};
+
+static const char *module_name = "smccc_test";
+static const char *dev_name = "/dev/smccc_test";
+static bool module_loaded;
+static int smccc_fd = -1;
+
+static int smccc_init(fwts_framework *fw)
+{
+ if (fwts_module_load(fw, module_name) != FWTS_OK) {
+ module_loaded = false;
+ smccc_fd = -1;
+ return FWTS_ERROR;
+ }
+
+ smccc_fd = open(dev_name, O_RDWR);
+ if (smccc_fd < 0) {
+ smccc_fd = -1;
+ fwts_log_error(fw, "Cannot open %s, errno=%d (%s)\n",
+ dev_name, errno, strerror(errno));
+ }
+
+ return FWTS_OK;
+}
+
+static int smccc_deinit(fwts_framework *fw)
+{
+ if (smccc_fd >= 0) {
+ close(smccc_fd);
+ smccc_fd = -1;
+ }
+
+ if (module_loaded && fwts_module_unload(fw, module_name) != FWTS_OK)
+ return FWTS_ERROR;
+
+ module_loaded = true;
+
+ return FWTS_OK;
+}
+
+/*
+ * smccc_pci_conduit_name()
+ * map the conduit number to human readable string
+ */
+static char *smccc_pci_conduit_name(struct smccc_test_arg *arg)
+{
+ static char unknown[32];
+
+ switch (arg->conduit) {
+ case FWTS_SMCCC_CONDUIT_NONE:
+ return "SMCCC_CONDUIT_NONE";
+ case FWTS_SMCCC_CONDUIT_HVC:
+ return "SMCCC_CONDUIT_HVC";
+ case FWTS_SMCCC_CONDUIT_SMC:
+ return "SMCCC_CONDUIT_SMC";
+ default:
+ break;
+ }
+
+ snprintf(unknown, sizeof(unknown), "Unknown: 0x%x", arg->conduit);
+ return unknown;
+}
+
+/*
+ * smccc_pci_conduit_check()
+ * check if conduit number is valid
+ */
+static int smccc_pci_conduit_check(fwts_framework *fw, struct smccc_test_arg *arg)
+{
+ switch (arg->conduit) {
+ case FWTS_SMCCC_CONDUIT_HVC:
+ return FWTS_OK;
+ case FWTS_SMCCC_CONDUIT_SMC:
+ return FWTS_OK;
+ default:
+ fwts_log_error(fw, "Invalid SMCCC conduit used: %s\n",
+ smccc_pci_conduit_name(arg));
+ return FWTS_ERROR;
+ }
+ return FWTS_OK;
+}
+
+/*
+ * smccc_pci_func_implemented()
+ * return true if function has been implemented, only valid
+ * once smccc_pci_features_test has been run.
+ */
+static bool smccc_pci_func_implemented(const uint32_t pci_func_id)
+{
+ size_t i;
+
+ for (i = 0; i < FWTS_ARRAY_SIZE(pci_func_ids); i++) {
+ if (pci_func_ids[i].pci_func_id == pci_func_id)
+ return pci_func_ids[i].implemented;
+ }
+ return false;
+}
+
+/*
+ * smccc_pci_version_test()
+ * test SMCCC function PCI_VERSION
+ */
+static int smccc_pci_version_test(fwts_framework *fw)
+{
+ int ret;
+ struct smccc_test_arg arg = { };
+
+ arg.size = sizeof(arg);
+ arg.w[0] = PCI_VERSION;
+
+ ret = ioctl(smccc_fd, SMCCC_TEST_PCI_VERSION, &arg);
+ if (ret < 0) {
+ fwts_log_error(fw, "SMCCC test driver ioctl SMCCC_TEST_PCI_VERSION "
+ "failed, errno=%d (%s)\n", errno, strerror(errno));
+ return FWTS_ERROR;
+ }
+ if (smccc_pci_conduit_check(fw, &arg) != FWTS_OK)
+ return FWTS_ERROR;
+
+ fwts_log_info_verbatim(fw, " SMCCC conduit type: 0x%x ('%s')",
+ arg.conduit, smccc_pci_conduit_name(&arg));
+
+ fwts_log_info_verbatim(fw, " Major Rev: 0x%" PRIx16 ", Minor Rev: 0x%" PRIx16,
+ (arg.w[0] >> 16) & 0xffff, arg.w[0] & 0xffff);
+ fwts_passed(fw, "SMCCC v1.0 PCI_VERSION passed");
+
+ return FWTS_OK;
+}
+
+/*
+ * smccc_pci_features_test()
+ * test SMCCC function PCI_FEATURES
+ */
+static int smccc_pci_features_test(fwts_framework *fw)
+{
+ struct smccc_test_arg arg = { };
+ int ret, implemented_funcs = 0;
+ bool passed = true;
+ static const char *test = "SMCCC v1.0 PCI_FEATURES";
+ size_t i;
+
+ /*
+ * Check SMCCC functions are implemented in the firmware
+ */
+ for (i = 0; i < FWTS_ARRAY_SIZE(pci_func_ids); i++) {
+ memset(&arg, 0, sizeof(arg));
+
+ /* Assume it is not implemented */
+ pci_func_ids[i].implemented = false;
+
+ arg.size = sizeof(arg);
+ arg.w[0] = PCI_FEATURES;
+ arg.w[1] = pci_func_ids[i].pci_func_id;
+ ret = ioctl(smccc_fd, SMCCC_TEST_PCI_FEATURES, &arg);
+ if (ret < 0) {
+ passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH, "SMCCC_PCI_VERSION",
+ "SMCCC test driver ioctl SMCCC_TEST_PCI_FEATURES "
+ "failed, errno=%d (%s)\n", errno, strerror(errno));
+ } else {
+ const bool implemented = (int)arg.w[0] >= 0;
+
+ fwts_log_info_verbatim(fw, " function 0x%x %-18.18s: %simplemented (%x)",
+ pci_func_ids[i].pci_func_id,
+ pci_func_ids[i].pci_func_id_name,
+ implemented ? "" : "not ",
+ arg.w[0]);
+ if (implemented) {
+ pci_func_ids[i].implemented = true;
+ implemented_funcs++;
+ }
+ }
+ }
+ if (implemented_funcs == 0)
+ fwts_log_warning(fw, "Note: No PCI functions were implemented");
+
+ if (passed)
+ fwts_passed(fw, "%s", test);
+
+ return FWTS_OK;
+}
+
+/*
+ * smccc_pci_get_seg_info()
+ * test SMCCC function PCI_GET_SEG_INFO
+ */
+static int smccc_pci_get_seg_info(fwts_framework *fw)
+{
+ struct smccc_test_arg arg = { };
+ int ret, segments = 0;
+ bool passed = true;
+ static const char *test = "SMCCC v1.0 PCI_GET_SEG_INFO";
+ int i;
+
+ if (!smccc_pci_func_implemented(PCI_GET_SEG_INFO)) {
+ fwts_skipped(fw, "%s: not enabled on this platform", test);
+ return EXIT_SUCCESS;
+ }
+
+ /*
+ * Scan over all potential 65536 segment infos..
+ */
+ for (i = 0; i <= 0xffff; i++) {
+ memset(&arg, 0, sizeof(arg));
+
+ arg.size = sizeof(arg);
+ arg.w[0] = PCI_GET_SEG_INFO;
+ arg.w[1] = i & 0xffff;
+ ret = ioctl(smccc_fd, SMCCC_TEST_PCI_GET_SEG_INFO, &arg);
+ if (ret < 0) {
+ passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH, "SMCCC_PCI_VERSION",
+ "SMCCC test driver ioctl PCI_GET_SEG_INFO "
+ "failed, errno=%d (%s)\n", errno, strerror(errno));
+ break;
+ } else {
+ if (arg.w[0] == 0) {
+ const uint8_t pci_bus_start = arg.w[1] & 0xff;
+ const uint8_t pci_bus_end = (arg.w[1] >> 8) & 0xff;
+ const uint32_t next_seg = arg.w[2];
+
+ fwts_log_info_verbatim(fw, " PCI segment %4x: Bus 0x%2.2x .. 0x%2.2x",
+ i, (int)pci_bus_start, (int)pci_bus_end);
+ segments++;
+
+ /*
+ * a zero next segment id marks the end
+ * of the segment information structs
+ */
+ if (next_seg == 0)
+ break;
+
+ /* if next_seg is valid skip to this */
+ if (next_seg <= 0xffff)
+ i = next_seg;
+ } else {
+ fwts_log_info_verbatim(fw, " PCI segment %4x: error return: %x\n", i, arg.w[0]);
+ break;
+ }
+ }
+ }
+ if (segments == 0)
+ fwts_log_warning(fw, "No PCI segments were found");
+
+ if (passed)
+ fwts_passed(fw, "%s", test);
+
+ return FWTS_OK;
+}
+
+static fwts_framework_minor_test smccc_tests[] = {
+ { smccc_pci_version_test, "Test PCI_VERSION" },
+ { smccc_pci_features_test, "Test PCI_FEATURES" },
+ { smccc_pci_get_seg_info, "Test PCI_GET_SEG_INFO" },
+ { NULL, NULL }
+};
+
+static fwts_framework_ops smcccops = {
+ .description = "ARM64 PCI SMMCCC tests.",
+ .init = smccc_init,
+ .deinit = smccc_deinit,
+ .minor_tests = smccc_tests
+};
+
+FWTS_REGISTER("smccc", &smcccops, FWTS_TEST_ANYTIME, FWTS_FLAG_UNSAFE | FWTS_FLAG_ROOT_PRIV)
+
+#endif
--
2.32.0
More information about the fwts-devel
mailing list