Commit d41987e9 authored by Edward Cree's avatar Edward Cree Committed by Jakub Kicinski
Browse files

sfc: extend NVRAM MCDI handlers



Support variable write-alignment, and background updates.  The latter
 allows other MCDI to continue while the device is processing an
 MC_CMD_NVRAM_UPDATE_FINISH, since this can take a long time owing to
 e.g. cryptographic signature verification.
Expose these handlers in mcdi.h, and build them even when
 CONFIG_SFC_MTD=n, so they can be used for devlink flash in a
 subsequent patch.

Signed-off-by: default avatarEdward Cree <ecree.xilinx@gmail.com>
Link: https://patch.msgid.link/de3d9e14fee69e15d95b46258401a93b75659f78.1739186253.git.ecree.xilinx@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent fd118a77
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -3501,7 +3501,7 @@ static int efx_ef10_mtd_probe_partition(struct efx_nic *efx,
	MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_METADATA_IN_LEN);
	MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_METADATA_OUT_LENMAX);
	const struct efx_ef10_nvram_type_info *info;
	size_t size, erase_size, outlen;
	size_t size, erase_size, write_size, outlen;
	int type_idx = 0;
	bool protected;
	int rc;
@@ -3516,7 +3516,8 @@ static int efx_ef10_mtd_probe_partition(struct efx_nic *efx,
	if (info->port != efx_port_num(efx))
		return -ENODEV;

	rc = efx_mcdi_nvram_info(efx, type, &size, &erase_size, &protected);
	rc = efx_mcdi_nvram_info(efx, type, &size, &erase_size, &write_size,
				 &protected);
	if (rc)
		return rc;
	if (protected &&
@@ -3561,6 +3562,8 @@ static int efx_ef10_mtd_probe_partition(struct efx_nic *efx,
	if (!erase_size)
		part->common.mtd.flags |= MTD_NO_ERASE;

	part->common.mtd.writesize = write_size;

	return 0;
}

+95 −20
Original line number Diff line number Diff line
@@ -1625,12 +1625,15 @@ static int efx_new_mcdi_nvram_types(struct efx_nic *efx, u32 *number,
	return rc;
}

#define EFX_MCDI_NVRAM_DEFAULT_WRITE_LEN 128

int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type,
			size_t *size_out, size_t *erase_size_out,
			bool *protected_out)
			size_t *write_size_out, bool *protected_out)
{
	MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_INFO_IN_LEN);
	MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_INFO_OUT_LEN);
	MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_INFO_V2_OUT_LEN);
	size_t write_size = 0;
	size_t outlen;
	int rc;

@@ -1645,6 +1648,12 @@ int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type,
		goto fail;
	}

	if (outlen >= MC_CMD_NVRAM_INFO_V2_OUT_LEN)
		write_size = MCDI_DWORD(outbuf, NVRAM_INFO_V2_OUT_WRITESIZE);
	else
		write_size = EFX_MCDI_NVRAM_DEFAULT_WRITE_LEN;

	*write_size_out = write_size;
	*size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_SIZE);
	*erase_size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_ERASESIZE);
	*protected_out = !!(MCDI_DWORD(outbuf, NVRAM_INFO_OUT_FLAGS) &
@@ -2163,11 +2172,9 @@ int efx_mcdi_nvram_metadata(struct efx_nic *efx, unsigned int type,
	return rc;
}

#ifdef CONFIG_SFC_MTD

#define EFX_MCDI_NVRAM_LEN_MAX 128

static int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type)
int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type)
{
	MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_START_V2_IN_LEN);
	int rc;
@@ -2185,6 +2192,8 @@ static int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type)
	return rc;
}

#ifdef CONFIG_SFC_MTD

static int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type,
			       loff_t offset, u8 *buffer, size_t length)
{
@@ -2209,13 +2218,20 @@ static int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type,
	return 0;
}

static int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
#endif /* CONFIG_SFC_MTD */

int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
			 loff_t offset, const u8 *buffer, size_t length)
{
	MCDI_DECLARE_BUF(inbuf,
			 MC_CMD_NVRAM_WRITE_IN_LEN(EFX_MCDI_NVRAM_LEN_MAX));
	efx_dword_t *inbuf;
	size_t inlen;
	int rc;

	inlen = ALIGN(MC_CMD_NVRAM_WRITE_IN_LEN(length), 4);
	inbuf = kzalloc(inlen, GFP_KERNEL);
	if (!inbuf)
		return -ENOMEM;

	MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_TYPE, type);
	MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_OFFSET, offset);
	MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_LENGTH, length);
