Contiki-NG
Loading...
Searching...
No Matches
tz-radio.c
1/*
2 * Copyright (c) 2026, RISE Research Institutes of Sweden
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * 3. Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31 * OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34/*
35 * \file
36 * TrustZone secure-world radio NSC entry points.
37 *
38 * These functions are marked as Non-Secure Callable (NSC) so
39 * the normal world can invoke radio operations through the
40 * secure world. All normal-world pointers are validated via
41 * CMSE before use.
42 *
43 * \author
44 * Nicolas Tsiftes <nicolas.tsiftes@ri.se>
45 */
46
47#include "contiki.h"
48#include "dev/radio.h"
49#include "tz-radio.h"
50
51#include <arm_cmse.h>
52#include <string.h>
53
54#include "nrf.h"
55#include "nrf_ficr.h"
56
57/*---------------------------------------------------------------------------*/
58#include "sys/log.h"
59#define LOG_MODULE "TZRadio"
60#define LOG_LEVEL LOG_LEVEL_INFO
61/*---------------------------------------------------------------------------*/
62/* Maximum payload and object sizes for secure-side copy buffers. */
63#define TZ_RADIO_MAX_PAYLOAD 128
64#define TZ_RADIO_MAX_OBJECT 140
65#define TZ_RADIO_BUF_SIZE \
66 (TZ_RADIO_MAX_OBJECT > TZ_RADIO_MAX_PAYLOAD \
67 ? TZ_RADIO_MAX_OBJECT : TZ_RADIO_MAX_PAYLOAD)
68/*---------------------------------------------------------------------------*/
69extern const struct radio_driver ipc_radio_driver;
70/*---------------------------------------------------------------------------*/
71static tz_radio_ns_rx_callback_t ns_rx_callback;
72static bool rx_callback_registered;
73/*---------------------------------------------------------------------------*/
74/* RX attributes stored by tz_radio_notify_rx for normal-world retrieval. */
75static int8_t last_rx_rssi;
76static uint8_t last_rx_lqi;
77/*---------------------------------------------------------------------------*/
78/*
79 * A single static bounce buffer shared by all NSC entry points that copy
80 * data across the secure/non-secure boundary. These entry points are only
81 * invoked by the single-threaded, cooperative normal-world scheduler, so at
82 * most one secure radio call runs at a time and the buffer is never used by
83 * two calls concurrently. It is sized for the largest transfer.
84 */
85static uint8_t secure_buf[TZ_RADIO_BUF_SIZE];
86/*---------------------------------------------------------------------------*/
87CC_TRUSTZONE_SECURE_CALL int
88tz_radio_init(void)
89{
90 return ipc_radio_driver.init();
91}
92/*---------------------------------------------------------------------------*/
93CC_TRUSTZONE_SECURE_CALL int
94tz_radio_prepare(const void *payload, unsigned short payload_len)
95{
96 if(payload_len > TZ_RADIO_MAX_PAYLOAD) {
97 return 1;
98 }
99
100 if(cmse_check_address_range((void *)payload, payload_len,
101 CMSE_NONSECURE) == NULL) {
102 LOG_ERR("prepare: invalid NS pointer\n");
103 return 1;
104 }
105
106 memcpy(secure_buf, payload, payload_len);
107
108 return ipc_radio_driver.prepare(secure_buf, payload_len);
109}
110/*---------------------------------------------------------------------------*/
111CC_TRUSTZONE_SECURE_CALL int
112tz_radio_transmit(unsigned short transmit_len)
113{
114 return ipc_radio_driver.transmit(transmit_len);
115}
116/*---------------------------------------------------------------------------*/
117/*---------------------------------------------------------------------------*/
118/*---------------------------------------------------------------------------*/
119CC_TRUSTZONE_SECURE_CALL int
120tz_radio_send(const void *payload, unsigned short payload_len)
121{
122 if(payload_len > TZ_RADIO_MAX_PAYLOAD) {
123 return RADIO_TX_ERR;
124 }
125
126 if(cmse_check_address_range((void *)payload, payload_len,
127 CMSE_NONSECURE) == NULL) {
128 LOG_ERR("send: invalid NS pointer\n");
129 return RADIO_TX_ERR;
130 }
131
132 memcpy(secure_buf, payload, payload_len);
133
134 return ipc_radio_driver.send(secure_buf, payload_len);
135}
136/*---------------------------------------------------------------------------*/
137CC_TRUSTZONE_SECURE_CALL int
138tz_radio_read(void *buf, unsigned short buf_len)
139{
140 int len;
141
142 if(cmse_check_address_range(buf, buf_len, CMSE_NONSECURE) == NULL) {
143 LOG_ERR("read: invalid NS pointer\n");
144 return 0;
145 }
146
147 len = ipc_radio_driver.read(secure_buf,
148 buf_len < TZ_RADIO_MAX_PAYLOAD
149 ? buf_len : TZ_RADIO_MAX_PAYLOAD);
150 if(len > 0) {
151 memcpy(buf, secure_buf, len);
152 }
153
154 return len;
155}
156/*---------------------------------------------------------------------------*/
157CC_TRUSTZONE_SECURE_CALL int
158tz_radio_channel_clear(void)
159{
160 return ipc_radio_driver.channel_clear();
161}
162/*---------------------------------------------------------------------------*/
163CC_TRUSTZONE_SECURE_CALL int
164tz_radio_receiving_packet(void)
165{
166 return ipc_radio_driver.receiving_packet();
167}
168/*---------------------------------------------------------------------------*/
169CC_TRUSTZONE_SECURE_CALL int
170tz_radio_pending_packet(void)
171{
172 return ipc_radio_driver.pending_packet();
173}
174/*---------------------------------------------------------------------------*/
175CC_TRUSTZONE_SECURE_CALL int
176tz_radio_on(void)
177{
178 return ipc_radio_driver.on();
179}
180/*---------------------------------------------------------------------------*/
181CC_TRUSTZONE_SECURE_CALL int
182tz_radio_off(void)
183{
184 return ipc_radio_driver.off();
185}
186/*---------------------------------------------------------------------------*/
187CC_TRUSTZONE_SECURE_CALL radio_result_t
188tz_radio_get_value(radio_param_t param, radio_value_t *value)
189{
190 radio_value_t secure_value;
191 radio_result_t result;
192
193 if(cmse_check_address_range(value, sizeof(*value),
194 CMSE_NONSECURE) == NULL) {
195 LOG_ERR("get_value: invalid NS pointer\n");
196 return RADIO_RESULT_ERROR;
197 }
198
199 result = ipc_radio_driver.get_value(param, &secure_value);
200 if(result == RADIO_RESULT_OK) {
201 *value = secure_value;
202 }
203
204 return result;
205}
206/*---------------------------------------------------------------------------*/
207CC_TRUSTZONE_SECURE_CALL radio_result_t
208tz_radio_set_value(radio_param_t param, radio_value_t value)
209{
210 return ipc_radio_driver.set_value(param, value);
211}
212/*---------------------------------------------------------------------------*/
213CC_TRUSTZONE_SECURE_CALL radio_result_t
214tz_radio_get_object(radio_param_t param, void *dest, size_t size)
215{
216 radio_result_t result;
217
218 if(size > TZ_RADIO_MAX_OBJECT) {
220 }
221
222 if(cmse_check_address_range(dest, size, CMSE_NONSECURE) == NULL) {
223 LOG_ERR("get_object: invalid NS pointer\n");
224 return RADIO_RESULT_ERROR;
225 }
226
227 result = ipc_radio_driver.get_object(param, secure_buf, size);
228 if(result == RADIO_RESULT_OK) {
229 memcpy(dest, secure_buf, size);
230 }
231
232 return result;
233}
234/*---------------------------------------------------------------------------*/
235CC_TRUSTZONE_SECURE_CALL radio_result_t
236tz_radio_set_object(radio_param_t param, const void *src, size_t size)
237{
238 if(size > TZ_RADIO_MAX_OBJECT) {
240 }
241
242 if(cmse_check_address_range((void *)src, size, CMSE_NONSECURE) == NULL) {
243 LOG_ERR("set_object: invalid NS pointer\n");
244 return RADIO_RESULT_ERROR;
245 }
246
247 memcpy(secure_buf, src, size);
248
249 return ipc_radio_driver.set_object(param, secure_buf, size);
250}
251/*---------------------------------------------------------------------------*/
252CC_TRUSTZONE_SECURE_CALL bool
253tz_radio_get_device_id(uint32_t *id0, uint32_t *id1)
254{
255 if(cmse_check_address_range(id0, sizeof(*id0),
256 CMSE_NONSECURE) == NULL) {
257 return false;
258 }
259 if(cmse_check_address_range(id1, sizeof(*id1),
260 CMSE_NONSECURE) == NULL) {
261 return false;
262 }
263
264#if defined(NRF_FICR_S)
265 *id0 = nrf_ficr_deviceid_get(NRF_FICR_S, 0);
266 *id1 = nrf_ficr_deviceid_get(NRF_FICR_S, 1);
267#elif defined(NRF_FICR)
268 *id0 = nrf_ficr_deviceid_get(NRF_FICR, 0);
269 *id1 = nrf_ficr_deviceid_get(NRF_FICR, 1);
270#else
271 *id0 = 0;
272 *id1 = 0;
273#endif
274
275 return true;
276}
277/*---------------------------------------------------------------------------*/
278CC_TRUSTZONE_SECURE_CALL bool
279tz_radio_get_rx_attributes(int8_t *rssi, uint8_t *lqi)
280{
281 if(cmse_check_address_range(rssi, sizeof(*rssi),
282 CMSE_NONSECURE) == NULL) {
283 return false;
284 }
285 if(cmse_check_address_range(lqi, sizeof(*lqi),
286 CMSE_NONSECURE) == NULL) {
287 return false;
288 }
289
290 *rssi = last_rx_rssi;
291 *lqi = last_rx_lqi;
292 return true;
293}
294/*---------------------------------------------------------------------------*/
295CC_TRUSTZONE_SECURE_CALL bool
296tz_radio_register_rx_callback(tz_radio_ns_rx_callback_t callback)
297{
298 if(rx_callback_registered) {
299 return false;
300 }
301
302 if(cmse_check_address_range((void *)callback, sizeof(callback),
303 CMSE_NONSECURE) == NULL) {
304 LOG_ERR("register_rx_callback: invalid NS function pointer\n");
305 return false;
306 }
307
308 /* Tag the function pointer as non-secure so the eventual call goes
309 * through BLXNS with the correct state transition. cmse_check_address_range
310 * only validates the address; cmse_nsfptr_create sets the NS attribute. */
311 ns_rx_callback =
312 (tz_radio_ns_rx_callback_t)cmse_nsfptr_create((void *)callback);
313 rx_callback_registered = true;
314
315 LOG_INFO("RX callback registered\n");
316 return true;
317}
318/*---------------------------------------------------------------------------*/
319void
320tz_radio_notify_rx(int8_t rssi, uint8_t lqi)
321{
322 last_rx_rssi = rssi;
323 last_rx_lqi = lqi;
324
325 if(rx_callback_registered && cmse_is_nsfptr((void *)ns_rx_callback)) {
326 ns_rx_callback();
327 }
328}
329/*---------------------------------------------------------------------------*/
enum radio_result_e radio_result_t
Radio return values when setting or getting radio parameters.
int radio_value_t
Each radio has a set of parameters that designate the current configuration and state of the radio.
Definition radio.h:88
@ RADIO_RESULT_ERROR
An error occurred when getting/setting the parameter, but the arguments were otherwise correct.
Definition radio.h:488
@ RADIO_RESULT_INVALID_VALUE
The value argument was incorrect.
Definition radio.h:482
@ RADIO_RESULT_OK
The parameter was set/read successfully.
Definition radio.h:480
@ RADIO_TX_ERR
An error occurred during transmission.
Definition radio.h:506
Header file for the logging system.
Header file for the radio API.
The structure of a Contiki-NG radio device driver.
Definition radio.h:534
radio_result_t(* get_object)(radio_param_t param, void *dest, size_t size)
Get a radio parameter object.
Definition radio.h:770
int(* read)(void *buf, unsigned short buf_len)
Read a received packet into a buffer.
Definition radio.h:655
int(* prepare)(const void *payload, unsigned short payload_len)
Prepare the radio with a packet to be sent.
Definition radio.h:580
radio_result_t(* set_value)(radio_param_t param, radio_value_t value)
Set a radio parameter value.
Definition radio.h:756
int(* off)(void)
Turn the radio off.
Definition radio.h:729
int(* init)(void)
Initialise the radio hardware.
Definition radio.h:555
int(* send)(const void *payload, unsigned short payload_len)
Prepare & transmit a packet.
Definition radio.h:631
int(* receiving_packet)(void)
Check if the radio driver is currently receiving a packet.
Definition radio.h:684
radio_result_t(* set_object)(radio_param_t param, const void *src, size_t size)
Set a radio parameter object.
Definition radio.h:787
int(* on)(void)
Turn the radio on.
Definition radio.h:711
int(* transmit)(unsigned short transmit_len)
Send the packet that has previously been prepared.
Definition radio.h:619
int(* pending_packet)(void)
Check if a packet has been received and is available in the radio driver's buffers.
Definition radio.h:697
radio_result_t(* get_value)(radio_param_t param, radio_value_t *value)
Get a radio parameter value.
Definition radio.h:741
int(* channel_clear)(void)
Perform a Clear-Channel Assessment (CCA) to find out if there is a packet in the air or not.
Definition radio.h:672