From ea78f07b5eb1c2236814d35a280c34541fb1cf82 Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Wed, 8 May 2019 10:50:35 +0000 Subject: [PATCH] Implement userspace firmware update for ConnectX-4/5/6. Submitted by: kib@ MFC after: 3 days Sponsored by: Mellanox Technologies --- sys/dev/mlx5/mlx5_core/mlx5_fwdump.c | 32 ++++++++++++++ sys/dev/mlx5/mlx5io.h | 7 +++ usr.sbin/mlx5tool/mlx5tool.c | 65 ++++++++++++++++++++++++++-- 3 files changed, 101 insertions(+), 3 deletions(-) diff --git a/sys/dev/mlx5/mlx5_core/mlx5_fwdump.c b/sys/dev/mlx5/mlx5_core/mlx5_fwdump.c index 0db9f235c252..054f6513f572 100644 --- a/sys/dev/mlx5/mlx5_core/mlx5_fwdump.c +++ b/sys/dev/mlx5/mlx5_core/mlx5_fwdump.c @@ -233,6 +233,8 @@ mlx5_fwdump_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct mlx5_fwdump_get *fwg; struct mlx5_tool_addr *devaddr; struct mlx5_dump_data *dd; + struct mlx5_fw_update *fu; + struct firmware fake_fw; int error; error = 0; @@ -275,6 +277,36 @@ mlx5_fwdump_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, break; mlx5_fwdump(mdev); break; + case MLX5_FW_UPDATE: + if ((fflag & FWRITE) == 0) { + error = EBADF; + break; + } + fu = (struct mlx5_fw_update *)data; + if (fu->img_fw_data_len > 10 * 1024 * 1024) { + error = EINVAL; + break; + } + devaddr = &fu->devaddr; + error = mlx5_dbsf_to_core(devaddr, &mdev); + if (error != 0) + break; + bzero(&fake_fw, sizeof(fake_fw)); + fake_fw.name = "umlx_fw_up"; + fake_fw.datasize = fu->img_fw_data_len; + fake_fw.version = 1; + fake_fw.data = (void *)kmem_malloc(fu->img_fw_data_len, + M_WAITOK); + if (fake_fw.data == NULL) { + error = ENOMEM; + break; + } + error = copyin(fu->img_fw_data, __DECONST(void *, fake_fw.data), + fu->img_fw_data_len); + if (error == 0) + error = -mlx5_firmware_flash(mdev, &fake_fw); + kmem_free((vm_offset_t)fake_fw.data, fu->img_fw_data_len); + break; default: error = ENOTTY; break; diff --git a/sys/dev/mlx5/mlx5io.h b/sys/dev/mlx5/mlx5io.h index ebf528930090..38043f469052 100644 --- a/sys/dev/mlx5/mlx5io.h +++ b/sys/dev/mlx5/mlx5io.h @@ -49,9 +49,16 @@ struct mlx5_fwdump_get { size_t reg_filled; /* out */ }; +struct mlx5_fw_update { + struct mlx5_tool_addr devaddr; + void *img_fw_data; + size_t img_fw_data_len; +}; + #define MLX5_FWDUMP_GET _IOWR('m', 1, struct mlx5_fwdump_get) #define MLX5_FWDUMP_RESET _IOW('m', 2, struct mlx5_tool_addr) #define MLX5_FWDUMP_FORCE _IOW('m', 3, struct mlx5_tool_addr) +#define MLX5_FW_UPDATE _IOW('m', 4, struct mlx5_fw_update) #ifndef _KERNEL #define MLX5_DEV_PATH _PATH_DEV"mlx5ctl" diff --git a/usr.sbin/mlx5tool/mlx5tool.c b/usr.sbin/mlx5tool/mlx5tool.c index 522f97eb5ec0..79777776b3d2 100644 --- a/usr.sbin/mlx5tool/mlx5tool.c +++ b/usr.sbin/mlx5tool/mlx5tool.c @@ -28,6 +28,8 @@ __FBSDID("$FreeBSD$"); #include #include +#include +#include #include #include #include @@ -144,15 +146,60 @@ mlx5tool_dump_force(int ctldev, const struct mlx5_tool_addr *addr) return (0); } +static int +mlx5tool_fw_update(int ctldev, const struct mlx5_tool_addr *addr, + const char *img_fw_path) +{ + struct stat st; + struct mlx5_fw_update fwup; + int error, fd, res; + + res = 0; + fd = open(img_fw_path, O_RDONLY); + if (fd == -1) { + warn("Unable to open %s", img_fw_path); + res = 1; + goto close_fd; + } + error = fstat(fd, &st); + if (error != 0) { + warn("Unable to stat %s", img_fw_path); + res = 1; + goto close_fd; + } + memset(&fwup, 0, sizeof(fwup)); + memcpy(&fwup.devaddr, addr, sizeof(fwup.devaddr)); + fwup.img_fw_data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, + fd, 0); + if (fwup.img_fw_data == MAP_FAILED) { + warn("Unable to mmap %s", img_fw_path); + res = 1; + goto close_fd; + } + fwup.img_fw_data_len = st.st_size; + + error = ioctl(ctldev, MLX5_FW_UPDATE, &fwup); + if (error == -1) { + warn("MLX5_FW_UPDATE"); + } + + munmap(fwup.img_fw_data, st.st_size); +close_fd: + close(fd); + return (res); +} + static void usage(void) { fprintf(stderr, - "Usage: mlx5tool -d pci [-w -o dump.file | -r | -e]\n"); + "Usage: mlx5tool -d pci [-w -o dump.file | -r |" + " -e | -f fw.mfa2]\n"); fprintf(stderr, "\t-w - write firmware dump to the specified file\n"); fprintf(stderr, "\t-r - reset dump\n"); fprintf(stderr, "\t-e - force dump\n"); + fprintf(stderr, "\t-f fw.img - flash firmware from fw.img\n"); exit(1); } @@ -160,6 +207,7 @@ enum mlx5_action { ACTION_DUMP_GET, ACTION_DUMP_RESET, ACTION_DUMP_FORCE, + ACTION_FW_UPDATE, ACTION_NONE, }; @@ -169,13 +217,15 @@ main(int argc, char *argv[]) struct mlx5_tool_addr addr; char *dumpname; char *addrstr; + char *img_fw_path; int c, ctldev, res; enum mlx5_action act; act = ACTION_NONE; addrstr = NULL; dumpname = NULL; - while ((c = getopt(argc, argv, "d:eho:rw")) != -1) { + img_fw_path = NULL; + while ((c = getopt(argc, argv, "d:ef:ho:rw")) != -1) { switch (c) { case 'd': addrstr = optarg; @@ -192,12 +242,18 @@ main(int argc, char *argv[]) case 'r': act = ACTION_DUMP_RESET; break; + case 'f': + act = ACTION_FW_UPDATE; + img_fw_path = optarg; + break; case 'h': default: usage(); } } - if (act == ACTION_NONE || (dumpname != NULL && act != ACTION_DUMP_GET)) + if (act == ACTION_NONE || (dumpname != NULL && + act != ACTION_DUMP_GET) || (img_fw_path != NULL && + act != ACTION_FW_UPDATE)) usage(); if (parse_pci_addr(addrstr, &addr) != 0) exit(1); @@ -215,6 +271,9 @@ main(int argc, char *argv[]) case ACTION_DUMP_FORCE: res = mlx5tool_dump_force(ctldev, &addr); break; + case ACTION_FW_UPDATE: + res = mlx5tool_fw_update(ctldev, &addr, img_fw_path); + break; default: res = 0; break;