@@ -2223,14 +2239,14 @@ static int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,

	BUILD_BUG_ON(MC_CMD_NVRAM_WRITE_OUT_LEN != 0);

	rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_WRITE, inbuf,
			  ALIGN(MC_CMD_NVRAM_WRITE_IN_LEN(length), 4),
			  NULL, 0, NULL);
	rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_WRITE, inbuf, inlen, NULL, 0, NULL);
	kfree(inbuf);

	return rc;
}

static int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type,
				loff_t offset, size_t length)
int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type, loff_t offset,
			 size_t length)
{
	MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_ERASE_IN_LEN);
	int rc;
@@ -2246,7 +2262,8 @@ static int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type,
	return rc;
}

static int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type)
int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type,
				 enum efx_update_finish_mode mode)
{
	MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_FINISH_V2_IN_LEN);
	MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN);
@@ -2254,22 +2271,41 @@ static int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type)
	int rc, rc2;

	MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_FINISH_IN_TYPE, type);
	/* Always set this flag. Old firmware ignores it */
	MCDI_POPULATE_DWORD_1(inbuf, NVRAM_UPDATE_FINISH_V2_IN_FLAGS,

	/* Old firmware doesn't support background update finish and abort
	 * operations. Fallback to waiting if the requested mode is not
	 * supported.
	 */
	if (!efx_has_cap(efx, NVRAM_UPDATE_POLL_VERIFY_RESULT) ||
	    (!efx_has_cap(efx, NVRAM_UPDATE_ABORT_SUPPORTED) &&
	     mode == EFX_UPDATE_FINISH_ABORT))
		mode = EFX_UPDATE_FINISH_WAIT;

	MCDI_POPULATE_DWORD_4(inbuf, NVRAM_UPDATE_FINISH_V2_IN_FLAGS,
			      NVRAM_UPDATE_FINISH_V2_IN_FLAG_REPORT_VERIFY_RESULT,
			      1);
			      (mode != EFX_UPDATE_FINISH_ABORT),
			      NVRAM_UPDATE_FINISH_V2_IN_FLAG_RUN_IN_BACKGROUND,
			      (mode == EFX_UPDATE_FINISH_BACKGROUND),
			      NVRAM_UPDATE_FINISH_V2_IN_FLAG_POLL_VERIFY_RESULT,
			      (mode == EFX_UPDATE_FINISH_POLL),
			      NVRAM_UPDATE_FINISH_V2_IN_FLAG_ABORT,
			      (mode == EFX_UPDATE_FINISH_ABORT));

	rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_FINISH, inbuf, sizeof(inbuf),
			  outbuf, sizeof(outbuf), &outlen);
	if (!rc && outlen >= MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN) {
		rc2 = MCDI_DWORD(outbuf, NVRAM_UPDATE_FINISH_V2_OUT_RESULT_CODE);
		if (rc2 != MC_CMD_NVRAM_VERIFY_RC_SUCCESS)
		if (rc2 != MC_CMD_NVRAM_VERIFY_RC_SUCCESS &&
		    rc2 != MC_CMD_NVRAM_VERIFY_RC_PENDING)
			netif_err(efx, drv, efx->net_dev,
				  "NVRAM update failed verification with code 0x%x\n",
				  rc2);
		switch (rc2) {
		case MC_CMD_NVRAM_VERIFY_RC_SUCCESS:
			break;
		case MC_CMD_NVRAM_VERIFY_RC_PENDING:
			rc = -EAGAIN;
			break;
		case MC_CMD_NVRAM_VERIFY_RC_CMS_CHECK_FAILED:
		case MC_CMD_NVRAM_VERIFY_RC_MESSAGE_DIGEST_CHECK_FAILED:
		case MC_CMD_NVRAM_VERIFY_RC_SIGNATURE_CHECK_FAILED:
@@ -2284,6 +2320,8 @@ static int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type)
		case MC_CMD_NVRAM_VERIFY_RC_NO_VALID_SIGNATURES:
		case MC_CMD_NVRAM_VERIFY_RC_NO_TRUSTED_APPROVERS:
		case MC_CMD_NVRAM_VERIFY_RC_NO_SIGNATURE_MATCH:
		case MC_CMD_NVRAM_VERIFY_RC_REJECT_TEST_SIGNED:
		case MC_CMD_NVRAM_VERIFY_RC_SECURITY_LEVEL_DOWNGRADE:
			rc = -EPERM;
			break;
		default:
@@ -2296,6 +2334,42 @@ static int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type)
	return rc;
}

