All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V8 1/2] tools arch x86: Add Intel SDSi provisiong tool
@ 2022-02-17 23:09 David E. Box
  2022-02-17 23:09 ` [PATCH V8 2/2] selftests: sdsi: test sysfs setup David E. Box
  2022-02-22 14:55 ` [PATCH V8 1/2] tools arch x86: Add Intel SDSi provisiong tool Hans de Goede
  0 siblings, 2 replies; 4+ messages in thread
From: David E. Box @ 2022-02-17 23:09 UTC (permalink / raw)
  To: hdegoede, david.e.box, gregkh, andriy.shevchenko,
	srinivas.pandruvada, mgross
  Cc: linux-kernel, platform-driver-x86

Add tool for key certificate and activation payload provisioning on
Intel CPUs supporting Software Defined Silicon (SDSi).

Signed-off-by: David E. Box <david.e.box@linux.intel.com>
---

Applied on review-hans branch.

V8
  - Rename sdsi to intel_sdsi and add install target
  - Fix compiler warning for signedness mismatch
  - Add missing break in CMD_NONE case to avoid fall through
V7
  - No changes.
V6
  - No changes.
V5
  - Update copyright to 2022
V4
  - No changes.
V3
  - Move from samples to tools.
  - Fix bit fields in availability structure.
  - Check provisioning availability before issuing command.

V2
  - New patch.

 MAINTAINERS                            |   1 +
 tools/arch/x86/intel_sdsi/Makefile     |  21 +
 tools/arch/x86/intel_sdsi/intel_sdsi.c | 541 +++++++++++++++++++++++++
 3 files changed, 563 insertions(+)
 create mode 100644 tools/arch/x86/intel_sdsi/Makefile
 create mode 100644 tools/arch/x86/intel_sdsi/intel_sdsi.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 136f817428cf..dc3c9f271463 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9871,6 +9871,7 @@ INTEL SDSI DRIVER
 M:	David E. Box <david.e.box@linux.intel.com>
 S:	Supported
 F:	drivers/platform/x86/intel/sdsi.c
+F:	tools/arch/x86/intel_sdsi/
 
 INTEL SKYLAKE INT3472 ACPI DEVICE DRIVER
 M:	Daniel Scally <djrscally@gmail.com>
