diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index f5c9355..d8e896e 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -400,6 +400,10 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
 	if (chip->dev.parent)
 		pm_runtime_get_sync(chip->dev.parent);
 
+	rc = tpm2_emulate(chip, space, ordinal, buf, bufsiz);
+	if (rc)
+		goto out;
+
 	rc = tpm2_prepare_space(chip, space, ordinal, buf, bufsiz);
 	if (rc)
 		goto out;
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index adf7810..b922652 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -136,7 +136,8 @@ enum tpm2_capabilities {
 };
 
 enum tpm2_properties {
-	TPM_PT_TOTAL_COMMANDS	= 0x0129,
+	TPM_PT_ACTIVE_SESSIONS_MAX	= 0x0111,
+	TPM_PT_TOTAL_COMMANDS		= 0x0129,
 };
 
 enum tpm2_startup_types {
@@ -214,6 +215,8 @@ struct tpm_chip {
 	struct tpm_space *work_space;
 	u32 nr_commands;
 	u32 *cc_attrs_tbl;
+	struct tpm_sessions *sessions;
+	int max_sessions;
 };
 
 #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev)
@@ -583,4 +586,7 @@ int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space,
 		       u32 cc, u8 *buf, size_t bufsiz);
 int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,
 		      u32 cc, u8 *buf, size_t bufsiz);
+void tpm2_flush_space(struct tpm_chip *chip, struct tpm_space *space);
+int tpm2_emulate(struct tpm_chip *chip, struct tpm_space *space,
+		  u32 cc, u8 *buf, size_t bufsiz);
 #endif
diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c
index 285361e..e8e9f32 100644
--- a/drivers/char/tpm/tpm2-space.c
+++ b/drivers/char/tpm/tpm2-space.c
@@ -25,15 +25,29 @@ enum tpm2_handle_types {
 	TPM2_HT_TRANSIENT	= 0x80000000,
 };
 
-static void tpm2_flush_space(struct tpm_chip *chip)
+#define TPM2_HT_TAG_FOR_FLUSH	0xF0000000
+
+void tpm2_flush_space(struct tpm_chip *chip, struct tpm_space *space)
 {
-	struct tpm_space *space = chip->work_space;
 	int i;
 
-	for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++)
-		if (space->context_tbl[i] && ~space->context_tbl[i])
-			tpm2_flush_context_cmd(chip, space->context_tbl[i],
-					       TPM_TRANSMIT_UNLOCKED);
+	for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
+		u32 handle = space->context_tbl[i];
+		u32 handle_type;
+
+		if (handle == 0 || handle == ~0)
+			continue;
+
+		if ((handle & TPM2_HT_TAG_FOR_FLUSH) ==
+		    TPM2_HT_TAG_FOR_FLUSH)
+			handle &= ~TPM2_HT_TAG_FOR_FLUSH;
+
+		handle_type = (handle & 0xFF000000);
+
+		tpm2_flush_context_cmd(chip, handle, TPM_TRANSMIT_UNLOCKED);
+
+		space->context_tbl[i] = 0;
+	}
 }
 
 struct tpm2_context {
@@ -54,15 +68,11 @@ static int tpm2_load_space(struct tpm_chip *chip)
 	u32 s;
 
 	for (i = 0, j = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
+		u32 phandle, phandle_type;
+
 		if (!space->context_tbl[i])
 			continue;
 
-		/* sanity check, should never happen */
-		if (~space->context_tbl[i]) {
-			dev_err(&chip->dev, "context table is inconsistent");
-			return -EFAULT;
-		}
-
 		rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS,
 				 TPM2_CC_CONTEXT_LOAD);
 		if (rc)
@@ -80,9 +90,23 @@ static int tpm2_load_space(struct tpm_chip *chip)
 			rc = -EFAULT;
 			goto out_err;
 		}
+		phandle = get_unaligned_be32((__be32 *)&buf.data[TPM_HEADER_SIZE]);
+		phandle_type = (phandle & 0xFF000000);
+		if (phandle_type == TPM2_HT_TRANSIENT &&
+		    space->context_tbl[i] != ~0) {
+			dev_err(&chip->dev, "context table is inconsistent");
+			rc = -EFAULT;
+			goto out_err;
+		}
+		if ((phandle_type == TPM2_HT_HMAC_SESSION ||
+		     phandle_type == TPM2_HT_POLICY_SESSION) &&
+		    space->context_tbl[i] != phandle) {
+			dev_err(&chip->dev, "session handle changed\n");
+			rc = -EFAULT;
+			goto out_err;
+		}
 
-		space->context_tbl[i] =
-			be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE]);
+		space->context_tbl[i] = phandle;
 
 		j += s;
 
@@ -93,10 +117,85 @@ static int tpm2_load_space(struct tpm_chip *chip)
 
 out_err:
 	tpm_buf_destroy(&buf);
-	tpm2_flush_space(chip);
+	tpm2_flush_space(chip, space);
 	return rc;
 }
 
