mfg/meta.go (215 lines of code) (raw):

/** * 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" "github.com/apache/mynewt-artifact/errors" ) // 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 errors.Wrapf(err, "failed to write MMR element") } 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 } // StructuredBody constructs the appropriate "body" object from a raw TLV // (e.g., MetaTlvBodyHash from a TLV with type=META_TLV_TYPE_HASH). func (tlv *MetaTlv) StructuredBody() (interface{}, error) { r := bytes.NewReader(tlv.Data) readBody := func(dst interface{}) error { if err := binary.Read(r, binary.LittleEndian, dst); err != nil { return errors.Wrapf(err, "error parsing TLV data") } return nil } switch tlv.Header.Type { case META_TLV_TYPE_HASH: var body MetaTlvBodyHash if err := readBody(&body); err != nil { return nil, err } return &body, nil case META_TLV_TYPE_FLASH_AREA: var body MetaTlvBodyFlashArea if err := readBody(&body); err != nil { return nil, err } return &body, nil case META_TLV_TYPE_MMR_REF: var body MetaTlvBodyMmrRef if err := readBody(&body); err != nil { return nil, err } return &body, nil default: return nil, errors.Errorf("unknown meta TLV type: %d", tlv.Header.Type) } } // WritePlusOffsets writes a binary MMR to the given writer. It returns the // offsets of the mfgimage components that got written. 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 } // Offsets returns the offsets of each of an MMR's components if it were // serialized. func (meta *Meta) Offsets() MetaOffsets { mo, _ := meta.WritePlusOffsets(ioutil.Discard) return mo } // Write serializes and writes an MMR. func (meta *Meta) Write(w io.Writer) (int, error) { mo, err := meta.WritePlusOffsets(w) if err != nil { return 0, err } return mo.TotalSize, nil } // Size calculates the total size of an MMR if it were serialied. func (meta *Meta) Size() int { return meta.Offsets().TotalSize } // Bytes serializes an MMR to binary form. func (meta *Meta) Bytes() ([]byte, error) { b := &bytes.Buffer{} _, err := meta.Write(b) if err != nil { return nil, err } return b.Bytes(), nil } // FindTlvIndices searches an MMR for TLVs of the specified type and returns // their indices. 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 } // FindTlvIndices searches an MMR for all TLVs of the specified type. 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 } // FindTlvIndices searches an MMR for the first TLV of the specified type. func (meta *Meta) FindFirstTlv(typ uint8) *MetaTlv { tlvs := meta.FindTlvs(typ) if len(tlvs) == 0 { return nil } return tlvs[0] } // HashOffset calculates the offset of the SHA256 TLV in an MMR if it were // serialized. 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]] } // ClearHash zeroes out an MMRs SHA256 TLV. func (meta *Meta) ClearHash() { tlv := meta.FindFirstTlv(META_TLV_TYPE_HASH) if tlv != nil { tlv.Data = make([]byte, META_HASH_SZ) } } // Hash locates an MMR's SHA256 TLV and returns its value. It returns nil if // the MMR doesn't have a SHA256 TLV. func (meta *Meta) Hash() []byte { tlv := meta.FindFirstTlv(META_TLV_TYPE_HASH) if tlv == nil { return nil } return tlv.Data } // Clone performs a deep copy of an MMR. func (meta *Meta) Clone() Meta { tlvs := make([]MetaTlv, len(meta.Tlvs)) for i, src := range meta.Tlvs { tlvs[i] = MetaTlv{ Header: src.Header, Data: make([]byte, len(src.Data)), } copy(tlvs[i].Data, src.Data) } return Meta{ Tlvs: tlvs, Footer: meta.Footer, } }