1 /*
2 * Copyright (c) 2022, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include "rse_comms_atu.h"
9 #include "atu_rse_drv.h"
10 #include "tfm_spm_log.h"
11 #include "device_definition.h"
12 #include "platform_base_address.h"
13
14 struct comms_atu_region_params_t {
15 uint32_t log_addr;
16 uint64_t phys_addr;
17 uint32_t size;
18 uint8_t region;
19 uint32_t ref_count;
20 };
21
22 /* ATU config */
23 static struct comms_atu_region_params_t atu_regions[RSE_COMMS_ATU_REGION_AM] = {0};
24
round_down(uint64_t num,uint64_t boundary)25 static inline uint64_t round_down(uint64_t num, uint64_t boundary)
26 {
27 return num - (num % boundary);
28 }
29
comms_atu_add_region_to_set(comms_atu_region_set_t * set,uint8_t region)30 enum tfm_plat_err_t comms_atu_add_region_to_set(comms_atu_region_set_t *set,
31 uint8_t region)
32 {
33 if (region >= RSE_COMMS_ATU_REGION_AM) {
34 return TFM_PLAT_ERR_INVALID_INPUT;
35 }
36
37 set->ref_counts[region]++;
38
39 return TFM_PLAT_ERR_SUCCESS;
40 }
41
get_region_idx_from_host_buf(uint64_t host_addr,uint32_t size,uint32_t * region_idx)42 static enum tfm_plat_err_t get_region_idx_from_host_buf(uint64_t host_addr,
43 uint32_t size,
44 uint32_t *region_idx)
45 {
46 uint32_t idx;
47 struct comms_atu_region_params_t *region;
48
49 for (idx = 0; idx < RSE_COMMS_ATU_REGION_AM; idx++) {
50 region = &atu_regions[idx];
51
52 if (atu_regions[idx].ref_count > 0 &&
53 host_addr >= region->phys_addr &&
54 host_addr + size <= region->phys_addr + region->size) {
55 *region_idx = idx;
56 return TFM_PLAT_ERR_SUCCESS;
57 }
58 }
59
60 return TFM_PLAT_ERR_INVALID_INPUT;
61 }
62
comms_atu_get_rse_ptr_from_host_addr(uint8_t region,uint64_t host_addr,void ** rse_ptr)63 enum tfm_plat_err_t comms_atu_get_rse_ptr_from_host_addr(uint8_t region,
64 uint64_t host_addr,
65 void **rse_ptr)
66 {
67 struct comms_atu_region_params_t *region_params;
68
69 if (region >= RSE_COMMS_ATU_REGION_AM) {
70 return TFM_PLAT_ERR_INVALID_INPUT;
71 }
72
73
74 region_params = &atu_regions[region];
75 *rse_ptr = (uint8_t *)((uint32_t)(host_addr - region_params->phys_addr)
76 + region_params->log_addr);
77
78 return TFM_PLAT_ERR_SUCCESS;
79 }
80
get_free_region_idx(uint32_t * region_idx)81 static int get_free_region_idx(uint32_t *region_idx) {
82 uint32_t idx;
83
84 for (idx = 0; idx <= RSE_COMMS_ATU_REGION_AM; idx++) {
85 if (atu_regions[idx].ref_count == 0) {
86 *region_idx = idx;
87 return TFM_PLAT_ERR_SUCCESS;
88 }
89 }
90
91 return TFM_PLAT_ERR_MAX_VALUE;
92 }
93
setup_region_for_host_buf(uint64_t host_addr,uint32_t size,uint32_t region_idx)94 static enum tfm_plat_err_t setup_region_for_host_buf(uint64_t host_addr,
95 uint32_t size,
96 uint32_t region_idx)
97 {
98 struct comms_atu_region_params_t *region_params;
99 int32_t atu_err;
100 uint64_t host_buf_end = host_addr + size;
101
102 if (host_buf_end <= host_addr) {
103 return TFM_PLAT_ERR_INVALID_INPUT;
104 }
105
106 region_params = &atu_regions[region_idx];
107 region_params->region = region_idx + RSE_COMMS_ATU_REGION_MIN;
108
109 region_params->log_addr = HOST_COMMS_MAPPABLE_BASE_S
110 + (RSE_COMMS_ATU_REGION_SIZE * region_idx);
111
112 region_params->phys_addr = round_down(host_addr, RSE_COMMS_ATU_PAGE_SIZE);
113 region_params->size = RSE_COMMS_ATU_REGION_SIZE;
114
115 if (host_buf_end > region_params->phys_addr + region_params->size) {
116 return TFM_PLAT_ERR_INVALID_INPUT;
117 }
118
119 atu_err = atu_initialize_region(&ATU_DEV_S, region_params->region,
120 region_params->log_addr,
121 region_params->phys_addr,
122 region_params->size);
123 if (atu_err) {
124 return TFM_PLAT_ERR_SYSTEM_ERR;
125 }
126
127 SPMLOG_DBGMSGVAL("[COMMS ATU] Mapping new region: ", region_idx);
128 SPMLOG_DBGMSGVAL("[COMMS ATU] Region start: ", region_params->phys_addr);
129 SPMLOG_DBGMSGVAL("[COMMS ATU] Region end: ", region_params->phys_addr + region_params->size);
130
131 return TFM_PLAT_ERR_SUCCESS;
132 }
133
comms_atu_alloc_region(uint64_t host_addr,uint32_t size,uint8_t * region)134 enum tfm_plat_err_t comms_atu_alloc_region(uint64_t host_addr, uint32_t size,
135 uint8_t *region)
136 {
137 uint32_t region_idx;
138 enum tfm_plat_err_t err;
139
140 err = get_region_idx_from_host_buf(host_addr, size, ®ion_idx);
141 if (err != TFM_PLAT_ERR_SUCCESS) {
142 err = get_free_region_idx(®ion_idx);
143 if (err) {
144 return err;
145 }
146
147 err = setup_region_for_host_buf(host_addr, size, region_idx);
148 if (err) {
149 return err;
150 }
151 }
152
153 atu_regions[region_idx].ref_count++;
154
155 *region = region_idx;
156
157 return TFM_PLAT_ERR_SUCCESS;
158 }
159
comms_atu_free_region(uint8_t region)160 enum tfm_plat_err_t comms_atu_free_region(uint8_t region)
161 {
162 int32_t atu_err;
163 struct comms_atu_region_params_t *region_params;
164
165 if (region >= RSE_COMMS_ATU_REGION_AM) {
166 return TFM_PLAT_ERR_INVALID_INPUT;
167 }
168
169 atu_regions[region].ref_count--;
170 region_params = &atu_regions[region];
171
172 if (atu_regions[region].ref_count == 0) {
173 atu_err = atu_uninitialize_region(&ATU_DEV_S, region_params->region);
174 if (atu_err) {
175 return TFM_PLAT_ERR_SYSTEM_ERR;
176 }
177 SPMLOG_DBGMSGVAL("[COMMS ATU] Deallocating region: ", region);
178 }
179
180 return TFM_PLAT_ERR_SUCCESS;
181 }
182
comms_atu_free_regions(comms_atu_region_set_t regions)183 enum tfm_plat_err_t comms_atu_free_regions(comms_atu_region_set_t regions)
184 {
185 uint32_t region_idx;
186 int32_t atu_err;
187 struct comms_atu_region_params_t *region_params;
188
189 for (region_idx = 0; region_idx < RSE_COMMS_ATU_REGION_AM; region_idx++) {
190 if ((regions.ref_counts[region_idx]) > 0) {
191 atu_regions[region_idx].ref_count -= regions.ref_counts[region_idx];
192 region_params = &atu_regions[region_idx];
193
194 if (atu_regions[region_idx].ref_count == 0) {
195 atu_err = atu_uninitialize_region(&ATU_DEV_S,
196 region_params->region);
197 if (atu_err) {
198 return TFM_PLAT_ERR_SYSTEM_ERR;
199 }
200 SPMLOG_DBGMSGVAL("[COMMS ATU] Deallocating region: ", region_idx);
201 }
202 }
203 }
204
205 return TFM_PLAT_ERR_SUCCESS;
206 }
207