git: 8e55febde6b5 - main - ixl(4): Fix NVM update API
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 23 Oct 2026 17:57:45 UTC
The branch main has been updated by kgalazka:
URL: https://cgit.FreeBSD.org/src/commit/?id=8e55febde6b54685fe24c8b48ed6720bae97c491
commit 8e55febde6b54685fe24c8b48ed6720bae97c491
Author: Krzysztof Galazka <kgalazka@FreeBSD.org>
AuthorDate: 2026-10-05 09:22:10 +0000
Commit: Krzysztof Galazka <kgalazka@FreeBSD.org>
CommitDate: 2026-10-23 17:57:17 +0000
ixl(4): Fix NVM update API
Driver provides an interface for tools operating on adapters'
Non-Volatile Memory using SIOC(G|S)DRVSPEC ioctls. It allows
tools to issue Admin Queue commands to the firmware, which
may return an error code encoded as a negative integer.
The driver passed this negative integer as the return value
of the ioctl handler. This was causing issues because the FreeBSD
kernel reserves negative error codes in system calls for special
purposes such as restarting the handler or detecting incorrect
behavior in VFS operations. Simple remapping of negative values
to positive integers caused currently available tools to misbehave
and could break the NVM update process. Introduce a new API version,
which allows new versions of tools to detect the fixed driver.
Temporarily keep the old API, but issue a warning that tools need
to be updated.
Also refactor the code to use centralized cleanup and move the
debug message from the firmware earlier to make sure it is not masked
by an error from copyout.
Signed-off-by: Krzysztof Galazka <krzysztof.galazka@intel.com>
Tested by: Mateusz Moga <mateusz.moga@intel.com>
MFC after: 2 weeks
Assisted by: Copilot GPT 5.4
Sponsored by: Intel Corporation
Differential Revision: https://reviews.freebsd.org/D57942
---
sys/dev/ixl/if_ixl.c | 3 ++-
sys/dev/ixl/ixl_pf.h | 8 +++++-
sys/dev/ixl/ixl_pf_main.c | 66 +++++++++++++++++++++++++++++------------------
3 files changed, 50 insertions(+), 27 deletions(-)
diff --git a/sys/dev/ixl/if_ixl.c b/sys/dev/ixl/if_ixl.c
index 3822f4b5ace2..b0f89ae9a900 100644
--- a/sys/dev/ixl/if_ixl.c
+++ b/sys/dev/ixl/if_ixl.c
@@ -1983,7 +1983,8 @@ ixl_if_priv_ioctl(if_ctx_t ctx, u_long command, caddr_t data)
case SIOCGDRVSPEC:
case SIOCSDRVSPEC:
/* NVM update command */
- if (ifd->ifd_cmd == I40E_NVM_ACCESS) {
+ if (ifd->ifd_cmd == I40E_NVM_ACCESS ||
+ ifd->ifd_cmd == I40E_NVM_ACCESS2) {
error = priv_check(curthread, PRIV_DRIVER);
if (error)
break;
diff --git a/sys/dev/ixl/ixl_pf.h b/sys/dev/ixl/ixl_pf.h
index d244a2e36a38..3bc51e4c7459 100644
--- a/sys/dev/ixl/ixl_pf.h
+++ b/sys/dev/ixl/ixl_pf.h
@@ -93,6 +93,7 @@ enum ixl_state {
IXL_STATE_EEE_ENABLED = 10,
IXL_STATE_LINK_ACTIVE_ON_DOWN = 11,
IXL_STATE_LINK_POLLING = 12,
+ IXL_STATE_NVMUPD_API_WARNED = 13,
};
#define IXL_PF_IN_RECOVERY_MODE(pf) \
@@ -200,6 +201,12 @@ struct ixl_pf {
*/
#define I40E_NVM_ACCESS \
(((((((('E' << 4) + '1') << 4) + 'K') << 4) + 'G') << 4) | 5)
+/*
+ * New version of tools API. Omitting '6' to not conflict
+ * with debug dump supported by other drivers.
+ */
+#define I40E_NVM_ACCESS2 \
+ (((((((('E' << 4) + '1') << 4) + 'K') << 4) + 'G') << 4) | 7)
#define IXL_DEFAULT_PHY_INT_MASK \
((~(I40E_AQ_EVENT_LINK_UPDOWN | I40E_AQ_EVENT_MODULE_QUAL_FAIL \
@@ -310,7 +317,6 @@ void ixl_clear_state(volatile u32 *s, enum ixl_state bit);
bool ixl_test_state(volatile u32 *s, enum ixl_state bit);
u32 ixl_testandset_state(volatile u32 *s, enum ixl_state bit);
int ixl_setup_interface(device_t, struct ixl_pf *);
-void ixl_print_nvm_cmd(device_t, struct i40e_nvm_access *);
void ixl_handle_que(void *context, int pending);
diff --git a/sys/dev/ixl/ixl_pf_main.c b/sys/dev/ixl/ixl_pf_main.c
index d2a2fb74fe69..cc71ec8a2270 100644
--- a/sys/dev/ixl/ixl_pf_main.c
+++ b/sys/dev/ixl/ixl_pf_main.c
@@ -50,6 +50,7 @@ static u_int ixl_add_maddr(void *, struct sockaddr_dl *, u_int);
static u_int ixl_match_maddr(void *, struct sockaddr_dl *, u_int);
static char * ixl_switch_element_string(struct sbuf *, u8, u16);
static enum ixl_fw_mode ixl_get_fw_mode(struct ixl_pf *);
+static void ixl_print_nvm_cmd(device_t, int, struct i40e_nvm_access *);
/* Sysctls */
static int ixl_sysctl_set_advertise(SYSCTL_HANDLER_ARGS);
@@ -3123,8 +3124,8 @@ ixl_sysctl_show_fw(SYSCTL_HANDLER_ARGS)
return (0);
}
-void
-ixl_print_nvm_cmd(device_t dev, struct i40e_nvm_access *nvma)
+static void
+ixl_print_nvm_cmd(device_t dev, int apiver, struct i40e_nvm_access *nvma)
{
u8 nvma_ptr = nvma->config & 0xFF;
u8 nvma_flags = (nvma->config & 0xF00) >> 8;
@@ -3134,7 +3135,7 @@ ixl_print_nvm_cmd(device_t dev, struct i40e_nvm_access *nvma)
case I40E_NVM_READ:
if (nvma_ptr == 0xF && nvma_flags == 0xF &&
nvma->offset == 0 && nvma->data_size == 1) {
- device_printf(dev, "NVMUPD: Get Driver Status Command\n");
+ device_printf(dev, "NVMUPD: API: v%d Get Driver Status Command\n", apiver);
return;
}
cmd_str = "READ ";
@@ -3143,12 +3144,12 @@ ixl_print_nvm_cmd(device_t dev, struct i40e_nvm_access *nvma)
cmd_str = "WRITE";
break;
default:
- device_printf(dev, "NVMUPD: unknown command: 0x%08x\n", nvma->command);
+ device_printf(dev, "NVMUPD: API: v%d unknown command: 0x%08x\n", apiver, nvma->command);
return;
}
device_printf(dev,
- "NVMUPD: cmd: %s ptr: 0x%02x flags: 0x%01x offset: 0x%08x data_s: 0x%08x\n",
- cmd_str, nvma_ptr, nvma_flags, nvma->offset, nvma->data_size);
+ "NVMUPD: API: v%d cmd: %s ptr: 0x%02x flags: 0x%01x offset: 0x%08x data_s: 0x%08x\n",
+ apiver, cmd_str, nvma_ptr, nvma_flags, nvma->offset, nvma->data_size);
}
int
@@ -3159,7 +3160,7 @@ ixl_handle_nvmupd_cmd(struct ixl_pf *pf, struct ifdrv *ifd)
device_t dev = pf->dev;
enum i40e_status_code status = 0;
size_t nvma_size, ifd_len, exp_len;
- int err, perrno;
+ int err, perrno, apiver;
DEBUGFUNC("ixl_handle_nvmupd_cmd");
@@ -3175,20 +3176,26 @@ ixl_handle_nvmupd_cmd(struct ixl_pf *pf, struct ifdrv *ifd)
__func__, ifd_len, nvma_size);
device_printf(dev, "%s: data pointer: %p\n", __func__,
ifd->ifd_data);
- return (EINVAL);
+ err = EINVAL;
+ goto out;
}
+ if (ifd->ifd_cmd == I40E_NVM_ACCESS &&
+ !ixl_testandset_state(&pf->state, IXL_STATE_NVMUPD_API_WARNED))
+ device_printf(dev,
+ "Using deprecated tools API, please update the tool\n");
+
nvma = malloc(ifd_len, M_IXL, M_WAITOK);
err = copyin(ifd->ifd_data, nvma, ifd_len);
if (err) {
device_printf(dev, "%s: Cannot get request from user space\n",
__func__);
- free(nvma, M_IXL);
- return (err);
+ goto out_free;
}
+ apiver = ifd->ifd_cmd == I40E_NVM_ACCESS2 ? 2 : 1;
if (pf->dbg_mask & IXL_DBG_NVMUPD)
- ixl_print_nvm_cmd(dev, nvma);
+ ixl_print_nvm_cmd(dev, apiver, nvma);
if (IXL_PF_IS_RESETTING(pf)) {
int count = 0;
@@ -3203,16 +3210,16 @@ ixl_handle_nvmupd_cmd(struct ixl_pf *pf, struct ifdrv *ifd)
device_printf(dev,
"%s: timeout waiting for EMP reset to finish\n",
__func__);
- free(nvma, M_IXL);
- return (-EBUSY);
+ perrno = -EBUSY;
+ goto out_sanitize;
}
if (nvma->data_size < 1 || nvma->data_size > 4096) {
device_printf(dev,
"%s: invalid request, data size not in supported range\n",
__func__);
- free(nvma, M_IXL);
- return (EINVAL);
+ err = EINVAL;
+ goto out_free;
}
/*
@@ -3230,8 +3237,7 @@ ixl_handle_nvmupd_cmd(struct ixl_pf *pf, struct ifdrv *ifd)
if (err) {
device_printf(dev, "%s: Cannot get request from user space\n",
__func__);
- free(nvma, M_IXL);
- return (err);
+ goto out_free;
}
}
@@ -3240,27 +3246,37 @@ ixl_handle_nvmupd_cmd(struct ixl_pf *pf, struct ifdrv *ifd)
status = i40e_nvmupd_command(hw, nvma, nvma->data, &perrno);
// IXL_PF_UNLOCK(pf);
+ /* Let the nvmupdate report errors, show them only when debug is enabled */
+ if (status != 0 && (pf->dbg_mask & IXL_DBG_NVMUPD) != 0)
+ device_printf(dev, "i40e_nvmupd_command API: v%d status %s, perrno %d\n",
+ apiver, i40e_stat_str(hw, status), perrno);
+
err = copyout(nvma, ifd->ifd_data, ifd_len);
- free(nvma, M_IXL);
if (err) {
device_printf(dev, "%s: Cannot return data to user space\n",
__func__);
- return (err);
+ goto out_free;
}
- /* Let the nvmupdate report errors, show them only when debug is enabled */
- if (status != 0 && (pf->dbg_mask & IXL_DBG_NVMUPD) != 0)
- device_printf(dev, "i40e_nvmupd_command status %s, perrno %d\n",
- i40e_stat_str(hw, status), perrno);
+out_sanitize:
+ if (ifd->ifd_cmd == I40E_NVM_ACCESS2) {
+ err = -perrno;
+ goto out_free;
+ }
/*
* -EPERM is actually ERESTART, which the kernel interprets as it needing
* to run this ioctl again. So use -EACCES for -EPERM instead.
*/
if (perrno == -EPERM)
- return (-EACCES);
+ err = -EACCES;
else
- return (perrno);
+ err = perrno;
+
+out_free:
+ free(nvma, M_IXL);
+out:
+ return (err);
}
int