1 /*
2  * Copyright (c) 2023 Google Inc
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <sys/types.h>
8 #include <zephyr/device.h>
9 #include <zephyr/drivers/flash/stm32_flash_api_extensions.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/logging/log.h>
12 
13 #ifdef CONFIG_USERSPACE
14 #include <zephyr/syscall.h>
15 #include <zephyr/internal/syscall_handler.h>
16 #endif
17 
18 #include <soc.h>
19 #include "flash_stm32.h"
20 
21 LOG_MODULE_REGISTER(flash_stm32_ex_op, CONFIG_FLASH_LOG_LEVEL);
22 
23 #if defined(CONFIG_FLASH_STM32_WRITE_PROTECT)
flash_stm32_ex_op_sector_wp(const struct device * dev,const uintptr_t in,void * out)24 int flash_stm32_ex_op_sector_wp(const struct device *dev, const uintptr_t in,
25 				void *out)
26 {
27 	const struct flash_stm32_ex_op_sector_wp_in *request =
28 		(const struct flash_stm32_ex_op_sector_wp_in *)in;
29 	struct flash_stm32_ex_op_sector_wp_out *result =
30 		(struct flash_stm32_ex_op_sector_wp_out *)out;
31 	uint64_t change_mask;
32 	int rc = 0, rc2 = 0;
33 #ifdef CONFIG_USERSPACE
34 	bool syscall_trap = z_syscall_trap();
35 #endif
36 
37 	if (request != NULL) {
38 #ifdef CONFIG_USERSPACE
39 		struct flash_stm32_ex_op_sector_wp_in in_copy;
40 
41 		if (syscall_trap) {
42 			K_OOPS(k_usermode_from_copy(&in_copy, request,
43 						sizeof(in_copy)));
44 			request = &in_copy;
45 		}
46 #endif
47 		change_mask = request->enable_mask;
48 
49 		if (!IS_ENABLED(
50 			    CONFIG_FLASH_STM32_WRITE_PROTECT_DISABLE_PREVENTION)) {
51 			change_mask |= request->disable_mask;
52 		}
53 
54 		rc = flash_stm32_option_bytes_lock(dev, false);
55 		if (rc == 0) {
56 			rc = flash_stm32_update_wp_sectors(
57 				dev, change_mask, request->enable_mask);
58 		}
59 
60 		rc2 = flash_stm32_option_bytes_lock(dev, true);
61 		if (!rc) {
62 			rc = rc2;
63 		}
64 	}
65 
66 	if (result != NULL) {
67 #ifdef CONFIG_USERSPACE
68 		struct flash_stm32_ex_op_sector_wp_out out_copy;
69 
70 		if (syscall_trap) {
71 			result = &out_copy;
72 		}
73 #endif
74 		rc2 = flash_stm32_get_wp_sectors(dev, &result->protected_mask);
75 		if (!rc) {
76 			rc = rc2;
77 		}
78 
79 #ifdef CONFIG_USERSPACE
80 		if (syscall_trap) {
81 			K_OOPS(k_usermode_to_copy(out, result, sizeof(out_copy)));
82 		}
83 #endif
84 	}
85 
86 	return rc;
87 }
88 #endif /* CONFIG_FLASH_STM32_WRITE_PROTECT */
89 
90 #if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION)
flash_stm32_ex_op_update_rdp(const struct device * dev,bool enable,bool permanent)91 int flash_stm32_ex_op_update_rdp(const struct device *dev, bool enable,
92 			   bool permanent)
93 {
94 	uint8_t current_level, target_level;
95 
96 	current_level = flash_stm32_get_rdp_level(dev);
97 	target_level = current_level;
98 
99 	/*
100 	 * 0xAA = RDP level 0 (no protection)
101 	 * 0xCC = RDP level 2 (permanent protection)
102 	 * others = RDP level 1 (protection active)
103 	 */
104 	switch (current_level) {
105 	case FLASH_STM32_RDP2:
106 		if (!enable || !permanent) {
107 			LOG_DBG("RDP level 2 is permanent and can't be changed!");
108 			return -ENOTSUP;
109 		}
110 		break;
111 	case FLASH_STM32_RDP0:
112 		if (enable) {
113 			target_level = FLASH_STM32_RDP1;
114 			if (permanent) {
115 #if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION_PERMANENT_ALLOW)
116 				target_level = FLASH_STM32_RDP2;
117 #else
118 				LOG_DBG("Permanent readout protection (RDP "
119 					"level 0 -> 2) not allowed");
120 				return -ENOTSUP;
121 #endif
122 			}
123 		}
124 		break;
125 	default: /* FLASH_STM32_RDP1 */
126 		if (enable && permanent) {
127 #if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION_PERMANENT_ALLOW)
128 			target_level = FLASH_STM32_RDP2;
129 #else
130 			LOG_DBG("Permanent readout protection (RDP "
131 				"level 1 -> 2) not allowed");
132 			return -ENOTSUP;
133 #endif
134 		}
135 		if (!enable) {
136 #if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION_DISABLE_ALLOW)
137 			target_level = FLASH_STM32_RDP0;
138 #else
139 			LOG_DBG("Disabling readout protection (RDP "
140 				"level 1 -> 0) not allowed");
141 			return -EACCES;
142 #endif
143 		}
144 	}
145 
146 	/* Update RDP level if needed */
147 	if (current_level != target_level) {
148 		LOG_INF("RDP changed from 0x%02x to 0x%02x", current_level,
149 			target_level);
150 
151 		flash_stm32_set_rdp_level(dev, target_level);
152 	}
153 	return 0;
154 }
155 
flash_stm32_ex_op_rdp(const struct device * dev,const uintptr_t in,void * out)156 int flash_stm32_ex_op_rdp(const struct device *dev, const uintptr_t in,
157 			  void *out)
158 {
159 	const struct flash_stm32_ex_op_rdp *request =
160 		(const struct flash_stm32_ex_op_rdp *)in;
161 	struct flash_stm32_ex_op_rdp *result =
162 		(struct flash_stm32_ex_op_rdp *)out;
163 	uint8_t current_level;
164 
165 #ifdef CONFIG_USERSPACE
166 	struct flash_stm32_ex_op_rdp copy;
167 	bool syscall_trap = z_syscall_trap();
168 #endif
169 	int rc = 0, rc2 = 0;
170 
171 	if (request != NULL) {
172 #ifdef CONFIG_USERSPACE
173 		if (syscall_trap) {
174 			K_OOPS(k_usermode_from_copy(&copy, request, sizeof(copy)));
175 			request = &copy;
176 		}
177 #endif
178 		rc = flash_stm32_option_bytes_lock(dev, false);
179 		if (rc == 0) {
180 			rc = flash_stm32_ex_op_update_rdp(dev, request->enable,
181 						    request->permanent);
182 		}
183 
184 		rc2 = flash_stm32_option_bytes_lock(dev, true);
185 		if (!rc) {
186 			rc = rc2;
187 		}
188 	}
189 
190 	if (result != NULL) {
191 #ifdef CONFIG_USERSPACE
192 		if (syscall_trap) {
193 			result = &copy;
194 		}
195 #endif
196 
197 		current_level = flash_stm32_get_rdp_level(dev);
198 
199 		/*
200 		 * 0xAA = RDP level 0 (no protection)
201 		 * 0xCC = RDP level 2 (permanent protection)
202 		 * others = RDP level 1 (protection active)
203 		 */
204 		switch (current_level) {
205 		case FLASH_STM32_RDP2:
206 			result->enable = true;
207 			result->permanent = true;
208 			break;
209 		case FLASH_STM32_RDP0:
210 			result->enable = false;
211 			result->permanent = false;
212 			break;
213 		default: /* FLASH_STM32_RDP1 */
214 			result->enable = true;
215 			result->permanent = false;
216 		}
217 
218 #ifdef CONFIG_USERSPACE
219 		if (syscall_trap) {
220 			K_OOPS(k_usermode_to_copy(out, result, sizeof(copy)));
221 		}
222 #endif
223 	}
224 
225 	return rc;
226 }
227 #endif /* CONFIG_FLASH_STM32_READOUT_PROTECTION */
228 
flash_stm32_ex_op(const struct device * dev,uint16_t code,const uintptr_t in,void * out)229 int flash_stm32_ex_op(const struct device *dev, uint16_t code,
230 			     const uintptr_t in, void *out)
231 {
232 	int rv = -ENOTSUP;
233 
234 	flash_stm32_sem_take(dev);
235 
236 	switch (code) {
237 #if defined(CONFIG_FLASH_STM32_WRITE_PROTECT)
238 	case FLASH_STM32_EX_OP_SECTOR_WP:
239 		rv = flash_stm32_ex_op_sector_wp(dev, in, out);
240 		break;
241 #endif /* CONFIG_FLASH_STM32_WRITE_PROTECT */
242 #if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION)
243 	case FLASH_STM32_EX_OP_RDP:
244 		rv = flash_stm32_ex_op_rdp(dev, in, out);
245 		break;
246 #endif /* CONFIG_FLASH_STM32_READOUT_PROTECTION */
247 #if defined(CONFIG_FLASH_STM32_BLOCK_REGISTERS)
248 	case FLASH_STM32_EX_OP_BLOCK_OPTION_REG:
249 		rv = flash_stm32_option_bytes_disable(dev);
250 		break;
251 	case FLASH_STM32_EX_OP_BLOCK_CONTROL_REG:
252 		rv = flash_stm32_control_register_disable(dev);
253 		break;
254 #endif /* CONFIG_FLASH_STM32_BLOCK_REGISTERS */
255 #if defined(CONFIG_FLASH_STM32_OPTION_BYTES) && ( \
256 		defined(CONFIG_DT_HAS_ST_STM32F4_FLASH_CONTROLLER_ENABLED) || \
257 		defined(CONFIG_DT_HAS_ST_STM32F7_FLASH_CONTROLLER_ENABLED) || \
258 		defined(CONFIG_DT_HAS_ST_STM32G4_FLASH_CONTROLLER_ENABLED) || \
259 		defined(CONFIG_DT_HAS_ST_STM32L4_FLASH_CONTROLLER_ENABLED))
260 	case FLASH_STM32_EX_OP_OPTB_READ:
261 		if (out == NULL) {
262 			rv = -EINVAL;
263 			break;
264 		}
265 
266 		*(uint32_t *)out = flash_stm32_option_bytes_read(dev);
267 		rv = 0;
268 
269 		break;
270 	case FLASH_STM32_EX_OP_OPTB_WRITE:
271 		int rv2;
272 
273 		rv = flash_stm32_option_bytes_lock(dev, false);
274 		if (rv > 0) {
275 			break;
276 		}
277 
278 		rv2 = flash_stm32_option_bytes_write(dev, UINT32_MAX, (uint32_t)in);
279 		/* returned later, we always re-lock */
280 
281 		rv = flash_stm32_option_bytes_lock(dev, true);
282 		if (rv > 0) {
283 			break;
284 		}
285 
286 		rv = rv2;
287 
288 		break;
289 #endif
290 	}
291 
292 	flash_stm32_sem_give(dev);
293 
294 	return rv;
295 }
296