[sherlock] 9.20221010.3.176 u-boot source

GitOrigin-RevId: a6d3993ddd37fe5c304ac8201a8d9ebc4c6ce96d
Change-Id: Ia900dabbc2a7edca14776d46bf9550e58b7a037e
Reviewed-on: https://turquoise-internal-review.googlesource.com/c/third_party/u-boot/+/655994
Reviewed-by: David Pursell <dpursell@google.com>
diff --git a/arb_version.txt b/arb_version.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/arb_version.txt
@@ -0,0 +1 @@
+1
diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c
index 92b4267..30e5cdb 100644
--- a/arch/arm/lib/bootm.c
+++ b/arch/arm/lib/bootm.c
@@ -85,12 +85,12 @@
 }
 
 #ifndef CONFIG_TA_VX
-static inline void vx_finalize(void) {}
+static inline void vx_finalize_or_panic(void) {}
 #else
-static void vx_finalize(void)
+static void vx_finalize_or_panic(void)
 {
 	printf("VX: finalizing policies and configurations...\n");
-	ta_vx_exit_bootloader();  // Panics on error.
+	ta_vx_exit_bootloader_or_panic();  // Panics on error.
 }
 #endif  /* CONFIG_TA_VX */
 
@@ -116,7 +116,7 @@
 #endif
 
 	/* Finalize Verified Execution policies and configurations. */
-	vx_finalize();
+	vx_finalize_or_panic();
 
 	board_quiesce_devices();
 
diff --git a/board/amlogic/g12b_newman_bx/zircon_partition_map.c b/board/amlogic/g12b_newman_bx/zircon_partition_map.c
index 65a449a..5a2f164 100644
--- a/board/amlogic/g12b_newman_bx/zircon_partition_map.c
+++ b/board/amlogic/g12b_newman_bx/zircon_partition_map.c
@@ -5,6 +5,7 @@
  */
 
 #include <common.h>
+#include <emmc_partitions.h>
 #include <mmc.h>
 #include <zircon_uboot/partition_internal.h>
 
@@ -193,7 +194,14 @@
 		.name = "fvm",
 		.fastboot_locked_access = ZIRCON_PARTITION_ACCESS_FLAG_WRITE,
 		.gpt_name = "fuchsia-fvm",
-	}
+	},
+	{
+		.name = MMC_SECURE_STORAGE_NAME,
+		.fastboot_locked_access = ZIRCON_PARTITION_ACCESS_DEV_ONLY,
+		.gpt_name = "reserved",
+		.gpt_offset = MMC_SECURE_STORAGE_OFFSET,
+		.size = MMC_SECURE_STORAGE_SIZE,
+	},
 };
 
 const size_t zircon_partition_map_count = ARRAY_SIZE(zircon_partition_map);
diff --git a/common/Kconfig b/common/Kconfig
index 4f90eae..f07b201 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -641,14 +641,6 @@
 	bool "Support Verified Execution Trusted App"
 	default n
 
-config TA_VX_AUTO_PROVISION_RPMB
-	bool "Automatically provision RPMB in the VX TA"
-	help
-	  Configure the VX TA such that it will automatically provision the
-	  RPMB if the RPMB authentication key is not already programmed.
-	depends on TA_VX
-	default n
-
 config TA_VX_TESTS
 	bool "Enable `fastboot oem vx-test` and `vx test` commands."
 	help
diff --git a/common/ta_vx_helper.c b/common/ta_vx_helper.c
index b6d0412..2406b3e 100644
--- a/common/ta_vx_helper.c
+++ b/common/ta_vx_helper.c
@@ -9,47 +9,13 @@
 #include <tee/ta_helper.h>
 #include <tee/ta_vx.h>
 #include <tee/ta_vx_helper.h>
+#include <zircon_uboot/boot_args.h>
 
 #ifdef CONFIG_TA_VX
 #define VX_PERM_ATTR_HASH_SIZE 32
 
 static struct tee_optee_ta_uuid vx_uuid = TA_VX_UUID;
 
