1 /*
2 * Copyright (c) 2023, Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <limits.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/init.h>
10 #include <zephyr/devicetree.h>
11 #include <zephyr/retention/retention.h>
12 #include <zephyr/logging/log.h>
13 #include <zephyr/settings/settings.h>
14 #include <zephyr/retention/blinfo.h>
15 #include <bootutil/boot_status.h>
16
17 LOG_MODULE_REGISTER(blinfo_mcuboot, CONFIG_RETENTION_LOG_LEVEL);
18
19 static const struct device *bootloader_info_dev =
20 DEVICE_DT_GET(DT_CHOSEN(zephyr_bootloader_info));
21
22 #if !defined(CONFIG_RETENTION_BOOTLOADER_INFO_OUTPUT_FUNCTION)
23 static
24 #endif
blinfo_lookup(uint16_t key,char * val,int val_len_max)25 int blinfo_lookup(uint16_t key, char *val, int val_len_max)
26 {
27 struct shared_data_tlv_header header;
28 struct shared_data_tlv_entry tlv_entry = {0};
29 uintptr_t offset = SHARED_DATA_HEADER_SIZE;
30 int rc;
31
32 rc = retention_read(bootloader_info_dev, 0, (void *)&header, sizeof(header));
33
34 if (rc != 0) {
35 return rc;
36 }
37
38 /* Iterate over the whole shared MCUboot data section and look for a TLV with
39 * the required tag.
40 */
41 while (offset < header.tlv_tot_len) {
42 rc = retention_read(bootloader_info_dev, offset, (void *)&tlv_entry,
43 sizeof(tlv_entry));
44
45 if (rc != 0) {
46 return rc;
47 }
48
49 if (GET_MAJOR(tlv_entry.tlv_type) == TLV_MAJOR_BLINFO &&
50 GET_MINOR(tlv_entry.tlv_type) == key) {
51 /* Return an error if the provided buffer is too small to fit the
52 * value in it, bootloader values are small and concise and should
53 * be able to fit in a single buffer.
54 */
55 if (tlv_entry.tlv_len > val_len_max) {
56 return -EOVERFLOW;
57 }
58
59 offset += SHARED_DATA_ENTRY_HEADER_SIZE;
60 rc = retention_read(bootloader_info_dev, offset, val,
61 tlv_entry.tlv_len);
62
63 if (rc != 0) {
64 return rc;
65 }
66
67 return tlv_entry.tlv_len;
68 }
69
70 offset += SHARED_DATA_ENTRY_SIZE(tlv_entry.tlv_len);
71 }
72
73 /* Return IO error as a valid key name was provided but the TLV was not found in
74 * the shared data section.
75 */
76 return -EIO;
77 }
78
79 #if defined(CONFIG_RETENTION_BOOTLOADER_INFO_OUTPUT_SETTINGS)
80 static int blinfo_handle_get(const char *name, char *val, int val_len_max);
81 static int blinfo_handle_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg);
82
83 static struct settings_handler blinfo_handler = {
84 .name = "blinfo",
85 .h_get = blinfo_handle_get,
86 .h_set = blinfo_handle_set,
87 };
88
blinfo_handle_get(const char * name,char * val,int val_len_max)89 static int blinfo_handle_get(const char *name, char *val, int val_len_max)
90 {
91 const char *next;
92 uint16_t index;
93
94 /* Allowed keys are mode, signature_type, recovery, running_slot, bootloader_version
95 * and max_application_size which cannot contain any additional entries
96 */
97 if (settings_name_steq(name, "mode", &next) && !next) {
98 index = BLINFO_MODE;
99 } else if (settings_name_steq(name, "signature_type", &next) && !next) {
100 index = BLINFO_SIGNATURE_TYPE;
101 } else if (settings_name_steq(name, "recovery", &next) && !next) {
102 index = BLINFO_RECOVERY;
103 } else if (settings_name_steq(name, "running_slot", &next) && !next) {
104 index = BLINFO_RUNNING_SLOT;
105 } else if (settings_name_steq(name, "bootloader_version", &next) && !next) {
106 index = BLINFO_BOOTLOADER_VERSION;
107 } else if (settings_name_steq(name, "max_application_size", &next) && !next) {
108 index = BLINFO_MAX_APPLICATION_SIZE;
109 } else {
110 return -ENOENT;
111 }
112
113 return blinfo_lookup(index, val, val_len_max);
114 }
115
blinfo_handle_set(const char * name,size_t len,settings_read_cb read_cb,void * cb_arg)116 static int blinfo_handle_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg)
117 {
118 return -ENOTSUP;
119 }
120 #endif
121
blinfo_init(void)122 static int blinfo_init(void)
123 {
124 int rc;
125
126 rc = retention_is_valid(bootloader_info_dev);
127
128 if (rc == 1 || rc == -ENOTSUP) {
129 struct shared_data_tlv_header header;
130
131 rc = retention_read(bootloader_info_dev, 0, (void *)&header, sizeof(header));
132
133 if (rc == 0 && header.tlv_magic != SHARED_DATA_TLV_INFO_MAGIC) {
134 /* Unknown data present */
135 LOG_ERR("MCUboot data load failed, expected magic value: 0x%x, got: 0x%x",
136 SHARED_DATA_TLV_INFO_MAGIC, header.tlv_magic);
137 rc = -EINVAL;
138 }
139 }
140
141 #if defined(CONFIG_RETENTION_BOOTLOADER_INFO_OUTPUT_SETTINGS)
142 if (rc == 0) {
143 settings_register(&blinfo_handler);
144 }
145 #endif
146
147 return rc;
148 }
149
150 SYS_INIT(blinfo_init, APPLICATION, CONFIG_RETENTION_BOOTLOADER_INFO_INIT_PRIORITY);
151