diff --git a/tools/arch/x86/intel_sdsi/Makefile b/tools/arch/x86/intel_sdsi/Makefile
new file mode 100644
index 000000000000..5de2288cda79
--- /dev/null
+++ b/tools/arch/x86/intel_sdsi/Makefile
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0
+# Makefile for Intel Software Defined Silicon provisioning tool
+
+intel_sdsi: intel_sdsi.c
+
+CFLAGS = -Wextra
+
+BINDIR ?= /usr/sbin
+
+override CFLAGS += -O2 -Wall
+
+%: %.c
+	$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+
+.PHONY : clean
+clean :
+	@rm -f intel_sdsi
+
+install : intel_sdsi
+	install -d  $(DESTDIR)$(BINDIR)
+	install -m 755 -p intel_sdsi $(DESTDIR)$(BINDIR)/intel_sdsi
diff --git a/tools/arch/x86/intel_sdsi/intel_sdsi.c b/tools/arch/x86/intel_sdsi/intel_sdsi.c
new file mode 100644
index 000000000000..52acb518e845
--- /dev/null
+++ b/tools/arch/x86/intel_sdsi/intel_sdsi.c
@@ -0,0 +1,541 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * sdsi: Intel Software Defined Silicon tool for provisioning certificates
+ * and activation payloads on supported cpus.
+ *
+ * See https://github.com/intel/intel-sdsi/blob/master/os-interface.rst
+ * for register descriptions.
+ *
+ * Copyright (C) 2022 Intel Corporation. All rights reserved.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+
+#define SDSI_DEV		"intel_vsec.sdsi"
+#define AUX_DEV_PATH		"/sys/bus/auxiliary/devices/"
+#define SDSI_PATH		(AUX_DEV_DIR SDSI_DEV)
+#define GUID			0x6dd191
+#define REGISTERS_MIN_SIZE	72
+
+#define __round_mask(x, y) ((__typeof__(x))((y) - 1))
+#define round_up(x, y) ((((x) - 1) | __round_mask(x, y)) + 1)
+
+struct enabled_features {
+	uint64_t reserved:3;
+	uint64_t sdsi:1;
+	uint64_t reserved1:60;
+};
+
+struct auth_fail_count {
+	uint64_t key_failure_count:3;
+	uint64_t key_failure_threshold:3;
+	uint64_t auth_failure_count:3;
+	uint64_t auth_failure_threshold:3;
+	uint64_t reserved:52;
+};
+
+struct availability {
+	uint64_t reserved:48;
+	uint64_t available:3;
+	uint64_t threshold:3;
+};
+
+struct sdsi_regs {
+	uint64_t ppin;
+	uint64_t reserved;
+	struct enabled_features en_features;
+	uint64_t reserved1;
+	struct auth_fail_count auth_fail_count;
+	struct availability prov_avail;
+	uint64_t reserved2;
+	uint64_t reserved3;
+	uint64_t socket_id;
+};
+
+struct sdsi_dev {
+	struct sdsi_regs regs;
+	char *dev_name;
+	char *dev_path;
+	int guid;
+};
+
+enum command {
+	CMD_NONE,
+	CMD_LIST_DEVICES,
+	CMD_SOCKET_INFO,
+	CMD_DUMP_CERT,
+	CMD_PROV_AKC,
+	CMD_PROV_CAP,
+};
+
+static void sdsi_list_devices(void)
+{
+	struct dirent *entry;
+	DIR *aux_dir;
+	bool found = false;
+
+	aux_dir = opendir(AUX_DEV_PATH);
+	if (!aux_dir) {
+		fprintf(stderr, "Cannot open directory %s\n", AUX_DEV_PATH);
+		return;
+	}
+
+	while ((entry = readdir(aux_dir))) {
+		if (!strncmp(SDSI_DEV, entry->d_name, strlen(SDSI_DEV))) {
+			found = true;
+			printf("%s\n", entry->d_name);
+		}
+	}
+
+	if (!found)
+		fprintf(stderr, "No sdsi devices found.\n");
+}
+
+static int sdsi_update_registers(struct sdsi_dev *s)
+{
+	FILE *regs_ptr;
+	int ret;
+
+	memset(&s->regs, 0, sizeof(s->regs));
+
+	/* Open the registers file */
+	ret = chdir(s->dev_path);
+	if (ret == -1) {
+		perror("chdir");
+		return ret;
+	}
+
+	regs_ptr = fopen("registers", "r");
+	if (!regs_ptr) {
+		perror("Could not open 'registers' file");
+		return -1;
+	}
+
+	if (s->guid != GUID) {
+		fprintf(stderr, "Unrecognized guid, 0x%x\n", s->guid);
+		fclose(regs_ptr);
+		return -1;
+	}
+
+	/* Update register info for this guid */
+	ret = fread(&s->regs, sizeof(uint8_t), sizeof(s->regs), regs_ptr);
+	if (ret != sizeof(s->regs)) {
+		fprintf(stderr, "Could not read 'registers' file\n");
+		fclose(regs_ptr);
+		return -1;
+	}
+
+	fclose(regs_ptr);
+
+	return 0;
+}
+
+static int sdsi_read_reg(struct sdsi_dev *s)
+{
+	int ret;
+
+	ret = sdsi_update_registers(s);
+	if (ret)
+		return ret;
+
+	/* Print register info for this guid */
+	printf("\n");
+	printf("Socket information for device %s\n", s->dev_name);
+	printf("\n");
+	printf("PPIN:                           0x%lx\n", s->regs.ppin);
+	printf("Enabled Features\n");
+	printf("    SDSi:                       %s\n", !!s->regs.en_features.sdsi ? "Enabled" : "Disabled");
+	printf("Authorization Failure Count\n");
+	printf("    AKC Failure Count:          %d\n", s->regs.auth_fail_count.key_failure_count);
+	printf("    AKC Failure Threshold:      %d\n", s->regs.auth_fail_count.key_failure_threshold);
+	printf("    CAP Failure Count:          %d\n", s->regs.auth_fail_count.auth_failure_count);
+	printf("    CAP Failure Threshold:      %d\n", s->regs.auth_fail_count.auth_failure_threshold);
+	printf("Provisioning Availability\n");
+	printf("    Updates Available:          %d\n", s->regs.prov_avail.available);
+	printf("    Updates Threshold:          %d\n", s->regs.prov_avail.threshold);
+	printf("Socket ID:                      %ld\n", s->regs.socket_id & 0xF);
+
+	return 0;
+}
+
+static int sdsi_certificate_dump(struct sdsi_dev *s)
+{
+	uint64_t state_certificate[512] = {0};
+	bool first_instance;
+	uint64_t previous;
+	FILE *cert_ptr;
+	int i, ret, size;
+
+	ret = sdsi_update_registers(s);
+	if (ret)
+		return ret;
+
+	if (!s->regs.en_features.sdsi) {
+		fprintf(stderr, "SDSi feature is present but not enabled.");
+		fprintf(stderr, " Unable to read state certificate");
+		return -1;
+	}
+
+	ret = chdir(s->dev_path);
+	if (ret == -1) {
+		perror("chdir");
+		return ret;
+	}
+
+	cert_ptr = fopen("state_certificate", "r");
+	if (!cert_ptr) {
+		perror("Could not open 'state_certificate' file");
+		return -1;
+	}
+
+	size = fread(state_certificate, 1, sizeof(state_certificate), cert_ptr);
+	if (!size) {
+		fprintf(stderr, "Could not read 'state_certificate' file\n");
+		fclose(cert_ptr);
+		return -1;
+	}
+
+	printf("%3d: 0x%lx\n", 0, state_certificate[0]);
+	previous = state_certificate[0];
+	first_instance = true;
+
+	for (i = 1; i < (int)(round_up(size, sizeof(uint64_t))/sizeof(uint64_t)); i++) {
+		if (state_certificate[i] == previous) {
+			if (first_instance) {
+				puts("*");
+				first_instance = false;
+			}
+			continue;
+		}
+		printf("%3d: 0x%lx\n", i, state_certificate[i]);
+		previous = state_certificate[i];
+		first_instance = true;
+	}
+	printf("%3d\n", i);
+
+	fclose(cert_ptr);
+
+	return 0;
+}
+
+static int sdsi_provision(struct sdsi_dev *s, char *bin_file, enum command command)
+{
+	int bin_fd, prov_fd, size, ret;
+	char buf[4096] = { 0 };
+	char cap[] = "provision_cap";
+	char akc[] = "provision_akc";
+	char *prov_file;
+
+	if (!bin_file) {
+		fprintf(stderr, "No binary file provided\n");
+		return -1;
+	}
+
+	/* Open the binary */
+	bin_fd = open(bin_file, O_RDONLY);
+	if (bin_fd == -1) {
+		fprintf(stderr, "Could not open file %s: %s\n", bin_file, strerror(errno));
+		return bin_fd;
+	}
+
+	prov_file = (command == CMD_PROV_AKC) ? akc : cap;
+
+	ret = chdir(s->dev_path);
+	if (ret == -1) {
+		perror("chdir");
+		close(bin_fd);
+		return ret;
+	}
+
+	/* Open the provision file */
+	prov_fd = open(prov_file, O_WRONLY);
+	if (prov_fd == -1) {
+		fprintf(stderr, "Could not open file %s: %s\n", prov_file, strerror(errno));
+		close(bin_fd);
+		return prov_fd;
+	}
+
+	/* Read the binary file into the buffer */
+	size = read(bin_fd, buf, 4096);
+	if (size == -1) {
+		close(bin_fd);
+		close(prov_fd);
+		return -1;
+	}
+
+	ret = write(prov_fd, buf, size);
+	if (ret == -1) {
+		close(bin_fd);
+		close(prov_fd);
+		perror("Provisioning failed");
+		return ret;
+	}
+
+	printf("Provisioned %s file %s successfully\n", prov_file, bin_file);
+
+	close(bin_fd);
+	close(prov_fd);
+
+	return 0;
+}
+
+static int sdsi_provision_akc(struct sdsi_dev *s, char *bin_file)
+{
+	int ret;
+
+	ret = sdsi_update_registers(s);
+	if (ret)
+		return ret;
+
+	if (!s->regs.en_features.sdsi) {
+		fprintf(stderr, "SDSi feature is present but not enabled. Unable to provision");
+		return -1;
+	}
+
+	if (!s->regs.prov_avail.available) {
+		fprintf(stderr, "Maximum number of updates (%d) has been reached.\n",
+			s->regs.prov_avail.threshold);
+		return -1;
+	}
+
+	if (s->regs.auth_fail_count.key_failure_count ==
+	    s->regs.auth_fail_count.key_failure_threshold) {
+		fprintf(stderr, "Maximum number of AKC provision failures (%d) has been reached.\n",
+			s->regs.auth_fail_count.key_failure_threshold);
+		fprintf(stderr, "Power cycle the system to reset the counter\n");
+		return -1;
+	}
+
+	return sdsi_provision(s, bin_file, CMD_PROV_AKC);
+}
+
+static int sdsi_provision_cap(struct sdsi_dev *s, char *bin_file)
+{
+	int ret;
+
+	ret = sdsi_update_registers(s);
+	if (ret)
+		return ret;
+
+	if (!s->regs.en_features.sdsi) {
+		fprintf(stderr, "SDSi feature is present but not enabled. Unable to provision");
+		return -1;
+	}
+
+	if (!s->regs.prov_avail.available) {
+		fprintf(stderr, "Maximum number of updates (%d) has been reached.\n",
+			s->regs.prov_avail.threshold);
+		return -1;
+	}
+
+	if (s->regs.auth_fail_count.auth_failure_count ==
+	    s->regs.auth_fail_count.auth_failure_threshold) {
+		fprintf(stderr, "Maximum number of CAP provision failures (%d) has been reached.\n",
+			s->regs.auth_fail_count.auth_failure_threshold);
+		fprintf(stderr, "Power cycle the system to reset the counter\n");
+		return -1;
+	}
+
+	return sdsi_provision(s, bin_file, CMD_PROV_CAP);
+}
+
+static int read_sysfs_data(const char *file, int *value)
+{
+	char buff[16];
+	FILE *fp;
+
+	fp = fopen(file, "r");
+	if (!fp) {
+		perror(file);
+		return -1;
+	}
+
+	if (!fgets(buff, 16, fp)) {
+		fprintf(stderr, "Failed to read file '%s'", file);
+		fclose(fp);
+		return -1;
+	}
+
+	fclose(fp);
+	*value = strtol(buff, NULL, 0);
+
+	return 0;
+}
+
+static struct sdsi_dev *sdsi_create_dev(char *dev_no)
+{
+	int dev_name_len = sizeof(SDSI_DEV) + strlen(dev_no) + 1;
+	struct sdsi_dev *s;
+	int guid;
+	DIR *dir;
+
+	s = (struct sdsi_dev *)malloc(sizeof(*s));
+	if (!s) {
+		perror("malloc");
+		return NULL;
+	}
+
+	s->dev_name = (char *)malloc(sizeof(SDSI_DEV) + strlen(dev_no) + 1);
+	if (!s->dev_name) {
+		perror("malloc");
+		free(s);
+		return NULL;
+	}
+
+	snprintf(s->dev_name, dev_name_len, "%s.%s", SDSI_DEV, dev_no);
+
+	s->dev_path = (char *)malloc(sizeof(AUX_DEV_PATH) + dev_name_len);
+	if (!s->dev_path) {
+		perror("malloc");
+		free(s->dev_name);
+		free(s);
+		return NULL;
+	}
+
+	snprintf(s->dev_path, sizeof(AUX_DEV_PATH) + dev_name_len, "%s%s", AUX_DEV_PATH,
+		 s->dev_name);
+	dir = opendir(s->dev_path);
+	if (!dir) {
+		fprintf(stderr, "Could not open directory '%s': %s\n", s->dev_path,
+			strerror(errno));
+		free(s->dev_path);
+		free(s->dev_name);
+		free(s);
+		return NULL;
+	}
+
+	if (chdir(s->dev_path) == -1) {
+		perror("chdir");
+		free(s->dev_path);
+		free(s->dev_name);
+		free(s);
+		return NULL;
+	}
+
+	if (read_sysfs_data("guid", &guid)) {
+		free(s->dev_path);
+		free(s->dev_name);
+		free(s);
+		return NULL;
+	}
+
+	s->guid = guid;
+
+	return s;
+}
+
+static void sdsi_free_dev(struct sdsi_dev *s)
+{
+	free(s->dev_path);
+	free(s->dev_name);
+	free(s);
+}
+
+static void print_help(char *prog)
+{
+	printf("Usage: %s [-l] [-d dev_no [-is] [-a file] [-c file]]\n", prog);
+
+	printf("\n");
+	printf("Commands:\n");
+	printf("  %-13s\t%s\n", "-l", "list available sdsi devices");
+	printf("  %-13s\t%s\n", "-d <dev_no>", "sdsi device number");
+	printf("  %-13s\t%s\n", "-i", "show socket information");
+	printf("  %-13s\t%s\n", "-s", "dump state certificate data");
+	printf("  %-13s\t%s\n", "-a <file>", "provision socket with AKC file");
+	printf("  %-13s\t%s\n", "-c <file>", "provision socket with CAP file");
+}
+
+int main(int argc, char *argv[])
+{
+	char bin_file[PATH_MAX], *dev_no = NULL;
+	enum command command = CMD_NONE;
+	struct sdsi_dev *s;
+	int ret = 0, opt;
+
+	while ((opt = getopt(argc, argv, "d:lisa:c:h")) != -1) {
+		switch (opt) {
+		case 'd':
+			dev_no = optarg;
+			break;
+		case 'l':
+			command = CMD_LIST_DEVICES;
+			break;
+		case 'i':
+			command = CMD_SOCKET_INFO;
+			break;
+		case 's':
+			command = CMD_DUMP_CERT;
+			break;
+		case 'a':
+		case 'c':
+			if (!access(optarg, F_OK) == 0) {
+				fprintf(stderr, "Could not open file '%s': %s\n", optarg,
+					strerror(errno));
+				return -1;
+			}
+
+			if (!realpath(optarg, bin_file)) {
+				perror("realpath");
+				return -1;
+			}
+
+			command = (opt == 'a') ? CMD_PROV_AKC : CMD_PROV_CAP;
+			break;
+		case 'h':
+		default:
+			print_help(argv[0]);
+			return 0;
+		}
+	}
+
+	if (!dev_no && command != CMD_LIST_DEVICES) {
+		print_help(argv[0]);
+		return -1;
+	}
+
+	if (dev_no) {
+		s = sdsi_create_dev(dev_no);
+		if (!s)
+			return -1;
+	}
+
+	/* Run the command */
+	switch (command) {
+	case CMD_NONE:
+		fprintf(stderr, "need to specify a command\n");
+		print_help(argv[0]);
+		ret = -1;
+		break;
+	case CMD_LIST_DEVICES:
+		sdsi_list_devices();
+		break;
+	case CMD_SOCKET_INFO:
+		ret = sdsi_read_reg(s);
+		break;
+	case CMD_DUMP_CERT:
+		ret = sdsi_certificate_dump(s);
+		break;
+	case CMD_PROV_AKC:
+		ret = sdsi_provision_akc(s, bin_file);
+		break;
+	case CMD_PROV_CAP:
+		ret = sdsi_provision_cap(s, bin_file);
+		break;
+	}
+
+
+	if (dev_no)
+		sdsi_free_dev(s);
+
+	return ret;
+}

