1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /* Copyright (c) 2017-2019 Mellanox Technologies. All rights reserved */ |
3 | |
4 | #define pr_fmt(fmt) "mlxfw: " fmt |
5 | |
6 | #include <linux/kernel.h> |
7 | #include <linux/module.h> |
8 | #include <linux/delay.h> |
9 | |
10 | #include "mlxfw.h" |
11 | #include "mlxfw_mfa2.h" |
12 | |
13 | #define MLXFW_FSM_STATE_WAIT_CYCLE_MS 200 |
14 | #define MLXFW_FSM_STATE_WAIT_TIMEOUT_MS 30000 |
15 | #define MLXFW_FSM_STATE_WAIT_ROUNDS \ |
16 | (MLXFW_FSM_STATE_WAIT_TIMEOUT_MS / MLXFW_FSM_STATE_WAIT_CYCLE_MS) |
17 | #define MLXFW_FSM_MAX_COMPONENT_SIZE (10 * (1 << 20)) |
18 | |
19 | static const int mlxfw_fsm_state_errno[] = { |
20 | [MLXFW_FSM_STATE_ERR_ERROR] = -EIO, |
21 | [MLXFW_FSM_STATE_ERR_REJECTED_DIGEST_ERR] = -EBADMSG, |
22 | [MLXFW_FSM_STATE_ERR_REJECTED_NOT_APPLICABLE] = -ENOENT, |
23 | [MLXFW_FSM_STATE_ERR_REJECTED_UNKNOWN_KEY] = -ENOKEY, |
24 | [MLXFW_FSM_STATE_ERR_REJECTED_AUTH_FAILED] = -EACCES, |
25 | [MLXFW_FSM_STATE_ERR_REJECTED_UNSIGNED] = -EKEYREVOKED, |
26 | [MLXFW_FSM_STATE_ERR_REJECTED_KEY_NOT_APPLICABLE] = -EKEYREJECTED, |
27 | [MLXFW_FSM_STATE_ERR_REJECTED_BAD_FORMAT] = -ENOEXEC, |
28 | [MLXFW_FSM_STATE_ERR_BLOCKED_PENDING_RESET] = -EBUSY, |
29 | [MLXFW_FSM_STATE_ERR_MAX] = -EINVAL |
30 | }; |
31 | |
32 | #define MLXFW_ERR_PRFX "Firmware flash failed: " |
33 | #define MLXFW_ERR_MSG(fwdev, extack, msg, err) do { \ |
34 | mlxfw_err(fwdev, "%s, err (%d)\n", MLXFW_ERR_PRFX msg, err); \ |
35 | NL_SET_ERR_MSG_MOD(extack, MLXFW_ERR_PRFX msg); \ |
36 | } while (0) |
37 | |
38 | static int mlxfw_fsm_state_err(struct mlxfw_dev *mlxfw_dev, |
39 | struct netlink_ext_ack *extack, |
40 | enum mlxfw_fsm_state_err err) |
41 | { |
42 | enum mlxfw_fsm_state_err fsm_state_err; |
43 | |
44 | fsm_state_err = min_t(enum mlxfw_fsm_state_err, err, |
45 | MLXFW_FSM_STATE_ERR_MAX); |
46 | |
47 | switch (fsm_state_err) { |
48 | case MLXFW_FSM_STATE_ERR_ERROR: |
49 | MLXFW_ERR_MSG(mlxfw_dev, extack, "general error" , err); |
50 | break; |
51 | case MLXFW_FSM_STATE_ERR_REJECTED_DIGEST_ERR: |
52 | MLXFW_ERR_MSG(mlxfw_dev, extack, "component hash mismatch" , err); |
53 | break; |
54 | case MLXFW_FSM_STATE_ERR_REJECTED_NOT_APPLICABLE: |
55 | MLXFW_ERR_MSG(mlxfw_dev, extack, "component not applicable" , err); |
56 | break; |
57 | case MLXFW_FSM_STATE_ERR_REJECTED_UNKNOWN_KEY: |
58 | MLXFW_ERR_MSG(mlxfw_dev, extack, "unknown key" , err); |
59 | break; |
60 | case MLXFW_FSM_STATE_ERR_REJECTED_AUTH_FAILED: |
61 | MLXFW_ERR_MSG(mlxfw_dev, extack, "authentication failed" , err); |
62 | break; |
63 | case MLXFW_FSM_STATE_ERR_REJECTED_UNSIGNED: |
64 | MLXFW_ERR_MSG(mlxfw_dev, extack, "component was not signed" , err); |
65 | break; |
66 | case MLXFW_FSM_STATE_ERR_REJECTED_KEY_NOT_APPLICABLE: |
67 | MLXFW_ERR_MSG(mlxfw_dev, extack, "key not applicable" , err); |
68 | break; |
69 | case MLXFW_FSM_STATE_ERR_REJECTED_BAD_FORMAT: |
70 | MLXFW_ERR_MSG(mlxfw_dev, extack, "bad format" , err); |
71 | break; |
72 | case MLXFW_FSM_STATE_ERR_BLOCKED_PENDING_RESET: |
73 | MLXFW_ERR_MSG(mlxfw_dev, extack, "pending reset" , err); |
74 | break; |
75 | case MLXFW_FSM_STATE_ERR_OK: |
76 | case MLXFW_FSM_STATE_ERR_MAX: |
77 | MLXFW_ERR_MSG(mlxfw_dev, extack, "unknown error" , err); |
78 | break; |
79 | } |
80 | |
81 | return mlxfw_fsm_state_errno[fsm_state_err]; |
82 | }; |
83 | |
84 | static int mlxfw_fsm_state_wait(struct mlxfw_dev *mlxfw_dev, u32 fwhandle, |
85 | enum mlxfw_fsm_state fsm_state, |
86 | struct netlink_ext_ack *extack) |
87 | { |
88 | enum mlxfw_fsm_state_err fsm_state_err; |
89 | enum mlxfw_fsm_state curr_fsm_state; |
90 | int times; |
91 | int err; |
92 | |
93 | times = MLXFW_FSM_STATE_WAIT_ROUNDS; |
94 | retry: |
95 | err = mlxfw_dev->ops->fsm_query_state(mlxfw_dev, fwhandle, |
96 | &curr_fsm_state, &fsm_state_err); |
97 | if (err) { |
98 | MLXFW_ERR_MSG(mlxfw_dev, extack, "FSM state query failed" , err); |
99 | return err; |
100 | } |
101 | |
102 | if (fsm_state_err != MLXFW_FSM_STATE_ERR_OK) |
103 | return mlxfw_fsm_state_err(mlxfw_dev, extack, err: fsm_state_err); |
104 | |
105 | if (curr_fsm_state != fsm_state) { |
106 | if (--times == 0) { |
107 | MLXFW_ERR_MSG(mlxfw_dev, extack, |
108 | "Timeout reached on FSM state change" , -ETIMEDOUT); |
109 | return -ETIMEDOUT; |
110 | } |
111 | msleep(MLXFW_FSM_STATE_WAIT_CYCLE_MS); |
112 | goto retry; |
113 | } |
114 | return 0; |
115 | } |
116 | |
117 | static int |
118 | mlxfw_fsm_reactivate_err(struct mlxfw_dev *mlxfw_dev, |
119 | struct netlink_ext_ack *extack, u8 err) |
120 | { |
121 | enum mlxfw_fsm_reactivate_status status; |
122 | |
123 | #define MXFW_REACT_PRFX "Reactivate FSM: " |
124 | #define MLXFW_REACT_ERR(msg, err) \ |
125 | MLXFW_ERR_MSG(mlxfw_dev, extack, MXFW_REACT_PRFX msg, err) |
126 | |
127 | status = min_t(enum mlxfw_fsm_reactivate_status, err, |
128 | MLXFW_FSM_REACTIVATE_STATUS_MAX); |
129 | |
130 | switch (status) { |
131 | case MLXFW_FSM_REACTIVATE_STATUS_BUSY: |
132 | MLXFW_REACT_ERR("busy" , err); |
133 | break; |
134 | case MLXFW_FSM_REACTIVATE_STATUS_PROHIBITED_FW_VER_ERR: |
135 | MLXFW_REACT_ERR("prohibited fw ver" , err); |
136 | break; |
137 | case MLXFW_FSM_REACTIVATE_STATUS_FIRST_PAGE_COPY_FAILED: |
138 | MLXFW_REACT_ERR("first page copy failed" , err); |
139 | break; |
140 | case MLXFW_FSM_REACTIVATE_STATUS_FIRST_PAGE_ERASE_FAILED: |
141 | MLXFW_REACT_ERR("first page erase failed" , err); |
142 | break; |
143 | case MLXFW_FSM_REACTIVATE_STATUS_FIRST_PAGE_RESTORE_FAILED: |
144 | MLXFW_REACT_ERR("first page restore failed" , err); |
145 | break; |
146 | case MLXFW_FSM_REACTIVATE_STATUS_CANDIDATE_FW_DEACTIVATION_FAILED: |
147 | MLXFW_REACT_ERR("candidate fw deactivation failed" , err); |
148 | break; |
149 | case MLXFW_FSM_REACTIVATE_STATUS_ERR_DEVICE_RESET_REQUIRED: |
150 | MLXFW_REACT_ERR("device reset required" , err); |
151 | break; |
152 | case MLXFW_FSM_REACTIVATE_STATUS_ERR_FW_PROGRAMMING_NEEDED: |
153 | MLXFW_REACT_ERR("fw programming needed" , err); |
154 | break; |
155 | case MLXFW_FSM_REACTIVATE_STATUS_FW_ALREADY_ACTIVATED: |
156 | MLXFW_REACT_ERR("fw already activated" , err); |
157 | break; |
158 | case MLXFW_FSM_REACTIVATE_STATUS_OK: |
159 | case MLXFW_FSM_REACTIVATE_STATUS_MAX: |
160 | MLXFW_REACT_ERR("unexpected error" , err); |
161 | break; |
162 | } |
163 | return -EREMOTEIO; |
164 | }; |
165 | |
166 | static int mlxfw_fsm_reactivate(struct mlxfw_dev *mlxfw_dev, |
167 | struct netlink_ext_ack *extack, |
168 | bool *supported) |
169 | { |
170 | u8 status; |
171 | int err; |
172 | |
173 | if (!mlxfw_dev->ops->fsm_reactivate) |
174 | return 0; |
175 | |
176 | err = mlxfw_dev->ops->fsm_reactivate(mlxfw_dev, &status); |
177 | if (err == -EOPNOTSUPP) { |
178 | *supported = false; |
179 | return 0; |
180 | } |
181 | |
182 | if (err) { |
183 | MLXFW_ERR_MSG(mlxfw_dev, extack, |
184 | "Could not reactivate firmware flash" , err); |
185 | return err; |
186 | } |
187 | |
188 | if (status == MLXFW_FSM_REACTIVATE_STATUS_OK || |
189 | status == MLXFW_FSM_REACTIVATE_STATUS_FW_ALREADY_ACTIVATED) |
190 | return 0; |
191 | |
192 | return mlxfw_fsm_reactivate_err(mlxfw_dev, extack, err: status); |
193 | } |
194 | |
195 | static void mlxfw_status_notify(struct mlxfw_dev *mlxfw_dev, |
196 | const char *msg, const char *comp_name, |
197 | u32 done_bytes, u32 total_bytes) |
198 | { |
199 | devlink_flash_update_status_notify(devlink: mlxfw_dev->devlink, status_msg: msg, component: comp_name, |
200 | done: done_bytes, total: total_bytes); |
201 | } |
202 | |
203 | #define MLXFW_ALIGN_DOWN(x, align_bits) ((x) & ~((1 << (align_bits)) - 1)) |
204 | #define MLXFW_ALIGN_UP(x, align_bits) \ |
205 | MLXFW_ALIGN_DOWN((x) + ((1 << (align_bits)) - 1), (align_bits)) |
206 | |
207 | static int mlxfw_flash_component(struct mlxfw_dev *mlxfw_dev, |
208 | u32 fwhandle, |
209 | struct mlxfw_mfa2_component *comp, |
210 | bool reactivate_supp, |
211 | struct netlink_ext_ack *extack) |
212 | { |
213 | u16 comp_max_write_size; |
214 | u8 comp_align_bits; |
215 | u32 comp_max_size; |
216 | char comp_name[8]; |
217 | u16 block_size; |
218 | u8 *block_ptr; |
219 | u32 offset; |
220 | int err; |
221 | |
222 | sprintf(buf: comp_name, fmt: "%u" , comp->index); |
223 | |
224 | err = mlxfw_dev->ops->component_query(mlxfw_dev, comp->index, |
225 | &comp_max_size, &comp_align_bits, |
226 | &comp_max_write_size); |
227 | if (err) { |
228 | MLXFW_ERR_MSG(mlxfw_dev, extack, "FSM component query failed" , err); |
229 | return err; |
230 | } |
231 | |
232 | comp_max_size = min_t(u32, comp_max_size, MLXFW_FSM_MAX_COMPONENT_SIZE); |
233 | if (comp->data_size > comp_max_size) { |
234 | MLXFW_ERR_MSG(mlxfw_dev, extack, |
235 | "Component size is bigger than limit" , -EINVAL); |
236 | return -EINVAL; |
237 | } |
238 | |
239 | comp_max_write_size = MLXFW_ALIGN_DOWN(comp_max_write_size, |
240 | comp_align_bits); |
241 | |
242 | mlxfw_dbg(mlxfw_dev, "Component update\n" ); |
243 | mlxfw_status_notify(mlxfw_dev, msg: "Updating component" , comp_name, done_bytes: 0, total_bytes: 0); |
244 | err = mlxfw_dev->ops->fsm_component_update(mlxfw_dev, fwhandle, |
245 | comp->index, |
246 | comp->data_size); |
247 | if (err) { |
248 | if (!reactivate_supp) |
249 | MLXFW_ERR_MSG(mlxfw_dev, extack, |
250 | "FSM component update failed, FW reactivate is not supported" , |
251 | err); |
252 | else |
253 | MLXFW_ERR_MSG(mlxfw_dev, extack, |
254 | "FSM component update failed" , err); |
255 | return err; |
256 | } |
257 | |
258 | err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle, |
259 | fsm_state: MLXFW_FSM_STATE_DOWNLOAD, extack); |
260 | if (err) |
261 | goto err_out; |
262 | |
263 | mlxfw_dbg(mlxfw_dev, "Component download\n" ); |
264 | mlxfw_status_notify(mlxfw_dev, msg: "Downloading component" , |
265 | comp_name, done_bytes: 0, total_bytes: comp->data_size); |
266 | for (offset = 0; |
267 | offset < MLXFW_ALIGN_UP(comp->data_size, comp_align_bits); |
268 | offset += comp_max_write_size) { |
269 | block_ptr = comp->data + offset; |
270 | block_size = (u16) min_t(u32, comp->data_size - offset, |
271 | comp_max_write_size); |
272 | err = mlxfw_dev->ops->fsm_block_download(mlxfw_dev, fwhandle, |
273 | block_ptr, block_size, |
274 | offset); |
275 | if (err) { |
276 | MLXFW_ERR_MSG(mlxfw_dev, extack, |
277 | "Component download failed" , err); |
278 | goto err_out; |
279 | } |
280 | mlxfw_status_notify(mlxfw_dev, msg: "Downloading component" , |
281 | comp_name, done_bytes: offset + block_size, |
282 | total_bytes: comp->data_size); |
283 | } |
284 | |
285 | mlxfw_dbg(mlxfw_dev, "Component verify\n" ); |
286 | mlxfw_status_notify(mlxfw_dev, msg: "Verifying component" , comp_name, done_bytes: 0, total_bytes: 0); |
287 | err = mlxfw_dev->ops->fsm_component_verify(mlxfw_dev, fwhandle, |
288 | comp->index); |
289 | if (err) { |
290 | MLXFW_ERR_MSG(mlxfw_dev, extack, |
291 | "FSM component verify failed" , err); |
292 | goto err_out; |
293 | } |
294 | |
295 | err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle, |
296 | fsm_state: MLXFW_FSM_STATE_LOCKED, extack); |
297 | if (err) |
298 | goto err_out; |
299 | return 0; |
300 | |
301 | err_out: |
302 | mlxfw_dev->ops->fsm_cancel(mlxfw_dev, fwhandle); |
303 | return err; |
304 | } |
305 | |
306 | static int mlxfw_flash_components(struct mlxfw_dev *mlxfw_dev, u32 fwhandle, |
307 | struct mlxfw_mfa2_file *mfa2_file, |
308 | bool reactivate_supp, |
309 | struct netlink_ext_ack *extack) |
310 | { |
311 | u32 component_count; |
312 | int err; |
313 | int i; |
314 | |
315 | err = mlxfw_mfa2_file_component_count(mfa2_file, psid: mlxfw_dev->psid, |
316 | psid_size: mlxfw_dev->psid_size, |
317 | p_count: &component_count); |
318 | if (err) { |
319 | MLXFW_ERR_MSG(mlxfw_dev, extack, |
320 | "Could not find device PSID in MFA2 file" , err); |
321 | return err; |
322 | } |
323 | |
324 | for (i = 0; i < component_count; i++) { |
325 | struct mlxfw_mfa2_component *comp; |
326 | |
327 | comp = mlxfw_mfa2_file_component_get(mfa2_file, psid: mlxfw_dev->psid, |
328 | psid_size: mlxfw_dev->psid_size, component_index: i); |
329 | if (IS_ERR(ptr: comp)) { |
330 | err = PTR_ERR(ptr: comp); |
331 | MLXFW_ERR_MSG(mlxfw_dev, extack, |
332 | "Failed to get MFA2 component" , err); |
333 | return err; |
334 | } |
335 | |
336 | mlxfw_info(mlxfw_dev, "Flashing component type %d\n" , |
337 | comp->index); |
338 | err = mlxfw_flash_component(mlxfw_dev, fwhandle, comp, |
339 | reactivate_supp, extack); |
340 | mlxfw_mfa2_file_component_put(component: comp); |
341 | if (err) |
342 | return err; |
343 | } |
344 | return 0; |
345 | } |
346 | |
347 | int mlxfw_firmware_flash(struct mlxfw_dev *mlxfw_dev, |
348 | const struct firmware *firmware, |
349 | struct netlink_ext_ack *extack) |
350 | { |
351 | struct mlxfw_mfa2_file *mfa2_file; |
352 | bool reactivate_supp = true; |
353 | u32 fwhandle; |
354 | int err; |
355 | |
356 | if (!mlxfw_mfa2_check(fw: firmware)) { |
357 | MLXFW_ERR_MSG(mlxfw_dev, extack, |
358 | "Firmware file is not MFA2" , -EINVAL); |
359 | return -EINVAL; |
360 | } |
361 | |
362 | mfa2_file = mlxfw_mfa2_file_init(fw: firmware); |
363 | if (IS_ERR(ptr: mfa2_file)) { |
364 | err = PTR_ERR(ptr: mfa2_file); |
365 | MLXFW_ERR_MSG(mlxfw_dev, extack, |
366 | "Failed to initialize MFA2 firmware file" , err); |
367 | return err; |
368 | } |
369 | |
370 | mlxfw_info(mlxfw_dev, "Initialize firmware flash process\n" ); |
371 | mlxfw_status_notify(mlxfw_dev, msg: "Initializing firmware flash process" , |
372 | NULL, done_bytes: 0, total_bytes: 0); |
373 | err = mlxfw_dev->ops->fsm_lock(mlxfw_dev, &fwhandle); |
374 | if (err) { |
375 | MLXFW_ERR_MSG(mlxfw_dev, extack, |
376 | "Could not lock the firmware FSM" , err); |
377 | goto err_fsm_lock; |
378 | } |
379 | |
380 | err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle, |
381 | fsm_state: MLXFW_FSM_STATE_LOCKED, extack); |
382 | if (err) |
383 | goto err_state_wait_idle_to_locked; |
384 | |
385 | err = mlxfw_fsm_reactivate(mlxfw_dev, extack, supported: &reactivate_supp); |
386 | if (err) |
387 | goto err_fsm_reactivate; |
388 | |
389 | err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle, |
390 | fsm_state: MLXFW_FSM_STATE_LOCKED, extack); |
391 | if (err) |
392 | goto err_state_wait_reactivate_to_locked; |
393 | |
394 | err = mlxfw_flash_components(mlxfw_dev, fwhandle, mfa2_file, |
395 | reactivate_supp, extack); |
396 | if (err) |
397 | goto err_flash_components; |
398 | |
399 | mlxfw_dbg(mlxfw_dev, "Activate image\n" ); |
400 | mlxfw_status_notify(mlxfw_dev, msg: "Activating image" , NULL, done_bytes: 0, total_bytes: 0); |
401 | err = mlxfw_dev->ops->fsm_activate(mlxfw_dev, fwhandle); |
402 | if (err) { |
403 | MLXFW_ERR_MSG(mlxfw_dev, extack, |
404 | "Could not activate the downloaded image" , err); |
405 | goto err_fsm_activate; |
406 | } |
407 | |
408 | err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle, |
409 | fsm_state: MLXFW_FSM_STATE_LOCKED, extack); |
410 | if (err) |
411 | goto err_state_wait_activate_to_locked; |
412 | |
413 | mlxfw_dbg(mlxfw_dev, "Handle release\n" ); |
414 | mlxfw_dev->ops->fsm_release(mlxfw_dev, fwhandle); |
415 | |
416 | mlxfw_info(mlxfw_dev, "Firmware flash done\n" ); |
417 | mlxfw_status_notify(mlxfw_dev, msg: "Firmware flash done" , NULL, done_bytes: 0, total_bytes: 0); |
418 | mlxfw_mfa2_file_fini(mfa2_file); |
419 | return 0; |
420 | |
421 | err_state_wait_activate_to_locked: |
422 | err_fsm_activate: |
423 | err_flash_components: |
424 | err_state_wait_reactivate_to_locked: |
425 | err_fsm_reactivate: |
426 | err_state_wait_idle_to_locked: |
427 | mlxfw_dev->ops->fsm_release(mlxfw_dev, fwhandle); |
428 | err_fsm_lock: |
429 | mlxfw_mfa2_file_fini(mfa2_file); |
430 | return err; |
431 | } |
432 | EXPORT_SYMBOL(mlxfw_firmware_flash); |
433 | |
434 | MODULE_LICENSE("Dual BSD/GPL" ); |
435 | MODULE_AUTHOR("Yotam Gigi <yotamg@mellanox.com>" ); |
436 | MODULE_DESCRIPTION("Mellanox firmware flash lib" ); |
437 | |