xref: /openbmc/ipmitool/lib/ipmi_kontronoem.c (revision 531569ec)
1 /*
2  * Copyright (c) 2004 Kontron Canada, Inc.  All Rights Reserved.
3  *
4  * Base on code from
5  * Copyright (c) 2003 Sun Microsystems, Inc.  All Rights Reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * Redistribution of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  *
14  * Redistribution in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in the
16  * documentation and/or other materials provided with the distribution.
17  *
18  * Neither the name of Sun Microsystems, Inc. or the names of
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * This software is provided "AS IS," without a warranty of any kind.
23  * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
24  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
25  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
26  * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
27  * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
28  * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL
29  * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
30  * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
31  * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
32  * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
33  * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
34  */
35 
36 /*
37  * Tue Mar 7 14:36:12 2006
38  * <stephane.filion@ca.kontron.com>
39  *
40  * This code implements an Kontron OEM proprietary commands.
41  */
42 #include <string.h>
43 #include <ipmitool/helper.h>
44 #include <ipmitool/log.h>
45 #include <ipmitool/ipmi.h>
46 #include <ipmitool/ipmi_intf.h>
47 #include <ipmitool/ipmi_fru.h>
48 
49 extern int verbose;
50 extern int read_fru_area(struct ipmi_intf *intf, struct fru_info *fru,
51 		uint8_t id, uint32_t offset, uint32_t length,
52 		uint8_t *frubuf);
53 extern int write_fru_area(struct ipmi_intf * intf, struct fru_info *fru,
54 		uint8_t id, uint16_t soffset,
55 		uint16_t doffset,  uint16_t length,
56 		uint8_t *pFrubuf);
57 extern char *get_fru_area_str(uint8_t *data, uint32_t *offset);
58 
59 static void ipmi_kontron_help(void);
60 static int ipmi_kontron_set_serial_number(struct ipmi_intf *intf);
61 static int ipmi_kontron_set_mfg_date (struct ipmi_intf *intf);
62 static void ipmi_kontron_nextboot_help(void);
63 static int ipmi_kontron_nextboot_set(struct ipmi_intf *intf, int argc,
64 		char **argv);
65 static int ipmi_kontronoem_send_set_large_buffer(struct ipmi_intf *intf,
66 		unsigned char channel, unsigned char size);
67 
68 static char *bootdev[] = {"BIOS", "FDD", "HDD", "CDROM", "network", 0};
69 
70 int
71 ipmi_kontronoem_main(struct ipmi_intf *intf, int argc, char **argv)
72 {
73 	int rc = 0;
74 	if (argc == 0) {
75 		lprintf(LOG_ERR, "Not enough parameters given.");
76 		ipmi_kontron_help();
77 		return (-1);
78 	}
79 	if (strncmp(argv[0], "help", 4) == 0) {
80 		ipmi_kontron_help();
81 		rc = 0;
82 	} else if (!strncmp(argv[0], "setsn", 5)) {
83 		if (argc < 1) {
84 			printf("fru setsn\n");
85 			return (-1);
86 		}
87 		if (ipmi_kontron_set_serial_number(intf) > 0) {
88 			printf("FRU serial number setted successfully\n");
89 		} else {
90 			printf("FRU serial number set failed\n");
91 			rc = (-1);
92 		}
93 	} else if (!strncmp(argv[0], "setmfgdate", 10)) {
94 		if (argc < 1) {
95 			printf("fru setmfgdate\n");
96 			return (-1);
97 		}
98 		if (ipmi_kontron_set_mfg_date(intf) > 0) {
99 			printf("FRU manufacturing date setted successfully\n");
100 		} else {
101 			printf("FRU manufacturing date set failed\n");
102 			rc = (-1);
103 		}
104 	} else if (!strncmp(argv[0], "nextboot", 8)) {
105 		if (argc < 2) {
106 			lprintf(LOG_ERR, "Not enough parameters given.");
107 			ipmi_kontron_nextboot_help();
108 			return (-1);
109 		}
110 		rc = ipmi_kontron_nextboot_set(intf, (argc - 1), (argv + 1));
111 		if (rc == 0) {
112 			printf("Nextboot set successfully\n");
113 		} else {
114 			printf("Nextboot set failed\n");
115 			rc = (-1);
116 		}
117 	} else  {
118 		lprintf(LOG_ERR, "Invalid Kontron command: %s", argv[0]);
119 		ipmi_kontron_help();
120 		rc = (-1);
121 	}
122 	return rc;
123 }
124 
125 static void
126 ipmi_kontron_help(void)
127 {
128 	printf("Kontron Commands:  setsn setmfgdate nextboot\n");
129 }
130 
131 int
132 ipmi_kontronoem_set_large_buffer(struct ipmi_intf *intf, unsigned char size)
133 {
134 	uint8_t error_occurs = 0;
135 	uint32_t prev_target_addr = intf->target_addr ;
136 	if (intf->target_addr > 0 && (intf->target_addr != intf->my_addr)) {
137 		intf->target_addr = intf->my_addr;
138 		printf("Set local big buffer\n");
139 		if (ipmi_kontronoem_send_set_large_buffer(intf, 0x0e, size) == 0) {
140 			printf("Set local big buffer:success\n");
141 		} else {
142 			error_occurs = 1;
143 		}
144 		if (error_occurs == 0) {
145 			if (ipmi_kontronoem_send_set_large_buffer(intf, 0x00, size) == 0) {
146 				printf("IPMB was set\n");
147 			} else {
148 				/* Revert back the previous set large buffer */
149 				error_occurs = 1;
150 				ipmi_kontronoem_send_set_large_buffer( intf, 0x0e, 0 );
151 			}
152 		}
153 		/* Restore target address */
154 		intf->target_addr = prev_target_addr;
155 	}
156 	if (error_occurs == 0) {
157 		if(ipmi_kontronoem_send_set_large_buffer(intf, 0x0e, size) == 0) {
158 			/* printf("Set remote big buffer\n"); */
159 		} else {
160 			if (intf->target_addr > 0  && (intf->target_addr != intf->my_addr)) {
161 				/* Error occurs revert back the previous set large buffer */
162 				intf->target_addr = intf->my_addr;
163 				/* ipmi_kontronoem_send_set_large_buffer(intf, 0x00, 0); */
164 				ipmi_kontronoem_send_set_large_buffer(intf, 0x0e, 0);
165 				intf->target_addr = prev_target_addr;
166 			}
167 		}
168 	}
169 	return error_occurs;
170 }
171 
172 int
173 ipmi_kontronoem_send_set_large_buffer(struct ipmi_intf *intf,
174 		unsigned char channel, unsigned char size)
175 {
176 	struct ipmi_rs *rsp;
177 	struct ipmi_rq req;
178 	uint8_t msg_data[2];
179 	memset(msg_data, 0, sizeof(msg_data));
180 	/* channel =~ 0x0e => Currently running interface */
181 	msg_data[0] = channel;
182 	msg_data[1] = size;
183 	memset(&req, 0, sizeof(req));
184 	req.msg.netfn = 0x3E;
185 	/* Set Channel Buffer Length - OEM */
186 	req.msg.cmd = 0x82;
187 	req.msg.data = msg_data;
188 	req.msg.data_len = 2;
189 	req.msg.lun = 0x00;
190 	rsp = intf->sendrecv(intf, &req);
191 	if (rsp == NULL)  {
192 		printf("Cannot send large buffer command\n");
193 		return(-1);
194 	} else if (rsp->ccode > 0)  {
195 		printf("Invalid length for the selected interface (%s) %d\n",
196 				val2str(rsp->ccode, completion_code_vals), rsp->ccode);
197 		return(-1);
198 	}
199 	return 0;
200 }
201 
202 /* ipmi_fru_set_serial_number -  Set the Serial Number in FRU
203  *
204  * @intf: ipmi interface
205  * @id: fru id
206  *
207  * returns -1 on error
208  * returns 1 if successful
209  */
210 static int
211 ipmi_kontron_set_serial_number(struct ipmi_intf *intf)
212 {
213 	struct fru_header header;
214 	struct fru_info fru;
215 	struct ipmi_rs *rsp;
216 	struct ipmi_rq req;
217 	char *sn;
218 	char *fru_area;
219 	uint8_t checksum;
220 	uint8_t *fru_data;
221 	uint8_t msg_data[4];
222 	uint8_t sn_size;
223 	uint32_t board_sec_len;
224 	uint32_t fru_data_offset;
225 	uint32_t fru_data_offset_tmp;
226 	uint32_t i;
227 	uint32_t prod_sec_len;
228 
229 	sn = NULL;
230 	fru_data = NULL;
231 
232 	memset(msg_data, 0, 4);
233 	msg_data[0] = 0xb4;
234 	msg_data[1] = 0x90;
235 	msg_data[2] = 0x91;
236 	msg_data[3] = 0x8b;
237 
238 	memset(&req, 0, sizeof(req));
239 	req.msg.netfn = 0x3E;
240 	req.msg.cmd = 0x0C;
241 	req.msg.data = msg_data;
242 	req.msg.data_len = 4;
243 	/* Set Lun, necessary for this oem command */
244 	req.msg.lun = 0x03;
245 	rsp = intf->sendrecv(intf, &req);
246 	if (rsp == NULL) {
247 		printf(" Device not present (No Response)\n");
248 		return (-1);
249 	} else if (rsp->ccode > 0) {
250 		printf(" This option is not implemented for this board\n");
251 		return (-1);
252 	}
253 	sn_size = rsp->data_len;
254 	sn = malloc(sn_size + 1);
255 	if (sn == NULL) {
256 		lprintf(LOG_ERR, "ipmitool: malloc failure");
257 		return (-1);
258 	}
259 	memset(sn, 0, sn_size + 1);
260 	memcpy(sn, rsp->data, sn_size);
261 	if (verbose >= 1) {
262 		printf("Original serial number is : [%s]\n", sn);
263 	}
264 	memset(msg_data, 0, 4);
265 	msg_data[0] = 0;
266 	memset(&req, 0, sizeof(req));
267 	req.msg.netfn = IPMI_NETFN_STORAGE;
268 	req.msg.cmd = GET_FRU_INFO;
269 	req.msg.data = msg_data;
270 	req.msg.data_len = 1;
271 	rsp = intf->sendrecv(intf, &req);
272 	if (rsp == NULL) {
273 		printf(" Device not present (No Response)\n");
274 		free(sn);
275 		sn = NULL;
276 		return (-1);
277 	} else if (rsp->ccode > 0) {
278 		printf(" Device not present (%s)\n",
279 				val2str(rsp->ccode, completion_code_vals));
280 		free(sn);
281 		sn = NULL;
282 		return (-1);
283 	}
284 	memset(&fru, 0, sizeof(fru));
285 	fru.size = (rsp->data[1] << 8) | rsp->data[0];
286 	fru.access = rsp->data[2] & 0x1;
287 	if (fru.size < 1) {
288 		printf(" Invalid FRU size %d", fru.size);
289 		free(sn);
290 		sn = NULL;
291 		return (-1);
292 	}
293 	/* retrieve the FRU header */
294 	msg_data[0] = 0;
295 	msg_data[1] = 0;
296 	msg_data[2] = 0;
297 	msg_data[3] = 8;
298 
299 	memset(&req, 0, sizeof(req));
300 	req.msg.netfn = IPMI_NETFN_STORAGE;
301 	req.msg.cmd = GET_FRU_DATA;
302 	req.msg.data = msg_data;
303 	req.msg.data_len = 4;
304 	rsp = intf->sendrecv(intf, &req);
305 	if (rsp == NULL) {
306 		printf(" Device not present (No Response)\n");
307 		free(sn);
308 		sn = NULL;
309 		return (-1);
310 	} else if (rsp->ccode > 0) {
311 		printf(" Device not present (%s)\n",
312 				val2str(rsp->ccode, completion_code_vals));
313 		free(sn);
314 		sn = NULL;
315 		return (-1);
316 	}
317 	if (verbose > 1) {
318 		printbuf(rsp->data, rsp->data_len, "FRU DATA");
319 	}
320 	memcpy(&header, rsp->data + 1, 8);
321 	if (header.version != 1) {
322 		printf(" Unknown FRU header version 0x%02x",
323 				header.version);
324 		free(sn);
325 		sn = NULL;
326 		return(-1);
327 	}
328 	/* Set the Board Section */
329 	board_sec_len = (header.offset.product * 8) - (header.offset.board * 8);
330 	fru_data = malloc(fru.size);
331 	if (fru_data == NULL) {
332 		lprintf(LOG_ERR, "ipmitool: malloc failure");
333 		free(sn);
334 		sn = NULL;
335 		return (-1);
336 	}
337 	memset(fru_data, 0, fru.size);
338 	if (read_fru_area(intf, &fru, 0, (header.offset.board * 8),
339 				board_sec_len, fru_data) < 0) {
340 		free(sn);
341 		sn = NULL;
342 		free(fru_data);
343 		fru_data = NULL;
344 		return (-1);
345 	}
346 	/* Position at Board Manufacturer */
347 	fru_data_offset = (header.offset.board * 8) + 6;
348 	fru_area = get_fru_area_str(fru_data, &fru_data_offset);
349 	if (fru_area != NULL) {
350 		free(fru_area);
351 		fru_area = NULL;
352 	}
353 	/* Position at Board Product Name */
354 	fru_area = get_fru_area_str(fru_data, &fru_data_offset);
355 	if (fru_area != NULL) {
356 		free(fru_area);
357 		fru_area = NULL;
358 	}
359 	fru_data_offset_tmp = fru_data_offset;
360 	/* Position at Serial Number */
361 	fru_area = get_fru_area_str(fru_data, &fru_data_offset_tmp);
362 	if (fru_area == NULL) {
363 		lprintf(LOG_ERR, "Failed to read FRU Area string.");
364 		free(fru_data);
365 		fru_data = NULL;
366 		free(sn);
367 		sn = NULL;
368 		return (-1);
369 	}
370 
371 	fru_data_offset++;
372 	if (strlen(fru_area) != sn_size) {
373 		printf("The length of the serial number in the FRU Board Area is wrong.\n");
374 		free(sn);
375 		sn = NULL;
376 		free(fru_data);
377 		fru_data = NULL;
378 		free(fru_area);
379 		fru_area = NULL;
380 		return(-1);
381 	} else {
382 		free(fru_area);
383 		fru_area = NULL;
384 	}
385 	/* Copy the new serial number in the board section saved in memory*/
386 	memcpy(fru_data + fru_data_offset, sn, sn_size);
387 	checksum = 0;
388 	/* Calculate Header Checksum */
389 	for(i = (header.offset.board * 8);
390 			i < (((header.offset.board * 8) + board_sec_len) - 2);
391 			i++) {
392 		checksum += fru_data[i];
393 	}
394 	checksum = (~checksum) + 1;
395 	fru_data[(header.offset.board * 8) + board_sec_len - 1] = checksum;
396 	/* Write the new FRU Board section */
397 	if (write_fru_area(intf, &fru, 0, (header.offset.board * 8),
398 				(header.offset.board * 8),
399 				board_sec_len, fru_data) < 0) {
400 		free(sn);
401 		sn = NULL;
402 		free(fru_data);
403 		fru_data = NULL;
404 		free(fru_area);
405 		fru_area = NULL;
406 		return(-1);
407 	}
408 	/* Set the Product Section */
409 	prod_sec_len = (header.offset.multi * 8) - (header.offset.product * 8);
410 	if (read_fru_area(intf, &fru, 0, (header.offset.product * 8),
411 				prod_sec_len, fru_data) < 0) {
412 		free(sn);
413 		sn = NULL;
414 		free(fru_data);
415 		fru_data = NULL;
416 		free(fru_area);
417 		fru_area = NULL;
418 		return(-1);
419 	}
420 	/* Position at Product Manufacturer */
421 	fru_data_offset = (header.offset.product * 8) + 3;
422 	fru_area = get_fru_area_str(fru_data, &fru_data_offset);
423 	if (fru_area != NULL) {
424 		free(fru_area);
425 		fru_area = NULL;
426 	}
427 	/* Position at Product Name */
428 	fru_area = get_fru_area_str(fru_data, &fru_data_offset);
429 	if (fru_area != NULL) {
430 		free(fru_area);
431 		fru_area = NULL;
432 	}
433 	/* Position at Product Part */
434 	fru_area = get_fru_area_str(fru_data, &fru_data_offset);
435 	if (fru_area != NULL) {
436 		free(fru_area);
437 		fru_area = NULL;
438 	}
439 	/* Position at Product Version */
440 	fru_area = get_fru_area_str(fru_data, &fru_data_offset);
441 	if (fru_area != NULL) {
442 		free(fru_area);
443 		fru_area = NULL;
444 	}
445 	fru_data_offset_tmp = fru_data_offset;
446 	/* Position at Serial Number */
447 	fru_area = get_fru_area_str(fru_data, &fru_data_offset_tmp);
448 	if (fru_area == NULL) {
449 		lprintf(LOG_ERR, "Failed to read FRU Area string.");
450 		free(sn);
451 		sn = NULL;
452 		free(fru_data);
453 		fru_data = NULL;
454 		return (-1);
455 	}
456 	fru_data_offset ++;
457 	if (strlen(fru_area) != sn_size) {
458 		free(sn);
459 		sn = NULL;
460 		free(fru_data);
461 		fru_data = NULL;
462 		free(fru_area);
463 		fru_area = NULL;
464 		printf("The length of the serial number in the FRU Product Area is wrong.\n");
465 		return(-1);
466 	}
467 	/* Copy the new serial number in the product section saved in memory*/
468 	memcpy(fru_data + fru_data_offset, sn, sn_size);
469 	checksum = 0;
470 	/* Calculate Header Checksum */
471 	for (i = (header.offset.product * 8);
472 			i < (((header.offset.product * 8) + prod_sec_len) - 2);
473 			i ++) {
474 		checksum += fru_data[i];
475 	}
476 	checksum = (~checksum) + 1;
477 	fru_data[(header.offset.product * 8)+prod_sec_len - 1] = checksum;
478 	/* Write the new FRU Board section */
479 	if (write_fru_area(intf, &fru, 0, (header.offset.product * 8),
480 				(header.offset.product * 8),
481 				prod_sec_len, fru_data) < 0) {
482 		free(sn);
483 		sn = NULL;
484 		free(fru_data);
485 		fru_data = NULL;
486 		free(fru_area);
487 		fru_area = NULL;
488 		return -1;
489 	}
490 	free(sn);
491 	sn = NULL;
492 	free(fru_data);
493 	fru_data = NULL;
494 	free(fru_area);
495 	fru_area = NULL;
496 	return(1);
497 }
498 
499 /* ipmi_fru_set_mfg_date -  Set the Manufacturing Date in FRU
500  *
501  * @intf: ipmi interface
502  * @id: fru id
503  *
504  * returns -1 on error
505  * returns 1 if successful
506  */
507 static int
508 ipmi_kontron_set_mfg_date (struct ipmi_intf *intf)
509 {
510 	struct fru_header header;
511 	struct fru_info fru;
512 	struct ipmi_rs *rsp;
513 	struct ipmi_rq req;
514 	uint8_t *fru_data;
515 	uint8_t checksum;
516 	uint8_t msg_data[4];
517 	uint8_t mfg_date[3];
518 	uint32_t board_sec_len;
519 	uint32_t i;
520 
521 	memset(msg_data, 0, 4);
522 	msg_data[0] = 0xb4;
523 	msg_data[1] = 0x90;
524 	msg_data[2] = 0x91;
525 	msg_data[3] = 0x8b;
526 
527 	memset(&req, 0, sizeof(req));
528 	req.msg.netfn = 0x3E;
529 	req.msg.cmd = 0x0E;
530 	req.msg.data = msg_data;
531 	req.msg.data_len = 4;
532 	/* Set Lun temporary, necessary for this oem command */
533 	req.msg.lun = 0x03;
534 	rsp = intf->sendrecv(intf, &req);
535 	if (rsp == NULL)  {
536 		printf("Device not present (No Response)\n");
537 		return(-1);
538 	} else if (rsp->ccode > 0) {
539 		printf("This option is not implemented for this board\n");
540 		return(-1);
541 	}
542 	if (rsp->data_len != 3) {
543 		printf("Invalid response for the Manufacturing date\n");
544 		return(-1);
545 	}
546 	memset(mfg_date, 0, 3);
547 	memcpy(mfg_date, rsp->data, 3);
548 	memset(msg_data, 0, 4);
549 	msg_data[0] = 0;
550 
551 	memset(&req, 0, sizeof(req));
552 	req.msg.netfn = IPMI_NETFN_STORAGE;
553 	req.msg.cmd = GET_FRU_INFO;
554 	req.msg.data = msg_data;
555 	req.msg.data_len = 1;
556 	rsp = intf->sendrecv(intf, &req);
557 	if (rsp == NULL) {
558 		printf(" Device not present (No Response)\n");
559 		return(-1);
560 	} else if (rsp->ccode > 0) {
561 		printf(" Device not present (%s)\n",
562 				val2str(rsp->ccode, completion_code_vals));
563 		return(-1);
564 	}
565 
566 	memset(&fru, 0, sizeof(fru));
567 	fru.size = (rsp->data[1] << 8) | rsp->data[0];
568 	fru.access = rsp->data[2] & 0x1;
569 	if (fru.size < 1) {
570 		printf(" Invalid FRU size %d", fru.size);
571 		return(-1);
572 	}
573 	/* retrieve the FRU header */
574 	msg_data[0] = 0;
575 	msg_data[1] = 0;
576 	msg_data[2] = 0;
577 	msg_data[3] = 8;
578 
579 	memset(&req, 0, sizeof(req));
580 	req.msg.netfn = IPMI_NETFN_STORAGE;
581 	req.msg.cmd = GET_FRU_DATA;
582 	req.msg.data = msg_data;
583 	req.msg.data_len = 4;
584 	rsp = intf->sendrecv(intf, &req);
585 	if (rsp == NULL) {
586 		printf(" Device not present (No Response)\n");
587 		return (-1);
588 	} else if (rsp->ccode > 0) {
589 		printf(" Device not present (%s)\n",
590 				val2str(rsp->ccode, completion_code_vals));
591 		return (-1);
592 	}
593 	if (verbose > 1) {
594 		printbuf(rsp->data, rsp->data_len, "FRU DATA");
595 	}
596 	memcpy(&header, rsp->data + 1, 8);
597 	if (header.version != 1) {
598 		printf(" Unknown FRU header version 0x%02x",
599 				header.version);
600 		return(-1);
601 	}
602 	board_sec_len = (header.offset.product * 8) - (header.offset.board * 8);
603 	fru_data = malloc(fru.size);
604 	if(fru_data == NULL) {
605 		lprintf(LOG_ERR, "ipmitool: malloc failure");
606 		return(-1);
607 	}
608 	memset(fru_data, 0, fru.size);
609 	if (read_fru_area(intf ,&fru ,0 ,(header.offset.board * 8),
610 				board_sec_len ,fru_data) < 0) {
611 		free(fru_data);
612 		fru_data = NULL;
613 		return(-1);
614 	}
615 	/* Copy the new manufacturing date in the board section saved in memory*/
616 	memcpy(fru_data + (header.offset.board * 8) + 3, mfg_date, 3);
617 	checksum = 0;
618 	/* Calculate Header Checksum */
619 	for (i = (header.offset.board * 8);
620 			i < (((header.offset.board * 8) + board_sec_len) - 2);
621 			i ++ ) {
622 		checksum += fru_data[i];
623 	}
624 	checksum = (~checksum) + 1;
625 	fru_data[(header.offset.board * 8)+board_sec_len - 1] = checksum;
626 	/* Write the new FRU Board section */
627 	if (write_fru_area(intf, &fru, 0, (header.offset.board * 8),
628 				(header.offset.board * 8),
629 				board_sec_len, fru_data) < 0) {
630 		free(fru_data);
631 		fru_data = NULL;
632 		return (-1);
633 	}
634 	free(fru_data);
635 	fru_data = NULL;
636 	return (1);
637 }
638 
639 static void
640 ipmi_kontron_nextboot_help(void)
641 {
642 	int i;
643 	printf("nextboot <device>\n"
644 			"Supported devices:\n");
645 	for (i = 0; bootdev[i] != 0; i++) {
646 		printf("- %s\n", bootdev[i]);
647 	}
648 }
649 
650 /* ipmi_kontron_next_boot_set - Select the next boot order on CP6012
651  *
652  * @intf: ipmi interface
653  * @id: fru id
654  *
655  * returns -1 on error
656  * returns 1 if successful
657  */
658 static int
659 ipmi_kontron_nextboot_set(struct ipmi_intf *intf, int argc, char **argv)
660 {
661 	struct ipmi_rs *rsp;
662 	struct ipmi_rq req;
663 	uint8_t msg_data[8];
664 	int i;
665 
666 	memset(msg_data, 0, sizeof(msg_data));
667 	msg_data[0] = 0xb4;
668 	msg_data[1] = 0x90;
669 	msg_data[2] = 0x91;
670 	msg_data[3] = 0x8b;
671 	msg_data[4] = 0x9d;
672 	msg_data[5] = 0xFF;
673 	msg_data[6] = 0xFF; /* any */
674 	for (i = 0; bootdev[i] != 0; i++) {
675 		if (strcmp(argv[0], bootdev[i]) == 0) {
676 			msg_data[5] = i;
677 			break;
678 		}
679 	}
680 	/* Invalid device selected? */
681 	if (msg_data[5] == 0xFF) {
682 		printf("Unknown boot device: %s\n", argv[0]);
683 		return (-1);
684 	}
685 	memset(&req, 0, sizeof(req));
686 	req.msg.netfn = 0x3E;
687 	req.msg.cmd = 0x02;
688 	req.msg.data = msg_data;
689 	req.msg.data_len = 7;
690 	/* Set Lun temporary, necessary for this oem command */
691 	req.msg.lun = 0x03;
692 	rsp = intf->sendrecv(intf, &req);
693 	if (rsp == NULL) {
694 		printf("Device not present (No Response)\n");
695 		return(-1);
696 	} else if (rsp->ccode > 0) {
697 		printf("Device not present (%s)\n",
698 				val2str(rsp->ccode, completion_code_vals));
699 		return (-1);
700 	}
701 	return 0;
702 }
703