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

[mynewt-core] 02/03: sys/mfg: 2.0 support

This is an automated email from the ASF dual-hosted git repository.

ccollins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit 83a59b74d1b784c1be5dddf4b6039584753c824a
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Fri Dec 14 15:59:50 2018 -0800

    sys/mfg: 2.0 support
    
    This commit replaces 1.0 mfgimage support with support for version 2.0.
    For more information regarding mfgimage 2.0, see the documentation at:
    `docs/os/modules/mfg/mfg.rst`.
    
    Also see this email thread:
    https://lists.apache.org/thread.html/4185891ef587a778722a39fe5a1ae78bfd0dc7ab7e9348a7ea147df7@%3Cdev.mynewt.apache.org%3E
---
 sys/flash_map/src/flash_map.c |  29 ++-
 sys/id/src/id.c               |  52 +++--
 sys/mfg/include/mfg/mfg.h     | 117 +++++++++--
 sys/mfg/pkg.yml               |   1 +
 sys/mfg/src/mfg.c             | 451 ++++++++++++++++++++++++------------------
 sys/mfg/syscfg.yml            |   8 +
 6 files changed, 420 insertions(+), 238 deletions(-)

diff --git a/sys/flash_map/src/flash_map.c b/sys/flash_map/src/flash_map.c
index 98c58d0..88c11e5 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 f1dfe3e..79a7c87 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 4b61b5c..34e0d20 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 42fb19f..94d7840 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 5684776..d9a2bda 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 10f84f5..3bc0f16 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