Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package mlxbf-bootctl for openSUSE:Factory 
checked in at 2022-09-19 16:04:18
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/mlxbf-bootctl (Old)
 and      /work/SRC/openSUSE:Factory/.mlxbf-bootctl.new.2083 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "mlxbf-bootctl"

Mon Sep 19 16:04:18 2022 rev:2 rq:1004721 version:1.1.6.14

Changes:
--------
--- /work/SRC/openSUSE:Factory/mlxbf-bootctl/mlxbf-bootctl.changes      
2021-01-27 18:58:06.844422175 +0100
+++ /work/SRC/openSUSE:Factory/.mlxbf-bootctl.new.2083/mlxbf-bootctl.changes    
2022-09-19 16:04:27.238329431 +0200
@@ -1,0 +2,11 @@
+Thu Sep 15 11:18:42 UTC 2022 - Matthias Brugger <mbrug...@suse.com>
+
+- update to 1.1.6.14
+  * check image version to not install unsupported images
+
+-------------------------------------------------------------------
+Thu Jan 28 09:34:07 UTC 2021 - Matthias Brugger <mbrug...@suse.com>
+
+- Fix spec file comments 
+
+-------------------------------------------------------------------

Old:
----
  mlxbf-bootctl-1.1.6.11.obscpio
  mlxbf-bootctl-1.1.6.11.tar

New:
----
  mlxbf-bootctl-1.1.6.14.obscpio
  mlxbf-bootctl-1.1.6.14.tar

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ mlxbf-bootctl.spec ++++++
--- /var/tmp/diff_new_pack.3ZbqUr/_old  2022-09-19 16:04:27.978331408 +0200
+++ /var/tmp/diff_new_pack.3ZbqUr/_new  2022-09-19 16:04:27.986331429 +0200
@@ -1,8 +1,7 @@
 #
-# spec file for package rshim
+# spec file for package mlxbf-bootctl
 #
-# Copyright (c) 2020 SUSE LLC
-# Copyright (c) 2019 Mellanox Technologies. All Rights Reserved.
+# Copyright (c) 2022 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +17,7 @@
 
 
 Name:           mlxbf-bootctl
-Version:        1.1.6.11
+Version:        1.1.6.14
 Release:        0
 Summary:        User-space driver for Mellanox BlueField SoC
 License:        BSD-2-Clause

++++++ mlxbf-bootctl-1.1.6.11.obscpio -> mlxbf-bootctl-1.1.6.14.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mlxbf-bootctl-1.1.6.11/.gitignore 
new/mlxbf-bootctl-1.1.6.14/.gitignore
--- old/mlxbf-bootctl-1.1.6.11/.gitignore       1970-01-01 01:00:00.000000000 
+0100
+++ new/mlxbf-bootctl-1.1.6.14/.gitignore       2022-06-14 20:41:49.000000000 
+0200
@@ -0,0 +1,6 @@
+RPMBUILD
+git_dir_pack
+*.src.rpm
+mlxbf-bootctl
+mlxbf-bootctl.d
+mlxbf-bootctl.o
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mlxbf-bootctl-1.1.6.11/mlxbf-bootctl.8 
new/mlxbf-bootctl-1.1.6.14/mlxbf-bootctl.8
--- old/mlxbf-bootctl-1.1.6.11/mlxbf-bootctl.8  2020-10-04 22:04:14.000000000 
+0200
+++ new/mlxbf-bootctl-1.1.6.14/mlxbf-bootctl.8  2022-06-14 20:41:49.000000000 
+0200
@@ -86,6 +86,18 @@
 \-\-swap|\-s
 Set the boot software so that will swap the primary and alternate partitions
 at the next reset.
+.TP
+.B
+\-\-version|\-v
+Override automatic image version filtering. By default, when 
+.B \-v 
+is not specifed, image versions contained in the bootstream will not be
+installed to the boot partition if they are incompatible with the current
+BlueField platform.
+.B \-v
+allows you to manually specify the target version. Version 0 corresponds to 
BF-1,
+version 1 to BF-2, and so on. Version -1, or any version less than zero,
+will turn off image filtering entirely.
 .SH EXAMPLES
 To update to new firmware as safely as possible:
 .IP
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mlxbf-bootctl-1.1.6.11/mlxbf-bootctl.c 
new/mlxbf-bootctl-1.1.6.14/mlxbf-bootctl.c
--- old/mlxbf-bootctl-1.1.6.11/mlxbf-bootctl.c  2020-10-04 22:04:14.000000000 
+0200
+++ new/mlxbf-bootctl-1.1.6.14/mlxbf-bootctl.c  2022-06-14 20:41:49.000000000 
+0200
@@ -50,6 +50,15 @@
 #define MAX_SEG_LEN ((1 << 20) - SEGMENT_HEADER_LEN)
 #define SEGMENT_IS_END (1UL << 63)
 
+/* File size limits */
+#define INPUT_BFB_MAX_SIZE (20 * 1024 * 1024)
+#define MMC_BOOT_PARTITION_MAX_SIZE (4 * 1024 * 1024)
+
+/* DMI constants */
+#define DMI_TABLE_PATH "/sys/firmware/dmi/tables/DMI"
+#define DMI_PROCESSOR_INFO_TYPE 4
+#define DMI_PROCESSOR_VERSION_STR_MAX_SIZE 100
+
 void die(const char* fmt, ...)
 {
   va_list ap;
@@ -60,6 +69,29 @@
   exit(1);
 }
 
+/*
+ * BFB image header.
+ *
+ * This definition is extracted from file
+ * atf/plat/mellanox/common/include/drivers/io/bluefield_boot.h
+ */
+#define BFB_IMGHDR_MAGIC        0x13026642  /* "Bf^B^S" */
+typedef struct {
+    unsigned long magic:32;
+    unsigned long major:4;
+    unsigned long minor:4;
+    unsigned long reserved:4;
+    unsigned long next_img_ver:4;
+    unsigned long cur_img_ver:4;
+    unsigned long hdr_len:4;
+    unsigned long image_id:8;
+    unsigned long image_len:32;
+    unsigned long image_crc:32;
+    unsigned long following_images:64;
+} boot_image_header_t;
+
+ssize_t read_or_die(const char* filename, int fd, void* buf, size_t count);
+
 #ifndef OUTPUT_ONLY
 
 #include <linux/mmc/ioctl.h>