-static int ta_vx_setup(void)
-{
-	uint32_t options = 0;
-	static bool setup_done = false;
-	static int setup_result = 0;
-
-	if (setup_done) {
-		return setup_result;
-	}
-
-#ifdef CONFIG_TA_VX_AUTO_PROVISION_RPMB
-	options |= VX_OPTION_AUTO_PROVISION_RPMB;
-#endif
-
-	if (options) {
-		struct tee_param params[1] = { 0 };
-		params[0].attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT;
-		params[0].u.value.a = options;
-		setup_result = ta_call(&vx_uuid, TA_VX_CMD_SET_OPTIONS,
-				       ARRAY_SIZE(params), params);
-	}
-
-	setup_done = true;
-	return setup_result;
-}
-
-#define SETUP_OR_FAIL()                                                        \
-	do {                                                                   \
-		int rc = ta_vx_setup();                                        \
-		if (rc) {                                                      \
-			printf("ta_vx_setup() failed, rc = %d.", rc);          \
-			return rc;                                             \
-		}                                                              \
-	} while (0)
-
 static int ta_vx_read_lock_state(enum vx_lock_state *lock_state)
 {
 	int rc;
@@ -57,8 +23,6 @@
 	struct tee_param param = { 0 };
 	param.attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT;
 
-	SETUP_OR_FAIL();
-
 	rc = ta_call(&vx_uuid, TA_VX_CMD_READ_LOCK_STATE, 1, &param);
 	if (!rc) {
 		*lock_state = param.u.value.a;
@@ -73,16 +37,55 @@
 	param.attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT;
 	param.u.value.a = lock_state;
 
-	SETUP_OR_FAIL();
-
 	return ta_call(&vx_uuid, TA_VX_CMD_WRITE_LOCK_STATE, 1, &param);
 }
 
 int ta_vx_lock(void)
 {
+	int rc = zircon_clear_stored_cmdline();
+	if (rc) {
+		printf("Failed to clear stored boot args\n");
+		return rc;
+	}
+
 	return ta_vx_write_lock_state(VX_LOCKED);
 }
 
+int ta_vx_lock_if_ephemerally_unlocked(void)
+{
+	int rc;
+	uint32_t vars;
+
+	rc = ta_vx_getvar_all(&vars);
+	if (rc) {
+		return rc;
+	}
+
+	if (!(vars & VX_VAR_EPHEMERALLY_UNLOCKED)) {
+		return 0;
+	}
+
+	printf("Locking up an ephemerally unlocked device\n");
+	rc = ta_vx_lock();
+	if (!rc) {
+		printf("ta_vx_lock() failed\n");
+		return rc;
+	}
+
+	rc = ta_vx_getvar_all(&vars);
+	if (rc) {
+		return rc;
+	}
+
+	if (vars & VX_VAR_EPHEMERALLY_UNLOCKED) {
+		printf("SHOCKED: device is still ephemerally unlocked after "
+		       "successful locking\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 int ta_vx_unlock(void)
 {
 	return ta_vx_write_lock_state(VX_UNLOCKED);
@@ -103,8 +106,6 @@
 	int rc;
 	struct tee_param params[2] = { 0 };
 
-	SETUP_OR_FAIL();
-
 	params[0].attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT;
 	params[0].u.value.a = slot;
 
@@ -124,8 +125,6 @@
 {
 	struct tee_param params[2] = { 0 };
 
-	SETUP_OR_FAIL();
-
 	params[0].attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT;
 	params[0].u.value.a = slot;
 
@@ -149,8 +148,6 @@
 
 	size_t key_len = strlen(name) + 1;
 
-	SETUP_OR_FAIL();
-
 	/* Connect to TA. */
 	rc = ta_open(&vx_uuid, &context);
 	if (rc)
@@ -206,8 +203,6 @@
 
 	size_t key_len = strlen(name) + 1;
 
-	SETUP_OR_FAIL();
-
 	/* Connect to TA. */
 	rc = ta_open(&vx_uuid, &context);
 	if (rc)
@@ -254,8 +249,6 @@
 	struct tee_shm *shm_key = NULL;
 	size_t key_len = strlen(name) + 1;
 
-	SETUP_OR_FAIL();
-
 	/* Connect to TA. */
 	rc = ta_open(&vx_uuid, &context);
 	if (rc)
@@ -287,8 +280,6 @@
 	struct tee_param params[1] = { 0 };
 	struct tee_shm *shm_buf = NULL;
 
-	SETUP_OR_FAIL();
-
 	if (!(buf && buf_len))
 		return -EINVAL;
 
@@ -318,26 +309,16 @@
 	return rc;
 }
 
-void ta_vx_exit_bootloader(void)
+void ta_vx_exit_bootloader_or_panic(void)
 {
-	/* Exiting bootloader should panic on failure so call ta_vx_setup()
-	   directly rather than using SETUP_OR_FAIL. */
-	int rc = ta_vx_setup();
-	if (rc) {
-		printf("ta_vx_setup() failed, rc = %d.", rc);
-		panic("VX setup failed!");
-	}
+	uint32_t vars;
 
 	if (ta_call(&vx_uuid, TA_VX_CMD_EXIT_BOOTLOADER, 0, NULL)) {
 		panic("VX finalization failed!");
 	}
 
 	/* Make sure we are indeed beyond the bootloader security domain. */
-	struct tee_param params[1] = { 0 };
-	params[0].attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT;
-	params[0].u.value.a = VX_OPTION_FOR_TESTING;
-	if (!ta_call(&vx_uuid, TA_VX_CMD_SET_OPTIONS, ARRAY_SIZE(params),
-		     params)) {
+	if (ta_vx_getvar_all(&vars) || !(vars & VX_VAR_OUT_OF_BOOTLOADER)) {
 		panic("VX finalization verification failed.");
 	}
 
@@ -352,8 +333,6 @@
 
 	struct tee_shm *shm_buf = NULL;
 
-	SETUP_OR_FAIL();
-
 	if (!(buf && buf_len)) {
 		return -EINVAL;
 	}
@@ -400,8 +379,6 @@
 
 	struct tee_shm *shm_buf = NULL;
 
-	SETUP_OR_FAIL();
-
 	if (!(buf && buf_len))
 		return -EINVAL;
 
@@ -438,8 +415,6 @@
 	int rc;
 	struct tee_param params[1] = { 0 };
 
-	SETUP_OR_FAIL();
-
 	params[0].attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT;
 	rc = ta_call(&vx_uuid, TA_VX_CMD_GET_RPMB_STATUS, ARRAY_SIZE(params),
 		     params);
@@ -459,15 +434,26 @@
 
 int ta_vx_provision_rpmb(void)
 {
-	SETUP_OR_FAIL();
-
 	return ta_call(&vx_uuid, TA_VX_CMD_PROVISION_RPMB, 0, NULL);
 }
 
+int ta_vx_reroute_rpmb_till_reboot(void)
+{
+	return ta_call(&vx_uuid, TA_VX_CMD_REROUTE_RPMB_TILL_REBOOT, 0, NULL);
+}
+
+int ta_vx_reroute_rpmb_to_software(void)
+{
+	return ta_call(&vx_uuid, TA_VX_CMD_REROUTE_RPMB_TO_SOFTWARE, 0, NULL);
+}
+
+int ta_vx_reroute_rpmb_to_hardware(void)
+{
+	return ta_call(&vx_uuid, TA_VX_CMD_REROUTE_RPMB_TO_HARDWARE, 0, NULL);
+}
+
 int ta_vx_delete_perm_attr(void)
 {
-	SETUP_OR_FAIL();
-
 	return ta_call(&vx_uuid, TA_VX_CMD_DELETE_PERM_ATTR, 0, NULL);
 }
 
@@ -479,8 +465,6 @@
 
 	struct tee_shm *shm_buf = NULL;
 
-	SETUP_OR_FAIL();
-
 	if (!buf || (buf_len < VX_PERM_ATTR_HASH_SIZE))
 		return -EINVAL;
 
@@ -523,8 +507,6 @@
 	struct tee_param param = { 0 };
 	param.attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT;
 
-	SETUP_OR_FAIL();
-
 	rc = ta_call(&vx_uuid, TA_VX_CMD_GET_PERM_ATTR_STATUS, 1, &param);
 	if (!rc) {
 		if (status) {
@@ -537,15 +519,11 @@
 
 int ta_vx_lock_perm_attr(void)
 {
-	SETUP_OR_FAIL();
-
 	return ta_call(&vx_uuid, TA_VX_CMD_LOCK_PERM_ATTR, 0, NULL);
 }
 
 int ta_vx_provision_usb_hash(void)
 {
-	SETUP_OR_FAIL();
-
 	return ta_call(&vx_uuid, TA_VX_CMD_PROVISION_USBBOOT_PWD_HASH, 0, NULL);
 }
 
@@ -556,8 +534,6 @@
 	struct tee_param param = { 0 };
 	param.attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT;
 
-	SETUP_OR_FAIL();
-
 	rc = ta_call(&vx_uuid, TA_VX_CMD_GET_USBBOOT_STATUS, 1, &param);
 	if (!rc) {
 		if (status) {
@@ -568,6 +544,26 @@
 	return rc;
 }
 
+int ta_vx_getvar_all(uint32_t *out_vars)
+{
+	int rc;
+	struct tee_param params[1] = { 0 };
+
+	if (!out_vars) {
+		return -EINVAL;
+	}
+
+	params[0].attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT;
+	rc = ta_call(&vx_uuid, TA_VX_CMD_GETVAR_ALL, ARRAY_SIZE(params),
+		     params);
+	if (rc) {
+		return rc;
+	}
+
+	*out_vars = params[0].u.value.a;
+	return 0;
+}
+
 #ifdef CONFIG_TA_VX_TESTS
 int ta_vx_run_tests(const char *name)
 {
@@ -577,8 +573,6 @@
 	size_t name_len = strlen(name) + 1;
 	struct tee_shm *shm_buf = NULL;
 
-	SETUP_OR_FAIL();
-
 	rc = ta_open(&vx_uuid, &context);
 	if (rc)
 		return rc;
@@ -616,6 +610,11 @@
 	return ta_vx_fail(__func__);
 }
 
+int ta_vx_lock_if_ephemerally_unlocked()
+{
+	return ta_vx_fail(__func__);
+}
+
 int ta_vx_unlock()
 {
 	return ta_vx_fail(__func__);
@@ -658,7 +657,7 @@
 	return ta_vx_fail(__func__);
 }
 
-void ta_vx_exit_bootloader()
+void ta_vx_exit_bootloader_or_panic()
 {
 	/* Unlike other VX stubs, this one succeeds by default as a no-op if
 	   the VX TA is disabled. */
@@ -704,6 +703,21 @@
 	return ta_vx_fail(__func__);
 }
 
+int ta_vx_reroute_rpmb_till_reboot(void)
+{
+	return ta_vx_fail(__func__);
+}
+
+int ta_vx_reroute_rpmb_to_software(void)
+{
+	return ta_vx_fail(__func__);
+}
+
+int ta_vx_reroute_rpmb_to_hardware(void)
+{
+	return ta_vx_fail(__func__);
+}
+
 int ta_vx_provision_usb_hash(void)
 {
 	return ta_vx_fail(__func__);
@@ -714,4 +728,9 @@
 	return ta_vx_fail(__func__);
 }
 
+int ta_vx_getvar_all(uint32_t *out_vars)
+{
+	return ta_vx_fail(__func__);
+}
+
 #endif // CONFIG_TA_VX
diff --git a/common/zircon_partition.c b/common/zircon_partition.c
index 9960d0b..20a61cc 100644
--- a/common/zircon_partition.c
+++ b/common/zircon_partition.c
@@ -20,7 +20,7 @@
 /* Temporary buffer when we need to cache data locally. Only valid for use
    within a single call, do not expect the contents to be consistent across
    multiple calls since other functions may have modified it. */
-static uint8_t temp_buffer[ERASE_GROUP_SIZE] = { 0 };
+static uint8_t temp_buffer[ERASE_GROUP_SIZE] __aligned(ARCH_DMA_MINALIGN);
 
 static int store_call_setup(const zircon_partition *part, uint64_t offset,
 			    size_t length)
@@ -107,6 +107,49 @@
 		return -1;
 	}
 
+	// store_write() offset must be at a block boundary. If our |offset| is
+	// not, read the first partial block, modify and write back manually.
+	// Compute the unaligned write offset within the first block.
+	size_t first_block_write_offset =
+		offset % part->data->mmc->write_bl_len;
+	if (first_block_write_offset) {
+		static uint8_t scratch_buffer[512] __aligned(ARCH_DMA_MINALIGN);
+		assert(sizeof(scratch_buffer) == part->data->mmc->write_bl_len);
+		// The aligned offset of the first block.
+		size_t first_block_offset = offset - first_block_write_offset;
+
+		// Read the block
+		int res = gpt_part_read(part, first_block_offset,
+					scratch_buffer, sizeof(scratch_buffer));
+		if (res) {
+			return res;
+		}
+
+		// The amount of data to write for the first block. It's the smaller
+		// of the total length and the remaining part in the block.
+		size_t first_block_write_length =
+			min(length,
+			    sizeof(scratch_buffer) - first_block_write_offset);
+
+		// Modify
+		memcpy(scratch_buffer + first_block_write_offset, buffer,
+		       first_block_write_length);
+
+		// Write back the modified data.
+		res = gpt_part_write(part, first_block_offset, scratch_buffer,
+				     sizeof(scratch_buffer));
+		if (res) {
+			return res;
+		}
+
+		offset += first_block_write_length;
+		length -= first_block_write_length;
+		buffer = (const uint8_t *)(buffer) + first_block_write_length;
+		if (length == 0) {
+			return 0;
+		}
+	}
+
 	return store_write(part->data->gpt_name,
 			   part->data->gpt_offset + offset, length, buffer);
 }
@@ -860,6 +903,16 @@
 		return NULL;
 	}
 
+	// If we are not in dev build, check if access is dev only.
+#if !defined(DEV_BUILD_CONFIG)
+	if (part->data->fastboot_locked_access &
+	    ZIRCON_PARTITION_ACCESS_DEV_ONLY) {
+		part->write = NULL;
+		part->erase = NULL;
+		return part;
+	}
+#endif
+
 	if (unlocked) {
 		return part;
 	}
diff --git a/common/zircon_vboot.c b/common/zircon_vboot.c
index 1635e4c..9248ed5 100644
--- a/common/zircon_vboot.c
+++ b/common/zircon_vboot.c
@@ -376,6 +376,11 @@
 		NULL
 	};
 
+	int ret = ta_vx_lock_if_ephemerally_unlocked();
+	if (ret) {
+		return ret;
+	}
+
 	AvbSlotVerifyData *verify_data = NULL;
 
 	AvbSlotVerifyResult result =
@@ -383,7 +388,7 @@
 				AVB_SLOT_VERIFY_FLAGS_NONE,
 				AVB_HASHTREE_ERROR_MODE_EIO, &verify_data);
 
-	int ret = process_verify_data(&ops, result, verify_data, &context, zbi,
+	ret = process_verify_data(&ops, result, verify_data, &context, zbi,
 				      capacity, has_successfully_booted);
 	if (verify_data) {
 		avb_slot_verify_data_free(verify_data);
@@ -521,6 +526,11 @@
 
 	ramboot_ops.user_data = (void *)&context;
 
+	int ret = ta_vx_lock_if_ephemerally_unlocked();
+	if (ret) {
+		return ret;
+	}
+
 	AvbSlotVerifyData *verify_data = NULL;
 
 	AvbSlotVerifyResult result =
@@ -528,7 +538,7 @@
 				AVB_SLOT_VERIFY_FLAGS_NONE,
 				AVB_HASHTREE_ERROR_MODE_EIO, &verify_data);
 
-	int ret = process_verify_data(&ramboot_ops, result, verify_data, NULL,
+	ret = process_verify_data(&ramboot_ops, result, verify_data, NULL,
 				      zbi, capacity, false);
 	if (verify_data) {
 		avb_slot_verify_data_free(verify_data);
diff --git a/configs/g12b_newman_bx_zircon_defconfig b/configs/g12b_newman_bx_zircon_defconfig
index d76e341..5df275e 100644
--- a/configs/g12b_newman_bx_zircon_defconfig
+++ b/configs/g12b_newman_bx_zircon_defconfig
@@ -12,7 +12,6 @@
 CONFIG_TEE=y
 CONFIG_OPTEE=y
 CONFIG_TA_VX=y
-CONFIG_TA_VX_AUTO_PROVISION_RPMB=y
 CONFIG_SUPPORT_EMMC_RPMB=y
 CONFIG_CMD_TEE=y
 CONFIG_DM_GPIO=y
@@ -225,3 +224,4 @@
 CONFIG_LZ4=y
 CONFIG_NET=n
 CONFIG_AML_GPT_SYNC_ENTIRE_ENTRY=y
+CONFIG_OPTEE_RAW_PARTITION_IO=y
diff --git a/drivers/mmc/aml_emmc_partition.c b/drivers/mmc/aml_emmc_partition.c
index 0aa40c3..0e19001 100644
--- a/drivers/mmc/aml_emmc_partition.c
+++ b/drivers/mmc/aml_emmc_partition.c
@@ -119,25 +119,36 @@
 };
 
 struct virtual_partition virtual_partition_table[] = {
-    /* partition for name idx, off & size will not be used! */
+/* partition for name idx, off & size will not be used! */
 #if (CONFIG_PTBL_MBR)
-    VIRTUAL_PARTITION_ELEMENT(MMC_MBR_NAME, MMC_MBR_OFFSET, MMC_MBR_SIZE),
+	VIRTUAL_PARTITION_ELEMENT(MMC_MBR_NAME, MMC_MBR_OFFSET, MMC_MBR_SIZE),
 #endif
-    VIRTUAL_PARTITION_ELEMENT(MMC_BOOT_NAME0, 0, 0),
-    VIRTUAL_PARTITION_ELEMENT(MMC_BOOT_NAME1, 0, 0),
+	VIRTUAL_PARTITION_ELEMENT(MMC_BOOT_NAME0, 0, 0),
+	VIRTUAL_PARTITION_ELEMENT(MMC_BOOT_NAME1, 0, 0),
 
     /* virtual partition in reserved partition, take care off and size */
 #ifdef CONFIG_AML_PARTITION
-	VIRTUAL_PARTITION_ELEMENT(MMC_TABLE_NAME, MMC_TABLE_OFFSET, MMC_TABLE_SIZE),
+	VIRTUAL_PARTITION_ELEMENT(MMC_TABLE_NAME, MMC_TABLE_OFFSET,
+				  MMC_TABLE_SIZE),
 #endif
-	VIRTUAL_PARTITION_ELEMENT(MMC_KEY_NAME, EMMCKEY_RESERVE_OFFSET, MMC_KEY_SIZE),
-	VIRTUAL_PARTITION_ELEMENT(MMC_PATTERN_NAME, CALI_PATTERN_OFFSET, CALI_PATTERN_SIZE),
+	VIRTUAL_PARTITION_ELEMENT(MMC_KEY_NAME, EMMCKEY_RESERVE_OFFSET,
+				  MMC_KEY_SIZE),
+	VIRTUAL_PARTITION_ELEMENT(MMC_PATTERN_NAME, CALI_PATTERN_OFFSET,
+				  CALI_PATTERN_SIZE),
 #ifndef DTB_BIND_KERNEL
 	VIRTUAL_PARTITION_ELEMENT(MMC_DTB_NAME, DTB_OFFSET, DTB_SIZE),
 #endif
 	VIRTUAL_PARTITION_ELEMENT(MMC_FASTBOOT_CONTEXT_NAME,
-			FASTBOOT_CONTEXT_OFFSET, FASTBOOT_CONTEXT_SIZE),
-	VIRTUAL_PARTITION_ELEMENT(MMC_DDR_PARAMETER_NAME,DDR_PARAMETER_OFFSET, DDR_PARAMETER_SIZE),
+				  FASTBOOT_CONTEXT_OFFSET,
+				  FASTBOOT_CONTEXT_SIZE),
+	VIRTUAL_PARTITION_ELEMENT(MMC_DDR_PARAMETER_NAME, DDR_PARAMETER_OFFSET,
+				  DDR_PARAMETER_SIZE),
+	// Partition for storing secure storage data.
+	// Rerserve the entry here so that merging of any upstream code change
+	// on the table will be made aware of this local change.
+	VIRTUAL_PARTITION_ELEMENT(MMC_SECURE_STORAGE_NAME,
+				  MMC_SECURE_STORAGE_OFFSET,
+				  MMC_SECURE_STORAGE_SIZE),
 };
 
 int get_emmc_partition_arraysize(void)
diff --git a/drivers/mmc/mmc_write.c b/drivers/mmc/mmc_write.c
index c53a624..82ade52 100644
--- a/drivers/mmc/mmc_write.c
+++ b/drivers/mmc/mmc_write.c
@@ -134,8 +134,20 @@
 	return 0;
 }
 
-static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start,
-		lbaint_t blkcnt, const void *src)
+static int mmc_set_blockcount(struct mmc *mmc, unsigned int blockcount)
+{
+	struct mmc_cmd cmd = { 0 };
+
+	cmd.cmdidx = MMC_CMD_SET_BLOCK_COUNT;
+	cmd.cmdarg = min(blockcount, (unsigned int)MMC_BLOCK_COUNT_MAX);
+	cmd.cmdarg |= MMC_RELIABLE_WRITE_ARG;
+	cmd.resp_type = MMC_RSP_R1;
+
+	return mmc_send_cmd(mmc, &cmd, NULL);
+}
+
+static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start, lbaint_t blkcnt,
+			      const void *src)
 {
 	struct mmc_cmd cmd;
 	struct mmc_data data;
@@ -146,13 +158,20 @@
 		return 0;
 	}
 
+	if (mmc->ext_csd[EXT_CSD_DATA_SECTOR_SIZE] !=
+	    EXT_CSD_DATA_SECTOR_SIZE_512) {
+		printf("MMC: mmc is not operating in 512 bytes sector size.\n");
+		return 0;
+	}
+
 	if (blkcnt == 0)
 		return 0;
-	else if (blkcnt == 1)
-		cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK;
-	else
-		cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
 
+	// Set block count and activate reliable write.
+	if (mmc_set_blockcount(mmc, blkcnt))
+		return 0;
+
+	cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
 	if (mmc->high_capacity)
 		cmd.cmdarg = start;
 	else
@@ -170,18 +189,7 @@
 		return 0;
 	}
 
-	/* SPI multiblock writes terminate using a special
-	 * token, not a STOP_TRANSMISSION request.
-	 */
-	if (!mmc_host_is_spi(mmc) && blkcnt > 1) {
-		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
-		cmd.cmdarg = 0;
-		cmd.resp_type = MMC_RSP_R1b;
-		if (mmc_send_cmd(mmc, &cmd, NULL)) {
-			printf("mmc fail to send stop cmd\n");
-			return 0;
-		}
-	}
+	// Pre-defined block count. No need for stop command.
 
 	/* Waiting for the ready status */
 	if (mmc_send_status(mmc, timeout))
@@ -220,9 +228,10 @@
 		return 0;
 
 	do {
-		cur = (blocks_todo > mmc->cfg->b_max) ?
-			mmc->cfg->b_max : blocks_todo;
-
+		cur = min(blocks_todo, (lbaint_t)mmc->cfg->b_max);
+		// Reliable write requires to set block count, which is only 2 bytes.
+		// Thus limit the write size to no more than 0xffff blocks.
+		cur = min((lbaint_t)MMC_BLOCK_COUNT_MAX, cur);
 		if (mmc_write_blocks(mmc, start, cur, src) != cur)
 			return 0;
 
diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig
index d489834..e7ca2ec 100644
--- a/drivers/tee/optee/Kconfig
+++ b/drivers/tee/optee/Kconfig
@@ -22,6 +22,13 @@
 	  The TA can support the "avb" subcommands "read_rb", "write"rb"
 	  and "is_unlocked".
 
+config OPTEE_RAW_PARTITION_IO
+	bool "Enable secure storage backed by a raw partition"
+	help
+	  Enables supplicant support for routing raw IO requests to a designated
+	  partition.
+	default n
+
 endmenu
 
 endif
diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
index 928d3f8..fb4b79e 100644
--- a/drivers/tee/optee/Makefile
+++ b/drivers/tee/optee/Makefile
@@ -3,3 +3,4 @@
 obj-y += core.o
 obj-y += supplicant.o
 obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o
+obj-$(CONFIG_OPTEE_RAW_PARTITION_IO) += raw_io.o
diff --git a/drivers/tee/optee/optee_msg_supplicant.h b/drivers/tee/optee/optee_msg_supplicant.h
index a0fb806..9891c2b 100644
--- a/drivers/tee/optee/optee_msg_supplicant.h
+++ b/drivers/tee/optee/optee_msg_supplicant.h
@@ -237,4 +237,37 @@
  * End of definitions for messages with .cmd == OPTEE_MSG_RPC_CMD_SOCKET
  */
 
+/*
+ * Raw partition IO
+ *
+ * Starting with 0x100 to avoid conflicts with the upstream:
+ * https://github.com/OP-TEE/optee_client/blob/master/tee-supplicant/src/optee_msg_supplicant.h
+ */
+#define OPTEE_MSG_RPC_CMD_RAW   0x100
+
+
+/*
+ * Definitions for messages with .cmd == OPTEE_MSG_RPC_CMD_RAW
+ */
+
+/*
+ * Read bytes from a RAW partition
+ *
+ * [in]     param[0].u.value.a	OPTEE_MRC_RAW_READ
+ * [in]     param[0].u.value.b	offset into the partition
+ * [in]     param[0].u.value.c	length in bytes
+ * [in]     param[1].u.tmem	buffer to receive the bytes
+ */
+#define OPTEE_MRC_RAW_READ      0
+
+/*
+ * Write bytes to a RAW partition
+ *
+ * [in]     param[0].u.value.a	OPTEE_MRC_RAW_WRITE
+ * [in]     param[0].u.value.b	offset into the partition
+ * [in]     param[0].u.value.c	length in bytes
+ * [in]     param[1].u.tmem	buffer containing the bytes
+ */
+#define OPTEE_MRC_RAW_WRITE     1
+
 #endif /* __OPTEE_MSG_SUPPLICANT_H */
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index 1db953a..ba9e04a 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -62,4 +62,21 @@
 }
 #endif
 
+#ifdef CONFIG_OPTEE_RAW_PARTITION_IO
+
+/**
+ * optee_suppl_cmd_raw_io() - basic IO to a designated raw partition
+ * @arg:	OP-TEE message holding the request details.
+ */
+void optee_suppl_cmd_raw_io(struct optee_msg_arg *arg);
+
+#else
+
+static inline void optee_suppl_cmd_raw_io(struct optee_msg_arg *arg)
+{
+	arg->ret = TEE_ERROR_NOT_IMPLEMENTED;
+}
+
+#endif  /* CONFIG_OPTEE_RAW_PARTITION_IO */
+
 #endif /* __OPTEE_PRIVATE_H */
diff --git a/drivers/tee/optee/raw_io.c b/drivers/tee/optee/raw_io.c
new file mode 100644
index 0000000..dfb8baa
--- /dev/null
+++ b/drivers/tee/optee/raw_io.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (c) 2022 Google
+ */
+
+#include <common.h>
+#include <emmc_partitions.h>
+#include <tee.h>
+#include <tee/optee.h>
+#include <zircon_uboot/partition.h>
+
+#include "optee_msg.h"
+#include "optee_msg_supplicant.h"
+#include "optee_private.h"
+
+/*
+ * Read `length` bytes from `offset` of a designated raw partition into
+ * `buffer`. Buffer size is guaranteed to be >= `length`.
+ */
+static u32 do_read(u64 offset, u64 length, void *buffer)
+{
+	zircon_partition *part = zircon_get_partition(MMC_SECURE_STORAGE_NAME);
+	if (!part) {
+		return TEE_ERROR_ITEM_NOT_FOUND;
+	}
+
+	if (!part->read) {
+		printf("Optee raw IO: no read implementation\n");
+		zircon_free_partition(part);
+		return TEE_ERROR_NOT_SUPPORTED;
+	}
+
+	// Boundary check will be performed in read(). The API supports unaligned
+	// read.
+	int res = part->read(part, offset, buffer, length);
+	if (res) {
+		printf("Optee raw IO: read failed. %d\n", res);
+		zircon_free_partition(part);
+		return TEE_ERROR_GENERIC;
+	}
+
+	zircon_free_partition(part);
+	return TEE_SUCCESS;
+}
+
+/*
+ * Write `length` bytes from `buffer` into a designated raw partition at
+ * `offset`. Buffer size is guaranteed to be >= `length`.
+ */
+static u32 do_write(u64 offset, u64 length, const void *buffer)
+{
+	zircon_partition *part = zircon_get_partition(MMC_SECURE_STORAGE_NAME);
+	if (!part) {
+		return TEE_ERROR_ITEM_NOT_FOUND;
+	}
+
+	if (!part->write) {
+		printf("Optee raw IO: no write implementation\n");
+		zircon_free_partition(part);
+		return TEE_ERROR_NOT_SUPPORTED;
+	}
+
+	// Boundary check will be performed in write(). The API supports unaligned
+	// write.
+	int res = part->write(part, offset, buffer, length);
+	if (res) {
+		printf("Optee raw IO: write failed. %d\n", res);
+		zircon_free_partition(part);
+		return TEE_ERROR_GENERIC;
+	}
+
+	zircon_free_partition(part);
+	return TEE_SUCCESS;
+}
+
+void optee_suppl_cmd_raw_io(struct optee_msg_arg *arg)
+{
+	u64 offset;
+	u64 length;
+	void *buffer;
+	u64 buffer_size;
+
+	/* The supplicant is part of the REE<>TEE communications stack. */
+	arg->ret_origin = TEE_ORIGIN_COMMS;
+
+	if (!(arg->num_params == 2 &&
+	      arg->params[0].attr == OPTEE_MSG_ATTR_TYPE_VALUE_INPUT)) {
+		arg->ret = TEE_ERROR_BAD_PARAMETERS;
+		return;
+	}
+
+	offset = arg->params[0].u.value.b;
+	length = arg->params[0].u.value.c;
+	buffer = (u8 *)arg->params[1].u.tmem.buf_ptr;
+	buffer_size = arg->params[1].u.tmem.size;
+	if (!(buffer && buffer_size > 0 && buffer_size >= length)) {
+		arg->ret = TEE_ERROR_BAD_PARAMETERS;
+		return;
+	}
+
+	switch (arg->params[0].u.value.a) {
+	case OPTEE_MRC_RAW_READ:
+		if (arg->params[1].attr != OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT) {
+			arg->ret = TEE_ERROR_BAD_PARAMETERS;
+			return;
+		}
+
+		arg->ret = do_read(offset, length, buffer);
+		break;
+	case OPTEE_MRC_RAW_WRITE:
+		if (arg->params[1].attr != OPTEE_MSG_ATTR_TYPE_TMEM_INPUT) {
+			arg->ret = TEE_ERROR_BAD_PARAMETERS;
+			return;
+		}
+
+		arg->ret = do_write(offset, length, buffer);
+		break;
+	default:
+		arg->ret = TEE_ERROR_BAD_PARAMETERS;
+		break;
+	}
+
+	return;
+}
+
+#if defined(DEV_BUILD_CONFIG)
+int optee_suppl_cmd_raw_io_wrapper(enum optee_suppl_cmd_raw_io_op op,
+				   void *buffer, size_t offset, size_t length)
+{
+	struct optee_suppl_cmd_raw_io_test_param {
+		struct optee_msg_arg arg;
+		struct optee_msg_param params[2];
+	} test_arg = {
+		.arg = {
+			.num_params = 2,
+		},
+		.params[0] = {
+			.attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT,
+			.u.value.b = offset,
+			.u.value.c = length,
+		},
+		.params[1] = {
+			.u.tmem.buf_ptr = (u64)buffer,
+			.u.tmem.size = length,
+		},
+	};
+	switch (op) {
+	case optee_suppl_cmd_raw_io_read:
+		test_arg.arg.params[1].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT;
+		test_arg.arg.params[0].u.value.a = OPTEE_MRC_RAW_READ;
+		break;
+	case optee_suppl_cmd_raw_io_write:
+		test_arg.arg.params[1].attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT;
+		test_arg.arg.params[0].u.value.a = OPTEE_MRC_RAW_WRITE;
+		break;
+	default:
+		return -1;
+	}
+
+	optee_suppl_cmd_raw_io(&test_arg.arg);
+	return test_arg.arg.ret == TEE_SUCCESS ? 0 : -1;
+}
+#endif
\ No newline at end of file
diff --git a/drivers/tee/optee/supplicant.c b/drivers/tee/optee/supplicant.c
index d5338b2..03351aa 100644
--- a/drivers/tee/optee/supplicant.c
+++ b/drivers/tee/optee/supplicant.c
@@ -75,6 +75,10 @@
 	case OPTEE_MSG_RPC_CMD_RPMB:
 		optee_suppl_cmd_rpmb(dev, arg);
 		break;
+	case OPTEE_MSG_RPC_CMD_RAW:
+		/* Raw partition IO */
+		optee_suppl_cmd_raw_io(arg);
+		break;
 	default:
 		arg->ret = TEE_ERROR_NOT_IMPLEMENTED;
 	}
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
index 3e5f057..1d62557 100644
--- a/drivers/usb/gadget/f_fastboot.c
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -32,6 +32,7 @@
 #include <mmc.h>
 #include <stdalign.h>
 #include <tee/ta_vx.h>
+#include <tee/optee.h>
 #include <version.h>
 #include <zircon_uboot/boot_args.h>
 #include <zircon_uboot/bootimg.h>
@@ -550,6 +551,23 @@
 	return false;
 }
 
