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