]> git.ozlabs.org Git - petitboot/blob - discover/ipmi.c
discover/platform-powerpc: Implement direct IPMI interface
[petitboot] / discover / ipmi.c
1
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <poll.h>
5 #include <string.h>
6 #include <unistd.h>
7
8 #include <sys/ioctl.h>
9 #include <sys/time.h>
10 #include <sys/types.h>
11
12 #include <linux/ipmi.h>
13
14 #include <log/log.h>
15 #include <talloc/talloc.h>
16
17 #include "ipmi.h"
18
19 struct ipmi {
20         int     fd;
21         long    seq;
22 };
23
24 static const char *ipmi_devnode = "/dev/ipmi0";
25
26 bool ipmi_bootdev_is_valid(int x)
27 {
28         switch (x) {
29         case IPMI_BOOTDEV_NONE:
30         case IPMI_BOOTDEV_NETWORK:
31         case IPMI_BOOTDEV_DISK:
32         case IPMI_BOOTDEV_SAFE:
33         case IPMI_BOOTDEV_CDROM:
34         case IPMI_BOOTDEV_SETUP:
35                 return true;
36         }
37
38         return false;
39 }
40
41 static int ipmi_send(struct ipmi *ipmi, uint8_t netfn, uint8_t cmd,
42                 uint8_t *buf, uint16_t len)
43 {
44         struct ipmi_system_interface_addr addr;
45         struct ipmi_req req;
46         int rc;
47
48         memset(&addr, 0, sizeof(addr));
49         addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
50         addr.channel = IPMI_BMC_CHANNEL;
51
52         memset(&req, 0, sizeof(req));
53         req.addr = (unsigned char *)&addr;
54         req.addr_len = sizeof(addr);
55
56         req.msgid = ipmi->seq++;
57
58         req.msg.data = buf;
59         req.msg.data_len = len;
60         req.msg.netfn = netfn;
61         req.msg.cmd = cmd;
62
63         rc = ioctl(ipmi->fd, IPMICTL_SEND_COMMAND, &req);
64         if (rc < 0) {
65                 pb_log("IPMI: send (netfn %d, cmd %d, %d bytes) failed: %m\n",
66                                 netfn, cmd, len);
67                 return -1;
68         }
69
70         return 0;
71 }
72
73 static int ipmi_recv(struct ipmi *ipmi, uint8_t *netfn, uint8_t *cmd,
74                 long *seq, uint8_t *buf, uint16_t *len)
75 {
76         struct ipmi_recv recv;
77         struct ipmi_addr addr;
78         int rc;
79
80         recv.addr = (unsigned char *)&addr;
81         recv.addr_len = sizeof(addr);
82         recv.msg.data = buf;
83         recv.msg.data_len = *len;
84
85         rc = ioctl(ipmi->fd, IPMICTL_RECEIVE_MSG_TRUNC, &recv);
86         if (rc < 0 && errno != EMSGSIZE) {
87                 pb_log("IPMI: recv (%d bytes) failed: %m\n", *len);
88                 return -1;
89         } else if (rc < 0 && errno == EMSGSIZE) {
90                 pb_debug("IPMI: truncated message (netfn %d, cmd %d, "
91                                 "size %d), continuing anyway\n",
92                                 recv.msg.netfn, recv.msg.cmd, *len);
93         }
94
95         *netfn = recv.msg.netfn;
96         *cmd = recv.msg.cmd;
97         *seq = recv.msgid;
98         *len = recv.msg.data_len;
99
100         return 0;
101 }
102
103 int ipmi_transaction(struct ipmi *ipmi, uint8_t netfn, uint8_t cmd,
104                 uint8_t *req_buf, uint16_t req_len,
105                 uint8_t *resp_buf, uint16_t *resp_len,
106                 int timeout_ms)
107 {
108         struct timeval start, now, delta;
109         struct pollfd pollfds[1];
110         int expired_ms, rc;
111
112         rc = ipmi_send(ipmi, netfn, cmd, req_buf, req_len);
113         if (rc)
114                 return rc;
115
116         pollfds[0].fd = ipmi->fd;
117         pollfds[0].events = POLLIN;
118
119         gettimeofday(&start, NULL);
120         expired_ms = 0;
121
122         for (;;) {
123                 uint8_t resp_netfn, resp_cmd;
124                 long seq;
125
126                 rc = poll(pollfds, 1, timeout_ms - expired_ms);
127
128                 if (rc < 0) {
129                         pb_log("IPMI: poll() error %m");
130                         break;
131                 }
132                 if (rc == 0) {
133                         pb_log("IPMI: timeout waiting for response "
134                                         "(netfn %d, cmd %d)\n", netfn, cmd);
135                         break;
136                 }
137
138                 if (!(pollfds[0].revents & POLLIN)) {
139                         pb_log("IPMI: unexpected fd status from poll?\n");
140                         break;
141                 }
142
143                 rc = ipmi_recv(ipmi, &resp_netfn, &resp_cmd, &seq,
144                                 resp_buf, resp_len);
145                 if (rc)
146                         break;
147
148                 if (seq != ipmi->seq - 1) {
149                         pb_log("IPMI: out-of-sequence reply: "
150                                         "exp %ld, got %ld\n",
151                                         ipmi->seq, seq);
152
153                         if (timeout_ms) {
154                                 gettimeofday(&now, NULL);
155                                 timersub(&now, &start, &delta);
156                                 expired_ms = (delta.tv_sec * 1000) +
157                                                 (delta.tv_usec / 1000);
158
159                                 if (expired_ms >= timeout_ms)
160                                         break;
161                         }
162                 } else {
163                         pb_debug("IPMI: netfn(%x->%x), cmd(%x->%x)\n",
164                                         netfn, resp_netfn, cmd, resp_cmd);
165                         return 0;
166                 }
167         }
168
169         return -1;
170 }
171
172 static int ipmi_destroy(void *p)
173 {
174         struct ipmi *ipmi = p;
175         close(ipmi->fd);
176         return 1;
177 }
178
179 struct ipmi *ipmi_open(void *ctx)
180 {
181         struct ipmi *ipmi;
182         int fd;
183
184         fd = open(ipmi_devnode, O_RDWR);
185         if (fd < 0) {
186                 pb_log("IPMI: can't open IPMI device %s: %m\n", ipmi_devnode);
187                 return NULL;
188         }
189
190         ipmi = talloc(ctx, struct ipmi);
191         ipmi->fd = fd;
192         ipmi->seq = 0;
193
194         talloc_set_destructor(ipmi, ipmi_destroy);
195
196         return ipmi;
197 }
198
199 bool ipmi_present(void)
200 {
201         return !access(ipmi_devnode, R_OK | W_OK);
202 }
203