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 }