aboutsummaryrefslogtreecommitdiffstats
path: root/block
diff options
authorOndrej Kozina <okozina@redhat.com>2026-02-06 15:17:58 +0100
committerJens Axboe <axboe@kernel.dk>2026-03-09 14:29:59 -0600
commitaca086ff27c3f67e81617e4b063d1126544a4f19 (patch)
treee11efb1aa81a00bbbf7d857fab33ea4c80b559ce /block
parentc6c9dc91cb5fd30d2e11e7f2ae570e614b013ee1 (diff)
downloadlinux-next-history-aca086ff27c3f67e81617e4b063d1126544a4f19.tar.gz
sed-opal: add IOC_OPAL_REACTIVATE_LSP.
This adds the 'Reactivate' method as described in the "TCG Storage Opal SSC Feature Set: Single User Mode" document (ch. 3.1.1.1). The method enables switching an already active SED OPAL2 device, with appropriate firmware support for Single User Mode (SUM), to or from SUM. Signed-off-by: Ondrej Kozina <okozina@redhat.com> Reviewed-and-tested-by: Milan Broz <gmazyland@gmail.com> Reviewed-by: Hannes Reinecke <hare@suse.de> Signed-off-by: Jens Axboe <axboe@kernel.dk>
Diffstat (limited to 'block')
-rw-r--r--block/opal_proto.h1
-rw-r--r--block/sed-opal.c99
2 files changed, 100 insertions, 0 deletions
diff --git a/block/opal_proto.h b/block/opal_proto.h
index 3ccee5977c105..d138785b81982 100644
--- a/block/opal_proto.h
+++ b/block/opal_proto.h
@@ -155,6 +155,7 @@ enum opal_method {
OPAL_AUTHENTICATE,
OPAL_RANDOM,
OPAL_ERASE,
+ OPAL_REACTIVATE,
};
enum opal_token {
diff --git a/block/sed-opal.c b/block/sed-opal.c
index 83bee47aa29f6..5d06f5f433bf9 100644
--- a/block/sed-opal.c
+++ b/block/sed-opal.c
@@ -220,6 +220,8 @@ static const u8 opalmethod[][OPAL_METHOD_LENGTH] = {
{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x01 },
[OPAL_ERASE] =
{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x03 },
+ [OPAL_REACTIVATE] =
+ { 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x01 },
};
static int end_opal_session_error(struct opal_dev *dev);
@@ -2287,6 +2289,74 @@ static int activate_lsp(struct opal_dev *dev, void *data)
return finalize_and_send(dev, parse_and_check_status);
}
+static int reactivate_lsp(struct opal_dev *dev, void *data)
+{
+ struct opal_lr_react *opal_react = data;
+ u8 user_lr[OPAL_UID_LENGTH];
+ int err, i;
+
+ err = cmd_start(dev, opaluid[OPAL_THISSP_UID],
+ opalmethod[OPAL_REACTIVATE]);
+
+ if (err) {
+ pr_debug("Error building Reactivate LockingSP command.\n");
+ return err;
+ }
+
+ /*
+ * If neither 'entire_table' nor 'num_lrs' is set, the device
+ * gets reactivated with SUM disabled. Only Admin1PIN will change
+ * if set.
+ */
+ if (opal_react->entire_table) {
+ /* Entire Locking table (all locking ranges) will be put in SUM. */
+ add_token_u8(&err, dev, OPAL_STARTNAME);
+ add_token_u64(&err, dev, OPAL_SUM_SET_LIST);
+ add_token_bytestring(&err, dev, opaluid[OPAL_LOCKING_TABLE], OPAL_UID_LENGTH);
+ add_token_u8(&err, dev, OPAL_ENDNAME);
+ } else if (opal_react->num_lrs) {
+ /* Subset of Locking table (selected locking range(s)) to be put in SUM */
+ err = build_locking_range(user_lr, sizeof(user_lr),
+ opal_react->lr[0]);
+ if (err)
+ return err;
+
+ add_token_u8(&err, dev, OPAL_STARTNAME);
+ add_token_u64(&err, dev, OPAL_SUM_SET_LIST);
+
+ add_token_u8(&err, dev, OPAL_STARTLIST);
+ add_token_bytestring(&err, dev, user_lr, OPAL_UID_LENGTH);
+ for (i = 1; i < opal_react->num_lrs; i++) {
+ user_lr[7] = opal_react->lr[i];
+ add_token_bytestring(&err, dev, user_lr, OPAL_UID_LENGTH);
+ }
+ add_token_u8(&err, dev, OPAL_ENDLIST);
+ add_token_u8(&err, dev, OPAL_ENDNAME);
+ }
+
+ /* Skipping the rangle policy parameter is same as setting its value to zero */
+ if (opal_react->range_policy && (opal_react->num_lrs || opal_react->entire_table)) {
+ add_token_u8(&err, dev, OPAL_STARTNAME);
+ add_token_u64(&err, dev, OPAL_SUM_RANGE_POLICY);
+ add_token_u8(&err, dev, 1);
+ add_token_u8(&err, dev, OPAL_ENDNAME);
+ }
+
+ /*
+ * Optional parameter. If set, it changes the Admin1 PIN even when SUM
+ * is being disabled.
+ */
+ if (opal_react->new_admin_key.key_len) {
+ add_token_u8(&err, dev, OPAL_STARTNAME);
+ add_token_u64(&err, dev, OPAL_SUM_ADMIN1_PIN);
+ add_token_bytestring(&err, dev, opal_react->new_admin_key.key,
+ opal_react->new_admin_key.key_len);
+ add_token_u8(&err, dev, OPAL_ENDNAME);
+ }
+
+ return finalize_and_send(dev, parse_and_check_status);
+}
+
/* Determine if we're in the Manufactured Inactive or Active state */
static int get_lsp_lifecycle(struct opal_dev *dev, void *data)
{
@@ -2957,6 +3027,32 @@ static int opal_activate_lsp(struct opal_dev *dev,
return ret;
}
+static int opal_reactivate_lsp(struct opal_dev *dev,
+ struct opal_lr_react *opal_lr_react)
+{
+ const struct opal_step active_steps[] = {
+ { start_admin1LSP_opal_session, &opal_lr_react->key },
+ { reactivate_lsp, opal_lr_react },
+ /* No end_opal_session. The controller terminates the session */
+ };
+ int ret;
+
+ /* use either 'entire_table' parameter or set of locking ranges */
+ if (opal_lr_react->num_lrs > OPAL_MAX_LRS ||
+ (opal_lr_react->num_lrs && opal_lr_react->entire_table))
+ return -EINVAL;
+
+ ret = opal_get_key(dev, &opal_lr_react->key);
+ if (ret)
+ return ret;
+ mutex_lock(&dev->dev_lock);
+ setup_opal_dev(dev);
+ ret = execute_steps(dev, active_steps, ARRAY_SIZE(active_steps));
+ mutex_unlock(&dev->dev_lock);
+
+ return ret;
+}
+
static int opal_setup_locking_range(struct opal_dev *dev,
struct opal_user_lr_setup *opal_lrs)
{
@@ -3315,6 +3411,9 @@ int sed_ioctl(struct opal_dev *dev, unsigned int cmd, void __user *arg)
case IOC_OPAL_SET_SID_PW:
ret = opal_set_new_sid_pw(dev, p);
break;
+ case IOC_OPAL_REACTIVATE_LSP:
+ ret = opal_reactivate_lsp(dev, p);
+ break;
default:
break;