github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/ipmi/ipmi.go (about) 1 // Copyright 2019-2022 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 ipmi implements functions to communicate with the OpenIPMI driver 6 // interface. 7 // For a detailed description of OpenIPMI, see 8 // http://openipmi.sourceforge.net/IPMI.pdf 9 package ipmi 10 11 import ( 12 "bytes" 13 "encoding/binary" 14 "errors" 15 "fmt" 16 "math/rand" 17 "syscall" 18 "time" 19 "unsafe" 20 21 "github.com/vtolstov/go-ioctl" 22 ) 23 24 var ( 25 _IPMICTL_RECEIVE_MSG_TRUNC = ioctl.IOWR(_IPMI_IOC_MAGIC, 11, uintptr(unsafe.Sizeof(response{}))) 26 _IPMICTL_SEND_COMMAND = ioctl.IOR(_IPMI_IOC_MAGIC, 13, uintptr(unsafe.Sizeof(request{}))) 27 28 timeout = time.Second * 10 29 ) 30 31 // IPMI represents access to the IPMI interface. 32 type IPMI struct { 33 *dev 34 } 35 36 // SendRecv sends the IPMI message, receives the response, and returns the 37 // response data. This is recommended for use unless the user must be able to 38 // specify the data pointer and length on their own. 39 func (i *IPMI) SendRecv(netfn NetFn, cmd Command, data []byte) ([]byte, error) { 40 var dataPtr unsafe.Pointer 41 if data != nil { 42 dataPtr = unsafe.Pointer(&data[0]) 43 } 44 msg := Msg{ 45 Netfn: netfn, 46 Cmd: cmd, 47 Data: dataPtr, 48 DataLen: uint16(len(data)), 49 } 50 return i.RawSendRecv(msg) 51 } 52 53 // RawSendRecv sends the IPMI message, receives the response, and returns the 54 // response data. 55 func (i *IPMI) RawSendRecv(msg Msg) ([]byte, error) { 56 addr := &systemInterfaceAddr{ 57 addrType: _IPMI_SYSTEM_INTERFACE_ADDR_TYPE, 58 channel: _IPMI_BMC_CHANNEL, 59 } 60 req := &request{ 61 addr: addr, 62 addrLen: uint32(unsafe.Sizeof(addr)), 63 msgid: rand.Int63(), 64 msg: msg, 65 } 66 67 // Send request. 68 for { 69 switch err := i.dev.SendRequest(req); { 70 case err == syscall.EINTR: 71 continue 72 case err != nil: 73 return nil, fmt.Errorf("ioctlSetReq failed with %v", err) 74 } 75 break 76 } 77 78 buf := make([]byte, _IPMI_BUF_SIZE) 79 recvMsg := Msg{ 80 Data: unsafe.Pointer(&buf[0]), 81 DataLen: _IPMI_BUF_SIZE, 82 } 83 recv := &response{ 84 addr: req.addr, 85 addrLen: req.addrLen, 86 msg: recvMsg, 87 } 88 89 return i.dev.ReceiveResponse(req.msgid, recv, buf) 90 } 91 92 func (i *IPMI) WatchdogRunning() (bool, error) { 93 recv, err := i.SendRecv(_IPMI_NETFN_APP, BMC_GET_WATCHDOG_TIMER, nil) 94 if err != nil { 95 return false, err 96 } 97 98 if len(recv) > 2 && (recv[1]&0x40) != 0 { 99 return true, nil 100 } 101 102 return false, nil 103 } 104 105 func (i *IPMI) ShutoffWatchdog() error { 106 var data [6]byte 107 data[0] = IPM_WATCHDOG_SMS_OS 108 data[1] = IPM_WATCHDOG_NO_ACTION 109 data[2] = 0x00 // pretimeout interval 110 data[3] = IPM_WATCHDOG_CLEAR_SMS_OS 111 data[4] = 0xb8 // countdown lsb (100 ms/count) 112 data[5] = 0x0b // countdown msb - 5 mins 113 114 _, err := i.SendRecv(_IPMI_NETFN_APP, BMC_SET_WATCHDOG_TIMER, data[:6]) 115 if err != nil { 116 return err 117 } 118 119 return nil 120 } 121 122 // marshall converts the Event struct to binary data and the content of returned data is based on the record type 123 func (e *Event) marshall() ([]byte, error) { 124 buf := &bytes.Buffer{} 125 126 if err := binary.Write(buf, binary.LittleEndian, *e); err != nil { 127 return nil, err 128 } 129 130 data := make([]byte, 16) 131 132 // system event record 133 if buf.Bytes()[2] == 0x2 { 134 copy(data[:], buf.Bytes()[:16]) 135 } 136 137 // OEM timestamped 138 if buf.Bytes()[2] >= 0xC0 && buf.Bytes()[2] <= 0xDF { 139 copy(data[0:3], buf.Bytes()[0:3]) 140 copy(data[3:16], buf.Bytes()[16:29]) 141 } 142 143 // OEM non-timestamped 144 if buf.Bytes()[2] >= 0xE0 && buf.Bytes()[2] <= 0xFF { 145 copy(data[0:3], buf.Bytes()[0:3]) 146 copy(data[3:16], buf.Bytes()[29:42]) 147 } 148 149 return data, nil 150 } 151 152 // LogSystemEvent adds an SEL (System Event Log) entry. 153 func (i *IPMI) LogSystemEvent(e *Event) error { 154 data, err := e.marshall() 155 if err != nil { 156 return err 157 } 158 _, err = i.SendRecv(_IPMI_NETFN_STORAGE, BMC_ADD_SEL, data) 159 return err 160 } 161 162 func (i *IPMI) setsysinfo(data *setSystemInfoReq) error { 163 msg := Msg{ 164 Cmd: SET_SYSTEM_INFO_PARAMETERS, 165 Netfn: _IPMI_NETFN_APP, 166 Data: unsafe.Pointer(data), 167 DataLen: 18, 168 } 169 170 if _, err := i.RawSendRecv(msg); err != nil { 171 return err 172 } 173 174 return nil 175 } 176 177 func strcpyPadded(dst []byte, src string) { 178 dstLen := len(dst) 179 if copied := copy(dst, src); copied < dstLen { 180 padding := make([]byte, dstLen-copied) 181 copy(dst[copied:], padding) 182 } 183 } 184 185 // SetSystemFWVersion sets the provided system firmware version to BMC via IPMI. 186 func (i *IPMI) SetSystemFWVersion(version string) error { 187 len := len(version) 188 189 if len == 0 { 190 return fmt.Errorf("version length is 0") 191 } else if len > strlenMax { 192 len = strlenMax 193 } 194 195 var data setSystemInfoReq 196 var index int 197 data.paramSelector = _SYSTEM_FW_VERSION 198 data.setSelector = 0 199 for len > index { 200 if data.setSelector == 0 { // the fisrt block of string data 201 data.strData[0] = _ASCII 202 data.strData[1] = byte(len) 203 strcpyPadded(data.strData[2:], version) 204 index += _SYSTEM_INFO_BLK_SZ - 2 205 } else { 206 strcpyPadded(data.strData[:], version) 207 index += _SYSTEM_INFO_BLK_SZ 208 } 209 210 if err := i.setsysinfo(&data); err != nil { 211 return err 212 } 213 data.setSelector++ 214 } 215 216 return nil 217 } 218 219 func (i *IPMI) GetDeviceID() (*DevID, error) { 220 data, err := i.SendRecv(_IPMI_NETFN_APP, BMC_GET_DEVICE_ID, nil) 221 if err != nil { 222 return nil, err 223 } 224 buf := bytes.NewReader(data[1:]) 225 mcInfo := DevID{} 226 227 if err := binary.Read(buf, binary.LittleEndian, &mcInfo.DeviceID); err != nil { 228 return nil, err 229 } 230 if err := binary.Read(buf, binary.LittleEndian, &mcInfo.DeviceRevision); err != nil { 231 return nil, err 232 } 233 if err := binary.Read(buf, binary.LittleEndian, &mcInfo.FwRev1); err != nil { 234 return nil, err 235 } 236 if err := binary.Read(buf, binary.LittleEndian, &mcInfo.FwRev2); err != nil { 237 return nil, err 238 } 239 if err := binary.Read(buf, binary.LittleEndian, &mcInfo.IpmiVersion); err != nil { 240 return nil, err 241 } 242 if err := binary.Read(buf, binary.LittleEndian, &mcInfo.AdtlDeviceSupport); err != nil { 243 return nil, err 244 } 245 if err := binary.Read(buf, binary.LittleEndian, &mcInfo.ManufacturerID); err != nil { 246 return nil, err 247 } 248 if err := binary.Read(buf, binary.LittleEndian, &mcInfo.ProductID); err != nil { 249 return nil, err 250 } 251 // In some cases we have 11 bytes, in others we may have 15 bytes. Carefully parsing the struct is important here. 252 if buf.Len() > 0 { 253 if err := binary.Read(buf, binary.LittleEndian, &mcInfo.AuxFwRev); err != nil { 254 return nil, err 255 } 256 } 257 258 return &mcInfo, nil 259 } 260 261 func (i *IPMI) setGlobalEnables(enables byte) error { 262 _, err := i.SendRecv(_IPMI_NETFN_APP, BMC_SET_GLOBAL_ENABLES, []byte{enables}) 263 return err 264 } 265 266 func (i *IPMI) getGlobalEnables() ([]byte, error) { 267 return i.SendRecv(_IPMI_NETFN_APP, BMC_GET_GLOBAL_ENABLES, nil) 268 } 269 270 func (i *IPMI) EnableSEL() (bool, error) { 271 // Check if SEL device is supported or not 272 mcInfo, err := i.GetDeviceID() 273 274 if err != nil { 275 return false, err 276 } else if (mcInfo.AdtlDeviceSupport & ADTL_SEL_DEVICE) == 0 { 277 return false, nil 278 } 279 280 data, err := i.getGlobalEnables() 281 if err != nil { 282 return false, err 283 } 284 285 if (data[1] & EN_SYSTEM_EVENT_LOGGING) == 0 { 286 // SEL is not enabled, enable SEL 287 if err = i.setGlobalEnables(data[1] | EN_SYSTEM_EVENT_LOGGING); err != nil { 288 return false, err 289 } 290 } 291 292 return true, nil 293 } 294 295 func (i *IPMI) GetChassisStatus() (*ChassisStatus, error) { 296 data, err := i.SendRecv(_IPMI_NETFN_CHASSIS, BMC_GET_CHASSIS_STATUS, nil) 297 if err != nil { 298 return nil, err 299 } 300 301 buf := bytes.NewReader(data[1:]) 302 303 var status ChassisStatus 304 if err := binary.Read(buf, binary.LittleEndian, &status); err != nil { 305 return nil, err 306 } 307 return &status, nil 308 } 309 310 func (i *IPMI) GetSELInfo() (*SELInfo, error) { 311 data, err := i.SendRecv(_IPMI_NETFN_STORAGE, BMC_GET_SEL_INFO, nil) 312 if err != nil { 313 return nil, err 314 } 315 316 buf := bytes.NewReader(data[1:]) 317 318 var info SELInfo 319 if err := binary.Read(buf, binary.LittleEndian, &info); err != nil { 320 return nil, err 321 } 322 return &info, nil 323 } 324 325 func (i *IPMI) GetLanConfig(channel byte, param byte) ([]byte, error) { 326 var data [4]byte 327 data[0] = channel 328 data[1] = param 329 data[2] = 0 330 data[3] = 0 331 332 return i.SendRecv(_IPMI_NETFN_TRANSPORT, BMC_GET_LAN_CONFIG, data[:4]) 333 } 334 335 func (i *IPMI) RawCmd(param []byte) ([]byte, error) { 336 if len(param) < 2 { 337 return nil, errors.New("not enough parameters given") 338 } 339 340 msg := Msg{ 341 Netfn: NetFn(param[0]), 342 Cmd: Command(param[1]), 343 } 344 if len(param) > 2 { 345 msg.Data = unsafe.Pointer(¶m[2]) 346 } 347 348 msg.DataLen = uint16(len(param) - 2) 349 350 return i.RawSendRecv(msg) 351 } 352 353 // Close closes the file attached to ipmi 354 func (i *IPMI) Close() error { 355 return i.dev.Close() 356 }