1 /**
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdlib.h>
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/sys/util.h>
11 #include <zephyr/mgmt/mcumgr/smp/smp.h>
12 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
13 #include <zephyr/mgmt/mcumgr/mgmt/handlers.h>
14 #include <zephyr/mgmt/mcumgr/grp/enum_mgmt/enum_mgmt.h>
15 #include <zephyr/logging/log.h>
16
17 #include <mgmt/mcumgr/util/zcbor_bulk.h>
18
19 #include <zcbor_common.h>
20 #include <zcbor_encode.h>
21 #include <zcbor_decode.h>
22
23 #if defined(CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS)
24 #include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
25 #include <mgmt/mcumgr/transport/smp_internal.h>
26 #endif
27
28 LOG_MODULE_REGISTER(mcumgr_enum_grp, CONFIG_MCUMGR_GRP_ENUM_LOG_LEVEL);
29
30 #define MAX_MCUMGR_GROUPS 65535
31
32 struct enum_mgmt_single_arg {
33 uint32_t index;
34 uint32_t current_index;
35 uint32_t group;
36 bool found;
37 bool last;
38 };
39
40 struct enum_mgmt_list_arg {
41 bool *ok;
42 zcbor_state_t *zse;
43 };
44
45 struct enum_mgmt_details_arg {
46 bool *ok;
47 zcbor_state_t *zse;
48 uint16_t *allowed_group_ids;
49 uint16_t allowed_group_id_size;
50 #if defined(CONFIG_MCUMGR_GRP_ENUM_DETAILS_HOOK)
51 enum mgmt_cb_return status;
52 int32_t err_rc;
53 uint16_t err_group;
54 #endif
55 };
56
enum_mgmt_cb_count(const struct mgmt_group * group,void * user_data)57 static bool enum_mgmt_cb_count(const struct mgmt_group *group, void *user_data)
58 {
59 uint16_t *count = (uint16_t *)user_data;
60
61 ++*count;
62
63 return true;
64 }
65
enum_mgmt_cb_single(const struct mgmt_group * group,void * user_data)66 static bool enum_mgmt_cb_single(const struct mgmt_group *group, void *user_data)
67 {
68 struct enum_mgmt_single_arg *data = (struct enum_mgmt_single_arg *)user_data;
69
70 if (data->index == data->current_index) {
71 data->found = true;
72 data->group = group->mg_group_id;
73 ++data->current_index;
74 data->last = true;
75 } else {
76 if (data->found == true && data->current_index == (data->index + 1)) {
77 data->last = false;
78 return false;
79 }
80
81 ++data->current_index;
82 }
83
84 return true;
85 }
86
enum_mgmt_cb_list(const struct mgmt_group * group,void * user_data)87 static bool enum_mgmt_cb_list(const struct mgmt_group *group, void *user_data)
88 {
89 struct enum_mgmt_list_arg *data = (struct enum_mgmt_list_arg *)user_data;
90
91 *data->ok = zcbor_uint32_put(data->zse, group->mg_group_id);
92
93 return *data->ok;
94 }
95
96 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS
enum_mgmt_cb_details(const struct mgmt_group * group,void * user_data)97 static bool enum_mgmt_cb_details(const struct mgmt_group *group, void *user_data)
98 {
99 struct enum_mgmt_details_arg *data = (struct enum_mgmt_details_arg *)user_data;
100
101 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS_NAME
102 const char *group_name = group->mg_group_name;
103 int group_name_size = 0;
104 #endif
105
106 #if defined(CONFIG_MCUMGR_GRP_ENUM_DETAILS_HOOK)
107 struct enum_mgmt_detail_output group_detail_data = {
108 .group = group,
109 .zse = data->zse,
110 };
111 #endif
112
113 if (data->allowed_group_ids != NULL && data->allowed_group_id_size > 0) {
114 /* Check if this entry is in the allow list */
115 uint16_t i = 0;
116
117 while (i < data->allowed_group_id_size) {
118 if (data->allowed_group_ids[i] == group->mg_group_id) {
119 break;
120 }
121
122 ++i;
123 }
124
125 if (i == data->allowed_group_id_size) {
126 *data->ok = true;
127 goto finished;
128 }
129 }
130
131 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS_NAME
132 if (group_name != NULL) {
133 group_name_size = strlen(group_name);
134 }
135 #endif
136
137 *data->ok = zcbor_map_start_encode(data->zse, CONFIG_MCUMGR_GRP_ENUM_DETAILS_STATES) &&
138 zcbor_tstr_put_lit(data->zse, "group") &&
139 zcbor_uint32_put(data->zse, group->mg_group_id) &&
140 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS_NAME
141 zcbor_tstr_put_lit(data->zse, "name") &&
142 zcbor_tstr_encode_ptr(data->zse, group_name, group_name_size) &&
143 #endif
144 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS_HANDLERS
145 zcbor_tstr_put_lit(data->zse, "handlers") &&
146 zcbor_uint32_put(data->zse, group->mg_handlers_count) &&
147 #endif
148 1;
149
150 #if defined(CONFIG_MCUMGR_GRP_ENUM_DETAILS_HOOK)
151 /* Send notification to application to optionally append more fields */
152 data->status = mgmt_callback_notify(MGMT_EVT_OP_ENUM_MGMT_DETAILS, &group_detail_data,
153 sizeof(group_detail_data), &data->err_rc, &data->err_group);
154
155 if (data->status != MGMT_CB_OK) {
156 *data->ok = false;
157
158 return false;
159 }
160 #endif
161
162 if (*data->ok) {
163 *data->ok = zcbor_map_end_encode(data->zse, CONFIG_MCUMGR_GRP_ENUM_DETAILS_STATES);
164 }
165
166 finished:
167 return *data->ok;
168 }
169 #endif
170
enum_mgmt_count(struct smp_streamer * ctxt)171 static int enum_mgmt_count(struct smp_streamer *ctxt)
172 {
173 zcbor_state_t *zse = ctxt->writer->zs;
174 uint32_t count = 0;
175 bool ok;
176
177 mgmt_groups_foreach(enum_mgmt_cb_count, (void *)&count);
178
179 ok = zcbor_tstr_put_lit(zse, "count") &&
180 zcbor_uint32_put(zse, count);
181
182 return ok ? MGMT_ERR_EOK : MGMT_ERR_ECORRUPT;
183 }
184
enum_mgmt_list(struct smp_streamer * ctxt)185 static int enum_mgmt_list(struct smp_streamer *ctxt)
186 {
187 zcbor_state_t *zse = ctxt->writer->zs;
188 uint32_t count = 0;
189 bool ok;
190 struct enum_mgmt_list_arg arguments = {
191 .ok = &ok,
192 .zse = zse
193 };
194
195 mgmt_groups_foreach(enum_mgmt_cb_count, (void *)&count);
196
197 ok = zcbor_tstr_put_lit(zse, "groups") &&
198 zcbor_list_start_encode(zse, count);
199
200 if (!ok) {
201 goto failure;
202 }
203
204 mgmt_groups_foreach(enum_mgmt_cb_list, (void *)&arguments);
205
206 if (!ok) {
207 goto failure;
208 }
209
210 ok = zcbor_list_end_encode(zse, count);
211
212 failure:
213 return ok ? MGMT_ERR_EOK : MGMT_ERR_ECORRUPT;
214 }
215
enum_mgmt_single(struct smp_streamer * ctxt)216 static int enum_mgmt_single(struct smp_streamer *ctxt)
217 {
218 zcbor_state_t *zse = ctxt->writer->zs;
219 zcbor_state_t *zsd = ctxt->reader->zs;
220 size_t decoded = 0;
221 bool ok;
222 struct enum_mgmt_single_arg arguments = {
223 .index = 0,
224 .current_index = 0,
225 .group = 0,
226 .found = false,
227 .last = false
228 };
229
230 struct zcbor_map_decode_key_val enum_single_decode[] = {
231 ZCBOR_MAP_DECODE_KEY_DECODER("index", zcbor_uint32_decode, &arguments.index),
232 };
233
234 ok = zcbor_map_decode_bulk(zsd, enum_single_decode, ARRAY_SIZE(enum_single_decode),
235 &decoded) == 0;
236
237 if (!ok || arguments.index > MAX_MCUMGR_GROUPS) {
238 return MGMT_ERR_EINVAL;
239 }
240
241 mgmt_groups_foreach(enum_mgmt_cb_single, &arguments);
242
243 if (!arguments.found) {
244 /* Out of range index supplied */
245 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_ENUM, ENUM_MGMT_ERR_INDEX_TOO_LARGE);
246 } else {
247 ok = zcbor_tstr_put_lit(zse, "group") &&
248 zcbor_uint32_put(zse, arguments.group);
249
250 if (arguments.last) {
251 ok &= zcbor_tstr_put_lit(zse, "end") &&
252 zcbor_bool_put(zse, true);
253 }
254 }
255
256 return ok ? MGMT_ERR_EOK : MGMT_ERR_ECORRUPT;
257 }
258
259 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS
enum_mgmt_cb_count_entries(zcbor_state_t * state,void * user_data)260 static bool enum_mgmt_cb_count_entries(zcbor_state_t *state, void *user_data)
261 {
262 size_t *entries = (size_t *)user_data;
263
264 if (!zcbor_list_start_decode(state)) {
265 return true;
266 }
267
268 while (!zcbor_array_at_end(state)) {
269 ++*entries;
270 zcbor_any_skip(state, NULL);
271 }
272
273 (void)zcbor_list_end_decode(state);
274
275 return true;
276 }
277
enum_mgmt_cb_list_entries(zcbor_state_t * state,void * user_data)278 static bool enum_mgmt_cb_list_entries(zcbor_state_t *state, void *user_data)
279 {
280 uint32_t temp = 0;
281 uint16_t i = 0;
282 uint16_t *list = (uint16_t *)user_data;
283
284 if (!zcbor_list_start_decode(state)) {
285 return true;
286 }
287
288 while (!zcbor_array_at_end(state)) {
289 if (!zcbor_uint32_decode(state, &temp)) {
290 return false;
291 }
292
293 list[i] = (uint16_t)temp;
294 ++i;
295 }
296
297 (void)zcbor_list_end_decode(state);
298
299 return true;
300 }
301
enum_mgmt_details(struct smp_streamer * ctxt)302 static int enum_mgmt_details(struct smp_streamer *ctxt)
303 {
304 zcbor_state_t *zse = ctxt->writer->zs;
305 zcbor_state_t *zsd = ctxt->reader->zs;
306 uint32_t count = 0;
307 bool ok;
308 size_t backup_element_count_reader = zsd->elem_count;
309 size_t entries = 0;
310 size_t decoded = 0;
311 struct enum_mgmt_details_arg arguments = {
312 .ok = &ok,
313 .zse = zse,
314 .allowed_group_ids = NULL,
315 .allowed_group_id_size = 0,
316 #if defined(CONFIG_MCUMGR_GRP_ENUM_DETAILS_HOOK)
317 .status = MGMT_CB_OK,
318 #endif
319 };
320
321 if (!zcbor_new_backup(zsd, backup_element_count_reader)) {
322 LOG_ERR("Failed to create zcbor backup");
323 return MGMT_ERR_ENOMEM;
324 }
325
326 struct zcbor_map_decode_key_val enum_group_decode[] = {
327 ZCBOR_MAP_DECODE_KEY_DECODER("groups", enum_mgmt_cb_count_entries, &entries),
328 };
329
330 ok = zcbor_map_decode_bulk(zsd, enum_group_decode, ARRAY_SIZE(enum_group_decode),
331 &decoded) == 0;
332
333 if (!ok) {
334 return MGMT_ERR_EINVAL;
335 }
336
337 if (!zcbor_process_backup(zsd, (ZCBOR_FLAG_RESTORE | ZCBOR_FLAG_CONSUME),
338 backup_element_count_reader)) {
339 LOG_ERR("Failed to restore zcbor reader backup");
340 return MGMT_ERR_ENOMEM;
341 }
342
343 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS_BUFFER_TYPE_STACK
344 if (entries > CONFIG_MCUMGR_GRP_ENUM_DETAILS_BUFFER_TYPE_STACK_ENTRIES) {
345 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_ENUM,
346 ENUM_MGMT_ERR_TOO_MANY_GROUP_ENTRIES);
347 goto failure;
348 }
349 #endif
350
351 if (entries > 0) {
352 /* Return details on selected groups only */
353 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS_BUFFER_TYPE_HEAP
354 uint16_t *entry_list = malloc(sizeof(uint16_t) * entries);
355 #else
356 uint16_t entry_list[CONFIG_MCUMGR_GRP_ENUM_DETAILS_BUFFER_TYPE_STACK_ENTRIES];
357 #endif
358
359 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS_BUFFER_TYPE_HEAP
360 if (entry_list == NULL) {
361 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_ENUM,
362 ENUM_MGMT_ERR_INSUFFICIENT_HEAP_FOR_ENTRIES);
363 goto failure;
364 }
365 #endif
366
367 enum_group_decode[0].decoder = enum_mgmt_cb_list_entries;
368 enum_group_decode[0].value_ptr = entry_list;
369 enum_group_decode[0].found = false;
370 arguments.allowed_group_ids = entry_list;
371 arguments.allowed_group_id_size = (uint16_t)entries;
372
373 ok = zcbor_map_decode_bulk(zsd, enum_group_decode, ARRAY_SIZE(enum_group_decode),
374 &decoded) == 0;
375
376 if (!ok) {
377 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS_BUFFER_TYPE_HEAP
378 free(entry_list);
379 #endif
380
381 return MGMT_ERR_EINVAL;
382 }
383
384 ok = zcbor_tstr_put_lit(zse, "groups") &&
385 zcbor_list_start_encode(zse, count);
386
387 if (!ok) {
388 goto cleanup;
389 }
390
391 mgmt_groups_foreach(enum_mgmt_cb_details, (void *)&arguments);
392
393 cleanup:
394 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS_BUFFER_TYPE_HEAP
395 free(entry_list);
396 #endif
397
398 if (!ok) {
399 goto failure;
400 }
401 } else {
402 /* Return details on all groups */
403 mgmt_groups_foreach(enum_mgmt_cb_count, (void *)&count);
404
405 ok = zcbor_tstr_put_lit(zse, "groups") &&
406 zcbor_list_start_encode(zse, count);
407
408 if (!ok) {
409 goto failure;
410 }
411
412 mgmt_groups_foreach(enum_mgmt_cb_details, (void *)&arguments);
413
414 if (!ok) {
415 goto failure;
416 }
417 }
418
419 #if defined(CONFIG_MCUMGR_GRP_ENUM_DETAILS_HOOK)
420 if (arguments.status == MGMT_CB_ERROR_RC) {
421 return arguments.err_rc;
422 } else if (arguments.status == MGMT_CB_ERROR_ERR) {
423 /* Because there is already data in the buffer, it must be cleared first */
424 net_buf_reset(ctxt->writer->nb);
425 ctxt->writer->nb->len = sizeof(struct smp_hdr);
426 zcbor_new_encode_state(zse, ARRAY_SIZE(ctxt->writer->zs),
427 ctxt->writer->nb->data + sizeof(struct smp_hdr),
428 net_buf_tailroom(ctxt->writer->nb), 0);
429 ok = zcbor_map_start_encode(zse, CONFIG_MCUMGR_SMP_CBOR_MAX_MAIN_MAP_ENTRIES) &&
430 smp_add_cmd_err(zse, arguments.err_group, (uint16_t)arguments.err_rc);
431
432 goto failure;
433 }
434 #endif
435
436 ok = zcbor_list_end_encode(zse, count);
437
438 failure:
439 return ok ? MGMT_ERR_EOK : MGMT_ERR_ECORRUPT;
440 }
441 #endif
442
443 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
444 /*
445 * @brief Translate enum mgmt group error code into MCUmgr error code
446 *
447 * @param ret #enum_mgmt_err_code_t error code
448 *
449 * @return #mcumgr_err_t error code
450 */
enum_mgmt_translate_error_code(uint16_t err)451 static int enum_mgmt_translate_error_code(uint16_t err)
452 {
453 int rc;
454
455 switch (err) {
456 case ENUM_MGMT_ERR_TOO_MANY_GROUP_ENTRIES:
457 rc = MGMT_ERR_EINVAL;
458 break;
459
460 case ENUM_MGMT_ERR_UNKNOWN:
461 default:
462 rc = MGMT_ERR_EUNKNOWN;
463 }
464
465 return rc;
466 }
467 #endif
468
469 static const struct mgmt_handler enum_mgmt_group_handlers[] = {
470 [ENUM_MGMT_ID_COUNT] = { enum_mgmt_count, NULL },
471 [ENUM_MGMT_ID_LIST] = { enum_mgmt_list, NULL },
472 [ENUM_MGMT_ID_SINGLE] = { enum_mgmt_single, NULL },
473 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS
474 [ENUM_MGMT_ID_DETAILS] = { enum_mgmt_details, NULL },
475 #endif
476 };
477
478 static struct mgmt_group enum_mgmt_group = {
479 .mg_handlers = enum_mgmt_group_handlers,
480 .mg_handlers_count = ARRAY_SIZE(enum_mgmt_group_handlers),
481 .mg_group_id = MGMT_GROUP_ID_ENUM,
482 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
483 .mg_translate_error = enum_mgmt_translate_error_code,
484 #endif
485 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS_NAME
486 .mg_group_name = "enum mgmt",
487 #endif
488 };
489
enum_mgmt_register_group(void)490 static void enum_mgmt_register_group(void)
491 {
492 mgmt_register_group(&enum_mgmt_group);
493 }
494
495 MCUMGR_HANDLER_DEFINE(enum_mgmt, enum_mgmt_register_group);
496