github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/ipmi/dev.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 6 7 import ( 8 "fmt" 9 "os" 10 "runtime" 11 "unsafe" 12 13 "golang.org/x/sys/unix" 14 ) 15 16 type dev struct { 17 f *os.File 18 syscalls 19 } 20 21 // SendRequest uses unix.Syscall IOCTL to send a request to the BMC. 22 func (d *dev) SendRequest(req *request) error { 23 _, _, err := d.syscall(unix.SYS_IOCTL, d.File().Fd(), _IPMICTL_SEND_COMMAND, uintptr(unsafe.Pointer(req))) 24 runtime.KeepAlive(req) 25 if err != 0 { 26 return fmt.Errorf("syscall failed with: %v", err) 27 } 28 return nil 29 } 30 31 // ReceiveResponse uses syscall Rawconn to read a response via unix.Syscall IOCTL from the BMC. 32 // It takes the message ID of the request and awaits the response with the same message ID. 33 func (d *dev) ReceiveResponse(msgID int64, resp *response, buf []byte) ([]byte, error) { 34 var result []byte 35 var rerr error 36 readMsg := func(fd uintptr) bool { 37 _, _, errno := d.syscall(unix.SYS_IOCTL, d.File().Fd(), _IPMICTL_RECEIVE_MSG_TRUNC, uintptr(unsafe.Pointer(resp))) 38 runtime.KeepAlive(resp) 39 if errno != 0 { 40 rerr = fmt.Errorf("ioctlGetRecv failed with %v", errno) 41 return false 42 } 43 44 if resp.msgid != msgID { 45 rerr = fmt.Errorf("received wrong message") 46 return false 47 } 48 49 if resp.msg.DataLen >= _IPMI_BUF_SIZE { 50 rerr = fmt.Errorf("data length received too large: %d > %d", resp.msg.DataLen, _IPMI_BUF_SIZE) 51 } else if buf[0] != 0 { 52 rerr = fmt.Errorf("invalid response, expected first byte of response to be 0, got: %v", buf[0]) 53 } else { 54 result = buf[:resp.msg.DataLen:resp.msg.DataLen] 55 rerr = nil 56 } 57 return true 58 } 59 60 // Read response. 61 conn, err := d.fileSyscallConn(d.File()) 62 if err != nil { 63 return nil, fmt.Errorf("failed to get file rawconn: %v", err) 64 } 65 if err := d.fileSetReadDeadline(d.File(), timeout); err != nil { 66 return nil, fmt.Errorf("failed to set read deadline: %v", err) 67 } 68 if err := d.connRead(readMsg, conn); err != nil { 69 return nil, fmt.Errorf("failed to read rawconn: %v", err) 70 } 71 72 return result, rerr 73 } 74 75 // File returns the file of Dev 76 func (d *dev) File() *os.File { 77 return d.f 78 } 79 80 // Close closes the file attached to dev. 81 func (d *dev) Close() error { 82 return d.f.Close() 83 } 84 85 // newDev takes a file and returns a new Dev 86 func newDev(f *os.File) *dev { 87 return &dev{ 88 f: f, 89 syscalls: &realSyscalls{}, 90 } 91 }