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  */