base-commit: 42f8bcb37e2c65931799cdf61d086ed78456e798
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH V8 2/2] selftests: sdsi: test sysfs setup
  2022-02-17 23:09 [PATCH V8 1/2] tools arch x86: Add Intel SDSi provisiong tool David E. Box
@ 2022-02-17 23:09 ` David E. Box
  2022-02-22 14:56   ` Hans de Goede
  2022-02-22 14:55 ` [PATCH V8 1/2] tools arch x86: Add Intel SDSi provisiong tool Hans de Goede
  1 sibling, 1 reply; 4+ messages in thread
From: David E. Box @ 2022-02-17 23:09 UTC (permalink / raw)
  To: hdegoede, david.e.box, gregkh, andriy.shevchenko,
	srinivas.pandruvada, mgross
  Cc: linux-kernel, platform-driver-x86

Tests file configuration and error handling of the Intel Software
Defined Silicon sysfs ABI.

Signed-off-by: David E. Box <david.e.box@linux.intel.com>
---
Applied on review-hans branch.

V8
  - Skip if python3 or pytest aren't installed
  - Do not remove driver after test is run
V7
  - No changes.
V6
  - No changes.
V5
  - No changes.
V4
  - No changes.
V3
  - Add tests to check PCI device removal handling and to check for
    driver memory leaks.
V2
  - New patch.

 MAINTAINERS                                   |   1 +
 tools/testing/selftests/drivers/sdsi/sdsi.sh  |  26 ++
 .../selftests/drivers/sdsi/sdsi_test.py       | 226 ++++++++++++++++++
 3 files changed, 253 insertions(+)
 create mode 100755 tools/testing/selftests/drivers/sdsi/sdsi.sh
 create mode 100644 tools/testing/selftests/drivers/sdsi/sdsi_test.py

diff --git a/MAINTAINERS b/MAINTAINERS
index dc3c9f271463..be2c4c63e58f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9872,6 +9872,7 @@ M:	David E. Box <david.e.box@linux.intel.com>
 S:	Supported
 F:	drivers/platform/x86/intel/sdsi.c
 F:	tools/arch/x86/intel_sdsi/
+F:	tools/testing/selftests/drivers/sdsi/
 
 INTEL SKYLAKE INT3472 ACPI DEVICE DRIVER
 M:	Daniel Scally <djrscally@gmail.com>
diff --git a/tools/testing/selftests/drivers/sdsi/sdsi.sh b/tools/testing/selftests/drivers/sdsi/sdsi.sh
new file mode 100755
index 000000000000..b938b1d46b04
--- /dev/null
+++ b/tools/testing/selftests/drivers/sdsi/sdsi.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# Runs tests for the intel_sdsi driver
+
+if ! command -v python3 > /dev/null 2>&1; then
+	echo "drivers/sdsi: [SKIP] python3 not installed"
+	exit 77
+fi
+
+if ! python -c "import pytest" > /dev/null 2>&1; then
+	echo "drivers/sdsi: [SKIP] pytest module not installed"
+	exit 77
+fi
+
+if ! /sbin/modprobe -q -r intel_sdsi; then
+	echo "drivers/sdsi: [SKIP]"
+	exit 77
+fi
+
+if /sbin/modprobe -q intel_sdsi; then
+	python3 -m pytest sdsi_test.py
+	echo "drivers/sdsi: [OK]"
+else
+	echo "drivers/sdsi: [FAIL]"
+	exit 1
+fi
diff --git a/tools/testing/selftests/drivers/sdsi/sdsi_test.py b/tools/testing/selftests/drivers/sdsi/sdsi_test.py
new file mode 100644
index 000000000000..4922edfe461f
--- /dev/null
+++ b/tools/testing/selftests/drivers/sdsi/sdsi_test.py
@@ -0,0 +1,226 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+from struct import pack
+from time import sleep
+
+import errno
+import glob
+import os
+import subprocess
+
+try:
+    import pytest
+except ImportError:
+    print("Unable to import pytest python module.")
+    print("\nIf not already installed, you may do so with:")
+    print("\t\tpip3 install pytest")
+    exit(1)
+
+SOCKETS = glob.glob('/sys/bus/auxiliary/devices/intel_vsec.sdsi.*')
+NUM_SOCKETS = len(SOCKETS)
+
+MODULE_NAME = 'sdsi'
+DEV_PREFIX = 'intel_vsec.sdsi'
+CLASS_DIR = '/sys/bus/auxiliary/devices'
+GUID = "0x6dd191"
+
+def read_bin_file(file):
+    with open(file, mode='rb') as f:
+        content = f.read()
+    return content
+
+def get_dev_file_path(socket, file):
+    return CLASS_DIR + '/' + DEV_PREFIX + '.' + str(socket) + '/' + file
+
+def kmemleak_enabled():
+    kmemleak = "/sys/kernel/debug/kmemleak"
+    return os.path.isfile(kmemleak)
+
+class TestSDSiDriver:
+    def test_driver_loaded(self):
+        lsmod_p = subprocess.Popen(('lsmod'), stdout=subprocess.PIPE)
+        result = subprocess.check_output(('grep', '-q', MODULE_NAME), stdin=lsmod_p.stdout)
+
+@pytest.mark.parametrize('socket', range(0, NUM_SOCKETS))
+class TestSDSiFilesClass:
+
+    def read_value(self, file):
+        f = open(file, "r")
+        value = f.read().strip("\n")
+        return value
+
+    def get_dev_folder(self, socket):
+        return CLASS_DIR + '/' + DEV_PREFIX + '.' + str(socket) + '/'
+
+    def test_sysfs_files_exist(self, socket):
+        folder = self.get_dev_folder(socket)
+        print (folder)
+        assert os.path.isfile(folder + "guid") == True
+        assert os.path.isfile(folder + "provision_akc") == True
+        assert os.path.isfile(folder + "provision_cap") == True
+        assert os.path.isfile(folder + "state_certificate") == True
+        assert os.path.isfile(folder + "registers") == True
+
+    def test_sysfs_file_permissions(self, socket):
+        folder = self.get_dev_folder(socket)
+        mode = os.stat(folder + "guid").st_mode & 0o777
+        assert mode == 0o444    # Read all
+        mode = os.stat(folder + "registers").st_mode & 0o777
+        assert mode == 0o400    # Read owner
+        mode = os.stat(folder + "provision_akc").st_mode & 0o777
+        assert mode == 0o200    # Read owner
+        mode = os.stat(folder + "provision_cap").st_mode & 0o777
+        assert mode == 0o200    # Read owner
+        mode = os.stat(folder + "state_certificate").st_mode & 0o777
+        assert mode == 0o400    # Read owner
+
+    def test_sysfs_file_ownership(self, socket):
+        folder = self.get_dev_folder(socket)
+
+        st = os.stat(folder + "guid")
+        assert st.st_uid == 0
+        assert st.st_gid == 0
+
+        st = os.stat(folder + "registers")
+        assert st.st_uid == 0
+        assert st.st_gid == 0
+
+        st = os.stat(folder + "provision_akc")
+        assert st.st_uid == 0
+        assert st.st_gid == 0
+
+        st = os.stat(folder + "provision_cap")
+        assert st.st_uid == 0
+        assert st.st_gid == 0
+
+        st = os.stat(folder + "state_certificate")
+        assert st.st_uid == 0
+        assert st.st_gid == 0
+
+    def test_sysfs_file_sizes(self, socket):
+        folder = self.get_dev_folder(socket)
+
+        if self.read_value(folder + "guid") == GUID:
+            st = os.stat(folder + "registers")
+            assert st.st_size == 72
+
+        st = os.stat(folder + "provision_akc")
+        assert st.st_size == 1024
+
+        st = os.stat(folder + "provision_cap")
+        assert st.st_size == 1024
+
+        st = os.stat(folder + "state_certificate")
+        assert st.st_size == 4096
+
+    def test_no_seek_allowed(self, socket):
+        folder = self.get_dev_folder(socket)
+        rand_file = bytes(os.urandom(8))
+
+        f = open(folder + "provision_cap", "wb", 0)
+        f.seek(1)
+        with pytest.raises(OSError) as error:
+            f.write(rand_file)
+        assert error.value.errno == errno.ESPIPE
+        f.close()
+
+        f = open(folder + "provision_akc", "wb", 0)
+        f.seek(1)
+        with pytest.raises(OSError) as error:
+            f.write(rand_file)
+        assert error.value.errno == errno.ESPIPE
+        f.close()
+
+    def test_registers_seek(self, socket):
+        folder = self.get_dev_folder(socket)
+
+        # Check that the value read from an offset of the entire
+        # file is none-zero and the same as the value read
+        # from seeking to the same location
+        f = open(folder + "registers", "rb")
+        data = f.read()
+        f.seek(64)
+        id = f.read()
+        assert id != bytes(0)
+        assert data[64:] == id
+        f.close()
+
+@pytest.mark.parametrize('socket', range(0, NUM_SOCKETS))
+class TestSDSiMailboxCmdsClass:
+    def test_provision_akc_eoverflow_1017_bytes(self, socket):
+
+        # The buffer for writes is 1k, of with 8 bytes must be
+        # reserved for the command, leaving 1016 bytes max.
+        # Check that we get an overflow error for 1017 bytes.
+        node = get_dev_file_path(socket, "provision_akc")
+        rand_file = bytes(os.urandom(1017))
+
+        f = open(node, 'wb', 0)
+        with pytest.raises(OSError) as error:
+            f.write(rand_file)
+        assert error.value.errno == errno.EOVERFLOW
+        f.close()
+
+@pytest.mark.parametrize('socket', range(0, NUM_SOCKETS))
+class TestSdsiDriverLocksClass:
+    def test_enodev_when_pci_device_removed(self, socket):
+        node = get_dev_file_path(socket, "provision_akc")
+        dev_name = DEV_PREFIX + '.' + str(socket)
+        driver_dir = CLASS_DIR + '/' + dev_name + "/driver/"
+        rand_file = bytes(os.urandom(8))
+
+        f = open(node, 'wb', 0)
+        g = open(node, 'wb', 0)
+
+        with open(driver_dir + 'unbind', 'w') as k:
+            print(dev_name, file = k)
+
+        with pytest.raises(OSError) as error:
+            f.write(rand_file)
+        assert error.value.errno == errno.ENODEV
+
+        with pytest.raises(OSError) as error:
+            g.write(rand_file)
+        assert error.value.errno == errno.ENODEV
+
+        f.close()
+        g.close()
+
+        # Short wait needed to allow file to close before pulling driver
+        sleep(1)
+
+        p = subprocess.Popen(('modprobe', '-r', 'intel_sdsi'))
+        p.wait()
+        p = subprocess.Popen(('modprobe', '-r', 'intel_vsec'))
+        p.wait()
+        p = subprocess.Popen(('modprobe', 'intel_vsec'))
+        p.wait()
+
+        # Short wait needed to allow driver time to get inserted
+        # before continuing tests
+        sleep(1)
+
+    def test_memory_leak(self, socket):
+        if not kmemleak_enabled:
+            pytest.skip("kmemleak not enabled in kernel")
+
+        dev_name = DEV_PREFIX + '.' + str(socket)
+        driver_dir = CLASS_DIR + '/' + dev_name + "/driver/"
+
+        with open(driver_dir + 'unbind', 'w') as k:
+            print(dev_name, file = k)
+
+        sleep(1)
+
+        subprocess.check_output(('modprobe', '-r', 'intel_sdsi'))
+        subprocess.check_output(('modprobe', '-r', 'intel_vsec'))
+
+        with open('/sys/kernel/debug/kmemleak', 'w') as f:
+            print('scan', file = f)
+        sleep(5)
+
+        assert os.stat('/sys/kernel/debug/kmemleak').st_size == 0
+
+        subprocess.check_output(('modprobe', 'intel_vsec'))
+        sleep(1)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH V8 1/2] tools arch x86: Add Intel SDSi provisiong tool
  2022-02-17 23:09 [PATCH V8 1/2] tools arch x86: Add Intel SDSi provisiong tool David E. Box
  2022-02-17 23:09 ` [PATCH V8 2/2] selftests: sdsi: test sysfs setup David E. Box
