]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
sed: Add plugin for basic SED Opal operations
authorGreg Joyce <gjoyce@linux.ibm.com>
Fri, 19 Jan 2024 20:43:41 +0000 (14:43 -0600)
committerDaniel Wagner <wagi@monom.org>
Thu, 8 Feb 2024 09:09:01 +0000 (10:09 +0100)
A new plugin 'sed' is developed to provide basic SED Opal CLI
operations. These include:
        discover        Discover drive locking features
        intialize       Initialize a drive for SED Opal
        password        Change the authorization key
        revert          Revert drive to SED Opal disabled
        lock            Lock a SED Opal drive
        unlock          Unlock a SED Opal drive

Signed-off-by: Greg Joyce <gjoyce@linux.ibm.com>
meson.build
plugins/meson.build
plugins/sed/meson.build [new file with mode: 0644]
plugins/sed/sed.c [new file with mode: 0644]
plugins/sed/sed.h [new file with mode: 0644]
plugins/sed/sedopal_cmd.c [new file with mode: 0644]
plugins/sed/sedopal_cmd.h [new file with mode: 0644]
plugins/sed/sedopal_spec.h [new file with mode: 0644]

index 0961c263c9fd46b022b0b07ccce38838721ad408..7f6126266368abbfba4042e34567116c4956ab31 100644 (file)
@@ -153,6 +153,29 @@ conf.set10(
     cc.get_id() == 'clang',
     description: 'Is compiler warning about unused static line function?'
 )
+conf.set10(
+    'HAVE_SED_OPAL',
+    cc.compiles(
+        '''#include <linux/sed-opal.h>''',
+        name: 'linux/sed-opal.h'
+
+    ),
+    description: 'Is linux/sed-opa.h include-able?'
+)
+conf.set10(
+    'HAVE_KEY_TYPE',
+    cc.compiles(
+        '''
+          #include <linux/sed-opal.h>
+          int main(void) {
+                struct opal_key key;
+                key.key_type = OPAL_INCLUDED;
+           }
+        ''',
+        name: 'key_type'
+    ),
+    description: 'Does struct opal_key have a key_type field?'
+)
 
 if cc.has_function_attribute('fallthrough')
   conf.set('fallthrough', '__attribute__((__fallthrough__))')
index 38b7cdedc53e388bd3630a24dffd9a2270669a04..bb4c9ad7a1ead3d547aa3abafa29d423f614fad0 100644 (file)
@@ -29,4 +29,7 @@ if json_c_dep.found()
   ]
   subdir('solidigm')
   subdir('ocp')
+  if conf.has('HAVE_SED_OPAL')
+    subdir('sed')
+  endif
 endif
