/
meregion.go
241 lines (207 loc) · 6.47 KB
/
meregion.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
// Copyright 2019 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/binary"
"encoding/hex"
"fmt"
"github.com/linuxboot/fiano/pkg/log"
)
// ME Partition parsing, the goal is to spot a padding in the ME Region
// after the ME partitions so that this region can be shrunk in the IFD.
//
// ME partition informations from http://me.bios.io/ME_blob_format
// MEFTPSignature is the sequence of bytes that an ME Flash Partition
// Table is expected to start with ie "$FPT".
var (
MEFTPSignature = []byte{0x24, 0x46, 0x50, 0x54}
)
const (
// MEPartitionDescriptorMinLength is the min size of the descriptor
MEPartitionDescriptorMinLength = 28
// MEPartitionTableEntryLength is the size of a partition table entry
MEPartitionTableEntryLength = 32
)
// MEFPT is the main structure that represents an ME Flash Partition Table.
type MEFPT struct {
// Holds the raw buffer
buf []byte
PartitionCount uint32
PartitionMapStart int
Entries []MEPartitionEntry
// Metadata for extraction and recovery
ExtractPath string
}
// MEPartitionEntry is an entry in FTP
type MEPartitionEntry struct {
Name MEName
Owner [4]byte
Offset uint32
Length uint32
Reserved [3]uint32
Flags uint32
}
// MEName represent 4 bytes with JSON string support
type MEName [4]byte
// MarshalText converts MEName to a byte range (for JSON)
func (n MEName) MarshalText() ([]byte, error) {
return bytes.TrimRight(n[:], "\x00"), nil
}
// UnmarshalText converts a byte range to MEName (for JSON)
func (n *MEName) UnmarshalText(b []byte) error {
var m MEName
copy(m[:], b)
*n = m
if len(b) > len(m) {
return fmt.Errorf("can’t unmarshal %q to MEName, %d > %d", b, len(b), len(m))
}
return nil
}
func (n MEName) String() string {
b, _ := n.MarshalText()
return string(b)
}
// OffsetIsValid returns true if the entry has a valid offset
func (e MEPartitionEntry) OffsetIsValid() bool {
return e.Offset != 0 && e.Offset != 0xffffffff
}
var mePartitionEntryTypeNames = map[byte]string{0: "Code", 1: "Data", 2: "NVRAM", 3: "Generic", 4: "EFFS", 5: "ROM"}
// Type returns the type of the entry
func (e MEPartitionEntry) Type() string {
t := byte(e.Flags & 0x7f)
if s, ok := mePartitionEntryTypeNames[t]; ok {
return s
}
return fmt.Sprintf("Unknown (%d)", t)
}
// FindMEDescriptor searches for an Intel ME FTP signature
func FindMEDescriptor(buf []byte) (int, error) {
if bytes.Equal(buf[16:16+len(MEFTPSignature)], MEFTPSignature) {
// 16 + 4 since the descriptor starts after the signature
return 16 + len(MEFTPSignature), nil
}
if bytes.Equal(buf[:len(MEFTPSignature)], MEFTPSignature) {
// + 4 since the descriptor starts after the signature
return len(MEFTPSignature), nil
}
return -1, fmt.Errorf("ME Flash Partition Table signature %#02x not found: first 20 bytes are:\n%s", MEFTPSignature, hex.Dump(buf[:20]))
}
// Buf returns the buffer.
// Used mostly for things interacting with the Firmware interface.
func (fp *MEFPT) Buf() []byte {
return fp.buf
}
// SetBuf sets the buffer.
// Used mostly for things interacting with the Firmware interface.
func (fp *MEFPT) SetBuf(buf []byte) {
fp.buf = buf
}
// Apply calls the visitor on the MEFPT.
func (fp *MEFPT) Apply(v Visitor) error {
return v.Visit(fp)
}
// ApplyChildren calls the visitor on each child node of MEFPT.
func (fp *MEFPT) ApplyChildren(v Visitor) error {
return nil
}
// NewMEFPT tries to create a MEFPT
func NewMEFPT(buf []byte) (*MEFPT, error) {
o, err := FindMEDescriptor(buf)
if err != nil {
return nil, err
}
if len(buf) < o+MEPartitionDescriptorMinLength {
return nil, fmt.Errorf("ME section (%#x) too small for ME Flash Partition Table (%#x)", len(buf), o+MEPartitionDescriptorMinLength)
}
fp := &MEFPT{PartitionMapStart: o + MEPartitionDescriptorMinLength}
r := bytes.NewReader(buf[o:])
if err := binary.Read(r, binary.LittleEndian, &fp.PartitionCount); err != nil {
return nil, err
}
l := fp.PartitionMapStart + MEPartitionTableEntryLength*int(fp.PartitionCount)
if len(buf) < l {
return nil, fmt.Errorf("ME section (%#x) too small for %d entries in ME Flash Partition Table (%#x)", len(buf), fp.PartitionCount, l)
}
fp.buf = make([]byte, l)
copy(fp.buf, buf[:l])
if err := fp.parsePartitions(); err != nil {
return nil, err
}
return fp, nil
}
func (fp *MEFPT) parsePartitions() error {
fp.Entries = make([]MEPartitionEntry, fp.PartitionCount)
r := bytes.NewReader(fp.buf[fp.PartitionMapStart:])
return binary.Read(r, binary.LittleEndian, fp.Entries)
}
// MERegion implements Region for a raw chunk of bytes in the firmware image.
type MERegion struct {
FPT *MEFPT
// holds the raw data
buf []byte
// Metadata for extraction and recovery
ExtractPath string
// This is a pointer to the FlashRegion struct laid out in the ifd.
FRegion *FlashRegion
// Region Type as per the IFD
RegionType FlashRegionType
// Computed free space after parsing the partition table
FreeSpaceOffset uint64
}
// SetFlashRegion sets the flash region.
func (rr *MERegion) SetFlashRegion(fr *FlashRegion) {
rr.FRegion = fr
}
// FlashRegion gets the flash region.
func (rr *MERegion) FlashRegion() (fr *FlashRegion) {
return rr.FRegion
}
// NewMERegion creates a new region.
func NewMERegion(buf []byte, r *FlashRegion, rt FlashRegionType) (Region, error) {
rr := &MERegion{FRegion: r, RegionType: rt}
rr.buf = make([]byte, len(buf))
copy(rr.buf, buf)
fp, err := NewMEFPT(buf)
if err != nil {
log.Errorf("error parsing ME Flash Partition Table: %v", err)
return rr, nil
}
rr.FPT = fp
// Compute FreeSpaceOffset
for _, p := range fp.Entries {
if p.OffsetIsValid() {
endOffset := uint64(p.Offset) + uint64(p.Length)
if endOffset > rr.FreeSpaceOffset {
rr.FreeSpaceOffset = endOffset
}
}
}
return rr, nil
}
// Type returns the flash region type.
func (rr *MERegion) Type() FlashRegionType {
return RegionTypeME
}
// Buf returns the buffer.
// Used mostly for things interacting with the Firmware interface.
func (rr *MERegion) Buf() []byte {
return rr.buf
}
// SetBuf sets the buffer.
// Used mostly for things interacting with the Firmware interface.
func (rr *MERegion) SetBuf(buf []byte) {
rr.buf = buf
}
// Apply calls the visitor on the MERegion.
func (rr *MERegion) Apply(v Visitor) error {
return v.Visit(rr)
}
// ApplyChildren calls the visitor on each child node of MERegion.
func (rr *MERegion) ApplyChildren(v Visitor) error {
if rr.FPT == nil {
return nil
}
return rr.FPT.Apply(v)
}