aboutsummaryrefslogtreecommitdiff
path: root/include/sg_pt_nvme.h
blob: 06f8f0f4434a591459ea21c3ad911780332881e5 (plain)
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
#ifndef SG_PT_NVME_H
#define SG_PT_NVME_H

/*
 * Copyright (c) 2017-2019 Douglas Gilbert.
 * All rights reserved.
 * Use of this source code is governed by a BSD-style
 * license that can be found in the BSD_LICENSE file.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <stdint.h>
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

/* structures copied and slightly modified from <linux/nvme_ioctl.h> which
 * is Copyright (c) 2011-2014, Intel Corporation.  */


/* Note that the command input structure is in (packed) "cpu" format. That
 * means, for example, if the CPU is little endian (most are) then so is the
 * structure. However what comes out in the data-in buffer (e.g. for the
 * Admin Identify command response) is almost all little endian following ATA
 * (but no SCSI and IP which are big endian) and Intel's preference. There
 * are exceptions, for example the EUI-64 identifiers in the Admin Identify
 * response are big endian.
 *
 * Code online (e.g. nvme-cli at github.com) seems to favour packed
 * structures, while the author prefers byte offset plus a range of unaligned
 * integer builders such as those in sg_unaligned.h .
 */

#ifdef __GNUC__
#ifndef __clang__
  struct __attribute__((__packed__)) sg_nvme_user_io
#else
  struct sg_nvme_user_io
#endif
#else
struct sg_nvme_user_io
#endif
{
        uint8_t opcode;
        uint8_t flags;
        uint16_t control;
        uint16_t nblocks;
        uint16_t rsvd;
        uint64_t metadata;
        uint64_t addr;
        uint64_t slba;
        uint32_t dsmgmt;
        uint32_t reftag;
        uint16_t apptag;
        uint16_t appmask;
}
#ifdef SG_LIB_FREEBSD
__packed;
#else
;
#endif

/* Using byte offsets and unaligned be/le copies safer than packed
 * structures. These are for sg_nvme_user_io . */
#define SG_NVME_IO_OPCODE 0
#define SG_NVME_IO_FLAGS 1
#define SG_NVME_IO_CONTROL 2
#define SG_NVME_IO_NBLOCKS 4
#define SG_NVME_IO_RSVD 6
#define SG_NVME_IO_METADATA 8
#define SG_NVME_IO_ADDR 16
#define SG_NVME_IO_SLBA 24
#define SG_NVME_IO_DSMGMT 32
#define SG_NVME_IO_REFTAG 36
#define SG_NVME_IO_APPTAG 40
#define SG_NVME_IO_APPMASK 42

#ifdef __GNUC__
#ifndef __clang__
  struct __attribute__((__packed__)) sg_nvme_passthru_cmd
#else
  struct sg_nvme_passthru_cmd
#endif
#else
struct sg_nvme_passthru_cmd
#endif
{
        uint8_t opcode;
        uint8_t flags;
        uint16_t rsvd1;
        uint32_t nsid;
        uint32_t cdw2;
        uint32_t cdw3;
        uint64_t metadata;
        uint64_t addr;
        uint32_t metadata_len;
        uint32_t data_len;
        uint32_t cdw10;
        uint32_t cdw11;
        uint32_t cdw12;
        uint32_t cdw13;
        uint32_t cdw14;
        uint32_t cdw15;
#ifdef SG_LIB_LINUX
        uint32_t timeout_ms;
        uint32_t result;        /* out: DWord(0) from completion queue */
#endif
}
#ifdef SG_LIB_FREEBSD
__packed;
#else
;
#endif

struct sg_sntl_dev_state_t {
    uint8_t scsi_dsense;
    uint8_t enclosure_override; /* ENC_OV in sdparm */
    uint8_t pdt;        /* 6 bit value in INQUIRY response */
    uint8_t enc_serv;   /* single bit in INQUIRY response */
    uint8_t id_ctl253;  /* NVMSR field of Identify controller (byte 253) */
    bool wce;		/* Write Cache Enable (WCE) setting */
    bool wce_changed;	/* WCE setting has been changed */
};

struct sg_sntl_result_t {
    uint8_t sstatus;
    uint8_t sk;
    uint8_t asc;
    uint8_t ascq;
    uint8_t in_byte;
    uint8_t in_bit;     /* use 255 for 'no bit position given' */
};

struct sg_opcode_info_t {
    uint8_t opcode;
    uint16_t sa;            /* service action, 0 for none */
    uint32_t flags;         /* OR-ed set of F_* flags */
    uint8_t len_mask[16];   /* len=len_mask[0], then mask for cdb[1]... */
                            /* ignore cdb bytes after position 15 */
};

