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: stable/11/sbin/camcontrol/persist.c 352289 2019-09-13 15:13:21Z mav $");
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;
244317374Sasomers	uint32_t type_mask;
245268240Sken
246268240Sken	length = scsi_2btoul(cap->length);
247268240Sken	length = MIN(length, valid_len);
248317374Sasomers	type_mask = scsi_2btoul(cap->type_mask);
249268240Sken
250268240Sken	if (length < __offsetof(struct scsi_per_res_cap, type_mask)) {
251268240Sken		fprintf(stdout, "Insufficient data (%u bytes) to report "
252268240Sken			"full capabilities\n", length);
253268240Sken		return;
254268240Sken	}
255268240Sken	if (length >= __offsetof(struct scsi_per_res_cap, reserved))
256268240Sken		check_type_mask = 1;
257268240Sken
258268240Sken	fprintf(stdout, "Replace Lost Reservation Capable (RLR_C): %d\n",
259268240Sken		(cap->flags1 & SPRI_RLR_C) ? 1 : 0);
260268240Sken	fprintf(stdout, "Compatible Reservation Handling (CRH): %d\n",
261268240Sken		(cap->flags1 & SPRI_CRH) ? 1 : 0);
262268240Sken	fprintf(stdout, "Specify Initiator Ports Capable (SIP_C): %d\n",
263268240Sken		(cap->flags1 & SPRI_SIP_C) ? 1 : 0);
264268240Sken	fprintf(stdout, "All Target Ports Capable (ATP_C): %d\n",
265268240Sken		(cap->flags1 & SPRI_ATP_C) ? 1 : 0);
266268240Sken	fprintf(stdout, "Persist Through Power Loss Capable (PTPL_C): %d\n",
267268240Sken		(cap->flags1 & SPRI_PTPL_C) ? 1 : 0);
268268240Sken	fprintf(stdout, "ALLOW COMMANDS field: (%#x)\n",
269268240Sken		(cap->flags2 & SPRI_ALLOW_CMD_MASK) >> SPRI_ALLOW_CMD_SHIFT);
270268240Sken	/*
271268240Sken	 * These cases are cut-and-pasted from SPC4r36l.  There is no
272268240Sken	 * succinct way to describe these otherwise, and even with the
273268240Sken	 * verbose description, the user will probably have to refer to
274268240Sken	 * the spec to fully understand what is going on.
275268240Sken	 */
276268240Sken	switch (cap->flags2 & SPRI_ALLOW_CMD_MASK) {
277268240Sken	case SPRI_ALLOW_1:
278268240Sken		fprintf(stdout,
279268240Sken"    The device server allows the TEST UNIT READY command through Write\n"
280268240Sken"    Exclusive type reservations and Exclusive Access type reservations\n"
281268240Sken"    and does not provide information about whether the following commands\n"
282268240Sken"    are allowed through Write Exclusive type reservations:\n"
283268240Sken"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
284268240Sken"           command, RECEIVE COPY RESULTS command, RECEIVE DIAGNOSTIC\n"
285268240Sken"           RESULTS command, REPORT SUPPORTED OPERATION CODES command,\n"
286268240Sken"           and REPORT SUPPORTED TASK MANAGEMENT FUNCTION command; and\n"
287268240Sken"        b) the READ DEFECT DATA command (see SBC-3).\n");
288268240Sken		break;
289268240Sken	case SPRI_ALLOW_2:
290268240Sken		fprintf(stdout,
291268240Sken"    The device server allows the TEST UNIT READY command through Write\n"
292268240Sken"    Exclusive type reservations and Exclusive Access type reservations\n"
293268240Sken"    and does not allow the following commands through Write Exclusive type\n"
294268240Sken"    reservations:\n"
295268240Sken"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
296268240Sken"           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
297268240Sken"           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
298268240Sken"           FUNCTION command; and\n"
299268240Sken"        b) the READ DEFECT DATA command.\n"
300268240Sken"    The device server does not allow the RECEIVE COPY RESULTS command\n"
301268240Sken"    through Write Exclusive type reservations or Exclusive Access type\n"
302268240Sken"    reservations.\n");
303268240Sken		break;
304268240Sken	case SPRI_ALLOW_3:
305268240Sken		fprintf(stdout,
306268240Sken"    The device server allows the TEST UNIT READY command through Write\n"
307268240Sken"    Exclusive type reservations and Exclusive Access type reservations\n"
308268240Sken"    and allows the following commands through Write Exclusive type\n"
309268240Sken"    reservations:\n"
310268240Sken"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
311268240Sken"           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
312268240Sken"           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
313268240Sken"           FUNCTION command; and\n"
314268240Sken"        b) the READ DEFECT DATA command.\n"
315268240Sken"    The device server does not allow the RECEIVE COPY RESULTS command\n"
316268240Sken"    through Write Exclusive type reservations or Exclusive Access type\n"
317268240Sken"    reservations.\n");
318268240Sken		break;
319268240Sken	case SPRI_ALLOW_4:
320268240Sken		fprintf(stdout,
321268240Sken"    The device server allows the TEST UNIT READY command and the RECEIVE\n"
322268240Sken"    COPY RESULTS command through Write Exclusive type reservations and\n"
323268240Sken"    Exclusive Access type reservations and allows the following commands\n"
324268240Sken"    through Write Exclusive type reservations:\n"
325268240Sken"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
326268240Sken"           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
327268240Sken"           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
328268240Sken"           FUNCTION command; and\n"
329268240Sken"        b) the READ DEFECT DATA command.\n");
330268240Sken		break;
331268240Sken	case SPRI_ALLOW_NA:
332268240Sken		fprintf(stdout,
333268240Sken"    No information is provided about whether certain commands are allowed\n"
334268240Sken"    through certain types of persistent reservations.\n");
335268240Sken		break;
336268240Sken	default:
337268240Sken		fprintf(stdout,
338268240Sken"    Unknown ALLOW COMMANDS value %#x\n",
339268240Sken			(cap->flags2 & SPRI_ALLOW_CMD_MASK) >>
340268240Sken			SPRI_ALLOW_CMD_SHIFT);
341268240Sken		break;
342268240Sken	}
343268240Sken	fprintf(stdout, "Persist Through Power Loss Activated (PTPL_A): %d\n",
344268240Sken		(cap->flags2 & SPRI_PTPL_A) ? 1 : 0);
345268240Sken	if ((check_type_mask != 0)
346268240Sken	 && (cap->flags2 & SPRI_TMV)) {
347268240Sken		fprintf(stdout, "Supported Persistent Reservation Types:\n");
348268240Sken		fprintf(stdout, "    Write Exclusive - All Registrants "
349268240Sken			"(WR_EX_AR): %d\n",
350317374Sasomers			(type_mask & SPRI_TM_WR_EX_AR)? 1 : 0);
351268240Sken		fprintf(stdout, "    Exclusive Access - Registrants Only "
352268240Sken			"(EX_AC_RO): %d\n",
353317374Sasomers			(type_mask & SPRI_TM_EX_AC_RO) ? 1 : 0);
354268240Sken		fprintf(stdout, "    Write Exclusive - Registrants Only "
355268240Sken			"(WR_EX_RO): %d\n",
356317374Sasomers			(type_mask & SPRI_TM_WR_EX_RO)? 1 : 0);
357268240Sken		fprintf(stdout, "    Exclusive Access (EX_AC): %d\n",
358317374Sasomers			(type_mask & SPRI_TM_EX_AC) ? 1 : 0);
359268240Sken		fprintf(stdout, "    Write Exclusive (WR_EX): %d\n",
360317374Sasomers			(type_mask & SPRI_TM_WR_EX) ? 1 : 0);
361268240Sken		fprintf(stdout, "    Exclusive Access - All Registrants "
362268240Sken			"(EX_AC_AR): %d\n",
363317374Sasomers			(type_mask & SPRI_TM_EX_AC_AR) ? 1 : 0);
364268240Sken	} else {
365268240Sken		fprintf(stdout, "Persistent Reservation Type Mask is NOT "
366268240Sken			"valid\n");
367268240Sken	}
368268240Sken
369268240Sken
370268240Sken}
371268240Sken
372268240Skenstatic void
373268240Skenpersist_print_full(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
374268240Sken{
375268240Sken	uint32_t length, len_to_go = 0;
376268240Sken	struct scsi_per_res_in_full_desc *desc;
377268240Sken	uint8_t *cur_pos;
378268240Sken	int i;
379268240Sken
380268240Sken	length = scsi_4btoul(hdr->length);
381268240Sken	length = MIN(length, valid_len);
382268240Sken
383268240Sken	if (length < sizeof(*desc)) {
384268240Sken		if (length == 0)
385268240Sken			fprintf(stdout, "No reservations.\n");
386268240Sken		else
387268240Sken			warnx("unable to print reservation, only got %u "
388268240Sken			      "valid bytes", length);
389268240Sken		return;
390268240Sken	}
391268240Sken
392268240Sken	fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation));
393268240Sken	cur_pos = (uint8_t *)&hdr[1];
394268240Sken	for (len_to_go = length, i = 0,
395268240Sken	     desc = (struct scsi_per_res_in_full_desc *)cur_pos;
396268240Sken	     len_to_go >= sizeof(*desc);
397268240Sken	     desc = (struct scsi_per_res_in_full_desc *)cur_pos, i++) {
398268240Sken		uint32_t additional_length, cur_length;
399268240Sken
400268240Sken
401268240Sken		fprintf(stdout, "Reservation Key: %#jx\n",
402268240Sken			(uintmax_t)scsi_8btou64(desc->res_key.key));
403268240Sken		fprintf(stdout, "All Target Ports (ALL_TG_PT): %d\n",
404268240Sken			(desc->flags & SPRI_FULL_ALL_TG_PT) ? 1 : 0);
405268240Sken		fprintf(stdout, "Reservation Holder (R_HOLDER): %d\n",
406268240Sken			(desc->flags & SPRI_FULL_R_HOLDER) ? 1 : 0);
407268240Sken
408268240Sken		if (desc->flags & SPRI_FULL_R_HOLDER)
409268240Sken			persist_print_scopetype(desc->scopetype);
410268240Sken
411268240Sken		if ((desc->flags & SPRI_FULL_ALL_TG_PT) == 0)
412268240Sken			fprintf(stdout, "Relative Target Port ID: %#x\n",
413268240Sken				scsi_2btoul(desc->rel_trgt_port_id));
414268240Sken
415268240Sken		additional_length = scsi_4btoul(desc->additional_length);
416268240Sken
417268240Sken		persist_print_transportid(desc->transport_id,
418268240Sken					  additional_length);
419268240Sken
420268240Sken		cur_length = sizeof(*desc) + additional_length;
421268240Sken		len_to_go -= cur_length;
422268240Sken		cur_pos += cur_length;
423268240Sken	}
424268240Sken}
425268240Sken
426268240Skenint
427268240Skenscsipersist(struct cam_device *device, int argc, char **argv, char *combinedopt,
428314220Sken	    int task_attr, int retry_count, int timeout, int verbosemode,
429314220Sken	    int err_recover)
430268240Sken{
431268240Sken	union ccb *ccb = NULL;
432268240Sken	int c, in = 0, out = 0;
433268240Sken	int action = -1, num_ids = 0;
434268240Sken	int error = 0;
435268240Sken	uint32_t res_len = 0;
436268240Sken	unsigned long rel_tgt_port = 0;
437268240Sken	uint8_t *res_buf = NULL;
438283907Saraujo	int scope = SPR_LU_SCOPE, res_type = 0;
439268240Sken	struct persist_transport_id *id, *id2;
440268240Sken	STAILQ_HEAD(, persist_transport_id) transport_id_list;
441268240Sken	uint64_t key = 0, sa_key = 0;
442268240Sken	struct scsi_nv *table = NULL;
443268240Sken	size_t table_size = 0, id_len = 0;
444268240Sken	uint32_t valid_len = 0;
445268240Sken	int all_tg_pt = 0, aptpl = 0, spec_i_pt = 0, unreg = 0,rel_port_set = 0;
446268240Sken
447268240Sken	STAILQ_INIT(&transport_id_list);
448268240Sken
449268240Sken	ccb = cam_getccb(device);
450268240Sken	if (ccb == NULL) {
451268240Sken		warnx("%s: error allocating CCB", __func__);
452268240Sken		error = 1;
453268240Sken		goto bailout;
454268240Sken	}
455268240Sken
456300547Struckman	CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
457268240Sken
458268240Sken	while ((c = getopt(argc, argv, combinedopt)) != -1) {
459268240Sken		switch (c) {
460268240Sken		case 'a':
461268240Sken			all_tg_pt = 1;
462268240Sken			break;
463268240Sken		case 'I': {
464268240Sken			int error_str_len = 128;
465268240Sken			char error_str[error_str_len];
466268240Sken			char *id_str;
467268240Sken
468268240Sken			id = malloc(sizeof(*id));
469268240Sken			if (id == NULL) {
470268240Sken				warnx("%s: error allocating %zu bytes",
471268240Sken				    __func__, sizeof(*id));
472268240Sken				error = 1;
473268240Sken				goto bailout;
474268240Sken			}
475268240Sken			bzero(id, sizeof(*id));
476268240Sken
477268240Sken			id_str = strdup(optarg);
478268240Sken			if (id_str == NULL) {
479268240Sken				warnx("%s: error duplicating string %s",
480268240Sken				    __func__, optarg);
481268240Sken				free(id);
482268240Sken				error = 1;
483268240Sken				goto bailout;
484268240Sken			}
485268240Sken			error = scsi_parse_transportid(id_str, &id->hdr,
486268240Sken			    &id->alloc_len, error_str, error_str_len);
487268240Sken			if (error != 0) {
488268240Sken				warnx("%s", error_str);
489268240Sken				error = 1;
490268240Sken				free(id);
491268240Sken				free(id_str);
492268240Sken				goto bailout;
493268240Sken			}
494268240Sken			free(id_str);
495268240Sken
496268240Sken			STAILQ_INSERT_TAIL(&transport_id_list, id, links);
497268240Sken			num_ids++;
498268240Sken			id_len += id->alloc_len;
499268240Sken			break;
500268240Sken		}
501268240Sken		case 'k':
502268240Sken		case 'K': {
503268240Sken			char *endptr;
504268240Sken			uint64_t tmpval;
505268240Sken
506268240Sken			tmpval = strtoumax(optarg, &endptr, 0);
507268240Sken			if (*endptr != '\0') {
508268240Sken				warnx("%s: invalid key argument %s", __func__,
509268240Sken				    optarg);
510268240Sken				error = 1;
511268240Sken				goto bailout;
512268240Sken			}
513268240Sken			if (c == 'k') {
514268240Sken				key = tmpval;
515268240Sken			} else {
516268240Sken				sa_key = tmpval;
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,
760314220Sken					   /*tag_action*/ task_attr,
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,
842314220Sken					    /*tag_action*/ task_attr,
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		error = 1;
862268240Sken		goto bailout;
863268240Sken	}
864268240Sken
865268240Sken	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
866268240Sken		if (verbosemode != 0) {
867268240Sken			cam_error_print(device, ccb, CAM_ESF_ALL,
868268240Sken					CAM_EPF_ALL, stderr);
869268240Sken		}
870268240Sken		error = 1;
871268240Sken		goto bailout;
872268240Sken	}
873268240Sken
874268240Sken	if (in == 0)
875268240Sken		goto bailout;
876268240Sken
877268240Sken	valid_len = res_len - ccb->csio.resid;
878268240Sken
879268240Sken	switch (action) {
880268240Sken	case SPRI_RK:
881268240Sken	case SPRI_RR:
882268240Sken	case SPRI_RS: {
883268240Sken		struct scsi_per_res_in_header *hdr;
884268240Sken		uint32_t hdr_len;
885268240Sken
886268240Sken		if (valid_len < sizeof(*hdr)) {
887268240Sken			warnx("%s: only got %d valid bytes, need %zd",
888268240Sken			      __func__, valid_len, sizeof(*hdr));
889268240Sken			error = 1;
890268240Sken			goto bailout;
891268240Sken		}
892268240Sken		hdr = (struct scsi_per_res_in_header *)res_buf;
893268240Sken		hdr_len = scsi_4btoul(hdr->length);
894268240Sken
895268240Sken		if (hdr_len > (res_len - sizeof(*hdr))) {
896268240Sken			res_len = hdr_len + sizeof(*hdr);
897268240Sken			goto retry;
898268240Sken		}
899268240Sken
900268240Sken		if (action == SPRI_RK) {
901268240Sken			persist_print_keys(hdr, valid_len);
902268240Sken		} else if (action == SPRI_RR) {
903268240Sken			persist_print_res(hdr, valid_len);
904268240Sken		} else {
905268240Sken			persist_print_full(hdr, valid_len);
906268240Sken		}
907268240Sken		break;
908268240Sken	}
909268240Sken	case SPRI_RC: {
910268240Sken		struct scsi_per_res_cap *cap;
911268240Sken		uint32_t cap_len;
912268240Sken
913268240Sken		if (valid_len < sizeof(*cap)) {
914268240Sken			warnx("%s: only got %u valid bytes, need %zd",
915268240Sken			      __func__, valid_len, sizeof(*cap));
916268240Sken			error = 1;
917268240Sken			goto bailout;
918268240Sken		}
919268240Sken		cap = (struct scsi_per_res_cap *)res_buf;
920268240Sken		cap_len = scsi_2btoul(cap->length);
921268240Sken		if (cap_len != sizeof(*cap)) {
922268240Sken			/*
923268240Sken			 * We should be able to deal with this,
924268240Sken			 * it's just more trouble.
925268240Sken			 */
926268240Sken			warnx("%s: reported size %u is different "
927268240Sken			    "than expected size %zd", __func__,
928268240Sken			    cap_len, sizeof(*cap));
929268240Sken		}
930268240Sken
931268240Sken		/*
932268240Sken		 * If there is more data available, grab it all,
933268240Sken		 * even though we don't really know what to do with
934268240Sken		 * the extra data since it obviously wasn't in the
935268240Sken		 * spec when this code was written.
936268240Sken		 */
937268240Sken		if (cap_len > res_len) {
938268240Sken			res_len = cap_len;
939268240Sken			goto retry;
940268240Sken		}
941268240Sken		persist_print_cap(cap, valid_len);
942268240Sken		break;
943268240Sken	}
944268240Sken	default:
945268240Sken		break;
946268240Sken	}
947268240Sken
948268240Skenbailout:
949268240Sken	free(res_buf);
950268240Sken
951268240Sken	if (ccb != NULL)
952268240Sken		cam_freeccb(ccb);
953268240Sken
954268240Sken	STAILQ_FOREACH_SAFE(id, &transport_id_list, links, id2) {
955268240Sken		STAILQ_REMOVE(&transport_id_list, id, persist_transport_id,
956268240Sken		    links);
957268240Sken		free(id);
958268240Sken	}
959268240Sken	return (error);
960268240Sken}
961