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) 2012-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, 0, 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) 2012-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