@@ -112,29 +144,6 @@
 #define LIFECYCLE_STATE_PATH "lifecycle_state"
 #define SECURE_BOOT_FUSE_STATE_PATH "secure_boot_fuse_state"
 
-#define MMC_BOOT_PARTITION_MAX_SIZE (4 * 1024 * 1024)
-
-/*
- * BFB image header.
- *
- * This definition is extracted from file
- * atf/plat/mellanox/common/include/drivers/io/bluefield_boot.h
- */
-#define BFB_IMGHDR_MAGIC        0x13026642  /* "Bf^B^S" */
-typedef struct {
-    unsigned long magic:32;
-    unsigned long major:4;
-    unsigned long minor:4;
-    unsigned long reserved:4;
-    unsigned long next_img_ver:4;
-    unsigned long cur_img_ver:4;
-    unsigned long hdr_len:4;
-    unsigned long image_id:8;
-    unsigned long image_len:32;
-    unsigned long image_crc:32;
-    unsigned long following_images:64;
-} boot_image_header_t;
-
 /* Program variables */
 const char *mmc_path = "/dev/mmcblk0";
 
@@ -372,6 +381,142 @@
   printf("secure boot key free slots: %d\n", get_free_sbfuse_slots());
 }
 
+// Get the version of hardware we're currently running on.
+// If it can't be found, this will return -1.
+int get_hw_version(void) {
+  const char *dmi_file = DMI_TABLE_PATH;
+
+  int dmi_fd = open(dmi_file, O_RDONLY);
+  if (dmi_fd < 0) {
+    fprintf(stderr, "warn: %s: %m, disable version filtering\n", dmi_file);
+    return -1;
+  }
+
+  struct stat st;
+  if (fstat(dmi_fd, &st) < 0)
+    die("%s: stat: %m", dmi_file);
+
+  void *dmi_buf = malloc(st.st_size);
+  if (dmi_buf == NULL)
+    die("out of memory");
+
+  int n_bytes = 0;
+  n_bytes = read_or_die(dmi_file, dmi_fd, dmi_buf, st.st_size);
+  if (n_bytes != st.st_size)
+    die("%s: could not read DMI table", dmi_file);
+
+  if (close(dmi_fd) < 0)
+    die("%s: close: %m", dmi_file);
+
+  // Now, skip along table until we reach the processor info
+  int bytes_left = st.st_size;
+  void *idx = dmi_buf;
+  uint8_t table_size = 0;
+
+  while (bytes_left > 0) {
+    uint8_t table_type = *((uint8_t*)idx);
+    table_size = *((uint8_t*)idx + 1);
+
+    // Break if we hit the processor table.
+    if (table_type == DMI_PROCESSOR_INFO_TYPE)
+      break;
+
+    // Otherwise, skip to next table.
+    idx += table_size;
+    bytes_left -= table_size;
+
+    bool first_str = true;
+
+    // We might need to skip over strings, too.
+    while (bytes_left > 0) {
+      // If idx is over two 0 bytes, we're at the end, so
+      // advance to the next table.
+      if (*(uint16_t*)idx == 0) {
+        idx += sizeof(uint16_t);
+        bytes_left -= sizeof(uint16_t);
+        break;
+      } else {
+        // Skip a string. Note the first iteration will
+        // place the index at the beginning of the string,
+        // whereas all further iterations place the index at
+        // the NULL terminator.
+        if (!first_str) {
+          idx++;
+          bytes_left--;
+        }
+        first_str = false;
+        while (*(char*)idx && bytes_left > 0) {
+          idx++;
+          bytes_left--;
+        }
+      }
+    }
+  }
+
+  if (bytes_left <= 0) {
+    fprintf(stderr, "warning: could not find BlueField SoC revision\n");
+    free(dmi_buf);
+    return -1;
+  }
+
+  // Now idx is at processor version table - we can read the
+  // string now. SMBIOS tables append all strings to the end
+  // of the structure, and refer to them by their order.
+  // First string is 1, second is 2, etc.
+  uint8_t soc_ver_snum = *(uint8_t*)(idx + 0x10);
+  if (soc_ver_snum == 0) {
+    fprintf(stderr, "warning: found DMI processor ver field, but it's NULL\n");
+    free(dmi_buf);
+    return -1;
+  }
+
+  idx += table_size;
+  bytes_left -= table_size;
+
+  // Skip strings until we hit the desired one.
+  char version_string[DMI_PROCESSOR_VERSION_STR_MAX_SIZE] = {0};
+  bool first_str = true;
+  while (soc_ver_snum-- > 1 && bytes_left > 0) {
+    if (*(uint16_t*)idx == 0) {
+      fprintf(stderr, "warning: DMI processor ver specifies string that does 
not exist.\n");
+      free(dmi_buf);
+      return -1;
+    } else {
+      if (!first_str) {
+        idx++;
+        bytes_left--;
+      }
+      first_str = false;
+      // Skip a string.
+      while (*(char*)idx && bytes_left > 0) {
+        idx++;
+        bytes_left--;
+      }
+    }
+  }
+
+  idx++;
+  bytes_left--;
+
+  // Finally, actually copy the string.
+  strncpy(
+    version_string,
+    idx,
+    bytes_left < DMI_PROCESSOR_VERSION_STR_MAX_SIZE - 1 ? bytes_left : 
DMI_PROCESSOR_VERSION_STR_MAX_SIZE - 1
+  );
+
+  // Extract version
+  int version = -1;
+  if (1 != sscanf(version_string, "Mellanox BlueField-%d", &version))
+    fprintf(stderr, "warning: Unknown SoC revision");
+
+  // BlueField 1 is v0, BF2 is v1, etc.
+  version--;
+
+  free(dmi_buf);
+  return version;
+}
+
 #endif  // OUTPUT_ONLY
 
 // Read as much as possible despite EINTR or partial reads, and die on error.
@@ -496,10 +641,198 @@
   free(buf);
 }
 