+#if defined(DEV_BUILD_CONFIG)
+static bool fb_run_again_to_confirm_or_fail(void)
+{
+	static uint32_t nth_run = 0;
+
+	nth_run++;
+
+	if (nth_run % 2) {
+		fastboot_fail("RERUN COMMAND TO CONFIRM YOU KNOW WHAT YOU ARE "
+			      "DOING!");
+		return true;
+	}
+
+	return false;
+}
+#endif  // DEV_BUILD_CONFIG
+
 static void cb_reboot(struct usb_ep *ep, struct usb_request *req)
 {
 	char *cmd = req->buf;
@@ -861,6 +879,17 @@
 	return unlocked ? "no" : "yes";
 }
 
+static const char *get_dev_key_enabled(const char *arg)
+{
+	uint32_t vars = 0;
+
+	if (ta_vx_getvar_all(&vars)) {
+		return "error";
+	}
+
+	return !!(vars & VX_VAR_DEV_KEY_ENABLED)? "yes" : "no";
+}
+
 static const char *get_rpmb_key_programmed(const char *arg)
 {
 	uint32_t flags = 0;
@@ -894,6 +923,17 @@
 	return (flags & VX_RPMB_PROVISIONING_ALLOWED) ? "yes": "no";
 }
 
+static const char *get_rpmb_protection_level(const char *arg)
+{
+	uint32_t flags = 0;
+
+	if (ta_vx_get_rpmb_status(&flags, /* out_write_count= */NULL)) {
+		return "error";
+	}
+
+	return (flags & VX_RPMB_REROUTING_TRAFFIC) ? "100": "1000";
+}
+
 static const char *get_rpmb_total_bytes(const char *arg)
 {
 	uint8_t rpmb_size_mult;
@@ -961,6 +1001,10 @@
 		.func = get_current_slot,
 	},
 	{
+		.name = "dev-key-enabled",
+		.func = get_dev_key_enabled,
+	},
+	{
 		.name = "emmc-firmware-revision",
 		.func = get_emmc_firmware_revision,
 	},
