1*8226594fSRick McNeal /*
2*8226594fSRick McNeal  * This file and its contents are supplied under the terms of the
3*8226594fSRick McNeal  * Common Development and Distribution License ("CDDL"), version 1.0.
4*8226594fSRick McNeal  * You may only use this file in accordance with the terms of version
5*8226594fSRick McNeal  * 1.0 of the CDDL.
6*8226594fSRick McNeal  *
7*8226594fSRick McNeal  * A full copy of the text of the CDDL should have accompanied this
8*8226594fSRick McNeal  * source.  A copy of the CDDL is also available via the Internet at
9*8226594fSRick McNeal  * http://www.illumos.org/license/CDDL.
10*8226594fSRick McNeal  */
11*8226594fSRick McNeal 
12*8226594fSRick McNeal /*
13*8226594fSRick McNeal  * Copyright 2023 Nexenta by DDN, Inc. All rights reserved.
14*8226594fSRick McNeal  */
15*8226594fSRick McNeal 
16*8226594fSRick McNeal #include <sys/kmem.h>
17*8226594fSRick McNeal #include <sys/proc.h>
18*8226594fSRick McNeal #include <sys/time.h>
19*8226594fSRick McNeal #include <sys/conf.h>
20*8226594fSRick McNeal #include <sys/file.h>
21*8226594fSRick McNeal #include <sys/ddi.h>
22*8226594fSRick McNeal #include <sys/ddi_impldefs.h>
23*8226594fSRick McNeal #include <sys/modctl.h>
24*8226594fSRick McNeal #include <sys/sunddi.h>
25*8226594fSRick McNeal #include <sys/scsi/scsi.h>
26*8226594fSRick McNeal #include <sys/scsi/impl/scsi_reset_notify.h>
27*8226594fSRick McNeal #include <sys/sunmdi.h>
28*8226594fSRick McNeal #include <sys/mdi_impldefs.h>
29*8226594fSRick McNeal 
30*8226594fSRick McNeal #include <smartpqi.h>
31*8226594fSRick McNeal #include <smartpqi_hw.h>
32*8226594fSRick McNeal 
33*8226594fSRick McNeal #include <sys/scsi/scsi_types.h>
34*8226594fSRick McNeal #include <sys/disp.h>
35*8226594fSRick McNeal #include <sys/types.h>
36*8226594fSRick McNeal #include <sys/mdb_modapi.h>
37*8226594fSRick McNeal 
38*8226594fSRick McNeal #define	INVALID_OPT_VAL ((uintptr_t)(-1))
39*8226594fSRick McNeal 
40*8226594fSRick McNeal /* ---- Forward references ---- */
41*8226594fSRick McNeal static int smartpqi(uintptr_t, uint_t, int, const mdb_arg_t *);
42*8226594fSRick McNeal static void smartpqi_help(void);
43*8226594fSRick McNeal 
44*8226594fSRick McNeal static const mdb_dcmd_t dcmds[] = {
45*8226594fSRick McNeal 	{
46*8226594fSRick McNeal 		"smartpqi", "-c <controller number> [-v]",
47*8226594fSRick McNeal 		"display smartpqi state",
48*8226594fSRick McNeal 		smartpqi,
49*8226594fSRick McNeal 		smartpqi_help
50*8226594fSRick McNeal 	},
51*8226594fSRick McNeal 	{ NULL }
52*8226594fSRick McNeal };
53*8226594fSRick McNeal 
54*8226594fSRick McNeal static const mdb_modinfo_t modinfo = {
55*8226594fSRick McNeal 	MDB_API_VERSION, dcmds, NULL
56*8226594fSRick McNeal };
57*8226594fSRick McNeal 
smartpqi_help(void)58*8226594fSRick McNeal static void smartpqi_help(void)
59*8226594fSRick McNeal {
60*8226594fSRick McNeal 	mdb_printf("%s",
61*8226594fSRick McNeal 	    "-c <cntlr> display the state for <cntlr> and the no."
62*8226594fSRick McNeal 	    " of devices attached.\n"
63*8226594fSRick McNeal 	    "-v provide detailed information about each device attached.\n");
64*8226594fSRick McNeal }
65*8226594fSRick McNeal 
66*8226594fSRick McNeal char *
bool_to_str(int v)67*8226594fSRick McNeal bool_to_str(int v)
68*8226594fSRick McNeal {
69*8226594fSRick McNeal 	return (v ? "TRUE" : "FALSE");
70*8226594fSRick McNeal }
71*8226594fSRick McNeal 
72*8226594fSRick McNeal const mdb_modinfo_t *
_mdb_init(void)73*8226594fSRick McNeal _mdb_init(void)
74*8226594fSRick McNeal {
75*8226594fSRick McNeal 	return (&modinfo);
76*8226594fSRick McNeal }
77*8226594fSRick McNeal 
78*8226594fSRick McNeal static void
display_sense_data(struct scsi_extended_sense data)79*8226594fSRick McNeal display_sense_data(struct scsi_extended_sense data)
80*8226594fSRick McNeal {
81*8226594fSRick McNeal 	mdb_printf("    SCSI sense data es_key 0x%x  ", data.es_key);
82*8226594fSRick McNeal 	mdb_printf("    es_add_code 0x%x  ", data.es_add_code);
83*8226594fSRick McNeal 	mdb_printf("    es_qual_code 0x%x\n", data.es_qual_code);
84*8226594fSRick McNeal }
85*8226594fSRick McNeal 
86*8226594fSRick McNeal static void
display_scsi_status(struct scsi_arq_status scsi_status)87*8226594fSRick McNeal display_scsi_status(struct scsi_arq_status scsi_status)
88*8226594fSRick McNeal {
89*8226594fSRick McNeal 	mdb_printf("    req pkt status\t\t\t0x%x\n",
90*8226594fSRick McNeal 	    *(int8_t *)&scsi_status.sts_rqpkt_status);
91*8226594fSRick McNeal 	mdb_printf("    req pkt resid\t\t\t0x%x\n",
92*8226594fSRick McNeal 	    scsi_status.sts_rqpkt_resid);
93*8226594fSRick McNeal 	mdb_printf("    req pkt state\t\t\t%d\n", scsi_status.sts_rqpkt_state);
94*8226594fSRick McNeal 	mdb_printf("    req pkt state\t\t\t%d\n",
95*8226594fSRick McNeal 	    scsi_status.sts_rqpkt_statistics);
96*8226594fSRick McNeal 	if (scsi_status.sts_status.sts_chk)
97*8226594fSRick McNeal 		display_sense_data(scsi_status.sts_sensedata);
98*8226594fSRick McNeal }
99*8226594fSRick McNeal 
100*8226594fSRick McNeal static char *
cmd_action_str(pqi_cmd_action_t action,char * tmpstr,int tmplen)101*8226594fSRick McNeal cmd_action_str(pqi_cmd_action_t action, char *tmpstr, int tmplen)
102*8226594fSRick McNeal {
103*8226594fSRick McNeal 	switch (action) {
104*8226594fSRick McNeal 	case PQI_CMD_UNINIT:
105*8226594fSRick McNeal 		return ("UNINIT");
106*8226594fSRick McNeal 	case PQI_CMD_QUEUE:
107*8226594fSRick McNeal 		return ("QUEUE");
108*8226594fSRick McNeal 	case PQI_CMD_START:
109*8226594fSRick McNeal 		return ("START");
110*8226594fSRick McNeal 	case PQI_CMD_CMPLT:
111*8226594fSRick McNeal 		return ("COMPLETE");
112*8226594fSRick McNeal 	case PQI_CMD_TIMEOUT:
113*8226594fSRick McNeal 		return ("TIMEOUT");
114*8226594fSRick McNeal 	case PQI_CMD_FAIL:
115*8226594fSRick McNeal 		return ("FAIL");
116*8226594fSRick McNeal 	default:
117*8226594fSRick McNeal 		(void) mdb_snprintf(tmpstr, tmplen, "BAD ACTION <0x%x>",
118*8226594fSRick McNeal 		    action);
119*8226594fSRick McNeal 		return (tmpstr);
120*8226594fSRick McNeal 	}
121*8226594fSRick McNeal }
122*8226594fSRick McNeal 
123*8226594fSRick McNeal struct scsi_key_strings pqi_cmds[] = {
124*8226594fSRick McNeal 	SCSI_CMDS_KEY_STRINGS,
125*8226594fSRick McNeal 	BMIC_READ,	"BMIC Read",
126*8226594fSRick McNeal 	BMIC_WRITE,	"BMIC Write",
127*8226594fSRick McNeal 	CISS_REPORT_LOG,	"CISS Report Logical",
128*8226594fSRick McNeal 	CISS_REPORT_PHYS,	"CISS Report Physical",
129*8226594fSRick McNeal 	-1,	NULL
130*8226594fSRick McNeal };
131*8226594fSRick McNeal 
132*8226594fSRick McNeal static char *
mdb_cdb_to_str(uint8_t scsi_cmd,char * tmpstr,int tmplen)133*8226594fSRick McNeal mdb_cdb_to_str(uint8_t scsi_cmd, char *tmpstr, int tmplen)
134*8226594fSRick McNeal {
135*8226594fSRick McNeal 	int	i = 0;
136*8226594fSRick McNeal 
137*8226594fSRick McNeal 	while (pqi_cmds[i].key != -1) {
138*8226594fSRick McNeal 		if (scsi_cmd == pqi_cmds[i].key)
139*8226594fSRick McNeal 			return ((char *)pqi_cmds[i].message);
140*8226594fSRick McNeal 		i++;
141*8226594fSRick McNeal 	}
142*8226594fSRick McNeal 	(void) mdb_snprintf(tmpstr, tmplen, "<undecoded cmd 0x%x>", scsi_cmd);
143*8226594fSRick McNeal 	return (tmpstr);
144*8226594fSRick McNeal }
145*8226594fSRick McNeal 
146*8226594fSRick McNeal static void
display_cdb(uint8_t * cdb)147*8226594fSRick McNeal display_cdb(uint8_t *cdb)
148*8226594fSRick McNeal {
149*8226594fSRick McNeal 	int	i, tmplen;
150*8226594fSRick McNeal 	char	tmpstr[64];
151*8226594fSRick McNeal 
152*8226594fSRick McNeal 	tmplen = sizeof (tmpstr);
153*8226594fSRick McNeal 	mdb_printf("CDB %s", mdb_cdb_to_str(cdb[0], tmpstr, tmplen));
154*8226594fSRick McNeal 	for (i = 1; i < SCSI_CDB_SIZE; i++)
155*8226594fSRick McNeal 		mdb_printf(":%02x", cdb[i]);
156*8226594fSRick McNeal 
157*8226594fSRick McNeal 	mdb_printf("\n");
158*8226594fSRick McNeal }
159*8226594fSRick McNeal 
160*8226594fSRick McNeal static char *
pqi_iu_type_to_str(int val)161*8226594fSRick McNeal pqi_iu_type_to_str(int val)
162*8226594fSRick McNeal {
163*8226594fSRick McNeal 	switch (val) {
164*8226594fSRick McNeal 	case PQI_RESPONSE_IU_RAID_PATH_IO_SUCCESS: return ("Success");
165*8226594fSRick McNeal 	case PQI_RESPONSE_IU_AIO_PATH_IO_SUCCESS: return ("AIO Success");
166*8226594fSRick McNeal 	case PQI_RESPONSE_IU_GENERAL_MANAGEMENT: return ("General");
167*8226594fSRick McNeal 	case PQI_RESPONSE_IU_TASK_MANAGEMENT: return ("Task");
168*8226594fSRick McNeal 	case PQI_RESPONSE_IU_RAID_PATH_IO_ERROR: return ("IO Error");
169*8226594fSRick McNeal 	case PQI_RESPONSE_IU_AIO_PATH_IO_ERROR: return ("AIO IO Error");
170*8226594fSRick McNeal 	case PQI_RESPONSE_IU_AIO_PATH_DISABLED: return ("AIO Path Disabled");
171*8226594fSRick McNeal 	default: return ("UNHANDLED");
172*8226594fSRick McNeal 	}
173*8226594fSRick McNeal }
174*8226594fSRick McNeal 
175*8226594fSRick McNeal static void
display_raid_error_info(uintptr_t error_info)176*8226594fSRick McNeal display_raid_error_info(uintptr_t error_info)
177*8226594fSRick McNeal {
178*8226594fSRick McNeal 	struct pqi_raid_error_info info;
179*8226594fSRick McNeal 	int cnt;
180*8226594fSRick McNeal 
181*8226594fSRick McNeal 	if (error_info == 0)
182*8226594fSRick McNeal 		return;
183*8226594fSRick McNeal 	if ((cnt = mdb_vread((void *)&info, sizeof (struct pqi_raid_error_info),
184*8226594fSRick McNeal 	    (uintptr_t)error_info)) !=
185*8226594fSRick McNeal 	    sizeof (struct pqi_raid_error_info)) {
186*8226594fSRick McNeal 		mdb_warn(" Unable to read Raid error info(%d,%p)\n",
187*8226594fSRick McNeal 		    cnt, error_info);
188*8226594fSRick McNeal 		return;
189*8226594fSRick McNeal 	}
190*8226594fSRick McNeal 
191*8226594fSRick McNeal 	mdb_printf("    ---- Raid error info ----\n");
192*8226594fSRick McNeal 	mdb_printf("    data_in_result       %d\n", info.data_in_result);
193*8226594fSRick McNeal 	mdb_printf("    data_out_result      %d\n", info.data_out_result);
194*8226594fSRick McNeal 	mdb_printf("    status               %d\n", info.status);
195*8226594fSRick McNeal 	mdb_printf("    status_qualifier     %d\n", info.status_qualifier);
196*8226594fSRick McNeal 	mdb_printf("    sense_data_length    %d\n", info.sense_data_length);
197*8226594fSRick McNeal 	mdb_printf("    response_data_length %d\n", info.response_data_length);
198*8226594fSRick McNeal 	mdb_printf("    data_in_transferred  %d\n", info.data_in_transferred);
199*8226594fSRick McNeal 	mdb_printf("    data_out_transferred %d\n", info.data_out_transferred);
200*8226594fSRick McNeal }
201*8226594fSRick McNeal 
202*8226594fSRick McNeal static void
display_io_request(pqi_io_request_t * io)203*8226594fSRick McNeal display_io_request(pqi_io_request_t *io)
204*8226594fSRick McNeal {
205*8226594fSRick McNeal 	if (io == (pqi_io_request_t *)0)
206*8226594fSRick McNeal 		return;
207*8226594fSRick McNeal 
208*8226594fSRick McNeal 	mdb_printf("    ---- Command IO request ----\n");
209*8226594fSRick McNeal 	mdb_printf("    io_refcount\t\t\t\t%d\n", io->io_refcount);
210*8226594fSRick McNeal 	mdb_printf("    io_index\t\t\t\t%d\n", io->io_index);
211*8226594fSRick McNeal 	mdb_printf("    io_gen\t\t\t\t%d\n", io->io_gen);
212*8226594fSRick McNeal 	mdb_printf("    io_serviced\t\t\t\t%s\n", bool_to_str(io->io_serviced));
213*8226594fSRick McNeal 	mdb_printf("    io_raid_bypass\t\t\t%d\n", io->io_raid_bypass);
214*8226594fSRick McNeal 	mdb_printf("    io_status\t\t\t\t%d\n", io->io_status);
215*8226594fSRick McNeal 	mdb_printf("    io_iu\t\t\t\t0x%p\n", io->io_iu);
216*8226594fSRick McNeal 	mdb_printf("    io_pi\t\t\t\t%d\n", io->io_pi);
217*8226594fSRick McNeal 	mdb_printf("    io_iu_type\t\t\t\t%s\n",
218*8226594fSRick McNeal 	    pqi_iu_type_to_str(io->io_iu_type));
219*8226594fSRick McNeal 	display_raid_error_info((uintptr_t)io->io_error_info);
220*8226594fSRick McNeal }
221*8226594fSRick McNeal 
222*8226594fSRick McNeal static int
display_cmd(pqi_cmd_t * cmdp)223*8226594fSRick McNeal display_cmd(pqi_cmd_t *cmdp)
224*8226594fSRick McNeal {
225*8226594fSRick McNeal 	int			read_cnt, tmplen;
226*8226594fSRick McNeal 	char			tmpstr[64];
227*8226594fSRick McNeal 	pqi_io_request_t	pqi_io;
228*8226594fSRick McNeal 
229*8226594fSRick McNeal 	tmplen = sizeof (tmpstr);
230*8226594fSRick McNeal 	display_cdb(cmdp->pc_cdb);
231*8226594fSRick McNeal 	mdb_printf("    cur action\t\t\t%s\n",
232*8226594fSRick McNeal 	    cmd_action_str(cmdp->pc_cur_action, tmpstr, tmplen));
233*8226594fSRick McNeal 	mdb_printf("    last action\t\t\t%s\n",
234*8226594fSRick McNeal 	    cmd_action_str(cmdp->pc_last_action, tmpstr, tmplen));
235*8226594fSRick McNeal 	display_scsi_status(cmdp->pc_cmd_scb);
236*8226594fSRick McNeal 	mdb_printf("    pc_dma_count\t\t\t%d\n", cmdp->pc_dma_count);
237*8226594fSRick McNeal 	mdb_printf("    pc_flags\t\t\t\t0x%x\n", cmdp->pc_flags);
238*8226594fSRick McNeal 	mdb_printf("    pc_statuslen\t\t\t%d\n", cmdp->pc_statuslen);
239*8226594fSRick McNeal 	mdb_printf("    pc_cmdlen\t\t\t\t%d\n", cmdp->pc_cmdlen);
240*8226594fSRick McNeal 
241*8226594fSRick McNeal 	if (cmdp->pc_io_rqst == (pqi_io_request_t *)0)
242*8226594fSRick McNeal 		return (DCMD_OK);
243*8226594fSRick McNeal 
244*8226594fSRick McNeal 	read_cnt = mdb_vread(&pqi_io, sizeof (pqi_io_request_t),
245*8226594fSRick McNeal 	    (uintptr_t)cmdp->pc_io_rqst);
246*8226594fSRick McNeal 	if (read_cnt == -1) {
247*8226594fSRick McNeal 		mdb_warn(" Error reading IO structure address 0x%p - "
248*8226594fSRick McNeal 		    "skipping diplay of IO commands\n",
249*8226594fSRick McNeal 		    cmdp->pc_io_rqst);
250*8226594fSRick McNeal 		return (DCMD_ERR);
251*8226594fSRick McNeal 	} else if (read_cnt != sizeof (pqi_io_request_t)) {
252*8226594fSRick McNeal 		mdb_warn(" cannot read IO structure count %d at0x%p - "
253*8226594fSRick McNeal 		    "skipping diplay of IO commands\n",
254*8226594fSRick McNeal 		    read_cnt, cmdp->pc_io_rqst);
255*8226594fSRick McNeal 		return (DCMD_ERR);
256*8226594fSRick McNeal 	} else {
257*8226594fSRick McNeal 		display_io_request(&pqi_io);
258*8226594fSRick McNeal 	}
259*8226594fSRick McNeal 	return (DCMD_OK);
260*8226594fSRick McNeal }
261*8226594fSRick McNeal 
262*8226594fSRick McNeal /*
263*8226594fSRick McNeal  * listp  - the pointer to the head of the linked list
264*8226594fSRick McNeal  * sz     - size of the lest element to be read
265*8226594fSRick McNeal  * current - pointer to current list_node structure in local storage
266*8226594fSRick McNeal  */
267*8226594fSRick McNeal static list_node_t *
pqi_list_next(list_node_t * listp,size_t sz,void * structp,list_node_t * current)268*8226594fSRick McNeal pqi_list_next(list_node_t *listp, size_t sz, void *structp,
269*8226594fSRick McNeal     list_node_t *current)
270*8226594fSRick McNeal {
271*8226594fSRick McNeal 	int rval;
272*8226594fSRick McNeal 
273*8226594fSRick McNeal 	if (current->list_next == (list_node_t *)listp)
274*8226594fSRick McNeal 		return ((list_node_t *)NULL);
275*8226594fSRick McNeal 
276*8226594fSRick McNeal 	if (current->list_next == (list_node_t *)NULL)
277*8226594fSRick McNeal 		return ((list_node_t *)NULL);
278*8226594fSRick McNeal 
279*8226594fSRick McNeal 	if (current->list_next == current->list_prev)
280*8226594fSRick McNeal 		return ((list_node_t *)NULL);
281*8226594fSRick McNeal 
282*8226594fSRick McNeal 	rval = mdb_vread(structp, sz, (uintptr_t)current->list_next);
283*8226594fSRick McNeal 	if (rval == -1 || (size_t)rval != sz) {
284*8226594fSRick McNeal 		mdb_warn("Error reading a next list element so "
285*8226594fSRick McNeal 		    "skipping display of remaining elements\n");
286*8226594fSRick McNeal 		return ((list_node_t *)NULL);
287*8226594fSRick McNeal 	}
288*8226594fSRick McNeal 	return (current);
289*8226594fSRick McNeal }
290*8226594fSRick McNeal 
291*8226594fSRick McNeal static void
pqi_list_head(list_t list,uint8_t * drvp,size_t offset,list_node_t ** list_anchor)292*8226594fSRick McNeal pqi_list_head(list_t list, uint8_t *drvp, size_t offset,
293*8226594fSRick McNeal     list_node_t **list_anchor)
294*8226594fSRick McNeal {
295*8226594fSRick McNeal 	*list_anchor = (list_node_t *)(drvp +
296*8226594fSRick McNeal 	    offset + offsetof(list_t, list_head));
297*8226594fSRick McNeal 	if (*list_anchor == list.list_head.list_next) {
298*8226594fSRick McNeal 		*list_anchor = NULL;
299*8226594fSRick McNeal 	}
300*8226594fSRick McNeal }
301*8226594fSRick McNeal 
302*8226594fSRick McNeal static int
pqi_device_list_head(list_t s_devnodes,uint8_t * addr,list_node_t ** dev_head,struct pqi_device * dev)303*8226594fSRick McNeal pqi_device_list_head(list_t s_devnodes, uint8_t *addr,
304*8226594fSRick McNeal     list_node_t **dev_head, struct pqi_device *dev)
305*8226594fSRick McNeal {
306*8226594fSRick McNeal 	int rval;
307*8226594fSRick McNeal 
308*8226594fSRick McNeal 	pqi_list_head(s_devnodes, addr, offsetof(struct pqi_state, s_devnodes),
309*8226594fSRick McNeal 	    dev_head);
310*8226594fSRick McNeal 	if (*dev_head == NULL)
311*8226594fSRick McNeal 		return (DCMD_ERR);
312*8226594fSRick McNeal 
313*8226594fSRick McNeal 	rval = mdb_vread((void *)dev, sizeof (struct pqi_device),
314*8226594fSRick McNeal 	    (uintptr_t)s_devnodes.list_head.list_next);
315*8226594fSRick McNeal 	if (rval == -1) {
316*8226594fSRick McNeal 		mdb_warn(" cannot read device list head (0x%p)\n",
317*8226594fSRick McNeal 		    *dev_head);
318*8226594fSRick McNeal 		return (DCMD_ERR);
319*8226594fSRick McNeal 	}
320*8226594fSRick McNeal 	return (DCMD_OK);
321*8226594fSRick McNeal }
322*8226594fSRick McNeal 
323*8226594fSRick McNeal static int
pqi_cmd_list_head(list_t cmds,uint8_t * addr,list_node_t ** cmd_head,struct pqi_cmd * cmdp)324*8226594fSRick McNeal pqi_cmd_list_head(list_t cmds, uint8_t *addr,
325*8226594fSRick McNeal     list_node_t **cmd_head, struct pqi_cmd *cmdp)
326*8226594fSRick McNeal {
327*8226594fSRick McNeal 	int rval;
328*8226594fSRick McNeal 
329*8226594fSRick McNeal 	pqi_list_head(cmds, (uint8_t *)addr,
330*8226594fSRick McNeal 	    offsetof(struct pqi_device, pd_cmd_list),
331*8226594fSRick McNeal 	    cmd_head);
332*8226594fSRick McNeal 	/* Read in the first entry of the command list */
333*8226594fSRick McNeal 	rval = mdb_vread(cmdp, sizeof (struct pqi_cmd),
334*8226594fSRick McNeal 	    (uintptr_t)cmds.list_head.list_next);
335*8226594fSRick McNeal 	if (rval == -1) {
336*8226594fSRick McNeal 		mdb_warn(" cannot read initial entry in "
337*8226594fSRick McNeal 		    "command list (0x%p)\n", cmds.list_head.list_next);
338*8226594fSRick McNeal 	}
339*8226594fSRick McNeal 	return (rval);
340*8226594fSRick McNeal }
341*8226594fSRick McNeal 
342*8226594fSRick McNeal static char *
pqi_get_guid(char * pd_guid)343*8226594fSRick McNeal pqi_get_guid(char *pd_guid)
344*8226594fSRick McNeal {
345*8226594fSRick McNeal 	static char myguid[41];
346*8226594fSRick McNeal 
347*8226594fSRick McNeal 	if (mdb_vread(myguid, sizeof (myguid) - 1, (uintptr_t)pd_guid) == -1)
348*8226594fSRick McNeal 		myguid[0] = '\0';
349*8226594fSRick McNeal 
350*8226594fSRick McNeal 	return (myguid);
351*8226594fSRick McNeal }
352*8226594fSRick McNeal 
353*8226594fSRick McNeal static void
display_device_info(pqi_device_t * dev)354*8226594fSRick McNeal display_device_info(pqi_device_t *dev)
355*8226594fSRick McNeal {
356*8226594fSRick McNeal 	char str[40];
357*8226594fSRick McNeal 
358*8226594fSRick McNeal 	mdb_printf("-- Device pd_target %d --\n", dev->pd_target);
359*8226594fSRick McNeal 
360*8226594fSRick McNeal 	mdb_printf("pd_devtype\t\t\t\t%d\n", dev->pd_devtype);
361*8226594fSRick McNeal 	mdb_printf("device pd_flags\t\t\t\t0x%x\n", dev->pd_flags);
362*8226594fSRick McNeal 	mdb_printf("pd_active_cmds\t\t\t\t%d\n", dev->pd_active_cmds);
363*8226594fSRick McNeal 
364*8226594fSRick McNeal 	mdb_printf("pd_dip\t\t\t\t\t0x%p\n", dev->pd_dip);
365*8226594fSRick McNeal 	mdb_printf("pd_pip\t\t\t\t\t0x%p\n", dev->pd_pip);
366*8226594fSRick McNeal 	mdb_printf("pd_pip_offlined\t\t\t\t0x%p\n", dev->pd_pip_offlined);
367*8226594fSRick McNeal 
368*8226594fSRick McNeal 	mdb_printf("pd_online\t\t\t\t%s\n", bool_to_str(dev->pd_online));
369*8226594fSRick McNeal 	mdb_printf("pd_scanned\t\t\t\t%s\n", bool_to_str(dev->pd_scanned));
370*8226594fSRick McNeal 	mdb_printf("pd_phys_dev\t\t\t\t%s\n", bool_to_str(dev->pd_phys_dev));
371*8226594fSRick McNeal 	mdb_printf("pd_external_raid\t\t\t%s\n",
372*8226594fSRick McNeal 	    bool_to_str(dev->pd_external_raid));
373*8226594fSRick McNeal 	mdb_printf("pd_pd_aio_enabled\t\t\t%s\n",
374*8226594fSRick McNeal 	    bool_to_str(dev->pd_aio_enabled));
375*8226594fSRick McNeal 
376*8226594fSRick McNeal 	mdb_printf("GUID\t\t\t\t\t%s\n", pqi_get_guid(dev->pd_guid));
377*8226594fSRick McNeal 
378*8226594fSRick McNeal 	(void) strncpy(str, (char *)(dev->pd_vendor), sizeof (dev->pd_vendor));
379*8226594fSRick McNeal 	str[sizeof (dev->pd_vendor)] = '\0';
380*8226594fSRick McNeal 	mdb_printf("pd_vendor\t\t\t\t%s\n", str);
381*8226594fSRick McNeal 	(void) strncpy(str, (char *)(dev->pd_model), sizeof (dev->pd_model));
382*8226594fSRick McNeal 	str[sizeof (dev->pd_model)] = '\0';
383*8226594fSRick McNeal 	mdb_printf("pd_model\t\t\t\t%s\n", str);
384*8226594fSRick McNeal }
385*8226594fSRick McNeal 
386*8226594fSRick McNeal /*
387*8226594fSRick McNeal  * display device info: number of drives attached, number of commands running on
388*8226594fSRick McNeal  * each device, drive data and command data.
389*8226594fSRick McNeal  */
390*8226594fSRick McNeal static void
pqi_display_devices(list_t s_devnodes,pqi_state_t * drvp,uint_t dev_verbose)391*8226594fSRick McNeal pqi_display_devices(list_t s_devnodes, pqi_state_t *drvp, uint_t dev_verbose)
392*8226594fSRick McNeal {
393*8226594fSRick McNeal 	int rval;
394*8226594fSRick McNeal 	int dev_count = 0;
395*8226594fSRick McNeal 	struct pqi_device d;
396*8226594fSRick McNeal 	pqi_device_t *next_dp;
397*8226594fSRick McNeal 	pqi_cmd_t *cmdp;
398*8226594fSRick McNeal 
399*8226594fSRick McNeal 	struct list_node *list_head;
400*8226594fSRick McNeal 	struct list_node *d_list_head;
401*8226594fSRick McNeal 	struct list_node *dev_current;
402*8226594fSRick McNeal 	struct list_node *cmd_current;
403*8226594fSRick McNeal 	struct pqi_device *d_drvrp; /* driver addr of device list entry */
404*8226594fSRick McNeal 
405*8226594fSRick McNeal 	mdb_printf("---- Devices for controller (0x%p) ----\n",
406*8226594fSRick McNeal 	    ((uint8_t *)drvp) +
407*8226594fSRick McNeal 	    offsetof(struct pqi_state, s_devnodes));
408*8226594fSRick McNeal 
409*8226594fSRick McNeal 	rval = pqi_device_list_head(s_devnodes,
410*8226594fSRick McNeal 	    (uint8_t *)drvp, &d_list_head, &d);
411*8226594fSRick McNeal 	if (d_list_head == NULL) {
412*8226594fSRick McNeal 		mdb_printf("Number of devices %d\n", dev_count);
413*8226594fSRick McNeal 		return;
414*8226594fSRick McNeal 	}
415*8226594fSRick McNeal 	cmdp = (pqi_cmd_t *)mdb_alloc(sizeof (struct pqi_cmd), UM_SLEEP|UM_GC);
416*8226594fSRick McNeal 
417*8226594fSRick McNeal 	next_dp = &d;
418*8226594fSRick McNeal 
419*8226594fSRick McNeal 	dev_current = (list_node_t *)((uint8_t *)(&d) +
420*8226594fSRick McNeal 	    offsetof(struct pqi_device, pd_list));
421*8226594fSRick McNeal 	d_drvrp = (pqi_device_t *)(s_devnodes.list_head.list_next);
422*8226594fSRick McNeal 	while (dev_current != NULL) {
423*8226594fSRick McNeal 		if (dev_verbose) {
424*8226594fSRick McNeal 			display_device_info((pqi_device_t *)&d);
425*8226594fSRick McNeal 
426*8226594fSRick McNeal 			/* now display command information */
427*8226594fSRick McNeal 			rval = pqi_cmd_list_head(d.pd_cmd_list,
428*8226594fSRick McNeal 			    (uint8_t *)d_drvrp, &list_head, cmdp);
429*8226594fSRick McNeal 			if (rval == -1) {
430*8226594fSRick McNeal 				mdb_warn("unable to read the command list head"
431*8226594fSRick McNeal 				    " for device %d\n", d.pd_target);
432*8226594fSRick McNeal 				list_head = NULL;
433*8226594fSRick McNeal 			}
434*8226594fSRick McNeal 			if (list_head != NULL) {
435*8226594fSRick McNeal 				mdb_printf("    ---- Commands for device %d"
436*8226594fSRick McNeal 				    " (0x%p) ----\n",
437*8226594fSRick McNeal 				    next_dp->pd_target, list_head);
438*8226594fSRick McNeal 
439*8226594fSRick McNeal 				cmd_current =
440*8226594fSRick McNeal 				    (list_node_t *)((uint8_t *)(cmdp) +
441*8226594fSRick McNeal 				    offsetof(struct pqi_cmd, pc_list));
442*8226594fSRick McNeal 
443*8226594fSRick McNeal 				while (cmd_current != NULL) {
444*8226594fSRick McNeal 					rval = display_cmd(cmdp);
445*8226594fSRick McNeal 					if (rval != DCMD_OK) {
446*8226594fSRick McNeal 						mdb_warn("Display of commands"
447*8226594fSRick McNeal 						    " aborted (%d)\n",
448*8226594fSRick McNeal 						    rval);
449*8226594fSRick McNeal 						break;
450*8226594fSRick McNeal 					}
451*8226594fSRick McNeal 
452*8226594fSRick McNeal 					cmd_current = pqi_list_next(list_head,
453*8226594fSRick McNeal 					    sizeof (struct pqi_cmd),
454*8226594fSRick McNeal 					    (void *)cmdp, cmd_current);
455*8226594fSRick McNeal 				}
456*8226594fSRick McNeal 			}
457*8226594fSRick McNeal 		}
458*8226594fSRick McNeal 		d_drvrp = (pqi_device_t *)(next_dp->pd_list.list_next);
459*8226594fSRick McNeal 		dev_current = pqi_list_next(
460*8226594fSRick McNeal 		    d_list_head,
461*8226594fSRick McNeal 		    sizeof (struct pqi_device),
462*8226594fSRick McNeal 		    (void*)next_dp,
463*8226594fSRick McNeal 		    dev_current);
464*8226594fSRick McNeal 		dev_count++;
465*8226594fSRick McNeal 	}
466*8226594fSRick McNeal 
467*8226594fSRick McNeal 	if (!dev_verbose)
468*8226594fSRick McNeal 		mdb_printf("Number of devices\t\t\t%d\n", dev_count);
469*8226594fSRick McNeal }
470*8226594fSRick McNeal 
471*8226594fSRick McNeal static void
pqi_display_instance(pqi_state_t * pqi_statep)472*8226594fSRick McNeal pqi_display_instance(pqi_state_t *pqi_statep)
473*8226594fSRick McNeal {
474*8226594fSRick McNeal 	mdb_printf("s_dip\t\t\t\t\t0x%p\n", pqi_statep->s_dip);
475*8226594fSRick McNeal 	mdb_printf("s_flags\t\t\t\t\t0x%x\n", pqi_statep->s_flags);
476*8226594fSRick McNeal 	mdb_printf("s_firmware_version\t\t\t%s\n",
477*8226594fSRick McNeal 	    pqi_statep->s_firmware_version);
478*8226594fSRick McNeal 
479*8226594fSRick McNeal 	mdb_printf("s_offline\t\t\t\t%s\ns_disable_mpxio\t\t\t\t%s\n",
480*8226594fSRick McNeal 	    bool_to_str(pqi_statep->s_offline),
481*8226594fSRick McNeal 	    bool_to_str(pqi_statep->s_disable_mpxio));
482*8226594fSRick McNeal 	mdb_printf("s_debug level\t\t\t\t%d\n", pqi_statep->s_debug_level);
483*8226594fSRick McNeal 
484*8226594fSRick McNeal 	mdb_printf("---- State for watchdog----\n");
485*8226594fSRick McNeal 	mdb_printf("s_intr_count\t\t\t\t%d\n", pqi_statep->s_intr_count);
486*8226594fSRick McNeal 	mdb_printf("s_last_intr_count\t\t\t%d\n",
487*8226594fSRick McNeal 	    pqi_statep->s_last_intr_count);
488*8226594fSRick McNeal 	mdb_printf("s_last_heartbeat_count\t\t\t%d\n",
489*8226594fSRick McNeal 	    pqi_statep->s_last_heartbeat_count);
490*8226594fSRick McNeal 
491*8226594fSRick McNeal 	mdb_printf("---- PQI cpabilities from controller ----\n");
492*8226594fSRick McNeal 	mdb_printf("s_max_inbound_queues\t\t\t%d\n",
493*8226594fSRick McNeal 	    pqi_statep->s_max_inbound_queues);
494*8226594fSRick McNeal 	mdb_printf("s_max_elements_per_iq\t\t\t%d\n",
495*8226594fSRick McNeal 	    pqi_statep->s_max_elements_per_iq);
496*8226594fSRick McNeal 	mdb_printf("s_max_iq_element_length\t\t\t%d\n",
497*8226594fSRick McNeal 	    pqi_statep->s_max_iq_element_length);
498*8226594fSRick McNeal 	mdb_printf("s_max_outbound_queues\t\t\t%d\n",
499*8226594fSRick McNeal 	    pqi_statep->s_max_outbound_queues);
500*8226594fSRick McNeal 	mdb_printf("s_max_elements_per_oq\t\t\t%d\n",
501*8226594fSRick McNeal 	    pqi_statep->s_max_elements_per_oq);
502*8226594fSRick McNeal 	mdb_printf("s_max_elements_per_oq\t\t\t%d\n",
503*8226594fSRick McNeal 	    pqi_statep->s_max_elements_per_oq);
504*8226594fSRick McNeal 	mdb_printf("s_max_oq_element_length\t\t\t%d\n",
505*8226594fSRick McNeal 	    pqi_statep->s_max_oq_element_length);
506*8226594fSRick McNeal 	mdb_printf("s_max_inbound_iu_length_per_firmware\t%d\n",
507*8226594fSRick McNeal 	    pqi_statep->s_max_inbound_iu_length_per_firmware);
508*8226594fSRick McNeal 	mdb_printf("s_max_inbound_queues\t\t\t%d\n",
509*8226594fSRick McNeal 	    pqi_statep->s_max_inbound_queues);
510*8226594fSRick McNeal 	mdb_printf("s_inbound_spanning_supported:\t\t%d\n",
511*8226594fSRick McNeal 	    pqi_statep->s_inbound_spanning_supported);
512*8226594fSRick McNeal 	mdb_printf("s_outbound_spanning_supported:\t\t%dk\n",
513*8226594fSRick McNeal 	    pqi_statep->s_outbound_spanning_supported);
514*8226594fSRick McNeal 	mdb_printf("s_outbound_spanning_supported:\t\t%d\n",
515*8226594fSRick McNeal 	    pqi_statep->s_outbound_spanning_supported);
516*8226594fSRick McNeal 	mdb_printf("s_pqi_mode_enabled:\t\t\t%d\n",
517*8226594fSRick McNeal 	    pqi_statep->s_pqi_mode_enabled);
518*8226594fSRick McNeal 	mdb_printf("s_cmd_queue_len\t\t\t\t%d\n", pqi_statep->s_cmd_queue_len);
519*8226594fSRick McNeal 
520*8226594fSRick McNeal 	mdb_printf("---- SIS capabilities from controller ----\n");
521*8226594fSRick McNeal 	mdb_printf("s_max_sg_entries\t\t\t%d\n", pqi_statep->s_max_sg_entries);
522*8226594fSRick McNeal 	mdb_printf("s_max_xfer_size\t\t\t\t%d\n", pqi_statep->s_max_xfer_size);
523*8226594fSRick McNeal 	mdb_printf("s_max_outstainding_requests\t\t%d\n",
524*8226594fSRick McNeal 	    pqi_statep->s_max_sg_entries);
525*8226594fSRick McNeal 
526*8226594fSRick McNeal 	mdb_printf("---- Computed values from config ----\n");
527*8226594fSRick McNeal 	mdb_printf("s_max_sg_per_iu\t\t\t\t%d\n", pqi_statep->s_max_sg_per_iu);
528*8226594fSRick McNeal 	mdb_printf("s_num_elements_per_iq\t\t\t%d\n",
529*8226594fSRick McNeal 	    pqi_statep->s_num_elements_per_iq);
530*8226594fSRick McNeal 	mdb_printf("s_num_elements_per_oq\t\t\t%d\n",
531*8226594fSRick McNeal 	    pqi_statep->s_num_elements_per_oq);
532*8226594fSRick McNeal 	mdb_printf("s_max_inbound_iu_length\t\t\t%d\n",
533*8226594fSRick McNeal 	    pqi_statep->s_max_inbound_iu_length);
534*8226594fSRick McNeal 	mdb_printf("s_num_queue_groups\t\t\t%d\n",
535*8226594fSRick McNeal 	    pqi_statep->s_num_queue_groups);
536*8226594fSRick McNeal 	mdb_printf("s_max_io_slots\t\t\t\t%d\n", pqi_statep->s_max_io_slots);
537*8226594fSRick McNeal 	mdb_printf("s_sg_chain_buf_length\t\t\t%d\n",
538*8226594fSRick McNeal 	    pqi_statep->s_sg_chain_buf_length);
539*8226594fSRick McNeal 	mdb_printf("s_max_sectors\t\t\t\t%d\n", pqi_statep->s_max_sectors);
540*8226594fSRick McNeal 
541*8226594fSRick McNeal 	mdb_printf("---- IO slot information ----\n");
542*8226594fSRick McNeal 	mdb_printf("s_io_rqst_pool\t\t\t\t0x%p\n", pqi_statep->s_io_rqst_pool);
543*8226594fSRick McNeal 	mdb_printf("s_io_wait_cnt\t\t\t\t%d\n", pqi_statep->s_io_wait_cnt);
544*8226594fSRick McNeal 	mdb_printf("s_next_io_slot\t\t\t\t%d\n", pqi_statep->s_next_io_slot);
545*8226594fSRick McNeal 	mdb_printf("s_io_need\t\t\t\t%d\n", pqi_statep->s_io_need);
546*8226594fSRick McNeal 	mdb_printf("s_io_had2wait\t\t\t\t%d\n", pqi_statep->s_io_had2wait);
547*8226594fSRick McNeal 	mdb_printf("s_io_sig\t\t\t\t%d\n", pqi_statep->s_io_sig);
548*8226594fSRick McNeal }
549*8226594fSRick McNeal 
550*8226594fSRick McNeal static int
pqi_getopts(uintptr_t addr,int argc,const mdb_arg_t * argv,uintptr_t * cntlr,uint_t * print_devices)551*8226594fSRick McNeal pqi_getopts(uintptr_t addr, int argc, const mdb_arg_t *argv, uintptr_t *cntlr,
552*8226594fSRick McNeal     uint_t *print_devices)
553*8226594fSRick McNeal {
554*8226594fSRick McNeal 	uintptr_t device = INVALID_OPT_VAL;
555*8226594fSRick McNeal 
556*8226594fSRick McNeal 	*cntlr = INVALID_OPT_VAL;
557*8226594fSRick McNeal 	*print_devices = FALSE;
558*8226594fSRick McNeal 	mdb_getopts(argc, argv,
559*8226594fSRick McNeal 	    'v', MDB_OPT_SETBITS, TRUE, print_devices,
560*8226594fSRick McNeal 	    'c', MDB_OPT_UINTPTR, (uintptr_t)cntlr,
561*8226594fSRick McNeal 	    'd', MDB_OPT_UINTPTR, (uintptr_t)&device,
562*8226594fSRick McNeal 	    NULL);
563*8226594fSRick McNeal 
564*8226594fSRick McNeal 	if (*cntlr == INVALID_OPT_VAL) {
565*8226594fSRick McNeal 		mdb_warn("-c <controller> required\n");
566*8226594fSRick McNeal 		return (DCMD_USAGE);
567*8226594fSRick McNeal 	}
568*8226594fSRick McNeal 
569*8226594fSRick McNeal 	return (DCMD_OK);
570*8226594fSRick McNeal }
571*8226594fSRick McNeal 
572*8226594fSRick McNeal static int
smartpqi(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)573*8226594fSRick McNeal smartpqi(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
574*8226594fSRick McNeal {
575*8226594fSRick McNeal 	int array_size;
576*8226594fSRick McNeal 	int rval;
577*8226594fSRick McNeal 	int pqi_statesz = sizeof (struct pqi_state);
578*8226594fSRick McNeal 	uintptr_t instance = INVALID_OPT_VAL;
579*8226594fSRick McNeal 	uintptr_t adr;
580*8226594fSRick McNeal 	void   **array_vaddr;
581*8226594fSRick McNeal 	void   **pqi_array;
582*8226594fSRick McNeal 	pqi_state_t *pqi_drvp;
583*8226594fSRick McNeal 	struct i_ddi_soft_state ss;
584*8226594fSRick McNeal 	pqi_state_t *pqi_statep;
585*8226594fSRick McNeal 	uint_t print_devices = 0;
586*8226594fSRick McNeal 
587*8226594fSRick McNeal 	if ((flags & DCMD_ADDRSPEC) == 0) {
588*8226594fSRick McNeal 		/*
589*8226594fSRick McNeal 		 * MDB has this peculiarity that addr can be non-null
590*8226594fSRick McNeal 		 * from a previous invocation:
591*8226594fSRick McNeal 		 * e.g. 0xfffffef49cd64000::smartpqi,
592*8226594fSRick McNeal 		 * but flags shows that
593*8226594fSRick McNeal 		 * the command line is ::spamrtpqi or ::smartpai <options>
594*8226594fSRick McNeal 		 * To make sure the desired command line options are
595*8226594fSRick McNeal 		 * honored, we set addr to 0 and proceed with evaluating
596*8226594fSRick McNeal 		 * these command as entered.
597*8226594fSRick McNeal 		 */
598*8226594fSRick McNeal 		addr = (uintptr_t)0;
599*8226594fSRick McNeal 	}
600*8226594fSRick McNeal 	rval = pqi_getopts(addr, argc, argv, &instance, &print_devices);
601*8226594fSRick McNeal 	if (rval != DCMD_OK) {
602*8226594fSRick McNeal 		return (rval);
603*8226594fSRick McNeal 	}
604*8226594fSRick McNeal 
605*8226594fSRick McNeal 	/* read the address of the pqi_state variable in the smartpqi driver */
606*8226594fSRick McNeal 	if (mdb_readvar((void *)&adr, "pqi_state") == -1) {
607*8226594fSRick McNeal 		mdb_warn("Cannot read pqi driver variable pqi_softstate.\n");
608*8226594fSRick McNeal 		return (DCMD_ERR);
609*8226594fSRick McNeal 	}
610*8226594fSRick McNeal 	/* now read the i_ddi_soft_state structure pointer */
611*8226594fSRick McNeal 	if (mdb_vread((void *)&ss, sizeof (ss), adr) != sizeof (ss)) {
612*8226594fSRick McNeal 		mdb_warn("Cannot read smartpqi softstate struct"
613*8226594fSRick McNeal 		    " pqi_state (Invalid pointer?(0x%p)).\n",
614*8226594fSRick McNeal 		    (uintptr_t)adr);
615*8226594fSRick McNeal 		return (DCMD_ERR);
616*8226594fSRick McNeal 	}
617*8226594fSRick McNeal 	/*
618*8226594fSRick McNeal 	 * now allocate space for the array containing the pqi_state
619*8226594fSRick McNeal 	 * pointers and read in this array
620*8226594fSRick McNeal 	 */
621*8226594fSRick McNeal 	array_size = ss.n_items * (sizeof (void*));
622*8226594fSRick McNeal 	array_vaddr = ss.array;
623*8226594fSRick McNeal 	pqi_array = (void **)mdb_alloc(array_size, UM_SLEEP|UM_GC);
624*8226594fSRick McNeal 	if (mdb_vread(pqi_array, array_size, (uintptr_t)array_vaddr) !=
625*8226594fSRick McNeal 	    array_size) {
626*8226594fSRick McNeal 		mdb_warn("Corrupted softstate struct\n");
627*8226594fSRick McNeal 		return (DCMD_ERR);
628*8226594fSRick McNeal 	}
629*8226594fSRick McNeal 
630*8226594fSRick McNeal 	if (instance >= ss.n_items || pqi_array[instance] == NULL) {
631*8226594fSRick McNeal 		mdb_warn("smartpqi - no information available for %d\n",
632*8226594fSRick McNeal 		    instance);
633*8226594fSRick McNeal 		return (DCMD_USAGE);
634*8226594fSRick McNeal 	}
635*8226594fSRick McNeal 
636*8226594fSRick McNeal 	pqi_statep = mdb_alloc(sizeof (struct pqi_state), UM_SLEEP|UM_GC);
637*8226594fSRick McNeal 	adr = (uintptr_t)pqi_array[instance];
638*8226594fSRick McNeal 
639*8226594fSRick McNeal 	pqi_drvp = (pqi_state_t *)adr;
640*8226594fSRick McNeal 	if (mdb_vread(pqi_statep, pqi_statesz, adr) != pqi_statesz) {
641*8226594fSRick McNeal 		mdb_warn("Cannot read pqi_state. adr 0x%p, size %d\n",
642*8226594fSRick McNeal 		    adr, pqi_statesz);
643*8226594fSRick McNeal 		return (DCMD_ERR);
644*8226594fSRick McNeal 	}
645*8226594fSRick McNeal 	mdb_printf("-------- Controller %d  pqi_state (0x%p) --------\n",
646*8226594fSRick McNeal 	    instance, adr);
647*8226594fSRick McNeal 	pqi_display_instance(pqi_statep);
648*8226594fSRick McNeal 
649*8226594fSRick McNeal 	pqi_display_devices(pqi_statep->s_devnodes, pqi_drvp, print_devices);
650*8226594fSRick McNeal 
651*8226594fSRick McNeal 	return (DCMD_OK);
652*8226594fSRick McNeal }
653