1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 /* 26 * SES Log reader library 27 * 28 * This library is responsible for accessing the SES log at the target address, 29 * formatting and returning any log entries found. 30 * 31 * The data will be returned in an nvlist_t structure allocated here. 32 */ 33 34 #include <assert.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <sys/param.h> 38 #include <libseslog.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <sys/stat.h> 42 #include <unistd.h> 43 #include <dirent.h> 44 #include <sys/scsi/generic/commands.h> 45 #include <sys/scsi/generic/status.h> 46 47 /* 48 * open the device with given device name 49 */ 50 static int 51 open_device(const char *device_name) 52 { 53 int oflags = O_NONBLOCK | O_RDWR; 54 int fd; 55 56 fd = open(device_name, oflags); 57 if (fd < 0) 58 fd = -errno; 59 return (fd); 60 } 61 62 /* 63 * Initialize scsi struct 64 */ 65 static void 66 construct_scsi_pt_obj(struct uscsi_cmd *uscsi) 67 { 68 (void) memset(uscsi, 0, sizeof (struct uscsi_cmd)); 69 uscsi->uscsi_timeout = DEF_PT_TIMEOUT; 70 uscsi->uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_RQENABLE; 71 } 72 73 /* 74 * set control cdb of scsi structure 75 */ 76 static void 77 set_scsi_pt_cdb(struct uscsi_cmd *uscsi, const unsigned char *cdb, 78 int cdb_len) 79 { 80 uscsi->uscsi_cdb = (char *)cdb; 81 uscsi->uscsi_cdblen = cdb_len; 82 } 83 84 /* 85 * initialize sense data 86 */ 87 static void 88 set_scsi_pt_sense(struct uscsi_cmd *uscsi, unsigned char *sense, 89 int max_sense_len) 90 { 91 (void) memset(sense, 0, max_sense_len); 92 uscsi->uscsi_rqbuf = (char *)sense; 93 uscsi->uscsi_rqlen = max_sense_len; 94 } 95 96 /* 97 * Initialize data going to device 98 */ 99 static void 100 set_scsi_pt_data_in(struct uscsi_cmd *uscsi, unsigned char *dxferp, 101 int dxfer_len) 102 { 103 if (dxfer_len > 0) { 104 uscsi->uscsi_bufaddr = (char *)dxferp; 105 uscsi->uscsi_buflen = dxfer_len; 106 uscsi->uscsi_flags = USCSI_READ | USCSI_ISOLATE | 107 USCSI_RQENABLE; 108 } 109 } 110 111 /* 112 * Executes SCSI command(or at least forwards it to lower layers). 113 */ 114 static int 115 do_scsi_pt(struct uscsi_cmd *uscsi, int fd, int time_secs) 116 { 117 if (time_secs > 0) 118 uscsi->uscsi_timeout = time_secs; 119 120 if (ioctl(fd, USCSICMD, uscsi)) { 121 /* Took an error */ 122 return (errno); 123 } 124 return (0); 125 } 126 127 128 /* 129 * Read log from device 130 * Invokes a SCSI LOG SENSE command. 131 * Return: 132 * 0 -> success 133 * SG_LIB_CAT_INVALID_OP -> Log Sense not supported, 134 * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, 135 * SG_LIB_CAT_NOT_READY -> device not ready, 136 * -1 -> other failure 137 */ 138 139 static int 140 read_log(int sg_fd, unsigned char *resp, int mx_resp_len) 141 { 142 int res, ret; 143 unsigned char logsCmdBlk[CDB_GROUP1] = 144 {SCMD_LOG_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 145 unsigned char sense_b[SENSE_BUFF_LEN]; 146 struct uscsi_cmd uscsi; 147 148 149 150 if (mx_resp_len > 0xffff) { 151 return (-1); 152 } 153 logsCmdBlk[1] = 0; 154 /* pc = 1, pg_code = 0x7 (logs page) */ 155 /* (((pc << 6) & 0xc0) | (pg_code & 0x3f)) = 0x47; */ 156 logsCmdBlk[2] = 0x47; 157 /* pc = 1 current values */ 158 logsCmdBlk[3] = 0; /* No subpage code */ 159 logsCmdBlk[5] = 0; /* Want all logs starting from 0 */ 160 logsCmdBlk[6] = 0; 161 logsCmdBlk[7] = (unsigned char) ((mx_resp_len >> 8) & 0xff); 162 logsCmdBlk[8] = (unsigned char) (mx_resp_len & 0xff); 163 164 construct_scsi_pt_obj(&uscsi); 165 166 set_scsi_pt_cdb(&uscsi, logsCmdBlk, sizeof (logsCmdBlk)); 167 set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b)); 168 set_scsi_pt_data_in(&uscsi, resp, mx_resp_len); 169 res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT); 170 if (res) { 171 ret = res; 172 } else { 173 ret = uscsi.uscsi_status; 174 } 175 return (ret); 176 } 177 178 /* 179 * Save the logs by walking through the entries in the response buffer. 180 * 181 * resp buffer looks like: 182 * 183 * +=====-========-========-========-========-========-========-========-=====+ 184 * | Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 185 * |Byte | | | | | | | | | 186 * |=====+====================================================================| 187 * | 0 | reserved | page code | 188 * |-----+--------------------------------------------------------------------| 189 * | 1 | Reserved | 190 * |-----+--------------------------------------------------------------------| 191 * | 2 |(MSB) Page Length(n-3) | 192 * | -- | | 193 * | 3 | (LSB) | 194 * |-----+--------------------------------------------------------------------| 195 * | 4 | Log Parameter (First)(Length X) | 196 * | -- | | 197 * | x+3 | | 198 * |-----+--------------------------------------------------------------------| 199 * |n-y+1| Log Parameter (Last)(Length y) | 200 * | -- | | 201 * | n | | 202 * +==========================================================================+ 203 * 204 * Log parameter field looks like: 205 * 206 * +=====-========-========-========-========-========-========-========-=====+ 207 * | Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 208 * |Byte | | | | | | | | | 209 * |=====+====================================================================| 210 * | 0 |(MSB) Parameter Code | 211 * | -- | | 212 * | 1 | (LSB) | 213 * |-----+--------------------------------------------------------------------| 214 * | 2 | DU | DS | TSD | ETC | TMC | LBIN | LP | 215 * |-----+--------------------------------------------------------------------| 216 * |3 | Paramter Length(n-3) | 217 * |-----+--------------------------------------------------------------------| 218 * | 4 | Parameter Values | 219 * | -- | | 220 * | n | | 221 * |-----+--------------------------------------------------------------------| 222 */ 223 224 static int 225 save_logs(unsigned char *resp, int len, nvlist_t *log_data, 226 char *last_log_entry, unsigned long *seq_num_ret, int *number_log_entries) 227 { 228 int k, i; 229 int paramCode; /* Parameter code */ 230 int paramLen = 0; /* Paramter length */ 231 int pcb; /* Paramter control Byte */ 232 unsigned char *lpp; /* Log parameter pointer */ 233 unsigned char *log_str_ptr; /* ptr to ascii str returend by expander */ 234 235 unsigned long seq_num_ul = 0; 236 char seq_num[10]; 237 char log_event_type[10]; 238 char log_code[10]; 239 char log_level[10]; 240 nvlist_t *entry; 241 char entry_num[15]; 242 int type; 243 int match_found = 0; 244 long current_seq_num; 245 long last_num; 246 char save_buffer[256]; 247 char entry_added = 0; 248 char *s; 249 250 251 (void) memset(seq_num, 0, sizeof (seq_num)); 252 253 *number_log_entries = 0; 254 /* Initial log paramter pointer to point to first log entry */ 255 /* The resp includes 4 bytes of header info and then log entries */ 256 lpp = &resp[0] + 4; 257 k = len; 258 /* Find last sequence number from last log read */ 259 if (last_log_entry != NULL && 260 (strlen(last_log_entry) == SES_LOG_VALID_LOG_SIZE)) { 261 (void) strncpy(seq_num, (const char *) last_log_entry + 262 SES_LOG_SEQ_NUM_START, 8); 263 last_num = strtoul(seq_num, 0, 16); 264 /* save this in case there are no new entries */ 265 seq_num_ul = last_num; 266 267 /* First find if there are duplicate entries */ 268 lpp = &resp[0] + 4; 269 270 /* 271 * Start walking each log entry in return buffer looking for 272 * a duplicate entry. 273 */ 274 for (; k > 0; k -= paramLen) { 275 if (k < 3) { 276 /* 277 * Should always have at least 3 Bytes for 278 * each entry 279 * If not, it must be a bad record so stop 280 * processing 281 */ 282 nvlist_free(log_data); 283 log_data = NULL; 284 return (SES_LOG_FAILED_SHORT_LOG_PARAM_INIT); 285 } 286 pcb = lpp[2]; 287 paramLen = lpp[3] + 4; 288 /* 289 * initial log_str_ptr to point to string info returned 290 * by expander 291 * first 4 bytes of log 292 * parameter are 2 param: 293 * codes Control byte, Parameter length 294 */ 295 log_str_ptr = lpp + 4; 296 297 if (paramLen > 4) { 298 if ((pcb & 0x1) && !(pcb & 2)) { 299 300 (void) strncpy(seq_num, 301 (const char *)log_str_ptr + 302 SES_LOG_SEQ_NUM_START, 8); 303 current_seq_num = strtoul(seq_num, 0, 304 16); 305 306 if (current_seq_num == last_num) { 307 /* 308 * Check to see if this is the 309 * same line 310 */ 311 if (strncmp( 312 (char *)log_str_ptr, 313 last_log_entry, 314 SES_LOG_VALID_LOG_SIZE) == 315 0) { 316 /* 317 * Found an exact 318 * match 319 */ 320 lpp += paramLen; 321 k -= paramLen; 322 match_found = 1; 323 break; 324 } 325 } 326 } 327 } 328 lpp += paramLen; 329 } 330 } 331 if (!match_found) { 332 lpp = &resp[0] + 4; 333 k = len; 334 } 335 336 (void) memset(log_event_type, 0, sizeof (log_event_type)); 337 (void) memset(seq_num, 0, sizeof (seq_num)); 338 (void) memset(log_code, 0, sizeof (log_code)); 339 (void) memset(save_buffer, 0, sizeof (save_buffer)); 340 (void) memset(log_level, 0, sizeof (log_level)); 341 342 /* K will be initialized from above */ 343 for (; k > 0; k -= paramLen) { 344 if (k < 3) { 345 /* Should always have at least 3 Bytes for each entry */ 346 /* If not, it must be a bad record so stop processing */ 347 nvlist_free(log_data); 348 log_data = NULL; 349 return (SES_LOG_FAILED_SHORT_LOG_PARAM); 350 } 351 paramCode = (lpp[0] << 8) + lpp[1]; 352 pcb = lpp[2]; 353 paramLen = lpp[3] + 4; 354 /* 355 * initial log_str_ptr to point to string info of the log entry 356 * First 4 bytes of log entry contains param code, control 357 * byte, length 358 */ 359 log_str_ptr = lpp + 4; 360 361 /* 362 * Format of log str is as follows 363 * "%8x %8x %8x %8x %8x %8x %8x %8x", 364 * log_entry.log_word0, log_entry.ts_u, log_entry.ts_l, 365 * log_entry.seq_num, log_entry.log_code, log_entry.log_word2, 366 * log_entry.log_word3, log_entry.log_word4 367 * following example has extra spaces removed to fit in 80 char 368 * 40004 0 42d5f5fe 185b 630002 fd0800 50800207 e482813 369 */ 370 if (paramLen > 4) { 371 if ((pcb & 0x1) && !(pcb & 2)) { 372 373 (void) strncpy(save_buffer, 374 (const char *)log_str_ptr, 375 SES_LOG_VALID_LOG_SIZE); 376 for (i = 0; (i < 8) && (s = strtok(i ? 0 : 377 (char *)log_str_ptr, " ")); i++) { 378 char *ulp; 379 switch (i) { 380 case 0: 381 /* event type */ 382 ulp = (char *) 383 &log_event_type; 384 break; 385 case 3: 386 /* sequence number */ 387 ulp = (char *) 388 &seq_num; 389 break; 390 case 4: 391 /* log code */ 392 ulp = (char *) 393 &log_code; 394 break; 395 default: 396 ulp = 0; 397 } 398 399 if (ulp) { 400 (void) strncpy(ulp, s, 8); 401 } 402 } 403 404 405 seq_num_ul = strtoul(seq_num, 0, 16); 406 407 (void) strncpy(log_level, 408 (const char *) log_str_ptr + 409 SES_LOG_LEVEL_START, 1); 410 411 /* event type is in log_event_type */ 412 /* 4x004 = looking for x */ 413 type = (strtoul(log_event_type, 0, 16) >> 12) & 414 0xf; 415 416 /* 417 * Check type. If type is 1, level needs to be 418 * changed to FATAL. If type is something other 419 * than 0 or 1, they are info only. 420 */ 421 if (type == 1) { 422 (void) strcpy(log_level, "4"); 423 } else if (type > 1) { 424 /* These are not application log */ 425 /* entries */ 426 /* make them info only */ 427 (void) strcpy(log_level, "0"); 428 } 429 430 /* Add this entry to the nvlist log data */ 431 if (nvlist_alloc(&entry, 432 NV_UNIQUE_NAME, 0) != 0) { 433 nvlist_free(log_data); 434 log_data = NULL; 435 return (SES_LOG_FAILED_NV_UNIQUE); 436 } 437 438 439 if (nvlist_add_string(entry, ENTRY_LOG, 440 save_buffer) != 0) { 441 nvlist_free(entry); 442 nvlist_free(log_data); 443 log_data = NULL; 444 return (SES_LOG_FAILED_NV_LOG); 445 } 446 447 if (nvlist_add_string(entry, ENTRY_CODE, 448 log_code) != 0) { 449 nvlist_free(entry); 450 nvlist_free(log_data); 451 log_data = NULL; 452 return (SES_LOG_FAILED_NV_CODE); 453 } 454 if (nvlist_add_string(entry, ENTRY_SEVERITY, 455 log_level) != 0) { 456 nvlist_free(entry); 457 nvlist_free(log_data); 458 log_data = NULL; 459 return (SES_LOG_FAILED_NV_SEV); 460 } 461 462 (void) snprintf(entry_num, sizeof (entry_num), 463 "%s%d", ENTRY_PREFIX, paramCode); 464 465 if (nvlist_add_nvlist(log_data, entry_num, 466 entry) != 0) { 467 nvlist_free(entry); 468 nvlist_free(log_data); 469 log_data = NULL; 470 return (SES_LOG_FAILED_NV_ENTRY); 471 } 472 nvlist_free(entry); 473 474 entry_added = 1; 475 (*number_log_entries)++; 476 477 } 478 } 479 lpp += paramLen; 480 481 } 482 if (entry_added) { 483 /* Update the last log entry string with last one read */ 484 (void) strncpy(last_log_entry, save_buffer, MAXNAMELEN); 485 } 486 *seq_num_ret = seq_num_ul; 487 488 return (0); 489 } 490 491 492 493 /* Setup struct to send command to device */ 494 static void 495 set_scsi_pt_data_out(struct uscsi_cmd *uscsi, const unsigned char *dxferp, 496 int dxfer_len) 497 { 498 if (dxfer_len > 0) { 499 uscsi->uscsi_bufaddr = (char *)dxferp; 500 uscsi->uscsi_buflen = dxfer_len; 501 uscsi->uscsi_flags = USCSI_WRITE | USCSI_ISOLATE | 502 USCSI_RQENABLE; 503 } 504 } 505 506 /* 507 * Invokes a SCSI MODE SENSE(10) command. 508 * Return: 509 * 0 for success 510 * SG_LIB_CAT_INVALID_OP -> invalid opcode 511 * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb 512 * SG_LIB_CAT_NOT_READY -> device not ready 513 * -1 -> other failure 514 */ 515 516 static int 517 sg_ll_mode_sense10(int sg_fd, void * resp, int mx_resp_len) 518 { 519 int res, ret; 520 unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] = 521 {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 522 unsigned char sense_b[SENSE_BUFF_LEN]; 523 struct uscsi_cmd uscsi; 524 525 modesCmdBlk[1] = 0; 526 modesCmdBlk[2] = 0; /* page code 0 vendor specific */ 527 modesCmdBlk[3] = 0; 528 modesCmdBlk[7] = (unsigned char) ((mx_resp_len >> 8) & 0xff); 529 modesCmdBlk[8] = (unsigned char) (mx_resp_len & 0xff); 530 531 construct_scsi_pt_obj(&uscsi); 532 set_scsi_pt_cdb(&uscsi, modesCmdBlk, sizeof (modesCmdBlk)); 533 set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b)); 534 set_scsi_pt_data_in(&uscsi, (unsigned char *) resp, mx_resp_len); 535 res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT); 536 if (res) { 537 ret = res; 538 } else { 539 ret = uscsi.uscsi_status; 540 } 541 return (ret); 542 } 543 544 /* 545 * Invokes a SCSI MODE SELECT(10) command. 546 * Return: 547 * 0 for success. 548 * SG_LIB_CAT_INVALID_OP for invalid opcode 549 * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, 550 * SG_LIB_CAT_NOT_READY -> device not ready, 551 * -1 -> other failure 552 */ 553 static int 554 sg_ll_mode_select10(int sg_fd, void * paramp, int param_len) 555 { 556 int res, ret; 557 unsigned char modesCmdBlk[MODE_SELECT10_CMDLEN] = 558 {SCMD_MODE_SELECT_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 559 unsigned char sense_b[SENSE_BUFF_LEN]; 560 struct uscsi_cmd uscsi; 561 562 563 modesCmdBlk[1] = 0; 564 /* 565 * modesCmdBlk 2 equal 0 PC 0 return current page code 0 return 566 * vendor specific 567 */ 568 569 modesCmdBlk[7] = (unsigned char)((param_len >> 8) & 0xff); 570 modesCmdBlk[8] = (unsigned char)(param_len & 0xff); 571 572 construct_scsi_pt_obj(&uscsi); 573 574 set_scsi_pt_cdb(&uscsi, modesCmdBlk, sizeof (modesCmdBlk)); 575 set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b)); 576 set_scsi_pt_data_out(&uscsi, (unsigned char *) paramp, param_len); 577 res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT); 578 if (res) { 579 ret = res; 580 } else { 581 ret = uscsi.uscsi_status; 582 } 583 return (ret); 584 } 585 586 587 588 /* 589 * MODE SENSE 10 commands yield a response that has block descriptors followed 590 * by mode pages. In most cases users are interested in the first mode page. 591 * This function returns the(byte) offset of the start of the first mode page. 592 * Returns >= 0 is successful or -1 if failure. If there is a failure 593 * a message is written to err_buff. 594 */ 595 596 /* 597 * return data looks like: 598 * Table 92 - Mode parameter header(10) 599 * Bit 600 * Byte 601 * 7 6 5 4 3 2 1 0 602 * ---------------------------------------------------------- 603 * 0 MSB Data length 604 * 1 LSB Data length 605 * ---------------------------------------------------------- 606 * 2 Medium type 607 * ---------------------------------------------------------- 608 * 3 Device-specific parameter 609 * ---------------------------------------------------------- 610 * 4 Reserved 611 * ---------------------------------------------------------- 612 * 5 Reserved 613 * ---------------------------------------------------------- 614 * 6 MSB block descriptor length 615 * 7 LSB block descriptor length 616 * ---------------------------------------------------------- 617 * block desciptors.... 618 * ----------------------- 619 * mode sense page: 620 * 0 : ps Reserved : page Code 621 * 1 : Page Length(n-1) 622 * 2-N Mode parameters 623 */ 624 static int 625 sg_mode_page_offset(const unsigned char *resp, int resp_len, 626 char *err_buff, int err_buff_len) 627 { 628 int bd_len; 629 int calc_len; 630 int offset; 631 632 if ((NULL == resp) || (resp_len < 8)) { 633 /* Too short of a response buffer */ 634 return (-1); 635 } 636 637 calc_len = (resp[0] << 8) + resp[1] + 2; 638 bd_len = (resp[6] << 8) + resp[7]; 639 640 /* LongLBA doesn't change this calculation */ 641 offset = bd_len + MODE10_RESP_HDR_LEN; 642 643 if ((offset + 2) > resp_len) { 644 (void) snprintf(err_buff, err_buff_len, 645 "given response length " 646 "too small, offset=%d given_len=%d bd_len=%d\n", 647 offset, resp_len, bd_len); 648 offset = -1; 649 } else if ((offset + 2) > calc_len) { 650 (void) snprintf(err_buff, err_buff_len, "calculated response " 651 "length too small, offset=%d calc_len=%d bd_len=%d\n", 652 offset, calc_len, bd_len); 653 offset = -1; 654 } 655 return (offset); 656 } 657 658 /* 659 * Clear logs 660 */ 661 static int 662 clear_log(int sg_fd, unsigned long seq_num, long poll_time) 663 { 664 665 int res, alloc_len, off; 666 int md_len; 667 int read_in_len = 0; 668 unsigned char ref_md[MX_ALLOC_LEN]; 669 char ebuff[EBUFF_SZ]; 670 struct log_clear_control_struct clear_data; 671 long myhostid; 672 int error = 0; 673 674 (void) memset(&clear_data, 0, sizeof (clear_data)); 675 676 clear_data.pageControls = 0x40; 677 clear_data.subpage_code = 0; 678 clear_data.page_lengthLower = 0x16; 679 680 myhostid = gethostid(); 681 /* 0 -> 11 are memset to 0 */ 682 clear_data.host_id[12] = (myhostid & 0xff000000) >> 24; 683 clear_data.host_id[13] = (myhostid & 0xff0000) >> 16; 684 clear_data.host_id[14] = (myhostid & 0xff00) >> 8; 685 clear_data.host_id[15] = myhostid & 0xff; 686 687 /* Timeout set to 32 seconds for now */ 688 /* Add 5 minutes to poll time to allow for data retrievel time */ 689 poll_time = poll_time + 300; 690 clear_data.timeout[0] = (poll_time & 0xff00) >> 8; 691 clear_data.timeout[1] = poll_time & 0xff; 692 693 clear_data.seq_clear[0] = (seq_num & 0xff000000) >> 24; 694 clear_data.seq_clear[1] = (seq_num & 0xff0000) >> 16; 695 clear_data.seq_clear[2] = (seq_num & 0xff00) >> 8; 696 clear_data.seq_clear[3] = (seq_num & 0xff); 697 698 read_in_len = sizeof (clear_data); 699 700 701 /* do MODE SENSE to fetch current values */ 702 (void) memset(ref_md, 0, MX_ALLOC_LEN); 703 alloc_len = MX_ALLOC_LEN; 704 705 706 res = sg_ll_mode_sense10(sg_fd, ref_md, alloc_len); 707 if (0 != res) { 708 /* Error during mode sense */ 709 error = SES_LOG_FAILED_MODE_SENSE; 710 return (error); 711 } 712 713 /* Setup mode Select to clear logs */ 714 off = sg_mode_page_offset(ref_md, alloc_len, ebuff, EBUFF_SZ); 715 if (off < 0) { 716 /* Mode page offset error */ 717 error = SES_LOG_FAILED_MODE_SENSE_OFFSET; 718 return (error); 719 } 720 md_len = (ref_md[0] << 8) + ref_md[1] + 2; 721 722 ref_md[0] = 0; 723 ref_md[1] = 0; 724 725 if (md_len > alloc_len) { 726 /* Data length to large */ 727 error = SES_LOG_FAILED_BAD_DATA_LEN; 728 return (error); 729 } 730 731 if ((md_len - off) != read_in_len) { 732 /* Content length not correct */ 733 error = SES_LOG_FAILED_BAD_CONTENT_LEN; 734 return (error); 735 } 736 737 if ((clear_data.pageControls & 0x40) != (ref_md[off] & 0x40)) { 738 /* reference model doesn't have use subpage format bit set */ 739 /* Even though it should have */ 740 /* don't send the command */ 741 error = SES_LOG_FAILED_FORMAT_PAGE_ERROR; 742 return (error); 743 } 744 745 (void) memcpy(ref_md + off, (const void *) & clear_data, 746 sizeof (clear_data)); 747 748 res = sg_ll_mode_select10(sg_fd, ref_md, md_len); 749 if (res != 0) { 750 error = SES_LOG_FAILED_MODE_SELECT; 751 return (error); 752 } 753 754 return (error); 755 756 757 } 758 /* 759 * Gather data from given device. 760 */ 761 static int 762 gatherData(char *device_name, nvlist_t *log_data, char *last_log_entry, 763 long poll_time, int *number_log_entries) 764 { 765 int sg_fd; 766 unsigned long seq_num; 767 int pg_len, resp_len, res; 768 unsigned char rsp_buff[MX_ALLOC_LEN]; 769 int error; 770 771 772 773 /* Open device */ 774 if ((sg_fd = open_device(device_name)) < 0) { 775 /* Failed to open device */ 776 nvlist_free(log_data); 777 log_data = NULL; 778 return (SES_LOG_FAILED_TO_OPEN_DEVICE); 779 } 780 781 /* Read the logs */ 782 (void) memset(rsp_buff, 0, sizeof (rsp_buff)); 783 resp_len = 0x8000; /* Maximum size available to read */ 784 res = read_log(sg_fd, rsp_buff, resp_len); 785 786 if (res == 0) { 787 pg_len = (rsp_buff[2] << 8) + rsp_buff[3]; 788 if ((pg_len + 4) > resp_len) { 789 /* Didn't get entire response */ 790 /* Process what we did get */ 791 pg_len = resp_len - 4; 792 } 793 } else { 794 /* Some sort of Error during read of logs */ 795 nvlist_free(log_data); 796 log_data = NULL; 797 return (SES_LOG_FAILED_TO_READ_DEVICE); 798 } 799 800 /* Save the logs */ 801 error = save_logs(rsp_buff, pg_len, log_data, last_log_entry, 802 &seq_num, number_log_entries); 803 if (error != 0) { 804 return (error); 805 } 806 /* Clear logs */ 807 error = clear_log(sg_fd, seq_num, poll_time); 808 809 (void) close(sg_fd); 810 811 return (error); 812 813 } 814 815 /* 816 * Access the SES target identified by the indicated path. Read the logs 817 * and return them in a nvlist. 818 */ 819 int 820 access_ses_log(struct ses_log_call_struct *data) 821 { 822 char real_path[MAXPATHLEN]; 823 long poll_time; 824 struct stat buffer; 825 int error; 826 827 if (data->target_path == NULL) { 828 /* NULL Target path, return error */ 829 return (SES_LOG_FAILED_NULL_TARGET_PATH); 830 } 831 if (strncmp("SUN-GENESIS", data->product_id, 11) != 0) { 832 /* Not a supported node, return error */ 833 return (SES_LOG_UNSUPPORTED_HW_ERROR); 834 } 835 836 /* Try to find a valid path */ 837 (void) snprintf(real_path, sizeof (real_path), "/devices%s:ses", 838 data->target_path); 839 840 if (stat(real_path, &buffer) != 0) { 841 842 (void) snprintf(real_path, sizeof (real_path), "/devices%s:0", 843 data->target_path); 844 if (stat(real_path, &buffer) != 0) { 845 /* Couldn't find a path that exists */ 846 return (SES_LOG_FAILED_BAD_TARGET_PATH); 847 } 848 } 849 850 851 /* 852 * convert nanosecond time to seconds 853 */ 854 poll_time = data->poll_time / 1000000000; 855 856 error = nvlist_alloc(&data->log_data, NV_UNIQUE_NAME, 0); 857 if (error != 0) { 858 /* Couldn't alloc memory for nvlist */ 859 return (SES_LOG_FAILED_NVLIST_CREATE); 860 } 861 862 863 /* Record the protocol used for later when an ereport is generated. */ 864 error = nvlist_add_string(data->log_data, PROTOCOL, PROTOCOL_TYPE); 865 if (error != 0) { 866 nvlist_free(data->log_data); 867 data->log_data = NULL; 868 /* Error adding entry */ 869 return (SES_LOG_FAILED_NVLIST_PROTOCOL); 870 } 871 872 error = gatherData(real_path, data->log_data, data->last_log_entry, 873 poll_time, &data->number_log_entries); 874 875 /* Update the size of log entries being returned */ 876 data->size_of_log_entries = 877 data->number_log_entries * SES_LOG_VALID_LOG_SIZE; 878 879 return (error); 880 } 881