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