-void write_bootstream(const char *bootstream, const char *bootfile, int flags)
+// Read a header from a boot stream. This will consume the
+// entire header, verify it, and return the header
+// structure.
+size_t read_bootstream_header(const char *bootstream, int ifd, 
boot_image_header_t *hdr) {
+    int n;
+    uint64_t data;
+
+    // Read and verify the image header.
+    n = read_or_die(bootstream, ifd, hdr, sizeof(*hdr));
+    if (n != sizeof(*hdr))
+      die("Unable to read next header, n=%d", n);
+    n = hdr->hdr_len - sizeof(*hdr) / sizeof(uint64_t);
+    if (n < 0)
+      die("Invalid header length %d, n=%d", hdr->hdr_len, n);
+    // Drain the rest of header.
+    while (n--) {
+      if (read_or_die(bootstream, ifd, &data, sizeof(data)) != sizeof(data))
+        die("Not enough data for header");
+    }
+
+    return hdr->hdr_len * sizeof(uint64_t);
+}
+
+// Correct the following_images and next_img_ver fields in a BFB buffer.
+// Note this assumes a well-formed BFB stream in the buffer.
+void correct_bootstream_headers(void *buf, int num_images) {
+  uint64_t fi_map = 0;
+  void *idx = buf;
+  boot_image_header_t *hdr;
+  int img_size = 0;
+  int pad_size = 0;
+
+  if (num_images <= 0)
+    die("%s: num_images must be at least 1", __FUNCTION__);
+
+  // Markers to find images next time
+  void **images = malloc(num_images * sizeof(void*));
+  if (images == NULL)
+    die("out of memory");
+
+  // Iterate once to build fi_map and mark image starts
+  for (int i = 0; i < num_images; i++) {
+    hdr = (boot_image_header_t*) idx;
+
+    // Build image maps
+    fi_map |= 1UL << hdr->image_id;
+    images[i] = idx;
+
+    // Skip to next image
+    img_size = hdr->image_len;
+    pad_size = (img_size % 8) ? (8 - img_size % 8) : 0;
+    idx += img_size + pad_size + hdr->hdr_len * sizeof(uint64_t);
+  }
+
+  boot_image_header_t *prev_hdr;
+
+  // Now we can iterate over the images directly and fixup
+  // the fields that need it
+  for (int i = 0; i < num_images; i++) {
+    hdr = (boot_image_header_t*) images[i];
+
+    // Update image bitmap. Note that on the last
+    // image of a series of versions, this will be wrong,
+    // and will be corrected next iteration.
+    hdr->following_images = fi_map;
+
+    if (i > 0) {
+      prev_hdr = (boot_image_header_t*) images[i-1];
+
+      if (prev_hdr->image_id == hdr->image_id) {
+        // Update next_img_ver as long as we haven't gone to
+        // a new image ID.
+        prev_hdr->next_img_ver = hdr->cur_img_ver;
+      } else {
+        // If next image ID is different, then update
+        // fi_map, and correct the previous image.
+        fi_map &= ~(1UL << prev_hdr->image_id);
+        prev_hdr->following_images = fi_map;
+
+        // We also set prev_hdr's next_img_ver to 0 since
+        // there are no more images of that version.
+        prev_hdr->next_img_ver = 0;
+      }
+    }
+  }
+
+  // Edge case: Handle last image.
+  hdr = (boot_image_header_t*)images[num_images-1];
+  hdr->following_images = 0;
+  hdr->next_img_ver = 0;
+
+  free(images);
+}
+
+// Tell whether we should write an image to the boot
+// partition, based on a version number.
+// We do this conservatively, so we don't accidentally
+// filter out something common.
+bool should_install_image(boot_image_header_t *hdr, int version) {
+  // version < 0 indicates filtering should be off. All
+  // images will be installed in this case.
+  if (version < 0)
+    return true;
+
+  // For now, do not install anything that was introduced
+  // after the given version.
+  return hdr->cur_img_ver <= version;
+}
+
+// Read a bootstream to an internal buffer, optionally filtering out images
+// of a given version. Supply -1 to version to turn this behavior off.
+size_t read_bootstream_to_buffer(const char *bootstream, void *buf, int 
buf_size, int version)
+{
+  int ifd = open(bootstream, O_RDONLY);
+  if (ifd < 0)
+    die("%s: %m", bootstream);
+
+  struct stat st;
+  if (fstat(ifd, &st) < 0)
+    die("%s: stat: %m", bootstream);
+
+  int bytes_left = st.st_size;
+
+  // If no version filtering requested and file is too large for the
+  // buffer, error out.
+  if (version < 0 && bytes_left > buf_size) {
+    die("%s: Boot stream file is too large", bootstream);
+  }
+
+  void *idx = buf; // Index along buffer
+  size_t n_bytes = 0;
+  boot_image_header_t hdr;
+  size_t hdr_size; // Size of the image header, including reserved words
+  size_t pad_size;
+  size_t img_size;
+
+  int num_images = 0;
+
+  // Otherwise, we'll need to read the whole file and filter it to tell
+  // whether stream will fit.
+  while (bytes_left > 0) {
+    hdr_size = read_bootstream_header(bootstream, ifd, &hdr);
+    bytes_left -= hdr_size;
+    img_size = hdr.image_len;
+
+    pad_size = (img_size % 8) ? (8 - img_size % 8) : 0;
+
+    // Check whether the version should be included.
+    if (should_install_image(&hdr, version)) {
+      // If so, copy the header and img into the buffer.
+      if ((hdr_size + idx) - buf > buf_size)
+        die("Boot stream file is too large");
+
+      memcpy(idx, &hdr, sizeof(hdr));
+      idx += hdr_size;
+
+      // Copy image into buffer.
+      if ((hdr.image_len + idx) - buf > buf_size)
+        die("Boot stream file is too large");
+
+      n_bytes = read_or_die(bootstream, ifd, idx, img_size + pad_size);
+      if (n_bytes != img_size + pad_size)
+        die("Unable to read next image, n=%d", n_bytes);
+
+      idx += n_bytes;
+      bytes_left -= n_bytes;
+
+      // Finally, count the image. We reuse this later for
+      // fixing up the headers.
+      num_images++;
+    } else {
+      // Otherwise, skip over the entire image.
+      n_bytes = lseek(ifd, img_size + pad_size, SEEK_CUR);
+      if (n_bytes < 0)
+        die("%s: Could not skip filtered image: %m", bootstream);
+      bytes_left -= img_size + pad_size;
+    }
+  }
+
+  if (close(ifd) < 0)
+    die("%s: close: %m", bootstream);
+
+  correct_bootstream_headers(buf, num_images);
+
+  return idx - buf;
+}
+
+void write_bootstream(const char *bootstream, const char *bootfile, int flags, 
int version)
 {
   int sysfd = -1;
   char *sysname;
+  size_t ibuf_maxsize = INPUT_BFB_MAX_SIZE;
 
   // Reset the force_ro setting if need be
   if (strncmp(bootfile, "/dev/", 5) == 0)
@@ -536,23 +869,21 @@
       free(sysname);
       sysname = NULL;
     }
