1 // Copyright 2021 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "esp_crypto_shared_gdma.h"
16 
17 #include "freertos/FreeRTOS.h"
18 #include "freertos/task.h"
19 
20 #include "hal/gdma_ll.h"
21 #include "soc/soc_caps.h"
22 #include "esp_log.h"
23 #include "esp_err.h"
24 #include "esp_crypto_lock.h"
25 
26 #define NEW_CHANNEL_TIMEOUT_MS  1000
27 #define NEW_CHANNEL_DELAY_MS    100
28 
29 static const char *TAG = "crypto_shared_gdma";
30 
31 static gdma_channel_handle_t rx_channel;
32 static gdma_channel_handle_t tx_channel;
33 
34 /* Allocate a new GDMA channel, will keep trying until NEW_CHANNEL_TIMEOUT_MS */
crypto_shared_gdma_new_channel(gdma_channel_alloc_config_t * channel_config,gdma_channel_handle_t * channel)35 static inline esp_err_t crypto_shared_gdma_new_channel(gdma_channel_alloc_config_t *channel_config, gdma_channel_handle_t *channel)
36 {
37     esp_err_t ret;
38     int time_waited_ms = 0;
39 
40     while(1) {
41         ret = gdma_new_channel(channel_config, channel);
42 
43         if (ret == ESP_OK) {
44             break;
45         } else if (time_waited_ms >= NEW_CHANNEL_TIMEOUT_MS) {
46             *channel = NULL;
47             break;
48         }
49 
50         time_waited_ms += NEW_CHANNEL_DELAY_MS;
51         vTaskDelay(NEW_CHANNEL_DELAY_MS / portTICK_PERIOD_MS);
52     }
53     return ret;
54 }
55 
56 
57 #if SOC_GDMA_SUPPORT_EXTMEM
58 /* Initialize external memory specific DMA configs */
esp_crypto_shared_dma_init_extmem(void)59 static void esp_crypto_shared_dma_init_extmem(void)
60 {
61     int tx_ch_id = 0;
62     int rx_ch_id = 0;
63 
64     gdma_get_channel_id(tx_channel, &tx_ch_id);
65     gdma_get_channel_id(rx_channel, &rx_ch_id);
66 
67     /* An L2 FIFO bigger than 40 bytes is need when accessing external ram */
68     gdma_ll_tx_extend_fifo_size_to(&GDMA, tx_ch_id, 40);
69     gdma_ll_rx_extend_l2_fifo_size_to(&GDMA, rx_ch_id, 40);
70     gdma_ll_tx_set_block_size_psram(&GDMA, tx_ch_id, GDMA_OUT_EXT_MEM_BK_SIZE_16B);
71     gdma_ll_rx_set_block_size_psram(&GDMA, rx_ch_id, GDMA_OUT_EXT_MEM_BK_SIZE_16B);
72 }
73 #endif //SOC_GDMA_SUPPORT_EXTMEM
74 
75 /* Initialize GDMA module and channels */
crypto_shared_gdma_init(void)76 static esp_err_t crypto_shared_gdma_init(void)
77 {
78     esp_err_t ret;
79 
80     gdma_channel_alloc_config_t channel_config_tx = {
81         .direction = GDMA_CHANNEL_DIRECTION_TX,
82     };
83 
84     gdma_channel_alloc_config_t channel_config_rx = {
85         .direction = GDMA_CHANNEL_DIRECTION_RX,
86     };
87 
88     ret = crypto_shared_gdma_new_channel(&channel_config_tx, &tx_channel);
89     if (ret != ESP_OK) {
90         goto err;
91     }
92 
93     ret = crypto_shared_gdma_new_channel(&channel_config_rx, &rx_channel);
94     if (ret != ESP_OK) {
95         gdma_del_channel(tx_channel); // Clean up already allocated TX channel
96         goto err;
97     }
98 
99 #if SOC_GDMA_SUPPORT_EXTMEM
100     esp_crypto_shared_dma_init_extmem();
101 #endif //SOC_GDMA_SUPPORT_EXTMEM
102 
103     gdma_connect(rx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_AES, 0));
104     gdma_connect(tx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_AES, 0));
105 
106     return ESP_OK;
107 
108 err:
109     ESP_LOGE(TAG, "Failed to acquire DMA channel, Err=0x%X", ret);
110     tx_channel = NULL;
111     rx_channel = NULL;
112 
113     return ret;
114 }
115 
116 
esp_crypto_shared_gdma_start(const lldesc_t * input,const lldesc_t * output,gdma_trigger_peripheral_t peripheral)117 esp_err_t esp_crypto_shared_gdma_start(const lldesc_t *input, const lldesc_t *output, gdma_trigger_peripheral_t peripheral)
118 {
119     int rx_ch_id = 0;
120     esp_err_t ret = ESP_OK;
121 
122     if (tx_channel == NULL) {
123         /* Allocate a pair of RX and TX for crypto, should only happen the first time we use the GMDA
124            or if user called esp_crypto_shared_gdma_release */
125         ret = crypto_shared_gdma_init();
126     }
127 
128     if (ret != ESP_OK) {
129         return ret;
130     }
131 
132     /* Tx channel is shared between AES and SHA, need to connect to peripheral every time */
133     gdma_disconnect(tx_channel);
134 
135     if (peripheral == GDMA_TRIG_PERIPH_SHA) {
136         gdma_connect(tx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_SHA, 0));
137     } else if (peripheral == GDMA_TRIG_PERIPH_AES) {
138         gdma_connect(tx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_AES, 0));
139     } else {
140         return ESP_ERR_INVALID_ARG;
141     }
142 
143   /* tx channel is reset by gdma_connect(), also reset rx to ensure a known state */
144     gdma_get_channel_id(tx_channel, &rx_ch_id);
145     gdma_ll_rx_reset_channel(&GDMA, rx_ch_id);
146 
147     gdma_start(tx_channel, (intptr_t)input);
148     gdma_start(rx_channel, (intptr_t)output);
149 
150     return ESP_OK;
151 }
152 
esp_crypto_shared_gdma_free()153 void esp_crypto_shared_gdma_free()
154 {
155     esp_crypto_sha_aes_lock_acquire();
156 
157     if (rx_channel != NULL) {
158         gdma_disconnect(rx_channel);
159         gdma_del_channel(rx_channel);
160         rx_channel = NULL;
161     }
162 
163     if (tx_channel != NULL) {
164         gdma_disconnect(tx_channel);
165         gdma_del_channel(tx_channel);
166         tx_channel = NULL;
167     }
168 
169     esp_crypto_sha_aes_lock_release();
170 }
171