1 /*
2 * QEMU NVM Express End-to-End Data Protection support
3 *
4 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
5 *
6 * Authors:
7 * Klaus Jensen <k.jensen@samsung.com>
8 * Gollu Appalanaidu <anaidu.gollu@samsung.com>
9 */
10
11 #include "qemu/osdep.h"
12 #include "qapi/error.h"
13 #include "sysemu/block-backend.h"
14
15 #include "nvme.h"
16 #include "dif.h"
17 #include "trace.h"
18
nvme_check_prinfo(NvmeNamespace * ns,uint8_t prinfo,uint64_t slba,uint64_t reftag)19 uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba,
20 uint64_t reftag)
21 {
22 uint64_t mask = ns->pif ? 0xffffffffffff : 0xffffffff;
23
24 if ((NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) == NVME_ID_NS_DPS_TYPE_1) &&
25 (prinfo & NVME_PRINFO_PRCHK_REF) && (slba & mask) != reftag) {
26 return NVME_INVALID_PROT_INFO | NVME_DNR;
27 }
28
29 if ((NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) == NVME_ID_NS_DPS_TYPE_3) &&
30 (prinfo & NVME_PRINFO_PRCHK_REF)) {
31 return NVME_INVALID_PROT_INFO;
32 }
33
34 return NVME_SUCCESS;
35 }
36
37 /* from Linux kernel (crypto/crct10dif_common.c) */
crc16_t10dif(uint16_t crc,const unsigned char * buffer,size_t len)38 static uint16_t crc16_t10dif(uint16_t crc, const unsigned char *buffer,
39 size_t len)
40 {
41 unsigned int i;
42
43 for (i = 0; i < len; i++) {
44 crc = (crc << 8) ^ crc16_t10dif_table[((crc >> 8) ^ buffer[i]) & 0xff];
45 }
46
47 return crc;
48 }
49
50 /* from Linux kernel (lib/crc64.c) */
crc64_nvme(uint64_t crc,const unsigned char * buffer,size_t len)51 static uint64_t crc64_nvme(uint64_t crc, const unsigned char *buffer,
52 size_t len)
53 {
54 size_t i;
55
56 for (i = 0; i < len; i++) {
57 crc = (crc >> 8) ^ crc64_nvme_table[(crc & 0xff) ^ buffer[i]];
58 }
59
60 return crc ^ (uint64_t)~0;
61 }
62
nvme_dif_pract_generate_dif_crc16(NvmeNamespace * ns,uint8_t * buf,size_t len,uint8_t * mbuf,size_t mlen,uint16_t apptag,uint64_t * reftag)63 static void nvme_dif_pract_generate_dif_crc16(NvmeNamespace *ns, uint8_t *buf,
64 size_t len, uint8_t *mbuf,
65 size_t mlen, uint16_t apptag,
66 uint64_t *reftag)
67 {
68 uint8_t *end = buf + len;
69 int16_t pil = 0;
70
71 if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
72 pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
73 }
74
75 trace_pci_nvme_dif_pract_generate_dif_crc16(len, ns->lbasz,
76 ns->lbasz + pil, apptag,
77 *reftag);
78
79 for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) {
80 NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
81 uint16_t crc = crc16_t10dif(0x0, buf, ns->lbasz);
82
83 if (pil) {
84 crc = crc16_t10dif(crc, mbuf, pil);
85 }
86
87 dif->g16.guard = cpu_to_be16(crc);
88 dif->g16.apptag = cpu_to_be16(apptag);
89 dif->g16.reftag = cpu_to_be32(*reftag);
90
91 if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
92 (*reftag)++;
93 }
94 }
95 }
96
nvme_dif_pract_generate_dif_crc64(NvmeNamespace * ns,uint8_t * buf,size_t len,uint8_t * mbuf,size_t mlen,uint16_t apptag,uint64_t * reftag)97 static void nvme_dif_pract_generate_dif_crc64(NvmeNamespace *ns, uint8_t *buf,
98 size_t len, uint8_t *mbuf,
99 size_t mlen, uint16_t apptag,
100 uint64_t *reftag)
101 {
102 uint8_t *end = buf + len;
103 int16_t pil = 0;
104
105 if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
106 pil = ns->lbaf.ms - 16;
107 }
108
109 trace_pci_nvme_dif_pract_generate_dif_crc64(len, ns->lbasz,
110 ns->lbasz + pil, apptag,
111 *reftag);
112
113 for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) {
114 NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
115 uint64_t crc = crc64_nvme(~0ULL, buf, ns->lbasz);
116
117 if (pil) {
118 crc = crc64_nvme(~crc, mbuf, pil);
119 }
120
121 dif->g64.guard = cpu_to_be64(crc);
122 dif->g64.apptag = cpu_to_be16(apptag);
123
124 dif->g64.sr[0] = *reftag >> 40;
125 dif->g64.sr[1] = *reftag >> 32;
126 dif->g64.sr[2] = *reftag >> 24;
127 dif->g64.sr[3] = *reftag >> 16;
128 dif->g64.sr[4] = *reftag >> 8;
129 dif->g64.sr[5] = *reftag;
130
131 if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
132 (*reftag)++;
133 }
134 }
135 }
136
nvme_dif_pract_generate_dif(NvmeNamespace * ns,uint8_t * buf,size_t len,uint8_t * mbuf,size_t mlen,uint16_t apptag,uint64_t * reftag)137 void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len,
138 uint8_t *mbuf, size_t mlen, uint16_t apptag,
139 uint64_t *reftag)
140 {
141 switch (ns->pif) {
142 case NVME_PI_GUARD_16:
143 return nvme_dif_pract_generate_dif_crc16(ns, buf, len, mbuf, mlen,
144 apptag, reftag);
145 case NVME_PI_GUARD_64:
146 return nvme_dif_pract_generate_dif_crc64(ns, buf, len, mbuf, mlen,
147 apptag, reftag);
148 }
149
150 abort();
151 }
152
nvme_dif_prchk_crc16(NvmeNamespace * ns,NvmeDifTuple * dif,uint8_t * buf,uint8_t * mbuf,size_t pil,uint8_t prinfo,uint16_t apptag,uint16_t appmask,uint64_t reftag)153 static uint16_t nvme_dif_prchk_crc16(NvmeNamespace *ns, NvmeDifTuple *dif,
154 uint8_t *buf, uint8_t *mbuf, size_t pil,
155 uint8_t prinfo, uint16_t apptag,
156 uint16_t appmask, uint64_t reftag)
157 {
158 switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
159 case NVME_ID_NS_DPS_TYPE_3:
160 if (be32_to_cpu(dif->g16.reftag) != 0xffffffff) {
161 break;
162 }
163
164 /* fallthrough */
165 case NVME_ID_NS_DPS_TYPE_1:
166 case NVME_ID_NS_DPS_TYPE_2:
167 if (be16_to_cpu(dif->g16.apptag) != 0xffff) {
168 break;
169 }
170
171 trace_pci_nvme_dif_prchk_disabled_crc16(be16_to_cpu(dif->g16.apptag),
172 be32_to_cpu(dif->g16.reftag));
173
174 return NVME_SUCCESS;
175 }
176
177 if (prinfo & NVME_PRINFO_PRCHK_GUARD) {
178 uint16_t crc = crc16_t10dif(0x0, buf, ns->lbasz);
179
180 if (pil) {
181 crc = crc16_t10dif(crc, mbuf, pil);
182 }
183
184 trace_pci_nvme_dif_prchk_guard_crc16(be16_to_cpu(dif->g16.guard), crc);
185
186 if (be16_to_cpu(dif->g16.guard) != crc) {
187 return NVME_E2E_GUARD_ERROR;
188 }
189 }
190
191 if (prinfo & NVME_PRINFO_PRCHK_APP) {
192 trace_pci_nvme_dif_prchk_apptag(be16_to_cpu(dif->g16.apptag), apptag,
193 appmask);
194
195 if ((be16_to_cpu(dif->g16.apptag) & appmask) != (apptag & appmask)) {
196 return NVME_E2E_APP_ERROR;
197 }
198 }
199
200 if (prinfo & NVME_PRINFO_PRCHK_REF) {
201 trace_pci_nvme_dif_prchk_reftag_crc16(be32_to_cpu(dif->g16.reftag),
202 reftag);
203
204 if (be32_to_cpu(dif->g16.reftag) != reftag) {
205 return NVME_E2E_REF_ERROR;
206 }
207 }
208
209 return NVME_SUCCESS;
210 }
211
nvme_dif_prchk_crc64(NvmeNamespace * ns,NvmeDifTuple * dif,uint8_t * buf,uint8_t * mbuf,size_t pil,uint8_t prinfo,uint16_t apptag,uint16_t appmask,uint64_t reftag)212 static uint16_t nvme_dif_prchk_crc64(NvmeNamespace *ns, NvmeDifTuple *dif,
213 uint8_t *buf, uint8_t *mbuf, size_t pil,
214 uint8_t prinfo, uint16_t apptag,
215 uint16_t appmask, uint64_t reftag)
216 {
217 uint64_t r = 0;
218
219 r |= (uint64_t)dif->g64.sr[0] << 40;
220 r |= (uint64_t)dif->g64.sr[1] << 32;
221 r |= (uint64_t)dif->g64.sr[2] << 24;
222 r |= (uint64_t)dif->g64.sr[3] << 16;
223 r |= (uint64_t)dif->g64.sr[4] << 8;
224 r |= (uint64_t)dif->g64.sr[5];
225
226 switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
227 case NVME_ID_NS_DPS_TYPE_3:
228 if (r != 0xffffffffffff) {
229 break;
230 }
231
232 /* fallthrough */
233 case NVME_ID_NS_DPS_TYPE_1:
234 case NVME_ID_NS_DPS_TYPE_2:
235 if (be16_to_cpu(dif->g64.apptag) != 0xffff) {
236 break;
237 }
238
239 trace_pci_nvme_dif_prchk_disabled_crc64(be16_to_cpu(dif->g16.apptag),
240 r);
241
242 return NVME_SUCCESS;
243 }
244
245 if (prinfo & NVME_PRINFO_PRCHK_GUARD) {
246 uint64_t crc = crc64_nvme(~0ULL, buf, ns->lbasz);
247
248 if (pil) {
249 crc = crc64_nvme(~crc, mbuf, pil);
250 }
251
252 trace_pci_nvme_dif_prchk_guard_crc64(be64_to_cpu(dif->g64.guard), crc);
253
254 if (be64_to_cpu(dif->g64.guard) != crc) {
255 return NVME_E2E_GUARD_ERROR;
256 }
257 }
258
259 if (prinfo & NVME_PRINFO_PRCHK_APP) {
260 trace_pci_nvme_dif_prchk_apptag(be16_to_cpu(dif->g64.apptag), apptag,
261 appmask);
262
263 if ((be16_to_cpu(dif->g64.apptag) & appmask) != (apptag & appmask)) {
264 return NVME_E2E_APP_ERROR;
265 }
266 }
267
268 if (prinfo & NVME_PRINFO_PRCHK_REF) {
269 trace_pci_nvme_dif_prchk_reftag_crc64(r, reftag);
270
271 if (r != reftag) {
272 return NVME_E2E_REF_ERROR;
273 }
274 }
275
276 return NVME_SUCCESS;
277 }
278
nvme_dif_prchk(NvmeNamespace * ns,NvmeDifTuple * dif,uint8_t * buf,uint8_t * mbuf,size_t pil,uint8_t prinfo,uint16_t apptag,uint16_t appmask,uint64_t reftag)279 static uint16_t nvme_dif_prchk(NvmeNamespace *ns, NvmeDifTuple *dif,
280 uint8_t *buf, uint8_t *mbuf, size_t pil,
281 uint8_t prinfo, uint16_t apptag,
282 uint16_t appmask, uint64_t reftag)
283 {
284 switch (ns->pif) {
285 case NVME_PI_GUARD_16:
286 return nvme_dif_prchk_crc16(ns, dif, buf, mbuf, pil, prinfo, apptag,
287 appmask, reftag);
288 case NVME_PI_GUARD_64:
289 return nvme_dif_prchk_crc64(ns, dif, buf, mbuf, pil, prinfo, apptag,
290 appmask, reftag);
291 }
292
293 abort();
294 }
295
nvme_dif_check(NvmeNamespace * ns,uint8_t * buf,size_t len,uint8_t * mbuf,size_t mlen,uint8_t prinfo,uint64_t slba,uint16_t apptag,uint16_t appmask,uint64_t * reftag)296 uint16_t nvme_dif_check(NvmeNamespace *ns, uint8_t *buf, size_t len,
297 uint8_t *mbuf, size_t mlen, uint8_t prinfo,
298 uint64_t slba, uint16_t apptag,
299 uint16_t appmask, uint64_t *reftag)
300 {
301 uint8_t *bufp, *end = buf + len;
302 int16_t pil = 0;
303 uint16_t status;
304
305 status = nvme_check_prinfo(ns, prinfo, slba, *reftag);
306 if (status) {
307 return status;
308 }
309
310 if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
311 pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
312 }
313
314 trace_pci_nvme_dif_check(prinfo, ns->lbasz + pil);
315
316 for (bufp = buf; bufp < end; bufp += ns->lbasz, mbuf += ns->lbaf.ms) {
317 NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
318 status = nvme_dif_prchk(ns, dif, bufp, mbuf, pil, prinfo, apptag,
319 appmask, *reftag);
320 if (status) {
321 /*
322 * The first block of a 'raw' image is always allocated, so we
323 * cannot reliably know if the block is all zeroes or not. For
324 * CRC16 this works fine because the T10 CRC16 is 0x0 for all
325 * zeroes, but the Rocksoft CRC64 is not. Thus, if a guard error is
326 * detected for the first block, check if it is zeroed and manually
327 * set the protection information to all ones to disable protection
328 * information checking.
329 */
330 if (status == NVME_E2E_GUARD_ERROR && slba == 0x0 && bufp == buf) {
331 g_autofree uint8_t *zeroes = g_malloc0(ns->lbasz);
332
333 if (memcmp(bufp, zeroes, ns->lbasz) == 0) {
334 memset(mbuf + pil, 0xff, nvme_pi_tuple_size(ns));
335 }
336 } else {
337 return status;
338 }
339 }
340
341 if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
342 (*reftag)++;
343 }
344 }
345
346 return NVME_SUCCESS;
347 }
348
nvme_dif_mangle_mdata(NvmeNamespace * ns,uint8_t * mbuf,size_t mlen,uint64_t slba)349 uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen,
350 uint64_t slba)
351 {
352 BlockBackend *blk = ns->blkconf.blk;
353 BlockDriverState *bs = blk_bs(blk);
354
355 int64_t moffset = 0, offset = nvme_l2b(ns, slba);
356 uint8_t *mbufp, *end;
357 bool zeroed;
358 int16_t pil = 0;
359 int64_t bytes = (mlen / ns->lbaf.ms) << ns->lbaf.ds;
360 int64_t pnum = 0;
361
362 Error *err = NULL;
363
364
365 if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
366 pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
367 }
368
369 do {
370 int ret;
371
372 bytes -= pnum;
373
374 ret = bdrv_block_status(bs, offset, bytes, &pnum, NULL, NULL);
375 if (ret < 0) {
376 error_setg_errno(&err, -ret, "unable to get block status");
377 error_report_err(err);
378
379 return NVME_INTERNAL_DEV_ERROR;
380 }
381
382 zeroed = !!(ret & BDRV_BLOCK_ZERO);
383
384 trace_pci_nvme_block_status(offset, bytes, pnum, ret, zeroed);
385
386 if (zeroed) {
387 mbufp = mbuf + moffset;
388 mlen = (pnum >> ns->lbaf.ds) * ns->lbaf.ms;
389 end = mbufp + mlen;
390
391 for (; mbufp < end; mbufp += ns->lbaf.ms) {
392 memset(mbufp + pil, 0xff, nvme_pi_tuple_size(ns));
393 }
394 }
395
396 moffset += (pnum >> ns->lbaf.ds) * ns->lbaf.ms;
397 offset += pnum;
398 } while (pnum != bytes);
399
400 return NVME_SUCCESS;
401 }
402
nvme_dif_rw_cb(void * opaque,int ret)403 static void nvme_dif_rw_cb(void *opaque, int ret)
404 {
405 NvmeBounceContext *ctx = opaque;
406 NvmeRequest *req = ctx->req;
407 NvmeNamespace *ns = req->ns;
408 BlockBackend *blk = ns->blkconf.blk;
409
410 trace_pci_nvme_dif_rw_cb(nvme_cid(req), blk_name(blk));
411
412 qemu_iovec_destroy(&ctx->data.iov);
413 g_free(ctx->data.bounce);
414
415 qemu_iovec_destroy(&ctx->mdata.iov);
416 g_free(ctx->mdata.bounce);
417
418 g_free(ctx);
419
420 nvme_rw_complete_cb(req, ret);
421 }
422
nvme_dif_rw_check_cb(void * opaque,int ret)423 static void nvme_dif_rw_check_cb(void *opaque, int ret)
424 {
425 NvmeBounceContext *ctx = opaque;
426 NvmeRequest *req = ctx->req;
427 NvmeNamespace *ns = req->ns;
428 NvmeCtrl *n = nvme_ctrl(req);
429 NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
430 uint64_t slba = le64_to_cpu(rw->slba);
431 uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
432 uint16_t apptag = le16_to_cpu(rw->apptag);
433 uint16_t appmask = le16_to_cpu(rw->appmask);
434 uint64_t reftag = le32_to_cpu(rw->reftag);
435 uint64_t cdw3 = le32_to_cpu(rw->cdw3);
436 uint16_t status;
437
438 reftag |= cdw3 << 32;
439
440 trace_pci_nvme_dif_rw_check_cb(nvme_cid(req), prinfo, apptag, appmask,
441 reftag);
442
443 if (ret) {
444 goto out;
445 }
446
447 status = nvme_dif_mangle_mdata(ns, ctx->mdata.bounce, ctx->mdata.iov.size,
448 slba);
449 if (status) {
450 req->status = status;
451 goto out;
452 }
453
454 status = nvme_dif_check(ns, ctx->data.bounce, ctx->data.iov.size,
455 ctx->mdata.bounce, ctx->mdata.iov.size, prinfo,
456 slba, apptag, appmask, &reftag);
457 if (status) {
458 req->status = status;
459 goto out;
460 }
461
462 status = nvme_bounce_data(n, ctx->data.bounce, ctx->data.iov.size,
463 NVME_TX_DIRECTION_FROM_DEVICE, req);
464 if (status) {
465 req->status = status;
466 goto out;
467 }
468
469 if (prinfo & NVME_PRINFO_PRACT && ns->lbaf.ms == nvme_pi_tuple_size(ns)) {
470 goto out;
471 }
472
473 status = nvme_bounce_mdata(n, ctx->mdata.bounce, ctx->mdata.iov.size,
474 NVME_TX_DIRECTION_FROM_DEVICE, req);
475 if (status) {
476 req->status = status;
477 }
478
479 out:
480 nvme_dif_rw_cb(ctx, ret);
481 }
482
nvme_dif_rw_mdata_in_cb(void * opaque,int ret)483 static void nvme_dif_rw_mdata_in_cb(void *opaque, int ret)
484 {
485 NvmeBounceContext *ctx = opaque;
486 NvmeRequest *req = ctx->req;
487 NvmeNamespace *ns = req->ns;
488 NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
489 uint64_t slba = le64_to_cpu(rw->slba);
490 uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
491 size_t mlen = nvme_m2b(ns, nlb);
492 uint64_t offset = nvme_moff(ns, slba);
493 BlockBackend *blk = ns->blkconf.blk;
494
495 trace_pci_nvme_dif_rw_mdata_in_cb(nvme_cid(req), blk_name(blk));
496
497 if (ret) {
498 goto out;
499 }
500
501 ctx->mdata.bounce = g_malloc(mlen);
502
503 qemu_iovec_reset(&ctx->mdata.iov);
504 qemu_iovec_add(&ctx->mdata.iov, ctx->mdata.bounce, mlen);
505
506 req->aiocb = blk_aio_preadv(blk, offset, &ctx->mdata.iov, 0,
507 nvme_dif_rw_check_cb, ctx);
508 return;
509
510 out:
511 nvme_dif_rw_cb(ctx, ret);
512 }
513
nvme_dif_rw_mdata_out_cb(void * opaque,int ret)514 static void nvme_dif_rw_mdata_out_cb(void *opaque, int ret)
515 {
516 NvmeBounceContext *ctx = opaque;
517 NvmeRequest *req = ctx->req;
518 NvmeNamespace *ns = req->ns;
519 NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
520 uint64_t slba = le64_to_cpu(rw->slba);
521 uint64_t offset = nvme_moff(ns, slba);
522 BlockBackend *blk = ns->blkconf.blk;
523
524 trace_pci_nvme_dif_rw_mdata_out_cb(nvme_cid(req), blk_name(blk));
525
526 if (ret) {
527 goto out;
528 }
529
530 req->aiocb = blk_aio_pwritev(blk, offset, &ctx->mdata.iov, 0,
531 nvme_dif_rw_cb, ctx);
532 return;
533
534 out:
535 nvme_dif_rw_cb(ctx, ret);
536 }
537
nvme_dif_rw(NvmeCtrl * n,NvmeRequest * req)538 uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
539 {
540 NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
541 NvmeNamespace *ns = req->ns;
542 BlockBackend *blk = ns->blkconf.blk;
543 bool wrz = rw->opcode == NVME_CMD_WRITE_ZEROES;
544 uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
545 uint64_t slba = le64_to_cpu(rw->slba);
546 size_t len = nvme_l2b(ns, nlb);
547 size_t mlen = nvme_m2b(ns, nlb);
548 size_t mapped_len = len;
549 int64_t offset = nvme_l2b(ns, slba);
550 uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
551 uint16_t apptag = le16_to_cpu(rw->apptag);
552 uint16_t appmask = le16_to_cpu(rw->appmask);
553 uint64_t reftag = le32_to_cpu(rw->reftag);
554 uint64_t cdw3 = le32_to_cpu(rw->cdw3);
555 bool pract = !!(prinfo & NVME_PRINFO_PRACT);
556 NvmeBounceContext *ctx;
557 uint16_t status;
558
559 reftag |= cdw3 << 32;
560
561 trace_pci_nvme_dif_rw(pract, prinfo);
562
563 ctx = g_new0(NvmeBounceContext, 1);
564 ctx->req = req;
565
566 if (wrz) {
567 BdrvRequestFlags flags = BDRV_REQ_MAY_UNMAP;
568
569 if (prinfo & NVME_PRINFO_PRCHK_MASK) {
570 status = NVME_INVALID_PROT_INFO | NVME_DNR;
571 goto err;
572 }
573
574 if (pract) {
575 uint8_t *mbuf, *end;
576 int16_t pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
577
578 status = nvme_check_prinfo(ns, prinfo, slba, reftag);
579 if (status) {
580 goto err;
581 }
582
583 flags = 0;
584
585 ctx->mdata.bounce = g_malloc0(mlen);
586
587 qemu_iovec_init(&ctx->mdata.iov, 1);
588 qemu_iovec_add(&ctx->mdata.iov, ctx->mdata.bounce, mlen);
589
590 mbuf = ctx->mdata.bounce;
591 end = mbuf + mlen;
592
593 if (ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT) {
594 pil = 0;
595 }
596
597 for (; mbuf < end; mbuf += ns->lbaf.ms) {
598 NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
599
600 switch (ns->pif) {
601 case NVME_PI_GUARD_16:
602 dif->g16.apptag = cpu_to_be16(apptag);
603 dif->g16.reftag = cpu_to_be32(reftag);
604
605 break;
606
607 case NVME_PI_GUARD_64:
608 dif->g64.guard = cpu_to_be64(0x6482d367eb22b64e);
609 dif->g64.apptag = cpu_to_be16(apptag);
610
611 dif->g64.sr[0] = reftag >> 40;
612 dif->g64.sr[1] = reftag >> 32;
613 dif->g64.sr[2] = reftag >> 24;
614 dif->g64.sr[3] = reftag >> 16;
615 dif->g64.sr[4] = reftag >> 8;
616 dif->g64.sr[5] = reftag;
617
618 break;
619
620 default:
621 abort();
622 }
623
624 switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
625 case NVME_ID_NS_DPS_TYPE_1:
626 case NVME_ID_NS_DPS_TYPE_2:
627 reftag++;
628 }
629 }
630 }
631
632 req->aiocb = blk_aio_pwrite_zeroes(blk, offset, len, flags,
633 nvme_dif_rw_mdata_out_cb, ctx);
634 return NVME_NO_COMPLETE;
635 }
636
637 if (nvme_ns_ext(ns) && !(pract && ns->lbaf.ms == nvme_pi_tuple_size(ns))) {
638 mapped_len += mlen;
639 }
640
641 status = nvme_map_dptr(n, &req->sg, mapped_len, &req->cmd);
642 if (status) {
643 goto err;
644 }
645
646 ctx->data.bounce = g_malloc(len);
647
648 qemu_iovec_init(&ctx->data.iov, 1);
649 qemu_iovec_add(&ctx->data.iov, ctx->data.bounce, len);
650
651 if (req->cmd.opcode == NVME_CMD_READ) {
652 block_acct_start(blk_get_stats(blk), &req->acct, ctx->data.iov.size,
653 BLOCK_ACCT_READ);
654
655 req->aiocb = blk_aio_preadv(ns->blkconf.blk, offset, &ctx->data.iov, 0,
656 nvme_dif_rw_mdata_in_cb, ctx);
657 return NVME_NO_COMPLETE;
658 }
659
660 status = nvme_bounce_data(n, ctx->data.bounce, ctx->data.iov.size,
661 NVME_TX_DIRECTION_TO_DEVICE, req);
662 if (status) {
663 goto err;
664 }
665
666 ctx->mdata.bounce = g_malloc(mlen);
667
668 qemu_iovec_init(&ctx->mdata.iov, 1);
669 qemu_iovec_add(&ctx->mdata.iov, ctx->mdata.bounce, mlen);
670
671 if (!(pract && ns->lbaf.ms == nvme_pi_tuple_size(ns))) {
672 status = nvme_bounce_mdata(n, ctx->mdata.bounce, ctx->mdata.iov.size,
673 NVME_TX_DIRECTION_TO_DEVICE, req);
674 if (status) {
675 goto err;
676 }
677 }
678
679 status = nvme_check_prinfo(ns, prinfo, slba, reftag);
680 if (status) {
681 goto err;
682 }
683
684 if (pract) {
685 /* splice generated protection information into the buffer */
686 nvme_dif_pract_generate_dif(ns, ctx->data.bounce, ctx->data.iov.size,
687 ctx->mdata.bounce, ctx->mdata.iov.size,
688 apptag, &reftag);
689 } else {
690 status = nvme_dif_check(ns, ctx->data.bounce, ctx->data.iov.size,
691 ctx->mdata.bounce, ctx->mdata.iov.size, prinfo,
692 slba, apptag, appmask, &reftag);
693 if (status) {
694 goto err;
695 }
696 }
697
698 block_acct_start(blk_get_stats(blk), &req->acct, ctx->data.iov.size,
699 BLOCK_ACCT_WRITE);
700
701 req->aiocb = blk_aio_pwritev(ns->blkconf.blk, offset, &ctx->data.iov, 0,
702 nvme_dif_rw_mdata_out_cb, ctx);
703
704 return NVME_NO_COMPLETE;
705
706 err:
707 qemu_iovec_destroy(&ctx->data.iov);
708 g_free(ctx->data.bounce);
709
710 qemu_iovec_destroy(&ctx->mdata.iov);
711 g_free(ctx->mdata.bounce);
712
713 g_free(ctx);
714
715 return status;
716 }
717