+
+  // If writing to /dev/... assume it's an MMC partition
+  ibuf_maxsize = MMC_BOOT_PARTITION_MAX_SIZE;
   }
 
   // Copy the bootstream to the bootfile device
-  int ifd = open(bootstream, O_RDONLY);
-  if (ifd < 0)
-    die("%s: %m", bootstream);
+  void *ibuf = malloc(ibuf_maxsize);
+  void *inidx = ibuf;
+  uint8_t padbuf[8] = {0}; // There are at most 8 pad bytes.
+  if (ibuf == NULL)
+    die("out of memory");
+  size_t bytes_left = read_bootstream_to_buffer(bootstream, ibuf, 
ibuf_maxsize, version);
   int ofd = open(bootfile, O_WRONLY | flags, 0666);
   if (ofd < 0)
     die("%s: %m", bootfile);
-  struct stat st;
-  if (fstat(ifd, &st) < 0)
-    die("%s: stat: %m", bootstream);
-  size_t bytes_left = st.st_size;
-
-  char *buf = malloc(MAX_SEG_LEN);
-  if (buf == NULL)
-    die("out of memory");
 
   // Write the bootstream header word first.  This has the byte to
   // be displayed in the rev_id register as the low 8 bits (zero for now).
@@ -571,16 +902,14 @@
     write_or_die(bootfile, ofd, &segheader, sizeof(segheader));
 
     // Copy the segment plus any padding.
-    read_or_die(bootstream, ifd, buf, seg_size);
-    memset(buf + seg_size, 0, pad_size);
-    write_or_die(bootfile, ofd, buf, seg_size + pad_size);
+    write_or_die(bootfile, ofd, inidx, seg_size);
+    inidx += seg_size;
+    write_or_die(bootfile, ofd, padbuf, pad_size);
   }
 
-  if (close(ifd) < 0)
-    die("%s: close: %m", bootstream);
   if (close(ofd) < 0)
     die("%s: close: %m", bootfile);
-  free(buf);
+  free(ibuf);
 
   // Put back the force_ro setting if need be
   if (sysfd >= 0)
@@ -635,7 +964,7 @@
   if (bootstream == NULL || output_file == NULL)
     die("mlx-bootctl: Must specify --output and --bootstream");
 
-  write_bootstream(bootstream, output_file, O_CREAT | O_TRUNC);
+  write_bootstream(bootstream, output_file, O_CREAT | O_TRUNC, -1);
   return 0;
 }
 