@@ -1001,6 +1045,10 @@
 		.func = get_rpmb_key_verified,
 	},
 	{
+		.name = "rpmb-protection-level",
+		.func = get_rpmb_protection_level,
+	},
+	{
 		.name = "rpmb-provisioning-allowed",
 		.func = get_rpmb_provisioning_allowed,
 	},
@@ -1526,6 +1574,102 @@
 	fastboot_okay(NULL);
 }
 
+#if defined(DEV_BUILD_CONFIG)
+static void cb_kill_rpmb_till_reboot(struct usb_ep *ep,
+					struct usb_request *req)
+{
+	int rc;
+	uint32_t flags = 0;
+
+	rc = ta_vx_reroute_rpmb_till_reboot();
+	if (rc) {
+		fastboot_fail("Request somehow got rejected.");
+		return;
+	}
+
+	rc = ta_vx_get_rpmb_status(&flags, /* out_write_count= */NULL);
+	if (rc) {
+		fastboot_fail("Failed to query RPMB status.");
+		return;
+	}
+
+	if (!(flags & VX_RPMB_REROUTING_TRAFFIC)) {
+		fastboot_fail("Request went through but didn't take effect.");
+		return;
+	}
+
+	fastboot_okay(NULL);
+}
+
+static void cb_kill_rpmb(struct usb_ep *ep, struct usb_request *req)
+{
+	int rc;
+	uint32_t flags = 0;
+
+	if (fb_run_again_to_confirm_or_fail()) {
+		return;
+	}
+
+	rc = ta_vx_reroute_rpmb_to_software();
+	if (rc) {
+		fastboot_fail("Rejected. Have you killed RPMB before?");
+		return;
+	}
+
+	rc = ta_vx_get_rpmb_status(&flags, /* out_write_count= */NULL);
+	if (rc) {
+		fastboot_fail("Failed to query RPMB status.");
+		return;
+	}
+
+	if (!(flags & VX_RPMB_REROUTING_TRAFFIC)) {
+		fastboot_fail("Request went through but didn't take effect!");
+		return;
+	}
+
+	fastboot_okay(NULL);
+}
+
+static void cb_unkill_rpmb(struct usb_ep *ep, struct usb_request *req)
+{
+	int rc;
+	uint32_t flags = 0;
+	static bool just_need_fastboot_okay = false;
+
+	if (just_need_fastboot_okay) {
+		just_need_fastboot_okay = false;
+		fastboot_okay(NULL);
+		return;
+	}
+
+	rc = ta_vx_get_rpmb_status(&flags, /* out_write_count= */NULL);
+	if (rc) {
+		fastboot_fail("Failed to query RPMB status.");
+		return;
+	}
+
+	if (!(flags & VX_RPMB_REROUTING_TRAFFIC)) {
+		fastboot_fail("Your RPMB is alive, maybe kill it first?");
+		return;
+	}
+
+	if (fb_run_again_to_confirm_or_fail()) {
+		return;
+	}
+
+	rc = ta_vx_reroute_rpmb_to_hardware();
+	if (rc) {
+		fastboot_fail("Request somehow got rejected.");
+		return;
+	}
+
+	/* Reboot is required to take effect because it is not safe or reliable
+	 * for TEE to revert rerouting. */
+	just_need_fastboot_okay = true;
+	fastboot_info("Please reboot device to take effect.");
+}
+#endif  /* defined(DEV_BUILD_CONFIG) */
+
 static void cb_provision_rpmb(struct usb_ep *ep,
 				struct usb_request *req)
 {
@@ -1621,12 +1765,7 @@
 
 static void cb_vx_lock(struct usb_ep *ep, struct usb_request *req)
 {
-	if (zircon_clear_stored_cmdline()) {
-		FB_ERR("Failed to clear stored boot args\n");
-		fastboot_fail("Failed to clear boot args");
-		return;
-	}
-
+	// ta_vx_lock() will clear any stored boot args.
 	if (ta_vx_lock()) {
 		FB_ERR("Failed to lock\n");
 		fastboot_fail("Failed to lock");
@@ -1842,6 +1981,57 @@
 	fastboot_okay(NULL);
 }
 
+#if defined(DEV_BUILD_CONFIG)
+static void cb_optee_suppl_cmd_raw_io(struct usb_ep *ep,
+				      struct usb_request *req)
+{
+	// usage: oem optee-supl-cmd-raw-io <"read"|"write"> <off> <len>
+	const char usage[] = "Usage: optee-supl-cmd-raw-io <read|write> <off> <len>";
+
+	char *name = req->buf;
+
+	// skip past "oem optee-supl-cmd-raw-io"
+	if (!(strsep(&name, " ") && strsep(&name, " ") && name)) {
+		fastboot_fail(usage);
+		return;
+	}
+
+	enum optee_suppl_cmd_raw_io_op op = optee_suppl_cmd_raw_io_write;
+	if (strncmp(name, "read", strlen("read")) == 0) {
+		op = optee_suppl_cmd_raw_io_read;
+	} else if (strncmp(name, "write", strlen("write") - 1)) {
+		fastboot_fail(usage);
+		return;
+	}
+
+	// offset
+	if (!strsep(&name, " ")) {
+		fastboot_fail(usage);
+		return;
+	}
+	uint64_t offset = simple_strtoul(name, NULL, 16);
+
+	// length
+	if (!strsep(&name, " ")) {
+		fastboot_fail(usage);
+		return;
+	}
+	uint64_t length = simple_strtoul(name, NULL, 16);
+
+	int res = optee_suppl_cmd_raw_io_wrapper(
+		op, (void *)CONFIG_FASTBOOT_BUF_ADDR, offset, length);
+	if (res) {
+		fastboot_fail("IO failed");
+		return;
+	}
+
+	if (op == optee_suppl_cmd_raw_io_read)
+		upload_size = length;
+
+	fastboot_okay("");
+}
+#endif
+
 static void cb_staged_bootloader_file(struct usb_ep *ep,
 				      struct usb_request *req)
 {
@@ -2056,6 +2246,20 @@
 		.cb = cb_erase,
 	},
 #endif
+#if defined(DEV_BUILD_CONFIG)
+	{
+		.cmd = "oem kill-rpmb-till-reboot",
+		.cb = cb_kill_rpmb_till_reboot,
+	},
+	{
+		.cmd = "oem kill-rpmb",
+		.cb = cb_kill_rpmb,
+	},
+	{
+		.cmd = "oem unkill-rpmb",
+		.cb = cb_unkill_rpmb,
+	},
+#endif
 	{
 		.cmd = "oem stage-partition",
 		.cb = cb_stage_partition,
@@ -2102,6 +2306,12 @@
 		.cmd = "oem gpt-update",
 		.cb = cb_gpt_update,
 	},
+#if defined(DEV_BUILD_CONFIG)
+	{
+		.cmd = "oem optee-supl-cmd-raw-io",
+		.cb = cb_optee_suppl_cmd_raw_io,
+	},
+#endif
 #ifdef CONFIG_FACTORY_BOOT_KVS
 	{
 		.cmd = "oem factory-boot-kvs-get",
diff --git a/include/emmc_partitions.h b/include/emmc_partitions.h
index e8f4bc5..d964586 100644
--- a/include/emmc_partitions.h
+++ b/include/emmc_partitions.h
@@ -114,6 +114,10 @@
 #define GPT_LBA_COUNT 34
 #define GPT_TOTAL_SIZE (GPT_LBA_COUNT * 512)
 
+#define MMC_SECURE_STORAGE_NAME     "secure-storage"
+#define MMC_SECURE_STORAGE_OFFSET  (SZ_1M * 32)
+#define MMC_SECURE_STORAGE_SIZE    (SZ_1M * 32)
+
 struct virtual_partition {
 	char name[MAX_MMC_PART_NAME_LEN];
 	uint64_t offset;
diff --git a/include/mmc.h b/include/mmc.h
index 8c5ffa7..90a6803 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -165,6 +165,8 @@
 #define MMC_DISCARD_ARG		0x00000003
 #define MMC_SECURE_TRIM1_ARG	0x80000001
 #define MMC_SECURE_TRIM2_ARG	0x80008000
+#define MMC_RELIABLE_WRITE_ARG	(1 << 31)
+#define MMC_BLOCK_COUNT_MAX	(0xFFFF)
 
 #define MMC_STATUS_MASK		(~0x0206BF7F)
 #define MMC_STATUS_SWITCH_ERROR	(1 << 7)
@@ -208,6 +210,7 @@
  * EXT_CSD fields
  */
 #define EXT_CSD_CLASS_6_CTRL        59  /*R/W/E_P*/
+#define EXT_CSD_DATA_SECTOR_SIZE    61
 #define EXT_CSD_ENH_START_ADDR		136	/* R/W */
 #define EXT_CSD_ENH_SIZE_MULT		140	/* R/W */
 #define EXT_CSD_GP_SIZE_MULT		143	/* R/W */
@@ -301,6 +304,8 @@
 #define EXT_CSD_WR_DATA_REL_USR		(1 << 0)	/* user data area WR_REL */
 #define EXT_CSD_WR_DATA_REL_GP(x)	(1 << ((x)+1))	/* GP part (x+1) WR_REL */
 
+#define EXT_CSD_DATA_SECTOR_SIZE_512 0
+
 #define R1_ILLEGAL_COMMAND		(1 << 22)
 #define R1_APP_CMD			(1 << 5)
 
diff --git a/include/tee/optee.h b/include/tee/optee.h
index 9446928..b4a35d1 100644
--- a/include/tee/optee.h
+++ b/include/tee/optee.h
@@ -67,4 +67,18 @@
 }
 #endif
 
+#if defined(DEV_BUILD_CONFIG)
+enum optee_suppl_cmd_raw_io_op {
+	optee_suppl_cmd_raw_io_read,
+	optee_suppl_cmd_raw_io_write,
+};
+
+// A helper based on `optee_suppl_cmd_raw_io()` for writing/reading data
+// to/from the reserved partition. The helper is mainly for firmware testing by
+// fastboot oem command
+// `fastboot oem optee-supl-cmd-raw-io <"read"|"write"> <off> <len>`
+int optee_suppl_cmd_raw_io_wrapper(enum optee_suppl_cmd_raw_io_op op,
+				   void *buffer, size_t offset, size_t length);
+#endif
+
 #endif /* _OPTEE_H */
diff --git a/include/tee/ta_vx.h b/include/tee/ta_vx.h
index fe2e589..6744ceb 100644
--- a/include/tee/ta_vx.h
+++ b/include/tee/ta_vx.h
@@ -16,6 +16,14 @@
 	VX_UNLOCKED = 1,
 } vx_lock_state_t;
 
+/* VX variables. */
+typedef enum vx_variable {
+	VX_VAR_NONE = 0x0,
+	VX_VAR_DEV_KEY_ENABLED = 0x01,
+	VX_VAR_OUT_OF_BOOTLOADER = 0x02,
+	VX_VAR_EPHEMERALLY_UNLOCKED = 0x04,
+} vx_variable_t;
+
 /*
  * Gets the rollback index corresponding to the given rollback index slot.
  *
@@ -142,9 +150,17 @@
 #define TA_VX_CMD_READ_PERM_ATTR	13
 
 enum vx_rpmb_status {
+	/* eMMC claims the RPMB key has been programmed. */
 	VX_RPMB_AUTH_KEY_PROGRAMMED  = 0x01,
+
+	/* RPMB key has been verified by the firmware. */
 	VX_RPMB_AUTH_KEY_VERIFIED    = 0x02,
+
+	/* RPMB provisioning is no longer allowed (mitigates key phishing). */
 	VX_RPMB_PROVISIONING_ALLOWED = 0x04,
+
+	/* Reroute RPMB traffic (to REE controlled storage). */
+	VX_RPMB_REROUTING_TRAFFIC    = 0x08,
 };
 
 /* Reads the RPMB provisioning status.
@@ -210,8 +226,13 @@
 #define TA_VX_CMD_LOCK_PERM_ATTR 17
 
 typedef enum {
+	/* Programmed to RPMB. */
 	VX_PERM_ATTR_PROGRAMMED = (1 << 0),
+
+	/* SHA256 digest stored in OTP. */
 	VX_PERM_ATTR_LOCKED = (1 << 1),
+
+	/* Hardcoded. */
 	VX_PERM_ATTR_HARDCODED = (1 << 2),
 } perm_attr_status_t;
 
