[Skiboot] [RFC 2/2] external/opal-occ: Add usersapce wrapper for OPAL-OCC cmd/rsp interface
Cyril Bur
cyrilbur at gmail.com
Tue Jun 6 15:50:54 AEST 2017
On Mon, 2017-06-05 at 10:10 +0530, Shilpasri G Bhat wrote:
> This patch adds userspace application to issue OCC commands. The
> opal-occ utility is included within the opal-prd utility.
>
Hi Shilpasri,
Overall, looks good - just one thing really, see below.
Cyril
> Signed-off-by: Shilpasri G Bhat <shilpa.bhat at linux.vnet.ibm.com>
> ---
> external/opal-prd/Makefile | 8 +-
> external/opal-prd/opal-occ.c | 290 +++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 296 insertions(+), 2 deletions(-)
> create mode 100644 external/opal-prd/opal-occ.c
>
> 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..72460d2
> --- /dev/null
> +++ b/external/opal-prd/opal-occ.c
> @@ -0,0 +1,290 @@
> +/* 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
> + },
> +};
> +
> +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);
> +}
> +
> +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'},
> + {"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: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 '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;
> + default:
> + return EXIT_FAILURE;
> + }
> +
> + rc = write(fd, cmd, sizeof(*cmd) + cmd->size);
> + if (rc <= 0) {
Probably worth erroring out if rc != sizeof(*cmd) + cmd->size - feels
like you're guaranteed to get an error back from OCC if you couldn't
write the entire command.
Might also be worth printing errno.
> + printf("Write failed with %d\n", rc);
> + 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) {
Same as with the call to write()
> + printf("Read failed %d\n", rc);
> + goto out;
> + }
> +
> + switch (rsp->status) {
> + case OCC_SUCCESS:
> + printf("OCC: Success\n");
> + break;
> + case OCC_INVALID_COMMAND:
> + printf("OCC: Invalid Command\n");
> + break;
> + case OCC_INVALID_CMD_DATA_LENGTH:
> + printf("OCC: Invalid Command Data length\n");
> + break;
> + case OCC_INVALID_DATA:
> + printf("OCC: Invalid Data\n");
> + break;
> + case OCC_INTERNAL_ERROR:
> + printf("OCC: Internal OCC error\n");
> + break;
> + default:
> + printf("OCC: Unrecognized response status\n");
> + }
> +
> + if (rsp->status)
> + goto out;
Rather than break; in the switch statement, you could have goto out;
which removes the need for this statement.
> +
> + 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;
> + default:
> + break;
> + }
> +
> +out:
> + free(rsp);
> +free_cdata:
> + free(cmd);
> +close_fd:
> + close(fd);
> + return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
> +}
More information about the Skiboot
mailing list