summaryrefslogtreecommitdiffstats
path: root/plugins/sed
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 19:41:32 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 19:41:32 +0000
commitf26f66d866ba1a9f3204e6fdfe2b07e67b5492ad (patch)
treec953c007cbe4f60a147ab62f97937d58abb2e9ca /plugins/sed
parentInitial commit. (diff)
downloadnvme-cli-f26f66d866ba1a9f3204e6fdfe2b07e67b5492ad.tar.xz
nvme-cli-f26f66d866ba1a9f3204e6fdfe2b07e67b5492ad.zip
Adding upstream version 2.8.upstream/2.8
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plugins/sed')
-rw-r--r--plugins/sed/meson.build4
-rw-r--r--plugins/sed/sed.c178
-rw-r--r--plugins/sed/sed.h19
-rw-r--r--plugins/sed/sedopal_cmd.c512
-rw-r--r--plugins/sed/sedopal_cmd.h55
-rw-r--r--plugins/sed/sedopal_spec.h71
6 files changed, 839 insertions, 0 deletions
diff --git a/plugins/sed/meson.build b/plugins/sed/meson.build
new file mode 100644
index 0000000..4a2e544
--- /dev/null
+++ b/plugins/sed/meson.build
@@ -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
index 0000000..0471e54
--- /dev/null
+++ b/plugins/sed/sed.c
@@ -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
index 0000000..1618272
--- /dev/null
+++ b/plugins/sed/sed.h
@@ -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
index 0000000..649e0b2
--- /dev/null
+++ b/plugins/sed/sedopal_cmd.c
@@ -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
index 0000000..3b6eae2
--- /dev/null
+++ b/plugins/sed/sedopal_cmd.h
@@ -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
index 0000000..7523060
--- /dev/null
+++ b/plugins/sed/sedopal_spec.h
@@ -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 */