diff --git a/plugins/sed/meson.build b/plugins/sed/meson.build
new file mode 100644 (file)
index 0000000..4a2e544
--- /dev/null
@@ -0,0 +1,4 @@
+sources += [
+  'plugins/sed/sed.c',
+  'plugins/sed/sedopal_cmd.c',
+]
diff --git a/plugins/sed/sed.c b/plugins/sed/sed.c
new file mode 100644 (file)
index 0000000..0471e54
--- /dev/null
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <linux/fs.h>
+#include <sys/stat.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "nvme-print.h"
+#include "sedopal_cmd.h"
+#include <linux/sed-opal.h>
+
+#define CREATE_CMD
+#include "sed.h"
+
+OPT_ARGS(no_opts) = {
+       OPT_END()
+};
+
+OPT_ARGS(key_opts) = {
+       OPT_FLAG("ask-key", 'k', &sedopal_ask_key,
+                       "prompt for SED authentication key"),
+       OPT_END()
+};
+
+OPT_ARGS(revert_opts) = {
+       OPT_FLAG("destructive", 'e', &sedopal_destructive_revert,
+                       "destructive revert"),
+       OPT_FLAG("psid", 'p', &sedopal_psid_revert, "PSID revert"),
+       OPT_END()
+};
+
+
+/*
+ * Open the NVMe device specified on the command line. It must be the
+ * NVMe block device (e.g. /dev/nvme0n1).
+ */
+static int sed_opal_open_device(struct nvme_dev **dev, int argc, char **argv,
+               const char *desc, struct argconfig_commandline_options *opts)
+{
+       int err;
+
+       err = parse_and_open(dev, argc, argv, desc, opts);
+       if (err)
+               return err;
+
+       if (!S_ISBLK((*dev)->direct.stat.st_mode)) {
+               fprintf(stderr,
+                       "ERROR : The NVMe block device must be specified\n");
+               err = -EINVAL;
+               dev_close(*dev);
+       }
+
+       return err;
+}
+
+static int sed_opal_discover(int argc, char **argv, struct command *cmd,
+               struct plugin *plugin)
+{
+       int err;
+       const char *desc = "Query SED device and display locking features";
+       struct nvme_dev *dev;
+
+       err = sed_opal_open_device(&dev, argc, argv, desc, no_opts);
+       if (err)
+               return err;
+
+       err = sedopal_cmd_discover(dev->direct.fd);
+
+       dev_close(dev);
+       return err;
+}
+
+static int sed_opal_initialize(int argc, char **argv, struct command *cmd,
+               struct plugin *plugin)
+{
+       int err;
+       const char *desc = "Initialize a SED device for locking";
+       struct nvme_dev *dev;
+
+       err = sed_opal_open_device(&dev, argc, argv, desc, no_opts);
+       if (err)
+               return err;
+
+       err = sedopal_cmd_initialize(dev->direct.fd);
+       if (err != 0)
+               fprintf(stderr, "initialize: SED error -  %s\n",
+                               sedopal_error_to_text(err));
+
+       dev_close(dev);
+       return err;
+}
+
+static int sed_opal_revert(int argc, char **argv, struct command *cmd,
+               struct plugin *plugin)
+{
+       int err;
+       const char *desc = "Revert a SED device from locking state";
+       struct nvme_dev *dev;
+
+       err = sed_opal_open_device(&dev, argc, argv, desc, revert_opts);
+       if (err)
+               return err;
+
+       err = sedopal_cmd_revert(dev->direct.fd);
+       if (err != 0)
+               fprintf(stderr, "revert: SED error -  %s\n",
+                               sedopal_error_to_text(err));
+
+       dev_close(dev);
+       return err;
+}
+
+static int sed_opal_lock(int argc, char **argv, struct command *cmd,
+               struct plugin *plugin)
+{
+       int err;
+       const char *desc = "Lock a SED device";
+       struct nvme_dev *dev;
+
+       err = sed_opal_open_device(&dev, argc, argv, desc, key_opts);
+       if (err)
+               return err;
+
+       err = sedopal_cmd_lock(dev->direct.fd);
+       if (err != 0)
+               fprintf(stderr, "lock: SED error -  %s\n",
+                               sedopal_error_to_text(err));
+
+       dev_close(dev);
+       return err;
+}
+
+static int sed_opal_unlock(int argc, char **argv, struct command *cmd,
+               struct plugin *plugin)
+{
+       int err;
+       const char *desc = "Unlock a SED device";
+       struct nvme_dev *dev;
+
+       err = sed_opal_open_device(&dev, argc, argv, desc, key_opts);
+       if (err)
+               return err;
+
+       err = sedopal_cmd_unlock(dev->direct.fd);
+       if (err != 0)
+               fprintf(stderr, "unlock: SED error -  %s\n",
+                               sedopal_error_to_text(err));
+
+       dev_close(dev);
+       return err;
+}
+
+static int sed_opal_password(int argc, char **argv, struct command *cmd,
+               struct plugin *plugin)
+{
+       int err;
+       const char *desc = "Change the locking password of a SED device";
+       struct nvme_dev *dev;
+
+       err = sed_opal_open_device(&dev, argc, argv, desc, no_opts);
+       if (err)
+               return err;
+
+       err = sedopal_cmd_password(dev->direct.fd);
+       if (err != 0)
+               fprintf(stderr, "password: SED error -  %s\n",
+                               sedopal_error_to_text(err));
+
+       dev_close(dev);
+       return err;
+}
diff --git a/plugins/sed/sed.h b/plugins/sed/sed.h
new file mode 100644 (file)
index 0000000..1618272
--- /dev/null
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/sed/sed
+
+#include "cmd.h"
+#include <linux/sed-opal.h>
+
+PLUGIN(NAME("sed", "SED Opal Command Set", NVME_VERSION),
+       COMMAND_LIST(
+               ENTRY("discover", "Discover SED Opal Locking Features", sed_opal_discover, "1")
+               ENTRY("initialize", "Initialize a SED Opal Device for locking", sed_opal_initialize)
+               ENTRY("revert", "Revert a SED Opal Device from locking", sed_opal_revert)
+               ENTRY("lock", "Lock a SED Opal Device", sed_opal_lock)
+               ENTRY("unlock", "Unlock a SED Opal Device", sed_opal_unlock)
+               ENTRY("password", "Change the SED Opal Device password", sed_opal_password)
+       )
+);
+
+#include "define_cmd.h"
diff --git a/plugins/sed/sedopal_cmd.c b/plugins/sed/sedopal_cmd.c
new file mode 100644 (file)
index 0000000..649e0b2
--- /dev/null
@@ -0,0 +1,512 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <linux/sed-opal.h>
+#include "sedopal_spec.h"
+#include "sedopal_cmd.h"
+
+/*
+ * ask user for key rather than obtaining it from kernel keyring
+ */
+bool sedopal_ask_key;
+
+/*
+ * initiate dialog to ask for and confirm new password
+ */
+bool sedopal_ask_new_key;
+
+/*
+ * perform a destructive drive revert
+ */
+bool sedopal_destructive_revert;
+
+/*
+ * perform a PSID drive revert
+ */
+bool sedopal_psid_revert;
+
+/*
+ * Map method status codes to error text
+ */
+static const char * const sedopal_errors[] = {
+       [SED_STATUS_SUCCESS] =                  "Success",
+       [SED_STATUS_NOT_AUTHORIZED] =           "Host Not Authorized",
+       [SED_STATUS_OBSOLETE_1] =               "Obsolete",
+       [SED_STATUS_SP_BUSY] =                  "SP Session Busy",
+       [SED_STATUS_SP_FAILED] =                "SP Failed",
+       [SED_STATUS_SP_DISABLED] =              "SP Disabled",
+       [SED_STATUS_SP_FROZEN] =                "SP Frozen",
+       [SED_STATUS_NO_SESSIONS_AVAILABLE] =    "No Sessions Available",
+       [SED_STATUS_UNIQUENESS_CONFLICT] =      "Uniqueness Conflict",
+       [SED_STATUS_INSUFFICIENT_SPACE] =       "Insufficient Space",
+       [SED_STATUS_INSUFFICIENT_ROWS] =        "Insufficient Rows",
+       [SED_STATUS_OBSOLETE_2] =               "Obsolete",
+       [SED_STATUS_INVALID_PARAMETER] =        "Invalid Parameter",
+       [SED_STATUS_OBSOLETE_3] =               "Obsolete",
+       [SED_STATUS_OBSOLETE_4] =               "Obsolete",
+       [SED_STATUS_TPER_MALFUNCTION] =         "TPER Malfunction",
+       [SED_STATUS_TRANSACTION_FAILURE] =      "Transaction Failure",
+       [SED_STATUS_RESPONSE_OVERFLOW] =        "Response Overflow",
+       [SED_STATUS_AUTHORITY_LOCKED_OUT] =     "Authority Locked Out",
+};
+
+const char *sedopal_error_to_text(int code)
+{
+       if (code == SED_STATUS_FAIL)
+               return "Failed";
+
+       if (code == SED_STATUS_NO_METHOD_STATUS)
+               return "Method returned no status";
+
+       if (code < SED_STATUS_SUCCESS ||
+           code > SED_STATUS_AUTHORITY_LOCKED_OUT)
+               return("Unknown Error");
+
+       return sedopal_errors[code];
+}
+
+/*
+ * Read a user entered password and do some basic validity checks.
+ */
+char *sedopal_get_password(char *prompt)
+{
+       char *pass;
+       int len;
+
+       pass = getpass(prompt);
+       if (pass == NULL)
+               return NULL;
+
+       len = strlen(pass);
+       if (len < SEDOPAL_MIN_PASSWORD_LEN)
+               return NULL;
+
+       if (len > SEDOPAL_MAX_PASSWORD_LEN)
+               return NULL;
+
+       return pass;
+}
+
+/*
+ * Initialize a SED Opal key. The key can either specify that the actual
+ * key should be looked up in the kernel keyring, or it should be
+ * populated in the key by prompting the user.
+ */
+int sedopal_set_key(struct opal_key *key)
+{
+#if !HAVE_KEY_TYPE
+       /*
+        * If key_type isn't avaialable, force key prompt
+        */
+       sedopal_ask_key = true;
+#endif
+
+       if (sedopal_ask_key) {
+               char *pass;
+               char *prompt;
+
+               /*
+                * set proper prompt
+                */
+               if (sedopal_ask_new_key)
+                       prompt = SEDOPAL_NEW_PW_PROMPT;
+               else {
+                       if (sedopal_psid_revert)
+                               prompt = SEDOPAL_PSID_PROMPT;
+                       else
+                               prompt = SEDOPAL_CURRENT_PW_PROMPT;
+               }
+
+               pass = sedopal_get_password(prompt);
+               if (pass == NULL)
+                       return -EINVAL;
+
+#if HAVE_KEY_TYPE
+               key->key_type = OPAL_INCLUDED;
+#endif
+               key->key_len = strlen(pass);
+               memcpy(key->key, pass, key->key_len);
+
+               /*
+                * If getting a new key, ask for it to be re-entered
+                * and verify the two entries are the same.
+                */
+               if (sedopal_ask_new_key) {
+                       pass = sedopal_get_password(SEDOPAL_REENTER_PW_PROMPT);
+                       if (strncmp((char *)key->key, pass, key->key_len)) {
+                               fprintf(stderr,
+                                       "Error: passwords don't match\n");
+                               return -EINVAL;
+                       }
+               }
+       } else {
+#if HAVE_KEY_TYPE
+               key->key_type = OPAL_KEYRING;
+#endif
+               key->key_len = 0;
+       }
+
+       key->lr = 0;
+
+       return 0;
+}
+
+/*
+ * Prepare a drive for SED Opal locking.
+ */
+int sedopal_cmd_initialize(int fd)
+{
+       int rc;
+       struct opal_key key;
+       struct opal_lr_act lr_act = {};
+       struct opal_user_lr_setup lr_setup = {};
+
+       sedopal_ask_key = true;
+       rc = sedopal_set_key(&key);
+       if (rc != 0)
+               return rc;
+
+       /*
+        * take ownership of the device
+        */
+       rc = ioctl(fd, IOC_OPAL_TAKE_OWNERSHIP, &key);
+       if (rc != 0) {
+               fprintf(stderr,
+                       "Error: failed to take device ownership - %d\n", rc);
+               return rc;
+       }
+
+       /*
+        * activate lsp
+        */
+       lr_act.num_lrs = 1;
+       lr_act.sum = false;
+       lr_act.key = key;
+
+       rc = ioctl(fd, IOC_OPAL_ACTIVATE_LSP, &lr_act);
+       if (rc != 0) {
+               fprintf(stderr, "Error: failed to activate LSP - %d\n", rc);
+               return rc;
+       }
+
+       /*
+        * setup global locking range
+        */
+       lr_setup.range_start = 0;
+       lr_setup.range_length = 0;
+       lr_setup.RLE = true;
+       lr_setup.WLE = true;
+
+       lr_setup.session.opal_key = key;
+       lr_setup.session.sum = 0;
+       lr_setup.session.who = OPAL_ADMIN1;
+
+       rc = ioctl(fd, IOC_OPAL_LR_SETUP, &lr_setup);
+       if (rc != 0) {
+               fprintf(stderr,
+                       "Error: failed to setup locking range - %d\n", rc);
+               return rc;
+       }
+
+       return rc;
+}
+
+/*
+ * Lock a SED Opal drive
+ */
+int sedopal_cmd_lock(int fd)
+{
+
+       return sedopal_lock_unlock(fd, OPAL_LK);
+}
+
+/*
+ * Unlock a SED Opal drive
+ */
+int sedopal_cmd_unlock(int fd)
+{
+
+       return sedopal_lock_unlock(fd, OPAL_RW);
+}
+
+/*
+ * Prepare and issue an ioctl to lock/unlock a drive
+ */
+int sedopal_lock_unlock(int fd, int lock_state)
+{
+       int rc;
+       struct opal_lock_unlock opal_lu = {};
+
+       rc = sedopal_set_key(&opal_lu.session.opal_key);
+       if (rc != 0)
+               return rc;
+
+       opal_lu.session.sum = 0;
+       opal_lu.session.who = OPAL_ADMIN1;
+       opal_lu.l_state = lock_state;
+
+       rc = ioctl(fd, IOC_OPAL_LOCK_UNLOCK, &opal_lu);
+       if (rc != 0)
+               fprintf(stderr,
+                       "Error: failed locking or unlocking - %d\n", rc);
+
+       /*
+        * If the unlock was successful, force a re-read of the
+        * partition table.
+        */
+       if (rc == 0) {
+               rc = ioctl(fd, BLKRRPART, 0);
+               if (rc != 0)
+                       fprintf(stderr,
+                               "Error: failed re-reading partition\n");
+       }
+
+       return rc;
+}
+
+/*
+ * Confirm a destructive drive so that data is inadvertently erased
+ */
+static bool sedopal_confirm_revert(void)
+{
+       int rc;
+       char ans;
+       bool confirmed = false;
+
+       /*
+        * verify that destructive revert is really the intention
+        */
+       fprintf(stdout,
+               "Destructive revert erases drive data. Continue (y/n)? ");
+       rc = fscanf(stdin, " %c", &ans);
+       if ((rc == 1) && (ans == 'y' || ans == 'Y')) {
+               fprintf(stdout, "Are you sure (y/n)? ");
+               rc = fscanf(stdin, " %c", &ans);
+               if ((rc == 1) && (ans == 'y' || ans == 'Y'))
+                       confirmed = true;
+       }
+
+       return confirmed;
+}
+
+/*
+ * perform a destructive drive revert
+ */
+static int sedopal_revert_destructive(int fd)
+{
+       struct opal_key key;
+       int rc;
+
+       if (!sedopal_confirm_revert()) {
+               fprintf(stderr, "Aborting destructive revert\n");
+               return -1;
+       }
+
+       /*
+        * for destructive revert, require that key is provided
+        */
+       sedopal_ask_key = true;
+
+       rc = sedopal_set_key(&key);
+       if (rc == 0)
+               rc = ioctl(fd, IOC_OPAL_REVERT_TPR, &key);
+
+       return rc;
+}
+
+/*
+ * perform a PSID drive revert
+ */
+static int sedopal_revert_psid(int fd)
+{
+#ifdef IOC_OPAL_PSID_REVERT_TPR
+       struct opal_key key;
+       int rc;
+
+       if (!sedopal_confirm_revert()) {
+               fprintf(stderr, "Aborting PSID revert\n");
+               return -1;
+       }
+
+       rc = sedopal_set_key(&key);
+       if (rc == 0) {
+               rc = ioctl(fd, IOC_OPAL_PSID_REVERT_TPR, &key);
+               if (rc != 0)
+                       fprintf(stderr, "PSID_REVERT_TPR rc %d\n", rc);
+       }
+
+       return rc;
+#else
+       fprintf(stderr, "ERROR : PSID revert is not supported\n");
+       return -EOPNOTSUPP;
+#endif /* IOC_OPAL_PSID_REVERT_TPR */
+}
+
+/*
+ * revert a drive from the provisioned state to a state where locking
+ * is disabled.
+ */
+int sedopal_cmd_revert(int fd)
+{
+       int rc;
+
+       /*
+        * for revert, require that key/PSID is provided
+        */
+       sedopal_ask_key = true;
+
+       if (sedopal_psid_revert) {
+               rc = sedopal_revert_psid(fd);
+       } else if (sedopal_destructive_revert) {
+               rc = sedopal_revert_destructive(fd);
+       } else {
+#ifdef IOC_OPAL_REVERT_LSP
+               struct opal_revert_lsp revert_lsp;
+
+               rc = sedopal_set_key(&revert_lsp.key);
+               if (rc != 0)
+                       return rc;
+
+               revert_lsp.options = OPAL_PRESERVE;
+               revert_lsp.__pad = 0;
+
+               rc = ioctl(fd, IOC_OPAL_REVERT_LSP, &revert_lsp);
+#else
+               rc = -EOPNOTSUPP;
+#endif
+       }
+
+       if (rc != 0)
+               fprintf(stderr, "Error: failed reverting drive - %d\n", rc);
+
+       return rc;
+}
+
+/*
+ * Change the password of a drive. The existing password must be
+ * provided and the new password is confirmed by re-entry.
+ */
+int sedopal_cmd_password(int fd)
+{
+       int rc;
+       struct opal_new_pw new_pw = {};
+
+       new_pw.new_user_pw.who = OPAL_ADMIN1;
+       new_pw.new_user_pw.opal_key.lr = 0;
+       new_pw.session.who = OPAL_ADMIN1;
+       new_pw.session.sum = 0;
+       new_pw.session.opal_key.lr = 0;
+
+       /*
+        * get current key
+        */
+       sedopal_ask_key = true;
+       if (sedopal_set_key(&new_pw.session.opal_key) != 0)
+               return -EINVAL;
+
+       /*
+        * get new key
+        */
+       sedopal_ask_new_key = true;
+       if (sedopal_set_key(&new_pw.new_user_pw.opal_key) != 0)
+               return -EINVAL;
+
+       rc = ioctl(fd, IOC_OPAL_SET_PW, &new_pw);
+       if (rc != 0)
+               fprintf(stderr, "Error: failed setting password - %d\n", rc);
+
+       return rc;
+}
+
+/*
+ * Print the state of locking features.
+ */
+void sedopal_print_locking_features(uint8_t features)
+{
+       printf("Locking Features:\n");
+       printf("\tLocking Supported:         %s\n",
+               (features & OPAL_FEATURE_LOCKING_SUPPORTED) ? "Yes" : "No");
+       printf("\tLocking Feature Enabled:   %s\n",
+               (features & OPAL_FEATURE_LOCKING_ENABLED) ? "Yes" : "No");
+       printf("\tLocked:                    %s\n",
+               (features & OPAL_FEATURE_LOCKED) ? "Yes" : "No");
+}
+
+/*
+ * Query a drive to determine if it's SED Opal capable and
+ * it's current locking status.
+ */
+int sedopal_cmd_discover(int fd)
+{
+#ifdef IOC_OPAL_DISCOVERY
+       int rc;
+       bool sedopal_locking_supported = false;
+       struct opal_discovery discover;
+       struct level_0_discovery_header *dh;
+       struct level_0_discovery_features *feat;
+       struct level_0_discovery_features *feat_end;
+       uint16_t code;
+       uint8_t locking_flags;
+       char buf[4096];
+
+       discover.data = (__u64)buf;
+       discover.size = sizeof(buf);
+
+       rc = ioctl(fd, IOC_OPAL_DISCOVERY, &discover);
+       if (rc < 0) {
+               fprintf(stderr, "Error: ioctl IOC_OPAL_DISCOVERY failed\n");
+               return rc;
+       }
+
+       /*
+        * The returned buffer contains a level 0 discovery header
+        * folowed by an array of level 0 feature records.
+        *
+        * TCG Opal Specification v2.0.2 section 3.1.1
+        */
+       dh = (struct level_0_discovery_header *)buf;
+       feat = (struct level_0_discovery_features *)(dh + 1);
+       feat_end = (struct level_0_discovery_features *)
+               (buf + be32toh(dh->parameter_length));
+
+       /*
+        * iterate through all the features that were returned
+        */
+       while (feat < feat_end) {
+               code = be16toh(feat->code);
+               switch (code) {
+               case OPAL_FEATURE_CODE_LOCKING:
+                       locking_flags = feat->feature;
+                       break;
+               case OPAL_FEATURE_CODE_OPALV2:
+                       sedopal_locking_supported = true;
+                       break;
+               default:
+                       break;
+               }
+
+               feat++;
+       }
+
+       rc = 0;
+       if (!sedopal_locking_supported) {
+               fprintf(stderr, "Error: device does not support SED Opal\n");
+               rc = -1;
+       } else
+               sedopal_print_locking_features(locking_flags);
+
+       return rc;
+#else /* IOC_OPAL_DISCOVERY */
+       fprintf(stderr, "ERROR : NVMe device discovery is not supported\n");
+       return -EOPNOTSUPP;
+#endif
+}
diff --git a/plugins/sed/sedopal_cmd.h b/plugins/sed/sedopal_cmd.h
new file mode 100644 (file)
index 0000000..3b6eae2
--- /dev/null
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef _SED_OPAL_CMD_H
+#define _SED_OPAL_CMD_H
+
+#define        SEDOPAL_CURRENT_PW_PROMPT       "Password: "
+#define        SEDOPAL_NEW_PW_PROMPT           "New Password: "
+#define        SEDOPAL_REENTER_PW_PROMPT       "Re-enter New Password: "
+#define        SEDOPAL_PSID_PROMPT             "PSID: "
+
+#define        SEDOPAL_MIN_PASSWORD_LEN        8
+#define        SEDOPAL_MAX_PASSWORD_LEN        32
+
+#define NVME_DEV_PATH                  "/dev/nvme"
+
+extern bool sedopal_ask_key;
+extern bool sedopal_ask_new_key;
+extern bool sedopal_destructive_revert;
+extern bool sedopal_psid_revert;
+
+/*
+ * Sub-commands supported by the sedopal command
+ */
+enum sedopal_cmds {
+       SEDOPAL_CMD_NOT_SPECIFIED =     -1,
+       SEDOPAL_CMD_INITIALIZE =        0,
+       SEDOPAL_CMD_LOCK =              1,
+       SEDOPAL_CMD_UNLOCK =            2,
+       SEDOPAL_CMD_REVERT =            3,
+       SEDOPAL_CMD_PASSWORD =          4,
+       SEDOPAL_CMD_DISCOVER =          5,
+};
+
+struct cmd_table {
+       int (*cmd_handler)(int fd);
+};
+
+/*
+ * command handlers
+ */
+int sedopal_cmd_initialize(int fd);
+int sedopal_cmd_lock(int fd);
+int sedopal_cmd_unlock(int fd);
+int sedopal_cmd_revert(int fd);
+int sedopal_cmd_password(int fd);
+int sedopal_cmd_discover(int fd);
+
+/*
+ * utility functions
+ */
+int sedopal_open_nvme_device(char *device);
+int sedopal_lock_unlock(int fd, int lock_state);
+const char *sedopal_error_to_text(int code);
+
+#endif /* _SED_OPAL_CMD_H */
diff --git a/plugins/sed/sedopal_spec.h b/plugins/sed/sedopal_spec.h
new file mode 100644 (file)
index 0000000..7523060
--- /dev/null
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef _SED_OPAL_SPEC_H
+#define _SED_OPAL_SPEC_H
+
+/*
+ * TCP Storage Architecture Core Specification Version 2.01
+ * section 5.1.5 Method Status Codes
+ */
+enum sed_status_codes {
+       SED_STATUS_SUCCESS =                    0x00,
+       SED_STATUS_NOT_AUTHORIZED =             0x01,
+       SED_STATUS_OBSOLETE_1 =                 0x02,
+       SED_STATUS_SP_BUSY =                    0x03,
+       SED_STATUS_SP_FAILED =                  0x04,
+       SED_STATUS_SP_DISABLED =                0x05,
+       SED_STATUS_SP_FROZEN =                  0x06,
+       SED_STATUS_NO_SESSIONS_AVAILABLE =      0x07,
+       SED_STATUS_UNIQUENESS_CONFLICT =        0x08,
+       SED_STATUS_INSUFFICIENT_SPACE =         0x09,
+       SED_STATUS_INSUFFICIENT_ROWS =          0x0A,
+       SED_STATUS_OBSOLETE_2 =                 0x0B,
+       SED_STATUS_INVALID_PARAMETER =          0x0C,
+       SED_STATUS_OBSOLETE_3 =                 0x0D,
+       SED_STATUS_OBSOLETE_4 =                 0x0E,
+       SED_STATUS_TPER_MALFUNCTION =           0x0F,
+       SED_STATUS_TRANSACTION_FAILURE =        0x10,
+       SED_STATUS_RESPONSE_OVERFLOW =          0x11,
+       SED_STATUS_AUTHORITY_LOCKED_OUT =       0x12,
+       SED_STATUS_FAIL =                       0x3F,
+       SED_STATUS_NO_METHOD_STATUS =           0x89,
+};
+
+/*
+ * Definitions from TCG Opal Specification v2.0.2
+ */
+
+/*
+ * level 0 feature codes - section 3.1.1
+ */
+#define        OPAL_FEATURE_CODE_LOCKING               0x0002
+#define        OPAL_FEATURE_CODE_OPALV2                0x0203
+
+/* locking features */
+#define OPAL_FEATURE_LOCKING_SUPPORTED         0x01
+#define OPAL_FEATURE_LOCKING_ENABLED           0x02
+#define OPAL_FEATURE_LOCKED                    0x04
+
+
+/*
+ * discovery header as specified in section 3.1.1.1
+ */
+struct level_0_discovery_header {
+       uint32_t        parameter_length;
+       uint32_t        revision;
+       uint64_t        reserved;
+       uint8_t         vendor_specific[32];
+};
+
+/*
+ * level 0 features as specified in section 3.1.1.3
+ */
+struct level_0_discovery_features {
+       uint16_t        code;
+       uint8_t         version;
+       uint8_t         length;
+       uint8_t         feature;
+       uint8_t         reserved[11];
+};
+
+#endif /* _SED_OPAL_SPEC_H */