crypto: qat - add anti-rollback support for GEN6 devices

Anti-Rollback (ARB) is a QAT GEN6 hardware feature that prevents loading
firmware with a Security Version Number (SVN) lower than an authorized
minimum. This protects against downgrade attacks by ensuring that only
firmware at or above a committed SVN can run on the acceleration device.

During firmware loading, the driver checks the SVN validation status via
a hardware CSR. If the check reports a failure, firmware authentication
is aborted. If it reports a retry status, the driver reissues the
authentication command up to a maximum number of retries.

Extend the firmware admin interface with two new messages,
ICP_QAT_FW_SVN_READ and ICP_QAT_FW_SVN_COMMIT, to query and commit the
SVN, respectively. Integrate the SVN check into the firmware
authentication path in qat_uclo.c so the driver can react to
anti-rollback status during device bring-up.

Expose SVN information to userspace via a new sysfs attribute group,
qat_svn, under the PCI device directory. The group provides read-only
attributes for the active, enforced minimum, and permanent minimum SVN
values, as well as a write-only commit attribute that allows a system
administrator to commit the currently active SVN as the new authorized
minimum.

This is based on earlier work by Ciunas Bennett.

Signed-off-by: Suman Kumar Chakraborty <suman.kumar.chakraborty@intel.com>
Reviewed-by: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
Suman Kumar Chakraborty
2026-03-19 11:02:57 +00:00
committed by Herbert Xu
parent 177730a273
commit 6ac142bf26
14 changed files with 497 additions and 5 deletions

View File

@@ -0,0 +1,114 @@
What: /sys/bus/pci/devices/<BDF>/qat_svn/
Date: June 2026
KernelVersion: 7.1
Contact: qat-linux@intel.com
Description: Directory containing Security Version Number (SVN) attributes for
the Anti-Rollback (ARB) feature. The ARB feature prevents downloading
older firmware versions to the acceleration device.
What: /sys/bus/pci/devices/<BDF>/qat_svn/enforced_min
Date: June 2026
KernelVersion: 7.1
Contact: qat-linux@intel.com
Description:
(RO) Reports the minimum allowed firmware SVN.
Returns an integer greater than zero. Firmware with SVN lower than
this value is rejected.
A write to qat_svn/commit will update this value. The update is not
persistent across reboot; on reboot, this value is reset from
qat_svn/permanent_min.
Example usage::
# cat /sys/bus/pci/devices/<BDF>/qat_svn/enforced_min
2
This attribute is available only on devices that support
Anti-Rollback.
What: /sys/bus/pci/devices/<BDF>/qat_svn/permanent_min
Date: June 2026
KernelVersion: 7.1
Contact: qat-linux@intel.com
Description:
(RO) Reports the persistent minimum SVN used to initialize
qat_svn/enforced_min on each reboot.
Returns an integer greater than zero. A write to qat_svn/commit
may update this value, depending on platform/BIOS settings.
Example usage::
# cat /sys/bus/pci/devices/<BDF>/qat_svn/permanent_min
3
This attribute is available only on devices that support
Anti-Rollback.
What: /sys/bus/pci/devices/<BDF>/qat_svn/active
Date: June 2026
KernelVersion: 7.1
Contact: qat-linux@intel.com
Description:
(RO) Reports the SVN of the currently active firmware image.
Returns an integer greater than zero.
Example usage::
# cat /sys/bus/pci/devices/<BDF>/qat_svn/active
2
This attribute is available only on devices that support
Anti-Rollback.
What: /sys/bus/pci/devices/<BDF>/qat_svn/commit
Date: June 2026
KernelVersion: 7.1
Contact: qat-linux@intel.com
Description:
(WO) Commits the currently active SVN as the minimum allowed SVN.
Writing 1 sets qat_svn/enforced_min to the value of qat_svn/active,
preventing future firmware loads with lower SVN.
Depending on platform/BIOS settings, a commit may also update
qat_svn/permanent_min.
Note that on reboot, qat_svn/enforced_min reverts to
qat_svn/permanent_min.
It is advisable to use this attribute with caution, only when
it is necessary to set a new minimum SVN for the firmware.
Before committing the SVN update, it is crucial to check the
current values of qat_svn/active, qat_svn/enforced_min and
qat_svn/permanent_min. This verification helps ensure that the
commit operation aligns with the intended outcome.
While writing to the file, any value other than '1' will result
in an error and have no effect.
Example usage::
## Read current values
# cat /sys/bus/pci/devices/<BDF>/qat_svn/enforced_min
2
# cat /sys/bus/pci/devices/<BDF>/qat_svn/permanent_min
2
# cat /sys/bus/pci/devices/<BDF>/qat_svn/active
3
## Commit active SVN
# echo 1 > /sys/bus/pci/devices/<BDF>/qat_svn/commit
## Read updated values
# cat /sys/bus/pci/devices/<BDF>/qat_svn/enforced_min
3
# cat /sys/bus/pci/devices/<BDF>/qat_svn/permanent_min
3
This attribute is available only on devices that support
Anti-Rollback.

