xref: /titanic_44/usr/src/lib/fm/libseslog/common/libseslog.c (revision 6a634c9dca3093f3922e4b7ab826d7bdf17bf78e)
1*e4f5a11dSJames Kremer /*
2*e4f5a11dSJames Kremer  * CDDL HEADER START
3*e4f5a11dSJames Kremer  *
4*e4f5a11dSJames Kremer  * The contents of this file are subject to the terms of the
5*e4f5a11dSJames Kremer  * Common Development and Distribution License (the "License").
6*e4f5a11dSJames Kremer  * You may not use this file except in compliance with the License.
7*e4f5a11dSJames Kremer  *
8*e4f5a11dSJames Kremer  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*e4f5a11dSJames Kremer  * or http://www.opensolaris.org/os/licensing.
10*e4f5a11dSJames Kremer  * See the License for the specific language governing permissions
11*e4f5a11dSJames Kremer  * and limitations under the License.
12*e4f5a11dSJames Kremer  *
13*e4f5a11dSJames Kremer  * When distributing Covered Code, include this CDDL HEADER in each
14*e4f5a11dSJames Kremer  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*e4f5a11dSJames Kremer  * If applicable, add the following below this CDDL HEADER, with the
16*e4f5a11dSJames Kremer  * fields enclosed by brackets "[]" replaced with your own identifying
17*e4f5a11dSJames Kremer  * information: Portions Copyright [yyyy] [name of copyright owner]
18*e4f5a11dSJames Kremer  *
19*e4f5a11dSJames Kremer  * CDDL HEADER END
20*e4f5a11dSJames Kremer  */
21*e4f5a11dSJames Kremer /*
22*e4f5a11dSJames Kremer  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23*e4f5a11dSJames Kremer  */
24*e4f5a11dSJames Kremer 
25*e4f5a11dSJames Kremer /*
26*e4f5a11dSJames Kremer  * SES Log reader library
27*e4f5a11dSJames Kremer  *
28*e4f5a11dSJames Kremer  * This library is responsible for accessing the SES log at the target address,
29*e4f5a11dSJames Kremer  * formatting and returning any log entries found.
30*e4f5a11dSJames Kremer  *
31*e4f5a11dSJames Kremer  * The data will be returned in an nvlist_t structure allocated here.
32*e4f5a11dSJames Kremer  */
33*e4f5a11dSJames Kremer 
34*e4f5a11dSJames Kremer #include <assert.h>
35*e4f5a11dSJames Kremer #include <errno.h>
36*e4f5a11dSJames Kremer #include <fcntl.h>
37*e4f5a11dSJames Kremer #include <sys/param.h>
38*e4f5a11dSJames Kremer #include <libseslog.h>
39*e4f5a11dSJames Kremer #include <stdlib.h>
40*e4f5a11dSJames Kremer #include <string.h>
41*e4f5a11dSJames Kremer #include <sys/stat.h>
42*e4f5a11dSJames Kremer #include <unistd.h>
43*e4f5a11dSJames Kremer #include <dirent.h>
44*e4f5a11dSJames Kremer #include <sys/scsi/generic/commands.h>
45*e4f5a11dSJames Kremer #include <sys/scsi/generic/status.h>
465cf8276bSTodd McKenney #include <sys/scsi/impl/commands.h>
47*e4f5a11dSJames Kremer 
48*e4f5a11dSJames Kremer /*
49*e4f5a11dSJames Kremer  * open the device with given device name
50*e4f5a11dSJames Kremer  */
51*e4f5a11dSJames Kremer static int
open_device(const char * device_name)52*e4f5a11dSJames Kremer open_device(const char *device_name)
53*e4f5a11dSJames Kremer {
54*e4f5a11dSJames Kremer 	int oflags = O_NONBLOCK | O_RDWR;
55*e4f5a11dSJames Kremer 	int fd;
56*e4f5a11dSJames Kremer 
57*e4f5a11dSJames Kremer 	fd = open(device_name, oflags);
58*e4f5a11dSJames Kremer 	if (fd < 0)
59*e4f5a11dSJames Kremer 		fd = -errno;
60*e4f5a11dSJames Kremer 	return (fd);
61*e4f5a11dSJames Kremer }
62*e4f5a11dSJames Kremer 
63*e4f5a11dSJames Kremer /*
64*e4f5a11dSJames Kremer  * Initialize scsi struct
65*e4f5a11dSJames Kremer  */
66*e4f5a11dSJames Kremer static void
construct_scsi_pt_obj(struct uscsi_cmd * uscsi)67*e4f5a11dSJames Kremer construct_scsi_pt_obj(struct uscsi_cmd *uscsi)
68*e4f5a11dSJames Kremer {
69*e4f5a11dSJames Kremer 	(void) memset(uscsi, 0, sizeof (struct uscsi_cmd));
70*e4f5a11dSJames Kremer 	uscsi->uscsi_timeout = DEF_PT_TIMEOUT;
71*e4f5a11dSJames Kremer 	uscsi->uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_RQENABLE;
72*e4f5a11dSJames Kremer }
73*e4f5a11dSJames Kremer 
74*e4f5a11dSJames Kremer /*
75*e4f5a11dSJames Kremer  * set control cdb of scsi structure
76*e4f5a11dSJames Kremer  */
77*e4f5a11dSJames Kremer static void
set_scsi_pt_cdb(struct uscsi_cmd * uscsi,const unsigned char * cdb,int cdb_len)78*e4f5a11dSJames Kremer set_scsi_pt_cdb(struct uscsi_cmd *uscsi, const unsigned char *cdb,
79*e4f5a11dSJames Kremer     int cdb_len)
80*e4f5a11dSJames Kremer {
81*e4f5a11dSJames Kremer 	uscsi->uscsi_cdb = (char *)cdb;
82*e4f5a11dSJames Kremer 	uscsi->uscsi_cdblen = cdb_len;
83*e4f5a11dSJames Kremer }
84*e4f5a11dSJames Kremer 
85*e4f5a11dSJames Kremer /*
86*e4f5a11dSJames Kremer  * initialize sense data
87*e4f5a11dSJames Kremer  */
88*e4f5a11dSJames Kremer static void
set_scsi_pt_sense(struct uscsi_cmd * uscsi,unsigned char * sense,int max_sense_len)89*e4f5a11dSJames Kremer set_scsi_pt_sense(struct uscsi_cmd *uscsi, unsigned char *sense,
90*e4f5a11dSJames Kremer     int max_sense_len)
91*e4f5a11dSJames Kremer {
92*e4f5a11dSJames Kremer 	(void) memset(sense, 0, max_sense_len);
93*e4f5a11dSJames Kremer 	uscsi->uscsi_rqbuf = (char *)sense;
94*e4f5a11dSJames Kremer 	uscsi->uscsi_rqlen = max_sense_len;
95*e4f5a11dSJames Kremer }
96*e4f5a11dSJames Kremer 
97*e4f5a11dSJames Kremer /*
98*e4f5a11dSJames Kremer  * Initialize data going to device
99*e4f5a11dSJames Kremer  */
100*e4f5a11dSJames Kremer static void
set_scsi_pt_data_in(struct uscsi_cmd * uscsi,unsigned char * dxferp,int dxfer_len)101*e4f5a11dSJames Kremer set_scsi_pt_data_in(struct uscsi_cmd *uscsi, unsigned char *dxferp,
102*e4f5a11dSJames Kremer 		    int dxfer_len)
103*e4f5a11dSJames Kremer {
104*e4f5a11dSJames Kremer 	if (dxfer_len > 0) {
105*e4f5a11dSJames Kremer 		uscsi->uscsi_bufaddr = (char *)dxferp;
106*e4f5a11dSJames Kremer 		uscsi->uscsi_buflen = dxfer_len;
107*e4f5a11dSJames Kremer 		uscsi->uscsi_flags = USCSI_READ | USCSI_ISOLATE |
108*e4f5a11dSJames Kremer 		    USCSI_RQENABLE;
109*e4f5a11dSJames Kremer 	}
110*e4f5a11dSJames Kremer }
111*e4f5a11dSJames Kremer 
112*e4f5a11dSJames Kremer /*
113*e4f5a11dSJames Kremer  * Executes SCSI command(or at least forwards it to lower layers).
114*e4f5a11dSJames Kremer  */
115*e4f5a11dSJames Kremer static int
do_scsi_pt(struct uscsi_cmd * uscsi,int fd,int time_secs)116*e4f5a11dSJames Kremer do_scsi_pt(struct uscsi_cmd *uscsi, int fd, int time_secs)
117*e4f5a11dSJames Kremer {
118*e4f5a11dSJames Kremer 	if (time_secs > 0)
119*e4f5a11dSJames Kremer 		uscsi->uscsi_timeout = time_secs;
120*e4f5a11dSJames Kremer 
121*e4f5a11dSJames Kremer 	if (ioctl(fd, USCSICMD, uscsi)) {
122*e4f5a11dSJames Kremer 		/* Took an error */
123*e4f5a11dSJames Kremer 		return (errno);
124*e4f5a11dSJames Kremer 	}
125*e4f5a11dSJames Kremer 	return (0);
126*e4f5a11dSJames Kremer }
127*e4f5a11dSJames Kremer 
128*e4f5a11dSJames Kremer 
129*e4f5a11dSJames Kremer /*
130*e4f5a11dSJames Kremer  * Read log from device
131*e4f5a11dSJames Kremer  * Invokes a SCSI LOG SENSE command.
132*e4f5a11dSJames Kremer  * Return:
133*e4f5a11dSJames Kremer  * 0 -> success
134*e4f5a11dSJames Kremer  * SG_LIB_CAT_INVALID_OP -> Log Sense not supported,
135*e4f5a11dSJames Kremer  * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
136*e4f5a11dSJames Kremer  * SG_LIB_CAT_NOT_READY -> device not ready,
137*e4f5a11dSJames Kremer  * -1 -> other failure
138*e4f5a11dSJames Kremer  */
139*e4f5a11dSJames Kremer 
140*e4f5a11dSJames Kremer static int
read_log(int sg_fd,unsigned char * resp,int mx_resp_len)141*e4f5a11dSJames Kremer read_log(int sg_fd, unsigned char *resp, int mx_resp_len)
142*e4f5a11dSJames Kremer {
143*e4f5a11dSJames Kremer 	int res, ret;
144*e4f5a11dSJames Kremer 	unsigned char logsCmdBlk[CDB_GROUP1] =
145*e4f5a11dSJames Kremer 	    {SCMD_LOG_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
146*e4f5a11dSJames Kremer 	unsigned char sense_b[SENSE_BUFF_LEN];
147*e4f5a11dSJames Kremer 	struct uscsi_cmd uscsi;
148*e4f5a11dSJames Kremer 
149*e4f5a11dSJames Kremer 	if (mx_resp_len > 0xffff) {
150*e4f5a11dSJames Kremer 		return (-1);
151*e4f5a11dSJames Kremer 	}
152*e4f5a11dSJames Kremer 	logsCmdBlk[1] = 0;
153*e4f5a11dSJames Kremer 	/* pc = 1, pg_code = 0x7 (logs page) */
154*e4f5a11dSJames Kremer 	/* (((pc << 6) & 0xc0) | (pg_code & 0x3f)) = 0x47; */
155*e4f5a11dSJames Kremer 	logsCmdBlk[2] = 0x47;
156*e4f5a11dSJames Kremer 	/* pc = 1 current values */
157*e4f5a11dSJames Kremer 	logsCmdBlk[3] = 0; /* No subpage code */
158*e4f5a11dSJames Kremer 	logsCmdBlk[5] = 0; /* Want all logs starting from 0 */
159*e4f5a11dSJames Kremer 	logsCmdBlk[6] = 0;
160*e4f5a11dSJames Kremer 	logsCmdBlk[7] = (unsigned char) ((mx_resp_len >> 8) & 0xff);
161*e4f5a11dSJames Kremer 	logsCmdBlk[8] = (unsigned char) (mx_resp_len & 0xff);
162*e4f5a11dSJames Kremer 
163*e4f5a11dSJames Kremer 	construct_scsi_pt_obj(&uscsi);
164*e4f5a11dSJames Kremer 
165*e4f5a11dSJames Kremer 	set_scsi_pt_cdb(&uscsi, logsCmdBlk, sizeof (logsCmdBlk));
166*e4f5a11dSJames Kremer 	set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b));
167*e4f5a11dSJames Kremer 	set_scsi_pt_data_in(&uscsi, resp, mx_resp_len);
168*e4f5a11dSJames Kremer 	res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT);
169*e4f5a11dSJames Kremer 	if (res) {
170*e4f5a11dSJames Kremer 		ret = res;
171*e4f5a11dSJames Kremer 	} else {
172*e4f5a11dSJames Kremer 		ret = uscsi.uscsi_status;
173*e4f5a11dSJames Kremer 	}
174*e4f5a11dSJames Kremer 	return (ret);
175*e4f5a11dSJames Kremer }
176*e4f5a11dSJames Kremer 
177*e4f5a11dSJames Kremer /*
178*e4f5a11dSJames Kremer  * Save the logs by walking through the entries in the response buffer.
179*e4f5a11dSJames Kremer  *
180*e4f5a11dSJames Kremer  * resp buffer looks like:
181*e4f5a11dSJames Kremer  *
182*e4f5a11dSJames Kremer  * +=====-========-========-========-========-========-========-========-=====+
183*e4f5a11dSJames Kremer  * |  Bit|   7    |   6    |   5    |   4    |   3    |   2    |   1    |   0 |
184*e4f5a11dSJames Kremer  * |Byte |        |        |        |        |        |        |        |     |
185*e4f5a11dSJames Kremer  * |=====+====================================================================|
186*e4f5a11dSJames Kremer  * | 0   |  reserved       |     page code                                    |
187*e4f5a11dSJames Kremer  * |-----+--------------------------------------------------------------------|
188*e4f5a11dSJames Kremer  * | 1   |                   Reserved                                         |
189*e4f5a11dSJames Kremer  * |-----+--------------------------------------------------------------------|
190*e4f5a11dSJames Kremer  * | 2   |(MSB)                           Page Length(n-3)                    |
191*e4f5a11dSJames Kremer  * | --  |                                                                    |
192*e4f5a11dSJames Kremer  * | 3   |                                                            (LSB)   |
193*e4f5a11dSJames Kremer  * |-----+--------------------------------------------------------------------|
194*e4f5a11dSJames Kremer  * | 4   |                           Log Parameter (First)(Length X)          |
195*e4f5a11dSJames Kremer  * | --  |                                                                    |
196*e4f5a11dSJames Kremer  * | x+3 |                                                                    |
197*e4f5a11dSJames Kremer  * |-----+--------------------------------------------------------------------|
198*e4f5a11dSJames Kremer  * |n-y+1|                           Log Parameter (Last)(Length y)           |
199*e4f5a11dSJames Kremer  * | --  |                                                                    |
200*e4f5a11dSJames Kremer  * | n   |                                                                    |
201*e4f5a11dSJames Kremer  * +==========================================================================+
202*e4f5a11dSJames Kremer  *
203*e4f5a11dSJames Kremer  * Log parameter field looks like:
204*e4f5a11dSJames Kremer  *
205*e4f5a11dSJames Kremer  * +=====-========-========-========-========-========-========-========-=====+
206*e4f5a11dSJames Kremer  * |  Bit|   7    |   6    |   5    |   4    |   3    |   2    |   1    |   0 |
207*e4f5a11dSJames Kremer  * |Byte |        |        |        |        |        |        |        |     |
208*e4f5a11dSJames Kremer  * |=====+====================================================================|
209*e4f5a11dSJames Kremer  * | 0   |(MSB)                           Parameter Code                      |
210*e4f5a11dSJames Kremer  * | --  |                                                                    |
211*e4f5a11dSJames Kremer  * | 1   |                                                            (LSB)   |
212*e4f5a11dSJames Kremer  * |-----+--------------------------------------------------------------------|
213*e4f5a11dSJames Kremer  * | 2   | DU     |  DS    |  TSD    | ETC   |         TMC     |  LBIN  | LP  |
214*e4f5a11dSJames Kremer  * |-----+--------------------------------------------------------------------|
2155cf8276bSTodd McKenney  * | 3   |                          Parameter Length(n-3)                     |
216*e4f5a11dSJames Kremer  * |-----+--------------------------------------------------------------------|
217*e4f5a11dSJames Kremer  * | 4   |                           Parameter Values                         |
218*e4f5a11dSJames Kremer  * | --  |                                                                    |
219*e4f5a11dSJames Kremer  * | n	 |                                                                    |
220*e4f5a11dSJames Kremer  * |-----+--------------------------------------------------------------------|
221*e4f5a11dSJames Kremer  */
222*e4f5a11dSJames Kremer 
223*e4f5a11dSJames Kremer static int
save_logs(unsigned char * resp,ses_log_call_t * data)2245cf8276bSTodd McKenney save_logs(unsigned char *resp, ses_log_call_t *data)
225*e4f5a11dSJames Kremer {
2265cf8276bSTodd McKenney 	int k;
2275cf8276bSTodd McKenney 	int param_code; 	/* Parameter code */
2285cf8276bSTodd McKenney 	int param_len = 0; 	/* Paramter length */
2295cf8276bSTodd McKenney 	unsigned char *log_param_ptr; 	/* Log parameter pointer */
230*e4f5a11dSJames Kremer 	unsigned char *log_str_ptr; /* ptr to ascii str returend by expander */
231*e4f5a11dSJames Kremer 
2325cf8276bSTodd McKenney 	char log_code[ENTRY_MAX_SIZE];
2335cf8276bSTodd McKenney 	char log_level[ENTRY_MAX_SIZE];
234*e4f5a11dSJames Kremer 	nvlist_t *entry;
235*e4f5a11dSJames Kremer 	char entry_num[15];
236*e4f5a11dSJames Kremer 	int match_found = 0;
2375cf8276bSTodd McKenney 	char save_buffer[MAX_LOG_ENTRY_SZ];
238*e4f5a11dSJames Kremer 	char entry_added = 0;
2395cf8276bSTodd McKenney 	int all_log_data_len;
240*e4f5a11dSJames Kremer 
241*e4f5a11dSJames Kremer 	/*
2425cf8276bSTodd McKenney 	 * Bytes 2 and 3 of response buffer contain the page length of
2435cf8276bSTodd McKenney 	 * the log entries returned.
2445cf8276bSTodd McKenney 	 */
2455cf8276bSTodd McKenney 	all_log_data_len = SCSI_READ16(&resp[2]);
2465cf8276bSTodd McKenney 
2475cf8276bSTodd McKenney 	/*
2485cf8276bSTodd McKenney 	 * Initialize log parameter pointer to point to first log entry.
2495cf8276bSTodd McKenney 	 * The resp includes 4 bytes of header info and then log entries
2505cf8276bSTodd McKenney 	 */
2515cf8276bSTodd McKenney 	log_param_ptr = &resp[0] + 4;
2525cf8276bSTodd McKenney 
2535cf8276bSTodd McKenney 	/*
2545cf8276bSTodd McKenney 	 * If multiple heads are reading the logs, it is possible that we
2555cf8276bSTodd McKenney 	 * could be re-reading some of the same log entries plus some
2565cf8276bSTodd McKenney 	 * new additional entries. Check to see if any entries in this read
2575cf8276bSTodd McKenney 	 * contain the same log entry as the last entry we read last time.
2585cf8276bSTodd McKenney 	 */
2595cf8276bSTodd McKenney 	if (data->last_log_entry != NULL &&
2605cf8276bSTodd McKenney 	    (strlen(data->last_log_entry) == SES_LOG_VALID_LOG_SIZE)) {
2615cf8276bSTodd McKenney 		/*
2625cf8276bSTodd McKenney 		 * We have a valid log entry from a previous read log
2635cf8276bSTodd McKenney 		 * operation.
2645cf8276bSTodd McKenney 		 */
2655cf8276bSTodd McKenney 
2665cf8276bSTodd McKenney 
2675cf8276bSTodd McKenney 		/*
2685cf8276bSTodd McKenney 		 * Start walking each log entry in response buffer looking for
269*e4f5a11dSJames Kremer 		 * a duplicate entry.
270*e4f5a11dSJames Kremer 		 */
2715cf8276bSTodd McKenney 		for (k = 0; k < all_log_data_len; k += param_len) {
272*e4f5a11dSJames Kremer 			/*
2735cf8276bSTodd McKenney 			 * Calculate log entry length
2745cf8276bSTodd McKenney 			 * Log param ptr [3] contains the log length minus the
2755cf8276bSTodd McKenney 			 * header info which is 4 bytes so add that in.
276*e4f5a11dSJames Kremer 			 */
2775cf8276bSTodd McKenney 			param_len = log_param_ptr[3] + 4;
2785cf8276bSTodd McKenney 
2795cf8276bSTodd McKenney 			if (param_len <= 4) {
2805cf8276bSTodd McKenney 				/*
2815cf8276bSTodd McKenney 				 * Only header information in this entry
2825cf8276bSTodd McKenney 				 * process next log entry
2835cf8276bSTodd McKenney 				 */
2845cf8276bSTodd McKenney 				log_param_ptr += param_len;
2855cf8276bSTodd McKenney 				continue;
286*e4f5a11dSJames Kremer 			}
2875cf8276bSTodd McKenney 
2885cf8276bSTodd McKenney 
289*e4f5a11dSJames Kremer 			/*
2905cf8276bSTodd McKenney 			 * initialize log_str_ptr to point to string info
2915cf8276bSTodd McKenney 			 * returned by expander
2925cf8276bSTodd McKenney 			 * first 4 bytes of log parameter contains
2935cf8276bSTodd McKenney 			 * 2 bytes of parameter code, 1 byte of Control data
2945cf8276bSTodd McKenney 			 * and 1 byte for parameter length. Log string begins
2955cf8276bSTodd McKenney 			 * after that so add 4 to log param ptr.
296*e4f5a11dSJames Kremer 			 */
2975cf8276bSTodd McKenney 			log_str_ptr = log_param_ptr + 4;
298*e4f5a11dSJames Kremer 
299*e4f5a11dSJames Kremer 			/*
300*e4f5a11dSJames Kremer 			 * Check to see if this is the
301*e4f5a11dSJames Kremer 			 * same line
302*e4f5a11dSJames Kremer 			 */
3035cf8276bSTodd McKenney 			if (strncmp((char *)log_str_ptr, data->last_log_entry,
3045cf8276bSTodd McKenney 			    SES_LOG_VALID_LOG_SIZE) == 0) {
3055cf8276bSTodd McKenney 				/* Found an exact match */
3065cf8276bSTodd McKenney 				log_param_ptr += param_len;
3075cf8276bSTodd McKenney 				k += param_len;
308*e4f5a11dSJames Kremer 				match_found = 1;
309*e4f5a11dSJames Kremer 				break;
310*e4f5a11dSJames Kremer 			}
3115cf8276bSTodd McKenney 			log_param_ptr += param_len;
312*e4f5a11dSJames Kremer 		}
313*e4f5a11dSJames Kremer 	}
314*e4f5a11dSJames Kremer 	if (!match_found) {
3155cf8276bSTodd McKenney 		log_param_ptr = &resp[0] + 4;
3165cf8276bSTodd McKenney 		k = 0;
3175cf8276bSTodd McKenney 	}
3185cf8276bSTodd McKenney 	if (k == all_log_data_len) {
3195cf8276bSTodd McKenney 		/*
3205cf8276bSTodd McKenney 		 * Either there was no log data or we have
3215cf8276bSTodd McKenney 		 * already read these log entries.
3225cf8276bSTodd McKenney 		 * Just return.
3235cf8276bSTodd McKenney 		 */
3245cf8276bSTodd McKenney 		return (0);
3255cf8276bSTodd McKenney 	}
3265cf8276bSTodd McKenney 
3275cf8276bSTodd McKenney 	/* Grab memory to return logs with */
3285cf8276bSTodd McKenney 	if (nvlist_alloc(&data->log_data, NV_UNIQUE_NAME, 0) != 0) {
3295cf8276bSTodd McKenney 		/* Couldn't alloc memory for nvlist */
3305cf8276bSTodd McKenney 		return (SES_LOG_FAILED_NVLIST_CREATE);
331*e4f5a11dSJames Kremer 	}
332*e4f5a11dSJames Kremer 
333*e4f5a11dSJames Kremer 	(void) memset(log_code,		0, sizeof (log_code));
334*e4f5a11dSJames Kremer 	(void) memset(save_buffer,	0, sizeof (save_buffer));
335*e4f5a11dSJames Kremer 	(void) memset(log_level,	0, sizeof (log_level));
336*e4f5a11dSJames Kremer 
337*e4f5a11dSJames Kremer 	/*
3385cf8276bSTodd McKenney 	 * Start saving new log entries
3395cf8276bSTodd McKenney 	 * Walk the log data adding any new entries
340*e4f5a11dSJames Kremer 	 */
3415cf8276bSTodd McKenney 
3425cf8276bSTodd McKenney 	for (; k < all_log_data_len; k += param_len) {
3435cf8276bSTodd McKenney 		/*
3445cf8276bSTodd McKenney 		 * Calculate log entry length
3455cf8276bSTodd McKenney 		 * Log ptr [3] contains the log length minus the header info
3465cf8276bSTodd McKenney 		 * which is 4 bytes so add that in
3475cf8276bSTodd McKenney 		 */
3485cf8276bSTodd McKenney 		param_len = log_param_ptr[3] + 4;
3495cf8276bSTodd McKenney 
3505cf8276bSTodd McKenney 		if (param_len <= 4) {
3515cf8276bSTodd McKenney 			/* Only header information in this entry */
3525cf8276bSTodd McKenney 			/* process next log entry */
3535cf8276bSTodd McKenney 			log_param_ptr += param_len;
3545cf8276bSTodd McKenney 			continue;
3555cf8276bSTodd McKenney 		}
3565cf8276bSTodd McKenney 
3575cf8276bSTodd McKenney 		/*
3585cf8276bSTodd McKenney 		 * initialize log_str_ptr to point to string info of the log
3595cf8276bSTodd McKenney 		 * entry. First 4 bytes of log entry contains param code,
3605cf8276bSTodd McKenney 		 * control byte, and length. Log string starts after that.
3615cf8276bSTodd McKenney 		 */
3625cf8276bSTodd McKenney 		log_str_ptr = log_param_ptr + 4;
363*e4f5a11dSJames Kremer 
364*e4f5a11dSJames Kremer 		/*
365*e4f5a11dSJames Kremer 		 * Format of log str is as follows
366*e4f5a11dSJames Kremer 		 * "%8x %8x %8x %8x %8x %8x %8x %8x",
367*e4f5a11dSJames Kremer 		 * log_entry.log_word0, log_entry.ts_u, log_entry.ts_l,
368*e4f5a11dSJames Kremer 		 * log_entry.seq_num, log_entry.log_code, log_entry.log_word2,
369*e4f5a11dSJames Kremer 		 * log_entry.log_word3, log_entry.log_word4
370*e4f5a11dSJames Kremer 		 * following example has extra spaces removed to fit in 80 char
371*e4f5a11dSJames Kremer 		 * 40004 0 42d5f5fe 185b 630002 fd0800 50800207 e482813
372*e4f5a11dSJames Kremer 		 */
373*e4f5a11dSJames Kremer 
374*e4f5a11dSJames Kremer 		(void) strncpy(save_buffer,
375*e4f5a11dSJames Kremer 		    (const char *)log_str_ptr,
376*e4f5a11dSJames Kremer 		    SES_LOG_VALID_LOG_SIZE);
377*e4f5a11dSJames Kremer 
3785cf8276bSTodd McKenney 		(void) strncpy(log_code,
3795cf8276bSTodd McKenney 		    (const char *)log_str_ptr+SES_LOG_CODE_START,
3805cf8276bSTodd McKenney 		    SES_LOG_SPECIFIC_ENTRY_SIZE);
381*e4f5a11dSJames Kremer 
382*e4f5a11dSJames Kremer 		(void) strncpy(log_level,
383*e4f5a11dSJames Kremer 		    (const char *) log_str_ptr +
384*e4f5a11dSJames Kremer 		    SES_LOG_LEVEL_START, 1);
385*e4f5a11dSJames Kremer 
386*e4f5a11dSJames Kremer 
387*e4f5a11dSJames Kremer 		/* Add this entry to the nvlist log data */
3885cf8276bSTodd McKenney 		if (nvlist_alloc(&entry, NV_UNIQUE_NAME, 0) != 0) {
3895cf8276bSTodd McKenney 			/* Couldn't alloc space, return error */
390*e4f5a11dSJames Kremer 			return (SES_LOG_FAILED_NV_UNIQUE);
391*e4f5a11dSJames Kremer 		}
392*e4f5a11dSJames Kremer 
393*e4f5a11dSJames Kremer 
3945cf8276bSTodd McKenney 		if (nvlist_add_string(entry, ENTRY_LOG, save_buffer) != 0) {
3955cf8276bSTodd McKenney 			/* Error adding string, return error */
396*e4f5a11dSJames Kremer 			nvlist_free(entry);
397*e4f5a11dSJames Kremer 			return (SES_LOG_FAILED_NV_LOG);
398*e4f5a11dSJames Kremer 		}
399*e4f5a11dSJames Kremer 
4005cf8276bSTodd McKenney 		if (nvlist_add_string(entry, ENTRY_CODE, log_code) != 0) {
4015cf8276bSTodd McKenney 			/* Error adding string, return error */
402*e4f5a11dSJames Kremer 			nvlist_free(entry);
403*e4f5a11dSJames Kremer 			return (SES_LOG_FAILED_NV_CODE);
404*e4f5a11dSJames Kremer 		}
4055cf8276bSTodd McKenney 		if (nvlist_add_string(entry, ENTRY_SEVERITY, log_level) != 0) {
4065cf8276bSTodd McKenney 			/* Error adding srtring, return error */
407*e4f5a11dSJames Kremer 			nvlist_free(entry);
408*e4f5a11dSJames Kremer 			return (SES_LOG_FAILED_NV_SEV);
409*e4f5a11dSJames Kremer 		}
410*e4f5a11dSJames Kremer 
4115cf8276bSTodd McKenney 		param_code = SCSI_READ16(&log_param_ptr[0]);
412*e4f5a11dSJames Kremer 
4135cf8276bSTodd McKenney 		(void) snprintf(entry_num, sizeof (entry_num),
4145cf8276bSTodd McKenney 		    "%s%d", ENTRY_PREFIX, param_code);
4155cf8276bSTodd McKenney 
4165cf8276bSTodd McKenney 		if (nvlist_add_nvlist(data->log_data, entry_num, entry) != 0) {
4175cf8276bSTodd McKenney 			/* Error adding nvlist, return error */
418*e4f5a11dSJames Kremer 			nvlist_free(entry);
419*e4f5a11dSJames Kremer 			return (SES_LOG_FAILED_NV_ENTRY);
420*e4f5a11dSJames Kremer 		}
421*e4f5a11dSJames Kremer 		nvlist_free(entry);
422*e4f5a11dSJames Kremer 
423*e4f5a11dSJames Kremer 		entry_added = 1;
4245cf8276bSTodd McKenney 		(data->number_log_entries)++;
425*e4f5a11dSJames Kremer 
4265cf8276bSTodd McKenney 		log_param_ptr += param_len;
427*e4f5a11dSJames Kremer 
428*e4f5a11dSJames Kremer 	}
429*e4f5a11dSJames Kremer 	if (entry_added) {
430*e4f5a11dSJames Kremer 		/* Update the last log entry string with last one read */
4315cf8276bSTodd McKenney 		(void) strncpy(data->last_log_entry, save_buffer, MAXNAMELEN);
432*e4f5a11dSJames Kremer 	}
433*e4f5a11dSJames Kremer 	return (0);
434*e4f5a11dSJames Kremer }
435*e4f5a11dSJames Kremer 
436*e4f5a11dSJames Kremer 
437*e4f5a11dSJames Kremer 
438*e4f5a11dSJames Kremer /* Setup struct to send command to device */
439*e4f5a11dSJames Kremer static void
set_scsi_pt_data_out(struct uscsi_cmd * uscsi,const unsigned char * dxferp,int dxfer_len)440*e4f5a11dSJames Kremer set_scsi_pt_data_out(struct uscsi_cmd *uscsi, const unsigned char *dxferp,
441*e4f5a11dSJames Kremer     int dxfer_len)
442*e4f5a11dSJames Kremer {
443*e4f5a11dSJames Kremer 	if (dxfer_len > 0) {
444*e4f5a11dSJames Kremer 		uscsi->uscsi_bufaddr = (char *)dxferp;
445*e4f5a11dSJames Kremer 		uscsi->uscsi_buflen = dxfer_len;
446*e4f5a11dSJames Kremer 		uscsi->uscsi_flags = USCSI_WRITE | USCSI_ISOLATE |
447*e4f5a11dSJames Kremer 		    USCSI_RQENABLE;
448*e4f5a11dSJames Kremer 	}
449*e4f5a11dSJames Kremer }
450*e4f5a11dSJames Kremer 
451*e4f5a11dSJames Kremer /*
452*e4f5a11dSJames Kremer  * Invokes a SCSI MODE SENSE(10) command.
453*e4f5a11dSJames Kremer  * Return:
454*e4f5a11dSJames Kremer  * 0 for success
455*e4f5a11dSJames Kremer  * SG_LIB_CAT_INVALID_OP -> invalid opcode
456*e4f5a11dSJames Kremer  * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb
457*e4f5a11dSJames Kremer  * SG_LIB_CAT_NOT_READY -> device not ready
458*e4f5a11dSJames Kremer  * -1 -> other failure
459*e4f5a11dSJames Kremer  */
460*e4f5a11dSJames Kremer 
461*e4f5a11dSJames Kremer static int
sg_ll_mode_sense10(int sg_fd,void * resp,int mx_resp_len)462*e4f5a11dSJames Kremer sg_ll_mode_sense10(int sg_fd, void * resp, int mx_resp_len)
463*e4f5a11dSJames Kremer {
464*e4f5a11dSJames Kremer 	int res, ret;
465*e4f5a11dSJames Kremer 	unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] =
466*e4f5a11dSJames Kremer 	    {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
467*e4f5a11dSJames Kremer 	unsigned char sense_b[SENSE_BUFF_LEN];
468*e4f5a11dSJames Kremer 	struct uscsi_cmd uscsi;
469*e4f5a11dSJames Kremer 
470*e4f5a11dSJames Kremer 	modesCmdBlk[1] = 0;
471*e4f5a11dSJames Kremer 	modesCmdBlk[2] = 0; /* page code 0 vendor specific */
472*e4f5a11dSJames Kremer 	modesCmdBlk[3] = 0;
473*e4f5a11dSJames Kremer 	modesCmdBlk[7] = (unsigned char) ((mx_resp_len >> 8) & 0xff);
474*e4f5a11dSJames Kremer 	modesCmdBlk[8] = (unsigned char) (mx_resp_len & 0xff);
475*e4f5a11dSJames Kremer 
476*e4f5a11dSJames Kremer 	construct_scsi_pt_obj(&uscsi);
477*e4f5a11dSJames Kremer 	set_scsi_pt_cdb(&uscsi, modesCmdBlk, sizeof (modesCmdBlk));
478*e4f5a11dSJames Kremer 	set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b));
479*e4f5a11dSJames Kremer 	set_scsi_pt_data_in(&uscsi, (unsigned char *) resp, mx_resp_len);
480*e4f5a11dSJames Kremer 	res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT);
481*e4f5a11dSJames Kremer 	if (res) {
482*e4f5a11dSJames Kremer 		ret = res;
483*e4f5a11dSJames Kremer 	} else {
484*e4f5a11dSJames Kremer 		ret = uscsi.uscsi_status;
485*e4f5a11dSJames Kremer 	}
486*e4f5a11dSJames Kremer 	return (ret);
487*e4f5a11dSJames Kremer }
488*e4f5a11dSJames Kremer 
489*e4f5a11dSJames Kremer /*
490*e4f5a11dSJames Kremer  * Invokes a SCSI MODE SELECT(10) command.
491*e4f5a11dSJames Kremer  * Return:
492*e4f5a11dSJames Kremer  * 0 for success.
493*e4f5a11dSJames Kremer  * SG_LIB_CAT_INVALID_OP for invalid opcode
494*e4f5a11dSJames Kremer  * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
495*e4f5a11dSJames Kremer  * SG_LIB_CAT_NOT_READY -> device not ready,
496*e4f5a11dSJames Kremer  * -1 -> other failure
497*e4f5a11dSJames Kremer  */
498*e4f5a11dSJames Kremer static int
sg_ll_mode_select10(int sg_fd,void * paramp,int param_len)499*e4f5a11dSJames Kremer sg_ll_mode_select10(int sg_fd, void * paramp, int param_len)
500*e4f5a11dSJames Kremer {
501*e4f5a11dSJames Kremer 	int res, ret;
502*e4f5a11dSJames Kremer 	unsigned char modesCmdBlk[MODE_SELECT10_CMDLEN] =
503*e4f5a11dSJames Kremer 	    {SCMD_MODE_SELECT_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
504*e4f5a11dSJames Kremer 	unsigned char sense_b[SENSE_BUFF_LEN];
505*e4f5a11dSJames Kremer 	struct uscsi_cmd uscsi;
506*e4f5a11dSJames Kremer 
507*e4f5a11dSJames Kremer 
508*e4f5a11dSJames Kremer 	modesCmdBlk[1] = 0;
509*e4f5a11dSJames Kremer 	/*
510*e4f5a11dSJames Kremer 	 * modesCmdBlk 2 equal 0 PC 0 return current page code 0 return
511*e4f5a11dSJames Kremer 	 * vendor specific
512*e4f5a11dSJames Kremer 	 */
513*e4f5a11dSJames Kremer 
514*e4f5a11dSJames Kremer 	modesCmdBlk[7] = (unsigned char)((param_len >> 8) & 0xff);
515*e4f5a11dSJames Kremer 	modesCmdBlk[8] = (unsigned char)(param_len & 0xff);
516*e4f5a11dSJames Kremer 
517*e4f5a11dSJames Kremer 	construct_scsi_pt_obj(&uscsi);
518*e4f5a11dSJames Kremer 
519*e4f5a11dSJames Kremer 	set_scsi_pt_cdb(&uscsi, modesCmdBlk, sizeof (modesCmdBlk));
520*e4f5a11dSJames Kremer 	set_scsi_pt_sense(&uscsi, sense_b, sizeof (sense_b));
521*e4f5a11dSJames Kremer 	set_scsi_pt_data_out(&uscsi, (unsigned char *) paramp, param_len);
522*e4f5a11dSJames Kremer 	res = do_scsi_pt(&uscsi, sg_fd, DEF_PT_TIMEOUT);
523*e4f5a11dSJames Kremer 	if (res) {
524*e4f5a11dSJames Kremer 		ret = res;
525*e4f5a11dSJames Kremer 	} else {
526*e4f5a11dSJames Kremer 		ret = uscsi.uscsi_status;
527*e4f5a11dSJames Kremer 	}
528*e4f5a11dSJames Kremer 	return (ret);
529*e4f5a11dSJames Kremer }
530*e4f5a11dSJames Kremer 
531*e4f5a11dSJames Kremer 
532*e4f5a11dSJames Kremer 
533*e4f5a11dSJames Kremer /*
534*e4f5a11dSJames Kremer  * MODE SENSE 10 commands yield a response that has block descriptors followed
535*e4f5a11dSJames Kremer  * by mode pages. In most cases users are interested in the first mode page.
536*e4f5a11dSJames Kremer  * This function returns the(byte) offset of the start of the first mode page.
537*e4f5a11dSJames Kremer  * Returns >= 0 is successful or -1 if failure. If there is a failure
538*e4f5a11dSJames Kremer  * a message is written to err_buff.
539*e4f5a11dSJames Kremer  */
540*e4f5a11dSJames Kremer 
541*e4f5a11dSJames Kremer /*
542*e4f5a11dSJames Kremer  * return data looks like:
543*e4f5a11dSJames Kremer  * Table 92 - Mode parameter header(10)
544*e4f5a11dSJames Kremer  * Bit
545*e4f5a11dSJames Kremer  * Byte
546*e4f5a11dSJames Kremer  *	7 	6 	5 	4 	3 	2 	1 	0
547*e4f5a11dSJames Kremer  *	----------------------------------------------------------
548*e4f5a11dSJames Kremer  * 0	MSB Data length
549*e4f5a11dSJames Kremer  * 1	LSB Data length
550*e4f5a11dSJames Kremer  *	----------------------------------------------------------
551*e4f5a11dSJames Kremer  * 2	Medium type
552*e4f5a11dSJames Kremer  *	----------------------------------------------------------
553*e4f5a11dSJames Kremer  * 3 	Device-specific parameter
554*e4f5a11dSJames Kremer  *	----------------------------------------------------------
555*e4f5a11dSJames Kremer  * 4 	Reserved
556*e4f5a11dSJames Kremer  *	----------------------------------------------------------
557*e4f5a11dSJames Kremer  * 5	Reserved
558*e4f5a11dSJames Kremer  *	----------------------------------------------------------
559*e4f5a11dSJames Kremer  * 6	MSB block descriptor length
560*e4f5a11dSJames Kremer  * 7	LSB block descriptor length
561*e4f5a11dSJames Kremer  *	----------------------------------------------------------
562*e4f5a11dSJames Kremer  *	block desciptors....
563*e4f5a11dSJames Kremer  *	-----------------------
564*e4f5a11dSJames Kremer  *	mode sense page:
565*e4f5a11dSJames Kremer  *	0 : ps Reserved : page Code
566*e4f5a11dSJames Kremer  *	1 : Page Length(n-1)
567*e4f5a11dSJames Kremer  *	2-N  Mode parameters
568*e4f5a11dSJames Kremer  */
569*e4f5a11dSJames Kremer static int
sg_mode_page_offset(const unsigned char * resp,int resp_len)5705cf8276bSTodd McKenney sg_mode_page_offset(const unsigned char *resp, int resp_len)
571*e4f5a11dSJames Kremer {
572*e4f5a11dSJames Kremer 	int bd_len;
573*e4f5a11dSJames Kremer 	int calc_len;
574*e4f5a11dSJames Kremer 	int offset;
575*e4f5a11dSJames Kremer 
576*e4f5a11dSJames Kremer 	if ((NULL == resp) || (resp_len < 8)) {
577*e4f5a11dSJames Kremer 		/* Too short of a response buffer */
578*e4f5a11dSJames Kremer 		return (-1);
579*e4f5a11dSJames Kremer 	}
580*e4f5a11dSJames Kremer 
581*e4f5a11dSJames Kremer 	calc_len = (resp[0] << 8) + resp[1] + 2;
582*e4f5a11dSJames Kremer 	bd_len = (resp[6] << 8) + resp[7];
583*e4f5a11dSJames Kremer 
584*e4f5a11dSJames Kremer 	/* LongLBA doesn't change this calculation */
585*e4f5a11dSJames Kremer 	offset = bd_len + MODE10_RESP_HDR_LEN;
586*e4f5a11dSJames Kremer 
587*e4f5a11dSJames Kremer 	if ((offset + 2) > resp_len) {
5885cf8276bSTodd McKenney 		/* Given response length to small */
589*e4f5a11dSJames Kremer 		offset = -1;
590*e4f5a11dSJames Kremer 	} else if ((offset + 2) > calc_len) {
5915cf8276bSTodd McKenney 		/* Calculated response length too small */
592*e4f5a11dSJames Kremer 		offset = -1;
593*e4f5a11dSJames Kremer 	}
594*e4f5a11dSJames Kremer 	return (offset);
595*e4f5a11dSJames Kremer }
596*e4f5a11dSJames Kremer 
597*e4f5a11dSJames Kremer /*
598*e4f5a11dSJames Kremer  * Clear logs
599*e4f5a11dSJames Kremer  */
600*e4f5a11dSJames Kremer static int
clear_log(int sg_fd,ses_log_call_t * data)6015cf8276bSTodd McKenney clear_log(int sg_fd, ses_log_call_t *data)
602*e4f5a11dSJames Kremer {
603*e4f5a11dSJames Kremer 
604*e4f5a11dSJames Kremer 	int res, alloc_len, off;
605*e4f5a11dSJames Kremer 	int md_len;
606*e4f5a11dSJames Kremer 	int read_in_len = 0;
6075cf8276bSTodd McKenney 	unsigned char ref_md[MAX_ALLOC_LEN];
608*e4f5a11dSJames Kremer 	struct log_clear_control_struct clear_data;
609*e4f5a11dSJames Kremer 	long myhostid;
610*e4f5a11dSJames Kremer 	int error = 0;
6115cf8276bSTodd McKenney 	long poll_time;
6125cf8276bSTodd McKenney 	char seq_num_str[10];
6135cf8276bSTodd McKenney 	unsigned long seq_num = 0;
614*e4f5a11dSJames Kremer 
615*e4f5a11dSJames Kremer 	(void) memset(&clear_data, 0, sizeof (clear_data));
616*e4f5a11dSJames Kremer 
617*e4f5a11dSJames Kremer 	clear_data.pageControls = 0x40;
618*e4f5a11dSJames Kremer 	clear_data.subpage_code = 0;
619*e4f5a11dSJames Kremer 	clear_data.page_lengthLower = 0x16;
620*e4f5a11dSJames Kremer 
621*e4f5a11dSJames Kremer 	myhostid = gethostid();
622*e4f5a11dSJames Kremer 	/* 0 -> 11 are memset to 0 */
623*e4f5a11dSJames Kremer 	clear_data.host_id[12] = (myhostid & 0xff000000) >> 24;
624*e4f5a11dSJames Kremer 	clear_data.host_id[13] = (myhostid & 0xff0000) >> 16;
625*e4f5a11dSJames Kremer 	clear_data.host_id[14] = (myhostid & 0xff00) >> 8;
626*e4f5a11dSJames Kremer 	clear_data.host_id[15] = myhostid & 0xff;
627*e4f5a11dSJames Kremer 
6285cf8276bSTodd McKenney 	/*
6295cf8276bSTodd McKenney 	 * convert nanosecond time to seconds
6305cf8276bSTodd McKenney 	 */
6315cf8276bSTodd McKenney 	poll_time = data->poll_time / 1000000000;
6325cf8276bSTodd McKenney 	/* Add 5 minutes to poll time to allow for data retrieval time */
633*e4f5a11dSJames Kremer 	poll_time = poll_time + 300;
634*e4f5a11dSJames Kremer 	clear_data.timeout[0] = (poll_time & 0xff00) >> 8;
635*e4f5a11dSJames Kremer 	clear_data.timeout[1] = poll_time & 0xff;
636*e4f5a11dSJames Kremer 
6375cf8276bSTodd McKenney 	/*
6385cf8276bSTodd McKenney 	 * retrieve the last read sequence number from the last
6395cf8276bSTodd McKenney 	 * log entry read.
6405cf8276bSTodd McKenney 	 */
6415cf8276bSTodd McKenney 	if (data->last_log_entry != NULL &&
6425cf8276bSTodd McKenney 	    (strlen(data->last_log_entry) == SES_LOG_VALID_LOG_SIZE)) {
6435cf8276bSTodd McKenney 		/*
6445cf8276bSTodd McKenney 		 * We have a valid log entry from a previous read log
6455cf8276bSTodd McKenney 		 * operation.
6465cf8276bSTodd McKenney 		 */
6475cf8276bSTodd McKenney 		(void) strncpy(seq_num_str,
6485cf8276bSTodd McKenney 		    (const char *) data->last_log_entry +
6495cf8276bSTodd McKenney 		    SES_LOG_SEQ_NUM_START, 8);
6505cf8276bSTodd McKenney 		seq_num = strtoul(seq_num_str, 0, 16);
6515cf8276bSTodd McKenney 	}
652*e4f5a11dSJames Kremer 	clear_data.seq_clear[0] = (seq_num & 0xff000000) >> 24;
653*e4f5a11dSJames Kremer 	clear_data.seq_clear[1] = (seq_num & 0xff0000) >> 16;
654*e4f5a11dSJames Kremer 	clear_data.seq_clear[2] = (seq_num & 0xff00) >> 8;
655*e4f5a11dSJames Kremer 	clear_data.seq_clear[3] = (seq_num & 0xff);
656*e4f5a11dSJames Kremer 
657*e4f5a11dSJames Kremer 	read_in_len = sizeof (clear_data);
658*e4f5a11dSJames Kremer 
659*e4f5a11dSJames Kremer 
660*e4f5a11dSJames Kremer 	/* do MODE SENSE to fetch current values */
6615cf8276bSTodd McKenney 	(void) memset(ref_md, 0, MAX_ALLOC_LEN);
6625cf8276bSTodd McKenney 	alloc_len = MAX_ALLOC_LEN;
663*e4f5a11dSJames Kremer 
664*e4f5a11dSJames Kremer 
665*e4f5a11dSJames Kremer 	res = sg_ll_mode_sense10(sg_fd, ref_md, alloc_len);
666*e4f5a11dSJames Kremer 	if (0 != res) {
667*e4f5a11dSJames Kremer 		/* Error during mode sense */
668*e4f5a11dSJames Kremer 		error = SES_LOG_FAILED_MODE_SENSE;
669*e4f5a11dSJames Kremer 		return (error);
670*e4f5a11dSJames Kremer 	}
671*e4f5a11dSJames Kremer 
672*e4f5a11dSJames Kremer 	/* Setup mode Select to clear logs */
6735cf8276bSTodd McKenney 	off = sg_mode_page_offset(ref_md, alloc_len);
674*e4f5a11dSJames Kremer 	if (off < 0) {
675*e4f5a11dSJames Kremer 		/* Mode page offset error */
676*e4f5a11dSJames Kremer 		error =  SES_LOG_FAILED_MODE_SENSE_OFFSET;
677*e4f5a11dSJames Kremer 		return (error);
678*e4f5a11dSJames Kremer 	}
679*e4f5a11dSJames Kremer 	md_len = (ref_md[0] << 8) + ref_md[1] + 2;
680*e4f5a11dSJames Kremer 
681*e4f5a11dSJames Kremer 	ref_md[0] = 0;
682*e4f5a11dSJames Kremer 	ref_md[1] = 0;
683*e4f5a11dSJames Kremer 
684*e4f5a11dSJames Kremer 	if (md_len > alloc_len) {
685*e4f5a11dSJames Kremer 		/* Data length to large */
686*e4f5a11dSJames Kremer 		error = SES_LOG_FAILED_BAD_DATA_LEN;
687*e4f5a11dSJames Kremer 		return (error);
688*e4f5a11dSJames Kremer 	}
689*e4f5a11dSJames Kremer 
690*e4f5a11dSJames Kremer 	if ((md_len - off) != read_in_len) {
691*e4f5a11dSJames Kremer 		/* Content length not correct */
692*e4f5a11dSJames Kremer 		error = SES_LOG_FAILED_BAD_CONTENT_LEN;
693*e4f5a11dSJames Kremer 		return (error);
694*e4f5a11dSJames Kremer 	}
695*e4f5a11dSJames Kremer 
696*e4f5a11dSJames Kremer 	if ((clear_data.pageControls & 0x40) != (ref_md[off] & 0x40)) {
697*e4f5a11dSJames Kremer 		/* reference model doesn't have use subpage format bit set */
698*e4f5a11dSJames Kremer 		/* Even though it should have */
699*e4f5a11dSJames Kremer 		/* don't send the command */
7005cf8276bSTodd McKenney 		error = SES_LOG_FAILED_FORMAT_PAGE_ERR;
701*e4f5a11dSJames Kremer 		return (error);
702*e4f5a11dSJames Kremer 	}
703*e4f5a11dSJames Kremer 
704*e4f5a11dSJames Kremer 	(void) memcpy(ref_md + off, (const void *) &clear_data,
705*e4f5a11dSJames Kremer 	    sizeof (clear_data));
706*e4f5a11dSJames Kremer 
707*e4f5a11dSJames Kremer 	res = sg_ll_mode_select10(sg_fd, ref_md, md_len);
708*e4f5a11dSJames Kremer 	if (res != 0) {
709*e4f5a11dSJames Kremer 		error = SES_LOG_FAILED_MODE_SELECT;
710*e4f5a11dSJames Kremer 		return (error);
711*e4f5a11dSJames Kremer 	}
712*e4f5a11dSJames Kremer 
713*e4f5a11dSJames Kremer 	return (error);
714*e4f5a11dSJames Kremer }
715*e4f5a11dSJames Kremer /*
716*e4f5a11dSJames Kremer  * Gather data from given device.
717*e4f5a11dSJames Kremer  */
718*e4f5a11dSJames Kremer static int
gather_data(char * device_name,ses_log_call_t * data)7195cf8276bSTodd McKenney gather_data(char *device_name, ses_log_call_t *data)
720*e4f5a11dSJames Kremer {
721*e4f5a11dSJames Kremer 	int sg_fd;
7225cf8276bSTodd McKenney 	int resp_len, res;
7235cf8276bSTodd McKenney 	unsigned char rsp_buff[MAX_ALLOC_LEN];
724*e4f5a11dSJames Kremer 	int error;
725*e4f5a11dSJames Kremer 
726*e4f5a11dSJames Kremer 	/* Open device */
727*e4f5a11dSJames Kremer 	if ((sg_fd = open_device(device_name)) < 0) {
728*e4f5a11dSJames Kremer 		/* Failed to open device */
729*e4f5a11dSJames Kremer 		return (SES_LOG_FAILED_TO_OPEN_DEVICE);
730*e4f5a11dSJames Kremer 	}
731*e4f5a11dSJames Kremer 
732*e4f5a11dSJames Kremer 	/* Read the logs */
733*e4f5a11dSJames Kremer 	(void) memset(rsp_buff, 0, sizeof (rsp_buff));
734*e4f5a11dSJames Kremer 	resp_len = 0x8000; /* Maximum size available to read */
735*e4f5a11dSJames Kremer 	res = read_log(sg_fd, rsp_buff, resp_len);
736*e4f5a11dSJames Kremer 
7375cf8276bSTodd McKenney 	if (res != 0) {
738*e4f5a11dSJames Kremer 		/* Some sort of Error during read of logs */
7395cf8276bSTodd McKenney 		(void) close(sg_fd);
740*e4f5a11dSJames Kremer 		return (SES_LOG_FAILED_TO_READ_DEVICE);
741*e4f5a11dSJames Kremer 	}
742*e4f5a11dSJames Kremer 
743*e4f5a11dSJames Kremer 	/* Save the logs */
7445cf8276bSTodd McKenney 	error = save_logs(rsp_buff, data);
745*e4f5a11dSJames Kremer 	if (error != 0) {
7465cf8276bSTodd McKenney 		(void) close(sg_fd);
747*e4f5a11dSJames Kremer 		return (error);
748*e4f5a11dSJames Kremer 	}
7495cf8276bSTodd McKenney 	/* Clear the logs */
7505cf8276bSTodd McKenney 	error = clear_log(sg_fd, data);
751*e4f5a11dSJames Kremer 
752*e4f5a11dSJames Kremer 	(void) close(sg_fd);
753*e4f5a11dSJames Kremer 
754*e4f5a11dSJames Kremer 	return (error);
755*e4f5a11dSJames Kremer }
756*e4f5a11dSJames Kremer 
757*e4f5a11dSJames Kremer /*
758*e4f5a11dSJames Kremer  * Access the SES target identified by the indicated path.  Read the logs
759*e4f5a11dSJames Kremer  * and return them in a nvlist.
760*e4f5a11dSJames Kremer  */
761*e4f5a11dSJames Kremer int
access_ses_log(ses_log_call_t * data)7625cf8276bSTodd McKenney access_ses_log(ses_log_call_t *data)
763*e4f5a11dSJames Kremer {
764*e4f5a11dSJames Kremer 	char real_path[MAXPATHLEN];
765*e4f5a11dSJames Kremer 	struct stat buffer;
766*e4f5a11dSJames Kremer 	int error;
767*e4f5a11dSJames Kremer 
7685cf8276bSTodd McKenney 	/* Initialize return data */
7695cf8276bSTodd McKenney 	data->log_data = NULL;
7705cf8276bSTodd McKenney 	data->number_log_entries = 0;
7715cf8276bSTodd McKenney 
772*e4f5a11dSJames Kremer 	if (data->target_path == NULL) {
773*e4f5a11dSJames Kremer 		/* NULL Target path, return error */
774*e4f5a11dSJames Kremer 		return (SES_LOG_FAILED_NULL_TARGET_PATH);
775*e4f5a11dSJames Kremer 	}
776*e4f5a11dSJames Kremer 
777*e4f5a11dSJames Kremer 	/* Try to find a valid path */
778*e4f5a11dSJames Kremer 	(void) snprintf(real_path, sizeof (real_path), "/devices%s:ses",
779*e4f5a11dSJames Kremer 	    data->target_path);
780*e4f5a11dSJames Kremer 
781*e4f5a11dSJames Kremer 	if (stat(real_path, &buffer) != 0) {
782*e4f5a11dSJames Kremer 
783*e4f5a11dSJames Kremer 		(void) snprintf(real_path, sizeof (real_path), "/devices%s:0",
784*e4f5a11dSJames Kremer 		    data->target_path);
785*e4f5a11dSJames Kremer 		if (stat(real_path, &buffer) != 0) {
786*e4f5a11dSJames Kremer 			/* Couldn't find a path that exists */
787*e4f5a11dSJames Kremer 			return (SES_LOG_FAILED_BAD_TARGET_PATH);
788*e4f5a11dSJames Kremer 		}
789*e4f5a11dSJames Kremer 	}
790*e4f5a11dSJames Kremer 
7915cf8276bSTodd McKenney 	error = gather_data(real_path, data);
792*e4f5a11dSJames Kremer 
793*e4f5a11dSJames Kremer 	/* Update the size of log entries being returned */
794*e4f5a11dSJames Kremer 	data->size_of_log_entries =
795*e4f5a11dSJames Kremer 	    data->number_log_entries * SES_LOG_VALID_LOG_SIZE;
796*e4f5a11dSJames Kremer 
797*e4f5a11dSJames Kremer 	return (error);
798*e4f5a11dSJames Kremer }
799