From patchwork Mon Aug 23 21:42:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Colin Ian King X-Patchwork-Id: 1520004 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.ubuntu.com (client-ip=91.189.94.19; helo=huckleberry.canonical.com; envelope-from=fwts-devel-bounces@lists.ubuntu.com; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=QzeyVDZY; dkim-atps=neutral Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Gtm256XfNz9sW5 for ; Tue, 24 Aug 2021 07:43:09 +1000 (AEST) Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.86_2) (envelope-from ) id 1mIHik-0000wt-OI; Mon, 23 Aug 2021 21:43:06 +0000 Received: from smtp-relay-canonical-1.internal ([10.131.114.174] helo=smtp-relay-canonical-1.canonical.com) by huckleberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1mIHid-0000wQ-S6 for fwts-devel@lists.ubuntu.com; Mon, 23 Aug 2021 21:42:59 +0000 Received: from localhost (cpc154979-craw9-2-0-cust193.16-3.cable.virginm.net [80.193.200.194]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-canonical-1.canonical.com (Postfix) with ESMTPSA id A25B33F105; Mon, 23 Aug 2021 21:42:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1629754979; bh=L0Dw3sjzWhZT/ircbpXGS/DoYPh3pmkLorJ5NhEhYBQ=; h=From:To:Subject:Date:Message-Id:MIME-Version:Content-Type; b=QzeyVDZYnEPbVX5no8Lg98eJSc+TacJN4S5aGDQAV4KsmMKYrPXGUMdkwQYyncxP1 Ln9IA/1p4PRxG7hP5cHOFpogzeH37viL/K8QvWzaNcZZQV9Q+ukO1bgc/KU3a2HfDk oWCUkKOcDDbUv/Usa2T5JLBuRDsZHo4Xo5UPmkFImI1RdIv6cAFjmOH36Ur3/cLP9N 2RGw8fy8R/5RP9ZHkci2XfbN99h9ApfspqxckjNUBLG2GspsERjZ7Ezh1UeS19bVB9 uNH9V8ft3Q7yijUnfTfJNUsUcb8M7miKW7IyDRn8IQCgznHFGm22SgONxq93+CP6bJ jq5FJnSc04yBg== From: Colin King To: fwts-devel@lists.ubuntu.com Subject: [PATCH][V2] ARM64 SMCCC firmware API tests Date: Mon, 23 Aug 2021 22:42:59 +0100 Message-Id: <20210823214259.1903163-1-colin.king@canonical.com> X-Mailer: git-send-email 2.32.0 MIME-Version: 1.0 X-BeenThere: fwts-devel@lists.ubuntu.com X-Mailman-Version: 2.1.20 Precedence: list List-Id: Firmware Test Suite Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: fwts-devel-bounces@lists.ubuntu.com Sender: "fwts-devel" From: Colin Ian King 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 Acked-by: Alex Hung Acked-by: Ivan Hu --- 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 +#include +#include +#include +#include +#include +#include + +/* + * ARM SMCCC kernel test driver + * https://developer.arm.com/documentation/den0115/latest + */ + +#if defined(__aarch64__) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)) +#include +#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 + +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 +#include +#include +#include +#include +#include +#include +#include + +#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