github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/uefivars/vars.go (about)

     1  // Copyright 2015-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 uefivars
     9  
    10  import (
    11  	"bytes"
    12  	"fmt"
    13  	"io/ioutil"
    14  	"log"
    15  	"os"
    16  	fp "path/filepath"
    17  	"strings"
    18  	"unicode/utf16"
    19  	"unicode/utf8"
    20  )
    21  
    22  //http://kurtqiao.github.io/uefi/2015/01/13/uefi-boot-manager.html
    23  
    24  //overridden for testing
    25  var EfiVarDir = "/sys/firmware/efi/vars"
    26  
    27  //EfiVar is a generic efi var
    28  type EfiVar struct {
    29  	Uuid, Name string
    30  	Data       []byte
    31  }
    32  type EfiVars []EfiVar
    33  
    34  func ReadVar(uuid, name string) (e EfiVar, err error) {
    35  	path := fp.Join(EfiVarDir, name+"-"+uuid, "data")
    36  	e.Uuid = uuid
    37  	e.Name = name
    38  	e.Data, err = ioutil.ReadFile(path)
    39  	return
    40  }
    41  
    42  // AllVars returns all efi variables
    43  func AllVars() (vars EfiVars) { return ReadVars(nil) }
    44  
    45  // ReadVars returns efi variables matching filter
    46  func ReadVars(filt VarFilter) (vars EfiVars) {
    47  	entries, err := fp.Glob(fp.Join(EfiVarDir, "*-*"))
    48  	if err != nil {
    49  		log.Printf("error reading efi vars: %s", err)
    50  		return
    51  	}
    52  	for _, entry := range entries {
    53  		base := fp.Base(entry)
    54  		n := strings.Count(base, "-")
    55  		if n < 5 {
    56  			log.Printf("skipping %s - not a valid var?", base)
    57  			continue
    58  		}
    59  		components := strings.SplitN(base, "-", 2)
    60  		if filt != nil && !filt(components[1], components[0]) {
    61  			continue
    62  		}
    63  		info, err := os.Stat(entry)
    64  		if err == nil && info.IsDir() {
    65  			v, err := ReadVar(components[1], components[0])
    66  			if err != nil {
    67  				log.Printf("reading efi var %s: %s", base, err)
    68  				continue
    69  			}
    70  			vars = append(vars, v)
    71  		}
    72  	}
    73  	return
    74  }
    75  
    76  // VarFilter is a type of function used to filter efi vars
    77  type VarFilter func(uuid, name string) bool
    78  
    79  // NotFilter returns a filter negating the given filter.
    80  func NotFilter(f VarFilter) VarFilter {
    81  	return func(u, n string) bool { return !f(u, n) }
    82  }
    83  
    84  // AndFilter returns true only if all given filters return true.
    85  func AndFilter(filters ...VarFilter) VarFilter {
    86  	return func(u, n string) bool {
    87  		for _, f := range filters {
    88  			if !f(u, n) {
    89  				return false
    90  			}
    91  		}
    92  		return true
    93  	}
    94  }
    95  
    96  // Filter returns the elements of the list for which the filter function
    97  // returns true.
    98  func (vars EfiVars) Filter(filt VarFilter) EfiVars {
    99  	var res EfiVars
   100  	for _, v := range vars {
   101  		if filt(v.Uuid, v.Name) {
   102  			res = append(res, v)
   103  		}
   104  	}
   105  	return res
   106  }
   107  
   108  // DecodeUTF16 decodes the input as a utf16 string.
   109  // https://gist.github.com/bradleypeabody/185b1d7ed6c0c2ab6cec
   110  func DecodeUTF16(b []byte) (string, error) {
   111  	if len(b)%2 != 0 {
   112  		return "", fmt.Errorf("Must have even length byte slice")
   113  	}
   114  
   115  	u16s := make([]uint16, 1)
   116  	ret := &bytes.Buffer{}
   117  	b8buf := make([]byte, 4)
   118  
   119  	lb := len(b)
   120  	for i := 0; i < lb; i += 2 {
   121  		u16s[0] = BytesToU16(b[i : i+2])
   122  		r := utf16.Decode(u16s)
   123  		n := utf8.EncodeRune(b8buf, r[0])
   124  		ret.Write(b8buf[:n])
   125  	}
   126  
   127  	return ret.String(), nil
   128  }
   129  
   130  // BytesToU16 converts a []byte of length 2 to a uint16.
   131  func BytesToU16(b []byte) uint16 {
   132  	if len(b) != 2 {
   133  		log.Fatalf("bytesToU16: bad len %d (%x)", len(b), b)
   134  	}
   135  	return uint16(b[0]) + (uint16(b[1]) << 8)
   136  }