|
root / base / usr / src / cmd / nvmeadm / nvmeadm_sandisk.c
nvmeadm_sandisk.c C 161 lines 3.7 KB
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright 2026 Oxide Computer Company
 */

/*
 * Sandisk vendor-specific commands. These generally start with the x6x
 * generation of controllers. For prior generations (e.g. x4x and x5x), see
 * nvmeadm_wdc.c.
 */

#include <stdio.h>
#include <err.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/debug.h>
#include <sys/sysmacros.h>
#include <sys/nvme/wdc.h>

#include "nvmeadm.h"

typedef struct {
	uint8_t se_lane;
	const char *se_output;
} sandisk_eye_t;

void
usage_sandisk_hwrev(const char *c_name)
{
	(void) fprintf(stderr, "%s <ctl>\n\n"
	    "  Print device hardware revision\n", c_name);
}

int
do_sandisk_hwrev(const nvme_process_arg_t *npa)
{
	uint8_t major, minor;
	nvme_vuc_disc_t *vuc;

	if (npa->npa_argc > 0) {
		errx(-1, "%s passed extraneous arguments starting with %s",
		    npa->npa_cmd->c_name, npa->npa_argv[0]);
	}

	vuc = nvmeadm_vuc_init(npa, npa->npa_cmd->c_name);
	if (!nvme_sndk_hw_rev(npa->npa_ctrl, &major, &minor)) {
		nvmeadm_fatal(npa, "failed to retrieve hardware revision");
	}

	(void) printf("%u.%u\n", major, minor);
	nvmeadm_vuc_fini(npa, vuc);
	return (0);
}

void
usage_sandisk_pcieye(const char *c_name)
{
	(void) fprintf(stderr, "%s -l lane -o output <ctl>\n\n"
	    "  Write PCIe eye data from the specified lane to output file.\n",
	    c_name);
}

void
optparse_sandisk_pcieye(nvme_process_arg_t *npa)
{
	int c;
	sandisk_eye_t *eye;

	if ((eye = calloc(1, sizeof (sandisk_eye_t))) == NULL) {
		err(-1, "failed to allocate memory for pci-eye options "
		    "structure");
	}

	eye->se_lane = UINT8_MAX;
	while ((c = getopt(npa->npa_argc, npa->npa_argv, ":l:o:")) != -1) {
		const char *errstr;

		switch (c) {
		case 'l':
			eye->se_lane = (uint8_t)strtonumx(optarg, 0, 3, &errstr,
			    0);
			if (errstr != NULL) {
				errx(-1, "invalid lane specified, valid values "
				    "are 0-3: %s is %s", optarg, errstr);
			}
			break;
		case 'o':
			eye->se_output = optarg;
			break;
		case '?':
			errx(-1, "unknown option: -%c", optopt);
		case ':':
			errx(-1, "option -%c requires an argument", optopt);
		}
	}

	if (eye->se_lane == UINT8_MAX) {
		errx(-1, "missing required PCIe lane (0-3), specify with -l");
	}

	if (eye->se_output == NULL) {
		errx(-1, "missing required output file, specify with -o");
	}

	npa->npa_cmd_arg = eye;
}

int
do_sandisk_pcieye(const nvme_process_arg_t *npa)
{
	const sandisk_eye_t *eye = npa->npa_cmd_arg;
	nvme_vuc_disc_t *vuc;
	uint8_t *buf;
	int ofd;

	if ((buf = calloc(WDC_SN861_VUC_EYE_LEN, sizeof (uint8_t))) == NULL) {
		err(-1, "failed to allocate eye diagram buffer");
	}

	vuc = nvmeadm_vuc_init(npa, npa->npa_cmd->c_name);

	ofd = open(eye->se_output, O_WRONLY | O_CREAT | O_TRUNC, 0644);
	if (ofd < 0) {
		err(-1, "failed to open output file %s", eye->se_output);
	}

	if (!nvme_sndk_pci_eye(npa->npa_ctrl, eye->se_lane, buf,
	    WDC_SN861_VUC_EYE_LEN)) {
		nvmeadm_fatal(npa, "failed to retrieve PCIe eye information");
	}

	size_t off = 0, len = WDC_SN861_VUC_EYE_LEN;
	while (len > 0) {
		size_t towrite = MIN(len, 32 * 1024);
		ssize_t ret = write(ofd, buf + off, towrite);
		if (ret < 0) {
			err(-1, "failed to write eye data to output file");
		}

		off += (size_t)ret;
		len -= (size_t)ret;
	}

	nvmeadm_vuc_fini(npa, vuc);
	VERIFY0(close(ofd));
	free(buf);
	return (0);
}