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 }