2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * SES Log reader library
2N/A *
2N/A * This library is responsible for accessing the SES log at the target address,
2N/A * formatting and returning any log entries found.
2N/A *
2N/A * The data will be returned in an nvlist_t structure allocated here.
2N/A */
2N/A
2N/A#include <assert.h>
2N/A#include <errno.h>
2N/A#include <fcntl.h>
2N/A#include <sys/param.h>
2N/A#include <libseslog.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <sys/stat.h>
2N/A#include <unistd.h>
2N/A#include <dirent.h>
2N/A#include <sys/scsi/generic/commands.h>
2N/A#include <sys/scsi/generic/status.h>
2N/A#include <sys/scsi/impl/commands.h>
2N/A#include <libscf.h>
2N/A#include <uuid/uuid.h>
2N/A
2N/A/*
2N/A * open the device with given device name
2N/A */
2N/Astatic int
2N/Aopen_device(const char *device_name)
2N/A{
2N/A int oflags = O_NONBLOCK | O_RDWR;
2N/A int fd;
2N/A
2N/A fd = open(device_name, oflags);
2N/A if (fd < 0)
2N/A fd = -errno;
2N/A return (fd);
2N/A}
2N/A
2N/A/*
2N/A * Initialize scsi struct
2N/A */
2N/Astatic void
2N/Aconstruct_scsi_pt_obj(struct uscsi_cmd *uscsi)
2N/A{
2N/A (void) memset(uscsi, 0, sizeof (struct uscsi_cmd));
2N/A uscsi->uscsi_timeout = DEF_PT_TIMEOUT;
2N/A uscsi->uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_RQENABLE;
2N/A}
2N/A
2N/A/*
2N/A * set control cdb of scsi structure
2N/A */
2N/Astatic void
2N/Aset_scsi_pt_cdb(struct uscsi_cmd *uscsi, const unsigned char *cdb,
2N/A int cdb_len)
2N/A{
2N/A uscsi->uscsi_cdb = (char *)cdb;
2N/A uscsi->uscsi_cdblen = cdb_len;
2N/A}
2N/A
2N/A/*
2N/A * initialize sense data
2N/A */
2N/Astatic void
2N/Aset_scsi_pt_sense(struct uscsi_cmd *uscsi, unsigned char *sense,
2N/A int max_sense_len)
2N/A{
2N/A (void) memset(sense, 0, max_sense_len);
2N/A uscsi->uscsi_rqbuf = (char *)sense;
2N/A uscsi->uscsi_rqlen = max_sense_len;
2N/A}
2N/A
2N/A/*
2N/A * Initialize data going to device
2N/A */
2N/Astatic void
2N/Aset_scsi_pt_data_in(struct uscsi_cmd *uscsi, unsigned char *dxferp,
2N/A int dxfer_len)
2N/A{
2N/A if (dxfer_len > 0) {
2N/A uscsi->uscsi_bufaddr = (char *)dxferp;
2N/A uscsi->uscsi_buflen = dxfer_len;
2N/A uscsi->uscsi_flags = USCSI_READ | USCSI_ISOLATE |
2N/A USCSI_RQENABLE;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Executes SCSI command(or at least forwards it to lower layers).
2N/A */
2N/Astatic int
2N/Ado_scsi_pt(struct uscsi_cmd *uscsi, int fd, int time_secs)
2N/A{
2N/A if (time_secs > 0)
2N/A uscsi->uscsi_timeout = time_secs;
2N/A
2N/A if (ioctl(fd, USCSICMD, uscsi)) {
2N/A /* Took an error */
2N/A return (errno);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Read log from device
2N/A * Invokes a SCSI LOG SENSE command.
2N/A * Return:
2N/A * 0 -> success
2N/A * SG_LIB_CAT_INVALID_OP -> Log Sense not supported,
2N/A * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
2N/A * SG_LIB_CAT_NOT_READY -> device not ready,
2N/A * -1 -> other failure
2N/A */
2N/A
2N/Astatic int
2N/Aread_log(int sg_fd, unsigned char *resp, int mx_resp_len)
2N/A{
2N/A int res, ret;
2N/A unsigned char logsCmdBlk[CDB_GROUP1] =
2N/A {SCMD_LOG_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2N/A unsigned char sense_b[SENSE_BUFF_LEN];
2N/A struct uscsi_cmd uscsi;
2N/A
2N/A if (mx_resp_len > 0xffff) {
2N/A return (-1);
2N/A }
2N/A logsCmdBlk[1] = 0;
2N/A /* pc = 1, pg_code = 0x7 (logs page) */
2N/A /* (((pc << 6) & 0xc0) | (pg_code & 0x3f)) = 0x47; */
2N/A logsCmdBlk[2] = 0x47;
2N/A /* pc = 1 current values */
2N/A logsCmdBlk[3] = 0; /* No subpage code */
2N/A logsCmdBlk[5] = 0; /* Want all logs starting from 0 */
2N/A logsCmdBlk[6] = 0;
2N/A logsCmdBlk[7] = (unsigned char) ((mx_resp_len >> 8) & 0xff);
2N/A logsCmdBlk[8] = (unsigned char) (mx_resp_len & 0xff);
2N/A
2N/A construct_scsi_pt_obj(&uscsi);
2N/A
2N/A set_scsi_pt_cdb(&uscsi, logsCmdBlk, sizeof (logsCmdBlk));
2N/A set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b));
2N/A set_scsi_pt_data_in(&uscsi, resp, mx_resp_len);
2N/A res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT);
2N/A if (res) {
2N/A ret = res;
2N/A } else {
2N/A ret = uscsi.uscsi_status;
2N/A }
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * Save the logs by walking through the entries in the response buffer.
2N/A *
2N/A * resp buffer looks like:
2N/A *
2N/A * +=====-========-========-========-========-========-========-========-=====+
2N/A * | Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
2N/A * |Byte | | | | | | | | |
2N/A * |=====+====================================================================|
2N/A * | 0 | reserved | page code |
2N/A * |-----+--------------------------------------------------------------------|
2N/A * | 1 | Reserved |
2N/A * |-----+--------------------------------------------------------------------|
2N/A * | 2 |(MSB) Page Length(n-3) |
2N/A * | -- | |
2N/A * | 3 | (LSB) |
2N/A * |-----+--------------------------------------------------------------------|
2N/A * | 4 | Log Parameter (First)(Length X) |
2N/A * | -- | |
2N/A * | x+3 | |
2N/A * |-----+--------------------------------------------------------------------|
2N/A * |n-y+1| Log Parameter (Last)(Length y) |
2N/A * | -- | |
2N/A * | n | |
2N/A * +==========================================================================+
2N/A *
2N/A * Log parameter field looks like:
2N/A *
2N/A * +=====-========-========-========-========-========-========-========-=====+
2N/A * | Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
2N/A * |Byte | | | | | | | | |
2N/A * |=====+====================================================================|
2N/A * | 0 |(MSB) Parameter Code |
2N/A * | -- | |
2N/A * | 1 | (LSB) |
2N/A * |-----+--------------------------------------------------------------------|
2N/A * | 2 | DU | DS | TSD | ETC | TMC | LBIN | LP |
2N/A * |-----+--------------------------------------------------------------------|
2N/A * | 3 | Parameter Length(n-3) |
2N/A * |-----+--------------------------------------------------------------------|
2N/A * | 4 | Parameter Values |
2N/A * | -- | |
2N/A * | n | |
2N/A * |-----+--------------------------------------------------------------------|
2N/A */
2N/A
2N/Astatic int
2N/Asave_logs(unsigned char *resp, ses_log_call_t *data)
2N/A{
2N/A int k;
2N/A int param_code; /* Parameter code */
2N/A int param_len = 0; /* Paramter length */
2N/A unsigned char *log_param_ptr; /* Log parameter pointer */
2N/A unsigned char *log_str_ptr; /* ptr to ascii str returend by expander */
2N/A
2N/A char log_code[ENTRY_MAX_SIZE];
2N/A char log_level[ENTRY_MAX_SIZE];
2N/A nvlist_t *entry;
2N/A char entry_num[15];
2N/A int match_found = 0;
2N/A char save_buffer[MAX_LOG_ENTRY_SZ];
2N/A char entry_added = 0;
2N/A int all_log_data_len;
2N/A
2N/A /*
2N/A * Bytes 2 and 3 of response buffer contain the page length of
2N/A * the log entries returned.
2N/A */
2N/A all_log_data_len = SCSI_READ16(&resp[2]);
2N/A
2N/A /*
2N/A * Initialize log parameter pointer to point to first log entry.
2N/A * The resp includes 4 bytes of header info and then log entries
2N/A */
2N/A log_param_ptr = &resp[0] + 4;
2N/A
2N/A /*
2N/A * If multiple heads are reading the logs, it is possible that we
2N/A * could be re-reading some of the same log entries plus some
2N/A * new additional entries. Check to see if any entries in this read
2N/A * contain the same log entry as the last entry we read last time.
2N/A */
2N/A if (data->last_log_entry != NULL &&
2N/A (strlen(data->last_log_entry) == SES_LOG_VALID_LOG_SIZE)) {
2N/A /*
2N/A * We have a valid log entry from a previous read log
2N/A * operation.
2N/A */
2N/A
2N/A
2N/A /*
2N/A * Start walking each log entry in response buffer looking for
2N/A * a duplicate entry.
2N/A */
2N/A for (k = 0; k < all_log_data_len; k += param_len) {
2N/A /*
2N/A * Calculate log entry length
2N/A * Log param ptr [3] contains the log length minus the
2N/A * header info which is 4 bytes so add that in.
2N/A */
2N/A param_len = log_param_ptr[3] + 4;
2N/A
2N/A if (param_len <= 4) {
2N/A /*
2N/A * Only header information in this entry
2N/A * process next log entry
2N/A */
2N/A log_param_ptr += param_len;
2N/A continue;
2N/A }
2N/A
2N/A
2N/A /*
2N/A * initialize log_str_ptr to point to string info
2N/A * returned by expander
2N/A * first 4 bytes of log parameter contains
2N/A * 2 bytes of parameter code, 1 byte of Control data
2N/A * and 1 byte for parameter length. Log string begins
2N/A * after that so add 4 to log param ptr.
2N/A */
2N/A log_str_ptr = log_param_ptr + 4;
2N/A
2N/A /*
2N/A * Check to see if this is the
2N/A * same line
2N/A */
2N/A if (strncmp((char *)log_str_ptr, data->last_log_entry,
2N/A SES_LOG_VALID_LOG_SIZE) == 0) {
2N/A /* Found an exact match */
2N/A log_param_ptr += param_len;
2N/A k += param_len;
2N/A match_found = 1;
2N/A break;
2N/A }
2N/A log_param_ptr += param_len;
2N/A }
2N/A }
2N/A if (!match_found) {
2N/A log_param_ptr = &resp[0] + 4;
2N/A k = 0;
2N/A }
2N/A if (k == all_log_data_len) {
2N/A /*
2N/A * Either there was no log data or we have
2N/A * already read these log entries.
2N/A * Just return.
2N/A */
2N/A return (0);
2N/A }
2N/A
2N/A /* Grab memory to return logs with */
2N/A if (nvlist_alloc(&data->log_data, NV_UNIQUE_NAME, 0) != 0) {
2N/A /* Couldn't alloc memory for nvlist */
2N/A return (SES_LOG_FAILED_NVLIST_CREATE);
2N/A }
2N/A
2N/A (void) memset(log_code, 0, sizeof (log_code));
2N/A (void) memset(save_buffer, 0, sizeof (save_buffer));
2N/A (void) memset(log_level, 0, sizeof (log_level));
2N/A
2N/A /*
2N/A * Start saving new log entries
2N/A * Walk the log data adding any new entries
2N/A */
2N/A
2N/A for (; k < all_log_data_len; k += param_len) {
2N/A /*
2N/A * Calculate log entry length
2N/A * Log ptr [3] contains the log length minus the header info
2N/A * which is 4 bytes so add that in
2N/A */
2N/A param_len = log_param_ptr[3] + 4;
2N/A
2N/A if (param_len <= 4) {
2N/A /* Only header information in this entry */
2N/A /* process next log entry */
2N/A log_param_ptr += param_len;
2N/A continue;
2N/A }
2N/A
2N/A /*
2N/A * initialize log_str_ptr to point to string info of the log
2N/A * entry. First 4 bytes of log entry contains param code,
2N/A * control byte, and length. Log string starts after that.
2N/A */
2N/A log_str_ptr = log_param_ptr + 4;
2N/A
2N/A /*
2N/A * Format of log str is as follows
2N/A * "%8x %8x %8x %8x %8x %8x %8x %8x",
2N/A * log_entry.log_word0, log_entry.ts_u, log_entry.ts_l,
2N/A * log_entry.seq_num, log_entry.log_code, log_entry.log_word2,
2N/A * log_entry.log_word3, log_entry.log_word4
2N/A * following example has extra spaces removed to fit in 80 char
2N/A * 40004 0 42d5f5fe 185b 630002 fd0800 50800207 e482813
2N/A */
2N/A
2N/A (void) strncpy(save_buffer,
2N/A (const char *)log_str_ptr,
2N/A SES_LOG_VALID_LOG_SIZE);
2N/A
2N/A (void) strncpy(log_code,
2N/A (const char *)log_str_ptr+SES_LOG_CODE_START,
2N/A SES_LOG_SPECIFIC_ENTRY_SIZE);
2N/A
2N/A (void) strncpy(log_level,
2N/A (const char *) log_str_ptr +
2N/A SES_LOG_LEVEL_START, 1);
2N/A
2N/A
2N/A /* Add this entry to the nvlist log data */
2N/A if (nvlist_alloc(&entry, NV_UNIQUE_NAME, 0) != 0) {
2N/A /* Couldn't alloc space, return error */
2N/A return (SES_LOG_FAILED_NV_UNIQUE);
2N/A }
2N/A
2N/A
2N/A if (nvlist_add_string(entry, ENTRY_LOG, save_buffer) != 0) {
2N/A /* Error adding string, return error */
2N/A nvlist_free(entry);
2N/A return (SES_LOG_FAILED_NV_LOG);
2N/A }
2N/A
2N/A if (nvlist_add_string(entry, ENTRY_CODE, log_code) != 0) {
2N/A /* Error adding string, return error */
2N/A nvlist_free(entry);
2N/A return (SES_LOG_FAILED_NV_CODE);
2N/A }
2N/A if (nvlist_add_string(entry, ENTRY_SEVERITY, log_level) != 0) {
2N/A /* Error adding srtring, return error */
2N/A nvlist_free(entry);
2N/A return (SES_LOG_FAILED_NV_SEV);
2N/A }
2N/A
2N/A param_code = SCSI_READ16(&log_param_ptr[0]);
2N/A
2N/A (void) snprintf(entry_num, sizeof (entry_num),
2N/A "%s%d", ENTRY_PREFIX, param_code);
2N/A
2N/A if (nvlist_add_nvlist(data->log_data, entry_num, entry) != 0) {
2N/A /* Error adding nvlist, return error */
2N/A nvlist_free(entry);
2N/A return (SES_LOG_FAILED_NV_ENTRY);
2N/A }
2N/A nvlist_free(entry);
2N/A
2N/A entry_added = 1;
2N/A (data->number_log_entries)++;
2N/A
2N/A log_param_ptr += param_len;
2N/A
2N/A }
2N/A if (entry_added) {
2N/A /* Update the last log entry string with last one read */
2N/A (void) strncpy(data->last_log_entry, save_buffer, MAXNAMELEN);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A
2N/A
2N/A/* Setup struct to send command to device */
2N/Astatic void
2N/Aset_scsi_pt_data_out(struct uscsi_cmd *uscsi, const unsigned char *dxferp,
2N/A int dxfer_len)
2N/A{
2N/A if (dxfer_len > 0) {
2N/A uscsi->uscsi_bufaddr = (char *)dxferp;
2N/A uscsi->uscsi_buflen = dxfer_len;
2N/A uscsi->uscsi_flags = USCSI_WRITE | USCSI_ISOLATE |
2N/A USCSI_RQENABLE;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Invokes a SCSI MODE SENSE(10) command.
2N/A * Return:
2N/A * 0 for success
2N/A * SG_LIB_CAT_INVALID_OP -> invalid opcode
2N/A * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb
2N/A * SG_LIB_CAT_NOT_READY -> device not ready
2N/A * -1 -> other failure
2N/A */
2N/A
2N/Astatic int
2N/Asg_ll_mode_sense10(int sg_fd, void * resp, int mx_resp_len)
2N/A{
2N/A int res, ret;
2N/A unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] =
2N/A {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2N/A unsigned char sense_b[SENSE_BUFF_LEN];
2N/A struct uscsi_cmd uscsi;
2N/A
2N/A modesCmdBlk[1] = 0;
2N/A modesCmdBlk[2] = 0; /* page code 0 vendor specific */
2N/A modesCmdBlk[3] = 0;
2N/A modesCmdBlk[7] = (unsigned char) ((mx_resp_len >> 8) & 0xff);
2N/A modesCmdBlk[8] = (unsigned char) (mx_resp_len & 0xff);
2N/A
2N/A construct_scsi_pt_obj(&uscsi);
2N/A set_scsi_pt_cdb(&uscsi, modesCmdBlk, sizeof (modesCmdBlk));
2N/A set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b));
2N/A set_scsi_pt_data_in(&uscsi, (unsigned char *) resp, mx_resp_len);
2N/A res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT);
2N/A if (res) {
2N/A ret = res;
2N/A } else {
2N/A ret = uscsi.uscsi_status;
2N/A }
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * Invokes a SCSI MODE SELECT(10) command.
2N/A * Return:
2N/A * 0 for success.
2N/A * SG_LIB_CAT_INVALID_OP for invalid opcode
2N/A * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
2N/A * SG_LIB_CAT_NOT_READY -> device not ready,
2N/A * -1 -> other failure
2N/A */
2N/Astatic int
2N/Asg_ll_mode_select10(int sg_fd, void * paramp, int param_len)
2N/A{
2N/A int res, ret;
2N/A unsigned char modesCmdBlk[MODE_SELECT10_CMDLEN] =
2N/A {SCMD_MODE_SELECT_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2N/A unsigned char sense_b[SENSE_BUFF_LEN];
2N/A struct uscsi_cmd uscsi;
2N/A
2N/A
2N/A modesCmdBlk[1] = 0;
2N/A /*
2N/A * modesCmdBlk 2 equal 0 PC 0 return current page code 0 return
2N/A * vendor specific
2N/A */
2N/A
2N/A modesCmdBlk[7] = (unsigned char)((param_len >> 8) & 0xff);
2N/A modesCmdBlk[8] = (unsigned char)(param_len & 0xff);
2N/A
2N/A construct_scsi_pt_obj(&uscsi);
2N/A
2N/A set_scsi_pt_cdb(&uscsi, modesCmdBlk, sizeof (modesCmdBlk));
2N/A set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b));
2N/A set_scsi_pt_data_out(&uscsi, (unsigned char *) paramp, param_len);
2N/A res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT);
2N/A if (res) {
2N/A ret = res;
2N/A } else {
2N/A ret = uscsi.uscsi_status;
2N/A }
2N/A return (ret);
2N/A}
2N/A
2N/A
2N/A
2N/A/*
2N/A * MODE SENSE 10 commands yield a response that has block descriptors followed
2N/A * by mode pages. In most cases users are interested in the first mode page.
2N/A * This function returns the(byte) offset of the start of the first mode page.
2N/A * Returns >= 0 is successful or -1 if failure. If there is a failure
2N/A * a message is written to err_buff.
2N/A */
2N/A
2N/A/*
2N/A * return data looks like:
2N/A * Table 92 - Mode parameter header(10)
2N/A * Bit
2N/A * Byte
2N/A * 7 6 5 4 3 2 1 0
2N/A * ----------------------------------------------------------
2N/A * 0 MSB Data length
2N/A * 1 LSB Data length
2N/A * ----------------------------------------------------------
2N/A * 2 Medium type
2N/A * ----------------------------------------------------------
2N/A * 3 Device-specific parameter
2N/A * ----------------------------------------------------------
2N/A * 4 Reserved
2N/A * ----------------------------------------------------------
2N/A * 5 Reserved
2N/A * ----------------------------------------------------------
2N/A * 6 MSB block descriptor length
2N/A * 7 LSB block descriptor length
2N/A * ----------------------------------------------------------
2N/A * block desciptors....
2N/A * -----------------------
2N/A * mode sense page:
2N/A * 0 : ps Reserved : page Code
2N/A * 1 : Page Length(n-1)
2N/A * 2-N Mode parameters
2N/A */
2N/Astatic int
2N/Asg_mode_page_offset(const unsigned char *resp, int resp_len)
2N/A{
2N/A int bd_len;
2N/A int calc_len;
2N/A int offset;
2N/A
2N/A if ((NULL == resp) || (resp_len < 8)) {
2N/A /* Too short of a response buffer */
2N/A return (-1);
2N/A }
2N/A
2N/A calc_len = (resp[0] << 8) + resp[1] + 2;
2N/A bd_len = (resp[6] << 8) + resp[7];
2N/A
2N/A /* LongLBA doesn't change this calculation */
2N/A offset = bd_len + MODE10_RESP_HDR_LEN;
2N/A
2N/A if ((offset + 2) > resp_len) {
2N/A /* Given response length to small */
2N/A offset = -1;
2N/A } else if ((offset + 2) > calc_len) {
2N/A /* Calculated response length too small */
2N/A offset = -1;
2N/A }
2N/A return (offset);
2N/A}
2N/A
2N/A/* fill in unique host id */
2N/Astatic void
2N/Afill_host_id(uint8_t *host_id)
2N/A{
2N/A scf_simple_prop_t *prop;
2N/A char *str;
2N/A long myhostid;
2N/A
2N/A /*
2N/A * Attempt to find the factory/asn number from the appliance kit
2N/A * The appliance kit contains all the information for the AK systems.
2N/A * To see the values run:
2N/A * svcprop svc:/appliance/kit/identity:default
2N/A * Looking for this:
2N/A * factory/asn astring c0af6421-3ead-4b35-ccdf-8016a8f54825
2N/A */
2N/A if ((prop = scf_simple_prop_get(NULL,
2N/A "svc:/appliance/kit/identity:default", "factory", "asn")) != NULL) {
2N/A if ((str = scf_simple_prop_next_astring(prop)) != NULL) {
2N/A if (!uuid_parse(str, host_id)) {
2N/A /* Worked okay */
2N/A scf_simple_prop_free(prop);
2N/A return;
2N/A }
2N/A }
2N/A scf_simple_prop_free(prop);
2N/A }
2N/A /* If here, didn't get unique host id from AK */
2N/A /* Use host id */
2N/A myhostid = gethostid();
2N/A host_id[12] = (myhostid & 0xff000000) >> 24;
2N/A host_id[13] = (myhostid & 0xff0000) >> 16;
2N/A host_id[14] = (myhostid & 0xff00) >> 8;
2N/A host_id[15] = myhostid & 0xff;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Clear logs
2N/A */
2N/Astatic int
2N/Aclear_log(int sg_fd, ses_log_call_t *data)
2N/A{
2N/A
2N/A int res, alloc_len, off;
2N/A int md_len;
2N/A int read_in_len = 0;
2N/A unsigned char ref_md[MAX_ALLOC_LEN];
2N/A struct log_clear_control_struct clear_data;
2N/A long poll_time;
2N/A char seq_num_str[10];
2N/A unsigned long seq_num = 0;
2N/A
2N/A (void) memset(&clear_data, 0, sizeof (clear_data));
2N/A
2N/A clear_data.pageControls = 0x40;
2N/A clear_data.subpage_code = 0;
2N/A clear_data.page_lengthLower = 0x16;
2N/A
2N/A fill_host_id((uint8_t *)&clear_data.host_id);
2N/A
2N/A /*
2N/A * convert nanosecond time to seconds
2N/A */
2N/A poll_time = data->poll_time / 1000000000;
2N/A /* Add 5 minutes to poll time to allow for data retrieval time */
2N/A poll_time = poll_time + 300;
2N/A clear_data.timeout[0] = (poll_time & 0xff00) >> 8;
2N/A clear_data.timeout[1] = poll_time & 0xff;
2N/A
2N/A /*
2N/A * retrieve the last read sequence number from the last
2N/A * log entry read.
2N/A */
2N/A if (data->last_log_entry != NULL &&
2N/A (strlen(data->last_log_entry) == SES_LOG_VALID_LOG_SIZE)) {
2N/A /*
2N/A * We have a valid log entry from a previous read log
2N/A * operation.
2N/A */
2N/A (void) strncpy(seq_num_str,
2N/A (const char *) data->last_log_entry +
2N/A SES_LOG_SEQ_NUM_START, 8);
2N/A seq_num = strtoul(seq_num_str, 0, 16);
2N/A }
2N/A clear_data.seq_clear[0] = (seq_num & 0xff000000) >> 24;
2N/A clear_data.seq_clear[1] = (seq_num & 0xff0000) >> 16;
2N/A clear_data.seq_clear[2] = (seq_num & 0xff00) >> 8;
2N/A clear_data.seq_clear[3] = (seq_num & 0xff);
2N/A
2N/A read_in_len = sizeof (clear_data);
2N/A
2N/A
2N/A /* do MODE SENSE to fetch current values */
2N/A (void) memset(ref_md, 0, MAX_ALLOC_LEN);
2N/A alloc_len = MAX_ALLOC_LEN;
2N/A
2N/A
2N/A res = sg_ll_mode_sense10(sg_fd, ref_md, alloc_len);
2N/A if (0 != res) {
2N/A /* Error during mode sense */
2N/A return (SES_LOG_FAILED_MODE_SENSE);
2N/A }
2N/A
2N/A /* Setup mode Select to clear logs */
2N/A off = sg_mode_page_offset(ref_md, alloc_len);
2N/A if (off < 0) {
2N/A /* Mode page offset error */
2N/A return (SES_LOG_FAILED_MODE_SENSE_OFFSET);
2N/A }
2N/A md_len = (ref_md[0] << 8) + ref_md[1] + 2;
2N/A
2N/A ref_md[0] = 0;
2N/A ref_md[1] = 0;
2N/A
2N/A if (md_len > alloc_len) {
2N/A /* Data length to large */
2N/A return (SES_LOG_FAILED_BAD_DATA_LEN);
2N/A }
2N/A
2N/A if ((md_len - off) != read_in_len) {
2N/A /* Content length not correct */
2N/A return (SES_LOG_FAILED_BAD_CONTENT_LEN);
2N/A }
2N/A
2N/A if ((clear_data.pageControls & 0x40) != (ref_md[off] & 0x40)) {
2N/A /* reference model doesn't have use subpage format bit set */
2N/A /* Even though it should have */
2N/A /* don't send the command */
2N/A return (SES_LOG_FAILED_FORMAT_PAGE_ERR);
2N/A }
2N/A
2N/A (void) memcpy(ref_md + off, (const void *) &clear_data,
2N/A sizeof (clear_data));
2N/A
2N/A res = sg_ll_mode_select10(sg_fd, ref_md, md_len);
2N/A if (res != 0) {
2N/A return (SES_LOG_FAILED_MODE_SELECT);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A/*
2N/A * Gather data from given device.
2N/A */
2N/Astatic int
2N/Agather_data(char *device_name, ses_log_call_t *data)
2N/A{
2N/A int sg_fd;
2N/A int resp_len, res;
2N/A unsigned char rsp_buff[MAX_ALLOC_LEN];
2N/A int error;
2N/A
2N/A /* Open device */
2N/A if ((sg_fd = open_device(device_name)) < 0) {
2N/A /* Failed to open device */
2N/A return (SES_LOG_FAILED_TO_OPEN_DEVICE);
2N/A }
2N/A
2N/A /* Read the logs */
2N/A (void) memset(rsp_buff, 0, sizeof (rsp_buff));
2N/A resp_len = 0x8000; /* Maximum size available to read */
2N/A res = read_log(sg_fd, rsp_buff, resp_len);
2N/A
2N/A if (res != 0) {
2N/A /* Some sort of Error during read of logs */
2N/A (void) close(sg_fd);
2N/A return (SES_LOG_FAILED_TO_READ_DEVICE);
2N/A }
2N/A
2N/A /* Save the logs */
2N/A error = save_logs(rsp_buff, data);
2N/A if (error != 0) {
2N/A (void) close(sg_fd);
2N/A return (error);
2N/A }
2N/A /* Clear the logs */
2N/A error = clear_log(sg_fd, data);
2N/A
2N/A (void) close(sg_fd);
2N/A
2N/A return (error);
2N/A}
2N/A
2N/A/*
2N/A * Access the SES target identified by the indicated path. Read the logs
2N/A * and return them in a nvlist.
2N/A */
2N/Aint
2N/Aaccess_ses_log(ses_log_call_t *data)
2N/A{
2N/A char real_path[MAXPATHLEN];
2N/A struct stat buffer;
2N/A int error;
2N/A
2N/A /* Initialize return data */
2N/A data->log_data = NULL;
2N/A data->number_log_entries = 0;
2N/A data->size_of_log_entries = 0;
2N/A
2N/A if (data->target_path == NULL) {
2N/A /* NULL Target path, return error */
2N/A return (SES_LOG_FAILED_NULL_TARGET_PATH);
2N/A }
2N/A
2N/A /* Try to find a valid path */
2N/A (void) snprintf(real_path, sizeof (real_path), "/devices%s:ses",
2N/A data->target_path);
2N/A
2N/A if (stat(real_path, &buffer) != 0) {
2N/A
2N/A (void) snprintf(real_path, sizeof (real_path), "/devices%s:0",
2N/A data->target_path);
2N/A if (stat(real_path, &buffer) != 0) {
2N/A /* Couldn't find a path that exists */
2N/A return (SES_LOG_FAILED_BAD_TARGET_PATH);
2N/A }
2N/A }
2N/A
2N/A error = gather_data(real_path, data);
2N/A
2N/A /* Update the size of log entries being returned */
2N/A data->size_of_log_entries =
2N/A data->number_log_entries * SES_LOG_VALID_LOG_SIZE;
2N/A
2N/A return (error);
2N/A}