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: releng/11.0/sbin/camcontrol/persist.c 300547 2016-05-24 00:57:11Z truckman $");
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;
435283907Saraujo	int scope = SPR_LU_SCOPE, res_type = 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
453300547Struckman	CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
454268240Sken
455268240Sken	while ((c = getopt(argc, argv, combinedopt)) != -1) {
456268240Sken		switch (c) {
457268240Sken		case 'a':
458268240Sken			all_tg_pt = 1;
459268240Sken			break;
460268240Sken		case 'I': {
461268240Sken			int error_str_len = 128;
462268240Sken			char error_str[error_str_len];
463268240Sken			char *id_str;
464268240Sken
465268240Sken			id = malloc(sizeof(*id));
466268240Sken			if (id == NULL) {
467268240Sken				warnx("%s: error allocating %zu bytes",
468268240Sken				    __func__, sizeof(*id));
469268240Sken				error = 1;
470268240Sken				goto bailout;
471268240Sken			}
472268240Sken			bzero(id, sizeof(*id));
473268240Sken
474268240Sken			id_str = strdup(optarg);
475268240Sken			if (id_str == NULL) {
476268240Sken				warnx("%s: error duplicating string %s",
477268240Sken				    __func__, optarg);
478268240Sken				free(id);
479268240Sken				error = 1;
480268240Sken				goto bailout;
481268240Sken			}
482268240Sken			error = scsi_parse_transportid(id_str, &id->hdr,
483268240Sken			    &id->alloc_len, error_str, error_str_len);
484268240Sken			if (error != 0) {
485268240Sken				warnx("%s", error_str);
486268240Sken				error = 1;
487268240Sken				free(id);
488268240Sken				free(id_str);
489268240Sken				goto bailout;
490268240Sken			}
491268240Sken			free(id_str);
492268240Sken
493268240Sken			STAILQ_INSERT_TAIL(&transport_id_list, id, links);
494268240Sken			num_ids++;
495268240Sken			id_len += id->alloc_len;
496268240Sken			break;
497268240Sken		}
498268240Sken		case 'k':
499268240Sken		case 'K': {
500268240Sken			char *endptr;
501268240Sken			uint64_t tmpval;
502268240Sken
503268240Sken			tmpval = strtoumax(optarg, &endptr, 0);
504268240Sken			if (*endptr != '\0') {
505268240Sken				warnx("%s: invalid key argument %s", __func__,
506268240Sken				    optarg);
507268240Sken				error = 1;
508268240Sken				goto bailout;
509268240Sken			}
510268240Sken			if (c == 'k') {
511268240Sken				key = tmpval;
512268240Sken			} else {
513268240Sken				sa_key = tmpval;
514268240Sken			}
515268240Sken			break;
516268240Sken		}
517268240Sken		case 'i':
518268240Sken		case 'o': {
519268240Sken			scsi_nv_status status;
520268240Sken			int table_entry = 0;
521268240Sken
522268240Sken			if (c == 'i') {
523268240Sken				in = 1;
524268240Sken				table = persist_in_actions;
525268240Sken				table_size = sizeof(persist_in_actions) /
526268240Sken					sizeof(persist_in_actions[0]);
527268240Sken			} else {
528268240Sken				out = 1;
529268240Sken				table = persist_out_actions;
530268240Sken				table_size = sizeof(persist_out_actions) /
531268240Sken					sizeof(persist_out_actions[0]);
532268240Sken			}
533268240Sken
534268240Sken			if ((in + out) > 1) {
535268240Sken				warnx("%s: only one in (-i) or out (-o) "
536268240Sken				    "action is allowed", __func__);
537268240Sken				error = 1;
538268240Sken				goto bailout;
539268240Sken			}
540268240Sken
541268240Sken			status = scsi_get_nv(table, table_size, optarg,
542268240Sken					     &table_entry,SCSI_NV_FLAG_IG_CASE);
543268240Sken			if (status == SCSI_NV_FOUND)
544268240Sken				action = table[table_entry].value;
545268240Sken			else {
546268240Sken				warnx("%s: %s %s option %s", __func__,
547268240Sken				    (status == SCSI_NV_AMBIGUOUS) ?
548268240Sken				    "ambiguous" : "invalid", in ? "in" :
549268240Sken				    "out", optarg);
550268240Sken				error = 1;
551268240Sken				goto bailout;
552268240Sken			}
553268240Sken			break;
554268240Sken		}
555268240Sken		case 'p':
556268240Sken			aptpl = 1;
557268240Sken			break;
558268240Sken		case 'R': {
559268240Sken			char *endptr;
560268240Sken
561268240Sken			rel_tgt_port = strtoul(optarg, &endptr, 0);
562268240Sken			if (*endptr != '\0') {
563268240Sken				warnx("%s: invalid relative target port %s",
564268240Sken				    __func__, optarg);
565268240Sken				error = 1;
566268240Sken				goto bailout;
567268240Sken			}
568268240Sken			rel_port_set = 1;
569268240Sken			break;
570268240Sken		}
571268240Sken		case 's': {
572268240Sken			size_t scope_size;
573268240Sken			struct scsi_nv *scope_table = NULL;
574268240Sken			scsi_nv_status status;
575268240Sken			int table_entry = 0;
576268240Sken			char *endptr;
577268240Sken
578268240Sken			/*
579268240Sken			 * First check to see if the user gave us a numeric
580268240Sken			 * argument.  If so, we'll try using it.
581268240Sken			 */
582268240Sken			if (isdigit(optarg[0])) {
583268240Sken				scope = strtol(optarg, &endptr, 0);
584268240Sken				if (*endptr != '\0') {
585268240Sken					warnx("%s: invalid scope %s",
586268240Sken					       __func__, optarg);
587268240Sken					error = 1;
588268240Sken					goto bailout;
589268240Sken				}
590268240Sken				scope = (scope << SPR_SCOPE_SHIFT) &
591268240Sken				    SPR_SCOPE_MASK;
592268240Sken				break;
593268240Sken			}
594268240Sken
595268240Sken			scope_size = sizeof(persist_scope_table) /
596268240Sken				     sizeof(persist_scope_table[0]);
597268240Sken			scope_table = persist_scope_table;
598268240Sken			status = scsi_get_nv(scope_table, scope_size, optarg,
599268240Sken					     &table_entry,SCSI_NV_FLAG_IG_CASE);
600268240Sken			if (status == SCSI_NV_FOUND)
601268240Sken				scope = scope_table[table_entry].value;
602268240Sken			else {
603268240Sken				warnx("%s: %s scope %s", __func__,
604268240Sken				      (status == SCSI_NV_AMBIGUOUS) ?
605268240Sken				      "ambiguous" : "invalid", optarg);
606268240Sken				error = 1;
607268240Sken				goto bailout;
608268240Sken			}
609268240Sken			break;
610268240Sken		}
611268240Sken		case 'S':
612268240Sken			spec_i_pt = 1;
613268240Sken			break;
614268240Sken		case 'T': {
615268240Sken			size_t res_type_size;
616268240Sken			struct scsi_nv *rtype_table = NULL;
617268240Sken			scsi_nv_status status;
618268240Sken			char *endptr;
619268240Sken			int table_entry = 0;
620268240Sken
621268240Sken			/*
622268240Sken			 * First check to see if the user gave us a numeric
623268240Sken			 * argument.  If so, we'll try using it.
624268240Sken			 */
625268240Sken			if (isdigit(optarg[0])) {
626268240Sken				res_type = strtol(optarg, &endptr, 0);
627268240Sken				if (*endptr != '\0') {
628268240Sken					warnx("%s: invalid reservation type %s",
629268240Sken					       __func__, optarg);
630268240Sken					error = 1;
631268240Sken					goto bailout;
632268240Sken				}
633268240Sken				break;
634268240Sken			}
635268240Sken
636268240Sken			res_type_size = sizeof(persist_type_table) /
637268240Sken					sizeof(persist_type_table[0]);
638268240Sken			rtype_table = persist_type_table;
639268240Sken			status = scsi_get_nv(rtype_table, res_type_size,
640268240Sken					     optarg, &table_entry,
641268240Sken					     SCSI_NV_FLAG_IG_CASE);
642268240Sken			if (status == SCSI_NV_FOUND)
643268240Sken				res_type = rtype_table[table_entry].value;
644268240Sken			else {
645268240Sken				warnx("%s: %s reservation type %s", __func__,
646268240Sken				      (status == SCSI_NV_AMBIGUOUS) ?
647268240Sken				      "ambiguous" : "invalid", optarg);
648268240Sken				error = 1;
649268240Sken				goto bailout;
650268240Sken			}
651268240Sken			break;
652268240Sken		}
653268240Sken		case 'U':
654268240Sken			unreg = 1;
655268240Sken			break;
656268240Sken		default:
657268240Sken			break;
658268240Sken		}
659268240Sken	}
660268240Sken
661268240Sken	if ((in + out) != 1) {
662268240Sken		warnx("%s: you must specify one of -i or -o", __func__);
663268240Sken		error = 1;
664268240Sken		goto bailout;
665268240Sken	}
666268240Sken
667268240Sken	/*
668268240Sken	 * Note that we don't really try to figure out whether the user
669268240Sken	 * needs to specify one or both keys.  There are a number of
670268240Sken	 * scenarios, and sometimes 0 is a valid and desired value.
671268240Sken	 */
672268240Sken	if (in != 0) {
673268240Sken		switch (action) {
674268240Sken		case SPRI_RK:
675268240Sken		case SPRI_RR:
676268240Sken		case SPRI_RS:
677268240Sken			/*
678268240Sken			 * Allocate the maximum length possible for these
679268240Sken			 * service actions.  According to the spec, the
680268240Sken			 * target is supposed to return the available
681268240Sken			 * length in the header, regardless of the
682268240Sken			 * allocation length.  In practice, though, with
683268240Sken			 * the READ FULL STATUS (SPRI_RS) service action,
684268240Sken			 * some Seagate drives (in particular a
685268240Sken			 * Constellation ES, <SEAGATE ST32000444SS 0006>)
686268240Sken			 * don't return the available length if you only
687268240Sken			 * allocate the length of the header.  So just
688268240Sken			 * allocate the maximum here so we don't miss
689268240Sken			 * anything.
690268240Sken			 */
691268240Sken			res_len = SPRI_MAX_LEN;
692268240Sken			break;
693268240Sken		case SPRI_RC:
694268240Sken			res_len = sizeof(struct scsi_per_res_cap);
695268240Sken			break;
696268240Sken		default:
697268240Sken			/* In theory we should catch this above */
698268240Sken			warnx("%s: invalid action %d", __func__, action);
699268240Sken			error = 1;
700268240Sken			goto bailout;
701268240Sken			break;
702268240Sken		}
703268240Sken	} else {
704268240Sken
705268240Sken		/*
706268240Sken		 * XXX KDM need to add length for transport IDs for the
707268240Sken		 * register and move service action and the register
708268240Sken		 * service action with the SPEC_I_PT bit set.
709268240Sken		 */
710268240Sken		if (action == SPRO_REG_MOVE) {
711268240Sken			if (num_ids != 1) {
712268240Sken			    	warnx("%s: register and move requires a "
713268240Sken				    "single transport ID (-I)", __func__);
714268240Sken				error = 1;
715268240Sken				goto bailout;
716268240Sken			}
717268240Sken			if (rel_port_set == 0) {
718268240Sken				warnx("%s: register and move requires a "
719268240Sken				    "relative target port (-R)", __func__);
720268240Sken				error = 1;
721268240Sken				goto bailout;
722268240Sken			}
723268240Sken			res_len = sizeof(struct scsi_per_res_reg_move) + id_len;
724268240Sken		} else {
725268240Sken			res_len = sizeof(struct scsi_per_res_out_parms);
726268240Sken			if ((action == SPRO_REGISTER)
727268240Sken			 && (num_ids != 0)) {
728268240Sken				/*
729268240Sken				 * If the user specifies any IDs with the
730268240Sken				 * register service action, turn on the
731268240Sken				 * spec_i_pt bit.
732268240Sken				 */
733268240Sken				spec_i_pt = 1;
734268240Sken				res_len += id_len;
735268240Sken				res_len +=
736268240Sken				    sizeof(struct scsi_per_res_out_trans_ids);
737268240Sken			}
738268240Sken		}
739268240Sken	}
740268240Skenretry:
741268240Sken	if (res_buf != NULL) {
742268240Sken		free(res_buf);
743268240Sken		res_buf = NULL;
744268240Sken	}
745268240Sken	res_buf = malloc(res_len);
746268240Sken	if (res_buf == NULL) {
747268240Sken		warn("%s: error allocating %d bytes", __func__, res_len);
748268240Sken		error = 1;
749268240Sken		goto bailout;
750268240Sken	}
751268240Sken	bzero(res_buf, res_len);
752268240Sken
753268240Sken	if (in != 0) {
754268240Sken		scsi_persistent_reserve_in(&ccb->csio,
755268240Sken					   /*retries*/ retry_count,
756268240Sken					   /*cbfcnp*/ NULL,
757268240Sken					   /*tag_action*/ MSG_SIMPLE_Q_TAG,
758268240Sken					   /*service_action*/ action,
759268240Sken					   /*data_ptr*/ res_buf,
760268240Sken					   /*dxfer_len*/ res_len,
761268240Sken					   /*sense_len*/ SSD_FULL_SIZE,
762268240Sken					   /*timeout*/ timeout ? timeout :5000);
763268240Sken
764268240Sken	} else {
765268240Sken		switch (action) {
766268240Sken		case SPRO_REGISTER:
767268240Sken			if (spec_i_pt != 0) {
768268240Sken				struct scsi_per_res_out_trans_ids *id_hdr;
769268240Sken				uint8_t *bufptr;
770268240Sken
771268240Sken				bufptr = res_buf +
772268240Sken				    sizeof(struct scsi_per_res_out_parms) +
773268240Sken				    sizeof(struct scsi_per_res_out_trans_ids);
774268240Sken				STAILQ_FOREACH(id, &transport_id_list, links) {
775268240Sken					bcopy(id->hdr, bufptr, id->alloc_len);
776268240Sken					bufptr += id->alloc_len;
777268240Sken				}
778268240Sken				id_hdr = (struct scsi_per_res_out_trans_ids *)
779268240Sken				    (res_buf +
780268240Sken				    sizeof(struct scsi_per_res_out_parms));
781268240Sken				scsi_ulto4b(id_len, id_hdr->additional_length);
782268240Sken			}
783268240Sken		case SPRO_REG_IGNO:
784268240Sken		case SPRO_PREEMPT:
785268240Sken		case SPRO_PRE_ABO:
786268240Sken		case SPRO_RESERVE:
787268240Sken		case SPRO_RELEASE:
788268240Sken		case SPRO_CLEAR:
789268240Sken		case SPRO_REPL_LOST_RES: {
790268240Sken			struct scsi_per_res_out_parms *parms;
791268240Sken
792268240Sken			parms = (struct scsi_per_res_out_parms *)res_buf;
793268240Sken
794268240Sken			scsi_u64to8b(key, parms->res_key.key);
795268240Sken			scsi_u64to8b(sa_key, parms->serv_act_res_key);
796268240Sken			if (spec_i_pt != 0)
797268240Sken				parms->flags |= SPR_SPEC_I_PT;
798268240Sken			if (all_tg_pt != 0)
799268240Sken				parms->flags |= SPR_ALL_TG_PT;
800268240Sken			if (aptpl != 0)
801268240Sken				parms->flags |= SPR_APTPL;
802268240Sken			break;
803268240Sken		}
804268240Sken		case SPRO_REG_MOVE: {
805268240Sken			struct scsi_per_res_reg_move *reg_move;
806268240Sken			uint8_t *bufptr;
807268240Sken
808268240Sken			reg_move = (struct scsi_per_res_reg_move *)res_buf;
809268240Sken
810268240Sken			scsi_u64to8b(key, reg_move->res_key.key);
811268240Sken			scsi_u64to8b(sa_key, reg_move->serv_act_res_key);
812268240Sken			if (unreg != 0)
813268240Sken				reg_move->flags |= SPR_REG_MOVE_UNREG;
814268240Sken			if (aptpl != 0)
815268240Sken				reg_move->flags |= SPR_REG_MOVE_APTPL;
816268240Sken			scsi_ulto2b(rel_tgt_port, reg_move->rel_trgt_port_id);
817268240Sken			id = STAILQ_FIRST(&transport_id_list);
818268240Sken			/*
819268240Sken			 * This shouldn't happen, since we already checked
820268240Sken			 * the number of IDs above.
821268240Sken			 */
822268240Sken			if (id == NULL) {
823268240Sken				warnx("%s: No transport IDs found!", __func__);
824268240Sken				error = 1;
825268240Sken				goto bailout;
826268240Sken			}
827268240Sken			bufptr = (uint8_t *)&reg_move[1];
828268240Sken			bcopy(id->hdr, bufptr, id->alloc_len);
829268240Sken			scsi_ulto4b(id->alloc_len,
830268240Sken			    reg_move->transport_id_length);
831268240Sken			break;
832268240Sken		}
833268240Sken		default:
834268240Sken			break;
835268240Sken		}
836268240Sken		scsi_persistent_reserve_out(&ccb->csio,
837268240Sken					    /*retries*/ retry_count,
838268240Sken					    /*cbfcnp*/ NULL,
839268240Sken					    /*tag_action*/ MSG_SIMPLE_Q_TAG,
840268240Sken					    /*service_action*/ action,
841268240Sken					    /*scope*/ scope,
842268240Sken					    /*res_type*/ res_type,
843268240Sken					    /*data_ptr*/ res_buf,
844268240Sken					    /*dxfer_len*/ res_len,
845268240Sken					    /*sense_len*/ SSD_FULL_SIZE,
846268240Sken					    /*timeout*/ timeout ?timeout :5000);
847268240Sken	}
848268240Sken
849268240Sken	/* Disable freezing the device queue */
850268240Sken	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
851268240Sken
852268240Sken	if (err_recover != 0)
853268240Sken		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
854268240Sken
855268240Sken	if (cam_send_ccb(device, ccb) < 0) {
856268240Sken		warn("error sending PERSISTENT RESERVE %s", (in != 0) ?
857268240Sken		    "IN" : "OUT");
858268240Sken
859268240Sken		if (verbosemode != 0) {
860268240Sken			cam_error_print(device, ccb, CAM_ESF_ALL,
861268240Sken					CAM_EPF_ALL, stderr);
862268240Sken		}
863268240Sken
864268240Sken		error = 1;
865268240Sken		goto bailout;
866268240Sken	}
867268240Sken
868268240Sken	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
869268240Sken		if (verbosemode != 0) {
870268240Sken			cam_error_print(device, ccb, CAM_ESF_ALL,
871268240Sken					CAM_EPF_ALL, stderr);
872268240Sken		}
873268240Sken		error = 1;
874268240Sken		goto bailout;
875268240Sken	}
876268240Sken
877268240Sken	if (in == 0)
878268240Sken		goto bailout;
879268240Sken
880268240Sken	valid_len = res_len - ccb->csio.resid;
881268240Sken
882268240Sken	switch (action) {
883268240Sken	case SPRI_RK:
884268240Sken	case SPRI_RR:
885268240Sken	case SPRI_RS: {
886268240Sken		struct scsi_per_res_in_header *hdr;
887268240Sken		uint32_t hdr_len;
888268240Sken
889268240Sken		if (valid_len < sizeof(*hdr)) {
890268240Sken			warnx("%s: only got %d valid bytes, need %zd",
891268240Sken			      __func__, valid_len, sizeof(*hdr));
892268240Sken			error = 1;
893268240Sken			goto bailout;
894268240Sken		}
895268240Sken		hdr = (struct scsi_per_res_in_header *)res_buf;
896268240Sken		hdr_len = scsi_4btoul(hdr->length);
897268240Sken
898268240Sken		if (hdr_len > (res_len - sizeof(*hdr))) {
899268240Sken			res_len = hdr_len + sizeof(*hdr);
900268240Sken			goto retry;
901268240Sken		}
902268240Sken
903268240Sken		if (action == SPRI_RK) {
904268240Sken			persist_print_keys(hdr, valid_len);
905268240Sken		} else if (action == SPRI_RR) {
906268240Sken			persist_print_res(hdr, valid_len);
907268240Sken		} else {
908268240Sken			persist_print_full(hdr, valid_len);
909268240Sken		}
910268240Sken		break;
911268240Sken	}
912268240Sken	case SPRI_RC: {
913268240Sken		struct scsi_per_res_cap *cap;
914268240Sken		uint32_t cap_len;
915268240Sken
916268240Sken		if (valid_len < sizeof(*cap)) {
917268240Sken			warnx("%s: only got %u valid bytes, need %zd",
918268240Sken			      __func__, valid_len, sizeof(*cap));
919268240Sken			error = 1;
920268240Sken			goto bailout;
921268240Sken		}
922268240Sken		cap = (struct scsi_per_res_cap *)res_buf;
923268240Sken		cap_len = scsi_2btoul(cap->length);
924268240Sken		if (cap_len != sizeof(*cap)) {
925268240Sken			/*
926268240Sken			 * We should be able to deal with this,
927268240Sken			 * it's just more trouble.
928268240Sken			 */
929268240Sken			warnx("%s: reported size %u is different "
930268240Sken			    "than expected size %zd", __func__,
931268240Sken			    cap_len, sizeof(*cap));
932268240Sken		}
933268240Sken
934268240Sken		/*
935268240Sken		 * If there is more data available, grab it all,
936268240Sken		 * even though we don't really know what to do with
937268240Sken		 * the extra data since it obviously wasn't in the
938268240Sken		 * spec when this code was written.
939268240Sken		 */
940268240Sken		if (cap_len > res_len) {
941268240Sken			res_len = cap_len;
942268240Sken			goto retry;
943268240Sken		}
944268240Sken		persist_print_cap(cap, valid_len);
945268240Sken		break;
946268240Sken	}
947268240Sken	default:
948268240Sken		break;
949268240Sken	}
950268240Sken
951268240Skenbailout:
952268240Sken	free(res_buf);
953268240Sken
954268240Sken	if (ccb != NULL)
955268240Sken		cam_freeccb(ccb);
956268240Sken
957268240Sken	STAILQ_FOREACH_SAFE(id, &transport_id_list, links, id2) {
958268240Sken		STAILQ_REMOVE(&transport_id_list, id, persist_transport_id,
959268240Sken		    links);
960268240Sken		free(id);
961268240Sken	}
962268240Sken	return (error);
963268240Sken}
964