@ 2022-02-22 14:55 ` Hans de Goede
  1 sibling, 0 replies; 4+ messages in thread
From: Hans de Goede @ 2022-02-22 14:55 UTC (permalink / raw)
  To: David E. Box, gregkh, andriy.shevchenko, srinivas.pandruvada, mgross
  Cc: linux-kernel, platform-driver-x86

Hi David,

On 2/18/22 00:09, David E. Box wrote:
> Add tool for key certificate and activation payload provisioning on
> Intel CPUs supporting Software Defined Silicon (SDSi).
> 
> Signed-off-by: David E. Box <david.e.box@linux.intel.com>

When building this with gcc12 I get:

cc -Wextra -O2 -Wall -o intel_sdsi intel_sdsi.c 
In function ‘sdsi_provision_cap’,
    inlined from ‘main’ at intel_sdsi.c:532:9:
intel_sdsi.c:326:15: warning: ‘s’ may be used uninitialized [-Wmaybe-uninitialized]
  326 |         ret = sdsi_update_registers(s);
      |               ^~~~~~~~~~~~~~~~~~~~~~~~
intel_sdsi.c: In function ‘main’:
intel_sdsi.c:462:26: note: ‘s’ was declared here
  462 |         struct sdsi_dev *s;
      |                          ^

Please fix this.

Regards,

Hans


> ---
> 
> Applied on review-hans branch.
> 
> V8
>   - Rename sdsi to intel_sdsi and add install target
>   - Fix compiler warning for signedness mismatch
>   - Add missing break in CMD_NONE case to avoid fall through
> V7
>   - No changes.
> V6
>   - No changes.
> V5
>   - Update copyright to 2022
> V4
>   - No changes.
> V3
>   - Move from samples to tools.
>   - Fix bit fields in availability structure.
>   - Check provisioning availability before issuing command.
> 
> V2
>   - New patch.
> 
>  MAINTAINERS                            |   1 +
>  tools/arch/x86/intel_sdsi/Makefile     |  21 +
>  tools/arch/x86/intel_sdsi/intel_sdsi.c | 541 +++++++++++++++++++++++++
>  3 files changed, 563 insertions(+)
>  create mode 100644 tools/arch/x86/intel_sdsi/Makefile
>  create mode 100644 tools/arch/x86/intel_sdsi/intel_sdsi.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 136f817428cf..dc3c9f271463 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -9871,6 +9871,7 @@ INTEL SDSI DRIVER
>  M:	David E. Box <david.e.box@linux.intel.com>
>  S:	Supported
>  F:	drivers/platform/x86/intel/sdsi.c
> +F:	tools/arch/x86/intel_sdsi/
>  
>  INTEL SKYLAKE INT3472 ACPI DEVICE DRIVER
>  M:	Daniel Scally <djrscally@gmail.com>
> diff --git a/tools/arch/x86/intel_sdsi/Makefile b/tools/arch/x86/intel_sdsi/Makefile
> new file mode 100644
> index 000000000000..5de2288cda79
> --- /dev/null
> +++ b/tools/arch/x86/intel_sdsi/Makefile
> @@ -0,0 +1,21 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# Makefile for Intel Software Defined Silicon provisioning tool
> +
> +intel_sdsi: intel_sdsi.c
> +
> +CFLAGS = -Wextra
> +
> +BINDIR ?= /usr/sbin
> +
> +override CFLAGS += -O2 -Wall
> +
> +%: %.c
> +	$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
> +
> +.PHONY : clean
> +clean :
> +	@rm -f intel_sdsi
> +
> +install : intel_sdsi
> +	install -d  $(DESTDIR)$(BINDIR)
> +	install -m 755 -p intel_sdsi $(DESTDIR)$(BINDIR)/intel_sdsi
> diff --git a/tools/arch/x86/intel_sdsi/intel_sdsi.c b/tools/arch/x86/intel_sdsi/intel_sdsi.c
> new file mode 100644
> index 000000000000..52acb518e845
> --- /dev/null
> +++ b/tools/arch/x86/intel_sdsi/intel_sdsi.c
> @@ -0,0 +1,541 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * sdsi: Intel Software Defined Silicon tool for provisioning certificates
> + * and activation payloads on supported cpus.
> + *
> + * See https://github.com/intel/intel-sdsi/blob/master/os-interface.rst
> + * for register descriptions.
> + *
> + * Copyright (C) 2022 Intel Corporation. All rights reserved.
> + */
> +
> +#include <dirent.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +
> +#include <sys/types.h>
> +
> +#define SDSI_DEV		"intel_vsec.sdsi"
> +#define AUX_DEV_PATH		"/sys/bus/auxiliary/devices/"
> +#define SDSI_PATH		(AUX_DEV_DIR SDSI_DEV)
> +#define GUID			0x6dd191
> +#define REGISTERS_MIN_SIZE	72
> +
> +#define __round_mask(x, y) ((__typeof__(x))((y) - 1))
> +#define round_up(x, y) ((((x) - 1) | __round_mask(x, y)) + 1)
> +
> +struct enabled_features {
> +	uint64_t reserved:3;
> +	uint64_t sdsi:1;
> +	uint64_t reserved1:60;
> +};
> +
> +struct auth_fail_count {
> +	uint64_t key_failure_count:3;
> +	uint64_t key_failure_threshold:3;
> +	uint64_t auth_failure_count:3;
> +	uint64_t auth_failure_threshold:3;
> +	uint64_t reserved:52;
> +};
> +
> +struct availability {
> +	uint64_t reserved:48;
> +	uint64_t available:3;
> +	uint64_t threshold:3;
> +};
> +
> +struct sdsi_regs {
> +	uint64_t ppin;
> +	uint64_t reserved;
> +	struct enabled_features en_features;
> +	uint64_t reserved1;
> +	struct auth_fail_count auth_fail_count;
> +	struct availability prov_avail;
> +	uint64_t reserved2;
> +	uint64_t reserved3;
> +	uint64_t socket_id;
> +};
> +
> +struct sdsi_dev {
> +	struct sdsi_regs regs;
> +	char *dev_name;
> +	char *dev_path;
> +	int guid;
> +};
> +
> +enum command {
> +	CMD_NONE,
> +	CMD_LIST_DEVICES,
> +	CMD_SOCKET_INFO,
> +	CMD_DUMP_CERT,
> +	CMD_PROV_AKC,
> +	CMD_PROV_CAP,
> +};
> +
> +static void sdsi_list_devices(void)
> +{
> +	struct dirent *entry;
> +	DIR *aux_dir;
> +	bool found = false;
> +
> +	aux_dir = opendir(AUX_DEV_PATH);
> +	if (!aux_dir) {
> +		fprintf(stderr, "Cannot open directory %s\n", AUX_DEV_PATH);
> +		return;
> +	}
> +
> +	while ((entry = readdir(aux_dir))) {
> +		if (!strncmp(SDSI_DEV, entry->d_name, strlen(SDSI_DEV))) {
> +			found = true;
> +			printf("%s\n", entry->d_name);
> +		}
> +	}
> +
> +	if (!found)
> +		fprintf(stderr, "No sdsi devices found.\n");
> +}
> +
> +static int sdsi_update_registers(struct sdsi_dev *s)
> +{
> +	FILE *regs_ptr;
> +	int ret;
> +
> +	memset(&s->regs, 0, sizeof(s->regs));
> +
> +	/* Open the registers file */
> +	ret = chdir(s->dev_path);
> +	if (ret == -1) {
> +		perror("chdir");
> +		return ret;
> +	}
> +
> +	regs_ptr = fopen("registers", "r");
> +	if (!regs_ptr) {
> +		perror("Could not open 'registers' file");
> +		return -1;
> +	}
> +
> +	if (s->guid != GUID) {
> +		fprintf(stderr, "Unrecognized guid, 0x%x\n", s->guid);
> +		fclose(regs_ptr);
> +		return -1;
> +	}
> +
> +	/* Update register info for this guid */
> +	ret = fread(&s->regs, sizeof(uint8_t), sizeof(s->regs), regs_ptr);
> +	if (ret != sizeof(s->regs)) {
> +		fprintf(stderr, "Could not read 'registers' file\n");
> +		fclose(regs_ptr);
> +		return -1;
> +	}
> +
> +	fclose(regs_ptr);
> +
> +	return 0;
> +}
> +
> +static int sdsi_read_reg(struct sdsi_dev *s)
> +{
> +	int ret;
> +
> +	ret = sdsi_update_registers(s);
> +	if (ret)
> +		return ret;
> +
> +	/* Print register info for this guid */
> +	printf("\n");
> +	printf("Socket information for device %s\n", s->dev_name);
> +	printf("\n");
> +	printf("PPIN:                           0x%lx\n", s->regs.ppin);
> +	printf("Enabled Features\n");
> +	printf("    SDSi:                       %s\n", !!s->regs.en_features.sdsi ? "Enabled" : "Disabled");
> +	printf("Authorization Failure Count\n");
> +	printf("    AKC Failure Count:          %d\n", s->regs.auth_fail_count.key_failure_count);
> +	printf("    AKC Failure Threshold:      %d\n", s->regs.auth_fail_count.key_failure_threshold);
> +	printf("    CAP Failure Count:          %d\n", s->regs.auth_fail_count.auth_failure_count);
> +	printf("    CAP Failure Threshold:      %d\n", s->regs.auth_fail_count.auth_failure_threshold);
> +	printf("Provisioning Availability\n");
> +	printf("    Updates Available:          %d\n", s->regs.prov_avail.available);
> +	printf("    Updates Threshold:          %d\n", s->regs.prov_avail.threshold);
> +	printf("Socket ID:                      %ld\n", s->regs.socket_id & 0xF);
> +
> +	return 0;
> +}
> +
> +static int sdsi_certificate_dump(struct sdsi_dev *s)
> +{
> +	uint64_t state_certificate[512] = {0};
> +	bool first_instance;
> +	uint64_t previous;
> +	FILE *cert_ptr;
> +	int i, ret, size;
> +
> +	ret = sdsi_update_registers(s);
> +	if (ret)
> +		return ret;
> +
> +	if (!s->regs.en_features.sdsi) {
> +		fprintf(stderr, "SDSi feature is present but not enabled.");
> +		fprintf(stderr, " Unable to read state certificate");
> +		return -1;
> +	}
> +
> +	ret = chdir(s->dev_path);
> +	if (ret == -1) {
> +		perror("chdir");
> +		return ret;
> +	}
> +
> +	cert_ptr = fopen("state_certificate", "r");
> +	if (!cert_ptr) {
> +		perror("Could not open 'state_certificate' file");
> +		return -1;
> +	}
> +
> +	size = fread(state_certificate, 1, sizeof(state_certificate), cert_ptr);
> +	if (!size) {
> +		fprintf(stderr, "Could not read 'state_certificate' file\n");
> +		fclose(cert_ptr);
> +		return -1;
> +	}
> +
> +	printf("%3d: 0x%lx\n", 0, state_certificate[0]);
> +	previous = state_certificate[0];
> +	first_instance = true;
> +
> +	for (i = 1; i < (int)(round_up(size, sizeof(uint64_t))/sizeof(uint64_t)); i++) {
> +		if (state_certificate[i] == previous) {
> +			if (first_instance) {
> +				puts("*");
> +				first_instance = false;
> +			}
> +			continue;
> +		}
> +		printf("%3d: 0x%lx\n", i, state_certificate[i]);
> +		previous = state_certificate[i];
> +		first_instance = true;
> +	}
> +	printf("%3d\n", i);
> +
> +	fclose(cert_ptr);
> +
> +	return 0;
> +}
> +
> +static int sdsi_provision(struct sdsi_dev *s, char *bin_file, enum command command)
> +{
> +	int bin_fd, prov_fd, size, ret;
> +	char buf[4096] = { 0 };
> +	char cap[] = "provision_cap";
> +	char akc[] = "provision_akc";
> +	char *prov_file;
> +
> +	if (!bin_file) {
> +		fprintf(stderr, "No binary file provided\n");
> +		return -1;
> +	}
> +
> +	/* Open the binary */
> +	bin_fd = open(bin_file, O_RDONLY);
> +	if (bin_fd == -1) {
> +		fprintf(stderr, "Could not open file %s: %s\n", bin_file, strerror(errno));
> +		return bin_fd;
> +	}
> +
> +	prov_file = (command == CMD_PROV_AKC) ? akc : cap;
> +
> +	ret = chdir(s->dev_path);
> +	if (ret == -1) {
> +		perror("chdir");
> +		close(bin_fd);
> +		return ret;
> +	}
> +
> +	/* Open the provision file */
> +	prov_fd = open(prov_file, O_WRONLY);
> +	if (prov_fd == -1) {
> +		fprintf(stderr, "Could not open file %s: %s\n", prov_file, strerror(errno));
> +		close(bin_fd);
> +		return prov_fd;
> +	}
> +
> +	/* Read the binary file into the buffer */
> +	size = read(bin_fd, buf, 4096);
> +	if (size == -1) {
> +		close(bin_fd);
> +		close(prov_fd);
> +		return -1;
> +	}
> +
> +	ret = write(prov_fd, buf, size);
> +	if (ret == -1) {
> +		close(bin_fd);
> +		close(prov_fd);
> +		perror("Provisioning failed");
> +		return ret;
> +	}
> +
> +	printf("Provisioned %s file %s successfully\n", prov_file, bin_file);
> +
> +	close(bin_fd);
> +	close(prov_fd);
> +
> +	return 0;
> +}
> +
> +static int sdsi_provision_akc(struct sdsi_dev *s, char *bin_file)
> +{
> +	int ret;
> +
> +	ret = sdsi_update_registers(s);
> +	if (ret)
> +		return ret;
> +
> +	if (!s->regs.en_features.sdsi) {
> +		fprintf(stderr, "SDSi feature is present but not enabled. Unable to provision");
> +		return -1;
> +	}
> +
> +	if (!s->regs.prov_avail.available) {
> +		fprintf(stderr, "Maximum number of updates (%d) has been reached.\n",
> +			s->regs.prov_avail.threshold);
> +		return -1;
> +	}
> +
> +	if (s->regs.auth_fail_count.key_failure_count ==
> +	    s->regs.auth_fail_count.key_failure_threshold) {
> +		fprintf(stderr, "Maximum number of AKC provision failures (%d) has been reached.\n",
> +			s->regs.auth_fail_count.key_failure_threshold);
> +		fprintf(stderr, "Power cycle the system to reset the counter\n");
> +		return -1;
> +	}
> +
> +	return sdsi_provision(s, bin_file, CMD_PROV_AKC);
> +}
> +
> +static int sdsi_provision_cap(struct sdsi_dev *s, char *bin_file)
> +{
> +	int ret;
> +
> +	ret = sdsi_update_registers(s);
> +	if (ret)
> +		return ret;
> +
> +	if (!s->regs.en_features.sdsi) {
> +		fprintf(stderr, "SDSi feature is present but not enabled. Unable to provision");
> +		return -1;
> +	}
> +
> +	if (!s->regs.prov_avail.available) {
> +		fprintf(stderr, "Maximum number of updates (%d) has been reached.\n",
> +			s->regs.prov_avail.threshold);
> +		return -1;
> +	}
> +
> +	if (s->regs.auth_fail_count.auth_failure_count ==
> +	    s->regs.auth_fail_count.auth_failure_threshold) {
> +		fprintf(stderr, "Maximum number of CAP provision failures (%d) has been reached.\n",
> +			s->regs.auth_fail_count.auth_failure_threshold);
> +		fprintf(stderr, "Power cycle the system to reset the counter\n");
> +		return -1;
> +	}
> +
> +	return sdsi_provision(s, bin_file, CMD_PROV_CAP);
> +}
> +
> +static int read_sysfs_data(const char *file, int *value)
> +{
> +	char buff[16];
> +	FILE *fp;
> +
> +	fp = fopen(file, "r");
> +	if (!fp) {
> +		perror(file);
> +		return -1;
> +	}
> +
> +	if (!fgets(buff, 16, fp)) {
> +		fprintf(stderr, "Failed to read file '%s'", file);
> +		fclose(fp);
> +		return -1;
> +	}
> +
> +	fclose(fp);
> +	*value = strtol(buff, NULL, 0);
> +
> +	return 0;
> +}
> +
> +static struct sdsi_dev *sdsi_create_dev(char *dev_no)
> +{
> +	int dev_name_len = sizeof(SDSI_DEV) + strlen(dev_no) + 1;
> +	struct sdsi_dev *s;
> +	int guid;
> +	DIR *dir;
> +
> +	s = (struct sdsi_dev *)malloc(sizeof(*s));
> +	if (!s) {
> +		perror("malloc");
> +		return NULL;
> +	}
> +
> +	s->dev_name = (char *)malloc(sizeof(SDSI_DEV) + strlen(dev_no) + 1);
> +	if (!s->dev_name) {
> +		perror("malloc");
> +		free(s);
> +		return NULL;
> +	}
> +
> +	snprintf(s->dev_name, dev_name_len, "%s.%s", SDSI_DEV, dev_no);
> +
> +	s->dev_path = (char *)malloc(sizeof(AUX_DEV_PATH) + dev_name_len);
> +	if (!s->dev_path) {
> +		perror("malloc");
> +		free(s->dev_name);
> +		free(s);
> +		return NULL;
> +	}
> +
> +	snprintf(s->dev_path, sizeof(AUX_DEV_PATH) + dev_name_len, "%s%s", AUX_DEV_PATH,
> +		 s->dev_name);
> +	dir = opendir(s->dev_path);
> +	if (!dir) {
> +		fprintf(stderr, "Could not open directory '%s': %s\n", s->dev_path,
> +			strerror(errno));
> +		free(s->dev_path);
> +		free(s->dev_name);
> +		free(s);
> +		return NULL;
> +	}
> +
> +	if (chdir(s->dev_path) == -1) {
> +		perror("chdir");
> +		free(s->dev_path);
> +		free(s->dev_name);
> +		free(s);
> +		return NULL;
> +	}
> +
> +	if (read_sysfs_data("guid", &guid)) {
> +		free(s->dev_path);
> +		free(s->dev_name);
> +		free(s);
> +		return NULL;
> +	}
> +
> +	s->guid = guid;
> +
> +	return s;
> +}
> +
> +static void sdsi_free_dev(struct sdsi_dev *s)
> +{
> +	free(s->dev_path);
> +	free(s->dev_name);
> +	free(s);
> +}
> +
> +static void print_help(char *prog)
> +{
> +	printf("Usage: %s [-l] [-d dev_no [-is] [-a file] [-c file]]\n", prog);
> +
> +	printf("\n");
> +	printf("Commands:\n");
> +	printf("  %-13s\t%s\n", "-l", "list available sdsi devices");
> +	printf("  %-13s\t%s\n", "-d <dev_no>", "sdsi device number");
> +	printf("  %-13s\t%s\n", "-i", "show socket information");
> +	printf("  %-13s\t%s\n", "-s", "dump state certificate data");
> +	printf("  %-13s\t%s\n", "-a <file>", "provision socket with AKC file");
> +	printf("  %-13s\t%s\n", "-c <file>", "provision socket with CAP file");
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +	char bin_file[PATH_MAX], *dev_no = NULL;
> +	enum command command = CMD_NONE;
> +	struct sdsi_dev *s;
> +	int ret = 0, opt;
> +
> +	while ((opt = getopt(argc, argv, "d:lisa:c:h")) != -1) {
> +		switch (opt) {
> +		case 'd':
> +			dev_no = optarg;
> +			break;
> +		case 'l':
> +			command = CMD_LIST_DEVICES;
> +			break;
> +		case 'i':
> +			command = CMD_SOCKET_INFO;
> +			break;
> +		case 's':
> +			command = CMD_DUMP_CERT;
> +			break;
> +		case 'a':
> +		case 'c':
> +			if (!access(optarg, F_OK) == 0) {
> +				fprintf(stderr, "Could not open file '%s': %s\n", optarg,
> +					strerror(errno));
> +				return -1;
> +			}
> +
> +			if (!realpath(optarg, bin_file)) {
> +				perror("realpath");
> +				return -1;
> +			}
> +
> +			command = (opt == 'a') ? CMD_PROV_AKC : CMD_PROV_CAP;
> +			break;
> +		case 'h':
> +		default:
> +			print_help(argv[0]);
> +			return 0;
> +		}
> +	}
> +
> +	if (!dev_no && command != CMD_LIST_DEVICES) {
> +		print_help(argv[0]);
> +		return -1;
> +	}
> +
> +	if (dev_no) {
> +		s = sdsi_create_dev(dev_no);
> +		if (!s)
> +			return -1;
> +	}
> +
> +	/* Run the command */
> +	switch (command) {
> +	case CMD_NONE:
> +		fprintf(stderr, "need to specify a command\n");
> +		print_help(argv[0]);
> +		ret = -1;
> +		break;
> +	case CMD_LIST_DEVICES:
> +		sdsi_list_devices();
> +		break;
> +	case CMD_SOCKET_INFO:
> +		ret = sdsi_read_reg(s);
> +		break;
> +	case CMD_DUMP_CERT:
> +		ret = sdsi_certificate_dump(s);
> +		break;
> +	case CMD_PROV_AKC:
> +		ret = sdsi_provision_akc(s, bin_file);
> +		break;
> +	case CMD_PROV_CAP:
> +		ret = sdsi_provision_cap(s, bin_file);
> +		break;
> +	}
> +
> +
> +	if (dev_no)
> +		sdsi_free_dev(s);
> +
> +	return ret;
> +}
> 
> base-commit: 42f8bcb37e2c65931799cdf61d086ed78456e798


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH V8 2/2] selftests: sdsi: test sysfs setup
  2022-02-17 23:09 ` [PATCH V8 2/2] selftests: sdsi: test sysfs setup David E. Box
