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