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 * $FreeBSD: stable/11/sys/dev/mlxfw/mlxfw_fsm.c 347285 2019-05-08 10:49:05Z hselasky $
32 */
33
34#define pr_fmt(fmt) "mlxfw: " fmt
35
36#include <linux/kernel.h>
37#include <linux/module.h>
38#include <linux/delay.h>
39
40#include "mlxfw.h"
41#include "mlxfw_mfa2.h"
42
43#define MLXFW_FSM_STATE_WAIT_CYCLE_MS 200
44#define MLXFW_FSM_STATE_WAIT_TIMEOUT_MS 30000
45#define MLXFW_FSM_STATE_WAIT_ROUNDS \
46	(MLXFW_FSM_STATE_WAIT_TIMEOUT_MS / MLXFW_FSM_STATE_WAIT_CYCLE_MS)
47#define MLXFW_FSM_MAX_COMPONENT_SIZE (10 * (1 << 20))
48
49static const char * const mlxfw_fsm_state_err_str[] = {
50	[MLXFW_FSM_STATE_ERR_ERROR] =
51		"general error",
52	[MLXFW_FSM_STATE_ERR_REJECTED_DIGEST_ERR] =
53		"component hash mismatch",
54	[MLXFW_FSM_STATE_ERR_REJECTED_NOT_APPLICABLE] =
55		"component not applicable",
56	[MLXFW_FSM_STATE_ERR_REJECTED_UNKNOWN_KEY] =
57		"unknown key",
58	[MLXFW_FSM_STATE_ERR_REJECTED_AUTH_FAILED] =
59		"authentication failed",
60	[MLXFW_FSM_STATE_ERR_REJECTED_UNSIGNED] =
61		"component was not signed",
62	[MLXFW_FSM_STATE_ERR_REJECTED_KEY_NOT_APPLICABLE] =
63		"key not applicable",
64	[MLXFW_FSM_STATE_ERR_REJECTED_BAD_FORMAT] =
65		"bad format",
66	[MLXFW_FSM_STATE_ERR_BLOCKED_PENDING_RESET] =
67		"pending reset",
68	[MLXFW_FSM_STATE_ERR_MAX] =
69		"unknown error"
70};
71
72static int mlxfw_fsm_state_wait(struct mlxfw_dev *mlxfw_dev, u32 fwhandle,
73				enum mlxfw_fsm_state fsm_state)
74{
75	enum mlxfw_fsm_state_err fsm_state_err;
76	enum mlxfw_fsm_state curr_fsm_state;
77	int times;
78	int err;
79
80	times = MLXFW_FSM_STATE_WAIT_ROUNDS;
81retry:
82	err = mlxfw_dev->ops->fsm_query_state(mlxfw_dev, fwhandle,
83					      &curr_fsm_state, &fsm_state_err);
84	if (err)
85		return err;
86
87	if (fsm_state_err != MLXFW_FSM_STATE_ERR_OK) {
88		pr_err("Firmware flash failed: %s\n",
89		       mlxfw_fsm_state_err_str[fsm_state_err]);
90		return -EINVAL;
91	}
92	if (curr_fsm_state != fsm_state) {
93		if (--times == 0) {
94			pr_err("Timeout reached on FSM state change");
95			return -ETIMEDOUT;
96		}
97		msleep(MLXFW_FSM_STATE_WAIT_CYCLE_MS);
98		goto retry;
99	}
100	return 0;
101}
102
103#define MLXFW_ALIGN_DOWN(x, align_bits) ((x) & ~((1 << (align_bits)) - 1))
104#define MLXFW_ALIGN_UP(x, align_bits) \
105		MLXFW_ALIGN_DOWN((x) + ((1 << (align_bits)) - 1), (align_bits))
106
107static int mlxfw_flash_component(struct mlxfw_dev *mlxfw_dev,
108				 u32 fwhandle,
109				 struct mlxfw_mfa2_component *comp)
110{
111	u16 comp_max_write_size;
112	u8 comp_align_bits;
113	u32 comp_max_size;
114	u16 block_size;
115	u8 *block_ptr;
116	u32 offset;
117	int err;
118
119	err = mlxfw_dev->ops->component_query(mlxfw_dev, comp->index,
120					      &comp_max_size, &comp_align_bits,
121					      &comp_max_write_size);
122	if (err)
123		return err;
124
125	comp_max_size = min_t(u32, comp_max_size, MLXFW_FSM_MAX_COMPONENT_SIZE);
126	if (comp->data_size > comp_max_size) {
127		pr_err("Component %d is of size %d which is bigger than limit %d\n",
128		       comp->index, comp->data_size, comp_max_size);
129		return -EINVAL;
130	}
131
132	comp_max_write_size = MLXFW_ALIGN_DOWN(comp_max_write_size,
133					       comp_align_bits);
134
135	pr_debug("Component update\n");
136	err = mlxfw_dev->ops->fsm_component_update(mlxfw_dev, fwhandle,
137						   comp->index,
138						   comp->data_size);
139	if (err)
140		return err;
141
142	err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle,
143				   MLXFW_FSM_STATE_DOWNLOAD);
144	if (err)
145		goto err_out;
146
147	pr_debug("Component download\n");
148	for (offset = 0;
149	     offset < MLXFW_ALIGN_UP(comp->data_size, comp_align_bits);
150	     offset += comp_max_write_size) {
151		block_ptr = comp->data + offset;
152		block_size = (u16) min_t(u32, comp->data_size - offset,
153					 comp_max_write_size);
154		err = mlxfw_dev->ops->fsm_block_download(mlxfw_dev, fwhandle,
155							 block_ptr, block_size,
156							 offset);
157		if (err)
158			goto err_out;
159	}
160
161	pr_debug("Component verify\n");
162	err = mlxfw_dev->ops->fsm_component_verify(mlxfw_dev, fwhandle,
163						   comp->index);
164	if (err)
165		goto err_out;
166
167	err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle, MLXFW_FSM_STATE_LOCKED);
168	if (err)
169		goto err_out;
170	return 0;
171
172err_out:
173	mlxfw_dev->ops->fsm_cancel(mlxfw_dev, fwhandle);
174	return err;
175}
176
177static int mlxfw_flash_components(struct mlxfw_dev *mlxfw_dev, u32 fwhandle,
178				  struct mlxfw_mfa2_file *mfa2_file)
179{
180	u32 component_count;
181	int err;
182	int i;
183
184	err = mlxfw_mfa2_file_component_count(mfa2_file, mlxfw_dev->psid,
185					      mlxfw_dev->psid_size,
186					      &component_count);
187	if (err) {
188		pr_err("Could not find device PSID in MFA2 file\n");
189		return err;
190	}
191
192	for (i = 0; i < component_count; i++) {
193		struct mlxfw_mfa2_component *comp;
194
195		comp = mlxfw_mfa2_file_component_get(mfa2_file, mlxfw_dev->psid,
196						     mlxfw_dev->psid_size, i);
197		if (IS_ERR(comp))
198			return PTR_ERR(comp);
199
200		pr_info("Flashing component type %d\n", comp->index);
201		err = mlxfw_flash_component(mlxfw_dev, fwhandle, comp);
202		mlxfw_mfa2_file_component_put(comp);
203		if (err)
204			return err;
205	}
206	return 0;
207}
208
209int mlxfw_firmware_flash(struct mlxfw_dev *mlxfw_dev,
210			 const struct firmware *firmware)
211{
212	struct mlxfw_mfa2_file *mfa2_file;
213	u32 fwhandle;
214	int err;
215
216	if (!mlxfw_mfa2_check(firmware)) {
217		pr_err("Firmware file is not MFA2\n");
218		return -EINVAL;
219	}
220
221	mfa2_file = mlxfw_mfa2_file_init(firmware);
222	if (IS_ERR(mfa2_file))
223		return PTR_ERR(mfa2_file);
224
225	pr_info("Initialize firmware flash process\n");
226	err = mlxfw_dev->ops->fsm_lock(mlxfw_dev, &fwhandle);
227	if (err) {
228		pr_err("Could not lock the firmware FSM\n");
229		goto err_fsm_lock;
230	}
231
232	err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle,
233				   MLXFW_FSM_STATE_LOCKED);
234	if (err)
235		goto err_state_wait_idle_to_locked;
236
237	err = mlxfw_flash_components(mlxfw_dev, fwhandle, mfa2_file);
238	if (err)
239		goto err_flash_components;
240
241	pr_debug("Activate image\n");
242	err = mlxfw_dev->ops->fsm_activate(mlxfw_dev, fwhandle);
243	if (err) {
244		pr_err("Could not activate the downloaded image\n");
245		goto err_fsm_activate;
246	}
247
248	err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle, MLXFW_FSM_STATE_LOCKED);
249	if (err)
250		goto err_state_wait_activate_to_locked;
251
252	pr_debug("Handle release\n");
253	mlxfw_dev->ops->fsm_release(mlxfw_dev, fwhandle);
254
255	pr_info("Firmware flash done.\n");
256	mlxfw_mfa2_file_fini(mfa2_file);
257	return 0;
258
259err_state_wait_activate_to_locked:
260err_fsm_activate:
261err_flash_components:
262err_state_wait_idle_to_locked:
263	mlxfw_dev->ops->fsm_release(mlxfw_dev, fwhandle);
264err_fsm_lock:
265	mlxfw_mfa2_file_fini(mfa2_file);
266	return err;
267}
268EXPORT_SYMBOL(mlxfw_firmware_flash);
269
270MODULE_LICENSE("Dual BSD/GPL");
271MODULE_AUTHOR("Yotam Gigi <yotamg@mellanox.com>");
272MODULE_DESCRIPTION("Mellanox firmware flash lib");
273MODULE_VERSION(mlxfw, 1);
274MODULE_DEPEND(mlxfw, linuxkpi, 1, 1, 1);
275MODULE_DEPEND(mlxfw, xz, 1, 1, 1);
276