+static void tpm2_unmap_sessions(struct tpm_chip *chip, u32 rc)
+{
+	struct tpm_space *space = chip->work_space;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
+		if ((space->context_tbl[i] & TPM2_HT_TAG_FOR_FLUSH) !=
+		    TPM2_HT_TAG_FOR_FLUSH)
+			continue;
+		if (rc == TPM2_RC_SUCCESS)
+			space->context_tbl[i] = 0;
+		else
+			/* for unsuccessful command, keep session */
+			space->context_tbl[i] &= ~TPM2_HT_TAG_FOR_FLUSH;
+	}
+}
+
+static int tpm2_map_sessions(struct tpm_chip *chip, u8 *buf, size_t len,
+			     size_t start)
+{
+	struct tpm_space *space = chip->work_space;
+	u32 size = be32_to_cpup((__be32 *)&buf[start]);
+	int i;
+
+	/* skip over authorizationSize */
+	start += 4;
+
+	if (size > len - start) {
+		dev_err(&chip->dev, "Invalid authorization header size %u\n",
+			size);
+		return -EINVAL;
+	}
+
+	for (i = start; i < start+size; ) {
+		u16 skip;
+		__be32 *handlep;
+		u8 attr;
+		int j;
+		u32 handle_type;
+
+		/* TPMI_SH_AUTH_SESSION */
+		handlep = (__be32 *)&buf[i];
+		handle_type = get_unaligned_be32(handlep) & 0xFF000000;
+		i += 4;
+		/* TPM2B_DIGEST */
+		skip = get_unaligned_be16((__be16 *)&buf[i]);
+		i += skip + sizeof(skip);
+		/* TPMA_SESSION */
+		attr = buf[i++];
+		/* TPM2B_AUTH */
+		skip = get_unaligned_be16((__be16 *)&buf[i]);
+		i += skip + sizeof(skip);
+
+		if (handle_type != TPM2_HT_HMAC_SESSION &&
+		    handle_type != TPM2_HT_POLICY_SESSION)
+			continue;
+
+		j = 0xFFFFFF - (get_unaligned_be32(handlep) & 0xFFFFFF);
+		if (j > ARRAY_SIZE(space->context_tbl) ||
+		    !space->context_tbl[j])
+			return -EINVAL;
+		put_unaligned_be32(space->context_tbl[j], handlep);
+		if ((attr & 1) == 0)
+			/* session is flushed by the command */
+			space->context_tbl[j] |= TPM2_HT_TAG_FOR_FLUSH;
+	}
+
+	if (i != start+size) {
+		dev_err(&chip->dev, "Authorization session overflow\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd, size_t len)
 {
 	struct tpm_space *space = chip->work_space;
@@ -104,6 +203,7 @@ static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd, size_t len)
 	u32 vhandle;
 	u32 phandle;
 	u32 attrs;
+	u16 tag = get_unaligned_be16((__be16 *)cmd);
 	int i;
 	int j;
 	int rc;
@@ -131,11 +231,14 @@ static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd, size_t len)
 		*((__be32 *)&cmd[TPM_HEADER_SIZE + 4 * i]) =
 			cpu_to_be32(phandle);
 	}
+	if (tag == TPM2_ST_SESSIONS)
+		tpm2_map_sessions(chip, cmd, len,
+				  TPM_HEADER_SIZE + 4*nr_handles);
 
 	return 0;
 
 out_err:
-	tpm2_flush_space(chip);
+	tpm2_flush_space(chip, space);
 	return rc;
 }
 
@@ -163,13 +266,17 @@ int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space,
 static int tpm2_map_response(struct tpm_chip *chip, u32 cc, u8 *rsp, size_t len)
 {
 	struct tpm_space *space = chip->work_space;
-	u32 phandle;
+	u32 phandle, phandle_type;
 	u32 vhandle;
 	u32 attrs;
 	u32 return_code = get_unaligned_be32((__be32 *)&rsp[6]);
+	u16 tag = get_unaligned_be16((__be16 *)rsp);
 	int i;
 	int rc;
 
+	if (tag == TPM2_ST_SESSIONS)
+		tpm2_unmap_sessions(chip, return_code);
+
 	if (return_code != TPM2_RC_SUCCESS)
 		return 0;
 
@@ -185,7 +292,10 @@ static int tpm2_map_response(struct tpm_chip *chip, u32 cc, u8 *rsp, size_t len)
 		return 0;
 
 	phandle = be32_to_cpup((__be32 *)&rsp[TPM_HEADER_SIZE]);
-	if ((phandle & 0xFF000000) != TPM2_HT_TRANSIENT)
+	phandle_type = (phandle & 0xFF000000);
+	if (phandle_type != TPM2_HT_TRANSIENT &&
+	    phandle_type != TPM2_HT_HMAC_SESSION &&
+	    phandle_type != TPM2_HT_POLICY_SESSION)
 		return 0;
 
 	/* Garbage collect a dead context. */
@@ -208,13 +318,13 @@ static int tpm2_map_response(struct tpm_chip *chip, u32 cc, u8 *rsp, size_t len)
 	}
 
 	space->context_tbl[i] = phandle;
