ACK: [PATCH][V2] ARM64 SMCCC firmware API tests
Alex Hung
alex.hung at canonical.com
Mon Aug 23 22:37:03 UTC 2021
On 2021-08-23 3:42 p.m., Colin King wrote:
> 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
>
Acked-by: Alex Hung <alex.hung at canonical.com>
More information about the fwts-devel
mailing list