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