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  }