1 /*
2  * Copyright (c) 2018-2023, Arm Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 
8 #include "critical_section.h"
9 #include "internal_status_code.h"
10 #include "spm.h"
11 #include "tfm_pools.h"
12 #include "load/service_defs.h"
13 
14 #if !(defined CONFIG_TFM_CONN_HANDLE_MAX_NUM) || (CONFIG_TFM_CONN_HANDLE_MAX_NUM == 0)
15 #error "CONFIG_TFM_CONN_HANDLE_MAX_NUM must be defined and not zero."
16 #endif
17 
18 /* Pools */
19 TFM_POOL_DECLARE(connection_pool, sizeof(struct connection_t),
20                  CONFIG_TFM_CONN_HANDLE_MAX_NUM);
21 
22 /*********************** Connection handle conversion APIs *******************/
23 
24 #define CONVERSION_FACTOR_BITOFFSET    3
25 #define CONVERSION_FACTOR_VALUE        (1 << CONVERSION_FACTOR_BITOFFSET)
26 /* Set 32 as the maximum */
27 #define CONVERSION_FACTOR_VALUE_MAX    0x20
28 
29 #if CONVERSION_FACTOR_VALUE > CONVERSION_FACTOR_VALUE_MAX
30 #error "CONVERSION FACTOR OUT OF RANGE"
31 #endif
32 
33 static uint32_t loop_index;
34 
35 /*
36  * A connection instance connection_t allocated inside SPM is actually a memory
37  * address among the connection pool. Return this connection to the client directly
38  * exposes information of secure memory address. In this case, converting the
39  * connection into another handle value does not represent the memory address
40  * to avoid exposing secure memory directly to clients.
41  *
42  * This function converts the connection instance into another value by scaling
43  * the connection in pool offset, the converted value is named as a user handle.
44  *
45  * The formula:
46  *  handle =      (p_connection - POOL_START) * CONVERSION_FACTOR_VALUE +
47  *                CLIENT_HANDLE_VALUE_MIN + loop_index
48  * where:
49  *  CONVERSION_FACTOR_VALUE = 1 << CONVERSION_FACTOR_BITOFFSET, and should not
50  *  exceed CONVERSION_FACTOR_VALUE_MAX.
51  *
52  *  p_connection    in RANGE[POOL_START, POOL_END]
53  *  handle          in RANGE[CLIENT_HANDLE_VALUE_MIN, 0x3FFFFFFF]
54  *  loop_index      in RANGE[0, CONVERSION_FACTOR_VALUE - 1]
55  *
56  *  note:
57  *  loop_index is used to promise same connection instance is converted into
58  *  different user handles in short time.
59  */
connection_to_handle(struct connection_t * p_connection)60 psa_handle_t connection_to_handle(struct connection_t *p_connection)
61 {
62     psa_handle_t handle;
63 
64     loop_index = (loop_index + 1) % CONVERSION_FACTOR_VALUE;
65     handle = (psa_handle_t)((((uintptr_t)p_connection -
66                   (uintptr_t)connection_pool) << CONVERSION_FACTOR_BITOFFSET) +
67                   CLIENT_HANDLE_VALUE_MIN + loop_index);
68 
69     return handle;
70 }
71 
72 /*
73  * This function converts a user handle into a corresponded connection instance.
74  * The converted value is validated before returning, an invalid handle instance
75  * is returned as NULL.
76  *
77  * The formula:
78  *  p_connection =    ((handle - CLIENT_HANDLE_VALUE_MIN) /
79  *                    CONVERSION_FACTOR_VALUE) + POOL_START
80  * where:
81  *  CONVERSION_FACTOR_VALUE = 1 << CONVERSION_FACTOR_BITOFFSET, and should not
82  *  exceed CONVERSION_FACTOR_VALUE_MAX.
83  *
84  *  p_connection    in RANGE[POOL_START, POOL_END]
85  *  handle          in RANGE[CLIENT_HANDLE_VALUE_MIN, 0x3FFFFFFF]
86  *  loop_index      in RANGE[0, CONVERSION_FACTOR_VALUE - 1]
87  */
handle_to_connection(psa_handle_t handle)88 struct connection_t *handle_to_connection(psa_handle_t handle)
89 {
90     struct connection_t *p_connection;
91 
92     if (handle == PSA_NULL_HANDLE) {
93         return NULL;
94     }
95 
96     p_connection = (struct connection_t *)((((uintptr_t)handle -
97                     CLIENT_HANDLE_VALUE_MIN) >> CONVERSION_FACTOR_BITOFFSET) +
98                     (uintptr_t)connection_pool);
99 
100     return p_connection;
101 }
102 
103 /* Service handle management functions */
spm_init_connection_space(void)104 void spm_init_connection_space(void)
105 {
106     if (tfm_pool_init(connection_pool,
107                       POOL_BUFFER_SIZE(connection_pool),
108                       sizeof(struct connection_t),
109                       CONFIG_TFM_CONN_HANDLE_MAX_NUM) != PSA_SUCCESS) {
110         tfm_core_panic();
111     }
112 }
113 
spm_allocate_connection(void)114 struct connection_t *spm_allocate_connection(void)
115 {
116     /* Get buffer for handle list structure from handle pool */
117     return (struct connection_t *)tfm_pool_alloc(connection_pool);
118 }
119 
spm_validate_connection(const struct connection_t * p_connection)120 psa_status_t spm_validate_connection(const struct connection_t *p_connection)
121 {
122     /* Check the handle address is valid */
123     if (is_valid_chunk_data_in_pool(connection_pool,
124                                     (uint8_t *)p_connection) != true) {
125         return SPM_ERROR_GENERIC;
126     }
127 
128     return PSA_SUCCESS;
129 }
130 
spm_free_connection(struct connection_t * p_connection)131 void spm_free_connection(struct connection_t *p_connection)
132 {
133     struct critical_section_t cs_assert = CRITICAL_SECTION_STATIC_INIT;
134 
135     SPM_ASSERT(p_connection != NULL);
136 
137     CRITICAL_SECTION_ENTER(cs_assert);
138     /* Back handle buffer to pool */
139     tfm_pool_free(connection_pool, p_connection);
140     CRITICAL_SECTION_LEAVE(cs_assert);
141 }
142