github.com/linuxboot/fiano@v1.2.0/pkg/fmap/fmap.go (about)

     1  // Copyright 2017-2018 the LinuxBoot Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package fmap parses flash maps.
     6  package fmap
     7  
     8  import (
     9  	"bytes"
    10  	"encoding/binary"
    11  	"encoding/json"
    12  	"errors"
    13  	"fmt"
    14  	"hash"
    15  	"io"
    16  	"strconv"
    17  	"strings"
    18  )
    19  
    20  // Signature of the fmap structure.
    21  var Signature = []byte("__FMAP__")
    22  
    23  // Flags which can be applied to Area.Flags.
    24  const (
    25  	FmapAreaStatic = 1 << iota
    26  	FmapAreaCompressed
    27  	FmapAreaReadOnly
    28  )
    29  
    30  // String wraps around byte array to give us more control over how strings are
    31  // serialized.
    32  type String struct {
    33  	Value [32]uint8
    34  }
    35  
    36  func (s *String) String() string {
    37  	return strings.TrimRight(string(s.Value[:]), "\x00")
    38  }
    39  
    40  // MarshalJSON implements json.Marshaler.
    41  func (s *String) MarshalJSON() ([]byte, error) {
    42  	return json.Marshal(s.String())
    43  }
    44  
    45  // UnmarshalJSON implements json.Unmarshaler.
    46  func (s *String) UnmarshalJSON(b []byte) error {
    47  	str, err := strconv.Unquote(string(b))
    48  	if err != nil {
    49  		return err
    50  	}
    51  	if len(str) > len(s.Value) {
    52  		return fmt.Errorf("String %#v is longer than 32 bytes", str)
    53  	}
    54  	copy(s.Value[:], []byte(str))
    55  	return nil
    56  }
    57  
    58  // FMap structure serializable using encoding.Binary.
    59  type FMap struct {
    60  	Header
    61  	Areas []Area
    62  }
    63  
    64  // Header describes the flash part.
    65  type Header struct {
    66  	Signature [8]uint8
    67  	VerMajor  uint8
    68  	VerMinor  uint8
    69  	Base      uint64
    70  	Size      uint32
    71  	Name      String
    72  	NAreas    uint16
    73  }
    74  
    75  // Area describes each area.
    76  type Area struct {
    77  	Offset uint32
    78  	Size   uint32
    79  	Name   String
    80  	Flags  uint16
    81  }
    82  
    83  // Metadata contains additional data not part of the FMap.
    84  type Metadata struct {
    85  	Start uint64
    86  }
    87  
    88  func headerValid(h *Header) bool {
    89  	if h.VerMajor != 1 {
    90  		return false
    91  	}
    92  	// Check if some sensible value is used for the full flash size
    93  	if h.Size == 0 {
    94  		return false
    95  	}
    96  
    97  	// Name is specified to be null terminated single-word string without spaces
    98  	return bytes.Contains(h.Name.Value[:], []byte("\x00"))
    99  }
   100  
   101  // FlagNames returns human readable representation of the flags.
   102  func FlagNames(flags uint16) string {
   103  	names := []string{}
   104  	m := []struct {
   105  		val  uint16
   106  		name string
   107  	}{
   108  		{FmapAreaStatic, "STATIC"},
   109  		{FmapAreaCompressed, "COMPRESSED"},
   110  		{FmapAreaReadOnly, "READ_ONLY"},
   111  	}
   112  	for _, v := range m {
   113  		if v.val&flags != 0 {
   114  			names = append(names, v.name)
   115  			flags -= v.val
   116  		}
   117  	}
   118  	// Write a hex value for unknown flags.
   119  	if flags != 0 || len(names) == 0 {
   120  		names = append(names, fmt.Sprintf("%#x", flags))
   121  	}
   122  	return strings.Join(names, "|")
   123  }
   124  
   125  var errEOF = errors.New("unexpected EOF while parsing fmap")
   126  
   127  func readField(r io.Reader, data interface{}) error {
   128  	// The endianness might depend on your machine or it might not.
   129  	if err := binary.Read(r, binary.LittleEndian, data); err != nil {
   130  		return errEOF
   131  	}
   132  	return nil
   133  }
   134  
   135  var errSigNotFound = errors.New("cannot find FMAP signature")
   136  var errMultipleFound = errors.New("found multiple fmap")
   137  
   138  // Read an FMap into the data structure.
   139  func Read(f io.Reader) (*FMap, *Metadata, error) {
   140  	// Read flash into memory.
   141  	// TODO: it is possible to parse fmap without reading entire file into memory
   142  	data, err := io.ReadAll(f)
   143  	if err != nil {
   144  		return nil, nil, err
   145  	}
   146  
   147  	// Loop over __FMAP__ occurrences until a valid header is found
   148  	start := 0
   149  	validFmaps := 0
   150  	var fmap FMap
   151  	var fmapMetadata Metadata
   152  	for {
   153  		if start >= len(data) {
   154  			break
   155  		}
   156  
   157  		next := bytes.Index(data[start:], Signature)
   158  		if next == -1 {
   159  			break
   160  		}
   161  		start += next
   162  
   163  		// Reader anchored to the start of the fmap
   164  		r := bytes.NewReader(data[start:])
   165  
   166  		// Read fields.
   167  		var testFmap FMap
   168  		if err := readField(r, &testFmap.Header); err != nil {
   169  			return nil, nil, err
   170  		}
   171  		if !headerValid(&testFmap.Header) {
   172  			start += len(Signature)
   173  			continue
   174  		}
   175  		fmap = testFmap
   176  		validFmaps++
   177  
   178  		fmap.Areas = make([]Area, fmap.NAreas)
   179  		err := readField(r, &fmap.Areas)
   180  		if err != nil {
   181  			return nil, nil, err
   182  		}
   183  		// Return useful metadata
   184  		fmapMetadata = Metadata{
   185  			Start: uint64(start),
   186  		}
   187  		start += len(Signature)
   188  	}
   189  	if validFmaps >= 2 {
   190  		return nil, nil, errMultipleFound
   191  	} else if validFmaps == 1 {
   192  		return &fmap, &fmapMetadata, nil
   193  	}
   194  	return nil, nil, errSigNotFound
   195  }
   196  
   197  // Write overwrites the fmap in the flash file.
   198  func Write(f io.WriteSeeker, fmap *FMap, m *Metadata) error {
   199  	if _, err := f.Seek(int64(m.Start), io.SeekStart); err != nil {
   200  		return err
   201  	}
   202  	if err := binary.Write(f, binary.LittleEndian, fmap.Header); err != nil {
   203  		return err
   204  	}
   205  	return binary.Write(f, binary.LittleEndian, fmap.Areas)
   206  }
   207  
   208  // IndexOfArea returns the index of an area in the fmap given its name. If no
   209  // names match, -1 is returned.
   210  func (f *FMap) IndexOfArea(name string) int {
   211  	for i := 0; i < len(f.Areas); i++ {
   212  		if f.Areas[i].Name.String() == name {
   213  			return i
   214  		}
   215  	}
   216  	return -1
   217  }
   218  
   219  // ReadArea reads an area from the flash image as a byte array given its index.
   220  func (f *FMap) ReadArea(r io.ReaderAt, i int) ([]byte, error) {
   221  	if i < 0 || int(f.NAreas) <= i {
   222  		return nil, fmt.Errorf("area index %d out of range", i)
   223  	}
   224  	buf := make([]byte, f.Areas[i].Size)
   225  	_, err := r.ReadAt(buf, int64(f.Areas[i].Offset))
   226  	return buf, err
   227  }
   228  
   229  // ReadAreaByName is the same as ReadArea but uses the area's name.
   230  func (f *FMap) ReadAreaByName(r io.ReaderAt, name string) ([]byte, error) {
   231  	i := f.IndexOfArea(name)
   232  	if i == -1 {
   233  		return nil, fmt.Errorf("FMAP area %q not found", name)
   234  	}
   235  	return f.ReadArea(r, i)
   236  }
   237  
   238  // WriteArea writes a byte array to an area on the flash image given its index.
   239  // If the data is too large for the fmap area, the write is not performed and
   240  // an error returned. If the data is too small, the remainder is left untouched.
   241  func (f *FMap) WriteArea(r io.WriterAt, i int, data []byte) error {
   242  	if i < 0 || int(f.NAreas) <= i {
   243  		return fmt.Errorf("Area index %d out of range", i)
   244  	}
   245  	if uint32(len(data)) > f.Areas[i].Size {
   246  		return fmt.Errorf("data too large for fmap area: %#x > %#x",
   247  			len(data), f.Areas[i].Size)
   248  	}
   249  	_, err := r.WriteAt(data, int64(f.Areas[i].Offset))
   250  	return err
   251  }
   252  
   253  // WriteAreaByName is the same as WriteArea but uses the area's name.
   254  func (f *FMap) WriteAreaByName(r io.WriterAt, name string, data []byte) error {
   255  	i := f.IndexOfArea(name)
   256  	if i == -1 {
   257  		return fmt.Errorf("FMAP area %q not found", name)
   258  	}
   259  	return f.WriteArea(r, i, data)
   260  }
   261  
   262  // Checksum performs a hash of the static areas.
   263  func (f *FMap) Checksum(r io.ReaderAt, h hash.Hash) ([]byte, error) {
   264  	for i, v := range f.Areas {
   265  		if v.Flags&FmapAreaStatic == 0 {
   266  			continue
   267  		}
   268  		areaReader, err := f.ReadArea(r, i)
   269  		if err != nil {
   270  			return nil, err
   271  		}
   272  		_, err = bytes.NewReader(areaReader).WriteTo(h)
   273  		if err != nil {
   274  			return nil, err
   275  		}
   276  	}
   277  	return h.Sum([]byte{}), nil
   278  }