github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/uefivars/boot/efiDevicePathProtocol.go (about) 1 // Copyright 2020 the u-root 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 // SPDX-License-Identifier: BSD-3-Clause 6 // 7 8 package boot 9 10 import ( 11 "errors" 12 "fmt" 13 "log" 14 "strings" 15 16 "github.com/u-root/u-root/pkg/uefivars" 17 ) 18 19 var ( 20 Verbose bool 21 22 ErrParse = errors.New("parse error") 23 ErrNotFound = errors.New("Described device not found") 24 25 // Device Path Protocol is used for more than boot entries. Some types 26 // aren't suitable for boot entries, so a resolver doesn't make sense. 27 // 28 // There are probably others which can be used for boot entries, but are 29 // not implemented simply because they have not been needed yet. 30 ErrUnimpl = errors.New("Not implemented") 31 ) 32 33 // ParseFilePathList decodes a FilePathList as found in a boot var. 34 func ParseFilePathList(in []byte) (EfiDevicePathProtocolList, error) { 35 reachedEnd := false 36 b := in 37 var list EfiDevicePathProtocolList 38 loop: 39 for len(b) >= 4 { 40 h := EfiDevicePathProtocolHdr{ 41 ProtoType: EfiDevPathProtoType(b[0]), 42 ProtoSubType: EfiDevPathProtoSubType(b[1]), 43 Length: uefivars.BytesToU16(b[2:4]), 44 } 45 if h.Length < 4 { 46 log.Printf("invalid struct - len %d remain %d: 0x%x", h.Length, len(b), b) 47 return nil, ErrParse 48 } 49 if len(b) < int(h.Length) { 50 log.Printf("undersize %s: %d < %d %x\nin %q", h.ProtoType, len(b)+4, h.Length, b, in) 51 return nil, ErrParse 52 } 53 data := b[4:h.Length] 54 b = b[h.Length:] 55 var p EfiDevicePathProtocol 56 var err error 57 switch h.ProtoType { 58 case DppTypeHw: 59 st := EfiDppHwSubType(h.ProtoSubType) 60 if Verbose { 61 log.Printf("hw subtype %s", st) 62 } 63 switch st { 64 case DppHTypePCI: 65 p, err = ParseDppHwPci(h, data) 66 // case DppHTypePCCARD: 67 // case DppHTypeMMap: 68 // case DppHTypeVendor: 69 // case DppHTypeCtrl: 70 // case DppHTypeBMC: 71 default: 72 log.Printf("unhandled hw subtype %s: %q", st, data) 73 } 74 if err != nil { 75 log.Printf("%s %s: %s", h.ProtoType, st, err) 76 return nil, err 77 } 78 case DppTypeACPI: 79 st := EfiDppACPISubType(h.ProtoSubType) 80 if Verbose { 81 log.Printf("hw subtype %s", st) 82 } 83 switch st { 84 case DppAcpiTypeDevPath: 85 p, err = ParseDppAcpiDevPath(h, data) 86 case DppAcpiTypeExpandedDevPath: 87 p, err = ParseDppAcpiExDevPath(h, data) 88 default: 89 log.Printf("unhandled acpi subtype %s: %q", st, data) 90 } 91 if err != nil { 92 log.Printf("%s %s: %s", h.ProtoType, st, err) 93 return nil, err 94 } 95 case DppTypeMessaging: 96 st := EfiDppMsgSubType(h.ProtoSubType) 97 if Verbose { 98 log.Printf("msg subtype %s", st) 99 } 100 switch st { 101 case DppMsgTypeATAPI: 102 p, err = ParseDppMsgATAPI(h, data) 103 case DppMsgTypeMAC: 104 p, err = ParseDppMsgMAC(h, data) 105 default: 106 log.Printf("unhandled msg subtype %s: %q", st, data) 107 } 108 if err != nil { 109 log.Printf("%s %s: %s", h.ProtoType, st, err) 110 return nil, err 111 } 112 113 case DppTypeMedia: 114 st := EfiDppMediaSubType(h.ProtoSubType) 115 if Verbose { 116 log.Printf("media subtype %s", st) 117 } 118 switch st { 119 case DppMTypeHdd: 120 p, err = ParseDppMediaHdd(h, data) 121 case DppMTypeFilePath: 122 p, err = ParseDppMediaFilePath(h, data) 123 case DppMTypePIWGFF: 124 p, err = ParseDppMediaPIWGFF(h, data) 125 case DppMTypePIWGFV: 126 p, err = ParseDppMediaPIWGFV(h, data) 127 default: 128 log.Printf("unhandled media subtype %s: %q", st, data) 129 } 130 if err != nil { 131 log.Printf("%s %s: %s", h.ProtoType, st, err) 132 return nil, err 133 } 134 case DppTypeEnd: 135 //should be last item on list 136 reachedEnd = true 137 st := EfiDppEndSubType(h.ProtoSubType) 138 if st != DppETypeEndEntire { 139 log.Printf("unexpected end subtype %s", st) 140 } 141 break loop 142 default: 143 log.Printf("unhandled type %s", h.ProtoType) 144 } 145 if p == nil { 146 p = &EfiDevPathRaw{ 147 Hdr: h, 148 Raw: data, 149 } 150 } 151 list = append(list, p) 152 } 153 if !reachedEnd { 154 log.Printf("FilePathList incorrectly terminated") 155 return nil, ErrParse 156 } 157 if len(b) != 0 { 158 log.Printf("remaining bytes %x", b) 159 return nil, ErrParse 160 } 161 return list, nil 162 } 163 164 // EfiDevicePathProtocol identifies a device path. 165 type EfiDevicePathProtocol interface { 166 // Header returns the EfiDevicePathProtocolHdr. 167 Header() EfiDevicePathProtocolHdr 168 169 // ProtoSubTypeStr returns the subtype as human readable. 170 ProtoSubTypeStr() string 171 172 // String returns the path as human readable. 173 String() string 174 175 // Resolver returns an EfiPathSegmentResolver. In the case of filesystems, 176 // this locates and mounts the device. 177 Resolver() (EfiPathSegmentResolver, error) 178 } 179 180 type EfiDevicePathProtocolList []EfiDevicePathProtocol 181 182 func (list EfiDevicePathProtocolList) String() string { 183 var res string 184 for n, dpp := range list { 185 if dpp == nil { 186 log.Fatalf("nil dpp %d %#v", n, list) 187 continue 188 } 189 res += dpp.String() + "/" 190 } 191 return strings.Trim(res, "/") 192 } 193 194 // EfiDevicePathProtocolHdr is three one-byte fields that all DevicePathProtocol 195 // entries begin with. 196 // 197 // typedef struct _EFI_DEVICE_PATH_PROTOCOL { 198 // UINT8 Type; 199 // UINT8 SubType; 200 // UINT8 Length[2]; 201 // } EFI_DEVICE_PATH_PROTOCOL; 202 // 203 // It seems that the only relevant Type (for booting) is media. 204 // 205 // https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_A_Feb14.pdf 206 // pg 286 + 207 type EfiDevicePathProtocolHdr struct { 208 ProtoType EfiDevPathProtoType 209 ProtoSubType EfiDevPathProtoSubType 210 Length uint16 211 } 212 213 type EfiDevPathProtoType uint8 214 215 const ( 216 DppTypeHw EfiDevPathProtoType = iota + 1 //0x01, pg 288 217 DppTypeACPI //0x02, pg 290 218 DppTypeMessaging //0x03, pg 293 219 DppTypeMedia //0x04, pg 319 220 DppTypeBBS //0x05, pg 287 221 DppTypeEnd EfiDevPathProtoType = 0x7f 222 ) 223 224 var efiDevPathProtoTypeStrings = map[EfiDevPathProtoType]string{ 225 DppTypeHw: "HW", 226 DppTypeACPI: "ACPI", 227 DppTypeMessaging: "Messaging", 228 DppTypeMedia: "Media", 229 DppTypeBBS: "BBS", 230 DppTypeEnd: "End", 231 } 232 233 func (e EfiDevPathProtoType) String() string { 234 if s, ok := efiDevPathProtoTypeStrings[e]; ok { 235 return s 236 } 237 return fmt.Sprintf("UNKNOWN-0x%x", uint8(e)) 238 } 239 240 // EfiDevPathProtoSubType is a dpp subtype in the spec. We only define media 241 // and end subtypes; others exist in spec. 242 type EfiDevPathProtoSubType uint8 243 244 // EfiDppEndSubType defines the end of a device path protocol sequence. 245 type EfiDppEndSubType EfiDevPathProtoSubType 246 247 const ( 248 //DppTypeEnd, pg 287-288 249 DppETypeEndStartNew EfiDppEndSubType = 0x01 //only for DppTypeHw? 250 DppETypeEndEntire EfiDppEndSubType = 0xff 251 ) 252 253 var efiDppEndSubTypeStrings = map[EfiDppEndSubType]string{ 254 DppETypeEndEntire: "End", 255 DppETypeEndStartNew: "End one, start another", 256 } 257 258 func (e EfiDppEndSubType) String() string { 259 if s, ok := efiDppEndSubTypeStrings[e]; ok { 260 return s 261 } 262 return fmt.Sprintf("UNKNOWN-0x%x", uint8(e)) 263 } 264 265 // EfiDevPathEnd marks end of EfiDevicePathProtocol. 266 type EfiDevPathEnd struct { 267 Hdr EfiDevicePathProtocolHdr 268 } 269 270 var _ EfiDevicePathProtocol = (*EfiDevPathEnd)(nil) 271 272 func (e *EfiDevPathEnd) Header() EfiDevicePathProtocolHdr { return e.Hdr } 273 274 // ProtoSubTypeStr returns the subtype as human readable. 275 func (e *EfiDevPathEnd) ProtoSubTypeStr() string { 276 return EfiDppEndSubType(e.Hdr.ProtoSubType).String() 277 } 278 279 func (e *EfiDevPathEnd) String() string { return "" } 280 281 func (e *EfiDevPathEnd) Resolver() (EfiPathSegmentResolver, error) { 282 return nil, nil 283 } 284 285 type EfiDevPathRaw struct { 286 Hdr EfiDevicePathProtocolHdr 287 Raw []byte 288 } 289 290 func (e *EfiDevPathRaw) Header() EfiDevicePathProtocolHdr { return e.Hdr } 291 292 // ProtoSubTypeStr returns the subtype as human readable. 293 func (e *EfiDevPathRaw) ProtoSubTypeStr() string { 294 return EfiDppEndSubType(e.Hdr.ProtoSubType).String() 295 } 296 297 func (e *EfiDevPathRaw) String() string { 298 return fmt.Sprintf("RAW(%s,0x%x,%d,0x%x)", e.Hdr.ProtoType, e.Hdr.ProtoSubType, e.Hdr.Length, e.Raw) 299 } 300 301 func (e *EfiDevPathRaw) Resolver() (EfiPathSegmentResolver, error) { 302 return nil, ErrParse 303 } 304 305 /* https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_A_Feb14.pdf 306 Boot0007* UEFI OS HD(1,GPT,81635ccd-1b4f-4d3f-b7b7-f78a5b029f35,0x40,0xf000)/File(\EFI\BOOT\BOOTX64.EFI)..BO 307 308 00000000 01 00 00 00 5e 00 55 00 45 00 46 00 49 00 20 00 |....^.U.E.F.I. .| 309 00000010 4f 00 53 00 00 00[04 01 2a 00 01 00 00 00 40 00 |O.S.....*.....@.| 310 00000020 00 00 00 00 00 00 00 f0 00 00 00 00 00 00 cd 5c |...............\| 311 00000030 63 81 4f 1b 3f 4d b7 b7 f7 8a 5b 02 9f 35 02 02 |c.O.?M....[..5..| 312 00000040 04 04 30 00 5c 00 45 00 46 00 49 00 5c 00 42 00 |..0.\.E.F.I.\.B.| 313 00000050 4f 00 4f 00 54 00 5c 00 42 00 4f 00 4f 00 54 00 |O.O.T.\.B.O.O.T.| 314 00000060 58 00 36 00 34 00 2e 00 45 00 46 00 49 00 00 00 |X.6.4...E.F.I...| 315 00000070 7f ff 04 00]00 00 42 4f |......BO| 316 ^ ^ ][ = end, beginning of dpp list 317 dpp's alone 318 00000000 04 01 2a 00 01 00 00 00 40 00 00 00 00 00 00 00 |..*.....@.......| 319 00000010 00 f0 00 00 00 00 00 00 cd 5c 63 81 4f 1b 3f 4d |.........\c.O.?M| 320 00000020 b7 b7 f7 8a 5b 02 9f 35 02 02*04 04 30 00 5c 00 |....[..5....0.\.| 321 00000030 45 00 46 00 49 00 5c 00 42 00 4f 00 4f 00 54 00 |E.F.I.\.B.O.O.T.| 322 00000040 5c 00 42 00 4f 00 4f 00 54 00 58 00 36 00 34 00 |\.B.O.O.T.X.6.4.| 323 00000050 2e 00 45 00 46 00 49 00 00 00*7f ff 04 00 |..E.F.I.......| 324 ^ *=new dpp (x2) 325 type = 0x04 (media) 326 subtype = 0x01 (hdd) 327 struct len = 42 bytes always 328 part num = 0x01 329 part start = 0x40 330 part size = 0xf000 331 part sig = 0xCD5C63814F1B3F4DB7B7F78A5B029F35 332 part fmt = 0x02 (GPT) 333 sig type = 0x02 (GUID) 334 ===== 335 type = 0x04 (media) 336 subtype = 0x04 (file path) 337 struct len = 0x0030 + 4 338 path = \EFI\BOOT\BOOTX64.EFI 339 ==== 340 type = 0x7f (end) 341 subtype = 0xff (end of entire path) 342 struct len = 4 343 */