@@ -656,7 +985,7 @@
 
 static void verify_bootstream(const char *bootfile)
 {
-  int ifd, n, bytes_left, img_size;
+  int ifd, bytes_left, img_size;
   boot_image_header_t hdr;
   struct stat st;
   uint64_t data;
@@ -671,23 +1000,13 @@
   bytes_left = st.st_size;
   if (bytes_left % sizeof(uint64_t))
     die("Invalid size %d", bytes_left);
-  if (bytes_left > MMC_BOOT_PARTITION_MAX_SIZE)
-    die("Boot file size too big");
+  if (bytes_left > INPUT_BFB_MAX_SIZE)
+    die("Input boot file size too big");
 
   while (bytes_left > 0) {
-    // Read and verify the image header.
-    n = read_or_die(bootfile, ifd, &hdr, sizeof(hdr));
-    if (n != sizeof(hdr))
-      die("Unable to read next header, n=%d", n);
-    n = hdr.hdr_len - sizeof(hdr) / sizeof(uint64_t);
-    bytes_left -= hdr.hdr_len * sizeof(uint64_t);
-    if (n < 0 || bytes_left < 0)
+    bytes_left -= read_bootstream_header(bootfile, ifd, &hdr);
+    if (bytes_left < 0)
       die("Invalid header length");
-    // Drain the rest of header.
-    while (n--) {
-      if (read_or_die(bootfile, ifd, &data, sizeof(data)) != sizeof(data))
-        die("Not enough data for header");
-    }
 
     // Verify the image crc.
     crc = ~0;
@@ -717,15 +1036,17 @@
     { "device", required_argument, NULL, 'd' },
     { "output", required_argument, NULL, 'o' },
     { "read", required_argument, NULL, 'r' },
+    { "version", required_argument, NULL, 'v' },
     { "help", no_argument, NULL, 'h' },
     { NULL, 0, NULL, 0 }
   };
-  static const char short_options[] = "sb:d:o:r:he";
+  static const char short_options[] = "sb:d:o:r:hev:";
   static const char help_text[] =
     "syntax: mlxbf-bootctl [--help|-h] [--swap|-s] [--device|-d MMCFILE]\n"
     "                      [--output|-o OUTPUT] [--read|-r INPUT]\n"
     "                      [--bootstream|-b BFBFILE] [--overwrite-current]\n"
-    "                      [--watchdog-swap interval | --nowatchdog-swap]";
+    "                      [--watchdog-swap interval | --nowatchdog-swap]\n"
+    "                      [--version|-v VERSION]";
 
   const char *watchdog_swap = NULL;
   const char *bootstream = NULL;
@@ -733,6 +1054,8 @@
   const char *input_file = NULL;
   bool watchdog_disable = false;
   bool swap = false;
+  bool auto_version = true;
+  int version_arg = -1;
   int which_boot = 1;   // alternate boot partition by default
   int opt;
 
@@ -779,6 +1102,14 @@
       enable_rst_n();
       break;
 
+    case 'v':
+      auto_version = false;
+      char *end;
+      version_arg = strtol(optarg, &end, 0);
+      if (end == optarg || *end != '\0')
+        die("version argument ('%s') must be an integer", optarg);
+      break;
+
     case 'h':
     default:
       die(help_text);
@@ -802,7 +1133,8 @@
     else if (output_file)
     {
       // Write the bootstream to the given file, creating it if needed
-      write_bootstream(bootstream, output_file, O_CREAT | O_TRUNC);
+      // Don't filter anything here, since we're not writing to EMMC.
+      write_bootstream(bootstream, output_file, O_CREAT | O_TRUNC, -1);
     }
     else
     {
@@ -827,7 +1159,13 @@
       if (asprintf(&bootfile, "%sboot%d", mmc_path, boot_part ^ which_boot) <= 
0)
         die("unexpected failure in asprintf (%s/%d)", __FILE__, __LINE__);
 
-      write_bootstream(bootstream, bootfile, O_SYNC);
+      int version;
+      if (auto_version) {
+        version = get_hw_version();
+      } else {
+        version = version_arg;
+      }
+      write_bootstream(bootstream, bootfile, O_SYNC, version);
       // The eMMC driver works in an asynchronous way, thus any commands sent
       // should occur after the write to the bootstream has retired. Otherwise
       // a blk_update_request I/O error would be raised and the success of the

++++++ mlxbf-bootctl-1.1.6.11.tar -> mlxbf-bootctl-1.1.6.14.tar ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mlxbf-bootctl-1.1.6.11/.gitignore 
new/mlxbf-bootctl-1.1.6.14/.gitignore
--- old/mlxbf-bootctl-1.1.6.11/.gitignore       1970-01-01 01:00:00.000000000 
+0100
+++ new/mlxbf-bootctl-1.1.6.14/.gitignore       2022-06-14 20:41:49.000000000 
+0200
@@ -0,0 +1,6 @@
+RPMBUILD
+git_dir_pack
+*.src.rpm
+mlxbf-bootctl
+mlxbf-bootctl.d
+mlxbf-bootctl.o
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mlxbf-bootctl-1.1.6.11/mlxbf-bootctl.8 
new/mlxbf-bootctl-1.1.6.14/mlxbf-bootctl.8
--- old/mlxbf-bootctl-1.1.6.11/mlxbf-bootctl.8  2020-10-04 22:04:14.000000000 
+0200
+++ new/mlxbf-bootctl-1.1.6.14/mlxbf-bootctl.8  2022-06-14 20:41:49.000000000 
+0200
@@ -86,6 +86,18 @@
 \-\-swap|\-s
 Set the boot software so that will swap the primary and alternate partitions
 at the next reset.
+.TP
+.B
+\-\-version|\-v
+Override automatic image version filtering. By default, when 
+.B \-v 
+is not specifed, image versions contained in the bootstream will not be
+installed to the boot partition if they are incompatible with the current
+BlueField platform.
+.B \-v
+allows you to manually specify the target version. Version 0 corresponds to 
BF-1,
+version 1 to BF-2, and so on. Version -1, or any version less than zero,
+will turn off image filtering entirely.
 .SH EXAMPLES
 To update to new firmware as safely as possible:
 .IP
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mlxbf-bootctl-1.1.6.11/mlxbf-bootctl.c 
new/mlxbf-bootctl-1.1.6.14/mlxbf-bootctl.c
--- old/mlxbf-bootctl-1.1.6.11/mlxbf-bootctl.c  2020-10-04 22:04:14.000000000 
+0200
+++ new/mlxbf-bootctl-1.1.6.14/mlxbf-bootctl.c  2022-06-14 20:41:49.000000000 
+0200
@@ -50,6 +50,15 @@
 #define MAX_SEG_LEN ((1 << 20) - SEGMENT_HEADER_LEN)
 #define SEGMENT_IS_END (1UL << 63)
 
+/* File size limits */
+#define INPUT_BFB_MAX_SIZE (20 * 1024 * 1024)
+#define MMC_BOOT_PARTITION_MAX_SIZE (4 * 1024 * 1024)
+
+/* DMI constants */
+#define DMI_TABLE_PATH "/sys/firmware/dmi/tables/DMI"
+#define DMI_PROCESSOR_INFO_TYPE 4
+#define DMI_PROCESSOR_VERSION_STR_MAX_SIZE 100
+
 void die(const char* fmt, ...)
 {
   va_list ap;
@@ -60,6 +69,29 @@
   exit(1);
 }
 
+/*
+ * BFB image header.
+ *
+ * This definition is extracted from file
+ * atf/plat/mellanox/common/include/drivers/io/bluefield_boot.h
+ */
+#define BFB_IMGHDR_MAGIC        0x13026642  /* "Bf^B^S" */
+typedef struct {
+    unsigned long magic:32;
+    unsigned long major:4;
+    unsigned long minor:4;
+    unsigned long reserved:4;
+    unsigned long next_img_ver:4;
+    unsigned long cur_img_ver:4;
+    unsigned long hdr_len:4;
+    unsigned long image_id:8;
+    unsigned long image_len:32;
+    unsigned long image_crc:32;
+    unsigned long following_images:64;
+} boot_image_header_t;
+
+ssize_t read_or_die(const char* filename, int fd, void* buf, size_t count);
+
 #ifndef OUTPUT_ONLY
 
 #include <linux/mmc/ioctl.h>
@@ -112,29 +144,6 @@
 #define LIFECYCLE_STATE_PATH "lifecycle_state"
 #define SECURE_BOOT_FUSE_STATE_PATH "secure_boot_fuse_state"
 
-#define MMC_BOOT_PARTITION_MAX_SIZE (4 * 1024 * 1024)
-
-/*
- * BFB image header.
- *
- * This definition is extracted from file
- * atf/plat/mellanox/common/include/drivers/io/bluefield_boot.h
- */
-#define BFB_IMGHDR_MAGIC        0x13026642  /* "Bf^B^S" */
-typedef struct {
-    unsigned long magic:32;
-    unsigned long major:4;
-    unsigned long minor:4;
-    unsigned long reserved:4;
-    unsigned long next_img_ver:4;
-    unsigned long cur_img_ver:4;
-    unsigned long hdr_len:4;
-    unsigned long image_id:8;
-    unsigned long image_len:32;
-    unsigned long image_crc:32;
-    unsigned long following_images:64;
-} boot_image_header_t;
-
 /* Program variables */
 const char *mmc_path = "/dev/mmcblk0";
 
@@ -372,6 +381,142 @@
   printf("secure boot key free slots: %d\n", get_free_sbfuse_slots());
 }
 
+// Get the version of hardware we're currently running on.
+// If it can't be found, this will return -1.
+int get_hw_version(void) {
+  const char *dmi_file = DMI_TABLE_PATH;
+
+  int dmi_fd = open(dmi_file, O_RDONLY);
+  if (dmi_fd < 0) {
+    fprintf(stderr, "warn: %s: %m, disable version filtering\n", dmi_file);
+    return -1;
+  }
+
+  struct stat st;
+  if (fstat(dmi_fd, &st) < 0)
+    die("%s: stat: %m", dmi_file);
+
+  void *dmi_buf = malloc(st.st_size);
+  if (dmi_buf == NULL)
+    die("out of memory");
+
+  int n_bytes = 0;
+  n_bytes = read_or_die(dmi_file, dmi_fd, dmi_buf, st.st_size);
+  if (n_bytes != st.st_size)
+    die("%s: could not read DMI table", dmi_file);
+
+  if (close(dmi_fd) < 0)
+    die("%s: close: %m", dmi_file);
+
+  // Now, skip along table until we reach the processor info
+  int bytes_left = st.st_size;
+  void *idx = dmi_buf;
+  uint8_t table_size = 0;
+
+  while (bytes_left > 0) {
+    uint8_t table_type = *((uint8_t*)idx);
+    table_size = *((uint8_t*)idx + 1);
+
+    // Break if we hit the processor table.
+    if (table_type == DMI_PROCESSOR_INFO_TYPE)
+      break;
+
+    // Otherwise, skip to next table.
+    idx += table_size;
+    bytes_left -= table_size;
+
+    bool first_str = true;
+
+    // We might need to skip over strings, too.
+    while (bytes_left > 0) {
+      // If idx is over two 0 bytes, we're at the end, so
+      // advance to the next table.
+      if (*(uint16_t*)idx == 0) {
+        idx += sizeof(uint16_t);
+        bytes_left -= sizeof(uint16_t);
+        break;
+      } else {
+        // Skip a string. Note the first iteration will
+        // place the index at the beginning of the string,
+        // whereas all further iterations place the index at
+        // the NULL terminator.
+        if (!first_str) {
+          idx++;
+          bytes_left--;
+        }
+        first_str = false;
+        while (*(char*)idx && bytes_left > 0) {
+          idx++;
+          bytes_left--;
+        }
+      }
+    }
+  }
+
+  if (bytes_left <= 0) {
+    fprintf(stderr, "warning: could not find BlueField SoC revision\n");
+    free(dmi_buf);
+    return -1;
+  }
+
+  // Now idx is at processor version table - we can read the
+  // string now. SMBIOS tables append all strings to the end
+  // of the structure, and refer to them by their order.
+  // First string is 1, second is 2, etc.
+  uint8_t soc_ver_snum = *(uint8_t*)(idx + 0x10);
+  if (soc_ver_snum == 0) {
+    fprintf(stderr, "warning: found DMI processor ver field, but it's NULL\n");
+    free(dmi_buf);
+    return -1;
+  }
+
+  idx += table_size;
+  bytes_left -= table_size;
+
+  // Skip strings until we hit the desired one.
+  char version_string[DMI_PROCESSOR_VERSION_STR_MAX_SIZE] = {0};
+  bool first_str = true;
+  while (soc_ver_snum-- > 1 && bytes_left > 0) {
+    if (*(uint16_t*)idx == 0) {
+      fprintf(stderr, "warning: DMI processor ver specifies string that does 
not exist.\n");
+      free(dmi_buf);
+      return -1;
+    } else {
+      if (!first_str) {
+        idx++;
+        bytes_left--;
+      }
+      first_str = false;
+      // Skip a string.
+      while (*(char*)idx && bytes_left > 0) {
+        idx++;
+        bytes_left--;
+      }
+    }
+  }
+
+  idx++;
+  bytes_left--;
+
+  // Finally, actually copy the string.
+  strncpy(
+    version_string,
+    idx,
+    bytes_left < DMI_PROCESSOR_VERSION_STR_MAX_SIZE - 1 ? bytes_left : 
DMI_PROCESSOR_VERSION_STR_MAX_SIZE - 1
+  );
+
+  // Extract version
+  int version = -1;
+  if (1 != sscanf(version_string, "Mellanox BlueField-%d", &version))
+    fprintf(stderr, "warning: Unknown SoC revision");
+
+  // BlueField 1 is v0, BF2 is v1, etc.
+  version--;
+
+  free(dmi_buf);
+  return version;
+}
+
 #endif  // OUTPUT_ONLY
 
 // Read as much as possible despite EINTR or partial reads, and die on error.
@@ -496,10 +641,198 @@
   free(buf);
 }
 
-void write_bootstream(const char *bootstream, const char *bootfile, int flags)
+// Read a header from a boot stream. This will consume the
+// entire header, verify it, and return the header
+// structure.
+size_t read_bootstream_header(const char *bootstream, int ifd, 
boot_image_header_t *hdr) {
+    int n;
+    uint64_t data;
+
+    // Read and verify the image header.
+    n = read_or_die(bootstream, ifd, hdr, sizeof(*hdr));
+    if (n != sizeof(*hdr))
+      die("Unable to read next header, n=%d", n);
+    n = hdr->hdr_len - sizeof(*hdr) / sizeof(uint64_t);
+    if (n < 0)
+      die("Invalid header length %d, n=%d", hdr->hdr_len, n);
+    // Drain the rest of header.
+    while (n--) {
+      if (read_or_die(bootstream, ifd, &data, sizeof(data)) != sizeof(data))
+        die("Not enough data for header");
+    }
+
+    return hdr->hdr_len * sizeof(uint64_t);
+}
+
+// Correct the following_images and next_img_ver fields in a BFB buffer.
+// Note this assumes a well-formed BFB stream in the buffer.
+void correct_bootstream_headers(void *buf, int num_images) {
+  uint64_t fi_map = 0;
+  void *idx = buf;
+  boot_image_header_t *hdr;
+  int img_size = 0;
+  int pad_size = 0;
+
+  if (num_images <= 0)
+    die("%s: num_images must be at least 1", __FUNCTION__);
+
+  // Markers to find images next time
+  void **images = malloc(num_images * sizeof(void*));
+  if (images == NULL)
+    die("out of memory");
+
+  // Iterate once to build fi_map and mark image starts
+  for (int i = 0; i < num_images; i++) {
+    hdr = (boot_image_header_t*) idx;
+
+    // Build image maps
+    fi_map |= 1UL << hdr->image_id;
+    images[i] = idx;
+
+    // Skip to next image
+    img_size = hdr->image_len;
+    pad_size = (img_size % 8) ? (8 - img_size % 8) : 0;
+    idx += img_size + pad_size + hdr->hdr_len * sizeof(uint64_t);
+  }
+
+  boot_image_header_t *prev_hdr;
+
+  // Now we can iterate over the images directly and fixup
+  // the fields that need it
+  for (int i = 0; i < num_images; i++) {
+    hdr = (boot_image_header_t*) images[i];
+
+    // Update image bitmap. Note that on the last
+    // image of a series of versions, this will be wrong,
+    // and will be corrected next iteration.
+    hdr->following_images = fi_map;
+
+    if (i > 0) {
+      prev_hdr = (boot_image_header_t*) images[i-1];
+
+      if (prev_hdr->image_id == hdr->image_id) {
+        // Update next_img_ver as long as we haven't gone to
+        // a new image ID.
+        prev_hdr->next_img_ver = hdr->cur_img_ver;
+      } else {
+        // If next image ID is different, then update
+        // fi_map, and correct the previous image.
+        fi_map &= ~(1UL << prev_hdr->image_id);
+        prev_hdr->following_images = fi_map;
+
+        // We also set prev_hdr's next_img_ver to 0 since
+        // there are no more images of that version.
+        prev_hdr->next_img_ver = 0;
+      }
+    }
+  }
+
+  // Edge case: Handle last image.
+  hdr = (boot_image_header_t*)images[num_images-1];
+  hdr->following_images = 0;
+  hdr->next_img_ver = 0;
+
+  free(images);
+}
+
+// Tell whether we should write an image to the boot
+// partition, based on a version number.
+// We do this conservatively, so we don't accidentally
+// filter out something common.
+bool should_install_image(boot_image_header_t *hdr, int version) {
+  // version < 0 indicates filtering should be off. All
+  // images will be installed in this case.
+  if (version < 0)
+    return true;
+
+  // For now, do not install anything that was introduced
+  // after the given version.
+  return hdr->cur_img_ver <= version;
+}
+
+// Read a bootstream to an internal buffer, optionally filtering out images
+// of a given version. Supply -1 to version to turn this behavior off.
+size_t read_bootstream_to_buffer(const char *bootstream, void *buf, int 
buf_size, int version)
+{
+  int ifd = open(bootstream, O_RDONLY);
+  if (ifd < 0)
+    die("%s: %m", bootstream);
+
+  struct stat st;
+  if (fstat(ifd, &st) < 0)
+    die("%s: stat: %m", bootstream);
+
+  int bytes_left = st.st_size;
+
+  // If no version filtering requested and file is too large for the
+  // buffer, error out.
+  if (version < 0 && bytes_left > buf_size) {
+    die("%s: Boot stream file is too large", bootstream);
+  }
+
+  void *idx = buf; // Index along buffer
+  size_t n_bytes = 0;
+  boot_image_header_t hdr;
+  size_t hdr_size; // Size of the image header, including reserved words
+  size_t pad_size;
+  size_t img_size;
+
+  int num_images = 0;
+
+  // Otherwise, we'll need to read the whole file and filter it to tell
+  // whether stream will fit.
+  while (bytes_left > 0) {
+    hdr_size = read_bootstream_header(bootstream, ifd, &hdr);
+    bytes_left -= hdr_size;
+    img_size = hdr.image_len;
+
+    pad_size = (img_size % 8) ? (8 - img_size % 8) : 0;
+
+    // Check whether the version should be included.
+    if (should_install_image(&hdr, version)) {
+      // If so, copy the header and img into the buffer.
+      if ((hdr_size + idx) - buf > buf_size)
+        die("Boot stream file is too large");
+
+      memcpy(idx, &hdr, sizeof(hdr));
+      idx += hdr_size;
+
+      // Copy image into buffer.
+      if ((hdr.image_len + idx) - buf > buf_size)
+        die("Boot stream file is too large");
+
+      n_bytes = read_or_die(bootstream, ifd, idx, img_size + pad_size);
+      if (n_bytes != img_size + pad_size)
+        die("Unable to read next image, n=%d", n_bytes);
+
+      idx += n_bytes;
+      bytes_left -= n_bytes;
+
+      // Finally, count the image. We reuse this later for
+      // fixing up the headers.
+      num_images++;
+    } else {
+      // Otherwise, skip over the entire image.
+      n_bytes = lseek(ifd, img_size + pad_size, SEEK_CUR);
+      if (n_bytes < 0)
+        die("%s: Could not skip filtered image: %m", bootstream);
+      bytes_left -= img_size + pad_size;
+    }
+  }
+
+  if (close(ifd) < 0)
+    die("%s: close: %m", bootstream);
+
+  correct_bootstream_headers(buf, num_images);
+
+  return idx - buf;
+}
+
+void write_bootstream(const char *bootstream, const char *bootfile, int flags, 
int version)
 {
   int sysfd = -1;
   char *sysname;
+  size_t ibuf_maxsize = INPUT_BFB_MAX_SIZE;
 
   // Reset the force_ro setting if need be
   if (strncmp(bootfile, "/dev/", 5) == 0)
@@ -536,23 +869,21 @@
       free(sysname);
       sysname = NULL;
     }
+
+  // If writing to /dev/... assume it's an MMC partition
+  ibuf_maxsize = MMC_BOOT_PARTITION_MAX_SIZE;
   }
 
   // Copy the bootstream to the bootfile device