View File

@@ -462,6 +462,21 @@ static int reset_ring_pair(void __iomem *csr, u32 bank_number)
return 0;
}
static bool adf_anti_rb_enabled(struct adf_accel_dev *accel_dev)
{
struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
return !!(hw_data->fuses[0] & ADF_GEN6_ANTI_RB_FUSE_BIT);
}
static void adf_gen6_init_anti_rb(struct adf_anti_rb_hw_data *anti_rb_data)
{
anti_rb_data->anti_rb_enabled = adf_anti_rb_enabled;
anti_rb_data->svncheck_offset = ADF_GEN6_SVNCHECK_CSR_MSG;
anti_rb_data->svncheck_retry = 0;
anti_rb_data->sysfs_added = false;
}
static int ring_pair_reset(struct adf_accel_dev *accel_dev, u32 bank_number)
{
struct adf_hw_device_data *hw_data = accel_dev->hw_device;
@@ -1024,6 +1039,7 @@ void adf_init_hw_data_6xxx(struct adf_hw_device_data *hw_data)
adf_gen6_init_ras_ops(&hw_data->ras_ops);
adf_gen6_init_tl_data(&hw_data->tl_data);
adf_gen6_init_rl_data(&hw_data->rl_data);
adf_gen6_init_anti_rb(&hw_data->anti_rb_data);
}
void adf_clean_hw_data_6xxx(struct adf_hw_device_data *hw_data)

View File

