xref: /optee_os/core/tee/tee_rpmb_fs.c (revision dc2cf47a7607b4f7d1227c54524998216cba3f8e)
11bb92983SJerome Forissier // SPDX-License-Identifier: BSD-2-Clause
2b0104773SPascal Brand /*
3b0104773SPascal Brand  * Copyright (c) 2014, STMicroelectronics International N.V.
4b0104773SPascal Brand  */
5b0104773SPascal Brand 
60c96a71dSJerome Forissier #include <assert.h>
785076371SEtienne Carriere #include <config.h>
8e1770e71SJens Wiklander #include <crypto/crypto.h>
99cc10bc9SJens Wiklander #include <kernel/huk_subkey.h>
10142d5af2SVolodymyr Babchuk #include <kernel/misc.h>
11142d5af2SVolodymyr Babchuk #include <kernel/msg_param.h>
120c96a71dSJerome Forissier #include <kernel/mutex.h>
13d13278b8SEtienne Carriere #include <kernel/panic.h>
14b2215adfSJens Wiklander #include <kernel/tee_common.h>
150c96a71dSJerome Forissier #include <kernel/tee_common_otp.h>
16b2215adfSJens Wiklander #include <kernel/tee_misc.h>
170c96a71dSJerome Forissier #include <kernel/thread.h>
18b2284b11SJens Wiklander #include <kernel/user_access.h>
1964c6d291SEtienne Carriere #include <mempool.h>
200c96a71dSJerome Forissier #include <mm/core_memprot.h>
21142d5af2SVolodymyr Babchuk #include <mm/mobj.h>
22b0104773SPascal Brand #include <mm/tee_mm.h>
236009538cSJens Wiklander #include <optee_rpc_cmd.h>
24b0104773SPascal Brand #include <stdlib.h>
25a8a78b85SJerome Forissier #include <string_ext.h>
26b2215adfSJens Wiklander #include <string.h>
27a8a78b85SJerome Forissier #include <sys/queue.h>
28*dc2cf47aSEtienne Carriere #include <string_ext.h>
29add06171SJerome Forissier #include <tee/tee_fs.h>
30add06171SJerome Forissier #include <tee/tee_fs_key_manager.h>
31b2215adfSJens Wiklander #include <tee/tee_pobj.h>
32b2215adfSJens Wiklander #include <tee/tee_svc_storage.h>
330c96a71dSJerome Forissier #include <trace.h>
340c96a71dSJerome Forissier #include <util.h>
35c3e8a2d9SJerome Forissier 
36b0104773SPascal Brand #define RPMB_STORAGE_START_ADDRESS      0
37b0104773SPascal Brand #define RPMB_FS_FAT_START_ADDRESS       512
38b0104773SPascal Brand #define RPMB_BLOCK_SIZE_SHIFT           8
398d22c45dSPeifu Jiang #define RPMB_CID_PRV_OFFSET             9
408d22c45dSPeifu Jiang #define RPMB_CID_CRC_OFFSET             15
41b0104773SPascal Brand 
42b0104773SPascal Brand #define RPMB_FS_MAGIC                   0x52504D42
43a8a78b85SJerome Forissier #define FS_VERSION                      2
44b0104773SPascal Brand 
45b0104773SPascal Brand #define FILE_IS_ACTIVE                  (1u << 0)
46b0104773SPascal Brand #define FILE_IS_LAST_ENTRY              (1u << 1)
47b0104773SPascal Brand 
480c96a71dSJerome Forissier #define TEE_RPMB_FS_FILENAME_LENGTH 224
490c96a71dSJerome Forissier 
5064c6d291SEtienne Carriere #define TMP_BLOCK_SIZE			4096U
5164c6d291SEtienne Carriere 
52a8fb1651SJens Wiklander #define RPMB_MAX_RETRIES		10
53a8fb1651SJens Wiklander 
54b0104773SPascal Brand /**
555f68d784SManuel Huber  * Utilized when caching is enabled, i.e., when CFG_RPMB_FS_CACHE_ENTRIES > 0.
565f68d784SManuel Huber  * Cache size + the number of entries that are repeatedly read in and buffered
575f68d784SManuel Huber  * once the cache is full.
585f68d784SManuel Huber  */
595f68d784SManuel Huber #define RPMB_BUF_MAX_ENTRIES (CFG_RPMB_FS_CACHE_ENTRIES + \
605f68d784SManuel Huber 			      CFG_RPMB_FS_RD_ENTRIES)
615f68d784SManuel Huber 
625f68d784SManuel Huber /**
63b0104773SPascal Brand  * FS parameters: Information often used by internal functions.
64b0104773SPascal Brand  * fat_start_address will be set by rpmb_fs_setup().
65b0104773SPascal Brand  * rpmb_fs_parameters can be read by any other function.
66b0104773SPascal Brand  */
67b0104773SPascal Brand struct rpmb_fs_parameters {
68b0104773SPascal Brand 	uint32_t fat_start_address;
69a8a78b85SJerome Forissier 	uint32_t max_rpmb_address;
70b0104773SPascal Brand };
71b0104773SPascal Brand 
72b0104773SPascal Brand /**
73b0104773SPascal Brand  * File entry for a single file in a RPMB_FS partition.
74b0104773SPascal Brand  */
75b0104773SPascal Brand struct rpmb_fat_entry {
76b0104773SPascal Brand 	uint32_t start_address;
77b0104773SPascal Brand 	uint32_t data_size;
78b0104773SPascal Brand 	uint32_t flags;
79e92de4caSJerome Forissier 	uint32_t unused;
809e84c17eSJerome Forissier 	uint8_t fek[TEE_FS_KM_FEK_SIZE];
81a8a78b85SJerome Forissier 	char filename[TEE_RPMB_FS_FILENAME_LENGTH];
82b0104773SPascal Brand };
83b0104773SPascal Brand 
84b0104773SPascal Brand /**
855f68d784SManuel Huber  * Structure that describes buffered/cached FAT FS entries in RPMB storage.
865f68d784SManuel Huber  * This structure is used in functions traversing the FAT FS.
875f68d784SManuel Huber  */
885f68d784SManuel Huber struct rpmb_fat_entry_dir {
895f68d784SManuel Huber 	/*
905f68d784SManuel Huber 	 * Buffer storing the FAT FS entries read in from RPMB storage. It
915f68d784SManuel Huber 	 * includes the optional cache entries (CFG_RPMB_FS_CACHE_ENTRIES)
925f68d784SManuel Huber 	 * and entries temporary read for current FAT FS traversal
935f68d784SManuel Huber 	 * (CFG_RPMB_FS_RD_ENTRIES) when not found from cached entries.
945f68d784SManuel Huber 	 */
955f68d784SManuel Huber 	struct rpmb_fat_entry *rpmb_fat_entry_buf;
965f68d784SManuel Huber 	/* Current index of FAT FS entry to read from buffer. */
975f68d784SManuel Huber 	uint32_t idx_curr;
985f68d784SManuel Huber 	/* Total number of FAT FS entries in buffer. */
995f68d784SManuel Huber 	uint32_t num_buffered;
1005f68d784SManuel Huber 	/* Total number of FAT FS entries read during traversal. */
1015f68d784SManuel Huber 	uint32_t num_total_read;
1025f68d784SManuel Huber 	/* Indicates that last FAT FS entry was read. */
1035f68d784SManuel Huber 	bool last_reached;
1045f68d784SManuel Huber };
1055f68d784SManuel Huber 
1065f68d784SManuel Huber /**
107b0104773SPascal Brand  * FAT entry context with reference to a FAT entry and its
108b0104773SPascal Brand  * location in RPMB.
109b0104773SPascal Brand  */
110a8a78b85SJerome Forissier struct rpmb_file_handle {
111b0104773SPascal Brand 	struct rpmb_fat_entry fat_entry;
112b399f70bSJens Wiklander 	const TEE_UUID *uuid;
113a8a78b85SJerome Forissier 	char filename[TEE_RPMB_FS_FILENAME_LENGTH];
1140c96a71dSJerome Forissier 	/* Address for current entry in RPMB */
115b0104773SPascal Brand 	uint32_t rpmb_fat_address;
116b0104773SPascal Brand };
117b0104773SPascal Brand 
118b0104773SPascal Brand /**
119b0104773SPascal Brand  * RPMB_FS partition data
120b0104773SPascal Brand  */
121b0104773SPascal Brand struct rpmb_fs_partition {
122b0104773SPascal Brand 	uint32_t rpmb_fs_magic;
123b0104773SPascal Brand 	uint32_t fs_version;
124b0104773SPascal Brand 	uint32_t write_counter;
125b0104773SPascal Brand 	uint32_t fat_start_address;
126b0104773SPascal Brand 	/* Do not use reserved[] for other purpose than partition data. */
127b0104773SPascal Brand 	uint8_t reserved[112];
128b0104773SPascal Brand };
129b0104773SPascal Brand 
130a8a78b85SJerome Forissier /**
131b2215adfSJens Wiklander  * A node in a list of directory entries.
132a8a78b85SJerome Forissier  */
133a8a78b85SJerome Forissier struct tee_rpmb_fs_dirent {
134a8a78b85SJerome Forissier 	struct tee_fs_dirent entry;
135a8a78b85SJerome Forissier 	SIMPLEQ_ENTRY(tee_rpmb_fs_dirent) link;
136a8a78b85SJerome Forissier };
137a8a78b85SJerome Forissier 
138a8a78b85SJerome Forissier /**
139a8a78b85SJerome Forissier  * The RPMB directory representation. It contains a queue of
140a8a78b85SJerome Forissier  * RPMB directory entries: 'next'.
141a8a78b85SJerome Forissier  * The current pointer points to the last directory entry
142a8a78b85SJerome Forissier  * returned by readdir().
143a8a78b85SJerome Forissier  */
144a8a78b85SJerome Forissier struct tee_fs_dir {
145a8a78b85SJerome Forissier 	struct tee_rpmb_fs_dirent *current;
1460c96a71dSJerome Forissier 	/* */
147a8a78b85SJerome Forissier 	SIMPLEQ_HEAD(next_head, tee_rpmb_fs_dirent) next;
148a8a78b85SJerome Forissier };
149a8a78b85SJerome Forissier 
150b0104773SPascal Brand static struct rpmb_fs_parameters *fs_par;
1515f68d784SManuel Huber static struct rpmb_fat_entry_dir *fat_entry_dir;
152b0104773SPascal Brand 
1530c96a71dSJerome Forissier /*
1540c96a71dSJerome Forissier  * Lower interface to RPMB device
1550c96a71dSJerome Forissier  */
1560c96a71dSJerome Forissier 
1570c96a71dSJerome Forissier #define RPMB_DATA_OFFSET            (RPMB_STUFF_DATA_SIZE + RPMB_KEY_MAC_SIZE)
1580c96a71dSJerome Forissier #define RPMB_MAC_PROTECT_DATA_SIZE  (RPMB_DATA_FRAME_SIZE - RPMB_DATA_OFFSET)
1590c96a71dSJerome Forissier 
1600c96a71dSJerome Forissier #define RPMB_MSG_TYPE_REQ_AUTH_KEY_PROGRAM          0x0001
1610c96a71dSJerome Forissier #define RPMB_MSG_TYPE_REQ_WRITE_COUNTER_VAL_READ    0x0002
1620c96a71dSJerome Forissier #define RPMB_MSG_TYPE_REQ_AUTH_DATA_WRITE           0x0003
1630c96a71dSJerome Forissier #define RPMB_MSG_TYPE_REQ_AUTH_DATA_READ            0x0004
1640c96a71dSJerome Forissier #define RPMB_MSG_TYPE_REQ_RESULT_READ               0x0005
1650c96a71dSJerome Forissier #define RPMB_MSG_TYPE_RESP_AUTH_KEY_PROGRAM         0x0100
1660c96a71dSJerome Forissier #define RPMB_MSG_TYPE_RESP_WRITE_COUNTER_VAL_READ   0x0200
1670c96a71dSJerome Forissier #define RPMB_MSG_TYPE_RESP_AUTH_DATA_WRITE          0x0300
1680c96a71dSJerome Forissier #define RPMB_MSG_TYPE_RESP_AUTH_DATA_READ           0x0400
1690c96a71dSJerome Forissier 
1700c96a71dSJerome Forissier #define RPMB_STUFF_DATA_SIZE                        196
1710c96a71dSJerome Forissier #define RPMB_KEY_MAC_SIZE                           32
1720c96a71dSJerome Forissier #define RPMB_DATA_SIZE                              256
1730c96a71dSJerome Forissier #define RPMB_NONCE_SIZE                             16
1740c96a71dSJerome Forissier #define RPMB_DATA_FRAME_SIZE                        512
1750c96a71dSJerome Forissier 
1760c96a71dSJerome Forissier #define RPMB_RESULT_OK                              0x00
1770c96a71dSJerome Forissier #define RPMB_RESULT_GENERAL_FAILURE                 0x01
1780c96a71dSJerome Forissier #define RPMB_RESULT_AUTH_FAILURE                    0x02
1790c96a71dSJerome Forissier #define RPMB_RESULT_COUNTER_FAILURE                 0x03
1800c96a71dSJerome Forissier #define RPMB_RESULT_ADDRESS_FAILURE                 0x04
1810c96a71dSJerome Forissier #define RPMB_RESULT_WRITE_FAILURE                   0x05
1820c96a71dSJerome Forissier #define RPMB_RESULT_READ_FAILURE                    0x06
1830c96a71dSJerome Forissier #define RPMB_RESULT_AUTH_KEY_NOT_PROGRAMMED         0x07
1840c96a71dSJerome Forissier #define RPMB_RESULT_MASK                            0x3F
1850c96a71dSJerome Forissier #define RPMB_RESULT_WR_CNT_EXPIRED                  0x80
1860c96a71dSJerome Forissier 
1870c96a71dSJerome Forissier /* RPMB internal commands */
1880c96a71dSJerome Forissier #define RPMB_CMD_DATA_REQ      0x00
1890c96a71dSJerome Forissier #define RPMB_CMD_GET_DEV_INFO  0x01
1900c96a71dSJerome Forissier 
1910c96a71dSJerome Forissier #define RPMB_SIZE_SINGLE (128 * 1024)
1920c96a71dSJerome Forissier 
1930c96a71dSJerome Forissier /* Error codes for get_dev_info request/response. */
1940c96a71dSJerome Forissier #define RPMB_CMD_GET_DEV_INFO_RET_OK     0x00
1950c96a71dSJerome Forissier #define RPMB_CMD_GET_DEV_INFO_RET_ERROR  0x01
1960c96a71dSJerome Forissier 
1970c96a71dSJerome Forissier struct rpmb_data_frame {
1980c96a71dSJerome Forissier 	uint8_t stuff_bytes[RPMB_STUFF_DATA_SIZE];
1990c96a71dSJerome Forissier 	uint8_t key_mac[RPMB_KEY_MAC_SIZE];
2000c96a71dSJerome Forissier 	uint8_t data[RPMB_DATA_SIZE];
2010c96a71dSJerome Forissier 	uint8_t nonce[RPMB_NONCE_SIZE];
2020c96a71dSJerome Forissier 	uint8_t write_counter[4];
2030c96a71dSJerome Forissier 	uint8_t address[2];
2040c96a71dSJerome Forissier 	uint8_t block_count[2];
2050c96a71dSJerome Forissier 	uint8_t op_result[2];
2060c96a71dSJerome Forissier 	uint8_t msg_type[2];
2070c96a71dSJerome Forissier };
2080c96a71dSJerome Forissier 
2090c96a71dSJerome Forissier struct rpmb_req {
2100c96a71dSJerome Forissier 	uint16_t cmd;
2110c96a71dSJerome Forissier 	uint16_t dev_id;
2120c96a71dSJerome Forissier 	uint16_t block_count;
2130c96a71dSJerome Forissier 	/* variable length of data */
2140c96a71dSJerome Forissier 	/* uint8_t data[]; REMOVED! */
2150c96a71dSJerome Forissier };
2160c96a71dSJerome Forissier 
2170c96a71dSJerome Forissier #define TEE_RPMB_REQ_DATA(req) \
2180c96a71dSJerome Forissier 		((void *)((struct rpmb_req *)(req) + 1))
2190c96a71dSJerome Forissier 
2200c96a71dSJerome Forissier struct rpmb_raw_data {
2210c96a71dSJerome Forissier 	uint16_t msg_type;
2220c96a71dSJerome Forissier 	uint16_t *op_result;
2230c96a71dSJerome Forissier 	uint16_t *block_count;
2240c96a71dSJerome Forissier 	uint16_t *blk_idx;
2250c96a71dSJerome Forissier 	uint32_t *write_counter;
2260c96a71dSJerome Forissier 	uint8_t *nonce;
2270c96a71dSJerome Forissier 	uint8_t *key_mac;
2280c96a71dSJerome Forissier 	uint8_t *data;
2290c96a71dSJerome Forissier 	/* data length to read or write */
2300c96a71dSJerome Forissier 	uint32_t len;
2310c96a71dSJerome Forissier 	/* Byte address offset in the first block involved */
2320c96a71dSJerome Forissier 	uint8_t byte_offset;
2330c96a71dSJerome Forissier };
2340c96a71dSJerome Forissier 
2350c96a71dSJerome Forissier #define RPMB_EMMC_CID_SIZE 16
2360c96a71dSJerome Forissier struct rpmb_dev_info {
2370c96a71dSJerome Forissier 	uint8_t cid[RPMB_EMMC_CID_SIZE];
2380c96a71dSJerome Forissier 	/* EXT CSD-slice 168 "RPMB Size" */
2390c96a71dSJerome Forissier 	uint8_t rpmb_size_mult;
2400c96a71dSJerome Forissier 	/* EXT CSD-slice 222 "Reliable Write Sector Count" */
2410c96a71dSJerome Forissier 	uint8_t rel_wr_sec_c;
2420c96a71dSJerome Forissier 	/* Check the ret code and accept the data only if it is OK. */
2430c96a71dSJerome Forissier 	uint8_t ret_code;
2440c96a71dSJerome Forissier };
2450c96a71dSJerome Forissier 
2460c96a71dSJerome Forissier /*
2470c96a71dSJerome Forissier  * Struct for rpmb context data.
2480c96a71dSJerome Forissier  *
2490c96a71dSJerome Forissier  * @key              RPMB key.
2500c96a71dSJerome Forissier  * @cid              eMMC card ID.
2510c96a71dSJerome Forissier  * @wr_cnt           Current write counter.
2520c96a71dSJerome Forissier  * @max_blk_idx      The highest block index supported by current device.
2530c96a71dSJerome Forissier  * @rel_wr_blkcnt    Max number of data blocks for each reliable write.
2540c96a71dSJerome Forissier  * @dev_id           Device ID of the eMMC device.
2550c96a71dSJerome Forissier  * @wr_cnt_synced    Flag indicating if write counter is synced to RPMB.
2560c96a71dSJerome Forissier  * @key_derived      Flag indicating if key has been generated.
2570c96a71dSJerome Forissier  * @key_verified     Flag indicating the key generated is verified ok.
2580c96a71dSJerome Forissier  * @dev_info_synced  Flag indicating if dev info has been retrieved from RPMB.
2598dfdf392SJens Wiklander  * @legacy_operation Flag indicating if the legacy interface is used.
2608dfdf392SJens Wiklander  * @reinit           Flag indicating if the device needs to be found again
261e94194d4SJens Wiklander  * @shm_type         Indicates type of shared memory to allocate
2620c96a71dSJerome Forissier  */
2630c96a71dSJerome Forissier struct tee_rpmb_ctx {
2640c96a71dSJerome Forissier 	uint8_t key[RPMB_KEY_MAC_SIZE];
2650c96a71dSJerome Forissier 	uint8_t cid[RPMB_EMMC_CID_SIZE];
2660c96a71dSJerome Forissier 	uint32_t wr_cnt;
2670c96a71dSJerome Forissier 	uint16_t max_blk_idx;
2680c96a71dSJerome Forissier 	uint16_t rel_wr_blkcnt;
2690c96a71dSJerome Forissier 	uint16_t dev_id;
2700c96a71dSJerome Forissier 	bool wr_cnt_synced;
2710c96a71dSJerome Forissier 	bool key_derived;
2720c96a71dSJerome Forissier 	bool key_verified;
2730c96a71dSJerome Forissier 	bool dev_info_synced;
2748dfdf392SJens Wiklander 	bool legacy_operation;
2758dfdf392SJens Wiklander 	bool reinit;
276e94194d4SJens Wiklander 	enum thread_shm_type shm_type;
2770c96a71dSJerome Forissier };
2780c96a71dSJerome Forissier 
2790c96a71dSJerome Forissier static struct tee_rpmb_ctx *rpmb_ctx;
2800c96a71dSJerome Forissier 
281a8fb1651SJens Wiklander /* If set to true, don't try to access RPMB until rebooted */
282a8fb1651SJens Wiklander static bool rpmb_dead;
283a8fb1651SJens Wiklander 
2840c96a71dSJerome Forissier /*
2850c96a71dSJerome Forissier  * Mutex to serialize the operations exported by this file.
2860c96a71dSJerome Forissier  * It protects rpmb_ctx and prevents overlapping operations on eMMC devices with
2870c96a71dSJerome Forissier  * different IDs.
2880c96a71dSJerome Forissier  */
2890c96a71dSJerome Forissier static struct mutex rpmb_mutex = MUTEX_INITIALIZER;
2900c96a71dSJerome Forissier 
2910c96a71dSJerome Forissier #ifdef CFG_RPMB_TESTKEY
2920c96a71dSJerome Forissier 
2930c96a71dSJerome Forissier static const uint8_t rpmb_test_key[RPMB_KEY_MAC_SIZE] = {
2940c96a71dSJerome Forissier 	0xD3, 0xEB, 0x3E, 0xC3, 0x6E, 0x33, 0x4C, 0x9F,
2950c96a71dSJerome Forissier 	0x98, 0x8C, 0xE2, 0xC0, 0xB8, 0x59, 0x54, 0x61,
2960c96a71dSJerome Forissier 	0x0D, 0x2B, 0xCF, 0x86, 0x64, 0x84, 0x4D, 0xF2,
2970c96a71dSJerome Forissier 	0xAB, 0x56, 0xE6, 0xC6, 0x1B, 0xB7, 0x01, 0xE4
2980c96a71dSJerome Forissier };
2990c96a71dSJerome Forissier 
tee_rpmb_key_gen(uint8_t * key,uint32_t len)3003be2f85aSJens Wiklander static TEE_Result tee_rpmb_key_gen(uint8_t *key, uint32_t len)
3010c96a71dSJerome Forissier {
3020c96a71dSJerome Forissier 	TEE_Result res = TEE_SUCCESS;
3030c96a71dSJerome Forissier 
3040c96a71dSJerome Forissier 	if (!key || RPMB_KEY_MAC_SIZE != len) {
3050c96a71dSJerome Forissier 		res = TEE_ERROR_BAD_PARAMETERS;
3060c96a71dSJerome Forissier 		goto out;
3070c96a71dSJerome Forissier 	}
3080c96a71dSJerome Forissier 
3090c96a71dSJerome Forissier 	DMSG("RPMB: Using test key");
3100c96a71dSJerome Forissier 	memcpy(key, rpmb_test_key, RPMB_KEY_MAC_SIZE);
3110c96a71dSJerome Forissier 
3120c96a71dSJerome Forissier out:
3130c96a71dSJerome Forissier 	return res;
3140c96a71dSJerome Forissier }
3150c96a71dSJerome Forissier 
3160c96a71dSJerome Forissier #else /* !CFG_RPMB_TESTKEY */
3170c96a71dSJerome Forissier 
tee_rpmb_key_gen(uint8_t * key,uint32_t len)3183be2f85aSJens Wiklander static TEE_Result tee_rpmb_key_gen(uint8_t *key, uint32_t len)
3190c96a71dSJerome Forissier {
3208d22c45dSPeifu Jiang 	uint8_t message[RPMB_EMMC_CID_SIZE];
3210c96a71dSJerome Forissier 
3229cc10bc9SJens Wiklander 	if (!key || RPMB_KEY_MAC_SIZE != len)
3239cc10bc9SJens Wiklander 		return TEE_ERROR_BAD_PARAMETERS;
3240c96a71dSJerome Forissier 
3250c96a71dSJerome Forissier 	IMSG("RPMB: Using generated key");
3260c96a71dSJerome Forissier 
3278d22c45dSPeifu Jiang 	/*
3288d22c45dSPeifu Jiang 	 * PRV/CRC would be changed when doing eMMC FFU
3298d22c45dSPeifu Jiang 	 * The following fields should be masked off when deriving RPMB key
3308d22c45dSPeifu Jiang 	 *
3318d22c45dSPeifu Jiang 	 * CID [55: 48]: PRV (Product revision)
3328d22c45dSPeifu Jiang 	 * CID [07: 01]: CRC (CRC7 checksum)
3338d22c45dSPeifu Jiang 	 * CID [00]: not used
3348d22c45dSPeifu Jiang 	 */
3358d22c45dSPeifu Jiang 	memcpy(message, rpmb_ctx->cid, RPMB_EMMC_CID_SIZE);
3368d22c45dSPeifu Jiang 	memset(message + RPMB_CID_PRV_OFFSET, 0, 1);
3378d22c45dSPeifu Jiang 	memset(message + RPMB_CID_CRC_OFFSET, 0, 1);
3389cc10bc9SJens Wiklander 	return huk_subkey_derive(HUK_SUBKEY_RPMB, message, sizeof(message),
3399cc10bc9SJens Wiklander 				 key, len);
3400c96a71dSJerome Forissier }
3410c96a71dSJerome Forissier 
3420c96a71dSJerome Forissier #endif /* !CFG_RPMB_TESTKEY */
3430c96a71dSJerome Forissier 
u32_to_bytes(uint32_t u32,uint8_t * bytes)3440c96a71dSJerome Forissier static void u32_to_bytes(uint32_t u32, uint8_t *bytes)
3450c96a71dSJerome Forissier {
3460c96a71dSJerome Forissier 	*bytes = (uint8_t) (u32 >> 24);
3470c96a71dSJerome Forissier 	*(bytes + 1) = (uint8_t) (u32 >> 16);
3480c96a71dSJerome Forissier 	*(bytes + 2) = (uint8_t) (u32 >> 8);
3490c96a71dSJerome Forissier 	*(bytes + 3) = (uint8_t) u32;
3500c96a71dSJerome Forissier }
3510c96a71dSJerome Forissier 
bytes_to_u32(uint8_t * bytes,uint32_t * u32)3520c96a71dSJerome Forissier static void bytes_to_u32(uint8_t *bytes, uint32_t *u32)
3530c96a71dSJerome Forissier {
3540c96a71dSJerome Forissier 	*u32 = (uint32_t) ((*(bytes) << 24) +
3550c96a71dSJerome Forissier 			   (*(bytes + 1) << 16) +
3560c96a71dSJerome Forissier 			   (*(bytes + 2) << 8) + (*(bytes + 3)));
3570c96a71dSJerome Forissier }
3580c96a71dSJerome Forissier 
u16_to_bytes(uint16_t u16,uint8_t * bytes)3590c96a71dSJerome Forissier static void u16_to_bytes(uint16_t u16, uint8_t *bytes)
3600c96a71dSJerome Forissier {
3610c96a71dSJerome Forissier 	*bytes = (uint8_t) (u16 >> 8);
3620c96a71dSJerome Forissier 	*(bytes + 1) = (uint8_t) u16;
3630c96a71dSJerome Forissier }
3640c96a71dSJerome Forissier 
bytes_to_u16(uint8_t * bytes,uint16_t * u16)3650c96a71dSJerome Forissier static void bytes_to_u16(uint8_t *bytes, uint16_t *u16)
3660c96a71dSJerome Forissier {
3670c96a71dSJerome Forissier 	*u16 = (uint16_t) ((*bytes << 8) + *(bytes + 1));
3680c96a71dSJerome Forissier }
3690c96a71dSJerome Forissier 
get_op_result_bits(uint8_t * bytes,uint8_t * res)370fcd00ceaSVictor Chong static void get_op_result_bits(uint8_t *bytes, uint8_t *res)
371fcd00ceaSVictor Chong {
372fcd00ceaSVictor Chong 	*res = *(bytes + 1) & RPMB_RESULT_MASK;
373fcd00ceaSVictor Chong }
374fcd00ceaSVictor Chong 
tee_rpmb_mac_calc(uint8_t * mac,uint32_t macsize,uint8_t * key,uint32_t keysize,struct rpmb_data_frame * datafrms,uint16_t blkcnt)3750c96a71dSJerome Forissier static TEE_Result tee_rpmb_mac_calc(uint8_t *mac, uint32_t macsize,
3760c96a71dSJerome Forissier 				    uint8_t *key, uint32_t keysize,
3770c96a71dSJerome Forissier 				    struct rpmb_data_frame *datafrms,
3780c96a71dSJerome Forissier 				    uint16_t blkcnt)
3790c96a71dSJerome Forissier {
3800c96a71dSJerome Forissier 	TEE_Result res = TEE_ERROR_GENERIC;
3810c96a71dSJerome Forissier 	int i;
38282ef73bcSJens Wiklander 	void *ctx = NULL;
3830c96a71dSJerome Forissier 
3840c96a71dSJerome Forissier 	if (!mac || !key || !datafrms)
3850c96a71dSJerome Forissier 		return TEE_ERROR_BAD_PARAMETERS;
3860c96a71dSJerome Forissier 
38782ef73bcSJens Wiklander 	res = crypto_mac_alloc_ctx(&ctx, TEE_ALG_HMAC_SHA256);
38882ef73bcSJens Wiklander 	if (res)
38982ef73bcSJens Wiklander 		return res;
3900c96a71dSJerome Forissier 
391c69bc615SJens Wiklander 	res = crypto_mac_init(ctx, key, keysize);
3920c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
3930c96a71dSJerome Forissier 		goto func_exit;
3940c96a71dSJerome Forissier 
3950c96a71dSJerome Forissier 	for (i = 0; i < blkcnt; i++) {
396c69bc615SJens Wiklander 		res = crypto_mac_update(ctx, datafrms[i].data,
3970c96a71dSJerome Forissier 					RPMB_MAC_PROTECT_DATA_SIZE);
3980c96a71dSJerome Forissier 		if (res != TEE_SUCCESS)
3990c96a71dSJerome Forissier 			goto func_exit;
4000c96a71dSJerome Forissier 	}
4010c96a71dSJerome Forissier 
402c69bc615SJens Wiklander 	res = crypto_mac_final(ctx, mac, macsize);
4030c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
4040c96a71dSJerome Forissier 		goto func_exit;
4050c96a71dSJerome Forissier 
4060c96a71dSJerome Forissier 	res = TEE_SUCCESS;
4070c96a71dSJerome Forissier 
4080c96a71dSJerome Forissier func_exit:
409c69bc615SJens Wiklander 	crypto_mac_free_ctx(ctx);
4100c96a71dSJerome Forissier 	return res;
4110c96a71dSJerome Forissier }
4120c96a71dSJerome Forissier 
4130c96a71dSJerome Forissier struct tee_rpmb_mem {
414e94194d4SJens Wiklander 	struct mobj *mobj;
4150c96a71dSJerome Forissier 	size_t req_size;
4168dfdf392SJens Wiklander 	size_t resp_offs;
4170c96a71dSJerome Forissier 	size_t resp_size;
4188dfdf392SJens Wiklander 	struct rpmb_req *req_hdr;
4198dfdf392SJens Wiklander 	struct rpmb_data_frame *req_data;
4208dfdf392SJens Wiklander 	struct rpmb_data_frame *resp_data;
4210c96a71dSJerome Forissier };
4220c96a71dSJerome Forissier 
tee_rpmb_alloc(size_t req_size,size_t resp_size,struct tee_rpmb_mem * mem)4230c96a71dSJerome Forissier static TEE_Result tee_rpmb_alloc(size_t req_size, size_t resp_size,
4248dfdf392SJens Wiklander 				 struct tee_rpmb_mem *mem)
4250c96a71dSJerome Forissier {
4268dfdf392SJens Wiklander 	size_t req_s = 0;
4278dfdf392SJens Wiklander 	size_t resp_s = 0;
428e94194d4SJens Wiklander 	struct mobj *mobj = NULL;
429e94194d4SJens Wiklander 	void *va = NULL;
4300c96a71dSJerome Forissier 
4310c96a71dSJerome Forissier 	if (!mem)
4320c96a71dSJerome Forissier 		return TEE_ERROR_BAD_PARAMETERS;
4330c96a71dSJerome Forissier 
4348dfdf392SJens Wiklander 	if (rpmb_ctx->legacy_operation)
4358dfdf392SJens Wiklander 		req_size += sizeof(struct rpmb_req);
4368dfdf392SJens Wiklander 	req_s = ROUNDUP(req_size, SMALL_PAGE_SIZE);
4378dfdf392SJens Wiklander 	resp_s = ROUNDUP(resp_size, SMALL_PAGE_SIZE);
438e94194d4SJens Wiklander 	va = thread_rpc_shm_cache_alloc(THREAD_SHM_CACHE_USER_RPMB,
439e94194d4SJens Wiklander 					rpmb_ctx->shm_type, req_s + resp_s,
440e94194d4SJens Wiklander 					&mobj);
441e94194d4SJens Wiklander 	if (!va)
442e94194d4SJens Wiklander 		return TEE_ERROR_OUT_OF_MEMORY;
443142d5af2SVolodymyr Babchuk 
444e94194d4SJens Wiklander 	*mem = (struct tee_rpmb_mem){
445e94194d4SJens Wiklander 		.mobj = mobj,
446e94194d4SJens Wiklander 		.req_size = req_size,
4478dfdf392SJens Wiklander 		.resp_offs = req_s,
448e94194d4SJens Wiklander 		.resp_size = resp_size,
449e94194d4SJens Wiklander 	};
450142d5af2SVolodymyr Babchuk 
4518dfdf392SJens Wiklander 	if (rpmb_ctx->legacy_operation) {
4528dfdf392SJens Wiklander 		mem->req_hdr = mobj_get_va(mem->mobj, 0, req_s);
4538dfdf392SJens Wiklander 		if (!mem->req_hdr)
4548dfdf392SJens Wiklander 			return TEE_ERROR_GENERIC;
4558dfdf392SJens Wiklander 		mem->req_data = (void *)(mem->req_hdr + 1);
4568dfdf392SJens Wiklander 	} else {
4578dfdf392SJens Wiklander 		mem->req_data = mobj_get_va(mem->mobj, 0, req_s);
4588dfdf392SJens Wiklander 		if (!mem->req_data)
4598dfdf392SJens Wiklander 			return TEE_ERROR_GENERIC;
4608dfdf392SJens Wiklander 	}
4618dfdf392SJens Wiklander 	mem->resp_data = mobj_get_va(mem->mobj, req_s, resp_s);
4628dfdf392SJens Wiklander 	if (!mem->resp_data)
463e94194d4SJens Wiklander 		return TEE_ERROR_GENERIC;
4640c96a71dSJerome Forissier 
465e94194d4SJens Wiklander 	return TEE_SUCCESS;
4660c96a71dSJerome Forissier }
4670c96a71dSJerome Forissier 
tee_rpmb_invoke(struct tee_rpmb_mem * mem)4680c96a71dSJerome Forissier static TEE_Result tee_rpmb_invoke(struct tee_rpmb_mem *mem)
4690c96a71dSJerome Forissier {
47013eb4e3cSJens Wiklander 	struct thread_param params[2] = {
471e94194d4SJens Wiklander 		[0] = THREAD_PARAM_MEMREF(IN, mem->mobj, 0, mem->req_size),
4728dfdf392SJens Wiklander 		[1] = THREAD_PARAM_MEMREF(OUT, mem->mobj, mem->resp_offs,
47313eb4e3cSJens Wiklander 					  mem->resp_size),
47413eb4e3cSJens Wiklander 	};
4758dfdf392SJens Wiklander 	uint32_t cmd = OPTEE_RPC_CMD_RPMB_FRAMES;
4760c96a71dSJerome Forissier 
4778dfdf392SJens Wiklander 	if (rpmb_ctx->legacy_operation)
4788dfdf392SJens Wiklander 		cmd = OPTEE_RPC_CMD_RPMB;
4798dfdf392SJens Wiklander 
4808dfdf392SJens Wiklander 	return thread_rpc_cmd(cmd, 2, params);
4818dfdf392SJens Wiklander }
4828dfdf392SJens Wiklander 
rpmb_probe_reset(void)4838dfdf392SJens Wiklander static TEE_Result rpmb_probe_reset(void)
4848dfdf392SJens Wiklander {
4858dfdf392SJens Wiklander 	struct thread_param params[1] = {
4868dfdf392SJens Wiklander 		[0] = THREAD_PARAM_VALUE(OUT, 0, 0, 0),
4878dfdf392SJens Wiklander 	};
4888dfdf392SJens Wiklander 	TEE_Result res = TEE_SUCCESS;
4898dfdf392SJens Wiklander 
4908dfdf392SJens Wiklander 	res = thread_rpc_cmd(OPTEE_RPC_CMD_RPMB_PROBE_RESET, 1, params);
4918dfdf392SJens Wiklander 	if (res)
4928dfdf392SJens Wiklander 		return res;
4938dfdf392SJens Wiklander 
4948dfdf392SJens Wiklander 	rpmb_ctx->legacy_operation = false;
4958dfdf392SJens Wiklander 	rpmb_ctx->dev_id = 0;
4968dfdf392SJens Wiklander 
4978dfdf392SJens Wiklander 	switch (params[0].u.value.a) {
4988dfdf392SJens Wiklander 	case OPTEE_RPC_SHM_TYPE_APPL:
4998dfdf392SJens Wiklander 		rpmb_ctx->shm_type = THREAD_SHM_TYPE_APPLICATION;
5008dfdf392SJens Wiklander 		return TEE_SUCCESS;
5018dfdf392SJens Wiklander 	case OPTEE_RPC_SHM_TYPE_KERNEL:
5028dfdf392SJens Wiklander 		rpmb_ctx->shm_type = THREAD_SHM_TYPE_KERNEL_PRIVATE;
5038dfdf392SJens Wiklander 		return TEE_SUCCESS;
5048dfdf392SJens Wiklander 	default:
5058dfdf392SJens Wiklander 		return TEE_ERROR_GENERIC;
5068dfdf392SJens Wiklander 	}
5078dfdf392SJens Wiklander }
5088dfdf392SJens Wiklander 
rpmb_probe_next(struct rpmb_dev_info * dev_info)5098dfdf392SJens Wiklander static TEE_Result rpmb_probe_next(struct rpmb_dev_info *dev_info)
5108dfdf392SJens Wiklander {
5118dfdf392SJens Wiklander 	struct thread_param params[2] = { };
5128dfdf392SJens Wiklander 	TEE_Result res = TEE_SUCCESS;
5138dfdf392SJens Wiklander 	struct mobj *mobj = NULL;
5148dfdf392SJens Wiklander 	void *va = NULL;
5158dfdf392SJens Wiklander 
5168dfdf392SJens Wiklander 	va = thread_rpc_shm_cache_alloc(THREAD_SHM_CACHE_USER_RPMB,
5178dfdf392SJens Wiklander 					THREAD_SHM_TYPE_KERNEL_PRIVATE,
5188dfdf392SJens Wiklander 					sizeof(dev_info->cid), &mobj);
5198dfdf392SJens Wiklander 	if (!va)
5208dfdf392SJens Wiklander 		return TEE_ERROR_OUT_OF_MEMORY;
5218dfdf392SJens Wiklander 
5228dfdf392SJens Wiklander 	do {
5238dfdf392SJens Wiklander 		params[0] = THREAD_PARAM_VALUE(OUT, 0, 0, 0);
5248dfdf392SJens Wiklander 		params[1] = THREAD_PARAM_MEMREF(OUT, mobj, 0,
5258dfdf392SJens Wiklander 						sizeof(dev_info->cid));
5268dfdf392SJens Wiklander 		res = thread_rpc_cmd(OPTEE_RPC_CMD_RPMB_PROBE_NEXT, 2, params);
5278dfdf392SJens Wiklander 		/*
5288dfdf392SJens Wiklander 		 * If the ID buffer is too small, perhaps it's a new kind
5298dfdf392SJens Wiklander 		 * of RPMB device that we don't know how to communicate
5308dfdf392SJens Wiklander 		 * with, let's ignore it for now.
5318dfdf392SJens Wiklander 		 */
5328dfdf392SJens Wiklander 	} while (res == TEE_ERROR_SHORT_BUFFER);
5338dfdf392SJens Wiklander 	if (res)
5348dfdf392SJens Wiklander 		return res;
5358dfdf392SJens Wiklander 
5368dfdf392SJens Wiklander 	if (params[0].u.value.a != OPTEE_RPC_RPMB_EMMC)
5378dfdf392SJens Wiklander 		return TEE_ERROR_NOT_SUPPORTED;
5388dfdf392SJens Wiklander 
5398dfdf392SJens Wiklander 	*dev_info = (struct rpmb_dev_info){
5408dfdf392SJens Wiklander 		.rpmb_size_mult = params[0].u.value.b,
5418dfdf392SJens Wiklander 		.rel_wr_sec_c = params[0].u.value.c,
5428dfdf392SJens Wiklander 		.ret_code = RPMB_CMD_GET_DEV_INFO_RET_OK,
5438dfdf392SJens Wiklander 	};
5448dfdf392SJens Wiklander 	memcpy(dev_info->cid, va, sizeof(dev_info->cid));
5458dfdf392SJens Wiklander 
5468dfdf392SJens Wiklander 	return TEE_SUCCESS;
5470c96a71dSJerome Forissier }
5480c96a71dSJerome Forissier 
is_zero(const uint8_t * buf,size_t size)5490c96a71dSJerome Forissier static bool is_zero(const uint8_t *buf, size_t size)
5500c96a71dSJerome Forissier {
5510c96a71dSJerome Forissier 	size_t i;
5520c96a71dSJerome Forissier 
5530c96a71dSJerome Forissier 	for (i = 0; i < size; i++)
5540c96a71dSJerome Forissier 		if (buf[i])
5550c96a71dSJerome Forissier 			return false;
5560c96a71dSJerome Forissier 	return true;
5570c96a71dSJerome Forissier }
5580c96a71dSJerome Forissier 
encrypt_block(uint8_t * out,const uint8_t * in,uint16_t blk_idx,const uint8_t * fek,const TEE_UUID * uuid)5590c96a71dSJerome Forissier static TEE_Result encrypt_block(uint8_t *out, const uint8_t *in,
560b399f70bSJens Wiklander 				uint16_t blk_idx, const uint8_t *fek,
561b399f70bSJens Wiklander 				const TEE_UUID *uuid)
5620c96a71dSJerome Forissier {
563b399f70bSJens Wiklander 	return tee_fs_crypt_block(uuid, out, in, RPMB_DATA_SIZE,
5640c4e1284SJens Wiklander 				  blk_idx, fek, TEE_MODE_ENCRYPT);
5650c96a71dSJerome Forissier }
5660c96a71dSJerome Forissier 
decrypt_block(uint8_t * out,const uint8_t * in,uint16_t blk_idx,const uint8_t * fek,const TEE_UUID * uuid)5670c96a71dSJerome Forissier static TEE_Result decrypt_block(uint8_t *out, const uint8_t *in,
568b399f70bSJens Wiklander 				uint16_t blk_idx, const uint8_t *fek,
569b399f70bSJens Wiklander 				const TEE_UUID *uuid)
5700c96a71dSJerome Forissier {
571b399f70bSJens Wiklander 	return tee_fs_crypt_block(uuid, out, in, RPMB_DATA_SIZE,
5720c4e1284SJens Wiklander 				  blk_idx, fek, TEE_MODE_DECRYPT);
5730c96a71dSJerome Forissier }
5740c96a71dSJerome Forissier 
5750c96a71dSJerome Forissier /* Decrypt/copy at most one block of data */
decrypt(uint8_t * out,const struct rpmb_data_frame * frm,size_t size,size_t offset,uint16_t blk_idx __maybe_unused,const uint8_t * fek,const TEE_UUID * uuid)5760c96a71dSJerome Forissier static TEE_Result decrypt(uint8_t *out, const struct rpmb_data_frame *frm,
5770c96a71dSJerome Forissier 			  size_t size, size_t offset,
578b399f70bSJens Wiklander 			  uint16_t blk_idx __maybe_unused, const uint8_t *fek,
579b399f70bSJens Wiklander 			  const TEE_UUID *uuid)
5800c96a71dSJerome Forissier {
5810c96a71dSJerome Forissier 	uint8_t *tmp __maybe_unused;
582a1bc38c8SRobin van der Gracht 	TEE_Result res = TEE_SUCCESS;
5830c96a71dSJerome Forissier 
584d13278b8SEtienne Carriere 
585d13278b8SEtienne Carriere 	if ((size + offset < size) || (size + offset > RPMB_DATA_SIZE))
5868c9d9445SEtienne Carriere 		panic("invalid size or offset");
5870c96a71dSJerome Forissier 
5880c96a71dSJerome Forissier 	if (!fek) {
5890c96a71dSJerome Forissier 		/* Block is not encrypted (not a file data block) */
5900c96a71dSJerome Forissier 		memcpy(out, frm->data + offset, size);
5910c96a71dSJerome Forissier 	} else if (is_zero(fek, TEE_FS_KM_FEK_SIZE)) {
5928dceff9bSJens Wiklander 		/* The file was created with encryption disabled */
5930c96a71dSJerome Forissier 		return TEE_ERROR_SECURITY;
5940c96a71dSJerome Forissier 	} else {
5950c96a71dSJerome Forissier 		/* Block is encrypted */
5960c96a71dSJerome Forissier 		if (size < RPMB_DATA_SIZE) {
5970c96a71dSJerome Forissier 			/*
5980c96a71dSJerome Forissier 			 * Since output buffer is not large enough to hold one
5990c96a71dSJerome Forissier 			 * block we must allocate a temporary buffer.
6000c96a71dSJerome Forissier 			 */
6010c96a71dSJerome Forissier 			tmp = malloc(RPMB_DATA_SIZE);
6020c96a71dSJerome Forissier 			if (!tmp)
6030c96a71dSJerome Forissier 				return TEE_ERROR_OUT_OF_MEMORY;
604a1bc38c8SRobin van der Gracht 			res = decrypt_block(tmp, frm->data, blk_idx, fek, uuid);
605a1bc38c8SRobin van der Gracht 			if (res == TEE_SUCCESS)
6060c96a71dSJerome Forissier 				memcpy(out, tmp + offset, size);
6070c96a71dSJerome Forissier 			free(tmp);
6080c96a71dSJerome Forissier 		} else {
609a1bc38c8SRobin van der Gracht 			res = decrypt_block(out, frm->data, blk_idx, fek, uuid);
6100c96a71dSJerome Forissier 		}
6110c96a71dSJerome Forissier 	}
6120c96a71dSJerome Forissier 
613a1bc38c8SRobin van der Gracht 	return res;
6140c96a71dSJerome Forissier }
6150c96a71dSJerome Forissier 
tee_rpmb_req_pack(struct rpmb_req * req_hdr,struct rpmb_data_frame * req_data,struct rpmb_raw_data * rawdata,uint16_t nbr_frms,const uint8_t * fek,const TEE_UUID * uuid)6168dfdf392SJens Wiklander static TEE_Result tee_rpmb_req_pack(struct rpmb_req *req_hdr,
6178dfdf392SJens Wiklander 				    struct rpmb_data_frame *req_data,
6180c96a71dSJerome Forissier 				    struct rpmb_raw_data *rawdata,
6193be2f85aSJens Wiklander 				    uint16_t nbr_frms,
620b399f70bSJens Wiklander 				    const uint8_t *fek, const TEE_UUID *uuid)
6210c96a71dSJerome Forissier {
6220c96a71dSJerome Forissier 	TEE_Result res = TEE_ERROR_GENERIC;
6230c96a71dSJerome Forissier 	int i;
6240c96a71dSJerome Forissier 	struct rpmb_data_frame *datafrm;
6250c96a71dSJerome Forissier 
6268dfdf392SJens Wiklander 	if (!req_data || !rawdata || !nbr_frms)
6270c96a71dSJerome Forissier 		return TEE_ERROR_BAD_PARAMETERS;
6280c96a71dSJerome Forissier 
6290c96a71dSJerome Forissier 	/*
6300c96a71dSJerome Forissier 	 * Check write blockcount is not bigger than reliable write
6310c96a71dSJerome Forissier 	 * blockcount.
6320c96a71dSJerome Forissier 	 */
6330c96a71dSJerome Forissier 	if ((rawdata->msg_type == RPMB_MSG_TYPE_REQ_AUTH_DATA_WRITE) &&
6340c96a71dSJerome Forissier 	    (nbr_frms > rpmb_ctx->rel_wr_blkcnt)) {
6350c96a71dSJerome Forissier 		DMSG("wr_blkcnt(%d) > rel_wr_blkcnt(%d)", nbr_frms,
6360c96a71dSJerome Forissier 		     rpmb_ctx->rel_wr_blkcnt);
6370c96a71dSJerome Forissier 		return TEE_ERROR_GENERIC;
6380c96a71dSJerome Forissier 	}
6390c96a71dSJerome Forissier 
6408dfdf392SJens Wiklander 	if (req_hdr) {
6418dfdf392SJens Wiklander 		req_hdr->cmd = RPMB_CMD_DATA_REQ;
6428dfdf392SJens Wiklander 		req_hdr->dev_id = rpmb_ctx->dev_id;
6438dfdf392SJens Wiklander 	}
6440c96a71dSJerome Forissier 
6450c96a71dSJerome Forissier 	/* Allocate memory for construct all data packets and calculate MAC. */
6460c96a71dSJerome Forissier 	datafrm = calloc(nbr_frms, RPMB_DATA_FRAME_SIZE);
6470c96a71dSJerome Forissier 	if (!datafrm)
6480c96a71dSJerome Forissier 		return TEE_ERROR_OUT_OF_MEMORY;
6490c96a71dSJerome Forissier 
6500c96a71dSJerome Forissier 	for (i = 0; i < nbr_frms; i++) {
6510c96a71dSJerome Forissier 		u16_to_bytes(rawdata->msg_type, datafrm[i].msg_type);
6520c96a71dSJerome Forissier 
6530c96a71dSJerome Forissier 		if (rawdata->block_count)
6540c96a71dSJerome Forissier 			u16_to_bytes(*rawdata->block_count,
6550c96a71dSJerome Forissier 				     datafrm[i].block_count);
6560c96a71dSJerome Forissier 
6570c96a71dSJerome Forissier 		if (rawdata->blk_idx) {
6580c96a71dSJerome Forissier 			/* Check the block index is within range. */
6593b11b1d2SJerome Forissier 			if ((*rawdata->blk_idx + nbr_frms - 1) >
6600c96a71dSJerome Forissier 			    rpmb_ctx->max_blk_idx) {
6610c96a71dSJerome Forissier 				res = TEE_ERROR_GENERIC;
6620c96a71dSJerome Forissier 				goto func_exit;
6630c96a71dSJerome Forissier 			}
6640c96a71dSJerome Forissier 			u16_to_bytes(*rawdata->blk_idx, datafrm[i].address);
6650c96a71dSJerome Forissier 		}
6660c96a71dSJerome Forissier 
6670c96a71dSJerome Forissier 		if (rawdata->write_counter)
6680c96a71dSJerome Forissier 			u32_to_bytes(*rawdata->write_counter,
6690c96a71dSJerome Forissier 				     datafrm[i].write_counter);
6700c96a71dSJerome Forissier 
6710c96a71dSJerome Forissier 		if (rawdata->nonce)
6720c96a71dSJerome Forissier 			memcpy(datafrm[i].nonce, rawdata->nonce,
6730c96a71dSJerome Forissier 			       RPMB_NONCE_SIZE);
6740c96a71dSJerome Forissier 
6750c96a71dSJerome Forissier 		if (rawdata->data) {
6768f51d0a4SStefan Schmidt 			if (fek) {
6778f51d0a4SStefan Schmidt 				res = encrypt_block(datafrm[i].data,
6788f51d0a4SStefan Schmidt 						    rawdata->data +
6798f51d0a4SStefan Schmidt 						    (i * RPMB_DATA_SIZE),
6808f51d0a4SStefan Schmidt 						    *rawdata->blk_idx + i,
6818f51d0a4SStefan Schmidt 						    fek, uuid);
6828f51d0a4SStefan Schmidt 				if (res != TEE_SUCCESS)
6838f51d0a4SStefan Schmidt 					goto func_exit;
6848f51d0a4SStefan Schmidt 			} else {
6850c96a71dSJerome Forissier 				memcpy(datafrm[i].data,
6860c96a71dSJerome Forissier 				       rawdata->data + (i * RPMB_DATA_SIZE),
6870c96a71dSJerome Forissier 				       RPMB_DATA_SIZE);
6880c96a71dSJerome Forissier 			}
6890c96a71dSJerome Forissier 		}
6908f51d0a4SStefan Schmidt 	}
6910c96a71dSJerome Forissier 
6920c96a71dSJerome Forissier 	if (rawdata->key_mac) {
6930c96a71dSJerome Forissier 		if (rawdata->msg_type == RPMB_MSG_TYPE_REQ_AUTH_DATA_WRITE) {
6940c96a71dSJerome Forissier 			res =
6950c96a71dSJerome Forissier 			    tee_rpmb_mac_calc(rawdata->key_mac,
6960c96a71dSJerome Forissier 					      RPMB_KEY_MAC_SIZE, rpmb_ctx->key,
6970c96a71dSJerome Forissier 					      RPMB_KEY_MAC_SIZE, datafrm,
6980c96a71dSJerome Forissier 					      nbr_frms);
6990c96a71dSJerome Forissier 			if (res != TEE_SUCCESS)
7000c96a71dSJerome Forissier 				goto func_exit;
7010c96a71dSJerome Forissier 		}
7020c96a71dSJerome Forissier 		memcpy(datafrm[nbr_frms - 1].key_mac,
7030c96a71dSJerome Forissier 		       rawdata->key_mac, RPMB_KEY_MAC_SIZE);
7040c96a71dSJerome Forissier 	}
7050c96a71dSJerome Forissier 
7068dfdf392SJens Wiklander 	memcpy(req_data, datafrm, nbr_frms * RPMB_DATA_FRAME_SIZE);
7070c96a71dSJerome Forissier 
70885076371SEtienne Carriere 	if (IS_ENABLED(CFG_RPMB_FS_DEBUG_DATA)) {
7090c96a71dSJerome Forissier 		for (i = 0; i < nbr_frms; i++) {
7100c96a71dSJerome Forissier 			DMSG("Dumping data frame %d:", i);
7110c96a71dSJerome Forissier 			DHEXDUMP((uint8_t *)&datafrm[i] + RPMB_STUFF_DATA_SIZE,
7120c96a71dSJerome Forissier 				 512 - RPMB_STUFF_DATA_SIZE);
7130c96a71dSJerome Forissier 		}
71485076371SEtienne Carriere 	}
7150c96a71dSJerome Forissier 
7160c96a71dSJerome Forissier 	res = TEE_SUCCESS;
7170c96a71dSJerome Forissier func_exit:
7180c96a71dSJerome Forissier 	free(datafrm);
7190c96a71dSJerome Forissier 	return res;
7200c96a71dSJerome Forissier }
7210c96a71dSJerome Forissier 
data_cpy_mac_calc_1b(struct rpmb_raw_data * rawdata,struct rpmb_data_frame * frm,const uint8_t * fek,const TEE_UUID * uuid)7220c96a71dSJerome Forissier static TEE_Result data_cpy_mac_calc_1b(struct rpmb_raw_data *rawdata,
7230c96a71dSJerome Forissier 				       struct rpmb_data_frame *frm,
724b399f70bSJens Wiklander 				       const uint8_t *fek, const TEE_UUID *uuid)
7250c96a71dSJerome Forissier {
7260c96a71dSJerome Forissier 	TEE_Result res;
7270c96a71dSJerome Forissier 	uint8_t *data;
7280c96a71dSJerome Forissier 	uint16_t idx;
7290c96a71dSJerome Forissier 
7300c96a71dSJerome Forissier 	if (rawdata->len + rawdata->byte_offset > RPMB_DATA_SIZE)
7310c96a71dSJerome Forissier 		return TEE_ERROR_BAD_PARAMETERS;
7320c96a71dSJerome Forissier 
7330c96a71dSJerome Forissier 	res = tee_rpmb_mac_calc(rawdata->key_mac, RPMB_KEY_MAC_SIZE,
7340c96a71dSJerome Forissier 				rpmb_ctx->key, RPMB_KEY_MAC_SIZE, frm, 1);
7350c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
7360c96a71dSJerome Forissier 		return res;
7370c96a71dSJerome Forissier 
7380c96a71dSJerome Forissier 	data = rawdata->data;
7390c96a71dSJerome Forissier 	bytes_to_u16(frm->address, &idx);
7400c96a71dSJerome Forissier 
741b399f70bSJens Wiklander 	res = decrypt(data, frm, rawdata->len, rawdata->byte_offset, idx, fek,
742b399f70bSJens Wiklander 		      uuid);
7430c96a71dSJerome Forissier 	return res;
7440c96a71dSJerome Forissier }
7450c96a71dSJerome Forissier 
tee_rpmb_data_cpy_mac_calc(struct rpmb_data_frame * datafrm,struct rpmb_raw_data * rawdata,uint16_t nbr_frms,struct rpmb_data_frame * lastfrm,const uint8_t * fek,const TEE_UUID * uuid)7460c96a71dSJerome Forissier static TEE_Result tee_rpmb_data_cpy_mac_calc(struct rpmb_data_frame *datafrm,
7470c96a71dSJerome Forissier 					     struct rpmb_raw_data *rawdata,
7480c96a71dSJerome Forissier 					     uint16_t nbr_frms,
7490c96a71dSJerome Forissier 					     struct rpmb_data_frame *lastfrm,
750b399f70bSJens Wiklander 					     const uint8_t *fek,
751b399f70bSJens Wiklander 					     const TEE_UUID *uuid)
7520c96a71dSJerome Forissier {
7530c96a71dSJerome Forissier 	TEE_Result res = TEE_ERROR_GENERIC;
7540c96a71dSJerome Forissier 	int i;
75582ef73bcSJens Wiklander 	void *ctx = NULL;
7560c96a71dSJerome Forissier 	uint16_t offset;
7570c96a71dSJerome Forissier 	uint32_t size;
7580c96a71dSJerome Forissier 	uint8_t *data;
7590c96a71dSJerome Forissier 	uint16_t start_idx;
7600c96a71dSJerome Forissier 	struct rpmb_data_frame localfrm;
7610c96a71dSJerome Forissier 
7620c96a71dSJerome Forissier 	if (!datafrm || !rawdata || !nbr_frms || !lastfrm)
7630c96a71dSJerome Forissier 		return TEE_ERROR_BAD_PARAMETERS;
7640c96a71dSJerome Forissier 
7650c96a71dSJerome Forissier 	if (nbr_frms == 1)
766b399f70bSJens Wiklander 		return data_cpy_mac_calc_1b(rawdata, lastfrm, fek, uuid);
7670c96a71dSJerome Forissier 
7680c96a71dSJerome Forissier 	/* nbr_frms > 1 */
7690c96a71dSJerome Forissier 
7700c96a71dSJerome Forissier 	data = rawdata->data;
7710c96a71dSJerome Forissier 
77282ef73bcSJens Wiklander 	res = crypto_mac_alloc_ctx(&ctx, TEE_ALG_HMAC_SHA256);
77382ef73bcSJens Wiklander 	if (res)
7740c96a71dSJerome Forissier 		goto func_exit;
7750c96a71dSJerome Forissier 
776c69bc615SJens Wiklander 	res = crypto_mac_init(ctx, rpmb_ctx->key, RPMB_KEY_MAC_SIZE);
7770c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
7780c96a71dSJerome Forissier 		goto func_exit;
7790c96a71dSJerome Forissier 
7800c96a71dSJerome Forissier 	/*
7810c96a71dSJerome Forissier 	 * Note: JEDEC JESD84-B51: "In every packet the address is the start
7820c96a71dSJerome Forissier 	 * address of the full access (not address of the individual half a
7830c96a71dSJerome Forissier 	 * sector)"
7840c96a71dSJerome Forissier 	 */
7850c96a71dSJerome Forissier 	bytes_to_u16(lastfrm->address, &start_idx);
7860c96a71dSJerome Forissier 
7870c96a71dSJerome Forissier 	for (i = 0; i < (nbr_frms - 1); i++) {
7880c96a71dSJerome Forissier 
7890c96a71dSJerome Forissier 		/*
7900c96a71dSJerome Forissier 		 * By working on a local copy of the RPMB frame, we ensure that
7910c96a71dSJerome Forissier 		 * the data can not be modified after the MAC is computed but
7920c96a71dSJerome Forissier 		 * before the payload is decrypted/copied to the output buffer.
7930c96a71dSJerome Forissier 		 */
7940c96a71dSJerome Forissier 		memcpy(&localfrm, &datafrm[i], RPMB_DATA_FRAME_SIZE);
7950c96a71dSJerome Forissier 
796c69bc615SJens Wiklander 		res = crypto_mac_update(ctx, localfrm.data,
7970c96a71dSJerome Forissier 					RPMB_MAC_PROTECT_DATA_SIZE);
7980c96a71dSJerome Forissier 		if (res != TEE_SUCCESS)
7990c96a71dSJerome Forissier 			goto func_exit;
8000c96a71dSJerome Forissier 
8010c96a71dSJerome Forissier 		if (i == 0) {
8020c96a71dSJerome Forissier 			/* First block */
8030c96a71dSJerome Forissier 			offset = rawdata->byte_offset;
8040c96a71dSJerome Forissier 			size = RPMB_DATA_SIZE - offset;
8050c96a71dSJerome Forissier 		} else {
8060c96a71dSJerome Forissier 			/* Middle blocks */
8070c96a71dSJerome Forissier 			size = RPMB_DATA_SIZE;
8080c96a71dSJerome Forissier 			offset = 0;
8090c96a71dSJerome Forissier 		}
8100c96a71dSJerome Forissier 
8110c96a71dSJerome Forissier 		res = decrypt(data, &localfrm, size, offset, start_idx + i,
812b399f70bSJens Wiklander 			      fek, uuid);
8130c96a71dSJerome Forissier 		if (res != TEE_SUCCESS)
8140c96a71dSJerome Forissier 			goto func_exit;
8150c96a71dSJerome Forissier 
8160c96a71dSJerome Forissier 		data += size;
8170c96a71dSJerome Forissier 	}
8180c96a71dSJerome Forissier 
8190c96a71dSJerome Forissier 	/* Last block */
8200c96a71dSJerome Forissier 	size = (rawdata->len + rawdata->byte_offset) % RPMB_DATA_SIZE;
8210c96a71dSJerome Forissier 	if (size == 0)
8220c96a71dSJerome Forissier 		size = RPMB_DATA_SIZE;
823b399f70bSJens Wiklander 	res = decrypt(data, lastfrm, size, 0, start_idx + nbr_frms - 1, fek,
824b399f70bSJens Wiklander 		      uuid);
8250c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
8260c96a71dSJerome Forissier 		goto func_exit;
8270c96a71dSJerome Forissier 
8280c96a71dSJerome Forissier 	/* Update MAC against the last block */
829c69bc615SJens Wiklander 	res = crypto_mac_update(ctx, lastfrm->data, RPMB_MAC_PROTECT_DATA_SIZE);
8300c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
8310c96a71dSJerome Forissier 		goto func_exit;
8320c96a71dSJerome Forissier 
833c69bc615SJens Wiklander 	res = crypto_mac_final(ctx, rawdata->key_mac, RPMB_KEY_MAC_SIZE);
8340c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
8350c96a71dSJerome Forissier 		goto func_exit;
8360c96a71dSJerome Forissier 
8370c96a71dSJerome Forissier 	res = TEE_SUCCESS;
8380c96a71dSJerome Forissier 
8390c96a71dSJerome Forissier func_exit:
840c69bc615SJens Wiklander 	crypto_mac_free_ctx(ctx);
8410c96a71dSJerome Forissier 	return res;
8420c96a71dSJerome Forissier }
8430c96a71dSJerome Forissier 
tee_rpmb_resp_unpack_verify(struct rpmb_data_frame * datafrm,struct rpmb_raw_data * rawdata,uint16_t nbr_frms,const uint8_t * fek,const TEE_UUID * uuid)8440c96a71dSJerome Forissier static TEE_Result tee_rpmb_resp_unpack_verify(struct rpmb_data_frame *datafrm,
8450c96a71dSJerome Forissier 					      struct rpmb_raw_data *rawdata,
846b399f70bSJens Wiklander 					      uint16_t nbr_frms,
847b399f70bSJens Wiklander 					      const uint8_t *fek,
848b399f70bSJens Wiklander 					      const TEE_UUID *uuid)
8490c96a71dSJerome Forissier {
8500c96a71dSJerome Forissier 	TEE_Result res = TEE_ERROR_GENERIC;
8510c96a71dSJerome Forissier 	uint16_t msg_type;
8520c96a71dSJerome Forissier 	uint32_t wr_cnt;
8530c96a71dSJerome Forissier 	uint16_t blk_idx;
854fcd00ceaSVictor Chong 	uint8_t op_result;
8550c96a71dSJerome Forissier 	struct rpmb_data_frame lastfrm;
8560c96a71dSJerome Forissier 
8570c96a71dSJerome Forissier 	if (!datafrm || !rawdata || !nbr_frms)
8580c96a71dSJerome Forissier 		return TEE_ERROR_BAD_PARAMETERS;
8590c96a71dSJerome Forissier 
86085076371SEtienne Carriere 	if (IS_ENABLED(CFG_RPMB_FS_DEBUG_DATA)) {
86185076371SEtienne Carriere 		size_t i = 0;
86285076371SEtienne Carriere 
86385076371SEtienne Carriere 		for (i = 0; i < nbr_frms; i++) {
86485076371SEtienne Carriere 			DMSG("Dumping data frame %zu:", i);
8650c96a71dSJerome Forissier 			DHEXDUMP((uint8_t *)&datafrm[i] + RPMB_STUFF_DATA_SIZE,
8660c96a71dSJerome Forissier 				 512 - RPMB_STUFF_DATA_SIZE);
8670c96a71dSJerome Forissier 		}
86885076371SEtienne Carriere 	}
8690c96a71dSJerome Forissier 
8700c96a71dSJerome Forissier 	/* Make sure the last data packet can't be modified once verified */
8710c96a71dSJerome Forissier 	memcpy(&lastfrm, &datafrm[nbr_frms - 1], RPMB_DATA_FRAME_SIZE);
8720c96a71dSJerome Forissier 
8730c96a71dSJerome Forissier 	/* Handle operation result and translate to TEEC error code. */
874fcd00ceaSVictor Chong 	get_op_result_bits(lastfrm.op_result, &op_result);
875c340ba4bSVictor Chong 	if (op_result == RPMB_RESULT_AUTH_KEY_NOT_PROGRAMMED)
876c340ba4bSVictor Chong 		return TEE_ERROR_ITEM_NOT_FOUND;
8770c96a71dSJerome Forissier 	if (op_result != RPMB_RESULT_OK)
8780c96a71dSJerome Forissier 		return TEE_ERROR_GENERIC;
8790c96a71dSJerome Forissier 
8800c96a71dSJerome Forissier 	/* Check the response msg_type. */
8810c96a71dSJerome Forissier 	bytes_to_u16(lastfrm.msg_type, &msg_type);
8820c96a71dSJerome Forissier 	if (msg_type != rawdata->msg_type) {
8830c96a71dSJerome Forissier 		DMSG("Unexpected msg_type (0x%04x != 0x%04x)", msg_type,
8840c96a71dSJerome Forissier 		     rawdata->msg_type);
8850c96a71dSJerome Forissier 		return TEE_ERROR_GENERIC;
8860c96a71dSJerome Forissier 	}
8870c96a71dSJerome Forissier 
8880c96a71dSJerome Forissier 	if (rawdata->blk_idx) {
8890c96a71dSJerome Forissier 		bytes_to_u16(lastfrm.address, &blk_idx);
8900c96a71dSJerome Forissier 		if (blk_idx != *rawdata->blk_idx) {
8910c96a71dSJerome Forissier 			DMSG("Unexpected block index");
8920c96a71dSJerome Forissier 			return TEE_ERROR_GENERIC;
8930c96a71dSJerome Forissier 		}
8940c96a71dSJerome Forissier 	}
8950c96a71dSJerome Forissier 
8960c96a71dSJerome Forissier 	if (rawdata->write_counter) {
8970c96a71dSJerome Forissier 		wr_cnt = *rawdata->write_counter;
8980c96a71dSJerome Forissier 		bytes_to_u32(lastfrm.write_counter, rawdata->write_counter);
8990c96a71dSJerome Forissier 		if (msg_type == RPMB_MSG_TYPE_RESP_AUTH_DATA_WRITE) {
9000c96a71dSJerome Forissier 			/* Verify the write counter is incremented by 1 */
9010c96a71dSJerome Forissier 			if (*rawdata->write_counter != wr_cnt + 1) {
9020c96a71dSJerome Forissier 				DMSG("Counter mismatched (0x%04x/0x%04x)",
9030c96a71dSJerome Forissier 				     *rawdata->write_counter, wr_cnt + 1);
9040c96a71dSJerome Forissier 				return TEE_ERROR_SECURITY;
9050c96a71dSJerome Forissier 			}
9060c96a71dSJerome Forissier 			rpmb_ctx->wr_cnt++;
9070c96a71dSJerome Forissier 		}
9080c96a71dSJerome Forissier 	}
9090c96a71dSJerome Forissier 
9100c96a71dSJerome Forissier 	if (rawdata->nonce) {
9110c96a71dSJerome Forissier 		if (buf_compare_ct(rawdata->nonce, lastfrm.nonce,
9120c96a71dSJerome Forissier 				   RPMB_NONCE_SIZE) != 0) {
9130c96a71dSJerome Forissier 			DMSG("Nonce mismatched");
9140c96a71dSJerome Forissier 			return TEE_ERROR_SECURITY;
9150c96a71dSJerome Forissier 		}
9160c96a71dSJerome Forissier 	}
9170c96a71dSJerome Forissier 
9180c96a71dSJerome Forissier 	if (rawdata->key_mac) {
9190c96a71dSJerome Forissier 		if (msg_type == RPMB_MSG_TYPE_RESP_AUTH_DATA_READ) {
9200c96a71dSJerome Forissier 			if (!rawdata->data)
9210c96a71dSJerome Forissier 				return TEE_ERROR_GENERIC;
9220c96a71dSJerome Forissier 
9230c96a71dSJerome Forissier 			res = tee_rpmb_data_cpy_mac_calc(datafrm, rawdata,
9240c96a71dSJerome Forissier 							 nbr_frms, &lastfrm,
925b399f70bSJens Wiklander 							 fek, uuid);
9260c96a71dSJerome Forissier 
9270c96a71dSJerome Forissier 			if (res != TEE_SUCCESS)
9280c96a71dSJerome Forissier 				return res;
9290c96a71dSJerome Forissier 		} else {
9300c96a71dSJerome Forissier 			/*
9310c96a71dSJerome Forissier 			 * There should be only one data frame for
9320c96a71dSJerome Forissier 			 * other msg types.
9330c96a71dSJerome Forissier 			 */
9340c96a71dSJerome Forissier 			if (nbr_frms != 1)
9350c96a71dSJerome Forissier 				return TEE_ERROR_GENERIC;
9360c96a71dSJerome Forissier 
9370c96a71dSJerome Forissier 			res = tee_rpmb_mac_calc(rawdata->key_mac,
9380c96a71dSJerome Forissier 						RPMB_KEY_MAC_SIZE,
9390c96a71dSJerome Forissier 						rpmb_ctx->key,
9400c96a71dSJerome Forissier 						RPMB_KEY_MAC_SIZE,
9410c96a71dSJerome Forissier 						&lastfrm, 1);
9420c96a71dSJerome Forissier 
9430c96a71dSJerome Forissier 			if (res != TEE_SUCCESS)
9440c96a71dSJerome Forissier 				return res;
9450c96a71dSJerome Forissier 		}
9460c96a71dSJerome Forissier 
94748e10604SJerome Forissier 		if (consttime_memcmp(rawdata->key_mac,
9480c96a71dSJerome Forissier 				     (datafrm + nbr_frms - 1)->key_mac,
9490c96a71dSJerome Forissier 				     RPMB_KEY_MAC_SIZE) != 0) {
9500c96a71dSJerome Forissier 			DMSG("MAC mismatched:");
95185076371SEtienne Carriere 			if (IS_ENABLED(CFG_RPMB_FS_DEBUG_DATA))
95285076371SEtienne Carriere 				DHEXDUMP(rawdata->key_mac, RPMB_KEY_MAC_SIZE);
95385076371SEtienne Carriere 
9540c96a71dSJerome Forissier 			return TEE_ERROR_SECURITY;
9550c96a71dSJerome Forissier 		}
9560c96a71dSJerome Forissier 	}
9570c96a71dSJerome Forissier 
9580c96a71dSJerome Forissier 	return TEE_SUCCESS;
9590c96a71dSJerome Forissier }
9600c96a71dSJerome Forissier 
tee_rpmb_get_dev_info(struct rpmb_dev_info * dev_info)9613be2f85aSJens Wiklander static TEE_Result tee_rpmb_get_dev_info(struct rpmb_dev_info *dev_info)
9620c96a71dSJerome Forissier {
9630c96a71dSJerome Forissier 	TEE_Result res = TEE_ERROR_GENERIC;
9640c96a71dSJerome Forissier 	struct tee_rpmb_mem mem;
9650c96a71dSJerome Forissier 	struct rpmb_dev_info *di;
9660c96a71dSJerome Forissier 
9678dfdf392SJens Wiklander 	if (!dev_info || !rpmb_ctx->legacy_operation)
9680c96a71dSJerome Forissier 		return TEE_ERROR_BAD_PARAMETERS;
9690c96a71dSJerome Forissier 
9708dfdf392SJens Wiklander 	res = tee_rpmb_alloc(0, sizeof(struct rpmb_dev_info), &mem);
9710c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
972e94194d4SJens Wiklander 		return res;
9730c96a71dSJerome Forissier 
9748dfdf392SJens Wiklander 	mem.req_hdr->cmd = RPMB_CMD_GET_DEV_INFO;
9753be2f85aSJens Wiklander 	mem.req_hdr->dev_id = rpmb_ctx->dev_id;
9760c96a71dSJerome Forissier 
9778dfdf392SJens Wiklander 	di = (struct rpmb_dev_info *)mem.resp_data;
9780c96a71dSJerome Forissier 	di->ret_code = RPMB_CMD_GET_DEV_INFO_RET_ERROR;
9790c96a71dSJerome Forissier 
9800c96a71dSJerome Forissier 	res = tee_rpmb_invoke(&mem);
9810c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
982e94194d4SJens Wiklander 		return res;
9830c96a71dSJerome Forissier 
9848dfdf392SJens Wiklander 	*dev_info = *di;
9858dfdf392SJens Wiklander 	if (dev_info->ret_code != RPMB_CMD_GET_DEV_INFO_RET_OK)
986e94194d4SJens Wiklander 		return TEE_ERROR_GENERIC;
9870c96a71dSJerome Forissier 
98885076371SEtienne Carriere 	if (IS_ENABLED(CFG_RPMB_FS_DEBUG_DATA)) {
9890c96a71dSJerome Forissier 		DMSG("Dumping dev_info:");
9900c96a71dSJerome Forissier 		DHEXDUMP((uint8_t *)dev_info, sizeof(struct rpmb_dev_info));
99185076371SEtienne Carriere 	}
9920c96a71dSJerome Forissier 
993e94194d4SJens Wiklander 	return TEE_SUCCESS;
9940c96a71dSJerome Forissier }
9950c96a71dSJerome Forissier 
tee_rpmb_init_read_wr_cnt(uint32_t * wr_cnt)9968dfdf392SJens Wiklander static TEE_Result tee_rpmb_init_read_wr_cnt(uint32_t *wr_cnt)
9970c96a71dSJerome Forissier {
9980c96a71dSJerome Forissier 	TEE_Result res = TEE_ERROR_GENERIC;
9990c96a71dSJerome Forissier 	struct tee_rpmb_mem mem;
10000c96a71dSJerome Forissier 	uint16_t msg_type;
10010c96a71dSJerome Forissier 	uint8_t nonce[RPMB_NONCE_SIZE];
10020c96a71dSJerome Forissier 	uint8_t hmac[RPMB_KEY_MAC_SIZE];
10030c96a71dSJerome Forissier 	struct rpmb_raw_data rawdata;
10048dfdf392SJens Wiklander 	uint16_t op_result = 0;
10050c96a71dSJerome Forissier 
10060c96a71dSJerome Forissier 	if (!wr_cnt)
10070c96a71dSJerome Forissier 		return TEE_ERROR_BAD_PARAMETERS;
10080c96a71dSJerome Forissier 
10098dfdf392SJens Wiklander 	res = tee_rpmb_alloc(RPMB_DATA_FRAME_SIZE, RPMB_DATA_FRAME_SIZE, &mem);
10100c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
1011e94194d4SJens Wiklander 		return res;
10120c96a71dSJerome Forissier 
101336a063efSJens Wiklander 	res = crypto_rng_read(nonce, RPMB_NONCE_SIZE);
10140c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
1015e94194d4SJens Wiklander 		return res;
10160c96a71dSJerome Forissier 
10170c96a71dSJerome Forissier 	msg_type = RPMB_MSG_TYPE_REQ_WRITE_COUNTER_VAL_READ;
10180c96a71dSJerome Forissier 
10190c96a71dSJerome Forissier 	memset(&rawdata, 0x00, sizeof(struct rpmb_raw_data));
10200c96a71dSJerome Forissier 	rawdata.msg_type = msg_type;
10210c96a71dSJerome Forissier 	rawdata.nonce = nonce;
10220c96a71dSJerome Forissier 
10238dfdf392SJens Wiklander 	res = tee_rpmb_req_pack(mem.req_hdr, mem.req_data, &rawdata, 1, NULL,
10248dfdf392SJens Wiklander 				NULL);
10250c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
1026e94194d4SJens Wiklander 		return res;
10270c96a71dSJerome Forissier 
10280c96a71dSJerome Forissier 	res = tee_rpmb_invoke(&mem);
10290c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
1030e94194d4SJens Wiklander 		return res;
10310c96a71dSJerome Forissier 
10320c96a71dSJerome Forissier 	msg_type = RPMB_MSG_TYPE_RESP_WRITE_COUNTER_VAL_READ;
10330c96a71dSJerome Forissier 
10340c96a71dSJerome Forissier 	memset(&rawdata, 0x00, sizeof(struct rpmb_raw_data));
10350c96a71dSJerome Forissier 	rawdata.msg_type = msg_type;
10368dfdf392SJens Wiklander 	rawdata.op_result = &op_result;
10370c96a71dSJerome Forissier 	rawdata.write_counter = wr_cnt;
10380c96a71dSJerome Forissier 	rawdata.nonce = nonce;
10390c96a71dSJerome Forissier 	rawdata.key_mac = hmac;
10400c96a71dSJerome Forissier 
10418dfdf392SJens Wiklander 	return tee_rpmb_resp_unpack_verify(mem.resp_data, &rawdata, 1, NULL,
10428dfdf392SJens Wiklander 					   NULL);
10430c96a71dSJerome Forissier }
10440c96a71dSJerome Forissier 
1045ed1993b7SJerome Forissier #ifdef CFG_RPMB_WRITE_KEY
tee_rpmb_write_key(void)10463be2f85aSJens Wiklander static TEE_Result tee_rpmb_write_key(void)
10470c96a71dSJerome Forissier {
10480c96a71dSJerome Forissier 	TEE_Result res = TEE_ERROR_GENERIC;
10490c96a71dSJerome Forissier 	struct tee_rpmb_mem mem = { 0 };
10500c96a71dSJerome Forissier 	uint16_t msg_type;
10510c96a71dSJerome Forissier 	struct rpmb_raw_data rawdata;
10520c96a71dSJerome Forissier 
10538dfdf392SJens Wiklander 	res = tee_rpmb_alloc(RPMB_DATA_FRAME_SIZE, RPMB_DATA_FRAME_SIZE, &mem);
10540c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
1055e94194d4SJens Wiklander 		return res;
10560c96a71dSJerome Forissier 
10570c96a71dSJerome Forissier 	msg_type = RPMB_MSG_TYPE_REQ_AUTH_KEY_PROGRAM;
10580c96a71dSJerome Forissier 
10590c96a71dSJerome Forissier 	memset(&rawdata, 0x00, sizeof(struct rpmb_raw_data));
10600c96a71dSJerome Forissier 	rawdata.msg_type = msg_type;
10610c96a71dSJerome Forissier 	rawdata.key_mac = rpmb_ctx->key;
10620c96a71dSJerome Forissier 
10638dfdf392SJens Wiklander 	res = tee_rpmb_req_pack(mem.req_hdr, mem.req_data, &rawdata, 1, NULL,
10648dfdf392SJens Wiklander 				NULL);
10650c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
1066e94194d4SJens Wiklander 		return res;
10670c96a71dSJerome Forissier 
10680c96a71dSJerome Forissier 	res = tee_rpmb_invoke(&mem);
10690c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
1070e94194d4SJens Wiklander 		return res;
10710c96a71dSJerome Forissier 
10720c96a71dSJerome Forissier 	msg_type = RPMB_MSG_TYPE_RESP_AUTH_KEY_PROGRAM;
10730c96a71dSJerome Forissier 
10740c96a71dSJerome Forissier 	memset(&rawdata, 0x00, sizeof(struct rpmb_raw_data));
10750c96a71dSJerome Forissier 	rawdata.msg_type = msg_type;
10760c96a71dSJerome Forissier 
10778dfdf392SJens Wiklander 	return tee_rpmb_resp_unpack_verify(mem.resp_data, &rawdata, 1, NULL,
10788dfdf392SJens Wiklander 					   NULL);
10790c96a71dSJerome Forissier }
10800c96a71dSJerome Forissier 
tee_rpmb_write_and_verify_key(void)10813be2f85aSJens Wiklander static TEE_Result tee_rpmb_write_and_verify_key(void)
1082ed1993b7SJerome Forissier {
1083ed1993b7SJerome Forissier 	TEE_Result res;
1084ed1993b7SJerome Forissier 
1085db498484SJens Wiklander 	if (!plat_rpmb_key_is_ready()) {
1086b1042535SRouven Czerwinski 		DMSG("RPMB INIT: platform indicates RPMB key is not ready");
1087b1042535SRouven Czerwinski 		return TEE_ERROR_BAD_STATE;
1088b1042535SRouven Czerwinski 	}
1089b1042535SRouven Czerwinski 
1090e9ae33c4SVictor Chong 	DMSG("RPMB INIT: Writing Key value:");
1091e9ae33c4SVictor Chong 	DHEXDUMP(rpmb_ctx->key, RPMB_KEY_MAC_SIZE);
1092e9ae33c4SVictor Chong 
10933be2f85aSJens Wiklander 	res = tee_rpmb_write_key();
1094ed1993b7SJerome Forissier 	if (res == TEE_SUCCESS) {
1095ed1993b7SJerome Forissier 		DMSG("RPMB INIT: Verifying Key");
10968dfdf392SJens Wiklander 		res = tee_rpmb_init_read_wr_cnt(&rpmb_ctx->wr_cnt);
1097ed1993b7SJerome Forissier 	}
1098ed1993b7SJerome Forissier 	return res;
1099ed1993b7SJerome Forissier }
1100ed1993b7SJerome Forissier #else
tee_rpmb_write_and_verify_key(void)11013be2f85aSJens Wiklander static TEE_Result tee_rpmb_write_and_verify_key(void)
1102ed1993b7SJerome Forissier {
1103c340ba4bSVictor Chong 	DMSG("RPMB INIT: CFG_RPMB_WRITE_KEY is not set");
110490827a1eSJudy Wang 	return TEE_ERROR_STORAGE_NOT_AVAILABLE;
1105ed1993b7SJerome Forissier }
1106ed1993b7SJerome Forissier #endif
1107ed1993b7SJerome Forissier 
rpmb_set_dev_info(const struct rpmb_dev_info * dev_info)11088dfdf392SJens Wiklander static TEE_Result rpmb_set_dev_info(const struct rpmb_dev_info *dev_info)
11098dfdf392SJens Wiklander {
11108dfdf392SJens Wiklander 	uint32_t nblocks = 0;
11118dfdf392SJens Wiklander 
11128dfdf392SJens Wiklander 	DMSG("RPMB: Syncing device information");
11138dfdf392SJens Wiklander 
11148dfdf392SJens Wiklander 	DMSG("RPMB: RPMB size is %"PRIu8"*128 KB", dev_info->rpmb_size_mult);
11158dfdf392SJens Wiklander 	DMSG("RPMB: Reliable Write Sector Count is %"PRIu8,
11168dfdf392SJens Wiklander 	     dev_info->rel_wr_sec_c);
11178dfdf392SJens Wiklander 	DMSG("RPMB: CID");
11188dfdf392SJens Wiklander 	DHEXDUMP(dev_info->cid, sizeof(dev_info->cid));
11198dfdf392SJens Wiklander 
11208dfdf392SJens Wiklander 	if (!dev_info->rpmb_size_mult)
11218dfdf392SJens Wiklander 		return TEE_ERROR_GENERIC;
11228dfdf392SJens Wiklander 
11238dfdf392SJens Wiklander 	if (MUL_OVERFLOW(dev_info->rpmb_size_mult,
11248dfdf392SJens Wiklander 			 RPMB_SIZE_SINGLE / RPMB_DATA_SIZE, &nblocks) ||
11258dfdf392SJens Wiklander 	    SUB_OVERFLOW(nblocks, 1, &rpmb_ctx->max_blk_idx))
11268dfdf392SJens Wiklander 		return TEE_ERROR_BAD_PARAMETERS;
11278dfdf392SJens Wiklander 
11288dfdf392SJens Wiklander 	memcpy(rpmb_ctx->cid, dev_info->cid, RPMB_EMMC_CID_SIZE);
11298dfdf392SJens Wiklander 
11308dfdf392SJens Wiklander 	if (IS_ENABLED(RPMB_DRIVER_MULTIPLE_WRITE_FIXED))
11318dfdf392SJens Wiklander 		rpmb_ctx->rel_wr_blkcnt = dev_info->rel_wr_sec_c * 2;
11328dfdf392SJens Wiklander 	else
11338dfdf392SJens Wiklander 		rpmb_ctx->rel_wr_blkcnt = 1;
11348dfdf392SJens Wiklander 
11358dfdf392SJens Wiklander 	return TEE_SUCCESS;
11368dfdf392SJens Wiklander }
11378dfdf392SJens Wiklander 
legacy_rpmb_init(void)11388dfdf392SJens Wiklander static TEE_Result legacy_rpmb_init(void)
11390c96a71dSJerome Forissier {
11400c96a71dSJerome Forissier 	TEE_Result res = TEE_SUCCESS;
11415695e448SSadiq Hussain 	struct rpmb_dev_info dev_info = { };
11420c96a71dSJerome Forissier 
11438dfdf392SJens Wiklander 	DMSG("Trying legacy RPMB init");
11448dfdf392SJens Wiklander 	rpmb_ctx->legacy_operation = true;
11453be2f85aSJens Wiklander 	rpmb_ctx->dev_id = CFG_RPMB_FS_DEV_ID;
1146e94194d4SJens Wiklander 	rpmb_ctx->shm_type = THREAD_SHM_TYPE_APPLICATION;
11470c96a71dSJerome Forissier 
11480c96a71dSJerome Forissier 	if (!rpmb_ctx->dev_info_synced) {
11490c96a71dSJerome Forissier 		dev_info.rpmb_size_mult = 0;
11500c96a71dSJerome Forissier 		dev_info.rel_wr_sec_c = 0;
11513be2f85aSJens Wiklander 		res = tee_rpmb_get_dev_info(&dev_info);
11520c96a71dSJerome Forissier 		if (res != TEE_SUCCESS)
11538dfdf392SJens Wiklander 			return res;
11540c96a71dSJerome Forissier 
11558dfdf392SJens Wiklander 		res = rpmb_set_dev_info(&dev_info);
11568dfdf392SJens Wiklander 		if (res)
11578dfdf392SJens Wiklander 			return res;
11580c96a71dSJerome Forissier 
11590c96a71dSJerome Forissier 		rpmb_ctx->dev_info_synced = true;
11600c96a71dSJerome Forissier 	}
11610c96a71dSJerome Forissier 
11620c96a71dSJerome Forissier 	if (!rpmb_ctx->key_derived) {
11630c96a71dSJerome Forissier 		DMSG("RPMB INIT: Deriving key");
11640c96a71dSJerome Forissier 
11653be2f85aSJens Wiklander 		res = tee_rpmb_key_gen(rpmb_ctx->key, RPMB_KEY_MAC_SIZE);
1166ce925809SVictor Chong 		if (res != TEE_SUCCESS) {
1167ce925809SVictor Chong 			EMSG("RPMB INIT: Deriving key failed with error 0x%x",
1168ce925809SVictor Chong 				res);
11698dfdf392SJens Wiklander 			return res;
1170ce925809SVictor Chong 		}
11710c96a71dSJerome Forissier 
11720c96a71dSJerome Forissier 		rpmb_ctx->key_derived = true;
11730c96a71dSJerome Forissier 	}
11740c96a71dSJerome Forissier 
11750c96a71dSJerome Forissier 	/* Perform a write counter read to verify if the key is ok. */
11760c96a71dSJerome Forissier 	if (!rpmb_ctx->wr_cnt_synced || !rpmb_ctx->key_verified) {
11770c96a71dSJerome Forissier 		DMSG("RPMB INIT: Verifying Key");
11780c96a71dSJerome Forissier 
11798dfdf392SJens Wiklander 		res = tee_rpmb_init_read_wr_cnt(&rpmb_ctx->wr_cnt);
11808dfdf392SJens Wiklander 		if (res == TEE_SUCCESS) {
11818dfdf392SJens Wiklander 			DMSG("Found working RPMB device");
11828dfdf392SJens Wiklander 			rpmb_ctx->key_verified = true;
11838dfdf392SJens Wiklander 			rpmb_ctx->wr_cnt_synced = true;
11848dfdf392SJens Wiklander 		} else if (res == TEE_ERROR_ITEM_NOT_FOUND &&
1185c340ba4bSVictor Chong 			   !rpmb_ctx->key_verified) {
11860c96a71dSJerome Forissier 			/*
11870c96a71dSJerome Forissier 			 * Need to write the key here and verify it.
11880c96a71dSJerome Forissier 			 */
1189c340ba4bSVictor Chong 			DMSG("RPMB INIT: Auth key not yet written");
11903be2f85aSJens Wiklander 			res = tee_rpmb_write_and_verify_key();
11918dfdf392SJens Wiklander 		} else {
11928dfdf392SJens Wiklander 			EMSG("Verify key failed! %#"PRIx32, res);
1193c340ba4bSVictor Chong 			EMSG("Make sure key here matches device key");
11940c96a71dSJerome Forissier 		}
11950c96a71dSJerome Forissier 	}
11960c96a71dSJerome Forissier 
11970c96a71dSJerome Forissier 	return res;
11980c96a71dSJerome Forissier }
11990c96a71dSJerome Forissier 
12008dfdf392SJens Wiklander /* This function must never return TEE_SUCCESS if rpmb_ctx == NULL */
tee_rpmb_init(void)12018dfdf392SJens Wiklander static TEE_Result tee_rpmb_init(void)
12028dfdf392SJens Wiklander {
12038dfdf392SJens Wiklander 	TEE_Result res = TEE_SUCCESS;
12048dfdf392SJens Wiklander 	struct rpmb_dev_info dev_info = { };
12058dfdf392SJens Wiklander 
12068dfdf392SJens Wiklander 	if (rpmb_dead)
12078dfdf392SJens Wiklander 		return TEE_ERROR_COMMUNICATION;
12088dfdf392SJens Wiklander 
12098dfdf392SJens Wiklander 	if (!rpmb_ctx) {
12108dfdf392SJens Wiklander 		rpmb_ctx = calloc(1, sizeof(struct tee_rpmb_ctx));
12118dfdf392SJens Wiklander 		if (!rpmb_ctx)
12128dfdf392SJens Wiklander 			return TEE_ERROR_OUT_OF_MEMORY;
12138dfdf392SJens Wiklander 	}
12148dfdf392SJens Wiklander 
12158dfdf392SJens Wiklander 	if (rpmb_ctx->reinit) {
12168dfdf392SJens Wiklander 		if (!rpmb_ctx->key_verified) {
12178dfdf392SJens Wiklander 			rpmb_ctx->wr_cnt_synced = false;
12188dfdf392SJens Wiklander 			rpmb_ctx->key_derived = false;
12198dfdf392SJens Wiklander 			rpmb_ctx->dev_info_synced = false;
12208dfdf392SJens Wiklander 			rpmb_ctx->reinit = false;
12218dfdf392SJens Wiklander 			goto next;
12228dfdf392SJens Wiklander 		}
12238dfdf392SJens Wiklander 		res = rpmb_probe_reset();
12248dfdf392SJens Wiklander 		if (res) {
122596e8f740SEtienne Carriere 			if (res != TEE_ERROR_NOT_SUPPORTED &&
122696e8f740SEtienne Carriere 			    res != TEE_ERROR_NOT_IMPLEMENTED)
12278dfdf392SJens Wiklander 				return res;
12288dfdf392SJens Wiklander 			return legacy_rpmb_init();
12298dfdf392SJens Wiklander 		}
12308dfdf392SJens Wiklander 		while (true) {
12318dfdf392SJens Wiklander 			res = rpmb_probe_next(&dev_info);
12328dfdf392SJens Wiklander 			if (res) {
12338dfdf392SJens Wiklander 				DMSG("rpmb_probe_next error %#"PRIx32, res);
12348dfdf392SJens Wiklander 				return res;
12358dfdf392SJens Wiklander 			}
12368dfdf392SJens Wiklander 			if (!memcmp(rpmb_ctx->cid, dev_info.cid,
12378dfdf392SJens Wiklander 				    RPMB_EMMC_CID_SIZE)) {
12388dfdf392SJens Wiklander 				rpmb_ctx->reinit = false;
12398dfdf392SJens Wiklander 				return TEE_SUCCESS;
12408dfdf392SJens Wiklander 			}
12418dfdf392SJens Wiklander 		}
12428dfdf392SJens Wiklander 	}
12438dfdf392SJens Wiklander 
12448dfdf392SJens Wiklander 	if (rpmb_ctx->key_verified)
12458dfdf392SJens Wiklander 		return TEE_SUCCESS;
12468dfdf392SJens Wiklander 
12478dfdf392SJens Wiklander next:
12488dfdf392SJens Wiklander 	if (IS_ENABLED(CFG_RPMB_WRITE_KEY))
12498dfdf392SJens Wiklander 		return legacy_rpmb_init();
12508dfdf392SJens Wiklander 
12518dfdf392SJens Wiklander 	res = rpmb_probe_reset();
12528dfdf392SJens Wiklander 	if (res) {
125396e8f740SEtienne Carriere 		if (res != TEE_ERROR_NOT_SUPPORTED &&
125496e8f740SEtienne Carriere 		    res != TEE_ERROR_NOT_IMPLEMENTED)
12558dfdf392SJens Wiklander 			return res;
12568dfdf392SJens Wiklander 		return legacy_rpmb_init();
12578dfdf392SJens Wiklander 	}
12588dfdf392SJens Wiklander 
12598dfdf392SJens Wiklander 	while (true) {
12608dfdf392SJens Wiklander 		res = rpmb_probe_next(&dev_info);
12618dfdf392SJens Wiklander 		if (res) {
12628dfdf392SJens Wiklander 			DMSG("rpmb_probe_next error %#"PRIx32, res);
12638dfdf392SJens Wiklander 			return res;
12648dfdf392SJens Wiklander 		}
12658dfdf392SJens Wiklander 		res = rpmb_set_dev_info(&dev_info);
12668dfdf392SJens Wiklander 		if (res) {
12678dfdf392SJens Wiklander 			DMSG("Invalid device info, looking for another device");
12688dfdf392SJens Wiklander 			continue;
12698dfdf392SJens Wiklander 		}
12708dfdf392SJens Wiklander 
12718dfdf392SJens Wiklander 		res = tee_rpmb_key_gen(rpmb_ctx->key, RPMB_KEY_MAC_SIZE);
12728dfdf392SJens Wiklander 		if (res)
12738dfdf392SJens Wiklander 			return res;
12748dfdf392SJens Wiklander 
12758dfdf392SJens Wiklander 		res = tee_rpmb_init_read_wr_cnt(&rpmb_ctx->wr_cnt);
12768dfdf392SJens Wiklander 		if (res)
12778dfdf392SJens Wiklander 			continue;
12788dfdf392SJens Wiklander 		break;
12798dfdf392SJens Wiklander 	}
12808dfdf392SJens Wiklander 
12818dfdf392SJens Wiklander 	DMSG("Found working RPMB device");
12828dfdf392SJens Wiklander 	rpmb_ctx->key_verified = true;
12838dfdf392SJens Wiklander 	rpmb_ctx->wr_cnt_synced = true;
12848dfdf392SJens Wiklander 
12858dfdf392SJens Wiklander 	return TEE_SUCCESS;
12868dfdf392SJens Wiklander }
12878dfdf392SJens Wiklander 
tee_rpmb_reinit(void)12888dfdf392SJens Wiklander TEE_Result tee_rpmb_reinit(void)
12898dfdf392SJens Wiklander {
12908dfdf392SJens Wiklander 	if (rpmb_ctx)
12918dfdf392SJens Wiklander 		rpmb_ctx->reinit = true;
12928dfdf392SJens Wiklander 	return tee_rpmb_init();
12938dfdf392SJens Wiklander }
12948dfdf392SJens Wiklander 
1295a7e22cf5SLijianhui (Airbak) /*
1296a7e22cf5SLijianhui (Airbak)  * Read RPMB data in bytes.
1297a7e22cf5SLijianhui (Airbak)  *
1298a7e22cf5SLijianhui (Airbak)  * @addr       Byte address of data.
1299a7e22cf5SLijianhui (Airbak)  * @data       Pointer to the data.
1300a7e22cf5SLijianhui (Airbak)  * @len        Size of data in bytes.
1301a7e22cf5SLijianhui (Airbak)  * @fek        Encrypted File Encryption Key or NULL.
1302a7e22cf5SLijianhui (Airbak)  */
tee_rpmb_read(uint32_t addr,uint8_t * data,uint32_t len,const uint8_t * fek,const TEE_UUID * uuid)13033be2f85aSJens Wiklander static TEE_Result tee_rpmb_read(uint32_t addr, uint8_t *data,
1304b399f70bSJens Wiklander 				uint32_t len, const uint8_t *fek,
1305b399f70bSJens Wiklander 				const TEE_UUID *uuid)
13060c96a71dSJerome Forissier {
13070c96a71dSJerome Forissier 	TEE_Result res = TEE_ERROR_GENERIC;
13080c96a71dSJerome Forissier 	struct tee_rpmb_mem mem = { 0 };
13090c96a71dSJerome Forissier 	uint16_t msg_type;
13100c96a71dSJerome Forissier 	uint8_t nonce[RPMB_NONCE_SIZE];
13110c96a71dSJerome Forissier 	uint8_t hmac[RPMB_KEY_MAC_SIZE];
13120c96a71dSJerome Forissier 	struct rpmb_raw_data rawdata;
13130c96a71dSJerome Forissier 	uint16_t blk_idx;
13140c96a71dSJerome Forissier 	uint16_t blkcnt;
13150c96a71dSJerome Forissier 	uint8_t byte_offset;
13160c96a71dSJerome Forissier 
13170c96a71dSJerome Forissier 	if (!data || !len)
13180c96a71dSJerome Forissier 		return TEE_ERROR_BAD_PARAMETERS;
13190c96a71dSJerome Forissier 
13200c96a71dSJerome Forissier 	blk_idx = addr / RPMB_DATA_SIZE;
13210c96a71dSJerome Forissier 	byte_offset = addr % RPMB_DATA_SIZE;
13220c96a71dSJerome Forissier 
1323ea81076fSJerome Forissier 	if (len + byte_offset + RPMB_DATA_SIZE < RPMB_DATA_SIZE) {
1324ea81076fSJerome Forissier 		/* Overflow */
1325ea81076fSJerome Forissier 		return TEE_ERROR_BAD_PARAMETERS;
1326ea81076fSJerome Forissier 	}
132704e46975SEtienne Carriere 	blkcnt = ROUNDUP_DIV(len + byte_offset, RPMB_DATA_SIZE);
13283be2f85aSJens Wiklander 	res = tee_rpmb_init();
13290c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
1330e94194d4SJens Wiklander 		return res;
13310c96a71dSJerome Forissier 
13328dfdf392SJens Wiklander 	res = tee_rpmb_alloc(RPMB_DATA_FRAME_SIZE,
13338dfdf392SJens Wiklander 			     RPMB_DATA_FRAME_SIZE * blkcnt, &mem);
13340c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
1335e94194d4SJens Wiklander 		return res;
13360c96a71dSJerome Forissier 
13370c96a71dSJerome Forissier 	msg_type = RPMB_MSG_TYPE_REQ_AUTH_DATA_READ;
133836a063efSJens Wiklander 	res = crypto_rng_read(nonce, RPMB_NONCE_SIZE);
13390c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
1340e94194d4SJens Wiklander 		return res;
13410c96a71dSJerome Forissier 
13420c96a71dSJerome Forissier 	memset(&rawdata, 0x00, sizeof(struct rpmb_raw_data));
13430c96a71dSJerome Forissier 	rawdata.msg_type = msg_type;
13440c96a71dSJerome Forissier 	rawdata.nonce = nonce;
13450c96a71dSJerome Forissier 	rawdata.blk_idx = &blk_idx;
13468dfdf392SJens Wiklander 	res = tee_rpmb_req_pack(mem.req_hdr, mem.req_data, &rawdata, 1, NULL,
13478dfdf392SJens Wiklander 				NULL);
13480c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
1349e94194d4SJens Wiklander 		return res;
13500c96a71dSJerome Forissier 
13518dfdf392SJens Wiklander 	if (mem.req_hdr)
13528dfdf392SJens Wiklander 		mem.req_hdr->block_count = blkcnt;
13530c96a71dSJerome Forissier 
13540c96a71dSJerome Forissier 	DMSG("Read %u block%s at index %u", blkcnt, ((blkcnt > 1) ? "s" : ""),
13550c96a71dSJerome Forissier 	     blk_idx);
13560c96a71dSJerome Forissier 
13570c96a71dSJerome Forissier 	res = tee_rpmb_invoke(&mem);
13580c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
1359e94194d4SJens Wiklander 		return res;
13600c96a71dSJerome Forissier 
13610c96a71dSJerome Forissier 	msg_type = RPMB_MSG_TYPE_RESP_AUTH_DATA_READ;
13620c96a71dSJerome Forissier 
13630c96a71dSJerome Forissier 	memset(&rawdata, 0x00, sizeof(struct rpmb_raw_data));
13640c96a71dSJerome Forissier 	rawdata.msg_type = msg_type;
13650c96a71dSJerome Forissier 	rawdata.block_count = &blkcnt;
13660c96a71dSJerome Forissier 	rawdata.blk_idx = &blk_idx;
13670c96a71dSJerome Forissier 	rawdata.nonce = nonce;
13680c96a71dSJerome Forissier 	rawdata.key_mac = hmac;
13690c96a71dSJerome Forissier 	rawdata.data = data;
13700c96a71dSJerome Forissier 
13710c96a71dSJerome Forissier 	rawdata.len = len;
13720c96a71dSJerome Forissier 	rawdata.byte_offset = byte_offset;
13730c96a71dSJerome Forissier 
13748dfdf392SJens Wiklander 	return tee_rpmb_resp_unpack_verify(mem.resp_data, &rawdata, blkcnt,
13758dfdf392SJens Wiklander 					   fek, uuid);
13760c96a71dSJerome Forissier }
13770c96a71dSJerome Forissier 
write_req(uint16_t blk_idx,const void * data_blks,uint16_t blkcnt,const uint8_t * fek,const TEE_UUID * uuid,struct tee_rpmb_mem * mem)13783be2f85aSJens Wiklander static TEE_Result write_req(uint16_t blk_idx,
1379a8fb1651SJens Wiklander 			    const void *data_blks, uint16_t blkcnt,
1380a8fb1651SJens Wiklander 			    const uint8_t *fek, const TEE_UUID *uuid,
13818dfdf392SJens Wiklander 			    struct tee_rpmb_mem *mem)
1382a8fb1651SJens Wiklander {
1383a8fb1651SJens Wiklander 	TEE_Result res = TEE_SUCCESS;
1384a8fb1651SJens Wiklander 	uint8_t hmac[RPMB_KEY_MAC_SIZE] = { };
1385a8fb1651SJens Wiklander 	uint32_t wr_cnt = rpmb_ctx->wr_cnt;
1386a8fb1651SJens Wiklander 	struct rpmb_raw_data rawdata = { };
1387a8fb1651SJens Wiklander 	size_t retry_count = 0;
1388a8fb1651SJens Wiklander 
1389a8fb1651SJens Wiklander 	while (true) {
13908dfdf392SJens Wiklander 		if (mem->req_hdr)
13918dfdf392SJens Wiklander 			memset(mem->req_hdr, 0, mem->req_size);
13928dfdf392SJens Wiklander 		else
13938dfdf392SJens Wiklander 			memset(mem->req_data, 0, mem->req_size);
13948dfdf392SJens Wiklander 		memset(mem->resp_data, 0, mem->resp_size);
1395a8fb1651SJens Wiklander 
1396a8fb1651SJens Wiklander 		memset(&rawdata, 0, sizeof(struct rpmb_raw_data));
1397a8fb1651SJens Wiklander 		rawdata.msg_type = RPMB_MSG_TYPE_REQ_AUTH_DATA_WRITE;
1398a8fb1651SJens Wiklander 		rawdata.block_count = &blkcnt;
1399a8fb1651SJens Wiklander 		rawdata.blk_idx = &blk_idx;
1400a8fb1651SJens Wiklander 		rawdata.write_counter = &wr_cnt;
1401a8fb1651SJens Wiklander 		rawdata.key_mac = hmac;
1402a8fb1651SJens Wiklander 		rawdata.data = (uint8_t *)data_blks;
1403a8fb1651SJens Wiklander 
14048dfdf392SJens Wiklander 		res = tee_rpmb_req_pack(mem->req_hdr, mem->req_data, &rawdata,
14058dfdf392SJens Wiklander 					blkcnt, fek, uuid);
1406a8fb1651SJens Wiklander 		if (res) {
1407a8fb1651SJens Wiklander 			/*
1408a8fb1651SJens Wiklander 			 * If we haven't tried to send a request yet we can
1409a8fb1651SJens Wiklander 			 * allow a failure here since there's no chance of
1410a8fb1651SJens Wiklander 			 * an intercepted request with a valid write
1411a8fb1651SJens Wiklander 			 * counter.
1412a8fb1651SJens Wiklander 			 */
1413a8fb1651SJens Wiklander 			if (!retry_count)
1414a8fb1651SJens Wiklander 				return res;
1415a8fb1651SJens Wiklander 
1416a8fb1651SJens Wiklander 			retry_count++;
1417a8fb1651SJens Wiklander 			if (retry_count >= RPMB_MAX_RETRIES)
1418a8fb1651SJens Wiklander 				goto out_of_retries;
1419a8fb1651SJens Wiklander 
1420a8fb1651SJens Wiklander 			DMSG("Request pack failed, retrying %zu", retry_count);
1421a8fb1651SJens Wiklander 			continue;
1422a8fb1651SJens Wiklander 		}
1423a8fb1651SJens Wiklander 
1424a8fb1651SJens Wiklander 		res = tee_rpmb_invoke(mem);
1425a8fb1651SJens Wiklander 		if (res != TEE_SUCCESS) {
1426a8fb1651SJens Wiklander 			retry_count++;
1427a8fb1651SJens Wiklander 			if (retry_count >= RPMB_MAX_RETRIES)
1428a8fb1651SJens Wiklander 				goto out_of_retries;
1429a8fb1651SJens Wiklander 			/*
1430a8fb1651SJens Wiklander 			 * To force wr_cnt sync next time, as it might get
1431a8fb1651SJens Wiklander 			 * out of sync due to inconsistent operation result!
1432a8fb1651SJens Wiklander 			 */
1433a8fb1651SJens Wiklander 			rpmb_ctx->wr_cnt_synced = false;
1434a8fb1651SJens Wiklander 			DMSG("Write invoke failed, retrying %zu", retry_count);
1435a8fb1651SJens Wiklander 			continue;
1436a8fb1651SJens Wiklander 		}
1437a8fb1651SJens Wiklander 
1438a8fb1651SJens Wiklander 		memset(&rawdata, 0, sizeof(struct rpmb_raw_data));
1439a8fb1651SJens Wiklander 		rawdata.msg_type = RPMB_MSG_TYPE_RESP_AUTH_DATA_WRITE;
1440a8fb1651SJens Wiklander 		rawdata.block_count = &blkcnt;
1441a8fb1651SJens Wiklander 		rawdata.blk_idx = &blk_idx;
1442a8fb1651SJens Wiklander 		rawdata.write_counter = &wr_cnt;
1443a8fb1651SJens Wiklander 		rawdata.key_mac = hmac;
1444a8fb1651SJens Wiklander 
14458dfdf392SJens Wiklander 		res = tee_rpmb_resp_unpack_verify(mem->resp_data, &rawdata, 1,
14468dfdf392SJens Wiklander 						  NULL, NULL);
1447a8fb1651SJens Wiklander 		if (res != TEE_SUCCESS) {
1448a8fb1651SJens Wiklander 			retry_count++;
1449a8fb1651SJens Wiklander 			if (retry_count >= RPMB_MAX_RETRIES)
1450a8fb1651SJens Wiklander 				goto out_of_retries;
1451a8fb1651SJens Wiklander 			/*
1452a8fb1651SJens Wiklander 			 * To force wr_cnt sync next time, as it might get
1453a8fb1651SJens Wiklander 			 * out of sync due to inconsistent operation result!
1454a8fb1651SJens Wiklander 			 */
1455a8fb1651SJens Wiklander 			rpmb_ctx->wr_cnt_synced = false;
1456a8fb1651SJens Wiklander 			DMSG("Write resp unpack verify failed, retrying %zu",
1457a8fb1651SJens Wiklander 			     retry_count);
1458a8fb1651SJens Wiklander 			continue;
1459a8fb1651SJens Wiklander 		}
1460a8fb1651SJens Wiklander 
1461a8fb1651SJens Wiklander 		return TEE_SUCCESS;
1462a8fb1651SJens Wiklander 	}
1463a8fb1651SJens Wiklander 
1464a8fb1651SJens Wiklander out_of_retries:
1465a8fb1651SJens Wiklander 	rpmb_dead = true;
1466a8fb1651SJens Wiklander 	/*
1467a8fb1651SJens Wiklander 	 * We're using this error code to cause an eventuall calling TA to
1468a8fb1651SJens Wiklander 	 * panic since we don't know if the data to be written has been
1469a8fb1651SJens Wiklander 	 * committed to storage or not.
1470a8fb1651SJens Wiklander 	 */
1471a8fb1651SJens Wiklander 	return TEE_ERROR_COMMUNICATION;
1472a8fb1651SJens Wiklander }
1473a8fb1651SJens Wiklander 
tee_rpmb_write_blk(uint16_t blk_idx,const uint8_t * data_blks,uint16_t blkcnt,const uint8_t * fek,const TEE_UUID * uuid)14743be2f85aSJens Wiklander static TEE_Result tee_rpmb_write_blk(uint16_t blk_idx,
14750c96a71dSJerome Forissier 				     const uint8_t *data_blks, uint16_t blkcnt,
1476b399f70bSJens Wiklander 				     const uint8_t *fek, const TEE_UUID *uuid)
14770c96a71dSJerome Forissier {
14780c96a71dSJerome Forissier 	TEE_Result res;
14790c96a71dSJerome Forissier 	struct tee_rpmb_mem mem;
14800c96a71dSJerome Forissier 	uint32_t req_size;
14810c96a71dSJerome Forissier 	uint32_t nbr_writes;
14820c96a71dSJerome Forissier 	uint16_t tmp_blkcnt;
14830c96a71dSJerome Forissier 	uint16_t tmp_blk_idx;
14840c96a71dSJerome Forissier 	uint16_t i;
14850c96a71dSJerome Forissier 
14860c96a71dSJerome Forissier 	DMSG("Write %u block%s at index %u", blkcnt, ((blkcnt > 1) ? "s" : ""),
14870c96a71dSJerome Forissier 	     blk_idx);
14880c96a71dSJerome Forissier 
14890c96a71dSJerome Forissier 	if (!data_blks || !blkcnt)
14900c96a71dSJerome Forissier 		return TEE_ERROR_BAD_PARAMETERS;
14910c96a71dSJerome Forissier 
14923be2f85aSJens Wiklander 	res = tee_rpmb_init();
14930c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
14940c96a71dSJerome Forissier 		return res;
14950c96a71dSJerome Forissier 
14960c96a71dSJerome Forissier 	/*
14970c96a71dSJerome Forissier 	 * We need to split data when block count
14980c96a71dSJerome Forissier 	 * is bigger than reliable block write count.
14990c96a71dSJerome Forissier 	 */
15008dfdf392SJens Wiklander 	req_size = RPMB_DATA_FRAME_SIZE * MIN(blkcnt, rpmb_ctx->rel_wr_blkcnt);
15018dfdf392SJens Wiklander 	res = tee_rpmb_alloc(req_size, RPMB_DATA_FRAME_SIZE, &mem);
15020c96a71dSJerome Forissier 	if (res != TEE_SUCCESS)
15030c96a71dSJerome Forissier 		return res;
15040c96a71dSJerome Forissier 
15050c96a71dSJerome Forissier 	nbr_writes = blkcnt / rpmb_ctx->rel_wr_blkcnt;
15060c96a71dSJerome Forissier 	if (blkcnt % rpmb_ctx->rel_wr_blkcnt > 0)
15070c96a71dSJerome Forissier 		nbr_writes += 1;
15080c96a71dSJerome Forissier 
15090c96a71dSJerome Forissier 	tmp_blkcnt = rpmb_ctx->rel_wr_blkcnt;
15100c96a71dSJerome Forissier 	tmp_blk_idx = blk_idx;
15110c96a71dSJerome Forissier 	for (i = 0; i < nbr_writes; i++) {
1512a8fb1651SJens Wiklander 		size_t offs = i * rpmb_ctx->rel_wr_blkcnt * RPMB_DATA_SIZE;
1513a8fb1651SJens Wiklander 
15140c96a71dSJerome Forissier 		/*
15150c96a71dSJerome Forissier 		 * To handle the last write of block count which is
15160c96a71dSJerome Forissier 		 * equal or smaller than reliable write block count.
15170c96a71dSJerome Forissier 		 */
15180c96a71dSJerome Forissier 		if (i == nbr_writes - 1)
15190c96a71dSJerome Forissier 			tmp_blkcnt = blkcnt - rpmb_ctx->rel_wr_blkcnt *
15200c96a71dSJerome Forissier 			    (nbr_writes - 1);
15210c96a71dSJerome Forissier 
15223be2f85aSJens Wiklander 		res = write_req(tmp_blk_idx, data_blks + offs,
15238dfdf392SJens Wiklander 				tmp_blkcnt, fek, uuid, &mem);
1524a8fb1651SJens Wiklander 		if (res)
1525e94194d4SJens Wiklander 			return res;
15260c96a71dSJerome Forissier 
15270c96a71dSJerome Forissier 
15280c96a71dSJerome Forissier 		tmp_blk_idx += tmp_blkcnt;
15290c96a71dSJerome Forissier 	}
15300c96a71dSJerome Forissier 
1531e94194d4SJens Wiklander 	return TEE_SUCCESS;
15320c96a71dSJerome Forissier }
15330c96a71dSJerome Forissier 
tee_rpmb_write_is_atomic(uint32_t addr,uint32_t len)15343be2f85aSJens Wiklander static bool tee_rpmb_write_is_atomic(uint32_t addr, uint32_t len)
15350c96a71dSJerome Forissier {
15360c96a71dSJerome Forissier 	uint8_t byte_offset = addr % RPMB_DATA_SIZE;
153704e46975SEtienne Carriere 	uint16_t blkcnt = ROUNDUP_DIV(len + byte_offset, RPMB_DATA_SIZE);
15380c96a71dSJerome Forissier 
15390c96a71dSJerome Forissier 	return (blkcnt <= rpmb_ctx->rel_wr_blkcnt);
15400c96a71dSJerome Forissier }
15410c96a71dSJerome Forissier 
15420c96a71dSJerome Forissier /*
15430c96a71dSJerome Forissier  * Write RPMB data in bytes.
15440c96a71dSJerome Forissier  *
15450c96a71dSJerome Forissier  * @addr       Byte address of data.
15460c96a71dSJerome Forissier  * @data       Pointer to the data.
15470c96a71dSJerome Forissier  * @len        Size of data in bytes.
1548fde4a756SJerome Forissier  * @fek        Encrypted File Encryption Key or NULL.
15490c96a71dSJerome Forissier  */
tee_rpmb_write(uint32_t addr,const uint8_t * data,uint32_t len,const uint8_t * fek,const TEE_UUID * uuid)15503be2f85aSJens Wiklander static TEE_Result tee_rpmb_write(uint32_t addr,
15510c96a71dSJerome Forissier 				 const uint8_t *data, uint32_t len,
1552b399f70bSJens Wiklander 				 const uint8_t *fek, const TEE_UUID *uuid)
15530c96a71dSJerome Forissier {
15540c96a71dSJerome Forissier 	TEE_Result res = TEE_ERROR_GENERIC;
15550c96a71dSJerome Forissier 	uint8_t *data_tmp = NULL;
15560c96a71dSJerome Forissier 	uint16_t blk_idx;
15570c96a71dSJerome Forissier 	uint16_t blkcnt;
15580c96a71dSJerome Forissier 	uint8_t byte_offset;
15590c96a71dSJerome Forissier 
15600c96a71dSJerome Forissier 	blk_idx = addr / RPMB_DATA_SIZE;
15610c96a71dSJerome Forissier 	byte_offset = addr % RPMB_DATA_SIZE;
15620c96a71dSJerome Forissier 
156304e46975SEtienne Carriere 	blkcnt = ROUNDUP_DIV(len + byte_offset, RPMB_DATA_SIZE);
15640c96a71dSJerome Forissier 
15650c96a71dSJerome Forissier 	if (byte_offset == 0 && (len % RPMB_DATA_SIZE) == 0) {
15663be2f85aSJens Wiklander 		res = tee_rpmb_write_blk(blk_idx, data, blkcnt, fek, uuid);
15670c96a71dSJerome Forissier 		if (res != TEE_SUCCESS)
15680c96a71dSJerome Forissier 			goto func_exit;
15690c96a71dSJerome Forissier 	} else {
15700c96a71dSJerome Forissier 		data_tmp = calloc(blkcnt, RPMB_DATA_SIZE);
15710c96a71dSJerome Forissier 		if (!data_tmp) {
15720c96a71dSJerome Forissier 			res = TEE_ERROR_OUT_OF_MEMORY;
15730c96a71dSJerome Forissier 			goto func_exit;
15740c96a71dSJerome Forissier 		}
15750c96a71dSJerome Forissier 
15760c96a71dSJerome Forissier 		/* Read the complete blocks */
15773be2f85aSJens Wiklander 		res = tee_rpmb_read(blk_idx * RPMB_DATA_SIZE, data_tmp,
1578b399f70bSJens Wiklander 				    blkcnt * RPMB_DATA_SIZE, fek, uuid);
15790c96a71dSJerome Forissier 		if (res != TEE_SUCCESS)
15800c96a71dSJerome Forissier 			goto func_exit;
15810c96a71dSJerome Forissier 
15820c96a71dSJerome Forissier 		/* Partial update of the data blocks */
15830c96a71dSJerome Forissier 		memcpy(data_tmp + byte_offset, data, len);
15840c96a71dSJerome Forissier 
15853be2f85aSJens Wiklander 		res = tee_rpmb_write_blk(blk_idx, data_tmp, blkcnt, fek, uuid);
15860c96a71dSJerome Forissier 		if (res != TEE_SUCCESS)
15870c96a71dSJerome Forissier 			goto func_exit;
15880c96a71dSJerome Forissier 	}
15890c96a71dSJerome Forissier 
15900c96a71dSJerome Forissier 	res = TEE_SUCCESS;
15910c96a71dSJerome Forissier 
15920c96a71dSJerome Forissier func_exit:
15930c96a71dSJerome Forissier 	free(data_tmp);
15940c96a71dSJerome Forissier 	return res;
15950c96a71dSJerome Forissier }
15960c96a71dSJerome Forissier 
15970c96a71dSJerome Forissier /*
15980c96a71dSJerome Forissier  * Read the RPMB max block.
15990c96a71dSJerome Forissier  *
16000c96a71dSJerome Forissier  * @counter    Pointer to receive the max block.
16010c96a71dSJerome Forissier  */
tee_rpmb_get_max_block(uint32_t * max_block)16023be2f85aSJens Wiklander static TEE_Result tee_rpmb_get_max_block(uint32_t *max_block)
16030c96a71dSJerome Forissier {
16040c96a71dSJerome Forissier 	TEE_Result res = TEE_SUCCESS;
16050c96a71dSJerome Forissier 
16060c96a71dSJerome Forissier 	if (!max_block)
16070c96a71dSJerome Forissier 		return TEE_ERROR_BAD_PARAMETERS;
16080c96a71dSJerome Forissier 
1609a8fb1651SJens Wiklander 	if (rpmb_dead)
1610a8fb1651SJens Wiklander 		return TEE_ERROR_COMMUNICATION;
1611a8fb1651SJens Wiklander 
16120c96a71dSJerome Forissier 	if (!rpmb_ctx || !rpmb_ctx->dev_info_synced) {
16133be2f85aSJens Wiklander 		res = tee_rpmb_init();
16140c96a71dSJerome Forissier 		if (res != TEE_SUCCESS)
16150c96a71dSJerome Forissier 			goto func_exit;
16160c96a71dSJerome Forissier 	}
16170c96a71dSJerome Forissier 
16180c96a71dSJerome Forissier 	*max_block = rpmb_ctx->max_blk_idx;
16190c96a71dSJerome Forissier 
16200c96a71dSJerome Forissier func_exit:
16210c96a71dSJerome Forissier 	return res;
16220c96a71dSJerome Forissier }
16230c96a71dSJerome Forissier 
16240c96a71dSJerome Forissier /*
16250c96a71dSJerome Forissier  * End of lower interface to RPMB device
16260c96a71dSJerome Forissier  */
16270c96a71dSJerome Forissier 
16280c96a71dSJerome Forissier static TEE_Result get_fat_start_address(uint32_t *addr);
16295f68d784SManuel Huber static TEE_Result rpmb_fs_setup(void);
16300c96a71dSJerome Forissier 
16315f68d784SManuel Huber /**
16325f68d784SManuel Huber  * fat_entry_dir_free: Free the FAT entry dir.
16335f68d784SManuel Huber  */
fat_entry_dir_free(void)16345f68d784SManuel Huber static void fat_entry_dir_free(void)
16355f68d784SManuel Huber {
16365f68d784SManuel Huber 	if (fat_entry_dir) {
16375f68d784SManuel Huber 		free(fat_entry_dir->rpmb_fat_entry_buf);
16385f68d784SManuel Huber 		free(fat_entry_dir);
16395f68d784SManuel Huber 		fat_entry_dir = NULL;
16405f68d784SManuel Huber 	}
16415f68d784SManuel Huber }
16425f68d784SManuel Huber 
16435f68d784SManuel Huber /**
16445f68d784SManuel Huber  * fat_entry_dir_init: Initialize the FAT FS entry buffer/cache
16455f68d784SManuel Huber  * This function must be called before reading FAT FS entries using the
16465f68d784SManuel Huber  * function fat_entry_dir_get_next. This initializes the buffer/cache with the
16475f68d784SManuel Huber  * first FAT FS entries.
16485f68d784SManuel Huber  */
fat_entry_dir_init(void)16495f68d784SManuel Huber static TEE_Result fat_entry_dir_init(void)
1650a8a78b85SJerome Forissier {
1651a8a78b85SJerome Forissier 	TEE_Result res = TEE_ERROR_GENERIC;
16525f68d784SManuel Huber 	struct rpmb_fat_entry *fe = NULL;
16535f68d784SManuel Huber 	uint32_t fat_address = 0;
16545f68d784SManuel Huber 	uint32_t num_elems_read = 0;
16555f68d784SManuel Huber 
16565f68d784SManuel Huber 	if (fat_entry_dir)
16575f68d784SManuel Huber 		return TEE_SUCCESS;
16585f68d784SManuel Huber 
16595f68d784SManuel Huber 	res = rpmb_fs_setup();
16605f68d784SManuel Huber 	if (res)
16615f68d784SManuel Huber 		return res;
1662a8a78b85SJerome Forissier 
1663a8a78b85SJerome Forissier 	res = get_fat_start_address(&fat_address);
16645f68d784SManuel Huber 	if (res)
16655f68d784SManuel Huber 		return res;
1666a8a78b85SJerome Forissier 
16675f68d784SManuel Huber 	fat_entry_dir = calloc(1, sizeof(struct rpmb_fat_entry_dir));
16685f68d784SManuel Huber 	if (!fat_entry_dir)
16695f68d784SManuel Huber 		return TEE_ERROR_OUT_OF_MEMORY;
16705f68d784SManuel Huber 
16715f68d784SManuel Huber 	/*
16725f68d784SManuel Huber 	 * If caching is enabled, read in up to the maximum cache size, but
16735f68d784SManuel Huber 	 * never more than the single read in size. Otherwise, read in as many
16745f68d784SManuel Huber 	 * entries fit into the temporary buffer.
16755f68d784SManuel Huber 	 */
16765f68d784SManuel Huber 	if (CFG_RPMB_FS_CACHE_ENTRIES)
16775f68d784SManuel Huber 		num_elems_read = MIN(CFG_RPMB_FS_CACHE_ENTRIES,
16785f68d784SManuel Huber 				     CFG_RPMB_FS_RD_ENTRIES);
16795f68d784SManuel Huber 	else
16805f68d784SManuel Huber 		num_elems_read = CFG_RPMB_FS_RD_ENTRIES;
16815f68d784SManuel Huber 
16825f68d784SManuel Huber 	/*
16835f68d784SManuel Huber 	 * Allocate memory for the FAT FS entries to read in.
16845f68d784SManuel Huber 	 */
16855f68d784SManuel Huber 	fe = calloc(num_elems_read, sizeof(struct rpmb_fat_entry));
16865f68d784SManuel Huber 	if (!fe) {
1687a8a78b85SJerome Forissier 		res = TEE_ERROR_OUT_OF_MEMORY;
1688a8a78b85SJerome Forissier 		goto out;
1689a8a78b85SJerome Forissier 	}
1690a8a78b85SJerome Forissier 
16913be2f85aSJens Wiklander 	res = tee_rpmb_read(fat_address, (uint8_t *)fe,
16925f68d784SManuel Huber 			    num_elems_read * sizeof(*fe), NULL, NULL);
16935f68d784SManuel Huber 	if (res)
1694a8a78b85SJerome Forissier 		goto out;
1695a8a78b85SJerome Forissier 
16965f68d784SManuel Huber 	fat_entry_dir->rpmb_fat_entry_buf = fe;
1697a8a78b85SJerome Forissier 
16985f68d784SManuel Huber 	/*
16995f68d784SManuel Huber 	 * We use this variable when getting next entries from the buffer/cache
17005f68d784SManuel Huber 	 * to see whether we have to read in more entries from storage.
17015f68d784SManuel Huber 	 */
17025f68d784SManuel Huber 	fat_entry_dir->num_buffered = num_elems_read;
1703a8a78b85SJerome Forissier 
17045f68d784SManuel Huber 	return TEE_SUCCESS;
1705a8a78b85SJerome Forissier out:
17065f68d784SManuel Huber 	fat_entry_dir_free();
17075f68d784SManuel Huber 	free(fe);
17085f68d784SManuel Huber 	return res;
17095f68d784SManuel Huber }
17105f68d784SManuel Huber 
17115f68d784SManuel Huber /**
17125f68d784SManuel Huber  * fat_entry_dir_deinit: If caching is enabled, free the temporary buffer for
17135f68d784SManuel Huber  * FAT FS entries in case the cache was too small. Keep the elements in the
17145f68d784SManuel Huber  * cache. Reset the counter variables to start the next traversal from fresh
17155f68d784SManuel Huber  * from the first cached entry. If caching is disabled, just free the
17165f68d784SManuel Huber  * temporary buffer by calling fat_entry_dir_free and return.
17175f68d784SManuel Huber  */
fat_entry_dir_deinit(void)17185f68d784SManuel Huber static void fat_entry_dir_deinit(void)
17195f68d784SManuel Huber {
17205f68d784SManuel Huber 	struct rpmb_fat_entry *fe = NULL;
17215f68d784SManuel Huber 
17225f68d784SManuel Huber 	if (!fat_entry_dir)
17235f68d784SManuel Huber 		return;
17245f68d784SManuel Huber 
17255f68d784SManuel Huber 	if (!CFG_RPMB_FS_CACHE_ENTRIES) {
17265f68d784SManuel Huber 		fat_entry_dir_free();
17275f68d784SManuel Huber 		return;
17285f68d784SManuel Huber 	}
17295f68d784SManuel Huber 
17305f68d784SManuel Huber 	fe = fat_entry_dir->rpmb_fat_entry_buf;
17315f68d784SManuel Huber 	fat_entry_dir->idx_curr = 0;
17325f68d784SManuel Huber 	fat_entry_dir->num_total_read = 0;
17335f68d784SManuel Huber 	fat_entry_dir->last_reached = false;
17345f68d784SManuel Huber 
17355f68d784SManuel Huber 	if (fat_entry_dir->num_buffered > CFG_RPMB_FS_CACHE_ENTRIES) {
17365f68d784SManuel Huber 		fat_entry_dir->num_buffered = CFG_RPMB_FS_CACHE_ENTRIES;
17375f68d784SManuel Huber 
17385f68d784SManuel Huber 		fe = realloc(fe, fat_entry_dir->num_buffered * sizeof(*fe));
17395f68d784SManuel Huber 
17405f68d784SManuel Huber 		/*
17415f68d784SManuel Huber 		 * In case realloc fails, we are on the safe side if we destroy
17425f68d784SManuel Huber 		 * the whole structure. Upon the next init, the cache has to be
17435f68d784SManuel Huber 		 * re-established, but this case should not happen in practice.
17445f68d784SManuel Huber 		 */
17455f68d784SManuel Huber 		if (!fe)
17465f68d784SManuel Huber 			fat_entry_dir_free();
17475f68d784SManuel Huber 		else
17485f68d784SManuel Huber 			fat_entry_dir->rpmb_fat_entry_buf = fe;
17495f68d784SManuel Huber 	}
17505f68d784SManuel Huber }
17515f68d784SManuel Huber 
17525f68d784SManuel Huber /**
17535f68d784SManuel Huber  * fat_entry_dir_update: Updates a persisted FAT FS entry in the cache.
17545f68d784SManuel Huber  * This function updates the FAT entry fat_entry that was written to address
17555f68d784SManuel Huber  * fat_address onto RPMB storage in the cache.
17565f68d784SManuel Huber  */
fat_entry_dir_update(struct rpmb_fat_entry * fat_entry,uint32_t fat_address)17575f68d784SManuel Huber static TEE_Result __maybe_unused fat_entry_dir_update
17585f68d784SManuel Huber 					(struct rpmb_fat_entry *fat_entry,
17595f68d784SManuel Huber 					 uint32_t fat_address)
17605f68d784SManuel Huber {
17615f68d784SManuel Huber 	uint32_t fat_entry_buf_idx = 0;
17625f68d784SManuel Huber 	/* Use a temp var to avoid compiler warning if caching disabled. */
17635f68d784SManuel Huber 	uint32_t max_cache_entries = CFG_RPMB_FS_CACHE_ENTRIES;
17645f68d784SManuel Huber 
1765be041efcSNeil Shipp 	assert(!((fat_address - RPMB_FS_FAT_START_ADDRESS) %
1766be041efcSNeil Shipp 	       sizeof(struct rpmb_fat_entry)));
17675f68d784SManuel Huber 
17685f68d784SManuel Huber 	/* Nothing to update if the cache is not initialized. */
17695f68d784SManuel Huber 	if (!fat_entry_dir)
17705f68d784SManuel Huber 		return TEE_SUCCESS;
17715f68d784SManuel Huber 
17725f68d784SManuel Huber 	fat_entry_buf_idx = (fat_address - RPMB_FS_FAT_START_ADDRESS) /
17735f68d784SManuel Huber 			     sizeof(struct rpmb_fat_entry);
17745f68d784SManuel Huber 
17755f68d784SManuel Huber 	/* Only need to write if index points to an entry in cache. */
17763b354b19SNeil Shipp 	if (fat_entry_buf_idx < fat_entry_dir->num_buffered &&
17773b354b19SNeil Shipp 	    fat_entry_buf_idx < max_cache_entries) {
17785f68d784SManuel Huber 		memcpy(fat_entry_dir->rpmb_fat_entry_buf + fat_entry_buf_idx,
17795f68d784SManuel Huber 		       fat_entry, sizeof(struct rpmb_fat_entry));
17805f68d784SManuel Huber 	}
17815f68d784SManuel Huber 
17825f68d784SManuel Huber 	return TEE_SUCCESS;
17835f68d784SManuel Huber }
17845f68d784SManuel Huber 
17855f68d784SManuel Huber /**
17865f68d784SManuel Huber  * fat_entry_dir_get_next: Get next FAT FS entry.
17875f68d784SManuel Huber  * Read either from cache/buffer, or by reading from RPMB storage if the
17885f68d784SManuel Huber  * elements in the buffer/cache are fully read. When reading in from RPMB
17895f68d784SManuel Huber  * storage, the buffer is overwritten in case caching is disabled.
17905f68d784SManuel Huber  * In case caching is enabled, the cache is either further filled, or a
17915f68d784SManuel Huber  * temporary buffer populated if the cache is already full.
17925f68d784SManuel Huber  * The FAT FS entry is written to fat_entry. The respective address in RPMB
17935f68d784SManuel Huber  * storage is written to fat_address, if not NULL. When the last FAT FS entry
17945f68d784SManuel Huber  * was previously read, the function indicates this case by writing a NULL
17955f68d784SManuel Huber  * pointer to fat_entry.
17965f68d784SManuel Huber  * Returns a value different TEE_SUCCESS if the next FAT FS entry could not be
17975f68d784SManuel Huber  * retrieved.
17985f68d784SManuel Huber  */
fat_entry_dir_get_next(struct rpmb_fat_entry ** fat_entry,uint32_t * fat_address)17995f68d784SManuel Huber static TEE_Result fat_entry_dir_get_next(struct rpmb_fat_entry **fat_entry,
18005f68d784SManuel Huber 					 uint32_t *fat_address)
18015f68d784SManuel Huber {
18025f68d784SManuel Huber 	TEE_Result res = TEE_ERROR_GENERIC;
18035f68d784SManuel Huber 	struct rpmb_fat_entry *fe = NULL;
18045f68d784SManuel Huber 	uint32_t num_elems_read = 0;
18055f68d784SManuel Huber 	uint32_t fat_address_local = 0;
18065f68d784SManuel Huber 
18075f68d784SManuel Huber 	assert(fat_entry_dir && fat_entry);
18085f68d784SManuel Huber 
18095f68d784SManuel Huber 	/* Don't read further if we previously read the last FAT FS entry. */
18105f68d784SManuel Huber 	if (fat_entry_dir->last_reached) {
18115f68d784SManuel Huber 		*fat_entry = NULL;
18125f68d784SManuel Huber 		return TEE_SUCCESS;
18135f68d784SManuel Huber 	}
18145f68d784SManuel Huber 
18155f68d784SManuel Huber 	fe = fat_entry_dir->rpmb_fat_entry_buf;
18165f68d784SManuel Huber 
18175f68d784SManuel Huber 	/* Determine address of FAT FS entry in RPMB storage. */
18185f68d784SManuel Huber 	fat_address_local = RPMB_FS_FAT_START_ADDRESS +
18195f68d784SManuel Huber 			(fat_entry_dir->num_total_read *
18205f68d784SManuel Huber 			sizeof(struct rpmb_fat_entry));
18215f68d784SManuel Huber 
18225f68d784SManuel Huber 	/*
18235f68d784SManuel Huber 	 * We've read all so-far buffered elements, so we need to
18245f68d784SManuel Huber 	 * read in more entries from RPMB storage.
18255f68d784SManuel Huber 	 */
18265f68d784SManuel Huber 	if (fat_entry_dir->idx_curr >= fat_entry_dir->num_buffered) {
18275f68d784SManuel Huber 		/*
18285f68d784SManuel Huber 		 * This is the case where we do not cache entries, so just read
18295f68d784SManuel Huber 		 * in next set of FAT FS entries into the buffer.
18305f68d784SManuel Huber 		 * Goto the end of the when statement if that is done.
18315f68d784SManuel Huber 		 */
18325f68d784SManuel Huber 		if (!CFG_RPMB_FS_CACHE_ENTRIES) {
18335f68d784SManuel Huber 			num_elems_read = CFG_RPMB_FS_RD_ENTRIES;
18345f68d784SManuel Huber 			fat_entry_dir->idx_curr = 0;
18355f68d784SManuel Huber 
18363be2f85aSJens Wiklander 			res = tee_rpmb_read(fat_address_local, (uint8_t *)fe,
18375f68d784SManuel Huber 					    num_elems_read * sizeof(*fe), NULL,
18385f68d784SManuel Huber 					    NULL);
18395f68d784SManuel Huber 			if (res)
18405f68d784SManuel Huber 				return res;
18415f68d784SManuel Huber 			goto post_read_in;
18425f68d784SManuel Huber 		}
18435f68d784SManuel Huber 
18445f68d784SManuel Huber 		/*
18455f68d784SManuel Huber 		 * We cache FAT FS entries, and the buffer is not completely
18465f68d784SManuel Huber 		 * filled. Further keep on extending the buffer up to its max
18475f68d784SManuel Huber 		 * size by reading in from RPMB.
18485f68d784SManuel Huber 		 */
18495f68d784SManuel Huber 		if (fat_entry_dir->num_total_read < RPMB_BUF_MAX_ENTRIES) {
18505f68d784SManuel Huber 			/*
18515f68d784SManuel Huber 			 * Read at most as many elements as fit in the buffer
18525f68d784SManuel Huber 			 * and no more than the defined number of entries to
18535f68d784SManuel Huber 			 * read in at once.
18545f68d784SManuel Huber 			 */
18555f68d784SManuel Huber 			num_elems_read = MIN(RPMB_BUF_MAX_ENTRIES -
18565f68d784SManuel Huber 					     fat_entry_dir->num_total_read,
18575f68d784SManuel Huber 					     (uint32_t)CFG_RPMB_FS_RD_ENTRIES);
18585f68d784SManuel Huber 
18595f68d784SManuel Huber 			/*
18605f68d784SManuel Huber 			 * Expand the buffer to fit in the additional entries.
18615f68d784SManuel Huber 			 */
18625f68d784SManuel Huber 			fe = realloc(fe,
18635f68d784SManuel Huber 				     (fat_entry_dir->num_buffered +
18645f68d784SManuel Huber 				      num_elems_read) * sizeof(*fe));
18655f68d784SManuel Huber 			if (!fe)
18665f68d784SManuel Huber 				return TEE_ERROR_OUT_OF_MEMORY;
18675f68d784SManuel Huber 
18685f68d784SManuel Huber 			fat_entry_dir->rpmb_fat_entry_buf = fe;
18695f68d784SManuel Huber 
18705f68d784SManuel Huber 			/* Read in to the next free slot in the buffer/cache. */
18713be2f85aSJens Wiklander 			res = tee_rpmb_read(fat_address_local,
18725f68d784SManuel Huber 					    (uint8_t *)(fe +
18735f68d784SManuel Huber 					    fat_entry_dir->num_total_read),
18745f68d784SManuel Huber 					    num_elems_read * sizeof(*fe),
18755f68d784SManuel Huber 					    NULL, NULL);
18765f68d784SManuel Huber 			if (res)
18775f68d784SManuel Huber 				return res;
18785f68d784SManuel Huber 
18795f68d784SManuel Huber 			fat_entry_dir->num_buffered += num_elems_read;
18805f68d784SManuel Huber 		} else {
18815f68d784SManuel Huber 			/*
18825f68d784SManuel Huber 			 * This happens when we have read as many elements as
18835f68d784SManuel Huber 			 * can possibly fit into the buffer.
18845f68d784SManuel Huber 			 * As the first part of the buffer serves as our cache,
18855f68d784SManuel Huber 			 * we only overwrite the last part that serves as our
18865f68d784SManuel Huber 			 * temporary buffer used to iteratively read in entries
18875f68d784SManuel Huber 			 * when the cache is full. Read in the temporary buffer
18885f68d784SManuel Huber 			 * maximum size.
18895f68d784SManuel Huber 			 */
18905f68d784SManuel Huber 			num_elems_read = CFG_RPMB_FS_RD_ENTRIES;
18915f68d784SManuel Huber 			/* Reset index to beginning of the temporary buffer. */
18925f68d784SManuel Huber 			fat_entry_dir->idx_curr = CFG_RPMB_FS_CACHE_ENTRIES;
18935f68d784SManuel Huber 
18945f68d784SManuel Huber 			/* Read in elements after the end of the cache. */
18953be2f85aSJens Wiklander 			res = tee_rpmb_read(fat_address_local,
18965f68d784SManuel Huber 					    (uint8_t *)(fe +
18975f68d784SManuel Huber 					    fat_entry_dir->idx_curr),
18985f68d784SManuel Huber 					    num_elems_read * sizeof(*fe),
18995f68d784SManuel Huber 					    NULL, NULL);
19005f68d784SManuel Huber 			if (res)
19015f68d784SManuel Huber 				return res;
19025f68d784SManuel Huber 		}
19035f68d784SManuel Huber 	}
19045f68d784SManuel Huber 
19055f68d784SManuel Huber post_read_in:
19065f68d784SManuel Huber 	if (fat_address)
19075f68d784SManuel Huber 		*fat_address = fat_address_local;
19085f68d784SManuel Huber 
19095f68d784SManuel Huber 	*fat_entry = fe + fat_entry_dir->idx_curr;
19105f68d784SManuel Huber 
19115f68d784SManuel Huber 	fat_entry_dir->idx_curr++;
19125f68d784SManuel Huber 	fat_entry_dir->num_total_read++;
19135f68d784SManuel Huber 
19145f68d784SManuel Huber 	/*
19155f68d784SManuel Huber 	 * Indicate last entry was read.
19165f68d784SManuel Huber 	 * Ensures we return a zero value for fat_entry on next invocation.
19175f68d784SManuel Huber 	 */
19185f68d784SManuel Huber 	if ((*fat_entry)->flags & FILE_IS_LAST_ENTRY)
19195f68d784SManuel Huber 		fat_entry_dir->last_reached = true;
19205f68d784SManuel Huber 
19215f68d784SManuel Huber 	return TEE_SUCCESS;
19225f68d784SManuel Huber }
19235f68d784SManuel Huber 
19245f68d784SManuel Huber #if (TRACE_LEVEL >= TRACE_FLOW)
dump_fat(void)19255f68d784SManuel Huber static void dump_fat(void)
19265f68d784SManuel Huber {
1927e762809bSGianguido Sorà 	TEE_Result res = TEE_ERROR_SECURITY;
19285f68d784SManuel Huber 	struct rpmb_fat_entry *fe = NULL;
19295f68d784SManuel Huber 
1930ce9a20c1SJerome Forissier 	if (!fs_par)
1931ce9a20c1SJerome Forissier 		return;
1932ce9a20c1SJerome Forissier 
19335f68d784SManuel Huber 	if (fat_entry_dir_init())
19345f68d784SManuel Huber 		return;
19355f68d784SManuel Huber 
19365f68d784SManuel Huber 	while (true) {
19375f68d784SManuel Huber 		res = fat_entry_dir_get_next(&fe, NULL);
19385f68d784SManuel Huber 		if (res || !fe)
19395f68d784SManuel Huber 			break;
19405f68d784SManuel Huber 
19415f68d784SManuel Huber 		FMSG("flags %#"PRIx32", size %"PRIu32", address %#"PRIx32
19425f68d784SManuel Huber 		     ", filename '%s'",
19435f68d784SManuel Huber 		     fe->flags, fe->data_size, fe->start_address, fe->filename);
19445f68d784SManuel Huber 	}
19455f68d784SManuel Huber 
19465f68d784SManuel Huber 	fat_entry_dir_deinit();
1947a8a78b85SJerome Forissier }
1948213777fdSManuel Huber #else
dump_fat(void)1949213777fdSManuel Huber static void dump_fat(void)
1950213777fdSManuel Huber {
1951213777fdSManuel Huber }
1952213777fdSManuel Huber #endif
1953a8a78b85SJerome Forissier 
1954a8a78b85SJerome Forissier #if (TRACE_LEVEL >= TRACE_DEBUG)
dump_fh(struct rpmb_file_handle * fh)1955a8a78b85SJerome Forissier static void dump_fh(struct rpmb_file_handle *fh)
1956a8a78b85SJerome Forissier {
1957a8a78b85SJerome Forissier 	DMSG("fh->filename=%s", fh->filename);
1958a8a78b85SJerome Forissier 	DMSG("fh->rpmb_fat_address=%u", fh->rpmb_fat_address);
1959a8a78b85SJerome Forissier 	DMSG("fh->fat_entry.start_address=%u", fh->fat_entry.start_address);
1960a8a78b85SJerome Forissier 	DMSG("fh->fat_entry.data_size=%u", fh->fat_entry.data_size);
1961a8a78b85SJerome Forissier }
1962a8a78b85SJerome Forissier #else
dump_fh(struct rpmb_file_handle * fh __unused)1963a8a78b85SJerome Forissier static void dump_fh(struct rpmb_file_handle *fh __unused)
1964a8a78b85SJerome Forissier {
1965a8a78b85SJerome Forissier }
1966a8a78b85SJerome Forissier #endif
1967a8a78b85SJerome Forissier 
196834ab2802SJerome Forissier /* "/TA_uuid/object_id" or "/TA_uuid/.object_id" */
create_filename(void * buf,size_t blen,struct tee_pobj * po,bool transient)196934ab2802SJerome Forissier static TEE_Result create_filename(void *buf, size_t blen, struct tee_pobj *po,
197034ab2802SJerome Forissier 				  bool transient)
197134ab2802SJerome Forissier {
197234ab2802SJerome Forissier 	uint8_t *file = buf;
197334ab2802SJerome Forissier 	uint32_t pos = 0;
197434ab2802SJerome Forissier 	uint32_t hslen = 1 /* Leading slash */
197534ab2802SJerome Forissier 			+ TEE_B2HS_HSBUF_SIZE(sizeof(TEE_UUID) + po->obj_id_len)
197634ab2802SJerome Forissier 			+ 1; /* Intermediate slash */
197734ab2802SJerome Forissier 
197834ab2802SJerome Forissier 	/* +1 for the '.' (temporary persistent object) */
197934ab2802SJerome Forissier 	if (transient)
198034ab2802SJerome Forissier 		hslen++;
198134ab2802SJerome Forissier 
198234ab2802SJerome Forissier 	if (blen < hslen)
198334ab2802SJerome Forissier 		return TEE_ERROR_SHORT_BUFFER;
198434ab2802SJerome Forissier 
198534ab2802SJerome Forissier 	file[pos++] = '/';
198634ab2802SJerome Forissier 	pos += tee_b2hs((uint8_t *)&po->uuid, &file[pos],
198734ab2802SJerome Forissier 			sizeof(TEE_UUID), hslen);
198834ab2802SJerome Forissier 	file[pos++] = '/';
198934ab2802SJerome Forissier 
199034ab2802SJerome Forissier 	if (transient)
199134ab2802SJerome Forissier 		file[pos++] = '.';
199234ab2802SJerome Forissier 
199334ab2802SJerome Forissier 	tee_b2hs(po->obj_id, file + pos, po->obj_id_len, hslen - pos);
199434ab2802SJerome Forissier 
199534ab2802SJerome Forissier 	return TEE_SUCCESS;
199634ab2802SJerome Forissier }
199734ab2802SJerome Forissier 
199834ab2802SJerome Forissier /* "/TA_uuid" */
create_dirname(void * buf,size_t blen,const TEE_UUID * uuid)199934ab2802SJerome Forissier static TEE_Result create_dirname(void *buf, size_t blen, const TEE_UUID *uuid)
200034ab2802SJerome Forissier {
200134ab2802SJerome Forissier 	uint8_t *dir = buf;
200234ab2802SJerome Forissier 	uint32_t hslen = TEE_B2HS_HSBUF_SIZE(sizeof(TEE_UUID)) + 1;
200334ab2802SJerome Forissier 
200434ab2802SJerome Forissier 	if (blen < hslen)
200534ab2802SJerome Forissier 		return TEE_ERROR_SHORT_BUFFER;
200634ab2802SJerome Forissier 
200734ab2802SJerome Forissier 	dir[0] = '/';
200834ab2802SJerome Forissier 	tee_b2hs((uint8_t *)uuid, dir + 1, sizeof(TEE_UUID), hslen);
200934ab2802SJerome Forissier 
201034ab2802SJerome Forissier 	return TEE_SUCCESS;
201134ab2802SJerome Forissier }
201234ab2802SJerome Forissier 
alloc_file_handle(struct tee_pobj * po,bool temporary)2013b2215adfSJens Wiklander static struct rpmb_file_handle *alloc_file_handle(struct tee_pobj *po,
2014b2215adfSJens Wiklander 						  bool temporary)
2015a8a78b85SJerome Forissier {
2016a8a78b85SJerome Forissier 	struct rpmb_file_handle *fh = NULL;
2017a8a78b85SJerome Forissier 
2018a8a78b85SJerome Forissier 	fh = calloc(1, sizeof(struct rpmb_file_handle));
2019a8a78b85SJerome Forissier 	if (!fh)
2020b0104773SPascal Brand 		return NULL;
2021b0104773SPascal Brand 
2022b2215adfSJens Wiklander 	if (po)
202334ab2802SJerome Forissier 		create_filename(fh->filename, sizeof(fh->filename), po,
2024b2215adfSJens Wiklander 				temporary);
2025b0104773SPascal Brand 
2026b0104773SPascal Brand 	return fh;
2027b0104773SPascal Brand }
2028b0104773SPascal Brand 
2029b0104773SPascal Brand /**
2030b0104773SPascal Brand  * write_fat_entry: Store info in a fat_entry to RPMB.
2031b0104773SPascal Brand  */
write_fat_entry(struct rpmb_file_handle * fh)2032e92de4caSJerome Forissier static TEE_Result write_fat_entry(struct rpmb_file_handle *fh)
2033b0104773SPascal Brand {
2034b0104773SPascal Brand 	TEE_Result res = TEE_ERROR_GENERIC;
2035b0104773SPascal Brand 
2036b0104773SPascal Brand 	/* Protect partition data. */
2037b0104773SPascal Brand 	if (fh->rpmb_fat_address < sizeof(struct rpmb_fs_partition)) {
2038b0104773SPascal Brand 		res = TEE_ERROR_ACCESS_CONFLICT;
2039b0104773SPascal Brand 		goto out;
2040b0104773SPascal Brand 	}
2041b0104773SPascal Brand 
2042b0104773SPascal Brand 	if (fh->rpmb_fat_address % sizeof(struct rpmb_fat_entry) != 0) {
2043b0104773SPascal Brand 		res = TEE_ERROR_BAD_PARAMETERS;
2044b0104773SPascal Brand 		goto out;
2045b0104773SPascal Brand 	}
2046b0104773SPascal Brand 
20473be2f85aSJens Wiklander 	res = tee_rpmb_write(fh->rpmb_fat_address, (uint8_t *)&fh->fat_entry,
2048b399f70bSJens Wiklander 			     sizeof(struct rpmb_fat_entry), NULL, NULL);
2049b0104773SPascal Brand 
2050a8a78b85SJerome Forissier 	dump_fat();
2051a8a78b85SJerome Forissier 
20525f68d784SManuel Huber 	/* If caching enabled, update a successfully written entry in cache. */
20535f68d784SManuel Huber 	if (CFG_RPMB_FS_CACHE_ENTRIES && !res)
20545f68d784SManuel Huber 		res = fat_entry_dir_update(&fh->fat_entry,
20555f68d784SManuel Huber 					   fh->rpmb_fat_address);
20565f68d784SManuel Huber 
2057b0104773SPascal Brand out:
2058b0104773SPascal Brand 	return res;
2059b0104773SPascal Brand }
2060b0104773SPascal Brand 
2061b0104773SPascal Brand /**
20625f68d784SManuel Huber  * rpmb_fs_setup: Setup RPMB FS.
2063b0104773SPascal Brand  * Set initial partition and FS values and write to RPMB.
2064b0104773SPascal Brand  * Store frequently used data in RAM.
2065b0104773SPascal Brand  */
rpmb_fs_setup(void)2066b0104773SPascal Brand static TEE_Result rpmb_fs_setup(void)
2067b0104773SPascal Brand {
2068b0104773SPascal Brand 	TEE_Result res = TEE_ERROR_GENERIC;
2069b0104773SPascal Brand 	struct rpmb_fs_partition *partition_data = NULL;
2070a8a78b85SJerome Forissier 	struct rpmb_file_handle *fh = NULL;
2071a8a78b85SJerome Forissier 	uint32_t max_rpmb_block = 0;
2072a8a78b85SJerome Forissier 
2073a8a78b85SJerome Forissier 	if (fs_par) {
2074a8a78b85SJerome Forissier 		res = TEE_SUCCESS;
2075a8a78b85SJerome Forissier 		goto out;
2076a8a78b85SJerome Forissier 	}
2077a8a78b85SJerome Forissier 
20783be2f85aSJens Wiklander 	res = tee_rpmb_get_max_block(&max_rpmb_block);
2079a8a78b85SJerome Forissier 	if (res != TEE_SUCCESS)
2080a8a78b85SJerome Forissier 		goto out;
2081b0104773SPascal Brand 
2082a8fb1651SJens Wiklander 	/*
2083a8fb1651SJens Wiklander 	 * We're going to read a full block in order to have a full block
2084a8fb1651SJens Wiklander 	 * for the dummy write below.
2085a8fb1651SJens Wiklander 	 */
2086a8fb1651SJens Wiklander 	COMPILE_TIME_ASSERT(sizeof(struct rpmb_fs_partition) <=
2087a8fb1651SJens Wiklander 			    RPMB_DATA_SIZE);
2088a8fb1651SJens Wiklander 	partition_data = calloc(1, RPMB_DATA_SIZE);
2089a8a78b85SJerome Forissier 	if (!partition_data) {
2090b0104773SPascal Brand 		res = TEE_ERROR_OUT_OF_MEMORY;
2091b0104773SPascal Brand 		goto out;
2092b0104773SPascal Brand 	}
2093b0104773SPascal Brand 
20943be2f85aSJens Wiklander 	res = tee_rpmb_read(RPMB_STORAGE_START_ADDRESS,
2095a8fb1651SJens Wiklander 			    (uint8_t *)partition_data, RPMB_DATA_SIZE,
2096a8fb1651SJens Wiklander 			    NULL, NULL);
2097a8fb1651SJens Wiklander 	if (res != TEE_SUCCESS)
2098a8fb1651SJens Wiklander 		goto out;
2099a8fb1651SJens Wiklander 	/*
2100a8fb1651SJens Wiklander 	 * Perform a write in order to increase the write counter. This
2101a8fb1651SJens Wiklander 	 * prevents late usage (replay attack) of a previously blocked
2102a8fb1651SJens Wiklander 	 * request with a valid write counter value.
2103a8fb1651SJens Wiklander 	 */
21043be2f85aSJens Wiklander 	res = tee_rpmb_write(RPMB_STORAGE_START_ADDRESS,
2105a8fb1651SJens Wiklander 			     (uint8_t *)partition_data, RPMB_DATA_SIZE,
2106a8fb1651SJens Wiklander 			     NULL, NULL);
2107a8fb1651SJens Wiklander 	if (res != TEE_SUCCESS)
2108a8fb1651SJens Wiklander 		goto out;
2109a8fb1651SJens Wiklander 	/*
2110a8fb1651SJens Wiklander 	 * We're reading again in case a stale request was committed
2111a8fb1651SJens Wiklander 	 * instead of the one issued above. If this succeeds we're in sync
2112a8fb1651SJens Wiklander 	 * with the RPMB block since there are no other possible stale
2113a8fb1651SJens Wiklander 	 * blocks with valid write counters available.
2114a8fb1651SJens Wiklander 	 */
21153be2f85aSJens Wiklander 	res = tee_rpmb_read(RPMB_STORAGE_START_ADDRESS,
2116b0104773SPascal Brand 			    (uint8_t *)partition_data,
2117b399f70bSJens Wiklander 			    sizeof(struct rpmb_fs_partition), NULL, NULL);
2118b0104773SPascal Brand 	if (res != TEE_SUCCESS)
2119b0104773SPascal Brand 		goto out;
2120b0104773SPascal Brand 
2121a8a78b85SJerome Forissier #ifndef CFG_RPMB_RESET_FAT
2122b0104773SPascal Brand 	if (partition_data->rpmb_fs_magic == RPMB_FS_MAGIC) {
2123b0104773SPascal Brand 		if (partition_data->fs_version == FS_VERSION) {
2124b0104773SPascal Brand 			res = TEE_SUCCESS;
2125b0104773SPascal Brand 			goto store_fs_par;
2126b0104773SPascal Brand 		} else {
2127c3d1e005SVictor Chong 			EMSG("Wrong software is in use.");
2128b0104773SPascal Brand 			res = TEE_ERROR_ACCESS_DENIED;
2129b0104773SPascal Brand 			goto out;
2130b0104773SPascal Brand 		}
2131b0104773SPascal Brand 	}
2132a8a78b85SJerome Forissier #else
2133a8a78b85SJerome Forissier 	EMSG("**** Clearing Storage ****");
2134a8a78b85SJerome Forissier #endif
2135b0104773SPascal Brand 
2136b0104773SPascal Brand 	/* Setup new partition data. */
2137b0104773SPascal Brand 	partition_data->rpmb_fs_magic = RPMB_FS_MAGIC;
2138b0104773SPascal Brand 	partition_data->fs_version = FS_VERSION;
2139b0104773SPascal Brand 	partition_data->fat_start_address = RPMB_FS_FAT_START_ADDRESS;
2140b0104773SPascal Brand 
2141b0104773SPascal Brand 	/* Initial FAT entry with FILE_IS_LAST_ENTRY flag set. */
2142b2215adfSJens Wiklander 	fh = alloc_file_handle(NULL, false);
2143a8a78b85SJerome Forissier 	if (!fh) {
2144b0104773SPascal Brand 		res = TEE_ERROR_OUT_OF_MEMORY;
2145b0104773SPascal Brand 		goto out;
2146b0104773SPascal Brand 	}
2147b0104773SPascal Brand 	fh->fat_entry.flags = FILE_IS_LAST_ENTRY;
2148b0104773SPascal Brand 	fh->rpmb_fat_address = partition_data->fat_start_address;
2149b0104773SPascal Brand 
2150b0104773SPascal Brand 	/* Write init FAT entry and partition data to RPMB. */
2151e92de4caSJerome Forissier 	res = write_fat_entry(fh);
2152b0104773SPascal Brand 	if (res != TEE_SUCCESS)
2153b0104773SPascal Brand 		goto out;
2154b0104773SPascal Brand 
21553be2f85aSJens Wiklander 	res = tee_rpmb_write(RPMB_STORAGE_START_ADDRESS,
2156b0104773SPascal Brand 			     (uint8_t *)partition_data,
2157b399f70bSJens Wiklander 			     sizeof(struct rpmb_fs_partition), NULL, NULL);
2158b0104773SPascal Brand 
2159a8a78b85SJerome Forissier #ifndef CFG_RPMB_RESET_FAT
2160b0104773SPascal Brand store_fs_par:
2161a8a78b85SJerome Forissier #endif
2162a8a78b85SJerome Forissier 
2163b0104773SPascal Brand 	/* Store FAT start address. */
2164b0104773SPascal Brand 	fs_par = calloc(1, sizeof(struct rpmb_fs_parameters));
2165a8a78b85SJerome Forissier 	if (!fs_par) {
2166b0104773SPascal Brand 		res = TEE_ERROR_OUT_OF_MEMORY;
2167b0104773SPascal Brand 		goto out;
2168b0104773SPascal Brand 	}
2169b0104773SPascal Brand 
2170b0104773SPascal Brand 	fs_par->fat_start_address = partition_data->fat_start_address;
2171a8a78b85SJerome Forissier 	fs_par->max_rpmb_address = max_rpmb_block << RPMB_BLOCK_SIZE_SHIFT;
2172a8a78b85SJerome Forissier 
2173a8a78b85SJerome Forissier 	dump_fat();
2174b0104773SPascal Brand 
2175b0104773SPascal Brand out:
2176b0104773SPascal Brand 	free(fh);
2177b0104773SPascal Brand 	free(partition_data);
2178b0104773SPascal Brand 	return res;
2179b0104773SPascal Brand }
2180b0104773SPascal Brand 
2181b0104773SPascal Brand /**
2182b0104773SPascal Brand  * get_fat_start_address:
2183b0104773SPascal Brand  * FAT start_address from fs_par.
2184b0104773SPascal Brand  */
get_fat_start_address(uint32_t * addr)2185b0104773SPascal Brand static TEE_Result get_fat_start_address(uint32_t *addr)
2186b0104773SPascal Brand {
2187a8a78b85SJerome Forissier 	if (!fs_par)
2188a8a78b85SJerome Forissier 		return TEE_ERROR_NO_DATA;
2189b0104773SPascal Brand 
2190b0104773SPascal Brand 	*addr = fs_par->fat_start_address;
2191b0104773SPascal Brand 
2192a8a78b85SJerome Forissier 	return TEE_SUCCESS;
2193b0104773SPascal Brand }
2194b0104773SPascal Brand 
2195b0104773SPascal Brand /**
2196b0104773SPascal Brand  * read_fat: Read FAT entries
2197b0104773SPascal Brand  * Return matching FAT entry for read, rm rename and stat.
2198b0104773SPascal Brand  * Build up memory pool and return matching entry for write operation.
2199b0104773SPascal Brand  * "Last FAT entry" can be returned during write.
2200b0104773SPascal Brand  */
read_fat(struct rpmb_file_handle * fh,tee_mm_pool_t * p)2201a8a78b85SJerome Forissier static TEE_Result read_fat(struct rpmb_file_handle *fh, tee_mm_pool_t *p)
2202b0104773SPascal Brand {
2203b0104773SPascal Brand 	TEE_Result res = TEE_ERROR_GENERIC;
2204b0104773SPascal Brand 	tee_mm_entry_t *mm = NULL;
22055f68d784SManuel Huber 	struct rpmb_fat_entry *fe = NULL;
2206b0104773SPascal Brand 	uint32_t fat_address;
2207b0104773SPascal Brand 	bool entry_found = false;
2208a8a78b85SJerome Forissier 	bool expand_fat = false;
2209a8a78b85SJerome Forissier 	struct rpmb_file_handle last_fh;
2210a8a78b85SJerome Forissier 
2211a8a78b85SJerome Forissier 	DMSG("fat_address %d", fh->rpmb_fat_address);
2212b0104773SPascal Brand 
22135f68d784SManuel Huber 	res = fat_entry_dir_init();
22145f68d784SManuel Huber 	if (res)
2215b0104773SPascal Brand 		goto out;
2216b0104773SPascal Brand 
2217a8a78b85SJerome Forissier 	/*
2218a8a78b85SJerome Forissier 	 * The pool is used to represent the current RPMB layout. To find
2219a8a78b85SJerome Forissier 	 * a slot for the file tee_mm_alloc is called on the pool. Thus
2220a8a78b85SJerome Forissier 	 * if it is not NULL the entire FAT must be traversed to fill in
2221a8a78b85SJerome Forissier 	 * the pool.
2222a8a78b85SJerome Forissier 	 */
22235f68d784SManuel Huber 	while (true) {
22245f68d784SManuel Huber 		res = fat_entry_dir_get_next(&fe, &fat_address);
22255f68d784SManuel Huber 		if (res || !fe)
22265f68d784SManuel Huber 			break;
2227b0104773SPascal Brand 
2228b0104773SPascal Brand 		/*
2229b0104773SPascal Brand 		 * Look for an entry, matching filenames. (read, rm,
2230b0104773SPascal Brand 		 * rename and stat.). Only store first filename match.
2231b0104773SPascal Brand 		 */
223266d685f1SPeikan Tsai 		if ((!strcmp(fh->filename, fe->filename)) &&
22335f68d784SManuel Huber 		    (fe->flags & FILE_IS_ACTIVE) && !entry_found) {
2234b0104773SPascal Brand 			entry_found = true;
2235b0104773SPascal Brand 			fh->rpmb_fat_address = fat_address;
22365f68d784SManuel Huber 			memcpy(&fh->fat_entry, fe, sizeof(*fe));
2237a8a78b85SJerome Forissier 			if (!p)
2238b0104773SPascal Brand 				break;
2239b0104773SPascal Brand 		}
2240b0104773SPascal Brand 
2241b0104773SPascal Brand 		/* Add existing files to memory pool. (write) */
2242a8a78b85SJerome Forissier 		if (p) {
22435f68d784SManuel Huber 			if ((fe->flags & FILE_IS_ACTIVE) && fe->data_size > 0) {
2244a8a78b85SJerome Forissier 
22455f68d784SManuel Huber 				mm = tee_mm_alloc2(p, fe->start_address,
22465f68d784SManuel Huber 						   fe->data_size);
2247a8a78b85SJerome Forissier 				if (!mm) {
2248b0104773SPascal Brand 					res = TEE_ERROR_OUT_OF_MEMORY;
2249b0104773SPascal Brand 					goto out;
2250b0104773SPascal Brand 				}
2251b0104773SPascal Brand 			}
2252b0104773SPascal Brand 
2253b0104773SPascal Brand 			/* Unused FAT entries can be reused (write) */
22545f68d784SManuel Huber 			if (((fe->flags & FILE_IS_ACTIVE) == 0) &&
22555f68d784SManuel Huber 			    fh->rpmb_fat_address == 0) {
2256b0104773SPascal Brand 				fh->rpmb_fat_address = fat_address;
22575f68d784SManuel Huber 				memcpy(&fh->fat_entry, fe,
2258b0104773SPascal Brand 				       sizeof(struct rpmb_fat_entry));
2259b0104773SPascal Brand 			}
2260b0104773SPascal Brand 
22615f68d784SManuel Huber 			if (((fe->flags & FILE_IS_LAST_ENTRY) != 0) &&
22625f68d784SManuel Huber 			    fh->rpmb_fat_address == fat_address) {
2263a8a78b85SJerome Forissier 
2264a8a78b85SJerome Forissier 				/*
2265a8a78b85SJerome Forissier 				 * If the last entry was reached and was chosen
2266a8a78b85SJerome Forissier 				 * by the previous check, then the FAT needs to
2267a8a78b85SJerome Forissier 				 * be expanded.
2268a8a78b85SJerome Forissier 				 * fh->rpmb_fat_address is the address chosen
2269a8a78b85SJerome Forissier 				 * to store the files FAT entry and fat_address
2270a8a78b85SJerome Forissier 				 * is the current FAT entry address being
2271a8a78b85SJerome Forissier 				 * compared.
2272a8a78b85SJerome Forissier 				 */
2273a8a78b85SJerome Forissier 				expand_fat = true;
2274b0104773SPascal Brand 			}
2275b0104773SPascal Brand 		}
2276b0104773SPascal Brand 	}
2277b0104773SPascal Brand 
22785f68d784SManuel Huber 	if (res)
22795f68d784SManuel Huber 		goto out;
2280a8a78b85SJerome Forissier 	/*
2281a8a78b85SJerome Forissier 	 * Represent the FAT table in the pool.
2282a8a78b85SJerome Forissier 	 */
2283a8a78b85SJerome Forissier 	if (p) {
2284a8a78b85SJerome Forissier 		/*
2285a8a78b85SJerome Forissier 		 * Since fat_address is the start of the last entry it needs to
2286a8a78b85SJerome Forissier 		 * be moved up by an entry.
2287a8a78b85SJerome Forissier 		 */
2288a8a78b85SJerome Forissier 		fat_address += sizeof(struct rpmb_fat_entry);
2289a8a78b85SJerome Forissier 
2290b0104773SPascal Brand 		/* Make room for yet a FAT entry and add to memory pool. */
2291a8a78b85SJerome Forissier 		if (expand_fat)
2292a8a78b85SJerome Forissier 			fat_address += sizeof(struct rpmb_fat_entry);
2293a8a78b85SJerome Forissier 
2294b0104773SPascal Brand 		mm = tee_mm_alloc2(p, RPMB_STORAGE_START_ADDRESS, fat_address);
2295a8a78b85SJerome Forissier 		if (!mm) {
2296b0104773SPascal Brand 			res = TEE_ERROR_OUT_OF_MEMORY;
2297b0104773SPascal Brand 			goto out;
2298b0104773SPascal Brand 		}
2299a8a78b85SJerome Forissier 
2300a8a78b85SJerome Forissier 		if (expand_fat) {
2301a8a78b85SJerome Forissier 			/*
2302a8a78b85SJerome Forissier 			 * Point fat_address to the beginning of the new
2303a8a78b85SJerome Forissier 			 * entry.
2304a8a78b85SJerome Forissier 			 */
2305a8a78b85SJerome Forissier 			fat_address -= sizeof(struct rpmb_fat_entry);
2306a8a78b85SJerome Forissier 			memset(&last_fh, 0, sizeof(last_fh));
2307a8a78b85SJerome Forissier 			last_fh.fat_entry.flags = FILE_IS_LAST_ENTRY;
2308a8a78b85SJerome Forissier 			last_fh.rpmb_fat_address = fat_address;
2309e92de4caSJerome Forissier 			res = write_fat_entry(&last_fh);
2310a8a78b85SJerome Forissier 			if (res != TEE_SUCCESS)
2311a8a78b85SJerome Forissier 				goto out;
2312a8a78b85SJerome Forissier 		}
2313b0104773SPascal Brand 	}
2314b0104773SPascal Brand 
231566d685f1SPeikan Tsai 	if (!fh->rpmb_fat_address)
23168d5259f7SJens Wiklander 		res = TEE_ERROR_ITEM_NOT_FOUND;
2317b0104773SPascal Brand 
2318b0104773SPascal Brand out:
23195f68d784SManuel Huber 	fat_entry_dir_deinit();
2320b0104773SPascal Brand 	return res;
2321b0104773SPascal Brand }
2322b0104773SPascal Brand 
generate_fek(struct rpmb_fat_entry * fe,const TEE_UUID * uuid)23230c4e1284SJens Wiklander static TEE_Result generate_fek(struct rpmb_fat_entry *fe, const TEE_UUID *uuid)
23249e84c17eSJerome Forissier {
23259e84c17eSJerome Forissier 	TEE_Result res;
23269e84c17eSJerome Forissier 
23279e84c17eSJerome Forissier again:
23280c4e1284SJens Wiklander 	res = tee_fs_generate_fek(uuid, fe->fek, sizeof(fe->fek));
23299e84c17eSJerome Forissier 	if (res != TEE_SUCCESS)
23309e84c17eSJerome Forissier 		return res;
23319e84c17eSJerome Forissier 
23329e84c17eSJerome Forissier 	if (is_zero(fe->fek, sizeof(fe->fek)))
23339e84c17eSJerome Forissier 		goto again;
23349e84c17eSJerome Forissier 
23359e84c17eSJerome Forissier 	return res;
23369e84c17eSJerome Forissier }
23379e84c17eSJerome Forissier 
rpmb_fs_open_internal(struct rpmb_file_handle * fh,const TEE_UUID * uuid,bool create)2338078f18f8SJens Wiklander static TEE_Result rpmb_fs_open_internal(struct rpmb_file_handle *fh,
2339078f18f8SJens Wiklander 					const TEE_UUID *uuid, bool create)
2340b0104773SPascal Brand {
2341a8a78b85SJerome Forissier 	tee_mm_pool_t p;
2342a8a78b85SJerome Forissier 	bool pool_result;
2343d9f0ee43Sjames.jiang 	paddr_size_t pool_sz = 0;
2344b0104773SPascal Brand 	TEE_Result res = TEE_ERROR_GENERIC;
2345b0104773SPascal Brand 
2346a8a78b85SJerome Forissier 	/* We need to do setup in order to make sure fs_par is filled in */
2347a8a78b85SJerome Forissier 	res = rpmb_fs_setup();
2348a8a78b85SJerome Forissier 	if (res != TEE_SUCCESS)
2349a8a78b85SJerome Forissier 		goto out;
2350a8a78b85SJerome Forissier 
2351b399f70bSJens Wiklander 	fh->uuid = uuid;
2352b86c18ecSJens Wiklander 	if (create) {
2353a8a78b85SJerome Forissier 		/* Upper memory allocation must be used for RPMB_FS. */
2354d9f0ee43Sjames.jiang 		pool_sz = fs_par->max_rpmb_address - RPMB_STORAGE_START_ADDRESS;
2355a8a78b85SJerome Forissier 		pool_result = tee_mm_init(&p,
2356a8a78b85SJerome Forissier 					  RPMB_STORAGE_START_ADDRESS,
2357d9f0ee43Sjames.jiang 					  pool_sz,
2358a8a78b85SJerome Forissier 					  RPMB_BLOCK_SIZE_SHIFT,
2359a8a78b85SJerome Forissier 					  TEE_MM_POOL_HI_ALLOC);
2360a8a78b85SJerome Forissier 
2361a8a78b85SJerome Forissier 		if (!pool_result) {
2362a8a78b85SJerome Forissier 			res = TEE_ERROR_OUT_OF_MEMORY;
2363a8a78b85SJerome Forissier 			goto out;
2364a8a78b85SJerome Forissier 		}
2365a8a78b85SJerome Forissier 
2366a8a78b85SJerome Forissier 		res = read_fat(fh, &p);
2367a8a78b85SJerome Forissier 		tee_mm_final(&p);
2368a8a78b85SJerome Forissier 		if (res != TEE_SUCCESS)
2369a8a78b85SJerome Forissier 			goto out;
2370a8a78b85SJerome Forissier 	} else {
2371a8a78b85SJerome Forissier 		res = read_fat(fh, NULL);
2372a8a78b85SJerome Forissier 		if (res != TEE_SUCCESS)
2373a8a78b85SJerome Forissier 			goto out;
2374a8a78b85SJerome Forissier 	}
2375a8a78b85SJerome Forissier 
2376a8a78b85SJerome Forissier 	/*
2377a8a78b85SJerome Forissier 	 * If this is opened with create and the entry found was not active
2378a8a78b85SJerome Forissier 	 * then this is a new file and the FAT entry must be written
2379a8a78b85SJerome Forissier 	 */
2380b86c18ecSJens Wiklander 	if (create) {
2381a8a78b85SJerome Forissier 		if ((fh->fat_entry.flags & FILE_IS_ACTIVE) == 0) {
2382a8a78b85SJerome Forissier 			memset(&fh->fat_entry, 0,
2383a8a78b85SJerome Forissier 				sizeof(struct rpmb_fat_entry));
2384b2215adfSJens Wiklander 			memcpy(fh->fat_entry.filename, fh->filename,
2385b2215adfSJens Wiklander 				strlen(fh->filename));
2386a8a78b85SJerome Forissier 			/* Start address and size are 0 */
2387a8a78b85SJerome Forissier 			fh->fat_entry.flags = FILE_IS_ACTIVE;
2388a8a78b85SJerome Forissier 
2389078f18f8SJens Wiklander 			res = generate_fek(&fh->fat_entry, uuid);
2390b86c18ecSJens Wiklander 			if (res != TEE_SUCCESS)
23919e84c17eSJerome Forissier 				goto out;
23929e84c17eSJerome Forissier 			DMSG("GENERATE FEK key: %p",
23939e84c17eSJerome Forissier 			     (void *)fh->fat_entry.fek);
23949e84c17eSJerome Forissier 			DHEXDUMP(fh->fat_entry.fek, sizeof(fh->fat_entry.fek));
23959e84c17eSJerome Forissier 
2396e92de4caSJerome Forissier 			res = write_fat_entry(fh);
2397b86c18ecSJens Wiklander 			if (res != TEE_SUCCESS)
2398a8a78b85SJerome Forissier 				goto out;
2399a8a78b85SJerome Forissier 		}
2400a8a78b85SJerome Forissier 	}
2401a8a78b85SJerome Forissier 
2402a8a78b85SJerome Forissier 	res = TEE_SUCCESS;
2403a8a78b85SJerome Forissier 
2404a8a78b85SJerome Forissier out:
2405b86c18ecSJens Wiklander 	return res;
2406a8a78b85SJerome Forissier }
2407a8a78b85SJerome Forissier 
rpmb_fs_close(struct tee_file_handle ** tfh)2408b86c18ecSJens Wiklander static void rpmb_fs_close(struct tee_file_handle **tfh)
2409a8a78b85SJerome Forissier {
2410b86c18ecSJens Wiklander 	struct rpmb_file_handle *fh = (struct rpmb_file_handle *)*tfh;
2411a8a78b85SJerome Forissier 
2412a8a78b85SJerome Forissier 	free(fh);
2413b86c18ecSJens Wiklander 	*tfh = NULL;
2414a8a78b85SJerome Forissier }
2415a8a78b85SJerome Forissier 
rpmb_fs_read(struct tee_file_handle * tfh,size_t pos,void * buf_core,void * buf_user,size_t * len)2416879237aeSJens Wiklander static TEE_Result rpmb_fs_read(struct tee_file_handle *tfh, size_t pos,
2417b2284b11SJens Wiklander 			       void *buf_core, void *buf_user, size_t *len)
2418a8a78b85SJerome Forissier {
24190c96a71dSJerome Forissier 	TEE_Result res;
2420b86c18ecSJens Wiklander 	struct rpmb_file_handle *fh = (struct rpmb_file_handle *)tfh;
2421b86c18ecSJens Wiklander 	size_t size = *len;
2422a8a78b85SJerome Forissier 
2423b2284b11SJens Wiklander 	/* One of buf_core and buf_user must be NULL */
2424b2284b11SJens Wiklander 	assert(!buf_core || !buf_user);
2425b2284b11SJens Wiklander 
2426a8a78b85SJerome Forissier 	if (!size)
2427b86c18ecSJens Wiklander 		return TEE_SUCCESS;
2428a8a78b85SJerome Forissier 
2429a7e22cf5SLijianhui (Airbak) 	mutex_lock(&rpmb_mutex);
2430a7e22cf5SLijianhui (Airbak) 
2431a8a78b85SJerome Forissier 	dump_fh(fh);
2432a8a78b85SJerome Forissier 
2433b0104773SPascal Brand 	res = read_fat(fh, NULL);
2434b86c18ecSJens Wiklander 	if (res != TEE_SUCCESS)
2435b0104773SPascal Brand 		goto out;
2436b0104773SPascal Brand 
2437879237aeSJens Wiklander 	if (pos >= fh->fat_entry.data_size) {
2438b86c18ecSJens Wiklander 		*len = 0;
2439b5d2d36bSJerome Forissier 		goto out;
2440b5d2d36bSJerome Forissier 	}
2441b5d2d36bSJerome Forissier 
2442879237aeSJens Wiklander 	size = MIN(size, fh->fat_entry.data_size - pos);
2443b5d2d36bSJerome Forissier 	if (size) {
2444b2284b11SJens Wiklander 		if (buf_core) {
24453be2f85aSJens Wiklander 			res = tee_rpmb_read(fh->fat_entry.start_address + pos,
2446b2284b11SJens Wiklander 					    buf_core, size, fh->fat_entry.fek,
2447b2284b11SJens Wiklander 					    fh->uuid);
2448b86c18ecSJens Wiklander 			if (res != TEE_SUCCESS)
2449b0104773SPascal Brand 				goto out;
2450b2284b11SJens Wiklander 		} else if (buf_user) {
2451b2284b11SJens Wiklander 			uint32_t f = TEE_MEMORY_ACCESS_WRITE;
2452b2284b11SJens Wiklander 
2453b2284b11SJens Wiklander 			res = check_user_access(f, buf_user, size);
2454b2284b11SJens Wiklander 			if (res)
2455b2284b11SJens Wiklander 				goto out;
2456b2284b11SJens Wiklander 			enter_user_access();
24573be2f85aSJens Wiklander 			res = tee_rpmb_read(fh->fat_entry.start_address + pos,
2458b2284b11SJens Wiklander 					    buf_user, size, fh->fat_entry.fek,
2459b2284b11SJens Wiklander 					    fh->uuid);
2460b2284b11SJens Wiklander 			exit_user_access();
2461b2284b11SJens Wiklander 			if (res)
2462b2284b11SJens Wiklander 				goto out;
2463b2284b11SJens Wiklander 		}
2464b0104773SPascal Brand 	}
2465b86c18ecSJens Wiklander 	*len = size;
2466b0104773SPascal Brand 
2467b0104773SPascal Brand out:
2468a7e22cf5SLijianhui (Airbak) 	mutex_unlock(&rpmb_mutex);
2469b86c18ecSJens Wiklander 	return res;
2470b0104773SPascal Brand }
2471b0104773SPascal Brand 
update_write_helper(struct rpmb_file_handle * fh,size_t pos,const void * buf,size_t size,uintptr_t new_fat,size_t new_size)247264c6d291SEtienne Carriere static TEE_Result update_write_helper(struct rpmb_file_handle *fh,
247364c6d291SEtienne Carriere 				      size_t pos, const void *buf,
247464c6d291SEtienne Carriere 				      size_t size, uintptr_t new_fat,
247564c6d291SEtienne Carriere 				      size_t new_size)
247664c6d291SEtienne Carriere {
247764c6d291SEtienne Carriere 	uintptr_t old_fat = fh->fat_entry.start_address;
247864c6d291SEtienne Carriere 	size_t old_size = fh->fat_entry.data_size;
247964c6d291SEtienne Carriere 	const uint8_t *rem_buf = buf;
248064c6d291SEtienne Carriere 	size_t rem_size = size;
248164c6d291SEtienne Carriere 	uint8_t *blk_buf = NULL;
248264c6d291SEtienne Carriere 	size_t blk_offset = 0;
248364c6d291SEtienne Carriere 	size_t blk_size = 0;
248464c6d291SEtienne Carriere 	TEE_Result res = TEE_SUCCESS;
248564c6d291SEtienne Carriere 
248664c6d291SEtienne Carriere 	blk_buf = mempool_alloc(mempool_default, TMP_BLOCK_SIZE);
248764c6d291SEtienne Carriere 	if (!blk_buf)
248864c6d291SEtienne Carriere 		return TEE_ERROR_OUT_OF_MEMORY;
248964c6d291SEtienne Carriere 
249064c6d291SEtienne Carriere 	while (blk_offset < new_size) {
2491cd799689SEtienne Carriere 		uint8_t *copy_dst = blk_buf;
2492cd799689SEtienne Carriere 		size_t copy_size = 0;
2493cd799689SEtienne Carriere 		size_t rd_size = 0;
2494cd799689SEtienne Carriere 
249564c6d291SEtienne Carriere 		blk_size = MIN(TMP_BLOCK_SIZE, new_size - blk_offset);
2496d53897cdSJens Wiklander 		memset(blk_buf, 0, blk_size);
249764c6d291SEtienne Carriere 
249864c6d291SEtienne Carriere 		/* Possibly read old RPMB data in temporary buffer */
249964c6d291SEtienne Carriere 		if (blk_offset < pos && blk_offset < old_size) {
2500cd799689SEtienne Carriere 			rd_size = MIN(blk_size, old_size - blk_offset);
250164c6d291SEtienne Carriere 
25023be2f85aSJens Wiklander 			res = tee_rpmb_read(old_fat + blk_offset, blk_buf,
250364c6d291SEtienne Carriere 					    rd_size, fh->fat_entry.fek,
250464c6d291SEtienne Carriere 					    fh->uuid);
250564c6d291SEtienne Carriere 			if (res != TEE_SUCCESS)
250664c6d291SEtienne Carriere 				break;
250764c6d291SEtienne Carriere 		}
250864c6d291SEtienne Carriere 
250964c6d291SEtienne Carriere 		/* Possibly update data in temporary buffer */
251064c6d291SEtienne Carriere 		if ((blk_offset + TMP_BLOCK_SIZE > pos) &&
251164c6d291SEtienne Carriere 		    (blk_offset < pos + size)) {
2512cd799689SEtienne Carriere 			size_t offset = 0;
2513cd799689SEtienne Carriere 
2514cd799689SEtienne Carriere 			copy_dst = blk_buf;
2515cd799689SEtienne Carriere 			copy_size = TMP_BLOCK_SIZE;
251664c6d291SEtienne Carriere 
251764c6d291SEtienne Carriere 			if (blk_offset < pos) {
2518cd799689SEtienne Carriere 				offset = pos - blk_offset;
251964c6d291SEtienne Carriere 
2520cd799689SEtienne Carriere 				copy_dst += offset;
252164c6d291SEtienne Carriere 				copy_size -= offset;
252264c6d291SEtienne Carriere 			}
252364c6d291SEtienne Carriere 			copy_size = MIN(copy_size, rem_size);
252464c6d291SEtienne Carriere 
2525cd799689SEtienne Carriere 			memcpy(copy_dst, rem_buf, copy_size);
252664c6d291SEtienne Carriere 			rem_buf += copy_size;
252764c6d291SEtienne Carriere 			rem_size -= copy_size;
252864c6d291SEtienne Carriere 		}
252964c6d291SEtienne Carriere 
253064c6d291SEtienne Carriere 		/* Write temporary buffer to new RPMB destination */
25313be2f85aSJens Wiklander 		res = tee_rpmb_write(new_fat + blk_offset, blk_buf, blk_size,
253264c6d291SEtienne Carriere 				     fh->fat_entry.fek, fh->uuid);
253364c6d291SEtienne Carriere 		if (res != TEE_SUCCESS)
253464c6d291SEtienne Carriere 			break;
253564c6d291SEtienne Carriere 
253664c6d291SEtienne Carriere 		blk_offset += blk_size;
253764c6d291SEtienne Carriere 	}
253864c6d291SEtienne Carriere 
253964c6d291SEtienne Carriere 	mempool_free(mempool_default, blk_buf);
254064c6d291SEtienne Carriere 
254164c6d291SEtienne Carriere 	return res;
254264c6d291SEtienne Carriere }
254364c6d291SEtienne Carriere 
rpmb_fs_write_primitive(struct rpmb_file_handle * fh,size_t pos,const void * buf,size_t size)2544078f18f8SJens Wiklander static TEE_Result rpmb_fs_write_primitive(struct rpmb_file_handle *fh,
254573ea1cdeSJens Wiklander 					  size_t pos, const void *buf,
254673ea1cdeSJens Wiklander 					  size_t size)
2547b0104773SPascal Brand {
254864c6d291SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
254964c6d291SEtienne Carriere 	tee_mm_pool_t p = { };
2550a8a78b85SJerome Forissier 	bool pool_result = false;
255164c6d291SEtienne Carriere 	size_t end = 0;
255264c6d291SEtienne Carriere 	uint32_t start_addr = 0;
2553d9f0ee43Sjames.jiang 	paddr_size_t pool_sz = 0;
2554b0104773SPascal Brand 
2555a8a78b85SJerome Forissier 	if (!size)
2556b86c18ecSJens Wiklander 		return TEE_SUCCESS;
2557a8a78b85SJerome Forissier 
2558a8a78b85SJerome Forissier 	if (!fs_par) {
2559a8a78b85SJerome Forissier 		res = TEE_ERROR_GENERIC;
2560b0104773SPascal Brand 		goto out;
2561b0104773SPascal Brand 	}
2562b0104773SPascal Brand 
2563a8a78b85SJerome Forissier 	dump_fh(fh);
2564b0104773SPascal Brand 
2565b0104773SPascal Brand 	/* Upper memory allocation must be used for RPMB_FS. */
2566d9f0ee43Sjames.jiang 	pool_sz = fs_par->max_rpmb_address - RPMB_STORAGE_START_ADDRESS;
2567a8a78b85SJerome Forissier 	pool_result = tee_mm_init(&p,
2568a8a78b85SJerome Forissier 				  RPMB_STORAGE_START_ADDRESS,
2569d9f0ee43Sjames.jiang 				  pool_sz,
2570a8a78b85SJerome Forissier 				  RPMB_BLOCK_SIZE_SHIFT,
2571a8a78b85SJerome Forissier 				  TEE_MM_POOL_HI_ALLOC);
2572a8a78b85SJerome Forissier 	if (!pool_result) {
2573b0104773SPascal Brand 		res = TEE_ERROR_OUT_OF_MEMORY;
2574b0104773SPascal Brand 		goto out;
2575b0104773SPascal Brand 	}
2576b0104773SPascal Brand 
2577b0104773SPascal Brand 	res = read_fat(fh, &p);
2578b0104773SPascal Brand 	if (res != TEE_SUCCESS)
2579b0104773SPascal Brand 		goto out;
2580b0104773SPascal Brand 
2581d13278b8SEtienne Carriere 	if (fh->fat_entry.flags & FILE_IS_LAST_ENTRY)
25828c9d9445SEtienne Carriere 		panic("invalid last entry flag");
2583a8a78b85SJerome Forissier 
2584ea81076fSJerome Forissier 	if (ADD_OVERFLOW(pos, size, &end)) {
2585ea81076fSJerome Forissier 		res = TEE_ERROR_BAD_PARAMETERS;
2586ea81076fSJerome Forissier 		goto out;
2587ea81076fSJerome Forissier 	}
2588ea81076fSJerome Forissier 	if (ADD_OVERFLOW(fh->fat_entry.start_address, pos, &start_addr)) {
2589ea81076fSJerome Forissier 		res = TEE_ERROR_BAD_PARAMETERS;
2590ea81076fSJerome Forissier 		goto out;
2591ea81076fSJerome Forissier 	}
2592a8a78b85SJerome Forissier 
2593188f5aa5SJerome Forissier 	if (end <= fh->fat_entry.data_size &&
25943be2f85aSJens Wiklander 	    tee_rpmb_write_is_atomic(start_addr, size)) {
2595188f5aa5SJerome Forissier 
2596188f5aa5SJerome Forissier 		DMSG("Updating data in-place");
25973be2f85aSJens Wiklander 		res = tee_rpmb_write(start_addr, buf,
2598b399f70bSJens Wiklander 				     size, fh->fat_entry.fek, fh->uuid);
2599a8a78b85SJerome Forissier 	} else {
2600188f5aa5SJerome Forissier 		/*
2601188f5aa5SJerome Forissier 		 * File must be extended, or update cannot be atomic: allocate,
2602188f5aa5SJerome Forissier 		 * read, update, write.
2603188f5aa5SJerome Forissier 		 */
260464c6d291SEtienne Carriere 		size_t new_size = MAX(end, fh->fat_entry.data_size);
260564c6d291SEtienne Carriere 		tee_mm_entry_t *mm = tee_mm_alloc(&p, new_size);
260664c6d291SEtienne Carriere 		uintptr_t new_fat_entry = 0;
2607a8a78b85SJerome Forissier 
2608188f5aa5SJerome Forissier 		DMSG("Need to re-allocate");
26093c534211SStefan Schmidt 		if (!mm) {
26103c534211SStefan Schmidt 			DMSG("RPMB: No space left");
26113c534211SStefan Schmidt 			res = TEE_ERROR_STORAGE_NO_SPACE;
26123c534211SStefan Schmidt 			goto out;
26133c534211SStefan Schmidt 		}
2614b0104773SPascal Brand 
261564c6d291SEtienne Carriere 		new_fat_entry = tee_mm_get_smem(mm);
2616b0104773SPascal Brand 
261764c6d291SEtienne Carriere 		res = update_write_helper(fh, pos, buf, size,
261864c6d291SEtienne Carriere 					  new_fat_entry, new_size);
261964c6d291SEtienne Carriere 		if (res == TEE_SUCCESS) {
262064c6d291SEtienne Carriere 			fh->fat_entry.data_size = new_size;
262164c6d291SEtienne Carriere 			fh->fat_entry.start_address = new_fat_entry;
2622b0104773SPascal Brand 
2623e92de4caSJerome Forissier 			res = write_fat_entry(fh);
262464c6d291SEtienne Carriere 		}
2625a8a78b85SJerome Forissier 	}
2626b0104773SPascal Brand 
2627b0104773SPascal Brand out:
2628a8a78b85SJerome Forissier 	if (pool_result)
2629b0104773SPascal Brand 		tee_mm_final(&p);
2630b0104773SPascal Brand 
2631b86c18ecSJens Wiklander 	return res;
2632b0104773SPascal Brand }
2633b0104773SPascal Brand 
rpmb_fs_write(struct tee_file_handle * tfh,size_t pos,const void * buf_core,const void * buf_user,size_t size)263473ea1cdeSJens Wiklander static TEE_Result rpmb_fs_write(struct tee_file_handle *tfh, size_t pos,
2635b2284b11SJens Wiklander 				const void *buf_core, const void *buf_user,
2636b2284b11SJens Wiklander 				size_t size)
263773ea1cdeSJens Wiklander {
2638b2284b11SJens Wiklander 	TEE_Result res = TEE_SUCCESS;
2639b2284b11SJens Wiklander 
2640b2284b11SJens Wiklander 	/* One of buf_core and buf_user must be NULL */
2641b2284b11SJens Wiklander 	assert(!buf_core || !buf_user);
2642b2284b11SJens Wiklander 
2643b2284b11SJens Wiklander 	if (!size)
2644b2284b11SJens Wiklander 		return TEE_SUCCESS;
264573ea1cdeSJens Wiklander 
264673ea1cdeSJens Wiklander 	mutex_lock(&rpmb_mutex);
2647b2284b11SJens Wiklander 	if (buf_core) {
2648b2284b11SJens Wiklander 		res = rpmb_fs_write_primitive((struct rpmb_file_handle *)tfh,
2649b2284b11SJens Wiklander 					      pos, buf_core, size);
2650b2284b11SJens Wiklander 	} else if (buf_user) {
2651b2284b11SJens Wiklander 		uint32_t f = TEE_MEMORY_ACCESS_READ;
2652b2284b11SJens Wiklander 
2653b2284b11SJens Wiklander 		res = check_user_access(f, buf_user, size);
2654b2284b11SJens Wiklander 		if (res)
2655b2284b11SJens Wiklander 			goto out;
2656b2284b11SJens Wiklander 		enter_user_access();
2657b2284b11SJens Wiklander 		res = rpmb_fs_write_primitive((struct rpmb_file_handle *)tfh,
2658b2284b11SJens Wiklander 					      pos, buf_user, size);
2659b2284b11SJens Wiklander 		exit_user_access();
2660b2284b11SJens Wiklander 	}
2661b2284b11SJens Wiklander out:
266273ea1cdeSJens Wiklander 	mutex_unlock(&rpmb_mutex);
266373ea1cdeSJens Wiklander 
266473ea1cdeSJens Wiklander 	return res;
266573ea1cdeSJens Wiklander }
266673ea1cdeSJens Wiklander 
rpmb_fs_remove_internal(struct rpmb_file_handle * fh)2667078f18f8SJens Wiklander static TEE_Result rpmb_fs_remove_internal(struct rpmb_file_handle *fh)
2668b0104773SPascal Brand {
2669078f18f8SJens Wiklander 	TEE_Result res;
2670b0104773SPascal Brand 
2671b0104773SPascal Brand 	res = read_fat(fh, NULL);
2672078f18f8SJens Wiklander 	if (res)
2673078f18f8SJens Wiklander 		return res;
2674b0104773SPascal Brand 
2675b0104773SPascal Brand 	/* Clear this file entry. */
2676b0104773SPascal Brand 	memset(&fh->fat_entry, 0, sizeof(struct rpmb_fat_entry));
2677e92de4caSJerome Forissier 	return write_fat_entry(fh);
2678b0104773SPascal Brand }
2679b0104773SPascal Brand 
rpmb_fs_remove(struct tee_pobj * po)268073ea1cdeSJens Wiklander static TEE_Result rpmb_fs_remove(struct tee_pobj *po)
268173ea1cdeSJens Wiklander {
268273ea1cdeSJens Wiklander 	TEE_Result res;
2683078f18f8SJens Wiklander 	struct rpmb_file_handle *fh = alloc_file_handle(po, po->temporary);
2684078f18f8SJens Wiklander 
2685078f18f8SJens Wiklander 	if (!fh)
2686078f18f8SJens Wiklander 		return TEE_ERROR_OUT_OF_MEMORY;
268773ea1cdeSJens Wiklander 
268873ea1cdeSJens Wiklander 	mutex_lock(&rpmb_mutex);
2689078f18f8SJens Wiklander 
2690078f18f8SJens Wiklander 	res = rpmb_fs_remove_internal(fh);
2691078f18f8SJens Wiklander 
269273ea1cdeSJens Wiklander 	mutex_unlock(&rpmb_mutex);
269373ea1cdeSJens Wiklander 
2694078f18f8SJens Wiklander 	free(fh);
269573ea1cdeSJens Wiklander 	return res;
269673ea1cdeSJens Wiklander }
269773ea1cdeSJens Wiklander 
rpmb_fs_rename_internal(struct tee_pobj * old,struct tee_pobj * new,bool overwrite)269873ea1cdeSJens Wiklander static  TEE_Result rpmb_fs_rename_internal(struct tee_pobj *old,
269973ea1cdeSJens Wiklander 					   struct tee_pobj *new,
2700822203a8SJens Wiklander 					   bool overwrite)
2701b0104773SPascal Brand {
2702b0104773SPascal Brand 	TEE_Result res = TEE_ERROR_GENERIC;
2703a8a78b85SJerome Forissier 	struct rpmb_file_handle *fh_old = NULL;
2704a8a78b85SJerome Forissier 	struct rpmb_file_handle *fh_new = NULL;
2705b0104773SPascal Brand 
2706b2215adfSJens Wiklander 	if (!old) {
2707b0104773SPascal Brand 		res = TEE_ERROR_BAD_PARAMETERS;
2708b0104773SPascal Brand 		goto out;
2709b0104773SPascal Brand 	}
2710b0104773SPascal Brand 
2711b2215adfSJens Wiklander 	if (new)
2712b2215adfSJens Wiklander 		fh_old = alloc_file_handle(old, old->temporary);
2713b2215adfSJens Wiklander 	else
2714b2215adfSJens Wiklander 		fh_old = alloc_file_handle(old, true);
2715a8a78b85SJerome Forissier 	if (!fh_old) {
2716b0104773SPascal Brand 		res = TEE_ERROR_OUT_OF_MEMORY;
2717b0104773SPascal Brand 		goto out;
2718b0104773SPascal Brand 	}
2719b0104773SPascal Brand 
2720b2215adfSJens Wiklander 	if (new)
2721b2215adfSJens Wiklander 		fh_new = alloc_file_handle(new, new->temporary);
2722b2215adfSJens Wiklander 	else
2723b2215adfSJens Wiklander 		fh_new = alloc_file_handle(old, false);
2724a8a78b85SJerome Forissier 	if (!fh_new) {
2725b0104773SPascal Brand 		res = TEE_ERROR_OUT_OF_MEMORY;
2726b0104773SPascal Brand 		goto out;
2727b0104773SPascal Brand 	}
2728b0104773SPascal Brand 
2729b0104773SPascal Brand 	res = read_fat(fh_old, NULL);
2730b0104773SPascal Brand 	if (res != TEE_SUCCESS)
2731b0104773SPascal Brand 		goto out;
2732b0104773SPascal Brand 
2733b0104773SPascal Brand 	res = read_fat(fh_new, NULL);
2734b0104773SPascal Brand 	if (res == TEE_SUCCESS) {
2735822203a8SJens Wiklander 		if (!overwrite) {
2736ae54853cSEtienne Carriere 			res = TEE_ERROR_ACCESS_CONFLICT;
2737b0104773SPascal Brand 			goto out;
2738b0104773SPascal Brand 		}
2739b0104773SPascal Brand 
2740822203a8SJens Wiklander 		/* Clear this file entry. */
2741822203a8SJens Wiklander 		memset(&fh_new->fat_entry, 0, sizeof(struct rpmb_fat_entry));
2742e92de4caSJerome Forissier 		res = write_fat_entry(fh_new);
2743822203a8SJens Wiklander 		if (res != TEE_SUCCESS)
2744822203a8SJens Wiklander 			goto out;
2745822203a8SJens Wiklander 	}
2746822203a8SJens Wiklander 
2747a8a78b85SJerome Forissier 	memset(fh_old->fat_entry.filename, 0, TEE_RPMB_FS_FILENAME_LENGTH);
2748b2215adfSJens Wiklander 	memcpy(fh_old->fat_entry.filename, fh_new->filename,
2749b2215adfSJens Wiklander 	       strlen(fh_new->filename));
2750b0104773SPascal Brand 
2751e92de4caSJerome Forissier 	res = write_fat_entry(fh_old);
2752b0104773SPascal Brand 
2753b0104773SPascal Brand out:
2754b0104773SPascal Brand 	free(fh_old);
2755b0104773SPascal Brand 	free(fh_new);
2756b0104773SPascal Brand 
2757b86c18ecSJens Wiklander 	return res;
2758b0104773SPascal Brand }
2759b0104773SPascal Brand 
rpmb_fs_rename(struct tee_pobj * old,struct tee_pobj * new,bool overwrite)276073ea1cdeSJens Wiklander static  TEE_Result rpmb_fs_rename(struct tee_pobj *old, struct tee_pobj *new,
276173ea1cdeSJens Wiklander 				  bool overwrite)
276273ea1cdeSJens Wiklander {
276373ea1cdeSJens Wiklander 	TEE_Result res;
276473ea1cdeSJens Wiklander 
276573ea1cdeSJens Wiklander 	mutex_lock(&rpmb_mutex);
276673ea1cdeSJens Wiklander 	res = rpmb_fs_rename_internal(old, new, overwrite);
276773ea1cdeSJens Wiklander 	mutex_unlock(&rpmb_mutex);
276873ea1cdeSJens Wiklander 
276973ea1cdeSJens Wiklander 	return res;
277073ea1cdeSJens Wiklander }
277173ea1cdeSJens Wiklander 
rpmb_fs_truncate(struct tee_file_handle * tfh,size_t length)2772b86c18ecSJens Wiklander static TEE_Result rpmb_fs_truncate(struct tee_file_handle *tfh, size_t length)
2773a8a78b85SJerome Forissier {
2774b86c18ecSJens Wiklander 	struct rpmb_file_handle *fh = (struct rpmb_file_handle *)tfh;
2775a8a78b85SJerome Forissier 	tee_mm_pool_t p;
2776a8a78b85SJerome Forissier 	bool pool_result = false;
2777a8a78b85SJerome Forissier 	tee_mm_entry_t *mm;
2778a8a78b85SJerome Forissier 	uint32_t newsize;
2779a8a78b85SJerome Forissier 	uint8_t *newbuf = NULL;
2780a8a78b85SJerome Forissier 	uintptr_t newaddr;
2781a8a78b85SJerome Forissier 	TEE_Result res = TEE_ERROR_GENERIC;
2782d9f0ee43Sjames.jiang 	paddr_size_t pool_sz = 0;
2783a8a78b85SJerome Forissier 
2784a7e22cf5SLijianhui (Airbak) 	mutex_lock(&rpmb_mutex);
2785a7e22cf5SLijianhui (Airbak) 
2786b86c18ecSJens Wiklander 	if (length > INT32_MAX) {
2787a8a78b85SJerome Forissier 		res = TEE_ERROR_BAD_PARAMETERS;
2788a8a78b85SJerome Forissier 		goto out;
2789a8a78b85SJerome Forissier 	}
2790a8a78b85SJerome Forissier 	newsize = length;
2791a8a78b85SJerome Forissier 
2792a8a78b85SJerome Forissier 	res = read_fat(fh, NULL);
2793a8a78b85SJerome Forissier 	if (res != TEE_SUCCESS)
2794a8a78b85SJerome Forissier 		goto out;
2795a8a78b85SJerome Forissier 
2796a8a78b85SJerome Forissier 	if (newsize > fh->fat_entry.data_size) {
2797a8a78b85SJerome Forissier 		/* Extend file */
2798a8a78b85SJerome Forissier 
2799d9f0ee43Sjames.jiang 		pool_sz = fs_par->max_rpmb_address - RPMB_STORAGE_START_ADDRESS;
2800a8a78b85SJerome Forissier 		pool_result = tee_mm_init(&p,
2801a8a78b85SJerome Forissier 					  RPMB_STORAGE_START_ADDRESS,
2802d9f0ee43Sjames.jiang 					  pool_sz,
2803a8a78b85SJerome Forissier 					  RPMB_BLOCK_SIZE_SHIFT,
2804a8a78b85SJerome Forissier 					  TEE_MM_POOL_HI_ALLOC);
2805a8a78b85SJerome Forissier 		if (!pool_result) {
2806a8a78b85SJerome Forissier 			res = TEE_ERROR_OUT_OF_MEMORY;
2807a8a78b85SJerome Forissier 			goto out;
2808a8a78b85SJerome Forissier 		}
2809a8a78b85SJerome Forissier 		res = read_fat(fh, &p);
2810a8a78b85SJerome Forissier 		if (res != TEE_SUCCESS)
2811a8a78b85SJerome Forissier 			goto out;
2812a8a78b85SJerome Forissier 
2813a8a78b85SJerome Forissier 		mm = tee_mm_alloc(&p, newsize);
281405c5cd2eSVolodymyr Babchuk 		newbuf = calloc(1, newsize);
2815a8a78b85SJerome Forissier 		if (!mm || !newbuf) {
2816a8a78b85SJerome Forissier 			res = TEE_ERROR_OUT_OF_MEMORY;
2817a8a78b85SJerome Forissier 			goto out;
2818a8a78b85SJerome Forissier 		}
2819a8a78b85SJerome Forissier 
2820a8a78b85SJerome Forissier 		if (fh->fat_entry.data_size) {
28213be2f85aSJens Wiklander 			res = tee_rpmb_read(fh->fat_entry.start_address,
28229e84c17eSJerome Forissier 					    newbuf, fh->fat_entry.data_size,
2823b399f70bSJens Wiklander 					    fh->fat_entry.fek, fh->uuid);
2824a8a78b85SJerome Forissier 			if (res != TEE_SUCCESS)
2825a8a78b85SJerome Forissier 				goto out;
2826a8a78b85SJerome Forissier 		}
2827a8a78b85SJerome Forissier 
2828a8a78b85SJerome Forissier 		newaddr = tee_mm_get_smem(mm);
28293be2f85aSJens Wiklander 		res = tee_rpmb_write(newaddr, newbuf,
2830b399f70bSJens Wiklander 				     newsize, fh->fat_entry.fek, fh->uuid);
2831a8a78b85SJerome Forissier 		if (res != TEE_SUCCESS)
2832a8a78b85SJerome Forissier 			goto out;
2833a8a78b85SJerome Forissier 
2834a8a78b85SJerome Forissier 	} else {
2835a8a78b85SJerome Forissier 		/* Don't change file location */
2836a8a78b85SJerome Forissier 		newaddr = fh->fat_entry.start_address;
2837a8a78b85SJerome Forissier 	}
2838a8a78b85SJerome Forissier 
2839a8a78b85SJerome Forissier 	/* fh->pos is unchanged */
2840a8a78b85SJerome Forissier 	fh->fat_entry.data_size = newsize;
2841a8a78b85SJerome Forissier 	fh->fat_entry.start_address = newaddr;
2842e92de4caSJerome Forissier 	res = write_fat_entry(fh);
2843a8a78b85SJerome Forissier 
2844a8a78b85SJerome Forissier out:
2845a7e22cf5SLijianhui (Airbak) 	mutex_unlock(&rpmb_mutex);
2846a8a78b85SJerome Forissier 	if (pool_result)
2847a8a78b85SJerome Forissier 		tee_mm_final(&p);
2848a8a78b85SJerome Forissier 	if (newbuf)
2849a8a78b85SJerome Forissier 		free(newbuf);
2850a8a78b85SJerome Forissier 
2851b86c18ecSJens Wiklander 	return res;
2852a8a78b85SJerome Forissier }
2853a8a78b85SJerome Forissier 
rpmb_fs_dir_free(struct tee_fs_dir * dir)28540c96a71dSJerome Forissier static void rpmb_fs_dir_free(struct tee_fs_dir *dir)
2855a8a78b85SJerome Forissier {
2856a8a78b85SJerome Forissier 	struct tee_rpmb_fs_dirent *e;
2857a8a78b85SJerome Forissier 
2858a8a78b85SJerome Forissier 	if (!dir)
2859a8a78b85SJerome Forissier 		return;
2860a8a78b85SJerome Forissier 
2861a8a78b85SJerome Forissier 	free(dir->current);
2862a8a78b85SJerome Forissier 
2863a8a78b85SJerome Forissier 	while ((e = SIMPLEQ_FIRST(&dir->next))) {
2864a8a78b85SJerome Forissier 		SIMPLEQ_REMOVE_HEAD(&dir->next, link);
2865a8a78b85SJerome Forissier 		free(e);
2866a8a78b85SJerome Forissier 	}
2867a8a78b85SJerome Forissier }
2868a8a78b85SJerome Forissier 
rpmb_fs_dir_populate(const char * path,struct tee_fs_dir * dir)28690c96a71dSJerome Forissier static TEE_Result rpmb_fs_dir_populate(const char *path,
28700c96a71dSJerome Forissier 				       struct tee_fs_dir *dir)
2871a8a78b85SJerome Forissier {
2872a8a78b85SJerome Forissier 	struct tee_rpmb_fs_dirent *current = NULL;
28735f68d784SManuel Huber 	struct rpmb_fat_entry *fe = NULL;
2874a8a78b85SJerome Forissier 	uint32_t fat_address;
2875a8a78b85SJerome Forissier 	uint32_t filelen;
2876a8a78b85SJerome Forissier 	char *filename;
2877a8a78b85SJerome Forissier 	bool matched;
2878a8a78b85SJerome Forissier 	struct tee_rpmb_fs_dirent *next = NULL;
2879a8a78b85SJerome Forissier 	uint32_t pathlen;
2880a8a78b85SJerome Forissier 	TEE_Result res = TEE_ERROR_GENERIC;
2881a8a78b85SJerome Forissier 	char temp;
2882a8a78b85SJerome Forissier 
2883a7e22cf5SLijianhui (Airbak) 	mutex_lock(&rpmb_mutex);
2884a7e22cf5SLijianhui (Airbak) 
28855f68d784SManuel Huber 	res = fat_entry_dir_init();
28865f68d784SManuel Huber 	if (res)
2887a8a78b85SJerome Forissier 		goto out;
2888a8a78b85SJerome Forissier 
2889a8a78b85SJerome Forissier 	pathlen = strlen(path);
2890a8a78b85SJerome Forissier 
28915f68d784SManuel Huber 	while (true) {
28925f68d784SManuel Huber 		res = fat_entry_dir_get_next(&fe, &fat_address);
28935f68d784SManuel Huber 		if (res || !fe)
28945f68d784SManuel Huber 			break;
28955f68d784SManuel Huber 
28965f68d784SManuel Huber 		filename = fe->filename;
28975f68d784SManuel Huber 		if (fe->flags & FILE_IS_ACTIVE) {
2898a8a78b85SJerome Forissier 			matched = false;
2899a8a78b85SJerome Forissier 			filelen = strlen(filename);
2900a8a78b85SJerome Forissier 			if (filelen > pathlen) {
2901a8a78b85SJerome Forissier 				temp = filename[pathlen];
2902a8a78b85SJerome Forissier 				filename[pathlen] = '\0';
2903a8a78b85SJerome Forissier 				if (strcmp(filename, path) == 0)
2904a8a78b85SJerome Forissier 					matched = true;
2905a8a78b85SJerome Forissier 
2906a8a78b85SJerome Forissier 				filename[pathlen] = temp;
2907a8a78b85SJerome Forissier 			}
2908a8a78b85SJerome Forissier 
2909a8a78b85SJerome Forissier 			if (matched) {
2910a8a78b85SJerome Forissier 				next = malloc(sizeof(*next));
2911a8a78b85SJerome Forissier 				if (!next) {
2912a8a78b85SJerome Forissier 					res = TEE_ERROR_OUT_OF_MEMORY;
2913a8a78b85SJerome Forissier 					goto out;
2914a8a78b85SJerome Forissier 				}
2915a8a78b85SJerome Forissier 
29165f68d784SManuel Huber 				next->entry.oidlen = tee_hs2b((uint8_t *)
29175f68d784SManuel Huber 						&filename[pathlen],
2918b2215adfSJens Wiklander 						next->entry.oid,
2919b2215adfSJens Wiklander 						filelen - pathlen,
2920b2215adfSJens Wiklander 						sizeof(next->entry.oid));
2921b2215adfSJens Wiklander 				if (next->entry.oidlen) {
2922b2215adfSJens Wiklander 					SIMPLEQ_INSERT_TAIL(&dir->next,
2923b2215adfSJens Wiklander 							    next, link);
2924a8a78b85SJerome Forissier 					current = next;
2925b2215adfSJens Wiklander 				} else {
2926b2215adfSJens Wiklander 					free(next);
2927b2215adfSJens Wiklander 					next = NULL;
2928b2215adfSJens Wiklander 				}
29295f68d784SManuel Huber 			}
2930a8a78b85SJerome Forissier 		}
2931a8a78b85SJerome Forissier 	}
2932a8a78b85SJerome Forissier 
29335f68d784SManuel Huber 	if (res)
29345f68d784SManuel Huber 		goto out;
2935a8a78b85SJerome Forissier 
2936b86c18ecSJens Wiklander 	if (current)
2937a8a78b85SJerome Forissier 		res = TEE_SUCCESS;
2938b86c18ecSJens Wiklander 	else
2939b86c18ecSJens Wiklander 		res = TEE_ERROR_ITEM_NOT_FOUND; /* No directories were found. */
2940a8a78b85SJerome Forissier 
2941a8a78b85SJerome Forissier out:
29425f68d784SManuel Huber 	fat_entry_dir_deinit();
2943b565152eSEtienne Carriere 	mutex_unlock(&rpmb_mutex);
29445f68d784SManuel Huber 	if (res)
29450c96a71dSJerome Forissier 		rpmb_fs_dir_free(dir);
2946a8a78b85SJerome Forissier 
2947a8a78b85SJerome Forissier 	return res;
2948a8a78b85SJerome Forissier }
2949a8a78b85SJerome Forissier 
rpmb_fs_opendir(const TEE_UUID * uuid,struct tee_fs_dir ** dir)2950b2215adfSJens Wiklander static TEE_Result rpmb_fs_opendir(const TEE_UUID *uuid, struct tee_fs_dir **dir)
2951a8a78b85SJerome Forissier {
2952a8a78b85SJerome Forissier 	uint32_t len;
2953a8a78b85SJerome Forissier 	char path_local[TEE_RPMB_FS_FILENAME_LENGTH];
2954a8a78b85SJerome Forissier 	TEE_Result res = TEE_ERROR_GENERIC;
29550c96a71dSJerome Forissier 	struct tee_fs_dir *rpmb_dir = NULL;
2956a8a78b85SJerome Forissier 
2957b2215adfSJens Wiklander 	if (!uuid || !dir) {
2958a8a78b85SJerome Forissier 		res = TEE_ERROR_BAD_PARAMETERS;
2959a8a78b85SJerome Forissier 		goto out;
2960a8a78b85SJerome Forissier 	}
2961a8a78b85SJerome Forissier 
2962a8a78b85SJerome Forissier 	memset(path_local, 0, sizeof(path_local));
296334ab2802SJerome Forissier 	if (create_dirname(path_local, sizeof(path_local) - 1, uuid)) {
2964b2215adfSJens Wiklander 		res = TEE_ERROR_BAD_PARAMETERS;
2965b2215adfSJens Wiklander 		goto out;
2966b2215adfSJens Wiklander 	}
2967b2215adfSJens Wiklander 	len = strlen(path_local);
2968a8a78b85SJerome Forissier 
2969a8a78b85SJerome Forissier 	/* Add a slash to correctly match the full directory name. */
2970a8a78b85SJerome Forissier 	if (path_local[len - 1] != '/')
2971a8a78b85SJerome Forissier 		path_local[len] = '/';
2972a8a78b85SJerome Forissier 
29730c96a71dSJerome Forissier 	rpmb_dir = calloc(1, sizeof(*rpmb_dir));
2974a8a78b85SJerome Forissier 	if (!rpmb_dir) {
2975a8a78b85SJerome Forissier 		res = TEE_ERROR_OUT_OF_MEMORY;
2976a8a78b85SJerome Forissier 		goto out;
2977a8a78b85SJerome Forissier 	}
2978a8a78b85SJerome Forissier 	SIMPLEQ_INIT(&rpmb_dir->next);
2979a8a78b85SJerome Forissier 
29800c96a71dSJerome Forissier 	res = rpmb_fs_dir_populate(path_local, rpmb_dir);
2981a8a78b85SJerome Forissier 	if (res != TEE_SUCCESS) {
2982a8a78b85SJerome Forissier 		free(rpmb_dir);
2983a8a78b85SJerome Forissier 		rpmb_dir = NULL;
2984a8a78b85SJerome Forissier 		goto out;
2985a8a78b85SJerome Forissier 	}
2986a8a78b85SJerome Forissier 
2987a8a78b85SJerome Forissier 	*dir = rpmb_dir;
2988a8a78b85SJerome Forissier 
2989a8a78b85SJerome Forissier out:
2990a8a78b85SJerome Forissier 	return res;
2991a8a78b85SJerome Forissier }
2992a8a78b85SJerome Forissier 
rpmb_fs_readdir(struct tee_fs_dir * dir,struct tee_fs_dirent ** ent)2993b86c18ecSJens Wiklander static TEE_Result rpmb_fs_readdir(struct tee_fs_dir *dir,
2994b86c18ecSJens Wiklander 				  struct tee_fs_dirent **ent)
2995a8a78b85SJerome Forissier {
2996a8a78b85SJerome Forissier 	if (!dir)
2997b86c18ecSJens Wiklander 		return TEE_ERROR_GENERIC;
2998a8a78b85SJerome Forissier 
2999a8a78b85SJerome Forissier 	free(dir->current);
3000a8a78b85SJerome Forissier 
3001a8a78b85SJerome Forissier 	dir->current = SIMPLEQ_FIRST(&dir->next);
3002a8a78b85SJerome Forissier 	if (!dir->current)
3003b86c18ecSJens Wiklander 		return TEE_ERROR_ITEM_NOT_FOUND;
3004a8a78b85SJerome Forissier 
3005a8a78b85SJerome Forissier 	SIMPLEQ_REMOVE_HEAD(&dir->next, link);
3006a8a78b85SJerome Forissier 
3007b86c18ecSJens Wiklander 	*ent = &dir->current->entry;
3008b86c18ecSJens Wiklander 	return TEE_SUCCESS;
3009a8a78b85SJerome Forissier }
3010a8a78b85SJerome Forissier 
rpmb_fs_closedir(struct tee_fs_dir * dir)3011b86c18ecSJens Wiklander static void rpmb_fs_closedir(struct tee_fs_dir *dir)
3012a8a78b85SJerome Forissier {
3013b86c18ecSJens Wiklander 	if (dir) {
30140c96a71dSJerome Forissier 		rpmb_fs_dir_free(dir);
3015a8a78b85SJerome Forissier 		free(dir);
3016b86c18ecSJens Wiklander 	}
3017a8a78b85SJerome Forissier }
3018a8a78b85SJerome Forissier 
rpmb_fs_open(struct tee_pobj * po,size_t * size,struct tee_file_handle ** ret_fh)3019d5fe340fSJens Wiklander static TEE_Result rpmb_fs_open(struct tee_pobj *po, size_t *size,
3020078f18f8SJens Wiklander 			       struct tee_file_handle **ret_fh)
3021c3e8a2d9SJerome Forissier {
302273ea1cdeSJens Wiklander 	TEE_Result res;
3023078f18f8SJens Wiklander 	struct rpmb_file_handle *fh = alloc_file_handle(po, po->temporary);
3024078f18f8SJens Wiklander 
3025078f18f8SJens Wiklander 	if (!fh)
3026078f18f8SJens Wiklander 		return TEE_ERROR_OUT_OF_MEMORY;
302773ea1cdeSJens Wiklander 
302873ea1cdeSJens Wiklander 	mutex_lock(&rpmb_mutex);
3029d5fe340fSJens Wiklander 
3030078f18f8SJens Wiklander 	res = rpmb_fs_open_internal(fh, &po->uuid, false);
3031078f18f8SJens Wiklander 	if (!res && size)
3032078f18f8SJens Wiklander 		*size = fh->fat_entry.data_size;
3033078f18f8SJens Wiklander 
303473ea1cdeSJens Wiklander 	mutex_unlock(&rpmb_mutex);
303573ea1cdeSJens Wiklander 
3036078f18f8SJens Wiklander 	if (res)
3037078f18f8SJens Wiklander 		free(fh);
3038078f18f8SJens Wiklander 	else
3039078f18f8SJens Wiklander 		*ret_fh = (struct tee_file_handle *)fh;
3040078f18f8SJens Wiklander 
304173ea1cdeSJens Wiklander 	return res;
3042c3e8a2d9SJerome Forissier }
3043c3e8a2d9SJerome Forissier 
rpmb_fs_create(struct tee_pobj * po,bool overwrite,const void * head,size_t head_size,const void * attr,size_t attr_size,const void * data_core,const void * data_user,size_t data_size,struct tee_file_handle ** ret_fh)304473ea1cdeSJens Wiklander static TEE_Result rpmb_fs_create(struct tee_pobj *po, bool overwrite,
304573ea1cdeSJens Wiklander 				 const void *head, size_t head_size,
304673ea1cdeSJens Wiklander 				 const void *attr, size_t attr_size,
3047b2284b11SJens Wiklander 				 const void *data_core, const void *data_user,
3048b2284b11SJens Wiklander 				 size_t data_size,
3049078f18f8SJens Wiklander 				 struct tee_file_handle **ret_fh)
3050b86c18ecSJens Wiklander {
305173ea1cdeSJens Wiklander 	TEE_Result res;
305273ea1cdeSJens Wiklander 	size_t pos = 0;
3053078f18f8SJens Wiklander 	struct rpmb_file_handle *fh = alloc_file_handle(po, po->temporary);
305473ea1cdeSJens Wiklander 
3055b2284b11SJens Wiklander 	/* One of data_core and data_user must be NULL */
3056b2284b11SJens Wiklander 	assert(!data_core || !data_user);
3057b2284b11SJens Wiklander 
3058078f18f8SJens Wiklander 	if (!fh)
3059078f18f8SJens Wiklander 		return TEE_ERROR_OUT_OF_MEMORY;
3060078f18f8SJens Wiklander 
306173ea1cdeSJens Wiklander 	mutex_lock(&rpmb_mutex);
3062078f18f8SJens Wiklander 	res = rpmb_fs_open_internal(fh, &po->uuid, true);
306373ea1cdeSJens Wiklander 	if (res)
306473ea1cdeSJens Wiklander 		goto out;
306573ea1cdeSJens Wiklander 
306673ea1cdeSJens Wiklander 	if (head && head_size) {
3067078f18f8SJens Wiklander 		res = rpmb_fs_write_primitive(fh, pos, head, head_size);
306873ea1cdeSJens Wiklander 		if (res)
306973ea1cdeSJens Wiklander 			goto out;
307073ea1cdeSJens Wiklander 		pos += head_size;
307173ea1cdeSJens Wiklander 	}
307273ea1cdeSJens Wiklander 
307373ea1cdeSJens Wiklander 	if (attr && attr_size) {
3074078f18f8SJens Wiklander 		res = rpmb_fs_write_primitive(fh, pos, attr, attr_size);
307573ea1cdeSJens Wiklander 		if (res)
307673ea1cdeSJens Wiklander 			goto out;
307773ea1cdeSJens Wiklander 		pos += attr_size;
307873ea1cdeSJens Wiklander 	}
307973ea1cdeSJens Wiklander 
3080b2284b11SJens Wiklander 	if (data_size) {
3081b2284b11SJens Wiklander 		if (data_core) {
3082b2284b11SJens Wiklander 			res = rpmb_fs_write_primitive(fh, pos, data_core,
3083b2284b11SJens Wiklander 						      data_size);
308473ea1cdeSJens Wiklander 			if (res)
308573ea1cdeSJens Wiklander 				goto out;
3086b2284b11SJens Wiklander 		} else if (data_user) {
3087b2284b11SJens Wiklander 			uint32_t f = TEE_MEMORY_ACCESS_READ |
3088b2284b11SJens Wiklander 				     TEE_MEMORY_ACCESS_ANY_OWNER;
3089b2284b11SJens Wiklander 
3090b2284b11SJens Wiklander 			res = check_user_access(f, data_user, data_size);
3091b2284b11SJens Wiklander 			if (res)
3092b2284b11SJens Wiklander 				goto out;
3093b2284b11SJens Wiklander 			enter_user_access();
3094b2284b11SJens Wiklander 			res = rpmb_fs_write_primitive(fh, pos, data_user,
3095b2284b11SJens Wiklander 						      data_size);
3096b2284b11SJens Wiklander 			exit_user_access();
3097b2284b11SJens Wiklander 			if (res)
3098b2284b11SJens Wiklander 				goto out;
3099b2284b11SJens Wiklander 		}
310073ea1cdeSJens Wiklander 	}
310173ea1cdeSJens Wiklander 
310273ea1cdeSJens Wiklander 	if (po->temporary) {
310373ea1cdeSJens Wiklander 		/*
310473ea1cdeSJens Wiklander 		 * If it's a temporary filename (which it normally is)
310573ea1cdeSJens Wiklander 		 * rename into the final filename now that the file is
310673ea1cdeSJens Wiklander 		 * fully initialized.
310773ea1cdeSJens Wiklander 		 */
310873ea1cdeSJens Wiklander 		po->temporary = false;
310973ea1cdeSJens Wiklander 		res = rpmb_fs_rename_internal(po, NULL, overwrite);
311073ea1cdeSJens Wiklander 		if (res) {
311173ea1cdeSJens Wiklander 			po->temporary = true;
311273ea1cdeSJens Wiklander 			goto out;
311373ea1cdeSJens Wiklander 		}
311473ea1cdeSJens Wiklander 		/* Update file handle after rename. */
311534ab2802SJerome Forissier 		create_filename(fh->filename, sizeof(fh->filename), po, false);
311673ea1cdeSJens Wiklander 	}
311773ea1cdeSJens Wiklander 
311873ea1cdeSJens Wiklander out:
3119078f18f8SJens Wiklander 	if (res) {
3120078f18f8SJens Wiklander 		rpmb_fs_remove_internal(fh);
3121078f18f8SJens Wiklander 		free(fh);
3122078f18f8SJens Wiklander 	} else {
3123078f18f8SJens Wiklander 		*ret_fh = (struct tee_file_handle *)fh;
312473ea1cdeSJens Wiklander 	}
312573ea1cdeSJens Wiklander 	mutex_unlock(&rpmb_mutex);
312673ea1cdeSJens Wiklander 
312773ea1cdeSJens Wiklander 	return res;
31280c96a71dSJerome Forissier }
3129c3e8a2d9SJerome Forissier 
3130b44708c1SJerome Forissier const struct tee_file_operations rpmb_fs_ops = {
3131b0311ad8SJens Wiklander 	.open = rpmb_fs_open,
3132b0311ad8SJens Wiklander 	.create = rpmb_fs_create,
3133b0311ad8SJens Wiklander 	.close = rpmb_fs_close,
3134b0311ad8SJens Wiklander 	.read = rpmb_fs_read,
3135b0311ad8SJens Wiklander 	.write = rpmb_fs_write,
3136b0311ad8SJens Wiklander 	.truncate = rpmb_fs_truncate,
3137b0311ad8SJens Wiklander 	.rename = rpmb_fs_rename,
3138b0311ad8SJens Wiklander 	.remove = rpmb_fs_remove,
3139b0311ad8SJens Wiklander 	.opendir = rpmb_fs_opendir,
3140b0311ad8SJens Wiklander 	.closedir = rpmb_fs_closedir,
3141b0311ad8SJens Wiklander 	.readdir = rpmb_fs_readdir,
3142c3e8a2d9SJerome Forissier };
3143078f18f8SJens Wiklander 
tee_rpmb_fs_raw_open(const char * fname,bool create,struct tee_file_handle ** ret_fh)3144078f18f8SJens Wiklander TEE_Result tee_rpmb_fs_raw_open(const char *fname, bool create,
3145078f18f8SJens Wiklander 				struct tee_file_handle **ret_fh)
3146078f18f8SJens Wiklander {
3147078f18f8SJens Wiklander 	TEE_Result res;
3148078f18f8SJens Wiklander 	struct rpmb_file_handle *fh = calloc(1, sizeof(*fh));
3149b399f70bSJens Wiklander 	static const TEE_UUID uuid = { 0 };
3150078f18f8SJens Wiklander 
3151078f18f8SJens Wiklander 	if (!fh)
3152078f18f8SJens Wiklander 		return TEE_ERROR_OUT_OF_MEMORY;
3153078f18f8SJens Wiklander 
3154078f18f8SJens Wiklander 	snprintf(fh->filename, sizeof(fh->filename), "/%s", fname);
3155078f18f8SJens Wiklander 
3156078f18f8SJens Wiklander 	mutex_lock(&rpmb_mutex);
3157078f18f8SJens Wiklander 
3158078f18f8SJens Wiklander 	res = rpmb_fs_open_internal(fh, &uuid, create);
3159078f18f8SJens Wiklander 
3160078f18f8SJens Wiklander 	mutex_unlock(&rpmb_mutex);
3161078f18f8SJens Wiklander 
3162078f18f8SJens Wiklander 	if (res) {
3163078f18f8SJens Wiklander 		if (create)
3164078f18f8SJens Wiklander 			rpmb_fs_remove_internal(fh);
3165078f18f8SJens Wiklander 		free(fh);
3166078f18f8SJens Wiklander 	} else {
3167078f18f8SJens Wiklander 		*ret_fh = (struct tee_file_handle *)fh;
3168078f18f8SJens Wiklander 	}
3169078f18f8SJens Wiklander 
3170078f18f8SJens Wiklander 	return res;
3171078f18f8SJens Wiklander }
3172b1042535SRouven Czerwinski 
plat_rpmb_key_is_ready(void)3173b1042535SRouven Czerwinski bool __weak plat_rpmb_key_is_ready(void)
3174b1042535SRouven Czerwinski {
3175b1042535SRouven Czerwinski 	return true;
3176b1042535SRouven Czerwinski }
3177*dc2cf47aSEtienne Carriere 
3178*dc2cf47aSEtienne Carriere #ifdef CFG_WITH_STATS
rpmb_mem_stats(struct pta_stats_alloc * stats,bool reset)3179*dc2cf47aSEtienne Carriere TEE_Result rpmb_mem_stats(struct pta_stats_alloc *stats, bool reset)
3180*dc2cf47aSEtienne Carriere {
3181*dc2cf47aSEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
3182*dc2cf47aSEtienne Carriere 	struct rpmb_fat_entry *fe = NULL;
3183*dc2cf47aSEtienne Carriere 	tee_mm_entry_t *mm = NULL;
3184*dc2cf47aSEtienne Carriere 	tee_mm_pool_t pool = { };
3185*dc2cf47aSEtienne Carriere 	bool pool_result = false;
3186*dc2cf47aSEtienne Carriere 	paddr_size_t pool_sz = 0;
3187*dc2cf47aSEtienne Carriere 
3188*dc2cf47aSEtienne Carriere 	mutex_lock(&rpmb_mutex);
3189*dc2cf47aSEtienne Carriere 
3190*dc2cf47aSEtienne Carriere 	res = rpmb_fs_setup();
3191*dc2cf47aSEtienne Carriere 	if (res)
3192*dc2cf47aSEtienne Carriere 		goto out;
3193*dc2cf47aSEtienne Carriere 
3194*dc2cf47aSEtienne Carriere 	pool_sz = fs_par->max_rpmb_address - RPMB_STORAGE_START_ADDRESS;
3195*dc2cf47aSEtienne Carriere 	pool_result = tee_mm_init(&pool, RPMB_STORAGE_START_ADDRESS,
3196*dc2cf47aSEtienne Carriere 				  pool_sz, RPMB_BLOCK_SIZE_SHIFT,
3197*dc2cf47aSEtienne Carriere 				  TEE_MM_POOL_HI_ALLOC);
3198*dc2cf47aSEtienne Carriere 	if (!pool_result) {
3199*dc2cf47aSEtienne Carriere 		res = TEE_ERROR_OUT_OF_MEMORY;
3200*dc2cf47aSEtienne Carriere 		goto out;
3201*dc2cf47aSEtienne Carriere 	}
3202*dc2cf47aSEtienne Carriere 
3203*dc2cf47aSEtienne Carriere 	res = fat_entry_dir_init();
3204*dc2cf47aSEtienne Carriere 	if (res)
3205*dc2cf47aSEtienne Carriere 		goto out;
3206*dc2cf47aSEtienne Carriere 
3207*dc2cf47aSEtienne Carriere 	/*
3208*dc2cf47aSEtienne Carriere 	 * The pool is used to represent the current RPMB layout. To find
3209*dc2cf47aSEtienne Carriere 	 * a slot for the file tee_mm_alloc is called on the pool. Thus
3210*dc2cf47aSEtienne Carriere 	 * if it is not NULL the entire FAT must be traversed to fill in
3211*dc2cf47aSEtienne Carriere 	 * the pool.
3212*dc2cf47aSEtienne Carriere 	 */
3213*dc2cf47aSEtienne Carriere 	while (true) {
3214*dc2cf47aSEtienne Carriere 		res = fat_entry_dir_get_next(&fe, NULL);
3215*dc2cf47aSEtienne Carriere 		if (res || !fe)
3216*dc2cf47aSEtienne Carriere 			break;
3217*dc2cf47aSEtienne Carriere 
3218*dc2cf47aSEtienne Carriere 		if (!(fe->flags & FILE_IS_ACTIVE) || !fe->data_size)
3219*dc2cf47aSEtienne Carriere 			continue;
3220*dc2cf47aSEtienne Carriere 
3221*dc2cf47aSEtienne Carriere 		mm = tee_mm_alloc2(&pool, fe->start_address, fe->data_size);
3222*dc2cf47aSEtienne Carriere 		if (!mm) {
3223*dc2cf47aSEtienne Carriere 			res = TEE_ERROR_GENERIC;
3224*dc2cf47aSEtienne Carriere 			break;
3225*dc2cf47aSEtienne Carriere 		}
3226*dc2cf47aSEtienne Carriere 	}
3227*dc2cf47aSEtienne Carriere 
3228*dc2cf47aSEtienne Carriere 	fat_entry_dir_deinit();
3229*dc2cf47aSEtienne Carriere 
3230*dc2cf47aSEtienne Carriere out:
3231*dc2cf47aSEtienne Carriere 	mutex_unlock(&rpmb_mutex);
3232*dc2cf47aSEtienne Carriere 
3233*dc2cf47aSEtienne Carriere 	if (!res)
3234*dc2cf47aSEtienne Carriere 		tee_mm_get_pool_stats(&pool, stats, reset);
3235*dc2cf47aSEtienne Carriere 
3236*dc2cf47aSEtienne Carriere 	tee_mm_final(&pool);
3237*dc2cf47aSEtienne Carriere 
3238*dc2cf47aSEtienne Carriere 	return res;
3239*dc2cf47aSEtienne Carriere }
3240*dc2cf47aSEtienne Carriere #endif /*CFG_WITH_STATS*/
3241