@@ -235,40 +256,8 @@
  */
 #define TA_VX_CMD_DELETE_PERM_ATTR 19
 
-typedef enum vx_option {
-	/* If set, automatically provisions RPMB if RPMB is not already
-	 * provisioned. Default unset. */
-	VX_OPTION_AUTO_PROVISION_RPMB = (1 << 1),
-	/* For testing use. e.g. The Bootloader uses this to make sure options
-	 * CAN NOT be set after issuing CMD_EXIT_BOOTLOADER. */
-	VX_OPTION_FOR_TESTING         = (1 << 3),
-} vx_option_t;
-
-/*
- * Sets certain options for the VX TA.
- *
- * This command is only available to the Bootloader.
- *
- * in  params[0].value.a:  Options flags to set (VX_OPTION_*).
- *
- * Returns
- *   TEE_SUCCESS on success,
- *   TEE_ERROR_* otherwise.
- */
-#define TA_VX_CMD_SET_OPTIONS	20
-
-/*
- * Unsets certain options for the VX TA.
- *
- * This command is only available to the Bootloader.
- *
- * in  params[0].value.a:  Options flags to unset (VX_OPTION_*).
- *
- * Returns
- *   TEE_SUCCESS on success,
- *   TEE_ERROR_* otherwise.
- */
-#define TA_VX_CMD_UNSET_OPTIONS	21
+#define TA_VX_CMD_SET_OPTIONS_DEPRECATED	20
+#define TA_VX_CMD_UNSET_OPTIONS_DEPRECATED	21
 
 /* Provisions the USBBOOT password hash.
  *
@@ -303,6 +292,76 @@
  */
 #define TA_VX_CMD_GET_USBBOOT_STATUS	23
 
