diff mbox

[V2,2/2] external/opal-occ: Add usersapce wrapper for OPAL-OCC cmd/rsp interface

Message ID 1497351879-18894-3-git-send-email-shilpa.bhat@linux.vnet.ibm.com
State Superseded
Headers show

Commit Message

Shilpasri G Bhat June 13, 2017, 11:04 a.m. UTC
This patch adds userspace application to issue OCC commands. The
opal-occ utility is included within the opal-prd utility.

Signed-off-by: Shilpasri G Bhat <shilpa.bhat@linux.vnet.ibm.com>
---
Changes from V1:
- Handle size mismatch error paths for read() and write()
- Add OCC command to select sensor groups

 external/opal-prd/Makefile   |   8 +-
 external/opal-prd/opal-occ.c | 320 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 326 insertions(+), 2 deletions(-)
 create mode 100644 external/opal-prd/opal-occ.c
diff mbox

Patch

diff --git a/external/opal-prd/Makefile b/external/opal-prd/Makefile
index ccd3401..6389656 100644
--- a/external/opal-prd/Makefile
+++ b/external/opal-prd/Makefile
@@ -8,7 +8,7 @@  sbindir = $(prefix)/sbin
 datadir = $(prefix)/share
 mandir = $(datadir)/man
 
-all: links arch_links | opal-prd
+all: links arch_links | opal-prd opal-occ
 
 GET_ARCH = ../../external/common/get_arch.sh
 include ../../external/common/rules.mk
@@ -43,6 +43,9 @@  $(LIBFLASH_OBJS): libflash-%.o : libflash/%.c
 opal-prd: $(OBJS)
 	$(Q_LINK)$(LINK.o) -o $@ $^
 
