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