diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index 8f9e58317048..f6577c9e3138 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -35,3 +35,5 @@ obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/
 obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o
 obj-$(CONFIG_TCG_CRB) += tpm_crb.o
 obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o
+
+obj-m +=  tpm2-sessions-test.o
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 4838c6a9f0f2..5dda37047a71 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -252,6 +252,7 @@ struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip)
 		return NULL;
 	return chip;
 }
+EXPORT_SYMBOL(tpm_find_get_ops);
 
 /**
  * tpm_dev_release() - free chip memory and the device number
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index c88eee6376e4..06f63874e833 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -117,6 +117,7 @@ enum tpm2_command_codes {
 	TPM2_CC_CONTEXT_LOAD	        = 0x0161,
 	TPM2_CC_CONTEXT_SAVE	        = 0x0162,
 	TPM2_CC_FLUSH_CONTEXT	        = 0x0165,
+	TPM2_CC_POLICY_COMMAND_CODE	= 0x16c,
 	TPM2_CC_READ_PUBLIC		= 0x0173,
 	TPM2_CC_START_AUTH_SESS		= 0x0176,
 	TPM2_CC_VERIFY_SIGNATURE        = 0x0177,
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 2c798f18282f..d4bbe19170d1 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -222,6 +222,7 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
 	tpm_buf_destroy(&buf);
 	return rc;
 }
+EXPORT_SYMBOL_GPL(tpm2_pcr_read);
 
 /**
  * tpm2_pcr_extend() - extend a PCR value
@@ -269,6 +270,7 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
 
 	return rc;
 }
+EXPORT_SYMBOL_GPL(tpm2_pcr_extend);
 
 struct tpm2_get_random_out {
 	__be16 size;
@@ -353,6 +355,7 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 	tpm_buf_destroy(&buf);
 	return err;
 }
+EXPORT_SYMBOL_GPL(tpm2_get_random);
 
 /**
  * tpm2_flush_context() - execute a TPM2_FlushContext command
@@ -376,6 +379,7 @@ void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
 	tpm_transmit_cmd(chip, &buf, 0, "flushing context");
 	tpm_buf_destroy(&buf);
 }
+EXPORT_SYMBOL_GPL(tpm2_flush_context);
 
 /**
  * tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
@@ -406,6 +410,7 @@ void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle,
 	if (hmac && hmac_len)
 		tpm_buf_append(buf, hmac, hmac_len);
 }
+EXPORT_SYMBOL_GPL(tpm2_buf_append_auth);
 
 /**
  * tpm2_seal_trusted() - seal the payload of a trusted key
@@ -532,6 +537,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 
 	return rc;
 }
+EXPORT_SYMBOL_GPL(tpm2_seal_trusted);
 
 /**
  * tpm2_load_cmd() - execute a TPM2_Load command
@@ -715,6 +721,7 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
 	tpm2_flush_context(chip, blob_handle);
 	return rc;
 }
+EXPORT_SYMBOL_GPL(tpm2_unseal_trusted);
 
 struct tpm2_get_cap_out {
 	u8 more_data;
diff --git a/drivers/char/tpm/tpm2-sessions-test.c b/drivers/char/tpm/tpm2-sessions-test.c
new file mode 100644
index 000000000000..834811ab3466
--- /dev/null
+++ b/drivers/char/tpm/tpm2-sessions-test.c
@@ -0,0 +1,795 @@
+/* run a set of tests of the sessions code */
+#include "tpm.h"
+#include "tpm2-sessions.h"
+
+#include <linux/random.h>
+
+#include <asm/unaligned.h>
+
+#include <crypto/aes.h>
+#include <crypto/hash.h>
+
+#include <keys/trusted-type.h>
+
+/* simple policy: command code must be TPM2_CC_UNSEAL */
+static u8 policy[] = {
+	0xe6, 0x13, 0x13, 0x70, 0x76, 0x52, 0x4b, 0xde,
+	0x48, 0x75, 0x33, 0x86, 0x58, 0x84, 0xe9, 0x73,
+	0x2e, 0xbe, 0xe3, 0xaa, 0xcb, 0x09, 0x5d, 0x94,
+	0xa6, 0xde, 0x49, 0x2e, 0xc0, 0x6c, 0x46, 0xfa,
+};
+
+static struct crypto_shash *sha256_hash;
+
+static int parse_create_primary(struct tpm_chip *chip, u8 *data, u32 *nullkey)
+{
+	struct tpm_header *head = (struct tpm_header *)data;
+	u16 len;
+	u32 tot_len = be32_to_cpu(head->length);
+	u32 val, parm_len;
+	const u8 *resp, *tmp;
+	SHASH_DESC_ON_STACK(desc, sha256_hash);
+
+	data += TPM_HEADER_SIZE;
+	/* we're starting after the header so adjust the length */
+	tot_len -= TPM_HEADER_SIZE;
+
+	resp = data;
+	*nullkey = tpm_get_inc_u32(&resp);
+	parm_len = tpm_get_inc_u32(&resp);
+	if (parm_len + 8 > tot_len)
+		return -EINVAL;
+	len = tpm_get_inc_u16(&resp);
+	tmp = resp;
+	/* now we have the public area, compute the name of the object */
+	desc->tfm = sha256_hash;
+	put_unaligned_be16(TPM_ALG_SHA256, chip->tpmkeyname);
+	crypto_shash_init(desc);
+	crypto_shash_update(desc, resp, len);
+	crypto_shash_final(desc, chip->tpmkeyname + 2);
+	/* validate the public key */
+	val = tpm_get_inc_u16(&tmp);
+	/* key type (must be what we asked for) */
+	if (val != TPM_ALG_ECC)
+		return -EINVAL;
+	val = tpm_get_inc_u16(&tmp);
+	/* name algorithm */
+	if (val != TPM_ALG_SHA256)
+		return -EINVAL;
+	val = tpm_get_inc_u32(&tmp);
+	/* object properties */
+	if (val != (TPM2_OA_NO_DA |
+		    TPM2_OA_FIXED_TPM |
+		    TPM2_OA_FIXED_PARENT |
+		    TPM2_OA_SENSITIVE_DATA_ORIGIN |
+		    TPM2_OA_USER_WITH_AUTH |
+		    TPM2_OA_DECRYPT |
+		    TPM2_OA_RESTRICTED))
+		return -EINVAL;
+	/* auth policy (empty) */
+	val = tpm_get_inc_u16(&tmp);
+	if (val != 0)
+		return -EINVAL;
+	val = tpm_get_inc_u16(&tmp);
+	/* symmetric key parameters */
+	if (val != TPM_ALG_AES)
+		return -EINVAL;
+	val = tpm_get_inc_u16(&tmp);
+	/* symmetric key length */
+	if (val != AES_KEYSIZE_128 * 8)
+		return -EINVAL;
+	val = tpm_get_inc_u16(&tmp);
+	/* symmetric encryption scheme */
+	if (val != TPM_ALG_CFB)
+		return -EINVAL;
+	val = tpm_get_inc_u16(&tmp);
+	/* signing scheme */
+	if (val != TPM_ALG_NULL)
+		return -EINVAL;
+	val = tpm_get_inc_u16(&tmp);
+	/* ECC Curve */
+	if (val != TPM2_ECC_NIST_P256)
+		return -EINVAL;
+	val = tpm_get_inc_u16(&tmp);
+	/* KDF Scheme */
+	if (val != TPM_ALG_NULL)
+		return -EINVAL;
+	val = tpm_get_inc_u16(&tmp);
+	/* x point */
+	if (val != 32)
+		return -EINVAL;
+	memcpy(chip->ec_point_x, tmp, val);
+	tmp += val;
+	val = tpm_get_inc_u16(&tmp);
+	if (val != 32)
+		return -EINVAL;
+	memcpy(chip->ec_point_y, tmp, val);
+	tmp += val;
+	resp += len;
+	/* should have exactly consumed the tpm2b public structure */
+	if (tmp != resp)
+		return -EINVAL;
+	if (resp - data > parm_len)
+		return -EINVAL;
+	/* creation data (skip) */
+	len = tpm_get_inc_u16(&resp);
+	resp += len;
+	if (resp - data > parm_len)
+		return -EINVAL;
+	/* creation digest (must be sha256) */
+	len = tpm_get_inc_u16(&resp);
+	resp += len;
+	if (len != SHA256_DIGEST_SIZE || resp - data > parm_len)
+		return -EINVAL;
+	/* TPMT_TK_CREATION follows */
+	/* tag, must be TPM_ST_CREATION (0x8021) */
+	val = tpm_get_inc_u16(&resp);
+	if (val != TPM2_ST_CREATION || resp - data > parm_len)
+		return -EINVAL;
+	/* hierarchy (must be NULL) */
+	val = tpm_get_inc_u32(&resp);
+	if (val != TPM2_RH_NULL || resp - data > parm_len)
+		return -EINVAL;
+	/* the ticket digest HMAC (might not be sha256) */
+	len = tpm_get_inc_u16(&resp);
+	resp += len;
+	if (resp - data > parm_len)
+		return -EINVAL;
+	/*
+	 * finally we have the name, which is a sha256 digest plus a 2
+	 * byte algorithm type
+	 */
+	len = tpm_get_inc_u16(&resp);
+	if (resp + len - data != parm_len + 8)
+		return -EINVAL;
+	if (len != SHA256_DIGEST_SIZE + 2)
+		return -EINVAL;
+
+	if (memcmp(chip->tpmkeyname, resp, SHA256_DIGEST_SIZE + 2) != 0) {
+		printk("TPM NULL Seed name comparison failed\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy, u32 *handle)
+{
+	int rc;
+	struct tpm_buf buf;
+	struct tpm_buf template;
+
+	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE_PRIMARY);
+	if (rc)
+		return rc;
+
+	rc = tpm_buf_init_2b(&template);
+	if (rc) {
+		tpm_buf_destroy(&buf);
+		return rc;
+	}
+
+	/*
+	 * create the template.  Note: in order for userspace to
+	 * verify the security of the system, it will have to create
+	 * and certify this NULL primary, meaning all the template
+	 * parameters will have to be identical, so conform exactly to
+	 * the TCG TPM v2.0 Provisioning Guidance for the SRK ECC
+	 * key
+	 */
+
+	/* key type */
+	tpm_buf_append_u16(&template, TPM_ALG_ECC);
+	/* name algorithm */
+	tpm_buf_append_u16(&template, TPM_ALG_SHA256);
+	/* object properties */
+	tpm_buf_append_u32(&template, TPM2_OA_NO_DA |
+			   TPM2_OA_FIXED_TPM |
+			   TPM2_OA_FIXED_PARENT |
+			   TPM2_OA_SENSITIVE_DATA_ORIGIN |
+			   TPM2_OA_USER_WITH_AUTH |
+			   TPM2_OA_DECRYPT |
+			   TPM2_OA_RESTRICTED);
+	/* sauth policy (empty) */
+	tpm_buf_append_u16(&template, 0);
+
+	/* BEGIN parameters: key specific; for ECC*/
+	/* symmetric algorithm */
+	tpm_buf_append_u16(&template, TPM_ALG_AES);
+	/* bits for symmetric algorithm */
+	tpm_buf_append_u16(&template, 128);
+	/* algorithm mode (must be CFB) */
+	tpm_buf_append_u16(&template, TPM_ALG_CFB);
+	/* scheme (NULL means any scheme) */
+	tpm_buf_append_u16(&template, TPM_ALG_NULL);
+	/* ECC Curve ID */
+	tpm_buf_append_u16(&template, TPM2_ECC_NIST_P256);
+	/* KDF Scheme */
+	tpm_buf_append_u16(&template, TPM_ALG_NULL);
+	/* unique: key specific; for ECC it is two points */
+	tpm_buf_append_u16(&template, 0);
+	tpm_buf_append_u16(&template, 0);
+	/* END parameters */
+
+	/* primary handle */
+	tpm_buf_append_u32(&buf, hierarchy);
+	/* simple authorization for empty auth */
+	tpm2_buf_append_auth(&buf, TPM2_RS_PW, NULL, 0, 0, NULL, 0);
+	/* sensitive create size is 4 for two empty buffers */
+	tpm_buf_append_u16(&buf, 4);
+	/* sensitive create auth data (empty) */
+	tpm_buf_append_u16(&buf, 0);
+	/* sensitive create sensitive data (empty) */
+	tpm_buf_append_u16(&buf, 0);
+	/* the public template */
+	tpm_buf_append_2b(&buf, &template);
+	tpm_buf_destroy(&template);
+	/* outside info (empty) */
+	tpm_buf_append_u16(&buf, 0);
+	/* creation PCR (none) */
+	tpm_buf_append_u32(&buf, 0);
+
+	rc = tpm_transmit_cmd(chip, &buf, 0,
+			      "attempting to create NULL primary");
+
+	if (rc == TPM2_RC_SUCCESS)
+		rc = parse_create_primary(chip, buf.data, handle);
+
+	tpm_buf_destroy(&buf);
+
+	return rc;
+}
+
+static u32 get_policy(struct tpm_chip *chip)
+{
+	struct tpm_buf buf;
+	u8 nonce[SHA256_DIGEST_SIZE];
+	u32 h;
+	int rc;
+
+	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
+	if (rc)
+		return 0;
+
+	/* salt key */
+	tpm_buf_append_u32(&buf, TPM2_RH_NULL);
+	/* bind key */
+	tpm_buf_append_u32(&buf, TPM2_RH_NULL);
+	/* zero nonce */
+	memset(nonce, 0, sizeof(nonce));
+	tpm_buf_append_u16(&buf, sizeof(nonce));
+	tpm_buf_append(&buf, nonce, sizeof(nonce));
+	/* encrypted salt (empty) */
+	tpm_buf_append_u16(&buf, 0);
+	/* session type (HMAC, audit or policy) */
+	tpm_buf_append_u8(&buf, TPM2_SE_POLICY);
+	/* symmetric encryption parameters */
+	/* symmetric algorithm */
+	tpm_buf_append_u16(&buf, TPM_ALG_NULL);
+
+	/* hash algorithm for session */
+	tpm_buf_append_u16(&buf, TPM_ALG_SHA256);
+
+	rc = tpm_transmit_cmd(chip, &buf, 0, "start policy session");
+
+	h = get_unaligned_be32(&buf.data[TPM_HEADER_SIZE]);
+
+	tpm_buf_reset(&buf, TPM2_ST_NO_SESSIONS,
+		      TPM2_CC_POLICY_COMMAND_CODE);
+	tpm_buf_append_u32(&buf, h);
+	tpm_buf_append_u32(&buf, TPM2_CC_UNSEAL);
+
+	rc = tpm_transmit_cmd(chip, &buf, 0, "start policy session");
+
+	tpm_buf_destroy(&buf);
+
+	dev_info(&chip->dev, "got policy session %08x\n", h);
+
+	return h;
+}
+
+int tpm2_sessions_test(void)
+{
+	struct tpm2_auth *auth;
+	struct tpm_buf buf, b1;
+	struct tpm_buf t2b;
+	struct tpm_chip *chip;
+	int rc;
+	char payload[MIN_KEY_SIZE+3];
+	char *password = "Passw0Rd";
+	const u8 *p;
+	u32 h, ps;
+	u8 name[34];
+	u16 len;
+	int ret = -EINVAL;
+	u32 nullkey = 0;
+	struct trusted_key_payload tkp;
+	struct trusted_key_options tko;
+	struct tpm_digest digest, extend[3];
+	u16 digest_len;
+	int i;
+	SHASH_DESC_ON_STACK(desc, sha256_hash);
+
+	chip = tpm_find_get_ops(NULL);
+	if (!chip)
+		return -ENODEV;
+
+	if (!(chip->flags & TPM_CHIP_FLAG_TPM2))
+		return -ENODEV;
+
+	sha256_hash = crypto_alloc_shash("sha256", 0, 0);
+
+	/* precursor: get a session */
+	rc = tpm2_start_auth_session(chip, &auth);
+
+	dev_info(&chip->dev, "TPM: start auth session returned %d\n", rc);
+
+	if (rc)
+		return -ENODEV;
+
+	/* first test: get random bytes from TPM */
+	dev_info(&chip->dev, "test 1: TPM random number generator\n");
+	rc = tpm2_get_random(chip, payload, sizeof(payload));
+	if (rc != sizeof(payload)) {
+		dev_err(&chip->dev, "tpm2_get_random failed: %d\n", rc);
+		goto out;
+	}
+	dev_info(&chip->dev, "test 1: PASS\n");
+
+	/*
+	 * second test, seal random data protecting sensitive by
+	 * encryption and also doing response encryption (not
+	 * necessary) The encrypted payload has two components: an
+	 * authorization password which must be presented on useal and
+	 * the actual data (the random payload)
+	 */
+	dev_info(&chip->dev, "test2: seal data and check tpm unsealing functions\n");
+	rc = tpm2_create_primary(chip, TPM2_RH_NULL, &nullkey);
+
+	if (rc != TPM2_RC_SUCCESS) {
+		dev_err(&chip->dev, "tpm2_create_primary failed: %d\n", rc);
+		goto out;
+	}
+
+	dev_info(&chip->dev, "NULL KEY at %08x\n", nullkey);
+
+	tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
+	tpm_buf_append_name(&buf, auth, nullkey, chip->tpmkeyname);
+	/* close the session here, get a new one for next seal op */
+	tpm_buf_append_hmac_session(&buf, auth, TPM2_SA_DECRYPT
+				    | TPM2_SA_ENCRYPT, NULL, 0);
+	/* sensitive */
+	tpm_buf_init_2b(&t2b);
+	/* the authorization */
+	tpm_buf_append_u16(&t2b, strlen(password));
+	tpm_buf_append(&t2b, password, strlen(password));
+	/* the payload */
+	tpm_buf_append_u16(&t2b, sizeof(payload) + 1);
+	tpm_buf_append(&t2b, payload, sizeof(payload));
+	/* the migrateable flag used by the TPM sealing functions */
+	tpm_buf_append_u8(&t2b, 0);
+	tpm_buf_append_2b(&buf, &t2b);
+	/* the public */
+	/* type */
+	tpm_buf_append_u16(&t2b, TPM_ALG_KEYEDHASH);
+	/* name hash */
+	tpm_buf_append_u16(&t2b, TPM_ALG_SHA256);
+	/* object properties */
+	tpm_buf_append_u32(&t2b, TPM2_OA_USER_WITH_AUTH | TPM2_OA_NO_DA);
+	/* auth policy (empty) */
+	tpm_buf_append_u16(&t2b, 0);
+	/* keyed hash parameters (we're null for a non-HMAC data blob) */
+	tpm_buf_append_u16(&t2b, TPM_ALG_NULL);
+	/* unique */
+	tpm_buf_append_u16(&t2b, 0);
+	tpm_buf_append_2b(&buf, &t2b);
+	/* outside info (also empty) */
+	tpm_buf_append_u16(&buf, 0);
+	/* creation PCR (empty) */
+	tpm_buf_append_u32(&buf, 0);
+	tpm_buf_fill_hmac_session(&buf, auth);
+	rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data");
+	rc = tpm_buf_check_hmac_response(&buf, auth, rc);
+	dev_info(&chip->dev, "TPM: sealing response returned %d\n", rc);
+	if (rc)
+		goto out;
+
+	/*
+	 * now load the sealed object (we need the pub and priv parts
+	 * returned from prior command
+	 */
+	memset(&tkp, 0, sizeof(tkp));
+	memset(&tko, 0, sizeof(tko));
+
+	p = &buf.data[TPM_HEADER_SIZE];
+
+	tkp.blob_len = tpm_get_inc_u32(&p);
+	memcpy(tkp.blob, p, tkp.blob_len);
+	tko.keyhandle = nullkey;
+	strcpy(tko.blobauth, password);
+
+	rc = tpm2_unseal_trusted(chip, &tkp, &tko);
+	if (rc) {
+		dev_err(&chip->dev, "tpm2_unseal_trusted[password] returned %d\n", rc);
+		goto out;
+	}
+
+	if (tkp.key_len != sizeof(payload)) {
+		dev_err(&chip->dev, "tpm2_unseal_trusted[password] wrong unseal payload size %d != %ld",
+			tkp.key_len, sizeof(payload));
+		goto out;
+	}
+	if (memcmp(payload, tkp.key, tkp.key_len) != 0) {
+		dev_err(&chip->dev, "tpm2_unseal_trusted[password] Payload DID NOT compare correctly\n");
+		goto out;
+	}
+	dev_info(&chip->dev, "tpm2_unseal_trusted[password] returned correct data\n"
+		 "test2: PASS\n");
+
+	/*
+	 * third test, seal random data protecting sensitive by
+	 * encryption and also doing response encryption (not
+	 * necessary) The encrypted payload has two components: an
+	 * authorization password which must be presented on useal and
+	 * the actual data (the random payload)
+	 *
+	 * For this test use a policy to release the data testing that
+	 * we can have two sessions
+	 */
+
+	dev_info(&chip->dev, "test 3: seal to policy and test unseal\n");
+	rc = tpm2_get_random(chip, payload, sizeof(payload));
+	if (rc != sizeof(payload)) {
+		dev_err(&chip->dev, "tpm2_get_random failed: %d\n", rc);
+		goto out;
+	}
+
+	/* auth session should be closed, so get a new one */
+	rc = tpm2_start_auth_session(chip, &auth);
+	if (rc) {
+		dev_err(&chip->dev, "new auth session failed: %d\n", rc);
+		goto out;
+	}
+
+	tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
+	tpm_buf_append_name(&buf, auth, nullkey, chip->tpmkeyname);
+	tpm_buf_append_hmac_session(&buf, auth, TPM2_SA_DECRYPT
+				    | TPM2_SA_ENCRYPT
+				    | TPM2_SA_CONTINUE_SESSION, NULL, 0);
+	/* sensitive */
+	tpm_buf_init_2b(&t2b);
+	/* the authorization */
+	tpm_buf_append_u16(&t2b, 0);
+	/* the payload */
+	tpm_buf_append_u16(&t2b, sizeof(payload) + 1);
+	tpm_buf_append(&t2b, payload, sizeof(payload));
+	/* extra byte for migratability flag */
+	tpm_buf_append_u8(&t2b, 0);
+	tpm_buf_append_2b(&buf, &t2b);
+	/* the public */
+	/* type */
+	tpm_buf_append_u16(&t2b, TPM_ALG_KEYEDHASH);
+	/* name hash */
+	tpm_buf_append_u16(&t2b, TPM_ALG_SHA256);
+	/* object properties */
+	tpm_buf_append_u32(&t2b, TPM2_OA_NO_DA);
+	/* auth policy (specific to command code) */
+	tpm_buf_append_u16(&t2b, sizeof(policy));
+	tpm_buf_append(&t2b, policy, sizeof(policy));
+	/* keyed hash parameters (we're null for a non-HMAC data blob) */
+	tpm_buf_append_u16(&t2b, TPM_ALG_NULL);
+	/* unique */
+	tpm_buf_append_u16(&t2b, 0);
+	tpm_buf_append_2b(&buf, &t2b);
+	/* outside info (also empty) */
+	tpm_buf_append_u16(&buf, 0);
+	/* creation PCR (empty) */
+	tpm_buf_append_u32(&buf, 0);
+	tpm_buf_fill_hmac_session(&buf, auth);
+	rc = tpm_transmit_cmd(chip, &buf, 4, "policy sealing data");
+	rc = tpm_buf_check_hmac_response(&buf, auth, rc);
+	dev_info(&chip->dev, "policy sealing response returned %d\n", rc);
+	if (rc)
+		goto out;
+
+	/*
+	 * now load the sealed object (we need the pub and priv parts
+	 * returned from prior command
+	 */
+	memset(&tkp, 0, sizeof(tkp));
+	memset(&tko, 0, sizeof(tko));
+
+	p = &buf.data[TPM_HEADER_SIZE];
+
+	/* now get a policy session */
+	ps = get_policy(chip);
+
+	tkp.blob_len = tpm_get_inc_u32(&p);
+	memcpy(tkp.blob, p, tkp.blob_len);
+	tko.keyhandle = nullkey;
+	tko.policyhandle = ps;
+
+	rc = tpm2_unseal_trusted(chip, &tkp, &tko);
+	if (rc) {
+		dev_err(&chip->dev, "tpm2_unseal_trusted[policy] returned %d\n", rc);
+		tpm2_flush_context(chip, ps);
+		goto out;
+	}
+
+	if (tkp.key_len != sizeof(payload)) {
+		dev_err(&chip->dev, "tpm2_unseal_trusted[policy] wrong unseal payload size %d != %ld",
+			tkp.key_len, sizeof(payload));
+		goto out;
+	}
+	if (memcmp(payload, tkp.key, tkp.key_len) != 0) {
+		dev_err(&chip->dev, "tpm2_unseal_trusted[policy] Payload DID NOT compare correctly\n");
+		goto out;
+	}
+	dev_info(&chip->dev, "tpm2_unseal_trusted[policy] returned correct data\n"
+		 "test3: PASS\n");
+
+	/*
+	 * test4: get the TPM routines to seal to a password and then
+	 * do a manual unseal
+	 */
+
+	dev_info(&chip->dev, "test4: seal using tpm2-cmd[password] and unseal manually\n");
+
+	memset(&tkp, 0, sizeof(tkp));
+	memset(&tko, 0, sizeof(tko));
+
+	tko.keyhandle = nullkey;
+	tko.hash = HASH_ALGO_SHA256;
+	tkp.key_len = sizeof(payload);
+	memcpy(tkp.key, payload, tkp.key_len);
+	strcpy(tko.blobauth, password);
+
+	rc = tpm2_seal_trusted(chip, &tkp, &tko);
+	if (rc) {
+		dev_err(&chip->dev, "tpm2_seal_trusted returned error: %d\n ", rc);
+		goto out;
+	}
+
+	/*
+	 * now load the sealed object (we need the pub and priv parts
+	 * returned from prior command.  The blob from
+	 * tpm2_seal_trusted is the entire return from the create, so
+	 * only append the private and public parts
+	 */
+	p = tkp.blob;
+	/* private len */
+	len = tpm_get_inc_u16(&p);
+	p += len;
+	/* add public len */
+	len += tpm_get_inc_u16(&p);
+	/* add 4 for the two length values */
+	len += 4;
+
+	tpm_buf_init(&b1, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
+	/* parent */
+	tpm_buf_append_name(&b1, auth, nullkey, chip->tpmkeyname);
+	tpm_buf_append_hmac_session(&b1, auth, TPM2_SA_CONTINUE_SESSION,
+				    NULL, 0);
+
+	tpm_buf_append(&b1, tkp.blob, len);
+
+	tpm_buf_fill_hmac_session(&b1, auth);
+	rc = tpm_transmit_cmd(chip, &b1, 4, "loading seal");
+	rc = tpm_buf_check_hmac_response(&b1, auth, rc);
+	dev_info(&chip->dev, "TPM: load response returned %d\n", rc);
+	if (rc)
+		goto out;
+	p = &b1.data[TPM_HEADER_SIZE];
+	h = tpm_get_inc_u32(&p);
+	dev_info(&chip->dev, "sealed data loaded at %08x\n", h);
+	/* skip over parameter size */
+	p += 4;
+	len = tpm_get_inc_u16(&p);
+	if (len != sizeof(name)) {
+		dev_err(&chip->dev, "Wrong name size %d\n", len);
+		goto out;
+	}
+	memcpy(name, p, len);
+	tpm_buf_destroy(&b1);
+	tpm_buf_destroy(&buf);
+
+	/*
+	 * now unseal the data using the authority in a HMAC and
+	 * protecting the returned unseal by encryption
+	 */
+	tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
+	tpm_buf_append_name(&buf, auth, h, name);
+	tpm_buf_append_hmac_session(&buf, auth,
+				    TPM2_SA_ENCRYPT | TPM2_SA_CONTINUE_SESSION,
+				    password, strlen(password));
+	tpm_buf_fill_hmac_session(&buf, auth);
+	rc = tpm_transmit_cmd(chip, &buf, 4, "unseal");
+	rc = tpm_buf_check_hmac_response(&buf, auth, rc);
+	tpm2_flush_context(chip, h);
+	dev_info(&chip->dev, "unseal returns %d\n", rc);
+	if (rc)
+		goto out;
+	p = &buf.data[TPM_HEADER_SIZE + 4];
+	len = tpm_get_inc_u16(&p);
+	if (len != sizeof(payload) + 1) {
+		dev_err(&chip->dev, "wrong unseal payload size %d != %ld",
+			len, sizeof(payload) + 1);
+		goto out;
+	}
+	if (memcmp(payload, p, sizeof(payload)) != 0) {
+		dev_err(&chip->dev, "Payload DID NOT compare correctly\n");
+		goto out;
+	}
+
+	dev_info(&chip->dev, "test4: PASS\n");
+
+	/*
+	 * test5: get the TPM routines to seal to a policy and then
+	 * do a manual unseal
+	 */
+
+	dev_info(&chip->dev, "test5: seal using tpm2-cmd[policy] and unseal manually\n");
+
+	memset(&tkp, 0, sizeof(tkp));
+	memset(&tko, 0, sizeof(tko));
+
+	tko.keyhandle = nullkey;
+	tko.hash = HASH_ALGO_SHA256;
+	tkp.key_len = sizeof(payload);
+	memcpy(tkp.key, payload, tkp.key_len);
+	tko.policydigest_len = sizeof(policy);
+	memcpy(tko.policydigest, policy, tko.policydigest_len);
+
+	rc = tpm2_seal_trusted(chip, &tkp, &tko);
+	if (rc) {
+		dev_err(&chip->dev, "tpm2_seal_trusted returned error: %d\n ", rc);
+		goto out;
+	}
+
+	ps = get_policy(chip);
+	/*
+	 * now load the sealed object (we need the pub and priv parts
+	 * returned from prior command.  The blob from
+	 * tpm2_seal_trusted is the entire return from the create, so
+	 * only append the private and public parts
+	 */
+	p = tkp.blob;
+	/* private len */
+	len = tpm_get_inc_u16(&p);
+	p += len;
+	/* add public len */
+	len += tpm_get_inc_u16(&p);
+	/* add 4 for the two length values */
+	len += 4;
+
+	tpm_buf_init(&b1, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
+	/* parent */
+	tpm_buf_append_name(&b1, auth, nullkey, chip->tpmkeyname);
+	tpm_buf_append_hmac_session(&b1, auth, TPM2_SA_CONTINUE_SESSION,
+				    NULL, 0);
+
+	tpm_buf_append(&b1, tkp.blob, len);
+
+	tpm_buf_fill_hmac_session(&b1, auth);
+	rc = tpm_transmit_cmd(chip, &b1, 4, "loading seal");
+	rc = tpm_buf_check_hmac_response(&b1, auth, rc);
+	dev_info(&chip->dev, "TPM: load response returned %d\n", rc);
+	if (rc) {
+		tpm2_flush_context(chip, ps);
+		goto out;
+	}
+	p = &b1.data[TPM_HEADER_SIZE];
+	h = tpm_get_inc_u32(&p);
+	dev_info(&chip->dev, "sealed data loaded at %08x\n", h);
+	/* skip over parameter size */
+	p += 4;
+	len = tpm_get_inc_u16(&p);
+	if (len != sizeof(name)) {
+		dev_err(&chip->dev, "Wrong name size %d\n", len);
+		goto out;
+	}
+	memcpy(name, p, len);
+	tpm_buf_destroy(&b1);
+	tpm_buf_destroy(&buf);
+
+	/*
+	 * now unseal the data using the authority in a HMAC and
+	 * protecting the returned unseal by encryption
+	 */
+	tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
+	tpm_buf_append_name(&buf, auth, h, name);
+	tpm2_buf_append_auth(&buf, ps, NULL, 0, 0, NULL, 0);
+	tpm_buf_append_hmac_session(&buf, auth, TPM2_SA_ENCRYPT,
+				    NULL, 0);
+	tpm_buf_fill_hmac_session(&buf, auth);
+	rc = tpm_transmit_cmd(chip, &buf, 4, "unseal");
+	rc = tpm_buf_check_hmac_response(&buf, auth, rc);
+	tpm2_flush_context(chip, h);
+	dev_info(&chip->dev, "unseal returns %d\n", rc);
+	if (rc)
+		goto out;
+	p = &buf.data[TPM_HEADER_SIZE + 4];
+	len = tpm_get_inc_u16(&p);
+	if (len != sizeof(payload) + 1) {
+		dev_err(&chip->dev, "wrong unseal payload size %d != %ld",
+			len, sizeof(payload) + 1);
+		goto out;
+	}
+	if (memcmp(payload, p, sizeof(payload)) != 0) {
+		dev_err(&chip->dev, "Payload DID NOT compare correctly\n");
+		goto out;
+	}
+
+	dev_info(&chip->dev, "test5: PASS");
+
+	/*
+	 * test6: extend PCR 16 with a random value and verify it
+	 * comes back correctly
+	 */
+
+	dev_info(&chip->dev, "test6: extend PCR 16");
+
+	digest.alg_id = TPM_ALG_SHA256;
+
+	rc = tpm2_pcr_read(chip, 16, &digest, &digest_len);
+	if (rc) {
+		dev_err(&chip->dev, "tpm2_pcr_read returns error: %d\n", rc);
+		goto out;
+	}
+	if (digest_len != SHA256_DIGEST_SIZE) {
+		dev_err(&chip->dev, "wrong digest size %d!=%d\n", digest_len,
+			SHA256_DIGEST_SIZE);
+		goto out;
+	}
+
+	for (i = 0; i < chip->nr_allocated_banks; i++) {
+		rc = tpm2_get_random(chip, extend[i].digest, SHA256_DIGEST_SIZE);
+		if (rc != SHA256_DIGEST_SIZE) {
+			dev_err(&chip->dev, "tpm2_get_random failed: %d\n", rc);
+			goto out;
+		}
+		extend[i].alg_id = chip->allocated_banks[i].alg_id;
+	}
+	rc = tpm2_pcr_extend(chip, 16, extend);
+	if (rc) {
+		dev_err(&chip->dev, "tpm2_pcr_extend failed: %d\n", rc);
+		goto out;
+	}
+
+	desc->tfm = sha256_hash;
+	crypto_shash_init(desc);
+	crypto_shash_update(desc, digest.digest, SHA256_DIGEST_SIZE);
+	crypto_shash_update(desc, extend[0].digest, SHA256_DIGEST_SIZE);
+	crypto_shash_final(desc, digest.digest);
+
+	extend[0].alg_id = TPM_ALG_SHA256;
+	rc = tpm2_pcr_read(chip, 16, &extend[0], &digest_len);
+	if (rc) {
+		dev_err(&chip->dev, "tpm2_pcr_read returns error: %d\n", rc);
+		goto out;
+	}
+	if (digest_len != SHA256_DIGEST_SIZE) {
+		dev_err(&chip->dev, "wrong digest size %d!=%d\n", digest_len,
+			SHA256_DIGEST_SIZE);
+		goto out;
+	}
+
+	if (memcmp(digest.digest, extend[0].digest, SHA256_DIGEST_SIZE) != 0) {
+		dev_err(&chip->dev, "Extend digest comparison failed\n");
+		goto out;
+	}
+
+	dev_info(&chip->dev, "All tests passed\n");
+	ret = 0;
+
+ out:
+	tpm2_flush_context(chip, nullkey);
+
+	tpm_put_ops(chip);
+
+	return ret;
+}
+
+module_init(tpm2_sessions_test);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 7307f061e5df..8f50ad88c5ab 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -865,6 +865,7 @@ static int parse_start_auth_session(struct tpm2_auth *auth, const u8 *data)
  * tpm2_start_auth_session - create a HMAC authentication session with the TPM
  * @chip: the TPM chip structure to create the session with
  * @authp: A pointer to an opaque tpm2_auth structure to be allocated
+ * @key: A pointer to return the NULL key or NULL if key should be flushed
  *
  * This function loads the NULL seed from its saved context and starts
  * an authentication session on the null seed, allocates a tpm2_auth
diff --git a/drivers/char/tpm/tpm2-sessions.h b/drivers/char/tpm/tpm2-sessions.h
index ec245731edaf..23d62446dd71 100644
--- a/drivers/char/tpm/tpm2-sessions.h
+++ b/drivers/char/tpm/tpm2-sessions.h
@@ -45,7 +45,8 @@ void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle,
 
 #ifdef CONFIG_TPM_BUS_SECURITY
 
-int tpm2_start_auth_session(struct tpm_chip *chip, struct tpm2_auth **authp);
+int tpm2_start_auth_session(struct tpm_chip *chip,
+			    struct tpm2_auth **authp);
 void tpm_buf_append_name(struct tpm_buf *buf, struct tpm2_auth *auth,
 			 u32 handle, u8 *name);
 void tpm_buf_append_hmac_session(struct tpm_buf *buf, struct tpm2_auth *auth,
