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