1268240Sken/*-
2268240Sken * Copyright (c) 2013 Spectra Logic Corporation
3268240Sken * All rights reserved.
4268240Sken *
5268240Sken * Redistribution and use in source and binary forms, with or without
6268240Sken * modification, are permitted provided that the following conditions
7268240Sken * are met:
8268240Sken * 1. Redistributions of source code must retain the above copyright
9268240Sken *    notice, this list of conditions, and the following disclaimer,
10268240Sken *    without modification.
11268240Sken * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12268240Sken *    substantially similar to the "NO WARRANTY" disclaimer below
13268240Sken *    ("Disclaimer") and any redistribution must be conditioned upon
14268240Sken *    including a substantially similar Disclaimer requirement for further
15268240Sken *    binary redistribution.
16268240Sken *
17268240Sken * NO WARRANTY
18268240Sken * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19268240Sken * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20268240Sken * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
21268240Sken * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22268240Sken * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23268240Sken * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24268240Sken * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25268240Sken * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26268240Sken * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27268240Sken * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28268240Sken * POSSIBILITY OF SUCH DAMAGES.
29268240Sken *
30268240Sken * Authors: Ken Merry           (Spectra Logic Corporation)
31268240Sken */
32268240Sken/*
33268240Sken * SCSI Persistent Reservation support for camcontrol(8).
34268240Sken */
35268240Sken
36268240Sken#include <sys/cdefs.h>
37268240Sken__FBSDID("$FreeBSD$");
38268240Sken
39268240Sken#include <sys/ioctl.h>
40268240Sken#include <sys/stdint.h>
41268240Sken#include <sys/types.h>
42268240Sken#include <sys/endian.h>
43268240Sken#include <sys/sbuf.h>
44268240Sken#include <sys/queue.h>
45268240Sken
46268240Sken#include <stdio.h>
47268240Sken#include <stdlib.h>
48268240Sken#include <inttypes.h>
49268240Sken#include <unistd.h>
50268240Sken#include <string.h>
51268240Sken#include <strings.h>
52268240Sken#include <fcntl.h>
53268240Sken#include <ctype.h>
54268240Sken#include <limits.h>
55268240Sken#include <err.h>
56268240Sken
57268240Sken#include <cam/cam.h>
58268240Sken#include <cam/cam_debug.h>
59268240Sken#include <cam/cam_ccb.h>
60268240Sken#include <cam/scsi/scsi_all.h>
61268240Sken#include <cam/scsi/scsi_pass.h>
62268240Sken#include <cam/scsi/scsi_message.h>
63268240Sken#include <camlib.h>
64268240Sken#include "camcontrol.h"
65268240Sken
66268240Skenstruct persist_transport_id {
67268240Sken	struct scsi_transportid_header *hdr;
68268240Sken	unsigned int alloc_len;
69268240Sken	STAILQ_ENTRY(persist_transport_id) links;
70268240Sken};
71268240Sken
72268240Sken/*
73268240Sken * Service Actions for PERSISTENT RESERVE IN.
74268240Sken */
75268240Skenstatic struct scsi_nv persist_in_actions[] = {
76268240Sken	{ "read_keys", SPRI_RK },
77268240Sken	{ "read_reservation", SPRI_RR },
78268240Sken	{ "report_capabilities", SPRI_RC },
79268240Sken	{ "read_full_status", SPRI_RS }
80268240Sken};
81268240Sken
82268240Sken/*
83268240Sken * Service Actions for PERSISTENT RESERVE OUT.
84268240Sken */
85268240Skenstatic struct scsi_nv persist_out_actions[] = {
86268240Sken	{ "register", SPRO_REGISTER },
87268240Sken	{ "reserve", SPRO_RESERVE },
88268240Sken	{ "release" , SPRO_RELEASE },
89268240Sken	{ "clear", SPRO_CLEAR },
90268240Sken	{ "preempt", SPRO_PREEMPT },
91268240Sken	{ "preempt_abort", SPRO_PRE_ABO },
92268240Sken	{ "register_ignore", SPRO_REG_IGNO },
93268240Sken	{ "register_move", SPRO_REG_MOVE },
94268240Sken	{ "replace_lost", SPRO_REPL_LOST_RES }
95268240Sken};
96268240Sken
97268240Sken/*
98268240Sken * Known reservation scopes.  As of SPC-4, only LU_SCOPE is used in the
99268240Sken * spec.  The others are obsolete.
100268240Sken */
101268240Skenstatic struct scsi_nv persist_scope_table[] = {
102268240Sken	{ "lun", SPR_LU_SCOPE },
103268240Sken	{ "extent", SPR_EXTENT_SCOPE },
104268240Sken	{ "element", SPR_ELEMENT_SCOPE }
105268240Sken};
106268240Sken
107268240Sken/*
108268240Sken * Reservation types.  The longer name for a given reservation type is
109268240Sken * listed first, so that it makes more sense when we print out the
110268240Sken * reservation type.  We step through the table linearly when looking for
111268240Sken * the text name for a particular numeric reservation type value.
112268240Sken */
113268240Skenstatic struct scsi_nv persist_type_table[] = {
114268240Sken	{ "read_shared", SPR_TYPE_RD_SHARED },
115268240Sken	{ "write_exclusive", SPR_TYPE_WR_EX },
116268240Sken	{ "wr_ex", SPR_TYPE_WR_EX },
117268240Sken	{ "read_exclusive", SPR_TYPE_RD_EX },
118268240Sken	{ "rd_ex", SPR_TYPE_RD_EX },
119268240Sken	{ "exclusive_access", SPR_TYPE_EX_AC },
120268240Sken	{ "ex_ac", SPR_TYPE_EX_AC },
121268240Sken	{ "write_exclusive_reg_only", SPR_TYPE_WR_EX_RO },
122268240Sken	{ "wr_ex_ro", SPR_TYPE_WR_EX_RO },
123268240Sken	{ "exclusive_access_reg_only", SPR_TYPE_EX_AC_RO },
124268240Sken	{ "ex_ac_ro", SPR_TYPE_EX_AC_RO },
125268240Sken	{ "write_exclusive_all_regs", SPR_TYPE_WR_EX_AR },
126268240Sken	{ "wr_ex_ar", SPR_TYPE_WR_EX_AR },
127268240Sken	{ "exclusive_access_all_regs", SPR_TYPE_EX_AC_AR },
128268240Sken	{ "ex_ac_ar", SPR_TYPE_EX_AC_AR }
129268240Sken};
130268240Sken
131268240Sken/*
132268240Sken * Print out the standard scope/type field.
133268240Sken */
134268240Skenstatic void
135268240Skenpersist_print_scopetype(uint8_t scopetype)
136268240Sken{
137268240Sken	const char *tmpstr;
138268240Sken	int num_entries;
139268240Sken
140268240Sken	num_entries = sizeof(persist_scope_table) /
141268240Sken		      sizeof(persist_scope_table[0]);
142268240Sken	tmpstr = scsi_nv_to_str(persist_scope_table, num_entries,
143268240Sken				scopetype & SPR_SCOPE_MASK);
144268240Sken	fprintf(stdout, "Scope: %s (%#x)\n", (tmpstr != NULL) ? tmpstr :
145268240Sken		"Unknown", (scopetype & SPR_SCOPE_MASK) >> SPR_SCOPE_SHIFT);
146268240Sken
147268240Sken	num_entries = sizeof(persist_type_table) /
148268240Sken		      sizeof(persist_type_table[0]);
149268240Sken	tmpstr = scsi_nv_to_str(persist_type_table, num_entries,
150268240Sken				scopetype & SPR_TYPE_MASK);
151268240Sken	fprintf(stdout, "Type: %s (%#x)\n", (tmpstr != NULL) ? tmpstr :
152268240Sken		"Unknown", scopetype & SPR_TYPE_MASK);
153268240Sken}
154268240Sken
155268240Skenstatic void
156268240Skenpersist_print_transportid(uint8_t *buf, uint32_t len)
157268240Sken{
158268240Sken	struct sbuf *sb;
159268240Sken
160268240Sken	sb = sbuf_new_auto();
161268240Sken	if (sb == NULL)
162268240Sken		fprintf(stderr, "Unable to allocate sbuf\n");
163268240Sken
164268240Sken	scsi_transportid_sbuf(sb, (struct scsi_transportid_header *)buf, len);
165268240Sken
166268240Sken	sbuf_finish(sb);
167268240Sken
168268240Sken	fprintf(stdout, "%s\n", sbuf_data(sb));
169268240Sken
170268240Sken	sbuf_delete(sb);
171268240Sken}
172268240Sken
173268240Sken/*
174268240Sken * Print out a persistent reservation.  This is used with the READ
175268240Sken * RESERVATION (0x01) service action of the PERSISTENT RESERVE IN command.
176268240Sken */
177268240Skenstatic void
178268240Skenpersist_print_res(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
179268240Sken{
180268240Sken	uint32_t length;
181268240Sken	struct scsi_per_res_in_rsrv *res;
182268240Sken
183268240Sken	length = scsi_4btoul(hdr->length);
184268240Sken	length = MIN(length, valid_len);
185268240Sken
186268240Sken	res = (struct scsi_per_res_in_rsrv *)hdr;
187268240Sken
188268240Sken	if (length < sizeof(res->data) - sizeof(res->data.extent_length)) {
189268240Sken		if (length == 0)
190268240Sken			fprintf(stdout, "No reservations.\n");
191268240Sken		else
192268240Sken			warnx("unable to print reservation, only got %u "
193268240Sken			      "valid bytes", length);
194268240Sken		return;
195268240Sken	}
196268240Sken	fprintf(stdout, "PRgeneration: %#x\n",
197268240Sken		scsi_4btoul(res->header.generation));
198268240Sken	fprintf(stdout, "Reservation Key: %#jx\n",
199268240Sken		(uintmax_t)scsi_8btou64(res->data.reservation));
200268240Sken	fprintf(stdout, "Scope address: %#x\n",
201268240Sken		scsi_4btoul(res->data.scope_addr));
202268240Sken
203268240Sken	persist_print_scopetype(res->data.scopetype);
204268240Sken
205268240Sken	fprintf(stdout, "Extent length: %u\n",
206268240Sken		scsi_2btoul(res->data.extent_length));
207268240Sken}
208268240Sken
209268240Sken/*
210268240Sken * Print out persistent reservation keys.  This is used with the READ KEYS
211268240Sken * service action of the PERSISTENT RESERVE IN command.
212268240Sken */
213268240Skenstatic void
214268240Skenpersist_print_keys(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
215268240Sken{
216268240Sken	uint32_t length, num_keys, i;
217268240Sken	struct scsi_per_res_key *key;
218268240Sken
219268240Sken	length = scsi_4btoul(hdr->length);
220268240Sken	length = MIN(length, valid_len);
221268240Sken
222268240Sken	num_keys = length / sizeof(*key);
223268240Sken
224268240Sken	fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation));
225268240Sken	fprintf(stdout, "%u key%s%s\n", num_keys, (num_keys == 1) ? "" : "s",
226268240Sken		(num_keys == 0) ? "." : ":");
227268240Sken
228268240Sken	for (i = 0, key = (struct scsi_per_res_key *)&hdr[1]; i < num_keys;
229268240Sken	     i++, key++) {
230268240Sken		fprintf(stdout, "%u: %#jx\n", i,
231268240Sken			(uintmax_t)scsi_8btou64(key->key));
232268240Sken	}
233268240Sken}
234268240Sken
235268240Sken/*
236268240Sken * Print out persistent reservation capabilities.  This is used with the
237268240Sken * REPORT CAPABILITIES service action of the PERSISTENT RESERVE IN command.
238268240Sken */
239268240Skenstatic void
240268240Skenpersist_print_cap(struct scsi_per_res_cap *cap, uint32_t valid_len)
241268240Sken{
242268240Sken	uint32_t length;
243268240Sken	int check_type_mask = 0;
244268240Sken
245268240Sken	length = scsi_2btoul(cap->length);
246268240Sken	length = MIN(length, valid_len);
247268240Sken
248268240Sken	if (length < __offsetof(struct scsi_per_res_cap, type_mask)) {
249268240Sken		fprintf(stdout, "Insufficient data (%u bytes) to report "
250268240Sken			"full capabilities\n", length);
251268240Sken		return;
252268240Sken	}
253268240Sken	if (length >= __offsetof(struct scsi_per_res_cap, reserved))
254268240Sken		check_type_mask = 1;
255268240Sken
256268240Sken	fprintf(stdout, "Replace Lost Reservation Capable (RLR_C): %d\n",
257268240Sken		(cap->flags1 & SPRI_RLR_C) ? 1 : 0);
258268240Sken	fprintf(stdout, "Compatible Reservation Handling (CRH): %d\n",
259268240Sken		(cap->flags1 & SPRI_CRH) ? 1 : 0);
260268240Sken	fprintf(stdout, "Specify Initiator Ports Capable (SIP_C): %d\n",
261268240Sken		(cap->flags1 & SPRI_SIP_C) ? 1 : 0);
262268240Sken	fprintf(stdout, "All Target Ports Capable (ATP_C): %d\n",
263268240Sken		(cap->flags1 & SPRI_ATP_C) ? 1 : 0);
264268240Sken	fprintf(stdout, "Persist Through Power Loss Capable (PTPL_C): %d\n",
265268240Sken		(cap->flags1 & SPRI_PTPL_C) ? 1 : 0);
266268240Sken	fprintf(stdout, "ALLOW COMMANDS field: (%#x)\n",
267268240Sken		(cap->flags2 & SPRI_ALLOW_CMD_MASK) >> SPRI_ALLOW_CMD_SHIFT);
268268240Sken	/*
269268240Sken	 * These cases are cut-and-pasted from SPC4r36l.  There is no
270268240Sken	 * succinct way to describe these otherwise, and even with the
271268240Sken	 * verbose description, the user will probably have to refer to
272268240Sken	 * the spec to fully understand what is going on.
273268240Sken	 */
274268240Sken	switch (cap->flags2 & SPRI_ALLOW_CMD_MASK) {
275268240Sken	case SPRI_ALLOW_1:
276268240Sken		fprintf(stdout,
277268240Sken"    The device server allows the TEST UNIT READY command through Write\n"
278268240Sken"    Exclusive type reservations and Exclusive Access type reservations\n"
279268240Sken"    and does not provide information about whether the following commands\n"
280268240Sken"    are allowed through Write Exclusive type reservations:\n"
281268240Sken"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
282268240Sken"           command, RECEIVE COPY RESULTS command, RECEIVE DIAGNOSTIC\n"
283268240Sken"           RESULTS command, REPORT SUPPORTED OPERATION CODES command,\n"
284268240Sken"           and REPORT SUPPORTED TASK MANAGEMENT FUNCTION command; and\n"
285268240Sken"        b) the READ DEFECT DATA command (see SBC-3).\n");
286268240Sken		break;
287268240Sken	case SPRI_ALLOW_2:
288268240Sken		fprintf(stdout,
289268240Sken"    The device server allows the TEST UNIT READY command through Write\n"
290268240Sken"    Exclusive type reservations and Exclusive Access type reservations\n"
291268240Sken"    and does not allow the following commands through Write Exclusive type\n"
292268240Sken"    reservations:\n"
293268240Sken"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
294268240Sken"           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
295268240Sken"           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
296268240Sken"           FUNCTION command; and\n"
297268240Sken"        b) the READ DEFECT DATA command.\n"
298268240Sken"    The device server does not allow the RECEIVE COPY RESULTS command\n"
299268240Sken"    through Write Exclusive type reservations or Exclusive Access type\n"
300268240Sken"    reservations.\n");
301268240Sken		break;
302268240Sken	case SPRI_ALLOW_3:
303268240Sken		fprintf(stdout,
304268240Sken"    The device server allows the TEST UNIT READY command through Write\n"
305268240Sken"    Exclusive type reservations and Exclusive Access type reservations\n"
306268240Sken"    and allows the following commands through Write Exclusive type\n"
307268240Sken"    reservations:\n"
308268240Sken"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
309268240Sken"           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
310268240Sken"           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
311268240Sken"           FUNCTION command; and\n"
312268240Sken"        b) the READ DEFECT DATA command.\n"
313268240Sken"    The device server does not allow the RECEIVE COPY RESULTS command\n"
314268240Sken"    through Write Exclusive type reservations or Exclusive Access type\n"
315268240Sken"    reservations.\n");
316268240Sken		break;
317268240Sken	case SPRI_ALLOW_4:
318268240Sken		fprintf(stdout,
319268240Sken"    The device server allows the TEST UNIT READY command and the RECEIVE\n"
320268240Sken"    COPY RESULTS command through Write Exclusive type reservations and\n"
321268240Sken"    Exclusive Access type reservations and allows the following commands\n"
322268240Sken"    through Write Exclusive type reservations:\n"
323268240Sken"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
324268240Sken"           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
325268240Sken"           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
326268240Sken"           FUNCTION command; and\n"
327268240Sken"        b) the READ DEFECT DATA command.\n");
328268240Sken		break;
329268240Sken	case SPRI_ALLOW_NA:
330268240Sken		fprintf(stdout,
331268240Sken"    No information is provided about whether certain commands are allowed\n"
332268240Sken"    through certain types of persistent reservations.\n");
333268240Sken		break;
334268240Sken	default:
335268240Sken		fprintf(stdout,
336268240Sken"    Unknown ALLOW COMMANDS value %#x\n",
337268240Sken			(cap->flags2 & SPRI_ALLOW_CMD_MASK) >>
338268240Sken			SPRI_ALLOW_CMD_SHIFT);
339268240Sken		break;
340268240Sken	}
341268240Sken	fprintf(stdout, "Persist Through Power Loss Activated (PTPL_A): %d\n",
342268240Sken		(cap->flags2 & SPRI_PTPL_A) ? 1 : 0);
343268240Sken	if ((check_type_mask != 0)
344268240Sken	 && (cap->flags2 & SPRI_TMV)) {
345268240Sken		fprintf(stdout, "Supported Persistent Reservation Types:\n");
346268240Sken		fprintf(stdout, "    Write Exclusive - All Registrants "
347268240Sken			"(WR_EX_AR): %d\n",
348268240Sken			(cap->type_mask[0] & SPRI_TM_WR_EX_AR)? 1 : 0);
349268240Sken		fprintf(stdout, "    Exclusive Access - Registrants Only "
350268240Sken			"(EX_AC_RO): %d\n",
351268240Sken			(cap->type_mask[0] & SPRI_TM_EX_AC_RO) ? 1 : 0);
352268240Sken		fprintf(stdout, "    Write Exclusive - Registrants Only "
353268240Sken			"(WR_EX_RO): %d\n",
354268240Sken			(cap->type_mask[0] & SPRI_TM_WR_EX_RO)? 1 : 0);
355268240Sken		fprintf(stdout, "    Exclusive Access (EX_AC): %d\n",
356268240Sken			(cap->type_mask[0] & SPRI_TM_EX_AC) ? 1 : 0);
357268240Sken		fprintf(stdout, "    Write Exclusive (WR_EX): %d\n",
358268240Sken			(cap->type_mask[0] & SPRI_TM_WR_EX) ? 1 : 0);
359268240Sken		fprintf(stdout, "    Exclusive Access - All Registrants "
360268240Sken			"(EX_AC_AR): %d\n",
361268240Sken			(cap->type_mask[1] & SPRI_TM_EX_AC_AR) ? 1 : 0);
362268240Sken	} else {
363268240Sken		fprintf(stdout, "Persistent Reservation Type Mask is NOT "
364268240Sken			"valid\n");
365268240Sken	}
366268240Sken
367268240Sken
368268240Sken}
369268240Sken
370268240Skenstatic void
371268240Skenpersist_print_full(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
372268240Sken{
373268240Sken	uint32_t length, len_to_go = 0;
374268240Sken	struct scsi_per_res_in_full_desc *desc;
375268240Sken	uint8_t *cur_pos;
376268240Sken	int i;
377268240Sken
378268240Sken	length = scsi_4btoul(hdr->length);
379268240Sken	length = MIN(length, valid_len);
380268240Sken
381268240Sken	if (length < sizeof(*desc)) {
382268240Sken		if (length == 0)
383268240Sken			fprintf(stdout, "No reservations.\n");
384268240Sken		else
385268240Sken			warnx("unable to print reservation, only got %u "
386268240Sken			      "valid bytes", length);
387268240Sken		return;
388268240Sken	}
389268240Sken
390268240Sken	fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation));
391268240Sken	cur_pos = (uint8_t *)&hdr[1];
392268240Sken	for (len_to_go = length, i = 0,
393268240Sken	     desc = (struct scsi_per_res_in_full_desc *)cur_pos;
394268240Sken	     len_to_go >= sizeof(*desc);
395268240Sken	     desc = (struct scsi_per_res_in_full_desc *)cur_pos, i++) {
396268240Sken		uint32_t additional_length, cur_length;
397268240Sken
398268240Sken
399268240Sken		fprintf(stdout, "Reservation Key: %#jx\n",
400268240Sken			(uintmax_t)scsi_8btou64(desc->res_key.key));
401268240Sken		fprintf(stdout, "All Target Ports (ALL_TG_PT): %d\n",
402268240Sken			(desc->flags & SPRI_FULL_ALL_TG_PT) ? 1 : 0);
403268240Sken		fprintf(stdout, "Reservation Holder (R_HOLDER): %d\n",
404268240Sken			(desc->flags & SPRI_FULL_R_HOLDER) ? 1 : 0);
405268240Sken
406268240Sken		if (desc->flags & SPRI_FULL_R_HOLDER)
407268240Sken			persist_print_scopetype(desc->scopetype);
408268240Sken
409268240Sken		if ((desc->flags & SPRI_FULL_ALL_TG_PT) == 0)
410268240Sken			fprintf(stdout, "Relative Target Port ID: %#x\n",
411268240Sken				scsi_2btoul(desc->rel_trgt_port_id));
412268240Sken
413268240Sken		additional_length = scsi_4btoul(desc->additional_length);
414268240Sken
415268240Sken		persist_print_transportid(desc->transport_id,
416268240Sken					  additional_length);
417268240Sken
418268240Sken		cur_length = sizeof(*desc) + additional_length;
419268240Sken		len_to_go -= cur_length;
420268240Sken		cur_pos += cur_length;
421268240Sken	}
422268240Sken}
423268240Sken
424268240Skenint
425268240Skenscsipersist(struct cam_device *device, int argc, char **argv, char *combinedopt,
426268240Sken	    int retry_count, int timeout, int verbosemode, int err_recover)
427268240Sken{
428268240Sken	union ccb *ccb = NULL;
429268240Sken	int c, in = 0, out = 0;
430268240Sken	int action = -1, num_ids = 0;
431268240Sken	int error = 0;
432268240Sken	uint32_t res_len = 0;
433268240Sken	unsigned long rel_tgt_port = 0;
434268240Sken	uint8_t *res_buf = NULL;
435268240Sken	int scope = SPR_LU_SCOPE, res_type = 0, key_set = 0, sa_key_set = 0;
436268240Sken	struct persist_transport_id *id, *id2;
437268240Sken	STAILQ_HEAD(, persist_transport_id) transport_id_list;
438268240Sken	uint64_t key = 0, sa_key = 0;
439268240Sken	struct scsi_nv *table = NULL;
440268240Sken	size_t table_size = 0, id_len = 0;
441268240Sken	uint32_t valid_len = 0;
442268240Sken	int all_tg_pt = 0, aptpl = 0, spec_i_pt = 0, unreg = 0,rel_port_set = 0;
443268240Sken
444268240Sken	STAILQ_INIT(&transport_id_list);
445268240Sken
446268240Sken	ccb = cam_getccb(device);
447268240Sken	if (ccb == NULL) {
448268240Sken		warnx("%s: error allocating CCB", __func__);
449268240Sken		error = 1;
450268240Sken		goto bailout;
451268240Sken	}
452268240Sken
453268240Sken	bzero(&(&ccb->ccb_h)[1],
454268240Sken	      sizeof(union ccb) - sizeof(struct ccb_hdr));
455268240Sken
456268240Sken	while ((c = getopt(argc, argv, combinedopt)) != -1) {
457268240Sken		switch (c) {
458268240Sken		case 'a':
459268240Sken			all_tg_pt = 1;
460268240Sken			break;
461268240Sken		case 'I': {
462268240Sken			int error_str_len = 128;
463268240Sken			char error_str[error_str_len];
464268240Sken			char *id_str;
465268240Sken
466268240Sken			id = malloc(sizeof(*id));
467268240Sken			if (id == NULL) {
468268240Sken				warnx("%s: error allocating %zu bytes",
469268240Sken				    __func__, sizeof(*id));
470268240Sken				error = 1;
471268240Sken				goto bailout;
472268240Sken			}
473268240Sken			bzero(id, sizeof(*id));
474268240Sken
475268240Sken			id_str = strdup(optarg);
476268240Sken			if (id_str == NULL) {
477268240Sken				warnx("%s: error duplicating string %s",
478268240Sken				    __func__, optarg);
479268240Sken				free(id);
480268240Sken				error = 1;
481268240Sken				goto bailout;
482268240Sken			}
483268240Sken			error = scsi_parse_transportid(id_str, &id->hdr,
484268240Sken			    &id->alloc_len, error_str, error_str_len);
485268240Sken			if (error != 0) {
486268240Sken				warnx("%s", error_str);
487268240Sken				error = 1;
488268240Sken				free(id);
489268240Sken				free(id_str);
490268240Sken				goto bailout;
491268240Sken			}
492268240Sken			free(id_str);
493268240Sken
494268240Sken			STAILQ_INSERT_TAIL(&transport_id_list, id, links);
495268240Sken			num_ids++;
496268240Sken			id_len += id->alloc_len;
497268240Sken			break;
498268240Sken		}
499268240Sken		case 'k':
500268240Sken		case 'K': {
501268240Sken			char *endptr;
502268240Sken			uint64_t tmpval;
503268240Sken
504268240Sken			tmpval = strtoumax(optarg, &endptr, 0);
505268240Sken			if (*endptr != '\0') {
506268240Sken				warnx("%s: invalid key argument %s", __func__,
507268240Sken				    optarg);
508268240Sken				error = 1;
509268240Sken				goto bailout;
510268240Sken			}
511268240Sken			if (c == 'k') {
512268240Sken				key = tmpval;
513268240Sken				key_set = 1;
514268240Sken			} else {
515268240Sken				sa_key = tmpval;
516268240Sken				sa_key_set = 1;
517268240Sken			}
518268240Sken			break;
519268240Sken		}
520268240Sken		case 'i':
521268240Sken		case 'o': {
522268240Sken			scsi_nv_status status;
523268240Sken			int table_entry = 0;
524268240Sken
525268240Sken			if (c == 'i') {
526268240Sken				in = 1;
527268240Sken				table = persist_in_actions;
528268240Sken				table_size = sizeof(persist_in_actions) /
529268240Sken					sizeof(persist_in_actions[0]);
530268240Sken			} else {
531268240Sken				out = 1;
532268240Sken				table = persist_out_actions;
533268240Sken				table_size = sizeof(persist_out_actions) /
534268240Sken					sizeof(persist_out_actions[0]);
535268240Sken			}
536268240Sken
537268240Sken			if ((in + out) > 1) {
538268240Sken				warnx("%s: only one in (-i) or out (-o) "
539268240Sken				    "action is allowed", __func__);
540268240Sken				error = 1;
541268240Sken				goto bailout;
542268240Sken			}
543268240Sken
544268240Sken			status = scsi_get_nv(table, table_size, optarg,
545268240Sken					     &table_entry,SCSI_NV_FLAG_IG_CASE);
546268240Sken			if (status == SCSI_NV_FOUND)
547268240Sken				action = table[table_entry].value;
548268240Sken			else {
549268240Sken				warnx("%s: %s %s option %s", __func__,
550268240Sken				    (status == SCSI_NV_AMBIGUOUS) ?
551268240Sken				    "ambiguous" : "invalid", in ? "in" :
552268240Sken				    "out", optarg);
553268240Sken				error = 1;
554268240Sken				goto bailout;
555268240Sken			}
556268240Sken			break;
557268240Sken		}
558268240Sken		case 'p':
559268240Sken			aptpl = 1;
560268240Sken			break;
561268240Sken		case 'R': {
562268240Sken			char *endptr;
563268240Sken
564268240Sken			rel_tgt_port = strtoul(optarg, &endptr, 0);
565268240Sken			if (*endptr != '\0') {
566268240Sken				warnx("%s: invalid relative target port %s",
567268240Sken				    __func__, optarg);
568268240Sken				error = 1;
569268240Sken				goto bailout;
570268240Sken			}
571268240Sken			rel_port_set = 1;
572268240Sken			break;
573268240Sken		}
574268240Sken		case 's': {
575268240Sken			size_t scope_size;
576268240Sken			struct scsi_nv *scope_table = NULL;
577268240Sken			scsi_nv_status status;
578268240Sken			int table_entry = 0;
579268240Sken			char *endptr;
580268240Sken
581268240Sken			/*
582268240Sken			 * First check to see if the user gave us a numeric
583268240Sken			 * argument.  If so, we'll try using it.
584268240Sken			 */
585268240Sken			if (isdigit(optarg[0])) {
586268240Sken				scope = strtol(optarg, &endptr, 0);
587268240Sken				if (*endptr != '\0') {
588268240Sken					warnx("%s: invalid scope %s",
589268240Sken					       __func__, optarg);
590268240Sken					error = 1;
591268240Sken					goto bailout;
592268240Sken				}
593268240Sken				scope = (scope << SPR_SCOPE_SHIFT) &
594268240Sken				    SPR_SCOPE_MASK;
595268240Sken				break;
596268240Sken			}
597268240Sken
598268240Sken			scope_size = sizeof(persist_scope_table) /
599268240Sken				     sizeof(persist_scope_table[0]);
600268240Sken			scope_table = persist_scope_table;
601268240Sken			status = scsi_get_nv(scope_table, scope_size, optarg,
602268240Sken					     &table_entry,SCSI_NV_FLAG_IG_CASE);
603268240Sken			if (status == SCSI_NV_FOUND)
604268240Sken				scope = scope_table[table_entry].value;
605268240Sken			else {
606268240Sken				warnx("%s: %s scope %s", __func__,
607268240Sken				      (status == SCSI_NV_AMBIGUOUS) ?
608268240Sken				      "ambiguous" : "invalid", optarg);
609268240Sken				error = 1;
610268240Sken				goto bailout;
611268240Sken			}
612268240Sken			break;
613268240Sken		}
614268240Sken		case 'S':
615268240Sken			spec_i_pt = 1;
616268240Sken			break;
617268240Sken		case 'T': {
618268240Sken			size_t res_type_size;
619268240Sken			struct scsi_nv *rtype_table = NULL;
620268240Sken			scsi_nv_status status;
621268240Sken			char *endptr;
622268240Sken			int table_entry = 0;
623268240Sken
624268240Sken			/*
625268240Sken			 * First check to see if the user gave us a numeric
626268240Sken			 * argument.  If so, we'll try using it.
627268240Sken			 */
628268240Sken			if (isdigit(optarg[0])) {
629268240Sken				res_type = strtol(optarg, &endptr, 0);
630268240Sken				if (*endptr != '\0') {
631268240Sken					warnx("%s: invalid reservation type %s",
632268240Sken					       __func__, optarg);
633268240Sken					error = 1;
634268240Sken					goto bailout;
635268240Sken				}
636268240Sken				break;
637268240Sken			}
638268240Sken
639268240Sken			res_type_size = sizeof(persist_type_table) /
640268240Sken					sizeof(persist_type_table[0]);
641268240Sken			rtype_table = persist_type_table;
642268240Sken			status = scsi_get_nv(rtype_table, res_type_size,
643268240Sken					     optarg, &table_entry,
644268240Sken					     SCSI_NV_FLAG_IG_CASE);
645268240Sken			if (status == SCSI_NV_FOUND)
646268240Sken				res_type = rtype_table[table_entry].value;
647268240Sken			else {
648268240Sken				warnx("%s: %s reservation type %s", __func__,
649268240Sken				      (status == SCSI_NV_AMBIGUOUS) ?
650268240Sken				      "ambiguous" : "invalid", optarg);
651268240Sken				error = 1;
652268240Sken				goto bailout;
653268240Sken			}
654268240Sken			break;
655268240Sken		}
656268240Sken		case 'U':
657268240Sken			unreg = 1;
658268240Sken			break;
659268240Sken		default:
660268240Sken			break;
661268240Sken		}
662268240Sken	}
663268240Sken
664268240Sken	if ((in + out) != 1) {
665268240Sken		warnx("%s: you must specify one of -i or -o", __func__);
666268240Sken		error = 1;
667268240Sken		goto bailout;
668268240Sken	}
669268240Sken
670268240Sken	/*
671268240Sken	 * Note that we don't really try to figure out whether the user
672268240Sken	 * needs to specify one or both keys.  There are a number of
673268240Sken	 * scenarios, and sometimes 0 is a valid and desired value.
674268240Sken	 */
675268240Sken	if (in != 0) {
676268240Sken		switch (action) {
677268240Sken		case SPRI_RK:
678268240Sken		case SPRI_RR:
679268240Sken		case SPRI_RS:
680268240Sken			/*
681268240Sken			 * Allocate the maximum length possible for these
682268240Sken			 * service actions.  According to the spec, the
683268240Sken			 * target is supposed to return the available
684268240Sken			 * length in the header, regardless of the
685268240Sken			 * allocation length.  In practice, though, with
686268240Sken			 * the READ FULL STATUS (SPRI_RS) service action,
687268240Sken			 * some Seagate drives (in particular a
688268240Sken			 * Constellation ES, <SEAGATE ST32000444SS 0006>)
689268240Sken			 * don't return the available length if you only
690268240Sken			 * allocate the length of the header.  So just
691268240Sken			 * allocate the maximum here so we don't miss
692268240Sken			 * anything.
693268240Sken			 */
694268240Sken			res_len = SPRI_MAX_LEN;
695268240Sken			break;
696268240Sken		case SPRI_RC:
697268240Sken			res_len = sizeof(struct scsi_per_res_cap);
698268240Sken			break;
699268240Sken		default:
700268240Sken			/* In theory we should catch this above */
701268240Sken			warnx("%s: invalid action %d", __func__, action);
702268240Sken			error = 1;
703268240Sken			goto bailout;
704268240Sken			break;
705268240Sken		}
706268240Sken	} else {
707268240Sken
708268240Sken		/*
709268240Sken		 * XXX KDM need to add length for transport IDs for the
710268240Sken		 * register and move service action and the register
711268240Sken		 * service action with the SPEC_I_PT bit set.
712268240Sken		 */
713268240Sken		if (action == SPRO_REG_MOVE) {
714268240Sken			if (num_ids != 1) {
715268240Sken			    	warnx("%s: register and move requires a "
716268240Sken				    "single transport ID (-I)", __func__);
717268240Sken				error = 1;
718268240Sken				goto bailout;
719268240Sken			}
720268240Sken			if (rel_port_set == 0) {
721268240Sken				warnx("%s: register and move requires a "
722268240Sken				    "relative target port (-R)", __func__);
723268240Sken				error = 1;
724268240Sken				goto bailout;
725268240Sken			}
726268240Sken			res_len = sizeof(struct scsi_per_res_reg_move) + id_len;
727268240Sken		} else {
728268240Sken			res_len = sizeof(struct scsi_per_res_out_parms);
729268240Sken			if ((action == SPRO_REGISTER)
730268240Sken			 && (num_ids != 0)) {
731268240Sken				/*
732268240Sken				 * If the user specifies any IDs with the
733268240Sken				 * register service action, turn on the
734268240Sken				 * spec_i_pt bit.
735268240Sken				 */
736268240Sken				spec_i_pt = 1;
737268240Sken				res_len += id_len;
738268240Sken				res_len +=
739268240Sken				    sizeof(struct scsi_per_res_out_trans_ids);
740268240Sken			}
741268240Sken		}
742268240Sken	}
743268240Skenretry:
744268240Sken	if (res_buf != NULL) {
745268240Sken		free(res_buf);
746268240Sken		res_buf = NULL;
747268240Sken	}
748268240Sken	res_buf = malloc(res_len);
749268240Sken	if (res_buf == NULL) {
750268240Sken		warn("%s: error allocating %d bytes", __func__, res_len);
751268240Sken		error = 1;
752268240Sken		goto bailout;
753268240Sken	}
754268240Sken	bzero(res_buf, res_len);
755268240Sken
756268240Sken	if (in != 0) {
757268240Sken		scsi_persistent_reserve_in(&ccb->csio,
758268240Sken					   /*retries*/ retry_count,
759268240Sken					   /*cbfcnp*/ NULL,
760268240Sken					   /*tag_action*/ MSG_SIMPLE_Q_TAG,
761268240Sken					   /*service_action*/ action,
762268240Sken					   /*data_ptr*/ res_buf,
763268240Sken					   /*dxfer_len*/ res_len,
764268240Sken					   /*sense_len*/ SSD_FULL_SIZE,
765268240Sken					   /*timeout*/ timeout ? timeout :5000);
766268240Sken
767268240Sken	} else {
768268240Sken		switch (action) {
769268240Sken		case SPRO_REGISTER:
770268240Sken			if (spec_i_pt != 0) {
771268240Sken				struct scsi_per_res_out_trans_ids *id_hdr;
772268240Sken				uint8_t *bufptr;
773268240Sken
774268240Sken				bufptr = res_buf +
775268240Sken				    sizeof(struct scsi_per_res_out_parms) +
776268240Sken				    sizeof(struct scsi_per_res_out_trans_ids);
777268240Sken				STAILQ_FOREACH(id, &transport_id_list, links) {
778268240Sken					bcopy(id->hdr, bufptr, id->alloc_len);
779268240Sken					bufptr += id->alloc_len;
780268240Sken				}
781268240Sken				id_hdr = (struct scsi_per_res_out_trans_ids *)
782268240Sken				    (res_buf +
783268240Sken				    sizeof(struct scsi_per_res_out_parms));
784268240Sken				scsi_ulto4b(id_len, id_hdr->additional_length);
785268240Sken			}
786268240Sken		case SPRO_REG_IGNO:
787268240Sken		case SPRO_PREEMPT:
788268240Sken		case SPRO_PRE_ABO:
789268240Sken		case SPRO_RESERVE:
790268240Sken		case SPRO_RELEASE:
791268240Sken		case SPRO_CLEAR:
792268240Sken		case SPRO_REPL_LOST_RES: {
793268240Sken			struct scsi_per_res_out_parms *parms;
794268240Sken
795268240Sken			parms = (struct scsi_per_res_out_parms *)res_buf;
796268240Sken
797268240Sken			scsi_u64to8b(key, parms->res_key.key);
798268240Sken			scsi_u64to8b(sa_key, parms->serv_act_res_key);
799268240Sken			if (spec_i_pt != 0)
800268240Sken				parms->flags |= SPR_SPEC_I_PT;
801268240Sken			if (all_tg_pt != 0)
802268240Sken				parms->flags |= SPR_ALL_TG_PT;
803268240Sken			if (aptpl != 0)
804268240Sken				parms->flags |= SPR_APTPL;
805268240Sken			break;
806268240Sken		}
807268240Sken		case SPRO_REG_MOVE: {
808268240Sken			struct scsi_per_res_reg_move *reg_move;
809268240Sken			uint8_t *bufptr;
810268240Sken
811268240Sken			reg_move = (struct scsi_per_res_reg_move *)res_buf;
812268240Sken
813268240Sken			scsi_u64to8b(key, reg_move->res_key.key);
814268240Sken			scsi_u64to8b(sa_key, reg_move->serv_act_res_key);
815268240Sken			if (unreg != 0)
816268240Sken				reg_move->flags |= SPR_REG_MOVE_UNREG;
817268240Sken			if (aptpl != 0)
818268240Sken				reg_move->flags |= SPR_REG_MOVE_APTPL;
819268240Sken			scsi_ulto2b(rel_tgt_port, reg_move->rel_trgt_port_id);
820268240Sken			id = STAILQ_FIRST(&transport_id_list);
821268240Sken			/*
822268240Sken			 * This shouldn't happen, since we already checked
823268240Sken			 * the number of IDs above.
824268240Sken			 */
825268240Sken			if (id == NULL) {
826268240Sken				warnx("%s: No transport IDs found!", __func__);
827268240Sken				error = 1;
828268240Sken				goto bailout;
829268240Sken			}
830268240Sken			bufptr = (uint8_t *)&reg_move[1];
831268240Sken			bcopy(id->hdr, bufptr, id->alloc_len);
832268240Sken			scsi_ulto4b(id->alloc_len,
833268240Sken			    reg_move->transport_id_length);
834268240Sken			break;
835268240Sken		}
836268240Sken		default:
837268240Sken			break;
838268240Sken		}
839268240Sken		scsi_persistent_reserve_out(&ccb->csio,
840268240Sken					    /*retries*/ retry_count,
841268240Sken					    /*cbfcnp*/ NULL,
842268240Sken					    /*tag_action*/ MSG_SIMPLE_Q_TAG,
843268240Sken					    /*service_action*/ action,
844268240Sken					    /*scope*/ scope,
845268240Sken					    /*res_type*/ res_type,
846268240Sken					    /*data_ptr*/ res_buf,
847268240Sken					    /*dxfer_len*/ res_len,
848268240Sken					    /*sense_len*/ SSD_FULL_SIZE,
849268240Sken					    /*timeout*/ timeout ?timeout :5000);
850268240Sken	}
851268240Sken
852268240Sken	/* Disable freezing the device queue */
853268240Sken	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
854268240Sken
855268240Sken	if (err_recover != 0)
856268240Sken		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
857268240Sken
858268240Sken	if (cam_send_ccb(device, ccb) < 0) {
859268240Sken		warn("error sending PERSISTENT RESERVE %s", (in != 0) ?
860268240Sken		    "IN" : "OUT");
861268240Sken
862268240Sken		if (verbosemode != 0) {
863268240Sken			cam_error_print(device, ccb, CAM_ESF_ALL,
864268240Sken					CAM_EPF_ALL, stderr);
865268240Sken		}
866268240Sken
867268240Sken		error = 1;
868268240Sken		goto bailout;
869268240Sken	}
870268240Sken
871268240Sken	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
872268240Sken		if (verbosemode != 0) {
873268240Sken			cam_error_print(device, ccb, CAM_ESF_ALL,
874268240Sken					CAM_EPF_ALL, stderr);
875268240Sken		}
876268240Sken		error = 1;
877268240Sken		goto bailout;
878268240Sken	}
879268240Sken
880268240Sken	if (in == 0)
881268240Sken		goto bailout;
882268240Sken
883268240Sken	valid_len = res_len - ccb->csio.resid;
884268240Sken
885268240Sken	switch (action) {
886268240Sken	case SPRI_RK:
887268240Sken	case SPRI_RR:
888268240Sken	case SPRI_RS: {
889268240Sken		struct scsi_per_res_in_header *hdr;
890268240Sken		uint32_t hdr_len;
891268240Sken
892268240Sken		if (valid_len < sizeof(*hdr)) {
893268240Sken			warnx("%s: only got %d valid bytes, need %zd",
894268240Sken			      __func__, valid_len, sizeof(*hdr));
895268240Sken			error = 1;
896268240Sken			goto bailout;
897268240Sken		}
898268240Sken		hdr = (struct scsi_per_res_in_header *)res_buf;
899268240Sken		hdr_len = scsi_4btoul(hdr->length);
900268240Sken
901268240Sken		if (hdr_len > (res_len - sizeof(*hdr))) {
902268240Sken			res_len = hdr_len + sizeof(*hdr);
903268240Sken			goto retry;
904268240Sken		}
905268240Sken
906268240Sken		if (action == SPRI_RK) {
907268240Sken			persist_print_keys(hdr, valid_len);
908268240Sken		} else if (action == SPRI_RR) {
909268240Sken			persist_print_res(hdr, valid_len);
910268240Sken		} else {
911268240Sken			persist_print_full(hdr, valid_len);
912268240Sken		}
913268240Sken		break;
914268240Sken	}
915268240Sken	case SPRI_RC: {
916268240Sken		struct scsi_per_res_cap *cap;
917268240Sken		uint32_t cap_len;
918268240Sken
919268240Sken		if (valid_len < sizeof(*cap)) {
920268240Sken			warnx("%s: only got %u valid bytes, need %zd",
921268240Sken			      __func__, valid_len, sizeof(*cap));
922268240Sken			error = 1;
923268240Sken			goto bailout;
924268240Sken		}
925268240Sken		cap = (struct scsi_per_res_cap *)res_buf;
926268240Sken		cap_len = scsi_2btoul(cap->length);
927268240Sken		if (cap_len != sizeof(*cap)) {
928268240Sken			/*
929268240Sken			 * We should be able to deal with this,
930268240Sken			 * it's just more trouble.
931268240Sken			 */
932268240Sken			warnx("%s: reported size %u is different "
933268240Sken			    "than expected size %zd", __func__,
934268240Sken			    cap_len, sizeof(*cap));
935268240Sken		}
936268240Sken
937268240Sken		/*
938268240Sken		 * If there is more data available, grab it all,
939268240Sken		 * even though we don't really know what to do with
940268240Sken		 * the extra data since it obviously wasn't in the
941268240Sken		 * spec when this code was written.
942268240Sken		 */
943268240Sken		if (cap_len > res_len) {
944268240Sken			res_len = cap_len;
945268240Sken			goto retry;
946268240Sken		}
947268240Sken		persist_print_cap(cap, valid_len);
948268240Sken		break;
949268240Sken	}
950268240Sken	default:
951268240Sken		break;
952268240Sken	}
953268240Sken
954268240Skenbailout:
955268240Sken	free(res_buf);
956268240Sken
957268240Sken	if (ccb != NULL)
958268240Sken		cam_freeccb(ccb);
959268240Sken
960268240Sken	STAILQ_FOREACH_SAFE(id, &transport_id_list, links, id2) {
961268240Sken		STAILQ_REMOVE(&transport_id_list, id, persist_transport_id,
962268240Sken		    links);
963268240Sken		free(id);
964268240Sken	}
965268240Sken	return (error);
966268240Sken}
967