@@ -53,6 +53,12 @@
#define ADF_GEN6_ADMINMSGLR_OFFSET 0x500578
#define ADF_GEN6_MAILBOX_BASE_OFFSET 0x600970
/* Anti-rollback */
#define ADF_GEN6_SVNCHECK_CSR_MSG 0x640004
/* Fuse bits */
#define ADF_GEN6_ANTI_RB_FUSE_BIT BIT(24)
/*
* Watchdog timers
* Timeout is in cycles. Clock speed may vary across products but this

View File

@@ -4,6 +4,7 @@ ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE='"CRYPTO_QAT"'
intel_qat-y := adf_accel_engine.o \
adf_admin.o \
adf_aer.o \
adf_anti_rb.o \
adf_bank_state.o \
adf_cfg.o \
adf_cfg_services.o \
@@ -29,6 +30,7 @@ intel_qat-y := adf_accel_engine.o \
adf_rl_admin.o \
adf_rl.o \
adf_sysfs.o \
adf_sysfs_anti_rb.o \
adf_sysfs_ras_counters.o \
adf_sysfs_rl.o \
adf_timer.o \

View File

@@ -11,6 +11,7 @@
#include <linux/types.h>
#include <linux/qat/qat_mig_dev.h>
#include <linux/wordpart.h>
#include "adf_anti_rb.h"
#include "adf_cfg_common.h"
#include "adf_dc.h"
#include "adf_rl.h"
@@ -328,6 +329,7 @@ struct adf_hw_device_data {
struct adf_dev_err_mask dev_err_mask;
struct adf_rl_hw_data rl_data;
struct adf_tl_hw_data tl_data;
struct adf_anti_rb_hw_data anti_rb_data;
struct qat_migdev_ops vfmig_ops;
const char *fw_name;
const char *fw_mmp_name;

View File

@@ -6,8 +6,10 @@
#include <linux/iopoll.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include "adf_accel_devices.h"
#include "adf_admin.h"
#include "adf_anti_rb.h"
#include "adf_common_drv.h"
#include "adf_cfg.h"
#include "adf_heartbeat.h"
@@ -19,6 +21,7 @@
#define ADF_ADMIN_POLL_DELAY_US 20
#define ADF_ADMIN_POLL_TIMEOUT_US (5 * USEC_PER_SEC)
#define ADF_ONE_AE 1
#define ADF_ADMIN_RETRY_MAX 60
static const u8 const_tab[1024] __aligned(1024) = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -536,6 +539,73 @@ int adf_send_admin_tl_stop(struct adf_accel_dev *accel_dev)
return adf_send_admin(accel_dev, &req, &resp, ae_mask);
}
static int adf_send_admin_retry(struct adf_accel_dev *accel_dev, u8 cmd_id,
struct icp_qat_fw_init_admin_resp *resp,
unsigned int sleep_ms)
{
u32 admin_ae_mask = GET_HW_DATA(accel_dev)->admin_ae_mask;
struct icp_qat_fw_init_admin_req req = { };
unsigned int retries = ADF_ADMIN_RETRY_MAX;
int ret;
req.cmd_id = cmd_id;
do {
ret = adf_send_admin(accel_dev, &req, resp, admin_ae_mask);
if (!ret)
return 0;
if (resp->status != ICP_QAT_FW_INIT_RESP_STATUS_RETRY)
return ret;
msleep(sleep_ms);
} while (--retries);
return -ETIMEDOUT;
}
static int adf_send_admin_svn(struct adf_accel_dev *accel_dev, u8 cmd_id,
struct icp_qat_fw_init_admin_resp *resp)
{
return adf_send_admin_retry(accel_dev, cmd_id, resp, ADF_SVN_RETRY_MS);
}
int adf_send_admin_arb_query(struct adf_accel_dev *accel_dev, int cmd, u8 *svn)
{
struct icp_qat_fw_init_admin_resp resp = { };
int ret;
ret = adf_send_admin_svn(accel_dev, ICP_QAT_FW_SVN_READ, &resp);
if (ret)
return ret;
switch (cmd) {
case ARB_ENFORCED_MIN_SVN:
*svn = resp.enforced_min_svn;
break;
case ARB_PERMANENT_MIN_SVN:
*svn = resp.permanent_min_svn;
break;
case ARB_ACTIVE_SVN:
*svn = resp.active_svn;
break;
default:
*svn = 0;
dev_err(&GET_DEV(accel_dev),
"Unknown secure version number request\n");
ret = -EINVAL;
}
return ret;
}
int adf_send_admin_arb_commit(struct adf_accel_dev *accel_dev)
{
struct icp_qat_fw_init_admin_resp resp = { };
return adf_send_admin_svn(accel_dev, ICP_QAT_FW_SVN_COMMIT, &resp);
}
int adf_init_admin_comms(struct adf_accel_dev *accel_dev)
{
struct adf_admin_comms *admin;

View File

@@ -27,5 +27,7 @@ int adf_send_admin_tl_start(struct adf_accel_dev *accel_dev,
dma_addr_t tl_dma_addr, size_t layout_sz, u8 *rp_indexes,
struct icp_qat_fw_init_admin_slice_cnt *slice_count);
int adf_send_admin_tl_stop(struct adf_accel_dev *accel_dev);
int adf_send_admin_arb_query(struct adf_accel_dev *accel_dev, int cmd, u8 *svn);
int adf_send_admin_arb_commit(struct adf_accel_dev *accel_dev);
#endif

View File

@@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2026 Intel Corporation */
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/kstrtox.h>
#include "adf_accel_devices.h"
#include "adf_admin.h"
#include "adf_anti_rb.h"
#include "adf_common_drv.h"
#include "icp_qat_fw_init_admin.h"
#define ADF_SVN_RETRY_MAX 60
int adf_anti_rb_commit(struct adf_accel_dev *accel_dev)
{
return adf_send_admin_arb_commit(accel_dev);
}
int adf_anti_rb_query(struct adf_accel_dev *accel_dev, enum anti_rb cmd, u8 *svn)
{
return adf_send_admin_arb_query(accel_dev, cmd, svn);
}
int adf_anti_rb_check(struct pci_dev *pdev)
{
struct adf_anti_rb_hw_data *anti_rb;
u32 svncheck_sts, cfc_svncheck_sts;
struct adf_accel_dev *accel_dev;
void __iomem *pmisc_addr;
accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
if (!accel_dev)
return -EINVAL;
anti_rb = GET_ANTI_RB_DATA(accel_dev);
if (!anti_rb->anti_rb_enabled || !anti_rb->anti_rb_enabled(accel_dev))
return 0;
pmisc_addr = adf_get_pmisc_base(accel_dev);
cfc_svncheck_sts = ADF_CSR_RD(pmisc_addr, anti_rb->svncheck_offset);
svncheck_sts = FIELD_GET(ADF_SVN_STS_MASK, cfc_svncheck_sts);
switch (svncheck_sts) {
case ADF_SVN_NO_STS:
return 0;
case ADF_SVN_PASS_STS:
anti_rb->svncheck_retry = 0;
return 0;
case ADF_SVN_FAIL_STS:
dev_err(&GET_DEV(accel_dev), "Security Version Number failure\n");
return -EIO;
case ADF_SVN_RETRY_STS:
if (anti_rb->svncheck_retry++ >= ADF_SVN_RETRY_MAX) {
anti_rb->svncheck_retry = 0;
return -ETIMEDOUT;
}
msleep(ADF_SVN_RETRY_MS);
return -EAGAIN;
default:
dev_err(&GET_DEV(accel_dev), "Invalid SVN check status\n");
return -EINVAL;
}
}