@ 2022-02-22 14:56   ` Hans de Goede
  0 siblings, 0 replies; 4+ messages in thread
From: Hans de Goede @ 2022-02-22 14:56 UTC (permalink / raw)
  To: David E. Box, gregkh, andriy.shevchenko, srinivas.pandruvada, mgross
  Cc: linux-kernel, platform-driver-x86

Hi David,

On 2/18/22 00:09, David E. Box wrote:
> Tests file configuration and error handling of the Intel Software
> Defined Silicon sysfs ABI.
> 
> Signed-off-by: David E. Box <david.e.box@linux.intel.com>
> ---
> Applied on review-hans branch.
> 
> V8
>   - Skip if python3 or pytest aren't installed
>   - Do not remove driver after test is run
> V7
>   - No changes.
> V6
>   - No changes.
> V5
>   - No changes.
> V4
>   - No changes.
> V3
>   - Add tests to check PCI device removal handling and to check for
>     driver memory leaks.
> V2
>   - New patch.
> 
>  MAINTAINERS                                   |   1 +
>  tools/testing/selftests/drivers/sdsi/sdsi.sh  |  26 ++
>  .../selftests/drivers/sdsi/sdsi_test.py       | 226 ++++++++++++++++++
>  3 files changed, 253 insertions(+)
>  create mode 100755 tools/testing/selftests/drivers/sdsi/sdsi.sh
>  create mode 100644 tools/testing/selftests/drivers/sdsi/sdsi_test.py
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index dc3c9f271463..be2c4c63e58f 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -9872,6 +9872,7 @@ M:	David E. Box <david.e.box@linux.intel.com>
>  S:	Supported
>  F:	drivers/platform/x86/intel/sdsi.c
>  F:	tools/arch/x86/intel_sdsi/
> +F:	tools/testing/selftests/drivers/sdsi/
>  
>  INTEL SKYLAKE INT3472 ACPI DEVICE DRIVER
>  M:	Daniel Scally <djrscally@gmail.com>
> diff --git a/tools/testing/selftests/drivers/sdsi/sdsi.sh b/tools/testing/selftests/drivers/sdsi/sdsi.sh
> new file mode 100755
> index 000000000000..b938b1d46b04
> --- /dev/null
> +++ b/tools/testing/selftests/drivers/sdsi/sdsi.sh
> @@ -0,0 +1,26 @@
> +#!/bin/sh
> +# SPDX-License-Identifier: GPL-2.0
> +# Runs tests for the intel_sdsi driver
> +
> +if ! command -v python3 > /dev/null 2>&1; then
> +	echo "drivers/sdsi: [SKIP] python3 not installed"
> +	exit 77
> +fi
> +
> +if ! python -c "import pytest" > /dev/null 2>&1; then
> +	echo "drivers/sdsi: [SKIP] pytest module not installed"
> +	exit 77
> +fi
> +
> +if ! /sbin/modprobe -q -r intel_sdsi; then
> +	echo "drivers/sdsi: [SKIP]"
> +	exit 77
> +fi
> +
> +if /sbin/modprobe -q intel_sdsi; then
> +	python3 -m pytest sdsi_test.py
> +	echo "drivers/sdsi: [OK]"

You will now echo "[OK]" and exit with 0 independent of
the results of the pytest invocation, please change this to:

if /sbin/modprobe -q intel_sdsi && python3 -m pytest sdsi_test.py; then
	echo "drivers/sdsi: [OK]"
else
	echo "drivers/sdsi: [FAIL]"
	exit 1
fi

So that errors of the pytest will result in the test failing.

Regards,

Hans



> +else
> +	echo "drivers/sdsi: [FAIL]"
> +	exit 1
> +fi
> diff --git a/tools/testing/selftests/drivers/sdsi/sdsi_test.py b/tools/testing/selftests/drivers/sdsi/sdsi_test.py
> new file mode 100644
> index 000000000000..4922edfe461f
> --- /dev/null
> +++ b/tools/testing/selftests/drivers/sdsi/sdsi_test.py
> @@ -0,0 +1,226 @@
> +#!/usr/bin/env python3
> +# SPDX-License-Identifier: GPL-2.0
> +
> +from struct import pack
> +from time import sleep
> +
> +import errno
> +import glob
> +import os
> +import subprocess
> +
> +try:
> +    import pytest
> +except ImportError:
> +    print("Unable to import pytest python module.")
> +    print("\nIf not already installed, you may do so with:")
> +    print("\t\tpip3 install pytest")
> +    exit(1)
> +
> +SOCKETS = glob.glob('/sys/bus/auxiliary/devices/intel_vsec.sdsi.*')
> +NUM_SOCKETS = len(SOCKETS)
> +
> +MODULE_NAME = 'sdsi'
> +DEV_PREFIX = 'intel_vsec.sdsi'
> +CLASS_DIR = '/sys/bus/auxiliary/devices'
> +GUID = "0x6dd191"
> +
> +def read_bin_file(file):
> +    with open(file, mode='rb') as f:
> +        content = f.read()
> +    return content
> +
> +def get_dev_file_path(socket, file):
> +    return CLASS_DIR + '/' + DEV_PREFIX + '.' + str(socket) + '/' + file
> +
> +def kmemleak_enabled():
> +    kmemleak = "/sys/kernel/debug/kmemleak"
> +    return os.path.isfile(kmemleak)
> +
> +class TestSDSiDriver:
> +    def test_driver_loaded(self):
> +        lsmod_p = subprocess.Popen(('lsmod'), stdout=subprocess.PIPE)
> +        result = subprocess.check_output(('grep', '-q', MODULE_NAME), stdin=lsmod_p.stdout)
> +
> +@pytest.mark.parametrize('socket', range(0, NUM_SOCKETS))
> +class TestSDSiFilesClass:
> +
> +    def read_value(self, file):
> +        f = open(file, "r")
> +        value = f.read().strip("\n")
> +        return value
> +
> +    def get_dev_folder(self, socket):
> +        return CLASS_DIR + '/' + DEV_PREFIX + '.' + str(socket) + '/'
> +
> +    def test_sysfs_files_exist(self, socket):
> +        folder = self.get_dev_folder(socket)
> +        print (folder)
> +        assert os.path.isfile(folder + "guid") == True
> +        assert os.path.isfile(folder + "provision_akc") == True
> +        assert os.path.isfile(folder + "provision_cap") == True
> +        assert os.path.isfile(folder + "state_certificate") == True
> +        assert os.path.isfile(folder + "registers") == True
> +
> +    def test_sysfs_file_permissions(self, socket):
> +        folder = self.get_dev_folder(socket)
> +        mode = os.stat(folder + "guid").st_mode & 0o777
> +        assert mode == 0o444    # Read all
> +        mode = os.stat(folder + "registers").st_mode & 0o777
> +        assert mode == 0o400    # Read owner
> +        mode = os.stat(folder + "provision_akc").st_mode & 0o777
> +        assert mode == 0o200    # Read owner
> +        mode = os.stat(folder + "provision_cap").st_mode & 0o777
> +        assert mode == 0o200    # Read owner
> +        mode = os.stat(folder + "state_certificate").st_mode & 0o777
> +        assert mode == 0o400    # Read owner
> +
> +    def test_sysfs_file_ownership(self, socket):
> +        folder = self.get_dev_folder(socket)
> +
> +        st = os.stat(folder + "guid")
> +        assert st.st_uid == 0
> +        assert st.st_gid == 0
> +
> +        st = os.stat(folder + "registers")
> +        assert st.st_uid == 0
> +        assert st.st_gid == 0
> +
> +        st = os.stat(folder + "provision_akc")
> +        assert st.st_uid == 0
> +        assert st.st_gid == 0
> +
> +        st = os.stat(folder + "provision_cap")
> +        assert st.st_uid == 0
> +        assert st.st_gid == 0
> +
> +        st = os.stat(folder + "state_certificate")
> +        assert st.st_uid == 0
> +        assert st.st_gid == 0
> +
> +    def test_sysfs_file_sizes(self, socket):
> +        folder = self.get_dev_folder(socket)
> +
> +        if self.read_value(folder + "guid") == GUID:
> +            st = os.stat(folder + "registers")
> +            assert st.st_size == 72
> +
> +        st = os.stat(folder + "provision_akc")
> +        assert st.st_size == 1024
> +
> +        st = os.stat(folder + "provision_cap")
> +        assert st.st_size == 1024
> +
> +        st = os.stat(folder + "state_certificate")
> +        assert st.st_size == 4096
> +
> +    def test_no_seek_allowed(self, socket):
> +        folder = self.get_dev_folder(socket)
> +        rand_file = bytes(os.urandom(8))
> +
> +        f = open(folder + "provision_cap", "wb", 0)
> +        f.seek(1)
> +        with pytest.raises(OSError) as error:
> +            f.write(rand_file)
> +        assert error.value.errno == errno.ESPIPE
> +        f.close()
> +
> +        f = open(folder + "provision_akc", "wb", 0)
> +        f.seek(1)
> +        with pytest.raises(OSError) as error:
> +            f.write(rand_file)
> +        assert error.value.errno == errno.ESPIPE
> +        f.close()
> +
> +    def test_registers_seek(self, socket):
> +        folder = self.get_dev_folder(socket)
> +
> +        # Check that the value read from an offset of the entire
> +        # file is none-zero and the same as the value read
> +        # from seeking to the same location
> +        f = open(folder + "registers", "rb")
> +        data = f.read()
> +        f.seek(64)
> +        id = f.read()
> +        assert id != bytes(0)
> +        assert data[64:] == id
> +        f.close()
> +
> +@pytest.mark.parametrize('socket', range(0, NUM_SOCKETS))
> +class TestSDSiMailboxCmdsClass:
> +    def test_provision_akc_eoverflow_1017_bytes(self, socket):
> +
> +        # The buffer for writes is 1k, of with 8 bytes must be
> +        # reserved for the command, leaving 1016 bytes max.
> +        # Check that we get an overflow error for 1017 bytes.
> +        node = get_dev_file_path(socket, "provision_akc")
> +        rand_file = bytes(os.urandom(1017))
> +
> +        f = open(node, 'wb', 0)
> +        with pytest.raises(OSError) as error:
> +            f.write(rand_file)
> +        assert error.value.errno == errno.EOVERFLOW
> +        f.close()
> +
> +@pytest.mark.parametrize('socket', range(0, NUM_SOCKETS))
> +class TestSdsiDriverLocksClass:
> +    def test_enodev_when_pci_device_removed(self, socket):
> +        node = get_dev_file_path(socket, "provision_akc")
> +        dev_name = DEV_PREFIX + '.' + str(socket)
> +        driver_dir = CLASS_DIR + '/' + dev_name + "/driver/"
> +        rand_file = bytes(os.urandom(8))
> +
> +        f = open(node, 'wb', 0)
> +        g = open(node, 'wb', 0)
> +
> +        with open(driver_dir + 'unbind', 'w') as k:
> +            print(dev_name, file = k)
> +
> +        with pytest.raises(OSError) as error:
> +            f.write(rand_file)
> +        assert error.value.errno == errno.ENODEV
> +
> +        with pytest.raises(OSError) as error:
> +            g.write(rand_file)
> +        assert error.value.errno == errno.ENODEV
> +
> +        f.close()
> +        g.close()
> +
> +        # Short wait needed to allow file to close before pulling driver
> +        sleep(1)
> +
> +        p = subprocess.Popen(('modprobe', '-r', 'intel_sdsi'))
> +        p.wait()
> +        p = subprocess.Popen(('modprobe', '-r', 'intel_vsec'))
> +        p.wait()
> +        p = subprocess.Popen(('modprobe', 'intel_vsec'))
> +        p.wait()
> +
> +        # Short wait needed to allow driver time to get inserted
> +        # before continuing tests
> +        sleep(1)
> +
> +    def test_memory_leak(self, socket):
> +        if not kmemleak_enabled:
> +            pytest.skip("kmemleak not enabled in kernel")
> +
> +        dev_name = DEV_PREFIX + '.' + str(socket)
> +        driver_dir = CLASS_DIR + '/' + dev_name + "/driver/"
> +
> +        with open(driver_dir + 'unbind', 'w') as k:
> +            print(dev_name, file = k)
> +
> +        sleep(1)
> +
> +        subprocess.check_output(('modprobe', '-r', 'intel_sdsi'))
> +        subprocess.check_output(('modprobe', '-r', 'intel_vsec'))
> +
> +        with open('/sys/kernel/debug/kmemleak', 'w') as f:
> +            print('scan', file = f)
> +        sleep(5)
> +
> +        assert os.stat('/sys/kernel/debug/kmemleak').st_size == 0
> +
> +        subprocess.check_output(('modprobe', 'intel_vsec'))
> +        sleep(1)


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2022-02-22 14:57 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-17 23:09 [PATCH V8 1/2] tools arch x86: Add Intel SDSi provisiong tool David E. Box
2022-02-17 23:09 ` [PATCH V8 2/2] selftests: sdsi: test sysfs setup David E. Box
2022-02-22 14:56   ` Hans de Goede
2022-02-22 14:55 ` [PATCH V8 1/2] tools arch x86: Add Intel SDSi provisiong tool Hans de Goede

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.