github.com/linuxboot/fiano@v1.2.0/pkg/fsp/header.go (about) 1 // Copyright 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 fsp implements FSP info header parsing 6 package fsp 7 8 import ( 9 "bytes" 10 "encoding/binary" 11 "encoding/json" 12 "fmt" 13 "strings" 14 15 "github.com/linuxboot/fiano/pkg/log" 16 ) 17 18 // TODO support FSP versions < 2.0 19 // TODO implement FSP_INFO_EXTENDED_HEADER 20 21 // FSP 2.0 specification 22 // https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/fsp-architecture-spec-v2.pdf 23 24 // values from the FSP 2.0 spec 25 var ( 26 Signature = [4]byte{'F', 'S', 'P', 'H'} 27 ) 28 29 // constants from the FSP 2.0 spec 30 const ( 31 // size of the shared part of the header across FSP spec revisions 32 FixedInfoHeaderLength = 12 33 HeaderV3Length = 72 34 HeaderV4Length = 72 35 HeaderV5Length = 76 36 HeaderV6Length = 80 37 // FSP 2.0 38 CurrentSpecVersion = SpecVersion(0x20) 39 HeaderMinRevision = 3 40 HeaderMaxRevision = 6 41 // FSP 3.0 42 UnsupportedSpecVersion = SpecVersion(0x30) 43 ) 44 45 // FixedInfoHeader is the common header among the various revisions of the FSP 46 // info header. 47 type FixedInfoHeader struct { 48 Signature [4]byte 49 HeaderLength uint32 50 Reserved1 [2]uint8 51 SpecVersion SpecVersion 52 HeaderRevision uint8 53 } 54 55 // InfoHeaderRev3 represents the FSP_INFO_HEADER structure revision 3 + 4 (FSP 56 // 2.0) as defined by Intel. 57 type InfoHeaderRev3 struct { 58 FixedInfoHeader 59 ImageRevisionLowBytes uint32 60 ImageID [8]byte 61 ImageSize uint32 62 ImageBase uint32 63 ImageAttribute ImageAttribute 64 ComponentAttribute ComponentAttribute 65 CfgRegionOffset uint32 66 CfgRegionSize uint32 67 Reserved2 [4]byte 68 TempRAMInitEntryOffset uint32 69 Reserved3 [4]byte 70 NotifyPhaseEntryOffset uint32 71 FSPMemoryInitEntryOffset uint32 72 TempRAMExitEntryOffset uint32 73 FSPSiliconInitEntryOffset uint32 74 } 75 76 // InfoHeaderRev5 represents the FSP_INFO_HEADER structure revision 5 (FSP 77 // 2.0) as defined by Intel. 78 type InfoHeaderRev5 struct { 79 InfoHeaderRev3 80 FspMultiPhaseSiInitEntryOffset uint32 81 } 82 83 // InfoHeaderRev6 represents the FSP_INFO_HEADER structure revision 6 (FSP 84 // 2.0) as defined by Intel. 85 type InfoHeaderRev6 struct { 86 InfoHeaderRev5 87 ExtendedImageRevision uint16 88 Reserved4 uint16 89 } 90 91 // CommonInfoHeader represents the FSP_INFO_HEADER structure 92 // revision independent 93 type CommonInfoHeader struct { 94 Signature [4]byte 95 HeaderLength uint32 96 SpecVersion SpecVersion 97 HeaderRevision uint8 98 ImageRevision ImageRevision 99 ImageID [8]byte 100 ImageSize uint32 101 ImageBase uint32 102 ImageAttribute ImageAttribute 103 ComponentAttribute ComponentAttribute 104 CfgRegionOffset uint32 105 CfgRegionSize uint32 106 TempRAMInitEntryOffset uint32 107 NotifyPhaseEntryOffset uint32 108 FSPMemoryInitEntryOffset uint32 109 TempRAMExitEntryOffset uint32 110 FSPSiliconInitEntryOffset uint32 111 FspMultiPhaseSiInitEntryOffset uint32 112 ExtendedImageRevision uint16 113 } 114 115 // Summary prints a multi-line summary of the header's content. 116 func (ih CommonInfoHeader) Summary() string { 117 s := fmt.Sprintf("Signature : %s\n", ih.Signature) 118 s += fmt.Sprintf("Header Length : %d\n", ih.HeaderLength) 119 s += fmt.Sprintf("Spec Version : %s\n", ih.SpecVersion) 120 s += fmt.Sprintf("Header Revision : %d\n", ih.HeaderRevision) 121 s += fmt.Sprintf("Image Revision : %s\n", ih.ImageRevision) 122 s += fmt.Sprintf("Image ID : %s\n", ih.ImageID) 123 s += fmt.Sprintf("Image Size : %#08x %d\n", ih.ImageSize, ih.ImageSize) 124 s += fmt.Sprintf("Image Base : %#08x %d\n", ih.ImageBase, ih.ImageBase) 125 s += fmt.Sprintf("Image Attribute : %s\n", ih.ImageAttribute) 126 s += fmt.Sprintf("Component Attribute : %s\n", ih.ComponentAttribute) 127 s += fmt.Sprintf("Cfg Region Offset : %#08x %d\n", ih.CfgRegionOffset, ih.CfgRegionOffset) 128 s += fmt.Sprintf("Cfg Region Size : %#08x %d\n", ih.CfgRegionSize, ih.CfgRegionSize) 129 s += fmt.Sprintf("TempRAMInit Entry Offset : %#08x %d\n", ih.TempRAMInitEntryOffset, ih.TempRAMInitEntryOffset) 130 s += fmt.Sprintf("NotifyPhase Entry Offset : %#08x %d\n", ih.NotifyPhaseEntryOffset, ih.NotifyPhaseEntryOffset) 131 s += fmt.Sprintf("FSPMemoryInit Entry Offset : %#08x %d\n", ih.FSPMemoryInitEntryOffset, ih.FSPMemoryInitEntryOffset) 132 s += fmt.Sprintf("TempRAMExit Entry Offset : %#08x %d\n", ih.TempRAMExitEntryOffset, ih.TempRAMExitEntryOffset) 133 s += fmt.Sprintf("FSPSiliconInit Entry Offset : %#08x %d\n", ih.FSPSiliconInitEntryOffset, ih.FSPSiliconInitEntryOffset) 134 s += fmt.Sprintf("FspMultiPhaseSiInit Entry Offset : %#08x %d\n", ih.FspMultiPhaseSiInitEntryOffset, ih.FspMultiPhaseSiInitEntryOffset) 135 s += fmt.Sprintf("ExtendedImageRevision : %#08x %d\n", ih.ExtendedImageRevision, ih.ExtendedImageRevision) 136 137 return s 138 } 139 140 // ImageRevision is the image revision field of the FSP info header. 141 type ImageRevision uint64 142 143 func (ir ImageRevision) String() string { 144 return fmt.Sprintf("%d.%d.%d.%d", 145 (ir>>48)&0xffff, 146 (ir>>32)&0xffff, 147 (ir>>16)&0xffff, 148 ir&0xffff, 149 ) 150 } 151 152 func DecodeImageRevision(HeaderRevision uint8, Revision uint32, ExtendedRevision uint16) ImageRevision { 153 /// Major.Minor.Revision.Build 154 /// If FSP HeaderRevision is <= 5, the ImageRevision can be decoded as follows: 155 /// 7 : 0 - Build Number 156 /// 15 : 8 - Revision 157 /// 23 : 16 - Minor Version 158 /// 31 : 24 - Major Version 159 /// If FSP HeaderRevision is >= 6, ImageRevision specifies the low-order bytes of the build number and revision 160 /// while ExtendedImageRevision specifies the high-order bytes of the build number and revision. 161 /// 7 : 0 - Low Byte of Build Number 162 /// 15 : 8 - Low Byte of Revision 163 /// 23 : 16 - Minor Version 164 /// 31 : 24 - Major Version 165 /// This value is only valid if FSP HeaderRevision is >= 6. 166 /// 167 /// ExtendedImageRevision specifies the high-order byte of the revision and build number in the FSP binary revision. 168 /// 7 : 0 - High Byte of Build Number 169 /// 15 : 8 - High Byte of Revision 170 /// The FSP binary build number can be decoded as follows: 171 /// Build Number = (ExtendedImageRevision[7:0] << 8) | ImageRevision[7:0] 172 /// Revision = (ExtendedImageRevision[15:8] << 8) | ImageRevision[15:8] 173 /// Minor Version = ImageRevision[23:16] 174 /// Major Version = ImageRevision[31:24] 175 176 if HeaderRevision < 6 { 177 ExtendedRevision = 0 178 } 179 return ImageRevision((uint64(Revision)&0xff)<<0 | 180 (uint64(Revision)&0xff00)<<8 | 181 (uint64(Revision)&0xff0000)<<16 | 182 (uint64(Revision)&0xff000000)<<24 | 183 (uint64(ExtendedRevision)&0xff)<<8 | 184 (uint64(ExtendedRevision)&0xff00)<<16) 185 } 186 187 // SpecVersion represents the spec version as a packed BCD two-digit, 188 // dot-separated unsigned integer. 189 type SpecVersion uint8 190 191 func (sv SpecVersion) String() string { 192 return fmt.Sprintf("%d.%d", (sv>>4)&0x0f, sv&0x0f) 193 } 194 195 // ImageAttribute represents the image attributes. 196 type ImageAttribute uint16 197 198 func (ia ImageAttribute) String() string { 199 ret := fmt.Sprintf("%#04x ", uint16(ia)) 200 if ia.IsGraphicsDisplaySupported() { 201 ret += "GraphicsDisplaySupported" 202 } else { 203 ret += "GraphicsDisplayNotSupported" 204 } 205 if ia.IsDispatchModeSupported() { 206 ret += " DispatchModeSupported" 207 } else { 208 ret += " DispatchModeNotSupported" 209 } 210 if uint16(ia) & ^(uint16(1)) != 0 { 211 ret += " (reserved bits are not zeroed)" 212 } 213 return ret 214 } 215 216 // IsGraphicsDisplaySupported returns true if FSP supports enabling graphics display. 217 func (ia ImageAttribute) IsGraphicsDisplaySupported() bool { 218 return uint16(ia)&0x1 == 1 219 } 220 221 // IsDispatchModeSupported returns true if FSP supports Dispatch mode. 222 func (ia ImageAttribute) IsDispatchModeSupported() bool { 223 return uint16(ia)&0x2 == 2 224 } 225 226 // Type identifies the FSP type. 227 type Type uint8 228 229 // FSP types. All the other values are reserved. 230 var ( 231 TypeT Type = 1 232 TypeM Type = 2 233 TypeS Type = 3 234 TypeO Type = 8 235 // TypeReserved is a fake type that represents a reserved FSP type. 236 TypeReserved Type 237 ) 238 239 var fspTypeNames = map[Type]string{ 240 TypeT: "FSP-T", 241 TypeM: "FSP-M", 242 TypeS: "FSP-S", 243 TypeO: "FSP-O", 244 TypeReserved: "FSP-ReservedType", 245 } 246 247 // ComponentAttribute represents the component attribute. 248 type ComponentAttribute uint16 249 250 // IsDebugBuild returns true if the FSP build is a debug build, and false 251 // if it's a release build. 252 func (ca ComponentAttribute) IsDebugBuild() bool { 253 return uint16(ca)&0x01 == 0 254 } 255 256 // IsTestRelease returns true if the release is a test release, and false if 257 // it's an official release. 258 func (ca ComponentAttribute) IsTestRelease() bool { 259 return uint16(ca)&0x03 == 0 260 } 261 262 // Type returns the FSP type. 263 func (ca ComponentAttribute) Type() Type { 264 typ := Type(uint16(ca) >> 12) 265 if _, ok := fspTypeNames[typ]; ok { 266 return typ 267 } 268 return TypeReserved 269 } 270 271 func (ca ComponentAttribute) String() string { 272 var attrs []string 273 if ca.IsDebugBuild() { 274 attrs = append(attrs, "DebugBuild") 275 } else { 276 attrs = append(attrs, "ReleaseBuild") 277 } 278 if ca.IsTestRelease() { 279 attrs = append(attrs, "TestRelease") 280 } else { 281 attrs = append(attrs, "OfficialRelease") 282 } 283 if typeName, ok := fspTypeNames[ca.Type()]; ok { 284 attrs = append(attrs, typeName) 285 } else { 286 attrs = append(attrs, fmt.Sprintf("TypeUnknown(%v)", ca.Type())) 287 } 288 ret := fmt.Sprintf("%#04x %s", uint16(ca), strings.Join(attrs, "|")) 289 // bits 11:2 are reserved 290 if uint16(ca)&0x0ffe != 0 { 291 ret += " (reserved bits are not zeroed)" 292 } 293 return ret 294 } 295 296 // NewInfoHeader creates an CommonInfoHeader from a byte buffer. 297 func NewInfoHeader(b []byte) (*CommonInfoHeader, error) { 298 if len(b) < FixedInfoHeaderLength { 299 return nil, fmt.Errorf("short FSP Info Header length %d; want at least %d", len(b), FixedInfoHeaderLength) 300 } 301 var hdr FixedInfoHeader 302 303 reader := bytes.NewReader(b) 304 if err := binary.Read(reader, binary.LittleEndian, &hdr); err != nil { 305 return nil, err 306 } 307 308 // check signature 309 if !bytes.Equal(hdr.Signature[:], Signature[:]) { 310 return nil, fmt.Errorf("invalid signature %v; want %v", hdr.Signature, Signature) 311 } 312 // reserved bytes must be zero'ed 313 if !bytes.Equal(hdr.Reserved1[:], []byte{0, 0}) { 314 log.Warnf("reserved bytes must be zero, got %v", hdr.Reserved1) 315 } 316 // check spec version 317 // TODO currently, only FSP 2.x is supported 318 if hdr.SpecVersion < CurrentSpecVersion || hdr.SpecVersion >= UnsupportedSpecVersion { 319 return nil, fmt.Errorf("cannot handle spec version %s; want %s", hdr.SpecVersion, CurrentSpecVersion) 320 } 321 322 // check header revision 323 if hdr.HeaderRevision < HeaderMinRevision { 324 return nil, fmt.Errorf("cannot handle header revision %d; want min %d", hdr.HeaderRevision, HeaderMinRevision) 325 } 326 327 l := uint32(0) 328 switch hdr.HeaderRevision { 329 case 3: 330 l = HeaderV3Length 331 case 4: 332 l = HeaderV4Length 333 case 5: 334 l = HeaderV5Length 335 case 6: 336 l = HeaderV6Length 337 default: 338 l = HeaderV6Length 339 } 340 if hdr.HeaderRevision <= HeaderMaxRevision { 341 // Intel violates their own spec! Warn here and don't care about additional fields. 342 if l != hdr.HeaderLength { 343 log.Warnf("Spec violation. header length is %d; expected %d", hdr.HeaderLength, l) 344 } 345 } 346 if hdr.HeaderLength < l { 347 return nil, fmt.Errorf("invalid header length %d; want at least %d", hdr.HeaderLength, l) 348 } 349 350 // now that we know it's an info header spec 2.0, re-read the 351 // buffer to fill the whole header. 352 reader = bytes.NewReader(b) 353 var InfoHeader InfoHeaderRev6 354 355 if hdr.HeaderRevision >= 6 { 356 if err := binary.Read(reader, binary.LittleEndian, &InfoHeader); err != nil { 357 return nil, err 358 } 359 } else if hdr.HeaderRevision >= 5 { 360 if err := binary.Read(reader, binary.LittleEndian, &InfoHeader.InfoHeaderRev5); err != nil { 361 return nil, err 362 } 363 } else { 364 if err := binary.Read(reader, binary.LittleEndian, &InfoHeader.InfoHeaderRev3); err != nil { 365 return nil, err 366 } 367 } 368 369 // Fill common info header 370 j, _ := json.Marshal(InfoHeader) 371 var c CommonInfoHeader 372 if err := json.Unmarshal(j, &c); err != nil { 373 return nil, err 374 } 375 376 // Update custom types 377 c.ImageRevision = DecodeImageRevision(hdr.HeaderRevision, 378 InfoHeader.ImageRevisionLowBytes, 379 InfoHeader.ExtendedImageRevision) 380 381 return &c, nil 382 }