blob: b4786bc4e96e95994383bac3d4a2a262760d181e [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
}
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)
}
}
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 (meta *Meta) Clone() Meta {
var tlvs []MetaTlv
for _, src := range meta.Tlvs {
dst := MetaTlv{
Header: src.Header,
Data: make([]byte, len(src.Data)),
}
copy(dst.Data, src.Data)
tlvs = append(tlvs, dst)
}
return Meta{
Tlvs: tlvs,
Footer: meta.Footer,
}
}
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, errors.Wrapf(err, "error reading TLV header")
}
data := make([]byte, tlv.Header.Size)
sz, err := r.Read(data)
if err != nil {
return tlv, 0, errors.Wrapf(err,
"error reading %d bytes of TLV data",
tlv.Header.Size)
}
if sz != len(data) {
return tlv, 0, errors.Errorf(
"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, errors.Wrapf(err,
"error reading meta footer")
}
if ftr.Magic != META_MAGIC {
return ftr, 0, errors.Errorf(
"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, errors.Errorf(
"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, errors.Errorf(
"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
}