View File

@@ -0,0 +1,37 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright(c) 2026 Intel Corporation */
#ifndef ADF_ANTI_RB_H_
#define ADF_ANTI_RB_H_
#include <linux/types.h>
#define GET_ANTI_RB_DATA(accel_dev) (&(accel_dev)->hw_device->anti_rb_data)
#define ADF_SVN_NO_STS 0x00
#define ADF_SVN_PASS_STS 0x01
#define ADF_SVN_RETRY_STS 0x02
#define ADF_SVN_FAIL_STS 0x03
#define ADF_SVN_RETRY_MS 250
#define ADF_SVN_STS_MASK GENMASK(7, 0)
enum anti_rb {
ARB_ENFORCED_MIN_SVN,
ARB_PERMANENT_MIN_SVN,
ARB_ACTIVE_SVN,
};
struct adf_accel_dev;
struct pci_dev;
struct adf_anti_rb_hw_data {
bool (*anti_rb_enabled)(struct adf_accel_dev *accel_dev);
u32 svncheck_offset;
u32 svncheck_retry;
bool sysfs_added;
};
int adf_anti_rb_commit(struct adf_accel_dev *accel_dev);
int adf_anti_rb_query(struct adf_accel_dev *accel_dev, enum anti_rb cmd, u8 *svn);
int adf_anti_rb_check(struct pci_dev *pdev);
#endif /* ADF_ANTI_RB_H_ */

View File

@@ -10,6 +10,7 @@
#include "adf_dbgfs.h"
#include "adf_heartbeat.h"
#include "adf_rl.h"
#include "adf_sysfs_anti_rb.h"
#include "adf_sysfs_ras_counters.h"
#include "adf_telemetry.h"
@@ -263,6 +264,7 @@ static int adf_dev_start(struct adf_accel_dev *accel_dev)
adf_dbgfs_add(accel_dev);
adf_sysfs_start_ras(accel_dev);
adf_sysfs_start_arb(accel_dev);
return 0;
}
@@ -292,6 +294,7 @@ static void adf_dev_stop(struct adf_accel_dev *accel_dev)
adf_rl_stop(accel_dev);
adf_dbgfs_rm(accel_dev);
adf_sysfs_stop_ras(accel_dev);
adf_sysfs_stop_arb(accel_dev);
clear_bit(ADF_STATUS_STARTING, &accel_dev->status);
clear_bit(ADF_STATUS_STARTED, &accel_dev->status);