-  int ifd = open(bootstream, O_RDONLY);
-  if (ifd < 0)
-    die("%s: %m", bootstream);
+  void *ibuf = malloc(ibuf_maxsize);
+  void *inidx = ibuf;
+  uint8_t padbuf[8] = {0}; // There are at most 8 pad bytes.
+  if (ibuf == NULL)
+    die("out of memory");
+  size_t bytes_left = read_bootstream_to_buffer(bootstream, ibuf, 
ibuf_maxsize, version);
   int ofd = open(bootfile, O_WRONLY | flags, 0666);
   if (ofd < 0)
     die("%s: %m", bootfile);
-  struct stat st;
-  if (fstat(ifd, &st) < 0)
-    die("%s: stat: %m", bootstream);
-  size_t bytes_left = st.st_size;
-
-  char *buf = malloc(MAX_SEG_LEN);
-  if (buf == NULL)
-    die("out of memory");
 
   // Write the bootstream header word first.  This has the byte to
   // be displayed in the rev_id register as the low 8 bits (zero for now).
@@ -571,16 +902,14 @@
     write_or_die(bootfile, ofd, &segheader, sizeof(segheader));
 
     // Copy the segment plus any padding.
-    read_or_die(bootstream, ifd, buf, seg_size);
-    memset(buf + seg_size, 0, pad_size);
-    write_or_die(bootfile, ofd, buf, seg_size + pad_size);
+    write_or_die(bootfile, ofd, inidx, seg_size);
+    inidx += seg_size;
+    write_or_die(bootfile, ofd, padbuf, pad_size);
   }
 
