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, &region_idx);
141     if (err != TFM_PLAT_ERR_SUCCESS) {
142         err = get_free_region_idx(&region_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