1 /*
2 * Copyright (c) 2015 American Megatrends, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * 3. Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from this
18 * software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #define _BSD_SOURCE
34
35 #include <ipmitool/helper.h>
36 #include <ipmitool/log.h>
37 #include <ipmitool/bswap.h>
38 #include <ipmitool/ipmi.h>
39 #include <ipmitool/ipmi_intf.h>
40 #include <ipmitool/ipmi_oem.h>
41 #include <ipmitool/ipmi_strings.h>
42 #include <ipmitool/ipmi_constants.h>
43 #include <scsi/sg.h>
44 #include <sys/ioctl.h>
45 #include <scsi/scsi_ioctl.h>
46 #include <scsi/scsi.h>
47 #include <sys/file.h>
48 #include <sys/stat.h>
49 #include <sys/types.h>
50 #include <fcntl.h>
51 #include <errno.h>
52 #include <unistd.h>
53
54 #define PACKED __attribute__ ((packed))
55 #define BEGIN_SIG "$G2-CONFIG-HOST$"
56 #define BEGIN_SIG_LEN 16
57 #define MAX_REQUEST_SIZE 64 * 1024
58 #define CMD_RESERVED 0x0000
59 #define SCSI_AMICMD_CURI_WRITE 0xE2
60 #define SCSI_AMICMD_CURI_READ 0xE3
61 #define SCSI_AMIDEF_CMD_SECTOR 0x01
62 #define SCSI_AMIDEF_DATA_SECTOR 0x02
63 #define ERR_SUCCESS 0 /* Success */
64 #define ERR_BIG_DATA 1 /* Too Much Data */
65 #define ERR_NO_DATA 2 /* No/Less Data Available */
66 #define ERR_UNSUPPORTED 3 /* Unsupported Command */
67 #define IN_PROCESS 0x8000 /* Bit 15 of Status */
68 #define SCSI_AMICMD_ID 0xEE
69
70 /* SCSI Command Packets */
71 typedef struct {
72 unsigned char OpCode;
73 unsigned char Lun;
74 unsigned int Lba;
75 union {
76 struct {
77 unsigned char Reserved6;
78 unsigned short Length;
79 unsigned char Reserved9[3];
80 } PACKED Cmd10;
81 struct Len32 {
82 unsigned int Length32;
83 unsigned char Reserved10[2];
84 } PACKED Cmd12;
85 } PACKED CmdLen;
86 } PACKED SCSI_COMMAND_PACKET;
87
88 typedef struct {
89 uint8_t byNetFnLUN;
90 uint8_t byCmd;
91 uint8_t byData[MAX_REQUEST_SIZE];
92 } PACKED IPMIUSBRequest_T;
93
94 typedef struct {
95 uint8_t BeginSig[BEGIN_SIG_LEN];
96 uint16_t Command;
97 uint16_t Status;
98 uint32_t DataInLen;
99 uint32_t DataOutLen;
100 uint32_t InternalUseDataIn;
101 uint32_t InternalUseDataOut;
102 } CONFIG_CMD;
103
104 static int ipmi_usb_setup(struct ipmi_intf *intf);
105 static struct ipmi_rs *ipmi_usb_send_cmd(struct ipmi_intf *intf,
106 struct ipmi_rq *req);
107
108 struct ipmi_intf ipmi_usb_intf = {
109 .name = "usb",
110 .desc = "IPMI USB Interface(OEM Interface for AMI Devices)",
111 .setup = ipmi_usb_setup,
112 .sendrecv = ipmi_usb_send_cmd,
113 };
114
115 int
scsiProbeNew(int * num_ami_devices,int * sg_nos)116 scsiProbeNew(int *num_ami_devices, int *sg_nos)
117 {
118 int inplen = *num_ami_devices;
119 int numdevfound = 0;
120 char linebuf[81];
121 char vendor[81];
122 int lineno = 0;
123 FILE *fp;
124
125 fp = fopen("/proc/scsi/sg/device_strs", "r");
126 if (fp == NULL) {
127 /* Return 1 on error */
128 return 1;
129 }
130
131 while (1) {
132 /* Read line by line and search for "AMI" */
133 if (fgets(linebuf, 80, fp) == NULL) {
134 if (fp != NULL) {
135 fclose(fp);
136 }
137 /* Return 1 on error */
138 return 1;
139 }
140
141 if (sscanf(linebuf, "%s", vendor) == 1) {
142 if (strncmp(vendor, "AMI", strlen("AMI")) == 0) {
143 numdevfound++;
144 sg_nos[numdevfound - 1] = lineno;
145 if (numdevfound == inplen) {
146 break;
147 }
148 }
149 lineno++;
150 }
151 }
152
153 *num_ami_devices = numdevfound;
154 if (fp != NULL) {
155 fclose(fp);
156 }
157
158 return 0;
159 }
160
161 int
OpenCD(struct ipmi_intf * intf,char * CDName)162 OpenCD(struct ipmi_intf *intf, char *CDName)
163 {
164 intf->fd = open(CDName, O_RDWR);
165 if (intf->fd == (-1)) {
166 lprintf(LOG_ERR, "OpenCD:Unable to open device, %s",
167 strerror(errno));
168 return 1;
169 }
170 return 0;
171 }
172
173 int
sendscsicmd_SGIO(int cd_desc,unsigned char * cdb_buf,unsigned char cdb_len,void * data_buf,unsigned int * data_len,int direction,void * sense_buf,unsigned char slen,unsigned int timeout)174 sendscsicmd_SGIO(int cd_desc, unsigned char *cdb_buf, unsigned char cdb_len,
175 void *data_buf, unsigned int *data_len, int direction,
176 void *sense_buf, unsigned char slen, unsigned int timeout)
177 {
178 sg_io_hdr_t io_hdr;
179
180 /* Prepare command */
181 memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
182 io_hdr.interface_id = 'S';
183 io_hdr.cmd_len = cdb_len;
184
185 /* Transfer direction and length */
186 io_hdr.dxfer_direction = direction;
187 io_hdr.dxfer_len = *data_len;
188
189 io_hdr.dxferp = data_buf;
190
191 io_hdr.cmdp = cdb_buf;
192
193 io_hdr.sbp = (unsigned char *)sense_buf;
194 io_hdr.mx_sb_len = slen;
195
196 io_hdr.timeout = timeout;
197
198 if (!timeout) {
199 io_hdr.timeout = 20000;
200 }
201
202 if (ioctl(cd_desc, SG_IO, &io_hdr) < 0) {
203 lprintf(LOG_ERR, "sendscsicmd_SGIO: SG_IO ioctl error");
204 return 1;
205 } else {
206 if (io_hdr.status != 0) {
207 return 1;
208 }
209 }
210
211 if (!timeout) {
212 return 0;
213 }
214
215 if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) {
216 lprintf(LOG_DEBUG, "sendscsicmd_SGIO: SG_INFO_OK - Not OK");
217 } else {
218 lprintf(LOG_DEBUG, "sendscsicmd_SGIO: SG_INFO_OK - OK");
219 return 0;
220 }
221
222 return 1;
223 }
224
225 int
AMI_SPT_CMD_Identify(int cd_desc,char * szSignature)226 AMI_SPT_CMD_Identify(int cd_desc, char *szSignature)
227 {
228 SCSI_COMMAND_PACKET IdPkt = {0};
229 int ret;
230 unsigned int siglen = 10;
231
232 IdPkt.OpCode = SCSI_AMICMD_ID;
233 ret = sendscsicmd_SGIO(cd_desc, (unsigned char *)&IdPkt,
234 10, szSignature, &siglen, SG_DXFER_FROM_DEV,
235 NULL, 0, 5000);
236
237 return ret;
238 }
239
240 int
IsG2Drive(int cd_desc)241 IsG2Drive(int cd_desc)
242 {
243 char szSignature[15];
244 int ret;
245
246 memset(szSignature, 0, 15);
247
248 flock(cd_desc, LOCK_EX);
249 ret = AMI_SPT_CMD_Identify(cd_desc, szSignature);
250 flock(cd_desc, LOCK_UN);
251 if (ret != 0) {
252 lprintf(LOG_DEBUG,
253 "IsG2Drive:Unable to send ID command to the device");
254 return 1;
255 }
256
257 if (strncmp(szSignature, "$$$AMI$$$", strlen("$$$AMI$$$")) != 0) {
258 lprintf(LOG_ERR,
259 "IsG2Drive:Signature mismatch when ID command sent");
260 return 1;
261 }
262
263 return 0;
264 }
265
266 int
FindG2CDROM(struct ipmi_intf * intf)267 FindG2CDROM(struct ipmi_intf *intf)
268 {
269 int err = 0;
270 char device[256];
271 int devarray[8];
272 int numdev = 8;
273 int iter;
274 err = scsiProbeNew(&numdev, devarray);
275
276 if (err == 0 && numdev > 0) {
277 for (iter = 0; iter < numdev; iter++) {
278 sprintf(device, "/dev/sg%d", devarray[iter]);
279
280 if (!OpenCD(intf, device)) {
281 if (!IsG2Drive(intf->fd)) {
282 lprintf(LOG_DEBUG, "USB Device found");
283 return 1;
284 }
285 close(intf->fd);
286 }
287 }
288 } else {
289 lprintf(LOG_DEBUG, "Unable to find Virtual CDROM Device");
290 }
291
292 return 0;
293 }
294
295 static int
ipmi_usb_setup(struct ipmi_intf * intf)296 ipmi_usb_setup(struct ipmi_intf *intf)
297 {
298 if (FindG2CDROM(intf) == 0) {
299 lprintf(LOG_ERR, "Error in USB session setup \n");
300 return (-1);
301 }
302 intf->opened = 1;
303 return 0;
304 }
305
306 void
InitCmdHeader(CONFIG_CMD * pG2CDCmdHeader)307 InitCmdHeader(CONFIG_CMD *pG2CDCmdHeader)
308 {
309 memset(pG2CDCmdHeader, 0, sizeof(CONFIG_CMD));
310 memcpy((char *)pG2CDCmdHeader->BeginSig, BEGIN_SIG, BEGIN_SIG_LEN);
311 }
312
313 int
AMI_SPT_CMD_SendCmd(int cd_desc,char * Buffer,char type,uint16_t buflen,unsigned int timeout)314 AMI_SPT_CMD_SendCmd(int cd_desc, char *Buffer, char type, uint16_t buflen,
315 unsigned int timeout)
316 {
317 SCSI_COMMAND_PACKET Cmdpkt;
318 char sensebuff[32];
319 int ret;
320 unsigned int pktLen;
321 int count = 3;
322
323 memset(&Cmdpkt, 0, sizeof(SCSI_COMMAND_PACKET));
324
325 Cmdpkt.OpCode = SCSI_AMICMD_CURI_WRITE;
326 Cmdpkt.Lba = htonl(type);
327 Cmdpkt.CmdLen.Cmd10.Length = htons(1);
328
329 pktLen = buflen;
330 while (count > 0) {
331 ret = sendscsicmd_SGIO(cd_desc, (unsigned char *)&Cmdpkt,
332 10, Buffer, &pktLen, SG_DXFER_TO_DEV,
333 sensebuff, 32, timeout);
334 count--;
335 if (ret == 0) {
336 break;
337 } else {
338 ret = (-1);
339 }
340 }
341
342 return ret;
343 }
344
345 int
AMI_SPT_CMD_RecvCmd(int cd_desc,char * Buffer,char type,uint16_t buflen)346 AMI_SPT_CMD_RecvCmd(int cd_desc, char *Buffer, char type, uint16_t buflen)
347 {
348 SCSI_COMMAND_PACKET Cmdpkt;
349 char sensebuff[32];
350 int ret;
351 unsigned int pktLen;
352 int count = 3;
353
354 memset(&Cmdpkt, 0, sizeof(SCSI_COMMAND_PACKET));
355
356 Cmdpkt.OpCode = SCSI_AMICMD_CURI_READ;
357 Cmdpkt.Lba = htonl(type);
358 Cmdpkt.CmdLen.Cmd10.Length = htons(1);
359
360 pktLen = buflen;
361 while (count > 0) {
362 ret = sendscsicmd_SGIO(cd_desc, (unsigned char *)&Cmdpkt,
363 10, Buffer, &pktLen, SG_DXFER_FROM_DEV,
364 sensebuff, 32, 5000);
365 count--;
366 if (0 == ret) {
367 break;
368 } else {
369 ret = (-1);
370 }
371 }
372
373 return ret;
374 }
375
376 int
ReadCD(int cd_desc,char CmdData,char * Buffer,uint32_t DataLen)377 ReadCD(int cd_desc, char CmdData, char *Buffer, uint32_t DataLen)
378 {
379 int ret;
380
381 ret = AMI_SPT_CMD_RecvCmd(cd_desc, Buffer, CmdData, DataLen);
382 if (ret != 0) {
383 lprintf(LOG_ERR, "Error while reading CD-Drive");
384 return (-1);
385 }
386 return 0;
387 }
388
389 int
WriteCD(int cd_desc,char CmdData,char * Buffer,unsigned int timeout,uint32_t DataLen)390 WriteCD(int cd_desc, char CmdData, char *Buffer, unsigned int timeout,
391 uint32_t DataLen)
392 {
393 int ret;
394
395 ret = AMI_SPT_CMD_SendCmd(cd_desc, Buffer, CmdData, DataLen, timeout);
396 if (ret != 0) {
397 lprintf(LOG_ERR, "Error while writing to CD-Drive");
398 return (-1);
399 }
400 return 0;
401 }
402
403 int
WriteSplitData(struct ipmi_intf * intf,char * Buffer,char Sector,uint32_t NumBytes,uint32_t timeout)404 WriteSplitData(struct ipmi_intf *intf, char *Buffer, char Sector,
405 uint32_t NumBytes, uint32_t timeout)
406 {
407 uint32_t BytesWritten = 0;
408 int retVal;
409
410 if (NumBytes == 0) {
411 return 0;
412 }
413
414 while (BytesWritten < NumBytes) {
415 if ((retVal = WriteCD(intf->fd, Sector,
416 (Buffer + BytesWritten),
417 timeout, NumBytes)) != 0) {
418 return retVal;
419 }
420
421 BytesWritten += NumBytes;
422 }
423
424 return 0;
425 }
426
427 int
ReadSplitData(struct ipmi_intf * intf,char * Buffer,char Sector,uint32_t NumBytes)428 ReadSplitData(struct ipmi_intf *intf, char *Buffer, char Sector,
429 uint32_t NumBytes)
430 {
431 uint32_t BytesRead = 0;
432
433 if (NumBytes == 0) {
434 return 0;
435 }
436
437 while (BytesRead < NumBytes) {
438 if (ReadCD(intf->fd, Sector, (Buffer + BytesRead),
439 NumBytes) == (-1)) {
440 return 1;
441 }
442 BytesRead += NumBytes;
443 }
444
445 return 0;
446 }
447
448 int
WaitForCommandCompletion(struct ipmi_intf * intf,CONFIG_CMD * pG2CDCmdHeader,uint32_t timeout,uint32_t DataLen)449 WaitForCommandCompletion(struct ipmi_intf *intf, CONFIG_CMD *pG2CDCmdHeader,
450 uint32_t timeout, uint32_t DataLen)
451 {
452 uint32_t TimeCounter = 0;
453
454 do {
455 if (ReadCD(intf->fd, SCSI_AMIDEF_CMD_SECTOR,
456 (char *)(pG2CDCmdHeader), DataLen) == (-1)) {
457 lprintf(LOG_ERR, "ReadCD returned ERROR");
458 return 1;
459 }
460
461 if (pG2CDCmdHeader->Status & IN_PROCESS) {
462 usleep(1000);
463 if (timeout > 0) {
464 TimeCounter++;
465 if (TimeCounter == (timeout + 1)) {
466 return 2;
467 }
468 }
469 } else {
470 lprintf(LOG_DEBUG, "Command completed");
471 break;
472 }
473 } while (1);
474
475 return 0;
476 }
477
478 int
SendDataToUSBDriver(struct ipmi_intf * intf,char * ReqBuffer,unsigned int ReqBuffLen,unsigned char * ResBuffer,int * ResBuffLen,unsigned int timeout)479 SendDataToUSBDriver(struct ipmi_intf *intf, char *ReqBuffer,
480 unsigned int ReqBuffLen, unsigned char *ResBuffer,
481 int *ResBuffLen, unsigned int timeout)
482 {
483 char CmdHeaderBuffer[sizeof(CONFIG_CMD)];
484 int retVal;
485 int waitretval = 0;
486 unsigned int to = 0;
487 uint32_t DataLen = 0;
488
489 CONFIG_CMD *pG2CDCmdHeader = (CONFIG_CMD *)CmdHeaderBuffer;
490
491 /* FillHeader */
492 InitCmdHeader(pG2CDCmdHeader);
493
494 /* Set command number */
495 pG2CDCmdHeader->Command = CMD_RESERVED;
496
497 /* Fill Lengths */
498 pG2CDCmdHeader->DataOutLen = *ResBuffLen;
499 pG2CDCmdHeader->DataInLen = ReqBuffLen;
500
501 if (!timeout) {
502 to = 3000;
503 }
504
505 DataLen = sizeof(CONFIG_CMD);
506
507 if (WriteCD(intf->fd, SCSI_AMIDEF_CMD_SECTOR,
508 (char *)(pG2CDCmdHeader), to, DataLen) == (-1)) {
509 lprintf(LOG_ERR,
510 "Error in Write CD of SCSI_AMIDEF_CMD_SECTOR");
511 return (-1);
512 }
513
514 /* Write the data to hard disk */
515 if ((retVal = WriteSplitData(intf, ReqBuffer,
516 SCSI_AMIDEF_DATA_SECTOR,
517 ReqBuffLen, timeout)) != 0) {
518 lprintf(LOG_ERR,
519 "Error in WriteSplitData of SCSI_AMIDEF_DATA_SECTOR");
520 return (-1);
521 }
522
523 if (!timeout) {
524 return 0;
525 }
526
527 /* Read Status now */
528 waitretval = WaitForCommandCompletion(intf, pG2CDCmdHeader, timeout,
529 DataLen);
530 if (waitretval != 0) {
531 lprintf(LOG_ERR, "WaitForCommandComplete failed");
532 return (0 - waitretval);
533 } else {
534 lprintf(LOG_DEBUG, "WaitForCommandCompletion SUCCESS");
535 }
536
537 switch (pG2CDCmdHeader->Status) {
538 case ERR_SUCCESS:
539 *ResBuffLen = pG2CDCmdHeader->DataOutLen;
540 lprintf(LOG_DEBUG, "Before ReadSplitData %x", *ResBuffLen);
541 if (ReadSplitData(intf, (char *)ResBuffer,
542 SCSI_AMIDEF_DATA_SECTOR,
543 pG2CDCmdHeader->DataOutLen) != 0) {
544 lprintf(LOG_ERR,
545 "Err ReadSplitData SCSI_AMIDEF_DATA_SCTR");
546 return (-1);
547 }
548 /* Additional read to see verify there was not problem
549 * with the previous read
550 */
551 DataLen = sizeof(CONFIG_CMD);
552 ReadCD(intf->fd, SCSI_AMIDEF_CMD_SECTOR,
553 (char *)(pG2CDCmdHeader), DataLen);
554 break;
555 case ERR_BIG_DATA:
556 lprintf(LOG_ERR, "Too much data");
557 break;
558 case ERR_NO_DATA:
559 lprintf(LOG_ERR, "Too little data");
560 break;
561 case ERR_UNSUPPORTED:
562 lprintf(LOG_ERR, "Unsupported command");
563 break;
564 default:
565 lprintf(LOG_ERR, "Unknown status");
566 }
567
568 return pG2CDCmdHeader->Status;
569 }
570
571 static struct ipmi_rs *
ipmi_usb_send_cmd(struct ipmi_intf * intf,struct ipmi_rq * req)572 ipmi_usb_send_cmd(struct ipmi_intf *intf, struct ipmi_rq *req)
573 {
574 static struct ipmi_rs rsp;
575 long timeout = 20000;
576 uint8_t byRet = 0;
577 char ReqBuff[MAX_REQUEST_SIZE] = {0};
578 IPMIUSBRequest_T *pReqPkt = (IPMIUSBRequest_T *)ReqBuff;
579 int retries = 0;
580 /********** FORM IPMI PACKET *****************/
581 pReqPkt->byNetFnLUN = req->msg.netfn << 2;
582 pReqPkt->byNetFnLUN += req->msg.lun;
583 pReqPkt->byCmd = req->msg.cmd;
584 if (req->msg.data_len) {
585 memcpy(pReqPkt->byData, req->msg.data, req->msg.data_len);
586 }
587
588 /********** SEND DATA TO USB ******************/
589 while (retries < 3) {
590 retries++;
591 byRet = SendDataToUSBDriver(intf, ReqBuff,
592 2 + req->msg.data_len, rsp.data,
593 &rsp.data_len,timeout);
594
595 if (byRet == 0) {
596 break;
597 }
598 }
599
600 if (retries == 3) {
601 lprintf(LOG_ERR,
602 "Error while sending command using",
603 "SendDataToUSBDriver");
604 rsp.ccode = byRet;
605 return &rsp;
606 }
607
608 rsp.ccode = rsp.data[0];
609
610 /* Save response data for caller */
611 if ((rsp.ccode == 0) && (rsp.data_len > 0)) {
612 memmove(rsp.data, rsp.data + 1, rsp.data_len - 1);
613 rsp.data[rsp.data_len] = 0;
614 rsp.data_len -= 1;
615 }
616 return &rsp;
617 }
618