github.com/linuxboot/fiano@v1.2.0/pkg/uefi/flash.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 uefi 6 7 import ( 8 "bytes" 9 "encoding/hex" 10 "fmt" 11 "sort" 12 ) 13 14 // FlashSignature is the sequence of bytes that a Flash image is expected to 15 // start with. 16 var ( 17 FlashSignature = []byte{0x5a, 0xa5, 0xf0, 0x0f} 18 ) 19 20 const ( 21 // FlashDescriptorLength represents the size of the descriptor region. 22 FlashDescriptorLength = 0x1000 23 ) 24 25 // FlashDescriptor is the main structure that represents an Intel Flash Descriptor. 26 type FlashDescriptor struct { 27 // Holds the raw buffer 28 buf []byte 29 DescriptorMapStart uint 30 RegionStart uint 31 MasterStart uint 32 DescriptorMap *FlashDescriptorMap 33 Region *FlashRegionSection 34 Master *FlashMasterSection 35 36 //Metadata for extraction and recovery 37 ExtractPath string 38 } 39 40 // FindSignature searches for an Intel flash signature. 41 func FindSignature(buf []byte) (int, error) { 42 if len(buf) >= 16+len(FlashSignature) && bytes.Equal(buf[16:16+len(FlashSignature)], FlashSignature) { 43 // 16 + 4 since the descriptor starts after the signature 44 return 20, nil 45 } 46 if len(buf) >= len(FlashSignature) && bytes.Equal(buf[:len(FlashSignature)], FlashSignature) { 47 // + 4 since the descriptor starts after the signature 48 return len(FlashSignature), nil 49 } 50 51 firstBytesCnt := 20 52 if len(buf) < firstBytesCnt { 53 firstBytesCnt = len(buf) 54 } 55 return -1, fmt.Errorf("flash signature not found: first %d bytes are:\n%s", 56 firstBytesCnt, hex.Dump(buf[:firstBytesCnt])) 57 } 58 59 // Buf returns the buffer. 60 // Used mostly for things interacting with the Firmware interface. 61 func (fd *FlashDescriptor) Buf() []byte { 62 return fd.buf 63 } 64 65 // SetBuf sets the buffer. 66 // Used mostly for things interacting with the Firmware interface. 67 func (fd *FlashDescriptor) SetBuf(buf []byte) { 68 fd.buf = buf 69 } 70 71 // Apply calls the visitor on the FlashDescriptor. 72 func (fd *FlashDescriptor) Apply(v Visitor) error { 73 return v.Visit(fd) 74 } 75 76 // ApplyChildren calls the visitor on each child node of FlashDescriptor. 77 func (fd *FlashDescriptor) ApplyChildren(v Visitor) error { 78 return nil 79 } 80 81 // ParseFlashDescriptor parses the ifd from the buffer 82 func (fd *FlashDescriptor) ParseFlashDescriptor() error { 83 if buflen := len(fd.buf); buflen != FlashDescriptorLength { 84 return fmt.Errorf("flash descriptor length not %#x, was %#x", FlashDescriptorLength, buflen) 85 } 86 87 descriptorMapStart, err := FindSignature(fd.buf) 88 if err != nil { 89 return err 90 } 91 fd.DescriptorMapStart = uint(descriptorMapStart) 92 93 // Descriptor Map 94 desc, err := NewFlashDescriptorMap(fd.buf[fd.DescriptorMapStart:]) 95 if err != nil { 96 return err 97 } 98 fd.DescriptorMap = desc 99 100 // Region 101 fd.RegionStart = uint(fd.DescriptorMap.RegionBase) * 0x10 102 region, err := NewFlashRegionSection(fd.buf[fd.RegionStart : fd.RegionStart+uint(FlashRegionSectionSize)]) 103 if err != nil { 104 return err 105 } 106 fd.Region = region 107 108 // Master 109 fd.MasterStart = uint(fd.DescriptorMap.MasterBase) * 0x10 110 master, err := NewFlashMasterSection(fd.buf[fd.MasterStart : fd.MasterStart+uint(FlashMasterSectionSize)]) 111 if err != nil { 112 return err 113 } 114 fd.Master = master 115 116 return nil 117 } 118 119 // FlashImage is the main structure that represents an Intel Flash image. It 120 // implements the Firmware interface. 121 type FlashImage struct { 122 // Holds the raw buffer 123 buf []byte 124 // Holds the Flash Descriptor 125 IFD FlashDescriptor 126 // Actual regions 127 Regions []*TypedFirmware `json:",omitempty"` 128 129 // Metadata for extraction and recovery 130 ExtractPath string 131 FlashSize uint64 132 } 133 134 // Buf returns the buffer. 135 // Used mostly for things interacting with the Firmware interface. 136 func (f *FlashImage) Buf() []byte { 137 return f.buf 138 } 139 140 // SetBuf sets the buffer. 141 // Used mostly for things interacting with the Firmware interface. 142 func (f *FlashImage) SetBuf(buf []byte) { 143 f.buf = buf 144 } 145 146 // Apply calls the visitor on the FlashImage. 147 func (f *FlashImage) Apply(v Visitor) error { 148 return v.Visit(f) 149 } 150 151 // ApplyChildren calls the visitor on each child node of FlashImage. 152 func (f *FlashImage) ApplyChildren(v Visitor) error { 153 if err := f.IFD.Apply(v); err != nil { 154 return err 155 } 156 for _, r := range f.Regions { 157 if err := r.Value.Apply(v); err != nil { 158 return err 159 } 160 } 161 return nil 162 } 163 164 // IsPCH returns whether the flash image has the more recent PCH format, or not. 165 // PCH images have the first 16 bytes reserved, and the 4-bytes signature starts 166 // immediately after. Older images (ICH8/9/10) have the signature at the 167 // beginning. 168 // TODO: Check this. What if we have the signature in both places? I feel like the check 169 // should be IsICH because I expect the ICH to override PCH if the signature exists in 0:4 170 // since in that case 16:20 should be data. If that's the case, FindSignature needs to 171 // be fixed as well 172 func (f *FlashImage) IsPCH() bool { 173 return bytes.Equal(f.buf[16:16+len(FlashSignature)], FlashSignature) 174 } 175 176 // FindSignature looks for the Intel flash signature, and returns its offset 177 // from the start of the image. The PCH images are located at offset 16, while 178 // in ICH8/9/10 they start at 0. If no signature is found, it returns -1. 179 func (f *FlashImage) FindSignature() (int, error) { 180 return FindSignature(f.buf) 181 } 182 183 func (f *FlashImage) String() string { 184 return fmt.Sprintf("FlashImage{Size=%v, Descriptor=%v, Region=%v, Master=%v}", 185 len(f.buf), 186 f.IFD.DescriptorMap.String(), 187 f.IFD.Region.String(), 188 f.IFD.Master.String(), 189 ) 190 } 191 192 func (f *FlashImage) fillRegionGaps() error { 193 // Search for gaps and fill in with unknown regions 194 offset := uint64(FlashDescriptorLength) 195 var newRegions []*TypedFirmware 196 for _, t := range f.Regions { 197 r, _ := t.Value.(Region) 198 nextBase := uint64(r.FlashRegion().BaseOffset()) 199 if nextBase < offset { 200 // Something is wrong, overlapping regions 201 // TODO: print a better error message describing what it overlaps with 202 return fmt.Errorf("overlapping regions! region type %s overlaps with the previous region", 203 r.Type().String()) 204 } 205 if nextBase > offset { 206 // There is a gap, create an unknown region 207 tempFR := &FlashRegion{Base: uint16(offset / RegionBlockSize), 208 Limit: uint16(nextBase/RegionBlockSize) - 1} 209 newRegions = append(newRegions, MakeTyped(&RawRegion{buf: f.buf[offset:nextBase], 210 FRegion: tempFR, 211 RegionType: RegionTypeUnknown})) 212 } 213 offset = uint64(r.FlashRegion().EndOffset()) 214 newRegions = append(newRegions, MakeTyped(r)) 215 } 216 // check for the last region 217 if offset != f.FlashSize { 218 tempFR := &FlashRegion{Base: uint16(offset / RegionBlockSize), 219 Limit: uint16(f.FlashSize/RegionBlockSize) - 1} 220 newRegions = append(newRegions, MakeTyped(&RawRegion{buf: f.buf[offset:f.FlashSize], 221 FRegion: tempFR, 222 RegionType: RegionTypeUnknown})) 223 } 224 f.Regions = newRegions 225 return nil 226 } 227 228 // NewFlashImage tries to create a FlashImage structure, and returns a FlashImage 229 // and an error if any. This only works with images that operate in Descriptor 230 // mode. 231 func NewFlashImage(buf []byte) (*FlashImage, error) { 232 if len(buf) < FlashDescriptorLength { 233 return nil, fmt.Errorf("flash Descriptor Map size too small: expected %v bytes, got %v", 234 FlashDescriptorLength, 235 len(buf), 236 ) 237 } 238 f := FlashImage{FlashSize: uint64(len(buf))} 239 240 // Copy out buffers 241 f.buf = make([]byte, len(buf)) 242 copy(f.buf, buf) 243 f.IFD.buf = make([]byte, FlashDescriptorLength) 244 copy(f.IFD.buf, buf[:FlashDescriptorLength]) 245 246 if err := f.IFD.ParseFlashDescriptor(); err != nil { 247 return nil, err 248 } 249 250 // FlashRegions is an array, make a slice to keep reference to it's content 251 frs := f.IFD.Region.FlashRegions[:] 252 253 // BIOS region has to be valid 254 if !frs[RegionTypeBIOS].Valid() { 255 return nil, fmt.Errorf("no BIOS region: invalid region parameters %v", frs[RegionTypeBIOS]) 256 } 257 258 nr := int(f.IFD.DescriptorMap.NumberOfRegions) 259 // Parse all the regions 260 for i, fr := range frs { 261 // Parse only a smaller number of regions if number of regions isn't 0 262 // Number of regions is deprecated in newer IFDs and is just 0, older IFDs report 263 // the number of regions and have falsely "valid" regions after that number. 264 if nr != 0 && i >= nr { 265 break 266 } 267 if !fr.Valid() { 268 continue 269 } 270 if o := uint64(fr.BaseOffset()); o >= f.FlashSize { 271 fmt.Printf("region %s (%d, %v) out of bounds: BaseOffset %#x, Flash size %#x, skipping...\n", 272 flashRegionTypeNames[FlashRegionType(i)], i, fr, o, f.FlashSize) 273 continue 274 } 275 if o := uint64(fr.EndOffset()); o > f.FlashSize { 276 fmt.Printf("region %s (%d, %v) out of bounds: EndOffset %#x, Flash size %#x, skipping...\n", 277 flashRegionTypeNames[FlashRegionType(i)], i, fr, o, f.FlashSize) 278 continue 279 } 280 if c, ok := regionConstructors[FlashRegionType(i)]; ok { 281 r, err := c(buf[fr.BaseOffset():fr.EndOffset()], &frs[i], FlashRegionType(i)) 282 if err != nil { 283 return nil, err 284 } 285 f.Regions = append(f.Regions, MakeTyped(r)) 286 } 287 } 288 289 // Sort the regions by offset so we can look for gaps 290 sort.Slice(f.Regions, func(i, j int) bool { 291 ri := f.Regions[i].Value.(Region) 292 rj := f.Regions[j].Value.(Region) 293 return ri.FlashRegion().Base < rj.FlashRegion().Base 294 }) 295 296 if err := f.fillRegionGaps(); err != nil { 297 return nil, err 298 } 299 return &f, nil 300 }