github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/cmds/boot/systemboot/main.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  	"flag"
     9  	"log"
    10  	"os"
    11  	"os/exec"
    12  	"os/signal"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/u-root/u-root/pkg/boot/systembooter"
    17  	"github.com/u-root/u-root/pkg/ipmi"
    18  	"github.com/u-root/u-root/pkg/ipmi/ocp"
    19  	"github.com/u-root/u-root/pkg/smbios"
    20  )
    21  
    22  var (
    23  	allowInteractive = flag.Bool("i", true, "Allow user to interrupt boot process and run commands")
    24  	doQuiet          = flag.Bool("q", false, "Disable verbose output")
    25  	interval         = flag.Int("I", 1, "Interval in seconds before looping to the next boot command")
    26  	noDefaultBoot    = flag.Bool("nodefault", false, "Do not attempt default boot entries if regular ones fail")
    27  )
    28  
    29  var defaultBootsequence = [][]string{
    30  	{"fbnetboot", "-userclass", "linuxboot"},
    31  	{"localboot", "-grub"},
    32  }
    33  
    34  // Product list for running IPMI OEM commands
    35  var productList = [3]string{"Tioga Pass", "Mono Lake", "Delta Lake"}
    36  
    37  var selRecorded bool
    38  
    39  func isMatched(productName string) bool {
    40  	for _, v := range productList {
    41  		if strings.HasPrefix(productName, v) {
    42  			return true
    43  		}
    44  	}
    45  	return false
    46  }
    47  
    48  func getBaseboardProductName(si *smbios.Info) (string, error) {
    49  	t2, err := si.GetBaseboardInfo()
    50  	if err != nil {
    51  		log.Printf("Error getting Baseboard Information: %v", err)
    52  		return "", err
    53  	}
    54  	return t2[0].Product, nil
    55  }
    56  
    57  func getSystemFWVersion(si *smbios.Info) (string, error) {
    58  	t0, err := si.GetBIOSInfo()
    59  	if err != nil {
    60  		log.Printf("Error getting BIOS Information: %v", err)
    61  		return "", err
    62  	}
    63  	return t0.Version, nil
    64  }
    65  
    66  func checkCMOSClear(ipmi *ipmi.IPMI) error {
    67  	if cmosclear, bootorder, err := ocp.IsCMOSClearSet(ipmi); cmosclear {
    68  		log.Printf("CMOS clear starts")
    69  		if err = cmosClear(); err != nil {
    70  			return err
    71  		}
    72  		// ToDo: Reset RW_VPD to default values
    73  		if err = ocp.ClearCMOSClearValidBits(ipmi, bootorder); err != nil {
    74  			return err
    75  		}
    76  		addSEL("cmosclear")
    77  		if err = reboot(); err != nil {
    78  			return err
    79  		}
    80  	} else if err != nil {
    81  		return err
    82  	}
    83  
    84  	return nil
    85  }
    86  
    87  func runIPMICommands() {
    88  	i, err := ipmi.Open(0)
    89  	if err != nil {
    90  		log.Printf("Failed to open ipmi device %v, watchdog may still be running", err)
    91  		return
    92  	}
    93  	defer i.Close()
    94  
    95  	if err = i.ShutoffWatchdog(); err != nil {
    96  		log.Printf("Failed to stop watchdog %v.", err)
    97  	} else {
    98  		log.Printf("Watchdog is stopped.")
    99  	}
   100  
   101  	// Below IPMI commands would require SMBIOS data
   102  	si, err := smbios.FromSysfs()
   103  	if err != nil {
   104  		log.Printf("Error reading SMBIOS info: %v", err)
   105  		return
   106  	}
   107  
   108  	if fwVersion, err := getSystemFWVersion(si); err == nil {
   109  		log.Printf("System firmware version: %s", fwVersion)
   110  		if err = i.SetSystemFWVersion(fwVersion); err != nil {
   111  			log.Printf("Failed to set system firmware version to BMC %v.", err)
   112  		}
   113  	}
   114  
   115  	if productName, err := getBaseboardProductName(si); err == nil {
   116  		if isMatched(productName) {
   117  			log.Printf("Running OEM IPMI commands.")
   118  			if err = checkCMOSClear(i); err != nil {
   119  				log.Printf("IPMI CMOS clear err: %v", err)
   120  			}
   121  
   122  			dimmInfo, err := ocp.GetOemIpmiDimmInfo(si)
   123  			if err == nil {
   124  				if err = ocp.SendOemIpmiDimmInfo(i, dimmInfo); err == nil {
   125  					log.Printf("Send the information of DIMMs to BMC.")
   126  				} else {
   127  					log.Printf("Failed to send the information of DIMMs to BMC: %v.", err)
   128  				}
   129  			} else {
   130  				log.Printf("Failed to get the information of DIMMs: %v.", err)
   131  			}
   132  
   133  			processorInfo, err := ocp.GetOemIpmiProcessorInfo(si)
   134  			if err == nil {
   135  				if err = ocp.SendOemIpmiProcessorInfo(i, processorInfo); err == nil {
   136  					log.Printf("Send the information of processors to BMC.")
   137  				} else {
   138  					log.Printf("Failed to send the information of processors to BMC: %v.", err)
   139  				}
   140  			} else {
   141  				log.Printf("Failed to get the information of Processors: %v.", err)
   142  			}
   143  
   144  			if err = ocp.SetOemIpmiPostEnd(i); err == nil {
   145  				log.Printf("Send IPMI POST end to BMC")
   146  			} else {
   147  				log.Printf("Failed to send IPMI POST end to BMC: %v.", err)
   148  			}
   149  
   150  		} else {
   151  			log.Printf("No product name is matched for OEM commands.")
   152  		}
   153  	}
   154  }
   155  
   156  func addSEL(sequence string) {
   157  	var bootErr ipmi.Event
   158  
   159  	i, err := ipmi.Open(0)
   160  	if err != nil {
   161  		log.Printf("Failed to open ipmi device to send SEL %v", err)
   162  		return
   163  	}
   164  	defer i.Close()
   165  
   166  	switch sequence {
   167  	case "fbnetboot":
   168  		bootErr.RecordID = 0
   169  		bootErr.RecordType = ipmi.OEM_NTS_TYPE
   170  		bootErr.OEMNontsDefinedData[0] = 0x28
   171  		bootErr.OEMNontsDefinedData[5] = 0xf0
   172  		for idx := 6; idx < 13; idx++ {
   173  			bootErr.OEMNontsDefinedData[idx] = 0xff
   174  		}
   175  		if err := i.LogSystemEvent(&bootErr); err != nil {
   176  			log.Printf("SEL recorded: %s fail\n", sequence)
   177  		}
   178  	case "cmosclear":
   179  		bootErr.RecordID = 0
   180  		bootErr.RecordType = ipmi.OEM_NTS_TYPE
   181  		bootErr.OEMNontsDefinedData[0] = 0x28
   182  		bootErr.OEMNontsDefinedData[5] = 0xf1
   183  		for idx := 6; idx < 13; idx++ {
   184  			bootErr.OEMNontsDefinedData[idx] = 0xff
   185  		}
   186  		if err := i.LogSystemEvent(&bootErr); err != nil {
   187  			log.Printf("SEL recorded: %s fail\n", sequence)
   188  		}
   189  	default:
   190  	}
   191  }
   192  
   193  func main() {
   194  	flag.Parse()
   195  
   196  	log.Print(`
   197                       ____            _                 _                 _   
   198                      / ___| _   _ ___| |_ ___ _ __ ___ | |__   ___   ___ | |_ 
   199                      \___ \| | | / __| __/ _ \ '_ ` + "`" + ` _ \| '_ \ / _ \ / _ \| __|
   200                       ___) | |_| \__ \ ||  __/ | | | | | |_) | (_) | (_) | |_ 
   201                      |____/ \__, |___/\__\___|_| |_| |_|_.__/ \___/ \___/ \__|
   202                             |___/
   203  `)
   204  	runIPMICommands()
   205  	sleepInterval := time.Duration(*interval) * time.Second
   206  	if *allowInteractive {
   207  		log.Printf("**************************************************************************")
   208  		log.Print("Starting boot sequence, press CTRL-C within 5 seconds to drop into a shell")
   209  		log.Printf("**************************************************************************")
   210  		time.Sleep(5 * time.Second)
   211  	} else {
   212  		signal.Ignore()
   213  	}
   214  
   215  	// Get and show boot entries
   216  	bootEntries := systembooter.GetBootEntries()
   217  	log.Printf("BOOT ENTRIES:")
   218  	for _, entry := range bootEntries {
   219  		log.Printf("    %v) %+v", entry.Name, string(entry.Config))
   220  	}
   221  	for _, entry := range bootEntries {
   222  		log.Printf("Trying boot entry %s: %s", entry.Name, string(entry.Config))
   223  		if err := entry.Booter.Boot(); err != nil {
   224  			log.Printf("Warning: failed to boot with configuration: %+v", entry)
   225  			addSEL(entry.Booter.TypeName())
   226  		}
   227  		if !*doQuiet {
   228  			log.Printf("Sleeping %v before attempting next boot command", sleepInterval)
   229  		}
   230  		time.Sleep(sleepInterval)
   231  	}
   232  
   233  	// if boot entries failed, use the default boot sequence
   234  	log.Printf("Boot entries failed")
   235  
   236  	if !*noDefaultBoot {
   237  		log.Print("Falling back to the default boot sequence")
   238  		for {
   239  			for _, bootcmd := range defaultBootsequence {
   240  				if !*doQuiet {
   241  					bootcmd = append(bootcmd, "-d")
   242  				}
   243  				log.Printf("Running boot command: %v", bootcmd)
   244  				cmd := exec.Command(bootcmd[0], bootcmd[1:]...)
   245  				cmd.Stdout = os.Stdout
   246  				cmd.Stderr = os.Stderr
   247  				if err := cmd.Run(); err != nil {
   248  					log.Printf("Error executing %v: %v", cmd, err)
   249  					if !selRecorded {
   250  						addSEL(bootcmd[0])
   251  					}
   252  				}
   253  			}
   254  			selRecorded = true
   255  
   256  			if !*doQuiet {
   257  				log.Printf("Sleeping %v before attempting next boot command", sleepInterval)
   258  			}
   259  			time.Sleep(sleepInterval)
   260  		}
   261  	}
   262  }