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