1533affcbSRobert Mustacchi /*
2533affcbSRobert Mustacchi  * This file and its contents are supplied under the terms of the
3533affcbSRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4533affcbSRobert Mustacchi  * You may only use this file in accordance with the terms of version
5533affcbSRobert Mustacchi  * 1.0 of the CDDL.
6533affcbSRobert Mustacchi  *
7533affcbSRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8533affcbSRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9533affcbSRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10533affcbSRobert Mustacchi  */
11533affcbSRobert Mustacchi 
12533affcbSRobert Mustacchi /*
13533affcbSRobert Mustacchi  * Copyright 2024 Oxide Computer Company
14533affcbSRobert Mustacchi  */
15533affcbSRobert Mustacchi 
16533affcbSRobert Mustacchi /*
17533affcbSRobert Mustacchi  * libnvme pieces specific to WDC.
18533affcbSRobert Mustacchi  *
19533affcbSRobert Mustacchi  * Currently this defines several common log pages that are found in a few
20533affcbSRobert Mustacchi  * generations of WDC devices such as the SN840 and SN65x. There is also support
21533affcbSRobert Mustacchi  * for a few of the vendor specific commands in the device.
22533affcbSRobert Mustacchi  *
23533affcbSRobert Mustacchi  * Currently there is support for two commands in library form: getting an e6
24533affcbSRobert Mustacchi  * log and performing a device resize. Because there are a few different
25533affcbSRobert Mustacchi  * parameters needed to issue the e6 request, we end up structuring it like the
26533affcbSRobert Mustacchi  * library's other request structures, even though it just uses the vendor
27533affcbSRobert Mustacchi  * unique commands. We do not use the full field validation structures for this
28533affcbSRobert Mustacchi  * because a portion of that is used by the vendor unique subsystem. Instead we
29533affcbSRobert Mustacchi  * manually validate the offset and track fields being set.
30533affcbSRobert Mustacchi  */
31533affcbSRobert Mustacchi 
32533affcbSRobert Mustacchi #include <string.h>
33533affcbSRobert Mustacchi #include <sys/sysmacros.h>
34533affcbSRobert Mustacchi #include <sys/nvme/wdc.h>
35533affcbSRobert Mustacchi 
36533affcbSRobert Mustacchi #include "libnvme_impl.h"
37533affcbSRobert Mustacchi 
38533affcbSRobert Mustacchi /*
39533affcbSRobert Mustacchi  * The amount of time that this command takes appears to somewhat relate to the
40533affcbSRobert Mustacchi  * size of the overall device and transformations that are going on. This value
41533affcbSRobert Mustacchi  * is an attempt to get through most resize testing plus a little slack in
42533affcbSRobert Mustacchi  * all of our testing to date.
43533affcbSRobert Mustacchi  */
44533affcbSRobert Mustacchi static const uint32_t nvme_wdc_resize_timeout = 30;
45533affcbSRobert Mustacchi 
46533affcbSRobert Mustacchi /*
47533affcbSRobert Mustacchi  * We expect a given read of a region of an e6 log to take this amount of time
48533affcbSRobert Mustacchi  * in seconds.
49533affcbSRobert Mustacchi  */
50533affcbSRobert Mustacchi static const uint32_t nvme_wdc_e6_timeout = 30;
51533affcbSRobert Mustacchi 
527a27a99aSRobert Mustacchi /*
537a27a99aSRobert Mustacchi  * Timeout for injecting and clearing asserts. We make this generous as assert
547a27a99aSRobert Mustacchi  * injection may take some time.
557a27a99aSRobert Mustacchi  */
567a27a99aSRobert Mustacchi static const uint32_t nvme_wdc_assert_timeout = 45;
577a27a99aSRobert Mustacchi 
58533affcbSRobert Mustacchi typedef enum {
59533affcbSRobert Mustacchi 	NVME_WDC_E6_REQ_FIELD_OFFSET	= 0,
60533affcbSRobert Mustacchi 	NVME_WDC_E6_REQ_FIELD_LEN
61533affcbSRobert Mustacchi } nvme_wdc_e6_req_field_t;
62533affcbSRobert Mustacchi 
63533affcbSRobert Mustacchi static bool
nvme_wdc_e6_field_valid_offset(const nvme_field_info_t * field,const nvme_valid_ctrl_data_t * data,uint64_t off,char * msg,size_t msglen)64533affcbSRobert Mustacchi nvme_wdc_e6_field_valid_offset(const nvme_field_info_t *field,
65533affcbSRobert Mustacchi     const nvme_valid_ctrl_data_t *data, uint64_t off, char *msg, size_t msglen)
66533affcbSRobert Mustacchi {
67533affcbSRobert Mustacchi 	uint64_t max;
68533affcbSRobert Mustacchi 
69533affcbSRobert Mustacchi 	if ((off % NVME_DWORD_SIZE) != 0) {
70533affcbSRobert Mustacchi 		(void) snprintf(msg, msglen, "field %s (%s) value 0x%" PRIx64
71533affcbSRobert Mustacchi 		    "must be %u-byte aligned", field->nlfi_human,
72533affcbSRobert Mustacchi 		    field->nlfi_spec, off, NVME_DWORD_SIZE);
73533affcbSRobert Mustacchi 		return (false);
74533affcbSRobert Mustacchi 	}
75533affcbSRobert Mustacchi 
76533affcbSRobert Mustacchi 	max = (uint64_t)UINT32_MAX << NVME_DWORD_SHIFT;
77533affcbSRobert Mustacchi 	return (nvme_field_range_check(field, 0, max, msg, msglen, off));
78533affcbSRobert Mustacchi }
79533affcbSRobert Mustacchi 
80533affcbSRobert Mustacchi const nvme_field_info_t nvme_wdc_e6_req_fields[] = {
81533affcbSRobert Mustacchi 	[NVME_WDC_E6_REQ_FIELD_OFFSET] = {
82533affcbSRobert Mustacchi 		.nlfi_vers = &nvme_vers_1v0,
83533affcbSRobert Mustacchi 		.nlfi_valid = nvme_wdc_e6_field_valid_offset,
84533affcbSRobert Mustacchi 		.nlfi_spec = "offset",
85533affcbSRobert Mustacchi 		.nlfi_human = "e6 log offset",
86533affcbSRobert Mustacchi 		.nlfi_def_req = true,
87533affcbSRobert Mustacchi 		.nlfi_def_allow = true
88533affcbSRobert Mustacchi 	},
89533affcbSRobert Mustacchi 	/*
90533affcbSRobert Mustacchi 	 * Note there is no validation of this field because we rely on the
91533affcbSRobert Mustacchi 	 * underlying vendor unique command output length to do so.
92533affcbSRobert Mustacchi 	 */
93533affcbSRobert Mustacchi 	[NVME_WDC_E6_REQ_FIELD_LEN] = {
94533affcbSRobert Mustacchi 		.nlfi_vers = &nvme_vers_1v0,
95533affcbSRobert Mustacchi 		.nlfi_spec = "length",
96533affcbSRobert Mustacchi 		.nlfi_human = "data transfer length",
97533affcbSRobert Mustacchi 		.nlfi_def_req = true,
98533affcbSRobert Mustacchi 		.nlfi_def_allow = true
99533affcbSRobert Mustacchi 	},
100533affcbSRobert Mustacchi };
101533affcbSRobert Mustacchi 
102533affcbSRobert Mustacchi static bool
nvme_wdc_log_dev_mgmt_var_len(uint64_t * outp,const void * data,size_t len)103533affcbSRobert Mustacchi nvme_wdc_log_dev_mgmt_var_len(uint64_t *outp, const void *data, size_t len)
104533affcbSRobert Mustacchi {
105533affcbSRobert Mustacchi 	wdc_vsd_t vsd;
106533affcbSRobert Mustacchi 
107533affcbSRobert Mustacchi 	if (len < sizeof (vsd)) {
108533affcbSRobert Mustacchi 		return (false);
109533affcbSRobert Mustacchi 	}
110533affcbSRobert Mustacchi 
111533affcbSRobert Mustacchi 	(void) memcpy(&vsd, data, sizeof (vsd));
112533affcbSRobert Mustacchi 	*outp = vsd.vsd_len;
113533affcbSRobert Mustacchi 	return (true);
114533affcbSRobert Mustacchi }
115533affcbSRobert Mustacchi 
116533affcbSRobert Mustacchi static bool
nvme_wdc_log_samples_var_len(uint64_t * outp,const void * data,size_t len)117533affcbSRobert Mustacchi nvme_wdc_log_samples_var_len(uint64_t *outp, const void *data, size_t len)
118533affcbSRobert Mustacchi {
119533affcbSRobert Mustacchi 	uint32_t nsamp;
120533affcbSRobert Mustacchi 
121533affcbSRobert Mustacchi 	if (len < sizeof (uint32_t)) {
122533affcbSRobert Mustacchi 		return (false);
123533affcbSRobert Mustacchi 	}
124533affcbSRobert Mustacchi 
125533affcbSRobert Mustacchi 	(void) memcpy(&nsamp, data, sizeof (uint32_t));
126533affcbSRobert Mustacchi 	*outp = (uint64_t)nsamp * sizeof (uint32_t);
127533affcbSRobert Mustacchi 	return (true);
128533affcbSRobert Mustacchi }
129533affcbSRobert Mustacchi 
130533affcbSRobert Mustacchi static bool
nvme_wdc_sn840_fw_act_var_len(uint64_t * outp,const void * data,size_t len)131533affcbSRobert Mustacchi nvme_wdc_sn840_fw_act_var_len(uint64_t *outp, const void *data, size_t len)
132533affcbSRobert Mustacchi {
133533affcbSRobert Mustacchi 	wdc_vul_sn840_fw_act_hdr_t hdr;
134533affcbSRobert Mustacchi 
135533affcbSRobert Mustacchi 	if (len < sizeof (wdc_vul_sn840_fw_act_hdr_t)) {
136533affcbSRobert Mustacchi 		return (false);
137533affcbSRobert Mustacchi 	}
138533affcbSRobert Mustacchi 
139533affcbSRobert Mustacchi 	(void) memcpy(&hdr, data, sizeof (uint32_t));
140533affcbSRobert Mustacchi 	*outp = (uint64_t)hdr.fah_nent * hdr.fah_entlen;
141533affcbSRobert Mustacchi 	return (true);
142533affcbSRobert Mustacchi }
143533affcbSRobert Mustacchi 
144533affcbSRobert Mustacchi static const nvme_log_page_info_t wdc_sn840_log_pages[] = { {
145533affcbSRobert Mustacchi 	.nlpi_short = "wdc/eol",
146533affcbSRobert Mustacchi 	.nlpi_human = "EOL",
147533affcbSRobert Mustacchi 	.nlpi_lid = WDC_SN840_LOG_EOL,
148533affcbSRobert Mustacchi 	.nlpi_csi = NVME_CSI_NVM,
149533affcbSRobert Mustacchi 	.nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC,
150533affcbSRobert Mustacchi 	.nlpi_source = NVME_LOG_DISC_S_DB,
151533affcbSRobert Mustacchi 	.nlpi_scope = NVME_LOG_SCOPE_NVM,
152533affcbSRobert Mustacchi 	.nlpi_len = sizeof (wdc_vul_sn840_eol_t)
153533affcbSRobert Mustacchi }, {
154533affcbSRobert Mustacchi 	.nlpi_short = "wdc/devmgmt",
155533affcbSRobert Mustacchi 	.nlpi_human = "Device Manageability",
156533affcbSRobert Mustacchi 	.nlpi_lid = WDC_SN840_LOG_DEV_MANAGE,
157533affcbSRobert Mustacchi 	.nlpi_csi = NVME_CSI_NVM,
158533affcbSRobert Mustacchi 	.nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC,
159533affcbSRobert Mustacchi 	.nlpi_source = NVME_LOG_DISC_S_DB,
160533affcbSRobert Mustacchi 	.nlpi_scope = NVME_LOG_SCOPE_CTRL | NVME_LOG_SCOPE_NS,
161533affcbSRobert Mustacchi 	.nlpi_len = sizeof (wdc_vsd_t),
162533affcbSRobert Mustacchi 	.nlpi_var_func = nvme_wdc_log_dev_mgmt_var_len
163533affcbSRobert Mustacchi }, {
164533affcbSRobert Mustacchi 	.nlpi_short = "wdc/pciesi",
165533affcbSRobert Mustacchi 	.nlpi_human = "PCIe Signal Integrity",
166533affcbSRobert Mustacchi 	.nlpi_lid = WDC_SN840_LOG_PCIE_SI,
167533affcbSRobert Mustacchi 	.nlpi_csi = NVME_CSI_NVM,
168533affcbSRobert Mustacchi 	.nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC,
169533affcbSRobert Mustacchi 	.nlpi_source = NVME_LOG_DISC_S_DB,
170533affcbSRobert Mustacchi 	.nlpi_disc = NVME_LOG_DISC_F_NEED_LSP,
171533affcbSRobert Mustacchi 	.nlpi_scope = NVME_LOG_SCOPE_CTRL
172533affcbSRobert Mustacchi }, {
173533affcbSRobert Mustacchi 	.nlpi_short = "wdc/power",
174533affcbSRobert Mustacchi 	.nlpi_human = "Power Samples",
175533affcbSRobert Mustacchi 	.nlpi_lid = WDC_SN840_LOG_POWER,
176533affcbSRobert Mustacchi 	.nlpi_csi = NVME_CSI_NVM,
177533affcbSRobert Mustacchi 	.nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC,
178533affcbSRobert Mustacchi 	.nlpi_source = NVME_LOG_DISC_S_DB,
179533affcbSRobert Mustacchi 	.nlpi_scope = NVME_LOG_SCOPE_CTRL,
180533affcbSRobert Mustacchi 	.nlpi_len = sizeof (uint32_t),
181533affcbSRobert Mustacchi 	.nlpi_var_func = nvme_wdc_log_samples_var_len
182533affcbSRobert Mustacchi }, {
183533affcbSRobert Mustacchi 	.nlpi_short = "wdc/temp",
184533affcbSRobert Mustacchi 	.nlpi_human = "Temperature Samples",
185533affcbSRobert Mustacchi 	.nlpi_lid = WDC_SN840_LOG_TEMP,
186533affcbSRobert Mustacchi 	.nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC,
187533affcbSRobert Mustacchi 	.nlpi_source = NVME_LOG_DISC_S_DB,
188533affcbSRobert Mustacchi 	.nlpi_scope = NVME_LOG_SCOPE_CTRL,
189533affcbSRobert Mustacchi 	.nlpi_len = sizeof (uint32_t),
190533affcbSRobert Mustacchi 	.nlpi_var_func = nvme_wdc_log_samples_var_len
191533affcbSRobert Mustacchi }, {
192533affcbSRobert Mustacchi 	.nlpi_short = "wdc/fwact",
193533affcbSRobert Mustacchi 	.nlpi_human = "Firmware Activation",
194533affcbSRobert Mustacchi 	.nlpi_lid = WDC_SN840_LOG_FW_ACT,
195533affcbSRobert Mustacchi 	.nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC,
196533affcbSRobert Mustacchi 	.nlpi_source = NVME_LOG_DISC_S_DB,
197533affcbSRobert Mustacchi 	.nlpi_scope = NVME_LOG_SCOPE_CTRL,
198533affcbSRobert Mustacchi 	.nlpi_len = sizeof (wdc_vul_sn840_fw_act_hdr_t),
199533affcbSRobert Mustacchi 	.nlpi_var_func = nvme_wdc_sn840_fw_act_var_len
200533affcbSRobert Mustacchi }, {
201533affcbSRobert Mustacchi 	.nlpi_short = "wdc/ccds",
202533affcbSRobert Mustacchi 	.nlpi_human = "CCDS Build Information",
203533affcbSRobert Mustacchi 	.nlpi_lid = WDC_SN840_LOG_CCDS,
204533affcbSRobert Mustacchi 	.nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC,
205533affcbSRobert Mustacchi 	.nlpi_source = NVME_LOG_DISC_S_DB,
206533affcbSRobert Mustacchi 	.nlpi_scope = NVME_LOG_SCOPE_CTRL,
207533affcbSRobert Mustacchi 	.nlpi_len = sizeof (wdc_vul_sn840_ccds_info_t)
208533affcbSRobert Mustacchi } };
209533affcbSRobert Mustacchi 
210533affcbSRobert Mustacchi static const nvme_log_page_info_t wdc_sn65x_log_pages[] = { {
211*627ade2aSRobert Mustacchi 	.nlpi_short = "ocp/smart",
212*627ade2aSRobert Mustacchi 	.nlpi_human = "OCP SMART / Health",
213*627ade2aSRobert Mustacchi 	.nlpi_lid = OCP_LOG_DSSD_SMART,
214*627ade2aSRobert Mustacchi 	.nlpi_csi = NVME_CSI_NVM,
215*627ade2aSRobert Mustacchi 	.nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC,
216*627ade2aSRobert Mustacchi 	.nlpi_source = NVME_LOG_DISC_S_DB,
217*627ade2aSRobert Mustacchi 	.nlpi_scope = NVME_LOG_SCOPE_NVM,
218*627ade2aSRobert Mustacchi 	.nlpi_len = sizeof (ocp_vul_smart_t),
219*627ade2aSRobert Mustacchi }, {
220533affcbSRobert Mustacchi 	.nlpi_short = "wdc/power",
221533affcbSRobert Mustacchi 	.nlpi_human = "Power Samples",
222533affcbSRobert Mustacchi 	.nlpi_lid = WDC_SN65X_LOG_POWER,
223533affcbSRobert Mustacchi 	.nlpi_csi = NVME_CSI_NVM,
224533affcbSRobert Mustacchi 	.nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC,
225533affcbSRobert Mustacchi 	.nlpi_source = NVME_LOG_DISC_S_DB,
226533affcbSRobert Mustacchi 	.nlpi_scope = NVME_LOG_SCOPE_CTRL,
227533affcbSRobert Mustacchi 	.nlpi_len = sizeof (uint32_t),
228533affcbSRobert Mustacchi 	.nlpi_var_func = nvme_wdc_log_samples_var_len
229533affcbSRobert Mustacchi }, {
230533affcbSRobert Mustacchi 	.nlpi_short = "wdc/temp",
231533affcbSRobert Mustacchi 	.nlpi_human = "Temperature Samples",
232533affcbSRobert Mustacchi 	.nlpi_lid = WDC_SN65X_LOG_TEMP,
233533affcbSRobert Mustacchi 	.nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC,
234533affcbSRobert Mustacchi 	.nlpi_source = NVME_LOG_DISC_S_DB,
235533affcbSRobert Mustacchi 	.nlpi_scope = NVME_LOG_SCOPE_CTRL,
236533affcbSRobert Mustacchi 	.nlpi_len = sizeof (uint32_t),
237533affcbSRobert Mustacchi 	.nlpi_var_func = nvme_wdc_log_samples_var_len
238533affcbSRobert Mustacchi }, {
239533affcbSRobert Mustacchi 	.nlpi_short = "wdc/cusmart",
240533affcbSRobert Mustacchi 	.nlpi_human = "Customer Unique SMART",
241533affcbSRobert Mustacchi 	.nlpi_lid = WDC_SN65X_LOG_UNIQUE_SMART,
242533affcbSRobert Mustacchi 	.nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC,
243533affcbSRobert Mustacchi 	.nlpi_source = NVME_LOG_DISC_S_DB,
244533affcbSRobert Mustacchi 	.nlpi_scope = NVME_LOG_SCOPE_CTRL,
245533affcbSRobert Mustacchi 	.nlpi_len = sizeof (wdc_vul_sn65x_smart_t)
246533affcbSRobert Mustacchi } };
247533affcbSRobert Mustacchi 
248533affcbSRobert Mustacchi /*
249533affcbSRobert Mustacchi  * Currently these commands are shared across the SN840, SN650, and SN655.
250533affcbSRobert Mustacchi  * This will likely need to be split up and redone when we end up with more
251533affcbSRobert Mustacchi  * device-specific commands that aren't shared across controller generations.
252533affcbSRobert Mustacchi  * When we get to that we should choose whether we want to redefine the vuc like
253533affcbSRobert Mustacchi  * we have with log pages or if we should move to a shared structure that is
254533affcbSRobert Mustacchi  * incorporated as an array of pointers.
255533affcbSRobert Mustacchi  */
256533affcbSRobert Mustacchi static const nvme_vuc_disc_t wdc_sn840_sn65x_vuc[] = { {
257533affcbSRobert Mustacchi 	.nvd_short = "wdc/resize",
258533affcbSRobert Mustacchi 	.nvd_desc = "drive resize",
259533affcbSRobert Mustacchi 	.nvd_opc = WDC_VUC_RESIZE_OPC,
260533affcbSRobert Mustacchi 	.nvd_impact = NVME_VUC_DISC_IMPACT_DATA | NVME_VUC_DISC_IMPACT_NS,
261533affcbSRobert Mustacchi 	.nvd_dt = NVME_VUC_DISC_IO_NONE,
262533affcbSRobert Mustacchi 	.nvd_lock = NVME_VUC_DISC_LOCK_WRITE
263533affcbSRobert Mustacchi }, {
264533affcbSRobert Mustacchi 	.nvd_short = "wdc/e6dump",
265533affcbSRobert Mustacchi 	.nvd_desc = "dump e6 diagnostic data",
266533affcbSRobert Mustacchi 	.nvd_opc = WDC_VUC_E6_DUMP_OPC,
267533affcbSRobert Mustacchi 	.nvd_dt = NVME_VUC_DISC_IO_OUTPUT,
268533affcbSRobert Mustacchi 	.nvd_lock = NVME_VUC_DISC_LOCK_READ
2697a27a99aSRobert Mustacchi }, {
2707a27a99aSRobert Mustacchi 	.nvd_short = "wdc/clear-assert",
2717a27a99aSRobert Mustacchi 	.nvd_desc = "clear internal drive assertion",
2727a27a99aSRobert Mustacchi 	.nvd_opc = WDC_VUC_ASSERT_OPC,
2737a27a99aSRobert Mustacchi 	.nvd_dt = NVME_VUC_DISC_IO_NONE,
2747a27a99aSRobert Mustacchi 	.nvd_lock = NVME_VUC_DISC_LOCK_NONE
2757a27a99aSRobert Mustacchi }, {
2767a27a99aSRobert Mustacchi 	/*
2777a27a99aSRobert Mustacchi 	 * It's hard to come up with a good impact statement from this. It will
2787a27a99aSRobert Mustacchi 	 * cause I/O to fail but may or may not cause issues with data.
2797a27a99aSRobert Mustacchi 	 */
2807a27a99aSRobert Mustacchi 	.nvd_short = "wdc/inject-assert",
2817a27a99aSRobert Mustacchi 	.nvd_desc = "inject internal drive assertion",
2827a27a99aSRobert Mustacchi 	.nvd_opc = WDC_VUC_ASSERT_OPC,
2837a27a99aSRobert Mustacchi 	.nvd_dt = NVME_VUC_DISC_IO_NONE,
2847a27a99aSRobert Mustacchi 	.nvd_lock = NVME_VUC_DISC_LOCK_WRITE
285533affcbSRobert Mustacchi } };
286533affcbSRobert Mustacchi 
287533affcbSRobert Mustacchi const nvme_vsd_t wdc_sn840 = {
288533affcbSRobert Mustacchi 	.nvd_vid = WDC_PCI_VID,
289533affcbSRobert Mustacchi 	.nvd_did = WDC_SN840_DID,
290533affcbSRobert Mustacchi 	.nvd_human = "WDC Ultrastar DC SN840",
291533affcbSRobert Mustacchi 	.nvd_logs = wdc_sn840_log_pages,
292533affcbSRobert Mustacchi 	.nvd_nlogs = ARRAY_SIZE(wdc_sn840_log_pages),
293533affcbSRobert Mustacchi 	.nvd_vuc = wdc_sn840_sn65x_vuc,
294533affcbSRobert Mustacchi 	.nvd_nvuc = ARRAY_SIZE(wdc_sn840_sn65x_vuc)
295533affcbSRobert Mustacchi };
296533affcbSRobert Mustacchi 
297533affcbSRobert Mustacchi const nvme_vsd_t wdc_sn650 = {
298533affcbSRobert Mustacchi 	.nvd_vid = WDC_PCI_VID,
299533affcbSRobert Mustacchi 	.nvd_did = WDC_SN650_DID,
300533affcbSRobert Mustacchi 	.nvd_human = "WDC Ultrastar DC SN650",
301533affcbSRobert Mustacchi 	.nvd_logs = wdc_sn65x_log_pages,
302533affcbSRobert Mustacchi 	.nvd_nlogs = ARRAY_SIZE(wdc_sn65x_log_pages),
303533affcbSRobert Mustacchi 	.nvd_vuc = wdc_sn840_sn65x_vuc,
304533affcbSRobert Mustacchi 	.nvd_nvuc = ARRAY_SIZE(wdc_sn840_sn65x_vuc)
305533affcbSRobert Mustacchi };
306533affcbSRobert Mustacchi 
307533affcbSRobert Mustacchi const nvme_vsd_t wdc_sn655 = {
308533affcbSRobert Mustacchi 	.nvd_vid = WDC_PCI_VID,
309533affcbSRobert Mustacchi 	.nvd_did = WDC_SN655_DID,
310533affcbSRobert Mustacchi 	.nvd_human = "WDC Ultrastar DC SN655",
311533affcbSRobert Mustacchi 	.nvd_logs = wdc_sn65x_log_pages,
312533affcbSRobert Mustacchi 	.nvd_nlogs = ARRAY_SIZE(wdc_sn65x_log_pages),
313533affcbSRobert Mustacchi 	.nvd_vuc = wdc_sn840_sn65x_vuc,
314533affcbSRobert Mustacchi 	.nvd_nvuc = ARRAY_SIZE(wdc_sn840_sn65x_vuc)
315533affcbSRobert Mustacchi };
316533affcbSRobert Mustacchi 
317533affcbSRobert Mustacchi static nvme_vuc_req_t *
nvme_wdc_resize_vuc(nvme_ctrl_t * ctrl,uint8_t subcmd,uint32_t gib)318533affcbSRobert Mustacchi nvme_wdc_resize_vuc(nvme_ctrl_t *ctrl, uint8_t subcmd, uint32_t gib)
319533affcbSRobert Mustacchi {
320533affcbSRobert Mustacchi 	nvme_vuc_req_t *req = NULL;
321533affcbSRobert Mustacchi 	uint32_t cdw12 = WDC_VUC_RESIZE_CMD | ((uint32_t)subcmd << 8);
322533affcbSRobert Mustacchi 
323533affcbSRobert Mustacchi 	if (!nvme_vendor_vuc_supported(ctrl, "wdc/resize")) {
324533affcbSRobert Mustacchi 		return (false);
325533affcbSRobert Mustacchi 	}
326533affcbSRobert Mustacchi 
327533affcbSRobert Mustacchi 	if (!nvme_vuc_req_init(ctrl, &req)) {
328533affcbSRobert Mustacchi 		return (false);
329533affcbSRobert Mustacchi 	}
330533affcbSRobert Mustacchi 
331533affcbSRobert Mustacchi 	if (!nvme_vuc_req_set_opcode(req, WDC_VUC_RESIZE_OPC) ||
332533affcbSRobert Mustacchi 	    !nvme_vuc_req_set_cdw12(req, cdw12) ||
333533affcbSRobert Mustacchi 	    !nvme_vuc_req_set_cdw13(req, gib) ||
334533affcbSRobert Mustacchi 	    !nvme_vuc_req_set_timeout(req, nvme_wdc_resize_timeout)) {
335533affcbSRobert Mustacchi 		nvme_vuc_req_fini(req);
336533affcbSRobert Mustacchi 		return (false);
337533affcbSRobert Mustacchi 	}
338533affcbSRobert Mustacchi 
339533affcbSRobert Mustacchi 	return (req);
340533affcbSRobert Mustacchi }
341533affcbSRobert Mustacchi 
342533affcbSRobert Mustacchi bool
nvme_wdc_resize_get(nvme_ctrl_t * ctrl,uint32_t * gbp)343533affcbSRobert Mustacchi nvme_wdc_resize_get(nvme_ctrl_t *ctrl, uint32_t *gbp)
344533affcbSRobert Mustacchi {
345533affcbSRobert Mustacchi 	nvme_vuc_req_t *vuc;
346533affcbSRobert Mustacchi 
347533affcbSRobert Mustacchi 	if (gbp == NULL) {
348533affcbSRobert Mustacchi 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
349533affcbSRobert Mustacchi 		    "encountered invalid uint32_t pointer: %p", gbp));
350533affcbSRobert Mustacchi 	}
351533affcbSRobert Mustacchi 
352533affcbSRobert Mustacchi 	if ((vuc = nvme_wdc_resize_vuc(ctrl, WDC_VUC_RESIZE_SUB_GET, 0)) ==
353533affcbSRobert Mustacchi 	    NULL) {
354533affcbSRobert Mustacchi 		return (false);
355533affcbSRobert Mustacchi 	}
356533affcbSRobert Mustacchi 
357533affcbSRobert Mustacchi 	if (!nvme_vuc_req_exec(vuc)) {
358533affcbSRobert Mustacchi 		nvme_vuc_req_fini(vuc);
359533affcbSRobert Mustacchi 		return (false);
360533affcbSRobert Mustacchi 	}
361533affcbSRobert Mustacchi 
362533affcbSRobert Mustacchi 	if (!nvme_vuc_req_get_cdw0(vuc, gbp)) {
363533affcbSRobert Mustacchi 		nvme_vuc_req_fini(vuc);
364533affcbSRobert Mustacchi 		return (false);
365533affcbSRobert Mustacchi 	}
366533affcbSRobert Mustacchi 
367533affcbSRobert Mustacchi 	return (nvme_ctrl_success(ctrl));
368533affcbSRobert Mustacchi }
369533affcbSRobert Mustacchi 
370533affcbSRobert Mustacchi bool
nvme_wdc_resize_set(nvme_ctrl_t * ctrl,uint32_t gb)371533affcbSRobert Mustacchi nvme_wdc_resize_set(nvme_ctrl_t *ctrl, uint32_t gb)
372533affcbSRobert Mustacchi {
373533affcbSRobert Mustacchi 	nvme_vuc_req_t *vuc;
374533affcbSRobert Mustacchi 
375533affcbSRobert Mustacchi 	if ((vuc = nvme_wdc_resize_vuc(ctrl, WDC_VUC_RESIZE_SUB_SET, gb)) ==
376533affcbSRobert Mustacchi 	    NULL) {
377533affcbSRobert Mustacchi 		return (false);
378533affcbSRobert Mustacchi 	}
379533affcbSRobert Mustacchi 
380533affcbSRobert Mustacchi 	if (!nvme_vuc_req_set_impact(vuc, NVME_VUC_DISC_IMPACT_DATA |
381533affcbSRobert Mustacchi 	    NVME_VUC_DISC_IMPACT_NS)) {
382533affcbSRobert Mustacchi 		nvme_vuc_req_fini(vuc);
383533affcbSRobert Mustacchi 		return (false);
384533affcbSRobert Mustacchi 	}
385533affcbSRobert Mustacchi 
386533affcbSRobert Mustacchi 	if (!nvme_vuc_req_exec(vuc)) {
387533affcbSRobert Mustacchi 		nvme_vuc_req_fini(vuc);
388533affcbSRobert Mustacchi 		return (false);
389533affcbSRobert Mustacchi 	}
390533affcbSRobert Mustacchi 
391533affcbSRobert Mustacchi 	nvme_vuc_req_fini(vuc);
392533affcbSRobert Mustacchi 	return (nvme_ctrl_success(ctrl));
393533affcbSRobert Mustacchi }
394533affcbSRobert Mustacchi 
395533affcbSRobert Mustacchi void
nvme_wdc_e6_req_fini(nvme_wdc_e6_req_t * req)396533affcbSRobert Mustacchi nvme_wdc_e6_req_fini(nvme_wdc_e6_req_t *req)
397533affcbSRobert Mustacchi {
398533affcbSRobert Mustacchi 	if (req == NULL) {
399533affcbSRobert Mustacchi 		return;
400533affcbSRobert Mustacchi 	}
401533affcbSRobert Mustacchi 
402533affcbSRobert Mustacchi 	nvme_vuc_req_fini(req->wer_vuc);
403533affcbSRobert Mustacchi 	req->wer_vuc = NULL;
404533affcbSRobert Mustacchi 	free(req);
405533affcbSRobert Mustacchi }
406533affcbSRobert Mustacchi 
407533affcbSRobert Mustacchi bool
nvme_wdc_e6_req_init(nvme_ctrl_t * ctrl,nvme_wdc_e6_req_t ** reqp)408533affcbSRobert Mustacchi nvme_wdc_e6_req_init(nvme_ctrl_t *ctrl, nvme_wdc_e6_req_t **reqp)
409533affcbSRobert Mustacchi {
410533affcbSRobert Mustacchi 	nvme_wdc_e6_req_t *req;
411533affcbSRobert Mustacchi 
412533affcbSRobert Mustacchi 	if (reqp == NULL) {
413533affcbSRobert Mustacchi 		return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
414533affcbSRobert Mustacchi 		    "encountered invalid nvme_commit_req_t output pointer: %p",
415533affcbSRobert Mustacchi 		    reqp));
416533affcbSRobert Mustacchi 	}
417533affcbSRobert Mustacchi 
418533affcbSRobert Mustacchi 	if (!nvme_vendor_vuc_supported(ctrl, "wdc/e6dump")) {
419533affcbSRobert Mustacchi 		return (false);
420533affcbSRobert Mustacchi 	}
421533affcbSRobert Mustacchi 
422533affcbSRobert Mustacchi 	req = calloc(1, sizeof (nvme_wdc_e6_req_t));
423533affcbSRobert Mustacchi 	if (req == NULL) {
424533affcbSRobert Mustacchi 		int e = errno;
425533affcbSRobert Mustacchi 		return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
426533affcbSRobert Mustacchi 		    "allocate memory for a new nvme_wdc_e6_req_t: %s",
427533affcbSRobert Mustacchi 		    strerror(e)));
428533affcbSRobert Mustacchi 	}
429533affcbSRobert Mustacchi 
430533affcbSRobert Mustacchi 	if (!nvme_vuc_req_init(ctrl, &req->wer_vuc)) {
431533affcbSRobert Mustacchi 		nvme_wdc_e6_req_fini(req);
432533affcbSRobert Mustacchi 		return (false);
433533affcbSRobert Mustacchi 	}
434533affcbSRobert Mustacchi 
435533affcbSRobert Mustacchi 	/*
436533affcbSRobert Mustacchi 	 * The documentation suggests we must explicitly set the mode in cdw12
437533affcbSRobert Mustacchi 	 * to zero. While that should be the default, we do anyways.
438533affcbSRobert Mustacchi 	 */
439533affcbSRobert Mustacchi 	if (!nvme_vuc_req_set_opcode(req->wer_vuc, WDC_VUC_E6_DUMP_OPC) ||
440533affcbSRobert Mustacchi 	    !nvme_vuc_req_set_cdw12(req->wer_vuc, 0) ||
441533affcbSRobert Mustacchi 	    !nvme_vuc_req_set_timeout(req->wer_vuc, nvme_wdc_e6_timeout)) {
442533affcbSRobert Mustacchi 		nvme_wdc_e6_req_fini(req);
443533affcbSRobert Mustacchi 		return (false);
444533affcbSRobert Mustacchi 	}
445533affcbSRobert Mustacchi 
446533affcbSRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(nvme_wdc_e6_req_fields); i++) {
447533affcbSRobert Mustacchi 		if (nvme_wdc_e6_req_fields[i].nlfi_def_req) {
448533affcbSRobert Mustacchi 			req->wer_need |= 1 << i;
449533affcbSRobert Mustacchi 		}
450533affcbSRobert Mustacchi 	}
451533affcbSRobert Mustacchi 
452533affcbSRobert Mustacchi 	*reqp = req;
453533affcbSRobert Mustacchi 	return (nvme_ctrl_success(ctrl));
454533affcbSRobert Mustacchi }
455533affcbSRobert Mustacchi 
456533affcbSRobert Mustacchi static void
nvme_wdc_e6_req_clear_need(nvme_wdc_e6_req_t * req,nvme_wdc_e6_req_field_t field)457533affcbSRobert Mustacchi nvme_wdc_e6_req_clear_need(nvme_wdc_e6_req_t *req,
458533affcbSRobert Mustacchi     nvme_wdc_e6_req_field_t field)
459533affcbSRobert Mustacchi {
460533affcbSRobert Mustacchi 	req->wer_need &= ~(1 << field);
461533affcbSRobert Mustacchi }
462533affcbSRobert Mustacchi 
463533affcbSRobert Mustacchi static const nvme_field_check_t nvme_wdc_e6_check_off = {
464533affcbSRobert Mustacchi 	nvme_wdc_e6_req_fields, NVME_WDC_E6_REQ_FIELD_OFFSET,
465533affcbSRobert Mustacchi 	NVME_ERR_WDC_E6_OFFSET_RANGE, 0, 0
466533affcbSRobert Mustacchi };
467533affcbSRobert Mustacchi 
468533affcbSRobert Mustacchi bool
nvme_wdc_e6_req_set_offset(nvme_wdc_e6_req_t * req,uint64_t off)469533affcbSRobert Mustacchi nvme_wdc_e6_req_set_offset(nvme_wdc_e6_req_t *req, uint64_t off)
470533affcbSRobert Mustacchi {
471533affcbSRobert Mustacchi 	nvme_ctrl_t *ctrl = req->wer_vuc->nvr_ctrl;
472533affcbSRobert Mustacchi 	uint32_t ndw;
473533affcbSRobert Mustacchi 
474533affcbSRobert Mustacchi 	if (!nvme_field_check_one(ctrl, off, "e6 dump", &nvme_wdc_e6_check_off,
475533affcbSRobert Mustacchi 	    0)) {
476533affcbSRobert Mustacchi 		return (false);
477533affcbSRobert Mustacchi 	}
478533affcbSRobert Mustacchi 
479533affcbSRobert Mustacchi 	ndw = off >> 2;
480533affcbSRobert Mustacchi 	if (!nvme_vuc_req_set_cdw13(req->wer_vuc, ndw)) {
481533affcbSRobert Mustacchi 		return (false);
482533affcbSRobert Mustacchi 	}
483533affcbSRobert Mustacchi 
484533affcbSRobert Mustacchi 	nvme_wdc_e6_req_clear_need(req, NVME_WDC_E6_REQ_FIELD_OFFSET);
485533affcbSRobert Mustacchi 	return (nvme_ctrl_success(ctrl));
486533affcbSRobert Mustacchi }
487533affcbSRobert Mustacchi 
488533affcbSRobert Mustacchi bool
nvme_wdc_e6_req_set_output(nvme_wdc_e6_req_t * req,void * buf,size_t len)489533affcbSRobert Mustacchi nvme_wdc_e6_req_set_output(nvme_wdc_e6_req_t *req, void *buf, size_t len)
490533affcbSRobert Mustacchi {
491533affcbSRobert Mustacchi 	nvme_ctrl_t *ctrl = req->wer_vuc->nvr_ctrl;
492533affcbSRobert Mustacchi 
493533affcbSRobert Mustacchi 	/*
494533affcbSRobert Mustacchi 	 * The set output validation handling takes care of all the actual
495533affcbSRobert Mustacchi 	 * normal field validation work that we need.
496533affcbSRobert Mustacchi 	 */
497533affcbSRobert Mustacchi 	if (!nvme_vuc_req_set_output(req->wer_vuc, buf, len)) {
498533affcbSRobert Mustacchi 		return (false);
499533affcbSRobert Mustacchi 	}
500533affcbSRobert Mustacchi 
501533affcbSRobert Mustacchi 	nvme_wdc_e6_req_clear_need(req, NVME_WDC_E6_REQ_FIELD_LEN);
502533affcbSRobert Mustacchi 	return (nvme_ctrl_success(ctrl));
503533affcbSRobert Mustacchi }
504533affcbSRobert Mustacchi 
505533affcbSRobert Mustacchi bool
nvme_wdc_e6_req_exec(nvme_wdc_e6_req_t * req)506533affcbSRobert Mustacchi nvme_wdc_e6_req_exec(nvme_wdc_e6_req_t *req)
507533affcbSRobert Mustacchi {
508533affcbSRobert Mustacchi 	nvme_ctrl_t *ctrl = req->wer_vuc->nvr_ctrl;
509533affcbSRobert Mustacchi 
510533affcbSRobert Mustacchi 	if (req->wer_need != 0) {
511533affcbSRobert Mustacchi 		return (nvme_field_miss_err(ctrl, nvme_wdc_e6_req_fields,
512533affcbSRobert Mustacchi 		    ARRAY_SIZE(nvme_wdc_e6_req_fields),
513533affcbSRobert Mustacchi 		    NVME_ERR_WDC_E6_REQ_MISSING_FIELDS, "wdc e6",
514533affcbSRobert Mustacchi 		    req->wer_need));
515533affcbSRobert Mustacchi 	}
516533affcbSRobert Mustacchi 
517533affcbSRobert Mustacchi 	if (!nvme_vuc_req_exec(req->wer_vuc)) {
518533affcbSRobert Mustacchi 		return (false);
519533affcbSRobert Mustacchi 	}
520533affcbSRobert Mustacchi 
521533affcbSRobert Mustacchi 	return (nvme_ctrl_success(ctrl));
522533affcbSRobert Mustacchi }
5237a27a99aSRobert Mustacchi 
5247a27a99aSRobert Mustacchi static bool
nvme_wdc_assert_common(nvme_ctrl_t * ctrl,uint32_t subcmd)5257a27a99aSRobert Mustacchi nvme_wdc_assert_common(nvme_ctrl_t *ctrl, uint32_t subcmd)
5267a27a99aSRobert Mustacchi {
5277a27a99aSRobert Mustacchi 	nvme_vuc_req_t *req = NULL;
5287a27a99aSRobert Mustacchi 	const char *name = subcmd == WDC_VUC_ASSERT_SUB_CLEAR ?
5297a27a99aSRobert Mustacchi 	    "wdc/clear-assert" : "wdc/inject-assert";
5307a27a99aSRobert Mustacchi 	uint32_t cdw12 = WDC_VUC_ASSERT_CMD | (subcmd << 8);
5317a27a99aSRobert Mustacchi 
5327a27a99aSRobert Mustacchi 	if (!nvme_vendor_vuc_supported(ctrl, name)) {
5337a27a99aSRobert Mustacchi 		return (false);
5347a27a99aSRobert Mustacchi 	}
5357a27a99aSRobert Mustacchi 
5367a27a99aSRobert Mustacchi 	if (!nvme_vuc_req_init(ctrl, &req)) {
5377a27a99aSRobert Mustacchi 		return (false);
5387a27a99aSRobert Mustacchi 	}
5397a27a99aSRobert Mustacchi 
5407a27a99aSRobert Mustacchi 	if (!nvme_vuc_req_set_opcode(req, WDC_VUC_ASSERT_OPC) ||
5417a27a99aSRobert Mustacchi 	    !nvme_vuc_req_set_cdw12(req, cdw12) ||
5427a27a99aSRobert Mustacchi 	    !nvme_vuc_req_set_timeout(req, nvme_wdc_assert_timeout) ||
5437a27a99aSRobert Mustacchi 	    !nvme_vuc_req_exec(req)) {
5447a27a99aSRobert Mustacchi 		nvme_vuc_req_fini(req);
5457a27a99aSRobert Mustacchi 		return (false);
5467a27a99aSRobert Mustacchi 	}
5477a27a99aSRobert Mustacchi 
5487a27a99aSRobert Mustacchi 	nvme_vuc_req_fini(req);
5497a27a99aSRobert Mustacchi 	return (nvme_ctrl_success(ctrl));
5507a27a99aSRobert Mustacchi }
5517a27a99aSRobert Mustacchi 
5527a27a99aSRobert Mustacchi bool
nvme_wdc_assert_clear(nvme_ctrl_t * ctrl)5537a27a99aSRobert Mustacchi nvme_wdc_assert_clear(nvme_ctrl_t *ctrl)
5547a27a99aSRobert Mustacchi {
5557a27a99aSRobert Mustacchi 	return (nvme_wdc_assert_common(ctrl, WDC_VUC_ASSERT_SUB_CLEAR));
5567a27a99aSRobert Mustacchi }
5577a27a99aSRobert Mustacchi 
5587a27a99aSRobert Mustacchi bool
nvme_wdc_assert_inject(nvme_ctrl_t * ctrl)5597a27a99aSRobert Mustacchi nvme_wdc_assert_inject(nvme_ctrl_t *ctrl)
5607a27a99aSRobert Mustacchi {
5617a27a99aSRobert Mustacchi 	return (nvme_wdc_assert_common(ctrl, WDC_VUC_ASSERT_SUB_INJECT));
5627a27a99aSRobert Mustacchi }
563