-	vhandle = TPM2_HT_TRANSIENT | (0xFFFFFF - i);
+	vhandle = phandle_type | (0xFFFFFF - i);
 	*(__be32 *)&rsp[TPM_HEADER_SIZE] = cpu_to_be32(vhandle);
 
 	return 0;
 
 out_err:
-	tpm2_flush_space(chip);
+	tpm2_flush_space(chip, space);
 	return rc;
 }
 
@@ -228,9 +338,20 @@ static int tpm2_save_space(struct tpm_chip *chip)
 	u32 s;
 
 	for (i = 0, j = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
-		if (!(space->context_tbl[i] && ~space->context_tbl[i]))
+		u32 phandle, phandle_type;
+
+		phandle = space->context_tbl[i];
+
+		if (phandle == 0)
 			continue;
 
+		if (phandle == ~0) {
+			dev_err(&chip->dev, "context table is inconsistent\n");
+			return -EFAULT;
+		}
+
+		phandle_type = (phandle & 0xFF000000);
+
 		rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS,
 				  TPM2_CC_CONTEXT_SAVE);
 		if (rc)
@@ -260,10 +381,11 @@ static int tpm2_save_space(struct tpm_chip *chip)
 
 		memcpy(&space->context_buf[j], &buf.data[TPM_HEADER_SIZE], s);
 
-		tpm2_flush_context_cmd(chip, space->context_tbl[i],
-				       TPM_TRANSMIT_UNLOCKED);
-
-		space->context_tbl[i] = ~0;
+		if (phandle_type == TPM2_HT_TRANSIENT) {
+			tpm2_flush_context_cmd(chip, space->context_tbl[i],
+					       TPM_TRANSMIT_UNLOCKED);
+			space->context_tbl[i] = ~0;
+		}
 
 		j += s;
 
@@ -273,7 +395,7 @@ static int tpm2_save_space(struct tpm_chip *chip)
 	return 0;
 out_err:
 	tpm_buf_destroy(&buf);
-	tpm2_flush_space(chip);
+	tpm2_flush_space(chip, space);
 	return rc;
 }
 
@@ -297,3 +419,60 @@ int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,
 
 	return 0;
 }
+
+/* if a space is active, emulate some commands */
+int tpm2_emulate(struct tpm_chip *chip, struct tpm_space *space,
+		 u32 cc, u8 *buf, size_t bufsiz)
+{
+	int i, j, k;
+	u32 vhandle, handle_type, phandle;
+	struct tpm2_context *ctx;
+	static struct tpm_output_header buf_rc = {
+		.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+		.length = cpu_to_be32(sizeof(struct tpm_output_header)),
+	};
+
+	if (!space)
+		return 0;
+
+	if (cc != TPM2_CC_FLUSH_CONTEXT)
+		return 0;
+	vhandle = get_unaligned_be32((__be32 *)&buf[10]);
+	handle_type = (vhandle & 0xFF000000);
+
+	if (handle_type != TPM2_HT_HMAC_SESSION &&
+	    handle_type != TPM2_HT_POLICY_SESSION &&
+	    handle_type != TPM2_HT_TRANSIENT)
+		/* let the TPM figure out and return the error */
+		return 0;
+
+	buf_rc.return_code = TPM2_RC_HANDLE;
+
+	j = 0xFFFFFF - (vhandle & 0xFFFFFF);
+	if (j > ARRAY_SIZE(space->context_tbl))
+		goto error;
+	phandle = space->context_tbl[j];
+	if (phandle != ~0)
+		goto error;
+
+	for (i = 0, k = 0; i <= j; i++) {
+		ctx = (struct tpm2_context *)&space->context_buf[k];
+
+		if (space->context_tbl[i] == 0)
+			continue;
+
+		k += sizeof(*ctx) + get_unaligned_be16(&ctx->blob_size);
+	}
+	/* move all the contexts up */
+	memcpy(ctx, &space->context_buf[k], PAGE_SIZE - k);
+	space->context_tbl[j] = 0;
+
+	if (handle_type != TPM2_HT_TRANSIENT)
+		tpm2_flush_context_cmd(chip, phandle, TPM_TRANSMIT_UNLOCKED);
+
+	buf_rc.return_code = TPM2_RC_SUCCESS;
+
+ error:
+	memcpy(buf, &buf_rc, sizeof(buf_rc));
+	return sizeof(buf_rc);
+}
diff --git a/drivers/char/tpm/tpms-dev.c b/drivers/char/tpm/tpms-dev.c
index c10b308..3eb5955 100644
--- a/drivers/char/tpm/tpms-dev.c
+++ b/drivers/char/tpm/tpms-dev.c
@@ -36,6 +36,7 @@ static int tpms_release(struct inode *inode, struct file *file)
 	struct file_priv *fpriv = file->private_data;
 	struct tpms_priv *priv = container_of(fpriv, struct tpms_priv, priv);
 
+	tpm2_flush_space(fpriv->chip, &priv->space);
 	tpm_common_release(file, fpriv);
 	kfree(priv);
 
