1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 2017-2019 Mellanox Technologies.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of Mellanox nor the names of contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#define pr_fmt(fmt) "mlxfw: " fmt
33
34#include <linux/kernel.h>
35#include <linux/module.h>
36#include <linux/delay.h>
37
38#include "mlxfw.h"
39#include "mlxfw_mfa2.h"
40
41#define MLXFW_FSM_STATE_WAIT_CYCLE_MS 200
42#define MLXFW_FSM_STATE_WAIT_TIMEOUT_MS 30000
43#define MLXFW_FSM_STATE_WAIT_ROUNDS \
44	(MLXFW_FSM_STATE_WAIT_TIMEOUT_MS / MLXFW_FSM_STATE_WAIT_CYCLE_MS)
45#define MLXFW_FSM_MAX_COMPONENT_SIZE (10 * (1 << 20))
46
47static const char * const mlxfw_fsm_state_err_str[] = {
48	[MLXFW_FSM_STATE_ERR_ERROR] =
49		"general error",
50	[MLXFW_FSM_STATE_ERR_REJECTED_DIGEST_ERR] =
51		"component hash mismatch",
52	[MLXFW_FSM_STATE_ERR_REJECTED_NOT_APPLICABLE] =
53		"component not applicable",
54	[MLXFW_FSM_STATE_ERR_REJECTED_UNKNOWN_KEY] =
55		"unknown key",
56	[MLXFW_FSM_STATE_ERR_REJECTED_AUTH_FAILED] =
57		"authentication failed",
58	[MLXFW_FSM_STATE_ERR_REJECTED_UNSIGNED] =
59		"component was not signed",
60	[MLXFW_FSM_STATE_ERR_REJECTED_KEY_NOT_APPLICABLE] =
61		"key not applicable",
62	[MLXFW_FSM_STATE_ERR_REJECTED_BAD_FORMAT] =
63		"bad format",
64	[MLXFW_FSM_STATE_ERR_BLOCKED_PENDING_RESET] =
65		"pending reset",
66	[MLXFW_FSM_STATE_ERR_MAX] =
67		"unknown error"
68};
69
70static int mlxfw_fsm_state_wait(struct mlxfw_dev *mlxfw_dev, u32 fwhandle,
71				enum mlxfw_fsm_state fsm_state)
72{
73	enum mlxfw_fsm_state_err fsm_state_err;
74	enum mlxfw_fsm_state curr_fsm_state;
75	int times;
76	int err;
77
78	times = MLXFW_FSM_STATE_WAIT_ROUNDS;
79retry:
80	err = mlxfw_dev->ops->fsm_query_state(mlxfw_dev, fwhandle,
81					      &curr_fsm_state, &fsm_state_err);
82	if (err)
83		return err;
84
85	if (fsm_state_err != MLXFW_FSM_STATE_ERR_OK) {
86		pr_err("Firmware flash failed: %s\n",
87		       mlxfw_fsm_state_err_str[fsm_state_err]);
88		return -EINVAL;
89	}
90	if (curr_fsm_state != fsm_state) {
91		if (--times == 0) {
92			pr_err("Timeout reached on FSM state change");
93			return -ETIMEDOUT;
94		}
95		msleep(MLXFW_FSM_STATE_WAIT_CYCLE_MS);
96		goto retry;
97	}
98	return 0;
99}
100
101#define MLXFW_ALIGN_DOWN(x, align_bits) ((x) & ~((1 << (align_bits)) - 1))
102#define MLXFW_ALIGN_UP(x, align_bits) \
103		MLXFW_ALIGN_DOWN((x) + ((1 << (align_bits)) - 1), (align_bits))
104
105static int mlxfw_flash_component(struct mlxfw_dev *mlxfw_dev,
106				 u32 fwhandle,
107				 struct mlxfw_mfa2_component *comp)
108{
109	u16 comp_max_write_size;
110	u8 comp_align_bits;
111	u32 comp_max_size;
112	u16 block_size;
113	u8 *block_ptr;
114	u32 offset;
115	int err;
116
117	err = mlxfw_dev->ops->component_query(mlxfw_dev, comp->index,
118					      &comp_max_size, &comp_align_bits,
119					      &comp_max_write_size);
120	if (err)
121		return err;
122
123	comp_max_size = min_t(u32, comp_max_size, MLXFW_FSM_MAX_COMPONENT_SIZE);
124	if (comp->data_size > comp_max_size) {
125		pr_err("Component %d is of size %d which is bigger than limit %d\n",
126		       comp->index, comp->data_size, comp_max_size);
127		return -EINVAL;
128	}
129
130	comp_max_write_size = MLXFW_ALIGN_DOWN(comp_max_write_size,
131					       comp_align_bits);
132
133	pr_debug("Component update\n");
134	err = mlxfw_dev->ops->fsm_component_update(mlxfw_dev, fwhandle,
135						   comp->index,
136						   comp->data_size);
137	if (err)
138		return err;
139
140	err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle,
141				   MLXFW_FSM_STATE_DOWNLOAD);
142	if (err)
143		goto err_out;
144
145	pr_debug("Component download\n");
146	for (offset = 0;
147	     offset < MLXFW_ALIGN_UP(comp->data_size, comp_align_bits);
148	     offset += comp_max_write_size) {
149		block_ptr = comp->data + offset;
150		block_size = (u16) min_t(u32, comp->data_size - offset,
151					 comp_max_write_size);
152		err = mlxfw_dev->ops->fsm_block_download(mlxfw_dev, fwhandle,
153							 block_ptr, block_size,
154							 offset);
155		if (err)
156			goto err_out;
157	}
158
159	pr_debug("Component verify\n");
160	err = mlxfw_dev->ops->fsm_component_verify(mlxfw_dev, fwhandle,
161						   comp->index);
162	if (err)
163		goto err_out;
164
165	err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle, MLXFW_FSM_STATE_LOCKED);
166	if (err)
167		goto err_out;
168	return 0;
169
170err_out:
171	mlxfw_dev->ops->fsm_cancel(mlxfw_dev, fwhandle);
172	return err;
173}
174
175static int mlxfw_flash_components(struct mlxfw_dev *mlxfw_dev, u32 fwhandle,
176				  struct mlxfw_mfa2_file *mfa2_file)
177{
178	u32 component_count;
179	int err;
180	int i;
181
182	err = mlxfw_mfa2_file_component_count(mfa2_file, mlxfw_dev->psid,
183					      mlxfw_dev->psid_size,
184					      &component_count);
185	if (err) {
186		pr_err("Could not find device PSID in MFA2 file\n");
187		return err;
188	}
189
190	for (i = 0; i < component_count; i++) {
191		struct mlxfw_mfa2_component *comp;
192
193		comp = mlxfw_mfa2_file_component_get(mfa2_file, mlxfw_dev->psid,
194						     mlxfw_dev->psid_size, i);
195		if (IS_ERR(comp))
196			return PTR_ERR(comp);
197
198		pr_info("Flashing component type %d\n", comp->index);
199		err = mlxfw_flash_component(mlxfw_dev, fwhandle, comp);
200		mlxfw_mfa2_file_component_put(comp);
201		if (err)
202			return err;
203	}
204	return 0;
205}
206
207int mlxfw_firmware_flash(struct mlxfw_dev *mlxfw_dev,
208			 const struct firmware *firmware)
209{
210	struct mlxfw_mfa2_file *mfa2_file;
211	u32 fwhandle;
212	int err;
213
214	if (!mlxfw_mfa2_check(firmware)) {
215		pr_err("Firmware file is not MFA2\n");
216		return -EINVAL;
217	}
218
219	mfa2_file = mlxfw_mfa2_file_init(firmware);
220	if (IS_ERR(mfa2_file))
221		return PTR_ERR(mfa2_file);
222
223	pr_info("Initialize firmware flash process\n");
224	err = mlxfw_dev->ops->fsm_lock(mlxfw_dev, &fwhandle);
225	if (err) {
226		pr_err("Could not lock the firmware FSM\n");
227		goto err_fsm_lock;
228	}
229
230	err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle,
231				   MLXFW_FSM_STATE_LOCKED);
232	if (err)
233		goto err_state_wait_idle_to_locked;
234
235	err = mlxfw_flash_components(mlxfw_dev, fwhandle, mfa2_file);
236	if (err)
237		goto err_flash_components;
238
239	pr_debug("Activate image\n");
240	err = mlxfw_dev->ops->fsm_activate(mlxfw_dev, fwhandle);
241	if (err) {
242		pr_err("Could not activate the downloaded image\n");
243		goto err_fsm_activate;
244	}
245
246	err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle, MLXFW_FSM_STATE_LOCKED);
247	if (err)
248		goto err_state_wait_activate_to_locked;
249
250	pr_debug("Handle release\n");
251	mlxfw_dev->ops->fsm_release(mlxfw_dev, fwhandle);
252
253	pr_info("Firmware flash done.\n");
254	mlxfw_mfa2_file_fini(mfa2_file);
255	return 0;
256
257err_state_wait_activate_to_locked:
258err_fsm_activate:
259err_flash_components:
260err_state_wait_idle_to_locked:
261	mlxfw_dev->ops->fsm_release(mlxfw_dev, fwhandle);
262err_fsm_lock:
263	mlxfw_mfa2_file_fini(mfa2_file);
264	return err;
265}
266EXPORT_SYMBOL(mlxfw_firmware_flash);
267
268MODULE_LICENSE("Dual BSD/GPL");
269MODULE_AUTHOR("Yotam Gigi <yotamg@mellanox.com>");
270MODULE_DESCRIPTION("Mellanox firmware flash lib");
271MODULE_VERSION(mlxfw, 1);
272MODULE_DEPEND(mlxfw, linuxkpi, 1, 1, 1);
273MODULE_DEPEND(mlxfw, xz, 1, 1, 1);
274