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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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