You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by GitBox <gi...@apache.org> on 2019/01/11 21:50:54 UTC

[GitHub] ccollins476ad closed pull request #1591: sys/mfg: 2.0 support

ccollins476ad closed pull request #1591:  sys/mfg: 2.0 support
URL: https://github.com/apache/mynewt-core/pull/1591
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/docs/os/modules/mfg/mfg.rst b/docs/os/modules/mfg/mfg.rst
new file mode 100644
index 0000000000..180740ae73
--- /dev/null
+++ b/docs/os/modules/mfg/mfg.rst
@@ -0,0 +1,127 @@
+Manufacturing Support
+=====================
+
+.. toctree::
+    :hidden:
+
+Description
+~~~~~~~~~~~
+
+An mfgimage is a binary that gets written to a Mynewt device at
+manufacturing time.  Unlike a Mynewt target which corresponds to a
+single executable image, an mfgimage represents the entire contents
+of a flash device.
+
+
+Definitions
+~~~~~~~~~~~
+
+=============  ============================  =======
+Term           Long Name                     Meaning
+=============  ============================  =======
+Flashdev       Flash device                  A single piece of flash hardware. A typical device might contain two flashdevs: 1) internal flash, and 2) external SPI flash.
+Mfgimage       Manufacturing image           A file with the entire contents of a single flashdev. At manufacturing time, a separate mfgimage is written to each of the device's flashdevs.
+Boot Mfgimage  Boot manufacturing image      The mfgimage containing the boot loader; always written to internal flash.
+MMR            Manufacturing Meta Region     A chunk of read-only data included in every mfgimage. Contains identifying information for the mfgimage and other data that stays with the device until end of life.
+TLV            Type Length Value             A simple extensible means of representing data. Contains three fields: 1) type of data, 2) length of data, and 3) the data itself.
+MfgID          Manufacturing ID              Identifies which set of mfgimages a device was built with.  Expressed as a list of SHA256 hashes.
+=============  ============================  =======
+
+
+Details
+~~~~~~~
+
+Typically, an mfgimage consists of:
+
+* 1 boot loader.
+* 1 or 2 Mynewt images.
+* Extra configuration (e.g., a pre-populated ``sys/config`` region).
+
+In addition, each mfgimage contains a manufacturing meta region (MMR).
+The MMR consists of read-only data that resides in flash for the
+lifetime of the device.  There is currently support for three MMR TLV
+types:
+
+* Hash of mfgimage
+* Flash map
+* Device / offset of next MMR
+
+The manufacturing hash indicates which manufacuturing image a device
+was built with.  A management system may need this information to
+determine which images a device can be upgraded to, for example.  A
+Mynewt device exposes its manufacturing hash via the ``id/mfghash``
+config setting.
+
+Since MMRs are not intended to be modified or erased, they must be placed in
+unmodifiable areas of flash.  In the boot mfgimage, the MMR *must* be placed in
+the flash area containing the boot loader.  For non-boot mfgimages, the MMR can go in any unused area in the relevant flashdev.
+
+Manufacturing ID
+~~~~~~~~~~~~~~~~
+
+Each mfgimage has its own MMR containing a hash.
+
+The MMR at the end of the boot mfgimage ("boot MMR") must be present. The boot
+MMR indicates the flash locations of other MMRs via the ``mmr_ref`` TLV type.
+
+At startup, the firmware reads the boot MMR. Next, it reads
+any additional MMRs indicated by ``mmr_ref`` TLVs. An ``mmr_ref`` TLV contains
+one field: The ID of the flash area where the next MMR is located.
+
+After all MMRs have been read, the firmware populates the ``id/mfghash``
+setting with a colon-separated list of hashes. By reading and parsing
+this setting, a client can derive the full list of mfgimages that the
+device was built with.
+
+One important implication is that MMR areas should never be moved in a BSP's
+flash map.  Such a change would produce firmware that is incompatible with
+older devices.
+
+
+MMR Structure
+~~~~~~~~~~~~~
+
+An MMR is always located at the end its flash area.  Any other placement is invalid.
+
+An MMR has the following structure:
+
+::
+
+     0                   1                   2                   3
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |   TLV type    |   TLV size    | TLV data ("TLV size" bytes)   ~
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               ~
+    ~                                                               ~
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |   TLV type    |   TLV size    | TLV data ("TLV size" bytes)   ~
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               ~
+    ~                                                               ~
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |          Region size          |    Version    | 0xff padding  |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                       Magic (0x3bb2a269)                      |
+    +-+-+-+-+-+--+-+-+-+-+-+end of flash area-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+The number of TLVs is variable; two are shown above for illustrative
+purposes.
+
+**Fields:**
+
+*<TLVs>*
+
+1. TLV type: Indicates the type of data to follow.
+2. TLV size: The number of bytes of data to follow.
+3. TLV data: "TLV size" bytes of data.
+
+*<Footer>*
+
+4. Region size: The size, in bytes, of the entire manufacturing meta region; includes TLVs and footer.
+5. Version: Manufacturing meta version number; always 0x02.
+6. Magic: Indicates the presence of the manufacturing meta region.
+
+API
+---
+
+.. doxygenfile:: sys/mfg/include/mfg/mfg.h
diff --git a/docs/os/os_user_guide.rst b/docs/os/os_user_guide.rst
index f707e6bdfb..f69e6a2bf9 100644
--- a/docs/os/os_user_guide.rst
+++ b/docs/os/os_user_guide.rst
@@ -20,6 +20,7 @@ OS User Guide
    Sensor Framework <modules/sensor_framework/sensor_framework>
    Test Utilities <modules/testutil/testutil>
    JSON <modules/json/json>