+/* Sets the OTA Config option.
+ *
+ * Args:
+ *   in params[0].value.a: config option.
+ *
+ * Returns:
+ *   TEE_SUCCESS if the option value was set successfully.
+ *   Otherwise TEE_ERROR_*.
+ */
+#define TA_VX_CMD_OTA_CONFIG_SET 24
+
+/* Reads the OTA Config option.
+ *
+ * Args:
+ *   in params[0].value.a: default value for option, which will be returned if
+ *      there is no stored value in secure storage.
+ *   out params[1].value.a: config option
+ *
+ * Returns:
+ *   TEE_SUCCESS if the option value was read successfully.
+ *   Otherwise TEE_ERROR_*.
+ */
+#define TA_VX_CMD_OTA_CONFIG_GET 25
+
+/*
+ * Delete the OTA Config.
+ *
+ * Returns:
+ *   TEE_SUCCESS if the config was deleted successfully.
+ *   Otherwise TEE_ERROR_*.
+ */
+#define TA_VX_CMD_OTA_CONFIG_DELETE 26
+
+/*
+ * Query VX variables.
+ *
+ * out	params[0].value.a:	or'ed VX_VAR_*
+ */
+#define TA_VX_CMD_GETVAR_ALL 27
+
+/*
+ * Force reroute RPMB until reboot.
+ *
+ * For testing on dev-key hardware only.
+ *
+ * Returns:
+ *   TEE_SUCCESS if successful. Otherwise TEE_ERROR_*.
+ */
+#define TA_VX_CMD_REROUTE_RPMB_TILL_REBOOT 28
+
+/*
+ * Force reroute RPMB to REE-controlled secure storage.
+ *
+ * Works on dev-key hardware only.
+ *
+ * Returns:
+ *   TEE_SUCCESS if successful. Otherwise TEE_ERROR_*.
+ */
+#define TA_VX_CMD_REROUTE_RPMB_TO_SOFTWARE 29
+
+/*
+ * Force route RPMB to hardware (eMMC).
+ *
+ * Works on dev-key hardware only.
+ *
+ * Returns:
+ *   TEE_SUCCESS if successful. Otherwise TEE_ERROR_*.
+ */
+#define TA_VX_CMD_REROUTE_RPMB_TO_HARDWARE 30
+
 /**
  * Persistent Nonces Management.
  */