View File

@@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2026 Intel Corporation */
#include <linux/sysfs.h>
#include <linux/types.h>
#include "adf_anti_rb.h"
#include "adf_common_drv.h"
#include "adf_sysfs_anti_rb.h"
static ssize_t enforced_min_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct adf_accel_dev *accel_dev;
int err;
u8 svn;
accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev));
if (!accel_dev)
return -EINVAL;
err = adf_anti_rb_query(accel_dev, ARB_ENFORCED_MIN_SVN, &svn);
if (err)
return err;
return sysfs_emit(buf, "%u\n", svn);
}
static DEVICE_ATTR_RO(enforced_min);
static ssize_t active_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct adf_accel_dev *accel_dev;
int err;
u8 svn;
accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev));
if (!accel_dev)
return -EINVAL;
err = adf_anti_rb_query(accel_dev, ARB_ACTIVE_SVN, &svn);
if (err)
return err;
return sysfs_emit(buf, "%u\n", svn);
}
static DEVICE_ATTR_RO(active);
static ssize_t permanent_min_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct adf_accel_dev *accel_dev;
int err;
u8 svn;
accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev));
if (!accel_dev)
return -EINVAL;
err = adf_anti_rb_query(accel_dev, ARB_PERMANENT_MIN_SVN, &svn);
if (err)
return err;
return sysfs_emit(buf, "%u\n", svn);
}
static DEVICE_ATTR_RO(permanent_min);
static ssize_t commit_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct adf_accel_dev *accel_dev;
bool val;
int err;
accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev));
if (!accel_dev)
return -EINVAL;
err = kstrtobool(buf, &val);
if (err)
return err;
if (!val)
return -EINVAL;
err = adf_anti_rb_commit(accel_dev);
if (err)
return err;
return count;
}
static DEVICE_ATTR_WO(commit);
static struct attribute *qat_svn_attrs[] = {
&dev_attr_commit.attr,
&dev_attr_active.attr,
&dev_attr_enforced_min.attr,
&dev_attr_permanent_min.attr,
NULL
};
static const struct attribute_group qat_svn_group = {
.attrs = qat_svn_attrs,
.name = "qat_svn",
};
void adf_sysfs_start_arb(struct adf_accel_dev *accel_dev)
{
struct adf_anti_rb_hw_data *anti_rb = GET_ANTI_RB_DATA(accel_dev);
if (!anti_rb->anti_rb_enabled || !anti_rb->anti_rb_enabled(accel_dev))
return;
if (device_add_group(&GET_DEV(accel_dev), &qat_svn_group)) {
dev_warn(&GET_DEV(accel_dev),
"Failed to create qat_svn attribute group\n");
return;
}
anti_rb->sysfs_added = true;
}
void adf_sysfs_stop_arb(struct adf_accel_dev *accel_dev)
{
struct adf_anti_rb_hw_data *anti_rb = GET_ANTI_RB_DATA(accel_dev);
if (!anti_rb->sysfs_added)
return;
device_remove_group(&GET_DEV(accel_dev), &qat_svn_group);
anti_rb->sysfs_added = false;
anti_rb->svncheck_retry = 0;
}

View File

@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright(c) 2026 Intel Corporation */
#ifndef ADF_SYSFS_ANTI_RB_H_
#define ADF_SYSFS_ANTI_RB_H_
struct adf_accel_dev;
void adf_sysfs_start_arb(struct adf_accel_dev *accel_dev);
void adf_sysfs_stop_arb(struct adf_accel_dev *accel_dev);
#endif /* ADF_SYSFS_ANTI_RB_H_ */

