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