All of lore.kernel.org
 help / color / mirror / Atom feed
From: Scott Bauer <scott.bauer@intel.com>
To: linux-nvme@lists.infradead.org
Cc: Rafael.Antognolli@intel.com, axboe@fb.com, keith.busch@intel.com,
	jonathan.derrick@intel.com, viro@zeniv.linux.org.uk,
	hch@infradead.org, linux-kernel@vger.kernel.org,
	sagi@grimberg.me, Scott Bauer <scott.bauer@intel.com>
Subject: [PATCH v3 2/5] lib: Add Sed-opal library
Date: Mon, 19 Dec 2016 12:35:46 -0700	[thread overview]
Message-ID: <1482176149-2257-3-git-send-email-scott.bauer@intel.com> (raw)
In-Reply-To: <1482176149-2257-1-git-send-email-scott.bauer@intel.com>

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.

Signed-off-by: Scott Bauer <scott.bauer@intel.com>
Signed-off-by: Rafael Antognolli <Rafael.Antognolli@intel.com>
---
 lib/Makefile            |    2 +-
 lib/sed-opal.c          | 2376 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/sed-opal_internal.h |  601 ++++++++++++
 lib/sed.c               |  197 ++++
 4 files changed, 3175 insertions(+), 1 deletion(-)
 create mode 100644 lib/sed-opal.c
 create mode 100644 lib/sed-opal_internal.h
 create mode 100644 lib/sed.c

diff --git a/lib/Makefile b/lib/Makefile
index 50144a3..acb5d82 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -36,7 +36,7 @@ obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
 	 gcd.o lcm.o list_sort.o uuid.o flex_array.o iov_iter.o clz_ctz.o \
 	 bsearch.o find_bit.o llist.o memweight.o kfifo.o \
 	 percpu-refcount.o percpu_ida.o rhashtable.o reciprocal_div.o \
-	 once.o
+	 once.o sed.o sed-opal.o
 obj-y += string_helpers.o
 obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o
 obj-y += hexdump.o
