[RFC,iproute2] devlink: add support for updating device flash

Message ID 20190211065923.22670-5-jakub.kicinski@netronome.com
State New
Delegated to: stephen hemminger
Headers show
Series
  • [RFC,iproute2] devlink: add support for updating device flash
Related show

Commit Message

Jakub Kicinski Feb. 11, 2019, 6:59 a.m.
Add new command for updating flash of devices via devlink API.
Example:

$ cp flash-boot.bin /lib/firmware/
$ devlink dev flash pci/0000:05:00.0 file flash-boot.bin

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
 devlink/devlink.c      | 54 ++++++++++++++++++++++++++++++++++++++++++
 man/man8/devlink-dev.8 | 29 +++++++++++++++++++++++
 2 files changed, 83 insertions(+)

Patch

diff --git a/devlink/devlink.c b/devlink/devlink.c
index d823512a4030..dd5f153eddc6 100644
--- a/devlink/devlink.c
+++ b/devlink/devlink.c
@@ -199,6 +199,8 @@  static void ifname_map_free(struct ifname_map *ifname_map)
 #define DL_OPT_REGION_SNAPSHOT_ID	BIT(22)
 #define DL_OPT_REGION_ADDRESS		BIT(23)
 #define DL_OPT_REGION_LENGTH		BIT(24)
+#define DL_OPT_FLASH_FILE_NAME	BIT(25)
+#define DL_OPT_FLASH_TARGET_ID	BIT(26)
 
 struct dl_opts {
 	uint32_t present; /* flags of present items */
@@ -230,6 +232,8 @@  struct dl_opts {
 	uint32_t region_snapshot_id;
 	uint64_t region_address;
 	uint64_t region_length;
+	const char *flash_file_name;
+	uint32_t flash_target_id;
 };
 
 struct dl {
@@ -1185,6 +1189,20 @@  static int dl_argv_parse(struct dl *dl, uint32_t o_required,
 			if (err)
 				return err;
 			o_found |= DL_OPT_REGION_LENGTH;
+		} else if (dl_argv_match(dl, "file") &&
+			   (o_all & DL_OPT_FLASH_FILE_NAME)) {
+			dl_arg_inc(dl);
+			err = dl_argv_str(dl, &opts->flash_file_name);
+			if (err)
+				return err;
+			o_found |= DL_OPT_FLASH_FILE_NAME;
+		} else if (dl_argv_match(dl, "target") &&
+			   (o_all & DL_OPT_FLASH_TARGET_ID)) {
+			dl_arg_inc(dl);
+			err = dl_argv_uint32_t(dl, &opts->flash_target_id);
+			if (err)
+				return err;
+			o_found |= DL_OPT_FLASH_TARGET_ID;
 		} else {
 			pr_err("Unknown option \"%s\"\n", dl_argv(dl));
 			return -EINVAL;
@@ -1389,6 +1407,12 @@  static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
 	if (opts->present & DL_OPT_REGION_LENGTH)
 		mnl_attr_put_u64(nlh, DEVLINK_ATTR_REGION_CHUNK_LEN,
 				 opts->region_length);
+	if (opts->present & DL_OPT_FLASH_FILE_NAME)
+		mnl_attr_put_strz(nlh, DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME,
+				  opts->flash_file_name);
+	if (opts->present & DL_OPT_FLASH_TARGET_ID)
+		mnl_attr_put_u32(nlh, DEVLINK_ATTR_FLASH_UPDATE_TARGET_ID,
+				 opts->flash_target_id);
 }
 
 static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl,
@@ -1451,6 +1475,7 @@  static void cmd_dev_help(void)
 	pr_err("       devlink dev param show [DEV name PARAMETER]\n");
 	pr_err("       devlink dev reload DEV\n");
 	pr_err("       devlink dev info [ DEV ]\n");
+	pr_err("       devlink dev flash DEV file PATH [ target ID ]\n");
 }
 
 static bool cmp_arr_last_handle(struct dl *dl, const char *bus_name,
@@ -2583,6 +2608,32 @@  static int cmd_dev_info(struct dl *dl)
 	return err;
 }
 
+static void cmd_dev_flash_help(void)
+{
+	pr_err("Usage: devlink dev flash DEV file PATH [ target ID ]\n");
+}
+
+static int cmd_dev_flash(struct dl *dl)
+{
+	struct nlmsghdr *nlh;
+	int err;
+
+	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
+		cmd_dev_flash_help();
+		return 0;
+	}
+
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_FLASH_UPDATE,
+			       NLM_F_REQUEST | NLM_F_ACK);
+
+	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_FLASH_FILE_NAME,
+				DL_OPT_FLASH_TARGET_ID);
+	if (err)
+		return err;
+
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
+}
+
 static int cmd_dev(struct dl *dl)
 {
 	if (dl_argv_match(dl, "help")) {
@@ -2604,6 +2655,9 @@  static int cmd_dev(struct dl *dl)
 	} else if (dl_argv_match(dl, "info")) {
 		dl_arg_inc(dl);
 		return cmd_dev_info(dl);
+	} else if (dl_argv_match(dl, "flash")) {
+		dl_arg_inc(dl);
+		return cmd_dev_flash(dl);
 	}
 	pr_err("Command \"%s\" not found\n", dl_argv(dl));
 	return -ENOENT;
diff --git a/man/man8/devlink-dev.8 b/man/man8/devlink-dev.8
index 47838371fecd..dda35fb09ee0 100644
--- a/man/man8/devlink-dev.8
+++ b/man/man8/devlink-dev.8
@@ -69,6 +69,16 @@  devlink-dev \- devlink device configuration
 .IR DEV
 .RI "]"
 
+.ti -8
+.BR "devlink dev flash"
+.IR DEV
+.BR file
+.IR PATH
+.RI "["
+.BR target
+.IR ID
+.RI "]"
+
 .SH "DESCRIPTION"
 .SS devlink dev show - display devlink device attributes
 
@@ -177,6 +187,25 @@  versions may differ after flash has been updated, but before reboot.
 - specifies the devlink device to show.
 If this argument is omitted all devices are listed.
 
+.SS devlink dev flash - write device's non-volatile memory.
+
+.PP
+.I "DEV"
+- specifies the devlink device to write to.
+
+.BR file
+.I PATH
+- Path to the file which will be written into device's flash. The path needs
+to be relative to one of the directories searched by the kernel firmware loaded,
+such as /lib/firmware.
+
+.BR target
+.I ID
+- If device stores multiple firmware images in non-volatile memory, this
+parameter may be used to indicate which firmware image should be written.
+The default value of 0 means that all regions should be updated.
+Interpretation of other values is driver-dependent.
+
 .SH "EXAMPLES"
 .PP
 devlink dev show