github.com/linuxboot/fiano@v1.2.0/pkg/intel/metadata/fit/entry_headers.go (about) 1 // Copyright 2017-2021 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 fit 6 7 import ( 8 "bytes" 9 "encoding/binary" 10 "encoding/json" 11 "fmt" 12 "io" 13 "strings" 14 15 "github.com/xaionaro-go/bytesextra" 16 ) 17 18 var ( 19 entryHeadersSize = uint(binary.Size(EntryHeaders{})) 20 ) 21 22 // EntryHeaders implements a "FIT Entry Format". 23 // 24 // See "Table 1-1" in "1.2 Firmware Interface Table" in "Firmware Interface Table" specification: 25 // * https://www.intel.com/content/dam/www/public/us/en/documents/guides/fit-bios-specification.pdf 26 // 27 // Descriptions of the fields are adapted descriptions from the document by the link above. 28 type EntryHeaders struct { 29 // Address is the base address of the firmware component. 30 // Must be aligned on 16 byte boundary. 31 Address Address64 32 33 Size Uint24 34 35 // Reserved should always be equal to zero. 36 Reserved uint8 37 38 Version EntryVersion 39 40 TypeAndIsChecksumValid TypeAndIsChecksumValid 41 42 Checksum uint8 43 } 44 45 func (hdr EntryHeaders) copy() *EntryHeaders { 46 return &hdr 47 } 48 49 // GoString implements fmt.GoStringer. 50 func (hdr *EntryHeaders) GoString() string { 51 var result strings.Builder 52 result.WriteString(fmt.Sprintf(" Address: 0x%x\n", hdr.Address.Pointer())) 53 result.WriteString(fmt.Sprintf(" size: 0x%x\n", hdr.Size.Uint32())) 54 result.WriteString(fmt.Sprintf(" Version: 0x%x\n", uint16(hdr.Version))) 55 result.WriteString(fmt.Sprintf(" Type: 0x%x\n", uint8(hdr.TypeAndIsChecksumValid))) 56 result.WriteString(fmt.Sprintf(" Checksum: 0x%x\n", hdr.Checksum)) 57 return result.String() 58 } 59 60 type entryHeadersForJSON struct { 61 Address uint64 62 Size uint32 63 Reserved uint8 `json:",omitempty"` 64 Version EntryVersion 65 Type EntryType 66 IsChecksumValid bool 67 Checksum uint8 68 } 69 70 // MarshalJSON just implements encoding/json.Marshaler 71 func (hdr EntryHeaders) MarshalJSON() ([]byte, error) { 72 return json.Marshal(&entryHeadersForJSON{ 73 Address: hdr.Address.Pointer(), 74 Size: hdr.Size.Uint32(), 75 Reserved: hdr.Reserved, 76 Version: hdr.Version, 77 Type: hdr.Type(), 78 IsChecksumValid: hdr.IsChecksumValid(), 79 Checksum: hdr.Checksum, 80 }) 81 } 82 83 // UnmarshalJSON just implements encoding/json.Unmarshaler 84 func (hdr *EntryHeaders) UnmarshalJSON(b []byte) error { 85 var parsed entryHeadersForJSON 86 err := json.Unmarshal(b, &parsed) 87 if err != nil { 88 return err 89 } 90 *hdr = EntryHeaders{ 91 Address: Address64(parsed.Address), 92 Reserved: parsed.Reserved, 93 Version: parsed.Version, 94 Checksum: parsed.Checksum, 95 } 96 hdr.Size.SetUint32(parsed.Size) 97 hdr.TypeAndIsChecksumValid.SetType(parsed.Type) 98 hdr.TypeAndIsChecksumValid.SetIsChecksumValid(parsed.IsChecksumValid) 99 return nil 100 } 101 102 // Uint24 is a 24 bit unsigned little-endian integer value. 103 type Uint24 struct { 104 Value [3]byte 105 } 106 107 // Uint32 returns the value as parsed uint32. 108 // 109 // If the value is used in "Size" then in the most cases the value should be 110 // shifted with "<< 4" to get the real size value. 111 // 112 // See also the code of EntryHeaders.getDataCoordinates() 113 func (size Uint24) Uint32() uint32 { 114 b := make([]byte, 4) 115 copy(b[:], size.Value[:]) 116 return binary.LittleEndian.Uint32(b) 117 } 118 119 // SetUint32 sets the value. See also Uint32. 120 func (size *Uint24) SetUint32(newValue uint32) { 121 if newValue >= 1<<24 { 122 panic(fmt.Errorf("too big integer: %d >= %d", newValue, 1<<24)) 123 } 124 b := make([]byte, 4) 125 binary.LittleEndian.PutUint32(b, newValue) 126 copy(size.Value[:], b[:]) 127 } 128 129 // MarshalJSON just implements encoding/json.Marshaler 130 func (size Uint24) MarshalJSON() ([]byte, error) { 131 return json.Marshal(size.Uint32()) 132 } 133 134 // UnmarshalJSON just implements encoding/json.Unmarshaler 135 func (size *Uint24) UnmarshalJSON(b []byte) error { 136 var parsed uint32 137 err := json.Unmarshal(b, &parsed) 138 if err != nil { 139 return err 140 } 141 if parsed >= 1<<24 { 142 return fmt.Errorf("too big integer: %d >= %d", parsed, 1<<24) 143 } 144 size.SetUint32(parsed) 145 return nil 146 } 147 148 // Address64 is a 64bit address type 149 type Address64 uint64 150 151 // Pointer returns the pointer which could be used for pointer arithmetics. 152 func (addr Address64) Pointer() uint64 { return uint64(addr) } 153 154 // Offset returns an offset from the beginning of a firmware of a defined size. 155 func (addr Address64) Offset(firmwareSize uint64) uint64 { 156 return CalculateOffsetFromPhysAddr(addr.Pointer(), firmwareSize) 157 } 158 159 // SetOffset sets the value to a physical address corresponding to 160 // an offset from the beginning of the firmware. 161 // 162 // See also the description of calculatePhysAddrFromOffset. 163 func (addr *Address64) SetOffset(offset, firmwareSize uint64) { 164 physAddr := CalculatePhysAddrFromOffset(offset, firmwareSize) 165 *addr = Address64(physAddr) 166 } 167 168 // String implements fmt.Stringer 169 func (addr Address64) String() string { return fmt.Sprintf("0x%x", addr.Pointer()) } 170 171 // EntryVersion contains the component's version number in binary 172 // coded decimal (BCD) format. For the FIT header entry, the value in this 173 // field will indicate the revision number of the FIT data structure. 174 // The upper byte of the revision field indicates the major revision and 175 // the lower byte indicates the minor revision. The format 0x1234 conveys 176 // the major number encoded in the first two digits and the minor number 177 // in the last two with a fixed point assumed in between 178 type EntryVersion uint16 179 180 // Major returns the major part of the entry version 181 func (ver EntryVersion) Major() uint8 { return uint8(ver & 0xff00 >> 8) } 182 183 // Minor returns the minor part of the entry version 184 func (ver EntryVersion) Minor() uint8 { return uint8(ver & 0xff) } 185 186 func (ver EntryVersion) String() string { 187 b, _ := ver.MarshalJSON() 188 return string(b) 189 } 190 191 type entryVersionStruct struct { 192 Major uint8 `json:"maj"` 193 Minor uint8 `json:"min,omitempty"` 194 } 195 196 // MarshalJSON just implements encoding/json.Marshaler 197 func (ver EntryVersion) MarshalJSON() ([]byte, error) { 198 return json.Marshal(&entryVersionStruct{ 199 Major: ver.Major(), 200 Minor: ver.Minor(), 201 }) 202 } 203 204 // UnmarshalJSON just implements encoding/json.Unmarshaler 205 func (ver *EntryVersion) UnmarshalJSON(b []byte) error { 206 parsed := entryVersionStruct{} 207 err := json.Unmarshal(b, &parsed) 208 if err != nil { 209 return err 210 } 211 *ver = EntryVersion(parsed.Major)<<8 | EntryVersion(parsed.Minor) 212 return nil 213 } 214 215 // SizeM16 is a size in multiple of 16 bytes (M16). 216 type SizeM16 uint16 217 218 // Size returns the size in bytes 219 func (size SizeM16) Size() uint { return uint(size) << 4 } 220 func (size SizeM16) String() string { return fmt.Sprintf("0x%x*0x10", uint16(size)) } 221 222 // TypeAndIsChecksumValid combines two fields: 223 // * "C_V" -- Checksum Valid bit. This is a one bit field that indicates, 224 // whether component has a valid checksum. CPU must ignore 225 // "Checksum" field, if C_V bit is not set. 226 // * EntryType (see "entry_type.go"). 227 type TypeAndIsChecksumValid uint8 228 229 // IsChecksumValid returns bit "C_V" of the FIT entry. 230 // 231 // A quote from the specification: 232 // Checksum Valid bit. This is a one bit field that indicates, whether 233 // component has a valid checksum. CPU must ignore CHKSUM field, if C_V bit is not set. 234 func (f TypeAndIsChecksumValid) IsChecksumValid() bool { 235 return f&0x80 != 0 236 } 237 238 // Type returns field EntryType ("TYPE" of the FIT entry in terms of 239 // the specification). 240 func (f TypeAndIsChecksumValid) Type() EntryType { 241 return EntryType(f & 0x7f) 242 } 243 244 func (f TypeAndIsChecksumValid) String() string { 245 b, _ := f.MarshalJSON() 246 return string(b) 247 } 248 249 // SetType sets the value of field EntryType ("TYPE" of the FIT entry in terms of 250 // the specification). 251 func (f *TypeAndIsChecksumValid) SetType(newType EntryType) { 252 if uint(newType) & ^uint(0x7f) != 0 { 253 panic(fmt.Errorf("invalid type: 0x%X", newType)) 254 } 255 otherBits := TypeAndIsChecksumValid(uint(*f) & ^uint(0x7f)) 256 *f = TypeAndIsChecksumValid(newType) | otherBits 257 } 258 259 // SetIsChecksumValid sets the value of field IsChecksumValid ("C_V" of the FIT entry in terms of 260 // the specification). 261 func (f *TypeAndIsChecksumValid) SetIsChecksumValid(newValue bool) { 262 valueBits := TypeAndIsChecksumValid(0) 263 if newValue { 264 valueBits = TypeAndIsChecksumValid(0x80) 265 } 266 267 otherBits := TypeAndIsChecksumValid(uint(*f) & uint(0x7f)) 268 *f = valueBits | otherBits 269 } 270 271 type typeAndIsChecksumValidStruct struct { 272 Type EntryType `json:"type"` 273 IsChecksumValid bool `json:"isChecksumValid,omitempty"` 274 } 275 276 // MarshalJSON just implements encoding/json.Marshaler 277 func (f TypeAndIsChecksumValid) MarshalJSON() ([]byte, error) { 278 return json.Marshal(&typeAndIsChecksumValidStruct{ 279 IsChecksumValid: f.IsChecksumValid(), 280 Type: f.Type(), 281 }) 282 } 283 284 // UnmarshalJSON just implements encoding/json.Unmarshaler 285 func (f *TypeAndIsChecksumValid) UnmarshalJSON(b []byte) error { 286 parsed := typeAndIsChecksumValidStruct{} 287 err := json.Unmarshal(b, &parsed) 288 if err != nil { 289 return err 290 } 291 if parsed.Type >= 0x80 { 292 return fmt.Errorf(`"type" value is too high`) 293 } 294 *f = TypeAndIsChecksumValid(parsed.Type & 0x7f) 295 if parsed.IsChecksumValid { 296 *f |= 0x80 297 } 298 return nil 299 } 300 301 // GetEntry returns a full entry (headers + data) 302 func (hdr EntryHeaders) GetEntry(firmware []byte) Entry { 303 return hdr.GetEntryFrom(bytesextra.NewReadWriteSeeker(firmware)) 304 } 305 306 // GetEntryFrom returns a full entry (headers + data) 307 func (hdr EntryHeaders) GetEntryFrom(firmware io.ReadSeeker) Entry { 308 return NewEntry(hdr.copy(), firmware) 309 } 310 311 // Type returns the type of the FIT entry 312 func (hdr *EntryHeaders) Type() EntryType { 313 return hdr.TypeAndIsChecksumValid.Type() 314 } 315 316 // IsChecksumValid returns if bit "C_V" has value "1". 317 func (hdr *EntryHeaders) IsChecksumValid() bool { 318 return hdr.TypeAndIsChecksumValid.IsChecksumValid() 319 } 320 321 func (hdr *EntryHeaders) String() string { 322 return fmt.Sprintf("&%+v", *hdr) 323 } 324 325 var _ io.Writer = (*EntryHeaders)(nil) 326 327 // Write implements io.Writer. It writes the headers in a binary format to `b`. 328 func (hdr *EntryHeaders) Write(b []byte) (int, error) { 329 n, err := hdr.WriteTo(bytes.NewBuffer(b)) 330 return int(n), err 331 } 332 333 var _ io.WriterTo = (*EntryHeaders)(nil) 334 335 // WriteTo implements io.WriterTo. It writes the headers in a binary format to `w`. 336 func (hdr *EntryHeaders) WriteTo(w io.Writer) (int64, error) { 337 if hdr == nil { 338 return 0, nil 339 } 340 341 err := binary.Write(w, binary.LittleEndian, hdr) 342 if err != nil { 343 return -1, fmt.Errorf("unable to write headers %#+v: %w", *hdr, err) 344 } 345 346 return int64(binary.Size(*hdr)), nil 347 } 348 349 // CalculateChecksum calculates the checksum ("CHKSUM") 350 // according to point 4.0 of the FIT specification. 351 func (hdr *EntryHeaders) CalculateChecksum() uint8 { 352 _copy := *hdr 353 _copy.Checksum = 0 354 355 var buf bytes.Buffer 356 if err := binary.Write(&buf, binary.LittleEndian, _copy); err != nil { 357 panic(err) 358 } 359 360 result := uint8(0) 361 for _, _byte := range buf.Bytes() { 362 result += _byte 363 } 364 365 return result 366 } 367 368 // getDataSegmentCoordinates returns the offset of the data segment 369 // associated with the entry. 370 func (hdr *EntryHeaders) getDataSegmentOffset(firmware io.Seeker) (uint64, error) { 371 firmwareSize, err := firmware.Seek(0, io.SeekEnd) 372 if err != nil { 373 return 0, fmt.Errorf("unable to get the size of the firmware: %w", err) 374 } 375 376 return hdr.Address.Offset(uint64(firmwareSize)), nil 377 } 378 379 // mostCommonGetDataSegmentCoordinates returns the length of the data segment 380 // associated with the entry using the most common rule: 381 // * The size equals to "Size" multiplied by 16. 382 // 383 // This is considered the most common rule for the most FIT entry types. But different types may break it. 384 func (hdr *EntryHeaders) mostCommonGetDataSegmentSize() uint64 { 385 return uint64(hdr.Size.Uint32()) << 4 386 }