diff --git a/lib/sed-opal.c b/lib/sed-opal.c
new file mode 100644
index 0000000..65f7263
--- /dev/null
+++ b/lib/sed-opal.c
@@ -0,0 +1,2376 @@
+/*
+ * Copyright © 2016 Intel Corporation
+ *
+ * Authors:
+ *    Scott  Bauer      <scott.bauer@intel.com>
+ *    Rafael Antognolli <rafael.antognolli@intel.com>
+ *
+ * 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.
+ */
+
+#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 <linux/kdev_t.h>
+#include <linux/key.h>
+
+#include "sed-opal_internal.h"
+
+#define IO_BUFFER_LENGTH 2048
+#define MAX_TOKS 64
+
+struct opal_dev;
+typedef int (cont_fn)(struct opal_dev *dev);
+
+struct opal_cmd {
+	cont_fn *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 int (*opal_step)(struct opal_dev *dev);
+
+struct opal_suspend_data {
+	struct opal_lock_unlock unlk;
+	u8 lr;
+	size_t key_name_len;
+	char key_name[36];
+	struct list_head node;
+};
+
+/**
+ * struct opal_dev - The structure representing a OPAL enabled SED.
+ * @sed_ctx:The SED context, contains fn pointers to sec_send/recv.
+ * @opal_step:A series of opal methods that are necessary to complete a comannd.
+ * @func_data:An array of parameters for the opal methods above.
+ * @state:Describes the current opal_step we're working on.
+ * @dev_lock:Locks the entire opal_dev structure.
+ * @parsed:Parsed response from controller.
+ * @prev_data:Data returned from a method to the controller
+ * @error_cb:Error function that handles closing sessions after a failed method.
+ * @unlk_lst:A list of Locking ranges to unlock on this device during a resume.
+ */
+struct opal_dev {
+	struct sed_context *sed_ctx;
+	const opal_step *funcs;
+	void **func_data;
+	int state;
+	struct mutex dev_lock;
+	u16 comID;
+	u32 HSN;
+	u32 TSN;
+	u64 align;
+	u64 lowest_lba;
+	struct opal_cmd cmd;
+	struct parsed_resp parsed;
+	size_t prev_d_len;
+	void *prev_data;
+	opal_step error_cb;
+	void *error_cb_data;
+
+	struct list_head unlk_lst;
+};
+
+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_dev *dev)
+{
+	return dev->sed_ctx->ops->sec_send(dev->sed_ctx->sec_data,
+					   dev->comID, TCG_SECP_01,
+					   dev->cmd.cmd, IO_BUFFER_LENGTH);
+}
+
+static int opal_recv_cmd(struct opal_dev *dev)
+{
+	return dev->sed_ctx->ops->sec_recv(dev->sed_ctx->sec_data,
+					   dev->comID, TCG_SECP_01,
+					   dev->cmd.resp, IO_BUFFER_LENGTH);
+}
+
+static int opal_recv_check(struct opal_dev *dev)
+{
+	size_t buflen = IO_BUFFER_LENGTH;
+	void *buffer = dev->cmd.resp;
+	struct opal_header *hdr = buffer;
+	int ret;
+
+	do {
+		pr_debug("Sent OPAL command: outstanding=%d, minTransfer=%d\n",
+			 hdr->cp.outstandingData,
+			 hdr->cp.minTransfer);
+
+		if (hdr->cp.outstandingData == 0 ||
+		    hdr->cp.minTransfer != 0)
+			return 0;
+
+		memset(buffer, 0, buflen);
+		ret = opal_recv_cmd(dev);
+	} while (!ret);
+
+	return ret;
+}
+
+static int opal_send_recv(struct opal_dev *dev, cont_fn *cont)
+{
+	int ret;
+
+	ret = opal_send_cmd(dev);
+	if (ret)
+		return ret;
+	ret = opal_recv_cmd(dev);
+	if (ret)
+		return ret;
+	ret = opal_recv_check(dev);
+	if (ret)
+		return ret;
+	return cont(dev);
+}
+
+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 int next(struct opal_dev *dev)
+{
+	opal_step func;
+	int error = 0;
+
+	do {
+		func = dev->funcs[dev->state];
+		if (!func)
+			break;
+
+		dev->state++;
+		error = func(dev);
+
+		if (error) {
+			pr_err("Error on step function: %d with error %d: %s\n",
+			       dev->state, error,
+			       opal_error_to_human(error));
+
+			if (dev->error_cb && dev->state > 2)
+				dev->error_cb(dev->error_cb_data);
+		}
+	} while (!error);
+
+	return error;
+}
+
+static int opal_discovery0_end(struct opal_dev *dev)
+{
+	bool foundComID = false, supported = true, single_user = false;
+	const struct d0_header *hdr;
+	const u8 *epos, *cpos;
+	u16 comID = 0;
+	int error = 0;
+
+	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("Found OPAL feature description: %d\n",
+				 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;
+		case 0xbfff ... 0xffff:
+			/* vendor specific, just ignore */
+			break;
+		default:
+			pr_warn("OPAL Unknown feature: %d\n",
+				be16_to_cpu(body->code));
+
+		}
+		cpos += body->length + 4;
+	}
+
+	if (!supported) {
+		pr_err("This device is not Opal enabled. Not Supported!\n");
+		return 1;
+	}
+
+	if (!single_user)
+		pr_warn("Device doesn't support single user mode\n");
+
+
+	if (!foundComID) {
+		pr_warn("Could not find OPAL comID for device. Returning early\n");
+		return 1;
+	}
+
+	dev->comID = comID;
+
+	return 0;
+}
+
+static int opal_discovery0(struct opal_dev *dev)
+{
+	int ret;
+
+	memset(dev->cmd.resp, 0, IO_BUFFER_LENGTH);
+	dev->comID = 0x0001;
+	ret = opal_recv_cmd(dev);
+	if (ret)
+		return ret;
+	return 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 0;
+}
+
+#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 0;
+}
+
+static ssize_t 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 (len >= IO_BUFFER_LENGTH - cmd->pos - 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);
+
+	memcpy(&cmd->cmd[cmd->pos], bytestring, len);
+	cmd->pos += len;
+
+	return 0;
+}
+
+static ssize_t test_and_add_string(struct opal_cmd *cmd,
+				   const u8 *string,
+				   size_t len)
+{
+	return add_token_bytestring(cmd, string, len);
+}
+
+static ssize_t test_and_add_token_bytestr(struct opal_cmd *cmd,
+					     const u8 *bytestring)
+{
+	return add_token_bytestring(cmd, bytestring, OPAL_UID_LENGTH);
+}
+
+static ssize_t test_and_add_token_half(struct opal_cmd *cmd,
+				       const u8 *bytestring)
+{
+	return add_token_bytestring(cmd, bytestring, OPAL_UID_LENGTH/2);
+}
+
+#define LOCKING_RANGE_NON_GLOBAL 0x03
+
+static int build_locking_range(u8 *buffer, size_t length, u8 lr)
+{
+	if (length < OPAL_UID_LENGTH) {
+		pr_err("Can't build locking range. Length OOB\n");
+		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) {
+		pr_err("Can't build locking range user, Length OOB\n");
+		return -ERANGE;
+	}
+
+	memcpy(buffer, OPALUID[OPAL_USER1_UID], OPAL_UID_LENGTH);
+
+	buffer[7] = lr + 1;
+
+	return 0;
+}
+
+#define ADD_TOKEN_STRING(cmd, key, keylen)		        \
+	if (!err)					        \
+		err = test_and_add_string(cmd, key, keylen);
+
+#define ADD_TOKEN(type, cmd, tok)				\
+	if (!err)						\
+		err = test_and_add_token_##type(cmd, tok);
+
+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 err = 0;
+
+
+	ADD_TOKEN(u8, cmd, OPAL_ENDOFDATA);
+	ADD_TOKEN(u8, cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8, cmd, 0);
+	ADD_TOKEN(u8, cmd, 0);
+	ADD_TOKEN(u8, cmd, 0);
+	ADD_TOKEN(u8, cmd, OPAL_ENDLIST);
+
+	if (err) {
+		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");
+			return -EINVAL;
+		}
+		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));
+		return -EINVAL;
+	}
+
+	if (pos > buf + length)
+		return -EFAULT;
+
+	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);
+
+		if (token_length == -EINVAL)
+			return -EINVAL;
+
+		pos += token_length;
+		cpos += token_length;
+		iter++;
+		num_entries++;
+	}
+
+	if (num_entries == 0) {
+		pr_err("Couldn't parse response.\n");
+		return -EINVAL;
+	resp->num = num_entries;
+
+	return 0;
+}
+
+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("Couldn't parse response.\n");
+		return error;
+	}
+
+	return response_status(&dev->parsed);
+}
+
+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 int start_opal_session_cont(struct opal_dev *dev)
+{
+	u32 HSN, TSN;
+	int error = 0;
+
+	error = parse_and_check_status(dev);
+	if (error)
+		return error;
+
+	HSN = response_get_u64(&dev->parsed, 4);
+	TSN = response_get_u64(&dev->parsed, 5);
+
+	if (HSN == 0 && TSN == 0) {
+		pr_err("Couldn't authenticate session\n");
+		return -EPERM;
+	}
+
+	dev->HSN = HSN;
+	dev->TSN = TSN;
+	return 0;
+}
+
+static inline void opal_dev_get(struct opal_dev *dev)
+{
+	mutex_lock(&dev->dev_lock);
+}
+
+static inline void opal_dev_put(struct opal_dev *dev)
+{
+	mutex_unlock(&dev->dev_lock);
+}
+
+static int add_suspend_info(struct opal_dev *dev, struct opal_suspend_data *sus)
+{
+	struct opal_suspend_data *iter;
+	bool found = false;
+
+	if (list_empty(&dev->unlk_lst))
+		goto add_out;
+
+	list_for_each_entry(iter, &dev->unlk_lst, node) {
+		if (iter->lr == sus->lr) {
+			found = true;
+			break;
+		}
+	}
+
+	if (found) {
+		/* Replace the old with the new */
+		list_del(&iter->node);
+		kfree(iter);
+	}
+
+ add_out:
+	list_add_tail(&sus->node, &dev->unlk_lst);
+	return 0;
+}
+
+static int end_session_cont(struct opal_dev *dev)
+{
+	dev->HSN = 0;
+	dev->TSN = 0;
+	return parse_and_check_status(dev);
+}
+
+static int finalize_and_send(struct opal_dev *dev, struct opal_cmd *cmd,
+			     cont_fn cont)
+{
+	int ret;
+
+	ret = cmd_finalize(cmd, dev->HSN, dev->TSN);
+	if (ret) {
+		pr_err("Error finalizing command buffer: %d\n", ret);
+		return ret;
+	}
+
+	print_buffer(cmd->cmd, cmd->pos);
+
+	return opal_send_recv(dev, cont);
+}
+
+static int gen_key(struct opal_dev *dev)
+{
+	const u8 *method;
+	u8 uid[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	int err = 0;
+
+	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;
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, uid);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_GENKEY]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error building gen key command\n");
+		return err;
+
+	}
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int get_active_key_cont(struct opal_dev *dev)
+{
+	const char *activekey;
+	size_t keylen;
+	int error = 0;
+
+	error = parse_and_check_status(dev);
+	if (error)
+		return error;
+	keylen = response_get_string(&dev->parsed, 4, &activekey);
+	if (!activekey) {
+		pr_err("%s: Couldn't extract the Activekey from the response\n",
+		       __func__);
+		return 0x0A;
+	}
+	dev->prev_data = kmemdup(activekey, keylen, GFP_KERNEL);
+
+	if (!dev->prev_data)
+		return -ENOMEM;
+
+	dev->prev_d_len = keylen;
+
+	return 0;
+}
+
+static int get_active_key(struct opal_dev *dev)
+{
+	u8 uid[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	int err = 0;
+	u8 *lr;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+	lr = dev->func_data[dev->state - 1];
+
+	err = build_locking_range(uid, sizeof(uid), *lr);
+	if (err)
+		return err;
+
+	err = 0;
+	ADD_TOKEN(u8, cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, uid);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_GET]);
+	ADD_TOKEN(u8, cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8, cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8, cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8, cmd, OPAL_TINY_UINT_03); /* startCloumn */
+	ADD_TOKEN(u8, cmd, OPAL_TINY_UINT_10); /* ActiveKey */
+	ADD_TOKEN(u8, cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8, cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8, cmd, OPAL_TINY_UINT_04); /* endColumn */
+	ADD_TOKEN(u8, cmd, OPAL_TINY_UINT_10); /* ActiveKey */
+	ADD_TOKEN(u8, cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8, cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8, cmd, OPAL_ENDLIST);
+	if (err) {
+		pr_err("Error building get active key command\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, get_active_key_cont);
+}
+
+static int generic_lr_enable_disable(struct opal_cmd *cmd,
+				     u8 *uid, bool rle, bool wle,
+				     bool rl, bool wl)
+{
+	int err = 0;
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, uid);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_SET]);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_VALUES);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_05); /* ReadLockEnabled */
+	ADD_TOKEN(u8,      cmd, rle);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_06); /* WriteLockEnabled */
+	ADD_TOKEN(u8,      cmd, wle);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_READLOCKED);
+	ADD_TOKEN(u8,      cmd, rl);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_WRITELOCKED);
+	ADD_TOKEN(u8,      cmd, wl);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	return err;
+}
+
+static inline int enable_global_lr(struct opal_cmd *cmd, u8 *uid,
+				   struct opal_user_lr_setup *setup)
+{
+	int err;
+	err = generic_lr_enable_disable(cmd, uid, !!setup->RLE, !!setup->WLE,
+					0, 0);
+	if (err)
+		pr_err("Failed to create enable global lr command\n");
+	return err;
+}
+
+static int setup_locking_range(struct opal_dev *dev)
+{
+	u8 uid[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	struct opal_user_lr_setup *setup;
+	u8 lr;
+	int err = 0;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	setup = dev->func_data[dev->state - 1];
+	lr = setup->session.opal_key.lr;
+	err = build_locking_range(uid, sizeof(uid), lr);
+	if (err)
+		return err;
+
+	if (lr == 0)
+		err = enable_global_lr(cmd, uid, setup);
+	else {
+		ADD_TOKEN(u8,      cmd, OPAL_CALL);
+		ADD_TOKEN(bytestr, cmd, uid);
+		ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_SET]);
+
+		ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+		ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_VALUES);
+		ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+
+		ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_03); /* Ranges Start */
+		ADD_TOKEN(u64,     cmd, setup->range_start);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+		ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_04); /* Ranges length */
+		ADD_TOKEN(u64,     cmd, setup->range_length);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+		ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_05); /* ReadLockEnabled */
+		ADD_TOKEN(u64,     cmd, !!setup->RLE);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+		ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_06); /* WriteLockEnabled */
+		ADD_TOKEN(u64,     cmd, !!setup->WLE);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+		ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+
+	}
+	if (err) {
+		pr_err("Error building Setup Locking range command.\n");
+		return err;
+
+	}
+
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int start_generic_opal_session(struct opal_dev *dev,
+				      enum OPAL_UID auth,
+				      enum OPAL_UID sp_type,
+				      const char *key,
+				      u8 key_len)
+{
+	struct opal_cmd *cmd;
+	u32 HSN;
+	int err = 0;
+
+	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 = GENERIC_HOST_SESSION_NUM;
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, OPALUID[OPAL_SMUID_UID]);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_STARTSESSION]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u64,     cmd, HSN);
+	ADD_TOKEN(bytestr, cmd, OPALUID[sp_type]);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_01);
+
+	switch (auth) {
+	case OPAL_ANYBODY_UID:
+		ADD_TOKEN(u8, cmd, OPAL_ENDLIST);
+		break;
+	case OPAL_ADMIN1_UID:
+	case OPAL_SID_UID:
+		ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_00); /* HostChallenge */
+		ADD_TOKEN_STRING(cmd, key, key_len);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_03); /* HostSignAuth */
+		ADD_TOKEN(bytestr, cmd, OPALUID[auth]);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+		break;
+	default:
+		pr_err("Cannot start Admin SP session with auth %d\n", auth);
+		return 1;
+	}
+
+	if (err) {
+		pr_err("Error building start adminsp session command.\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, start_opal_session_cont);
+}
+
+static int start_anybodyASP_opal_session(struct opal_dev *dev)
+{
+	return start_generic_opal_session(dev, OPAL_ANYBODY_UID,
+					  OPAL_ADMINSP_UID, NULL, 0);
+}
+
+static int start_SIDASP_opal_session(struct opal_dev *dev)
+{
+	int ret;
+	const u8 *key = dev->prev_data;
+	struct opal_key *okey;
+
+	if (!key) {
+		okey = dev->func_data[dev->state - 1];
+		ret = start_generic_opal_session(dev, OPAL_SID_UID,
+						 OPAL_ADMINSP_UID,
+						 okey->key,
+						 okey->key_len);
+	}
+	else {
+		ret = start_generic_opal_session(dev, OPAL_SID_UID,
+						 OPAL_ADMINSP_UID,
+						 key, dev->prev_d_len);
+		kfree(key);
+		dev->prev_data = NULL;
+	}
+	return ret;
+}
+
+static inline int start_admin1LSP_opal_session(struct opal_dev *dev)
+{
+	struct opal_key *key = dev->func_data[dev->state - 1];
+	return start_generic_opal_session(dev, OPAL_ADMIN1_UID,
+					  OPAL_LOCKINGSP_UID,
+					  key->key, key->key_len);
+}
+
+static int start_auth_opal_session(struct opal_dev *dev)
+{
+	u8 lk_ul_user[OPAL_UID_LENGTH];
+	int err = 0;
+
+	struct opal_session_info *session = dev->func_data[dev->state - 1];
+	struct opal_cmd *cmd = &dev->cmd;
+	size_t keylen = session->opal_key.key_len;
+	u8 *key = session->opal_key.key;
+	u32 HSN = GENERIC_HOST_SESSION_NUM;
+
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	if (session->SUM) {
+		err = build_locking_user(lk_ul_user, sizeof(lk_ul_user),
+					 session->opal_key.lr);
+		if (err)
+			return err;
+
+	} else if (session->who != OPAL_ADMIN1 && !session->SUM) {
+		err = build_locking_user(lk_ul_user, sizeof(lk_ul_user),
+					 session->who - 1);
+		if (err)
+			return err;
+	} else
+		memcpy(lk_ul_user, OPALUID[OPAL_ADMIN1_UID], OPAL_UID_LENGTH);
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, OPALUID[OPAL_SMUID_UID]);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_STARTSESSION]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u64,     cmd, HSN);
+	ADD_TOKEN(bytestr, cmd, OPALUID[OPAL_LOCKINGSP_UID]);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_01);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_00);
+	ADD_TOKEN_STRING(cmd, key, keylen);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_03);
+	ADD_TOKEN(bytestr, cmd, lk_ul_user);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error building STARTSESSION command.\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, start_opal_session_cont);
+}
+
+static int revert_tper(struct opal_dev *dev)
+{
+	struct opal_cmd *cmd;
+	int err = 0;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, OPALUID[OPAL_ADMINSP_UID]);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_REVERT]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	if (err) {
+		pr_err("Error building REVERT TPER command.\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int internal_activate_user(struct opal_dev *dev)
+{
+	struct opal_session_info *session = dev->func_data[dev->state - 1];
+	u8 uid[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	int err = 0;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	memcpy(uid, OPALUID[OPAL_USER1_UID], OPAL_UID_LENGTH);
+	uid[7] = session->who;
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, uid);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_SET]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_VALUES);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_05); /* Enabled */
+	ADD_TOKEN(u8,      cmd, OPAL_TRUE);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error building Activate UserN command.\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int erase_locking_range(struct opal_dev *dev)
+{
+	struct opal_session_info *session;
+	u8 uid[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	int err = 0;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+	session = dev->func_data[dev->state - 1];
+
+	if (build_locking_range(uid, sizeof(uid), session->opal_key.lr) < 0)
+		return -ERANGE;
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, uid);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_ERASE]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error building Erase Locking Range Cmmand.\n");
+		return err;
+	}
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int set_mbr_done(struct opal_dev *dev)
+{
+	u8 mbr_done_tf = *(u8 *)dev->func_data[dev->state - 1];
+	struct opal_cmd *cmd = &dev->cmd;
+	int err = 0;
+
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, OPALUID[OPAL_MBRCONTROL]);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_SET]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_VALUES);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_02); /* Done */
+	ADD_TOKEN(u8,      cmd, mbr_done_tf); /* Done T or F */
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+  	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error Building set MBR Done command\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int set_mbr_enable_disable(struct opal_dev *dev)
+{
+	u8 mbr_en_dis = *(u8 *)dev->func_data[dev->state - 1];
+	struct opal_cmd *cmd = &dev->cmd;
+	int err = 0;
+
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, OPALUID[OPAL_MBRCONTROL]);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_SET]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_VALUES);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_01);
+	ADD_TOKEN(u8,      cmd, mbr_en_dis);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+  	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error Building set MBR done command\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int generic_pw_cmd(u8 *key, size_t key_len, u8 *cpin_uid,
+			  struct opal_dev *dev)
+{
+	struct opal_cmd *cmd = &dev->cmd;
+	int err = 0;
+
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, cpin_uid);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_SET]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_VALUES);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_03); /* PIN */
+	ADD_TOKEN_STRING(cmd, key, key_len);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	return err;
+}
+
+static int set_new_pw(struct opal_dev *dev)
+{
+	u8 cpin_uid[OPAL_UID_LENGTH];
+	struct opal_session_info *usr = dev->func_data[dev->state - 1];
+	struct opal_cmd *cmd = &dev->cmd;
+
+	memcpy(cpin_uid, OPALUID[OPAL_C_PIN_ADMIN1], OPAL_UID_LENGTH);
+
+	if (usr->who != OPAL_ADMIN1) {
+		cpin_uid[5] = 0x03;
+		if (usr->SUM)
+			cpin_uid[7] = usr->opal_key.lr + 1;
+		else
+			cpin_uid[7] = usr->who;
+	}
+
+
+	if (generic_pw_cmd(usr->opal_key.key, usr->opal_key.key_len,
+			   cpin_uid, dev)) {
+		pr_err("Error building set password command.\n");
+		return -ERANGE;
+	}
+
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int set_sid_cpin_pin(struct opal_dev *dev)
+{
+	u8 cpin_uid[OPAL_UID_LENGTH];
+	struct opal_key *key = dev->func_data[dev->state - 1];
+	struct opal_cmd *cmd = &dev->cmd;
+
+	memcpy(cpin_uid, OPALUID[OPAL_C_PIN_SID], OPAL_UID_LENGTH);
+	
+	if (generic_pw_cmd(key->key, key->key_len, cpin_uid, dev)) {
+		pr_err("Error building Set SID cpin\n");
+		return -ERANGE;
+	}
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int add_user_to_lr(struct opal_dev *dev)
+{
+	u8 lr_buffer[OPAL_UID_LENGTH];
+	u8 user_uid[OPAL_UID_LENGTH];
+	struct opal_lock_unlock *lkul;
+	struct opal_cmd *cmd;
+	int err = 0;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	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] = lkul->session.opal_key.lr;
+
+	memcpy(user_uid, OPALUID[OPAL_USER1_UID], OPAL_UID_LENGTH);
+
+	user_uid[7] = lkul->session.who;
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, lr_buffer);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_SET]);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_VALUES);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_03);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(half,    cmd, OPALUID[OPAL_HALF_UID_AUTHORITY_OBJ_REF]);
+	ADD_TOKEN(bytestr, cmd, user_uid);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(half,    cmd, OPALUID[OPAL_HALF_UID_AUTHORITY_OBJ_REF]);
+	ADD_TOKEN(bytestr, cmd, user_uid);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(half,    cmd, OPALUID[OPAL_HALF_UID_BOOLEAN_ACE]);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_01);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error building add user to locking range command.\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+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;
+	u8 read_locked = 1, write_locked = 1;
+	int err = 0;
+
+	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),
+				lkul->session.opal_key.lr) < 0)
+		return -ERANGE;
+
+	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;
+	}
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, lr_buffer);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_SET]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_VALUES);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_READLOCKED);
+	ADD_TOKEN(u8,      cmd, read_locked);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_WRITELOCKED);
+	ADD_TOKEN(u8,      cmd, write_locked);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error building SET command.\n");
+		return err;
+	}
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+
+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),
+				lkul->session.opal_key.lr) < 0)
+		return -ERANGE;
+
+	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 = generic_lr_enable_disable(cmd, lr_buffer, 1, 1,
+					read_locked, write_locked);
+
+	if (ret < 0) {
+		pr_err("Error building SET command.\n");
+		return ret;
+	}
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+int activate_lsp(struct opal_dev *dev)
+{
+	u8 user_lr[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	u8 uint_3 = 0x83;
+	int err = 0;
+	u8 *lr;
+
+	cmd = &dev->cmd;
+
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	lr = dev->func_data[dev->state - 1];
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, OPALUID[OPAL_LOCKINGSP_UID]);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_ACTIVATE]);
+	/* Activating as SUM */
+	if (*lr > 0) {
+		err = build_locking_range(user_lr, sizeof(user_lr), *lr);
+		if (err)
+			return err;
+
+		ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+		ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+		ADD_TOKEN(u8,      cmd, uint_3);
+		ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_06);
+		ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_00);
+		ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_00);
+
+		ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+		ADD_TOKEN(bytestr, cmd, user_lr);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	} else {
+		ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	}
+
+	if (err) {
+		pr_err("Error building Activate LockingSP command.\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int get_lsp_lifecycle_cont(struct opal_dev *dev)
+{
+	u8 lc_status;
+	int error = 0;
+
+	error = parse_and_check_status(dev);
+	if (error)
+		return error;
+
+	lc_status = response_get_u64(&dev->parsed, 4);
+	/* 0x08 is Manufacured Inactive */
+	/* 0x09 is Manufactured */
+	if (lc_status != 0x08) {
+		pr_err("Couldn't determine the status of the Lifcycle state\n");
+		return -ENODEV;
+	}
+
+err_return:
+	return 0;
+}
+
+/* Determine if we're in the Manufactured Inactive or Active state */
+int get_lsp_lifecycle(struct opal_dev *dev)
+{
+	struct opal_cmd *cmd;
+	int err = 0;
+
+	cmd = &dev->cmd;
+
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, OPALUID[OPAL_LOCKINGSP_UID]);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_GET]);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_03); /* Start Column */
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_06); /* Lifecycle Column */
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_04); /* End Column */
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_06); /* Lifecycle Column */
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error Building GET Lifecycle Status command\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, get_lsp_lifecycle_cont);
+}
+
+static int get_msid_cpin_pin_cont(struct opal_dev *dev)
+{
+	const char *msid_pin;
+	size_t strlen;
+	int error = 0;
+
+	error = parse_and_check_status(dev);
+	if (error)
+		return error;
+
+	strlen = response_get_string(&dev->parsed, 4, &msid_pin);
+	if (!msid_pin) {
+		pr_err("%s: Couldn't extract PIN from response\n", __func__);
+		return 11;
+	}
+
+	dev->prev_data = kmemdup(msid_pin, strlen, GFP_KERNEL);
+	if (!dev->prev_data)
+		return -ENOMEM;
+
+	dev->prev_d_len = strlen;
+
+ err_return:
+	return 0;
+}
+
+static int get_msid_cpin_pin(struct opal_dev *dev)
+{
+	struct opal_cmd *cmd;
+	int err = 0;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, OPALUID[OPAL_C_PIN_MSID]);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_GET]);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_03); /* Start Column */
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_03); /* PIN */
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_04); /* End Column */
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_03); /* Lifecycle Column */
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error building Get MSID CPIN PIN command.\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, get_msid_cpin_pin_cont);
+}
+
+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)
+{
+	int ret = build_end_opal_session(dev);
+
+	if (ret < 0)
+		return ret;
+	return finalize_and_send(dev, &dev->cmd, end_session_cont);
+}
+
+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;
+	return next(dev);
+}
+
+struct opal_dev *alloc_opal_dev(struct request_queue *q)
+{
+	struct opal_dev *opal_dev;
+	unsigned long dma_align;
+	struct opal_cmd *cmd;
+
+	opal_dev = kzalloc(sizeof(*opal_dev), GFP_KERNEL);
+	if (!opal_dev)
+		return opal_dev;
+
+	cmd = &opal_dev->cmd;
+	cmd->cmd = cmd->cmd_buf;
+	cmd->resp = cmd->resp_buf;
+
+	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->unlk_lst);
+
+	opal_dev->state = 0;
+
+	mutex_init(&opal_dev->dev_lock);
+
+	return opal_dev;
+
+}
+EXPORT_SYMBOL(alloc_opal_dev);
+
+static int do_cmds(struct opal_dev *dev)
+{
+	int ret;
+	ret = next(dev);
+	opal_dev_put(dev);
+	return ret;
+}
+
+static struct opal_dev *get_opal_dev(struct sed_context *sedc,
+				     const opal_step *funcs)
+{
+	struct opal_dev *dev = sedc->dev;
+	if (dev) {
+		dev->state = 0;
+		dev->funcs = funcs;
+		dev->TSN = 0;
+		dev->HSN = 0;
+		dev->error_cb = end_opal_session_error;
+		dev->error_cb_data = dev;
+		dev->func_data = NULL;
+		dev->sed_ctx = sedc;
+		opal_dev_get(dev);
+	}
+	return dev;
+}
+
+int opal_secure_erase_locking_range(struct sed_context *sedc, struct sed_key *key)
+{
+	struct opal_dev *dev;
+	void *data[3] = { NULL };
+	const opal_step erase_funcs[] = {
+		opal_discovery0,
+		start_auth_opal_session,
+		get_active_key,
+		gen_key,
+		end_opal_session,
+		NULL,
+	};
+
+	dev = get_opal_dev(sedc, erase_funcs);
+	if (!dev)
+		return -ENODEV;
+
+	dev->func_data = data;
+	dev->func_data[1] = &key->opal_session;
+	dev->func_data[2] = &key->opal_session.opal_key.lr;
+
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_secure_erase_locking_range);
+
+int opal_erase_locking_range(struct sed_context *sedc, struct sed_key *key)
+{
+	struct opal_dev *dev;
+	void *data[3] = { NULL };
+	const opal_step erase_funcs[] = {
+		opal_discovery0,
+		start_auth_opal_session,
+		erase_locking_range,
+		end_opal_session,
+		NULL,
+	};
+
+	dev = get_opal_dev(sedc, erase_funcs);
+	if (!dev)
+		return -ENODEV;
+
+	dev->func_data = data;
+	dev->func_data[1] = &key->opal_session;
+	dev->func_data[2] = &key->opal_session;
+
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_erase_locking_range);
+
+int opal_enable_disable_shadow_mbr(struct sed_context *sedc,
+				   struct sed_key *key)
+{
+	void *func_data[6] = { NULL };
+	struct opal_dev *dev;
+	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,
+	};
+
+	if (key->opal_mbr.enable_disable != OPAL_MBR_ENABLE &&
+	    key->opal_mbr.enable_disable != OPAL_MBR_DISABLE)
+		return -EINVAL;
+
+	dev = get_opal_dev(sedc, mbr_funcs);
+	if (!dev)
+		return -ENODEV;
+
+	dev->func_data = func_data;
+	dev->func_data[1] = &key->opal_mbr.key;
+	dev->func_data[2] = &key->opal_mbr.enable_disable;
+	dev->func_data[4] = &key->opal_mbr.key;
+	dev->func_data[5] = &key->opal_mbr.enable_disable;
+
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_enable_disable_shadow_mbr);
+
+int opal_save(struct sed_context *sedc, struct sed_key *key)
+{
+	struct opal_suspend_data *suspend;
+	struct opal_dev *dev;
+	int ret;
+
+	dev = get_opal_dev(sedc, NULL);
+	if (!dev)
+		return -ENODEV;
+	suspend = kzalloc(sizeof(*suspend), GFP_KERNEL);
+	if(!suspend)
+		return -ENOMEM;
+
+	suspend->unlk = key->opal_lk_unlk;
+	suspend->lr = key->opal_lk_unlk.session.opal_key.lr;
+	ret = add_suspend_info(dev, suspend);
+	opal_dev_put(dev);
+	return ret;
+}
+EXPORT_SYMBOL(opal_save);
+
+int opal_add_user_to_lr(struct sed_context *sedc, struct sed_key *key)
+{
+	void *func_data[3] = { NULL };
+	struct opal_dev *dev;
+		const opal_step funcs[] = {
+		opal_discovery0,
+		start_admin1LSP_opal_session,
+		add_user_to_lr,
+		end_opal_session,
+		NULL
+	};
+
+	if (key->opal_lk_unlk.l_state != OPAL_RO &&
+	    key->opal_lk_unlk.l_state != OPAL_RW) {
+		pr_err("Locking state was not RO or RW\n");
+		return -EINVAL;
+	}
+	if (key->opal_lk_unlk.session.who < OPAL_USER1 &&
+	    key->opal_lk_unlk.session.who > OPAL_USER9) {
+		pr_err("Authority was not within the range of users: %d\n",
+		       key->opal_lk_unlk.session.who);
+		return -EINVAL;
+	}
+	if (key->opal_lk_unlk.session.SUM) {
+		pr_err("%s not supported in SUM. Use setup locking range\n",
+		       __func__);
+		return -EINVAL;
+	}
+
+	dev = get_opal_dev(sedc, funcs);
+	if (!dev)
+		return -ENODEV;
+
+	dev->func_data = func_data;
+	dev->func_data[1] = &key->opal_lk_unlk.session.opal_key;
+	dev->func_data[2] = &key->opal_lk_unlk;
+
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_add_user_to_lr);
+
+int opal_reverttper(struct sed_context *sedc, struct sed_key *key)
+{
+	void *data[2] = { NULL };
+	const opal_step revert_funcs[] = {
+		opal_discovery0,
+		start_SIDASP_opal_session,
+		revert_tper, /* controller will terminate session */
+		NULL,
+	};
+	struct opal_dev *dev = get_opal_dev(sedc, revert_funcs);
+
+	if (!dev)
+		return -ENODEV;
+
+	dev->func_data = data;
+	dev->func_data[1] = &key->opal;
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_reverttper);
+
+/* These are global'd because both lock_unlock_internal
+ * and opal_unlock_from_suspend need them.
+ */
+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 opal_lock_unlock(struct sed_context *sedc, struct sed_key *key)
+{
+	void *func_data[3] = { NULL };
+	struct opal_dev *dev;
+
+	if (key->opal_lk_unlk.session.who < OPAL_ADMIN1 ||
+	    key->opal_lk_unlk.session.who > OPAL_USER9)
+		return -EINVAL;
+
+	dev = get_opal_dev(sedc, NULL);
+	if (!dev)
+		return -ENODEV;
+
+	if (key->opal_lk_unlk.session.SUM)
+		dev->funcs = _unlock_funcs;//ulk_funcs_SUM;
+	else
+		dev->funcs = _unlock_funcs;
+
+	dev->func_data = func_data;
+	dev->func_data[1] = &key->opal_lk_unlk.session;
+	dev->func_data[2] = &key->opal_lk_unlk;
+
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_lock_unlock);
+
+int opal_take_ownership(struct sed_context *sedc, struct sed_key *key)
+{
+	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
+	};
+	void *data[6] = { NULL };
+	struct opal_dev *dev = get_opal_dev(sedc, owner_funcs);
+
+	if (!dev)
+		return -ENODEV;
+
+	dev->func_data = data;
+	dev->func_data[4] = &key->opal;
+	dev->func_data[5] = &key->opal;
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_take_ownership);
+
+int opal_activate_lsp(struct sed_context *sedc, struct sed_key *key)
+{
+	void *data[4] = { NULL };
+	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
+	};
+	struct opal_dev *dev = get_opal_dev(sedc, active_funcs);
+
+	if (!dev)
+		return -ENODEV;
+	dev->func_data = data;
+	dev->func_data[1] = &key->opal;
+	dev->func_data[3] = &key->opal.lr;
+
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_activate_lsp);
+
+int opal_setup_locking_range(struct sed_context *sedc, struct sed_key *pw)
+{
+	void *data[3] = { NULL };
+	const opal_step lr_funcs[] = {
+		opal_discovery0,
+		start_auth_opal_session,
+		setup_locking_range,
+		end_opal_session,
+		NULL,
+	};
+	struct opal_dev *dev = get_opal_dev(sedc, lr_funcs);
+
+	if (!dev)
+		return -ENODEV;
+
+	dev->func_data = data;
+	dev->func_data[1] = &pw->opal_lrs.session;
+	dev->func_data[2] = &pw->opal_lrs;
+
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_setup_locking_range);
+
+int opal_set_new_pw(struct sed_context *sedc, struct sed_key *pw)
+{
+	const opal_step pw_funcs[] = {
+		opal_discovery0,
+		start_auth_opal_session,
+		set_new_pw,
+		end_opal_session,
+		NULL
+	};
+	struct opal_dev *dev;
+	void *data[3] = { NULL };
+
+	if (pw->sed_type != OPAL_PW)
+		return -EINVAL;
+
+	if (pw->opal_pw.session.who < OPAL_ADMIN1 ||
+	    pw->opal_pw.session.who > OPAL_USER9  ||
+	    pw->opal_pw.new_user_pw.who < OPAL_ADMIN1 ||
+	    pw->opal_pw.new_user_pw.who > OPAL_USER9)
+		return -EINVAL;
+
+	dev = get_opal_dev(sedc, pw_funcs);
+	if (!dev)
+		return -ENODEV;
+
+	dev->func_data = data;
+	dev->func_data[1] = (void *) &pw->opal_pw.session;
+	dev->func_data[2] = (void *) &pw->opal_pw.new_user_pw;
+
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_set_new_pw);
+
+int opal_activate_user(struct sed_context *sedc, struct sed_key *pw)
+{
+	const opal_step act_funcs[] = {
+		opal_discovery0,
+		start_admin1LSP_opal_session,
+		internal_activate_user,
+		end_opal_session,
+		NULL
+	};
+	struct opal_dev *dev;
+	void *data[3] = { NULL };
+
+	if (pw->sed_type != OPAL_ACT_USR) {
+		pr_err("Sed type was not act user\n");
+		return -EINVAL;
+	}
+
+	/* We can't activate Admin1 it's active as manufactured */
+	if (pw->opal_session.who < OPAL_USER1 &&
+	    pw->opal_session.who > OPAL_USER9) {
+		pr_err("Who was not a valid user: %d \n", pw->opal_session.who);
+		return -EINVAL;
+	}
+
+	dev = get_opal_dev(sedc, act_funcs);
+	if (!dev)
+		return -ENODEV;
+
+	dev->func_data = data;
+	dev->func_data[1] = &pw->opal_session.opal_key;
+	dev->func_data[2] = &pw->opal_session;
+
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_activate_user);
+
+int opal_unlock_from_suspend(struct sed_context *sedc)
+{
+	struct opal_suspend_data *suspend;
+	void *func_data[3] = { NULL };
+	bool was_failure = false;
+	struct opal_dev *dev = get_opal_dev(sedc, NULL);
+	int ret = 0;
+
+	if (!dev)
+		return 0;
+
+	dev->func_data = func_data;
+	dev->error_cb = end_opal_session_error;
+	dev->error_cb_data = dev;
+
+	if (!list_empty(&dev->unlk_lst)) {
+		list_for_each_entry(suspend, &dev->unlk_lst, node) {
+			dev->state = 0;
+			dev->func_data[1] = &suspend->unlk.session;
+			dev->func_data[2] = &suspend->unlk;
+			if (suspend->unlk.session.SUM)
+				dev->funcs = ulk_funcs_SUM;
+			else
+				dev->funcs = _unlock_funcs;
+			dev->TSN = 0;
+			dev->HSN = 0;
+			ret = next(dev);
+			if (ret)
+				was_failure = true;
+		}
+	}
+	opal_dev_put(dev);
+	return was_failure ? 1 : 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..12369eb
--- /dev/null
+++ b/lib/sed-opal_internal.h
@@ -0,0 +1,601 @@
+/*
+ * Copyright © 2016 Intel Corporation
+ *
+ * Authors:
+ *    Rafael Antognolli <rafael.antognolli@intel.com>
+ *    Scott  Bauer      <scott.bauer@intel.com>
+ *
+ * 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 _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
+#define GENERIC_HOST_SESSION_NUM 0x41
+
+
+
+/*
+ * Derived from:
+ * TCG_Storage_Architecture_Core_Spec_v2.01_r1.00
+ * Section: 5.1.5 Method Status Codes
+ */
+static const char *opal_errors[] = {
+	"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 >= ARRAY_SIZE(opal_errors) || error < 0)
+		return "Unknown Error";
+
+	return opal_errors[error];
+}
+
+/*
+ * User IDs used in the TCG storage SSCs
+ * Derived from: TCG_Storage_Architecture_Core_Spec_v2.01_r1.00
+ * Section: 6.3 Assigned UIDs
+ */
+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.
+ * Derived from: TCG_Storage_Architecture_Core_Spec_v2.01_r1.00
+ * Section: 6.3 Assigned UIDs
+ */
+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,
+};
+
+/*
+ * Token defs derived from:
+ * TCG_Storage_Architecture_Core_Spec_v2.01_r1.00
+ * 3.2.2 Data Stream Encoding
+ */
+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
+ */
+
+/* Packets derived from:
+ * TCG_Storage_Architecture_Core_Spec_v2.01_r1.00
+ * Secion: 3.2.3 ComPackets, Packets & Subpackets
+ */
+
+/* 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
+ * Section: 3.3.5 Capability Discovery
+ */
+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.c b/lib/sed.c
new file mode 100644
index 0000000..3b43a52
--- /dev/null
+++ b/lib/sed.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright © 2016 Intel Corporation
+ *
+ * Authors:
+ *    Rafael Antognolli <rafael.antognolli@intel.com>
+ *    Scott  Bauer      <scott.bauer@intel.com>
+ *
+ * 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.
+ */
+
+#include <linux/blkdev.h>
+#include <linux/sed.h>
+#include <linux/sed-opal.h>
+#include <asm/uaccess.h>
+
+int sed_save(struct sed_context *sed_ctx, struct sed_key *key)
+{
+	switch (key->sed_type) {
+	case OPAL_LOCK_UNLOCK:
+		return opal_save(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_lock_unlock(struct sed_context *sed_ctx, struct sed_key *key)
+{
+	switch (key->sed_type) {
+	case OPAL_LOCK_UNLOCK:
+		return opal_lock_unlock(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_take_ownership(struct sed_context *sed_ctx, struct sed_key *key)
+{
+
+	switch (key->sed_type) {
+	case OPAL:
+		return opal_take_ownership(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_activate_lsp(struct sed_context *sed_ctx, struct sed_key *key)
+{
+
+	switch (key->sed_type) {
+	case OPAL:
+		return opal_activate_lsp(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_set_pw(struct sed_context *sed_ctx, struct sed_key *key)
+{
+
+	switch (key->sed_type) {
+	case OPAL_PW:
+		return opal_set_new_pw(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_activate_user(struct sed_context *sed_ctx, struct sed_key *key)
+{
+
+	switch (key->sed_type) {
+	case OPAL_ACT_USR:
+		return opal_activate_user(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_reverttper(struct sed_context *sed_ctx, struct sed_key *key)
+{
+
+	switch (key->sed_type) {
+	case OPAL:
+		return opal_reverttper(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_setup_locking_range(struct sed_context *sed_ctx, struct sed_key *key)
+{
+
+	switch (key->sed_type) {
+	case OPAL_LR_SETUP:
+		return opal_setup_locking_range(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_adduser_to_lr(struct sed_context *sed_ctx, struct sed_key *key)
+{
+
+	switch (key->sed_type) {
+	case OPAL_LOCK_UNLOCK:
+		return opal_add_user_to_lr(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_do_mbr(struct sed_context *sed_ctx, struct sed_key *key)
+{
+
+	switch (key->sed_type) {
+	case OPAL_MBR_DATA:
+		return opal_enable_disable_shadow_mbr(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_erase_lr(struct sed_context *sed_ctx, struct sed_key *key)
+{
+
+	switch (key->sed_type) {
+	case OPAL:
+		return opal_erase_locking_range(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_secure_erase_lr(struct sed_context *sed_ctx, struct sed_key *key)
+{
+	switch (key->sed_type) {
+	case OPAL_ACT_USR:
+		return opal_secure_erase_locking_range(sed_ctx, key);
+
+	}
+	return -EOPNOTSUPP;
+}
+
+int fdev_sed_ioctl(struct file *filep, unsigned int cmd,
+		   unsigned long arg)
+{
+	struct sed_key key;
+	struct sed_context *sed_ctx;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	if (!filep->f_sedctx || !filep->f_sedctx->ops || !filep->f_sedctx->dev)
+		return -ENODEV;
+
+	sed_ctx = filep->f_sedctx;
+
+	if (copy_from_user(&key, (void __user *)arg, sizeof(key)))
+		return -EFAULT;
+
+	switch (cmd) {
+	case IOC_SED_SAVE:
+		return sed_save(sed_ctx, &key);
+	case IOC_SED_LOCK_UNLOCK:
+		return sed_lock_unlock(sed_ctx, &key);
+	case IOC_SED_TAKE_OWNERSHIP:
+		return sed_take_ownership(sed_ctx, &key);
+	case IOC_SED_ACTIVATE_LSP:
+		return sed_activate_lsp(sed_ctx, &key);
+	case IOC_SED_SET_PW:
+		return sed_set_pw(sed_ctx, &key);
+	case IOC_SED_ACTIVATE_USR:
+		return sed_activate_user(sed_ctx, &key);
+	case IOC_SED_REVERT_TPR:
+		return sed_reverttper(sed_ctx, &key);
+	case IOC_SED_LR_SETUP:
+		return sed_setup_locking_range(sed_ctx, &key);
+	case IOC_SED_ADD_USR_TO_LR:
+		return sed_adduser_to_lr(sed_ctx, &key);
+	case IOC_SED_ENABLE_DISABLE_MBR:
+		return sed_do_mbr(sed_ctx, &key);
+	case IOC_SED_ERASE_LR:
+		return sed_erase_lr(sed_ctx, &key);
+	case IOC_SED_SECURE_ERASE_LR:
+		return sed_secure_erase_lr(sed_ctx, &key);
+	}
+	return -ENOTTY;
+}
+EXPORT_SYMBOL_GPL(fdev_sed_ioctl);
-- 
2.7.4

WARNING: multiple messages have this Message-ID (diff)
From: scott.bauer@intel.com (Scott Bauer)
Subject: [PATCH v3 2/5] lib: Add Sed-opal library
Date: Mon, 19 Dec 2016 12:35:46 -0700	[thread overview]
Message-ID: <1482176149-2257-3-git-send-email-scott.bauer@intel.com> (raw)
In-Reply-To: <1482176149-2257-1-git-send-email-scott.bauer@intel.com>

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.

Signed-off-by: Scott Bauer <scott.bauer at intel.com>
Signed-off-by: Rafael Antognolli <Rafael.Antognolli at intel.com>
---
 lib/Makefile            |    2 +-
 lib/sed-opal.c          | 2376 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/sed-opal_internal.h |  601 ++++++++++++
 lib/sed.c               |  197 ++++
 4 files changed, 3175 insertions(+), 1 deletion(-)
 create mode 100644 lib/sed-opal.c
 create mode 100644 lib/sed-opal_internal.h
 create mode 100644 lib/sed.c

diff --git a/lib/Makefile b/lib/Makefile
index 50144a3..acb5d82 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -36,7 +36,7 @@ obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
 	 gcd.o lcm.o list_sort.o uuid.o flex_array.o iov_iter.o clz_ctz.o \
 	 bsearch.o find_bit.o llist.o memweight.o kfifo.o \
 	 percpu-refcount.o percpu_ida.o rhashtable.o reciprocal_div.o \
-	 once.o
+	 once.o sed.o sed-opal.o
 obj-y += string_helpers.o
 obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o
 obj-y += hexdump.o
diff --git a/lib/sed-opal.c b/lib/sed-opal.c
new file mode 100644
index 0000000..65f7263
--- /dev/null
+++ b/lib/sed-opal.c
@@ -0,0 +1,2376 @@
+/*
+ * Copyright ? 2016 Intel Corporation
+ *
+ * Authors:
+ *    Scott  Bauer      <scott.bauer at intel.com>
+ *    Rafael Antognolli <rafael.antognolli at intel.com>
+ *
+ * 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.
+ */
+
+#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 <linux/kdev_t.h>
+#include <linux/key.h>
+
+#include "sed-opal_internal.h"
+
+#define IO_BUFFER_LENGTH 2048
+#define MAX_TOKS 64
+
+struct opal_dev;
+typedef int (cont_fn)(struct opal_dev *dev);
+
+struct opal_cmd {
+	cont_fn *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 int (*opal_step)(struct opal_dev *dev);
+
+struct opal_suspend_data {
+	struct opal_lock_unlock unlk;
+	u8 lr;
+	size_t key_name_len;
+	char key_name[36];
+	struct list_head node;
+};
+
+/**
+ * struct opal_dev - The structure representing a OPAL enabled SED.
+ * @sed_ctx:The SED context, contains fn pointers to sec_send/recv.
+ * @opal_step:A series of opal methods that are necessary to complete a comannd.
+ * @func_data:An array of parameters for the opal methods above.
+ * @state:Describes the current opal_step we're working on.
+ * @dev_lock:Locks the entire opal_dev structure.
+ * @parsed:Parsed response from controller.
+ * @prev_data:Data returned from a method to the controller
+ * @error_cb:Error function that handles closing sessions after a failed method.
+ * @unlk_lst:A list of Locking ranges to unlock on this device during a resume.
+ */
+struct opal_dev {
+	struct sed_context *sed_ctx;
+	const opal_step *funcs;
+	void **func_data;
+	int state;
+	struct mutex dev_lock;
+	u16 comID;
+	u32 HSN;
+	u32 TSN;
+	u64 align;
+	u64 lowest_lba;
+	struct opal_cmd cmd;
+	struct parsed_resp parsed;
+	size_t prev_d_len;
+	void *prev_data;
+	opal_step error_cb;
+	void *error_cb_data;
+
+	struct list_head unlk_lst;
+};
+
+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_dev *dev)
+{
+	return dev->sed_ctx->ops->sec_send(dev->sed_ctx->sec_data,
+					   dev->comID, TCG_SECP_01,
+					   dev->cmd.cmd, IO_BUFFER_LENGTH);
+}
+
+static int opal_recv_cmd(struct opal_dev *dev)
+{
+	return dev->sed_ctx->ops->sec_recv(dev->sed_ctx->sec_data,
+					   dev->comID, TCG_SECP_01,
+					   dev->cmd.resp, IO_BUFFER_LENGTH);
+}
+
+static int opal_recv_check(struct opal_dev *dev)
+{
+	size_t buflen = IO_BUFFER_LENGTH;
+	void *buffer = dev->cmd.resp;
+	struct opal_header *hdr = buffer;
+	int ret;
+
+	do {
+		pr_debug("Sent OPAL command: outstanding=%d, minTransfer=%d\n",
+			 hdr->cp.outstandingData,
+			 hdr->cp.minTransfer);
+
+		if (hdr->cp.outstandingData == 0 ||
+		    hdr->cp.minTransfer != 0)
+			return 0;
+
+		memset(buffer, 0, buflen);
+		ret = opal_recv_cmd(dev);
+	} while (!ret);
+
+	return ret;
+}
+
+static int opal_send_recv(struct opal_dev *dev, cont_fn *cont)
+{
+	int ret;
+
+	ret = opal_send_cmd(dev);
+	if (ret)
+		return ret;
+	ret = opal_recv_cmd(dev);
+	if (ret)
+		return ret;
+	ret = opal_recv_check(dev);
+	if (ret)
+		return ret;
+	return cont(dev);
+}
+
+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 int next(struct opal_dev *dev)
+{
+	opal_step func;
+	int error = 0;
+
+	do {
+		func = dev->funcs[dev->state];
+		if (!func)
+			break;
+
+		dev->state++;
+		error = func(dev);
+
+		if (error) {
+			pr_err("Error on step function: %d with error %d: %s\n",
+			       dev->state, error,
+			       opal_error_to_human(error));
+
+			if (dev->error_cb && dev->state > 2)
+				dev->error_cb(dev->error_cb_data);
+		}
+	} while (!error);
+
+	return error;
+}
+
+static int opal_discovery0_end(struct opal_dev *dev)
+{
+	bool foundComID = false, supported = true, single_user = false;
+	const struct d0_header *hdr;
+	const u8 *epos, *cpos;
+	u16 comID = 0;
+	int error = 0;
+
+	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("Found OPAL feature description: %d\n",
+				 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;
+		case 0xbfff ... 0xffff:
+			/* vendor specific, just ignore */
+			break;
+		default:
+			pr_warn("OPAL Unknown feature: %d\n",
+				be16_to_cpu(body->code));
+
+		}
+		cpos += body->length + 4;
+	}
+
+	if (!supported) {
+		pr_err("This device is not Opal enabled. Not Supported!\n");
+		return 1;
+	}
+
+	if (!single_user)
+		pr_warn("Device doesn't support single user mode\n");
+
+
+	if (!foundComID) {
+		pr_warn("Could not find OPAL comID for device. Returning early\n");
+		return 1;
+	}
+
+	dev->comID = comID;
+
+	return 0;
+}
+
+static int opal_discovery0(struct opal_dev *dev)
+{
+	int ret;
+
+	memset(dev->cmd.resp, 0, IO_BUFFER_LENGTH);
+	dev->comID = 0x0001;
+	ret = opal_recv_cmd(dev);
+	if (ret)
+		return ret;
+	return 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 0;
+}
+
+#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 0;
+}
+
+static ssize_t 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 (len >= IO_BUFFER_LENGTH - cmd->pos - 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);
+
+	memcpy(&cmd->cmd[cmd->pos], bytestring, len);
+	cmd->pos += len;
+
+	return 0;
+}
+
+static ssize_t test_and_add_string(struct opal_cmd *cmd,
+				   const u8 *string,
+				   size_t len)
+{
+	return add_token_bytestring(cmd, string, len);
+}
+
+static ssize_t test_and_add_token_bytestr(struct opal_cmd *cmd,
+					     const u8 *bytestring)
+{
+	return add_token_bytestring(cmd, bytestring, OPAL_UID_LENGTH);
+}
+
+static ssize_t test_and_add_token_half(struct opal_cmd *cmd,
+				       const u8 *bytestring)
+{
+	return add_token_bytestring(cmd, bytestring, OPAL_UID_LENGTH/2);
+}
+
+#define LOCKING_RANGE_NON_GLOBAL 0x03
+
+static int build_locking_range(u8 *buffer, size_t length, u8 lr)
+{
+	if (length < OPAL_UID_LENGTH) {
+		pr_err("Can't build locking range. Length OOB\n");
+		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) {
+		pr_err("Can't build locking range user, Length OOB\n");
+		return -ERANGE;
+	}
+
+	memcpy(buffer, OPALUID[OPAL_USER1_UID], OPAL_UID_LENGTH);
+
+	buffer[7] = lr + 1;
+
+	return 0;
+}
+
+#define ADD_TOKEN_STRING(cmd, key, keylen)		        \
+	if (!err)					        \
+		err = test_and_add_string(cmd, key, keylen);
+
+#define ADD_TOKEN(type, cmd, tok)				\
+	if (!err)						\
+		err = test_and_add_token_##type(cmd, tok);
+
+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 err = 0;
+
+
+	ADD_TOKEN(u8, cmd, OPAL_ENDOFDATA);
+	ADD_TOKEN(u8, cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8, cmd, 0);
+	ADD_TOKEN(u8, cmd, 0);
+	ADD_TOKEN(u8, cmd, 0);
+	ADD_TOKEN(u8, cmd, OPAL_ENDLIST);
+
+	if (err) {
+		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");
+			return -EINVAL;
+		}
+		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));
+		return -EINVAL;
+	}
+
+	if (pos > buf + length)
+		return -EFAULT;
+
+	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);
+
+		if (token_length == -EINVAL)
+			return -EINVAL;
+
+		pos += token_length;
+		cpos += token_length;
+		iter++;
+		num_entries++;
+	}
+
+	if (num_entries == 0) {
+		pr_err("Couldn't parse response.\n");
+		return -EINVAL;
+	resp->num = num_entries;
+
+	return 0;
+}
+
+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("Couldn't parse response.\n");
+		return error;
+	}
+
+	return response_status(&dev->parsed);
+}
+
+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 int start_opal_session_cont(struct opal_dev *dev)
+{
+	u32 HSN, TSN;
+	int error = 0;
+
+	error = parse_and_check_status(dev);
+	if (error)
+		return error;
+
+	HSN = response_get_u64(&dev->parsed, 4);
+	TSN = response_get_u64(&dev->parsed, 5);
+
+	if (HSN == 0 && TSN == 0) {
+		pr_err("Couldn't authenticate session\n");
+		return -EPERM;
+	}
+
+	dev->HSN = HSN;
+	dev->TSN = TSN;
+	return 0;
+}
+
+static inline void opal_dev_get(struct opal_dev *dev)
+{
+	mutex_lock(&dev->dev_lock);
+}
+
+static inline void opal_dev_put(struct opal_dev *dev)
+{
+	mutex_unlock(&dev->dev_lock);
+}
+
+static int add_suspend_info(struct opal_dev *dev, struct opal_suspend_data *sus)
+{
+	struct opal_suspend_data *iter;
+	bool found = false;
+
+	if (list_empty(&dev->unlk_lst))
+		goto add_out;
+
+	list_for_each_entry(iter, &dev->unlk_lst, node) {
+		if (iter->lr == sus->lr) {
+			found = true;
+			break;
+		}
+	}
+
+	if (found) {
+		/* Replace the old with the new */
+		list_del(&iter->node);
+		kfree(iter);
+	}
+
+ add_out:
+	list_add_tail(&sus->node, &dev->unlk_lst);
+	return 0;
+}
+
+static int end_session_cont(struct opal_dev *dev)
+{
+	dev->HSN = 0;
+	dev->TSN = 0;
+	return parse_and_check_status(dev);
+}
+
+static int finalize_and_send(struct opal_dev *dev, struct opal_cmd *cmd,
+			     cont_fn cont)
+{
+	int ret;
+
+	ret = cmd_finalize(cmd, dev->HSN, dev->TSN);
+	if (ret) {
+		pr_err("Error finalizing command buffer: %d\n", ret);
+		return ret;
+	}
+
+	print_buffer(cmd->cmd, cmd->pos);
+
+	return opal_send_recv(dev, cont);
+}
+
+static int gen_key(struct opal_dev *dev)
+{
+	const u8 *method;
+	u8 uid[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	int err = 0;
+
+	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;
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, uid);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_GENKEY]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error building gen key command\n");
+		return err;
+
+	}
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int get_active_key_cont(struct opal_dev *dev)
+{
+	const char *activekey;
+	size_t keylen;
+	int error = 0;
+
+	error = parse_and_check_status(dev);
+	if (error)
+		return error;
+	keylen = response_get_string(&dev->parsed, 4, &activekey);
+	if (!activekey) {
+		pr_err("%s: Couldn't extract the Activekey from the response\n",
+		       __func__);
+		return 0x0A;
+	}
+	dev->prev_data = kmemdup(activekey, keylen, GFP_KERNEL);
+
+	if (!dev->prev_data)
+		return -ENOMEM;
+
+	dev->prev_d_len = keylen;
+
+	return 0;
+}
+
+static int get_active_key(struct opal_dev *dev)
+{
+	u8 uid[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	int err = 0;
+	u8 *lr;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+	lr = dev->func_data[dev->state - 1];
+
+	err = build_locking_range(uid, sizeof(uid), *lr);
+	if (err)
+		return err;
+
+	err = 0;
+	ADD_TOKEN(u8, cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, uid);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_GET]);
+	ADD_TOKEN(u8, cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8, cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8, cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8, cmd, OPAL_TINY_UINT_03); /* startCloumn */
+	ADD_TOKEN(u8, cmd, OPAL_TINY_UINT_10); /* ActiveKey */
+	ADD_TOKEN(u8, cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8, cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8, cmd, OPAL_TINY_UINT_04); /* endColumn */
+	ADD_TOKEN(u8, cmd, OPAL_TINY_UINT_10); /* ActiveKey */
+	ADD_TOKEN(u8, cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8, cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8, cmd, OPAL_ENDLIST);
+	if (err) {
+		pr_err("Error building get active key command\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, get_active_key_cont);
+}
+
+static int generic_lr_enable_disable(struct opal_cmd *cmd,
+				     u8 *uid, bool rle, bool wle,
+				     bool rl, bool wl)
+{
+	int err = 0;
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, uid);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_SET]);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_VALUES);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_05); /* ReadLockEnabled */
+	ADD_TOKEN(u8,      cmd, rle);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_06); /* WriteLockEnabled */
+	ADD_TOKEN(u8,      cmd, wle);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_READLOCKED);
+	ADD_TOKEN(u8,      cmd, rl);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_WRITELOCKED);
+	ADD_TOKEN(u8,      cmd, wl);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	return err;
+}
+
+static inline int enable_global_lr(struct opal_cmd *cmd, u8 *uid,
+				   struct opal_user_lr_setup *setup)
+{
+	int err;
+	err = generic_lr_enable_disable(cmd, uid, !!setup->RLE, !!setup->WLE,
+					0, 0);
+	if (err)
+		pr_err("Failed to create enable global lr command\n");
+	return err;
+}
+
+static int setup_locking_range(struct opal_dev *dev)
+{
+	u8 uid[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	struct opal_user_lr_setup *setup;
+	u8 lr;
+	int err = 0;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	setup = dev->func_data[dev->state - 1];
+	lr = setup->session.opal_key.lr;
+	err = build_locking_range(uid, sizeof(uid), lr);
+	if (err)
+		return err;
+
+	if (lr == 0)
+		err = enable_global_lr(cmd, uid, setup);
+	else {
+		ADD_TOKEN(u8,      cmd, OPAL_CALL);
+		ADD_TOKEN(bytestr, cmd, uid);
+		ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_SET]);
+
+		ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+		ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_VALUES);
+		ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+
+		ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_03); /* Ranges Start */
+		ADD_TOKEN(u64,     cmd, setup->range_start);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+		ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_04); /* Ranges length */
+		ADD_TOKEN(u64,     cmd, setup->range_length);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+		ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_05); /* ReadLockEnabled */
+		ADD_TOKEN(u64,     cmd, !!setup->RLE);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+		ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_06); /* WriteLockEnabled */
+		ADD_TOKEN(u64,     cmd, !!setup->WLE);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+		ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+
+	}
+	if (err) {
+		pr_err("Error building Setup Locking range command.\n");
+		return err;
+
+	}
+
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int start_generic_opal_session(struct opal_dev *dev,
+				      enum OPAL_UID auth,
+				      enum OPAL_UID sp_type,
+				      const char *key,
+				      u8 key_len)
+{
+	struct opal_cmd *cmd;
+	u32 HSN;
+	int err = 0;
+
+	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 = GENERIC_HOST_SESSION_NUM;
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, OPALUID[OPAL_SMUID_UID]);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_STARTSESSION]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u64,     cmd, HSN);
+	ADD_TOKEN(bytestr, cmd, OPALUID[sp_type]);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_01);
+
+	switch (auth) {
+	case OPAL_ANYBODY_UID:
+		ADD_TOKEN(u8, cmd, OPAL_ENDLIST);
+		break;
+	case OPAL_ADMIN1_UID:
+	case OPAL_SID_UID:
+		ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_00); /* HostChallenge */
+		ADD_TOKEN_STRING(cmd, key, key_len);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_03); /* HostSignAuth */
+		ADD_TOKEN(bytestr, cmd, OPALUID[auth]);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+		break;
+	default:
+		pr_err("Cannot start Admin SP session with auth %d\n", auth);
+		return 1;
+	}
+
+	if (err) {
+		pr_err("Error building start adminsp session command.\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, start_opal_session_cont);
+}
+
+static int start_anybodyASP_opal_session(struct opal_dev *dev)
+{
+	return start_generic_opal_session(dev, OPAL_ANYBODY_UID,
+					  OPAL_ADMINSP_UID, NULL, 0);
+}
+
+static int start_SIDASP_opal_session(struct opal_dev *dev)
+{
+	int ret;
+	const u8 *key = dev->prev_data;
+	struct opal_key *okey;
+
+	if (!key) {
+		okey = dev->func_data[dev->state - 1];
+		ret = start_generic_opal_session(dev, OPAL_SID_UID,
+						 OPAL_ADMINSP_UID,
+						 okey->key,
+						 okey->key_len);
+	}
+	else {
+		ret = start_generic_opal_session(dev, OPAL_SID_UID,
+						 OPAL_ADMINSP_UID,
+						 key, dev->prev_d_len);
+		kfree(key);
+		dev->prev_data = NULL;
+	}
+	return ret;
+}
+
+static inline int start_admin1LSP_opal_session(struct opal_dev *dev)
+{
+	struct opal_key *key = dev->func_data[dev->state - 1];
+	return start_generic_opal_session(dev, OPAL_ADMIN1_UID,
+					  OPAL_LOCKINGSP_UID,
+					  key->key, key->key_len);
+}
+
+static int start_auth_opal_session(struct opal_dev *dev)
+{
+	u8 lk_ul_user[OPAL_UID_LENGTH];
+	int err = 0;
+
+	struct opal_session_info *session = dev->func_data[dev->state - 1];
+	struct opal_cmd *cmd = &dev->cmd;
+	size_t keylen = session->opal_key.key_len;
+	u8 *key = session->opal_key.key;
+	u32 HSN = GENERIC_HOST_SESSION_NUM;
+
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	if (session->SUM) {
+		err = build_locking_user(lk_ul_user, sizeof(lk_ul_user),
+					 session->opal_key.lr);
+		if (err)
+			return err;
+
+	} else if (session->who != OPAL_ADMIN1 && !session->SUM) {
+		err = build_locking_user(lk_ul_user, sizeof(lk_ul_user),
+					 session->who - 1);
+		if (err)
+			return err;
+	} else
+		memcpy(lk_ul_user, OPALUID[OPAL_ADMIN1_UID], OPAL_UID_LENGTH);
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, OPALUID[OPAL_SMUID_UID]);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_STARTSESSION]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u64,     cmd, HSN);
+	ADD_TOKEN(bytestr, cmd, OPALUID[OPAL_LOCKINGSP_UID]);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_01);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_00);
+	ADD_TOKEN_STRING(cmd, key, keylen);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_03);
+	ADD_TOKEN(bytestr, cmd, lk_ul_user);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error building STARTSESSION command.\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, start_opal_session_cont);
+}
+
+static int revert_tper(struct opal_dev *dev)
+{
+	struct opal_cmd *cmd;
+	int err = 0;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, OPALUID[OPAL_ADMINSP_UID]);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_REVERT]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	if (err) {
+		pr_err("Error building REVERT TPER command.\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int internal_activate_user(struct opal_dev *dev)
+{
+	struct opal_session_info *session = dev->func_data[dev->state - 1];
+	u8 uid[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	int err = 0;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	memcpy(uid, OPALUID[OPAL_USER1_UID], OPAL_UID_LENGTH);
+	uid[7] = session->who;
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, uid);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_SET]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_VALUES);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_05); /* Enabled */
+	ADD_TOKEN(u8,      cmd, OPAL_TRUE);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error building Activate UserN command.\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int erase_locking_range(struct opal_dev *dev)
+{
+	struct opal_session_info *session;
+	u8 uid[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	int err = 0;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+	session = dev->func_data[dev->state - 1];
+
+	if (build_locking_range(uid, sizeof(uid), session->opal_key.lr) < 0)
+		return -ERANGE;
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, uid);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_ERASE]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error building Erase Locking Range Cmmand.\n");
+		return err;
+	}
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int set_mbr_done(struct opal_dev *dev)
+{
+	u8 mbr_done_tf = *(u8 *)dev->func_data[dev->state - 1];
+	struct opal_cmd *cmd = &dev->cmd;
+	int err = 0;
+
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, OPALUID[OPAL_MBRCONTROL]);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_SET]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_VALUES);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_02); /* Done */
+	ADD_TOKEN(u8,      cmd, mbr_done_tf); /* Done T or F */
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+  	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error Building set MBR Done command\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int set_mbr_enable_disable(struct opal_dev *dev)
+{
+	u8 mbr_en_dis = *(u8 *)dev->func_data[dev->state - 1];
+	struct opal_cmd *cmd = &dev->cmd;
+	int err = 0;
+
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, OPALUID[OPAL_MBRCONTROL]);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_SET]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_VALUES);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_01);
+	ADD_TOKEN(u8,      cmd, mbr_en_dis);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+  	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error Building set MBR done command\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int generic_pw_cmd(u8 *key, size_t key_len, u8 *cpin_uid,
+			  struct opal_dev *dev)
+{
+	struct opal_cmd *cmd = &dev->cmd;
+	int err = 0;
+
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, cpin_uid);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_SET]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_VALUES);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_03); /* PIN */
+	ADD_TOKEN_STRING(cmd, key, key_len);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	return err;
+}
+
+static int set_new_pw(struct opal_dev *dev)
+{
+	u8 cpin_uid[OPAL_UID_LENGTH];
+	struct opal_session_info *usr = dev->func_data[dev->state - 1];
+	struct opal_cmd *cmd = &dev->cmd;
+
+	memcpy(cpin_uid, OPALUID[OPAL_C_PIN_ADMIN1], OPAL_UID_LENGTH);
+
+	if (usr->who != OPAL_ADMIN1) {
+		cpin_uid[5] = 0x03;
+		if (usr->SUM)
+			cpin_uid[7] = usr->opal_key.lr + 1;
+		else
+			cpin_uid[7] = usr->who;
+	}
+
+
+	if (generic_pw_cmd(usr->opal_key.key, usr->opal_key.key_len,
+			   cpin_uid, dev)) {
+		pr_err("Error building set password command.\n");
+		return -ERANGE;
+	}
+
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int set_sid_cpin_pin(struct opal_dev *dev)
+{
+	u8 cpin_uid[OPAL_UID_LENGTH];
+	struct opal_key *key = dev->func_data[dev->state - 1];
+	struct opal_cmd *cmd = &dev->cmd;
+
+	memcpy(cpin_uid, OPALUID[OPAL_C_PIN_SID], OPAL_UID_LENGTH);
+	
+	if (generic_pw_cmd(key->key, key->key_len, cpin_uid, dev)) {
+		pr_err("Error building Set SID cpin\n");
+		return -ERANGE;
+	}
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int add_user_to_lr(struct opal_dev *dev)
+{
+	u8 lr_buffer[OPAL_UID_LENGTH];
+	u8 user_uid[OPAL_UID_LENGTH];
+	struct opal_lock_unlock *lkul;
+	struct opal_cmd *cmd;
+	int err = 0;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	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] = lkul->session.opal_key.lr;
+
+	memcpy(user_uid, OPALUID[OPAL_USER1_UID], OPAL_UID_LENGTH);
+
+	user_uid[7] = lkul->session.who;
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, lr_buffer);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_SET]);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_VALUES);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_03);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(half,    cmd, OPALUID[OPAL_HALF_UID_AUTHORITY_OBJ_REF]);
+	ADD_TOKEN(bytestr, cmd, user_uid);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(half,    cmd, OPALUID[OPAL_HALF_UID_AUTHORITY_OBJ_REF]);
+	ADD_TOKEN(bytestr, cmd, user_uid);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(half,    cmd, OPALUID[OPAL_HALF_UID_BOOLEAN_ACE]);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_01);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error building add user to locking range command.\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+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;
+	u8 read_locked = 1, write_locked = 1;
+	int err = 0;
+
+	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),
+				lkul->session.opal_key.lr) < 0)
+		return -ERANGE;
+
+	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;
+	}
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, lr_buffer);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_SET]);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_VALUES);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_READLOCKED);
+	ADD_TOKEN(u8,      cmd, read_locked);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_WRITELOCKED);
+	ADD_TOKEN(u8,      cmd, write_locked);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error building SET command.\n");
+		return err;
+	}
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+
+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),
+				lkul->session.opal_key.lr) < 0)
+		return -ERANGE;
+
+	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 = generic_lr_enable_disable(cmd, lr_buffer, 1, 1,
+					read_locked, write_locked);
+
+	if (ret < 0) {
+		pr_err("Error building SET command.\n");
+		return ret;
+	}
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+int activate_lsp(struct opal_dev *dev)
+{
+	u8 user_lr[OPAL_UID_LENGTH];
+	struct opal_cmd *cmd;
+	u8 uint_3 = 0x83;
+	int err = 0;
+	u8 *lr;
+
+	cmd = &dev->cmd;
+
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	lr = dev->func_data[dev->state - 1];
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, OPALUID[OPAL_LOCKINGSP_UID]);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_ACTIVATE]);
+	/* Activating as SUM */
+	if (*lr > 0) {
+		err = build_locking_range(user_lr, sizeof(user_lr), *lr);
+		if (err)
+			return err;
+
+		ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+		ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+		ADD_TOKEN(u8,      cmd, uint_3);
+		ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_06);
+		ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_00);
+		ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_00);
+
+		ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+		ADD_TOKEN(bytestr, cmd, user_lr);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	} else {
+		ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+		ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	}
+
+	if (err) {
+		pr_err("Error building Activate LockingSP command.\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, parse_and_check_status);
+}
+
+static int get_lsp_lifecycle_cont(struct opal_dev *dev)
+{
+	u8 lc_status;
+	int error = 0;
+
+	error = parse_and_check_status(dev);
+	if (error)
+		return error;
+
+	lc_status = response_get_u64(&dev->parsed, 4);
+	/* 0x08 is Manufacured Inactive */
+	/* 0x09 is Manufactured */
+	if (lc_status != 0x08) {
+		pr_err("Couldn't determine the status of the Lifcycle state\n");
+		return -ENODEV;
+	}
+
+err_return:
+	return 0;
+}
+
+/* Determine if we're in the Manufactured Inactive or Active state */
+int get_lsp_lifecycle(struct opal_dev *dev)
+{
+	struct opal_cmd *cmd;
+	int err = 0;
+
+	cmd = &dev->cmd;
+
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, OPALUID[OPAL_LOCKINGSP_UID]);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_GET]);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_03); /* Start Column */
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_06); /* Lifecycle Column */
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_04); /* End Column */
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_06); /* Lifecycle Column */
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error Building GET Lifecycle Status command\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, get_lsp_lifecycle_cont);
+}
+
+static int get_msid_cpin_pin_cont(struct opal_dev *dev)
+{
+	const char *msid_pin;
+	size_t strlen;
+	int error = 0;
+
+	error = parse_and_check_status(dev);
+	if (error)
+		return error;
+
+	strlen = response_get_string(&dev->parsed, 4, &msid_pin);
+	if (!msid_pin) {
+		pr_err("%s: Couldn't extract PIN from response\n", __func__);
+		return 11;
+	}
+
+	dev->prev_data = kmemdup(msid_pin, strlen, GFP_KERNEL);
+	if (!dev->prev_data)
+		return -ENOMEM;
+
+	dev->prev_d_len = strlen;
+
+ err_return:
+	return 0;
+}
+
+static int get_msid_cpin_pin(struct opal_dev *dev)
+{
+	struct opal_cmd *cmd;
+	int err = 0;
+
+	cmd = &dev->cmd;
+	clear_opal_cmd(cmd);
+	set_comID(cmd, dev->comID);
+
+
+	ADD_TOKEN(u8,      cmd, OPAL_CALL);
+	ADD_TOKEN(bytestr, cmd, OPALUID[OPAL_C_PIN_MSID]);
+	ADD_TOKEN(bytestr, cmd, OPALMETHOD[OPAL_GET]);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_STARTLIST);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_03); /* Start Column */
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_03); /* PIN */
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_STARTNAME);
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_04); /* End Column */
+	ADD_TOKEN(u8,      cmd, OPAL_TINY_UINT_03); /* Lifecycle Column */
+	ADD_TOKEN(u8,      cmd, OPAL_ENDNAME);
+
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+	ADD_TOKEN(u8,      cmd, OPAL_ENDLIST);
+
+	if (err) {
+		pr_err("Error building Get MSID CPIN PIN command.\n");
+		return err;
+	}
+
+	return finalize_and_send(dev, cmd, get_msid_cpin_pin_cont);
+}
+
+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)
+{
+	int ret = build_end_opal_session(dev);
+
+	if (ret < 0)
+		return ret;
+	return finalize_and_send(dev, &dev->cmd, end_session_cont);
+}
+
+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;
+	return next(dev);
+}
+
+struct opal_dev *alloc_opal_dev(struct request_queue *q)
+{
+	struct opal_dev *opal_dev;
+	unsigned long dma_align;
+	struct opal_cmd *cmd;
+
+	opal_dev = kzalloc(sizeof(*opal_dev), GFP_KERNEL);
+	if (!opal_dev)
+		return opal_dev;
+
+	cmd = &opal_dev->cmd;
+	cmd->cmd = cmd->cmd_buf;
+	cmd->resp = cmd->resp_buf;
+
+	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->unlk_lst);
+
+	opal_dev->state = 0;
+
+	mutex_init(&opal_dev->dev_lock);
+
+	return opal_dev;
+
+}
+EXPORT_SYMBOL(alloc_opal_dev);
+
+static int do_cmds(struct opal_dev *dev)
+{
+	int ret;
+	ret = next(dev);
+	opal_dev_put(dev);
+	return ret;
+}
+
+static struct opal_dev *get_opal_dev(struct sed_context *sedc,
+				     const opal_step *funcs)
+{
+	struct opal_dev *dev = sedc->dev;
+	if (dev) {
+		dev->state = 0;
+		dev->funcs = funcs;
+		dev->TSN = 0;
+		dev->HSN = 0;
+		dev->error_cb = end_opal_session_error;
+		dev->error_cb_data = dev;
+		dev->func_data = NULL;
+		dev->sed_ctx = sedc;
+		opal_dev_get(dev);
+	}
+	return dev;
+}
+
+int opal_secure_erase_locking_range(struct sed_context *sedc, struct sed_key *key)
+{
+	struct opal_dev *dev;
+	void *data[3] = { NULL };
+	const opal_step erase_funcs[] = {
+		opal_discovery0,
+		start_auth_opal_session,
+		get_active_key,
+		gen_key,
+		end_opal_session,
+		NULL,
+	};
+
+	dev = get_opal_dev(sedc, erase_funcs);
+	if (!dev)
+		return -ENODEV;
+
+	dev->func_data = data;
+	dev->func_data[1] = &key->opal_session;
+	dev->func_data[2] = &key->opal_session.opal_key.lr;
+
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_secure_erase_locking_range);
+
+int opal_erase_locking_range(struct sed_context *sedc, struct sed_key *key)
+{
+	struct opal_dev *dev;
+	void *data[3] = { NULL };
+	const opal_step erase_funcs[] = {
+		opal_discovery0,
+		start_auth_opal_session,
+		erase_locking_range,
+		end_opal_session,
+		NULL,
+	};
+
+	dev = get_opal_dev(sedc, erase_funcs);
+	if (!dev)
+		return -ENODEV;
+
+	dev->func_data = data;
+	dev->func_data[1] = &key->opal_session;
+	dev->func_data[2] = &key->opal_session;
+
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_erase_locking_range);
+
+int opal_enable_disable_shadow_mbr(struct sed_context *sedc,
+				   struct sed_key *key)
+{
+	void *func_data[6] = { NULL };
+	struct opal_dev *dev;
+	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,
+	};
+
+	if (key->opal_mbr.enable_disable != OPAL_MBR_ENABLE &&
+	    key->opal_mbr.enable_disable != OPAL_MBR_DISABLE)
+		return -EINVAL;
+
+	dev = get_opal_dev(sedc, mbr_funcs);
+	if (!dev)
+		return -ENODEV;
+
+	dev->func_data = func_data;
+	dev->func_data[1] = &key->opal_mbr.key;
+	dev->func_data[2] = &key->opal_mbr.enable_disable;
+	dev->func_data[4] = &key->opal_mbr.key;
+	dev->func_data[5] = &key->opal_mbr.enable_disable;
+
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_enable_disable_shadow_mbr);
+
+int opal_save(struct sed_context *sedc, struct sed_key *key)
+{
+	struct opal_suspend_data *suspend;
+	struct opal_dev *dev;
+	int ret;
+
+	dev = get_opal_dev(sedc, NULL);
+	if (!dev)
+		return -ENODEV;
+	suspend = kzalloc(sizeof(*suspend), GFP_KERNEL);
+	if(!suspend)
+		return -ENOMEM;
+
+	suspend->unlk = key->opal_lk_unlk;
+	suspend->lr = key->opal_lk_unlk.session.opal_key.lr;
+	ret = add_suspend_info(dev, suspend);
+	opal_dev_put(dev);
+	return ret;
+}
+EXPORT_SYMBOL(opal_save);
+
+int opal_add_user_to_lr(struct sed_context *sedc, struct sed_key *key)
+{
+	void *func_data[3] = { NULL };
+	struct opal_dev *dev;
+		const opal_step funcs[] = {
+		opal_discovery0,
+		start_admin1LSP_opal_session,
+		add_user_to_lr,
+		end_opal_session,
+		NULL
+	};
+
+	if (key->opal_lk_unlk.l_state != OPAL_RO &&
+	    key->opal_lk_unlk.l_state != OPAL_RW) {
+		pr_err("Locking state was not RO or RW\n");
+		return -EINVAL;
+	}
+	if (key->opal_lk_unlk.session.who < OPAL_USER1 &&
+	    key->opal_lk_unlk.session.who > OPAL_USER9) {
+		pr_err("Authority was not within the range of users: %d\n",
+		       key->opal_lk_unlk.session.who);
+		return -EINVAL;
+	}
+	if (key->opal_lk_unlk.session.SUM) {
+		pr_err("%s not supported in SUM. Use setup locking range\n",
+		       __func__);
+		return -EINVAL;
+	}
+
+	dev = get_opal_dev(sedc, funcs);
+	if (!dev)
+		return -ENODEV;
+
+	dev->func_data = func_data;
+	dev->func_data[1] = &key->opal_lk_unlk.session.opal_key;
+	dev->func_data[2] = &key->opal_lk_unlk;
+
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_add_user_to_lr);
+
+int opal_reverttper(struct sed_context *sedc, struct sed_key *key)
+{
+	void *data[2] = { NULL };
+	const opal_step revert_funcs[] = {
+		opal_discovery0,
+		start_SIDASP_opal_session,
+		revert_tper, /* controller will terminate session */
+		NULL,
+	};
+	struct opal_dev *dev = get_opal_dev(sedc, revert_funcs);
+
+	if (!dev)
+		return -ENODEV;
+
+	dev->func_data = data;
+	dev->func_data[1] = &key->opal;
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_reverttper);
+
+/* These are global'd because both lock_unlock_internal
+ * and opal_unlock_from_suspend need them.
+ */
+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 opal_lock_unlock(struct sed_context *sedc, struct sed_key *key)
+{
+	void *func_data[3] = { NULL };
+	struct opal_dev *dev;
+
+	if (key->opal_lk_unlk.session.who < OPAL_ADMIN1 ||
+	    key->opal_lk_unlk.session.who > OPAL_USER9)
+		return -EINVAL;
+
+	dev = get_opal_dev(sedc, NULL);
+	if (!dev)
+		return -ENODEV;
+
+	if (key->opal_lk_unlk.session.SUM)
+		dev->funcs = _unlock_funcs;//ulk_funcs_SUM;
+	else
+		dev->funcs = _unlock_funcs;
+
+	dev->func_data = func_data;
+	dev->func_data[1] = &key->opal_lk_unlk.session;
+	dev->func_data[2] = &key->opal_lk_unlk;
+
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_lock_unlock);
+
+int opal_take_ownership(struct sed_context *sedc, struct sed_key *key)
+{
+	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
+	};
+	void *data[6] = { NULL };
+	struct opal_dev *dev = get_opal_dev(sedc, owner_funcs);
+
+	if (!dev)
+		return -ENODEV;
+
+	dev->func_data = data;
+	dev->func_data[4] = &key->opal;
+	dev->func_data[5] = &key->opal;
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_take_ownership);
+
+int opal_activate_lsp(struct sed_context *sedc, struct sed_key *key)
+{
+	void *data[4] = { NULL };
+	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
+	};
+	struct opal_dev *dev = get_opal_dev(sedc, active_funcs);
+
+	if (!dev)
+		return -ENODEV;
+	dev->func_data = data;
+	dev->func_data[1] = &key->opal;
+	dev->func_data[3] = &key->opal.lr;
+
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_activate_lsp);
+
+int opal_setup_locking_range(struct sed_context *sedc, struct sed_key *pw)
+{
+	void *data[3] = { NULL };
+	const opal_step lr_funcs[] = {
+		opal_discovery0,
+		start_auth_opal_session,
+		setup_locking_range,
+		end_opal_session,
+		NULL,
+	};
+	struct opal_dev *dev = get_opal_dev(sedc, lr_funcs);
+
+	if (!dev)
+		return -ENODEV;
+
+	dev->func_data = data;
+	dev->func_data[1] = &pw->opal_lrs.session;
+	dev->func_data[2] = &pw->opal_lrs;
+
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_setup_locking_range);
+
+int opal_set_new_pw(struct sed_context *sedc, struct sed_key *pw)
+{
+	const opal_step pw_funcs[] = {
+		opal_discovery0,
+		start_auth_opal_session,
+		set_new_pw,
+		end_opal_session,
+		NULL
+	};
+	struct opal_dev *dev;
+	void *data[3] = { NULL };
+
+	if (pw->sed_type != OPAL_PW)
+		return -EINVAL;
+
+	if (pw->opal_pw.session.who < OPAL_ADMIN1 ||
+	    pw->opal_pw.session.who > OPAL_USER9  ||
+	    pw->opal_pw.new_user_pw.who < OPAL_ADMIN1 ||
+	    pw->opal_pw.new_user_pw.who > OPAL_USER9)
+		return -EINVAL;
+
+	dev = get_opal_dev(sedc, pw_funcs);
+	if (!dev)
+		return -ENODEV;
+
+	dev->func_data = data;
+	dev->func_data[1] = (void *) &pw->opal_pw.session;
+	dev->func_data[2] = (void *) &pw->opal_pw.new_user_pw;
+
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_set_new_pw);
+
+int opal_activate_user(struct sed_context *sedc, struct sed_key *pw)
+{
+	const opal_step act_funcs[] = {
+		opal_discovery0,
+		start_admin1LSP_opal_session,
+		internal_activate_user,
+		end_opal_session,
+		NULL
+	};
+	struct opal_dev *dev;
+	void *data[3] = { NULL };
+
+	if (pw->sed_type != OPAL_ACT_USR) {
+		pr_err("Sed type was not act user\n");
+		return -EINVAL;
+	}
+
+	/* We can't activate Admin1 it's active as manufactured */
+	if (pw->opal_session.who < OPAL_USER1 &&
+	    pw->opal_session.who > OPAL_USER9) {
+		pr_err("Who was not a valid user: %d \n", pw->opal_session.who);
+		return -EINVAL;
+	}
+
+	dev = get_opal_dev(sedc, act_funcs);
+	if (!dev)
+		return -ENODEV;
+
+	dev->func_data = data;
+	dev->func_data[1] = &pw->opal_session.opal_key;
+	dev->func_data[2] = &pw->opal_session;
+
+	return do_cmds(dev);
+}
+EXPORT_SYMBOL(opal_activate_user);
+
+int opal_unlock_from_suspend(struct sed_context *sedc)
+{
+	struct opal_suspend_data *suspend;
+	void *func_data[3] = { NULL };
+	bool was_failure = false;
+	struct opal_dev *dev = get_opal_dev(sedc, NULL);
+	int ret = 0;
+
+	if (!dev)
+		return 0;
+
+	dev->func_data = func_data;
+	dev->error_cb = end_opal_session_error;
+	dev->error_cb_data = dev;
+
+	if (!list_empty(&dev->unlk_lst)) {
+		list_for_each_entry(suspend, &dev->unlk_lst, node) {
+			dev->state = 0;
+			dev->func_data[1] = &suspend->unlk.session;
+			dev->func_data[2] = &suspend->unlk;
+			if (suspend->unlk.session.SUM)
+				dev->funcs = ulk_funcs_SUM;
+			else
+				dev->funcs = _unlock_funcs;
+			dev->TSN = 0;
+			dev->HSN = 0;
+			ret = next(dev);
+			if (ret)
+				was_failure = true;
+		}
+	}
+	opal_dev_put(dev);
+	return was_failure ? 1 : 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..12369eb
--- /dev/null
+++ b/lib/sed-opal_internal.h
@@ -0,0 +1,601 @@
+/*
+ * Copyright ? 2016 Intel Corporation
+ *
+ * Authors:
+ *    Rafael Antognolli <rafael.antognolli at intel.com>
+ *    Scott  Bauer      <scott.bauer at intel.com>
+ *
+ * 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 _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
+#define GENERIC_HOST_SESSION_NUM 0x41
+
+
+
+/*
+ * Derived from:
+ * TCG_Storage_Architecture_Core_Spec_v2.01_r1.00
+ * Section: 5.1.5 Method Status Codes
+ */
+static const char *opal_errors[] = {
+	"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 >= ARRAY_SIZE(opal_errors) || error < 0)
+		return "Unknown Error";
+
+	return opal_errors[error];
+}
+
+/*
+ * User IDs used in the TCG storage SSCs
+ * Derived from: TCG_Storage_Architecture_Core_Spec_v2.01_r1.00
+ * Section: 6.3 Assigned UIDs
+ */
+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.
+ * Derived from: TCG_Storage_Architecture_Core_Spec_v2.01_r1.00
+ * Section: 6.3 Assigned UIDs
+ */
+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,
+};
+
+/*
+ * Token defs derived from:
+ * TCG_Storage_Architecture_Core_Spec_v2.01_r1.00
+ * 3.2.2 Data Stream Encoding
+ */
+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
+ */
+
+/* Packets derived from:
+ * TCG_Storage_Architecture_Core_Spec_v2.01_r1.00
+ * Secion: 3.2.3 ComPackets, Packets & Subpackets
+ */
+
+/* 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
+ * Section: 3.3.5 Capability Discovery
+ */
+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.c b/lib/sed.c
new file mode 100644
index 0000000..3b43a52
--- /dev/null
+++ b/lib/sed.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright ? 2016 Intel Corporation
+ *
+ * Authors:
+ *    Rafael Antognolli <rafael.antognolli at intel.com>
+ *    Scott  Bauer      <scott.bauer at intel.com>
+ *
+ * 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.
+ */
+
+#include <linux/blkdev.h>
+#include <linux/sed.h>
+#include <linux/sed-opal.h>
+#include <asm/uaccess.h>
+
+int sed_save(struct sed_context *sed_ctx, struct sed_key *key)
+{
+	switch (key->sed_type) {
+	case OPAL_LOCK_UNLOCK:
+		return opal_save(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_lock_unlock(struct sed_context *sed_ctx, struct sed_key *key)
+{
+	switch (key->sed_type) {
+	case OPAL_LOCK_UNLOCK:
+		return opal_lock_unlock(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_take_ownership(struct sed_context *sed_ctx, struct sed_key *key)
+{
+
+	switch (key->sed_type) {
+	case OPAL:
+		return opal_take_ownership(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_activate_lsp(struct sed_context *sed_ctx, struct sed_key *key)
+{
+
+	switch (key->sed_type) {
+	case OPAL:
+		return opal_activate_lsp(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_set_pw(struct sed_context *sed_ctx, struct sed_key *key)
+{
+
+	switch (key->sed_type) {
+	case OPAL_PW:
+		return opal_set_new_pw(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_activate_user(struct sed_context *sed_ctx, struct sed_key *key)
+{
+
+	switch (key->sed_type) {
+	case OPAL_ACT_USR:
+		return opal_activate_user(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_reverttper(struct sed_context *sed_ctx, struct sed_key *key)
+{
+
+	switch (key->sed_type) {
+	case OPAL:
+		return opal_reverttper(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_setup_locking_range(struct sed_context *sed_ctx, struct sed_key *key)
+{
+
+	switch (key->sed_type) {
+	case OPAL_LR_SETUP:
+		return opal_setup_locking_range(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_adduser_to_lr(struct sed_context *sed_ctx, struct sed_key *key)
+{
+
+	switch (key->sed_type) {
+	case OPAL_LOCK_UNLOCK:
+		return opal_add_user_to_lr(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_do_mbr(struct sed_context *sed_ctx, struct sed_key *key)
+{
+
+	switch (key->sed_type) {
+	case OPAL_MBR_DATA:
+		return opal_enable_disable_shadow_mbr(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_erase_lr(struct sed_context *sed_ctx, struct sed_key *key)
+{
+
+	switch (key->sed_type) {
+	case OPAL:
+		return opal_erase_locking_range(sed_ctx, key);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+int sed_secure_erase_lr(struct sed_context *sed_ctx, struct sed_key *key)
+{
+	switch (key->sed_type) {
+	case OPAL_ACT_USR:
+		return opal_secure_erase_locking_range(sed_ctx, key);
+
+	}
+	return -EOPNOTSUPP;
+}
+
+int fdev_sed_ioctl(struct file *filep, unsigned int cmd,
+		   unsigned long arg)
+{
+	struct sed_key key;
+	struct sed_context *sed_ctx;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	if (!filep->f_sedctx || !filep->f_sedctx->ops || !filep->f_sedctx->dev)
+		return -ENODEV;
+
+	sed_ctx = filep->f_sedctx;
+
+	if (copy_from_user(&key, (void __user *)arg, sizeof(key)))
+		return -EFAULT;
+
+	switch (cmd) {
+	case IOC_SED_SAVE:
+		return sed_save(sed_ctx, &key);
+	case IOC_SED_LOCK_UNLOCK:
+		return sed_lock_unlock(sed_ctx, &key);
+	case IOC_SED_TAKE_OWNERSHIP:
+		return sed_take_ownership(sed_ctx, &key);
+	case IOC_SED_ACTIVATE_LSP:
+		return sed_activate_lsp(sed_ctx, &key);
+	case IOC_SED_SET_PW:
+		return sed_set_pw(sed_ctx, &key);
+	case IOC_SED_ACTIVATE_USR:
+		return sed_activate_user(sed_ctx, &key);
+	case IOC_SED_REVERT_TPR:
+		return sed_reverttper(sed_ctx, &key);
+	case IOC_SED_LR_SETUP:
+		return sed_setup_locking_range(sed_ctx, &key);
+	case IOC_SED_ADD_USR_TO_LR:
+		return sed_adduser_to_lr(sed_ctx, &key);
+	case IOC_SED_ENABLE_DISABLE_MBR:
+		return sed_do_mbr(sed_ctx, &key);
+	case IOC_SED_ERASE_LR:
+		return sed_erase_lr(sed_ctx, &key);
+	case IOC_SED_SECURE_ERASE_LR:
+		return sed_secure_erase_lr(sed_ctx, &key);
+	}
+	return -ENOTTY;
+}
+EXPORT_SYMBOL_GPL(fdev_sed_ioctl);
-- 
2.7.4

  parent reply	other threads:[~2016-12-19 19:43 UTC|newest]

Thread overview: 70+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-12-19 19:35 [PATCH v3 0/5] SED OPAL Library Scott Bauer
2016-12-19 19:35 ` Scott Bauer
2016-12-19 19:35 ` [PATCH v3 1/5] include: Add definitions for sed Scott Bauer
2016-12-19 19:35   ` Scott Bauer
2016-12-20  6:46   ` Christoph Hellwig
2016-12-20  6:46     ` Christoph Hellwig
2016-12-25 14:15   ` Jethro Beekman
2016-12-25 14:15     ` Jethro Beekman
2016-12-27 22:14     ` Scott Bauer
2016-12-27 22:14       ` Scott Bauer
2016-12-19 19:35 ` Scott Bauer [this message]
2016-12-19 19:35   ` [PATCH v3 2/5] lib: Add Sed-opal library Scott Bauer
2016-12-19 21:34   ` Keith Busch
2016-12-19 21:34     ` Keith Busch
2016-12-20  6:07     ` Christoph Hellwig
2016-12-20  6:07       ` Christoph Hellwig
2016-12-20  3:21   ` kbuild test robot
2016-12-20  3:21     ` kbuild test robot
2016-12-20  3:48   ` kbuild test robot
2016-12-20  3:48     ` kbuild test robot
2016-12-20  6:50   ` Al Viro
2016-12-20  6:50     ` Al Viro
2016-12-20  7:28   ` Christoph Hellwig
2016-12-20  7:28     ` Christoph Hellwig
2016-12-20 21:55     ` Scott Bauer
2016-12-20 21:55       ` Scott Bauer
2016-12-21  9:42       ` Christoph Hellwig
2016-12-21  9:42         ` Christoph Hellwig
2016-12-20 22:07     ` Jon Derrick
2016-12-20 22:07       ` Jon Derrick
2016-12-21  9:47       ` Christoph Hellwig
2016-12-21  9:47         ` Christoph Hellwig
2016-12-19 19:35 ` [PATCH v3 3/5] fs: Wire up SED/Opal to ioctl Scott Bauer
2016-12-19 19:35   ` Scott Bauer
2016-12-20  6:21   ` Christoph Hellwig
2016-12-20  6:21     ` Christoph Hellwig
2016-12-19 19:35 ` [PATCH v3 4/5] nvme: Implement resume_from_suspend and SED Allocation code Scott Bauer
2016-12-19 19:35   ` Scott Bauer
2016-12-19 21:59   ` Keith Busch
2016-12-19 21:59     ` Keith Busch
2016-12-19 22:23     ` Scott Bauer
2016-12-19 22:23       ` Scott Bauer
2016-12-20  6:17       ` Christoph Hellwig
2016-12-20  6:17         ` Christoph Hellwig
2016-12-20 15:49         ` Keith Busch
2016-12-20 15:49           ` Keith Busch
2016-12-20 15:46           ` Christoph Hellwig
2016-12-20 15:46             ` Christoph Hellwig
2016-12-20 16:05             ` Scott Bauer
2016-12-20 16:05               ` Scott Bauer
2016-12-21  9:01               ` Christoph Hellwig
2016-12-21  9:01                 ` Christoph Hellwig
2016-12-20 17:52             ` Scott Bauer
2016-12-20 17:52               ` Scott Bauer
2016-12-21  9:37               ` Christoph Hellwig
2016-12-21  9:37                 ` Christoph Hellwig
2016-12-20  4:11   ` kbuild test robot
2016-12-20  4:11     ` kbuild test robot
2016-12-20  6:21   ` Christoph Hellwig
2016-12-20  6:21     ` Christoph Hellwig
2016-12-20  6:49   ` Christoph Hellwig
2016-12-20  6:49     ` Christoph Hellwig
2016-12-25 14:15   ` Jethro Beekman
2016-12-25 14:15     ` Jethro Beekman
2016-12-27 22:12     ` Scott Bauer
2016-12-27 22:12       ` Scott Bauer
2016-12-28  8:39       ` Christoph Hellwig
2016-12-28  8:39         ` Christoph Hellwig
2016-12-19 19:35 ` [PATCH v3 5/5] Maintainers: Add Information for SED Opal library Scott Bauer
2016-12-19 19:35   ` Scott Bauer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1482176149-2257-3-git-send-email-scott.bauer@intel.com \
    --to=scott.bauer@intel.com \
    --cc=Rafael.Antognolli@intel.com \
    --cc=axboe@fb.com \
    --cc=hch@infradead.org \
    --cc=jonathan.derrick@intel.com \
    --cc=keith.busch@intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-nvme@lists.infradead.org \
    --cc=sagi@grimberg.me \
    --cc=viro@zeniv.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.