1 /*
2 * Copyright (c) 2023, Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <assert.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/devicetree.h>
10 #include <zephyr/retention/retention.h>
11 #include <zephyr/logging/log.h>
12 #include <bootutil/boot_record.h>
13 #include <bootutil/boot_status.h>
14 #include <../../bootutil/src/bootutil_priv.h>
15
16 #define SHARED_MEMORY_MIN_SIZE 8
17
18 LOG_MODULE_REGISTER(bootloader_info, CONFIG_RETENTION_LOG_LEVEL);
19
20 static bool shared_memory_init_done = false;
21 static uint16_t shared_data_size = SHARED_DATA_HEADER_SIZE;
22 static ssize_t shared_data_max_size = 0;
23 static const struct device *bootloader_info_dev =
24 DEVICE_DT_GET(DT_CHOSEN(zephyr_bootloader_info));
25
26 BUILD_ASSERT(SHARED_MEMORY_MIN_SIZE < \
27 DT_REG_SIZE_BY_IDX(DT_CHOSEN(zephyr_bootloader_info), 0), \
28 "zephyr,bootloader-info area is too small for bootloader information struct");
29
boot_add_data_to_shared_area(uint8_t major_type,uint16_t minor_type,size_t size,const uint8_t * data)30 int boot_add_data_to_shared_area(uint8_t major_type,
31 uint16_t minor_type,
32 size_t size,
33 const uint8_t *data)
34 {
35 struct shared_data_tlv_header header = {
36 .tlv_magic = SHARED_DATA_TLV_INFO_MAGIC,
37 .tlv_tot_len = shared_data_size,
38 };
39 struct shared_data_tlv_entry tlv_entry = {0};
40 uint16_t boot_data_size;
41 uintptr_t tlv_end, offset;
42 int rc;
43
44 if (data == NULL) {
45 return SHARED_MEMORY_GEN_ERROR;
46 }
47
48 /* Check whether first time to call this function. If does then initialise
49 * shared data area.
50 */
51 if (!shared_memory_init_done) {
52 retention_clear(bootloader_info_dev);
53 shared_data_max_size = retention_size(bootloader_info_dev);
54 shared_memory_init_done = true;
55 }
56
57 /* Check whether TLV entry is already added.
58 * Get the boundaries of TLV section
59 */
60 tlv_end = shared_data_size;
61 offset = SHARED_DATA_HEADER_SIZE;
62
63 /* Iterates over the TLV section looks for the same entry if found then
64 * returns with error: SHARED_MEMORY_OVERWRITE
65 */
66 while (offset < tlv_end) {
67 /* Create local copy to avoid unaligned access */
68 rc = retention_read(bootloader_info_dev, offset, (void *)&tlv_entry,
69 SHARED_DATA_ENTRY_HEADER_SIZE);
70
71 if (rc) {
72 return SHARED_MEMORY_READ_ERROR;
73 }
74
75 if (GET_MAJOR(tlv_entry.tlv_type) == major_type &&
76 GET_MINOR(tlv_entry.tlv_type) == minor_type) {
77 return SHARED_MEMORY_OVERWRITE;
78 }
79
80 offset += SHARED_DATA_ENTRY_SIZE(tlv_entry.tlv_len);
81 }
82
83 /* Add TLV entry */
84 tlv_entry.tlv_type = SET_TLV_TYPE(major_type, minor_type);
85 tlv_entry.tlv_len = size;
86
87 if (!boot_u16_safe_add(&boot_data_size, shared_data_size,
88 SHARED_DATA_ENTRY_SIZE(size))) {
89 return SHARED_MEMORY_GEN_ERROR;
90 }
91
92 /* Verify overflow of shared area */
93 if (boot_data_size > shared_data_max_size) {
94 return SHARED_MEMORY_OVERFLOW;
95 }
96
97 offset = shared_data_size;
98 rc = retention_write(bootloader_info_dev, offset, (void*)&tlv_entry,
99 SHARED_DATA_ENTRY_HEADER_SIZE);
100 if (rc) {
101 LOG_ERR("Shared data TLV header write failed: %d", rc);
102 return SHARED_MEMORY_WRITE_ERROR;
103 }
104
105 offset += SHARED_DATA_ENTRY_HEADER_SIZE;
106 rc = retention_write(bootloader_info_dev, offset, data, size);
107
108 if (rc) {
109 LOG_ERR("Shared data TLV data write failed: %d", rc);
110 return SHARED_MEMORY_WRITE_ERROR;
111 }
112
113 shared_data_size += SHARED_DATA_ENTRY_SIZE(size);
114 header.tlv_tot_len = shared_data_size;
115
116 rc = retention_write(bootloader_info_dev, 0, (void *)&header,
117 sizeof(header));
118
119 if (rc) {
120 return SHARED_MEMORY_WRITE_ERROR;
121 }
122
123 return SHARED_MEMORY_OK;
124 }
125