-  if (close(ifd) < 0)
-    die("%s: close: %m", bootstream);
   if (close(ofd) < 0)
     die("%s: close: %m", bootfile);
-  free(buf);
+  free(ibuf);
 
   // Put back the force_ro setting if need be
   if (sysfd >= 0)
@@ -635,7 +964,7 @@
   if (bootstream == NULL || output_file == NULL)
     die("mlx-bootctl: Must specify --output and --bootstream");
 
-  write_bootstream(bootstream, output_file, O_CREAT | O_TRUNC);
+  write_bootstream(bootstream, output_file, O_CREAT | O_TRUNC, -1);
   return 0;
 }
 
@@ -656,7 +985,7 @@
 
 static void verify_bootstream(const char *bootfile)
 {
-  int ifd, n, bytes_left, img_size;
+  int ifd, bytes_left, img_size;
   boot_image_header_t hdr;
   struct stat st;
   uint64_t data;
@@ -671,23 +1000,13 @@
   bytes_left = st.st_size;
   if (bytes_left % sizeof(uint64_t))
     die("Invalid size %d", bytes_left);
-  if (bytes_left > MMC_BOOT_PARTITION_MAX_SIZE)
-    die("Boot file size too big");
+  if (bytes_left > INPUT_BFB_MAX_SIZE)
+    die("Input boot file size too big");
 
   while (bytes_left > 0) {
-    // Read and verify the image header.
-    n = read_or_die(bootfile, ifd, &hdr, sizeof(hdr));
-    if (n != sizeof(hdr))
-      die("Unable to read next header, n=%d", n);
-    n = hdr.hdr_len - sizeof(hdr) / sizeof(uint64_t);
-    bytes_left -= hdr.hdr_len * sizeof(uint64_t);
-    if (n < 0 || bytes_left < 0)
+    bytes_left -= read_bootstream_header(bootfile, ifd, &hdr);
+    if (bytes_left < 0)
       die("Invalid header length");
-    // Drain the rest of header.
-    while (n--) {
-      if (read_or_die(bootfile, ifd, &data, sizeof(data)) != sizeof(data))
-        die("Not enough data for header");
-    }
 
     // Verify the image crc.
     crc = ~0;
@@ -717,15 +1036,17 @@
     { "device", required_argument, NULL, 'd' },
     { "output", required_argument, NULL, 'o' },
     { "read", required_argument, NULL, 'r' },
+    { "version", required_argument, NULL, 'v' },
     { "help", no_argument, NULL, 'h' },
     { NULL, 0, NULL, 0 }
   };
-  static const char short_options[] = "sb:d:o:r:he";
+  static const char short_options[] = "sb:d:o:r:hev:";
   static const char help_text[] =
     "syntax: mlxbf-bootctl [--help|-h] [--swap|-s] [--device|-d MMCFILE]\n"
     "                      [--output|-o OUTPUT] [--read|-r INPUT]\n"
     "                      [--bootstream|-b BFBFILE] [--overwrite-current]\n"
-    "                      [--watchdog-swap interval | --nowatchdog-swap]";
+    "                      [--watchdog-swap interval | --nowatchdog-swap]\n"
+    "                      [--version|-v VERSION]";
 
   const char *watchdog_swap = NULL;
   const char *bootstream = NULL;
@@ -733,6 +1054,8 @@
   const char *input_file = NULL;
   bool watchdog_disable = false;
   bool swap = false;
+  bool auto_version = true;
+  int version_arg = -1;
   int which_boot = 1;   // alternate boot partition by default
   int opt;
 
@@ -779,6 +1102,14 @@
       enable_rst_n();
       break;
 
+    case 'v':
+      auto_version = false;
+      char *end;
+      version_arg = strtol(optarg, &end, 0);
+      if (end == optarg || *end != '\0')
+        die("version argument ('%s') must be an integer", optarg);
+      break;
+
     case 'h':
     default:
       die(help_text);
@@ -802,7 +1133,8 @@
     else if (output_file)
     {
       // Write the bootstream to the given file, creating it if needed
-      write_bootstream(bootstream, output_file, O_CREAT | O_TRUNC);
+      // Don't filter anything here, since we're not writing to EMMC.
+      write_bootstream(bootstream, output_file, O_CREAT | O_TRUNC, -1);
     }
     else
     {
@@ -827,7 +1159,13 @@
       if (asprintf(&bootfile, "%sboot%d", mmc_path, boot_part ^ which_boot) <= 
0)
         die("unexpected failure in asprintf (%s/%d)", __FILE__, __LINE__);
 
-      write_bootstream(bootstream, bootfile, O_SYNC);
+      int version;
+      if (auto_version) {
+        version = get_hw_version();
+      } else {
+        version = version_arg;
+      }
+      write_bootstream(bootstream, bootfile, O_SYNC, version);
       // The eMMC driver works in an asynchronous way, thus any commands sent
       // should occur after the write to the bootstream has retired. Otherwise
       // a blk_update_request I/O error would be raised and the success of the

++++++ mlxbf-bootctl.obsinfo ++++++
--- /var/tmp/diff_new_pack.3ZbqUr/_old  2022-09-19 16:04:28.182331953 +0200
+++ /var/tmp/diff_new_pack.3ZbqUr/_new  2022-09-19 16:04:28.186331964 +0200
@@ -1,6 +1,5 @@
 name: mlxbf-bootctl
-version: 1.1.6.11
-mtime: 1601841854
-commit: b6dd3501668c45020f1e81690cd53aa6e21289b0
-
+version: 1.1.6.14
+mtime: 1655232109
+commit: 15a7d0e759292ae54b34e881cd0e1a4235c3f957
 

Reply via email to