github.com/linuxboot/fiano@v1.2.0/cmds/create-ffs/create-ffs.go (about) 1 // Copyright 2019 the LinuxBoot 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 package main 6 7 import ( 8 "crypto/sha1" 9 "errors" 10 "flag" 11 "fmt" 12 "log" 13 "os" 14 "strings" 15 16 "github.com/linuxboot/fiano/pkg/guid" 17 "github.com/linuxboot/fiano/pkg/uefi" 18 "github.com/linuxboot/fiano/pkg/visitors" 19 ) 20 21 var ( 22 debug = flag.Bool("d", false, "Enable debug prints") 23 outfile = flag.String("o", "", "output file, default is stdout") 24 name = flag.String("name", "", "Name to include in UI section") 25 filetype = flag.String("type", "DRIVER", "UEFI filetype") 26 version = flag.String("version", "1.0", "File version") 27 guidString = flag.String("guid", "", "File GUID") 28 depex = flag.String("depex", "", "Space or comma separated protocol guid dependencies or TRUE") 29 compress = flag.Bool("compress", false, "Wrap section data in a compressed section") 30 auto = flag.Bool("auto", false, "Attempt to determine section types from file extensions") 31 32 printf = func(string, ...interface{}) {} 33 ) 34 35 const ( 36 usageString = "Usage: create-ffs [flags] file.efi" 37 ) 38 39 func createDepExes(deps string) ([]uefi.DepExOp, error) { 40 var err error 41 ops := []uefi.DepExOp{} 42 43 if strings.ToUpper(deps) == "TRUE" { 44 // Create just "TRUE" and "END" for now, but this feels unnecessary to me. 45 ops = append(ops, uefi.DepExOp{OpCode: "TRUE"}) 46 ops = append(ops, uefi.DepExOp{OpCode: "END"}) 47 return ops, nil 48 } 49 50 // we expect a space or comma separated list of GUIDs, split by both. 51 guidSpace := strings.Split(deps, " ") 52 guids := []string{} 53 for _, g := range guidSpace { 54 guids = append(guids, strings.Split(g, ",")...) 55 } 56 57 for _, guidStr := range guids { 58 var g *guid.GUID 59 60 if g, err = guid.Parse(guidStr); err != nil { 61 return nil, err 62 } 63 printf("depex guid requested: %v", *g) 64 ops = append(ops, uefi.DepExOp{OpCode: "PUSH", GUID: g}) 65 } 66 67 // Append an "AND" for n-1 pushes. 68 for i := 1; i < len(guids); i++ { 69 ops = append(ops, uefi.DepExOp{OpCode: "AND"}) 70 } 71 72 ops = append(ops, uefi.DepExOp{OpCode: "END"}) 73 return ops, nil 74 } 75 76 func parseFlags() (fType uefi.FVFileType, fGUID *guid.GUID, depOps []uefi.DepExOp, err error) { 77 var ok bool 78 79 if *debug { 80 printf = log.Printf 81 } 82 83 // Check filetypes 84 fType, ok = uefi.NamesToFileType[*filetype] 85 if !ok { 86 validTypes := []string{} 87 for k := range uefi.NamesToFileType { 88 validTypes = append(validTypes, k) 89 } 90 err = fmt.Errorf("unable to get EFI File type, got %v, expected values in\n{%v}", 91 *filetype, strings.Join(validTypes, ", ")) 92 return 93 } 94 95 if *guidString != "" { 96 fGUID, err = guid.Parse(*guidString) 97 if err != nil { 98 return 99 } 100 } else if *name != "" { 101 // We sha1 the name to get a reproducible GUID. 102 fGUID = &guid.GUID{} 103 sum := sha1.Sum([]byte(*name)) 104 copy(fGUID[:], sum[:guid.Size]) 105 } else { 106 err = errors.New("no GUID or name provided, please provide at least one") 107 return 108 } 109 110 if *outfile == "" { 111 err = errors.New("we don't currently support dumping to stdout, please specify an output file") 112 return 113 } 114 115 if *depex != "" { 116 depOps, err = createDepExes(*depex) 117 if err != nil { 118 err = fmt.Errorf("can't parse depex guids, got %v", err) 119 } 120 } 121 122 return 123 } 124 125 func usage() { 126 log.Print(usageString) 127 flag.PrintDefaults() 128 os.Exit(1) 129 } 130 131 func main() { 132 var fType uefi.FVFileType 133 var fGUID *guid.GUID 134 var depOps []uefi.DepExOp 135 var err error 136 137 flag.Parse() 138 if fType, fGUID, depOps, err = parseFlags(); err != nil { 139 log.Fatal(err) 140 } 141 142 if flag.NArg() != 1 { 143 usage() 144 } 145 146 secData, err := os.ReadFile(flag.Arg(0)) 147 if err != nil { 148 log.Fatal(err) 149 } 150 151 printf("type requested: %v", fType) 152 printf("name requested: %v", *name) 153 printf("version requested: %v", *version) 154 printf("guid: %v", fGUID) 155 156 secType := uefi.SectionTypeRaw 157 switch fType { 158 case uefi.FVFileTypeApplication, uefi.FVFileTypeCombinedSMMDXE, 159 uefi.FVFileTypeCombinedPEIMDriver, uefi.FVFileTypeDriver: 160 secType = uefi.SectionTypePE32 161 } 162 163 file := &uefi.File{} 164 file.Header.Type = fType 165 file.Header.GUID = *fGUID 166 file.Header.State = 0xF8 167 file.Header.Attributes = 0x40 168 169 mainSection := &uefi.Section{} 170 mainSection.SetType(secType) 171 mainSection.SetBuf(secData) 172 mainSection.GenSecHeader() 173 file.Sections = append(file.Sections, mainSection) 174 printf("selected section type: %v", mainSection.Header.Type) 175 176 if *name != "" { 177 s := &uefi.Section{} 178 s.SetType(uefi.SectionTypeUserInterface) 179 s.Name = *name 180 file.Sections = append(file.Sections, s) 181 } 182 183 if *version != "" { 184 s := &uefi.Section{} 185 s.SetType(uefi.SectionTypeVersion) 186 s.Version = *version 187 file.Sections = append(file.Sections, s) 188 } 189 190 if *depex != "" { 191 s := &uefi.Section{} 192 s.SetType(uefi.SectionTypeDXEDepEx) 193 s.DepEx = depOps 194 file.Sections = append(file.Sections, s) 195 } 196 197 save := &visitors.Save{DirPath: *outfile} 198 199 err = file.Apply(save) 200 if err != nil { 201 log.Fatal(err) 202 } 203 204 if *debug { 205 // Dump file json for checking 206 jsonv := &visitors.JSON{W: os.Stdout} 207 if err = file.Apply(jsonv); err != nil { 208 log.Fatal(err) 209 } 210 } 211 }