11 #include <sys/types.h>
13 #include <linux/ipmi.h>
16 #include <talloc/talloc.h>
25 static const char *ipmi_devnode = "/dev/ipmi0";
27 bool ipmi_bootdev_is_valid(int x)
30 case IPMI_BOOTDEV_NONE:
31 case IPMI_BOOTDEV_NETWORK:
32 case IPMI_BOOTDEV_DISK:
33 case IPMI_BOOTDEV_SAFE:
34 case IPMI_BOOTDEV_CDROM:
35 case IPMI_BOOTDEV_SETUP:
42 static int ipmi_send(struct ipmi *ipmi, uint8_t netfn, uint8_t cmd,
43 uint8_t *buf, uint16_t len)
45 struct ipmi_system_interface_addr addr;
49 memset(&addr, 0, sizeof(addr));
50 addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
51 addr.channel = IPMI_BMC_CHANNEL;
53 memset(&req, 0, sizeof(req));
54 req.addr = (unsigned char *)&addr;
55 req.addr_len = sizeof(addr);
57 req.msgid = ipmi->seq++;
60 req.msg.data_len = len;
61 req.msg.netfn = netfn;
64 rc = ioctl(ipmi->fd, IPMICTL_SEND_COMMAND, &req);
66 pb_log("IPMI: send (netfn %d, cmd %d, %d bytes) failed: %m\n",
74 static int ipmi_recv(struct ipmi *ipmi, uint8_t *netfn, uint8_t *cmd,
75 long *seq, uint8_t *buf, uint16_t *len)
77 struct ipmi_recv recv;
78 struct ipmi_addr addr;
81 recv.addr = (unsigned char *)&addr;
82 recv.addr_len = sizeof(addr);
84 recv.msg.data_len = *len;
86 rc = ioctl(ipmi->fd, IPMICTL_RECEIVE_MSG_TRUNC, &recv);
87 if (rc < 0 && errno != EMSGSIZE) {
88 pb_log("IPMI: recv (%d bytes) failed: %m\n", *len);
90 } else if (rc < 0 && errno == EMSGSIZE) {
91 pb_debug("IPMI: truncated message (netfn %d, cmd %d, "
92 "size %d), continuing anyway\n",
93 recv.msg.netfn, recv.msg.cmd, *len);
96 *netfn = recv.msg.netfn;
99 *len = recv.msg.data_len;
104 int ipmi_transaction(struct ipmi *ipmi, uint8_t netfn, uint8_t cmd,
105 uint8_t *req_buf, uint16_t req_len,
106 uint8_t *resp_buf, uint16_t *resp_len,
109 struct timeval start, now, delta;
110 struct pollfd pollfds[1];
114 memset(&lock, 0, sizeof(lock));
115 lock.l_type = F_WRLCK;
116 lock.l_whence = SEEK_SET;
117 rc = fcntl(ipmi->fd, F_SETLKW, &lock);
119 pb_log("IPMI: error locking IPMI device: %m\n");
123 rc = ipmi_send(ipmi, netfn, cmd, req_buf, req_len);
127 pollfds[0].fd = ipmi->fd;
128 pollfds[0].events = POLLIN;
130 gettimeofday(&start, NULL);
134 uint8_t resp_netfn, resp_cmd;
137 rc = poll(pollfds, 1, timeout_ms - expired_ms);
140 pb_log("IPMI: poll() error %m");
144 pb_log("IPMI: timeout waiting for response "
145 "(netfn %d, cmd %d)\n", netfn, cmd);
150 if (!(pollfds[0].revents & POLLIN)) {
151 pb_log("IPMI: unexpected fd status from poll?\n");
156 rc = ipmi_recv(ipmi, &resp_netfn, &resp_cmd, &seq,
161 if (seq != ipmi->seq - 1) {
162 pb_log("IPMI: out-of-sequence reply: "
163 "exp %ld, got %ld\n",
167 gettimeofday(&now, NULL);
168 timersub(&now, &start, &delta);
169 expired_ms = (delta.tv_sec * 1000) +
170 (delta.tv_usec / 1000);
172 if (expired_ms >= timeout_ms) {
178 pb_debug("IPMI: netfn(%x->%x), cmd(%x->%x)\n",
179 netfn, resp_netfn, cmd, resp_cmd);
186 lock.l_type = F_UNLCK;
187 if (fcntl(ipmi->fd, F_SETLKW, &lock) == -1)
188 pb_log("IPMI: error unlocking IPMI device: %m\n");
192 static int ipmi_destroy(void *p)
194 struct ipmi *ipmi = p;
199 struct ipmi *ipmi_open(void *ctx)
204 fd = open(ipmi_devnode, O_RDWR | O_CLOEXEC);
206 pb_log("IPMI: can't open IPMI device %s: %m\n", ipmi_devnode);
210 ipmi = talloc(ctx, struct ipmi);
214 talloc_set_destructor(ipmi, ipmi_destroy);
219 bool ipmi_present(void)
221 return !access(ipmi_devnode, R_OK | W_OK);
224 /* Reads and applies an IPMI interface config override, which closely follows
225 * the format of an interface config struct as described in lib/types */
226 int parse_ipmi_interface_override(struct config *config, uint8_t *buf,
229 struct interface_config *ifconf;
230 char *ipstr, *gatewaystr;
231 uint8_t hwsize, ipsize;
232 int addr_type, i = 0;
235 /* Get 1-byte hardware address size and ip address size */
236 memcpy(&hwsize, &buf[i], sizeof(hwsize));
238 memcpy(&ipsize, &buf[i], sizeof(ipsize));
241 if (!hwsize || !ipsize) {
242 pb_log_fn("Empty response\n");
246 /* At the moment only support 6-byte MAC addresses */
247 if (hwsize != sizeof(ifconf->hwaddr)) {
248 pb_log("Unsupported HW address size in network override: %u\n",
253 /* Sanity check the IP address size */
256 addr_len = INET_ADDRSTRLEN;
257 } else if (ipsize == 16) {
258 addr_type = AF_INET6;
259 addr_len = INET6_ADDRSTRLEN;
261 pb_log("Unsupported IP address size: %u\n", ipsize);
265 /* Everything past here is the interface config */
266 ifconf = talloc_zero(config, struct interface_config);
268 pb_log("Failed to allocate network override\n");
272 /* Hardware Address */
273 memcpy(ifconf->hwaddr, &buf[i], hwsize);
276 /* Check 1-byte ignore and method flags */
277 ifconf->ignore = !!buf[i++];
278 ifconf->method = !!buf[i++];
280 if (ifconf->method == CONFIG_METHOD_STATIC) {
281 if (ipsize + ipsize + 1 > len - i) {
282 pb_log("Expected data greater than buffer size\n");
288 ipstr = talloc_array(ifconf, char, addr_len);
289 if (!inet_ntop(addr_type, &buf[i], ipstr, addr_len)) {
290 pb_log("Failed to convert ipaddr: %m\n");
296 /* IP address subnet */
297 ifconf->static_config.address = talloc_asprintf(ifconf,
298 "%s/%u", ipstr, buf[i]);
301 /* Gateway address */
302 gatewaystr = talloc_array(ifconf, char, addr_len);
303 if (!inet_ntop(addr_type, &buf[i], gatewaystr, addr_len)) {
304 pb_log("Failed to convert gateway: %m\n");
308 ifconf->static_config.gateway = gatewaystr;
311 ifconf->override = true;
312 pb_log("Applying IPMI network interface override\n");
314 /* Replace any existing interface config */
315 talloc_free(config->network.interfaces);
316 config->network.n_interfaces = 1;
317 config->network.interfaces = talloc(config, struct interface_config *);
318 config->network.interfaces[0] = ifconf;
323 void ipmi_get_bmc_mac(struct ipmi *ipmi, uint8_t *buf)
325 uint16_t resp_len = 8;
327 uint8_t req[] = { 0x1, 0x5, 0x0, 0x0 };
331 rc = ipmi_transaction(ipmi, IPMI_NETFN_TRANSPORT,
332 IPMI_CMD_TRANSPORT_GET_LAN_PARAMS,
337 debug_buf = format_buffer(ipmi, resp, resp_len);
338 pb_debug_fn("BMC MAC resp [%d][%d]:\n%s\n",
339 rc, resp_len, debug_buf);
340 talloc_free(debug_buf);
342 if (rc == 0 && resp_len > 0) {
343 for (i = 2; i < resp_len; i++) {
344 buf[i - 2] = resp[i];
351 * Retrieve info from the "Get Device ID" IPMI commands.
352 * See Chapter 20.1 in the IPMIv2 specification.
354 void ipmi_get_bmc_versions(struct ipmi *ipmi, struct system_info *info)
356 uint16_t resp_len = 16;
357 uint8_t resp[16], bcd;
361 /* Retrieve info from current side */
362 rc = ipmi_transaction(ipmi, IPMI_NETFN_APP,
363 IPMI_CMD_APP_GET_DEVICE_ID,
368 debug_buf = format_buffer(ipmi, resp, resp_len);
369 pb_debug_fn("BMC version resp [%d][%d]:\n%s\n",
370 rc, resp_len, debug_buf);
371 talloc_free(debug_buf);
373 if (rc == 0 && (resp_len == 12 || resp_len == 16)) {
374 info->bmc_current = talloc_array(info, char *, 4);
375 info->n_bmc_current = 4;
377 info->bmc_current[0] = talloc_asprintf(info, "Device ID: 0x%x",
379 info->bmc_current[1] = talloc_asprintf(info, "Device Rev: 0x%x",
381 bcd = resp[4] & 0x0f;
382 bcd += 10 * (resp[4] >> 4);
383 /* rev1.rev2.aux_revision */
384 info->bmc_current[2] = talloc_asprintf(info,
385 "Firmware version: %u.%02u",
387 if (resp_len == 16) {
388 info->bmc_current[2] = talloc_asprintf_append(
389 info->bmc_current[2],
391 resp[12], resp[13], resp[14], resp[15]);
393 bcd = resp[5] & 0x0f;
394 bcd += 10 * (resp[5] >> 4);
395 info->bmc_current[3] = talloc_asprintf(info, "IPMI version: %u",
398 pb_debug_fn("Failed to retrieve Device ID from IPMI\n");
400 /* Retrieve info from golden side */
401 memset(resp, 0, sizeof(resp));
403 rc = ipmi_transaction(ipmi, IPMI_NETFN_AMI,
404 IPMI_CMD_APP_GET_DEVICE_ID_GOLDEN,
409 debug_buf = format_buffer(ipmi, resp, resp_len);
410 pb_debug_fn("BMC golden resp [%d][%d]:\n%s\n",
411 rc, resp_len, debug_buf);
412 talloc_free(debug_buf);
414 if (rc == 0 && (resp_len == 12 || resp_len == 16)) {
415 info->bmc_golden = talloc_array(info, char *, 4);
416 info->n_bmc_golden = 4;
418 info->bmc_golden[0] = talloc_asprintf(info, "Device ID: 0x%x",
420 info->bmc_golden[1] = talloc_asprintf(info, "Device Rev: 0x%x",
422 bcd = resp[4] & 0x0f;
423 bcd += 10 * (resp[4] >> 4);
424 /* rev1.rev2.aux_revision */
425 info->bmc_golden[2] = talloc_asprintf(info,
426 "Firmware version: %u.%02u",
428 if (resp_len == 16) {
429 info->bmc_golden[2] = talloc_asprintf_append(
432 resp[12], resp[13], resp[14], resp[15]);
434 bcd = resp[5] & 0x0f;
435 bcd += 10 * (resp[5] >> 4);
436 info->bmc_golden[3] = talloc_asprintf(info, "IPMI version: %u",
439 pb_debug_fn("Failed to retrieve Golden Device ID from IPMI\n");