github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/tools/vpdbootmanager/add.go (about)

     1  // Copyright 2017-2019 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  package main
     6  
     7  import (
     8  	"encoding/json"
     9  	"errors"
    10  	"flag"
    11  	"fmt"
    12  	"net"
    13  	"os"
    14  
    15  	"github.com/mvdan/u-root-coreutils/pkg/boot/systembooter"
    16  	"github.com/mvdan/u-root-coreutils/pkg/vpd"
    17  )
    18  
    19  var dryRun = false
    20  
    21  func add(entrytype string, args []string) error {
    22  	var (
    23  		entry  systembooter.Booter
    24  		vpdDir string
    25  		err    error
    26  	)
    27  	switch entrytype {
    28  	case "netboot":
    29  		if len(args) < 2 {
    30  			return fmt.Errorf("you need to pass method and MAC address")
    31  		}
    32  		entry, vpdDir, err = parseNetbootFlags(args[0], args[1], args[2:])
    33  		if err != nil {
    34  			return err
    35  		}
    36  	case "localboot":
    37  		if len(args) < 1 {
    38  			return fmt.Errorf("you need to provide method")
    39  		}
    40  		entry, vpdDir, err = parseLocalbootFlags(args[0], args[1:])
    41  		if err != nil {
    42  			return err
    43  		}
    44  	default:
    45  		return fmt.Errorf("unknown entry type")
    46  	}
    47  	if dryRun {
    48  		b, err := json.Marshal(entry)
    49  		if err != nil {
    50  			return err
    51  		}
    52  		fmt.Fprintln(os.Stderr, "Using -dryrun, will not write any variable. Content of boot entry:")
    53  		fmt.Println(string(b))
    54  		return nil
    55  	}
    56  	return addBootEntry(entry, vpdDir)
    57  }
    58  
    59  func parseLocalbootFlags(method string, args []string) (*systembooter.LocalBooter, string, error) {
    60  	cfg := &systembooter.LocalBooter{
    61  		Type:   "localboot",
    62  		Method: method,
    63  	}
    64  	var flagVpdDir string
    65  	flg := flag.NewFlagSet("localboot", flag.ExitOnError)
    66  	flg.StringVar(&cfg.KernelArgs, "kernel-args", "", "additional kernel args")
    67  	flg.StringVar(&cfg.Initramfs, "ramfs", "", "path of ramfs to be used for kexec'ing into the target kernel.")
    68  	flg.StringVar(&flagVpdDir, "vpd-dir", vpd.DefaultVpdDir, "VPD dir to use")
    69  	flg.BoolVar(&dryRun, "dryrun", false, "only print values that would be set")
    70  
    71  	switch method {
    72  	case "grub":
    73  		if err := flg.Parse(args); err != nil {
    74  			return nil, flagVpdDir, err
    75  		}
    76  	case "path":
    77  		if len(args) < 2 {
    78  			return nil, "", fmt.Errorf("you need to pass DeviceGUID and Kernel path")
    79  		}
    80  		cfg.DeviceGUID = args[0]
    81  		cfg.Kernel = args[1]
    82  		if err := flg.Parse(args[2:]); err != nil {
    83  			return nil, flagVpdDir, err
    84  		}
    85  	default:
    86  		return nil, flagVpdDir, fmt.Errorf("method needs to be grub or path")
    87  	}
    88  	return cfg, flagVpdDir, nil
    89  }
    90  
    91  func parseNetbootFlags(method, mac string, args []string) (*systembooter.NetBooter, string, error) {
    92  	if method != "dhcpv4" && method != "dhcpv6" {
    93  		return nil, "", fmt.Errorf("method needs to be either dhcpv4 or dhcpv6")
    94  	}
    95  
    96  	_, err := net.ParseMAC(mac)
    97  	if err != nil {
    98  		return nil, "", err
    99  	}
   100  
   101  	cfg := &systembooter.NetBooter{
   102  		Type:   "netboot",
   103  		Method: method,
   104  		MAC:    mac,
   105  	}
   106  	var flagVpdDir string
   107  
   108  	flg := flag.NewFlagSet("netboot", flag.ExitOnError)
   109  	overrideURL := flg.String("override-url", "", "an optional URL used to override the boot file URL used")
   110  	retries := flg.Int("retries", -1, "the number of times a DHCP request should be retried if failed.")
   111  	flg.BoolVar(&dryRun, "dryrun", false, "only print values that would be set")
   112  	flg.StringVar(&flagVpdDir, "vpd-dir", vpd.DefaultVpdDir, "VPD dir to use")
   113  	if err := flg.Parse(args); err != nil {
   114  		return nil, "", err
   115  	}
   116  
   117  	if *overrideURL != "" {
   118  		cfg.OverrideURL = overrideURL
   119  	}
   120  
   121  	if *retries != -1 {
   122  		cfg.Retries = retries
   123  	}
   124  
   125  	return cfg, flagVpdDir, nil
   126  }
   127  
   128  func addBootEntry(cfg systembooter.Booter, vpdDir string) error {
   129  	data, err := json.Marshal(cfg)
   130  	if err != nil {
   131  		return err
   132  	}
   133  	vpdReader := vpd.NewReader()
   134  	vpdReader.VpdDir = vpdDir
   135  	for i := 1; i < vpd.MaxBootEntry; i++ {
   136  		key := fmt.Sprintf("Boot%04d", i)
   137  		if _, err := vpdReader.Get(key, false); err != nil {
   138  			if os.IsNotExist(err) {
   139  				if err := vpdReader.Set(key, data, false); err != nil {
   140  					return err
   141  				}
   142  				return nil
   143  			}
   144  			return err
   145  		}
   146  	}
   147  	return errors.New("Maximum number of boot entries already set")
   148  }
   149  
   150  func set(key string, value string) error {
   151  	return vpd.FlashromRWVpdSet(key, []byte(value), false)
   152  }
   153  
   154  func delete(key string) error {
   155  	return vpd.FlashromRWVpdSet(key, []byte("dummy"), true)
   156  }
   157  
   158  func dump() error {
   159  	return vpd.FlashromVpdDump()
   160  }