@@ -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
new file mode 100644
@@ -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;
+}
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> --- - No changes from V2 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