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