/* Using byte offsets and unaligned be/le copies safer than packed
 * structures. These are for sg_nvme_passthru_cmd . */
#define SG_NVME_PT_OPCODE 0             /* length: 1 byte */
#define SG_NVME_PT_FLAGS 1              /* length: 1 byte */
#define SG_NVME_PT_RSVD1 2              /* length: 2 bytes */
#define SG_NVME_PT_NSID 4               /* length: 4 bytes */
#define SG_NVME_PT_CDW2 8               /* length: 4 bytes */
#define SG_NVME_PT_CDW3 12              /* length: 4 bytes */
#define SG_NVME_PT_METADATA 16          /* length: 8 bytes */
#define SG_NVME_PT_ADDR 24              /* length: 8 bytes */
#define SG_NVME_PT_METADATA_LEN 32      /* length: 4 bytes */
#define SG_NVME_PT_DATA_LEN 36          /* length: 4 bytes */
#define SG_NVME_PT_CDW10 40             /* length: 4 bytes */
#define SG_NVME_PT_CDW11 44             /* length: 4 bytes */
#define SG_NVME_PT_CDW12 48             /* length: 4 bytes */
#define SG_NVME_PT_CDW13 52             /* length: 4 bytes */
#define SG_NVME_PT_CDW14 56             /* length: 4 bytes */
#define SG_NVME_PT_CDW15 60             /* length: 4 bytes */

#ifdef SG_LIB_LINUX
/* General references state that "all NVMe commands are 64 bytes long". If
 * so then the following are add-ons by Linux, go to the OS and not the
 * the NVMe device. */
#define SG_NVME_PT_TIMEOUT_MS 64        /* length: 4 bytes */
#define SG_NVME_PT_RESULT 68            /* length: 4 bytes */
#endif

/* Byte offset of Result and Status (plus phase bit) in CQ */
#define SG_NVME_PT_CQ_RESULT 0          /* CDW0, length: 4 bytes */
#define SG_NVME_PT_CQ_DW0 0             /* CDW0, length: 4 bytes */
#define SG_NVME_PT_CQ_DW1 4             /* CDW1, length: 4 bytes */
#define SG_NVME_PT_CQ_DW2 8             /* CDW2, length: 4 bytes */
#define SG_NVME_PT_CQ_DW3 12            /* CDW3, length: 4 bytes */
#define SG_NVME_PT_CQ_STATUS_P 14       /* CDW3 31:16, length: 2 bytes */


/* Valid namespace IDs (nsid_s) range from 1 to 0xfffffffe, leaving: */
#define SG_NVME_BROADCAST_NSID 0xffffffff       /* all namespaces */
#define SG_NVME_CTL_NSID 0x0            /* the "controller's" namespace */

/* Vendor specific (sg3_utils) VPD pages */
#define SG_NVME_VPD_NICR 0xde   /* NVME Identify controller response */


/* Given the NVMe Identify Controller response and optionally the NVMe
 * Identify Namespace response (NULL otherwise), generate the SCSI VPD
 * page 0x83 (device identification) descriptor(s) in dop. Return the
 * number of bytes written which will not exceed max_do_len. Probably use
 * Peripheral Device Type (pdt) of 0 (disk) for don't know. Transport
 * protocol (tproto) should be -1 if not known, else SCSI value.
 * N.B. Does not write total VPD page length into dop[2:3] . */
int sg_make_vpd_devid_for_nvme(const uint8_t * nvme_id_ctl_p,
                               const uint8_t * nvme_id_ns_p, int pdt,
                               int tproto, uint8_t * dop, int max_do_len);

/* Initialize dev_stat pointed to by dsp */
void sntl_init_dev_stat(struct sg_sntl_dev_state_t * dsp);

/* Internal function (common to all OSes) to support the SNTL SCSI MODE
 * SENSE(10) command. Has a vendor specific Unit Attention mpage which
 * has only one field currently: ENC_OV (enclosure override) */
int sntl_resp_mode_sense10(const struct sg_sntl_dev_state_t * dsp,
                           const uint8_t * cdbp, uint8_t * dip, int mx_di_len,
                           struct sg_sntl_result_t * resp);

/* Internal function (common to all OSes) to support the SNTL SCSI MODE
 * SELECT(10) command. */
int sntl_resp_mode_select10(struct sg_sntl_dev_state_t * dsp,
                            const uint8_t * cdbp, const uint8_t * dop,
                            int do_len, struct sg_sntl_result_t * resp);

/* Returns pointer to array of struct sg_opcode_info_t of SCSI commands
 * translated to NVMe. */
const struct sg_opcode_info_t * sg_get_opcode_translation(void);

#ifdef __cplusplus
}
#endif

#endif          /* SG_PT_NVME_H */