#define	EFX_MCDI_NVRAM_UPDATE_FINISH_INITIAL_POLL_DELAY_MS 5
#define	EFX_MCDI_NVRAM_UPDATE_FINISH_MAX_POLL_DELAY_MS 5000
#define	EFX_MCDI_NVRAM_UPDATE_FINISH_RETRIES 185

int efx_mcdi_nvram_update_finish_polled(struct efx_nic *efx, unsigned int type)
{
	unsigned int delay = EFX_MCDI_NVRAM_UPDATE_FINISH_INITIAL_POLL_DELAY_MS;
	unsigned int retry = 0;
	int rc;

	/* NVRAM updates can take a long time (e.g. up to 1 minute for bundle
	 * images). Polling for NVRAM update completion ensures that other MCDI
	 * commands can be issued before the background NVRAM update completes.
	 *
	 * The initial call either completes the update synchronously, or
	 * returns -EAGAIN to indicate processing is continuing. In the latter
	 * case, we poll for at least 900 seconds, at increasing intervals
	 * (5ms, 50ms, 500ms, 5s).
	 */
	rc = efx_mcdi_nvram_update_finish(efx, type, EFX_UPDATE_FINISH_BACKGROUND);
	while (rc == -EAGAIN) {
		if (retry > EFX_MCDI_NVRAM_UPDATE_FINISH_RETRIES)
			return -ETIMEDOUT;
		retry++;

		msleep(delay);
		if (delay < EFX_MCDI_NVRAM_UPDATE_FINISH_MAX_POLL_DELAY_MS)
			delay *= 10;

		rc = efx_mcdi_nvram_update_finish(efx, type, EFX_UPDATE_FINISH_POLL);
	}
	return rc;
}

#ifdef CONFIG_SFC_MTD

int efx_mcdi_mtd_read(struct mtd_info *mtd, loff_t start,
		      size_t len, size_t *retlen, u8 *buffer)
{
@@ -2389,7 +2463,8 @@ int efx_mcdi_mtd_sync(struct mtd_info *mtd)

	if (part->updating) {
		part->updating = false;
		rc = efx_mcdi_nvram_update_finish(efx, part->nvram_type);
		rc = efx_mcdi_nvram_update_finish(efx, part->nvram_type,
						  EFX_UPDATE_FINISH_WAIT);
	}

	return rc;
+21 −1
Original line number Diff line number Diff line
@@ -392,7 +392,7 @@ int efx_mcdi_log_ctrl(struct efx_nic *efx, bool evq, bool uart, u32 dest_evq);
int efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out);
int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type,
			size_t *size_out, size_t *erase_size_out,
			bool *protected_out);
			size_t *write_size_out, bool *protected_out);
int efx_new_mcdi_nvram_test_all(struct efx_nic *efx);
int efx_mcdi_nvram_metadata(struct efx_nic *efx, unsigned int type,
			    u32 *subtype, u16 version[4], char *desc,
@@ -424,6 +424,26 @@ static inline int efx_mcdi_mon_probe(struct efx_nic *efx) { return 0; }
static inline void efx_mcdi_mon_remove(struct efx_nic *efx) {}
#endif

int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type);
int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
			 loff_t offset, const u8 *buffer, size_t length);
int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type,
			 loff_t offset, size_t length);
int efx_mcdi_nvram_metadata(struct efx_nic *efx, unsigned int type,
			    u32 *subtype, u16 version[4], char *desc,
			    size_t descsize);

enum efx_update_finish_mode {
	EFX_UPDATE_FINISH_WAIT,
	EFX_UPDATE_FINISH_BACKGROUND,
	EFX_UPDATE_FINISH_POLL,
	EFX_UPDATE_FINISH_ABORT,
};

int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type,
				 enum efx_update_finish_mode mode);
int efx_mcdi_nvram_update_finish_polled(struct efx_nic *efx, unsigned int type);

#ifdef CONFIG_SFC_MTD
int efx_mcdi_mtd_read(struct mtd_info *mtd, loff_t start, size_t len,
		      size_t *retlen, u8 *buffer);