+opal-occ: opal-occ.o
+	$(Q_LINK)$(LINK.o) -o $@ $^
+
 version.c: ../../make_version.sh .version
 	@(if [ "a$(OPAL_PRD_VERSION)" = "a" ]; then \
 	echo "#error You need to set OPAL_PRD_VERSION environment variable" > $@ ;\
@@ -63,10 +66,11 @@  test/test_pnor: test/test_pnor.o pnor.o $(LIBFLASH_OBJS) common-arch_flash.o
 
 install: all
 	install -D opal-prd $(DESTDIR)$(sbindir)/opal-prd
+	install -D opal-occ $(DESTDIR)$(sbindir)/opal-occ
 	install -D -m 0644 opal-prd.8 $(DESTDIR)$(mandir)/man8/opal-prd.8
 
 clean:
-	$(RM) *.[odsa] opal-prd
+	$(RM) *.[odsa] opal-prd opal-occ
 	$(RM) test/*.[odsa] test/test_pnor
 
 distclean: clean
diff --git a/external/opal-prd/opal-occ.c b/external/opal-prd/opal-occ.c
new file mode 100644
index 0000000..0cc1a18
--- /dev/null
+++ b/external/opal-prd/opal-occ.c
@@ -0,0 +1,320 @@ 
+/* Copyright 2017 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * imitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <opal-api.h>
+
+struct command_info {
+	enum occ_cmd cmd;
+	enum occ_cmd_data_length csize;
+	enum occ_rsp_data_length rsize;
+};
+
+static struct command_info cmds[] = {
+	{	OCC_CMD_AMESTER_PASS_THRU,
+		OCC_CMD_DL_AMESTER_PASS_THRU,
+		OCC_RSP_DL_AMESTER_PASS_THRU
+	},
+	{	OCC_CMD_CLEAR_SENSOR_DATA,
+		OCC_CMD_DL_CLEAR_SENSOR_DATA,
+		OCC_RSP_DL_CLEAR_SENSOR_DATA
+	},
+	{	OCC_CMD_SET_POWER_CAP,
+		OCC_CMD_DL_SET_POWER_CAP,
+		OCC_RSP_DL_SET_POWER_CAP
+	},
+	{	OCC_CMD_SET_POWER_SHIFTING_RATIO,
+		OCC_CMD_DL_SET_POWER_SHIFTING_RATIO,
+		OCC_RSP_DL_SET_POWER_SHIFTING_RATIO
+	},
+	{	OCC_CMD_SELECT_SENSOR_GROUPS,
+		OCC_CMD_DL_SELECT_SENSOR_GROUPS,
+		OCC_RSP_DL_SELECT_SENSOR_GROUPS
+	},
+};
+
+static void usage(const char *progname)
+{
+	printf("Usage:\n");
+	printf("\t%s -c <chipid> -p|--power-cap <value>\n", progname);
+	printf("\t%s -c <chipid> -s|--power-shift <value>\n", progname);
+	printf("\t%s -c <chipid> -l|--clear-sensor-limits "
+			"<CSM|Profiler|Job-Scheduler>\n", progname);
+	printf("\t%s -c <chipid> -a|--amester-passthru "
+			"<hex bytes...>\n", progname);
+	printf("\t%s -c <chipid> -g|--select-sensor-groups <mask>\n", progname);
+}
+
+static struct option occ_diag_options[] = {
+	{"chip", required_argument, NULL, 'c'},
+	{"power-cap", required_argument, NULL, 'p'},
+	{"power-shift", required_argument, NULL, 's'},
+	{"clear-sensor-limits", required_argument, NULL, 'l'},
+	{"amester-passthru", required_argument, NULL, 'a'},
+	{"select-sensor-groups", required_argument, NULL, 'g'},
+	{"help", no_argument, NULL, 'h'},
+	{0},
+};
+
+int main(int argc, char *argv[])
+{
+	char occ_node[15];
+	struct opal_occ_cmd_data *cmd;
+	struct opal_occ_rsp_data *rsp;
+	char *str;
+	u64 cmd_data;
+	int fd, rsp_size, chip = -1, rc, i;
+	enum occ_cmd ocmd = OCC_CMD_LAST;
+
+	/* Parse options */
+	for (;;) {
+		int c;
+
+		c = getopt_long(argc, argv, "c:p:s:l:a:g:h", occ_diag_options,
+				NULL);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'c':
+			chip = atoi(optarg);
+			break;
+		case 'p':
+			ocmd = OCC_CMD_SET_POWER_CAP;
+			cmd_data = atoi(optarg);
+			break;
+		case 's':
+			ocmd = OCC_CMD_SET_POWER_SHIFTING_RATIO;
+			cmd_data = atoi(optarg);
+			break;
+		case 'l':
+			ocmd = OCC_CMD_CLEAR_SENSOR_DATA;
+			str = optarg;
+			break;
+		case 'a':
+			ocmd = OCC_CMD_AMESTER_PASS_THRU;
+			cmd_data = optind - 1;
+			break;
+		case 'g':
+			ocmd = OCC_CMD_SELECT_SENSOR_GROUPS;
+			cmd_data = atoi(optarg);
+			break;
+		case 'h':
+			usage(argv[0]);
+			return EXIT_SUCCESS;
+		case '?':
+		default:
+			usage(argv[0]);
+			return EXIT_FAILURE;
+		}
+
+		if (ocmd != OCC_CMD_LAST)
+			break;
+	}
+
+	if (ocmd == OCC_CMD_LAST) {
+		printf("Specify a command\n");
+		usage(argv[0]);
+		return EXIT_FAILURE;
+	}
+
+	if (chip == -1) {
+		printf("Requires -c --chip option to be specified\n");
+		return EXIT_FAILURE;
+	}
+
+	snprintf(occ_node, sizeof(occ_node), "/dev/occ%d", chip);
+	if (access(occ_node, 0)) {
+		printf("Failed to access node %s\n", occ_node);
+		return EXIT_FAILURE;
+	}
+
+	fd = open(occ_node, O_RDWR);
+	if (fd < 0) {
+		printf("Can't open OCC device %s: %m", occ_node);
+		return EXIT_FAILURE;
+	}
+
+	if (cmds[ocmd].csize) {
+		cmd = malloc(sizeof(struct opal_occ_cmd_data) +
+			     cmds[ocmd].csize);
+		if (!cmd) {
+			rc = -ENOMEM;
+			goto close_fd;
+		}
+		cmd->cmd = ocmd;
+		cmd->size = cmds[ocmd].csize;
+	}
+
+	switch (ocmd) {
+	case OCC_CMD_SET_POWER_CAP:
+		if (cmd_data >> cmd->size * 8) {
+			printf("Invalid power cap %d\n", (int)cmd_data);
+			rc = -EINVAL;
+			goto free_cdata;
+		}
+
+		memcpy(cmd->data, &cmd_data, cmd->size);
+		break;
+	case OCC_CMD_SET_POWER_SHIFTING_RATIO:
+		if (cmd_data >> cmd->size * 8) {
+			printf("Invalid power shift ratio %d\n", (int)cmd_data);
+			rc = -EINVAL;
+			goto free_cdata;
+		}
+
+		memcpy(cmd->data, &cmd_data, cmd->size);
+		break;
+	case OCC_CMD_CLEAR_SENSOR_DATA:
+		if (!strcmp(str, "CSM")) {
+			cmd_data = OCC_SENSOR_LIMIT_GROUP_CSM;
+		} else if (!strcmp(str, "Profiler")) {
+			cmd_data = OCC_SENSOR_LIMIT_GROUP_PROFILER;
+		} else if (!strcmp(str, "Job-Scheduler")) {
+			cmd_data = OCC_SENSOR_LIMIT_GROUP_JOB_SCHED;
+		} else {
+			printf("Invalid sensor limit group type\n");
+			rc = EXIT_FAILURE;
+			goto free_cdata;
+		}
+
+		memcpy(cmd->data, &cmd_data, cmd->size);
+		break;
+	case OCC_CMD_AMESTER_PASS_THRU:
+		if (argc - cmd_data > MAX_OPAL_CMD_DATA_LENGTH) {
+			printf("Too large data\n");
+			rc = -EINVAL;
+			goto close_fd;
+		}
+
+		cmd = malloc(sizeof(*cmd) + argc - cmd_data);
+		if (!cmd) {
+			rc = -ENOMEM;
+			goto close_fd;
+		}
+		cmd->cmd = OCC_CMD_AMESTER_PASS_THRU;
+		cmd->size = argc - cmd_data;
+		for (i = 0; i < cmd->size; i++)
+			sscanf(argv[i + cmd_data], "%hhx", &cmd->data[i]);
+		break;
+	case OCC_CMD_SELECT_SENSOR_GROUPS:
+		if (cmd_data >> cmd->size * 8) {
+			printf("Invalid mask %d\n", (int)cmd_data);
+			rc = -EINVAL;
+			goto free_cdata;
+		}
+		memcpy(cmd->data, &cmd_data, cmd->size);
+		break;
+	default:
+		return EXIT_FAILURE;
+	}
+
+	rc = write(fd, cmd, sizeof(*cmd) + cmd->size);
+	if (rc < 0) {
+		printf("Error writing OCC command %m\n");
+		goto free_cdata;
+	}
+
+	if (rc != sizeof(*cmd) + cmd->size) {
+		printf("Failed to write full command\n");
+		goto free_cdata;
+	}
+
+	if (cmds[ocmd].rsize)
+		rsp_size = sizeof(*rsp) + cmds[ocmd].rsize;
+	else
+		rsp_size = sizeof(*rsp) + MAX_OCC_RSP_DATA_LENGTH;
+
+	rsp = malloc(rsp_size);
+	if (!rsp)
+		goto free_cdata;
+
+	rc = read(fd, rsp, rsp_size);
+	if (rc < 0) {
+		printf("Error reading OCC response %m\n");
+		goto out;
+	}
+
+	if ((cmds[ocmd].rsize && rc != rsp_size) ||
+	    (!cmds[ocmd].rsize && rc < sizeof(*rsp))) {
+		printf("Failed to read full OCC response\n");
+		goto out;
+	}
+
+	switch (rsp->status) {
+	case OCC_SUCCESS:
+		printf("OCC: Success\n");
+		break;
+	case OCC_INVALID_COMMAND:
+		printf("OCC: Invalid Command\n");
+		goto out;
+	case OCC_INVALID_CMD_DATA_LENGTH:
+		printf("OCC: Invalid Command Data length\n");
+		goto out;
+	case OCC_INVALID_DATA:
+		printf("OCC: Invalid Data\n");
+		goto out;
+	case OCC_INTERNAL_ERROR:
+		printf("OCC: Internal OCC error\n");
+		goto out;
+	default:
+		printf("OCC: Unrecognized response status\n");
+		goto out;
+	}
+
+	switch (ocmd) {
+	case OCC_CMD_SET_POWER_CAP:
+		printf("Power Cap set to %dW\n", *(u16 *)rsp->data);
+		break;
+	case OCC_CMD_SET_POWER_SHIFTING_RATIO:
+		printf("Power Shifting ratio set to %d%%\n", *(u8 *)rsp->data);
+		break;
+	case OCC_CMD_CLEAR_SENSOR_DATA:
+		printf("Sensor limit cleared for %d\n", *(u8 *)rsp->data);
+		break;
+	case OCC_CMD_AMESTER_PASS_THRU:
+		printf("Amester response data size = %d\n", rsp->size);
+		for (i = 0; i < rsp->size; i++) {
+			printf("%hhx ", rsp->data[i]);
+			if (i && !(i % 8))
+				printf("\n");
+		}
+		printf("\n");
+		break;
+	case OCC_CMD_SELECT_SENSOR_GROUPS:
+		printf("Sensor group mask set to %d\n", *(u16 *)rsp->data);
+		break;
+	default:
+		break;
+	}
+
+out:
+	free(rsp);
+free_cdata:
+	free(cmd);
+close_fd:
+	close(fd);
+	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}