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/04 18:21:16 UTC
[mynewt-newt] 11/17: Support for the 2.0 mfgimage format
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-newt.git
commit b7f07170bff243c994d1d6d3dfdad63488c6d7db
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Fri Dec 14 14:19:57 2018 -0800
Support for the 2.0 mfgimage format
---
artifact/image/create.go | 101 ++----
artifact/image/image.go | 6 +
artifact/image/key.go | 19 +-
artifact/image/v1.go | 2 +-
artifact/manifest/manifest.go | 3 +-
artifact/manifest/mfg_manifest.go | 73 ++++
artifact/mfg/map_meta.go | 153 ++++++++
artifact/mfg/meta.go | 352 +++++++++++++++++++
artifact/mfg/mfg.go | 113 ++++++
newt/mfg/read.go => artifact/mfg/paths.go | 23 +-
larva/cli/image_cmds.go | 341 ++++++++++++++++--
larva/cli/mfg_cmds.go | 189 +++++++---
larva/lvimg/lvimg.go | 116 ++++++
larva/{mfg/mfg.go => lvmfg/lvmfg.go} | 111 +++---
newt/builder/targetbuild.go | 1 +
newt/cli/build_cmds.go | 11 +
newt/cli/mfg_cmds.go | 56 ++-
newt/flashmap/flashmap.go | 17 -
newt/imgprod/imgprod.go | 14 +-
newt/imgprod/v1.go | 9 +-
newt/manifest/manifest.go | 47 ++-
newt/mfg/build.go | 564 ++++++++++++++++++++++++++++++
newt/mfg/create.go | 535 ----------------------------
newt/mfg/decode.go | 310 ++++++++++++++++
newt/mfg/emit.go | 342 ++++++++++++++++++
newt/mfg/load.go | 317 -----------------
newt/mfg/meta.go | 248 -------------
newt/mfg/mfg.go | 98 ------
newt/mfg/misc.go | 92 +++++
newt/mfg/part.go | 96 +++++
newt/mfg/paths.go | 154 +-------
31 files changed, 2905 insertions(+), 1608 deletions(-)
diff --git a/artifact/image/create.go b/artifact/image/create.go
index 5b28120..c3e8820 100644
--- a/artifact/image/create.go
+++ b/artifact/image/create.go
@@ -90,12 +90,12 @@ func generateEncTlv(cipherSecret []byte) (ImageTlv, error) {
}, nil
}
-func generateSigRsa(key *rsa.PrivateKey, hash []byte) ([]byte, error) {
+func generateSigRsa(key ImageSigKey, hash []byte) ([]byte, error) {
opts := rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthEqualsHash,
}
signature, err := rsa.SignPSS(
- rand.Reader, key, crypto.SHA256, hash, &opts)
+ rand.Reader, key.Rsa, crypto.SHA256, hash, &opts)
if err != nil {
return nil, util.FmtNewtError("Failed to compute signature: %s", err)
}
@@ -103,24 +103,8 @@ func generateSigRsa(key *rsa.PrivateKey, hash []byte) ([]byte, error) {
return signature, nil
}
-func generateSigTlvRsa(key ImageSigKey, hash []byte) (ImageTlv, error) {
- sig, err := generateSigRsa(key.Rsa, hash)
- if err != nil {
- return ImageTlv{}, err
- }
-
- return ImageTlv{
- Header: ImageTlvHdr{
- Type: key.sigTlvType(),
- Pad: 0,
- Len: 256, /* 2048 bits */
- },
- Data: sig,
- }, nil
-}
-
-func generateSigEc(key *ecdsa.PrivateKey, hash []byte) ([]byte, error) {
- r, s, err := ecdsa.Sign(rand.Reader, key, hash)
+func generateSigEc(key ImageSigKey, hash []byte) ([]byte, error) {
+ r, s, err := ecdsa.Sign(rand.Reader, key.Ec, hash)
if err != nil {
return nil, util.FmtNewtError("Failed to compute signature: %s", err)
}
@@ -135,88 +119,65 @@ func generateSigEc(key *ecdsa.PrivateKey, hash []byte) ([]byte, error) {
return nil, util.FmtNewtError("Failed to construct signature: %s", err)
}
- return signature, nil
-}
-
-func generateSigTlvEc(key ImageSigKey, hash []byte) (ImageTlv, error) {
- sig, err := generateSigEc(key.Ec, hash)
- if err != nil {
- return ImageTlv{}, err
- }
-
sigLen := key.sigLen()
- if len(sig) > int(sigLen) {
- return ImageTlv{}, util.FmtNewtError("Something is really wrong\n")
- }
-
- b := &bytes.Buffer{}
-
- if _, err := b.Write(sig); err != nil {
- return ImageTlv{},
- util.FmtNewtError("Failed to append sig: %s", err.Error())
+ if len(signature) > int(sigLen) {
+ return nil, util.FmtNewtError("Something is really wrong\n")
}
- pad := make([]byte, int(sigLen)-len(sig))
- if _, err := b.Write(pad); err != nil {
- return ImageTlv{}, util.FmtNewtError(
- "Failed to serialize image trailer: %s", err.Error())
- }
+ pad := make([]byte, int(sigLen)-len(signature))
+ signature = append(signature, pad...)
- return ImageTlv{
- Header: ImageTlvHdr{
- Type: key.sigTlvType(),
- Pad: 0,
- Len: sigLen,
- },
- Data: b.Bytes(),
- }, nil
+ return signature, nil
}
-func generateSigTlv(key ImageSigKey, hash []byte) (ImageTlv, error) {
+func generateSig(key ImageSigKey, hash []byte) ([]byte, error) {
key.assertValid()
if key.Rsa != nil {
- return generateSigTlvRsa(key, hash)
+ return generateSigRsa(key, hash)
} else {
- return generateSigTlvEc(key, hash)
+ return generateSigEc(key, hash)
}
}
-func generateKeyHashTlv(key ImageSigKey) (ImageTlv, error) {
- key.assertValid()
-
- keyHash, err := key.sigKeyHash()
- if err != nil {
- return ImageTlv{}, util.FmtNewtError(
- "Failed to compute hash of the public key: %s", err.Error())
- }
-
+func BuildKeyHashTlv(keyBytes []byte) ImageTlv {
+ data := RawKeyHash(keyBytes)
return ImageTlv{
Header: ImageTlvHdr{
Type: IMAGE_TLV_KEYHASH,
Pad: 0,
- Len: uint16(len(keyHash)),
+ Len: uint16(len(data)),
},
- Data: keyHash,
- }, nil
+ Data: data,
+ }
}
-func GenerateSigTlvs(keys []ImageSigKey, hash []byte) ([]ImageTlv, error) {
+func BuildSigTlvs(keys []ImageSigKey, hash []byte) ([]ImageTlv, error) {
var tlvs []ImageTlv
for _, key := range keys {
key.assertValid()
- tlv, err := generateKeyHashTlv(key)
+ // Key hash TLV.
+ pubKey, err := key.PubBytes()
if err != nil {
return nil, err
}
+ tlv := BuildKeyHashTlv(pubKey)
tlvs = append(tlvs, tlv)
- tlv, err = generateSigTlv(key, hash)
+ // Signature TLV.
+ sig, err := generateSig(key, hash)
if err != nil {
return nil, err
}
+ tlv = ImageTlv{
+ Header: ImageTlvHdr{
+ Type: key.sigTlvType(),
+ Len: uint16(len(sig)),
+ },
+ Data: sig,
+ }
tlvs = append(tlvs, tlv)
}
@@ -409,7 +370,7 @@ func (ic *ImageCreator) Create() (Image, error) {
}
ri.Tlvs = append(ri.Tlvs, tlv)
- tlvs, err := GenerateSigTlvs(ic.SigKeys, hashBytes)
+ tlvs, err := BuildSigTlvs(ic.SigKeys, hashBytes)
if err != nil {
return ri, err
}
diff --git a/artifact/image/image.go b/artifact/image/image.go
index 244cfed..1ed093f 100644
--- a/artifact/image/image.go
+++ b/artifact/image/image.go
@@ -134,6 +134,12 @@ func ImageTlvTypeName(tlvType uint8) string {
return name
}
+func ImageTlvTypeIsSig(tlvType uint8) bool {
+ return tlvType == IMAGE_TLV_RSA2048 ||
+ tlvType == IMAGE_TLV_ECDSA224 ||
+ tlvType == IMAGE_TLV_ECDSA256
+}
+
func ParseVersion(versStr string) (ImageVersion, error) {
var err error
var major uint64
diff --git a/artifact/image/key.go b/artifact/image/key.go
index 9141f6e..8345cd9 100644
--- a/artifact/image/key.go
+++ b/artifact/image/key.go
@@ -152,25 +152,30 @@ func (key *ImageSigKey) assertValid() {
}
}
-func (key *ImageSigKey) sigKeyHash() ([]uint8, error) {
+func (key *ImageSigKey) PubBytes() ([]uint8, error) {
key.assertValid()
+ var pubkey []byte
+
if key.Rsa != nil {
- pubkey, _ := asn1.Marshal(key.Rsa.PublicKey)
- sum := sha256.Sum256(pubkey)
- return sum[:4], nil
+ pubkey, _ = asn1.Marshal(key.Rsa.PublicKey)
} else {
switch key.Ec.Curve.Params().Name {
case "P-224":
fallthrough
case "P-256":
- pubkey, _ := x509.MarshalPKIXPublicKey(&key.Ec.PublicKey)
- sum := sha256.Sum256(pubkey)
- return sum[:4], nil
+ pubkey, _ = x509.MarshalPKIXPublicKey(&key.Ec.PublicKey)
default:
return nil, util.NewNewtError("Unsupported ECC curve")
}
}
+
+ return pubkey, nil
+}
+
+func RawKeyHash(pubKeyBytes []byte) []byte {
+ sum := sha256.Sum256(pubKeyBytes)
+ return sum[:4]
}
func (key *ImageSigKey) sigLen() uint16 {
diff --git a/artifact/image/v1.go b/artifact/image/v1.go
index bab86f6..5540d85 100644
--- a/artifact/image/v1.go
+++ b/artifact/image/v1.go
@@ -233,7 +233,7 @@ func generateV1SigTlvRsa(key ImageSigKey, hash []byte) (ImageTlv, error) {
}
func generateV1SigTlvEc(key ImageSigKey, hash []byte) (ImageTlv, error) {
- sig, err := generateSigEc(key.Ec, hash)
+ sig, err := generateSigEc(key, hash)
if err != nil {
return ImageTlv{}, err
}
diff --git a/artifact/manifest/manifest.go b/artifact/manifest/manifest.go
index 62d8c06..3a3376a 100644
--- a/artifact/manifest/manifest.go
+++ b/artifact/manifest/manifest.go
@@ -5,7 +5,6 @@ import (
"io"
"io/ioutil"
- "mynewt.apache.org/newt/artifact/flash"
"mynewt.apache.org/newt/util"
)
@@ -57,7 +56,7 @@ type Manifest struct {
LoaderPkgs []*ManifestPkg `json:"loader_pkgs,omitempty"`
TgtVars []string `json:"target"`
Repos []*ManifestRepo `json:"repos"`
- FlashAreas []flash.FlashArea `json:"flash_map"`
+ Syscfg map[string]string `json:"syscfg"`
PkgSizes []*ManifestSizePkg `json:"pkgsz"`
LoaderPkgSizes []*ManifestSizePkg `json:"loader_pkgsz,omitempty"`
diff --git a/artifact/manifest/mfg_manifest.go b/artifact/manifest/mfg_manifest.go
new file mode 100644
index 0000000..25cf9b7
--- /dev/null
+++ b/artifact/manifest/mfg_manifest.go
@@ -0,0 +1,73 @@
+package manifest
+
+import (
+ "encoding/json"
+ "io/ioutil"
+
+ "mynewt.apache.org/newt/artifact/flash"
+ "mynewt.apache.org/newt/util"
+)
+
+type MfgManifestTarget struct {
+ Name string `json:"name"`
+ Offset int `json:"offset"`
+ BinPath string `json:"bin_path,omitempty"`
+ ImagePath string `json:"image_path,omitempty"`
+ ManifestPath string `json:"manifest_path"`
+}
+
+type MfgManifestMetaMmr struct {
+ Area string `json:"area"`
+ Device int `json:"_device"`
+ EndOffset int `json:"_end_offset"`
+}
+
+type MfgManifestMeta struct {
+ EndOffset int `json:"end_offset"`
+ Size int `json:"size"`
+ Hash bool `json:"hash_present"`
+ FlashMap bool `json:"flash_map_present"`
+ Mmrs []MfgManifestMetaMmr `json:"mmrs,omitempty"`
+ // XXX: refhash
+}
+
+type MfgManifest struct {
+ Name string `json:"name"`
+ BuildTime string `json:"build_time"`
+ Format int `json:"format"`
+ MfgHash string `json:"mfg_hash"`
+ Version string `json:"version"`
+ Device int `json:"device"`
+ BinPath string `json:"bin_path"`
+ FlashAreas []flash.FlashArea `json:"flash_map"`
+
+ Targets []MfgManifestTarget `json:"targets"`
+ Meta *MfgManifestMeta `json:"meta,omitempty"`
+}
+
+func ReadMfgManifest(path string) (MfgManifest, error) {
+ m := MfgManifest{}
+
+ content, err := ioutil.ReadFile(path)
+ if err != nil {
+ return m, util.ChildNewtError(err)
+ }
+
+ if err := json.Unmarshal(content, &m); err != nil {
+ return m, util.FmtNewtError(
+ "Failure decoding mfg manifest with path \"%s\": %s",
+ path, err.Error())
+ }
+
+ return m, nil
+}
+
+func (m *MfgManifest) MarshalJson() ([]byte, error) {
+ buffer, err := json.MarshalIndent(m, "", " ")
+ if err != nil {
+ return nil, util.FmtNewtError(
+ "Cannot encode mfg manifest: %s", err.Error())
+ }
+
+ return buffer, nil
+}
diff --git a/artifact/mfg/map_meta.go b/artifact/mfg/map_meta.go
new file mode 100644
index 0000000..b16c37e
--- /dev/null
+++ b/artifact/mfg/map_meta.go
@@ -0,0 +1,153 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package mfg
+
+import (
+ "bytes"
+ "encoding/binary"
+ "encoding/hex"
+ "encoding/json"
+
+ "mynewt.apache.org/newt/util"
+)
+
+func (t *MetaTlv) bodyMap() (map[string]interface{}, error) {
+ r := bytes.NewReader(t.Data)
+
+ readBody := func(dst interface{}) error {
+ if err := binary.Read(r, binary.LittleEndian, dst); err != nil {
+ return util.FmtNewtError(
+ "Error parsing TLV data: %s", err.Error())
+ }
+ return nil
+ }
+
+ switch t.Header.Type {
+ case META_TLV_TYPE_HASH:
+ var body MetaTlvBodyHash
+ if err := readBody(&body); err != nil {
+ return nil, err
+ }
+ return body.Map(), nil
+
+ case META_TLV_TYPE_FLASH_AREA:
+ var body MetaTlvBodyFlashArea
+ if err := readBody(&body); err != nil {
+ return nil, err
+ }
+ return body.Map(), nil
+
+ case META_TLV_TYPE_MMR_REF:
+ var body MetaTlvBodyMmrRef
+ if err := readBody(&body); err != nil {
+ return nil, err
+ }
+ return body.Map(), nil
+
+ default:
+ return nil, util.FmtNewtError(
+ "Unknown meta TLV type: %d", t.Header.Type)
+ }
+}
+
+func (b *MetaTlvBodyFlashArea) Map() map[string]interface{} {
+ return map[string]interface{}{
+ "area": b.Area,
+ "device": b.Device,
+ "offset": b.Offset,
+ "size": b.Size,
+ }
+}
+
+func (b *MetaTlvBodyHash) Map() map[string]interface{} {
+ return map[string]interface{}{
+ "hash": hex.EncodeToString(b.Hash[:]),
+ }
+}
+
+func (b *MetaTlvBodyMmrRef) Map() map[string]interface{} {
+ return map[string]interface{}{
+ "area": b.Area,
+ }
+}
+
+func (t *MetaTlv) Map(offset int) map[string]interface{} {
+ hmap := map[string]interface{}{
+ "_type_name": MetaTlvTypeName(t.Header.Type),
+ "type": t.Header.Type,
+ "size": t.Header.Size,
+ }
+
+ var body interface{}
+
+ bmap, err := t.bodyMap()
+ if err != nil {
+ body = hex.EncodeToString(t.Data)
+ } else {
+ body = bmap
+ }
+
+ return map[string]interface{}{
+ "_offset": offset,
+ "header": hmap,
+ "data": body,
+ }
+}
+
+func (f *MetaFooter) Map(offset int) map[string]interface{} {
+ return map[string]interface{}{
+ "_offset": offset,
+ "size": f.Size,
+ "magic": f.Magic,
+ "version": f.Version,
+ }
+}
+
+func (m *Meta) Map(endOffset int) map[string]interface{} {
+ offsets := m.Offsets()
+ startOffset := endOffset - int(m.Footer.Size)
+
+ tlvs := []map[string]interface{}{}
+ for i, t := range m.Tlvs {
+ tlv := t.Map(startOffset + offsets.Tlvs[i])
+ tlvs = append(tlvs, tlv)
+ }
+
+ ftr := m.Footer.Map(startOffset + offsets.Footer)
+
+ return map[string]interface{}{
+ "_offset": startOffset,
+ "_end_offset": endOffset,
+ "_size": m.Footer.Size,
+ "tlvs": tlvs,
+ "footer": ftr,
+ }
+}
+
+func (m *Meta) Json(offset int) (string, error) {
+ mmap := m.Map(offset)
+
+ bin, err := json.MarshalIndent(mmap, "", " ")
+ if err != nil {
+ return "", util.ChildNewtError(err)
+ }
+
+ return string(bin), nil
+}
diff --git a/artifact/mfg/meta.go b/artifact/mfg/meta.go
new file mode 100644
index 0000000..4521a89
--- /dev/null
+++ b/artifact/mfg/meta.go
@@ -0,0 +1,352 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package mfg
+
+import (
+ "bytes"
+ "encoding/binary"
+ "io"
+ "io/ioutil"
+
+ "mynewt.apache.org/newt/util"
+)
+
+// The "manufacturing meta region" is located at the end of the boot loader
+// flash area. This region 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
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |Version (0x01) | 0xff padding |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | TLV type | TLV size | TLV data ("TLV size" bytes) ~
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ~
+// ~ ~
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | TLV type | TLV size | TLV data ("TLV size" bytes) ~
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ~
+// ~ ~
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Region size | 0xff padding |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Magic (0x3bb2a269) |
+// +-+-+-+-+-+--+-+-+-+-end of boot loader area+-+-+-+-+-+-+-+-+-+-+
+//
+// The number of TLVs is variable; two are shown above for illustrative
+// 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.
+//
+// <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.
+
+const META_MAGIC = 0x3bb2a269
+const META_VERSION = 2
+const META_TLV_TYPE_HASH = 0x01
+const META_TLV_TYPE_FLASH_AREA = 0x02
+const META_TLV_TYPE_MMR_REF = 0x04
+
+const META_HASH_SZ = 32
+const META_FOOTER_SZ = 8
+const META_TLV_HEADER_SZ = 2
+const META_TLV_HASH_SZ = META_HASH_SZ
+const META_TLV_FLASH_AREA_SZ = 10
+const META_TLV_MMR_REF_SZ = 1
+
+type MetaFooter struct {
+ Size uint16 // Includes header, TLVs, and footer.
+ Version uint8
+ Pad8 uint8 // 0xff
+ Magic uint32 // META_MAGIC
+}
+
+type MetaTlvHeader struct {
+ Type uint8 // Indicates the type of data to follow.
+ Size uint8 // The number of bytes of data to follow.
+}
+
+type MetaTlvBodyFlashArea struct {
+ Area uint8 // Unique value identifying this flash area.
+ Device uint8 // Indicates host flash device (aka section number).
+ Offset uint32 // The byte offset within the flash device.
+ Size uint32 // Size, in bytes, of entire flash area.
+}
+
+type MetaTlvBodyHash struct {
+ Hash [META_HASH_SZ]byte
+}
+
+type MetaTlvBodyMmrRef struct {
+ Area uint8
+}
+
+type MetaTlv struct {
+ Header MetaTlvHeader
+ Data []byte
+}
+
+type Meta struct {
+ Tlvs []MetaTlv
+ Footer MetaFooter
+}
+
+type MetaOffsets struct {
+ Tlvs []int
+ Footer int
+ TotalSize int
+}
+
+var metaTlvTypeNameMap = map[uint8]string{
+ META_TLV_TYPE_HASH: "hash",
+ META_TLV_TYPE_FLASH_AREA: "flash_area",
+ META_TLV_TYPE_MMR_REF: "mmr_ref",
+}
+
+func MetaTlvTypeName(typ uint8) string {
+ name := metaTlvTypeNameMap[typ]
+ if name == "" {
+ name = "???"
+ }
+ return name
+}
+
+func writeElem(elem interface{}, w io.Writer) error {
+ /* XXX: Assume target platform uses little endian. */
+ if err := binary.Write(w, binary.LittleEndian, elem); err != nil {
+ return util.ChildNewtError(err)
+ }
+ return nil
+}
+
+func (tlv *MetaTlv) Write(w io.Writer) (int, error) {
+ sz := 0
+
+ if err := writeElem(tlv.Header, w); err != nil {
+ return sz, err
+ }
+ sz += META_TLV_HEADER_SZ
+
+ if err := writeElem(tlv.Data, w); err != nil {
+ return sz, err
+ }
+ sz += len(tlv.Data)
+
+ return sz, nil
+}
+
+func (meta *Meta) WritePlusOffsets(w io.Writer) (MetaOffsets, error) {
+ mo := MetaOffsets{}
+ sz := 0
+
+ for _, tlv := range meta.Tlvs {
+ tlvSz, err := tlv.Write(w)
+ if err != nil {
+ return mo, err
+ }
+ mo.Tlvs = append(mo.Tlvs, sz)
+ sz += tlvSz
+ }
+
+ if err := writeElem(meta.Footer, w); err != nil {
+ return mo, err
+ }
+ mo.Footer = sz
+ sz += META_FOOTER_SZ
+
+ mo.TotalSize = sz
+
+ return mo, nil
+}
+
+func (meta *Meta) Offsets() MetaOffsets {
+ mo, _ := meta.WritePlusOffsets(ioutil.Discard)
+ return mo
+}
+
+func (meta *Meta) Write(w io.Writer) (int, error) {
+ mo, err := meta.WritePlusOffsets(w)
+ if err != nil {
+ return 0, err
+ }
+
+ return mo.TotalSize, nil
+}
+
+func (meta *Meta) Size() int {
+ return meta.Offsets().TotalSize
+}
+
+func (meta *Meta) Bytes() ([]byte, error) {
+ b := &bytes.Buffer{}
+
+ _, err := meta.Write(b)
+ if err != nil {
+ return nil, err
+ }
+
+ return b.Bytes(), nil
+}
+
+func (meta *Meta) FindTlvIndices(typ uint8) []int {
+ indices := []int{}
+
+ for i, tlv := range meta.Tlvs {
+ if tlv.Header.Type == typ {
+ indices = append(indices, i)
+ }
+ }
+
+ return indices
+}
+
+func (meta *Meta) FindTlvs(typ uint8) []*MetaTlv {
+ indices := meta.FindTlvIndices(typ)
+
+ tlvs := []*MetaTlv{}
+ for _, index := range indices {
+ tlvs = append(tlvs, &meta.Tlvs[index])
+ }
+
+ return tlvs
+}
+
+func (meta *Meta) FindFirstTlv(typ uint8) *MetaTlv {
+ indices := meta.FindTlvIndices(typ)
+ if len(indices) == 0 {
+ return nil
+ }
+
+ return &meta.Tlvs[indices[0]]
+}
+
+func (meta *Meta) HashOffset() int {
+ mo := meta.Offsets()
+ indices := meta.FindTlvIndices(META_TLV_TYPE_HASH)
+ if len(indices) == 0 {
+ return -1
+ }
+
+ return META_TLV_HEADER_SZ + mo.Tlvs[indices[0]]
+}
+
+func (meta *Meta) ClearHash() {
+ tlv := meta.FindFirstTlv(META_TLV_TYPE_HASH)
+ if tlv != nil {
+ tlv.Data = make([]byte, META_HASH_SZ)
+ }
+}
+
+func (meta *Meta) Hash() []byte {
+ tlv := meta.FindFirstTlv(META_TLV_TYPE_HASH)
+ if tlv == nil {
+ return nil
+ }
+ return tlv.Data
+}
+
+func parseMetaTlv(bin []byte) (MetaTlv, int, error) {
+ r := bytes.NewReader(bin)
+
+ tlv := MetaTlv{}
+ if err := binary.Read(r, binary.LittleEndian, &tlv.Header); err != nil {
+ return tlv, 0, util.FmtNewtError(
+ "Error reading TLV header: %s", err.Error())
+ }
+
+ data := make([]byte, tlv.Header.Size)
+ sz, err := r.Read(data)
+ if err != nil {
+ return tlv, 0, util.FmtNewtError(
+ "Error reading %d bytes of TLV data: %s",
+ tlv.Header.Size, err.Error())
+ }
+ if sz != len(data) {
+ return tlv, 0, util.FmtNewtError(
+ "Error reading %d bytes of TLV data: incomplete read",
+ tlv.Header.Size)
+ }
+ tlv.Data = data
+
+ return tlv, META_TLV_HEADER_SZ + int(tlv.Header.Size), nil
+}
+
+func parseMetaFooter(bin []byte) (MetaFooter, int, error) {
+ r := bytes.NewReader(bin)
+
+ var ftr MetaFooter
+ if err := binary.Read(r, binary.LittleEndian, &ftr); err != nil {
+ return ftr, 0, util.FmtNewtError(
+ "Error reading meta footer: %s", err.Error())
+ }
+
+ if ftr.Magic != META_MAGIC {
+ return ftr, 0, util.FmtNewtError(
+ "Meta footer contains invalid magic; exp:0x%08x, got:0x%08x",
+ META_MAGIC, ftr.Magic)
+ }
+
+ return ftr, META_FOOTER_SZ, nil
+}
+
+func ParseMeta(bin []byte) (Meta, int, error) {
+ if len(bin) < META_FOOTER_SZ {
+ return Meta{}, 0, util.FmtNewtError(
+ "Binary too small to accommodate meta footer; "+
+ "bin-size=%d ftr-size=%d", len(bin), META_FOOTER_SZ)
+ }
+
+ ftr, _, err := parseMetaFooter(bin[len(bin)-META_FOOTER_SZ:])
+ if err != nil {
+ return Meta{}, 0, err
+ }
+
+ if int(ftr.Size) > len(bin) {
+ return Meta{}, 0, util.FmtNewtError(
+ "Binary too small to accommodate meta region; "+
+ "bin-size=%d meta-size=%d", len(bin), ftr.Size)
+ }
+
+ ftrOff := len(bin) - META_FOOTER_SZ
+ off := len(bin) - int(ftr.Size)
+
+ tlvs := []MetaTlv{}
+ for off < ftrOff {
+ tlv, sz, err := parseMetaTlv(bin[off:])
+ if err != nil {
+ return Meta{}, 0, err
+ }
+ tlvs = append(tlvs, tlv)
+ off += sz
+ }
+
+ return Meta{
+ Tlvs: tlvs,
+ Footer: ftr,
+ }, off, nil
+}
diff --git a/artifact/mfg/mfg.go b/artifact/mfg/mfg.go
new file mode 100644
index 0000000..8e999ad
--- /dev/null
+++ b/artifact/mfg/mfg.go
@@ -0,0 +1,113 @@
+package mfg
+
+import (
+ "crypto/sha256"
+
+ "mynewt.apache.org/newt/util"
+)
+
+const MFG_IMG_FILENAME = "mfgimg.bin"
+const MFG_MANIFEST_FILENAME = "manifest.json"
+
+type Mfg struct {
+ Bin []byte
+ Meta *Meta
+
+ // Unused if Meta==nil.
+ MetaOff int
+}
+
+func Parse(data []byte, metaEndOff int, eraseVal byte) (Mfg, error) {
+ m := Mfg{
+ Bin: data,
+ }
+
+ if metaEndOff >= 0 {
+ meta, _, err := ParseMeta(data[:metaEndOff])
+ if err != nil {
+ return m, err
+ }
+ m.Meta = &meta
+ m.MetaOff = metaEndOff - int(meta.Footer.Size)
+ }
+
+ return m, nil
+}
+
+func StripPadding(b []byte, eraseVal byte) []byte {
+ var pad int
+ for pad = 0; pad < len(b); pad++ {
+ off := len(b) - pad - 1
+ if b[off] != eraseVal {
+ break
+ }
+ }
+
+ return b[:len(b)-pad]
+}
+
+func AddPadding(b []byte, eraseVal byte, padLen int) []byte {
+ for i := 0; i < padLen; i++ {
+ b = append(b, eraseVal)
+ }
+ return b
+}
+
+func (m *Mfg) bytesZeroedHash(eraseVal byte) ([]byte, error) {
+ binCopy := make([]byte, len(m.Bin))
+ copy(binCopy, m.Bin)
+
+ m.Meta.ClearHash()
+
+ metaBytes, err := m.Meta.Bytes()
+ if err != nil {
+ return nil, err
+ }
+
+ padLen := m.MetaOff + len(metaBytes) - len(binCopy)
+ if padLen > 0 {
+ binCopy = AddPadding(binCopy, eraseVal, padLen)
+ }
+
+ copy(binCopy[m.MetaOff:m.MetaOff+len(metaBytes)], metaBytes)
+
+ return binCopy, nil
+}
+
+// Calculates the SHA256 hash, using the full manufacturing image as input.
+// Hash-calculation algorithm is as follows:
+// 1. Zero out the 32 bytes that will contain the hash.
+// 2. Apply SHA256 to the result.
+//
+// This function assumes that the 32 bytes of hash data have already been
+// zeroed.
+func CalcHash(bin []byte) []byte {
+ hash := sha256.Sum256(bin)
+ return hash[:]
+}
+
+func (m *Mfg) Bytes(eraseVal byte) ([]byte, error) {
+ // First, write with zeroed hash.
+ bin, err := m.bytesZeroedHash(eraseVal)
+ if err != nil {
+ return nil, err
+ }
+
+ // Calculate hash and fill TLV.
+ tlv := m.Meta.FindFirstTlv(META_TLV_TYPE_HASH)
+ if tlv != nil {
+ hashData := CalcHash(bin)
+ copy(tlv.Data, hashData)
+
+ hashOff := m.MetaOff + m.Meta.HashOffset()
+ if hashOff+META_HASH_SZ > len(bin) {
+ return nil, util.FmtNewtError(
+ "unexpected error: hash extends beyond end " +
+ "of manufacturing image")
+ }
+
+ copy(bin[hashOff:hashOff+META_HASH_SZ], tlv.Data)
+ }
+
+ return bin, nil
+}
diff --git a/newt/mfg/read.go b/artifact/mfg/paths.go
similarity index 64%
rename from newt/mfg/read.go
rename to artifact/mfg/paths.go
index 04520db..483aca2 100644
--- a/newt/mfg/read.go
+++ b/artifact/mfg/paths.go
@@ -20,21 +20,16 @@
package mfg
import (
- "strings"
-
- "mynewt.apache.org/newt/newt/builder"
+ "fmt"
+ "path/filepath"
)
-// @return mfg-image-path, error
-func (mi *MfgImage) Upload() (string, error) {
- // For now, we always upload section 0 only.
- section0Path := MfgSectionBinPath(mi.basePkg.Name(), 0)
- baseName := strings.TrimSuffix(section0Path, ".bin")
-
- envSettings := map[string]string{"MFG_IMAGE": "1"}
- if err := builder.Load(baseName, mi.bsp, envSettings); err != nil {
- return "", err
- }
+const MANIFEST_FILENAME = "manifest.json"
+const BOOT_DIR = "bootloader"
+const BOOT_MANIFEST_PATH = BOOT_DIR + "/manifest.json"
+const SECTION_BIN_DIR = "sections"
- return section0Path, nil
+func SectionBinPath(mfgPkgName string, sectionNum int) string {
+ return fmt.Sprintf("%s/%s-s%d.bin", SECTION_BIN_DIR,
+ filepath.Base(mfgPkgName), sectionNum)
}
diff --git a/larva/cli/image_cmds.go b/larva/cli/image_cmds.go
index ce1093e..cdcac33 100644
--- a/larva/cli/image_cmds.go
+++ b/larva/cli/image_cmds.go
@@ -20,16 +20,26 @@
package cli
import (
- "encoding/hex"
+ "encoding/binary"
"fmt"
+ "io/ioutil"
+ "os"
+ "sort"
log "github.com/Sirupsen/logrus"
"github.com/spf13/cobra"
"mynewt.apache.org/newt/artifact/image"
+ "mynewt.apache.org/newt/larva/lvimg"
"mynewt.apache.org/newt/util"
)
+func tlvStr(tlv image.ImageTlv) string {
+ return fmt.Sprintf("%s,0x%02x",
+ image.ImageTlvTypeName(tlv.Header.Type),
+ tlv.Header.Type)
+}
+
func readImage(filename string) (image.Image, error) {
img, err := image.ReadImage(filename)
if err != nil {
@@ -41,35 +51,39 @@ func readImage(filename string) (image.Image, error) {
}
func writeImage(img image.Image, filename string) error {
+ if err := lvimg.VerifyImage(img); err != nil {
+ return err
+ }
+
if err := img.WriteToFile(filename); err != nil {
return err
}
- log.Debugf("Wrote image %s", filename)
+ util.StatusMessage(util.VERBOSITY_DEFAULT, "Wrote image %s\n", filename)
return nil
}
-func reportDupSigs(img image.Image) {
- m := map[string]struct{}{}
- dups := map[string]struct{}{}
-
- for _, tlv := range img.Tlvs {
- if tlv.Header.Type == image.IMAGE_TLV_KEYHASH {
- h := hex.EncodeToString(tlv.Data)
- if _, ok := m[h]; ok {
- dups[h] = struct{}{}
- } else {
- m[h] = struct{}{}
- }
- }
+func parseTlvArgs(typeArg string, filenameArg string) (image.ImageTlv, error) {
+ tlvType, err := util.AtoiNoOct(typeArg)
+ if err != nil || tlvType < 0 {
+ return image.ImageTlv{}, util.FmtNewtError(
+ "Invalid TLV type integer: %s", typeArg)
}
- if len(dups) > 0 {
- fmt.Printf("Warning: duplicate signatures detected:\n")
- for d, _ := range dups {
- fmt.Printf(" %s\n", d)
- }
+ data, err := ioutil.ReadFile(filenameArg)
+ if err != nil {
+ return image.ImageTlv{}, util.FmtNewtError(
+ "Error reading TLV data file: %s", err.Error())
}
+
+ return image.ImageTlv{
+ Header: image.ImageTlvHdr{
+ Type: uint8(tlvType),
+ Pad: 0,
+ Len: uint16(len(data)),
+ },
+ Data: data,
+ }, nil
}
func runShowCmd(cmd *cobra.Command, args []string) {
@@ -89,6 +103,32 @@ func runShowCmd(cmd *cobra.Command, args []string) {
fmt.Printf("%s\n", s)
}
+func runBriefCmd(cmd *cobra.Command, args []string) {
+ if len(args) < 1 {
+ LarvaUsage(cmd, nil)
+ }
+
+ img, err := readImage(args[0])
+ if err != nil {
+ LarvaUsage(cmd, err)
+ }
+
+ offsets, err := img.Offsets()
+ if err != nil {
+ LarvaUsage(nil, err)
+ }
+
+ fmt.Printf("%8d| Header\n", offsets.Header)
+ fmt.Printf("%8d| Body\n", offsets.Body)
+ fmt.Printf("%8d| Trailer\n", offsets.Trailer)
+ for i, tlv := range img.Tlvs {
+ fmt.Printf("%8d| TLV%d: type=%s(%d)\n",
+ offsets.Tlvs[i], i, image.ImageTlvTypeName(tlv.Header.Type),
+ tlv.Header.Type)
+ }
+ fmt.Printf("Total=%d\n", offsets.TotalSize)
+}
+
func runSignCmd(cmd *cobra.Command, args []string) {
if len(args) < 2 {
LarvaUsage(cmd, nil)
@@ -116,14 +156,106 @@ func runSignCmd(cmd *cobra.Command, args []string) {
"Failed to read hash from specified image: %s", err.Error()))
}
- tlvs, err := image.GenerateSigTlvs(keys, hash)
+ tlvs, err := image.BuildSigTlvs(keys, hash)
if err != nil {
LarvaUsage(nil, err)
}
img.Tlvs = append(img.Tlvs, tlvs...)
- reportDupSigs(img)
+ if err := writeImage(img, outFilename); err != nil {
+ LarvaUsage(nil, err)
+ }
+}
+
+func runAddTlvsCmd(cmd *cobra.Command, args []string) {
+ if len(args) < 3 {
+ LarvaUsage(cmd, nil)
+ }
+
+ inFilename := args[0]
+ outFilename, err := CalcOutFilename(inFilename)
+ if err != nil {
+ LarvaUsage(cmd, err)
+ }
+
+ img, err := readImage(inFilename)
+ if err != nil {
+ LarvaUsage(cmd, err)
+ }
+
+ tlvArgs := args[1:]
+ if len(tlvArgs)%2 != 0 {
+ LarvaUsage(cmd, util.FmtNewtError(
+ "Invalid argument count; each TLV requires two arguments"))
+ }
+
+ tlvs := []image.ImageTlv{}
+ for i := 0; i < len(tlvArgs); i += 2 {
+ tlv, err := parseTlvArgs(tlvArgs[i], tlvArgs[i+1])
+ if err != nil {
+ LarvaUsage(cmd, err)
+ }
+
+ tlvs = append(tlvs, tlv)
+ }
+
+ img.Tlvs = append(img.Tlvs, tlvs...)
+
+ if err := writeImage(img, outFilename); err != nil {
+ LarvaUsage(nil, err)
+ }
+}
+
+func runRmtlvsCmd(cmd *cobra.Command, args []string) {
+ if len(args) < 2 {
+ LarvaUsage(cmd, nil)
+ }
+
+ inFilename := args[0]
+ outFilename, err := CalcOutFilename(inFilename)
+ if err != nil {
+ LarvaUsage(cmd, err)
+ }
+
+ img, err := readImage(inFilename)
+ if err != nil {
+ LarvaUsage(cmd, err)
+ }
+
+ tlvIndices := []int{}
+ idxMap := map[int]struct{}{}
+ for _, arg := range args[1:] {
+ idx, err := util.AtoiNoOct(arg)
+ if err != nil {
+ LarvaUsage(cmd, util.FmtNewtError("Invalid TLV index: %s", arg))
+ }
+
+ if idx < 0 || idx >= len(img.Tlvs) {
+ LarvaUsage(nil, util.FmtNewtError(
+ "TLV index %s out of range; "+
+ "must be in range [0, %d] for this image",
+ arg, len(img.Tlvs)-1))
+ }
+
+ if _, ok := idxMap[idx]; ok {
+ LarvaUsage(nil, util.FmtNewtError(
+ "TLV index %d specified more than once", idx))
+ }
+ idxMap[idx] = struct{}{}
+
+ tlvIndices = append(tlvIndices, idx)
+ }
+
+ // Remove TLVs in reverse order to preserve index mapping.
+ sort.Sort(sort.Reverse(sort.IntSlice(tlvIndices)))
+ for _, idx := range tlvIndices {
+ tlv := img.Tlvs[idx]
+ util.StatusMessage(util.VERBOSITY_DEFAULT,
+ "Removing TLV%d: %s\n", idx, tlvStr(tlv))
+
+ img.Tlvs = append(img.Tlvs[0:idx], img.Tlvs[idx+1:]...)
+ }
if err := writeImage(img, outFilename); err != nil {
LarvaUsage(nil, err)
@@ -160,6 +292,113 @@ func runRmsigsCmd(cmd *cobra.Command, args []string) {
}
}
+func runHashableCmd(cmd *cobra.Command, args []string) {
+ if len(args) < 1 {
+ LarvaUsage(cmd, nil)
+ }
+
+ if OptOutFilename == "" {
+ LarvaUsage(cmd, util.FmtNewtError("--outfile (-o) option required"))
+ }
+
+ inFilename := args[0]
+ outFilename := OptOutFilename
+
+ img, err := readImage(inFilename)
+ if err != nil {
+ LarvaUsage(cmd, err)
+ }
+
+ f, err := os.Create(outFilename)
+ if err != nil {
+ LarvaUsage(nil, util.ChildNewtError(err))
+ }
+ defer f.Close()
+
+ if err := binary.Write(f, binary.LittleEndian, &img.Header); err != nil {
+ LarvaUsage(nil, util.FmtNewtError(
+ "Error writing image header: %s", err.Error()))
+ }
+ _, err = f.Write(img.Body)
+ if err != nil {
+ LarvaUsage(nil, util.FmtNewtError(
+ "Error writing image body: %s", err.Error()))
+ }
+
+ util.StatusMessage(util.VERBOSITY_DEFAULT,
+ "Wrote hashable content to %s\n", outFilename)
+}
+
+func runAddsigCmd(cmd *cobra.Command, args []string) {
+ if len(args) < 4 {
+ LarvaUsage(cmd, nil)
+ }
+
+ imgFilename := args[0]
+ keyFilename := args[1]
+ sigFilename := args[2]
+
+ sigType, err := util.AtoiNoOct(args[3])
+ if err != nil || sigType < 0 || sigType > 255 ||
+ !image.ImageTlvTypeIsSig(uint8(sigType)) {
+
+ LarvaUsage(cmd, util.FmtNewtError(
+ "Invalid signature type: %s", args[3]))
+ }
+
+ outFilename, err := CalcOutFilename(imgFilename)
+ if err != nil {
+ LarvaUsage(cmd, err)
+ }
+
+ img, err := readImage(imgFilename)
+ if err != nil {
+ LarvaUsage(cmd, err)
+ }
+
+ keyData, err := ioutil.ReadFile(keyFilename)
+ if err != nil {
+ LarvaUsage(cmd, util.FmtNewtError(
+ "Error reading key file: %s", err.Error()))
+ }
+
+ sigData, err := ioutil.ReadFile(sigFilename)
+ if err != nil {
+ LarvaUsage(cmd, util.FmtNewtError(
+ "Error reading signature file: %s", err.Error()))
+ }
+
+ // ECDSA256 signatures need to be padded out to >=72 bytes.
+ if sigType == image.IMAGE_TLV_ECDSA256 {
+ sigData, err = lvimg.PadEcdsa256Sig(sigData)
+ if err != nil {
+ LarvaUsage(nil, err)
+ }
+ }
+
+ // Build and append key hash TLV.
+ keyHashTlv := image.BuildKeyHashTlv(keyData)
+ util.StatusMessage(util.VERBOSITY_DEFAULT, "Adding TLV%d (%s)\n",
+ len(img.Tlvs), tlvStr(keyHashTlv))
+ img.Tlvs = append(img.Tlvs, keyHashTlv)
+
+ // Build and append signature TLV.
+ sigTlv := image.ImageTlv{
+ Header: image.ImageTlvHdr{
+ Type: uint8(sigType),
+ Len: uint16(len(sigData)),
+ },
+ Data: sigData,
+ }
+ util.StatusMessage(util.VERBOSITY_DEFAULT, "Adding TLV%d (%s)\n",
+ len(img.Tlvs), tlvStr(sigTlv))
+ img.Tlvs = append(img.Tlvs, sigTlv)
+
+ if err := writeImage(img, outFilename); err != nil {
+ LarvaUsage(nil, err)
+ }
+}
+
func AddImageCommands(cmd *cobra.Command) {
imageCmd := &cobra.Command{
Use: "image",
@@ -177,6 +416,13 @@ func AddImageCommands(cmd *cobra.Command) {
}
imageCmd.AddCommand(showCmd)
+ briefCmd := &cobra.Command{
+ Use: "brief <img-file>",
+ Short: "Displays brief text description of a Mynewt image file",
+ Run: runBriefCmd,
+ }
+ imageCmd.AddCommand(briefCmd)
+
signCmd := &cobra.Command{
Use: "sign <img-file> <priv-key-pem> [priv-key-pem...]",
Short: "Appends signatures to a Mynewt image file",
@@ -190,6 +436,33 @@ func AddImageCommands(cmd *cobra.Command) {
imageCmd.AddCommand(signCmd)
+ addtlvsCmd := &cobra.Command{
+ Use: "addtlvs <img-file> <tlv-type> <data-filename> " +
+ "[tlv-type] [data-filename] [...]",
+ Short: "Adds the specified TLVs to a Mynewt image file",
+ Run: runAddTlvsCmd,
+ }
+
+ addtlvsCmd.PersistentFlags().StringVarP(&OptOutFilename, "outfile", "o", "",
+ "File to write to")
+ addtlvsCmd.PersistentFlags().BoolVarP(&OptInPlace, "inplace", "i", false,
+ "Replace input file")
+
+ imageCmd.AddCommand(addtlvsCmd)
+
+ rmtlvsCmd := &cobra.Command{
+ Use: "rmtlvs <img-file> <tlv-index> [tlv-index] [...]",
+ Short: "Removes the specified TLVs from a Mynewt image file",
+ Run: runRmtlvsCmd,
+ }
+
+ rmtlvsCmd.PersistentFlags().StringVarP(&OptOutFilename, "outfile", "o", "",
+ "File to write to")
+ rmtlvsCmd.PersistentFlags().BoolVarP(&OptInPlace, "inplace", "i", false,
+ "Replace input file")
+
+ imageCmd.AddCommand(rmtlvsCmd)
+
rmsigsCmd := &cobra.Command{
Use: "rmsigs",
Short: "Removes all signatures from a Mynewt image file",
@@ -202,4 +475,28 @@ func AddImageCommands(cmd *cobra.Command) {
"Replace input file")
imageCmd.AddCommand(rmsigsCmd)
+
+ hashableCmd := &cobra.Command{
+ Use: "hashable <img-file>",
+ Short: "Removes all signatures from a Mynewt image file",
+ Run: runHashableCmd,
+ }
+
+ hashableCmd.PersistentFlags().StringVarP(&OptOutFilename, "outfile", "o",
+ "", "File to write to")
+
+ imageCmd.AddCommand(hashableCmd)
+
+ addsigCmd := &cobra.Command{
+ Use: "addsig <image> <pub-key-der> <sig-der> <sig-tlv-type>",
+ Short: "Adds a signature to a Mynewt image file",
+ Run: runAddsigCmd,
+ }
+
+ addsigCmd.PersistentFlags().StringVarP(&OptOutFilename, "outfile", "o",
+ "", "File to write to")
+ addsigCmd.PersistentFlags().BoolVarP(&OptInPlace, "inplace", "i", false,
+ "Replace input file")
+
+ imageCmd.AddCommand(addsigCmd)
}
diff --git a/larva/cli/mfg_cmds.go b/larva/cli/mfg_cmds.go
index ad201cc..1a90b12 100644
--- a/larva/cli/mfg_cmds.go
+++ b/larva/cli/mfg_cmds.go
@@ -29,36 +29,39 @@ import (
"mynewt.apache.org/newt/artifact/flash"
"mynewt.apache.org/newt/artifact/manifest"
- "mynewt.apache.org/newt/larva/mfg"
+ "mynewt.apache.org/newt/artifact/mfg"
+ "mynewt.apache.org/newt/larva/lvmfg"
"mynewt.apache.org/newt/util"
)
-var optDeviceNum int
-
-func readManifest(filename string) (manifest.Manifest, error) {
- man, err := manifest.ReadManifest(filename)
+func readMfgBin(filename string) ([]byte, error) {
+ bin, err := ioutil.ReadFile(filename)
if err != nil {
- return man, err
+ return nil, util.FmtNewtError(
+ "Failed to read manufacturing image: %s", err.Error())
}
- log.Debugf("Successfully read manifest %s", filename)
- return man, nil
+ return bin, nil
}
-func readFlashAreas(manifestFilename string) ([]flash.FlashArea, error) {
- man, err := readManifest(manifestFilename)
- if err != nil {
- return nil, err
- }
+func readManifest(mfgDir string) (manifest.MfgManifest, error) {
+ return manifest.ReadMfgManifest(mfgDir + "/" + mfg.MANIFEST_FILENAME)
+}
- areas := flash.SortFlashAreasByDevOff(man.FlashAreas)
+func extractFlashAreas(mman manifest.MfgManifest) ([]flash.FlashArea, error) {
+ areas := flash.SortFlashAreasByDevOff(mman.FlashAreas)
+
+ if len(areas) == 0 {
+ LarvaUsage(nil, util.FmtNewtError(
+ "Boot loader manifest does not contain flash map"))
+ }
overlaps, conflicts := flash.DetectErrors(areas)
if len(overlaps) > 0 || len(conflicts) > 0 {
return nil, util.NewNewtError(flash.ErrorText(overlaps, conflicts))
}
- if err := mfg.VerifyAreas(areas, optDeviceNum); err != nil {
+ if err := lvmfg.VerifyAreas(areas); err != nil {
return nil, err
}
@@ -66,8 +69,10 @@ func readFlashAreas(manifestFilename string) ([]flash.FlashArea, error) {
return areas, nil
}
-func createMfgMap(binDir string, areas []flash.FlashArea) (mfg.MfgMap, error) {
- mm := mfg.MfgMap{}
+func createNameBlobMap(binDir string,
+ areas []flash.FlashArea) (lvmfg.NameBlobMap, error) {
+
+ mm := lvmfg.NameBlobMap{}
for _, area := range areas {
filename := fmt.Sprintf("%s/%s.bin", binDir, area.Name)
@@ -84,27 +89,68 @@ func createMfgMap(binDir string, areas []flash.FlashArea) (mfg.MfgMap, error) {
return mm, nil
}
-func runSplitCmd(cmd *cobra.Command, args []string) {
- if len(args) < 3 {
+func runMfgShowCmd(cmd *cobra.Command, args []string) {
+ if len(args) < 2 {
LarvaUsage(cmd, nil)
}
+ inFilename := args[0]
- imgFilename := args[0]
- manFilename := args[1]
- outDir := args[2]
-
- mfgBin, err := ioutil.ReadFile(imgFilename)
+ metaEndOff, err := util.AtoiNoOct(args[1])
if err != nil {
LarvaUsage(cmd, util.FmtNewtError(
- "Failed to read manufacturing image: %s", err.Error()))
+ "invalid meta offset \"%s\"", args[1]))
+ }
+
+ bin, err := readMfgBin(inFilename)
+ if err != nil {
+ LarvaUsage(cmd, err)
+ }
+
+ m, err := mfg.Parse(bin, metaEndOff, 0xff)
+ if err != nil {
+ LarvaUsage(nil, err)
}
- areas, err := readFlashAreas(manFilename)
+ if m.Meta == nil {
+ util.StatusMessage(util.VERBOSITY_DEFAULT,
+ "Manufacturing image %s does not contain an MMR\n", inFilename)
+ } else {
+ s, err := m.Meta.Json(metaEndOff)
+ if err != nil {
+ LarvaUsage(nil, err)
+ }
+ util.StatusMessage(util.VERBOSITY_DEFAULT,
+ "Manufacturing image %s contains an MMR with "+
+ "the following properties:\n%s\n", inFilename, s)
+ }
+}
+
+func runSplitCmd(cmd *cobra.Command, args []string) {
+ if len(args) < 2 {
+ LarvaUsage(cmd, nil)
+ }
+
+ mfgDir := args[0]
+ outDir := args[1]
+
+ mm, err := readManifest(mfgDir)
if err != nil {
LarvaUsage(cmd, err)
}
- mm, err := mfg.Split(mfgBin, optDeviceNum, areas)
+ areas, err := extractFlashAreas(mm)
+ if err != nil {
+ LarvaUsage(nil, err)
+ }
+
+ binPath := fmt.Sprintf("%s/%s", mfgDir, mm.BinPath)
+ bin, err := ioutil.ReadFile(binPath)
+ if err != nil {
+ LarvaUsage(cmd, util.FmtNewtError(
+ "Failed to read \"%s\": %s", binPath, err.Error()))
+ }
+
+ nbmap, err := lvmfg.Split(bin, mm.Device, areas, 0xff)
if err != nil {
LarvaUsage(nil, err)
}
@@ -113,39 +159,88 @@ func runSplitCmd(cmd *cobra.Command, args []string) {
LarvaUsage(nil, util.ChildNewtError(err))
}
- for name, data := range mm {
+ for name, data := range nbmap {
filename := fmt.Sprintf("%s/%s.bin", outDir, name)
- if err := ioutil.WriteFile(filename, data, os.ModePerm); err != nil {
+ if err := ioutil.WriteFile(filename, data,
+ os.ModePerm); err != nil {
+
LarvaUsage(nil, util.ChildNewtError(err))
}
}
+
+ mfgDstDir := fmt.Sprintf("%s/mfg", outDir)
+ util.StatusMessage(util.VERBOSITY_DEFAULT,
+ "Copying source mfg directory to %s\n", mfgDstDir)
+ if err := util.CopyDir(mfgDir, mfgDstDir); err != nil {
+ LarvaUsage(nil, err)
+ }
}
func runJoinCmd(cmd *cobra.Command, args []string) {
- if len(args) < 3 {
+ if len(args) < 2 {
LarvaUsage(cmd, nil)
}
- binDir := args[0]
- manFilename := args[1]
- outFilename := args[2]
+ splitDir := args[0]
+ outDir := args[1]
- areas, err := readFlashAreas(manFilename)
+ if util.NodeExist(outDir) {
+ LarvaUsage(nil, util.FmtNewtError(
+ "Destination \"%s\" already exists", outDir))
+ }
+
+ mm, err := readManifest(splitDir + "/mfg")
+ if err != nil {
+ LarvaUsage(cmd, err)
+ }
+ areas, err := extractFlashAreas(mm)
if err != nil {
LarvaUsage(cmd, err)
}
- mm, err := createMfgMap(binDir, areas)
+ nbmap, err := createNameBlobMap(splitDir, areas)
+ if err != nil {
+ LarvaUsage(nil, err)
+ }
+
+ bin, err := lvmfg.Join(nbmap, 0xff, areas)
if err != nil {
LarvaUsage(nil, err)
}
- mfgBin, err := mfg.Join(mm, 0xff, areas)
+ m, err := mfg.Parse(bin, mm.Meta.EndOffset, 0xff)
if err != nil {
LarvaUsage(nil, err)
}
- if err := ioutil.WriteFile(outFilename, mfgBin, os.ModePerm); err != nil {
+ infos, err := ioutil.ReadDir(splitDir + "/mfg")
+ if err != nil {
+ LarvaUsage(nil, util.FmtNewtError(
+ "Error reading source mfg directory: %s", err.Error()))
+ }
+ for _, info := range infos {
+ if info.Name() != mfg.MFG_IMG_FILENAME {
+ src := splitDir + "/mfg/" + info.Name()
+ dst := outDir + "/" + info.Name()
+ if info.IsDir() {
+ err = util.CopyDir(src, dst)
+ } else {
+ err = util.CopyFile(src, dst)
+ }
+ if err != nil {
+ LarvaUsage(nil, err)
+ }
+ }
+ }
+
+ finalBin, err := m.Bytes(0xff)
+ if err != nil {
+ LarvaUsage(nil, err)
+ }
+
+ if err := ioutil.WriteFile(outDir+"/"+mfg.MFG_IMG_FILENAME, finalBin,
+ os.ModePerm); err != nil {
+
LarvaUsage(nil, util.ChildNewtError(err))
}
}
@@ -182,7 +277,7 @@ func runBootKeyCmd(cmd *cobra.Command, args []string) {
"Failed to read new key der: %s", err.Error()))
}
- if err := mfg.ReplaceBootKey(sec0, okey, nkey); err != nil {
+ if err := lvmfg.ReplaceBootKey(sec0, okey, nkey); err != nil {
LarvaUsage(nil, err)
}
@@ -201,26 +296,28 @@ func AddMfgCommands(cmd *cobra.Command) {
}
cmd.AddCommand(mfgCmd)
+ showCmd := &cobra.Command{
+ Use: "show <mfgimg.bin> <meta-end-offset>",
+ Short: "Displays JSON describing a manufacturing image",
+ Run: runMfgShowCmd,
+ }
+
+ mfgCmd.AddCommand(showCmd)
+
splitCmd := &cobra.Command{
- Use: "split <mfg-image> <manifest> <out-dir>",
+ Use: "split <mfg-image-dir> <out-dir>",
Short: "Splits a Mynewt mfg section into several files",
Run: runSplitCmd,
}
- splitCmd.PersistentFlags().IntVarP(&optDeviceNum, "device", "d", 0,
- "Flash device number")
-
mfgCmd.AddCommand(splitCmd)
joinCmd := &cobra.Command{
- Use: "join <bin-dir> <manifest> <out-mfg-image>",
+ Use: "join <split-dir> <out-dir>",
Short: "Joins a split mfg section into a single file",
Run: runJoinCmd,
}
- joinCmd.PersistentFlags().IntVarP(&optDeviceNum, "device", "d", 0,
- "Flash device number")
-
mfgCmd.AddCommand(joinCmd)
bootKeyCmd := &cobra.Command{
diff --git a/larva/lvimg/lvimg.go b/larva/lvimg/lvimg.go
new file mode 100644
index 0000000..60d5ef4
--- /dev/null
+++ b/larva/lvimg/lvimg.go
@@ -0,0 +1,116 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package lvimg
+
+import (
+ "encoding/hex"
+ "fmt"
+ "strings"
+
+ "mynewt.apache.org/newt/artifact/image"
+ "mynewt.apache.org/newt/util"
+)
+
+func GetDupSigs(img image.Image) []string {
+ m := map[string]struct{}{}
+ var dups []string
+
+ for _, tlv := range img.Tlvs {
+ if tlv.Header.Type == image.IMAGE_TLV_KEYHASH {
+ h := hex.EncodeToString(tlv.Data)
+ if _, ok := m[h]; ok {
+ dups = append(dups, h)
+ } else {
+ m[h] = struct{}{}
+ }
+ }
+ }
+
+ return dups
+}
+
+func DetectInvalidSigTlvs(img image.Image) error {
+ var errStrs []string
+ addErr := func(format string, args ...interface{}) {
+ s := fmt.Sprintf(format, args...)
+ errStrs = append(errStrs, s)
+ }
+
+ prevIsHash := false
+ for i, tlv := range img.Tlvs {
+ curIsHash := tlv.Header.Type == image.IMAGE_TLV_KEYHASH
+ curIsSig := image.ImageTlvTypeIsSig(tlv.Header.Type)
+ isLast := i == len(img.Tlvs)-1
+
+ if prevIsHash && !curIsSig {
+ prevTlv := img.Tlvs[i-1]
+ addErr("TLV%d (%s) not immediately followed by signature TLV",
+ i-1, image.ImageTlvTypeName(prevTlv.Header.Type))
+ } else if curIsHash && isLast {
+ addErr("TLV%d (%s) not immediately followed by signature TLV",
+ i, image.ImageTlvTypeName(tlv.Header.Type))
+ } else if !prevIsHash && curIsSig {
+ addErr("TLV%d (%s) not immediately preceded by key hash TLV",
+ i, image.ImageTlvTypeName(tlv.Header.Type))
+ }
+
+ prevIsHash = curIsHash
+ }
+
+ if len(errStrs) > 0 {
+ return util.FmtNewtError("%s", strings.Join(errStrs, "\n"))
+ }
+
+ return nil
+}
+
+func VerifyImage(img image.Image) error {
+ if len(img.Tlvs) == 0 || img.Tlvs[0].Header.Type != image.IMAGE_TLV_SHA256 {
+ return util.FmtNewtError("First TLV must be SHA256")
+ }
+
+ if err := DetectInvalidSigTlvs(img); err != nil {
+ return err
+ }
+
+ if dups := GetDupSigs(img); len(dups) > 0 {
+ s := "Duplicate signatures detected:"
+ for _, d := range dups {
+ s += fmt.Sprintf("\n %s", d)
+ }
+
+ return util.FmtNewtError("%s", s)
+ }
+
+ return nil
+}
+
+func PadEcdsa256Sig(sig []byte) ([]byte, error) {
+ if len(sig) < 70 {
+ return nil, util.FmtNewtError(
+ "Invalid ECDSA256 signature; length (%d) less than 70", len(sig))
+ }
+
+ if len(sig) < 72 {
+ sig = append(sig, []byte{0x00, 0x00}...)
+ }
+
+ return sig, nil
+}
diff --git a/larva/mfg/mfg.go b/larva/lvmfg/lvmfg.go
similarity index 64%
rename from larva/mfg/mfg.go
rename to larva/lvmfg/lvmfg.go
index f98ab06..dbc0168 100644
--- a/larva/mfg/mfg.go
+++ b/larva/lvmfg/lvmfg.go
@@ -17,7 +17,7 @@
* under the License.
*/
-package mfg
+package lvmfg
import (
"bytes"
@@ -26,10 +26,17 @@ import (
"strings"
"mynewt.apache.org/newt/artifact/flash"
+ "mynewt.apache.org/newt/artifact/mfg"
"mynewt.apache.org/newt/util"
)
-type MfgMap map[string][]byte
+type NameBlobMap map[string][]byte
+
+func (to NameBlobMap) Union(from NameBlobMap) {
+ for k, v := range from {
+ to[k] = v
+ }
+}
func errInvalidArea(areaName string, format string,
args ...interface{}) error {
@@ -52,24 +59,27 @@ func verifyArea(area flash.FlashArea, minOffset int) error {
}
// `areas` must be sorted by device ID, then by offset.
-func VerifyAreas(areas []flash.FlashArea, deviceNum int) error {
+func VerifyAreas(areas []flash.FlashArea) error {
+ prevDevice := -1
off := 0
for _, area := range areas {
- if area.Device == deviceNum {
- if err := verifyArea(area, off); err != nil {
- return err
- }
- off += area.Size
+ if area.Device != prevDevice {
+ off = 0
+ }
+
+ if err := verifyArea(area, off); err != nil {
+ return err
}
+ off += area.Size
}
return nil
}
func Split(mfgBin []byte, deviceNum int,
- areas []flash.FlashArea) (MfgMap, error) {
+ areas []flash.FlashArea, eraseVal byte) (NameBlobMap, error) {
- mm := MfgMap{}
+ mm := NameBlobMap{}
for _, area := range areas {
if _, ok := mm[area.Name]; ok {
@@ -77,16 +87,18 @@ func Split(mfgBin []byte, deviceNum int,
"two or more flash areas with same name: \"%s\"", area.Name)
}
- if area.Device == deviceNum && area.Offset < len(mfgBin) {
- end := area.Offset + area.Size
- if end > len(mfgBin) {
- return nil, util.FmtNewtError(
- "area \"%s\" (offset=%d size=%d) "+
- "extends beyond end of manufacturing image",
- area.Name, area.Offset, area.Size)
+ if area.Device == deviceNum {
+ var areaBin []byte
+ if area.Offset < len(mfgBin) {
+ end := area.Offset + area.Size
+ overflow := end - len(mfgBin)
+ if overflow > 0 {
+ end -= overflow
+ }
+ areaBin = mfgBin[area.Offset:end]
}
- mm[area.Name] = mfgBin[area.Offset:end]
+ mm[area.Name] = StripPadding(areaBin, eraseVal)
}
}
@@ -94,19 +106,8 @@ func Split(mfgBin []byte, deviceNum int,
}
// `areas` must be sorted by device ID, then by offset.
-func Join(mm MfgMap, eraseVal byte, areas []flash.FlashArea) ([]byte, error) {
- // Ensure all areas in the mfg map belong to the same flash device.
- device := -1
- for _, area := range areas {
- if _, ok := mm[area.Name]; ok {
- if device == -1 {
- device = area.Device
- } else if device != area.Device {
- return nil, util.FmtNewtError(
- "multiple flash devices: %d != %d", device, area.Device)
- }
- }
- }
+func Join(mm NameBlobMap, eraseVal byte,
+ areas []flash.FlashArea) ([]byte, error) {
// Keep track of which areas we haven't seen yet.
unseen := map[string]struct{}{}
@@ -115,24 +116,33 @@ func Join(mm MfgMap, eraseVal byte, areas []flash.FlashArea) ([]byte, error) {
}
joined := []byte{}
-
- off := 0
for _, area := range areas {
bin := mm[area.Name]
- if bin == nil {
- break
- }
- delete(unseen, area.Name)
- padSize := area.Offset - off
- for i := 0; i < padSize; i++ {
- joined = append(joined, 0xff)
- }
+ // Only include this area if it belongs to the mfg image we are
+ // joining.
+ if bin != nil {
+ delete(unseen, area.Name)
- joined = append(joined, bin...)
+ // Pad remainder of previous area in this section.
+ padSize := area.Offset - len(joined)
+ if padSize > 0 {
+ joined = mfg.AddPadding(joined, eraseVal, padSize)
+ }
+
+ // Append data to joined binary.
+ binstr := ""
+ if len(bin) >= 4 {
+ binstr = fmt.Sprintf("%x", bin[:4])
+ }
+ util.StatusMessage(util.VERBOSITY_DEFAULT,
+ "inserting %s (%x) at offset %d (0x%x)\n",
+ area.Name, binstr, len(joined), len(joined))
+ joined = append(joined, bin...)
+ }
}
- // Ensure we processed every area in the mfg map.
+ // Ensure we processed every area in the map.
if len(unseen) > 0 {
names := []string{}
for name, _ := range unseen {
@@ -144,6 +154,9 @@ func Join(mm MfgMap, eraseVal byte, areas []flash.FlashArea) ([]byte, error) {
"unprocessed flash areas: %s", strings.Join(names, ", "))
}
+ // Strip padding from the end of the joined bianry.
+ joined = StripPadding(joined, eraseVal)
+
return joined, nil
}
@@ -176,3 +189,15 @@ func ReplaceBootKey(sec0 []byte, okey []byte, nkey []byte) error {
return nil
}
+
+func StripPadding(b []byte, eraseVal byte) []byte {
+ var pad int
+ for pad = 0; pad < len(b); pad++ {
+ off := len(b) - pad - 1
+ if b[off] != eraseVal {
+ break
+ }
+ }
+
+ return b[:len(b)-pad]
+}
diff --git a/newt/builder/targetbuild.go b/newt/builder/targetbuild.go
index 23c361a..94fe8f9 100644
--- a/newt/builder/targetbuild.go
+++ b/newt/builder/targetbuild.go
@@ -28,6 +28,7 @@ import (
"fmt"
"io/ioutil"
"os"
+ "path/filepath"
"strings"
log "github.com/Sirupsen/logrus"
diff --git a/newt/cli/build_cmds.go b/newt/cli/build_cmds.go
index d986510..d7cb8fc 100644
--- a/newt/cli/build_cmds.go
+++ b/newt/cli/build_cmds.go
@@ -27,6 +27,8 @@ import (
"github.com/spf13/cobra"
"mynewt.apache.org/newt/newt/builder"
+ "mynewt.apache.org/newt/newt/imgprod"
+ "mynewt.apache.org/newt/newt/manifest"
"mynewt.apache.org/newt/newt/pkg"
"mynewt.apache.org/newt/newt/project"
"mynewt.apache.org/newt/newt/target"
@@ -160,6 +162,15 @@ func buildRunCmd(cmd *cobra.Command, args []string, printShellCmds bool, execute
NewtUsage(nil, err)
}
+ // Produce bare "imageless" manifest.
+ mopts, err := manifest.OptsForNonImage(b)
+ if err != nil {
+ NewtUsage(nil, err)
+ }
+ if err := imgprod.ProduceManifest(mopts); err != nil {
+ NewtUsage(nil, err)
+ }
+
util.StatusMessage(util.VERBOSITY_DEFAULT,
"Target successfully built: %s\n", t.Name())
}
diff --git a/newt/cli/mfg_cmds.go b/newt/cli/mfg_cmds.go
index 513e2c4..78d5eb9 100644
--- a/newt/cli/mfg_cmds.go
+++ b/newt/cli/mfg_cmds.go
@@ -20,6 +20,8 @@
package cli
import (
+ "fmt"
+
"github.com/spf13/cobra"
"mynewt.apache.org/newt/artifact/image"
@@ -50,31 +52,34 @@ func ResolveMfgPkg(pkgName string) (*pkg.LocalPackage, error) {
return lpkg, nil
}
-func mfgCreate(mi *mfg.MfgImage) {
- pathStr := ""
- for _, path := range mi.FromPaths() {
- pathStr += " * " + path + "\n"
+func mfgCreate(me mfg.MfgEmitter) {
+ srcPaths, dstPaths, err := me.Emit()
+ if err != nil {
+ NewtUsage(nil, err)
}
- util.StatusMessage(util.VERBOSITY_DEFAULT,
- "Creating a manufacturing image from the following files:\n%s\n",
- pathStr)
+ srcStr := ""
+ dstStr := ""
- outputPaths, err := mi.CreateMfgImage()
- if err != nil {
- NewtUsage(nil, err)
+ for _, p := range srcPaths {
+ srcStr += fmt.Sprintf(" %s\n", p)
}
- pathStr = ""
- for _, path := range outputPaths {
- pathStr += " * " + path + "\n"
+ for _, p := range dstPaths {
+ dstStr += fmt.Sprintf(" %s\n", p)
}
+
util.StatusMessage(util.VERBOSITY_DEFAULT,
- "Generated the following files:\n%s", pathStr)
+ "Creating a manufacturing image from the following files:\n%s\n",
+ srcStr)
+
+ util.StatusMessage(util.VERBOSITY_DEFAULT,
+ "Generated the following files:\n%s\n",
+ dstStr)
}
-func mfgLoad(mi *mfg.MfgImage) {
- binPath, err := mi.Upload()
+func mfgLoad(basePkg *pkg.LocalPackage) {
+ binPath, err := mfg.Upload(basePkg)
if err != nil {
NewtUsage(nil, err)
}
@@ -101,13 +106,12 @@ func mfgCreateRunCmd(cmd *cobra.Command, args []string) {
NewtUsage(cmd, err)
}
- mi, err := mfg.Load(lpkg)
+ me, err := mfg.LoadMfgEmitter(lpkg, ver)
if err != nil {
NewtUsage(nil, err)
}
- mi.SetVersion(ver)
- mfgCreate(mi)
+ mfgCreate(me)
}
func mfgLoadRunCmd(cmd *cobra.Command, args []string) {
@@ -121,12 +125,7 @@ func mfgLoadRunCmd(cmd *cobra.Command, args []string) {
NewtUsage(cmd, err)
}
- mi, err := mfg.Load(lpkg)
- if err != nil {
- NewtUsage(nil, err)
- }
-
- mfgLoad(mi)
+ mfgLoad(lpkg)
}
func mfgDeployRunCmd(cmd *cobra.Command, args []string) {
@@ -149,15 +148,14 @@ func mfgDeployRunCmd(cmd *cobra.Command, args []string) {
}
}
- mi, err := mfg.Load(lpkg)
+ me, err := mfg.LoadMfgEmitter(lpkg, ver)
if err != nil {
NewtUsage(nil, err)
}
- mi.SetVersion(ver)
- mfgCreate(mi)
+ mfgCreate(me)
- mfgLoad(mi)
+ mfgLoad(lpkg)
}
func AddMfgCommands(cmd *cobra.Command) {
diff --git a/newt/flashmap/flashmap.go b/newt/flashmap/flashmap.go
index 68fa699..d044f42 100644
--- a/newt/flashmap/flashmap.go
+++ b/newt/flashmap/flashmap.go
@@ -26,7 +26,6 @@ import (
"io/ioutil"
"os"
"path/filepath"
- "sort"
"strings"
log "github.com/Sirupsen/logrus"
@@ -172,22 +171,6 @@ func (flashMap FlashMap) SortedAreas() []flash.FlashArea {
return flash.SortFlashAreasById(areas)
}
-func (flashMap FlashMap) DeviceIds() []int {
- deviceMap := map[int]struct{}{}
-
- for _, area := range flashMap.Areas {
- deviceMap[area.Device] = struct{}{}
- }
-
- devices := make([]int, 0, len(deviceMap))
- for device, _ := range deviceMap {
- devices = append(devices, device)
- }
- sort.Ints(devices)
-
- return devices
-}
-
func areasDistinct(a flash.FlashArea, b flash.FlashArea) bool {
var lo flash.FlashArea
var hi flash.FlashArea
diff --git a/newt/imgprod/imgprod.go b/newt/imgprod/imgprod.go
index a858c49..dc72eca 100644
--- a/newt/imgprod/imgprod.go
+++ b/newt/imgprod/imgprod.go
@@ -262,16 +262,14 @@ func ProduceAll(t *builder.TargetBuilder, ver image.ImageVersion,
return err
}
- mopts := manifest.ManifestCreateOpts{
- TgtBldr: t,
- AppHash: pset.App.Hash,
- Version: ver,
- BuildID: fmt.Sprintf("%x", pset.App.Hash),
- FlashAreas: t.BspPkg().FlashMap.SortedAreas(),
+ var loaderHash []byte
+ if pset.Loader != nil {
+ loaderHash = pset.Loader.Hash
}
- if pset.Loader != nil {
- mopts.LoaderHash = pset.Loader.Hash
+ mopts, err := manifest.OptsForImage(t, ver, pset.App.Hash, loaderHash)
+ if err != nil {
+ return err
}
if err := ProduceManifest(mopts); err != nil {
diff --git a/newt/imgprod/v1.go b/newt/imgprod/v1.go
index 37067d4..05f0a33 100644
--- a/newt/imgprod/v1.go
+++ b/newt/imgprod/v1.go
@@ -195,11 +195,10 @@ func ProduceAllV1(t *builder.TargetBuilder, ver image.ImageVersion,
}
mopts := manifest.ManifestCreateOpts{
- TgtBldr: t,
- AppHash: pset.App.Hash,
- Version: ver,
- BuildID: fmt.Sprintf("%x", pset.App.Hash),
- FlashAreas: t.BspPkg().FlashMap.SortedAreas(),
+ TgtBldr: t,
+ AppHash: pset.App.Hash,
+ Version: ver,
+ BuildID: fmt.Sprintf("%x", pset.App.Hash),
}
if pset.Loader != nil {
diff --git a/newt/manifest/manifest.go b/newt/manifest/manifest.go
index 7c9eaff..c2b5243 100644
--- a/newt/manifest/manifest.go
+++ b/newt/manifest/manifest.go
@@ -30,7 +30,6 @@ import (
log "github.com/Sirupsen/logrus"
- "mynewt.apache.org/newt/artifact/flash"
"mynewt.apache.org/newt/artifact/image"
"mynewt.apache.org/newt/artifact/manifest"
"mynewt.apache.org/newt/newt/builder"
@@ -49,7 +48,7 @@ type ManifestCreateOpts struct {
AppHash []byte
Version image.ImageVersion
BuildID string
- FlashAreas []flash.FlashArea
+ Syscfg map[string]string
}
type RepoManager struct {
@@ -245,17 +244,47 @@ func ManifestPkgSizes(b *builder.Builder) (ManifestSizeCollector, error) {
return msc, nil
}
+func OptsForNonImage(t *builder.TargetBuilder) (ManifestCreateOpts, error) {
+ res, err := t.Resolve()
+ if err != nil {
+ return ManifestCreateOpts{}, err
+ }
+
+ return ManifestCreateOpts{
+ TgtBldr: t,
+ Syscfg: res.Cfg.SettingValues(),
+ }, nil
+}
+
+func OptsForImage(t *builder.TargetBuilder, ver image.ImageVersion,
+ appHash []byte, loaderHash []byte) (ManifestCreateOpts, error) {
+
+ res, err := t.Resolve()
+ if err != nil {
+ return ManifestCreateOpts{}, err
+ }
+
+ return ManifestCreateOpts{
+ TgtBldr: t,
+ AppHash: appHash,
+ LoaderHash: loaderHash,
+ Version: ver,
+ BuildID: fmt.Sprintf("%x", appHash),
+ Syscfg: res.Cfg.SettingValues(),
+ }, nil
+}
+
func CreateManifest(opts ManifestCreateOpts) (manifest.Manifest, error) {
t := opts.TgtBldr
m := manifest.Manifest{
- Name: t.GetTarget().FullName(),
- Date: time.Now().Format(time.RFC3339),
- Version: opts.Version.String(),
- BuildID: opts.BuildID,
- Image: t.AppBuilder.AppImgPath(),
- ImageHash: fmt.Sprintf("%x", opts.AppHash),
- FlashAreas: opts.FlashAreas,
+ Name: t.GetTarget().FullName(),
+ Date: time.Now().Format(time.RFC3339),
+ Version: opts.Version.String(),
+ BuildID: opts.BuildID,
+ Image: t.AppBuilder.AppImgPath(),
+ ImageHash: fmt.Sprintf("%x", opts.AppHash),
+ Syscfg: opts.Syscfg,
}
rm := NewRepoManager()
diff --git a/newt/mfg/build.go b/newt/mfg/build.go
new file mode 100644
index 0000000..a3f7291
--- /dev/null
+++ b/newt/mfg/build.go
@@ -0,0 +1,564 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package mfg
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "io/ioutil"
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "mynewt.apache.org/newt/artifact/flash"
+ "mynewt.apache.org/newt/artifact/image"
+ "mynewt.apache.org/newt/artifact/manifest"
+ "mynewt.apache.org/newt/artifact/mfg"
+ "mynewt.apache.org/newt/newt/builder"
+ "mynewt.apache.org/newt/newt/flashmap"
+ "mynewt.apache.org/newt/newt/parse"
+ "mynewt.apache.org/newt/newt/pkg"
+ "mynewt.apache.org/newt/newt/project"
+ "mynewt.apache.org/newt/newt/target"
+ "mynewt.apache.org/newt/util"
+)
+
+type MfgBuildTarget struct {
+ Target *target.Target
+ Area flash.FlashArea
+ Offset int
+ IsBoot bool
+ BinPath string
+}
+
+type MfgBuildRaw struct {
+ Filename string
+ Offset int
+ Area flash.FlashArea
+}
+
+type MfgBuildMetaMmr struct {
+ Area flash.FlashArea
+}
+
+type MfgBuildMeta struct {
+ Area flash.FlashArea
+ Hash bool
+ FlashMap bool
+ Mmrs []MfgBuildMetaMmr
+}
+
+// Can be used to construct an Mfg object.
+type MfgBuilder struct {
+ BasePkg *pkg.LocalPackage
+ Bsp *pkg.BspPackage
+ Targets []MfgBuildTarget
+ Raws []MfgBuildRaw
+ Meta *MfgBuildMeta
+}
+
+// Searches the provided flash map for the named area.
+func lookUpArea(fm flashmap.FlashMap, name string) (flash.FlashArea, error) {
+ area, ok := fm.Areas[name]
+ if !ok {
+ return flash.FlashArea{}, util.FmtNewtError(
+ "reference to undefined flash area \"%s\"", name)
+ }
+
+ return area, nil
+}
+
+// Searches the project for the target corresponding to the specified decoded
+// entry (read from `mfg.yml`).
+func lookUpTarget(dt DecodedTarget) (*target.Target, error) {
+ t := target.GetTargets()[dt.Name]
+ if t == nil {
+ return nil, util.FmtNewtError(
+ "target entry references undefined target \"%s\"", dt.Name)
+ }
+
+ return t, nil
+}
+
+func normalizeOffset(offset int, length int,
+ area flash.FlashArea) (int, error) {
+
+ areaEnd := area.Offset + area.Size
+ if offset == OFFSET_END {
+ if length > area.Size {
+ return 0, util.FmtNewtError(
+ "segment is too large to fit in flash area \"%s\"; "+
+ "segment=%d, area=%d", area.Name, length, area.Size)
+ }
+ return areaEnd - length, nil
+ }
+
+ if offset+length > area.Size {
+ return 0, util.FmtNewtError(
+ "segment extends beyond end of flash area \"%s\"; "+
+ "offset=%d len=%d area_len=%d",
+ area.Name, offset, length, area.Size)
+ }
+
+ return area.Offset + offset, nil
+}
+
+func calcBsp(dm DecodedMfg,
+ basePkg *pkg.LocalPackage) (*pkg.BspPackage, error) {
+
+ var bspLpkg *pkg.LocalPackage
+ bspMap := map[*pkg.LocalPackage]struct{}{}
+ for _, dt := range dm.Targets {
+ t, err := lookUpTarget(dt)
+ if err != nil {
+ return nil, err
+ }
+
+ bspLpkg = t.Bsp()
+ bspMap[bspLpkg] = struct{}{}
+ }
+
+ if dm.Bsp != "" {
+ var err error
+ bspLpkg, err = project.GetProject().ResolvePackage(
+ basePkg.Repo(), dm.Bsp)
+ if err != nil {
+ return nil, util.FmtNewtError(
+ "failed to resolve BSP package: %s", err.Error())
+ }
+ bspMap[bspLpkg] = struct{}{}
+ }
+
+ if len(bspMap) == 0 {
+ return nil, util.FmtNewtError("at least one target required")
+ }
+
+ if len(bspMap) > 1 {
+ return nil, util.FmtNewtError("multiple BSPs detected")
+ }
+
+ bsp, err := pkg.NewBspPackage(bspLpkg)
+ if err != nil {
+ return nil, util.FmtNewtError(err.Error())
+ }
+
+ return bsp, nil
+}
+
+func (raw *MfgBuildRaw) ToPart(entryIdx int) (Part, error) {
+ data, err := ioutil.ReadFile(raw.Filename)
+ if err != nil {
+ return Part{}, util.ChildNewtError(err)
+ }
+
+ off, err := normalizeOffset(raw.Offset, len(data), raw.Area)
+ if err != nil {
+ return Part{}, err
+ }
+
+ return Part{
+ Name: fmt.Sprintf("raw-%d (%s)", entryIdx, raw.Filename),
+ Offset: off,
+ Data: data,
+ }, nil
+}
+
+func (mt *MfgBuildTarget) ToPart() (Part, error) {
+ data, err := ioutil.ReadFile(mt.BinPath)
+ if err != nil {
+ return Part{}, util.ChildNewtError(err)
+ }
+
+ off, err := normalizeOffset(mt.Offset, len(data), mt.Area)
+ if err != nil {
+ return Part{}, err
+ }
+
+ return Part{
+ Name: fmt.Sprintf("%s (%s)", mt.Area.Name, filepath.Base(mt.BinPath)),
+ Offset: off,
+ Data: data,
+ }, nil
+}
+
+func newMfgBuildTarget(dt DecodedTarget,
+ fm flashmap.FlashMap) (MfgBuildTarget, error) {
+
+ t, err := lookUpTarget(dt)
+ if err != nil {
+ return MfgBuildTarget{}, err
+ }
+
+ area, err := lookUpArea(fm, dt.Area)
+ if err != nil {
+ return MfgBuildTarget{}, err
+ }
+
+ mpath := builder.ManifestPath(dt.Name, builder.BUILD_NAME_APP,
+ t.App().Name())
+ man, err := manifest.ReadManifest(mpath)
+ if err != nil {
+ return MfgBuildTarget{}, util.FmtNewtError("%s", err.Error())
+ }
+
+ isBoot := parse.ValueIsTrue(man.Syscfg["BOOT_LOADER"])
+
+ return MfgBuildTarget{
+ Target: t,
+ Area: area,
+ Offset: dt.Offset,
+ IsBoot: isBoot,
+ BinPath: targetSrcBinPath(t, isBoot),
+ }, nil
+}
+
+func newMfgBuildRaw(dr DecodedRaw,
+ fm flashmap.FlashMap, basePath string) (MfgBuildRaw, error) {
+
+ filename := dr.Filename
+ if !strings.HasPrefix(filename, "/") {
+ filename = basePath + "/" + filename
+ }
+
+ area, err := lookUpArea(fm, dr.Area)
+ if err != nil {
+ return MfgBuildRaw{}, err
+ }
+
+ return MfgBuildRaw{
+ Filename: filename,
+ Offset: dr.Offset,
+ Area: area,
+ }, nil
+}
+
+func newMfgBuildMeta(dm DecodedMeta,
+ fm flashmap.FlashMap) (MfgBuildMeta, error) {
+
+ area, ok := fm.Areas[dm.Area]
+ if !ok {
+ return MfgBuildMeta{}, util.FmtNewtError(
+ "meta region specifies unrecognized flash area: \"%s\"", dm.Area)
+ }
+
+ var mmrs []MfgBuildMetaMmr
+ for _, dmmr := range dm.Mmrs {
+ area, err := lookUpArea(fm, dmmr.Area)
+ if err != nil {
+ return MfgBuildMeta{}, err
+ }
+ mmr := MfgBuildMetaMmr{
+ Area: area,
+ }
+ mmrs = append(mmrs, mmr)
+ }
+
+ return MfgBuildMeta{
+ Area: area,
+ Hash: dm.Hash,
+ FlashMap: dm.FlashMap,
+ Mmrs: mmrs,
+ }, nil
+}
+
+func (mb *MfgBuilder) parts() ([]Part, error) {
+ parts := []Part{}
+
+ // Create parts from the raw entries.
+ for i, raw := range mb.Raws {
+ part, err := raw.ToPart(i)
+ if err != nil {
+ return nil, err
+ }
+ parts = append(parts, part)
+ }
+
+ // Create parts from the target entries.
+ for _, t := range mb.Targets {
+ part, err := t.ToPart()
+ if err != nil {
+ return nil, err
+ }
+ parts = append(parts, part)
+ }
+
+ // Sort by offset.
+ return SortParts(parts), nil
+}
+
+func (mb *MfgBuilder) detectOverlaps() error {
+ type overlap struct {
+ p1 Part
+ p2 Part
+ }
+
+ overlaps := []overlap{}
+
+ parts, err := mb.parts()
+ if err != nil {
+ return err
+ }
+
+ for i, p1 := range parts[:len(parts)-1] {
+ p1end := p1.Offset + len(p1.Data)
+ for _, p2 := range parts[i+1:] {
+ // Parts are sorted by offset, so only one comparison is
+ // necessary to detect overlap.
+ if p2.Offset < p1end {
+ overlaps = append(overlaps, overlap{
+ p1: p1,
+ p2: p2,
+ })
+ }
+ }
+ }
+
+ if len(overlaps) > 0 {
+ str := "flash overlaps detected:"
+ for _, overlap := range overlaps {
+
+ p1end := overlap.p1.Offset + len(overlap.p1.Data)
+ p2end := overlap.p2.Offset + len(overlap.p2.Data)
+ str += fmt.Sprintf("\n * [%s] (%d - %d) <=> [%s] (%d - %d)",
+ overlap.p1.Name, overlap.p1.Offset, p1end,
+ overlap.p2.Name, overlap.p2.Offset, p2end)
+ }
+
+ return util.NewNewtError(str)
+ }
+
+ return nil
+}
+
+// Determines which flash device the manufacturing image is intended for. It
+// is an error if the mfg definition specifies 0 or >1 devices.
+func (mb *MfgBuilder) calcDevice() (int, error) {
+ deviceMap := map[int]struct{}{}
+ for _, t := range mb.Targets {
+ deviceMap[t.Area.Device] = struct{}{}
+ }
+ for _, r := range mb.Raws {
+ deviceMap[r.Area.Device] = struct{}{}
+ }
+
+ devices := make([]int, 0, len(deviceMap))
+ for d, _ := range deviceMap {
+ devices = append(devices, d)
+ }
+ sort.Ints(devices)
+
+ if len(devices) == 0 {
+ return 0, util.FmtNewtError(
+ "manufacturing image definition does not indicate flash device")
+ }
+
+ if len(devices) > 1 {
+ return 0, util.FmtNewtError(
+ "multiple flash devices in use by single manufacturing image: %v",
+ devices)
+ }
+
+ return devices[0], nil
+}
+
+func newMfgBuilder(basePkg *pkg.LocalPackage, dm DecodedMfg,
+ ver image.ImageVersion) (MfgBuilder, error) {
+
+ mb := MfgBuilder{
+ BasePkg: basePkg,
+ }
+
+ bsp, err := calcBsp(dm, basePkg)
+ if err != nil {
+ return mb, err
+ }
+ mb.Bsp = bsp
+
+ for _, dt := range dm.Targets {
+ mbt, err := newMfgBuildTarget(dt, bsp.FlashMap)
+ if err != nil {
+ return mb, err
+ }
+ mb.Targets = append(mb.Targets, mbt)
+ }
+
+ for _, dr := range dm.Raws {
+ mbr, err := newMfgBuildRaw(dr, bsp.FlashMap, basePkg.BasePath())
+ if err != nil {
+ return mb, err
+ }
+ mb.Raws = append(mb.Raws, mbr)
+ }
+
+ if dm.Meta != nil {
+ meta, err := newMfgBuildMeta(*dm.Meta, mb.Bsp.FlashMap)
+ if err != nil {
+ return mb, err
+ }
+ mb.Meta = &meta
+ }
+
+ if _, err := mb.calcDevice(); err != nil {
+ return mb, err
+ }
+
+ if err := mb.detectOverlaps(); err != nil {
+ return mb, err
+ }
+
+ return mb, nil
+}
+
+// Creates a zeroed-out hash MMR TLV. The hash's original value must be zero
+// for the actual hash to be calculated later. After the actual value is
+// calculated, it replaces the zeros in the TLV.
+func newZeroHashTlv() mfg.MetaTlv {
+ return mfg.MetaTlv{
+ Header: mfg.MetaTlvHeader{
+ Type: mfg.META_TLV_TYPE_HASH,
+ Size: mfg.META_TLV_HASH_SZ,
+ },
+ Data: make([]byte, mfg.META_HASH_SZ),
+ }
+}
+
+// Creates a flash area MMR TLV.
+func newFlashAreaTlv(area flash.FlashArea) (mfg.MetaTlv, error) {
+ tlv := mfg.MetaTlv{
+ Header: mfg.MetaTlvHeader{
+ Type: mfg.META_TLV_TYPE_FLASH_AREA,
+ Size: mfg.META_TLV_FLASH_AREA_SZ,
+ },
+ }
+
+ body := mfg.MetaTlvBodyFlashArea{
+ Area: uint8(area.Id),
+ Device: uint8(area.Device),
+ Offset: uint32(area.Offset),
+ Size: uint32(area.Size),
+ }
+
+ b := &bytes.Buffer{}
+ if err := binary.Write(b, binary.LittleEndian, body); err != nil {
+ return tlv, util.ChildNewtError(err)
+ }
+
+ tlv.Data = b.Bytes()
+
+ return tlv, nil
+}
+
+// Creates an MMR ref TLV.
+func newMmrRefTlv(area flash.FlashArea) (mfg.MetaTlv, error) {
+ tlv := mfg.MetaTlv{
+ Header: mfg.MetaTlvHeader{
+ Type: mfg.META_TLV_TYPE_MMR_REF,
+ Size: mfg.META_TLV_MMR_REF_SZ,
+ },
+ }
+
+ body := mfg.MetaTlvBodyMmrRef{
+ Area: uint8(area.Id),
+ }
+
+ b := &bytes.Buffer{}
+ if err := binary.Write(b, binary.LittleEndian, body); err != nil {
+ return tlv, util.ChildNewtError(err)
+ }
+
+ tlv.Data = b.Bytes()
+
+ return tlv, nil
+}
+
+// Builds a manufacturing meta region.
+func (mb *MfgBuilder) buildMeta() (mfg.Meta, error) {
+ meta := mfg.Meta{
+ Footer: mfg.MetaFooter{
+ Size: 0, // Filled in later.
+ Version: mfg.META_VERSION,
+ Pad8: 0xff,
+ Magic: mfg.META_MAGIC,
+ },
+ }
+
+ // Hash TLV.
+ if mb.Meta.Hash {
+ meta.Tlvs = append(meta.Tlvs, newZeroHashTlv())
+ }
+
+ // Flash map TLVs.
+ if mb.Meta.FlashMap {
+ for _, area := range mb.Bsp.FlashMap.SortedAreas() {
+ tlv, err := newFlashAreaTlv(area)
+ if err != nil {
+ return meta, err
+ }
+
+ meta.Tlvs = append(meta.Tlvs, tlv)
+ }
+ }
+
+ // MMR ref TLVs.
+ for _, mmr := range mb.Meta.Mmrs {
+ tlv, err := newMmrRefTlv(mmr.Area)
+ if err != nil {
+ return meta, err
+ }
+
+ meta.Tlvs = append(meta.Tlvs, tlv)
+ }
+
+ // Fill in region size in footer now that we know the value.
+ meta.Footer.Size = uint16(meta.Size())
+
+ return meta, nil
+}
+
+// Builds a manufacturing image.
+func (mb *MfgBuilder) Build() (mfg.Mfg, error) {
+ parts, err := mb.parts()
+ if err != nil {
+ return mfg.Mfg{}, err
+ }
+
+ bin, err := PartsBytes(parts)
+ if err != nil {
+ return mfg.Mfg{}, err
+ }
+
+ var metaOff int
+ var metap *mfg.Meta
+ if mb.Meta != nil {
+ meta, err := mb.buildMeta()
+ if err != nil {
+ return mfg.Mfg{}, err
+ }
+ metap = &meta
+ metaOff = mb.Meta.Area.Offset + mb.Meta.Area.Size - meta.Size()
+ }
+
+ return mfg.Mfg{
+ Bin: bin,
+ Meta: metap,
+ MetaOff: metaOff,
+ }, nil
+}
diff --git a/newt/mfg/create.go b/newt/mfg/create.go
deleted file mode 100644
index 4930329..0000000
--- a/newt/mfg/create.go
+++ /dev/null
@@ -1,535 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package mfg
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "sort"
- "time"
-
- "mynewt.apache.org/newt/artifact/flash"
- "mynewt.apache.org/newt/newt/builder"
- "mynewt.apache.org/newt/newt/pkg"
- "mynewt.apache.org/newt/newt/target"
- "mynewt.apache.org/newt/util"
-)
-
-type mfgManifest struct {
- BuildTime string `json:"build_time"`
- MfgHash string `json:"mfg_hash"`
- Version string `json:"version"`
- MetaSection int `json:"meta_section"`
- MetaOffset int `json:"meta_offset"`
-}
-
-type mfgSection struct {
- offset int
- blob []byte
-}
-
-type createState struct {
- // {0:[section0], 1:[section1], ...}
- dsMap map[int]mfgSection
- metaOffset int
- hashOffset int
- hash []byte
-}
-
-func insertPartIntoBlob(section mfgSection, part mfgPart) {
- partEnd := part.offset + len(part.data)
-
- if len(section.blob) < partEnd {
- panic("internal error; mfg blob too small")
- }
-
- copy(section.blob[part.offset:partEnd], part.data)
-}
-
-func (mi *MfgImage) partFromImage(
- imgPath string, flashAreaName string) (mfgPart, error) {
-
- part := mfgPart{
- // Boot loader and images always go in device 0.
- device: 0,
- }
-
- area, ok := mi.bsp.FlashMap.Areas[flashAreaName]
- if !ok {
- return part, util.FmtNewtError(
- "Image at \"%s\" requires undefined flash area \"%s\"",
- imgPath, flashAreaName)
- }
-
- part.name = fmt.Sprintf("%s (%s)", flashAreaName, filepath.Base(imgPath))
- part.offset = area.Offset
-
- var err error
-
- part.data, err = ioutil.ReadFile(imgPath)
- if err != nil {
- return part, util.ChildNewtError(err)
- }
-
- overflow := len(part.data) - area.Size
- if overflow > 0 {
- return part, util.FmtNewtError(
- "Image \"%s\" is too large to fit in flash area \"%s\"; "+
- "image-size=%d flash-area-size=%d overflow=%d",
- imgPath, flashAreaName, len(part.data), area.Size, overflow)
- }
-
- // If an image slot is used, the entire flash area is unwritable. This
- // restriction comes from the boot loader's need to write status at the end
- // of an area. Pad out part with unwriten flash (0xff). This probably
- // isn't terribly efficient...
- for i := 0; i < -overflow; i++ {
- part.data = append(part.data, 0xff)
- }
-
- return part, nil
-}
-
-func partFromRawEntry(entry MfgRawEntry, entryIdx int) mfgPart {
- return mfgPart{
- name: fmt.Sprintf("entry-%d (%s)", entryIdx, entry.filename),
- offset: entry.offset,
- data: entry.data,
- }
-}
-
-func (mi *MfgImage) targetParts() ([]mfgPart, error) {
- parts := []mfgPart{}
-
- bootPath := mi.dstBootBinPath()
- if bootPath != "" {
- bootPart, err := mi.partFromImage(
- bootPath, flash.FLASH_AREA_NAME_BOOTLOADER)
- if err != nil {
- return nil, err
- }
-
- parts = append(parts, bootPart)
- }
-
- for i := 0; i < 2; i++ {
- imgPath := mi.dstImgPath(i)
- if imgPath != "" {
- areaName, err := areaNameFromImgIdx(i)
- if err != nil {
- return nil, err
- }
-
- part, err := mi.partFromImage(imgPath, areaName)
- if err != nil {
- return nil, err
- }
- parts = append(parts, part)
- }
- }
-
- return parts, nil
-}
-
-func sectionSize(parts []mfgPart) (int, int) {
- greatest := 0
- lowest := 0
- if len(parts) > 0 {
- lowest = parts[0].offset
- }
- for _, part := range parts {
- lowest = util.IntMin(lowest, part.offset)
- }
- for _, part := range parts {
- end := part.offset + len(part.data)
- greatest = util.IntMax(greatest, end)
- }
-
- return lowest, greatest
-}
-
-func sectionFromParts(parts []mfgPart) mfgSection {
- offset, sectionSize := sectionSize(parts)
- blob := make([]byte, sectionSize)
-
- section := mfgSection{
- offset: offset,
- blob: blob,
- }
-
- // Initialize section 0's data as unwritten flash (0xff).
- for i, _ := range blob {
- blob[i] = 0xff
- }
-
- for _, part := range parts {
- insertPartIntoBlob(section, part)
- }
-
- return section
-}
-
-func (mi *MfgImage) devicePartMap() (map[int][]mfgPart, error) {
- dpMap := map[int][]mfgPart{}
-
- // Create parts from the raw entries.
- for i, entry := range mi.rawEntries {
- part := partFromRawEntry(entry, i)
- dpMap[entry.device] = append(dpMap[entry.device], part)
- }
-
- // Insert the boot loader and image parts into section 0.
- targetParts, err := mi.targetParts()
- if err != nil {
- return nil, err
- }
- dpMap[0] = append(dpMap[0], targetParts...)
-
- // Sort each part slice by offset.
- for device, _ := range dpMap {
- sortParts(dpMap[device])
- }
-
- return dpMap, nil
-}
-
-func (mi *MfgImage) deviceSectionMap() (map[int]mfgSection, error) {
- dpMap, err := mi.devicePartMap()
- if err != nil {
- return nil, err
- }
-
- // Convert each part slice into a section.
- dsMap := map[int]mfgSection{}
- for device, parts := range dpMap {
- dsMap[device] = sectionFromParts(parts)
- }
-
- return dsMap, nil
-}
-
-func (mi *MfgImage) createSections() (createState, error) {
- cs := createState{}
-
- var err error
-
- if err := mi.detectOverlaps(); err != nil {
- return cs, err
- }
-
- cs.dsMap, err = mi.deviceSectionMap()
- if err != nil {
- return cs, err
- }
-
- if len(cs.dsMap) < 1 {
- panic("Invalid state; no section 0")
- }
-
- cs.metaOffset, cs.hashOffset, err = insertMeta(cs.dsMap[0].blob,
- mi.bsp.FlashMap)
- if err != nil {
- return cs, err
- }
-
- // Calculate manufacturing hash.
- devices := make([]int, 0, len(cs.dsMap))
- for device, _ := range cs.dsMap {
- devices = append(devices, device)
- }
- sort.Ints(devices)
-
- sections := make([][]byte, len(devices))
- for i, device := range devices {
- sections[i] = cs.dsMap[device].blob
- }
- cs.hash = calcMetaHash(sections)
- copy(cs.dsMap[0].blob[cs.hashOffset:cs.hashOffset+META_HASH_SZ], cs.hash)
-
- return cs, nil
-}
-
-func areaNameFromImgIdx(imgIdx int) (string, error) {
- switch imgIdx {
- case 0:
- return flash.FLASH_AREA_NAME_IMAGE_0, nil
- case 1:
- return flash.FLASH_AREA_NAME_IMAGE_1, nil
- default:
- return "", util.FmtNewtError("invalid image index: %d", imgIdx)
- }
-}
-
-func bootLoaderFromPaths(t *target.Target) []string {
- return []string{
- /* boot.elf */
- builder.AppElfPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),
-
- /* boot.elf.bin */
- builder.AppBinPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),
-
- /* manifest.json */
- builder.ManifestPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),
- }
-}
-
-func loaderFromPaths(t *target.Target) []string {
- if t.LoaderName == "" {
- return nil
- }
-
- return []string{
- /* <loader>.elf */
- builder.AppElfPath(t.Name(), builder.BUILD_NAME_LOADER,
- t.Loader().Name()),
-
- /* <app>.img */
- builder.AppImgPath(t.Name(), builder.BUILD_NAME_LOADER,
- t.Loader().Name()),
- }
-}
-
-func appFromPaths(t *target.Target) []string {
- return []string{
- /* <app>.elf */
- builder.AppElfPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),
-
- /* <app>.img */
- builder.AppImgPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),
-
- /* manifest.json */
- builder.ManifestPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()),
- }
-}
-
-func imageFromPaths(t *target.Target) []string {
- paths := loaderFromPaths(t)
- paths = append(paths, appFromPaths(t)...)
- return paths
-}
-
-func (mi *MfgImage) copyBinFile(srcPath string, dstDir string) error {
- dstPath := dstDir + "/" + filepath.Base(srcPath)
-
- util.StatusMessage(util.VERBOSITY_VERBOSE, "copying file %s --> %s\n",
- srcPath, dstPath)
-
- if err := util.CopyFile(srcPath, dstPath); err != nil {
- return err
- }
-
- return nil
-}
-
-func (mi *MfgImage) copyBinFiles() error {
- dstPath := MfgBinDir(mi.basePkg.Name())
- if err := os.MkdirAll(filepath.Dir(dstPath), 0755); err != nil {
- return util.ChildNewtError(err)
- }
-
- bootPaths := bootLoaderFromPaths(mi.boot)
- for _, path := range bootPaths {
- dstDir := MfgBootDir(mi.basePkg.Name())
- if err := mi.copyBinFile(path, dstDir); err != nil {
- return err
- }
- }
-
- for i, imgTarget := range mi.images {
- imgPaths := imageFromPaths(imgTarget)
- dstDir := MfgImageBinDir(mi.basePkg.Name(), i)
- for _, path := range imgPaths {
- if err := mi.copyBinFile(path, dstDir); err != nil {
- return err
- }
- }
- }
-
- return nil
-}
-
-func (mi *MfgImage) dstBootBinPath() string {
- if mi.boot == nil {
- return ""
- }
-
- return fmt.Sprintf("%s/%s.elf.bin",
- MfgBootDir(mi.basePkg.Name()),
- pkg.ShortName(mi.boot.App()))
-}
-
-func (mi *MfgImage) dstImgPath(slotIdx int) string {
- var pack *pkg.LocalPackage
- var imgIdx int
-
- if len(mi.images) >= 1 {
- switch slotIdx {
- case 0:
- if mi.images[0].LoaderName != "" {
- pack = mi.images[0].Loader()
- } else {
- pack = mi.images[0].App()
- }
- imgIdx = 0
-
- case 1:
- if mi.images[0].LoaderName != "" {
- pack = mi.images[0].App()
- imgIdx = 0
- } else {
- if len(mi.images) >= 2 {
- pack = mi.images[1].App()
- }
- imgIdx = 1
- }
-
- default:
- panic(fmt.Sprintf("invalid image index: %d", imgIdx))
- }
- }
-
- if pack == nil {
- return ""
- }
-
- return fmt.Sprintf("%s/%s.img",
- MfgImageBinDir(mi.basePkg.Name(), imgIdx), pkg.ShortName(pack))
-}
-
-// Returns a slice containing the path of each file required to build the
-// manufacturing image.
-func (mi *MfgImage) FromPaths() []string {
- paths := []string{}
-
- if mi.boot != nil {
- paths = append(paths, bootLoaderFromPaths(mi.boot)...)
- }
- if len(mi.images) >= 1 {
- paths = append(paths, imageFromPaths(mi.images[0])...)
- }
- if len(mi.images) >= 2 {
- paths = append(paths, imageFromPaths(mi.images[1])...)
- }
-
- for _, raw := range mi.rawEntries {
- paths = append(paths, raw.filename)
- }
-
- return paths
-}
-
-func (mi *MfgImage) build() (createState, error) {
- if err := mi.copyBinFiles(); err != nil {
- return createState{}, err
- }
-
- cs, err := mi.createSections()
- if err != nil {
- return cs, err
- }
-
- return cs, nil
-}
-
-func (mi *MfgImage) createManifest(cs createState) ([]byte, error) {
- manifest := mfgManifest{
- BuildTime: time.Now().Format(time.RFC3339),
- Version: mi.version.String(),
- MfgHash: fmt.Sprintf("%x", cs.hash),
- MetaSection: 0,
- MetaOffset: cs.metaOffset,
- }
- buffer, err := json.MarshalIndent(manifest, "", " ")
- if err != nil {
- return nil, util.FmtNewtError("Failed to encode mfg manifest: %s",
- err.Error())
- }
-
- return buffer, nil
-}
-
-func appendNonEmptyStr(dst []string, src string) []string {
- if src != "" {
- dst = append(dst, src)
- }
-
- return dst
-}
-
-func (mi *MfgImage) ToPaths() []string {
- paths := []string{}
-
- paths = appendNonEmptyStr(paths, mi.BootBinPath())
- paths = appendNonEmptyStr(paths, mi.BootElfPath())
- paths = appendNonEmptyStr(paths, mi.BootManifestPath())
-
- for i := 0; i < len(mi.images); i++ {
- paths = appendNonEmptyStr(paths, mi.LoaderImgPath(i))
- paths = appendNonEmptyStr(paths, mi.LoaderElfPath(i))
- paths = appendNonEmptyStr(paths, mi.AppImgPath(i))
- paths = appendNonEmptyStr(paths, mi.AppElfPath(i))
- paths = appendNonEmptyStr(paths, mi.ImageManifestPath(i))
- }
-
- paths = append(paths, mi.SectionBinPaths()...)
- paths = append(paths, mi.SectionHexPaths()...)
- paths = append(paths, mi.ManifestPath())
-
- return paths
-}
-
-// @return [paths-of-artifacts], error
-func (mi *MfgImage) CreateMfgImage() ([]string, error) {
- cs, err := mi.build()
- if err != nil {
- return nil, err
- }
-
- sectionDir := MfgSectionBinDir(mi.basePkg.Name())
- if err := os.MkdirAll(sectionDir, 0755); err != nil {
- return nil, util.ChildNewtError(err)
- }
-
- for device, section := range cs.dsMap {
- sectionPath := MfgSectionBinPath(mi.basePkg.Name(), device)
- err := ioutil.WriteFile(sectionPath, section.blob[section.offset:], 0644)
- if err != nil {
- return nil, util.ChildNewtError(err)
- }
- hexPath := MfgSectionHexPath(mi.basePkg.Name(), device)
- mi.compiler.ConvertBinToHex(sectionPath, hexPath, section.offset)
- }
-
- manifest, err := mi.createManifest(cs)
- if err != nil {
- return nil, err
- }
-
- manifestPath := mi.ManifestPath()
- if err := ioutil.WriteFile(manifestPath, manifest, 0644); err != nil {
- return nil, util.FmtNewtError("Failed to write mfg manifest file: %s",
- err.Error())
- }
-
- return mi.ToPaths(), nil
-}
diff --git a/newt/mfg/decode.go b/newt/mfg/decode.go
new file mode 100644
index 0000000..802e88d
--- /dev/null
+++ b/newt/mfg/decode.go
@@ -0,0 +1,310 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// This file contains functionality for loading mfg definitions from `mfg.yml`
+// files.
+
+package mfg
+
+import (
+ "github.com/spf13/cast"
+
+ "mynewt.apache.org/newt/newt/ycfg"
+ "mynewt.apache.org/newt/util"
+)
+
+// Indicates that an element is located at the end of a flash area.
+const OFFSET_END = -1
+
+type DecodedTarget struct {
+ Name string
+ Area string
+ Offset int
+}
+
+type DecodedRaw struct {
+ Filename string
+ Area string
+ Offset int
+}
+
+type DecodedMmrRef struct {
+ Area string
+}
+
+type DecodedMeta struct {
+ Area string
+ Hash bool
+ FlashMap bool
+ Mmrs []DecodedMmrRef
+}
+
+type DecodedMfg struct {
+ Targets []DecodedTarget
+ Raws []DecodedRaw
+ Meta *DecodedMeta
+
+ // Only required if no targets present.
+ Bsp string
+}
+
+func decodeOffsetStr(offsetStr string) (int, error) {
+ if offsetStr == "end" {
+ return OFFSET_END, nil
+ }
+
+ offsetInt, err := cast.ToIntE(offsetStr)
+ if err != nil {
+ return 0, util.FmtNewtError("invalid offset value: \"%s\"", offsetStr)
+ }
+
+ return offsetInt, nil
+}
+
+func decodeBool(kv map[string]interface{}, key string) (*bool, error) {
+ var bp *bool
+
+ val := kv[key]
+ if val != nil {
+ b, err := cast.ToBoolE(val)
+ if err != nil {
+ return nil, util.FmtNewtError(
+ "invalid `%s` value \"%v\"; "+
+ "value must be either \"true\" or \"false\"", key, val)
+ }
+
+ bp = &b
+ }
+
+ return bp, nil
+}
+
+func decodeBoolDflt(kv map[string]interface{}, key string,
+ dflt bool) (bool, error) {
+
+ bp, err := decodeBool(kv, key)
+ if err != nil {
+ return false, err
+ }
+
+ if bp == nil {
+ return dflt, nil
+ } else {
+ return *bp, nil
+ }
+}
+
+func decodeTarget(yamlTarget interface{}) (DecodedTarget, error) {
+ dt := DecodedTarget{}
+
+ kv, err := cast.ToStringMapE(yamlTarget)
+ if err != nil {
+ return dt, util.FmtNewtError(
+ "mfg contains invalid `mfg.targets` map: %s", err.Error())
+ }
+
+ nameVal := kv["name"]
+ if nameVal == nil {
+ return dt, util.FmtNewtError(
+ "mfg target entry missing required field \"name\"")
+ }
+ dt.Name = cast.ToString(nameVal)
+
+ areaVal := kv["area"]
+ if areaVal == nil {
+ return dt, util.FmtNewtError(
+ "target entry \"%s\" missing required field \"area\"", dt.Name)
+ }
+ dt.Area = cast.ToString(areaVal)
+
+ offsetVal := kv["offset"]
+ if offsetVal == nil {
+ return dt, util.FmtNewtError(
+ "target entry \"%s\" missing required field \"offset\"", dt.Name)
+ }
+ offsetStr := cast.ToString(offsetVal)
+ offsetInt, err := decodeOffsetStr(offsetStr)
+ if err != nil {
+ return dt, util.FmtNewtError(
+ "in target entry \"%s\": %s", dt.Name, err.Error())
+ }
+ dt.Offset = offsetInt
+
+ return dt, nil
+}
+
+func decodeRaw(yamlRaw interface{}, entryIdx int) (DecodedRaw, error) {
+ dr := DecodedRaw{}
+
+ kv, err := cast.ToStringMapE(yamlRaw)
+ if err != nil {
+ return dr, util.FmtNewtError(
+ "mfg contains invalid `mfg.raw` map: %s", err.Error())
+ }
+
+ areaVal := kv["area"]
+ if areaVal == nil {
+ return dr, util.FmtNewtError(
+ "raw entry missing required field \"area\"")
+ }
+ dr.Area = cast.ToString(areaVal)
+
+ offsetVal := kv["offset"]
+ if offsetVal == nil {
+ return dr, util.FmtNewtError(
+ "mfg raw entry missing required field \"offset\"")
+ }
+ offsetStr := cast.ToString(offsetVal)
+ offsetInt, err := decodeOffsetStr(offsetStr)
+ if err != nil {
+ return dr, util.FmtNewtError(
+ "in raw entry %d: %s", entryIdx, err.Error())
+ }
+ dr.Offset = offsetInt
+
+ filenameVal := kv["name"]
+ if filenameVal == nil {
+ return dr, util.FmtNewtError(
+ "mfg raw entry missing required field \"filename\"")
+ }
+ dr.Filename = cast.ToString(filenameVal)
+
+ return dr, nil
+}
+
+func decodeMmr(yamlMmr interface{}) (DecodedMmrRef, error) {
+ dm := DecodedMmrRef{}
+
+ kv, err := cast.ToStringMapE(yamlMmr)
+ if err != nil {
+ return dm, util.FmtNewtError(
+ "mfg meta contains invalid `mmrs` sequence: %s", err.Error())
+ }
+
+ areaVal := kv["area"]
+ if areaVal == nil {
+ return dm, util.FmtNewtError(
+ "mmr entry missing required field \"area\"")
+ }
+ dm.Area = cast.ToString(areaVal)
+
+ return dm, nil
+}
+
+func decodeMmrs(yamlMmrs interface{}) ([]DecodedMmrRef, error) {
+ yamlSlice, err := cast.ToSliceE(yamlMmrs)
+ if err != nil {
+ return nil, util.FmtNewtError(
+ "mfg meta contains invalid `mmrs` sequence: %s", err.Error())
+ }
+
+ mmrs := []DecodedMmrRef{}
+ for _, yamlMmr := range yamlSlice {
+ mmr, err := decodeMmr(yamlMmr)
+ if err != nil {
+ return nil, err
+ }
+ mmrs = append(mmrs, mmr)
+ }
+
+ return mmrs, nil
+}
+
+func decodeMeta(
+ kv map[string]interface{}) (DecodedMeta, error) {
+
+ dm := DecodedMeta{}
+
+ areaVal := kv["area"]
+ if areaVal == nil {
+ return dm, util.FmtNewtError(
+ "meta map missing required field \"area\"")
+ }
+ dm.Area = cast.ToString(areaVal)
+
+ hash, err := decodeBoolDflt(kv, "hash", false)
+ if err != nil {
+ return dm, err
+ }
+ dm.Hash = hash
+
+ fm, err := decodeBoolDflt(kv, "flash_map", false)
+ if err != nil {
+ return dm, err
+ }
+ dm.FlashMap = fm
+
+ yamlMmrs := kv["mmrs"]
+ if yamlMmrs != nil {
+ mmrs, err := decodeMmrs(yamlMmrs)
+ if err != nil {
+ return dm, err
+ }
+ dm.Mmrs = mmrs
+ }
+
+ return dm, nil
+}
+
+func decodeMfg(yc ycfg.YCfg) (DecodedMfg, error) {
+ dm := DecodedMfg{}
+
+ yamlTargets := yc.GetValSlice("mfg.targets", nil)
+ if yamlTargets != nil {
+ for _, yamlTarget := range yamlTargets {
+ t, err := decodeTarget(yamlTarget)
+ if err != nil {
+ return dm, err
+ }
+
+ dm.Targets = append(dm.Targets, t)
+ }
+ }
+
+ dm.Bsp = yc.GetValString("mfg.bsp", nil)
+
+ if len(dm.Targets) == 0 && dm.Bsp == "" {
+ return dm, util.FmtNewtError(
+ "\"mfg.bsp\" field required for mfg images without any targets")
+ }
+
+ itf := yc.GetValSlice("mfg.raw", nil)
+ slice := cast.ToSlice(itf)
+ if slice != nil {
+ for i, yamlRaw := range slice {
+ raw, err := decodeRaw(yamlRaw, i)
+ if err != nil {
+ return dm, err
+ }
+
+ dm.Raws = append(dm.Raws, raw)
+ }
+ }
+
+ yamlMeta := yc.GetValStringMap("mfg.meta", nil)
+ if yamlMeta != nil {
+ meta, err := decodeMeta(yamlMeta)
+ if err != nil {
+ return dm, err
+ }
+ dm.Meta = &meta
+ }
+
+ return dm, nil
+}
diff --git a/newt/mfg/emit.go b/newt/mfg/emit.go
new file mode 100644
index 0000000..b977fe7
--- /dev/null
+++ b/newt/mfg/emit.go
@@ -0,0 +1,342 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package mfg
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "time"
+
+ "mynewt.apache.org/newt/artifact/flash"
+ "mynewt.apache.org/newt/artifact/image"
+ "mynewt.apache.org/newt/artifact/manifest"
+ "mynewt.apache.org/newt/artifact/mfg"
+ "mynewt.apache.org/newt/newt/builder"
+ "mynewt.apache.org/newt/newt/flashmap"
+ "mynewt.apache.org/newt/newt/target"
+ "mynewt.apache.org/newt/util"
+)
+
+// Current manufacturing image binary format version.
+const MANIFEST_FORMAT = 2
+
+// Represents a file copy operation.
+type CpEntry struct {
+ From string
+ To string
+}
+
+type MfgEmitTarget struct {
+ Name string
+ Offset int
+ IsBoot bool
+ BinPath string
+ ElfPath string
+ ManifestPath string
+}
+
+type MfgEmitRaw struct {
+ Filename string
+ Offset int
+}
+
+type MfgEmitMetaMmr struct {
+ Area flash.FlashArea
+}
+
+type MfgEmitMeta struct {
+ Offset int
+ Hash bool
+ FlashMap bool
+ Mmrs []MfgEmitMetaMmr
+}
+
+type MfgEmitter struct {
+ Name string
+ Ver image.ImageVersion
+ Targets []MfgEmitTarget
+ Raws []MfgEmitRaw
+ Meta *MfgEmitMeta
+
+ Mfg mfg.Mfg
+ Device int
+ FlashMap flashmap.FlashMap
+}
+
+// Calculates the source path of a target's binary. Boot loader targets use
+// `.bin` files; image targets use `.img`.
+func targetSrcBinPath(t *target.Target, isBoot bool) string {
+ if isBoot {
+ return builder.AppBinPath(t.Name(), builder.BUILD_NAME_APP,
+ t.App().Name())
+ } else {
+ return builder.AppImgPath(t.Name(), builder.BUILD_NAME_APP,
+ t.App().Name())
+ }
+}
+
+// Calculates the source path of a target's `.elf` file.
+func targetSrcElfPath(t *target.Target) string {
+ return builder.AppElfPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name())
+}
+
+// Calculates the source path of a target's manifest file.
+func targetSrcManifestPath(t *target.Target) string {
+ return builder.ManifestPath(t.Name(), builder.BUILD_NAME_APP,
+ t.App().Name())
+}
+
+func newMfgEmitTarget(bt MfgBuildTarget) (MfgEmitTarget, error) {
+ return MfgEmitTarget{
+ Name: bt.Target.FullName(),
+ Offset: bt.Area.Offset + bt.Offset,
+ IsBoot: bt.IsBoot,
+ BinPath: targetSrcBinPath(bt.Target, bt.IsBoot),
+ ElfPath: targetSrcElfPath(bt.Target),
+ ManifestPath: builder.ManifestPath(bt.Target.Name(),
+ builder.BUILD_NAME_APP, bt.Target.App().Name()),
+ }, nil
+}
+
+func newMfgEmitRaw(br MfgBuildRaw) MfgEmitRaw {
+ return MfgEmitRaw{
+ Filename: br.Filename,
+ Offset: br.Area.Offset + br.Offset,
+ }
+}
+
+func newMfgEmitMeta(bm MfgBuildMeta, metaOff int) MfgEmitMeta {
+ mmrs := []MfgEmitMetaMmr{}
+ for _, bmmr := range bm.Mmrs {
+ mmr := MfgEmitMetaMmr{
+ Area: bmmr.Area,
+ }
+ mmrs = append(mmrs, mmr)
+ }
+
+ return MfgEmitMeta{
+ Offset: bm.Area.Offset + metaOff,
+ Hash: bm.Hash,
+ FlashMap: bm.FlashMap,
+ Mmrs: mmrs,
+ }
+}
+
+// NewMfgEmitter creates an mfg emitter from an mfg builder.
+func NewMfgEmitter(mb MfgBuilder, name string, ver image.ImageVersion,
+ device int) (MfgEmitter, error) {
+
+ me := MfgEmitter{
+ Name: name,
+ Ver: ver,
+ Device: device,
+ FlashMap: mb.Bsp.FlashMap,
+ }
+
+ m, err := mb.Build()
+ if err != nil {
+ return me, err
+ }
+ me.Mfg = m
+
+ for _, bt := range mb.Targets {
+ et, err := newMfgEmitTarget(bt)
+ if err != nil {
+ return me, err
+ }
+
+ me.Targets = append(me.Targets, et)
+ }
+
+ for _, br := range mb.Raws {
+ et := newMfgEmitRaw(br)
+ me.Raws = append(me.Raws, et)
+ }
+
+ if mb.Meta != nil {
+ mm := newMfgEmitMeta(*mb.Meta, me.Mfg.MetaOff)
+ me.Meta = &mm
+ }
+
+ return me, nil
+}
+
+// Calculates the necessary file copy operations for emitting an mfg image.
+func (me *MfgEmitter) calcCpEntries() []CpEntry {
+ entries := []CpEntry{}
+ for i, mt := range me.Targets {
+ var binTo string
+ if mt.IsBoot {
+ binTo = MfgTargetBinPath(i)
+ } else {
+ binTo = MfgTargetImgPath(i)
+ }
+
+ entry := CpEntry{
+ From: mt.BinPath,
+ To: MfgBinDir(me.Name) + "/" + binTo,
+ }
+ entries = append(entries, entry)
+
+ entry = CpEntry{
+ From: mt.ElfPath,
+ To: MfgBinDir(me.Name) + "/" +
+ MfgTargetElfPath(i),
+ }
+ entries = append(entries, entry)
+
+ entry = CpEntry{
+ From: mt.ManifestPath,
+ To: MfgBinDir(me.Name) + "/" +
+ MfgTargetManifestPath(i),
+ }
+ entries = append(entries, entry)
+ }
+
+ return entries
+}
+
+func copyBinFiles(entries []CpEntry) error {
+ for _, entry := range entries {
+ if err := os.MkdirAll(filepath.Dir(entry.To), 0755); err != nil {
+ return util.ChildNewtError(err)
+ }
+
+ util.StatusMessage(util.VERBOSITY_VERBOSE, "copying file %s --> %s\n",
+ entry.From, entry.To)
+
+ if err := util.CopyFile(entry.From, entry.To); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// emitManifest generates an mfg manifest.
+func (me *MfgEmitter) emitManifest() ([]byte, error) {
+ hashBytes := me.Mfg.Meta.Hash()
+ if hashBytes == nil {
+ // No hash TLV; calculate hash manually.
+ bin, err := me.Mfg.Bytes(0xff)
+ if err != nil {
+ return nil, err
+ }
+ hashBytes = mfg.CalcHash(bin)
+ }
+ hashStr := fmt.Sprintf("%x", hashBytes)
+
+ mm := manifest.MfgManifest{
+ Name: me.Name,
+ BuildTime: time.Now().Format(time.RFC3339),
+ Format: MANIFEST_FORMAT,
+ MfgHash: hashStr,
+ Version: me.Ver.String(),
+ Device: me.Device,
+ BinPath: mfg.MFG_IMG_FILENAME,
+ FlashAreas: me.FlashMap.SortedAreas(),
+ }
+
+ for i, t := range me.Targets {
+ mmt := manifest.MfgManifestTarget{
+ Name: t.Name,
+ ManifestPath: MfgTargetManifestPath(i),
+ Offset: t.Offset,
+ }
+
+ if t.IsBoot {
+ mmt.BinPath = MfgTargetBinPath(i)
+ } else {
+ mmt.ImagePath = MfgTargetImgPath(i)
+ }
+
+ mm.Targets = append(mm.Targets, mmt)
+ }
+
+ if me.Meta != nil {
+ mmm := manifest.MfgManifestMeta{
+ EndOffset: me.Mfg.MetaOff + int(me.Mfg.Meta.Footer.Size),
+ Size: int(me.Mfg.Meta.Footer.Size),
+ }
+
+ mmm.Hash = me.Meta.Hash
+ mmm.FlashMap = me.Meta.FlashMap
+
+ for _, mmr := range me.Meta.Mmrs {
+ mmm.Mmrs = append(mmm.Mmrs, manifest.MfgManifestMetaMmr{
+ Area: mmr.Area.Name,
+ Device: mmr.Area.Device,
+ EndOffset: mmr.Area.Offset + mmr.Area.Size,
+ })
+ }
+
+ mm.Meta = &mmm
+ }
+
+ return mm.MarshalJson()
+}
+
+// @return [source-paths], [dest-paths], error
+func (me *MfgEmitter) Emit() ([]string, []string, error) {
+ mbin, err := me.Mfg.Bytes(0xff)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ cpEntries := me.calcCpEntries()
+ if err := copyBinFiles(cpEntries); err != nil {
+ return nil, nil, err
+ }
+
+ // Write mfgimg.bin
+ binPath := MfgBinPath(me.Name)
+ if err := os.MkdirAll(filepath.Dir(binPath), 0755); err != nil {
+ return nil, nil, util.ChildNewtError(err)
+ }
+ if err := ioutil.WriteFile(binPath, mbin, 0644); err != nil {
+ return nil, nil, err
+ }
+
+ // Write manifest.
+ manifest, err := me.emitManifest()
+ if err != nil {
+ return nil, nil, err
+ }
+
+ manifestPath := MfgManifestPath(me.Name)
+ if err := ioutil.WriteFile(manifestPath, manifest, 0644); err != nil {
+ return nil, nil, util.FmtNewtError(
+ "Failed to write mfg manifest file: %s", err.Error())
+ }
+
+ srcPaths := []string{}
+ dstPaths := []string{
+ binPath,
+ manifestPath,
+ }
+ for _, entry := range cpEntries {
+ srcPaths = append(srcPaths, entry.From)
+ dstPaths = append(dstPaths, entry.To)
+ }
+
+ return srcPaths, dstPaths, nil
+}
diff --git a/newt/mfg/load.go b/newt/mfg/load.go
deleted file mode 100644
index e17635b..0000000
--- a/newt/mfg/load.go
+++ /dev/null
@@ -1,317 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package mfg
-
-import (
- "fmt"
- "io/ioutil"
- "sort"
- "strconv"
- "strings"
-
- "github.com/spf13/cast"
-
- "mynewt.apache.org/newt/newt/newtutil"
- "mynewt.apache.org/newt/newt/pkg"
- "mynewt.apache.org/newt/newt/project"
- "mynewt.apache.org/newt/newt/target"
- "mynewt.apache.org/newt/newt/toolchain"
- "mynewt.apache.org/newt/util"
-)
-
-const MFG_YAML_FILENAME string = "mfg.yml"
-
-type partSorter struct {
- parts []mfgPart
-}
-
-func (s partSorter) Len() int {
- return len(s.parts)
-}
-func (s partSorter) Swap(i, j int) {
- s.parts[i], s.parts[j] = s.parts[j], s.parts[i]
-}
-func (s partSorter) Less(i, j int) bool {
- return s.parts[i].offset < s.parts[j].offset
-}
-
-func sortParts(parts []mfgPart) []mfgPart {
- sorter := partSorter{
- parts: parts,
- }
-
- sort.Sort(sorter)
- return sorter.parts
-}
-
-func (mi *MfgImage) loadError(
- msg string, args ...interface{}) *util.NewtError {
-
- return util.FmtNewtError("Error in %s mfg: %s", mi.basePkg.Name(),
- fmt.Sprintf(msg, args...))
-
-}
-
-func (mi *MfgImage) loadTarget(targetName string) (
- *target.Target, error) {
-
- tgt := target.GetTargets()[targetName]
- if tgt == nil {
- return nil, mi.loadError("cannot resolve referenced target \"%s\"",
- targetName)
- }
-
- return tgt, nil
-}
-
-func (mi *MfgImage) loadRawEntry(
- entryIdx int, rawEntry map[string]string) (MfgRawEntry, error) {
-
- raw := MfgRawEntry{}
-
- var err error
-
- deviceStr := rawEntry["device"]
- if deviceStr == "" {
- return raw, mi.loadError(
- "raw entry %d missing required \"device\" field", entryIdx)
- }
-
- raw.device, err = util.AtoiNoOct(deviceStr)
- if err != nil {
- return raw, mi.loadError(
- "raw entry %d contains invalid offset: %s", entryIdx, deviceStr)
- }
-
- offsetStr := rawEntry["offset"]
- if offsetStr == "" {
- return raw, mi.loadError(
- "raw entry %d missing required \"offset\" field", entryIdx)
- }
-
- raw.offset, err = util.AtoiNoOct(offsetStr)
- if err != nil {
- return raw, mi.loadError(
- "raw entry %d contains invalid offset: %s", entryIdx, offsetStr)
- }
-
- raw.filename = rawEntry["file"]
- if raw.filename == "" {
- return raw, mi.loadError(
- "raw entry %d missing required \"file\" field", entryIdx)
- }
-
- if !strings.HasPrefix(raw.filename, "/") {
- raw.filename = mi.basePkg.BasePath() + "/" + raw.filename
- }
-
- raw.data, err = ioutil.ReadFile(raw.filename)
- if err != nil {
- return raw, mi.loadError(
- "error loading file for raw entry %d; filename=%s: %s",
- entryIdx, raw.filename, err.Error())
- }
-
- return raw, nil
-}
-
-func (mi *MfgImage) detectInvalidDevices() error {
- sectionIds := mi.sectionIds()
- deviceIds := mi.bsp.FlashMap.DeviceIds()
-
- deviceMap := map[int]struct{}{}
- for _, device := range deviceIds {
- deviceMap[device] = struct{}{}
- }
-
- invalidIds := []int{}
- for _, sectionId := range sectionIds {
- if _, ok := deviceMap[sectionId]; !ok {
- invalidIds = append(invalidIds, sectionId)
- }
- }
-
- if len(invalidIds) == 0 {
- return nil
- }
-
- listStr := ""
- for i, id := range invalidIds {
- if i != 0 {
- listStr += ", "
- }
- listStr += strconv.Itoa(id)
- }
-
- return util.FmtNewtError(
- "image specifies flash devices that are not present in the BSP's "+
- "flash map: %s", listStr)
-}
-
-func (mi *MfgImage) detectOverlaps() error {
- type overlap struct {
- part0 mfgPart
- part1 mfgPart
- }
-
- overlaps := []overlap{}
-
- dpMap, err := mi.devicePartMap()
- if err != nil {
- return err
- }
-
- // Iterate flash devices in order.
- devices := make([]int, 0, len(dpMap))
- for device, _ := range dpMap {
- devices = append(devices, device)
- }
- sort.Ints(devices)
-
- for _, device := range devices {
- parts := dpMap[device]
- for i, part0 := range parts[:len(parts)-1] {
- part0End := part0.offset + len(part0.data)
- for _, part1 := range parts[i+1:] {
- // Parts are sorted by offset, so only one comparison is
- // necessary to detect overlap.
- if part1.offset < part0End {
- overlaps = append(overlaps, overlap{
- part0: part0,
- part1: part1,
- })
- }
- }
- }
- }
-
- if len(overlaps) > 0 {
- str := "flash overlaps detected:"
- for _, overlap := range overlaps {
-
- part0End := overlap.part0.offset + len(overlap.part0.data)
- part1End := overlap.part1.offset + len(overlap.part1.data)
- str += fmt.Sprintf("\n * s%d [%s] (%d - %d) <=> [%s] (%d - %d)",
- overlap.part0.device,
- overlap.part0.name, overlap.part0.offset, part0End,
- overlap.part1.name, overlap.part1.offset, part1End)
- }
-
- return util.NewNewtError(str)
- }
-
- return nil
-}
-
-func Load(basePkg *pkg.LocalPackage) (*MfgImage, error) {
- v, err := newtutil.ReadConfig(basePkg.BasePath(),
- strings.TrimSuffix(MFG_YAML_FILENAME, ".yml"))
- if err != nil {
- return nil, err
- }
-
- mi := &MfgImage{
- basePkg: basePkg,
- }
-
- bootName := v.GetValString("mfg.bootloader", nil)
- if bootName == "" {
- return nil, mi.loadError("mfg.bootloader field required")
- }
- mi.boot, err = mi.loadTarget(bootName)
- if err != nil {
- return nil, err
- }
-
- imgNames := v.GetValStringSlice("mfg.images", nil)
- if imgNames != nil {
- for _, imgName := range imgNames {
- imgTarget, err := mi.loadTarget(imgName)
- if err != nil {
- return nil, err
- }
-
- mi.images = append(mi.images, imgTarget)
- }
- }
-
- if len(mi.images) > 2 {
- return nil, mi.loadError("too many images (%d); maximum is 2",
- len(mi.images))
- }
-
- itf := v.GetFirstVal("mfg.raw", nil)
- slice := cast.ToSlice(itf)
- if slice != nil {
- for i, entryItf := range slice {
- yamlEntry := cast.ToStringMapString(entryItf)
- entry, err := mi.loadRawEntry(i, yamlEntry)
- if err != nil {
- return nil, err
- }
-
- mi.rawEntries = append(mi.rawEntries, entry)
- }
- }
-
- proj := project.GetProject()
-
- bspLpkg, err := proj.ResolvePackage(mi.basePkg.Repo(),
- mi.boot.BspName)
- if err != nil {
- return nil, mi.loadError(
- "could not resolve boot loader BSP package: %s",
- mi.boot.BspName)
- }
- mi.bsp, err = pkg.NewBspPackage(bspLpkg)
- if err != nil {
- return nil, mi.loadError(err.Error())
- }
-
- compilerPkg, err := proj.ResolvePackage(mi.bsp.Repo(), mi.bsp.CompilerName)
- if err != nil {
- return nil, mi.loadError(err.Error())
- }
- mi.compiler, err = toolchain.NewCompiler(compilerPkg.BasePath(), "",
- target.DEFAULT_BUILD_PROFILE)
- if err != nil {
- return nil, mi.loadError(err.Error())
- }
-
- for _, imgTarget := range mi.images {
- if len(mi.images) > 1 && imgTarget.LoaderName != "" {
- return nil, mi.loadError("only one image allowed in "+
- "split image mode (%s is a split build)", imgTarget.Name())
- }
-
- if imgTarget.Bsp() != mi.bsp.LocalPackage {
- return nil, mi.loadError(
- "image target \"%s\" specified conflicting BSP; "+
- "boot loader uses %s, image uses %s",
- imgTarget.Name(), mi.bsp.Name(), imgTarget.BspName)
- }
- }
-
- if err := mi.detectInvalidDevices(); err != nil {
- return nil, err
- }
-
- return mi, nil
-}
diff --git a/newt/mfg/meta.go b/newt/mfg/meta.go
deleted file mode 100644
index 5e6e5ad..0000000
--- a/newt/mfg/meta.go
+++ /dev/null
@@ -1,248 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package mfg
-
-import (
- "bytes"
- "crypto/sha256"
- "encoding/binary"
-
- "mynewt.apache.org/newt/artifact/flash"
- "mynewt.apache.org/newt/newt/flashmap"
- "mynewt.apache.org/newt/util"
-)
-
-// The "manufacturing meta region" is located at the end of the boot loader
-// flash area. This region 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
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// |Version (0x01) | 0xff padding |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | TLV type | TLV size | TLV data ("TLV size" bytes) ~
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ~
-// ~ ~
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | TLV type | TLV size | TLV data ("TLV size" bytes) ~
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ~
-// ~ ~
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | Region size | 0xff padding |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | Magic (0x3bb2a269) |
-// +-+-+-+-+-+--+-+-+-+-end of boot loader area+-+-+-+-+-+-+-+-+-+-+
-//
-// The number of TLVs is variable; two are shown above for illustrative
-// 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.
-//
-// <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.
-
-const META_MAGIC = 0x3bb2a269
-const META_VERSION = 1
-const META_TLV_CODE_HASH = 0x01
-const META_TLV_CODE_FLASH_AREA = 0x02
-
-const META_HASH_SZ = 32
-const META_FOOTER_SZ = 8
-const META_TLV_HASH_SZ = META_HASH_SZ
-const META_TLV_FLASH_AREA_SZ = 12
-
-type metaHeader struct {
- version uint8 // 1
- pad8 uint8 // 0xff
- pad16 uint16 // 0xffff
-}
-
-type metaFooter struct {
- size uint16 // Includes header, TLVs, and footer.
- pad16 uint16 // 0xffff
- magic uint32 // META_MAGIC
-}
-
-type metaTlvHeader struct {
- typ uint8 // Indicates the type of data to follow.
- size uint8 // The number of bytes of data to follow.
-}
-
-type metaTlvFlashArea struct {
- header metaTlvHeader
- areaId uint8 // Unique value identifying this flash area.
- deviceId uint8 // Indicates host flash device (aka section number).
- pad16 uint16 // 0xffff
- offset uint32 // The byte offset within the flash device.
- size uint32 // Size, in bytes, of entire flash area.
-}
-
-type metaTlvHash struct {
- header metaTlvHeader
- hash [META_HASH_SZ]byte
-}
-
-func writeElem(elem interface{}, buf *bytes.Buffer) error {
- /* XXX: Assume target platform uses little endian. */
- if err := binary.Write(buf, binary.LittleEndian, elem); err != nil {
- return util.ChildNewtError(err)
- }
- return nil
-}
-
-func writeHeader(buf *bytes.Buffer) error {
- hdr := metaHeader{
- version: META_VERSION,
- pad8: 0xff,
- pad16: 0xffff,
- }
- return writeElem(hdr, buf)
-}
-
-func writeFooter(buf *bytes.Buffer) error {
- ftr := metaFooter{
- size: uint16(buf.Len() + META_FOOTER_SZ),
- pad16: 0xffff,
- magic: META_MAGIC,
- }
- return writeElem(ftr, buf)
-}
-
-func writeTlvHeader(typ uint8, size uint8, buf *bytes.Buffer) error {
- tlvHdr := metaTlvHeader{
- typ: typ,
- size: size,
- }
- return writeElem(tlvHdr, buf)
-}
-
-// Writes a single entry of the flash map TLV.
-func writeFlashMapEntry(area flash.FlashArea, buf *bytes.Buffer) error {
- tlv := metaTlvFlashArea{
- header: metaTlvHeader{
- typ: META_TLV_CODE_FLASH_AREA,
- size: META_TLV_FLASH_AREA_SZ,
- },
- areaId: uint8(area.Id),
- deviceId: uint8(area.Device),
- pad16: 0xffff,
- offset: uint32(area.Offset),
- size: uint32(area.Size),
- }
- return writeElem(tlv, buf)
-}
-
-// Writes a zeroed-out hash TLV. The hash's original value must be zero for
-// the actual hash to be calculated later. After the actual value is
-// calculated, it replaces the zeros in the TLV.
-func writeZeroHash(buf *bytes.Buffer) error {
- tlv := metaTlvHash{
- header: metaTlvHeader{
- typ: META_TLV_CODE_HASH,
- size: META_TLV_HASH_SZ,
- },
- hash: [META_HASH_SZ]byte{},
- }
- return writeElem(tlv, buf)
-}
-
-// @return meta-offset, hash-offset, error
-func insertMeta(section0Data []byte, flashMap flashmap.FlashMap) (
- int, int, error) {
-
- buf := &bytes.Buffer{}
-
- if err := writeHeader(buf); err != nil {
- return 0, 0, err
- }
-
- for _, area := range flashMap.SortedAreas() {
- if err := writeFlashMapEntry(area, buf); err != nil {
- return 0, 0, err
- }
- }
-
- if err := writeZeroHash(buf); err != nil {
- return 0, 0, err
- }
- hashSubOff := buf.Len() - META_HASH_SZ
-
- if err := writeFooter(buf); err != nil {
- return 0, 0, err
- }
-
- // The meta region gets placed at the very end of the boot loader slot.
- bootArea, ok := flashMap.Areas[flash.FLASH_AREA_NAME_BOOTLOADER]
- if !ok {
- return 0, 0,
- util.NewNewtError("Required boot loader flash area missing")
- }
-
- if bootArea.Size < buf.Len() {
- return 0, 0, util.FmtNewtError(
- "Boot loader flash area too small to accommodate meta region; "+
- "boot=%d meta=%d", bootArea.Size, buf.Len())
- }
-
- metaOff := bootArea.Offset + bootArea.Size - buf.Len()
- for i := metaOff; i < bootArea.Size; i++ {
- if section0Data[i] != 0xff {
- return 0, 0, util.FmtNewtError(
- "Boot loader extends into meta region; "+
- "meta region starts at offset %d", metaOff)
- }
- }
-
- // Copy the meta region into the manufacturing image. The meta hash is
- // still zeroed.
- copy(section0Data[metaOff:], buf.Bytes())
-
- return metaOff, metaOff + hashSubOff, nil
-}
-
-// Calculates the SHA256 hash, using the full manufacturing image as input.
-// Hash-calculation algorithm is as follows:
-// 1. Concatenate sections in ascending order of index.
-// 2. Zero out the 32 bytes that will contain the hash.
-// 3. Apply SHA256 to the result.
-//
-// This function assumes that the 32 bytes of hash data have already been
-// zeroed.
-func calcMetaHash(sections [][]byte) []byte {
- // Concatenate all sections.
- blob := []byte{}
- for _, section := range sections {
- blob = append(blob, section...)
- }
-
- // Calculate hash.
- hash := sha256.Sum256(blob)
-
- return hash[:]
-}
diff --git a/newt/mfg/mfg.go b/newt/mfg/mfg.go
deleted file mode 100644
index 10c6c8e..0000000
--- a/newt/mfg/mfg.go
+++ /dev/null
@@ -1,98 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package mfg
-
-import (
- "sort"
-
- "mynewt.apache.org/newt/artifact/image"
- "mynewt.apache.org/newt/newt/pkg"
- "mynewt.apache.org/newt/newt/target"
- "mynewt.apache.org/newt/newt/toolchain"
-)
-
-type MfgRawEntry struct {
- device int
- offset int
- filename string
- data []byte
-}
-
-// A chunk of data in the manufacturing image. Can be a firmware image or a
-// raw entry (contents of a data file).
-type mfgPart struct {
- device int
- offset int
- data []byte
- name string
-}
-
-type MfgImage struct {
- basePkg *pkg.LocalPackage
-
- bsp *pkg.BspPackage
- compiler *toolchain.Compiler
-
- boot *target.Target
- images []*target.Target
- rawEntries []MfgRawEntry
-
- version image.ImageVersion
-}
-
-func (mi *MfgImage) SetVersion(ver image.ImageVersion) {
- mi.version = ver
-}
-
-func (mi *MfgImage) imgApps(imageIdx int) (
- app *pkg.LocalPackage, loader *pkg.LocalPackage) {
-
- if imageIdx >= len(mi.images) {
- return
- }
-
- t := mi.images[imageIdx]
- app = t.App()
- loader = t.Loader()
- return
-}
-
-func (mi *MfgImage) sectionIds() []int {
- idMap := map[int]struct{}{}
-
- // The bootloader and images always go in section 0.
- idMap[0] = struct{}{}
-
- for _, entry := range mi.rawEntries {
- idMap[entry.device] = struct{}{}
- }
-
- ids := make([]int, 0, len(idMap))
- for id, _ := range idMap {
- ids = append(ids, id)
- }
- sort.Ints(ids)
-
- return ids
-}
-
-func (mi *MfgImage) NumImages() int {
- return len(mi.images)
-}
diff --git a/newt/mfg/misc.go b/newt/mfg/misc.go
new file mode 100644
index 0000000..73d911d
--- /dev/null
+++ b/newt/mfg/misc.go
@@ -0,0 +1,92 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package mfg
+
+import (
+ "strings"
+
+ "mynewt.apache.org/newt/artifact/image"
+ "mynewt.apache.org/newt/newt/builder"
+ "mynewt.apache.org/newt/newt/newtutil"
+ "mynewt.apache.org/newt/newt/pkg"
+)
+
+func loadDecodedMfg(basePath string) (DecodedMfg, error) {
+ yc, err := newtutil.ReadConfig(basePath,
+ strings.TrimSuffix(YAML_FILENAME, ".yml"))
+ if err != nil {
+ return DecodedMfg{}, err
+ }
+
+ dm, err := decodeMfg(yc)
+ if err != nil {
+ return DecodedMfg{}, err
+ }
+
+ return dm, nil
+}
+
+func LoadMfgEmitter(basePkg *pkg.LocalPackage,
+ ver image.ImageVersion) (MfgEmitter, error) {
+
+ dm, err := loadDecodedMfg(basePkg.BasePath())
+ if err != nil {
+ return MfgEmitter{}, err
+ }
+
+ mb, err := newMfgBuilder(basePkg, dm, ver)
+ if err != nil {
+ return MfgEmitter{}, err
+ }
+
+ device, err := mb.calcDevice()
+ if err != nil {
+ return MfgEmitter{}, err
+ }
+
+ me, err := NewMfgEmitter(mb, basePkg.Name(), ver, device)
+ if err != nil {
+ return MfgEmitter{}, err
+ }
+
+ return me, nil
+}
+
+func Upload(basePkg *pkg.LocalPackage) (string, error) {
+ dm, err := loadDecodedMfg(basePkg.BasePath())
+ if err != nil {
+ return "", err
+ }
+
+ mb, err := newMfgBuilder(basePkg, dm, image.ImageVersion{})
+ if err != nil {
+ return "", err
+ }
+
+ envSettings := map[string]string{"MFG_IMAGE": "1"}
+ binPath := MfgBinPath(basePkg.Name())
+ basePath := strings.TrimSuffix(binPath, ".bin")
+
+ if err := builder.Load(basePath, mb.Bsp, envSettings); err != nil {
+ return "", err
+ }
+
+ return binPath, nil
+}
diff --git a/newt/mfg/part.go b/newt/mfg/part.go
new file mode 100644
index 0000000..ca84d29
--- /dev/null
+++ b/newt/mfg/part.go
@@ -0,0 +1,96 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package mfg
+
+import (
+ "bytes"
+ "io"
+ "sort"
+
+ "mynewt.apache.org/newt/util"
+)
+
+// A chunk of data in the manufacturing image. Can be a firmware image or a
+// raw entry (contents of a data file).
+type Part struct {
+ Name string
+ Offset int
+ Data []byte
+}
+
+type partSorter struct {
+ parts []Part
+}
+
+func (s partSorter) Len() int {
+ return len(s.parts)
+}
+func (s partSorter) Swap(i, j int) {
+ s.parts[i], s.parts[j] = s.parts[j], s.parts[i]
+}
+func (s partSorter) Less(i, j int) bool {
+ return s.parts[i].Offset < s.parts[j].Offset
+}
+
+func SortParts(parts []Part) []Part {
+ sorter := partSorter{
+ parts: parts,
+ }
+
+ sort.Sort(sorter)
+ return sorter.parts
+}
+
+func WriteParts(parts []Part, w io.Writer, eraseVal byte) (int, error) {
+ off := 0
+ for _, p := range parts {
+ if p.Offset < off {
+ return off, util.FmtNewtError(
+ "Invalid mfg parts: out of order")
+ }
+
+ // Pad the previous block up to the current offset.
+ for off < p.Offset {
+ if _, err := w.Write([]byte{eraseVal}); err != nil {
+ return off, util.ChildNewtError(err)
+ }
+ off++
+ }
+
+ // Write the current block's data.
+ size, err := w.Write(p.Data)
+ if err != nil {
+ return off, util.ChildNewtError(err)
+ }
+ off += size
+ }
+
+ // Note: the final block does not get padded.
+
+ return off, nil
+}
+
+func PartsBytes(parts []Part) ([]byte, error) {
+ b := &bytes.Buffer{}
+ if _, err := WriteParts(parts, b, 0xff); err != nil {
+ return nil, err
+ }
+ return b.Bytes(), nil
+}
diff --git a/newt/mfg/paths.go b/newt/mfg/paths.go
index 04cf448..3b3e1f6 100644
--- a/newt/mfg/paths.go
+++ b/newt/mfg/paths.go
@@ -21,162 +21,42 @@ package mfg
import (
"fmt"
- "path/filepath"
- "strconv"
+ "mynewt.apache.org/newt/artifact/mfg"
"mynewt.apache.org/newt/newt/builder"
- "mynewt.apache.org/newt/newt/pkg"
)
+// Filename containing a manufacturing image definition.
+const YAML_FILENAME string = "mfg.yml"
+
func MfgBinDir(mfgPkgName string) string {
return builder.BinRoot() + "/" + mfgPkgName
}
-func MfgBootDir(mfgPkgName string) string {
- return MfgBinDir(mfgPkgName) + "/bootloader"
-}
-
-func MfgBootBinPath(mfgPkgName string, appName string) string {
- return MfgBootDir(mfgPkgName) + "/" + appName + ".elf.bin"
-}
-
-func MfgBootElfPath(mfgPkgName string, appName string) string {
- return MfgBootDir(mfgPkgName) + "/" + appName + ".elf"
-}
-
-func MfgBootManifestPath(mfgPkgName string, appName string) string {
- return MfgBootDir(mfgPkgName) + "/manifest.json"
-}
-
-// Image indices start at 0.
-func MfgImageBinDir(mfgPkgName string, imageIdx int) string {
- return MfgBinDir(mfgPkgName) + "/image" + strconv.Itoa(imageIdx)
-}
-
-func MfgImageImgPath(mfgPkgName string, imageIdx int,
- appName string) string {
-
- return MfgImageBinDir(mfgPkgName, imageIdx) + "/" + appName + ".img"
-}
-
-func MfgImageElfPath(mfgPkgName string, imageIdx int,
- appName string) string {
-
- return MfgImageBinDir(mfgPkgName, imageIdx) + "/" + appName + ".elf"
-}
-
-func MfgImageManifestPath(mfgPkgName string, imageIdx int) string {
- return MfgImageBinDir(mfgPkgName, imageIdx) + "/manifest.json"
-}
-
-func MfgSectionBinDir(mfgPkgName string) string {
- return MfgBinDir(mfgPkgName) + "/sections"
-}
-
-func MfgSectionBinPath(mfgPkgName string, sectionNum int) string {
- return fmt.Sprintf("%s/%s-s%d.bin", MfgSectionBinDir(mfgPkgName),
- filepath.Base(mfgPkgName), sectionNum)
-}
-
-func MfgSectionHexPath(mfgPkgName string, sectionNum int) string {
- return fmt.Sprintf("%s/%s-s%d.hex", MfgSectionBinDir(mfgPkgName),
- filepath.Base(mfgPkgName), sectionNum)
+func MfgBinPath(mfgPkgName string) string {
+ return MfgBinDir(mfgPkgName) + "/" + mfg.MFG_IMG_FILENAME
}
func MfgManifestPath(mfgPkgName string) string {
- return MfgBinDir(mfgPkgName) + "/manifest.json"
+ return MfgBinDir(mfgPkgName) + "/" + mfg.MFG_MANIFEST_FILENAME
}
-func (mi *MfgImage) ManifestPath() string {
- return MfgManifestPath(mi.basePkg.Name())
+func MfgTargetDir(targetNum int) string {
+ return fmt.Sprintf("targets/%d", targetNum)
}
-func (mi *MfgImage) BootBinPath() string {
- if mi.boot == nil {
- return ""
- }
-
- return MfgBootBinPath(mi.basePkg.Name(),
- pkg.ShortName(mi.boot.App()))
-}
-
-func (mi *MfgImage) BootElfPath() string {
- if mi.boot == nil {
- return ""
- }
-
- return MfgBootElfPath(mi.basePkg.Name(), pkg.ShortName(mi.boot.App()))
-}
-
-func (mi *MfgImage) BootManifestPath() string {
- if mi.boot == nil {
- return ""
- }
-
- return MfgBootManifestPath(mi.basePkg.Name(),
- pkg.ShortName(mi.boot.App()))
-}
-
-func (mi *MfgImage) AppImgPath(imageIdx int) string {
- app, _ := mi.imgApps(imageIdx)
- if app == nil {
- return ""
- }
-
- return MfgImageImgPath(mi.basePkg.Name(), imageIdx, pkg.ShortName(app))
-}
-
-func (mi *MfgImage) AppElfPath(imageIdx int) string {
- app, _ := mi.imgApps(imageIdx)
- if app == nil {
- return ""
- }
-
- return MfgImageElfPath(mi.basePkg.Name(), imageIdx, pkg.ShortName(app))
+func MfgTargetBinPath(targetNum int) string {
+ return fmt.Sprintf("%s/binary.bin", MfgTargetDir(targetNum))
}
-func (mi *MfgImage) LoaderImgPath(imageIdx int) string {
- _, loader := mi.imgApps(imageIdx)
- if loader == nil {
- return ""
- }
-
- return MfgImageImgPath(mi.basePkg.Name(), imageIdx, pkg.ShortName(loader))
+func MfgTargetImgPath(targetNum int) string {
+ return fmt.Sprintf("%s/image.img", MfgTargetDir(targetNum))
}
-func (mi *MfgImage) LoaderElfPath(imageIdx int) string {
- _, loader := mi.imgApps(imageIdx)
- if loader == nil {
- return ""
- }
-
- return MfgImageElfPath(mi.basePkg.Name(), imageIdx, pkg.ShortName(loader))
+func MfgTargetElfPath(targetNum int) string {
+ return fmt.Sprintf("%s/elf.elf", MfgTargetDir(targetNum))
}
-func (mi *MfgImage) ImageManifestPath(imageIdx int) string {
- if imageIdx >= len(mi.images) {
- return ""
- }
-
- return MfgImageManifestPath(mi.basePkg.Name(), imageIdx)
-}
-
-func (mi *MfgImage) SectionBinPaths() []string {
- sectionIds := mi.sectionIds()
-
- paths := make([]string, len(sectionIds))
- for i, sectionId := range sectionIds {
- paths[i] = MfgSectionBinPath(mi.basePkg.Name(), sectionId)
- }
- return paths
-}
-
-func (mi *MfgImage) SectionHexPaths() []string {
- sectionIds := mi.sectionIds()
-
- paths := make([]string, len(sectionIds))
- for i, sectionId := range sectionIds {
- paths[i] = MfgSectionHexPath(mi.basePkg.Name(), sectionId)
- }
- return paths
+func MfgTargetManifestPath(targetNum int) string {
+ return fmt.Sprintf("%s/manifest.json", MfgTargetDir(targetNum))
}