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