linux-nvme.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/6] Sed Opal
@ 2016-10-31 21:58 Scott Bauer
  2016-10-31 21:58 ` [RFC PATCH 1/6] Include: Add definitions for sed Scott Bauer
                   ` (5 more replies)
  0 siblings, 6 replies; 16+ messages in thread
From: Scott Bauer @ 2016-10-31 21:58 UTC (permalink / raw)


This Patch series implements a large portion of the Opal protocol for
self encrypting devices. The driver has the capability of storing a
locking range's password, either directly in the driver, or in the
Kernel's key managment. The password can then be replayed during a
resume from previous suspend-to-RAM.

The driver also supports logic to bring the device out of a factory
default-inactive state into a functional Opal state.

The following logic is supported in order to bring the tper into a
working state:

1) Taking Ownership of the drive (Setting the Admin CPIN).
2) Activating the Locking SP (In Single User Mode or Normal Mode).
3) Setting up Locking Ranges (Single User or Normal Mode).
4) Adding users to Locking Ranges (Normal Mode Only).
5) Locking or Unlocking Locking Rangs (Single User Mode or Normal Mode).
6) Reverting the TPer (Restore to factory default).
7) Setting LR/User passwords (Single User Mode or Normal Mode).
8) Eabling/disabling Shadow MBR.
9) Enabling Users in the LockingSP (Normal Mode Only).
10) Saving Password for resume from suspend.


Each command above is exported through an ioctl in the block layer.

We have userland tooling staged in nvme-cli which can be used for
testing:
https://github.com/linux-nvme/nvme-cli/pull/137

Once we've fixed any nits and issues we will merge the userland tooling
into the master branch of nvme-cli.

I have a series of test scripts I've been using which can be helpful if
people want to test or immediately start using and testing the code:

https://github.com/ScottyBauer/nvme-cli-sed-sh


At a high level the code works by declaring a set of opal_step functions,
which we save in our opal_dev structure. The opal_step functions declare
the functions which are necessary to do the opal command. We start
dispatching the commands to the device, which return immediately. The
thread from userland calls into wait_for_cmd_completion to wait until
all the commands have finished. Each new command is issued in an interrupt
that signaled the completion of the previous command.

If a command requires information to complete we pass that information in
the opal_dev structure in the func_data. If a command needs to save data
to give to a future command it is saved in the prev_data.



On a side note, since these files are new I assume we will need to add
a new entry into MAINTAINERS for this code base, can anyone confirm that?

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [RFC PATCH 1/6] Include: Add definitions for sed
  2016-10-31 21:58 [RFC PATCH 0/6] Sed Opal Scott Bauer
@ 2016-10-31 21:58 ` Scott Bauer
  2016-10-31 21:58 ` [RFC PATCH 2/6] lib: Add Sed-opal library Scott Bauer
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Scott Bauer @ 2016-10-31 21:58 UTC (permalink / raw)


This patch adds the definitions and structures for the SED
Opal code.

Signed-off-by: Scott Bauer <scott.bauer at intel.com>
Signed-off-by: Rafael Antognolli <Rafael.Antognolli at intel.com>
---
 include/linux/sed-opal.h      |  58 +++++++++++++++++++++
 include/linux/sed.h           |  91 ++++++++++++++++++++++++++++++++
 include/uapi/linux/sed-opal.h | 118 ++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/sed.h      |  55 ++++++++++++++++++++
 4 files changed, 322 insertions(+)
 create mode 100644 include/linux/sed-opal.h
 create mode 100644 include/linux/sed.h
 create mode 100644 include/uapi/linux/sed-opal.h
 create mode 100644 include/uapi/linux/sed.h

diff --git a/include/linux/sed-opal.h b/include/linux/sed-opal.h
new file mode 100644
index 0000000..e0ee21e
--- /dev/null
+++ b/include/linux/sed-opal.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright ? 2016 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Rafael Antognolli <rafael.antognolli at intel.com>
+ *    Scott  Bauer      <scott.bauer at intel.com>
+ */
+
+#ifndef LINUX_OPAL_H
+#define LINUX_OPAL_H
+
+#include <linux/sed.h>
+#include <linux/kernel.h>
+
+enum {
+	TCG_SECP_00 = 0,
+	TCG_SECP_01,
+};
+
+struct opal_suspend_unlk {
+	void *data;
+	const char *name;
+	struct sec_ops ops;
+};
+
+int opal_save(struct block_device *bdev, struct sed_key *key);
+int opal_lock_unlock(struct block_device *bdev, struct sed_key *key);
+int opal_take_ownership(struct block_device *bdev, struct sed_key *key);
+int opal_activate_lsp(struct block_device *bdev, struct sed_key *key);
+int opal_set_new_pw(struct block_device *bdev, struct sed_key *key);
+int opal_activate_user(struct block_device *bdev, struct sed_key *key);
+int opal_reverttper(struct block_device *bdev, struct sed_key *key);
+int opal_setup_locking_range(struct block_device *bdev, struct sed_key *key);
+int opal_add_user_to_lr(struct block_device *bdev, struct sed_key *key);
+int opal_enable_disable_shadow_mbr(struct block_device *bdev, struct sed_key *key);
+int opal_unlock_from_suspend(struct opal_suspend_unlk *data);
+int opal_erase_locking_range(struct block_device *bdev, struct sed_key *key);
+
+#endif /* LINUX_OPAL_H */
diff --git a/include/linux/sed.h b/include/linux/sed.h
new file mode 100644
index 0000000..6c9bae9
--- /dev/null
+++ b/include/linux/sed.h
@@ -0,0 +1,91 @@
+/*
+ * Self-Encrypting Drive interface - sed.h
+ *
+ * Copyright (C) 2016 Intel Corporation <jonathan.derrick at intel.com>
+ *
+ * This code is the generic layer to interface with self-encrypting
+ * drives. Specific command sets should advertise support to sed uapi
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef LINUX_SED_H
+#define LINUX_SED_H
+
+#include <linux/blkdev.h>
+#include <uapi/linux/sed.h>
+
+/*
+ * sec_ops - transport specific Trusted Send/Receive functions
+ * See SPC-4 for specific definitions
+ *
+ * @sec_send: sends the payload to the trusted peripheral
+ *	SPSP: Security Protocol Specific
+ *	SECP: Security Protocol
+ *	buf: Payload
+ *	len: Payload length
+ * @recv: Receives a payload from the trusted peripheral
+ *	SPSP: Security Protocol Specific
+ *	SECP: Security Protocol
+ *	buf: Payload
+ *	len: Payload length
+ */
+
+typedef void (sec_cb)(int error, void *data);
+
+struct sec_ops {
+	int (*send)(void *data, __u16 SPSP, __u8 SECP,
+			void *buffer, size_t len,
+			sec_cb *cb, void *cb_data);
+	int (*recv)(void *data, __u16 SPSP, __u8 SECP,
+			void *buffer, size_t len,
+			sec_cb *cb, void *cb_data);
+};
+
+
+#ifdef CONFIG_SED
+int sed_save(struct block_device *bdev, struct sed_key *key);
+int sed_lock_unlock(struct block_device *bdev, struct sed_key *key);
+int sed_take_ownership(struct block_device *bdev, struct sed_key *key);
+int sed_activate_lsp(struct block_device *bdev, struct sed_key *key);
+int sed_set_pw(struct block_device *bdev, struct sed_key *key);
+int sed_activate_user(struct block_device *bdev, struct sed_key *key);
+int sed_reverttper(struct block_device *bdev, struct sed_key *key);
+int sed_setup_locking_range(struct block_device *bdev, struct sed_key *key);
+int sed_adduser_to_lr(struct block_device *bdev, struct sed_key *key);
+int sed_do_mbr(struct block_device *bdev, struct sed_key *key);
+int sed_erase_lr(struct block_device *bdev, struct sed_key *key);
+#else
+static inline int sed_save(struct block_device *bdev, struct sed_key *key)
+		{ return -EOPNOTSUPP; }
+static inline int sed_lock_unlock(struct block_device *bdev, struct sed_key *key)
+		{ return -EOPNOTSUPP; }
+static inline int sed_take_ownership(struct block_device *bdev, struct sed_key *key)
+                { return -EOPNOTSUPP; }
+static inline int sed_activate_lsp(struct block_device *bdev, struct sed_key *key)
+                { return -EOPNOTSUPP; }
+static inline int sed_set_pw(struct block_device *bdev, struct sed_key *key)
+                { return -EOPNOTSUPP; }
+static inline int sed_activate_user(struct block_device *bdev, struct sed_key *key)
+                { return -EOPNOTSUPP; }
+static inline int sed_reverttper(struct block_device *bdev, struct sed_key *key)
+                { return -EOPNOTSUPP; }
+static inline int sed_setup_locking_range(struct block_device *bdev, struct sed_key *key)
+                { return -EOPNOTSUPP; }
+static inline int sed_adduser_to_lr(struct block_device *bdev, struct sed_key *key)
+                { return -EOPNOTSUPP; }
+static inline int sed_do_mbr(struct block_device *bdev, struct sed_key *key)
+                { return -EOPNOTSUPP; }
+static inline int sed_erase_lr(struct block_device *bdev, struct sed_key *key)
+                { return -EOPNOTSUPP; }
+#endif
+
+#endif /* LINUX_SED_H */
diff --git a/include/uapi/linux/sed-opal.h b/include/uapi/linux/sed-opal.h
new file mode 100644
index 0000000..527eb9a
--- /dev/null
+++ b/include/uapi/linux/sed-opal.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright ? 2016 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Author:
+ *    Rafael Antognolli <rafael.antognolli at intel.com>
+ *    Scott  Bauer      <rafael.antognolli at intel.com>
+ */
+
+#ifndef _UAPI_OPAL_H
+#define _UAPI_OPAL_H
+
+#include <linux/types.h>
+
+#define OPAL_KEY_MAX 256
+
+enum opal_mbr {
+	OPAL_MBR_ENABLE,
+	OPAL_MBR_DISABLE,
+};
+
+enum opal_user {
+	OPAL_ADMIN1,
+	OPAL_USER1,
+	OPAL_USER2,
+	OPAL_USER3,
+	OPAL_USER4,
+	OPAL_USER5,
+	OPAL_USER6,
+	OPAL_USER7,
+	OPAL_USER8,
+	OPAL_USER9,
+};
+
+struct opal_user_info {
+	bool SUM;
+	enum opal_user who;
+};
+
+enum opal_key_type {
+	OPAL_KEY_PLAIN,
+	OPAL_KEY_KEYRING,
+};
+
+enum opal_lock_state {
+	OPAL_RO = 0x01, /* 0001 */
+	OPAL_RW = 0x02, /* 0010 */
+	OPAL_LK = 0x04, /* 0100 */
+};
+
+struct opal_key {
+	__u8	lr;
+	__u8	key_type;
+	__u8	key_len;
+	__u8	key[OPAL_KEY_MAX];
+};
+
+struct opal_activate_user {
+	struct opal_user_info who;
+	struct opal_key key;
+};
+
+struct opal_user_lr_setup {
+	struct opal_user_info who;
+	struct opal_key key;
+	size_t range_start;
+	size_t range_length;
+	int    RLE; /* Read Lock enabled */
+	int    WLE; /* Write Lock Enabled */
+};
+
+struct opal_lock_unlock {
+	struct opal_user_info authority;
+	enum opal_lock_state l_state;
+	struct opal_key key;
+};
+
+struct opal_new_pw {
+	struct opal_user_info who;
+
+	/* When we're not operating in SUM, and we first set
+	 * passwords we need to set them via ADMIN authority.
+	 * After passwords are changed, we can set them via,
+	 * User authorities.
+	 * Because of this restriction we need to know about
+	 * Two different users. One in 'who' which we will use
+	 * to start the session and user_for_pw as the user we're
+	 * chaning the pw for.
+	 */
+	enum opal_user user_for_pw;
+	struct opal_key current_pin;
+	struct opal_key new_pin;
+};
+
+struct opal_mbr_data {
+	u8 enable_disable;
+	struct opal_key key;
+};
+
+#endif /* _UAPI_SED_H */
diff --git a/include/uapi/linux/sed.h b/include/uapi/linux/sed.h
new file mode 100644
index 0000000..6973044
--- /dev/null
+++ b/include/uapi/linux/sed.h
@@ -0,0 +1,55 @@
+/*
+ * Definitions for the self-encrypting drive interface
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef _UAPI_SED_H
+#define _UAPI_SED_H
+
+#include <linux/types.h>
+
+enum sed_key_type {
+	OPAL,
+	OPAL_PW,
+	OPAL_ACT_USR,
+	OPAL_LR_SETUP,
+	OPAL_LOCK_UNLOCK,
+	OPAL_MBR_DATA,
+};
+
+struct sed_key {
+	__u32 sed_type;
+	union {
+		struct opal_key           __user *opal;
+		struct opal_new_pw        __user *opal_pw;
+		struct opal_activate_user __user *opal_act;
+		struct opal_user_lr_setup __user *opal_lrs;
+		struct opal_lock_unlock   __user *opal_lk_unlk;
+		struct opal_mbr_data      __user *opal_mbr;
+		/* additional command set key types */
+	};
+};
+
+
+#define IOC_SED_SAVE		   _IOW('p', 220, struct sed_key)
+#define IOC_SED_LOCK_UNLOCK	   _IOW('p', 221, struct sed_key)
+#define IOC_SED_TAKE_OWNERSHIP	   _IOW('p', 222, struct sed_key)
+#define IOC_SED_ACTIVATE_LSP       _IOW('p', 223, struct sed_key)
+#define IOC_SED_SET_PW             _IOW('p', 224, struct sed_key)
+#define IOC_SED_ACTIVATE_USR       _IOW('p', 225, struct sed_key)
+#define IOC_SED_REVERT_TPR         _IOW('p', 226, struct sed_key)
+#define IOC_SED_LR_SETUP           _IOW('p', 227, struct sed_key)
+#define IOC_SED_ADD_USR_TO_LR      _IOW('p', 228, struct sed_key)
+#define IOC_SED_ENABLE_DISABLE_MBR _IOW('p', 229, struct sed_key)
+#define IOC_SED_ERASE_LR           _IOW('p', 230, struct sed_key)
+
+#endif /* _UAPI_SED_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC PATCH 2/6] lib: Add Sed-opal library
  2016-10-31 21:58 [RFC PATCH 0/6] Sed Opal Scott Bauer
  2016-10-31 21:58 ` [RFC PATCH 1/6] Include: Add definitions for sed Scott Bauer
@ 2016-10-31 21:58 ` Scott Bauer
  2016-11-01 18:56   ` Jon Derrick
  2016-10-31 21:58 ` [RFC PATCH 3/6] lib: Add Sed to Kconfig and Makefile Scott Bauer
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 16+ messages in thread
From: Scott Bauer @ 2016-10-31 21:58 UTC (permalink / raw)


This patch implements the necessary logic to bring an Opal
enabled drive out of a factory-enabled into a working
Opal state.

This patch set also enables logic to save a password to
be replayed during a resume from suspend. The key can be
saved in the driver or in the Kernel's Key managment.

Signed-off-by: Scott Bauer <scott.bauer at intel.com>
Signed-off-by: Rafael Antognolli <Rafael.Antognolli at intel.com>
---
 lib/sed-opal.c          | 3337 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/sed-opal_internal.h |  586 +++++++++
 lib/sed-opal_key.c      |   46 +
 lib/sed.c               |  303 +++++
 4 files changed, 4272 insertions(+)
 create mode 100644 lib/sed-opal.c
 create mode 100644 lib/sed-opal_internal.h
 create mode 100644 lib/sed-opal_key.c
 create mode 100644 lib/sed.c

diff --git a/lib/sed-opal.c b/lib/sed-opal.c
new file mode 100644
index 0000000..10b3348
--- /dev/null
+++ b/lib/sed-opal.c
@@ -0,0 +1,3337 @@
+/*
+ * Copyright ? 2016 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Rafael Antognolli <rafael.antognolli at intel.com>
+ *    Scott  Bauer      <scott.bauer at intel.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ":OPAL: " fmt
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/genhd.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/sed-opal.h>
+#include <linux/sed.h>
+#include <linux/sed-opal.h>
+#include <linux/string.h>
+
+#include "sed-opal_internal.h"
+
+#define IO_BUFFER_LENGTH 2048
+
+#define MAX_TOKS 64
+
+struct opal_cmd {
+	struct block_device *bdev;
+	sec_cb *cb;
+	void *cb_data;
+
+	size_t pos;
+	u8 cmd_buf[IO_BUFFER_LENGTH * 2];
+	u8 resp_buf[IO_BUFFER_LENGTH * 2];
+	u8 *cmd;
+	u8 *resp;
+};
+
+/*
+ * On the parsed response, we don't store again the toks that are already
+ * stored in the response buffer. Instead, for each token, we just store a
+ * pointer to the position in the buffer where the token starts, and the size
+ * of the token in bytes.
+ */
+struct opal_resp_tok {
+	const u8 *pos;
+	size_t len;
+	enum OPAL_RESPONSE_TOKEN type;
+	enum OPAL_ATOM_WIDTH width;
+	union {
+		u64 u;
+		s64 s;
+	} stored;
+};
+
+/*
+ * From the response header it's not possible to know how many tokens there are
+ * on the payload. So we hardcode that the maximum will be MAX_TOKS, and later
+ * if we start dealing with messages that have more than that, we can increase
+ * this number. This is done to avoid having to make two passes through the
+ * response, the first one counting how many tokens we have and the second one
+ * actually storing the positions.
+ */
+struct parsed_resp {
+	int num;
+	struct opal_resp_tok toks[MAX_TOKS];
+};
+
+struct opal_dev;
+
+typedef void (*opal_cb)(int error, struct opal_dev *dev);
+
+typedef int (*opal_step)(struct opal_dev *dev);
+
+struct opal_completion {
+	struct completion cmd_completion;
+	int completion_status;
+};
+
+struct opal_dev {
+	struct block_device *bdev;
+	struct opal_completion *completion;
+	struct opal_lock_unlock lkul;
+	const opal_step *funcs;
+	void **func_data;
+	bool resume_from_suspend;
+	struct opal_suspend_unlk *resume_data;
+	size_t num_func_data;
+	atomic_t in_use;
+	sector_t start;
+	sector_t length;
+	u8 lr;
+	u8 key_type;
+	u8 key_name[OPAL_KEY_MAX];
+	size_t key_name_len;
+	u8 key[OPAL_KEY_MAX];
+	size_t key_len;
+	u16 comID;
+	u32 HSN;
+	u32 TSN;
+	u64 align;
+	u64 lowest_lba;
+	struct list_head node;
+	char disk_name[DISK_NAME_LEN];
+	int state;
+
+	struct opal_cmd cmd;
+	struct parsed_resp parsed;
+
+	size_t prev_d_len;
+	void *prev_data;
+
+	sec_cb *final_cb;
+	void *final_cb_data;
+	opal_step error_cb;
+	void *error_cb_data;
+	opal_cb oper_cb;
+};
+
+LIST_HEAD(opal_list);
+DEFINE_SPINLOCK(list_spinlock);
+
+static void print_buffer(const u8 *ptr, u32 length)
+{
+#ifdef DEBUG
+	print_hex_dump_bytes("OPAL: ", DUMP_PREFIX_OFFSET, ptr, length);
+	pr_debug("\n");
+#endif
+}
+
+#define TPER_SYNC_SUPPORTED BIT(0)
+
+static bool check_tper(const void *data)
+{
+	const struct d0_tper_features *tper = data;
+	u8 flags = tper->supported_features;
+
+	if (!(flags & TPER_SYNC_SUPPORTED)) {
+		pr_err("TPer sync not supported. flags = %d\n",
+		       tper->supported_features);
+		return false;
+	}
+
+	return true;
+}
+
+static bool check_SUM(const void *data)
+{
+	const struct d0_single_user_mode *sum = data;
+	u32 nlo = be32_to_cpu(sum->num_locking_objects);
+
+	if (nlo == 0) {
+		pr_err("Need at least one locking object.\n");
+		return false;
+	}
+
+	pr_debug("Number of locking objects: %d\n", nlo);
+
+	return true;
+}
+
+static u16 get_comID_v100(const void *data)
+{
+	const struct d0_opal_v100 *v100 = data;
+
+	return be16_to_cpu(v100->baseComID);
+}
+
+static u16 get_comID_v200(const void *data)
+{
+	const struct d0_opal_v200 *v200 = data;
+
+	return be16_to_cpu(v200->baseComID);
+}
+
+static int __opal_send_cmd(struct opal_suspend_unlk *data, u16 comID,
+			   void *buffer, size_t buflen, sec_cb *cb,
+			   void *cb_data)
+{
+	return data->ops.send(data->data, comID, TCG_SECP_01, buffer, buflen,
+			     cb, cb_data);
+}
+static int _opal_send_cmd(struct block_device *bdev, u16 comID,
+			  void *buffer, size_t buflen,
+			  sec_cb *cb, void *cb_data)
+{
+	const struct sec_ops *ops = bdev->bd_disk->fops->sec_ops;
+
+	return ops->send(bdev->bd_disk->private_data, comID,
+			 TCG_SECP_01, buffer, buflen,
+			 cb, cb_data);
+}
+
+static int __opal_recv_cmd(struct opal_suspend_unlk *data, u16 comID,
+			   void *buffer, size_t buflen, sec_cb *cb,
+			   void *cb_data)
+{
+	return data->ops.recv(data->data, comID, TCG_SECP_01, buffer, buflen,
+			     cb, cb_data);
+}
+
+static int _opal_recv_cmd(struct block_device *bdev, u16 comID,
+			  void *buffer, size_t buflen,
+			  sec_cb *cb, void *cb_data)
+{
+	const struct sec_ops *ops = bdev->bd_disk->fops->sec_ops;
+
+	return ops->recv(bdev->bd_disk->private_data, comID,
+			 TCG_SECP_01, buffer, buflen,
+			 cb, cb_data);
+}
+
+static void opal_send_cb_cont(int error, void *data)
+{
+	struct opal_dev *dev = data;
+	size_t buflen = IO_BUFFER_LENGTH;
+	void *buffer = dev->cmd.resp;
+	struct opal_header *hdr = buffer;
+
+	pr_debug("%s: Sent OPAL command: error=%d, outstanding=%d, minTransfer=%d\n",
+	       dev->disk_name, error, hdr->cp.outstandingData,
+	       hdr->cp.minTransfer);
+
+	if (error || hdr->cp.outstandingData == 0 ||
+	    hdr->cp.minTransfer != 0) {
+		if (dev->cmd.cb)
+			dev->cmd.cb(error, dev->cmd.cb_data);
+		return;
+	}
+
+	memset(buffer, 0, buflen);
+	if (dev->resume_from_suspend)
+		__opal_recv_cmd(dev->resume_data, dev->comID,
+				buffer, buflen, opal_send_cb_cont, dev);
+	else
+		_opal_recv_cmd(dev->bdev, dev->comID, buffer, buflen,
+			       opal_send_cb_cont, dev);
+}
+
+static void opal_send_cb(int error, void *data)
+{
+	struct opal_dev *dev = data;
+	size_t buflen = IO_BUFFER_LENGTH;
+	void *buffer = dev->cmd.resp;
+
+	if (error) {
+		if (dev->cmd.cb)
+			dev->cmd.cb(error, dev->cmd.cb_data);
+		return;
+	}
+
+	memset(buffer, 0, buflen);
+	if (dev->resume_from_suspend)
+		__opal_recv_cmd(dev->resume_data, dev->comID,
+				buffer, buflen, opal_send_cb_cont, dev);
+	else
+		_opal_recv_cmd(dev->bdev, dev->comID, buffer, buflen,
+			       opal_send_cb_cont, dev);
+}
+
+static int opal_send_recv(struct opal_dev *dev, sec_cb *cb, void *cb_data)
+{
+	size_t buflen = IO_BUFFER_LENGTH;
+	void *buffer = dev->cmd.cmd;
+	int ret;
+
+	dev->cmd.cb = cb;
+	dev->cmd.cb_data = cb_data;
+	if (dev->resume_from_suspend)
+		ret = __opal_send_cmd(dev->resume_data, dev->comID, buffer,
+				      buflen, opal_send_cb, dev);
+	else
+		ret = _opal_send_cmd(dev->bdev, dev->comID, buffer, buflen,
+				     opal_send_cb, dev);
+
+	return ret;
+}
+
+static void check_geometry(struct opal_dev *dev, const void *data)
+{
+	const struct d0_geometry_features *geo = data;
+
+	dev->align = geo->alignment_granularity;
+	dev->lowest_lba = geo->lowest_aligned_lba;
+}
+
+static void opal_discovery0_end(int error, void *data)
+{
+	bool foundComID = false, supported = true, single_user = false;
+	struct opal_dev *dev = data;
+	const struct d0_header *hdr;
+	const u8 *epos, *cpos;
+	u16 comID = 0;
+
+	if (error) {
+		pr_err("%s: Sending discovery0 failed\n", dev->disk_name);
+		goto err_callback;
+	}
+
+	epos = dev->cmd.resp;
+	cpos = dev->cmd.resp;
+	hdr = (struct d0_header *)dev->cmd.resp;
+
+	print_buffer(dev->cmd.resp, be32_to_cpu(hdr->length));
+
+	epos += be32_to_cpu(hdr->length); /* end of buffer */
+	cpos += sizeof(*hdr); /* current position on buffer */
+
+	while (cpos < epos && supported) {
+		const struct d0_features *body =
+			(const struct d0_features *)cpos;
+
+		switch (be16_to_cpu(body->code)) {
+		case FC_TPER:
+			supported = check_tper(body->features);
+			break;
+		case FC_SINGLEUSER:
+			single_user = check_SUM(body->features);
+			break;
+		case FC_GEOMETRY:
+			check_geometry(dev, body);
+			break;
+		case FC_LOCKING:
+		case FC_ENTERPRISE:
+		case FC_DATASTORE:
+			/* some ignored properties */
+			pr_debug("%s: Found OPAL feature description: %d\n",
+				 dev->disk_name, be16_to_cpu(body->code));
+			break;
+		case FC_OPALV100:
+			comID = get_comID_v100(body->features);
+			foundComID = true;
+			break;
+		case FC_OPALV200:
+			comID = get_comID_v200(body->features);
+			foundComID = true;
+			break;
+		default:
+			if (be16_to_cpu(body->code) > 0xbfff) {
+				/* vendor specific, just ignore */
+			} else {
+				pr_warn("%s: OPAL Unknown feature: %d\n",
+					dev->disk_name, be16_to_cpu(body->code));
+			}
+		}
+		cpos += body->length + 4;
+	}
+
+	if (!supported) {
+		pr_err("%s: Device not supported\n", dev->disk_name);
+		goto err_callback;
+	}
+
+	if (!single_user)
+		pr_warn("%s: Device doesn't support single user mode\n",
+			dev->disk_name);
+
+	if (!foundComID) {
+		pr_warn("%s: Could not find OPAL comID for device. OPAL kernel unlocking will be disabled\n",
+			dev->disk_name);
+		goto err_callback;
+	}
+
+	dev->comID = comID;
+
+err_callback:
+	if (dev->oper_cb)
+		dev->oper_cb(error, dev);
+}
+
+static int opal_discovery0(struct opal_dev *dev)
+{
+	memset(dev->cmd.resp, 0, IO_BUFFER_LENGTH);
+
+	if (dev->resume_from_suspend)
+		return __opal_recv_cmd(dev->resume_data, 0x0001,
+				       dev->cmd.resp, IO_BUFFER_LENGTH,
+				       opal_discovery0_end, dev);
+
+	return _opal_recv_cmd(dev->bdev, 0x0001, dev->cmd.resp,
+			      IO_BUFFER_LENGTH, opal_discovery0_end,
+			      dev);
+}
+
+static void add_token_u8(struct opal_cmd *cmd, u8 tok)
+{
+	cmd->cmd[cmd->pos++] = tok;
+}
+
+static ssize_t test_and_add_token_u8(struct opal_cmd *cmd, u8 tok)
+{
+	BUILD_BUG_ON(IO_BUFFER_LENGTH >= SIZE_MAX);
+
+	if (cmd->pos >= IO_BUFFER_LENGTH - 1) {
+		pr_err("Error adding u8: end of buffer.\n");
+		return -ERANGE;
+	}
+
+	add_token_u8(cmd, tok);
+
+	return 1;
+}
+
+#define TINY_ATOM_DATA_MASK GENMASK(5, 0)
+#define TINY_ATOM_SIGNED BIT(6)
+
+#define SHORT_ATOM_ID BIT(7)
+#define SHORT_ATOM_BYTESTRING BIT(5)
+#define SHORT_ATOM_SIGNED BIT(4)
+#define SHORT_ATOM_LEN_MASK GENMASK(3, 0)
+
+static void add_short_atom_header(struct opal_cmd *cmd, bool bytestring,
+				  bool has_sign, int len)
+{
+	u8 atom;
+
+	atom = SHORT_ATOM_ID;
+	atom |= bytestring ? SHORT_ATOM_BYTESTRING : 0;
+	atom |= has_sign ? SHORT_ATOM_SIGNED : 0;
+	atom |= len & SHORT_ATOM_LEN_MASK;
+
+	add_token_u8(cmd, atom);
+}
+
+#define MEDIUM_ATOM_ID (BIT(7) | BIT(6))
+#define MEDIUM_ATOM_BYTESTRING BIT(4)
+#define MEDIUM_ATOM_SIGNED BIT(3)
+#define MEDIUM_ATOM_LEN_MASK GENMASK(2, 0)
+
+static void add_medium_atom_header(struct opal_cmd *cmd, bool bytestring,
+				   bool has_sign, int len)
+{
+	u8 header0;
+
+	header0 = MEDIUM_ATOM_ID;
+	header0 |= bytestring ? MEDIUM_ATOM_BYTESTRING : 0;
+	header0 |= has_sign ? MEDIUM_ATOM_SIGNED : 0;
+	header0 |= (len >> 8) & MEDIUM_ATOM_LEN_MASK;
+	cmd->cmd[cmd->pos++] = header0;
+	cmd->cmd[cmd->pos++] = len;
+}
+
+static void add_token_u64(struct opal_cmd *cmd, u64 number, size_t len)
+{
+	add_short_atom_header(cmd, false, false, len);
+
+	while (len--) {
+		u8 n = number >> (len * 8);
+
+		add_token_u8(cmd, n);
+	}
+}
+
+static ssize_t test_and_add_token_u64(struct opal_cmd *cmd, u64 number)
+{
+	int len;
+	int msb;
+
+	if (!(number & ~TINY_ATOM_DATA_MASK))
+		return test_and_add_token_u8(cmd, number);
+
+	msb = fls(number);
+	len = DIV_ROUND_UP(msb, 4);
+
+	if (cmd->pos >= IO_BUFFER_LENGTH - len - 1) {
+		pr_err("Error adding u64: end of buffer.\n");
+		return -ERANGE;
+	}
+
+	add_token_u64(cmd, number, len);
+
+	/* return length of token plus atom */
+	return len + 1;
+}
+
+static void add_token_bytestring(struct opal_cmd *cmd,
+				 const u8 *bytestring, size_t len)
+{
+	memcpy(&cmd->cmd[cmd->pos], bytestring, len);
+	cmd->pos += len;
+}
+
+static ssize_t test_and_add_token_bytestring(struct opal_cmd *cmd,
+					     const u8 *bytestring, size_t len)
+{
+	size_t header_len = 1;
+	bool is_short_atom = true;
+
+	if (len & ~SHORT_ATOM_LEN_MASK) {
+		header_len = 2;
+		is_short_atom = false;
+	}
+
+	if (cmd->pos >= IO_BUFFER_LENGTH - len - header_len) {
+		pr_err("Error adding bytestring: end of buffer.\n");
+		return -ERANGE;
+	}
+
+	if (is_short_atom)
+		add_short_atom_header(cmd, true, false, len);
+	else
+		add_medium_atom_header(cmd, true, false, len);
+
+	add_token_bytestring(cmd, bytestring, len);
+
+	return header_len + len;
+}
+
+#define LOCKING_RANGE_NON_GLOBAL 0x03
+
+static int build_locking_range(u8 *buffer, size_t length, u8 lr)
+{
+	if (length < OPAL_UID_LENGTH)
+		return -ERANGE;
+
+	memcpy(buffer, OPALUID[OPAL_LOCKINGRANGE_GLOBAL], OPAL_UID_LENGTH);
+
+	if (lr == 0)
+		return 0;
+	buffer[5] = LOCKING_RANGE_NON_GLOBAL;
+	buffer[7] = lr;
+
+	return 0;
+}
+
+static int build_locking_user(u8 *buffer, size_t length, u8 lr)
+{
+	if (length < OPAL_UID_LENGTH)
+		return -ERANGE;
+
+	memcpy(buffer, OPALUID[OPAL_USER1_UID], OPAL_UID_LENGTH);
+
+	buffer[7] = lr + 1;
+
+	return 0;
+}
+
+/*
+ * N = number of format specifiers (1-999) to be replicated
+ * c = u8
+ * u = u64
+ * s = bytestring, length
+ *
+ * ret = test_and_add_token_va(cmd, "c",
+ *			       u8_val1);
+ *
+ * ret = test_and_add_token_va(cmd, "2c2u",
+ *			       u8_val1, u8_val2, u64_val1, u64_val2);
+ *
+ * ret = test_and_add_token_va(cmd, "3s",
+ *			       bytestring1, length1,
+ *			       bytestring2, length2,
+ *			       bytestring3, length3);
+ */
+static int test_and_add_token_va(struct opal_cmd *cmd,
+				 const char *fmt, ...)
+{
+	const u8 *it = fmt, *tmp;
+	int ret, num = 1, sum = 0;
+	va_list ap;
+
+	va_start(ap, fmt);
+
+	while (*it != '\0') {
+		u64 tok64 = 0;
+		u8 tok, *bstr;
+		size_t len;
+
+		ret = 0;
+
+		switch (*it) {
+		case '1' ... '9':
+			tmp = it;
+			num = 0;
+			while (*tmp >= '0' && *tmp <= '9')
+				num = num * 10 + (*tmp++ - '0');
+			it = tmp;
+			continue;
+		case 'c':
+			while (num--) {
+				tok = va_arg(ap, unsigned int);
+				ret = test_and_add_token_u8(cmd, tok);
+				if (ret < 0)
+					goto err;
+			}
+			num = 1;
+			break;
+		case 'u':
+			while (num--) {
+				tok64 = va_arg(ap, u64);
+				ret = test_and_add_token_u64(cmd, tok64);
+				if (ret < 0)
+					goto err;
+			}
+			num = 1;
+			break;
+		case 's':
+			while (num--) {
+				bstr = va_arg(ap, u8 *);
+				len = va_arg(ap, size_t);
+				ret = test_and_add_token_bytestring(cmd, bstr,
+								    len);
+				if (ret < 0)
+					goto err;
+			}
+			num = 1;
+			break;
+		case ' ':
+		case '\t':
+			/* ignored */
+			break;
+		default:
+			pr_warn("Unrecognized type.\n");
+		}
+
+		it++;
+		sum += ret;
+	}
+
+	va_end(ap);
+
+	return sum;
+
+ err:
+	pr_err("Token failed to be added.\n");
+	return ret;
+}
+
+static void set_comID(struct opal_cmd *cmd, u16 comID)
+{
+	struct opal_header *hdr = (struct opal_header *)cmd->cmd;
+
+	hdr->cp.extendedComID[0] = comID >> 8;
+	hdr->cp.extendedComID[1] = comID;
+	hdr->cp.extendedComID[2] = 0;
+	hdr->cp.extendedComID[3] = 0;
+}
+
+static int cmd_finalize(struct opal_cmd *cmd, u32 hsn, u32 tsn)
+{
+	struct opal_header *hdr;
+	int ret;
+
+	ret = test_and_add_token_va(cmd, "6c",
+				    OPAL_ENDOFDATA, OPAL_STARTLIST,
+				    0, 0, 0, OPAL_ENDLIST);
+
+	if (ret < 0) {
+		pr_err("Error finalizing command.\n");
+		return -EFAULT;
+	}
+
+	hdr = (struct opal_header *) cmd->cmd;
+
+	hdr->pkt.TSN = cpu_to_be32(tsn);
+	hdr->pkt.HSN = cpu_to_be32(hsn);
+
+	hdr->subpkt.length = cpu_to_be32(cmd->pos - sizeof(*hdr));
+	while (cmd->pos % 4) {
+		if (cmd->pos >= IO_BUFFER_LENGTH) {
+			pr_err("Error: Buffer overrun\n");
+			return -ERANGE;
+		}
+		cmd->cmd[cmd->pos++] = 0;
+	}
+	hdr->pkt.length = cpu_to_be32(cmd->pos - sizeof(hdr->cp) -
+				      sizeof(hdr->pkt));
+	hdr->cp.length = cpu_to_be32(cmd->pos - sizeof(hdr->cp));
+
+	return 0;
+}
+
+static enum OPAL_RESPONSE_TOKEN token_type(const struct parsed_resp *resp,
+					   int n)
+{
+	const struct opal_resp_tok *tok;
+
+	if (n >= resp->num) {
+		pr_err("Token number doesn't exist: %d, resp: %d\n",
+		       n, resp->num);
+		return OPAL_DTA_TOKENID_INVALID;
+	}
+
+	tok = &resp->toks[n];
+	if (tok->len == 0) {
+		pr_err("Token length must be non-zero\n");
+		return OPAL_DTA_TOKENID_INVALID;
+	}
+
+	return tok->type;
+}
+
+/*
+ * This function returns 0 in case of invalid token. One should call
+ * token_type() first to find out if the token is valid or not.
+ */
+static enum OPAL_TOKEN response_get_token(const struct parsed_resp *resp,
+					  int n)
+{
+	const struct opal_resp_tok *tok;
+
+	if (n >= resp->num) {
+		pr_err("Token number doesn't exist: %d, resp: %d\n",
+		       n, resp->num);
+		return 0;
+	}
+
+	tok = &resp->toks[n];
+	if (tok->len == 0) {
+		pr_err("Token length must be non-zero\n");
+		return 0;
+	}
+
+	return tok->pos[0];
+}
+
+static size_t response_parse_tiny(struct opal_resp_tok *tok,
+				  const u8 *pos)
+{
+	tok->pos = pos;
+	tok->len = 1;
+	tok->width = OPAL_WIDTH_TINY;
+
+	if (pos[0] & TINY_ATOM_SIGNED) {
+		tok->type = OPAL_DTA_TOKENID_SINT;
+	} else {
+		tok->type = OPAL_DTA_TOKENID_UINT;
+		tok->stored.u = pos[0] & 0x3f;
+	}
+
+	return tok->len;
+}
+
+static size_t response_parse_short(struct opal_resp_tok *tok,
+				   const u8 *pos)
+{
+	tok->pos = pos;
+	tok->len = (pos[0] & SHORT_ATOM_LEN_MASK) + 1;
+	tok->width = OPAL_WIDTH_SHORT;
+
+	if (pos[0] & SHORT_ATOM_BYTESTRING) {
+		tok->type = OPAL_DTA_TOKENID_BYTESTRING;
+	} else if (pos[0] & SHORT_ATOM_SIGNED) {
+		tok->type = OPAL_DTA_TOKENID_SINT;
+	} else {
+		u64 u_integer = 0;
+		int i, b = 0;
+
+		tok->type = OPAL_DTA_TOKENID_UINT;
+		if (tok->len > 9)
+			pr_warn("uint64 with more than 8 bytes\n");
+		for (i = tok->len - 1; i > 0; i--) {
+			u_integer |= ((u64)pos[i] << (8 * b));
+			b++;
+		}
+		tok->stored.u = u_integer;
+	}
+
+	return tok->len;
+}
+
+static size_t response_parse_medium(struct opal_resp_tok *tok,
+				    const u8 *pos)
+{
+	tok->pos = pos;
+	tok->len = (((pos[0] & MEDIUM_ATOM_LEN_MASK) << 8) | pos[1]) + 2;
+	tok->width = OPAL_WIDTH_MEDIUM;
+
+	if (pos[0] & MEDIUM_ATOM_BYTESTRING)
+		tok->type = OPAL_DTA_TOKENID_BYTESTRING;
+	else if (pos[0] & MEDIUM_ATOM_SIGNED)
+		tok->type = OPAL_DTA_TOKENID_SINT;
+	else
+		tok->type = OPAL_DTA_TOKENID_UINT;
+
+	return tok->len;
+}
+
+#define LONG_ATOM_ID (BIT(7) | BIT(6) | BIT(5))
+#define LONG_ATOM_BYTESTRING BIT(1)
+#define LONG_ATOM_SIGNED BIT(0)
+static size_t response_parse_long(struct opal_resp_tok *tok,
+				  const u8 *pos)
+{
+	tok->pos = pos;
+	tok->len = ((pos[1] << 16) | (pos[2] << 8) | pos[3]) + 4;
+	tok->width = OPAL_WIDTH_LONG;
+
+	if (pos[0] & LONG_ATOM_BYTESTRING)
+		tok->type = OPAL_DTA_TOKENID_BYTESTRING;
+	else if (pos[0] & LONG_ATOM_SIGNED)
+		tok->type = OPAL_DTA_TOKENID_SINT;
+	else
+		tok->type = OPAL_DTA_TOKENID_UINT;
+
+	return tok->len;
+}
+
+static size_t response_parse_token(struct opal_resp_tok *tok,
+				   const u8 *pos)
+{
+	tok->pos = pos;
+	tok->len = 1;
+	tok->type = OPAL_DTA_TOKENID_TOKEN;
+	tok->width = OPAL_WIDTH_TOKEN;
+
+	return tok->len;
+}
+
+static int response_parse(const u8 *buf, size_t length,
+			  struct parsed_resp *resp)
+{
+	const struct opal_header *hdr;
+	struct opal_resp_tok *iter;
+	int ret, num_entries = 0;
+	u32 cpos = 0, total;
+	size_t token_length;
+	const u8 *pos;
+
+	if (!buf)
+		return -EFAULT;
+
+	if (!resp)
+		return -EFAULT;
+
+	hdr = (struct opal_header *)buf;
+	pos = buf;
+	pos += sizeof(*hdr);
+
+	pr_debug("Response size: cp: %d, pkt: %d, subpkt: %d\n",
+		 be32_to_cpu(hdr->cp.length),
+		 be32_to_cpu(hdr->pkt.length),
+		 be32_to_cpu(hdr->subpkt.length));
+
+	if ((hdr->cp.length == 0)
+	    || (hdr->pkt.length == 0)
+	    || (hdr->subpkt.length == 0)) {
+		pr_err("Bad header length. cp: %d, pkt: %d, subpkt: %d\n",
+		       be32_to_cpu(hdr->cp.length),
+		       be32_to_cpu(hdr->pkt.length),
+		       be32_to_cpu(hdr->subpkt.length));
+		print_buffer(pos, sizeof(*hdr));
+		ret = -EINVAL;
+		goto err;
+	}
+
+	if (pos > buf + length) {
+		ret = -EFAULT;
+		goto err;
+	}
+
+	iter = resp->toks;
+	total = be32_to_cpu(hdr->subpkt.length);
+	print_buffer(pos, total);
+	while (cpos < total) {
+		if (!(pos[0] & 0x80)) /* tiny atom */
+			token_length = response_parse_tiny(iter, pos);
+		else if (!(pos[0] & 0x40)) /* short atom */
+			token_length = response_parse_short(iter, pos);
+		else if (!(pos[0] & 0x20)) /* medium atom */
+			token_length = response_parse_medium(iter, pos);
+		else if (!(pos[0] & 0x10)) /* long atom */
+			token_length = response_parse_long(iter, pos);
+		else /* TOKEN */
+			token_length = response_parse_token(iter, pos);
+
+		pos += token_length;
+		cpos += token_length;
+		iter++;
+		num_entries++;
+	}
+
+	if (num_entries == 0) {
+		pr_err("Couldn't parse response.\n");
+		ret = -EINVAL;
+		goto err;
+	}
+	resp->num = num_entries;
+
+	return 0;
+err:
+	return ret;
+}
+
+static size_t response_get_string(const struct parsed_resp *resp, int n,
+				  const char **store)
+{
+	*store = NULL;
+	if (!resp) {
+		pr_err("Response is NULL\n");
+		return 0;
+	}
+
+	if (n > resp->num) {
+		pr_err("Response has %d tokens. Can't access %d\n",
+		       resp->num, n);
+		return 0;
+	}
+
+	if (resp->toks[n].type != OPAL_DTA_TOKENID_BYTESTRING) {
+		pr_err("Token is not a byte string!\n");
+		return 0;
+	}
+
+	*store = resp->toks[n].pos + 1;
+	return resp->toks[n].len - 1;
+}
+
+static u64 response_get_u64(const struct parsed_resp *resp, int n)
+{
+	if (!resp) {
+		pr_err("Response is NULL\n");
+		return 0;
+	}
+
+	if (n > resp->num) {
+		pr_err("Response has %d tokens. Can't access %d\n",
+		       resp->num, n);
+		return 0;
+	}
+
+	if (resp->toks[n].type != OPAL_DTA_TOKENID_UINT) {
+		pr_err("Token is not unsigned it: %d\n",
+		       resp->toks[n].type);
+		return 0;
+	}
+
+	if (!((resp->toks[n].width == OPAL_WIDTH_TINY) ||
+	      (resp->toks[n].width == OPAL_WIDTH_SHORT))) {
+		pr_err("Atom is not short or tiny: %d\n",
+		       resp->toks[n].width);
+		return 0;
+	}
+
+	return resp->toks[n].stored.u;
+}
+
+static u8 response_status(const struct parsed_resp *resp)
+{
+	if ((token_type(resp, 0) == OPAL_DTA_TOKENID_TOKEN)
+	    && (response_get_token(resp, 0) == OPAL_ENDOFSESSION)) {
+		return 0;
+	}
+
+	if (resp->num < 5)
+		return DTAERROR_NO_METHOD_STATUS;
+
+	if ((token_type(resp, resp->num - 1) != OPAL_DTA_TOKENID_TOKEN) ||
+	    (token_type(resp, resp->num - 5) != OPAL_DTA_TOKENID_TOKEN) ||
+	    (response_get_token(resp, resp->num - 1) != OPAL_ENDLIST) ||
+	    (response_get_token(resp, resp->num - 5) != OPAL_STARTLIST))
+		return DTAERROR_NO_METHOD_STATUS;
+
+	return response_get_u64(resp, resp->num - 4);
+}
+
+/* Parses and checks for errors */
+static int parse_and_check_status(struct opal_dev *dev)
+{
+	struct opal_cmd *cmd;
+	int error;
+
+	cmd = &dev->cmd;
+	print_buffer(cmd->cmd, cmd->pos);
+
+	error = response_parse(cmd->resp, IO_BUFFER_LENGTH, &dev->parsed);
+	if (error) {
+		pr_err("%s: Couldn't parse response.\n", dev->disk_name);
+		goto err_return;
+	}
+
+	error = response_status(&dev->parsed);
+	if (error)
+		pr_err("%s: Response Status: %d\n", dev->disk_name,
+		       error);
+
+ err_return:
+	return error;
+}
+
+static void clear_opal_cmd(struct opal_cmd *cmd)
+{
+	cmd->pos = sizeof(struct opal_header);
+	memset(cmd->cmd, 0, IO_BUFFER_LENGTH);
+	cmd->cb = NULL;
+	cmd->cb_data = NULL;
+}
+
+static void start_opal_session_cont(int error, void *data)
+{
+	struct opal_dev *dev = data;
+	u32 HSN, TSN;
+
+	if (error)
+		goto err_return;
+
+	error = parse_and_check_status(dev);
+	if (error)
+		goto err_return;
+
+	HSN = response_get_u64(&dev->parsed, 4);
+	TSN = response_get_u64(&dev->parsed, 5);
+
+	if (HSN == 0 && TSN == 0) {
+		pr_err("%s: Couldn't authenticate session\n", dev->disk_name);
+		error = -EPERM;
+		goto err_return;
+	}
+
+	dev->HSN = HSN;
+	dev->TSN = TSN;
+
+err_return:
+	if (dev->oper_cb)
+		dev->oper_cb(error, dev);
+}
+
+static int get_opal_key(struct opal_dev *dev)
+{
+	struct key *ukey = NULL;
+	const u8 *tmpkey = NULL;
+	size_t tmplen;
+	int ret = 0;
+
+	if (dev->key_type == OPAL_KEY_PLAIN) {
+		tmpkey = dev->key_name;
+		tmplen = dev->key_name_len;
+	} else if (dev->key_type == OPAL_KEY_KEYRING) {
+		ukey = request_user_key(dev->key_name, &tmpkey, &tmplen);
+		if (IS_ERR(ukey)) {
+			pr_err("%s: Can't retrieve key: %ld\n", dev->disk_name,
+			       PTR_ERR(ukey));
+			return PTR_ERR(ukey);
+		}
+	} else {
+		pr_err("Requested invalid key type: %d\n", dev->key_type);
+		return -EINVAL;
+	}
+
+	if (tmplen > OPAL_KEY_MAX) {
+		pr_err("Requested key with invalid size: %zd\n", tmplen);
+		ret = -EINVAL;
+		goto err_exit;
+	}
+
+	dev->key_len = tmplen;
+	if (!memcpy(dev->key, tmpkey, tmplen)) {
+		pr_err("Error when copying key");
+		ret = -EFAULT;
+		goto err_exit;
+	}
+
+err_exit:
+	key_put(ukey);
+
+	return 0;
+}
+
+static void clean_opal_key(struct opal_dev *dev)
+{
+	memset(dev->key, 0, OPAL_KEY_MAX);
+	dev->key_len = 0;
+}
+
+static inline void clean_function_data(struct opal_dev *dev)
+{
+		dev->func_data = NULL;
+		dev->num_func_data = 0;
+}
+
+/* This is a generic continue fn.
+ * We use this when we don't care about the response data
+ * and simply want to check the status and continue.
+ */
+static void generic_cont(int error, void *data)
+{
+	struct opal_dev *dev = data;
+
+	if (error)
+		goto err_return;
+
+	error = parse_and_check_status(dev);
+
+ err_return:
+	if (dev->oper_cb)
+		dev->oper_cb(error, dev);
+}
+
+static void end_session_cont(int error, void *data)
+{
+	struct opal_dev *dev = data;
+
+	dev->HSN = 0;
+	dev->TSN = 0;
+	generic_cont(error, data);
+}
+
+static int finalize_and_send(struct opal_dev *dev, struct opal_cmd *cmd,
+			     sec_cb cont)
+{
+	int ret;
+
+	ret = cmd_finalize(cmd, dev->HSN, dev->TSN);
+	if (ret) {
+		pr_err("%s: Error finalizing command buffer: %d\n",
+		       dev->disk_name, ret);
+		return ret;
+	}
+
+	print_buffer(cmd->cmd, cmd->pos);
+
+	ret = opal_send_recv(dev, cont, dev);
+	if (ret)
+		pr_err("%s: Error running command: %d\n",
+		       dev->disk_name, ret);
+
+	return ret;
+}
+
+static int wait_for_cmd_completion(struct opal_completion *completion)
+{
+	wait_for_completion_interruptible(&completion->cmd_completion);
+	return completion->completion_status;
+}
+
+static int gen_key(struct opal_dev *dev)
+{
+	const u8 *method;
+	u8 uid[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	int ret;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	memcpy(uid, dev->prev_data, min(sizeof(uid), dev->prev_d_len));
+	method = OPALMETHOD[OPAL_GENKEY];
+	kfree(dev->prev_data);
+	dev->prev_data = NULL;
+
+	ret = test_and_add_token_va(cmd, "c2s 2c",
+				    OPAL_CALL,
+				    uid, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+
+				    OPAL_STARTLIST,
+				    OPAL_ENDLIST);
+	if (ret < 0) {
+		pr_err("%s: Error building gen key command\n",
+		       dev->disk_name);
+		return ret;
+	}
+
+	return finalize_and_send(dev, cmd, generic_cont);
+}
+
+static void get_active_key_cont(int error, void *data)
+{
+	struct opal_dev *dev = data;
+	const char *activekey;
+	size_t keylen;
+
+	if (error)
+		goto err_return;
+
+	error = parse_and_check_status(dev);
+	if (error)
+		goto err_return;
+	keylen = response_get_string(&dev->parsed, 4, &activekey);
+	if (!activekey) {
+		pr_err("%s: Couldn't extract the Activekey from the response\n",
+		       __func__);
+		error = 0x0A;
+		goto err_return;
+	}
+	dev->prev_data = kmemdup(activekey, keylen, GFP_KERNEL);
+
+	if (!dev->prev_data)
+		error = -ENOMEM;
+
+	dev->prev_d_len = keylen;
+
+err_return:
+	if (dev->oper_cb)
+		dev->oper_cb(error, dev);
+}
+
+static int get_active_key(struct opal_dev *dev)
+{
+	const u8 *method;
+	u8 uid[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	int ret;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	method = OPALMETHOD[OPAL_GET];
+
+	ret = build_locking_range(uid, sizeof(uid), dev->lr);
+	if (ret < 0) {
+		pr_err("%s: Can't build locking range\n", dev->disk_name);
+		return -EINVAL;
+	}
+
+	ret = test_and_add_token_va(cmd, "c2s 6c 4c 2c",
+				    OPAL_CALL,
+				    uid, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+
+				    OPAL_STARTLIST,
+				    OPAL_STARTLIST,
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_03, /* startCloumn */
+				    OPAL_TINY_UINT_10, /* ActiveKey */
+				    OPAL_ENDNAME,
+
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_04, /* endColumn */
+				    OPAL_TINY_UINT_10, /* ActiveKey */
+				    OPAL_ENDNAME,
+
+				    OPAL_ENDLIST,
+				    OPAL_ENDLIST);
+	if (ret < 0) {
+		pr_err("%s: Error building get active key command\n",
+		       dev->disk_name);
+		return ret;
+	}
+
+	return finalize_and_send(dev, cmd, get_active_key_cont);
+}
+
+static int setup_locking_range(struct opal_dev *dev)
+{
+	const u8 *method;
+	u8 uid[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	struct opal_user_lr_setup *setup;
+	int ret;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	method = OPALMETHOD[OPAL_SET];
+	ret = build_locking_range(uid, sizeof(uid), dev->lr);
+	if (ret < 0) {
+		pr_err("%s: Can't build locking range\n", dev->disk_name);
+		return -EINVAL;
+	}
+	setup = dev->func_data[dev->state - 1];
+
+	ret = test_and_add_token_va(cmd, "c2s  4c 2cuc 2cuc 2cuc 2cu 4c",
+				    OPAL_CALL,
+				    uid, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+
+				    OPAL_STARTLIST,
+				    OPAL_STARTNAME,
+				    OPAL_VALUES,
+				    OPAL_STARTLIST,
+
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_03, /* Range Start */
+				    setup->range_start,
+				    OPAL_ENDNAME,
+
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_04, /* Range Length */
+				    setup->range_length,
+				    OPAL_ENDNAME,
+
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_05, /* ReadLockEnabled */
+				    !!setup->RLE,
+				    OPAL_ENDNAME,
+
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_06, /* WriteLockEnabled */
+				    !!setup->WLE,
+
+				    OPAL_ENDNAME,
+				    OPAL_ENDLIST,
+				    OPAL_ENDNAME,
+				    OPAL_ENDLIST);
+	if (ret < 0) {
+		pr_err("%s: Error building Setup Locking range command.\n",
+		       dev->disk_name);
+		return ret;
+
+	}
+
+	return finalize_and_send(dev, cmd, generic_cont);
+}
+
+static int start_adminsp_opal_session(struct opal_dev *dev,
+				      enum OPAL_UID auth,
+				      const char *key,
+				      u8 key_len)
+{
+	const u8 *method, *smuid, *admin_sp, *hsa;
+	struct opal_cmd *cmd;
+	u32 HSN;
+	int ret;
+
+	if (key == NULL && auth != OPAL_ANYBODY_UID) {
+		pr_err("%s: Attempted to open ADMIN_SP Session without a Host" \
+		       "Challenge, and not as the Anybody UID\n", __func__);
+		return 1;
+	}
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+
+	set_comID(cmd, dev->comID);
+	HSN = 0x41;
+
+	smuid = OPALUID[OPAL_SMUID_UID];
+	method = OPALMETHOD[OPAL_STARTSESSION];
+	admin_sp = OPALUID[OPAL_ADMINSP_UID];
+
+	ret = test_and_add_token_va(cmd, "c2s cusc",
+				    OPAL_CALL,
+				    smuid, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+				    OPAL_STARTLIST,
+				    HSN,
+				    admin_sp, OPAL_UID_LENGTH,
+				    OPAL_TINY_UINT_01);
+	if (ret < 0) {
+		pr_err("%s: Error building start adminsp session command.\n",
+		       dev->disk_name);
+		return ret;
+	}
+
+	switch (auth) {
+	case OPAL_ANYBODY_UID:
+		/* nothing left to do for anybody, just end and finalize */
+		ret = test_and_add_token_va(cmd, "c",
+					    OPAL_ENDLIST);
+		break;
+	case OPAL_SID_UID:
+		hsa = OPALUID[OPAL_SID_UID];
+		ret = test_and_add_token_va(cmd, "2c s 3c s 2c",
+					    OPAL_STARTNAME,
+					    OPAL_TINY_UINT_00, /* HostChallenge */
+					    key, key_len,
+					    OPAL_ENDNAME,
+					    OPAL_STARTNAME,
+					    OPAL_TINY_UINT_03, /* HostSignAuth */
+					    hsa, OPAL_UID_LENGTH,
+					    OPAL_ENDNAME,
+					    OPAL_ENDLIST);
+		break;
+	default:
+		pr_err("Cannot start Admin SP session with auth %d\n", auth);
+		return 1;
+	}
+
+	if (ret < 0) {
+		pr_err("%s: Error building start adminsp session command.\n",
+		       dev->disk_name);
+		return ret;
+	}
+
+	return finalize_and_send(dev, cmd, start_opal_session_cont);
+}
+
+static int start_anybodyASP_opal_session(struct opal_dev *dev)
+{
+	return start_adminsp_opal_session(dev, OPAL_ANYBODY_UID, NULL, 0);
+}
+
+static int start_SIDASP_opal_session(struct opal_dev *dev)
+{
+	int ret;
+	const u8 *key = dev->prev_data;
+
+	if (!key)
+		ret = start_adminsp_opal_session(dev, OPAL_SID_UID, dev->key,
+						 dev->key_len);
+	else {
+		ret = start_adminsp_opal_session(dev, OPAL_SID_UID, key,
+						 dev->prev_d_len);
+		kfree(key);
+		dev->prev_data = NULL;
+	}
+	return ret;
+}
+
+static int start_lockingsp_opal_session(struct opal_dev *dev,
+					enum OPAL_UID auth, const u8 *key,
+					u8 key_len)
+{
+
+	const u8 *method, *smuid, *locking_sp, *hsa;
+	struct opal_cmd *cmd;
+	size_t klen = key_len;
+	u32 HSN;
+	int ret;
+
+	if (key == NULL) {
+		pr_err("Cannot start Locking SP session without a key\n");
+		return -EINVAL;
+	}
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+
+	set_comID(cmd, dev->comID);
+	HSN = 0x41;
+
+	smuid = OPALUID[OPAL_SMUID_UID];
+	method = OPALMETHOD[OPAL_STARTSESSION];
+	locking_sp = OPALUID[OPAL_LOCKINGSP_UID];
+	hsa = OPALUID[auth];
+
+	ret = test_and_add_token_va(cmd, "c2s cusc 2csc 2csc c",
+				    OPAL_CALL,
+				    smuid, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+
+				    OPAL_STARTLIST,
+				    HSN,
+				    locking_sp, OPAL_UID_LENGTH,
+				    OPAL_TINY_UINT_01,
+
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_00, /* HostChallenge */
+				    key, klen,
+				    OPAL_ENDNAME,
+
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_03, /* Host Sign Authority */
+				    hsa, OPAL_UID_LENGTH,
+				    OPAL_ENDNAME,
+
+				    OPAL_ENDLIST);
+
+	if (ret < 0) {
+		pr_err("%s: Error building start adminsp session command.\n",
+		       dev->disk_name);
+		return ret;
+	}
+	return finalize_and_send(dev, cmd, start_opal_session_cont);
+}
+
+static inline int start_admin1LSP_opal_session(struct opal_dev *dev)
+{
+	return start_lockingsp_opal_session(dev, OPAL_ADMIN1_UID,
+					    dev->key, dev->key_len);
+}
+
+static int start_auth_opal_session(struct opal_dev *dev)
+{
+	const u8 *method, *smuid, *locking_sp;
+	u8 lk_ul_user[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	u32 HSN;
+	int ret;
+	struct opal_user_info *uinfo;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+
+	set_comID(cmd, dev->comID);
+
+	HSN = 0x41;
+
+	uinfo = dev->func_data[dev->state - 1];
+
+	smuid = OPALUID[OPAL_SMUID_UID];
+	method = OPALMETHOD[OPAL_STARTSESSION];
+	locking_sp = OPALUID[OPAL_LOCKINGSP_UID];
+
+	if (uinfo->SUM) {
+		ret = build_locking_user(lk_ul_user, sizeof(lk_ul_user),
+					 dev->lr);
+		if (ret < 0) {
+			pr_err("%s: Can't build locking user\n",
+			       dev->disk_name);
+			return ret;
+		}
+	} else if (uinfo->who != OPAL_ADMIN1 && !uinfo->SUM) {
+		ret = build_locking_user(lk_ul_user, sizeof(lk_ul_user),
+					 uinfo->who - 1);
+		if (ret < 0) {
+			pr_err("%s: Can't build locking user\n",
+			       dev->disk_name);
+			return ret;
+		}
+	} else
+		memcpy(lk_ul_user, OPALUID[OPAL_ADMIN1_UID], OPAL_UID_LENGTH);
+
+
+	ret = test_and_add_token_va(cmd, "c2s cus3cs3c s 2c",
+				    OPAL_CALL,
+				    smuid, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+
+				    OPAL_STARTLIST,
+				    HSN,
+				    locking_sp, OPAL_UID_LENGTH,
+				    OPAL_TINY_UINT_01,
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_00,
+				    dev->key, dev->key_len,
+				    OPAL_ENDNAME,
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_03,
+
+				    lk_ul_user, OPAL_UID_LENGTH,
+
+				    OPAL_ENDNAME,
+				    OPAL_ENDLIST);
+
+	if (ret < 0) {
+		pr_err("%s: Error building STARTSESSION command.\n",
+		       dev->disk_name);
+		return ret;
+	}
+
+	return finalize_and_send(dev, cmd, start_opal_session_cont);
+}
+
+static int revert_tper(struct opal_dev *dev)
+{
+	const u8 *method, *smuid;
+	struct opal_cmd *cmd;
+	int ret;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+
+	set_comID(cmd, dev->comID);
+
+	smuid = OPALUID[OPAL_ADMINSP_UID];
+	method = OPALMETHOD[OPAL_REVERT];
+
+	ret = test_and_add_token_va(cmd, "c2s 2c",
+				    OPAL_CALL,
+				    smuid, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+				    OPAL_STARTLIST,
+				    OPAL_ENDLIST);
+	if (ret < 0) {
+		pr_err("%s: Error building REVERT TPER command.\n",
+		       dev->disk_name);
+		return ret;
+	}
+
+	return finalize_and_send(dev, cmd, generic_cont);
+}
+
+static int internal_activate_user(struct opal_dev *dev)
+{
+	const u8 *method;
+	u8 uid[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	int ret;
+	struct opal_activate_user *act;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	act = dev->func_data[dev->state - 1];
+
+	memcpy(uid, OPALUID[OPAL_USER1_UID], OPAL_UID_LENGTH);
+	uid[7] = act->who.who;
+
+	method = OPALMETHOD[OPAL_SET];
+
+	ret = test_and_add_token_va(cmd, "c2s 3c c 4c 3c",
+				    OPAL_CALL,
+				    uid, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+
+				    OPAL_STARTLIST,
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_01, /* Values */
+
+				    OPAL_STARTLIST,
+
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_05, /* Enabled */
+				    OPAL_TINY_UINT_01, /* True */
+				    OPAL_ENDNAME,
+
+				    OPAL_ENDLIST,
+				    OPAL_ENDNAME,
+				    OPAL_ENDLIST);
+
+	if (ret < 0) {
+		pr_err("%s: Error building Activate UserN command.\n",
+		       dev->disk_name);
+		return ret;
+	}
+
+	return finalize_and_send(dev, cmd, generic_cont);
+}
+
+static int erase_locking_range(struct opal_dev *dev)
+{
+	const u8 *method;
+	u8 uid[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	int ret;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	method = OPALMETHOD[OPAL_ERASE];
+
+	if (build_locking_range(uid, sizeof(uid), dev->lr) < 0) {
+		pr_err("%s: Can't build locking range\n", dev->disk_name);
+		return -EINVAL;
+	}
+
+	ret = test_and_add_token_va(cmd, "c2s 2c",
+				    OPAL_CALL,
+				    uid, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+
+				    OPAL_STARTLIST,
+				    OPAL_ENDLIST);
+
+	if (ret < 0) {
+		pr_err("%s: Error building Erase Locking Range Cmmand.\n",
+		       dev->disk_name);
+		return ret;
+	}
+	return finalize_and_send(dev, cmd, generic_cont);
+}
+
+static int set_mbr_done(struct opal_dev *dev)
+{
+	const u8 *method, *uid;
+	struct opal_cmd *cmd;
+	int ret;
+
+	u8 mbr_done_tf = *(u8 *)dev->func_data[dev->state - 1];
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	method = OPALMETHOD[OPAL_SET];
+	uid = OPALUID[OPAL_MBRCONTROL];
+
+	ret = test_and_add_token_va(cmd, "c2s 3c 6c 2c",
+				    OPAL_CALL,
+				    uid, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+
+				    OPAL_STARTLIST,
+				    OPAL_STARTNAME,
+				    OPAL_VALUES,
+
+				    OPAL_STARTLIST,
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_02, /* Done */
+				    mbr_done_tf,       /* Done T or F */
+				    OPAL_ENDNAME,
+				    OPAL_ENDLIST,
+
+				    OPAL_ENDNAME,
+				    OPAL_ENDLIST);
+	if (ret < 0) {
+		pr_err("%s: Error Building set MBR Dont/Not done command\n",
+		       dev->disk_name);
+		return ret;
+	}
+
+	return finalize_and_send(dev, cmd, generic_cont);
+}
+
+static int set_mbr_enable_disable(struct opal_dev *dev)
+{
+	const u8 *method, *uid;
+	struct opal_cmd *cmd;
+	int ret;
+
+	u8 mbr_en_dis = *(u8 *)dev->func_data[dev->state - 1];
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	method = OPALMETHOD[OPAL_SET];
+	uid = OPALUID[OPAL_MBRCONTROL];
+
+	ret = test_and_add_token_va(cmd, "c2s 3c 6c 2c",
+				    OPAL_CALL,
+				    uid, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+
+				    OPAL_STARTLIST,
+				    OPAL_STARTNAME,
+				    OPAL_VALUES,
+
+				    OPAL_STARTLIST,
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_01, /* Enable */
+				    mbr_en_dis,        /* Enable or Disable */
+				    OPAL_ENDNAME,
+				    OPAL_ENDLIST,
+
+				    OPAL_ENDNAME,
+				    OPAL_ENDLIST);
+	if (ret < 0) {
+		pr_err("%s: Error Building set MBR Dont/Not done command\n",
+		       dev->disk_name);
+		return ret;
+	}
+
+	return finalize_and_send(dev, cmd, generic_cont);
+}
+
+static int set_new_pw(struct opal_dev *dev)
+{
+	const u8 *method;
+	u8 cpin_uid[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	int ret;
+	struct opal_new_pw *pw;
+	size_t key_len;
+	u8 *key;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	pw = dev->func_data[dev->state - 1];
+	key = pw->new_pin.key;
+	key_len = pw->new_pin.key_len;
+	memcpy(cpin_uid, OPALUID[OPAL_C_PIN_ADMIN1], OPAL_UID_LENGTH);
+
+	if (pw->user_for_pw != OPAL_ADMIN1) {
+		cpin_uid[5] = 0x03;
+		if (pw->who.SUM)
+			cpin_uid[7] = pw->new_pin.lr + 1;
+		else
+			cpin_uid[7] = pw->user_for_pw;
+	}
+
+	method = OPALMETHOD[OPAL_SET];
+
+	ret = test_and_add_token_va(cmd, "c2s 3c 3cs2c 2c",
+				    OPAL_CALL,
+				    cpin_uid, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+
+				    OPAL_STARTLIST,
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_01, /* Values */
+
+				    OPAL_STARTLIST,
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_03, /* PIN */
+				    key, key_len,
+				    OPAL_ENDNAME,
+				    OPAL_ENDLIST,
+
+				    OPAL_ENDNAME,
+				    OPAL_ENDLIST);
+
+	if (ret < 0) {
+		pr_err("%s: Error building SET AMIN1 PIN command.\n",
+		       dev->disk_name);
+		return ret;
+	}
+
+	return finalize_and_send(dev, cmd, generic_cont);
+}
+
+static int set_sid_cpin_pin(struct opal_dev *dev)
+{
+	const u8 *method, *cpin_uid;
+	struct opal_cmd *cmd;
+	int ret;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	cpin_uid = OPALUID[OPAL_C_PIN_SID];
+	method = OPALMETHOD[OPAL_SET];
+
+	ret = test_and_add_token_va(cmd, "c2s 2c 4cs2c 2c",
+				    OPAL_CALL,
+				    cpin_uid, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+
+				    OPAL_STARTLIST,
+				    OPAL_STARTNAME,
+
+				    OPAL_TINY_UINT_01, /* Values */
+				    OPAL_STARTLIST,
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_03, /* PIN */
+				    dev->key, dev->key_len,
+				    OPAL_ENDNAME,
+				    OPAL_ENDLIST,
+
+				    OPAL_ENDNAME,
+				    OPAL_ENDLIST);
+
+	if (ret < 0) {
+		pr_err("%s: Error building SET CPIN PIN command.\n",
+		       dev->disk_name);
+		return ret;
+	}
+
+	return finalize_and_send(dev, cmd, generic_cont);
+}
+
+static void query_locking_range_cont(int error, void *data)
+{
+	struct opal_dev *dev = data;
+
+	if (error)
+		goto err_return;
+
+	error = parse_and_check_status(dev);
+	if (error)
+		goto err_return;
+
+	dev->start = response_get_u64(&dev->parsed, 4);
+	dev->length = response_get_u64(&dev->parsed, 8);
+
+err_return:
+	if (dev->oper_cb)
+		dev->oper_cb(error, dev);
+}
+
+static int query_locking_range(struct opal_dev *dev)
+{
+	u8 lr_buffer[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	const u8 *method;
+	int ret;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+
+	method = OPALMETHOD[OPAL_GET];
+
+	if (build_locking_range(lr_buffer, sizeof(lr_buffer), dev->lr) < 0) {
+		pr_err("%s: Can't build locking range\n", dev->disk_name);
+		return -EINVAL;
+	}
+
+	set_comID(cmd, dev->comID);
+
+	ret = test_and_add_token_va(cmd, "c2s 12c",
+				    OPAL_CALL,
+				    lr_buffer, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+
+				    OPAL_STARTLIST,
+				    OPAL_STARTLIST,
+				    OPAL_STARTNAME,
+				    OPAL_STARTCOLUMN,
+				    OPAL_RANGESTART,
+				    OPAL_ENDNAME,
+				    OPAL_STARTNAME,
+				    OPAL_ENDCOLUMN,
+				    OPAL_RANGELENGTH,
+				    OPAL_ENDNAME,
+				    OPAL_ENDLIST,
+				    OPAL_ENDLIST);
+
+	if (ret < 0) {
+		pr_err("%s: Error building GET Locking Range command.\n",
+		       dev->disk_name);
+		return ret;
+	}
+
+	return finalize_and_send(dev, cmd, query_locking_range_cont);
+}
+
+static int add_user_to_lr(struct opal_dev *dev)
+{
+	u8 lr_buffer[OPAL_UID_LENGTH];
+	u8 user_uid[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	const u8 *method;
+	struct opal_lock_unlock *lkul;
+	int ret;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	method = OPALMETHOD[OPAL_SET];
+
+	lkul = dev->func_data[dev->state - 1];
+
+	memcpy(lr_buffer, OPALUID[OPAL_LOCKINGRANGE_ACE_RDLOCKED],
+	       OPAL_UID_LENGTH);
+
+	if (lkul->l_state == OPAL_RW)
+		memcpy(lr_buffer, OPALUID[OPAL_LOCKINGRANGE_ACE_WRLOCKED],
+		       OPAL_UID_LENGTH);
+
+	lr_buffer[7] = dev->lr;
+
+	memcpy(user_uid, OPALUID[OPAL_USER1_UID], OPAL_UID_LENGTH);
+	user_uid[7] = lkul->authority.who;
+
+	ret = test_and_add_token_va(cmd, "c2s 3c 3c 2c 2sc c2sc cs2c 5c",
+				    OPAL_CALL,
+				    lr_buffer, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+
+				    OPAL_STARTLIST,
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_01, /* Values */
+
+				    OPAL_STARTLIST,
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_03, /* BooleanExpr */
+
+				    OPAL_STARTLIST,
+				    OPAL_STARTNAME,
+
+				    OPALUID[OPAL_HALF_UID_AUTHORITY_OBJ_REF],
+				    OPAL_UID_LENGTH_HALF,
+				    user_uid, OPAL_UID_LENGTH,
+				    OPAL_ENDNAME,
+
+				    OPAL_STARTNAME,
+				    OPALUID[OPAL_HALF_UID_AUTHORITY_OBJ_REF],
+				    OPAL_UID_LENGTH_HALF,
+				    user_uid, OPAL_UID_LENGTH,
+				    OPAL_ENDNAME,
+
+				    OPAL_STARTNAME,
+				    OPALUID[OPAL_HALF_UID_BOOLEAN_ACE],
+				    OPAL_UID_LENGTH_HALF,
+				    OPAL_TINY_UINT_01,
+				    OPAL_ENDNAME,
+
+				    OPAL_ENDLIST,
+				    OPAL_ENDNAME,
+				    OPAL_ENDLIST,
+				    OPAL_ENDNAME,
+				    OPAL_ENDLIST);
+	if (ret < 0) {
+		pr_err("%s: Error building add user to locking range command.\n",
+		       dev->disk_name);
+		return ret;
+	}
+
+	return finalize_and_send(dev, cmd, generic_cont);
+}
+
+static int lock_unlock_locking_range(struct opal_dev *dev)
+{
+	u8 lr_buffer[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	const u8 *method;
+	struct opal_lock_unlock *lkul;
+	int ret;
+	u8 read_locked = 1, write_locked = 1;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	method = OPALMETHOD[OPAL_SET];
+	lkul = dev->func_data[dev->state - 1];
+	if (build_locking_range(lr_buffer, sizeof(lr_buffer), dev->lr) < 0) {
+		pr_err("%s: Can't build locking range\n", dev->disk_name);
+		return -EINVAL;
+	}
+
+	switch (lkul->l_state) {
+	case OPAL_RO:
+		read_locked = 0;
+		write_locked = 1;
+		break;
+	case OPAL_RW:
+		read_locked = 0;
+		write_locked = 0;
+		break;
+	case OPAL_LK:
+		/* vars are initalized to locked */
+		break;
+	default:
+		pr_err("Tried to set an invalid locking state... returning to uland\n");
+		return 1;
+	}
+
+	ret = test_and_add_token_va(cmd, "c2sc 3c 4c 4c 3c",
+				    OPAL_CALL,
+				    lr_buffer, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+				    OPAL_STARTLIST,
+
+				    OPAL_STARTNAME,
+				    OPAL_VALUES,
+				    OPAL_STARTLIST,
+
+				    OPAL_STARTNAME,
+				    OPAL_READLOCKED,
+				    read_locked,
+				    OPAL_ENDNAME,
+
+				    OPAL_STARTNAME,
+				    OPAL_WRITELOCKED,
+				    write_locked,
+				    OPAL_ENDNAME,
+
+				    OPAL_ENDLIST,
+				    OPAL_ENDNAME,
+				    OPAL_ENDLIST);
+
+	if (ret < 0) {
+		pr_err("%s: Error building SET command.\n", dev->disk_name);
+		return ret;
+	}
+	return finalize_and_send(dev, cmd, generic_cont);
+}
+
+
+static int lock_unlock_locking_range_SUM(struct opal_dev *dev)
+{
+	u8 lr_buffer[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	const u8 *method;
+	struct opal_lock_unlock *lkul;
+	int ret;
+	u8 read_locked = 1, write_locked = 1;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	method = OPALMETHOD[OPAL_SET];
+	lkul = dev->func_data[dev->state - 1];
+	if (build_locking_range(lr_buffer, sizeof(lr_buffer), dev->lr) < 0) {
+		pr_err("%s: Can't build locking range\n", dev->disk_name);
+		return -EINVAL;
+	}
+
+	switch (lkul->l_state) {
+	case OPAL_RO:
+		read_locked = 0;
+		write_locked = 1;
+		break;
+	case OPAL_RW:
+		read_locked = 0;
+		write_locked = 0;
+		break;
+	case OPAL_LK:
+		/* vars are initalized to locked */
+		break;
+	default:
+		pr_err("Tried to set an invalid locking state.\n");
+		return 1;
+	}
+
+	ret = test_and_add_token_va(cmd, "c2sc 3c 4c 4c 4c 4c 3c",
+				    OPAL_CALL,
+				    lr_buffer, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+				    OPAL_STARTLIST,
+
+				    OPAL_STARTNAME,
+				    OPAL_VALUES,
+				    OPAL_STARTLIST,
+
+				    OPAL_STARTNAME,
+				    OPAL_READLOCKENABLED,
+				    OPAL_TRUE,
+				    OPAL_ENDNAME,
+
+				    OPAL_STARTNAME,
+				    OPAL_WRITELOCKENABLED,
+				    OPAL_TRUE,
+				    OPAL_ENDNAME,
+
+				    OPAL_STARTNAME,
+				    OPAL_READLOCKED,
+				    read_locked,
+				    OPAL_ENDNAME,
+
+				    OPAL_STARTNAME,
+				    OPAL_WRITELOCKED,
+				    write_locked,
+				    OPAL_ENDNAME,
+
+				    OPAL_ENDLIST,
+				    OPAL_ENDNAME,
+				    OPAL_ENDLIST);
+	if (ret < 0) {
+		pr_err("%s: Error building SET command.\n", dev->disk_name);
+		return ret;
+	}
+	return finalize_and_send(dev, cmd, generic_cont);
+}
+
+int activate_lsp(struct opal_dev *dev)
+{
+	u8 user_lr[OPAL_UID_LENGTH];
+	const u8 *method, *uid;
+	struct opal_cmd *cmd;
+	int ret;
+	size_t uint_3 = 0x83;
+
+	cmd = &dev->cmd;
+
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	uid = OPALUID[OPAL_LOCKINGSP_UID];
+	method = OPALMETHOD[OPAL_ACTIVATE];
+
+	ret = test_and_add_token_va(cmd, "c2s",
+				    OPAL_CALL,
+				    uid, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH);
+	if (ret < 0) {
+		pr_err("%s: Error building Activate LockingSP command.\n",
+		       dev->disk_name);
+		return ret;
+	}
+	/* Activating as SUM */
+	if (dev->lr > 0) {
+		ret = build_locking_range(user_lr, sizeof(user_lr), dev->lr);
+		if (ret < 0) {
+			pr_err("%s: Can't build locking user\n",
+			       dev->disk_name);
+			return ret;
+		}
+		test_and_add_token_va(cmd, "2c 4c csc 2c",
+				      OPAL_STARTLIST,
+				      OPAL_STARTNAME,
+
+				      uint_3,
+				      OPAL_TINY_UINT_06,
+				      OPAL_TINY_UINT_00,
+				      OPAL_TINY_UINT_00,
+
+				      OPAL_STARTLIST,
+				      user_lr, OPAL_UID_LENGTH,
+				      OPAL_ENDLIST,
+
+				      OPAL_ENDNAME,
+				      OPAL_ENDLIST);
+	} else /* Actiave Normal Mode */
+		ret = test_and_add_token_va(cmd, "2c",
+					    OPAL_STARTLIST,
+					    OPAL_ENDLIST);
+
+	if (ret < 0) {
+		pr_err("%s: Error building Activate LockingSP command.\n",
+		       dev->disk_name);
+		return ret;
+	}
+
+	return finalize_and_send(dev, cmd, generic_cont);
+}
+
+static void get_lsp_lifecycle_cont(int error, void *data)
+{
+
+	struct opal_dev *dev = data;
+	u8 lc_status;
+
+	if (error)
+		goto err_return;
+
+	error = parse_and_check_status(dev);
+	if (error)
+		goto err_return;
+
+	lc_status = response_get_u64(&dev->parsed, 4);
+	/* 0x08 is Manufacured Inactive */
+	/* 0x09 is Manufactured */
+	if (lc_status != 0x08) {
+		pr_err("%s: Couldn't determine the status of the Lifcycle state\n",
+		       dev->disk_name);
+		error = -ENODEV;
+	}
+
+err_return:
+	if (dev->oper_cb)
+		dev->oper_cb(error, dev);
+}
+
+/* Determine if we're in the Manufactured Inactive or Active state */
+int get_lsp_lifecycle(struct opal_dev *dev)
+{
+	struct opal_cmd *cmd;
+	const u8 *method, *uid;
+	int ret;
+
+	cmd = &dev->cmd;
+
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	uid = OPALUID[OPAL_LOCKINGSP_UID];
+	method = OPALMETHOD[OPAL_GET];
+
+	ret = test_and_add_token_va(cmd, "c2s 2c 4c 4c 2c",
+				    OPAL_CALL,
+				    uid, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+
+				    OPAL_STARTLIST,
+				    OPAL_STARTLIST,
+
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_03, /* Start Column */
+				    OPAL_TINY_UINT_06, /* Lifcycle Column */
+				    OPAL_ENDNAME,
+
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_04, /* End Column */
+				    OPAL_TINY_UINT_06, /* Lifecycle Column */
+				    OPAL_ENDNAME,
+
+				    OPAL_ENDLIST,
+				    OPAL_ENDLIST);
+
+	if (ret < 0) {
+		pr_err("%s: Error Building GET Lifecycle Status command\n",
+		       dev->disk_name);
+		return ret;
+	}
+
+	return finalize_and_send(dev, cmd, get_lsp_lifecycle_cont);
+}
+
+static void get_msid_cpin_pin_cont(int error, void *data)
+{
+	const char *msid_pin;
+	struct opal_dev *dev = data;
+	size_t strlen;
+
+	if (error)
+		goto err_return;
+
+	error = parse_and_check_status(dev);
+	if (error)
+		goto err_return;
+
+	strlen = response_get_string(&dev->parsed, 4, &msid_pin);
+	if (!msid_pin) {
+		pr_err("%s: Couldn't extract PIN from response\n", __func__);
+		error = 1;
+		goto err_return;
+	}
+
+	dev->prev_data = kmemdup(msid_pin, strlen, GFP_KERNEL);
+	if (!dev->prev_data)
+		error = -ENOMEM;
+
+	dev->prev_d_len = strlen;
+
+ err_return:
+	if (dev->oper_cb)
+		dev->oper_cb(error, dev);
+}
+
+static int get_msid_cpin_pin(struct opal_dev *dev)
+{
+	const u8 *method, *smuid;
+	int ret;
+	struct opal_cmd *cmd;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	smuid = OPALUID[OPAL_C_PIN_MSID];
+	method = OPALMETHOD[OPAL_GET];
+
+	ret = test_and_add_token_va(cmd, "c 2s 12c",
+				    OPAL_CALL,
+
+				    smuid, OPAL_UID_LENGTH,
+				    method, OPAL_METHOD_LENGTH,
+
+				    OPAL_STARTLIST,
+				    OPAL_STARTLIST,
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_03, /* Sart Column */
+				    OPAL_TINY_UINT_03, /* PIN */
+				    OPAL_ENDNAME,
+				    OPAL_STARTNAME,
+				    OPAL_TINY_UINT_04, /* End Column */
+				    OPAL_TINY_UINT_03, /* PIN */
+				    OPAL_ENDNAME,
+				    OPAL_ENDLIST,
+				    OPAL_ENDLIST);
+
+	if (ret < 0) {
+		pr_err("%s: Error building Get MSID CPIN PIN command.\n",
+		       dev->disk_name);
+		return ret;
+	}
+
+	return finalize_and_send(dev, cmd, get_msid_cpin_pin_cont);
+}
+
+static void unlock_suspend_final(int error, void *data)
+{
+	struct opal_dev *dev = data;
+
+	dev->resume_from_suspend = false;
+	dev->resume_data = NULL;
+	dev->func_data = NULL;
+	dev->bdev = NULL;
+}
+
+static int build_end_opal_session(struct opal_dev *dev)
+{
+	struct opal_cmd *cmd;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+
+	set_comID(cmd, dev->comID);
+	return test_and_add_token_u8(cmd, OPAL_ENDOFSESSION);
+}
+
+static int end_opal_session(struct opal_dev *dev)
+{
+	if (build_end_opal_session(dev) < 0)
+		return -1;
+	return finalize_and_send(dev, &dev->cmd, end_session_cont);
+}
+
+static struct opal_dev *find_opal_dev(struct block_device *bdev, u8 lr)
+{
+	struct opal_dev *iter, *opal_dev = NULL;
+
+	list_for_each_entry(iter, &opal_list, node) {
+		if (strncmp(iter->disk_name, bdev->bd_disk->disk_name,
+			    DISK_NAME_LEN))
+			continue;
+		if (iter->lr == lr) {
+			opal_dev = iter;
+			break;
+		}
+	}
+
+	return opal_dev;
+}
+
+static int update_opal_dev(struct opal_dev *old_dev, struct opal_dev *new_dev)
+{
+	if (!atomic_add_unless(&old_dev->in_use, 1, 1)) {
+		pr_err("%s: dev was in use\n", __func__);
+		return -EBUSY;
+	}
+
+	old_dev->key_name_len = new_dev->key_name_len;
+	if (!memcpy(old_dev->key_name, new_dev->key_name, old_dev->key_name_len)) {
+		pr_err("%s: Error updating device:\n", old_dev->disk_name);
+		return -EFAULT;
+	}
+
+	if (!strncpy(old_dev->disk_name, new_dev->disk_name, DISK_NAME_LEN)) {
+		pr_err("%s: Error registering device: copying disk name\n",
+		       old_dev->disk_name);
+		return -EFAULT;
+	}
+
+	old_dev->comID = new_dev->comID;
+	old_dev->start = new_dev->start;
+	old_dev->length = new_dev->length;
+	old_dev->align = new_dev->align;
+	old_dev->lowest_lba = new_dev->lowest_lba;
+	old_dev->bdev = NULL;
+	old_dev->final_cb = new_dev->final_cb;
+	old_dev->final_cb_data = new_dev->final_cb_data;
+	old_dev->oper_cb = new_dev->oper_cb;
+	old_dev->state = new_dev->state;
+	old_dev->funcs = new_dev->funcs;
+
+	kfree(old_dev->completion);
+	clean_function_data(old_dev);
+
+	old_dev->completion = new_dev->completion;
+
+	/*
+	 * Won't be able to auto unlock this locking range based on block
+	 * requestes.
+	 */
+	if (old_dev->length == 0)
+		pr_warn("%s: Missing block information for locking range %d\n",
+			old_dev->disk_name, old_dev->lr);
+
+	return 0;
+}
+
+int opal_register_cont(struct opal_dev *new_dev)
+{
+	struct opal_dev *old_dev;
+	unsigned long flags;
+	int error = 0;
+
+	spin_lock_irqsave(&list_spinlock, flags);
+
+	old_dev = find_opal_dev(new_dev->bdev, new_dev->lr);
+	if (!old_dev) {
+		list_add_tail(&new_dev->node, &opal_list);
+		old_dev = new_dev;
+	} else {
+		if (old_dev == new_dev)
+			error = 0;
+		else {
+			error = update_opal_dev(old_dev, new_dev);
+			clean_opal_key(new_dev);
+			kfree(new_dev);
+		}
+	}
+
+	if (error)
+		list_del(&old_dev->node);
+
+	spin_unlock_irqrestore(&list_spinlock, flags);
+
+	if (!error)
+		pr_info("%s: Registered key for locking range: %d\n",
+			old_dev->disk_name, old_dev->lr);
+
+	if (old_dev->oper_cb)
+		old_dev->oper_cb(error, old_dev);
+
+	return 0;
+}
+
+static void next(int error, struct opal_dev *dev)
+{
+	opal_step func = dev->funcs[dev->state];
+	void *cb_data = dev->final_cb_data;
+	sec_cb *cb = dev->final_cb;
+	bool done = false;
+
+
+	if (error || !func) {
+		done = true;
+		goto next_exit;
+	}
+	dev->state++;
+	dev->oper_cb = next;
+	error = func(dev);
+ next_exit:
+	if (error) {
+		pr_err("%s: Error on step function: %d with error %d: %s\n",
+		       dev->disk_name, dev->state, error,
+		       opal_error_to_human(error));
+
+
+		atomic_dec(&dev->in_use);
+		if (dev->error_cb) {
+			dev->error_cb(dev->error_cb_data);
+			return;
+		}
+		if (cb)
+			cb(error, cb_data);
+
+		dev->completion->completion_status = error;
+		complete(&dev->completion->cmd_completion);
+	} else if (!error && done) {
+		atomic_dec(&dev->in_use);
+		if (cb)
+			cb(error, cb_data);
+		dev->completion->completion_status = error;
+		complete(&dev->completion->cmd_completion);
+	}
+}
+
+const opal_step error_end_session[] = {
+	end_opal_session,
+	NULL,
+};
+static int end_opal_session_error(struct opal_dev *dev)
+{
+
+	dev->funcs = error_end_session;
+	dev->state = 0;
+	dev->error_cb = NULL;
+	next(0, dev);
+	return 0;
+}
+
+static struct opal_dev *alloc_opal_dev(struct block_device *bdev, u8 lr)
+{
+	struct opal_dev *opal_dev;
+	struct request_queue *q;
+	unsigned long dma_align;
+	const char *disk_name;
+	struct opal_cmd *cmd;
+	int ret;
+
+	opal_dev = kzalloc(sizeof(*opal_dev), GFP_KERNEL);
+	if (!opal_dev)
+		return ERR_PTR(-ENOMEM);
+
+	opal_dev->bdev = bdev;
+	opal_dev->lr = lr;
+	cmd = &opal_dev->cmd;
+	cmd->cmd = cmd->cmd_buf;
+	cmd->resp = cmd->resp_buf;
+
+	disk_name = bdev->bd_disk->disk_name;
+	if (!strncpy(opal_dev->disk_name, disk_name, DISK_NAME_LEN)) {
+		pr_err("%s: Error registering device: copying disk name\n",
+		       disk_name);
+		ret = -EFAULT;
+		goto err_free_dev;
+	}
+
+	q = bdev->bd_queue;
+	dma_align = (queue_dma_alignment(q) | q->dma_pad_mask) + 1;
+	cmd->cmd = (u8 *)round_up((uintptr_t)cmd->cmd, dma_align);
+	cmd->resp = (u8 *)round_up((uintptr_t)cmd->resp, dma_align);
+
+	INIT_LIST_HEAD(&opal_dev->node);
+	atomic_set(&opal_dev->in_use, 1);
+
+	opal_dev->completion = kzalloc(sizeof(*opal_dev->completion),
+				       GFP_KERNEL);
+
+	if (!opal_dev->completion)
+		goto err_free_dev;
+
+	init_completion(&opal_dev->completion->cmd_completion);
+	opal_dev->completion->completion_status = 0;
+	opal_dev->state = 0;
+
+	return opal_dev;
+
+err_free_dev:
+	kfree(opal_dev);
+	return ERR_PTR(ret);
+}
+
+int opal_register(struct block_device *bdev, struct opal_key *key_cmd,
+		  const opal_step *funcs)
+{
+	struct opal_dev *new_dev = NULL;
+	u8 key_len = key_cmd->key_len;
+	u8 lr = key_cmd->lr;
+	struct opal_completion *completion;
+	int ret;
+
+	new_dev = alloc_opal_dev(bdev, lr);
+	if (IS_ERR(new_dev)) {
+		pr_err("%s: Error registering device: allocation\n",
+		       bdev->bd_disk->disk_name);
+		return PTR_ERR(new_dev);
+	}
+
+	if (!memcpy(new_dev->key_name, key_cmd->key, key_len)) {
+		pr_err("%s: Error registering key: couldn't copy key\n",
+		       new_dev->disk_name);
+		return -EFAULT;
+	}
+
+	new_dev->key_name_len = key_len;
+	new_dev->key_type = key_cmd->key_type;
+	ret = get_opal_key(new_dev);
+	if (ret) {
+		pr_err("%s: Couldn't get key: %d\n", new_dev->disk_name, ret);
+		return ret;
+	}
+
+	new_dev->funcs = funcs;
+
+	new_dev->state = 0;
+	completion = new_dev->completion;
+	next(0, new_dev);
+
+	return wait_for_cmd_completion(completion);
+}
+
+static struct opal_dev *get_registered_opal_dev(struct block_device *bdev,
+						u8 lr)
+{
+	const char *diskname = bdev->bd_disk->disk_name;
+	struct opal_dev *iter, *dev = NULL;
+	unsigned long flags;
+	bool in_use = false;
+
+	spin_lock_irqsave(&list_spinlock, flags);
+	list_for_each_entry(iter, &opal_list, node) {
+		if (strncmp(iter->disk_name, diskname, DISK_NAME_LEN))
+			continue;
+		if (iter->lr == lr) {
+			dev = iter;
+			if (!atomic_add_unless(&iter->in_use, 1, 1)) {
+				dev = NULL;
+				in_use = true;
+			}
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&list_spinlock, flags);
+
+	if (!dev)
+		return NULL;
+
+	dev->bdev = bdev;
+	return dev;
+}
+
+/* Free up the Opal dev and its keys during two scenarios:
+ *
+ * 1) When a command is complete that no longer requires
+ *    the opal dev to be around.
+ * 2) When a command, including Opal Save fails we clean
+ *    and free the opal dev.
+ *
+ *    If we find the opal dev structure in the list of
+ *    saved passwords we will *not* remove it.
+ */
+static void remove_and_clean_opal_dev(struct opal_dev *dev)
+{
+	struct opal_dev *iter;
+	bool found = false;
+
+	spin_lock(&list_spinlock);
+	list_for_each_entry(iter, &opal_list, node) {
+		if (iter == dev) {
+			found = true;
+			break;
+		}
+	}
+
+	spin_unlock(&list_spinlock);
+	if (!found) {
+		clean_opal_key(dev);
+		clean_function_data(dev);
+		kfree(dev);
+	}
+}
+
+static struct opal_dev *get_or_create_opal_dev(struct block_device *bdev,
+					       u8 lr, bool use_new)
+{
+	struct opal_dev *dev;
+
+	if (use_new)
+		return alloc_opal_dev(bdev, lr);
+
+	dev = get_registered_opal_dev(bdev, lr);
+	if (!dev) {
+		dev = alloc_opal_dev(bdev, lr);
+		if (!dev)
+			return NULL;
+	}
+	return dev;
+}
+
+static struct opal_completion *setup_opal_dev(struct block_device *bdev,
+					      struct opal_dev *dev,
+					      const opal_step *funcs,
+					      struct opal_key *key)
+{
+	int ret;
+
+	dev->bdev = bdev;
+	dev->state = 0;
+	dev->funcs = funcs;
+	dev->TSN = 0;
+	dev->HSN = 0;
+	dev->final_cb = NULL;
+	dev->final_cb_data = NULL;
+	dev->lr = key->lr;
+	dev->error_cb = end_opal_session_error;
+	dev->error_cb_data = dev;
+
+	if (key) {
+		memcpy(dev->key_name, key->key, key->key_len);
+		dev->key_name_len = key->key_len;
+		dev->key_type = key->key_type;
+
+		ret = get_opal_key(dev);
+		if (ret) {
+			kfree(dev->completion);
+			pr_err("%s: Couldn't get key: %d\n",
+			       dev->disk_name, ret);
+			return ERR_PTR(ret);
+		}
+	}
+	dev->func_data = NULL;
+	dev->completion->completion_status = 0;
+
+	return dev->completion;
+}
+
+static int internal_setup_lr(struct block_device *bdev,
+			     struct opal_user_lr_setup *setup)
+{
+	struct opal_dev *dev;
+	struct opal_completion *completion;
+	void *data[3] = { NULL };
+	const opal_step lr_funcs[] = {
+		opal_discovery0,
+		start_auth_opal_session,
+		setup_locking_range,
+		get_active_key,
+		gen_key,
+		end_opal_session,
+		NULL,
+	};
+	int ret;
+
+	dev = get_or_create_opal_dev(bdev, setup->key.lr, true);
+	if (!dev)
+		return -ENOMEM;
+
+	completion = setup_opal_dev(bdev, dev, lr_funcs, &setup->key);
+	if (IS_ERR(completion)) {
+		ret = PTR_ERR(completion);
+		goto error_return;
+	}
+
+	dev->func_data = data;
+	dev->num_func_data = 3;
+	dev->func_data[1] = &setup->who;
+	dev->func_data[2] = setup;
+
+	next(0, dev);
+	ret = wait_for_cmd_completion(completion);
+
+ error_return:
+	remove_and_clean_opal_dev(dev);
+	return ret;
+}
+
+int opal_revert(struct block_device *bdev, struct opal_key *key)
+{
+	const opal_step revert_funcs[] = {
+		opal_discovery0,
+		start_SIDASP_opal_session,
+		revert_tper, /* controller will terminate session */
+		NULL,
+	};
+
+	return opal_register(bdev, key, revert_funcs);
+}
+
+int activate_user(struct block_device *bdev, struct opal_activate_user *act)
+{
+	const opal_step act_funcs[] = {
+		opal_discovery0,
+		start_admin1LSP_opal_session,
+		internal_activate_user,
+		end_opal_session,
+		NULL
+	};
+	struct opal_dev *dev;
+	struct opal_completion *completion;
+	void *data[3] = { NULL };
+	int ret;
+
+	dev = get_or_create_opal_dev(bdev, act->key.lr, true);
+	if (!dev)
+		return -ENOMEM;
+
+	completion = setup_opal_dev(bdev, dev, act_funcs, &act->key);
+	if (IS_ERR(completion)) {
+		ret = PTR_ERR(completion);
+		goto error_return;
+	}
+
+	dev->num_func_data = 3;
+	dev->func_data = data;
+	dev->func_data[1] = act;
+	dev->func_data[2] = act;
+
+	next(0, dev);
+	ret = wait_for_cmd_completion(completion);
+
+ error_return:
+	remove_and_clean_opal_dev(dev);
+	return ret;
+}
+
+int opal_set_pw(struct block_device *bdev, struct opal_new_pw *pw)
+
+{
+	const opal_step pw_funcs[] = {
+		opal_discovery0,
+		start_auth_opal_session,
+		set_new_pw,
+		end_opal_session,
+		NULL
+	};
+	struct opal_dev *dev;
+	struct opal_completion *completion;
+	void *data[3] = { NULL };
+	int ret;
+
+	dev = get_or_create_opal_dev(bdev, pw->current_pin.lr, true);
+	if (!dev)
+		return -ENOMEM;
+
+	completion = setup_opal_dev(bdev, dev, pw_funcs, &pw->current_pin);
+	if (IS_ERR(completion)) {
+		ret = PTR_ERR(completion);
+		goto error_return;
+	}
+
+	dev->num_func_data = 3;
+	dev->func_data = data;
+	dev->func_data[1] = (void *) &pw->who;
+	dev->func_data[2] = (void *) pw;
+
+	next(0, dev);
+	ret = wait_for_cmd_completion(completion);
+
+ error_return:
+	remove_and_clean_opal_dev(dev);
+	return ret;
+}
+
+int opal_act_lsp_int(struct block_device *bdev, struct opal_key *key,
+		     const opal_step *funcs)
+{
+	struct opal_dev *dev;
+	struct opal_completion *completion;
+	int ret;
+
+	dev = get_or_create_opal_dev(bdev, key->lr, true);
+	if (!dev)
+		return -ENOMEM;
+	completion = setup_opal_dev(bdev, dev, funcs, key);
+	if (IS_ERR(completion)) {
+		ret = PTR_ERR(completion);
+		goto error_return;
+	}
+
+	next(0, dev);
+	ret = wait_for_cmd_completion(completion);
+
+ error_return:
+	remove_and_clean_opal_dev(dev);
+	return ret;
+}
+
+
+static int opal_save_internal(struct block_device *bdev,
+			      struct opal_lock_unlock *lk)
+{
+	void *func_data[3] = { NULL };
+	struct opal_dev *dev;
+	struct opal_completion *completion;
+	const opal_step _auth_funcs[] = {
+		opal_discovery0,
+		start_auth_opal_session,
+		query_locking_range,
+		end_opal_session,
+		opal_register_cont,
+		NULL
+	};
+	int ret;
+
+	dev = get_or_create_opal_dev(bdev, lk->key.lr, false);
+	if (!dev)
+		return -ENOMEM;
+	completion = setup_opal_dev(bdev, dev, _auth_funcs, &lk->key);
+	if (IS_ERR(completion)) {
+		ret = PTR_ERR(completion);
+		goto error_return;
+	}
+
+	dev->num_func_data = 3;
+	dev->func_data = func_data;
+	dev->func_data[1] = &lk->authority;
+	dev->lkul = *lk;
+
+	next(0, dev);
+	ret = wait_for_cmd_completion(completion);
+
+ error_return:
+	remove_and_clean_opal_dev(dev);
+	return ret;
+}
+
+static int add_user_lr_internal(struct block_device *bdev,
+				struct opal_lock_unlock *lk)
+{
+	void *func_data[3] = { NULL };
+	struct opal_dev *dev;
+	struct opal_completion *completion;
+	const opal_step funcs[] = {
+		opal_discovery0,
+		start_admin1LSP_opal_session,
+		add_user_to_lr,
+		end_opal_session,
+		NULL
+	};
+	int ret;
+
+	dev = get_or_create_opal_dev(bdev, lk->key.lr, true);
+	if (!dev)
+		return -ENOMEM;
+	completion = setup_opal_dev(bdev, dev, funcs, &lk->key);
+	if (IS_ERR(completion)) {
+		ret = PTR_ERR(completion);
+		goto error_return;
+	}
+
+	dev->num_func_data = 3;
+	dev->func_data = func_data;
+	dev->func_data[2] = lk;
+
+	next(0, dev);
+	ret = wait_for_cmd_completion(completion);
+
+ error_return:
+	remove_and_clean_opal_dev(dev);
+	return ret;
+}
+
+static int lock_unlock_internal(struct block_device *bdev,
+				struct opal_lock_unlock *lk)
+{
+	void *func_data[3] = { NULL };
+	struct opal_dev *dev;
+	struct opal_completion *completion;
+
+	const opal_step ulk_funcs_SUM[] = {
+		opal_discovery0,
+		start_auth_opal_session,
+		lock_unlock_locking_range_SUM,
+		end_opal_session,
+		NULL
+	};
+	const opal_step _unlock_funcs[] = {
+		opal_discovery0,
+		start_auth_opal_session,
+		lock_unlock_locking_range,
+		end_opal_session,
+		NULL
+	};
+	int ret;
+
+	dev = get_or_create_opal_dev(bdev, lk->key.lr, true);
+	if (!dev)
+		return -ENOMEM;
+
+	if (lk->authority.SUM)
+		completion = setup_opal_dev(bdev, dev, ulk_funcs_SUM, &lk->key);
+	else
+		completion = setup_opal_dev(bdev, dev, _unlock_funcs, &lk->key);
+
+	if (IS_ERR(completion)) {
+		ret = PTR_ERR(completion);
+		goto error_return;
+	}
+
+	dev->num_func_data = 3;
+	dev->func_data = func_data;
+	dev->func_data[1] = &lk->authority;
+	dev->func_data[2] = lk;
+
+	next(0, dev);
+	ret = wait_for_cmd_completion(completion);
+
+ error_return:
+	remove_and_clean_opal_dev(dev);
+	return ret;
+}
+
+int opal_erase_locking_range(struct block_device *bdev, struct sed_key *key)
+{
+	struct opal_dev *dev;
+	struct opal_completion *completion;
+	struct opal_key k;
+	int ret;
+	const opal_step erase_funcs[] = {
+		opal_discovery0,
+		start_admin1LSP_opal_session,
+		erase_locking_range,
+		end_opal_session,
+		NULL,
+	};
+
+	if (!bdev || !bdev->bd_disk) {
+		pr_err("Can't save password for NULL block device.\n");
+		return -EINVAL;
+	}
+
+	if (copy_from_user(&k, key->opal, sizeof(*key->opal)))
+		return -EFAULT;
+
+	dev = get_or_create_opal_dev(bdev, k.lr, true);
+	if (!dev)
+		return -ENOMEM;
+
+	completion = setup_opal_dev(bdev, dev, erase_funcs, &k);
+	if (IS_ERR(completion)) {
+		ret = PTR_ERR(completion);
+		goto error_return;
+	}
+
+	next(0, dev);
+	ret = wait_for_cmd_completion(completion);
+
+ error_return:
+	remove_and_clean_opal_dev(dev);
+	return ret;
+}
+EXPORT_SYMBOL(opal_erase_locking_range);
+
+int opal_enable_disable_shadow_mbr(struct block_device *bdev,
+				   struct sed_key *key)
+{
+	void *func_data[6] = { NULL };
+	struct opal_mbr_data mbr;
+	struct opal_dev *dev;
+	struct opal_completion *completion;
+	const opal_step mbr_funcs[] = {
+		opal_discovery0,
+		start_admin1LSP_opal_session,
+		set_mbr_done,
+		end_opal_session,
+		start_admin1LSP_opal_session,
+		set_mbr_enable_disable,
+		end_opal_session,
+		NULL,
+	};
+	int ret;
+
+	if (!bdev || !bdev->bd_disk) {
+		pr_err("Can't save password for NULL block device.\n");
+		return -EINVAL;
+	}
+
+	if (copy_from_user(&mbr, key->opal_mbr, sizeof(*key->opal_mbr)))
+		return -EFAULT;
+
+	if (mbr.enable_disable != OPAL_MBR_ENABLE &&
+	    mbr.enable_disable != OPAL_MBR_DISABLE)
+		return -EINVAL;
+
+	dev = get_or_create_opal_dev(bdev, mbr.key.lr, true);
+	if (!dev)
+		return -ENOMEM;
+
+	completion = setup_opal_dev(bdev, dev, mbr_funcs, &mbr.key);
+	if (IS_ERR(completion)) {
+		ret = PTR_ERR(completion);
+		goto error_return;
+	}
+
+	dev->num_func_data = 6;
+	dev->func_data = func_data;
+	dev->func_data[2] = &mbr.enable_disable;
+	dev->func_data[5] = &mbr.enable_disable;
+
+	next(0, dev);
+	ret = wait_for_cmd_completion(completion);
+
+ error_return:
+	remove_and_clean_opal_dev(dev);
+	return ret;
+
+}
+EXPORT_SYMBOL(opal_enable_disable_shadow_mbr);
+
+int opal_save(struct block_device *bdev, struct sed_key *key)
+{
+	struct opal_lock_unlock lkul;
+
+	if (!bdev || !bdev->bd_disk) {
+		pr_err("Can't save password for NULL block device.\n");
+		return -EINVAL;
+	}
+
+	if (copy_from_user(&lkul, key->opal_lk_unlk, sizeof(*key->opal)))
+		return -EFAULT;
+
+	return opal_save_internal(bdev, &lkul);
+}
+EXPORT_SYMBOL(opal_save);
+
+int opal_add_user_to_lr(struct block_device *bdev, struct sed_key *key)
+{
+	struct opal_lock_unlock lkul;
+
+	if (copy_from_user(&lkul, key->opal_lk_unlk, sizeof(lkul)))
+		return -EFAULT;
+
+	if (!bdev || !bdev->bd_disk) {
+		pr_err("Can't assign user to LR without backing disk\n");
+		return -EFAULT;
+	}
+	if (lkul.l_state != OPAL_RO && lkul.l_state != OPAL_RW) {
+		pr_err("Locking state was not RO or RW\n");
+		return -EINVAL;
+	}
+	if (lkul.authority.who < OPAL_USER1 &&
+	    lkul.authority.who > OPAL_USER9) {
+		pr_err("Authority was not within the range of users: %d\n",
+		       lkul.authority.who);
+		return -EINVAL;
+	}
+	if (lkul.authority.SUM) {
+		pr_err("%s not supported in SUM. Use setup locking range\n",
+		       __func__);
+		return -EINVAL;
+	}
+
+	return add_user_lr_internal(bdev, &lkul);
+}
+EXPORT_SYMBOL(opal_add_user_to_lr);
+
+int opal_reverttper(struct block_device *bdev, struct sed_key *key)
+{
+	struct opal_key k;
+
+	if (copy_from_user(&k, key->opal, sizeof(*key->opal)))
+		return -EFAULT;
+
+	return opal_revert(bdev, &k);
+}
+EXPORT_SYMBOL(opal_reverttper);
+
+int opal_lock_unlock(struct block_device *bdev, struct sed_key *key)
+{
+	struct opal_lock_unlock k;
+
+	if (copy_from_user(&k, key->opal_lk_unlk, sizeof(*key->opal_lk_unlk)))
+		return -EFAULT;
+
+	if (k.authority.who < OPAL_ADMIN1 || k.authority.who >= OPAL_USER9)
+		return -EINVAL;
+
+	return lock_unlock_internal(bdev, &k);
+}
+EXPORT_SYMBOL(opal_lock_unlock);
+
+int opal_take_ownership(struct block_device *bdev, struct sed_key *key)
+{
+	struct opal_key k;
+	const opal_step owner_funcs[] = {
+		opal_discovery0,
+		start_anybodyASP_opal_session,
+		get_msid_cpin_pin,
+		end_opal_session,
+		start_SIDASP_opal_session,
+		set_sid_cpin_pin,
+		end_opal_session,
+		NULL
+	};
+
+	if (!bdev || !bdev->bd_disk) {
+		pr_err("Can't save password for NULL block device.\n");
+		return -EINVAL;
+	}
+
+	if (copy_from_user(&k, key->opal, sizeof(*key->opal)))
+		return -EFAULT;
+
+	return opal_register(bdev, &k, owner_funcs);
+}
+EXPORT_SYMBOL(opal_take_ownership);
+
+int opal_activate_lsp(struct block_device *bdev, struct sed_key *key)
+{
+	struct opal_key k;
+	const opal_step active_funcs[] = {
+		opal_discovery0,
+		start_SIDASP_opal_session, /* Open session as SID auth */
+		get_lsp_lifecycle,
+		activate_lsp,
+		end_opal_session,
+		NULL
+	};
+
+	if (!bdev || !bdev->bd_disk) {
+		pr_err("Can't save password for NULL block device.\n");
+		return -EINVAL;
+	}
+
+	if (copy_from_user(&k, key->opal, sizeof(*key->opal)))
+		return -EFAULT;
+
+	return opal_act_lsp_int(bdev, &k, active_funcs);
+}
+EXPORT_SYMBOL(opal_activate_lsp);
+
+int opal_setup_locking_range(struct block_device *bdev, struct sed_key *pw)
+{
+	struct opal_user_lr_setup k;
+
+	if (!bdev || !bdev->bd_disk) {
+		pr_err("Can't save password for NULL block device.\n");
+		return -EINVAL;
+	}
+
+	if (copy_from_user(&k, pw->opal_lrs, sizeof(*pw->opal_lrs)))
+		return -EFAULT;
+
+	return internal_setup_lr(bdev, &k);
+}
+
+int opal_set_new_pw(struct block_device *bdev, struct sed_key *pw)
+{
+	struct opal_new_pw k;
+
+	if (pw->sed_type != OPAL_PW)
+		return -EINVAL;
+
+	if (copy_from_user(&k, pw->opal_pw, sizeof(*pw->opal_pw)))
+		return -EFAULT;
+
+	if (k.who.who < OPAL_ADMIN1 || k.who.who > OPAL_USER9)
+		return -EINVAL;
+
+	return opal_set_pw(bdev, &k);
+}
+EXPORT_SYMBOL(opal_set_new_pw);
+
+int opal_activate_user(struct block_device *bdev, struct sed_key *pw)
+{
+	struct opal_activate_user k;
+
+	if (pw->sed_type != OPAL_ACT_USR) {
+		pr_err("Sed type was not act user\n");
+		return -EINVAL;
+	}
+
+	if (copy_from_user(&k, pw->opal_act, sizeof(*pw->opal_act))) {
+		pr_err("copy from user error\n");
+		return -EFAULT;
+	}
+
+	/* We can't activate Admin1 it's active as manufactured */
+	if (k.who.who < OPAL_USER1 && k.who.who > OPAL_USER9) {
+		pr_err("Who was not a valid user: %d \n", k.who.who);
+		return -EINVAL;
+	}
+
+	return activate_user(bdev, &k);
+
+}
+EXPORT_SYMBOL(opal_activate_user);
+
+int opal_unlock_from_suspend(struct opal_suspend_unlk *data)
+{
+	const char *diskname = data->name;
+	struct opal_dev *iter, *dev = NULL;
+	struct opal_completion *completion;
+	void *func_data[3] = { NULL };
+	const opal_step _unlock_funcs_SUM[] = {
+		opal_discovery0,
+		start_auth_opal_session,
+		lock_unlock_locking_range,
+		end_opal_session,
+		NULL
+	};
+	const opal_step _unlock_funcs[] = {
+		opal_discovery0,
+		start_auth_opal_session,
+		lock_unlock_locking_range,
+		end_opal_session,
+		NULL
+	};
+
+
+	spin_lock(&list_spinlock);
+	list_for_each_entry(iter, &opal_list, node) {
+		if (strncmp(iter->disk_name, diskname, DISK_NAME_LEN)) {
+			pr_err("iterdisk was %s and diskname is %s\n",
+			       iter->disk_name, diskname);
+			continue;
+		}
+		if (atomic_add_unless(&iter->in_use, 1, 1)) {
+			dev = iter;
+			dev->func_data = func_data;
+			dev->resume_from_suspend = true;
+			dev->resume_data = data;
+			dev->final_cb = unlock_suspend_final;
+			dev->final_cb_data = dev;
+			dev->error_cb = end_opal_session_error;
+			dev->error_cb_data = dev;
+			dev->state = 0;
+			if (dev->lkul.authority.SUM)
+				dev->funcs = _unlock_funcs_SUM;
+			else
+				dev->funcs = _unlock_funcs;
+			dev->TSN = 0;
+			dev->HSN = 0;
+			dev->func_data[2] = &dev->lkul;
+			dev->func_data[1] = &dev->lkul.authority;
+			completion = dev->completion;
+			next(0, dev);
+			wait_for_cmd_completion(completion);
+		}
+	}
+	spin_unlock(&list_spinlock);
+
+	if (!dev)
+		return -ENODEV;
+	return 0;
+}
+EXPORT_SYMBOL(opal_unlock_from_suspend);
diff --git a/lib/sed-opal_internal.h b/lib/sed-opal_internal.h
new file mode 100644
index 0000000..c9d3883
--- /dev/null
+++ b/lib/sed-opal_internal.h
@@ -0,0 +1,586 @@
+/*
+ * Copyright ? 2016 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Author:
+ *    Rafael Antognolli <rafael.antognolli at intel.com>
+ *    Scott  Bauer      <scott.bauer at intel.com>
+ */
+
+#ifndef _NVME_OPAL_INTERNAL_H
+#define _NVME_OPAL_INTERNAL_H
+
+#include <linux/key-type.h>
+#include <keys/user-type.h>
+
+#define DTAERROR_NO_METHOD_STATUS 0x89
+
+static const char *opal_errors[19] = {
+	"Success",
+	"Not Authorized",
+	"Unknown Error",
+	"SP Busy",
+	"SP Failed",
+	"SP Disabled",
+	"SP Frozen",
+	"No Sessions Available",
+	"Uniqueness Conflict",
+	"Insufficient Space",
+	"Insufficient Rows",
+	"Invalid Function",
+	"Invalid Parameter",
+	"Invalid Reference",
+	"Unknown Error",
+	"TPER Malfunction",
+	"Transaction Failure",
+	"Response Overflow",
+	"Authority Locked Out",
+};
+
+static const char *opal_error_to_human(int error)
+{
+	if (error == 0x3f)
+		return "Failed";
+
+	if (error >= sizeof(opal_errors) || error < 0)
+		return "Unknown Error";
+
+	return opal_errors[error];
+}
+
+/* User IDs used in the TCG storage SSCs */
+static const u8 OPALUID[][8] = {
+	/* users */
+
+	/* session management  */
+	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff},
+	/* special "thisSP" syntax */
+	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 },
+	/* Administrative SP */
+	{ 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x00, 0x01 },
+	/* Locking SP */
+	{ 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x00, 0x02 },
+	/* ENTERPRISE Locking SP  */
+	{ 0x00, 0x00, 0x02, 0x05, 0x00, 0x01, 0x00, 0x01 },
+	/* anybody */
+	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01 },
+	/* SID */
+	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06 },
+	/* ADMIN1 */
+	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x01, 0x00, 0x01 },
+	/* USER1 */
+	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x03, 0x00, 0x01 },
+	/* USER2 */
+	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x03, 0x00, 0x02 },
+	/* PSID user */
+	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x01, 0xff, 0x01 },
+	/* BandMaster 0 */
+	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x80, 0x01 },
+	 /* EraseMaster */
+	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x84, 0x01 },
+
+	/* tables */
+
+	/* Locking_GlobalRange */
+	{ 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00, 0x01 },
+	/* ACE_Locking_Range_Set_RdLocked UID */
+	{ 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0xE0, 0x01 },
+	/* ACE_Locking_Range_Set_WrLocked UID */
+	{ 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0xE8, 0x01 },
+	/* MBR Control */
+	{ 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x00, 0x01 },
+	/* Shadow MBR */
+	{ 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00 },
+	/* AUTHORITY_TABLE */
+	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00},
+	/* C_PIN_TABLE */
+	{ 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00},
+	/* OPAL Locking Info */
+	{ 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x01 },
+	/* Enterprise Locking Info */
+	{ 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00 },
+
+	/* C_PIN_TABLE object ID's */
+
+	/* C_PIN_MSID */
+	{ 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x84, 0x02},
+	/* C_PIN_SID */
+	{ 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01},
+	 /* C_PIN_ADMIN1 */
+	{ 0x00, 0x00, 0x00, 0x0B, 0x00, 0x01, 0x00, 0x01},
+
+	/* half UID's (only first 4 bytes used) */
+
+	/* Half-UID ? Authority_object_ref */
+	{ 0x00, 0x00, 0x0C, 0x05, 0xff, 0xff, 0xff, 0xff },
+	/* Half-UID ? Boolean ACE */
+	{ 0x00, 0x00, 0x04, 0x0E, 0xff, 0xff, 0xff, 0xff },
+
+	/* special value for omitted optional parameter */
+
+	/* HEXFF for omitted */
+	{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+};
+static const size_t OPAL_UID_LENGTH = 8;
+static const size_t OPAL_MSID_KEYLEN = 15;
+static const size_t OPAL_UID_LENGTH_HALF = 4;
+
+
+/* Enum to index OPALUID array */
+enum OPAL_UID {
+	/* users */
+	OPAL_SMUID_UID,
+	OPAL_THISSP_UID,
+	OPAL_ADMINSP_UID,
+	OPAL_LOCKINGSP_UID,
+	OPAL_ENTERPRISE_LOCKINGSP_UID,
+	OPAL_ANYBODY_UID,
+	OPAL_SID_UID,
+	OPAL_ADMIN1_UID,
+	OPAL_USER1_UID,
+	OPAL_USER2_UID,
+	OPAL_PSID_UID,
+	OPAL_ENTERPRISE_BANDMASTER0_UID,
+	OPAL_ENTERPRISE_ERASEMASTER_UID,
+	/* tables */
+	OPAL_LOCKINGRANGE_GLOBAL,
+	OPAL_LOCKINGRANGE_ACE_RDLOCKED,
+	OPAL_LOCKINGRANGE_ACE_WRLOCKED,
+	OPAL_MBRCONTROL,
+	OPAL_MBR,
+	OPAL_AUTHORITY_TABLE,
+	OPAL_C_PIN_TABLE,
+	OPAL_LOCKING_INFO_TABLE,
+	OPAL_ENTERPRISE_LOCKING_INFO_TABLE,
+	/* C_PIN_TABLE object ID's */
+	OPAL_C_PIN_MSID,
+	OPAL_C_PIN_SID,
+	OPAL_C_PIN_ADMIN1,
+	/* half UID's (only first 4 bytes used) */
+	OPAL_HALF_UID_AUTHORITY_OBJ_REF,
+	OPAL_HALF_UID_BOOLEAN_ACE,
+	/* omitted optional parameter */
+	OPAL_UID_HEXFF,
+};
+
+/*
+ * TCG Storage SSC Methods.
+ */
+static const u8 OPALMETHOD[][8] = {
+	/* Properties */
+	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01 },
+	/* STARTSESSION */
+	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02 },
+	/* Revert */
+	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x02 },
+	/* Activate */
+	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x03 },
+	/* Enterprise Get */
+	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06 },
+	/* Enterprise Set */
+	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07 },
+	/* NEXT */
+	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08 },
+	/* Enterprise Authenticate */
+	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0c },
+	/* GetACL */
+	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0d },
+	/* GenKey */
+	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10 },
+	/* revertSP */
+	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11 },
+	/* Get */
+	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x16 },
+	/* Set */
+	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17 },
+	/* Authenticate */
+	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1c },
+	/* Random */
+	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x01 },
+	/* Erase */
+	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x03 },
+};
+static const size_t OPAL_METHOD_LENGTH = 8;
+
+/* Enum for indexing the OPALMETHOD array */
+enum OPAL_METHOD {
+	OPAL_PROPERTIES,
+	OPAL_STARTSESSION,
+	OPAL_REVERT,
+	OPAL_ACTIVATE,
+	OPAL_EGET,
+	OPAL_ESET,
+	OPAL_NEXT,
+	OPAL_EAUTHENTICATE,
+	OPAL_GETACL,
+	OPAL_GENKEY,
+	OPAL_REVERTSP,
+	OPAL_GET,
+	OPAL_SET,
+	OPAL_AUTHENTICATE,
+	OPAL_RANDOM,
+	OPAL_ERASE,
+};
+
+enum OPAL_RESPONSE_TOKEN {
+	OPAL_DTA_TOKENID_BYTESTRING = 0xe0,
+	OPAL_DTA_TOKENID_SINT = 0xe1,
+	OPAL_DTA_TOKENID_UINT = 0xe2,
+	OPAL_DTA_TOKENID_TOKEN = 0xe3, /* actual token is returned */
+	OPAL_DTA_TOKENID_INVALID = 0X0
+};
+
+enum OPAL_TOKEN {
+	/* Boolean */
+	OPAL_TRUE = 0x01,
+	OPAL_FALSE = 0x00,
+	OPAL_BOOLEAN_EXPR = 0x03,
+	/* cellblocks */
+	OPAL_TABLE = 0x00,
+	OPAL_STARTROW = 0x01,
+	OPAL_ENDROW = 0x02,
+	OPAL_STARTCOLUMN = 0x03,
+	OPAL_ENDCOLUMN = 0x04,
+	OPAL_VALUES = 0x01,
+	/* authority table */
+	OPAL_PIN = 0x03,
+	/* locking tokens */
+	OPAL_RANGESTART = 0x03,
+	OPAL_RANGELENGTH = 0x04,
+	OPAL_READLOCKENABLED = 0x05,
+	OPAL_WRITELOCKENABLED = 0x06,
+	OPAL_READLOCKED = 0x07,
+	OPAL_WRITELOCKED = 0x08,
+	OPAL_ACTIVEKEY = 0x0A,
+	/* locking info table */
+	OPAL_MAXRANGES = 0x04,
+	 /* mbr control */
+	OPAL_MBRENABLE = 0x01,
+	OPAL_MBRDONE = 0x02,
+	/* properties */
+	OPAL_HOSTPROPERTIES = 0x00,
+	/* atoms */
+	OPAL_STARTLIST = 0xf0,
+	OPAL_ENDLIST = 0xf1,
+	OPAL_STARTNAME = 0xf2,
+	OPAL_ENDNAME = 0xf3,
+	OPAL_CALL = 0xf8,
+	OPAL_ENDOFDATA = 0xf9,
+	OPAL_ENDOFSESSION = 0xfa,
+	OPAL_STARTTRANSACTON = 0xfb,
+	OPAL_ENDTRANSACTON = 0xfC,
+	OPAL_EMPTYATOM = 0xff,
+	OPAL_WHERE = 0x00,
+};
+
+/* Useful tiny atoms.
+ * Useful for table columns etc
+ */
+enum OPAL_TINY_ATOM {
+	OPAL_TINY_UINT_00 = 0x00,
+	OPAL_TINY_UINT_01 = 0x01,
+	OPAL_TINY_UINT_02 = 0x02,
+	OPAL_TINY_UINT_03 = 0x03,
+	OPAL_TINY_UINT_04 = 0x04,
+	OPAL_TINY_UINT_05 = 0x05,
+	OPAL_TINY_UINT_06 = 0x06,
+	OPAL_TINY_UINT_07 = 0x07,
+	OPAL_TINY_UINT_08 = 0x08,
+	OPAL_TINY_UINT_09 = 0x09,
+	OPAL_TINY_UINT_10 = 0x0a,
+	OPAL_TINY_UINT_11 = 0x0b,
+	OPAL_TINY_UINT_12 = 0x0c,
+	OPAL_TINY_UINT_13 = 0x0d,
+	OPAL_TINY_UINT_14 = 0x0e,
+	OPAL_TINY_UINT_15 = 0x0f,
+};
+
+enum OPAL_ATOM_WIDTH {
+	OPAL_WIDTH_TINY,
+	OPAL_WIDTH_SHORT,
+	OPAL_WIDTH_MEDIUM,
+	OPAL_WIDTH_LONG,
+	OPAL_WIDTH_TOKEN
+};
+
+/* Locking state for a locking range */
+enum OPAL_LOCKINGSTATE {
+	OPAL_LOCKING_READWRITE = 0x01,
+	OPAL_LOCKING_READONLY = 0x02,
+	OPAL_LOCKING_LOCKED = 0x03,
+};
+
+/*
+ * Structures to build and decode the Opal SSC messages
+ * fields that are NOT really numeric are defined as u8[] to
+ * help reduce the endianness issues
+ */
+
+/* Comm Packet (header) for transmissions. */
+struct opal_compacket {
+	u32 reserved0;
+	u8 extendedComID[4];
+	u32 outstandingData;
+	u32 minTransfer;
+	u32 length;
+};
+
+/* Packet structure. */
+struct opal_packet {
+	u32 TSN;
+	u32 HSN;
+	u32 seq_number;
+	u16 reserved0;
+	u16 ack_type;
+	u32 acknowledgment;
+	u32 length;
+};
+
+/* Data sub packet header */
+struct opal_data_subpacket {
+	u8 reserved0[6];
+	u16 kind;
+	u32 length;
+};
+
+/* header of a response */
+struct opal_header {
+	struct opal_compacket cp;
+	struct opal_packet pkt;
+	struct opal_data_subpacket subpkt;
+};
+
+#define FC_TPER       0x0001
+#define FC_LOCKING    0x0002
+#define FC_GEOMETRY   0x0003
+#define FC_ENTERPRISE 0x0100
+#define FC_DATASTORE  0x0202
+#define FC_SINGLEUSER 0x0201
+#define FC_OPALV100   0x0200
+#define FC_OPALV200   0x0203
+
+/*
+ * The Discovery 0 Header. As defined in
+ * Opal SSC Documentation
+ */
+struct d0_header {
+	u32 length; /* the length of the header 48 in 2.00.100 */
+	u32 revision; /**< revision of the header 1 in 2.00.100 */
+	u32 reserved01;
+	u32 reserved02;
+	/*
+	 * the remainder of the structure is vendor specific and will not be
+	 * addressed now
+	 */
+	u8 ignored[32];
+};
+
+/*
+ * TPer Feature Descriptor. Contains flags indicating support for the
+ * TPer features described in the OPAL specification. The names match the
+ * OPAL terminology
+ *
+ * code == 0x001 in 2.00.100
+ */
+struct d0_tper_features {
+	/*
+	 * supported_features bits:
+	 * bit 7: reserved
+	 * bit 6: com ID management
+	 * bit 5: reserved
+	 * bit 4: streaming support
+	 * bit 3: buffer management
+	 * bit 2: ACK/NACK
+	 * bit 1: async
+	 * bit 0: sync
+	 */
+	u8 supported_features;
+	/*
+	 * bytes 5 through 15 are reserved, but we represent the first 3 as
+	 * u8 to keep the other two 32bits integers aligned.
+	 */
+	u8 reserved01[3];
+	u32 reserved02;
+	u32 reserved03;
+};
+
+/*
+ * Locking Feature Descriptor. Contains flags indicating support for the
+ * locking features described in the OPAL specification. The names match the
+ * OPAL terminology
+ *
+ * code == 0x0002 in 2.00.100
+ */
+struct d0_locking_features {
+	/*
+	 * supported_features bits:
+	 * bits 6-7: reserved
+	 * bit 5: MBR done
+	 * bit 4: MBR enabled
+	 * bit 3: media encryption
+	 * bit 2: locked
+	 * bit 1: locking enabled
+	 * bit 0: locking supported
+	 */
+	u8 supported_features;
+	/*
+	 * bytes 5 through 15 are reserved, but we represent the first 3 as
+	 * u8 to keep the other two 32bits integers aligned.
+	 */
+	u8 reserved01[3];
+	u32 reserved02;
+	u32 reserved03;
+};
+
+/*
+ * Geometry Feature Descriptor. Contains flags indicating support for the
+ * geometry features described in the OPAL specification. The names match the
+ * OPAL terminology
+ *
+ * code == 0x0003 in 2.00.100
+ */
+struct d0_geometry_features {
+	/*
+	 * skip 32 bits from header, needed to align the struct to 64 bits.
+	 */
+	u8 header[4];
+	/*
+	 * reserved01:
+	 * bits 1-6: reserved
+	 * bit 0: align
+	 */
+	u8 reserved01;
+	u8 reserved02[7];
+	u32 logical_block_size;
+	u64 alignment_granularity;
+	u64 lowest_aligned_lba;
+};
+
+/*
+ * Enterprise SSC Feature
+ *
+ * code == 0x0100
+ */
+struct d0_enterprise_ssc {
+	u16 baseComID;
+	u16 numComIDs;
+	/* range_crossing:
+	 * bits 1-6: reserved
+	 * bit 0: range crossing
+	 */
+	u8 range_crossing;
+	u8 reserved01;
+	u16 reserved02;
+	u32 reserved03;
+	u32 reserved04;
+};
+
+/*
+ * Opal V1 feature
+ *
+ * code == 0x0200
+ */
+struct d0_opal_v100 {
+	u16 baseComID;
+	u16 numComIDs;
+};
+
+/*
+ * Single User Mode feature
+ *
+ * code == 0x0201
+ */
+struct d0_single_user_mode {
+	u32 num_locking_objects;
+	/* reserved01:
+	 * bit 0: any
+	 * bit 1: all
+	 * bit 2: policy
+	 * bits 3-7: reserved
+	 */
+	u8 reserved01;
+	u8 reserved02;
+	u16 reserved03;
+	u32 reserved04;
+};
+
+/*
+ * Additonal Datastores feature
+ *
+ * code == 0x0202
+ */
+struct d0_datastore_table {
+	u16 reserved01;
+	u16 max_tables;
+	u32 max_size_tables;
+	u32 table_size_alignment;
+};
+
+/*
+ * OPAL 2.0 feature
+ *
+ * code == 0x0203
+ */
+struct d0_opal_v200 {
+	u16 baseComID;
+	u16 numComIDs;
+	/* range_crossing:
+	 * bits 1-6: reserved
+	 * bit 0: range crossing
+	 */
+	u8 range_crossing;
+	/* num_locking_admin_auth:
+	 * not aligned to 16 bits, so use two u8.
+	 * stored in big endian:
+	 * 0: MSB
+	 * 1: LSB
+	 */
+	u8 num_locking_admin_auth[2];
+	/* num_locking_user_auth:
+	 * not aligned to 16 bits, so use two u8.
+	 * stored in big endian:
+	 * 0: MSB
+	 * 1: LSB
+	 */
+	u8 num_locking_user_auth[2];
+	u8 initialPIN;
+	u8 revertedPIN;
+	u8 reserved01;
+	u32 reserved02;
+};
+
+/* Union of features used to parse the discovery 0 response */
+struct d0_features {
+	u16 code;
+	/*
+	 * r_version bits:
+	 * bits 4-7: version
+	 * bits 0-3: reserved
+	 */
+	u8 r_version;
+	u8 length;
+	u8 features[];
+};
+
+struct key *request_user_key(const char *master_desc, const u8 **master_key,
+			     size_t *master_keylen);
+
+#endif /* _NVME_OPAL_INTERNAL_H */
diff --git a/lib/sed-opal_key.c b/lib/sed-opal_key.c
new file mode 100644
index 0000000..0b4de01
--- /dev/null
+++ b/lib/sed-opal_key.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright ? 2016 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Author:
+ *    Rafael Antognolli <rafael.antognolli at intel.com>
+ */
+
+#include <linux/key.h>
+#include "sed-opal_internal.h"
+
+struct key *request_user_key(const char *master_desc, const u8 **master_key,
+			     size_t *master_keylen)
+{
+	const struct user_key_payload *upayload;
+	struct key *ukey;
+
+	ukey = request_key(&key_type_user, master_desc, NULL);
+	if (IS_ERR(ukey))
+		goto error;
+
+	down_read(&ukey->sem);
+	upayload = user_key_payload(ukey);
+	*master_key = upayload->data;
+	*master_keylen = upayload->datalen;
+error:
+	return ukey;
+}
diff --git a/lib/sed.c b/lib/sed.c
new file mode 100644
index 0000000..06cacd9
--- /dev/null
+++ b/lib/sed.c
@@ -0,0 +1,303 @@
+#include <linux/blkdev.h>
+#include <linux/sed.h>
+#include <linux/sed-opal.h>
+
+#ifndef CONFIG_SED_OPAL
+static int sed_opal_save(struct block_device *bdev, struct sed_key *sed)
+	{ return -EOPNOTSUPP; }
+static int sed_opal_lock_unlock(struct block_device *bdev, struct sed_key *key)
+	{ return -EOPNOTSUPP; }
+static int sed_opal_take_ownership(struct block_device *bdev, struct sed_key *key)
+	{ return -EOPNOTSUPP; }
+static int sed_opal_activate_lsp(struct block_device *bdev, struct sed_key *key)
+	{ return -EOPNOTSUPP; }
+static int sed_opal_set_pw(struct block_device *bdev, struct sed_key *key)
+	{ return -EOPNOTSUPP; }
+static int sed_opal_activate_user(struct block_device *bdev, struct sed_key *key)
+	{ return -EOPNOTSUPP; }
+static int sed_opal_reverttper(struct block_device *bdev, struct sed_key *key)
+	{ return -EOPNOTSUPP; }
+static int sed_opal_setup_locking_range(struct block_device *bdev, struct sed_key *key)
+	{ return -EOPNOTSUPP; }
+static int sed_opal_adduser_to_lr(struct block_device *bdev, struct sed_key *key)
+	{ return -EOPNOTSUPP; }
+static int sed_opal_do_mbr(struct block_device *bdev, struct sed_key *key)
+	{ return -EOPNOTSUPP; }
+static int sed_opal_erase_lr(struct block_device *bdev, struct sed_key *key)
+	{ return -EOPNOTSUPP; }
+
+#else
+
+static int sed_opal_save(struct block_device *bdev, struct sed_key *key)
+{
+
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	return opal_save(bdev, key);
+}
+
+static int sed_opal_lock_unlock(struct block_device *bdev, struct sed_key *key)
+{
+
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	return opal_lock_unlock(bdev, key);
+}
+
+static int sed_opal_take_ownership(struct block_device *bdev,
+				   struct sed_key *key)
+{
+
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	return opal_take_ownership(bdev, key);
+}
+
+static int sed_opal_activate_lsp(struct block_device *bdev,
+				 struct sed_key *key)
+{
+
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	return opal_activate_lsp(bdev, key);
+}
+
+static int sed_opal_set_pw(struct block_device *bdev,
+			   struct sed_key *key)
+{
+
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	return opal_set_new_pw(bdev, key);
+}
+
+static int sed_opal_activate_user(struct block_device *bdev,
+				  struct sed_key *key)
+{
+
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	return opal_activate_user(bdev, key);
+}
+
+static int sed_opal_reverttper(struct block_device *bdev,
+			       struct sed_key *key)
+{
+
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	return opal_reverttper(bdev, key);
+}
+
+static int sed_opal_setup_lr(struct block_device *bdev,
+			     struct sed_key *key)
+{
+
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	return opal_setup_locking_range(bdev, key);
+}
+
+static int sed_opal_adduser_to_lr(struct block_device *bdev,
+			     struct sed_key *key)
+{
+
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	return opal_add_user_to_lr(bdev, key);
+}
+
+static int sed_opal_do_mbr(struct block_device *bdev,
+			   struct sed_key *key)
+{
+
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	return opal_enable_disable_shadow_mbr(bdev, key);
+}
+
+static int sed_opal_erase_lr(struct block_device *bdev,
+			     struct sed_key *key)
+{
+
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	return opal_erase_locking_range(bdev, key);
+}
+#endif
+
+int sed_save(struct block_device *bdev, struct sed_key *key)
+{
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	switch (key->sed_type) {
+	case OPAL_LOCK_UNLOCK:
+		return sed_opal_save(bdev, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_lock_unlock(struct block_device *bdev, struct sed_key *key)
+{
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	switch (key->sed_type) {
+	case OPAL_LOCK_UNLOCK:
+		return sed_opal_lock_unlock(bdev, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_take_ownership(struct block_device *bdev, struct sed_key *key)
+{
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	switch (key->sed_type) {
+	case OPAL:
+		return sed_opal_take_ownership(bdev, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_activate_lsp(struct block_device *bdev, struct sed_key *key)
+{
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	switch (key->sed_type) {
+	case OPAL:
+		return sed_opal_activate_lsp(bdev, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_set_pw(struct block_device *bdev, struct sed_key *key)
+{
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	switch (key->sed_type) {
+	case OPAL_PW:
+		return sed_opal_set_pw(bdev, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_activate_user(struct block_device *bdev, struct sed_key *key)
+{
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	switch (key->sed_type) {
+	case OPAL_ACT_USR:
+		return sed_opal_activate_user(bdev, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_reverttper(struct block_device *bdev, struct sed_key *key)
+{
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	switch (key->sed_type) {
+	case OPAL:
+		return sed_opal_reverttper(bdev, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_setup_locking_range(struct block_device *bdev, struct sed_key *key)
+{
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	switch (key->sed_type) {
+	case OPAL_LR_SETUP:
+		return sed_opal_setup_lr(bdev, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_adduser_to_lr(struct block_device *bdev, struct sed_key *key)
+{
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	switch (key->sed_type) {
+	case OPAL_LOCK_UNLOCK:
+		return sed_opal_adduser_to_lr(bdev, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_do_mbr(struct block_device *bdev, struct sed_key *key)
+{
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	switch (key->sed_type) {
+	case OPAL_MBR_DATA:
+		return sed_opal_do_mbr(bdev, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_erase_lr(struct block_device *bdev, struct sed_key *key)
+{
+	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
+	    !bdev->bd_disk->fops->sec_ops)
+		return -EOPNOTSUPP;
+
+	switch (key->sed_type) {
+	case OPAL:
+		return sed_opal_erase_lr(bdev, key);
+	}
+
+	return -EOPNOTSUPP;
+}
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC PATCH 3/6] lib: Add Sed to Kconfig and Makefile
  2016-10-31 21:58 [RFC PATCH 0/6] Sed Opal Scott Bauer
  2016-10-31 21:58 ` [RFC PATCH 1/6] Include: Add definitions for sed Scott Bauer
  2016-10-31 21:58 ` [RFC PATCH 2/6] lib: Add Sed-opal library Scott Bauer
@ 2016-10-31 21:58 ` Scott Bauer
  2016-10-31 21:58 ` [RFC PATCH 4/6] include: Add sec_ops to block device operations Scott Bauer
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Scott Bauer @ 2016-10-31 21:58 UTC (permalink / raw)


Signed-off-by: Scott Bauer <scott.bauer at intel.com>
Signed-off-by: Rafael Antognolli <Rafael.Antognolli at intel.com>
---
 lib/Kconfig  | 12 ++++++++++++
 lib/Makefile |  7 +++++++
 2 files changed, 19 insertions(+)

diff --git a/lib/Kconfig b/lib/Kconfig
index 260a80e..47c39ac 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -547,6 +547,18 @@ config STACKDEPOT
 	bool
 	select STACKTRACE
 
+config SED
+	bool "Self-Encrypting Drive library"
+	help
+	  Provides self-encrypting drive helpers
+
+config SED_OPAL
+	bool "Self-Encrypting Drive library - Opal implementation"
+	depends on SED
+	help
+	  Provides self-encrypting drive helpers for drives that implement the
+	  Opal spec.
+
 config SBITMAP
 	bool
 
diff --git a/lib/Makefile b/lib/Makefile
index 50144a3..976da7a 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -229,4 +229,11 @@ obj-$(CONFIG_UBSAN) += ubsan.o
 
 UBSAN_SANITIZE_ubsan.o := n
 
+
+obj-$(CONFIG_SED) += sed.o
+
+obj-$(CONFIG_SED_OPAL) += sed-opal.o
+obj-$(CONFIG_SED_OPAL) += sed-opal_key.o
+
 obj-$(CONFIG_SBITMAP) += sbitmap.o
+
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC PATCH 4/6] include: Add sec_ops to block device operations
  2016-10-31 21:58 [RFC PATCH 0/6] Sed Opal Scott Bauer
                   ` (2 preceding siblings ...)
  2016-10-31 21:58 ` [RFC PATCH 3/6] lib: Add Sed to Kconfig and Makefile Scott Bauer
@ 2016-10-31 21:58 ` Scott Bauer
  2016-10-31 21:58 ` [RFC PATCH 5/6] nvme: Add unlock_from_suspend Scott Bauer
  2016-10-31 21:58 ` [RFC PATCH 6/6] block: ioctl: Wire up Sed to block ioctls Scott Bauer
  5 siblings, 0 replies; 16+ messages in thread
From: Scott Bauer @ 2016-10-31 21:58 UTC (permalink / raw)


Signed-off-by: Scott Bauer <scott.bauer at intel.com>
Signed-off-by: Rafael Antognolli <Rafael.Antognolli at intel.com>
---
 include/linux/blkdev.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index c47c358..6fceff1 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1690,6 +1690,7 @@ struct block_device_operations {
 	void (*swap_slot_free_notify) (struct block_device *, unsigned long);
 	struct module *owner;
 	const struct pr_ops *pr_ops;
+	struct sec_ops *sec_ops;
 };
 
 extern int __blkdev_driver_ioctl(struct block_device *, fmode_t, unsigned int,
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC PATCH 5/6] nvme: Add unlock_from_suspend
  2016-10-31 21:58 [RFC PATCH 0/6] Sed Opal Scott Bauer
                   ` (3 preceding siblings ...)
  2016-10-31 21:58 ` [RFC PATCH 4/6] include: Add sec_ops to block device operations Scott Bauer
@ 2016-10-31 21:58 ` Scott Bauer
  2016-11-01  8:18   ` Sagi Grimberg
  2016-10-31 21:58 ` [RFC PATCH 6/6] block: ioctl: Wire up Sed to block ioctls Scott Bauer
  5 siblings, 1 reply; 16+ messages in thread
From: Scott Bauer @ 2016-10-31 21:58 UTC (permalink / raw)


This patch adds a new function unlock_from_suspend which is used
to call into the Opal code to attempt to unlock Locking Ranges,
after a suspend-to-RAM.

The patch also modifies nvme_req_needs_retry to *not* retry
a request that failed due to a NVME_SC_ACCESS_DENIED, which
gets returned if a request is attempting to much with a locked
range. The range won't magically unlock itself without user
interaction so we shouldn't retry the request -- it will fail
again.

Signed-off-by: Scott Bauer <scott.bauer at intel.com>
Signed-off-by: Rafael Antognolli <Rafael.Antognolli at intel.com>
---
 drivers/nvme/host/core.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/nvme/host/nvme.h |   4 +-
 drivers/nvme/host/pci.c  |  19 ++++---
 3 files changed, 149 insertions(+), 8 deletions(-)

diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 79e679d..1321331 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -28,6 +28,8 @@
 #include <linux/t10-pi.h>
 #include <scsi/sg.h>
 #include <asm/unaligned.h>
+#include <linux/sed.h>
+#include <linux/sed-opal.h>
 
 #include "nvme.h"
 #include "fabrics.h"
@@ -1067,6 +1069,137 @@ static const struct pr_ops nvme_pr_ops = {
 	.pr_clear	= nvme_pr_clear,
 };
 
+struct sed_cb_data {
+	sec_cb	*cb;
+	void	*cb_data;
+	struct nvme_command cmd;
+};
+
+static void sec_submit_endio(struct request *req, int error)
+{
+	struct sed_cb_data *sed_data = req->end_io_data;
+
+	if (sed_data->cb)
+		sed_data->cb(error, sed_data->cb_data);
+
+	kfree(sed_data);
+	blk_mq_free_request(req);
+}
+
+static int nvme_insert_rq(struct request_queue *q, struct request *rq,
+			  int at_head, rq_end_io_fn *done)
+{
+	WARN_ON(rq->cmd_type == REQ_TYPE_FS);
+
+	rq->end_io = done;
+
+	if (!q->mq_ops)
+		return -EINVAL;
+
+	blk_mq_insert_request(rq, at_head, true, true);
+
+	return 0;
+}
+
+static int nvme_sec_submit(void *data, u8 opcode, u16 SPSP,
+			   u8 SECP, void *buffer, size_t len,
+			   sec_cb *cb, void *cb_data)
+{
+	struct request_queue *q;
+	struct request *req;
+	struct sed_cb_data *sed_data;
+	struct nvme_ns *ns;
+	struct nvme_command *cmd;
+	int ret;
+
+	ns = data;//bdev->bd_disk->private_data;
+
+	sed_data = kzalloc(sizeof(*sed_data), GFP_NOWAIT);
+	if (!sed_data)
+		return -ENOMEM;
+	sed_data->cb = cb;
+	sed_data->cb_data = cb_data;
+	cmd = &sed_data->cmd;
+
+	cmd->common.opcode = opcode;
+	cmd->common.nsid = ns->ns_id;
+	cmd->common.cdw10[0] = SECP << 24 | SPSP << 8;
+	cmd->common.cdw10[1] = len;
+
+	q = ns->ctrl->admin_q;
+
+	req = nvme_alloc_request(q, cmd, 0, NVME_QID_ANY);
+	if (IS_ERR(req)) {
+		ret = PTR_ERR(req);
+		goto err_free;
+	}
+
+	req->timeout = ADMIN_TIMEOUT;
+	req->special = NULL;
+
+	if (buffer && len) {
+		ret = blk_rq_map_kern(q, req, buffer, len, GFP_NOWAIT);
+		if (ret) {
+			blk_mq_free_request(req);
+			goto err_free;
+		}
+	}
+
+	req->end_io_data = sed_data;
+	//req->rq_disk = bdev->bd_disk;
+
+	return nvme_insert_rq(q, req, 1, sec_submit_endio);
+
+err_free:
+	kfree(sed_data);
+	return ret;
+}
+
+static int nvme_sec_recv(void *data, u16 SPSP, u8 SECP,
+			 void *buffer, size_t len,
+			 sec_cb *cb, void *cb_data)
+{
+	return nvme_sec_submit(data, nvme_admin_security_recv, SPSP, SECP,
+			       buffer, len, cb, cb_data);
+}
+
+static int nvme_sec_send(void *data, u16 SPSP, u8 SECP,
+			 void *buffer, size_t len,
+			 sec_cb *cb, void *cb_data)
+{
+	return nvme_sec_submit(data, nvme_admin_security_send, SPSP, SECP,
+			       buffer, len, cb, cb_data);
+}
+
+void nvme_unlock_from_suspend(struct nvme_ctrl *ctrl)
+{
+	struct opal_suspend_unlk ulk = { 0 };
+	struct nvme_ns *ns;
+	char diskname[DISK_NAME_LEN];
+       	mutex_lock(&ctrl->namespaces_mutex);
+	if (list_empty(&ctrl->namespaces))
+		goto out_no_namespace;
+	ulk.data = ns =list_first_entry(&ctrl->namespaces, struct nvme_ns, list);
+	mutex_unlock(&ctrl->namespaces_mutex);
+	snprintf(diskname, sizeof(diskname), "%sn%d",
+		 dev_name(ctrl->device), ns->instance);
+	ulk.name = diskname;
+
+	ulk.ops.send = nvme_sec_send;
+	ulk.ops.recv = nvme_sec_recv;
+	opal_unlock_from_suspend(&ulk);
+
+	return;
+ out_no_namespace:
+	mutex_unlock(&ctrl->namespaces_mutex);
+}
+EXPORT_SYMBOL_GPL(nvme_unlock_from_suspend);
+
+static struct sec_ops nvme_sec_ops = {
+	.send	= nvme_sec_send,
+	.recv	= nvme_sec_recv,
+};
+
 static const struct block_device_operations nvme_fops = {
 	.owner		= THIS_MODULE,
 	.ioctl		= nvme_ioctl,
@@ -1076,6 +1209,7 @@ static const struct block_device_operations nvme_fops = {
 	.getgeo		= nvme_getgeo,
 	.revalidate_disk= nvme_revalidate_disk,
 	.pr_ops		= &nvme_pr_ops,
+	.sec_ops	= &nvme_sec_ops,
 };
 
 static int nvme_wait_ready(struct nvme_ctrl *ctrl, u64 cap, bool enabled)
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index d47f5a5..ac7e5b1 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -240,7 +240,8 @@ static inline int nvme_error_status(u16 status)
 
 static inline bool nvme_req_needs_retry(struct request *req, u16 status)
 {
-	return !(status & NVME_SC_DNR || blk_noretry_request(req)) &&
+	return !(status & NVME_SC_DNR || status & NVME_SC_ACCESS_DENIED ||
+		 blk_noretry_request(req)) &&
 		(jiffies - req->start_time) < req->timeout &&
 		req->retries < nvme_max_retries;
 }
@@ -259,6 +260,7 @@ int nvme_init_identify(struct nvme_ctrl *ctrl);
 
 void nvme_queue_scan(struct nvme_ctrl *ctrl);
 void nvme_remove_namespaces(struct nvme_ctrl *ctrl);
+void nvme_unlock_from_suspend(struct nvme_ctrl *ctrl);
 
 #define NVME_NR_AERS	1
 void nvme_complete_async_event(struct nvme_ctrl *ctrl,
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index 0248d0e..18fd878 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -43,6 +43,7 @@
 #include <linux/types.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
 #include <asm/unaligned.h>
+#include <linux/sed-opal.h>
 
 #include "nvme.h"
 
@@ -582,6 +583,7 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
 	struct nvme_command cmnd;
 	unsigned map_len;
 	int ret = BLK_MQ_RQ_QUEUE_OK;
+	unsigned long flags;
 
 	/*
 	 * If formated with metadata, require the block layer provide a buffer
@@ -614,18 +616,18 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
 	cmnd.common.command_id = req->tag;
 	blk_mq_start_request(req);
 
-	spin_lock_irq(&nvmeq->q_lock);
+	spin_lock_irqsave(&nvmeq->q_lock, flags);
 	if (unlikely(nvmeq->cq_vector < 0)) {
 		if (ns && !test_bit(NVME_NS_DEAD, &ns->flags))
 			ret = BLK_MQ_RQ_QUEUE_BUSY;
 		else
 			ret = BLK_MQ_RQ_QUEUE_ERROR;
-		spin_unlock_irq(&nvmeq->q_lock);
+		spin_unlock_irqrestore(&nvmeq->q_lock, flags);
 		goto out;
 	}
 	__nvme_submit_cmd(nvmeq, &cmnd);
 	nvme_process_cq(nvmeq);
-	spin_unlock_irq(&nvmeq->q_lock);
+	spin_unlock_irqrestore(&nvmeq->q_lock, flags);
 	return BLK_MQ_RQ_QUEUE_OK;
 out:
 	nvme_free_iod(dev, req);
@@ -635,11 +637,11 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
 static void nvme_complete_rq(struct request *req)
 {
 	struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
-	struct nvme_dev *dev = iod->nvmeq->dev;
+	struct nvme_queue *nvmeq = iod->nvmeq;
+	struct nvme_dev *dev = nvmeq->dev;
 	int error = 0;
 
 	nvme_unmap_data(dev, req);
-
 	if (unlikely(req->errors)) {
 		if (nvme_req_needs_retry(req, req->errors)) {
 			req->retries++;
@@ -658,7 +660,6 @@ static void nvme_complete_rq(struct request *req)
 			"completing aborted command with status: %04x\n",
 			req->errors);
 	}
-
 	blk_mq_end_request(req, error);
 }
 
@@ -1758,10 +1759,11 @@ static void nvme_reset_work(struct work_struct *work)
 {
 	struct nvme_dev *dev = container_of(work, struct nvme_dev, reset_work);
 	int result = -ENODEV;
-
+	bool was_suspend = false;
 	if (WARN_ON(dev->ctrl.state == NVME_CTRL_RESETTING))
 		goto out;
 
+	was_suspend = !!(dev->ctrl.ctrl_config & NVME_CC_SHN_NORMAL);
 	/*
 	 * If we're called to reset a live controller first shut it down before
 	 * moving on.
@@ -1789,6 +1791,9 @@ static void nvme_reset_work(struct work_struct *work)
 	if (result)
 		goto out;
 
+	if (was_suspend)
+		nvme_unlock_from_suspend(&dev->ctrl);
+
 	result = nvme_setup_io_queues(dev);
 	if (result)
 		goto out;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC PATCH 6/6] block: ioctl: Wire up Sed to block ioctls
  2016-10-31 21:58 [RFC PATCH 0/6] Sed Opal Scott Bauer
                   ` (4 preceding siblings ...)
  2016-10-31 21:58 ` [RFC PATCH 5/6] nvme: Add unlock_from_suspend Scott Bauer
@ 2016-10-31 21:58 ` Scott Bauer
  5 siblings, 0 replies; 16+ messages in thread
From: Scott Bauer @ 2016-10-31 21:58 UTC (permalink / raw)


Signed-off-by: Scott Bauer <scott.bauer at intel.com>
Signed-off-by: Rafael Antognolli <Rafael.Antognolli at intel.com>
---
 block/compat_ioctl.c |  14 ++++
 block/ioctl.c        | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 213 insertions(+), 1 deletion(-)

diff --git a/block/compat_ioctl.c b/block/compat_ioctl.c
index 556826a..2b83019 100644
--- a/block/compat_ioctl.c
+++ b/block/compat_ioctl.c
@@ -10,6 +10,7 @@
 #include <linux/syscalls.h>
 #include <linux/types.h>
 #include <linux/uaccess.h>
+#include <linux/sed.h>
 
 static int compat_put_ushort(unsigned long arg, unsigned short val)
 {
@@ -746,6 +747,19 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 	case BLKTRACETEARDOWN: /* compatible */
 		ret = blk_trace_ioctl(bdev, cmd, compat_ptr(arg));
 		return ret;
+	case IOC_SED_SAVE:
+	case IOC_SED_LOCK_UNLOCK:
+	case IOC_SED_TAKE_OWNERSHIP:
+	case IOC_SED_ACTIVATE_LSP:
+	case IOC_SED_SET_PW:
+	case IOC_SED_ACTIVATE_USR:
+	case IOC_SED_REVERT_TPR:
+	case IOC_SED_LR_SETUP:
+	case IOC_SED_ADD_USR_TO_LR:
+	case IOC_SED_ENABLE_DISABLE_MBR:
+	case IOC_SED_ERASE_LR:
+		return blkdev_ioctl(bdev, mode, cmd,
+				(unsigned long)compat_ptr(arg));
 	default:
 		if (disk->fops->compat_ioctl)
 			ret = disk->fops->compat_ioctl(bdev, mode, cmd, arg);
diff --git a/block/ioctl.c b/block/ioctl.c
index 755119c..f5c971b 100644
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -8,6 +8,7 @@
 #include <linux/fs.h>
 #include <linux/blktrace_api.h>
 #include <linux/pr.h>
+#include <linux/sed.h>
 #include <asm/uaccess.h>
 
 static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user *arg)
@@ -392,6 +393,181 @@ static int blkdev_pr_clear(struct block_device *bdev,
 	return ops->pr_clear(bdev, c.key);
 }
 
+static int blkdev_sed_save(struct block_device *bdev,
+		struct sed_key __user *arg)
+{
+	const struct sec_ops *ops = bdev->bd_disk->fops->sec_ops;
+	struct sed_key k;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (!ops || !ops->send || !ops->recv)
+		return -EOPNOTSUPP;
+	if (copy_from_user(&k, arg, sizeof(k)))
+		return -EFAULT;
+
+	return sed_save(bdev, &k);
+}
+
+static int blkdev_sed_lock_unlock(struct block_device *bdev,
+		struct sed_key __user *arg)
+{
+	const struct sec_ops *ops = bdev->bd_disk->fops->sec_ops;
+	struct sed_key k;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (!ops || !ops->send || !ops->recv)
+		return -EOPNOTSUPP;
+	if (copy_from_user(&k, arg, sizeof(k)))
+		return -EFAULT;
+
+	return sed_lock_unlock(bdev, &k);
+}
+
+static int blkdev_sed_take_ownership(struct block_device *bdev,
+		struct sed_key __user *arg)
+{
+	const struct sec_ops *ops = bdev->bd_disk->fops->sec_ops;
+	struct sed_key k;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (!ops || !ops->send || !ops->recv)
+		return -EOPNOTSUPP;
+	if (copy_from_user(&k, arg, sizeof(k)))
+		return -EFAULT;
+
+	return sed_take_ownership(bdev, &k);
+}
+
+static int blkdev_sed_activate_lsp(struct block_device *bdev,
+		struct sed_key __user *arg)
+{
+	const struct sec_ops *ops = bdev->bd_disk->fops->sec_ops;
+	struct sed_key k;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (!ops || !ops->send || !ops->recv)
+		return -EOPNOTSUPP;
+	if (copy_from_user(&k, arg, sizeof(k)))
+		return -EFAULT;
+
+	return sed_activate_lsp(bdev, &k);
+}
+
+static int blkdev_sed_set_pw(struct block_device *bdev,
+			     struct sed_key __user *arg)
+{
+	const struct sec_ops *ops = bdev->bd_disk->fops->sec_ops;
+	struct sed_key k;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (!ops || !ops->send || !ops->recv)
+		return -EOPNOTSUPP;
+	if (copy_from_user(&k, arg, sizeof(k)))
+		return -EFAULT;
+
+	return sed_set_pw(bdev, &k);
+}
+
+static int blkdev_sed_activate_user(struct block_device *bdev,
+				    struct sed_key __user *arg)
+{
+	const struct sec_ops *ops = bdev->bd_disk->fops->sec_ops;
+	struct sed_key k;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (!ops || !ops->send || !ops->recv)
+		return -EOPNOTSUPP;
+	if (copy_from_user(&k, arg, sizeof(k)))
+		return -EFAULT;
+	return sed_activate_user(bdev, &k);
+}
+
+static int blkdev_sed_reverttper(struct block_device *bdev,
+				 struct sed_key __user *arg)
+{
+	const struct sec_ops *ops = bdev->bd_disk->fops->sec_ops;
+	struct sed_key k;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (!ops || !ops->send || !ops->recv)
+		return -EOPNOTSUPP;
+	if (copy_from_user(&k, arg, sizeof(k)))
+		return -EFAULT;
+
+	return sed_reverttper(bdev, &k);
+}
+
+static int blkdev_sed_setuplr(struct block_device *bdev,
+			      struct sed_key __user *arg)
+{
+	const struct sec_ops *ops = bdev->bd_disk->fops->sec_ops;
+	struct sed_key k;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (!ops || !ops->send || !ops->recv)
+		return -EOPNOTSUPP;
+	if (copy_from_user(&k, arg, sizeof(k)))
+		return -EFAULT;
+
+	return sed_setup_locking_range(bdev, &k);
+}
+
+static int blkdev_sed_add_usr_to_lr(struct block_device *bdev,
+				    struct sed_key __user *arg)
+{
+	const struct sec_ops *ops = bdev->bd_disk->fops->sec_ops;
+	struct sed_key k;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (!ops || !ops->send || !ops->recv)
+		return -EOPNOTSUPP;
+	if (copy_from_user(&k, arg, sizeof(k)))
+		return -EFAULT;
+
+	return sed_adduser_to_lr(bdev, &k);
+}
+
+static int blkdev_sed_do_mbr(struct block_device *bdev,
+			     struct sed_key __user *arg)
+{
+	const struct sec_ops *ops = bdev->bd_disk->fops->sec_ops;
+	struct sed_key k;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (!ops || !ops->send || !ops->recv)
+		return -EOPNOTSUPP;
+	if (copy_from_user(&k, arg, sizeof(k)))
+		return -EFAULT;
+
+	return sed_do_mbr(bdev, &k);
+}
+
+static int blkdev_sed_erase_lr(struct block_device *bdev,
+			     struct sed_key __user *arg)
+{
+	const struct sec_ops *ops = bdev->bd_disk->fops->sec_ops;
+	struct sed_key k;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (!ops || !ops->send || !ops->recv)
+		return -EOPNOTSUPP;
+	if (copy_from_user(&k, arg, sizeof(k)))
+		return -EFAULT;
+
+	return sed_erase_lr(bdev, &k);
+}
+
 /*
  * Is it an unrecognized ioctl? The correct returns are either
  * ENOTTY (final) or ENOIOCTLCMD ("I don't know this one, try a
@@ -551,7 +727,7 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
 		return put_ushort(arg, !blk_queue_nonrot(bdev_get_queue(bdev)));
 	case BLKRASET:
 	case BLKFRASET:
-		if(!capable(CAP_SYS_ADMIN))
+		if (!capable(CAP_SYS_ADMIN))
 			return -EACCES;
 		bdi = blk_get_backing_dev_info(bdev);
 		bdi->ra_pages = (arg * 512) / PAGE_SIZE;
@@ -586,6 +762,28 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
 		return blkdev_pr_preempt(bdev, argp, true);
 	case IOC_PR_CLEAR:
 		return blkdev_pr_clear(bdev, argp);
+	case IOC_SED_SAVE:
+		return blkdev_sed_save(bdev, argp);
+	case IOC_SED_LOCK_UNLOCK:
+		return blkdev_sed_lock_unlock(bdev, argp);
+	case IOC_SED_TAKE_OWNERSHIP:
+		return blkdev_sed_take_ownership(bdev, argp);
+	case IOC_SED_ACTIVATE_LSP:
+		return blkdev_sed_activate_lsp(bdev, argp);
+	case IOC_SED_SET_PW:
+		return blkdev_sed_set_pw(bdev, argp);
+	case IOC_SED_ACTIVATE_USR:
+		return blkdev_sed_activate_user(bdev, argp);
+	case IOC_SED_REVERT_TPR:
+		return blkdev_sed_reverttper(bdev, argp);
+	case IOC_SED_LR_SETUP:
+		return blkdev_sed_setuplr(bdev, argp);
+	case IOC_SED_ADD_USR_TO_LR:
+		return blkdev_sed_add_usr_to_lr(bdev, argp);
+	case IOC_SED_ENABLE_DISABLE_MBR:
+		return blkdev_sed_do_mbr(bdev, argp);
+	case IOC_SED_ERASE_LR:
+		return blkdev_sed_erase_lr(bdev, argp);
 	default:
 		return __blkdev_driver_ioctl(bdev, mode, cmd, arg);
 	}
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC PATCH 5/6] nvme: Add unlock_from_suspend
  2016-10-31 21:58 ` [RFC PATCH 5/6] nvme: Add unlock_from_suspend Scott Bauer
@ 2016-11-01  8:18   ` Sagi Grimberg
  2016-11-01 13:57     ` Christoph Hellwig
  2016-11-07 18:45     ` Keith Busch
  0 siblings, 2 replies; 16+ messages in thread
From: Sagi Grimberg @ 2016-11-01  8:18 UTC (permalink / raw)



> +struct sed_cb_data {
> +	sec_cb	*cb;
> +	void	*cb_data;
> +	struct nvme_command cmd;
> +};
> +
> +static void sec_submit_endio(struct request *req, int error)
> +{
> +	struct sed_cb_data *sed_data = req->end_io_data;
> +
> +	if (sed_data->cb)
> +		sed_data->cb(error, sed_data->cb_data);
> +
> +	kfree(sed_data);
> +	blk_mq_free_request(req);
> +}
> +
> +static int nvme_insert_rq(struct request_queue *q, struct request *rq,
> +			  int at_head, rq_end_io_fn *done)
> +{
> +	WARN_ON(rq->cmd_type == REQ_TYPE_FS);
> +
> +	rq->end_io = done;
> +
> +	if (!q->mq_ops)
> +		return -EINVAL;
> +
> +	blk_mq_insert_request(rq, at_head, true, true);
> +
> +	return 0;
> +}

No need for this function... you control the call site...

> +
> +static int nvme_sec_submit(void *data, u8 opcode, u16 SPSP,
> +			   u8 SECP, void *buffer, size_t len,
> +			   sec_cb *cb, void *cb_data)
> +{
> +	struct request_queue *q;
> +	struct request *req;
> +	struct sed_cb_data *sed_data;
> +	struct nvme_ns *ns;
> +	struct nvme_command *cmd;
> +	int ret;
> +
> +	ns = data;//bdev->bd_disk->private_data;

??

you don't even have data anywhere in here...

> +
> +	sed_data = kzalloc(sizeof(*sed_data), GFP_NOWAIT);
> +	if (!sed_data)
> +		return -ENOMEM;
> +	sed_data->cb = cb;
> +	sed_data->cb_data = cb_data;
> +	cmd = &sed_data->cmd;
> +
> +	cmd->common.opcode = opcode;
> +	cmd->common.nsid = ns->ns_id;
> +	cmd->common.cdw10[0] = SECP << 24 | SPSP << 8;
> +	cmd->common.cdw10[1] = len;
> +
> +	q = ns->ctrl->admin_q;
> +
> +	req = nvme_alloc_request(q, cmd, 0, NVME_QID_ANY);
> +	if (IS_ERR(req)) {
> +		ret = PTR_ERR(req);
> +		goto err_free;
> +	}
> +
> +	req->timeout = ADMIN_TIMEOUT;
> +	req->special = NULL;
> +
> +	if (buffer && len) {
> +		ret = blk_rq_map_kern(q, req, buffer, len, GFP_NOWAIT);
> +		if (ret) {
> +			blk_mq_free_request(req);
> +			goto err_free;
> +		}
> +	}
> +
> +	req->end_io_data = sed_data;
> +	//req->rq_disk = bdev->bd_disk;

??

> +
> +	return nvme_insert_rq(q, req, 1, sec_submit_endio);

No need to introduce nvme_insert_rq at all, just call
blk_mq_insert_request (other examples call blk_execute_rq_nowait
but its pretty much the same...)

> @@ -582,6 +583,7 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
>  	struct nvme_command cmnd;
>  	unsigned map_len;
>  	int ret = BLK_MQ_RQ_QUEUE_OK;
> +	unsigned long flags;
>
>  	/*
>  	 * If formated with metadata, require the block layer provide a buffer
> @@ -614,18 +616,18 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
>  	cmnd.common.command_id = req->tag;
>  	blk_mq_start_request(req);
>
> -	spin_lock_irq(&nvmeq->q_lock);
> +	spin_lock_irqsave(&nvmeq->q_lock, flags);
>  	if (unlikely(nvmeq->cq_vector < 0)) {
>  		if (ns && !test_bit(NVME_NS_DEAD, &ns->flags))
>  			ret = BLK_MQ_RQ_QUEUE_BUSY;
>  		else
>  			ret = BLK_MQ_RQ_QUEUE_ERROR;
> -		spin_unlock_irq(&nvmeq->q_lock);
> +		spin_unlock_irqrestore(&nvmeq->q_lock, flags);
>  		goto out;
>  	}
>  	__nvme_submit_cmd(nvmeq, &cmnd);
>  	nvme_process_cq(nvmeq);
> -	spin_unlock_irq(&nvmeq->q_lock);
> +	spin_unlock_irqrestore(&nvmeq->q_lock, flags);

No documentation why this is needed...

>  	return BLK_MQ_RQ_QUEUE_OK;
>  out:
>  	nvme_free_iod(dev, req);
> @@ -635,11 +637,11 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
>  static void nvme_complete_rq(struct request *req)
>  {
>  	struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
> -	struct nvme_dev *dev = iod->nvmeq->dev;
> +	struct nvme_queue *nvmeq = iod->nvmeq;
> +	struct nvme_dev *dev = nvmeq->dev;

This is a cleanup that should go in a different patch...

>  	int error = 0;
>
>  	nvme_unmap_data(dev, req);
> -

Same here...

>  	if (unlikely(req->errors)) {
>  		if (nvme_req_needs_retry(req, req->errors)) {
>  			req->retries++;
> @@ -658,7 +660,6 @@ static void nvme_complete_rq(struct request *req)
>  			"completing aborted command with status: %04x\n",
>  			req->errors);
>  	}
> -

Here...

>  	blk_mq_end_request(req, error);
>  }
>
> @@ -1758,10 +1759,11 @@ static void nvme_reset_work(struct work_struct *work)
>  {
>  	struct nvme_dev *dev = container_of(work, struct nvme_dev, reset_work);
>  	int result = -ENODEV;
> -
> +	bool was_suspend = false;
>  	if (WARN_ON(dev->ctrl.state == NVME_CTRL_RESETTING))
>  		goto out;
>
> +	was_suspend = !!(dev->ctrl.ctrl_config & NVME_CC_SHN_NORMAL);
>  	/*
>  	 * If we're called to reset a live controller first shut it down before
>  	 * moving on.
> @@ -1789,6 +1791,9 @@ static void nvme_reset_work(struct work_struct *work)
>  	if (result)
>  		goto out;
>
> +	if (was_suspend)
> +		nvme_unlock_from_suspend(&dev->ctrl);
> +
>  	result = nvme_setup_io_queues(dev);
>  	if (result)
>  		goto out;
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [RFC PATCH 5/6] nvme: Add unlock_from_suspend
  2016-11-01  8:18   ` Sagi Grimberg
@ 2016-11-01 13:57     ` Christoph Hellwig
  2016-11-01 14:40       ` Scott Bauer
  2016-11-10 23:01       ` Scott Bauer
  2016-11-07 18:45     ` Keith Busch
  1 sibling, 2 replies; 16+ messages in thread
From: Christoph Hellwig @ 2016-11-01 13:57 UTC (permalink / raw)


On Tue, Nov 01, 2016@10:18:13AM +0200, Sagi Grimberg wrote:
> > +
> > +	return nvme_insert_rq(q, req, 1, sec_submit_endio);
> 
> No need to introduce nvme_insert_rq at all, just call
> blk_mq_insert_request (other examples call blk_execute_rq_nowait
> but its pretty much the same...)

blk_execute_rq_nowait is the API to use - blk_mq_insert_request isn't
even exported.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [RFC PATCH 5/6] nvme: Add unlock_from_suspend
  2016-11-01 13:57     ` Christoph Hellwig
@ 2016-11-01 14:40       ` Scott Bauer
  2016-11-10 23:01       ` Scott Bauer
  1 sibling, 0 replies; 16+ messages in thread
From: Scott Bauer @ 2016-11-01 14:40 UTC (permalink / raw)


On Tue, Nov 01, 2016@06:57:05AM -0700, Christoph Hellwig wrote:
> On Tue, Nov 01, 2016@10:18:13AM +0200, Sagi Grimberg wrote:
> > > +
> > > +	return nvme_insert_rq(q, req, 1, sec_submit_endio);
> > 
> > No need to introduce nvme_insert_rq at all, just call
> > blk_mq_insert_request (other examples call blk_execute_rq_nowait
> > but its pretty much the same...)
> 
> blk_execute_rq_nowait is the API to use - blk_mq_insert_request isn't
> even exported.

Thanks for the reviews. This patch needs to be separated into two 
patches. There is the addition of the nvme-suspend stuff and the
addition of sec_ops. Most of the clutter and weird stuff is coming
from the latter. I'll separate the patches, use the correct api and 
clean the clutter.

Thanks

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [RFC PATCH 2/6] lib: Add Sed-opal library
  2016-10-31 21:58 ` [RFC PATCH 2/6] lib: Add Sed-opal library Scott Bauer
@ 2016-11-01 18:56   ` Jon Derrick
  0 siblings, 0 replies; 16+ messages in thread
From: Jon Derrick @ 2016-11-01 18:56 UTC (permalink / raw)


Hi Rafael, Scott,


First off, congrats on the set! It looks good so far.

Just some small nits below since you have to respin it anyways :)

On Mon, Oct 31, 2016@03:58:15PM -0600, Scott Bauer wrote:
> This patch implements the necessary logic to bring an Opal
> enabled drive out of a factory-enabled into a working
> Opal state.
> 
> This patch set also enables logic to save a password to
> be replayed during a resume from suspend. The key can be
> saved in the driver or in the Kernel's Key managment.
> 
> Signed-off-by: Scott Bauer <scott.bauer at intel.com>
> Signed-off-by: Rafael Antognolli <Rafael.Antognolli at intel.com>
> ---
>  lib/sed-opal.c          | 3337 +++++++++++++++++++++++++++++++++++++++++++++++
>  lib/sed-opal_internal.h |  586 +++++++++
>  lib/sed-opal_key.c      |   46 +
>  lib/sed.c               |  303 +++++
>  4 files changed, 4272 insertions(+)
>  create mode 100644 lib/sed-opal.c
>  create mode 100644 lib/sed-opal_internal.h
>  create mode 100644 lib/sed-opal_key.c
>  create mode 100644 lib/sed.c
> 
> diff --git a/lib/sed-opal.c b/lib/sed-opal.c
> new file mode 100644
> index 0000000..10b3348
> --- /dev/null
> +++ b/lib/sed-opal.c
> @@ -0,0 +1,3337 @@
> +/*
> + * Copyright ? 2016 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + *
> + * Authors:
> + *    Rafael Antognolli <rafael.antognolli at intel.com>
> + *    Scott  Bauer      <scott.bauer at intel.com>
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ":OPAL: " fmt
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <linux/genhd.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include <uapi/linux/sed-opal.h>
> +#include <linux/sed.h>
> +#include <linux/sed-opal.h>
> +#include <linux/string.h>
> +
> +#include "sed-opal_internal.h"
> +
> +#define IO_BUFFER_LENGTH 2048
> +
> +#define MAX_TOKS 64
> +
> +struct opal_cmd {
> +	struct block_device *bdev;
> +	sec_cb *cb;
> +	void *cb_data;
> +
> +	size_t pos;
> +	u8 cmd_buf[IO_BUFFER_LENGTH * 2];
> +	u8 resp_buf[IO_BUFFER_LENGTH * 2];
> +	u8 *cmd;
> +	u8 *resp;
> +};
> +
> +/*
> + * On the parsed response, we don't store again the toks that are already
> + * stored in the response buffer. Instead, for each token, we just store a
> + * pointer to the position in the buffer where the token starts, and the size
> + * of the token in bytes.
> + */
> +struct opal_resp_tok {
> +	const u8 *pos;
> +	size_t len;
> +	enum OPAL_RESPONSE_TOKEN type;
> +	enum OPAL_ATOM_WIDTH width;
> +	union {
> +		u64 u;
> +		s64 s;
> +	} stored;
> +};
> +
> +/*
> + * From the response header it's not possible to know how many tokens there are
> + * on the payload. So we hardcode that the maximum will be MAX_TOKS, and later
> + * if we start dealing with messages that have more than that, we can increase
> + * this number. This is done to avoid having to make two passes through the
> + * response, the first one counting how many tokens we have and the second one
> + * actually storing the positions.
> + */
> +struct parsed_resp {
> +	int num;
> +	struct opal_resp_tok toks[MAX_TOKS];
> +};
> +
> +struct opal_dev;
> +
> +typedef void (*opal_cb)(int error, struct opal_dev *dev);
> +
> +typedef int (*opal_step)(struct opal_dev *dev);
> +
> +struct opal_completion {
> +	struct completion cmd_completion;
> +	int completion_status;
> +};
> +
> +struct opal_dev {
> +	struct block_device *bdev;
> +	struct opal_completion *completion;
> +	struct opal_lock_unlock lkul;
> +	const opal_step *funcs;
> +	void **func_data;
> +	bool resume_from_suspend;
> +	struct opal_suspend_unlk *resume_data;
> +	size_t num_func_data;
> +	atomic_t in_use;
> +	sector_t start;
> +	sector_t length;
> +	u8 lr;
> +	u8 key_type;
> +	u8 key_name[OPAL_KEY_MAX];
> +	size_t key_name_len;
> +	u8 key[OPAL_KEY_MAX];
> +	size_t key_len;
> +	u16 comID;
> +	u32 HSN;
> +	u32 TSN;
> +	u64 align;
> +	u64 lowest_lba;
> +	struct list_head node;
> +	char disk_name[DISK_NAME_LEN];
> +	int state;
> +
> +	struct opal_cmd cmd;
> +	struct parsed_resp parsed;
> +
> +	size_t prev_d_len;
> +	void *prev_data;
> +
> +	sec_cb *final_cb;
> +	void *final_cb_data;
> +	opal_step error_cb;
> +	void *error_cb_data;
> +	opal_cb oper_cb;
> +};
> +
> +LIST_HEAD(opal_list);
> +DEFINE_SPINLOCK(list_spinlock);
> +
> +static void print_buffer(const u8 *ptr, u32 length)
> +{
> +#ifdef DEBUG
> +	print_hex_dump_bytes("OPAL: ", DUMP_PREFIX_OFFSET, ptr, length);
> +	pr_debug("\n");
> +#endif
> +}
> +
> +#define TPER_SYNC_SUPPORTED BIT(0)
> +
> +static bool check_tper(const void *data)
> +{
> +	const struct d0_tper_features *tper = data;
> +	u8 flags = tper->supported_features;
> +
> +	if (!(flags & TPER_SYNC_SUPPORTED)) {
> +		pr_err("TPer sync not supported. flags = %d\n",
> +		       tper->supported_features);
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +static bool check_SUM(const void *data)
> +{
> +	const struct d0_single_user_mode *sum = data;
> +	u32 nlo = be32_to_cpu(sum->num_locking_objects);
> +
> +	if (nlo == 0) {
> +		pr_err("Need at least one locking object.\n");
> +		return false;
> +	}
> +
> +	pr_debug("Number of locking objects: %d\n", nlo);
> +
> +	return true;
> +}
> +
> +static u16 get_comID_v100(const void *data)
> +{
> +	const struct d0_opal_v100 *v100 = data;
> +
> +	return be16_to_cpu(v100->baseComID);
> +}
> +
> +static u16 get_comID_v200(const void *data)
> +{
> +	const struct d0_opal_v200 *v200 = data;
> +
> +	return be16_to_cpu(v200->baseComID);
> +}
> +
> +static int __opal_send_cmd(struct opal_suspend_unlk *data, u16 comID,
> +			   void *buffer, size_t buflen, sec_cb *cb,
> +			   void *cb_data)
> +{
> +	return data->ops.send(data->data, comID, TCG_SECP_01, buffer, buflen,
> +			     cb, cb_data);
> +}
> +static int _opal_send_cmd(struct block_device *bdev, u16 comID,
> +			  void *buffer, size_t buflen,
> +			  sec_cb *cb, void *cb_data)
> +{
> +	const struct sec_ops *ops = bdev->bd_disk->fops->sec_ops;
> +
> +	return ops->send(bdev->bd_disk->private_data, comID,
> +			 TCG_SECP_01, buffer, buflen,
> +			 cb, cb_data);
> +}
> +
> +static int __opal_recv_cmd(struct opal_suspend_unlk *data, u16 comID,
> +			   void *buffer, size_t buflen, sec_cb *cb,
> +			   void *cb_data)
> +{
> +	return data->ops.recv(data->data, comID, TCG_SECP_01, buffer, buflen,
> +			     cb, cb_data);
> +}
> +
> +static int _opal_recv_cmd(struct block_device *bdev, u16 comID,
> +			  void *buffer, size_t buflen,
> +			  sec_cb *cb, void *cb_data)
> +{
> +	const struct sec_ops *ops = bdev->bd_disk->fops->sec_ops;
> +
> +	return ops->recv(bdev->bd_disk->private_data, comID,
> +			 TCG_SECP_01, buffer, buflen,
> +			 cb, cb_data);
> +}
> +
> +static void opal_send_cb_cont(int error, void *data)
> +{
> +	struct opal_dev *dev = data;
> +	size_t buflen = IO_BUFFER_LENGTH;
> +	void *buffer = dev->cmd.resp;
> +	struct opal_header *hdr = buffer;
> +
> +	pr_debug("%s: Sent OPAL command: error=%d, outstanding=%d, minTransfer=%d\n",
> +	       dev->disk_name, error, hdr->cp.outstandingData,
> +	       hdr->cp.minTransfer);
> +
> +	if (error || hdr->cp.outstandingData == 0 ||
> +	    hdr->cp.minTransfer != 0) {
> +		if (dev->cmd.cb)
> +			dev->cmd.cb(error, dev->cmd.cb_data);
> +		return;
> +	}
> +
> +	memset(buffer, 0, buflen);
> +	if (dev->resume_from_suspend)
> +		__opal_recv_cmd(dev->resume_data, dev->comID,
> +				buffer, buflen, opal_send_cb_cont, dev);
> +	else
> +		_opal_recv_cmd(dev->bdev, dev->comID, buffer, buflen,
> +			       opal_send_cb_cont, dev);
> +}
> +
> +static void opal_send_cb(int error, void *data)
> +{
> +	struct opal_dev *dev = data;
> +	size_t buflen = IO_BUFFER_LENGTH;
> +	void *buffer = dev->cmd.resp;
> +
> +	if (error) {
> +		if (dev->cmd.cb)
> +			dev->cmd.cb(error, dev->cmd.cb_data);
> +		return;
> +	}
> +
> +	memset(buffer, 0, buflen);
> +	if (dev->resume_from_suspend)
> +		__opal_recv_cmd(dev->resume_data, dev->comID,
> +				buffer, buflen, opal_send_cb_cont, dev);
> +	else
> +		_opal_recv_cmd(dev->bdev, dev->comID, buffer, buflen,
> +			       opal_send_cb_cont, dev);
> +}
> +
> +static int opal_send_recv(struct opal_dev *dev, sec_cb *cb, void *cb_data)
> +{
> +	size_t buflen = IO_BUFFER_LENGTH;
> +	void *buffer = dev->cmd.cmd;
> +	int ret;
> +
> +	dev->cmd.cb = cb;
> +	dev->cmd.cb_data = cb_data;
> +	if (dev->resume_from_suspend)
> +		ret = __opal_send_cmd(dev->resume_data, dev->comID, buffer,
> +				      buflen, opal_send_cb, dev);
> +	else
> +		ret = _opal_send_cmd(dev->bdev, dev->comID, buffer, buflen,
> +				     opal_send_cb, dev);
> +
> +	return ret;
> +}
> +
> +static void check_geometry(struct opal_dev *dev, const void *data)
> +{
> +	const struct d0_geometry_features *geo = data;
> +
> +	dev->align = geo->alignment_granularity;
> +	dev->lowest_lba = geo->lowest_aligned_lba;
> +}
> +
> +static void opal_discovery0_end(int error, void *data)
> +{
> +	bool foundComID = false, supported = true, single_user = false;
> +	struct opal_dev *dev = data;
> +	const struct d0_header *hdr;
> +	const u8 *epos, *cpos;
> +	u16 comID = 0;
> +
> +	if (error) {
> +		pr_err("%s: Sending discovery0 failed\n", dev->disk_name);
> +		goto err_callback;
> +	}
> +
> +	epos = dev->cmd.resp;
> +	cpos = dev->cmd.resp;
> +	hdr = (struct d0_header *)dev->cmd.resp;
> +
> +	print_buffer(dev->cmd.resp, be32_to_cpu(hdr->length));
> +
> +	epos += be32_to_cpu(hdr->length); /* end of buffer */
> +	cpos += sizeof(*hdr); /* current position on buffer */
> +
> +	while (cpos < epos && supported) {
> +		const struct d0_features *body =
> +			(const struct d0_features *)cpos;
> +
> +		switch (be16_to_cpu(body->code)) {
> +		case FC_TPER:
> +			supported = check_tper(body->features);
> +			break;
> +		case FC_SINGLEUSER:
> +			single_user = check_SUM(body->features);
> +			break;
> +		case FC_GEOMETRY:
> +			check_geometry(dev, body);
> +			break;
> +		case FC_LOCKING:
> +		case FC_ENTERPRISE:
> +		case FC_DATASTORE:
> +			/* some ignored properties */
> +			pr_debug("%s: Found OPAL feature description: %d\n",
> +				 dev->disk_name, be16_to_cpu(body->code));
> +			break;
> +		case FC_OPALV100:
> +			comID = get_comID_v100(body->features);
> +			foundComID = true;
> +			break;
> +		case FC_OPALV200:
> +			comID = get_comID_v200(body->features);
> +			foundComID = true;
> +			break;
> +		default:
> +			if (be16_to_cpu(body->code) > 0xbfff) {
> +				/* vendor specific, just ignore */
> +			} else {
> +				pr_warn("%s: OPAL Unknown feature: %d\n",
> +					dev->disk_name, be16_to_cpu(body->code));
> +			}
Small nit, how about:

case 0xbfff ... 0xffff:
	/* vendor specific, just ignore */
	break;
default:
	pr_warn(...



> +		}
> +		cpos += body->length + 4;
> +	}
> +
> +	if (!supported) {
> +		pr_err("%s: Device not supported\n", dev->disk_name);
> +		goto err_callback;
> +	}
> +
> +	if (!single_user)
> +		pr_warn("%s: Device doesn't support single user mode\n",
> +			dev->disk_name);
> +
> +	if (!foundComID) {
> +		pr_warn("%s: Could not find OPAL comID for device. OPAL kernel unlocking will be disabled\n",
> +			dev->disk_name);
> +		goto err_callback;
> +	}
> +
> +	dev->comID = comID;
> +
> +err_callback:
> +	if (dev->oper_cb)
> +		dev->oper_cb(error, dev);
> +}
> +
> +static int opal_discovery0(struct opal_dev *dev)
> +{
> +	memset(dev->cmd.resp, 0, IO_BUFFER_LENGTH);
> +
> +	if (dev->resume_from_suspend)
> +		return __opal_recv_cmd(dev->resume_data, 0x0001,
> +				       dev->cmd.resp, IO_BUFFER_LENGTH,
> +				       opal_discovery0_end, dev);
> +
> +	return _opal_recv_cmd(dev->bdev, 0x0001, dev->cmd.resp,
> +			      IO_BUFFER_LENGTH, opal_discovery0_end,
> +			      dev);
> +}
> +
> +static void add_token_u8(struct opal_cmd *cmd, u8 tok)
> +{
> +	cmd->cmd[cmd->pos++] = tok;
> +}
> +
> +static ssize_t test_and_add_token_u8(struct opal_cmd *cmd, u8 tok)
> +{
> +	BUILD_BUG_ON(IO_BUFFER_LENGTH >= SIZE_MAX);
> +
> +	if (cmd->pos >= IO_BUFFER_LENGTH - 1) {
> +		pr_err("Error adding u8: end of buffer.\n");
> +		return -ERANGE;
> +	}
> +
> +	add_token_u8(cmd, tok);
> +
> +	return 1;
> +}
> +
> +#define TINY_ATOM_DATA_MASK GENMASK(5, 0)
> +#define TINY_ATOM_SIGNED BIT(6)
> +
> +#define SHORT_ATOM_ID BIT(7)
> +#define SHORT_ATOM_BYTESTRING BIT(5)
> +#define SHORT_ATOM_SIGNED BIT(4)
> +#define SHORT_ATOM_LEN_MASK GENMASK(3, 0)
> +
> +static void add_short_atom_header(struct opal_cmd *cmd, bool bytestring,
> +				  bool has_sign, int len)
> +{
> +	u8 atom;
> +
> +	atom = SHORT_ATOM_ID;
> +	atom |= bytestring ? SHORT_ATOM_BYTESTRING : 0;
> +	atom |= has_sign ? SHORT_ATOM_SIGNED : 0;
> +	atom |= len & SHORT_ATOM_LEN_MASK;
> +
> +	add_token_u8(cmd, atom);
> +}
> +
> +#define MEDIUM_ATOM_ID (BIT(7) | BIT(6))
> +#define MEDIUM_ATOM_BYTESTRING BIT(4)
> +#define MEDIUM_ATOM_SIGNED BIT(3)
> +#define MEDIUM_ATOM_LEN_MASK GENMASK(2, 0)
> +
> +static void add_medium_atom_header(struct opal_cmd *cmd, bool bytestring,
> +				   bool has_sign, int len)
> +{
> +	u8 header0;
> +
> +	header0 = MEDIUM_ATOM_ID;
> +	header0 |= bytestring ? MEDIUM_ATOM_BYTESTRING : 0;
> +	header0 |= has_sign ? MEDIUM_ATOM_SIGNED : 0;
> +	header0 |= (len >> 8) & MEDIUM_ATOM_LEN_MASK;
> +	cmd->cmd[cmd->pos++] = header0;
> +	cmd->cmd[cmd->pos++] = len;
> +}
> +
> +static void add_token_u64(struct opal_cmd *cmd, u64 number, size_t len)
> +{
> +	add_short_atom_header(cmd, false, false, len);
> +
> +	while (len--) {
> +		u8 n = number >> (len * 8);
> +
> +		add_token_u8(cmd, n);
> +	}
> +}
> +
> +static ssize_t test_and_add_token_u64(struct opal_cmd *cmd, u64 number)
> +{
> +	int len;
> +	int msb;
> +
> +	if (!(number & ~TINY_ATOM_DATA_MASK))
> +		return test_and_add_token_u8(cmd, number);
> +
> +	msb = fls(number);
> +	len = DIV_ROUND_UP(msb, 4);
> +
> +	if (cmd->pos >= IO_BUFFER_LENGTH - len - 1) {
> +		pr_err("Error adding u64: end of buffer.\n");
> +		return -ERANGE;
> +	}
> +
> +	add_token_u64(cmd, number, len);
> +
> +	/* return length of token plus atom */
> +	return len + 1;
> +}
> +
> +static void add_token_bytestring(struct opal_cmd *cmd,
> +				 const u8 *bytestring, size_t len)
> +{
> +	memcpy(&cmd->cmd[cmd->pos], bytestring, len);
> +	cmd->pos += len;
> +}
> +
> +static ssize_t test_and_add_token_bytestring(struct opal_cmd *cmd,
> +					     const u8 *bytestring, size_t len)
> +{
> +	size_t header_len = 1;
> +	bool is_short_atom = true;
> +
> +	if (len & ~SHORT_ATOM_LEN_MASK) {
> +		header_len = 2;
> +		is_short_atom = false;
> +	}
> +
> +	if (cmd->pos >= IO_BUFFER_LENGTH - len - header_len) {
> +		pr_err("Error adding bytestring: end of buffer.\n");
> +		return -ERANGE;
> +	}
> +
> +	if (is_short_atom)
> +		add_short_atom_header(cmd, true, false, len);
> +	else
> +		add_medium_atom_header(cmd, true, false, len);
> +
> +	add_token_bytestring(cmd, bytestring, len);
> +
> +	return header_len + len;
> +}
> +
> +#define LOCKING_RANGE_NON_GLOBAL 0x03
> +
> +static int build_locking_range(u8 *buffer, size_t length, u8 lr)
> +{
> +	if (length < OPAL_UID_LENGTH)
> +		return -ERANGE;
> +
> +	memcpy(buffer, OPALUID[OPAL_LOCKINGRANGE_GLOBAL], OPAL_UID_LENGTH);
> +
> +	if (lr == 0)
> +		return 0;
> +	buffer[5] = LOCKING_RANGE_NON_GLOBAL;
> +	buffer[7] = lr;
> +
> +	return 0;
> +}
> +
> +static int build_locking_user(u8 *buffer, size_t length, u8 lr)
> +{
> +	if (length < OPAL_UID_LENGTH)
> +		return -ERANGE;
> +
> +	memcpy(buffer, OPALUID[OPAL_USER1_UID], OPAL_UID_LENGTH);
> +
> +	buffer[7] = lr + 1;
> +
> +	return 0;
> +}
> +
> +/*
> + * N = number of format specifiers (1-999) to be replicated
> + * c = u8
> + * u = u64
> + * s = bytestring, length
> + *
> + * ret = test_and_add_token_va(cmd, "c",
> + *			       u8_val1);
> + *
> + * ret = test_and_add_token_va(cmd, "2c2u",
> + *			       u8_val1, u8_val2, u64_val1, u64_val2);
> + *
> + * ret = test_and_add_token_va(cmd, "3s",
> + *			       bytestring1, length1,
> + *			       bytestring2, length2,
> + *			       bytestring3, length3);
> + */
> +static int test_and_add_token_va(struct opal_cmd *cmd,
> +				 const char *fmt, ...)
> +{
> +	const u8 *it = fmt, *tmp;
> +	int ret, num = 1, sum = 0;
> +	va_list ap;
> +
> +	va_start(ap, fmt);
> +
> +	while (*it != '\0') {
> +		u64 tok64 = 0;
> +		u8 tok, *bstr;
> +		size_t len;
> +
> +		ret = 0;
> +
> +		switch (*it) {
> +		case '1' ... '9':
> +			tmp = it;
> +			num = 0;
> +			while (*tmp >= '0' && *tmp <= '9')
> +				num = num * 10 + (*tmp++ - '0');
> +			it = tmp;
> +			continue;
> +		case 'c':
> +			while (num--) {
> +				tok = va_arg(ap, unsigned int);
> +				ret = test_and_add_token_u8(cmd, tok);
> +				if (ret < 0)
> +					goto err;
> +			}
> +			num = 1;
> +			break;
> +		case 'u':
> +			while (num--) {
> +				tok64 = va_arg(ap, u64);
> +				ret = test_and_add_token_u64(cmd, tok64);
> +				if (ret < 0)
> +					goto err;
> +			}
> +			num = 1;
> +			break;
> +		case 's':
> +			while (num--) {
> +				bstr = va_arg(ap, u8 *);
> +				len = va_arg(ap, size_t);
> +				ret = test_and_add_token_bytestring(cmd, bstr,
> +								    len);
> +				if (ret < 0)
> +					goto err;
> +			}
> +			num = 1;
> +			break;
> +		case ' ':
> +		case '\t':
> +			/* ignored */
> +			break;
> +		default:
> +			pr_warn("Unrecognized type.\n");
> +		}
> +
> +		it++;
> +		sum += ret;
> +	}
> +
> +	va_end(ap);
> +
> +	return sum;
> +
> + err:
> +	pr_err("Token failed to be added.\n");
> +	return ret;
> +}
> +
> +static void set_comID(struct opal_cmd *cmd, u16 comID)
> +{
> +	struct opal_header *hdr = (struct opal_header *)cmd->cmd;
> +
> +	hdr->cp.extendedComID[0] = comID >> 8;
> +	hdr->cp.extendedComID[1] = comID;
> +	hdr->cp.extendedComID[2] = 0;
> +	hdr->cp.extendedComID[3] = 0;
> +}
> +
> +static int cmd_finalize(struct opal_cmd *cmd, u32 hsn, u32 tsn)
> +{
> +	struct opal_header *hdr;
> +	int ret;
> +
> +	ret = test_and_add_token_va(cmd, "6c",
> +				    OPAL_ENDOFDATA, OPAL_STARTLIST,
> +				    0, 0, 0, OPAL_ENDLIST);
> +
> +	if (ret < 0) {
> +		pr_err("Error finalizing command.\n");
> +		return -EFAULT;
> +	}
> +
> +	hdr = (struct opal_header *) cmd->cmd;
> +
> +	hdr->pkt.TSN = cpu_to_be32(tsn);
> +	hdr->pkt.HSN = cpu_to_be32(hsn);
> +
> +	hdr->subpkt.length = cpu_to_be32(cmd->pos - sizeof(*hdr));
> +	while (cmd->pos % 4) {
> +		if (cmd->pos >= IO_BUFFER_LENGTH) {
> +			pr_err("Error: Buffer overrun\n");
> +			return -ERANGE;
> +		}
> +		cmd->cmd[cmd->pos++] = 0;
> +	}
> +	hdr->pkt.length = cpu_to_be32(cmd->pos - sizeof(hdr->cp) -
> +				      sizeof(hdr->pkt));
> +	hdr->cp.length = cpu_to_be32(cmd->pos - sizeof(hdr->cp));
> +
> +	return 0;
> +}
> +
> +static enum OPAL_RESPONSE_TOKEN token_type(const struct parsed_resp *resp,
> +					   int n)
> +{
> +	const struct opal_resp_tok *tok;
> +
> +	if (n >= resp->num) {
> +		pr_err("Token number doesn't exist: %d, resp: %d\n",
> +		       n, resp->num);
> +		return OPAL_DTA_TOKENID_INVALID;
> +	}
> +
> +	tok = &resp->toks[n];
> +	if (tok->len == 0) {
> +		pr_err("Token length must be non-zero\n");
> +		return OPAL_DTA_TOKENID_INVALID;
> +	}
> +
> +	return tok->type;
> +}
> +
> +/*
> + * This function returns 0 in case of invalid token. One should call
> + * token_type() first to find out if the token is valid or not.
> + */
> +static enum OPAL_TOKEN response_get_token(const struct parsed_resp *resp,
> +					  int n)
> +{
> +	const struct opal_resp_tok *tok;
> +
> +	if (n >= resp->num) {
> +		pr_err("Token number doesn't exist: %d, resp: %d\n",
> +		       n, resp->num);
> +		return 0;
> +	}
> +
> +	tok = &resp->toks[n];
> +	if (tok->len == 0) {
> +		pr_err("Token length must be non-zero\n");
> +		return 0;
> +	}
> +
> +	return tok->pos[0];
> +}
> +
> +static size_t response_parse_tiny(struct opal_resp_tok *tok,
> +				  const u8 *pos)
> +{
> +	tok->pos = pos;
> +	tok->len = 1;
> +	tok->width = OPAL_WIDTH_TINY;
> +
> +	if (pos[0] & TINY_ATOM_SIGNED) {
> +		tok->type = OPAL_DTA_TOKENID_SINT;
> +	} else {
> +		tok->type = OPAL_DTA_TOKENID_UINT;
> +		tok->stored.u = pos[0] & 0x3f;
> +	}
> +
> +	return tok->len;
> +}
> +
> +static size_t response_parse_short(struct opal_resp_tok *tok,
> +				   const u8 *pos)
> +{
> +	tok->pos = pos;
> +	tok->len = (pos[0] & SHORT_ATOM_LEN_MASK) + 1;
> +	tok->width = OPAL_WIDTH_SHORT;
> +
> +	if (pos[0] & SHORT_ATOM_BYTESTRING) {
> +		tok->type = OPAL_DTA_TOKENID_BYTESTRING;
> +	} else if (pos[0] & SHORT_ATOM_SIGNED) {
> +		tok->type = OPAL_DTA_TOKENID_SINT;
> +	} else {
> +		u64 u_integer = 0;
> +		int i, b = 0;
> +
> +		tok->type = OPAL_DTA_TOKENID_UINT;
> +		if (tok->len > 9)
Should this be len > 8 or len >= 9 ?

> +			pr_warn("uint64 with more than 8 bytes\n");
> +		for (i = tok->len - 1; i > 0; i--) {
> +			u_integer |= ((u64)pos[i] << (8 * b));
> +			b++;
> +		}
It doesn't look particularly safe to keep using this driver on a drive
if it returns a len > 8. But the specs do allow 16-byte data in a short
atom, so maybe we should bail on allowing the driver to manage the
drive, or change the types to some 16-byte type?

> +		tok->stored.u = u_integer;
> +	}
> +
> +	return tok->len;
> +}
> +
> +static size_t response_parse_medium(struct opal_resp_tok *tok,
> +				    const u8 *pos)
> +{
> +	tok->pos = pos;
> +	tok->len = (((pos[0] & MEDIUM_ATOM_LEN_MASK) << 8) | pos[1]) + 2;
> +	tok->width = OPAL_WIDTH_MEDIUM;
> +
> +	if (pos[0] & MEDIUM_ATOM_BYTESTRING)
> +		tok->type = OPAL_DTA_TOKENID_BYTESTRING;
> +	else if (pos[0] & MEDIUM_ATOM_SIGNED)
> +		tok->type = OPAL_DTA_TOKENID_SINT;
> +	else
> +		tok->type = OPAL_DTA_TOKENID_UINT;
> +
> +	return tok->len;
> +}
> +
> +#define LONG_ATOM_ID (BIT(7) | BIT(6) | BIT(5))
> +#define LONG_ATOM_BYTESTRING BIT(1)
> +#define LONG_ATOM_SIGNED BIT(0)
> +static size_t response_parse_long(struct opal_resp_tok *tok,
> +				  const u8 *pos)
> +{
> +	tok->pos = pos;
> +	tok->len = ((pos[1] << 16) | (pos[2] << 8) | pos[3]) + 4;
> +	tok->width = OPAL_WIDTH_LONG;
> +
> +	if (pos[0] & LONG_ATOM_BYTESTRING)
> +		tok->type = OPAL_DTA_TOKENID_BYTESTRING;
> +	else if (pos[0] & LONG_ATOM_SIGNED)
> +		tok->type = OPAL_DTA_TOKENID_SINT;
> +	else
> +		tok->type = OPAL_DTA_TOKENID_UINT;
> +
> +	return tok->len;
> +}
> +
> +static size_t response_parse_token(struct opal_resp_tok *tok,
> +				   const u8 *pos)
> +{
> +	tok->pos = pos;
> +	tok->len = 1;
> +	tok->type = OPAL_DTA_TOKENID_TOKEN;
> +	tok->width = OPAL_WIDTH_TOKEN;
> +
> +	return tok->len;
> +}
> +
> +static int response_parse(const u8 *buf, size_t length,
> +			  struct parsed_resp *resp)
> +{
> +	const struct opal_header *hdr;
> +	struct opal_resp_tok *iter;
> +	int ret, num_entries = 0;
> +	u32 cpos = 0, total;
> +	size_t token_length;
> +	const u8 *pos;
> +
> +	if (!buf)
> +		return -EFAULT;
> +
> +	if (!resp)
> +		return -EFAULT;
> +
> +	hdr = (struct opal_header *)buf;
> +	pos = buf;
> +	pos += sizeof(*hdr);
> +
> +	pr_debug("Response size: cp: %d, pkt: %d, subpkt: %d\n",
> +		 be32_to_cpu(hdr->cp.length),
> +		 be32_to_cpu(hdr->pkt.length),
> +		 be32_to_cpu(hdr->subpkt.length));
> +
> +	if ((hdr->cp.length == 0)
> +	    || (hdr->pkt.length == 0)
> +	    || (hdr->subpkt.length == 0)) {
> +		pr_err("Bad header length. cp: %d, pkt: %d, subpkt: %d\n",
> +		       be32_to_cpu(hdr->cp.length),
> +		       be32_to_cpu(hdr->pkt.length),
> +		       be32_to_cpu(hdr->subpkt.length));
> +		print_buffer(pos, sizeof(*hdr));
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	if (pos > buf + length) {
> +		ret = -EFAULT;
> +		goto err;
> +	}
> +
> +	iter = resp->toks;
> +	total = be32_to_cpu(hdr->subpkt.length);
> +	print_buffer(pos, total);
> +	while (cpos < total) {
> +		if (!(pos[0] & 0x80)) /* tiny atom */
> +			token_length = response_parse_tiny(iter, pos);
> +		else if (!(pos[0] & 0x40)) /* short atom */
> +			token_length = response_parse_short(iter, pos);
> +		else if (!(pos[0] & 0x20)) /* medium atom */
> +			token_length = response_parse_medium(iter, pos);
> +		else if (!(pos[0] & 0x10)) /* long atom */
> +			token_length = response_parse_long(iter, pos);
> +		else /* TOKEN */
> +			token_length = response_parse_token(iter, pos);
> +
> +		pos += token_length;
> +		cpos += token_length;
> +		iter++;
> +		num_entries++;
> +	}
> +
> +	if (num_entries == 0) {
> +		pr_err("Couldn't parse response.\n");
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +	resp->num = num_entries;
> +
> +	return 0;
> +err:
> +	return ret;
> +}
> +
> +static size_t response_get_string(const struct parsed_resp *resp, int n,
> +				  const char **store)
> +{
> +	*store = NULL;
> +	if (!resp) {
> +		pr_err("Response is NULL\n");
> +		return 0;
> +	}
> +
> +	if (n > resp->num) {
> +		pr_err("Response has %d tokens. Can't access %d\n",
> +		       resp->num, n);
> +		return 0;
> +	}
> +
> +	if (resp->toks[n].type != OPAL_DTA_TOKENID_BYTESTRING) {
> +		pr_err("Token is not a byte string!\n");
> +		return 0;
> +	}
> +
> +	*store = resp->toks[n].pos + 1;
> +	return resp->toks[n].len - 1;
> +}
> +
> +static u64 response_get_u64(const struct parsed_resp *resp, int n)
> +{
> +	if (!resp) {
> +		pr_err("Response is NULL\n");
> +		return 0;
> +	}
> +
> +	if (n > resp->num) {
> +		pr_err("Response has %d tokens. Can't access %d\n",
> +		       resp->num, n);
> +		return 0;
> +	}
> +
> +	if (resp->toks[n].type != OPAL_DTA_TOKENID_UINT) {
> +		pr_err("Token is not unsigned it: %d\n",
> +		       resp->toks[n].type);
> +		return 0;
> +	}
> +
> +	if (!((resp->toks[n].width == OPAL_WIDTH_TINY) ||
> +	      (resp->toks[n].width == OPAL_WIDTH_SHORT))) {
> +		pr_err("Atom is not short or tiny: %d\n",
> +		       resp->toks[n].width);
> +		return 0;
> +	}
> +
> +	return resp->toks[n].stored.u;
> +}
> +
> +static u8 response_status(const struct parsed_resp *resp)
> +{
> +	if ((token_type(resp, 0) == OPAL_DTA_TOKENID_TOKEN)
> +	    && (response_get_token(resp, 0) == OPAL_ENDOFSESSION)) {
> +		return 0;
> +	}
> +
> +	if (resp->num < 5)
> +		return DTAERROR_NO_METHOD_STATUS;
> +
> +	if ((token_type(resp, resp->num - 1) != OPAL_DTA_TOKENID_TOKEN) ||
> +	    (token_type(resp, resp->num - 5) != OPAL_DTA_TOKENID_TOKEN) ||
> +	    (response_get_token(resp, resp->num - 1) != OPAL_ENDLIST) ||
> +	    (response_get_token(resp, resp->num - 5) != OPAL_STARTLIST))
> +		return DTAERROR_NO_METHOD_STATUS;
> +
> +	return response_get_u64(resp, resp->num - 4);
> +}
> +
> +/* Parses and checks for errors */
> +static int parse_and_check_status(struct opal_dev *dev)
> +{
> +	struct opal_cmd *cmd;
> +	int error;
> +
> +	cmd = &dev->cmd;
> +	print_buffer(cmd->cmd, cmd->pos);
> +
> +	error = response_parse(cmd->resp, IO_BUFFER_LENGTH, &dev->parsed);
> +	if (error) {
> +		pr_err("%s: Couldn't parse response.\n", dev->disk_name);
> +		goto err_return;
> +	}
> +
> +	error = response_status(&dev->parsed);
> +	if (error)
> +		pr_err("%s: Response Status: %d\n", dev->disk_name,
> +		       error);
> +
> + err_return:
> +	return error;
> +}
> +
> +static void clear_opal_cmd(struct opal_cmd *cmd)
> +{
> +	cmd->pos = sizeof(struct opal_header);
> +	memset(cmd->cmd, 0, IO_BUFFER_LENGTH);
> +	cmd->cb = NULL;
> +	cmd->cb_data = NULL;
> +}
> +
> +static void start_opal_session_cont(int error, void *data)
> +{
> +	struct opal_dev *dev = data;
> +	u32 HSN, TSN;
> +
> +	if (error)
> +		goto err_return;
> +
> +	error = parse_and_check_status(dev);
> +	if (error)
> +		goto err_return;
> +
> +	HSN = response_get_u64(&dev->parsed, 4);
> +	TSN = response_get_u64(&dev->parsed, 5);
> +
> +	if (HSN == 0 && TSN == 0) {
> +		pr_err("%s: Couldn't authenticate session\n", dev->disk_name);
> +		error = -EPERM;
> +		goto err_return;
> +	}
> +
> +	dev->HSN = HSN;
> +	dev->TSN = TSN;
> +
> +err_return:
> +	if (dev->oper_cb)
> +		dev->oper_cb(error, dev);
> +}
> +
> +static int get_opal_key(struct opal_dev *dev)
> +{
> +	struct key *ukey = NULL;
> +	const u8 *tmpkey = NULL;
> +	size_t tmplen;
> +	int ret = 0;
> +
> +	if (dev->key_type == OPAL_KEY_PLAIN) {
> +		tmpkey = dev->key_name;
> +		tmplen = dev->key_name_len;
> +	} else if (dev->key_type == OPAL_KEY_KEYRING) {
> +		ukey = request_user_key(dev->key_name, &tmpkey, &tmplen);
> +		if (IS_ERR(ukey)) {
> +			pr_err("%s: Can't retrieve key: %ld\n", dev->disk_name,
> +			       PTR_ERR(ukey));
> +			return PTR_ERR(ukey);
> +		}
> +	} else {
> +		pr_err("Requested invalid key type: %d\n", dev->key_type);
> +		return -EINVAL;
> +	}
> +
> +	if (tmplen > OPAL_KEY_MAX) {
> +		pr_err("Requested key with invalid size: %zd\n", tmplen);
> +		ret = -EINVAL;
> +		goto err_exit;
> +	}
> +
> +	dev->key_len = tmplen;
> +	if (!memcpy(dev->key, tmpkey, tmplen)) {
> +		pr_err("Error when copying key");
> +		ret = -EFAULT;
> +		goto err_exit;
> +	}
> +
> +err_exit:
> +	key_put(ukey);
> +
> +	return 0;
> +}
> +
> +static void clean_opal_key(struct opal_dev *dev)
> +{
> +	memset(dev->key, 0, OPAL_KEY_MAX);
> +	dev->key_len = 0;
> +}
> +
> +static inline void clean_function_data(struct opal_dev *dev)
> +{
> +		dev->func_data = NULL;
> +		dev->num_func_data = 0;
> +}
> +
> +/* This is a generic continue fn.
> + * We use this when we don't care about the response data
> + * and simply want to check the status and continue.
> + */
> +static void generic_cont(int error, void *data)
> +{
> +	struct opal_dev *dev = data;
> +
> +	if (error)
> +		goto err_return;
> +
> +	error = parse_and_check_status(dev);
> +
> + err_return:
> +	if (dev->oper_cb)
> +		dev->oper_cb(error, dev);
> +}

How about:

static void generic_cont(int error, void *data)
{
	struct opal_dev *dev = data;

	if (!error)
		error = parse_and_check_status(dev);

	if (dev->oper_cb)
		dev->oper_cb(error, dev);
}

> +
> +static void end_session_cont(int error, void *data)
> +{
> +	struct opal_dev *dev = data;
> +
> +	dev->HSN = 0;
> +	dev->TSN = 0;
> +	generic_cont(error, data);
> +}
> +
> +static int finalize_and_send(struct opal_dev *dev, struct opal_cmd *cmd,
> +			     sec_cb cont)
> +{
> +	int ret;
> +
> +	ret = cmd_finalize(cmd, dev->HSN, dev->TSN);
> +	if (ret) {
> +		pr_err("%s: Error finalizing command buffer: %d\n",
> +		       dev->disk_name, ret);
> +		return ret;
> +	}
> +
> +	print_buffer(cmd->cmd, cmd->pos);
> +
> +	ret = opal_send_recv(dev, cont, dev);
> +	if (ret)
> +		pr_err("%s: Error running command: %d\n",
> +		       dev->disk_name, ret);
> +
> +	return ret;
> +}
> +
> +static int wait_for_cmd_completion(struct opal_completion *completion)
> +{
> +	wait_for_completion_interruptible(&completion->cmd_completion);
> +	return completion->completion_status;
> +}
> +
> +static int gen_key(struct opal_dev *dev)
> +{
> +	const u8 *method;
> +	u8 uid[OPAL_UID_LENGTH];
> +	struct opal_cmd *cmd;
> +	int ret;
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +	set_comID(cmd, dev->comID);
> +
> +	memcpy(uid, dev->prev_data, min(sizeof(uid), dev->prev_d_len));
> +	method = OPALMETHOD[OPAL_GENKEY];
> +	kfree(dev->prev_data);
> +	dev->prev_data = NULL;
> +
> +	ret = test_and_add_token_va(cmd, "c2s 2c",
> +				    OPAL_CALL,
> +				    uid, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +
> +				    OPAL_STARTLIST,
> +				    OPAL_ENDLIST);
> +	if (ret < 0) {
> +		pr_err("%s: Error building gen key command\n",
> +		       dev->disk_name);
> +		return ret;
> +	}
> +
> +	return finalize_and_send(dev, cmd, generic_cont);
> +}
> +
> +static void get_active_key_cont(int error, void *data)
> +{
> +	struct opal_dev *dev = data;
> +	const char *activekey;
> +	size_t keylen;
> +
> +	if (error)
> +		goto err_return;
> +
> +	error = parse_and_check_status(dev);
> +	if (error)
> +		goto err_return;
> +	keylen = response_get_string(&dev->parsed, 4, &activekey);
> +	if (!activekey) {
> +		pr_err("%s: Couldn't extract the Activekey from the response\n",
> +		       __func__);
> +		error = 0x0A;
> +		goto err_return;
> +	}
> +	dev->prev_data = kmemdup(activekey, keylen, GFP_KERNEL);
> +
> +	if (!dev->prev_data)
> +		error = -ENOMEM;
> +
> +	dev->prev_d_len = keylen;
> +
> +err_return:
> +	if (dev->oper_cb)
> +		dev->oper_cb(error, dev);
> +}
> +
> +static int get_active_key(struct opal_dev *dev)
> +{
> +	const u8 *method;
> +	u8 uid[OPAL_UID_LENGTH];
> +	struct opal_cmd *cmd;
> +	int ret;
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +	set_comID(cmd, dev->comID);
> +
> +	method = OPALMETHOD[OPAL_GET];
> +
> +	ret = build_locking_range(uid, sizeof(uid), dev->lr);
> +	if (ret < 0) {
> +		pr_err("%s: Can't build locking range\n", dev->disk_name);
> +		return -EINVAL;
> +	}
> +
> +	ret = test_and_add_token_va(cmd, "c2s 6c 4c 2c",
> +				    OPAL_CALL,
> +				    uid, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_03, /* startCloumn */
> +				    OPAL_TINY_UINT_10, /* ActiveKey */
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_04, /* endColumn */
> +				    OPAL_TINY_UINT_10, /* ActiveKey */
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_ENDLIST,
> +				    OPAL_ENDLIST);
> +	if (ret < 0) {
> +		pr_err("%s: Error building get active key command\n",
> +		       dev->disk_name);
> +		return ret;
> +	}
> +
> +	return finalize_and_send(dev, cmd, get_active_key_cont);
> +}
> +
> +static int setup_locking_range(struct opal_dev *dev)
> +{
> +	const u8 *method;
> +	u8 uid[OPAL_UID_LENGTH];
> +	struct opal_cmd *cmd;
> +	struct opal_user_lr_setup *setup;
> +	int ret;
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +	set_comID(cmd, dev->comID);
> +
> +	method = OPALMETHOD[OPAL_SET];
> +	ret = build_locking_range(uid, sizeof(uid), dev->lr);
> +	if (ret < 0) {
> +		pr_err("%s: Can't build locking range\n", dev->disk_name);
> +		return -EINVAL;
> +	}
> +	setup = dev->func_data[dev->state - 1];
> +
> +	ret = test_and_add_token_va(cmd, "c2s  4c 2cuc 2cuc 2cuc 2cu 4c",
> +				    OPAL_CALL,
> +				    uid, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTNAME,
> +				    OPAL_VALUES,
> +				    OPAL_STARTLIST,
> +
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_03, /* Range Start */
> +				    setup->range_start,
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_04, /* Range Length */
> +				    setup->range_length,
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_05, /* ReadLockEnabled */
> +				    !!setup->RLE,
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_06, /* WriteLockEnabled */
> +				    !!setup->WLE,
> +
> +				    OPAL_ENDNAME,
> +				    OPAL_ENDLIST,
> +				    OPAL_ENDNAME,
> +				    OPAL_ENDLIST);
> +	if (ret < 0) {
> +		pr_err("%s: Error building Setup Locking range command.\n",
> +		       dev->disk_name);
> +		return ret;
> +
> +	}
> +
> +	return finalize_and_send(dev, cmd, generic_cont);
> +}
> +
> +static int start_adminsp_opal_session(struct opal_dev *dev,
> +				      enum OPAL_UID auth,
> +				      const char *key,
> +				      u8 key_len)
> +{
> +	const u8 *method, *smuid, *admin_sp, *hsa;
> +	struct opal_cmd *cmd;
> +	u32 HSN;
> +	int ret;
> +
> +	if (key == NULL && auth != OPAL_ANYBODY_UID) {
> +		pr_err("%s: Attempted to open ADMIN_SP Session without a Host" \
> +		       "Challenge, and not as the Anybody UID\n", __func__);
> +		return 1;
> +	}
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +
> +	set_comID(cmd, dev->comID);
> +	HSN = 0x41;
> +
> +	smuid = OPALUID[OPAL_SMUID_UID];
> +	method = OPALMETHOD[OPAL_STARTSESSION];
> +	admin_sp = OPALUID[OPAL_ADMINSP_UID];
> +
> +	ret = test_and_add_token_va(cmd, "c2s cusc",
> +				    OPAL_CALL,
> +				    smuid, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +				    OPAL_STARTLIST,
> +				    HSN,
> +				    admin_sp, OPAL_UID_LENGTH,
> +				    OPAL_TINY_UINT_01);
> +	if (ret < 0) {
> +		pr_err("%s: Error building start adminsp session command.\n",
> +		       dev->disk_name);
> +		return ret;
> +	}
> +
> +	switch (auth) {
> +	case OPAL_ANYBODY_UID:
> +		/* nothing left to do for anybody, just end and finalize */
> +		ret = test_and_add_token_va(cmd, "c",
> +					    OPAL_ENDLIST);
> +		break;
> +	case OPAL_SID_UID:
> +		hsa = OPALUID[OPAL_SID_UID];
> +		ret = test_and_add_token_va(cmd, "2c s 3c s 2c",
> +					    OPAL_STARTNAME,
> +					    OPAL_TINY_UINT_00, /* HostChallenge */
> +					    key, key_len,
> +					    OPAL_ENDNAME,
> +					    OPAL_STARTNAME,
> +					    OPAL_TINY_UINT_03, /* HostSignAuth */
> +					    hsa, OPAL_UID_LENGTH,
> +					    OPAL_ENDNAME,
> +					    OPAL_ENDLIST);
> +		break;
> +	default:
> +		pr_err("Cannot start Admin SP session with auth %d\n", auth);
> +		return 1;
> +	}
> +
> +	if (ret < 0) {
> +		pr_err("%s: Error building start adminsp session command.\n",
> +		       dev->disk_name);
> +		return ret;
> +	}
> +
> +	return finalize_and_send(dev, cmd, start_opal_session_cont);
> +}
> +
> +static int start_anybodyASP_opal_session(struct opal_dev *dev)
> +{
> +	return start_adminsp_opal_session(dev, OPAL_ANYBODY_UID, NULL, 0);
> +}
> +
> +static int start_SIDASP_opal_session(struct opal_dev *dev)
> +{
> +	int ret;
> +	const u8 *key = dev->prev_data;
> +
> +	if (!key)
> +		ret = start_adminsp_opal_session(dev, OPAL_SID_UID, dev->key,
> +						 dev->key_len);
> +	else {
> +		ret = start_adminsp_opal_session(dev, OPAL_SID_UID, key,
> +						 dev->prev_d_len);
> +		kfree(key);
> +		dev->prev_data = NULL;
> +	}
> +	return ret;
> +}
> +
> +static int start_lockingsp_opal_session(struct opal_dev *dev,
> +					enum OPAL_UID auth, const u8 *key,
> +					u8 key_len)
> +{
> +
> +	const u8 *method, *smuid, *locking_sp, *hsa;
> +	struct opal_cmd *cmd;
> +	size_t klen = key_len;
> +	u32 HSN;
> +	int ret;
> +
> +	if (key == NULL) {
> +		pr_err("Cannot start Locking SP session without a key\n");
> +		return -EINVAL;
> +	}
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +
> +	set_comID(cmd, dev->comID);
> +	HSN = 0x41;
> +
> +	smuid = OPALUID[OPAL_SMUID_UID];
> +	method = OPALMETHOD[OPAL_STARTSESSION];
> +	locking_sp = OPALUID[OPAL_LOCKINGSP_UID];
> +	hsa = OPALUID[auth];
> +
> +	ret = test_and_add_token_va(cmd, "c2s cusc 2csc 2csc c",
> +				    OPAL_CALL,
> +				    smuid, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +
> +				    OPAL_STARTLIST,
> +				    HSN,
> +				    locking_sp, OPAL_UID_LENGTH,
> +				    OPAL_TINY_UINT_01,
> +
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_00, /* HostChallenge */
> +				    key, klen,
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_03, /* Host Sign Authority */
> +				    hsa, OPAL_UID_LENGTH,
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_ENDLIST);
> +
> +	if (ret < 0) {
> +		pr_err("%s: Error building start adminsp session command.\n",
> +		       dev->disk_name);
> +		return ret;
> +	}
> +	return finalize_and_send(dev, cmd, start_opal_session_cont);
> +}
> +
> +static inline int start_admin1LSP_opal_session(struct opal_dev *dev)
> +{
> +	return start_lockingsp_opal_session(dev, OPAL_ADMIN1_UID,
> +					    dev->key, dev->key_len);
> +}
> +
> +static int start_auth_opal_session(struct opal_dev *dev)
> +{
> +	const u8 *method, *smuid, *locking_sp;
> +	u8 lk_ul_user[OPAL_UID_LENGTH];
> +	struct opal_cmd *cmd;
> +	u32 HSN;
> +	int ret;
> +	struct opal_user_info *uinfo;
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +
> +	set_comID(cmd, dev->comID);
> +
> +	HSN = 0x41;
Can we #define this; it's used a few other places and is magicky

> +
> +	uinfo = dev->func_data[dev->state - 1];
> +
> +	smuid = OPALUID[OPAL_SMUID_UID];
> +	method = OPALMETHOD[OPAL_STARTSESSION];
> +	locking_sp = OPALUID[OPAL_LOCKINGSP_UID];
> +
> +	if (uinfo->SUM) {
> +		ret = build_locking_user(lk_ul_user, sizeof(lk_ul_user),
> +					 dev->lr);
> +		if (ret < 0) {
> +			pr_err("%s: Can't build locking user\n",
> +			       dev->disk_name);
> +			return ret;
> +		}
> +	} else if (uinfo->who != OPAL_ADMIN1 && !uinfo->SUM) {
> +		ret = build_locking_user(lk_ul_user, sizeof(lk_ul_user),
> +					 uinfo->who - 1);
> +		if (ret < 0) {
> +			pr_err("%s: Can't build locking user\n",
> +			       dev->disk_name);
> +			return ret;
> +		}
> +	} else
> +		memcpy(lk_ul_user, OPALUID[OPAL_ADMIN1_UID], OPAL_UID_LENGTH);
> +
> +
> +	ret = test_and_add_token_va(cmd, "c2s cus3cs3c s 2c",
> +				    OPAL_CALL,
> +				    smuid, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +
> +				    OPAL_STARTLIST,
> +				    HSN,
> +				    locking_sp, OPAL_UID_LENGTH,
> +				    OPAL_TINY_UINT_01,
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_00,
> +				    dev->key, dev->key_len,
> +				    OPAL_ENDNAME,
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_03,
> +
> +				    lk_ul_user, OPAL_UID_LENGTH,
> +
> +				    OPAL_ENDNAME,
> +				    OPAL_ENDLIST);
> +
> +	if (ret < 0) {
> +		pr_err("%s: Error building STARTSESSION command.\n",
> +		       dev->disk_name);
> +		return ret;
> +	}
> +
> +	return finalize_and_send(dev, cmd, start_opal_session_cont);
> +}
> +
> +static int revert_tper(struct opal_dev *dev)
> +{
> +	const u8 *method, *smuid;
> +	struct opal_cmd *cmd;
> +	int ret;
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +
> +	set_comID(cmd, dev->comID);
> +
> +	smuid = OPALUID[OPAL_ADMINSP_UID];
> +	method = OPALMETHOD[OPAL_REVERT];
> +
> +	ret = test_and_add_token_va(cmd, "c2s 2c",
> +				    OPAL_CALL,
> +				    smuid, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +				    OPAL_STARTLIST,
> +				    OPAL_ENDLIST);
> +	if (ret < 0) {
> +		pr_err("%s: Error building REVERT TPER command.\n",
> +		       dev->disk_name);
> +		return ret;
> +	}
> +
> +	return finalize_and_send(dev, cmd, generic_cont);
> +}
> +
> +static int internal_activate_user(struct opal_dev *dev)
> +{
> +	const u8 *method;
> +	u8 uid[OPAL_UID_LENGTH];
> +	struct opal_cmd *cmd;
> +	int ret;
> +	struct opal_activate_user *act;
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +	set_comID(cmd, dev->comID);
> +
> +	act = dev->func_data[dev->state - 1];
> +
> +	memcpy(uid, OPALUID[OPAL_USER1_UID], OPAL_UID_LENGTH);
> +	uid[7] = act->who.who;
> +
> +	method = OPALMETHOD[OPAL_SET];
> +
> +	ret = test_and_add_token_va(cmd, "c2s 3c c 4c 3c",
> +				    OPAL_CALL,
> +				    uid, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_01, /* Values */
> +
> +				    OPAL_STARTLIST,
> +
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_05, /* Enabled */
> +				    OPAL_TINY_UINT_01, /* True */
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_ENDLIST,
> +				    OPAL_ENDNAME,
> +				    OPAL_ENDLIST);
> +
> +	if (ret < 0) {
> +		pr_err("%s: Error building Activate UserN command.\n",
> +		       dev->disk_name);
> +		return ret;
> +	}
> +
> +	return finalize_and_send(dev, cmd, generic_cont);
> +}
> +
> +static int erase_locking_range(struct opal_dev *dev)
> +{
> +	const u8 *method;
> +	u8 uid[OPAL_UID_LENGTH];
> +	struct opal_cmd *cmd;
> +	int ret;
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +	set_comID(cmd, dev->comID);
> +
> +	method = OPALMETHOD[OPAL_ERASE];
> +
> +	if (build_locking_range(uid, sizeof(uid), dev->lr) < 0) {
> +		pr_err("%s: Can't build locking range\n", dev->disk_name);
> +		return -EINVAL;
> +	}
> +
> +	ret = test_and_add_token_va(cmd, "c2s 2c",
> +				    OPAL_CALL,
> +				    uid, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +
> +				    OPAL_STARTLIST,
> +				    OPAL_ENDLIST);
> +
> +	if (ret < 0) {
> +		pr_err("%s: Error building Erase Locking Range Cmmand.\n",
> +		       dev->disk_name);
> +		return ret;
> +	}
> +	return finalize_and_send(dev, cmd, generic_cont);
> +}
> +
> +static int set_mbr_done(struct opal_dev *dev)
> +{
> +	const u8 *method, *uid;
> +	struct opal_cmd *cmd;
> +	int ret;
> +
> +	u8 mbr_done_tf = *(u8 *)dev->func_data[dev->state - 1];
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +	set_comID(cmd, dev->comID);
> +
> +	method = OPALMETHOD[OPAL_SET];
> +	uid = OPALUID[OPAL_MBRCONTROL];
> +
> +	ret = test_and_add_token_va(cmd, "c2s 3c 6c 2c",
> +				    OPAL_CALL,
> +				    uid, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTNAME,
> +				    OPAL_VALUES,
> +
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_02, /* Done */
> +				    mbr_done_tf,       /* Done T or F */
> +				    OPAL_ENDNAME,
> +				    OPAL_ENDLIST,
> +
> +				    OPAL_ENDNAME,
> +				    OPAL_ENDLIST);
> +	if (ret < 0) {
> +		pr_err("%s: Error Building set MBR Dont/Not done command\n",
s/Dont/Done/ ?


> +		       dev->disk_name);
> +		return ret;
> +	}
> +
> +	return finalize_and_send(dev, cmd, generic_cont);
> +}
> +
> +static int set_mbr_enable_disable(struct opal_dev *dev)
> +{
> +	const u8 *method, *uid;
> +	struct opal_cmd *cmd;
> +	int ret;
> +
> +	u8 mbr_en_dis = *(u8 *)dev->func_data[dev->state - 1];
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +	set_comID(cmd, dev->comID);
> +
> +	method = OPALMETHOD[OPAL_SET];
> +	uid = OPALUID[OPAL_MBRCONTROL];
> +
> +	ret = test_and_add_token_va(cmd, "c2s 3c 6c 2c",
> +				    OPAL_CALL,
> +				    uid, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTNAME,
> +				    OPAL_VALUES,
> +
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_01, /* Enable */
> +				    mbr_en_dis,        /* Enable or Disable */
> +				    OPAL_ENDNAME,
> +				    OPAL_ENDLIST,
> +
> +				    OPAL_ENDNAME,
> +				    OPAL_ENDLIST);
> +	if (ret < 0) {
> +		pr_err("%s: Error Building set MBR Dont/Not done command\n",
s/Dont/Done/ ?


> +		       dev->disk_name);
> +		return ret;
> +	}
> +
> +	return finalize_and_send(dev, cmd, generic_cont);
> +}
> +
> +static int set_new_pw(struct opal_dev *dev)
> +{
> +	const u8 *method;
> +	u8 cpin_uid[OPAL_UID_LENGTH];
> +	struct opal_cmd *cmd;
> +	int ret;
> +	struct opal_new_pw *pw;
> +	size_t key_len;
> +	u8 *key;
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +	set_comID(cmd, dev->comID);
> +
> +	pw = dev->func_data[dev->state - 1];
> +	key = pw->new_pin.key;
> +	key_len = pw->new_pin.key_len;
> +	memcpy(cpin_uid, OPALUID[OPAL_C_PIN_ADMIN1], OPAL_UID_LENGTH);
> +
> +	if (pw->user_for_pw != OPAL_ADMIN1) {
> +		cpin_uid[5] = 0x03;
> +		if (pw->who.SUM)
> +			cpin_uid[7] = pw->new_pin.lr + 1;
> +		else
> +			cpin_uid[7] = pw->user_for_pw;
> +	}
> +
> +	method = OPALMETHOD[OPAL_SET];
> +
> +	ret = test_and_add_token_va(cmd, "c2s 3c 3cs2c 2c",
> +				    OPAL_CALL,
> +				    cpin_uid, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_01, /* Values */
> +
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_03, /* PIN */
> +				    key, key_len,
> +				    OPAL_ENDNAME,
> +				    OPAL_ENDLIST,
> +
> +				    OPAL_ENDNAME,
> +				    OPAL_ENDLIST);
> +
> +	if (ret < 0) {
> +		pr_err("%s: Error building SET AMIN1 PIN command.\n",
> +		       dev->disk_name);
> +		return ret;
> +	}
> +
> +	return finalize_and_send(dev, cmd, generic_cont);
> +}
> +
> +static int set_sid_cpin_pin(struct opal_dev *dev)
> +{
> +	const u8 *method, *cpin_uid;
> +	struct opal_cmd *cmd;
> +	int ret;
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +	set_comID(cmd, dev->comID);
> +
> +	cpin_uid = OPALUID[OPAL_C_PIN_SID];
> +	method = OPALMETHOD[OPAL_SET];
> +
> +	ret = test_and_add_token_va(cmd, "c2s 2c 4cs2c 2c",
> +				    OPAL_CALL,
> +				    cpin_uid, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTNAME,
> +
> +				    OPAL_TINY_UINT_01, /* Values */
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_03, /* PIN */
> +				    dev->key, dev->key_len,
> +				    OPAL_ENDNAME,
> +				    OPAL_ENDLIST,
> +
> +				    OPAL_ENDNAME,
> +				    OPAL_ENDLIST);
> +
> +	if (ret < 0) {
> +		pr_err("%s: Error building SET CPIN PIN command.\n",
> +		       dev->disk_name);
> +		return ret;
> +	}
> +
> +	return finalize_and_send(dev, cmd, generic_cont);
> +}
> +
> +static void query_locking_range_cont(int error, void *data)
> +{
> +	struct opal_dev *dev = data;
> +
> +	if (error)
> +		goto err_return;
> +
> +	error = parse_and_check_status(dev);
> +	if (error)
> +		goto err_return;
> +
> +	dev->start = response_get_u64(&dev->parsed, 4);
> +	dev->length = response_get_u64(&dev->parsed, 8);
> +
> +err_return:
> +	if (dev->oper_cb)
> +		dev->oper_cb(error, dev);
> +}
> +
> +static int query_locking_range(struct opal_dev *dev)
> +{
> +	u8 lr_buffer[OPAL_UID_LENGTH];
> +	struct opal_cmd *cmd;
> +	const u8 *method;
> +	int ret;
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +
> +	method = OPALMETHOD[OPAL_GET];
> +
> +	if (build_locking_range(lr_buffer, sizeof(lr_buffer), dev->lr) < 0) {
> +		pr_err("%s: Can't build locking range\n", dev->disk_name);
> +		return -EINVAL;
> +	}
> +
> +	set_comID(cmd, dev->comID);
> +
> +	ret = test_and_add_token_va(cmd, "c2s 12c",
> +				    OPAL_CALL,
> +				    lr_buffer, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTNAME,
> +				    OPAL_STARTCOLUMN,
> +				    OPAL_RANGESTART,
> +				    OPAL_ENDNAME,
> +				    OPAL_STARTNAME,
> +				    OPAL_ENDCOLUMN,
> +				    OPAL_RANGELENGTH,
> +				    OPAL_ENDNAME,
> +				    OPAL_ENDLIST,
> +				    OPAL_ENDLIST);
> +
> +	if (ret < 0) {
> +		pr_err("%s: Error building GET Locking Range command.\n",
> +		       dev->disk_name);
> +		return ret;
> +	}
> +
> +	return finalize_and_send(dev, cmd, query_locking_range_cont);
> +}
> +
> +static int add_user_to_lr(struct opal_dev *dev)
> +{
> +	u8 lr_buffer[OPAL_UID_LENGTH];
> +	u8 user_uid[OPAL_UID_LENGTH];
> +	struct opal_cmd *cmd;
> +	const u8 *method;
> +	struct opal_lock_unlock *lkul;
> +	int ret;
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +	set_comID(cmd, dev->comID);
> +
> +	method = OPALMETHOD[OPAL_SET];
> +
> +	lkul = dev->func_data[dev->state - 1];
> +
> +	memcpy(lr_buffer, OPALUID[OPAL_LOCKINGRANGE_ACE_RDLOCKED],
> +	       OPAL_UID_LENGTH);
> +
> +	if (lkul->l_state == OPAL_RW)
> +		memcpy(lr_buffer, OPALUID[OPAL_LOCKINGRANGE_ACE_WRLOCKED],
> +		       OPAL_UID_LENGTH);
> +
> +	lr_buffer[7] = dev->lr;
> +
> +	memcpy(user_uid, OPALUID[OPAL_USER1_UID], OPAL_UID_LENGTH);
> +	user_uid[7] = lkul->authority.who;
> +
> +	ret = test_and_add_token_va(cmd, "c2s 3c 3c 2c 2sc c2sc cs2c 5c",
> +				    OPAL_CALL,
> +				    lr_buffer, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_01, /* Values */
> +
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_03, /* BooleanExpr */
> +
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTNAME,
> +
> +				    OPALUID[OPAL_HALF_UID_AUTHORITY_OBJ_REF],
> +				    OPAL_UID_LENGTH_HALF,
> +				    user_uid, OPAL_UID_LENGTH,
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_STARTNAME,
> +				    OPALUID[OPAL_HALF_UID_AUTHORITY_OBJ_REF],
> +				    OPAL_UID_LENGTH_HALF,
> +				    user_uid, OPAL_UID_LENGTH,
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_STARTNAME,
> +				    OPALUID[OPAL_HALF_UID_BOOLEAN_ACE],
> +				    OPAL_UID_LENGTH_HALF,
> +				    OPAL_TINY_UINT_01,
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_ENDLIST,
> +				    OPAL_ENDNAME,
> +				    OPAL_ENDLIST,
> +				    OPAL_ENDNAME,
> +				    OPAL_ENDLIST);
> +	if (ret < 0) {
> +		pr_err("%s: Error building add user to locking range command.\n",
> +		       dev->disk_name);
> +		return ret;
> +	}
> +
> +	return finalize_and_send(dev, cmd, generic_cont);
> +}
> +
> +static int lock_unlock_locking_range(struct opal_dev *dev)
> +{
> +	u8 lr_buffer[OPAL_UID_LENGTH];
> +	struct opal_cmd *cmd;
> +	const u8 *method;
> +	struct opal_lock_unlock *lkul;
> +	int ret;
> +	u8 read_locked = 1, write_locked = 1;
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +	set_comID(cmd, dev->comID);
> +
> +	method = OPALMETHOD[OPAL_SET];
> +	lkul = dev->func_data[dev->state - 1];
> +	if (build_locking_range(lr_buffer, sizeof(lr_buffer), dev->lr) < 0) {
> +		pr_err("%s: Can't build locking range\n", dev->disk_name);
> +		return -EINVAL;
> +	}
> +
> +	switch (lkul->l_state) {
> +	case OPAL_RO:
> +		read_locked = 0;
> +		write_locked = 1;
> +		break;
> +	case OPAL_RW:
> +		read_locked = 0;
> +		write_locked = 0;
> +		break;
> +	case OPAL_LK:
> +		/* vars are initalized to locked */
> +		break;
> +	default:
> +		pr_err("Tried to set an invalid locking state... returning to uland\n");
> +		return 1;
> +	}
> +
> +	ret = test_and_add_token_va(cmd, "c2sc 3c 4c 4c 3c",
> +				    OPAL_CALL,
> +				    lr_buffer, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +				    OPAL_STARTLIST,
> +
> +				    OPAL_STARTNAME,
> +				    OPAL_VALUES,
> +				    OPAL_STARTLIST,
> +
> +				    OPAL_STARTNAME,
> +				    OPAL_READLOCKED,
> +				    read_locked,
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_STARTNAME,
> +				    OPAL_WRITELOCKED,
> +				    write_locked,
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_ENDLIST,
> +				    OPAL_ENDNAME,
> +				    OPAL_ENDLIST);
> +
> +	if (ret < 0) {
> +		pr_err("%s: Error building SET command.\n", dev->disk_name);
> +		return ret;
> +	}
> +	return finalize_and_send(dev, cmd, generic_cont);
> +}
> +
> +
> +static int lock_unlock_locking_range_SUM(struct opal_dev *dev)
> +{
> +	u8 lr_buffer[OPAL_UID_LENGTH];
> +	struct opal_cmd *cmd;
> +	const u8 *method;
> +	struct opal_lock_unlock *lkul;
> +	int ret;
> +	u8 read_locked = 1, write_locked = 1;
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +	set_comID(cmd, dev->comID);
> +
> +	method = OPALMETHOD[OPAL_SET];
> +	lkul = dev->func_data[dev->state - 1];
> +	if (build_locking_range(lr_buffer, sizeof(lr_buffer), dev->lr) < 0) {
> +		pr_err("%s: Can't build locking range\n", dev->disk_name);
> +		return -EINVAL;
> +	}
> +
> +	switch (lkul->l_state) {
> +	case OPAL_RO:
> +		read_locked = 0;
> +		write_locked = 1;
> +		break;
> +	case OPAL_RW:
> +		read_locked = 0;
> +		write_locked = 0;
> +		break;
> +	case OPAL_LK:
> +		/* vars are initalized to locked */
> +		break;
> +	default:
> +		pr_err("Tried to set an invalid locking state.\n");
> +		return 1;
> +	}
> +
> +	ret = test_and_add_token_va(cmd, "c2sc 3c 4c 4c 4c 4c 3c",
> +				    OPAL_CALL,
> +				    lr_buffer, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +				    OPAL_STARTLIST,
> +
> +				    OPAL_STARTNAME,
> +				    OPAL_VALUES,
> +				    OPAL_STARTLIST,
> +
> +				    OPAL_STARTNAME,
> +				    OPAL_READLOCKENABLED,
> +				    OPAL_TRUE,
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_STARTNAME,
> +				    OPAL_WRITELOCKENABLED,
> +				    OPAL_TRUE,
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_STARTNAME,
> +				    OPAL_READLOCKED,
> +				    read_locked,
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_STARTNAME,
> +				    OPAL_WRITELOCKED,
> +				    write_locked,
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_ENDLIST,
> +				    OPAL_ENDNAME,
> +				    OPAL_ENDLIST);
> +	if (ret < 0) {
> +		pr_err("%s: Error building SET command.\n", dev->disk_name);
> +		return ret;
> +	}
> +	return finalize_and_send(dev, cmd, generic_cont);
> +}
> +
> +int activate_lsp(struct opal_dev *dev)
> +{
> +	u8 user_lr[OPAL_UID_LENGTH];
> +	const u8 *method, *uid;
> +	struct opal_cmd *cmd;
> +	int ret;
> +	size_t uint_3 = 0x83;
> +
> +	cmd = &dev->cmd;
> +
> +	clear_opal_cmd(cmd);
> +	set_comID(cmd, dev->comID);
> +
> +	uid = OPALUID[OPAL_LOCKINGSP_UID];
> +	method = OPALMETHOD[OPAL_ACTIVATE];
> +
> +	ret = test_and_add_token_va(cmd, "c2s",
> +				    OPAL_CALL,
> +				    uid, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH);
> +	if (ret < 0) {
> +		pr_err("%s: Error building Activate LockingSP command.\n",
> +		       dev->disk_name);
> +		return ret;
> +	}
> +	/* Activating as SUM */
> +	if (dev->lr > 0) {
> +		ret = build_locking_range(user_lr, sizeof(user_lr), dev->lr);
> +		if (ret < 0) {
> +			pr_err("%s: Can't build locking user\n",
> +			       dev->disk_name);
> +			return ret;
> +		}
> +		test_and_add_token_va(cmd, "2c 4c csc 2c",
> +				      OPAL_STARTLIST,
> +				      OPAL_STARTNAME,
> +
> +				      uint_3,
> +				      OPAL_TINY_UINT_06,
> +				      OPAL_TINY_UINT_00,
> +				      OPAL_TINY_UINT_00,
> +
> +				      OPAL_STARTLIST,
> +				      user_lr, OPAL_UID_LENGTH,
> +				      OPAL_ENDLIST,
> +
> +				      OPAL_ENDNAME,
> +				      OPAL_ENDLIST);
> +	} else /* Actiave Normal Mode */
> +		ret = test_and_add_token_va(cmd, "2c",
> +					    OPAL_STARTLIST,
> +					    OPAL_ENDLIST);
> +
> +	if (ret < 0) {
> +		pr_err("%s: Error building Activate LockingSP command.\n",
> +		       dev->disk_name);
> +		return ret;
> +	}
> +
> +	return finalize_and_send(dev, cmd, generic_cont);
> +}
> +
> +static void get_lsp_lifecycle_cont(int error, void *data)
> +{
> +
> +	struct opal_dev *dev = data;
> +	u8 lc_status;
> +
> +	if (error)
> +		goto err_return;
> +
> +	error = parse_and_check_status(dev);
> +	if (error)
> +		goto err_return;
> +
> +	lc_status = response_get_u64(&dev->parsed, 4);
> +	/* 0x08 is Manufacured Inactive */
> +	/* 0x09 is Manufactured */
> +	if (lc_status != 0x08) {
> +		pr_err("%s: Couldn't determine the status of the Lifcycle state\n",
> +		       dev->disk_name);
> +		error = -ENODEV;
> +	}
> +
> +err_return:
> +	if (dev->oper_cb)
> +		dev->oper_cb(error, dev);
> +}
> +
> +/* Determine if we're in the Manufactured Inactive or Active state */
> +int get_lsp_lifecycle(struct opal_dev *dev)
> +{
> +	struct opal_cmd *cmd;
> +	const u8 *method, *uid;
> +	int ret;
> +
> +	cmd = &dev->cmd;
> +
> +	clear_opal_cmd(cmd);
> +	set_comID(cmd, dev->comID);
> +
> +	uid = OPALUID[OPAL_LOCKINGSP_UID];
> +	method = OPALMETHOD[OPAL_GET];
> +
> +	ret = test_and_add_token_va(cmd, "c2s 2c 4c 4c 2c",
> +				    OPAL_CALL,
> +				    uid, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTLIST,
> +
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_03, /* Start Column */
> +				    OPAL_TINY_UINT_06, /* Lifcycle Column */
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_04, /* End Column */
> +				    OPAL_TINY_UINT_06, /* Lifecycle Column */
> +				    OPAL_ENDNAME,
> +
> +				    OPAL_ENDLIST,
> +				    OPAL_ENDLIST);
> +
> +	if (ret < 0) {
> +		pr_err("%s: Error Building GET Lifecycle Status command\n",
> +		       dev->disk_name);
> +		return ret;
> +	}
> +
> +	return finalize_and_send(dev, cmd, get_lsp_lifecycle_cont);
> +}
> +
> +static void get_msid_cpin_pin_cont(int error, void *data)
> +{
> +	const char *msid_pin;
> +	struct opal_dev *dev = data;
> +	size_t strlen;
> +
> +	if (error)
> +		goto err_return;
> +
> +	error = parse_and_check_status(dev);
> +	if (error)
> +		goto err_return;
> +
> +	strlen = response_get_string(&dev->parsed, 4, &msid_pin);
> +	if (!msid_pin) {
> +		pr_err("%s: Couldn't extract PIN from response\n", __func__);
> +		error = 1;
> +		goto err_return;
> +	}
> +
> +	dev->prev_data = kmemdup(msid_pin, strlen, GFP_KERNEL);
> +	if (!dev->prev_data)
> +		error = -ENOMEM;
> +
> +	dev->prev_d_len = strlen;
> +
> + err_return:
> +	if (dev->oper_cb)
> +		dev->oper_cb(error, dev);
> +}
> +
> +static int get_msid_cpin_pin(struct opal_dev *dev)
> +{
> +	const u8 *method, *smuid;
> +	int ret;
> +	struct opal_cmd *cmd;
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +	set_comID(cmd, dev->comID);
> +
> +	smuid = OPALUID[OPAL_C_PIN_MSID];
> +	method = OPALMETHOD[OPAL_GET];
> +
> +	ret = test_and_add_token_va(cmd, "c 2s 12c",
> +				    OPAL_CALL,
> +
> +				    smuid, OPAL_UID_LENGTH,
> +				    method, OPAL_METHOD_LENGTH,
> +
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTLIST,
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_03, /* Sart Column */
> +				    OPAL_TINY_UINT_03, /* PIN */
> +				    OPAL_ENDNAME,
> +				    OPAL_STARTNAME,
> +				    OPAL_TINY_UINT_04, /* End Column */
> +				    OPAL_TINY_UINT_03, /* PIN */
> +				    OPAL_ENDNAME,
> +				    OPAL_ENDLIST,
> +				    OPAL_ENDLIST);
> +
> +	if (ret < 0) {
> +		pr_err("%s: Error building Get MSID CPIN PIN command.\n",
> +		       dev->disk_name);
> +		return ret;
> +	}
> +
> +	return finalize_and_send(dev, cmd, get_msid_cpin_pin_cont);
> +}
> +
> +static void unlock_suspend_final(int error, void *data)
> +{
> +	struct opal_dev *dev = data;
> +
> +	dev->resume_from_suspend = false;
> +	dev->resume_data = NULL;
> +	dev->func_data = NULL;
> +	dev->bdev = NULL;
> +}
> +
> +static int build_end_opal_session(struct opal_dev *dev)
> +{
> +	struct opal_cmd *cmd;
> +
> +	cmd = &dev->cmd;
> +	clear_opal_cmd(cmd);
> +
> +	set_comID(cmd, dev->comID);
> +	return test_and_add_token_u8(cmd, OPAL_ENDOFSESSION);
> +}
> +
> +static int end_opal_session(struct opal_dev *dev)
> +{
> +	if (build_end_opal_session(dev) < 0)
> +		return -1;
> +	return finalize_and_send(dev, &dev->cmd, end_session_cont);
> +}
Any reason we cant:

int ret = build_...
if (ret < 0)
	return ret;


> +
> +static struct opal_dev *find_opal_dev(struct block_device *bdev, u8 lr)
> +{
> +	struct opal_dev *iter, *opal_dev = NULL;
> +
> +	list_for_each_entry(iter, &opal_list, node) {
> +		if (strncmp(iter->disk_name, bdev->bd_disk->disk_name,
> +			    DISK_NAME_LEN))
> +			continue;
> +		if (iter->lr == lr) {
> +			opal_dev = iter;
> +			break;
> +		}
> +	}
> +
> +	return opal_dev;
> +}
> +
> +static int update_opal_dev(struct opal_dev *old_dev, struct opal_dev *new_dev)
> +{
> +	if (!atomic_add_unless(&old_dev->in_use, 1, 1)) {
> +		pr_err("%s: dev was in use\n", __func__);
> +		return -EBUSY;
> +	}
> +
> +	old_dev->key_name_len = new_dev->key_name_len;
> +	if (!memcpy(old_dev->key_name, new_dev->key_name, old_dev->key_name_len)) {
> +		pr_err("%s: Error updating device:\n", old_dev->disk_name);
> +		return -EFAULT;
> +	}
> +
> +	if (!strncpy(old_dev->disk_name, new_dev->disk_name, DISK_NAME_LEN)) {
> +		pr_err("%s: Error registering device: copying disk name\n",
> +		       old_dev->disk_name);
> +		return -EFAULT;
> +	}
> +
> +	old_dev->comID = new_dev->comID;
> +	old_dev->start = new_dev->start;
> +	old_dev->length = new_dev->length;
> +	old_dev->align = new_dev->align;
> +	old_dev->lowest_lba = new_dev->lowest_lba;
> +	old_dev->bdev = NULL;
> +	old_dev->final_cb = new_dev->final_cb;
> +	old_dev->final_cb_data = new_dev->final_cb_data;
> +	old_dev->oper_cb = new_dev->oper_cb;
> +	old_dev->state = new_dev->state;
> +	old_dev->funcs = new_dev->funcs;
> +
> +	kfree(old_dev->completion);
> +	clean_function_data(old_dev);
> +
> +	old_dev->completion = new_dev->completion;
> +
> +	/*
> +	 * Won't be able to auto unlock this locking range based on block
> +	 * requestes.
> +	 */
> +	if (old_dev->length == 0)
> +		pr_warn("%s: Missing block information for locking range %d\n",
> +			old_dev->disk_name, old_dev->lr);
> +
> +	return 0;
> +}
> +
> +int opal_register_cont(struct opal_dev *new_dev)
> +{
> +	struct opal_dev *old_dev;
> +	unsigned long flags;
> +	int error = 0;
> +
> +	spin_lock_irqsave(&list_spinlock, flags);
> +
> +	old_dev = find_opal_dev(new_dev->bdev, new_dev->lr);
> +	if (!old_dev) {
> +		list_add_tail(&new_dev->node, &opal_list);
> +		old_dev = new_dev;
> +	} else {
> +		if (old_dev == new_dev)
> +			error = 0;
> +		else {
> +			error = update_opal_dev(old_dev, new_dev);
> +			clean_opal_key(new_dev);
> +			kfree(new_dev);
> +		}
> +	}
> +
> +	if (error)
> +		list_del(&old_dev->node);
> +
> +	spin_unlock_irqrestore(&list_spinlock, flags);
> +
> +	if (!error)
> +		pr_info("%s: Registered key for locking range: %d\n",
> +			old_dev->disk_name, old_dev->lr);
> +
> +	if (old_dev->oper_cb)
> +		old_dev->oper_cb(error, old_dev);
> +
> +	return 0;
> +}
> +
> +static void next(int error, struct opal_dev *dev)
> +{
> +	opal_step func = dev->funcs[dev->state];
> +	void *cb_data = dev->final_cb_data;
> +	sec_cb *cb = dev->final_cb;
> +	bool done = false;
> +
> +
> +	if (error || !func) {
> +		done = true;
> +		goto next_exit;
> +	}
> +	dev->state++;
> +	dev->oper_cb = next;
> +	error = func(dev);
> + next_exit:
> +	if (error) {
> +		pr_err("%s: Error on step function: %d with error %d: %s\n",
> +		       dev->disk_name, dev->state, error,
> +		       opal_error_to_human(error));
> +
> +
> +		atomic_dec(&dev->in_use);
> +		if (dev->error_cb) {
> +			dev->error_cb(dev->error_cb_data);
> +			return;
> +		}
> +		if (cb)
> +			cb(error, cb_data);
> +
> +		dev->completion->completion_status = error;
> +		complete(&dev->completion->cmd_completion);
> +	} else if (!error && done) {
> +		atomic_dec(&dev->in_use);
> +		if (cb)
> +			cb(error, cb_data);
> +		dev->completion->completion_status = error;
> +		complete(&dev->completion->cmd_completion);
> +	}
> +}
> +
> +const opal_step error_end_session[] = {
> +	end_opal_session,
> +	NULL,
> +};
> +static int end_opal_session_error(struct opal_dev *dev)
> +{
> +
> +	dev->funcs = error_end_session;
> +	dev->state = 0;
> +	dev->error_cb = NULL;
> +	next(0, dev);
> +	return 0;
> +}
> +
> +static struct opal_dev *alloc_opal_dev(struct block_device *bdev, u8 lr)
> +{
> +	struct opal_dev *opal_dev;
> +	struct request_queue *q;
> +	unsigned long dma_align;
> +	const char *disk_name;
> +	struct opal_cmd *cmd;
> +	int ret;
> +
> +	opal_dev = kzalloc(sizeof(*opal_dev), GFP_KERNEL);
> +	if (!opal_dev)
> +		return ERR_PTR(-ENOMEM);
> +
> +	opal_dev->bdev = bdev;
> +	opal_dev->lr = lr;
> +	cmd = &opal_dev->cmd;
> +	cmd->cmd = cmd->cmd_buf;
> +	cmd->resp = cmd->resp_buf;
> +
> +	disk_name = bdev->bd_disk->disk_name;
> +	if (!strncpy(opal_dev->disk_name, disk_name, DISK_NAME_LEN)) {
> +		pr_err("%s: Error registering device: copying disk name\n",
> +		       disk_name);
> +		ret = -EFAULT;
> +		goto err_free_dev;
> +	}
> +
> +	q = bdev->bd_queue;
> +	dma_align = (queue_dma_alignment(q) | q->dma_pad_mask) + 1;
> +	cmd->cmd = (u8 *)round_up((uintptr_t)cmd->cmd, dma_align);
> +	cmd->resp = (u8 *)round_up((uintptr_t)cmd->resp, dma_align);
> +
> +	INIT_LIST_HEAD(&opal_dev->node);
> +	atomic_set(&opal_dev->in_use, 1);
> +
> +	opal_dev->completion = kzalloc(sizeof(*opal_dev->completion),
> +				       GFP_KERNEL);
> +
> +	if (!opal_dev->completion)
> +		goto err_free_dev;
> +
> +	init_completion(&opal_dev->completion->cmd_completion);
> +	opal_dev->completion->completion_status = 0;
> +	opal_dev->state = 0;
> +
> +	return opal_dev;
> +
> +err_free_dev:
> +	kfree(opal_dev);
> +	return ERR_PTR(ret);
> +}
> +
> +int opal_register(struct block_device *bdev, struct opal_key *key_cmd,
> +		  const opal_step *funcs)
> +{
> +	struct opal_dev *new_dev = NULL;
> +	u8 key_len = key_cmd->key_len;
> +	u8 lr = key_cmd->lr;
> +	struct opal_completion *completion;
> +	int ret;
> +
> +	new_dev = alloc_opal_dev(bdev, lr);
> +	if (IS_ERR(new_dev)) {
> +		pr_err("%s: Error registering device: allocation\n",
> +		       bdev->bd_disk->disk_name);
> +		return PTR_ERR(new_dev);
> +	}
> +
> +	if (!memcpy(new_dev->key_name, key_cmd->key, key_len)) {
> +		pr_err("%s: Error registering key: couldn't copy key\n",
> +		       new_dev->disk_name);
> +		return -EFAULT;
> +	}
> +
> +	new_dev->key_name_len = key_len;
> +	new_dev->key_type = key_cmd->key_type;
> +	ret = get_opal_key(new_dev);
> +	if (ret) {
> +		pr_err("%s: Couldn't get key: %d\n", new_dev->disk_name, ret);
> +		return ret;
> +	}
> +
> +	new_dev->funcs = funcs;
> +
> +	new_dev->state = 0;
> +	completion = new_dev->completion;
> +	next(0, new_dev);
> +
> +	return wait_for_cmd_completion(completion);
> +}
> +
> +static struct opal_dev *get_registered_opal_dev(struct block_device *bdev,
> +						u8 lr)
> +{
> +	const char *diskname = bdev->bd_disk->disk_name;
> +	struct opal_dev *iter, *dev = NULL;
> +	unsigned long flags;
> +	bool in_use = false;
> +
> +	spin_lock_irqsave(&list_spinlock, flags);
> +	list_for_each_entry(iter, &opal_list, node) {
> +		if (strncmp(iter->disk_name, diskname, DISK_NAME_LEN))
> +			continue;
> +		if (iter->lr == lr) {
> +			dev = iter;
> +			if (!atomic_add_unless(&iter->in_use, 1, 1)) {
> +				dev = NULL;
> +				in_use = true;
> +			}
> +			break;
> +		}
> +	}
> +
> +	spin_unlock_irqrestore(&list_spinlock, flags);
> +
> +	if (!dev)
> +		return NULL;
> +
> +	dev->bdev = bdev;
> +	return dev;
> +}
> +
> +/* Free up the Opal dev and its keys during two scenarios:
> + *
> + * 1) When a command is complete that no longer requires
> + *    the opal dev to be around.
> + * 2) When a command, including Opal Save fails we clean
> + *    and free the opal dev.
> + *
> + *    If we find the opal dev structure in the list of
> + *    saved passwords we will *not* remove it.
> + */
> +static void remove_and_clean_opal_dev(struct opal_dev *dev)
> +{
> +	struct opal_dev *iter;
> +	bool found = false;
> +
> +	spin_lock(&list_spinlock);
> +	list_for_each_entry(iter, &opal_list, node) {
> +		if (iter == dev) {
> +			found = true;
> +			break;
> +		}
> +	}
> +
> +	spin_unlock(&list_spinlock);
> +	if (!found) {
> +		clean_opal_key(dev);
> +		clean_function_data(dev);
> +		kfree(dev);
> +	}
> +}
> +
> +static struct opal_dev *get_or_create_opal_dev(struct block_device *bdev,
> +					       u8 lr, bool use_new)
> +{
> +	struct opal_dev *dev;
> +
> +	if (use_new)
> +		return alloc_opal_dev(bdev, lr);
> +
> +	dev = get_registered_opal_dev(bdev, lr);
> +	if (!dev) {
> +		dev = alloc_opal_dev(bdev, lr);
> +		if (!dev)
> +			return NULL;
No need for this check when you already return dev


> +	}
> +	return dev;
> +}
Instead, how about:
static struct opal_dev *get_or_create_opal_dev(struct block_device *bdev,
					       u8 lr, bool use_new)
{
	struct opal_dev *dev = NULL;

	if (!use_new)
		dev = get_registered_opal_dev(bdev, lr);

	if (!dev)
		dev = alloc_opal_dev(bdev, lr);

	return dev;
}


> +
> +static struct opal_completion *setup_opal_dev(struct block_device *bdev,
> +					      struct opal_dev *dev,
> +					      const opal_step *funcs,
> +					      struct opal_key *key)
> +{
> +	int ret;
> +
> +	dev->bdev = bdev;
> +	dev->state = 0;
> +	dev->funcs = funcs;
> +	dev->TSN = 0;
> +	dev->HSN = 0;
> +	dev->final_cb = NULL;
> +	dev->final_cb_data = NULL;
> +	dev->lr = key->lr;
> +	dev->error_cb = end_opal_session_error;
> +	dev->error_cb_data = dev;
> +
> +	if (key) {
> +		memcpy(dev->key_name, key->key, key->key_len);
> +		dev->key_name_len = key->key_len;
> +		dev->key_type = key->key_type;
> +
> +		ret = get_opal_key(dev);
> +		if (ret) {
> +			kfree(dev->completion);
> +			pr_err("%s: Couldn't get key: %d\n",
> +			       dev->disk_name, ret);
> +			return ERR_PTR(ret);
> +		}
> +	}
> +	dev->func_data = NULL;
> +	dev->completion->completion_status = 0;
> +
> +	return dev->completion;
> +}
> +
> +static int internal_setup_lr(struct block_device *bdev,
> +			     struct opal_user_lr_setup *setup)
> +{
> +	struct opal_dev *dev;
> +	struct opal_completion *completion;
> +	void *data[3] = { NULL };
> +	const opal_step lr_funcs[] = {
> +		opal_discovery0,
> +		start_auth_opal_session,
> +		setup_locking_range,
> +		get_active_key,
> +		gen_key,
> +		end_opal_session,
> +		NULL,
> +	};
> +	int ret;
> +
> +	dev = get_or_create_opal_dev(bdev, setup->key.lr, true);
> +	if (!dev)
> +		return -ENOMEM;
> +
> +	completion = setup_opal_dev(bdev, dev, lr_funcs, &setup->key);
> +	if (IS_ERR(completion)) {
> +		ret = PTR_ERR(completion);
> +		goto error_return;
> +	}
> +
> +	dev->func_data = data;
> +	dev->num_func_data = 3;
> +	dev->func_data[1] = &setup->who;
> +	dev->func_data[2] = setup;
> +
> +	next(0, dev);
> +	ret = wait_for_cmd_completion(completion);
> +
> + error_return:
> +	remove_and_clean_opal_dev(dev);
> +	return ret;
> +}
> +
> +int opal_revert(struct block_device *bdev, struct opal_key *key)
> +{
> +	const opal_step revert_funcs[] = {
> +		opal_discovery0,
> +		start_SIDASP_opal_session,
> +		revert_tper, /* controller will terminate session */
> +		NULL,
> +	};
> +
> +	return opal_register(bdev, key, revert_funcs);
> +}
> +
> +int activate_user(struct block_device *bdev, struct opal_activate_user *act)
> +{
> +	const opal_step act_funcs[] = {
> +		opal_discovery0,
> +		start_admin1LSP_opal_session,
> +		internal_activate_user,
> +		end_opal_session,
> +		NULL
> +	};
> +	struct opal_dev *dev;
> +	struct opal_completion *completion;
> +	void *data[3] = { NULL };
> +	int ret;
> +
> +	dev = get_or_create_opal_dev(bdev, act->key.lr, true);
> +	if (!dev)
> +		return -ENOMEM;
> +
> +	completion = setup_opal_dev(bdev, dev, act_funcs, &act->key);
> +	if (IS_ERR(completion)) {
> +		ret = PTR_ERR(completion);
> +		goto error_return;
> +	}
> +
> +	dev->num_func_data = 3;
> +	dev->func_data = data;
> +	dev->func_data[1] = act;
> +	dev->func_data[2] = act;
> +
> +	next(0, dev);
> +	ret = wait_for_cmd_completion(completion);
> +
> + error_return:
> +	remove_and_clean_opal_dev(dev);
> +	return ret;
> +}
> +
> +int opal_set_pw(struct block_device *bdev, struct opal_new_pw *pw)
> +
> +{
> +	const opal_step pw_funcs[] = {
> +		opal_discovery0,
> +		start_auth_opal_session,
> +		set_new_pw,
> +		end_opal_session,
> +		NULL
> +	};
> +	struct opal_dev *dev;
> +	struct opal_completion *completion;
> +	void *data[3] = { NULL };
> +	int ret;
> +
> +	dev = get_or_create_opal_dev(bdev, pw->current_pin.lr, true);
> +	if (!dev)
> +		return -ENOMEM;
> +
> +	completion = setup_opal_dev(bdev, dev, pw_funcs, &pw->current_pin);
> +	if (IS_ERR(completion)) {
> +		ret = PTR_ERR(completion);
> +		goto error_return;
> +	}
> +
> +	dev->num_func_data = 3;
> +	dev->func_data = data;
> +	dev->func_data[1] = (void *) &pw->who;
> +	dev->func_data[2] = (void *) pw;
> +
> +	next(0, dev);
> +	ret = wait_for_cmd_completion(completion);
> +
> + error_return:
> +	remove_and_clean_opal_dev(dev);
> +	return ret;
> +}
> +
> +int opal_act_lsp_int(struct block_device *bdev, struct opal_key *key,
> +		     const opal_step *funcs)
> +{
> +	struct opal_dev *dev;
> +	struct opal_completion *completion;
> +	int ret;
> +
> +	dev = get_or_create_opal_dev(bdev, key->lr, true);
> +	if (!dev)
> +		return -ENOMEM;
> +	completion = setup_opal_dev(bdev, dev, funcs, key);
> +	if (IS_ERR(completion)) {
> +		ret = PTR_ERR(completion);
> +		goto error_return;
> +	}
> +
> +	next(0, dev);
> +	ret = wait_for_cmd_completion(completion);
> +
> + error_return:
> +	remove_and_clean_opal_dev(dev);
> +	return ret;
> +}
> +
> +
> +static int opal_save_internal(struct block_device *bdev,
> +			      struct opal_lock_unlock *lk)
> +{
> +	void *func_data[3] = { NULL };
> +	struct opal_dev *dev;
> +	struct opal_completion *completion;
> +	const opal_step _auth_funcs[] = {
> +		opal_discovery0,
> +		start_auth_opal_session,
> +		query_locking_range,
> +		end_opal_session,
> +		opal_register_cont,
> +		NULL
> +	};
> +	int ret;
> +
> +	dev = get_or_create_opal_dev(bdev, lk->key.lr, false);
> +	if (!dev)
> +		return -ENOMEM;
> +	completion = setup_opal_dev(bdev, dev, _auth_funcs, &lk->key);
> +	if (IS_ERR(completion)) {
> +		ret = PTR_ERR(completion);
> +		goto error_return;
> +	}
> +
> +	dev->num_func_data = 3;
> +	dev->func_data = func_data;
> +	dev->func_data[1] = &lk->authority;
> +	dev->lkul = *lk;
> +
> +	next(0, dev);
> +	ret = wait_for_cmd_completion(completion);
> +
> + error_return:
> +	remove_and_clean_opal_dev(dev);
> +	return ret;
> +}
> +
> +static int add_user_lr_internal(struct block_device *bdev,
> +				struct opal_lock_unlock *lk)
> +{
> +	void *func_data[3] = { NULL };
> +	struct opal_dev *dev;
> +	struct opal_completion *completion;
> +	const opal_step funcs[] = {
> +		opal_discovery0,
> +		start_admin1LSP_opal_session,
> +		add_user_to_lr,
> +		end_opal_session,
> +		NULL
> +	};
> +	int ret;
> +
> +	dev = get_or_create_opal_dev(bdev, lk->key.lr, true);
> +	if (!dev)
> +		return -ENOMEM;
> +	completion = setup_opal_dev(bdev, dev, funcs, &lk->key);
> +	if (IS_ERR(completion)) {
> +		ret = PTR_ERR(completion);
> +		goto error_return;
> +	}
> +
> +	dev->num_func_data = 3;
> +	dev->func_data = func_data;
> +	dev->func_data[2] = lk;
> +
> +	next(0, dev);
> +	ret = wait_for_cmd_completion(completion);
> +
> + error_return:
> +	remove_and_clean_opal_dev(dev);
> +	return ret;
> +}
> +
> +static int lock_unlock_internal(struct block_device *bdev,
> +				struct opal_lock_unlock *lk)
> +{
> +	void *func_data[3] = { NULL };
> +	struct opal_dev *dev;
> +	struct opal_completion *completion;
> +
> +	const opal_step ulk_funcs_SUM[] = {
> +		opal_discovery0,
> +		start_auth_opal_session,
> +		lock_unlock_locking_range_SUM,
> +		end_opal_session,
> +		NULL
> +	};
> +	const opal_step _unlock_funcs[] = {
> +		opal_discovery0,
> +		start_auth_opal_session,
> +		lock_unlock_locking_range,
> +		end_opal_session,
> +		NULL
> +	};
> +	int ret;
> +
> +	dev = get_or_create_opal_dev(bdev, lk->key.lr, true);
> +	if (!dev)
> +		return -ENOMEM;
> +
> +	if (lk->authority.SUM)
> +		completion = setup_opal_dev(bdev, dev, ulk_funcs_SUM, &lk->key);
> +	else
> +		completion = setup_opal_dev(bdev, dev, _unlock_funcs, &lk->key);
> +
> +	if (IS_ERR(completion)) {
> +		ret = PTR_ERR(completion);
> +		goto error_return;
> +	}
> +
> +	dev->num_func_data = 3;
> +	dev->func_data = func_data;
> +	dev->func_data[1] = &lk->authority;
> +	dev->func_data[2] = lk;
> +
> +	next(0, dev);
> +	ret = wait_for_cmd_completion(completion);
> +
> + error_return:
> +	remove_and_clean_opal_dev(dev);
> +	return ret;
> +}
> +
> +int opal_erase_locking_range(struct block_device *bdev, struct sed_key *key)
> +{
> +	struct opal_dev *dev;
> +	struct opal_completion *completion;
> +	struct opal_key k;
> +	int ret;
> +	const opal_step erase_funcs[] = {
> +		opal_discovery0,
> +		start_admin1LSP_opal_session,
> +		erase_locking_range,
> +		end_opal_session,
> +		NULL,
> +	};
> +
> +	if (!bdev || !bdev->bd_disk) {
> +		pr_err("Can't save password for NULL block device.\n");
> +		return -EINVAL;
> +	}
> +
> +	if (copy_from_user(&k, key->opal, sizeof(*key->opal)))
> +		return -EFAULT;
> +
> +	dev = get_or_create_opal_dev(bdev, k.lr, true);
> +	if (!dev)
> +		return -ENOMEM;
> +
> +	completion = setup_opal_dev(bdev, dev, erase_funcs, &k);
> +	if (IS_ERR(completion)) {
> +		ret = PTR_ERR(completion);
> +		goto error_return;
> +	}
> +
> +	next(0, dev);
> +	ret = wait_for_cmd_completion(completion);
> +
> + error_return:
> +	remove_and_clean_opal_dev(dev);
> +	return ret;
> +}
> +EXPORT_SYMBOL(opal_erase_locking_range);
> +
> +int opal_enable_disable_shadow_mbr(struct block_device *bdev,
> +				   struct sed_key *key)
> +{
> +	void *func_data[6] = { NULL };
> +	struct opal_mbr_data mbr;
> +	struct opal_dev *dev;
> +	struct opal_completion *completion;
> +	const opal_step mbr_funcs[] = {
> +		opal_discovery0,
> +		start_admin1LSP_opal_session,
> +		set_mbr_done,
> +		end_opal_session,
> +		start_admin1LSP_opal_session,
> +		set_mbr_enable_disable,
> +		end_opal_session,
> +		NULL,
> +	};
> +	int ret;
> +
> +	if (!bdev || !bdev->bd_disk) {
> +		pr_err("Can't save password for NULL block device.\n");
> +		return -EINVAL;
> +	}
> +
> +	if (copy_from_user(&mbr, key->opal_mbr, sizeof(*key->opal_mbr)))
> +		return -EFAULT;
> +
> +	if (mbr.enable_disable != OPAL_MBR_ENABLE &&
> +	    mbr.enable_disable != OPAL_MBR_DISABLE)
> +		return -EINVAL;
> +
> +	dev = get_or_create_opal_dev(bdev, mbr.key.lr, true);
> +	if (!dev)
> +		return -ENOMEM;
> +
> +	completion = setup_opal_dev(bdev, dev, mbr_funcs, &mbr.key);
> +	if (IS_ERR(completion)) {
> +		ret = PTR_ERR(completion);
> +		goto error_return;
> +	}
> +
> +	dev->num_func_data = 6;
> +	dev->func_data = func_data;
> +	dev->func_data[2] = &mbr.enable_disable;
> +	dev->func_data[5] = &mbr.enable_disable;
> +
> +	next(0, dev);
> +	ret = wait_for_cmd_completion(completion);
> +
> + error_return:
> +	remove_and_clean_opal_dev(dev);
> +	return ret;
> +
> +}
> +EXPORT_SYMBOL(opal_enable_disable_shadow_mbr);
> +
> +int opal_save(struct block_device *bdev, struct sed_key *key)
> +{
> +	struct opal_lock_unlock lkul;
> +
> +	if (!bdev || !bdev->bd_disk) {
> +		pr_err("Can't save password for NULL block device.\n");
> +		return -EINVAL;
> +	}
> +
> +	if (copy_from_user(&lkul, key->opal_lk_unlk, sizeof(*key->opal)))
> +		return -EFAULT;
> +
> +	return opal_save_internal(bdev, &lkul);
> +}
> +EXPORT_SYMBOL(opal_save);
> +
> +int opal_add_user_to_lr(struct block_device *bdev, struct sed_key *key)
> +{
> +	struct opal_lock_unlock lkul;
> +
> +	if (copy_from_user(&lkul, key->opal_lk_unlk, sizeof(lkul)))
> +		return -EFAULT;
> +
> +	if (!bdev || !bdev->bd_disk) {
> +		pr_err("Can't assign user to LR without backing disk\n");
> +		return -EFAULT;
> +	}
> +	if (lkul.l_state != OPAL_RO && lkul.l_state != OPAL_RW) {
> +		pr_err("Locking state was not RO or RW\n");
> +		return -EINVAL;
> +	}
> +	if (lkul.authority.who < OPAL_USER1 &&
> +	    lkul.authority.who > OPAL_USER9) {
> +		pr_err("Authority was not within the range of users: %d\n",
> +		       lkul.authority.who);
> +		return -EINVAL;
> +	}
> +	if (lkul.authority.SUM) {
> +		pr_err("%s not supported in SUM. Use setup locking range\n",
> +		       __func__);
> +		return -EINVAL;
> +	}
> +
> +	return add_user_lr_internal(bdev, &lkul);
> +}
> +EXPORT_SYMBOL(opal_add_user_to_lr);
> +
> +int opal_reverttper(struct block_device *bdev, struct sed_key *key)
> +{
> +	struct opal_key k;
> +
> +	if (copy_from_user(&k, key->opal, sizeof(*key->opal)))
> +		return -EFAULT;
> +
> +	return opal_revert(bdev, &k);
> +}
> +EXPORT_SYMBOL(opal_reverttper);
> +
> +int opal_lock_unlock(struct block_device *bdev, struct sed_key *key)
> +{
> +	struct opal_lock_unlock k;
> +
> +	if (copy_from_user(&k, key->opal_lk_unlk, sizeof(*key->opal_lk_unlk)))
> +		return -EFAULT;
> +
> +	if (k.authority.who < OPAL_ADMIN1 || k.authority.who >= OPAL_USER9)
Should this be > OPAL_USER9 ?


> +		return -EINVAL;
> +
> +	return lock_unlock_internal(bdev, &k);
> +}
> +EXPORT_SYMBOL(opal_lock_unlock);
> +
> +int opal_take_ownership(struct block_device *bdev, struct sed_key *key)
> +{
> +	struct opal_key k;
> +	const opal_step owner_funcs[] = {
> +		opal_discovery0,
> +		start_anybodyASP_opal_session,
> +		get_msid_cpin_pin,
> +		end_opal_session,
> +		start_SIDASP_opal_session,
> +		set_sid_cpin_pin,
> +		end_opal_session,
> +		NULL
> +	};
> +
> +	if (!bdev || !bdev->bd_disk) {
> +		pr_err("Can't save password for NULL block device.\n");
> +		return -EINVAL;
> +	}
> +
> +	if (copy_from_user(&k, key->opal, sizeof(*key->opal)))
> +		return -EFAULT;
> +
> +	return opal_register(bdev, &k, owner_funcs);
> +}
> +EXPORT_SYMBOL(opal_take_ownership);
> +
> +int opal_activate_lsp(struct block_device *bdev, struct sed_key *key)
> +{
> +	struct opal_key k;
> +	const opal_step active_funcs[] = {
> +		opal_discovery0,
> +		start_SIDASP_opal_session, /* Open session as SID auth */
> +		get_lsp_lifecycle,
> +		activate_lsp,
> +		end_opal_session,
> +		NULL
> +	};
> +
> +	if (!bdev || !bdev->bd_disk) {
> +		pr_err("Can't save password for NULL block device.\n");
> +		return -EINVAL;
> +	}
> +
> +	if (copy_from_user(&k, key->opal, sizeof(*key->opal)))
> +		return -EFAULT;
> +
> +	return opal_act_lsp_int(bdev, &k, active_funcs);
> +}
> +EXPORT_SYMBOL(opal_activate_lsp);
> +
> +int opal_setup_locking_range(struct block_device *bdev, struct sed_key *pw)
> +{
> +	struct opal_user_lr_setup k;
> +
> +	if (!bdev || !bdev->bd_disk) {
> +		pr_err("Can't save password for NULL block device.\n");
> +		return -EINVAL;
> +	}
> +
> +	if (copy_from_user(&k, pw->opal_lrs, sizeof(*pw->opal_lrs)))
> +		return -EFAULT;
> +
> +	return internal_setup_lr(bdev, &k);
> +}
> +
> +int opal_set_new_pw(struct block_device *bdev, struct sed_key *pw)
> +{
> +	struct opal_new_pw k;
> +
> +	if (pw->sed_type != OPAL_PW)
> +		return -EINVAL;
> +
> +	if (copy_from_user(&k, pw->opal_pw, sizeof(*pw->opal_pw)))
> +		return -EFAULT;
> +
> +	if (k.who.who < OPAL_ADMIN1 || k.who.who > OPAL_USER9)
> +		return -EINVAL;
> +
> +	return opal_set_pw(bdev, &k);
> +}
> +EXPORT_SYMBOL(opal_set_new_pw);
> +
> +int opal_activate_user(struct block_device *bdev, struct sed_key *pw)
> +{
> +	struct opal_activate_user k;
> +
> +	if (pw->sed_type != OPAL_ACT_USR) {
> +		pr_err("Sed type was not act user\n");
> +		return -EINVAL;
> +	}
> +
> +	if (copy_from_user(&k, pw->opal_act, sizeof(*pw->opal_act))) {
> +		pr_err("copy from user error\n");
> +		return -EFAULT;
> +	}
> +
> +	/* We can't activate Admin1 it's active as manufactured */
> +	if (k.who.who < OPAL_USER1 && k.who.who > OPAL_USER9) {
> +		pr_err("Who was not a valid user: %d \n", k.who.who);
> +		return -EINVAL;
> +	}
> +
> +	return activate_user(bdev, &k);
> +
> +}
> +EXPORT_SYMBOL(opal_activate_user);
> +
> +int opal_unlock_from_suspend(struct opal_suspend_unlk *data)
> +{
> +	const char *diskname = data->name;
> +	struct opal_dev *iter, *dev = NULL;
> +	struct opal_completion *completion;
> +	void *func_data[3] = { NULL };
> +	const opal_step _unlock_funcs_SUM[] = {
> +		opal_discovery0,
> +		start_auth_opal_session,
> +		lock_unlock_locking_range,
Did you mean to use lock_unlock_locking_range_SUM ?
Can we pull these two arrays out since they are required for both this
function and lock_unlock_internal ?


> +		end_opal_session,
> +		NULL
> +	};
> +	const opal_step _unlock_funcs[] = {
> +		opal_discovery0,
> +		start_auth_opal_session,
> +		lock_unlock_locking_range,
> +		end_opal_session,
> +		NULL
> +	};
> +
> +
> +	spin_lock(&list_spinlock);
> +	list_for_each_entry(iter, &opal_list, node) {
> +		if (strncmp(iter->disk_name, diskname, DISK_NAME_LEN)) {
> +			pr_err("iterdisk was %s and diskname is %s\n",
> +			       iter->disk_name, diskname);
> +			continue;
> +		}
> +		if (atomic_add_unless(&iter->in_use, 1, 1)) {
> +			dev = iter;
> +			dev->func_data = func_data;
> +			dev->resume_from_suspend = true;
> +			dev->resume_data = data;
> +			dev->final_cb = unlock_suspend_final;
> +			dev->final_cb_data = dev;
> +			dev->error_cb = end_opal_session_error;
> +			dev->error_cb_data = dev;
> +			dev->state = 0;
> +			if (dev->lkul.authority.SUM)
> +				dev->funcs = _unlock_funcs_SUM;
> +			else
> +				dev->funcs = _unlock_funcs;
> +			dev->TSN = 0;
> +			dev->HSN = 0;
> +			dev->func_data[2] = &dev->lkul;
> +			dev->func_data[1] = &dev->lkul.authority;
> +			completion = dev->completion;
> +			next(0, dev);
> +			wait_for_cmd_completion(completion);
> +		}
> +	}
> +	spin_unlock(&list_spinlock);
> +
> +	if (!dev)
> +		return -ENODEV;
> +	return 0;
> +}
> +EXPORT_SYMBOL(opal_unlock_from_suspend);
> diff --git a/lib/sed-opal_internal.h b/lib/sed-opal_internal.h
> new file mode 100644
> index 0000000..c9d3883
> --- /dev/null
> +++ b/lib/sed-opal_internal.h
> @@ -0,0 +1,586 @@
> +/*
> + * Copyright ? 2016 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + *
> + * Author:
> + *    Rafael Antognolli <rafael.antognolli at intel.com>
> + *    Scott  Bauer      <scott.bauer at intel.com>
> + */
> +
> +#ifndef _NVME_OPAL_INTERNAL_H
> +#define _NVME_OPAL_INTERNAL_H
> +
> +#include <linux/key-type.h>
> +#include <keys/user-type.h>
> +
> +#define DTAERROR_NO_METHOD_STATUS 0x89
> +
> +static const char *opal_errors[19] = {
Remove the 19 so it's just opal_errors[] (doesnt silently truncate any
new additions)


> +	"Success",
> +	"Not Authorized",
> +	"Unknown Error",
> +	"SP Busy",
> +	"SP Failed",
> +	"SP Disabled",
> +	"SP Frozen",
> +	"No Sessions Available",
> +	"Uniqueness Conflict",
> +	"Insufficient Space",
> +	"Insufficient Rows",
> +	"Invalid Function",
> +	"Invalid Parameter",
> +	"Invalid Reference",
> +	"Unknown Error",
> +	"TPER Malfunction",
> +	"Transaction Failure",
> +	"Response Overflow",
> +	"Authority Locked Out",
> +};
> +
> +static const char *opal_error_to_human(int error)
> +{
> +	if (error == 0x3f)
> +		return "Failed";
> +
> +	if (error >= sizeof(opal_errors) || error < 0)
You want sizeof(opal_errors)/sizeof(*opal_errors) here, but you can just
use the ARRAY_SIZE macro instead


> +		return "Unknown Error";
> +
> +	return opal_errors[error];
> +}
> +
> +/* User IDs used in the TCG storage SSCs */
> +static const u8 OPALUID[][8] = {
> +	/* users */
> +
> +	/* session management  */
> +	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff},
> +	/* special "thisSP" syntax */
> +	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 },
> +	/* Administrative SP */
> +	{ 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x00, 0x01 },
> +	/* Locking SP */
> +	{ 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x00, 0x02 },
> +	/* ENTERPRISE Locking SP  */
> +	{ 0x00, 0x00, 0x02, 0x05, 0x00, 0x01, 0x00, 0x01 },
> +	/* anybody */
> +	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01 },
> +	/* SID */
> +	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06 },
> +	/* ADMIN1 */
> +	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x01, 0x00, 0x01 },
> +	/* USER1 */
> +	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x03, 0x00, 0x01 },
> +	/* USER2 */
> +	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x03, 0x00, 0x02 },
> +	/* PSID user */
> +	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x01, 0xff, 0x01 },
> +	/* BandMaster 0 */
> +	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x80, 0x01 },
> +	 /* EraseMaster */
> +	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x84, 0x01 },
> +
> +	/* tables */
> +
> +	/* Locking_GlobalRange */
> +	{ 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00, 0x01 },
> +	/* ACE_Locking_Range_Set_RdLocked UID */
> +	{ 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0xE0, 0x01 },
> +	/* ACE_Locking_Range_Set_WrLocked UID */
> +	{ 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0xE8, 0x01 },
> +	/* MBR Control */
> +	{ 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x00, 0x01 },
> +	/* Shadow MBR */
> +	{ 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00 },
> +	/* AUTHORITY_TABLE */
> +	{ 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00},
> +	/* C_PIN_TABLE */
> +	{ 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00},
> +	/* OPAL Locking Info */
> +	{ 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x01 },
> +	/* Enterprise Locking Info */
> +	{ 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00 },
> +
> +	/* C_PIN_TABLE object ID's */
> +
> +	/* C_PIN_MSID */
> +	{ 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x84, 0x02},
> +	/* C_PIN_SID */
> +	{ 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01},
> +	 /* C_PIN_ADMIN1 */
> +	{ 0x00, 0x00, 0x00, 0x0B, 0x00, 0x01, 0x00, 0x01},
> +
> +	/* half UID's (only first 4 bytes used) */
> +
> +	/* Half-UID ? Authority_object_ref */
> +	{ 0x00, 0x00, 0x0C, 0x05, 0xff, 0xff, 0xff, 0xff },
> +	/* Half-UID ? Boolean ACE */
> +	{ 0x00, 0x00, 0x04, 0x0E, 0xff, 0xff, 0xff, 0xff },
> +
> +	/* special value for omitted optional parameter */
> +
> +	/* HEXFF for omitted */
> +	{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
> +};
> +static const size_t OPAL_UID_LENGTH = 8;
> +static const size_t OPAL_MSID_KEYLEN = 15;
> +static const size_t OPAL_UID_LENGTH_HALF = 4;
> +
> +
> +/* Enum to index OPALUID array */
> +enum OPAL_UID {
> +	/* users */
> +	OPAL_SMUID_UID,
> +	OPAL_THISSP_UID,
> +	OPAL_ADMINSP_UID,
> +	OPAL_LOCKINGSP_UID,
> +	OPAL_ENTERPRISE_LOCKINGSP_UID,
> +	OPAL_ANYBODY_UID,
> +	OPAL_SID_UID,
> +	OPAL_ADMIN1_UID,
> +	OPAL_USER1_UID,
> +	OPAL_USER2_UID,
> +	OPAL_PSID_UID,
> +	OPAL_ENTERPRISE_BANDMASTER0_UID,
> +	OPAL_ENTERPRISE_ERASEMASTER_UID,
> +	/* tables */
> +	OPAL_LOCKINGRANGE_GLOBAL,
> +	OPAL_LOCKINGRANGE_ACE_RDLOCKED,
> +	OPAL_LOCKINGRANGE_ACE_WRLOCKED,
> +	OPAL_MBRCONTROL,
> +	OPAL_MBR,
> +	OPAL_AUTHORITY_TABLE,
> +	OPAL_C_PIN_TABLE,
> +	OPAL_LOCKING_INFO_TABLE,
> +	OPAL_ENTERPRISE_LOCKING_INFO_TABLE,
> +	/* C_PIN_TABLE object ID's */
> +	OPAL_C_PIN_MSID,
> +	OPAL_C_PIN_SID,
> +	OPAL_C_PIN_ADMIN1,
> +	/* half UID's (only first 4 bytes used) */
> +	OPAL_HALF_UID_AUTHORITY_OBJ_REF,
> +	OPAL_HALF_UID_BOOLEAN_ACE,
> +	/* omitted optional parameter */
> +	OPAL_UID_HEXFF,
> +};
> +
> +/*
> + * TCG Storage SSC Methods.
> + */
> +static const u8 OPALMETHOD[][8] = {
> +	/* Properties */
> +	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01 },
> +	/* STARTSESSION */
> +	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02 },
> +	/* Revert */
> +	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x02 },
> +	/* Activate */
> +	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x03 },
> +	/* Enterprise Get */
> +	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06 },
> +	/* Enterprise Set */
> +	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07 },
> +	/* NEXT */
> +	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08 },
> +	/* Enterprise Authenticate */
> +	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0c },
> +	/* GetACL */
> +	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0d },
> +	/* GenKey */
> +	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10 },
> +	/* revertSP */
> +	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11 },
> +	/* Get */
> +	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x16 },
> +	/* Set */
> +	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17 },
> +	/* Authenticate */
> +	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1c },
> +	/* Random */
> +	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x01 },
> +	/* Erase */
> +	{ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x03 },
> +};
> +static const size_t OPAL_METHOD_LENGTH = 8;
> +
> +/* Enum for indexing the OPALMETHOD array */
> +enum OPAL_METHOD {
> +	OPAL_PROPERTIES,
> +	OPAL_STARTSESSION,
> +	OPAL_REVERT,
> +	OPAL_ACTIVATE,
> +	OPAL_EGET,
> +	OPAL_ESET,
> +	OPAL_NEXT,
> +	OPAL_EAUTHENTICATE,
> +	OPAL_GETACL,
> +	OPAL_GENKEY,
> +	OPAL_REVERTSP,
> +	OPAL_GET,
> +	OPAL_SET,
> +	OPAL_AUTHENTICATE,
> +	OPAL_RANDOM,
> +	OPAL_ERASE,
> +};
> +
> +enum OPAL_RESPONSE_TOKEN {
> +	OPAL_DTA_TOKENID_BYTESTRING = 0xe0,
> +	OPAL_DTA_TOKENID_SINT = 0xe1,
> +	OPAL_DTA_TOKENID_UINT = 0xe2,
> +	OPAL_DTA_TOKENID_TOKEN = 0xe3, /* actual token is returned */
> +	OPAL_DTA_TOKENID_INVALID = 0X0
> +};
> +
> +enum OPAL_TOKEN {
> +	/* Boolean */
> +	OPAL_TRUE = 0x01,
> +	OPAL_FALSE = 0x00,
> +	OPAL_BOOLEAN_EXPR = 0x03,
> +	/* cellblocks */
> +	OPAL_TABLE = 0x00,
> +	OPAL_STARTROW = 0x01,
> +	OPAL_ENDROW = 0x02,
> +	OPAL_STARTCOLUMN = 0x03,
> +	OPAL_ENDCOLUMN = 0x04,
> +	OPAL_VALUES = 0x01,
> +	/* authority table */
> +	OPAL_PIN = 0x03,
> +	/* locking tokens */
> +	OPAL_RANGESTART = 0x03,
> +	OPAL_RANGELENGTH = 0x04,
> +	OPAL_READLOCKENABLED = 0x05,
> +	OPAL_WRITELOCKENABLED = 0x06,
> +	OPAL_READLOCKED = 0x07,
> +	OPAL_WRITELOCKED = 0x08,
> +	OPAL_ACTIVEKEY = 0x0A,
> +	/* locking info table */
> +	OPAL_MAXRANGES = 0x04,
> +	 /* mbr control */
> +	OPAL_MBRENABLE = 0x01,
> +	OPAL_MBRDONE = 0x02,
> +	/* properties */
> +	OPAL_HOSTPROPERTIES = 0x00,
> +	/* atoms */
> +	OPAL_STARTLIST = 0xf0,
> +	OPAL_ENDLIST = 0xf1,
> +	OPAL_STARTNAME = 0xf2,
> +	OPAL_ENDNAME = 0xf3,
> +	OPAL_CALL = 0xf8,
> +	OPAL_ENDOFDATA = 0xf9,
> +	OPAL_ENDOFSESSION = 0xfa,
> +	OPAL_STARTTRANSACTON = 0xfb,
> +	OPAL_ENDTRANSACTON = 0xfC,
> +	OPAL_EMPTYATOM = 0xff,
> +	OPAL_WHERE = 0x00,
> +};
> +
> +/* Useful tiny atoms.
> + * Useful for table columns etc
> + */
> +enum OPAL_TINY_ATOM {
> +	OPAL_TINY_UINT_00 = 0x00,
> +	OPAL_TINY_UINT_01 = 0x01,
> +	OPAL_TINY_UINT_02 = 0x02,
> +	OPAL_TINY_UINT_03 = 0x03,
> +	OPAL_TINY_UINT_04 = 0x04,
> +	OPAL_TINY_UINT_05 = 0x05,
> +	OPAL_TINY_UINT_06 = 0x06,
> +	OPAL_TINY_UINT_07 = 0x07,
> +	OPAL_TINY_UINT_08 = 0x08,
> +	OPAL_TINY_UINT_09 = 0x09,
> +	OPAL_TINY_UINT_10 = 0x0a,
> +	OPAL_TINY_UINT_11 = 0x0b,
> +	OPAL_TINY_UINT_12 = 0x0c,
> +	OPAL_TINY_UINT_13 = 0x0d,
> +	OPAL_TINY_UINT_14 = 0x0e,
> +	OPAL_TINY_UINT_15 = 0x0f,
> +};
> +
> +enum OPAL_ATOM_WIDTH {
> +	OPAL_WIDTH_TINY,
> +	OPAL_WIDTH_SHORT,
> +	OPAL_WIDTH_MEDIUM,
> +	OPAL_WIDTH_LONG,
> +	OPAL_WIDTH_TOKEN
> +};
> +
> +/* Locking state for a locking range */
> +enum OPAL_LOCKINGSTATE {
> +	OPAL_LOCKING_READWRITE = 0x01,
> +	OPAL_LOCKING_READONLY = 0x02,
> +	OPAL_LOCKING_LOCKED = 0x03,
> +};
> +
> +/*
> + * Structures to build and decode the Opal SSC messages
> + * fields that are NOT really numeric are defined as u8[] to
> + * help reduce the endianness issues
> + */
> +
> +/* Comm Packet (header) for transmissions. */
> +struct opal_compacket {
> +	u32 reserved0;
> +	u8 extendedComID[4];
> +	u32 outstandingData;
> +	u32 minTransfer;
> +	u32 length;
> +};
> +
> +/* Packet structure. */
> +struct opal_packet {
> +	u32 TSN;
> +	u32 HSN;
> +	u32 seq_number;
> +	u16 reserved0;
> +	u16 ack_type;
> +	u32 acknowledgment;
> +	u32 length;
> +};
> +
> +/* Data sub packet header */
> +struct opal_data_subpacket {
> +	u8 reserved0[6];
> +	u16 kind;
> +	u32 length;
> +};
> +
> +/* header of a response */
> +struct opal_header {
> +	struct opal_compacket cp;
> +	struct opal_packet pkt;
> +	struct opal_data_subpacket subpkt;
> +};
> +
> +#define FC_TPER       0x0001
> +#define FC_LOCKING    0x0002
> +#define FC_GEOMETRY   0x0003
> +#define FC_ENTERPRISE 0x0100
> +#define FC_DATASTORE  0x0202
> +#define FC_SINGLEUSER 0x0201
> +#define FC_OPALV100   0x0200
> +#define FC_OPALV200   0x0203
> +
> +/*
> + * The Discovery 0 Header. As defined in
> + * Opal SSC Documentation
> + */
> +struct d0_header {
> +	u32 length; /* the length of the header 48 in 2.00.100 */
> +	u32 revision; /**< revision of the header 1 in 2.00.100 */
> +	u32 reserved01;
> +	u32 reserved02;
> +	/*
> +	 * the remainder of the structure is vendor specific and will not be
> +	 * addressed now
> +	 */
> +	u8 ignored[32];
> +};
> +
> +/*
> + * TPer Feature Descriptor. Contains flags indicating support for the
> + * TPer features described in the OPAL specification. The names match the
> + * OPAL terminology
> + *
> + * code == 0x001 in 2.00.100
> + */
> +struct d0_tper_features {
> +	/*
> +	 * supported_features bits:
> +	 * bit 7: reserved
> +	 * bit 6: com ID management
> +	 * bit 5: reserved
> +	 * bit 4: streaming support
> +	 * bit 3: buffer management
> +	 * bit 2: ACK/NACK
> +	 * bit 1: async
> +	 * bit 0: sync
> +	 */
> +	u8 supported_features;
> +	/*
> +	 * bytes 5 through 15 are reserved, but we represent the first 3 as
> +	 * u8 to keep the other two 32bits integers aligned.
> +	 */
> +	u8 reserved01[3];
> +	u32 reserved02;
> +	u32 reserved03;
> +};
> +
> +/*
> + * Locking Feature Descriptor. Contains flags indicating support for the
> + * locking features described in the OPAL specification. The names match the
> + * OPAL terminology
> + *
> + * code == 0x0002 in 2.00.100
> + */
> +struct d0_locking_features {
> +	/*
> +	 * supported_features bits:
> +	 * bits 6-7: reserved
> +	 * bit 5: MBR done
> +	 * bit 4: MBR enabled
> +	 * bit 3: media encryption
> +	 * bit 2: locked
> +	 * bit 1: locking enabled
> +	 * bit 0: locking supported
> +	 */
> +	u8 supported_features;
> +	/*
> +	 * bytes 5 through 15 are reserved, but we represent the first 3 as
> +	 * u8 to keep the other two 32bits integers aligned.
> +	 */
> +	u8 reserved01[3];
> +	u32 reserved02;
> +	u32 reserved03;
> +};
> +
> +/*
> + * Geometry Feature Descriptor. Contains flags indicating support for the
> + * geometry features described in the OPAL specification. The names match the
> + * OPAL terminology
> + *
> + * code == 0x0003 in 2.00.100
> + */
> +struct d0_geometry_features {
> +	/*
> +	 * skip 32 bits from header, needed to align the struct to 64 bits.
> +	 */
> +	u8 header[4];
> +	/*
> +	 * reserved01:
> +	 * bits 1-6: reserved
> +	 * bit 0: align
> +	 */
> +	u8 reserved01;
> +	u8 reserved02[7];
> +	u32 logical_block_size;
> +	u64 alignment_granularity;
> +	u64 lowest_aligned_lba;
> +};
> +
> +/*
> + * Enterprise SSC Feature
> + *
> + * code == 0x0100
> + */
> +struct d0_enterprise_ssc {
> +	u16 baseComID;
> +	u16 numComIDs;
> +	/* range_crossing:
> +	 * bits 1-6: reserved
> +	 * bit 0: range crossing
> +	 */
> +	u8 range_crossing;
> +	u8 reserved01;
> +	u16 reserved02;
> +	u32 reserved03;
> +	u32 reserved04;
> +};
> +
> +/*
> + * Opal V1 feature
> + *
> + * code == 0x0200
> + */
> +struct d0_opal_v100 {
> +	u16 baseComID;
> +	u16 numComIDs;
> +};
> +
> +/*
> + * Single User Mode feature
> + *
> + * code == 0x0201
> + */
> +struct d0_single_user_mode {
> +	u32 num_locking_objects;
> +	/* reserved01:
> +	 * bit 0: any
> +	 * bit 1: all
> +	 * bit 2: policy
> +	 * bits 3-7: reserved
> +	 */
> +	u8 reserved01;
> +	u8 reserved02;
> +	u16 reserved03;
> +	u32 reserved04;
> +};
> +
> +/*
> + * Additonal Datastores feature
> + *
> + * code == 0x0202
> + */
> +struct d0_datastore_table {
> +	u16 reserved01;
> +	u16 max_tables;
> +	u32 max_size_tables;
> +	u32 table_size_alignment;
> +};
> +
> +/*
> + * OPAL 2.0 feature
> + *
> + * code == 0x0203
> + */
> +struct d0_opal_v200 {
> +	u16 baseComID;
> +	u16 numComIDs;
> +	/* range_crossing:
> +	 * bits 1-6: reserved
> +	 * bit 0: range crossing
> +	 */
> +	u8 range_crossing;
> +	/* num_locking_admin_auth:
> +	 * not aligned to 16 bits, so use two u8.
> +	 * stored in big endian:
> +	 * 0: MSB
> +	 * 1: LSB
> +	 */
> +	u8 num_locking_admin_auth[2];
> +	/* num_locking_user_auth:
> +	 * not aligned to 16 bits, so use two u8.
> +	 * stored in big endian:
> +	 * 0: MSB
> +	 * 1: LSB
> +	 */
> +	u8 num_locking_user_auth[2];
> +	u8 initialPIN;
> +	u8 revertedPIN;
> +	u8 reserved01;
> +	u32 reserved02;
> +};
> +
> +/* Union of features used to parse the discovery 0 response */
> +struct d0_features {
> +	u16 code;
> +	/*
> +	 * r_version bits:
> +	 * bits 4-7: version
> +	 * bits 0-3: reserved
> +	 */
> +	u8 r_version;
> +	u8 length;
> +	u8 features[];
> +};
> +
> +struct key *request_user_key(const char *master_desc, const u8 **master_key,
> +			     size_t *master_keylen);
> +
> +#endif /* _NVME_OPAL_INTERNAL_H */
> diff --git a/lib/sed-opal_key.c b/lib/sed-opal_key.c
> new file mode 100644
> index 0000000..0b4de01
> --- /dev/null
> +++ b/lib/sed-opal_key.c
> @@ -0,0 +1,46 @@
> +/*
> + * Copyright ? 2016 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + *
> + * Author:
> + *    Rafael Antognolli <rafael.antognolli at intel.com>
> + */
> +
> +#include <linux/key.h>
> +#include "sed-opal_internal.h"
> +
> +struct key *request_user_key(const char *master_desc, const u8 **master_key,
> +			     size_t *master_keylen)
> +{
> +	const struct user_key_payload *upayload;
> +	struct key *ukey;
> +
> +	ukey = request_key(&key_type_user, master_desc, NULL);
> +	if (IS_ERR(ukey))
> +		goto error;
> +
> +	down_read(&ukey->sem);
> +	upayload = user_key_payload(ukey);
> +	*master_key = upayload->data;
> +	*master_keylen = upayload->datalen;
> +error:
> +	return ukey;
> +}
> diff --git a/lib/sed.c b/lib/sed.c
> new file mode 100644
> index 0000000..06cacd9
> --- /dev/null
> +++ b/lib/sed.c
> @@ -0,0 +1,303 @@
> +#include <linux/blkdev.h>
> +#include <linux/sed.h>
> +#include <linux/sed-opal.h>
> +
> +#ifndef CONFIG_SED_OPAL
> +static int sed_opal_save(struct block_device *bdev, struct sed_key *sed)
> +	{ return -EOPNOTSUPP; }
> +static int sed_opal_lock_unlock(struct block_device *bdev, struct sed_key *key)
> +	{ return -EOPNOTSUPP; }
> +static int sed_opal_take_ownership(struct block_device *bdev, struct sed_key *key)
> +	{ return -EOPNOTSUPP; }
> +static int sed_opal_activate_lsp(struct block_device *bdev, struct sed_key *key)
> +	{ return -EOPNOTSUPP; }
> +static int sed_opal_set_pw(struct block_device *bdev, struct sed_key *key)
> +	{ return -EOPNOTSUPP; }
> +static int sed_opal_activate_user(struct block_device *bdev, struct sed_key *key)
> +	{ return -EOPNOTSUPP; }
> +static int sed_opal_reverttper(struct block_device *bdev, struct sed_key *key)
> +	{ return -EOPNOTSUPP; }
> +static int sed_opal_setup_locking_range(struct block_device *bdev, struct sed_key *key)
> +	{ return -EOPNOTSUPP; }
> +static int sed_opal_adduser_to_lr(struct block_device *bdev, struct sed_key *key)
> +	{ return -EOPNOTSUPP; }
> +static int sed_opal_do_mbr(struct block_device *bdev, struct sed_key *key)
> +	{ return -EOPNOTSUPP; }
> +static int sed_opal_erase_lr(struct block_device *bdev, struct sed_key *key)
> +	{ return -EOPNOTSUPP; }
> +
> +#else
> +
> +static int sed_opal_save(struct block_device *bdev, struct sed_key *key)
> +{
> +
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
You might consider pulling this boilerplate out into something like:
static inline bool bdev_sec_capable(struct block_device *bdev)
{
	return bdev && bdev->bd_disk && bdev->bd_disk->fops &&
	       bdev->bd_disk->fops->sec_ops;
}


> +
> +	return opal_save(bdev, key);
> +}
> +
> +static int sed_opal_lock_unlock(struct block_device *bdev, struct sed_key *key)
> +{
> +
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	return opal_lock_unlock(bdev, key);
> +}
> +
> +static int sed_opal_take_ownership(struct block_device *bdev,
> +				   struct sed_key *key)
> +{
> +
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	return opal_take_ownership(bdev, key);
> +}
> +
> +static int sed_opal_activate_lsp(struct block_device *bdev,
> +				 struct sed_key *key)
> +{
> +
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	return opal_activate_lsp(bdev, key);
> +}
> +
> +static int sed_opal_set_pw(struct block_device *bdev,
> +			   struct sed_key *key)
> +{
> +
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	return opal_set_new_pw(bdev, key);
> +}
> +
> +static int sed_opal_activate_user(struct block_device *bdev,
> +				  struct sed_key *key)
> +{
> +
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	return opal_activate_user(bdev, key);
> +}
> +
> +static int sed_opal_reverttper(struct block_device *bdev,
> +			       struct sed_key *key)
> +{
> +
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	return opal_reverttper(bdev, key);
> +}
> +
> +static int sed_opal_setup_lr(struct block_device *bdev,
> +			     struct sed_key *key)
> +{
> +
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	return opal_setup_locking_range(bdev, key);
> +}
> +
> +static int sed_opal_adduser_to_lr(struct block_device *bdev,
> +			     struct sed_key *key)
> +{
> +
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	return opal_add_user_to_lr(bdev, key);
> +}
> +
> +static int sed_opal_do_mbr(struct block_device *bdev,
> +			   struct sed_key *key)
> +{
> +
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	return opal_enable_disable_shadow_mbr(bdev, key);
> +}
> +
> +static int sed_opal_erase_lr(struct block_device *bdev,
> +			     struct sed_key *key)
> +{
> +
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	return opal_erase_locking_range(bdev, key);
> +}
> +#endif
> +
> +int sed_save(struct block_device *bdev, struct sed_key *key)
> +{
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	switch (key->sed_type) {
> +	case OPAL_LOCK_UNLOCK:
> +		return sed_opal_save(bdev, key);
> +	}
> +
> +	return -EOPNOTSUPP;
> +}
> +
> +int sed_lock_unlock(struct block_device *bdev, struct sed_key *key)
> +{
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	switch (key->sed_type) {
> +	case OPAL_LOCK_UNLOCK:
> +		return sed_opal_lock_unlock(bdev, key);
> +	}
> +
> +	return -EOPNOTSUPP;
> +}
> +
> +int sed_take_ownership(struct block_device *bdev, struct sed_key *key)
> +{
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	switch (key->sed_type) {
> +	case OPAL:
> +		return sed_opal_take_ownership(bdev, key);
> +	}
> +
> +	return -EOPNOTSUPP;
> +}
> +
> +int sed_activate_lsp(struct block_device *bdev, struct sed_key *key)
> +{
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	switch (key->sed_type) {
> +	case OPAL:
> +		return sed_opal_activate_lsp(bdev, key);
> +	}
> +
> +	return -EOPNOTSUPP;
> +}
> +
> +int sed_set_pw(struct block_device *bdev, struct sed_key *key)
> +{
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	switch (key->sed_type) {
> +	case OPAL_PW:
> +		return sed_opal_set_pw(bdev, key);
> +	}
> +
> +	return -EOPNOTSUPP;
> +}
> +
> +int sed_activate_user(struct block_device *bdev, struct sed_key *key)
> +{
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	switch (key->sed_type) {
> +	case OPAL_ACT_USR:
> +		return sed_opal_activate_user(bdev, key);
> +	}
> +
> +	return -EOPNOTSUPP;
> +}
> +
> +int sed_reverttper(struct block_device *bdev, struct sed_key *key)
> +{
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	switch (key->sed_type) {
> +	case OPAL:
> +		return sed_opal_reverttper(bdev, key);
> +	}
> +
> +	return -EOPNOTSUPP;
> +}
> +
> +int sed_setup_locking_range(struct block_device *bdev, struct sed_key *key)
> +{
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	switch (key->sed_type) {
> +	case OPAL_LR_SETUP:
> +		return sed_opal_setup_lr(bdev, key);
> +	}
> +
> +	return -EOPNOTSUPP;
> +}
> +
> +int sed_adduser_to_lr(struct block_device *bdev, struct sed_key *key)
> +{
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	switch (key->sed_type) {
> +	case OPAL_LOCK_UNLOCK:
> +		return sed_opal_adduser_to_lr(bdev, key);
> +	}
> +
> +	return -EOPNOTSUPP;
> +}
> +
> +int sed_do_mbr(struct block_device *bdev, struct sed_key *key)
> +{
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	switch (key->sed_type) {
> +	case OPAL_MBR_DATA:
> +		return sed_opal_do_mbr(bdev, key);
> +	}
> +
> +	return -EOPNOTSUPP;
> +}
> +
> +int sed_erase_lr(struct block_device *bdev, struct sed_key *key)
> +{
> +	if (!bdev || !bdev->bd_disk || !bdev->bd_disk->fops ||
> +	    !bdev->bd_disk->fops->sec_ops)
> +		return -EOPNOTSUPP;
> +
> +	switch (key->sed_type) {
> +	case OPAL:
> +		return sed_opal_erase_lr(bdev, key);
> +	}
> +
> +	return -EOPNOTSUPP;
> +}
> -- 
> 2.7.4
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [RFC PATCH 5/6] nvme: Add unlock_from_suspend
  2016-11-07 18:45     ` Keith Busch
@ 2016-11-07 18:33       ` Scott Bauer
  0 siblings, 0 replies; 16+ messages in thread
From: Scott Bauer @ 2016-11-07 18:33 UTC (permalink / raw)


On Mon, Nov 07, 2016@01:45:42PM -0500, Keith Busch wrote:
> On Tue, Nov 01, 2016@10:18:13AM +0200, Sagi Grimberg wrote:
> > > -	spin_lock_irq(&nvmeq->q_lock);
> > > +	spin_lock_irqsave(&nvmeq->q_lock, flags);
> > >  	if (unlikely(nvmeq->cq_vector < 0)) {
> > >  		if (ns && !test_bit(NVME_NS_DEAD, &ns->flags))
> > >  			ret = BLK_MQ_RQ_QUEUE_BUSY;
> > >  		else
> > >  			ret = BLK_MQ_RQ_QUEUE_ERROR;
> > > -		spin_unlock_irq(&nvmeq->q_lock);
> > > +		spin_unlock_irqrestore(&nvmeq->q_lock, flags);
> > >  		goto out;
> > >  	}
> > >  	__nvme_submit_cmd(nvmeq, &cmnd);
> > >  	nvme_process_cq(nvmeq);
> > > -	spin_unlock_irq(&nvmeq->q_lock);
> > > +	spin_unlock_irqrestore(&nvmeq->q_lock, flags);
> > 
> > No documentation why this is needed...
> 
> Let's forget documenting why it's needed; this solution should instead
> figure out a way to make it so it's not needed.

This is some code that was used for previous iterations while we were testing.
We missed this and some other things in this patch set when we were cleaning up.
It's gone from V2 as well as the other weirdness in the patch.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [RFC PATCH 5/6] nvme: Add unlock_from_suspend
  2016-11-01  8:18   ` Sagi Grimberg
  2016-11-01 13:57     ` Christoph Hellwig
@ 2016-11-07 18:45     ` Keith Busch
  2016-11-07 18:33       ` Scott Bauer
  1 sibling, 1 reply; 16+ messages in thread
From: Keith Busch @ 2016-11-07 18:45 UTC (permalink / raw)


On Tue, Nov 01, 2016@10:18:13AM +0200, Sagi Grimberg wrote:
> > -	spin_lock_irq(&nvmeq->q_lock);
> > +	spin_lock_irqsave(&nvmeq->q_lock, flags);
> >  	if (unlikely(nvmeq->cq_vector < 0)) {
> >  		if (ns && !test_bit(NVME_NS_DEAD, &ns->flags))
> >  			ret = BLK_MQ_RQ_QUEUE_BUSY;
> >  		else
> >  			ret = BLK_MQ_RQ_QUEUE_ERROR;
> > -		spin_unlock_irq(&nvmeq->q_lock);
> > +		spin_unlock_irqrestore(&nvmeq->q_lock, flags);
> >  		goto out;
> >  	}
> >  	__nvme_submit_cmd(nvmeq, &cmnd);
> >  	nvme_process_cq(nvmeq);
> > -	spin_unlock_irq(&nvmeq->q_lock);
> > +	spin_unlock_irqrestore(&nvmeq->q_lock, flags);
> 
> No documentation why this is needed...

Let's forget documenting why it's needed; this solution should instead
figure out a way to make it so it's not needed.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [RFC PATCH 5/6] nvme: Add unlock_from_suspend
  2016-11-01 13:57     ` Christoph Hellwig
  2016-11-01 14:40       ` Scott Bauer
@ 2016-11-10 23:01       ` Scott Bauer
  2016-11-10 23:23         ` Keith Busch
  1 sibling, 1 reply; 16+ messages in thread
From: Scott Bauer @ 2016-11-10 23:01 UTC (permalink / raw)


On Tue, Nov 01, 2016@06:57:05AM -0700, Christoph Hellwig wrote:
> On Tue, Nov 01, 2016@10:18:13AM +0200, Sagi Grimberg wrote:
> > > +
> > > +	return nvme_insert_rq(q, req, 1, sec_submit_endio);
> > 
> > No need to introduce nvme_insert_rq at all, just call
> > blk_mq_insert_request (other examples call blk_execute_rq_nowait
> > but its pretty much the same...)
> 
> blk_execute_rq_nowait is the API to use - blk_mq_insert_request isn't
> even exported.

I remember now, after I changed it to use rq_nowait, why we added this wrapper
function and used blk_mq_insert_request.

When we dispatch opal commands down to the controller we're doing so in an IRQ,
so if we use rq_nowait, we lockup.

Will there be pushback if we continue with the original patch idea, where we 
export blk_mq_insert_request (forgot to send that) and use it? I looked through
the block API and I didn't see a execute_rq that was irq safe.

Any suggestions?

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [RFC PATCH 5/6] nvme: Add unlock_from_suspend
  2016-11-10 23:23         ` Keith Busch
@ 2016-11-10 23:19           ` Christoph Hellwig
  0 siblings, 0 replies; 16+ messages in thread
From: Christoph Hellwig @ 2016-11-10 23:19 UTC (permalink / raw)


On Thu, Nov 10, 2016@06:23:12PM -0500, Keith Busch wrote:
> On Thu, Nov 10, 2016@04:01:31PM -0700, Scott Bauer wrote:
> > On Tue, Nov 01, 2016@06:57:05AM -0700, Christoph Hellwig wrote:
> > > blk_execute_rq_nowait is the API to use - blk_mq_insert_request isn't
> > > even exported.
> > 
> > I remember now, after I changed it to use rq_nowait, why we added this wrapper
> > function and used blk_mq_insert_request.
> > 
> > When we dispatch opal commands down to the controller we're doing so in an IRQ,
> > so if we use rq_nowait, we lockup.
> 
> In this case can we push the submission off to a work queue and use
> blk_mq_insert_rq_nowait?

That's what we have to do anyway to avoid taking irqsafe locks.
(and the function is blk_execute_rq_nowait)

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [RFC PATCH 5/6] nvme: Add unlock_from_suspend
  2016-11-10 23:01       ` Scott Bauer
@ 2016-11-10 23:23         ` Keith Busch
  2016-11-10 23:19           ` Christoph Hellwig
  0 siblings, 1 reply; 16+ messages in thread
From: Keith Busch @ 2016-11-10 23:23 UTC (permalink / raw)


On Thu, Nov 10, 2016@04:01:31PM -0700, Scott Bauer wrote:
> On Tue, Nov 01, 2016@06:57:05AM -0700, Christoph Hellwig wrote:
> > blk_execute_rq_nowait is the API to use - blk_mq_insert_request isn't
> > even exported.
> 
> I remember now, after I changed it to use rq_nowait, why we added this wrapper
> function and used blk_mq_insert_request.
> 
> When we dispatch opal commands down to the controller we're doing so in an IRQ,
> so if we use rq_nowait, we lockup.

In this case can we push the submission off to a work queue and use
blk_mq_insert_rq_nowait?

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2016-11-10 23:23 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-10-31 21:58 [RFC PATCH 0/6] Sed Opal Scott Bauer
2016-10-31 21:58 ` [RFC PATCH 1/6] Include: Add definitions for sed Scott Bauer
2016-10-31 21:58 ` [RFC PATCH 2/6] lib: Add Sed-opal library Scott Bauer
2016-11-01 18:56   ` Jon Derrick
2016-10-31 21:58 ` [RFC PATCH 3/6] lib: Add Sed to Kconfig and Makefile Scott Bauer
2016-10-31 21:58 ` [RFC PATCH 4/6] include: Add sec_ops to block device operations Scott Bauer
2016-10-31 21:58 ` [RFC PATCH 5/6] nvme: Add unlock_from_suspend Scott Bauer
2016-11-01  8:18   ` Sagi Grimberg
2016-11-01 13:57     ` Christoph Hellwig
2016-11-01 14:40       ` Scott Bauer
2016-11-10 23:01       ` Scott Bauer
2016-11-10 23:23         ` Keith Busch
2016-11-10 23:19           ` Christoph Hellwig
2016-11-07 18:45     ` Keith Busch
2016-11-07 18:33       ` Scott Bauer
2016-10-31 21:58 ` [RFC PATCH 6/6] block: ioctl: Wire up Sed to block ioctls Scott Bauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).