blob: 1e7029c334a8a7ba38c51a5831de5007db9ada94 [file] [log] [blame]
/**
* 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,
}
}