diff --git a/include/tee/ta_vx_helper.h b/include/tee/ta_vx_helper.h
index 2b78f3d..d2bf2f3 100644
--- a/include/tee/ta_vx_helper.h
+++ b/include/tee/ta_vx_helper.h
@@ -27,6 +27,13 @@
 int ta_vx_lock(void);
 
 /**
+ * ta_vx_lock_if_ephemerally_unlocked()
+ *
+ * Return: 0 if successful, non-zero error code on failure.
+ */
+int ta_vx_lock_if_ephemerally_unlocked(void);
+
+/**
  * ta_vx_unlock() - Unlocks the device.
  *
  * Return: 0 if successful, non-zero error code on failure.
@@ -101,10 +108,10 @@
 int ta_vx_cprng_draw(void *buf, size_t buf_len);
 
 /**
- * ta_vx_exit_bootloader() - Finalize verified execution policies and
+ * ta_vx_exit_bootloader_or_panic() - Finalize verified execution policies and
  * 		configurations. Panics on any error.
  */
-void ta_vx_exit_bootloader(void);
+void ta_vx_exit_bootloader_or_panic(void);
 
 /**
  * ta_vx_read_perm_attr() - Read permanent attributes
@@ -149,6 +156,28 @@
 int ta_vx_provision_rpmb(void);
 
 /**
+ * ta_vx_reroute_rpmb_till_reboot() - Reroute RPMB traffic till reboot for
+ * testing.
+ *
+ * Return: 0 if successful, non-zero code otherwise.
+ */
+int ta_vx_reroute_rpmb_till_reboot(void);
+
+/**
+ * ta_vx_reroute_rpmb_to_software() - Persistently reroute RPMB traffic.
+ *
+ * Return: 0 if successful, non-zero code otherwise.
+ */
+int ta_vx_reroute_rpmb_to_software(void);
+
+/**
+ * ta_vx_reroute_rpmb_to_hardware() - Permanently restore RPMB traffic.
+ *
+ * Return: 0 if successful, non-zero code otherwise.
+ */
+int ta_vx_reroute_rpmb_to_hardware(void);
+
+/**
  * ta_vx_read_perm_attr_hash() - Read permanent attributes hash
  * @buf: Buffer to write value into.
  * @buf_len: Length of buf.
@@ -197,6 +226,15 @@
  */
 int ta_vx_get_usbboot_status(uint32_t *status);
 
+/**
+ * ta_vx_getvar_all() - Query hardware capabilities
+ *
+ * @out_caps: Receives VX_HARDWARE_* flags as defined in tee/ta_vx.h
+ *
+ * Return: 0 if successful, non-zero error code otherwise.
+ */
+int ta_vx_getvar_all(uint32_t *out_caps);
+
 #ifdef CONFIG_TA_VX_TESTS
 /**
  * ta_vx_run_tests() - Run tests defined in the VX TA.
diff --git a/include/zircon_uboot/partition_internal.h b/include/zircon_uboot/partition_internal.h
index 55286e2..76fbe32 100644
--- a/include/zircon_uboot/partition_internal.h
+++ b/include/zircon_uboot/partition_internal.h
@@ -18,6 +18,9 @@
 // erase if the user can write anyway.
 #define ZIRCON_PARTITION_ACCESS_FLAG_ERASE (1 << 0)
 #define ZIRCON_PARTITION_ACCESS_FLAG_WRITE (1 << 1)
+// write/erase access is only allowed when variant is dev, regardless of
+// lock/unlock status.
+#define ZIRCON_PARTITION_ACCESS_DEV_ONLY (1 << 2)
 
 typedef enum {
 	// Alias for partition within GPT on eMMC USER hardware partition.