+   Manufacturing support <modules/mfg/mfg>
 
 This guide provides comprehensive information about Mynewt OS, the
 real-time operating system for embedded systems. It is intended both for
diff --git a/sys/defs/include/defs/error.h b/sys/defs/include/defs/error.h
index 9d7cd0ee5c..b2000e7069 100644
--- a/sys/defs/include/defs/error.h
+++ b/sys/defs/include/defs/error.h
@@ -24,21 +24,22 @@
 extern "C" {
 #endif
 
-#define SYS_EOK      (0)
-#define SYS_ENOMEM   (-1)
-#define SYS_EINVAL   (-2)
-#define SYS_ETIMEOUT (-3)
-#define SYS_ENOENT   (-4)
-#define SYS_EIO      (-5)
-#define SYS_EAGAIN   (-6)
-#define SYS_EACCES   (-7)
-#define SYS_EBUSY    (-8)
-#define SYS_ENODEV   (-9)
-#define SYS_ERANGE   (-10)
-#define SYS_EALREADY (-11)
-#define SYS_ENOTSUP  (-12)
-#define SYS_EUNKNOWN (-13)
+#define SYS_EOK         (0)
+#define SYS_ENOMEM      (-1)
+#define SYS_EINVAL      (-2)
+#define SYS_ETIMEOUT    (-3)
+#define SYS_ENOENT      (-4)
+#define SYS_EIO         (-5)
+#define SYS_EAGAIN      (-6)
+#define SYS_EACCES      (-7)
+#define SYS_EBUSY       (-8)
+#define SYS_ENODEV      (-9)
+#define SYS_ERANGE      (-10)
+#define SYS_EALREADY    (-11)
+#define SYS_ENOTSUP     (-12)
+#define SYS_EUNKNOWN    (-13)
 #define SYS_EREMOTEIO   (-14)
+#define SYS_EDONE       (-15)
 
 #define SYS_EPERUSER (-65535)
 
diff --git a/sys/flash_map/src/flash_map.c b/sys/flash_map/src/flash_map.c
index 98c58d0ebe..88c11e51ab 100644
--- a/sys/flash_map/src/flash_map.c
+++ b/sys/flash_map/src/flash_map.c
@@ -349,36 +349,33 @@ flash_map_read_mfg(int max_areas,
                    struct flash_area *out_areas, int *out_num_areas)
 {
     struct mfg_meta_flash_area meta_flash_area;
-    struct mfg_meta_tlv tlv;
+    struct mfg_reader reader;
     struct flash_area *fap;
-    uint32_t off;
     int rc;
 
     *out_num_areas = 0;
-    off = 0;
 
     /* Ensure manufacturing meta region has been located in flash. */
-    rc = mfg_init();
-    if (rc != 0) {
-        return rc;
-    }
+    mfg_init();
+
+    mfg_open(&reader);
 
     while (1) {
         if (*out_num_areas >= max_areas) {
             return -1;
         }
 
-        rc = mfg_next_tlv_with_type(&tlv, &off, MFG_META_TLV_TYPE_FLASH_AREA);
+        rc = mfg_seek_next_with_type(&reader, MFG_META_TLV_TYPE_FLASH_AREA);
         switch (rc) {
         case 0:
             break;
-        case MFG_EDONE:
+        case SYS_EDONE:
             return 0;
         default:
             return rc;
         }
 
-        rc = mfg_read_tlv_flash_area(&tlv, off, &meta_flash_area);
+        rc = mfg_read_tlv_flash_area(&reader, &meta_flash_area);
         if (rc != 0) {
             return rc;
         }
@@ -409,16 +406,16 @@ flash_map_init(void)
 
     /* Use the hardcoded default flash map.  This is done for two reasons:
      * 1. A minimal flash map configuration is required to boot strap the
-     *    process of reading the flash map from the manufacturing meta region.
-     *    In particular, a FLASH_AREA_BOOTLOADER entry is required, as the meta
-     *    region is located at the end of the boot loader area.
-     * 2. If we fail to read the flash map from the meta region, the system
-     *    continues to use the default flash map.
+     *    process of reading the flash map from the manufacturing meta regions.
+     *    In particular, a FLASH_AREA_BOOTLOADER entry is required for the boot
+     *    MMR, as well as an entry for each extended MMR.
+     * 2. If we fail to read the flash map from the MMRs, the system continues
+     *    to use the default flash map.
      */
     flash_map = sysflash_map_dflt;
     flash_map_entries = sizeof sysflash_map_dflt / sizeof sysflash_map_dflt[0];
 
-    /* Attempt to read the flash map from the manufacturing meta region.  On
+    /* Attempt to read the flash map from the manufacturing meta regions.  On
      * success, use the new flash map instead of the default hardcoded one.
      */
     rc = flash_map_read_mfg(sizeof mfg_areas / sizeof mfg_areas[0],
diff --git a/sys/id/src/id.c b/sys/id/src/id.c
index f1dfe3e62a..79a7c8724e 100644
--- a/sys/id/src/id.c
+++ b/sys/id/src/id.c
@@ -30,6 +30,8 @@
 
 #include "id/id.h"
 
+#define ID_BASE64_MFG_HASH_SZ  (BASE64_ENCODE_SIZE(MFG_HASH_SZ))
+
 static char *id_conf_get(int argc, char **argv, char *val, int val_len_max);
 static int id_conf_set(int argc, char **argv, char *val);
 static int id_conf_export(void (*export_func)(char *name, char *val),
@@ -56,8 +58,8 @@ char id_manufacturer[ID_MANUFACTURER_MAX_LEN];
 char id_model[ID_MODEL_MAX_LEN];
 #endif
 
-/** Base64-encoded null-terminated manufacturing hash. */
-char id_mfghash[BASE64_ENCODE_SIZE(MFG_HASH_SZ) + 1];
+/** Colon-delimited null-terminated list of base64-encoded mfgimage hashes. */
+char id_mfghash[MYNEWT_VAL(MFG_MAX_MMRS) * (ID_BASE64_MFG_HASH_SZ + 1)];
 
 struct conf_handler id_conf = {
     .ch_name = "id",
@@ -177,27 +179,43 @@ static void
 id_read_mfghash(void)
 {
     uint8_t raw_hash[MFG_HASH_SZ];
-    struct mfg_meta_tlv tlv;
-    uint32_t off;
+    struct mfg_reader reader;
+    int str_off;
     int rc;
 
     memset(id_mfghash, 0, sizeof id_mfghash);
 
-    /* Find hash TLV in the manufacturing meta region. */
-    off = 0;
-    rc = mfg_next_tlv_with_type(&tlv, &off, MFG_META_TLV_TYPE_HASH);
-    if (rc != 0) {
-        return;
-    }
+    mfg_open(&reader);
 
-    /* Read the TLV contents. */
-    rc = mfg_read_tlv_hash(&tlv, off, raw_hash);
-    if (rc != 0) {
-        return;
-    }
+    str_off = 0;
+    while (1) {
+        rc = mfg_seek_next_with_type(&reader, MFG_META_TLV_TYPE_HASH);
+        if (rc != 0) {
+            return;
+        }
+
+        if (str_off + ID_BASE64_MFG_HASH_SZ + 1 > sizeof id_mfghash) {
+            return;
+        }
 
-    /* Store the SHA256 hash as a base64-encoded string. */
-    base64_encode(raw_hash, sizeof raw_hash, id_mfghash, 1);
+        /* Read the TLV contents. */
+        rc = mfg_read_tlv_hash(&reader, raw_hash);
+        if (rc != 0) {
+            return;
+        }
+
+        /* Append a delimiter if this isn't the first hash. */
+        if (str_off != 0) {
+            id_mfghash[str_off] = ':';
+            str_off++;
+        }
+
+        /* Append the SHA256 hash as a base64-encoded string. */
+        base64_encode(raw_hash, sizeof raw_hash, &id_mfghash[str_off], 1);
+        str_off += ID_BASE64_MFG_HASH_SZ;
+
+        id_mfghash[str_off] = '\0';
+    }
 }
 
 void
diff --git a/sys/mfg/include/mfg/mfg.h b/sys/mfg/include/mfg/mfg.h
index 4b61b5c033..34e0d2023d 100644
--- a/sys/mfg/include/mfg/mfg.h
+++ b/sys/mfg/include/mfg/mfg.h
@@ -20,11 +20,6 @@
 #ifndef H_MFG_
 #define H_MFG_
 
-#define MFG_EUNINIT                     1
-#define MFG_EBADDATA                    2
-#define MFG_EDONE                       3
-#define MFG_EFLASH                      4
-
 #define MFG_HASH_SZ                     32
 
 #define MFG_META_TLV_TYPE_HASH          0x01
@@ -33,33 +28,125 @@
 /** Informational only; not read by firmware. */
 #define MFG_META_TLV_TYPE_FLASH_TRAITS  0x03
 
+#define MFG_META_TLV_TYPE_MMR_REF       0x04
+
 struct mfg_meta_tlv {
     uint8_t type;
     uint8_t size;
     /* Followed by packed data. */
-};
+} __attribute__((packed));
 
 struct mfg_meta_flash_area {
     uint8_t area_id;
     uint8_t device_id;
-    uint16_t pad16;
     uint32_t offset;
     uint32_t size;
-};
+} __attribute__((packed));
 
 /** Informational only; not read by firmware. */
 struct mfg_meta_flash_traits {
     uint8_t device_id;
     uint8_t min_write_sz;
+} __attribute__((packed));
+
+struct mfg_meta_mmr_ref {
+    uint8_t area_id;
+} __attribute__((packed));
+
+/**
+ * Object used for reading records from the manufacturing space.  The
+ * `mfg_open()` function should be used to construct a reader object.
+ */
+struct mfg_reader {
+    /** Public (read-only). */
+    struct mfg_meta_tlv cur_tlv;
+
+    /** Private. */
+    uint8_t mmr_idx;
+    uint32_t offset;
 };
 
-int mfg_next_tlv(struct mfg_meta_tlv *tlv, uint32_t *off);
-int mfg_next_tlv_with_type(struct mfg_meta_tlv *tlv, uint32_t *off,
-                           uint8_t type);
-int mfg_read_tlv_flash_area(const struct mfg_meta_tlv *tlv, uint32_t off,
+/**
+ * Opens the manufacturing space for reading.  The resulting `mfg_reader`
+ * object should be passed to subsequent seek and read functions.
+ */
+void mfg_open(struct mfg_reader *out_reader);
+
+/**
+ * Seeks to the next mfg TLV.  The caller must initialize the supplied
+ * `mfg_reader` with `mfg_open()` prior to calling this function.
+ *
+ * @param reader                The reader to seek with.
+ *
+ * @return                      0 if the next TLV was successfully seeked to.
+ *                              SYS_EDONE if there are no additional TLVs
+ *                                  available.
+ *                              Other MFG error code on failure.
+ */
+int mfg_seek_next(struct mfg_reader *reader);
+/**
+ * Seeks to the next mfg TLV with the specified type.  The caller must
+ * initialize the supplied `mfg_reader` with `mfg_open()` prior to calling this
+ * function.
+ *
+ * @param reader                The reader to seek with.
+ * @param type                  The type of TLV to seek to; one of the
+ *                                  MFG_META_TLV_TYPE_[...] constants.
+ *
+ * @return                      0 if the next TLV was successfully seeked to.
+ *                              SYS_EDONE if there are no additional TLVs
+ *                                  with the specified type available.
+ *                              Other MFG error code on failure.
+ */
+int mfg_seek_next_with_type(struct mfg_reader *reader, uint8_t type);
+
+/**
+ * Reads a hash TLV from the manufacturing space.  This function should
+ * only be called when the provided reader is pointing at a TLV with the
+ * MFG_META_TLV_TYPE_HASH type.
+ *
+ * @param reader                The reader to read with.
+ * @param out_mr (out)          On success, the retrieved MMR reference
+ *                                  information gets written here.
+ *
+ * @return                      0 on success; MFG error code on failure.
+ */
+int mfg_read_tlv_hash(const struct mfg_reader *reader, void *out_hash);
+
+/**
+ * Reads a flash-area TLV from the manufacturing space.  This function should
+ * only be called when the provided reader is pointing at a TLV with the
+ * MFG_META_TLV_TYPE_FLASH_AREA type.
+ *
+ * @param reader                The reader to read with.
+ * @param out_mfa (out)         On success, the retrieved flash area
+ *                                  information gets written here.
+ *
+ * @return                      0 on success; MFG error code on failure.
+ */
+int mfg_read_tlv_flash_area(const struct mfg_reader *reader,
                             struct mfg_meta_flash_area *out_mfa);
-int mfg_read_tlv_hash(const struct mfg_meta_tlv *tlv, uint32_t off,
-                      void *out_hash);
-int mfg_init(void);
+
+/**
+ * Reads an MMR ref TLV from the manufacturing space.  This function should
+ * only be called when the provided reader is pointing at a TLV with the
+ * MFG_META_TLV_TYPE_MMR_REF type.
+ *
+ * @param reader                The reader to read with.
+ * @param out_mr (out)          On success, the retrieved MMR reference
+ *                                  information gets written here.
+ *
+ * @return                      0 on success; MFG error code on failure.
+ */
+int mfg_read_tlv_mmr_ref(const struct mfg_reader *reader,
+                         struct mfg_meta_mmr_ref *out_mr);
+
+/**
+ * Initializes the mfg package.
+ */
+void mfg_init(void);
+
+#define MFG_LOG(lvl_, ...) \
+    MODLOG_ ## lvl_(MYNEWT_VAL(MFG_LOG_MODULE), __VA_ARGS__)
 
 #endif
diff --git a/sys/mfg/pkg.yml b/sys/mfg/pkg.yml
index 42fb19fa97..94d7840a60 100644
--- a/sys/mfg/pkg.yml
+++ b/sys/mfg/pkg.yml
@@ -27,6 +27,7 @@ pkg.keywords:
 pkg.deps:
     - "@apache-mynewt-core/kernel/os"
     - "@apache-mynewt-core/sys/flash_map"
+    - "@apache-mynewt-core/sys/log/modlog"
 
 # sys/flash_map must get initialized before sys/mfg.
 pkg.init:
diff --git a/sys/mfg/src/mfg.c b/sys/mfg/src/mfg.c
index 5684776231..d9a2bda9a0 100644
--- a/sys/mfg/src/mfg.c
+++ b/sys/mfg/src/mfg.c
@@ -19,6 +19,7 @@
 
 #include <string.h>
 #include "os/mynewt.h"
+#include "modlog/modlog.h"
 #include "mfg/mfg.h"
 
 /**
@@ -28,8 +29,6 @@
  *  0                   1                   2                   3
  *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * |Version (0x01) |                  0xff padding                 |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  * |   TLV type    |   TLV size    | TLV data ("TLV size" bytes)   ~
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               ~
  * ~                                                               ~
@@ -38,7 +37,7 @@
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               ~
  * ~                                                               ~
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * |   Region size                 |         0xff padding          |
+ * |          Region size          |    Version    | 0xff padding  |
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  * |                       Magic (0x3bb2a269)                      |
  * +-+-+-+-+-+--+-+-+-+-end of boot loader area+-+-+-+-+-+-+-+-+-+-+
@@ -47,149 +46,148 @@
  * purposes.
  *
  * Fields:
- * <Header>
- * 1. Version: Manufacturing meta version number; always 0x01.
- *
  * <TLVs>
- * 2. TLV type: Indicates the type of data to follow.
- * 3. TLV size: The number of bytes of data to follow.
- * 4. TLV data: "TLV size" bytes of data.
+ * 1. TLV type: Indicates the type of data to follow.
+ * 2. TLV size: The number of bytes of data to follow.
+ * 3. TLV data: "TLV size" bytes of data.
  *
  * <Footer>
- * 5. Region size: The size, in bytes, of the entire manufacturing meta region;
- *    includes header, TLVs, and footer.
- * 6. Magic: indicates the presence of the manufacturing meta region.
+ * 4. Region size: The size, in bytes, of the entire manufacturing meta region;
+ *    includes TLVs and footer.
+ * 5. Version: Manufacturing meta version number; always 0x02.
+ * 6. Magic: Indicates the presence of the manufacturing meta region.
  */
 
 #define MFG_META_MAGIC          0x3bb2a269
-#define MFG_META_HEADER_SZ      4
-#define MFG_META_FOOTER_SZ      8
-#define MFG_META_TLV_SZ         2
-#define MFG_META_FLASH_AREA_SZ  12
-
-struct {
-    uint8_t valid:1;
-    uint32_t off;
-    uint32_t size;
-} mfg_state;
+#define MFG_META_VERSION        2
+#define MFG_META_FOOTER_SZ      (sizeof (struct mfg_meta_footer))
+#define MFG_META_TLV_SZ         (sizeof (struct mfg_meta_tlv))
 
-struct mfg_meta_header {
+struct mfg_meta_footer {
+    uint16_t size;
     uint8_t version;
     uint8_t pad8;
-    uint16_t pad16;
+    uint32_t magic;
 };
 
-struct mfg_meta_footer {
-    uint16_t size;
-    uint16_t pad16;
-    uint32_t magic;
+/** Represents an MMR after it has been read from flash. */
+struct mfg_mmr {
+    /* Flash area containing MMR. */
+    uint8_t area_id;
+
+    /* Offset within flash area of start of MMR. */
+    uint32_t offset;
+
+    /* Total size of MMR (TLVs + footer). */
+    uint32_t size;
 };
 
-_Static_assert(sizeof (struct mfg_meta_header) == MFG_META_HEADER_SZ,
-               "mfg_meta_header must be 4 bytes");
-_Static_assert(sizeof (struct mfg_meta_footer) == MFG_META_FOOTER_SZ,
-               "mfg_meta_footer must be 8 bytes");
-_Static_assert(sizeof (struct mfg_meta_flash_area) == MFG_META_FLASH_AREA_SZ,
-               "mfg_meta_flash_area must be 12 bytes");
+/** The full set of MMRs comprised by all installed mfgimages. */
+static struct mfg_mmr mfg_mmrs[MYNEWT_VAL(MFG_MAX_MMRS)];
+static int mfg_num_mmrs;
+
+/** True if MMR detection has occurred. */
+static bool mfg_initialized;
+
+void
+mfg_open(struct mfg_reader *out_reader)
+{
+    /* Ensure MMRs have been detected. */
+    mfg_init();
+
+    /* Start at MMR index 0. */
+    *out_reader = (struct mfg_reader) { 0 };
+}
 
 /**
- * Retrieves a TLV header from the mfg meta region.  To request the first TLV
- * in the region, specify an offset of 0.  To request a subsequent TLV, specify
- * the values retrieved by the previous call to this function.
+ * Seeks to the next mfg TLV.
  *
- * @param tlv (in / out)        Input: The previously-read TLV header; not used
- *                                  as input when requesting the first TLV.
- *                              Output: On success, the requested TLV header
- *                                  gets written here.
- * @param off (in / out)        Input: The flash-area-offset of the previously
- *                                  read TLV header; 0 when requesting the
- *                                  first TLV.
- *                              Output: On success, the flash-area-offset of
- *                                  the retrieved TLV header.
+ * @param reader                The reader to seek with.
  *
- * @return                      0 if a TLV header was successfully retrieved;
- *                              MFG_EDONE if there are no additional TLVs to
- *                                  read;
+ * @return                      0 if the next TLV was successfully seeked to.
+ *                              SYS_EDONE if there are no additional TLVs
+ *                                  available.
+ *                              SYS_EAGAIN if the end of the current MMR is
+ *                                  reached, but additional MMRs are available
+ *                                  for reading.
  *                              Other MFG error code on failure.
  */
-int
-mfg_next_tlv(struct mfg_meta_tlv *tlv, uint32_t *off)
+static int
+mfg_seek_next_aux(struct mfg_reader *reader)
 {
     const struct flash_area *fap;
+    const struct mfg_mmr *mmr;
     int rc;
 
-    if (!mfg_state.valid) {
-        return MFG_EUNINIT;
+    if (reader->mmr_idx >= mfg_num_mmrs) {
+        /* The reader is expired. */
+        return SYS_EINVAL;
     }
 
-    rc = flash_area_open(FLASH_AREA_BOOTLOADER, &fap);
+    mmr = &mfg_mmrs[reader->mmr_idx];
+
+    rc = flash_area_open(mmr->area_id, &fap);
     if (rc != 0) {
-        rc = MFG_EFLASH;
-        goto done;
+        return SYS_EIO;
     }
 
-    if (*off == 0) {
-        *off = mfg_state.off + MFG_META_HEADER_SZ;
+    if (reader->offset == 0) {
+        /* First seek; advance to the start of the MMR. */
+        reader->offset = mmr->offset;
     } else {
-        /* Advance past current TLV. */
-        *off += MFG_META_TLV_SZ + tlv->size;
+        /* Follow-up seek; skip the current TLV. */
+        reader->offset += MFG_META_TLV_SZ + reader->cur_tlv.size;
     }
 
-    if (*off + MFG_META_FOOTER_SZ >= fap->fa_size) {
-        /* Reached end of boot area; no more TLVs. */
-        return MFG_EDONE;
+    if (reader->offset >= fap->fa_size - MFG_META_FOOTER_SZ) {
+        /* Reached end of the MMR; advance to the next MMR if one exists. */
+        if (reader->mmr_idx + 1 >= mfg_num_mmrs) {
+            rc = SYS_EDONE;
+        } else {
+            reader->offset = 0;
+            reader->mmr_idx++;
+            rc = SYS_EAGAIN;
+        }
+        goto done;
     }
 
-    /* Read next TLV. */
-    memset(tlv, 0, sizeof *tlv);
-    rc = flash_area_read(fap, *off, tlv, MFG_META_TLV_SZ);
+    /* Read current TLV header. */
+    rc = flash_area_read(fap, reader->offset, &reader->cur_tlv,
+                         MFG_META_TLV_SZ);
     if (rc != 0) {
-        rc = MFG_EFLASH;
+        rc = SYS_EIO;
         goto done;
     }
 
-    rc = 0;
-
 done:
     flash_area_close(fap);
     return rc;
 }
 
-/**
- * Retrieves a TLV header of the specified type from the mfg meta region.  To
- * request the first TLV in the region, specify an offset of 0.  To request a
- * subsequent TLV, specify the values retrieved by the previous call to this
- * function.
- *
- * @param tlv (in / out)        Input: The previously-read TLV header; not used
- *                                  as input when requesting the first TLV.
- *                              Output: On success, the requested TLV header
- *                                  gets written here.
- * @param off (in / out)        Input: The flash-area-offset of the previously
- *                                  read TLV header; 0 when requesting the
- *                                  first TLV.
- *                              Output: On success, the flash-area-offset of
- *                                  the retrieved TLV header.
- * @param type                  The type of TLV to retrieve; one of the
- *                                  MFG_META_TLV_TYPE_[...] constants.
- *
- * @return                      0 if a TLV header was successfully retrieved;
- *                              MFG_EDONE if there are no additional TLVs of
- *                                  the specified type to read;
- *                              Other MFG error code on failure.
- */
 int
-mfg_next_tlv_with_type(struct mfg_meta_tlv *tlv, uint32_t *off, uint8_t type)
+mfg_seek_next(struct mfg_reader *reader)
+{
+    int rc;
+
+    do {
+        rc = mfg_seek_next_aux(reader);
+    } while (rc == SYS_EAGAIN);
+
+    return rc;
+}
+
+int
+mfg_seek_next_with_type(struct mfg_reader *reader, uint8_t type)
 {
     int rc;
 
     while (1) {
-        rc = mfg_next_tlv(tlv, off);
+        rc = mfg_seek_next(reader);
         if (rc != 0) {
             break;
         }
 
-        if (tlv->type == type) {
+        if (reader->cur_tlv.type == type) {
             break;
         }
 
@@ -200,149 +198,222 @@ mfg_next_tlv_with_type(struct mfg_meta_tlv *tlv, uint32_t *off, uint8_t type)
 }
 
 /**
- * Reads a flash-area TLV from the manufacturing meta region.  This function
- * should only be called after a TLV has been identified as having the
- * MFG_META_TLV_TYPE_FLASH_AREA type.
- *
- * @param tlv                   The header of the TLV to read.  This header
- *                                  should have been retrieved via a call to
- *                                  mfg_next_tlv() or mfg_next_tlv_with_type().
- * @param off                   The flash-area-offset of the TLV header.  Note:
- *                                  this is the offset of the TLV header, not
- *                                  the TLV data.
- * @param out_mfa (out)         On success, the retrieved flash area
- *                                  information gets written here.
- *
- * @return                      0 on success; MFG error code on failure.
+ * Opens the flash area pointed to by the provided reader.
  */
-int
-mfg_read_tlv_flash_area(const struct mfg_meta_tlv *tlv, uint32_t off,
-                        struct mfg_meta_flash_area *out_mfa)
+static int
+mfg_open_flash_area(const struct mfg_reader *reader,
+                    const struct flash_area **fap)
+{
+    const struct mfg_mmr *mmr;
+    int rc;
+
+    assert(reader->mmr_idx < mfg_num_mmrs);
+    mmr = &mfg_mmrs[reader->mmr_idx];
+
+    rc = flash_area_open(mmr->area_id, fap);
+    if (rc != 0) {
+        return SYS_EIO;
+    }
+
+    return 0;
+}
+
+static int
+mfg_read_tlv_body(const struct mfg_reader *reader, void *dst, int max_size)
 {
     const struct flash_area *fap;
     int read_sz;
     int rc;
 
-    rc = flash_area_open(FLASH_AREA_BOOTLOADER, &fap);
+    rc = mfg_open_flash_area(reader, &fap);
     if (rc != 0) {
-        rc = MFG_EFLASH;
-        goto done;
+        return rc;
     }
 
-    memset(out_mfa, 0, sizeof *out_mfa);
+    memset(dst, 0, max_size);
+
+    read_sz = min(max_size, reader->cur_tlv.size);
+    rc = flash_area_read(fap, reader->offset + MFG_META_TLV_SZ, dst, read_sz);
+    flash_area_close(fap);
 
-    read_sz = min(MFG_META_FLASH_AREA_SZ, tlv->size);
-    rc = flash_area_read(fap, off + MFG_META_TLV_SZ, out_mfa, read_sz);
     if (rc != 0) {
-        rc = MFG_EFLASH;
-        goto done;
+        return SYS_EIO;
     }
 
-    rc = 0;
+    return 0;
+}
+
+int
+mfg_read_tlv_flash_area(const struct mfg_reader *reader,
+                        struct mfg_meta_flash_area *out_mfa)
+{
+    return mfg_read_tlv_body(reader, out_mfa, sizeof *out_mfa);
+}
 
-done:
-    flash_area_close(fap);
-    return rc;
+int
+mfg_read_tlv_mmr_ref(const struct mfg_reader *reader,
+                     struct mfg_meta_mmr_ref *out_mr)
+{
+    return mfg_read_tlv_body(reader, out_mr, sizeof *out_mr);
+}
+
+int
+mfg_read_tlv_hash(const struct mfg_reader *reader, void *out_hash)
+{
+    return mfg_read_tlv_body(reader, out_hash, MFG_HASH_SZ);
 }
 
 /**
- * Reads a hash TLV from the manufacturing meta region.  This function should
- * only be called after a TLV has been identified as having the
- * MFG_META_TLV_TYPE_HASH type.
- *
- * @param tlv                   The header of the TLV to read.  This header
- *                                  should have been retrieved via a call to
- *                                  mfg_next_tlv() or mfg_next_tlv_with_type().
- * @param off                   The flash-area-offset of the TLV header.  Note:
- *                                  this is the offset of the TLV header, not
- *                                  the TLV data.
- * @param out_hash (out)        On success, the retrieved SHA256 hash gets
- *                                  written here.  This buffer must be at least
- *                                  32 bytes wide.
- *
- * @return                      0 on success; MFG error code on failure.
+ * Reads an MMR from the end of the specified flash area.
  */
-int
-mfg_read_tlv_hash(const struct mfg_meta_tlv *tlv, uint32_t off, void *out_hash)
+static int
+mfg_read_mmr(uint8_t area_id, struct mfg_mmr *out_mmr)
 {
     const struct flash_area *fap;
-    int read_sz;
+    struct mfg_meta_footer ftr;
     int rc;
 
-    rc = flash_area_open(FLASH_AREA_BOOTLOADER, &fap);
+    rc = flash_area_open(area_id, &fap);
     if (rc != 0) {
-        rc = MFG_EFLASH;
-        goto done;
+        return SYS_EIO;
     }
 
-    read_sz = min(MFG_HASH_SZ, tlv->size);
-    rc = flash_area_read(fap, off + MFG_META_TLV_SZ, out_hash, read_sz);
+    /* Read the MMR footer. */
+    rc = flash_area_read(fap, fap->fa_size - sizeof ftr, &ftr, sizeof ftr);
+    flash_area_close(fap);
+
     if (rc != 0) {
-        rc = MFG_EFLASH;
-        goto done;
+        return SYS_EIO;
     }
 
-    rc = 0;
+    if (ftr.magic != MFG_META_MAGIC) {
+        return SYS_ENODEV;
+    }
 
-done:
-    flash_area_close(fap);
-    return rc;
+    if (ftr.version != MFG_META_VERSION) {
+        return SYS_ENOTSUP;
+    }
+
+    if (ftr.size > fap->fa_size) {
+        return SYS_ENODEV;
+    }
+
+    *out_mmr = (struct mfg_mmr) {
+        .area_id = area_id,
+        .offset = fap->fa_size - ftr.size,
+        .size = ftr.size,
+    };
+
+    return 0;
+}
+
+/**
+ * Reads an MMR from the end of the specified flash area.  On success, the
+ * global MMR list is populated with the result for subsequent reading.
+ */
+static int
+mfg_read_next_mmr(uint8_t area_id)
+{
+    int rc;
+    int i;
+
+    /* Detect if this MMR has already been read. */
+    for (i = 0; i < mfg_num_mmrs; i++) {
+        if (mfg_mmrs[i].area_id == area_id) {
+            return SYS_EALREADY;
+        }
+    }
+
+    if (mfg_num_mmrs >= MYNEWT_VAL(MFG_MAX_MMRS)) {
+        return SYS_ENOMEM;
+    }
+
+    rc = mfg_read_mmr(area_id, &mfg_mmrs[mfg_num_mmrs]);
+    if (rc != 0) {
+        return rc;
+    }
+
+    mfg_num_mmrs++;
+    return 0;
+}
+
+/**
+ * Reads all MMR ref TLVs in the specified MMR.  The global MMR list is
+ * populated with the results for subsequent reading.
+ */
+static int
+mfg_read_mmr_refs(void)
+{
+    struct mfg_meta_mmr_ref mmr_ref;
+    struct mfg_reader reader;
+    int rc;
+
+    mfg_open(&reader);
+
+    /* Repeatedly find and read the next MMR ref TLV.  As new MMRs are read,
+     * they are added to the global list and become available in this loop.
+     */
+    while (true) {
+        rc = mfg_seek_next_with_type(&reader, MFG_META_TLV_TYPE_MMR_REF);
+        switch (rc) {
+        case 0:
+            /* Found an MMR ref TLV.  Read it below. */
+            break;
+
+        case SYS_EDONE:
+            /* No more MMR ref TLVs. */
+            return 0;
+
+        default:
+            return rc;
+        }
+
+        rc = mfg_read_tlv_mmr_ref(&reader, &mmr_ref);
+        if (rc != 0) {
+            return rc;
+        }
+
+        rc = mfg_read_next_mmr(mmr_ref.area_id);
+        if (rc != 0 && rc != SYS_EALREADY) {
+            return rc;
+        }
+    }
+
+    return 0;
 }
 
 /**
  * Locates the manufacturing meta region in flash.  This function must be
  * called before any TLVs can be read.  No-op if this function has already
  * executed successfully.
- *
- * @return                      0 on success; MFG error code on failure.
  */
-int
+void
 mfg_init(void)
 {
-    const struct flash_area *fap;
-    struct mfg_meta_footer ftr;
-    uint16_t off;
     int rc;
 
-    /* Ensure this function only gets called by sysinit. */
-    SYSINIT_ASSERT_ACTIVE();
-
-    if (mfg_state.valid) {
-        /* Already initialized. */
-        return 0;
+    if (mfg_initialized) {
+        return;
     }
+    mfg_initialized = true;
 
-    mfg_state.valid = 0;
+    /* Ensure this function only gets called by sysinit. */
+    SYSINIT_ASSERT_ACTIVE();
 
-    rc = flash_area_open(FLASH_AREA_BOOTLOADER, &fap);
+    /* Read the first MMR from the boot loader area. */
+    rc = mfg_read_next_mmr(FLASH_AREA_BOOTLOADER);
     if (rc != 0) {
-        rc = MFG_EFLASH;
-        goto done;
+        goto err;
     }
 
-    off = fap->fa_size - sizeof ftr;
-    rc = flash_area_read(fap, off, &ftr, sizeof ftr);
+    /* Read all MMR references. */
+    rc = mfg_read_mmr_refs();
     if (rc != 0) {
-        rc = MFG_EFLASH;
-        goto done;
-    }
-
-    if (ftr.magic != MFG_META_MAGIC) {
-        rc = MFG_EBADDATA;
-        goto done;
-    }
-    if (ftr.size > fap->fa_size) {
-        rc = MFG_EBADDATA;
-        goto done;
+        goto err;
     }
 
-    mfg_state.valid = 1;
-    mfg_state.off = fap->fa_size - ftr.size;
-    mfg_state.size = ftr.size;
+    return;
 
-    rc = 0;
-
-done:
-    flash_area_close(fap);
-    return rc;
+err:
+    MFG_LOG(ERROR, "failed to read MMRs: rc=%d", rc);
 }
diff --git a/sys/mfg/syscfg.yml b/sys/mfg/syscfg.yml
index 10f84f521d..3bc0f16c1e 100644
--- a/sys/mfg/syscfg.yml
+++ b/sys/mfg/syscfg.yml
@@ -18,7 +18,15 @@
 #
 
 syscfg.defs:
+    MFG_MAX_MMRS:
+        description: >
+            The maximum allowed MMRs in the manufacturing space.  Excess MMRs
+            are not read.
+        value: 2
     MFG_SYSINIT_STAGE:
         description: >
             Sysinit stage for manufacturing support.
         value: 100
+    MFG_LOG_MODULE:
+        description: 'Numeric module ID to use for manufacturing log messages'
+        value: 128


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services