View File

@@ -31,11 +31,15 @@ enum icp_qat_fw_init_admin_cmd_id {
ICP_QAT_FW_RL_REMOVE = 136,
ICP_QAT_FW_TL_START = 137,
ICP_QAT_FW_TL_STOP = 138,
ICP_QAT_FW_SVN_READ = 146,
ICP_QAT_FW_SVN_COMMIT = 147,
};
enum icp_qat_fw_init_admin_resp_status {
ICP_QAT_FW_INIT_RESP_STATUS_SUCCESS = 0,
ICP_QAT_FW_INIT_RESP_STATUS_FAIL
ICP_QAT_FW_INIT_RESP_STATUS_FAIL = 1,
ICP_QAT_FW_INIT_RESP_STATUS_RETRY = 2,
ICP_QAT_FW_INIT_RESP_STATUS_UNSUPPORTED = 4,
};
struct icp_qat_fw_init_admin_tl_rp_indexes {
@@ -159,6 +163,15 @@ struct icp_qat_fw_init_admin_resp {
};
struct icp_qat_fw_init_admin_slice_cnt slices;
__u16 fw_capabilities;
struct {
__u8 enforced_min_svn;
__u8 permanent_min_svn;
__u8 active_svn;
__u8 resrvd9;
__u16 svn_status;
__u16 resrvd10;
__u64 resrvd11;
};
};
} __packed;

View File

@@ -12,6 +12,7 @@
#include <linux/pci_ids.h>
#include <linux/wordpart.h>
#include "adf_accel_devices.h"
#include "adf_anti_rb.h"
#include "adf_common_drv.h"
#include "icp_qat_uclo.h"
#include "icp_qat_hal.h"
@@ -1230,10 +1231,11 @@ static int qat_uclo_map_suof(struct icp_qat_fw_loader_handle *handle,
static int qat_uclo_auth_fw(struct icp_qat_fw_loader_handle *handle,
struct icp_qat_fw_auth_desc *desc)
{
u32 fcu_sts, retry = 0;
unsigned int retries = FW_AUTH_MAX_RETRY;
u32 fcu_ctl_csr, fcu_sts_csr;
u32 fcu_dram_hi_csr, fcu_dram_lo_csr;
u64 bus_addr;
u32 fcu_sts;
bus_addr = ADD_ADDR(desc->css_hdr_high, desc->css_hdr_low)
- sizeof(struct icp_qat_auth_chunk);
@@ -1248,17 +1250,32 @@ static int qat_uclo_auth_fw(struct icp_qat_fw_loader_handle *handle,
SET_CAP_CSR(handle, fcu_ctl_csr, FCU_CTRL_CMD_AUTH);
do {
int arb_ret;
msleep(FW_AUTH_WAIT_PERIOD);
fcu_sts = GET_CAP_CSR(handle, fcu_sts_csr);
arb_ret = adf_anti_rb_check(handle->pci_dev);
if (arb_ret == -EAGAIN) {
if ((fcu_sts & FCU_AUTH_STS_MASK) == FCU_STS_VERI_FAIL) {
SET_CAP_CSR(handle, fcu_ctl_csr, FCU_CTRL_CMD_AUTH);
continue;
}
} else if (arb_ret) {
goto auth_fail;
}
if ((fcu_sts & FCU_AUTH_STS_MASK) == FCU_STS_VERI_FAIL)
goto auth_fail;
if (((fcu_sts >> FCU_STS_AUTHFWLD_POS) & 0x1))
if ((fcu_sts & FCU_AUTH_STS_MASK) == FCU_STS_VERI_DONE)
return 0;
} while (retry++ < FW_AUTH_MAX_RETRY);
} while (--retries);
auth_fail:
pr_err("authentication error (FCU_STATUS = 0x%x),retry = %d\n",
fcu_sts & FCU_AUTH_STS_MASK, retry);
pr_err("authentication error (FCU_STATUS = 0x%x)\n", fcu_sts & FCU_AUTH_STS_MASK);
return -EINVAL;
}