xref: /openbmc/qemu/hw/nvme/dif.c (revision 44a9394b1d272b53306d097d4bc20ff7ad14b159)
188eea45cSKlaus Jensen /*
288eea45cSKlaus Jensen  * QEMU NVM Express End-to-End Data Protection support
388eea45cSKlaus Jensen  *
488eea45cSKlaus Jensen  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
588eea45cSKlaus Jensen  *
688eea45cSKlaus Jensen  * Authors:
788eea45cSKlaus Jensen  *   Klaus Jensen           <k.jensen@samsung.com>
888eea45cSKlaus Jensen  *   Gollu Appalanaidu      <anaidu.gollu@samsung.com>
988eea45cSKlaus Jensen  */
1088eea45cSKlaus Jensen 
1188eea45cSKlaus Jensen #include "qemu/osdep.h"
1288eea45cSKlaus Jensen #include "qapi/error.h"
1388eea45cSKlaus Jensen #include "sysemu/block-backend.h"
1488eea45cSKlaus Jensen 
1588eea45cSKlaus Jensen #include "nvme.h"
1605f7ae45SKlaus Jensen #include "dif.h"
1788eea45cSKlaus Jensen #include "trace.h"
1888eea45cSKlaus Jensen 
nvme_check_prinfo(NvmeNamespace * ns,uint8_t prinfo,uint64_t slba,uint64_t reftag)192a132309SKlaus Jensen uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba,
2044219b60SNaveen Nagar                            uint64_t reftag)
2188eea45cSKlaus Jensen {
2244219b60SNaveen Nagar     uint64_t mask = ns->pif ? 0xffffffffffff : 0xffffffff;
2344219b60SNaveen Nagar 
2488eea45cSKlaus Jensen     if ((NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) == NVME_ID_NS_DPS_TYPE_1) &&
2544219b60SNaveen Nagar         (prinfo & NVME_PRINFO_PRCHK_REF) && (slba & mask) != reftag) {
2688eea45cSKlaus Jensen         return NVME_INVALID_PROT_INFO | NVME_DNR;
2788eea45cSKlaus Jensen     }
2888eea45cSKlaus Jensen 
29d7fe639cSDmitry Tikhov     if ((NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) == NVME_ID_NS_DPS_TYPE_3) &&
30d7fe639cSDmitry Tikhov         (prinfo & NVME_PRINFO_PRCHK_REF)) {
31d7fe639cSDmitry Tikhov         return NVME_INVALID_PROT_INFO;
32d7fe639cSDmitry Tikhov     }
33d7fe639cSDmitry Tikhov 
3488eea45cSKlaus Jensen     return NVME_SUCCESS;
3588eea45cSKlaus Jensen }
3688eea45cSKlaus Jensen 
3788eea45cSKlaus Jensen /* from Linux kernel (crypto/crct10dif_common.c) */
crc16_t10dif(uint16_t crc,const unsigned char * buffer,size_t len)3844219b60SNaveen Nagar static uint16_t crc16_t10dif(uint16_t crc, const unsigned char *buffer,
3988eea45cSKlaus Jensen                              size_t len)
4088eea45cSKlaus Jensen {
4188eea45cSKlaus Jensen     unsigned int i;
4288eea45cSKlaus Jensen 
4388eea45cSKlaus Jensen     for (i = 0; i < len; i++) {
4444219b60SNaveen Nagar         crc = (crc << 8) ^ crc16_t10dif_table[((crc >> 8) ^ buffer[i]) & 0xff];
4588eea45cSKlaus Jensen     }
4688eea45cSKlaus Jensen 
4788eea45cSKlaus Jensen     return crc;
4888eea45cSKlaus Jensen }
4988eea45cSKlaus Jensen 
5044219b60SNaveen Nagar /* from Linux kernel (lib/crc64.c) */
crc64_nvme(uint64_t crc,const unsigned char * buffer,size_t len)5144219b60SNaveen Nagar static uint64_t crc64_nvme(uint64_t crc, const unsigned char *buffer,
5244219b60SNaveen Nagar                            size_t len)
5344219b60SNaveen Nagar {
5444219b60SNaveen Nagar     size_t i;
5544219b60SNaveen Nagar 
5644219b60SNaveen Nagar     for (i = 0; i < len; i++) {
5744219b60SNaveen Nagar         crc = (crc >> 8) ^ crc64_nvme_table[(crc & 0xff) ^ buffer[i]];
5844219b60SNaveen Nagar     }
5944219b60SNaveen Nagar 
6044219b60SNaveen Nagar     return crc ^ (uint64_t)~0;
6144219b60SNaveen Nagar }
6244219b60SNaveen Nagar 
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)6344219b60SNaveen Nagar static void nvme_dif_pract_generate_dif_crc16(NvmeNamespace *ns, uint8_t *buf,
6444219b60SNaveen Nagar                                               size_t len, uint8_t *mbuf,
6544219b60SNaveen Nagar                                               size_t mlen, uint16_t apptag,
6644219b60SNaveen Nagar                                               uint64_t *reftag)
6788eea45cSKlaus Jensen {
6888eea45cSKlaus Jensen     uint8_t *end = buf + len;
6988eea45cSKlaus Jensen     int16_t pil = 0;
7088eea45cSKlaus Jensen 
7188eea45cSKlaus Jensen     if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
72ac0b34c5SKlaus Jensen         pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
7388eea45cSKlaus Jensen     }
7488eea45cSKlaus Jensen 
7544219b60SNaveen Nagar     trace_pci_nvme_dif_pract_generate_dif_crc16(len, ns->lbasz,
7644219b60SNaveen Nagar                                                 ns->lbasz + pil, apptag,
7744219b60SNaveen Nagar                                                 *reftag);
7888eea45cSKlaus Jensen 
7988eea45cSKlaus Jensen     for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) {
8088eea45cSKlaus Jensen         NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
8144219b60SNaveen Nagar         uint16_t crc = crc16_t10dif(0x0, buf, ns->lbasz);
8288eea45cSKlaus Jensen 
8388eea45cSKlaus Jensen         if (pil) {
8444219b60SNaveen Nagar             crc = crc16_t10dif(crc, mbuf, pil);
8588eea45cSKlaus Jensen         }
8688eea45cSKlaus Jensen 
8744219b60SNaveen Nagar         dif->g16.guard = cpu_to_be16(crc);
8844219b60SNaveen Nagar         dif->g16.apptag = cpu_to_be16(apptag);
8944219b60SNaveen Nagar         dif->g16.reftag = cpu_to_be32(*reftag);
9088eea45cSKlaus Jensen 
9188eea45cSKlaus Jensen         if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
920ca5c3ccSKlaus Jensen             (*reftag)++;
9388eea45cSKlaus Jensen         }
9488eea45cSKlaus Jensen     }
9588eea45cSKlaus Jensen }
9688eea45cSKlaus Jensen 
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)9744219b60SNaveen Nagar static void nvme_dif_pract_generate_dif_crc64(NvmeNamespace *ns, uint8_t *buf,
9844219b60SNaveen Nagar                                               size_t len, uint8_t *mbuf,
9944219b60SNaveen Nagar                                               size_t mlen, uint16_t apptag,
10044219b60SNaveen Nagar                                               uint64_t *reftag)
10144219b60SNaveen Nagar {
10244219b60SNaveen Nagar     uint8_t *end = buf + len;
10344219b60SNaveen Nagar     int16_t pil = 0;
10444219b60SNaveen Nagar 
10544219b60SNaveen Nagar     if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
10644219b60SNaveen Nagar         pil = ns->lbaf.ms - 16;
10744219b60SNaveen Nagar     }
10844219b60SNaveen Nagar 
10944219b60SNaveen Nagar     trace_pci_nvme_dif_pract_generate_dif_crc64(len, ns->lbasz,
11044219b60SNaveen Nagar                                                 ns->lbasz + pil, apptag,
11144219b60SNaveen Nagar                                                 *reftag);
11244219b60SNaveen Nagar 
11344219b60SNaveen Nagar     for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) {
11444219b60SNaveen Nagar         NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
11544219b60SNaveen Nagar         uint64_t crc = crc64_nvme(~0ULL, buf, ns->lbasz);
11644219b60SNaveen Nagar 
11744219b60SNaveen Nagar         if (pil) {
118*dbdb13f9SAnkit Kumar             crc = crc64_nvme(~crc, mbuf, pil);
11944219b60SNaveen Nagar         }
12044219b60SNaveen Nagar 
12144219b60SNaveen Nagar         dif->g64.guard = cpu_to_be64(crc);
12244219b60SNaveen Nagar         dif->g64.apptag = cpu_to_be16(apptag);
12344219b60SNaveen Nagar 
12444219b60SNaveen Nagar         dif->g64.sr[0] = *reftag >> 40;
12544219b60SNaveen Nagar         dif->g64.sr[1] = *reftag >> 32;
12644219b60SNaveen Nagar         dif->g64.sr[2] = *reftag >> 24;
12744219b60SNaveen Nagar         dif->g64.sr[3] = *reftag >> 16;
12844219b60SNaveen Nagar         dif->g64.sr[4] = *reftag >> 8;
12944219b60SNaveen Nagar         dif->g64.sr[5] = *reftag;
13044219b60SNaveen Nagar 
13144219b60SNaveen Nagar         if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
13244219b60SNaveen Nagar             (*reftag)++;
13344219b60SNaveen Nagar         }
13444219b60SNaveen Nagar     }
13544219b60SNaveen Nagar }
13644219b60SNaveen Nagar 
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)13744219b60SNaveen Nagar void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len,
13844219b60SNaveen Nagar                                  uint8_t *mbuf, size_t mlen, uint16_t apptag,
13944219b60SNaveen Nagar                                  uint64_t *reftag)
14044219b60SNaveen Nagar {
14144219b60SNaveen Nagar     switch (ns->pif) {
14244219b60SNaveen Nagar     case NVME_PI_GUARD_16:
14344219b60SNaveen Nagar         return nvme_dif_pract_generate_dif_crc16(ns, buf, len, mbuf, mlen,
14444219b60SNaveen Nagar                                                  apptag, reftag);
14544219b60SNaveen Nagar     case NVME_PI_GUARD_64:
14644219b60SNaveen Nagar         return nvme_dif_pract_generate_dif_crc64(ns, buf, len, mbuf, mlen,
14744219b60SNaveen Nagar                                                  apptag, reftag);
14844219b60SNaveen Nagar     }
14944219b60SNaveen Nagar 
15044219b60SNaveen Nagar     abort();
15144219b60SNaveen Nagar }
15244219b60SNaveen Nagar 
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)15344219b60SNaveen Nagar static uint16_t nvme_dif_prchk_crc16(NvmeNamespace *ns, NvmeDifTuple *dif,
15488eea45cSKlaus Jensen                                      uint8_t *buf, uint8_t *mbuf, size_t pil,
1552a132309SKlaus Jensen                                      uint8_t prinfo, uint16_t apptag,
15644219b60SNaveen Nagar                                      uint16_t appmask, uint64_t reftag)
15788eea45cSKlaus Jensen {
15888eea45cSKlaus Jensen     switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
15988eea45cSKlaus Jensen     case NVME_ID_NS_DPS_TYPE_3:
16044219b60SNaveen Nagar         if (be32_to_cpu(dif->g16.reftag) != 0xffffffff) {
16188eea45cSKlaus Jensen             break;
16288eea45cSKlaus Jensen         }
16388eea45cSKlaus Jensen 
16488eea45cSKlaus Jensen         /* fallthrough */
16588eea45cSKlaus Jensen     case NVME_ID_NS_DPS_TYPE_1:
16688eea45cSKlaus Jensen     case NVME_ID_NS_DPS_TYPE_2:
16744219b60SNaveen Nagar         if (be16_to_cpu(dif->g16.apptag) != 0xffff) {
16888eea45cSKlaus Jensen             break;
16988eea45cSKlaus Jensen         }
17088eea45cSKlaus Jensen 
17144219b60SNaveen Nagar         trace_pci_nvme_dif_prchk_disabled_crc16(be16_to_cpu(dif->g16.apptag),
17244219b60SNaveen Nagar                                                 be32_to_cpu(dif->g16.reftag));
17388eea45cSKlaus Jensen 
17488eea45cSKlaus Jensen         return NVME_SUCCESS;
17588eea45cSKlaus Jensen     }
17688eea45cSKlaus Jensen 
1772a132309SKlaus Jensen     if (prinfo & NVME_PRINFO_PRCHK_GUARD) {
17844219b60SNaveen Nagar         uint16_t crc = crc16_t10dif(0x0, buf, ns->lbasz);
17988eea45cSKlaus Jensen 
18088eea45cSKlaus Jensen         if (pil) {
18144219b60SNaveen Nagar             crc = crc16_t10dif(crc, mbuf, pil);
18288eea45cSKlaus Jensen         }
18388eea45cSKlaus Jensen 
18444219b60SNaveen Nagar         trace_pci_nvme_dif_prchk_guard_crc16(be16_to_cpu(dif->g16.guard), crc);
18588eea45cSKlaus Jensen 
18644219b60SNaveen Nagar         if (be16_to_cpu(dif->g16.guard) != crc) {
18788eea45cSKlaus Jensen             return NVME_E2E_GUARD_ERROR;
18888eea45cSKlaus Jensen         }
18988eea45cSKlaus Jensen     }
19088eea45cSKlaus Jensen 
1912a132309SKlaus Jensen     if (prinfo & NVME_PRINFO_PRCHK_APP) {
19244219b60SNaveen Nagar         trace_pci_nvme_dif_prchk_apptag(be16_to_cpu(dif->g16.apptag), apptag,
19388eea45cSKlaus Jensen                                         appmask);
19488eea45cSKlaus Jensen 
19544219b60SNaveen Nagar         if ((be16_to_cpu(dif->g16.apptag) & appmask) != (apptag & appmask)) {
19688eea45cSKlaus Jensen             return NVME_E2E_APP_ERROR;
19788eea45cSKlaus Jensen         }
19888eea45cSKlaus Jensen     }
19988eea45cSKlaus Jensen 
2002a132309SKlaus Jensen     if (prinfo & NVME_PRINFO_PRCHK_REF) {
20144219b60SNaveen Nagar         trace_pci_nvme_dif_prchk_reftag_crc16(be32_to_cpu(dif->g16.reftag),
20244219b60SNaveen Nagar                                               reftag);
20388eea45cSKlaus Jensen 
20444219b60SNaveen Nagar         if (be32_to_cpu(dif->g16.reftag) != reftag) {
20588eea45cSKlaus Jensen             return NVME_E2E_REF_ERROR;
20688eea45cSKlaus Jensen         }
20788eea45cSKlaus Jensen     }
20888eea45cSKlaus Jensen 
20988eea45cSKlaus Jensen     return NVME_SUCCESS;
21088eea45cSKlaus Jensen }
21188eea45cSKlaus Jensen 
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)21244219b60SNaveen Nagar static uint16_t nvme_dif_prchk_crc64(NvmeNamespace *ns, NvmeDifTuple *dif,
21344219b60SNaveen Nagar                                      uint8_t *buf, uint8_t *mbuf, size_t pil,
21444219b60SNaveen Nagar                                      uint8_t prinfo, uint16_t apptag,
21544219b60SNaveen Nagar                                      uint16_t appmask, uint64_t reftag)
21644219b60SNaveen Nagar {
21744219b60SNaveen Nagar     uint64_t r = 0;
21844219b60SNaveen Nagar 
21944219b60SNaveen Nagar     r |= (uint64_t)dif->g64.sr[0] << 40;
22044219b60SNaveen Nagar     r |= (uint64_t)dif->g64.sr[1] << 32;
22144219b60SNaveen Nagar     r |= (uint64_t)dif->g64.sr[2] << 24;
22244219b60SNaveen Nagar     r |= (uint64_t)dif->g64.sr[3] << 16;
22344219b60SNaveen Nagar     r |= (uint64_t)dif->g64.sr[4] << 8;
22444219b60SNaveen Nagar     r |= (uint64_t)dif->g64.sr[5];
22544219b60SNaveen Nagar 
22644219b60SNaveen Nagar     switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
22744219b60SNaveen Nagar     case NVME_ID_NS_DPS_TYPE_3:
22844219b60SNaveen Nagar         if (r != 0xffffffffffff) {
22944219b60SNaveen Nagar             break;
23044219b60SNaveen Nagar         }
23144219b60SNaveen Nagar 
23244219b60SNaveen Nagar         /* fallthrough */
23344219b60SNaveen Nagar     case NVME_ID_NS_DPS_TYPE_1:
23444219b60SNaveen Nagar     case NVME_ID_NS_DPS_TYPE_2:
23544219b60SNaveen Nagar         if (be16_to_cpu(dif->g64.apptag) != 0xffff) {
23644219b60SNaveen Nagar             break;
23744219b60SNaveen Nagar         }
23844219b60SNaveen Nagar 
23944219b60SNaveen Nagar         trace_pci_nvme_dif_prchk_disabled_crc64(be16_to_cpu(dif->g16.apptag),
24044219b60SNaveen Nagar                                                 r);
24144219b60SNaveen Nagar 
24244219b60SNaveen Nagar         return NVME_SUCCESS;
24344219b60SNaveen Nagar     }
24444219b60SNaveen Nagar 
24544219b60SNaveen Nagar     if (prinfo & NVME_PRINFO_PRCHK_GUARD) {
24644219b60SNaveen Nagar         uint64_t crc = crc64_nvme(~0ULL, buf, ns->lbasz);
24744219b60SNaveen Nagar 
24844219b60SNaveen Nagar         if (pil) {
249*dbdb13f9SAnkit Kumar             crc = crc64_nvme(~crc, mbuf, pil);
25044219b60SNaveen Nagar         }
25144219b60SNaveen Nagar 
25244219b60SNaveen Nagar         trace_pci_nvme_dif_prchk_guard_crc64(be64_to_cpu(dif->g64.guard), crc);
25344219b60SNaveen Nagar 
25444219b60SNaveen Nagar         if (be64_to_cpu(dif->g64.guard) != crc) {
25544219b60SNaveen Nagar             return NVME_E2E_GUARD_ERROR;
25644219b60SNaveen Nagar         }
25744219b60SNaveen Nagar     }
25844219b60SNaveen Nagar 
25944219b60SNaveen Nagar     if (prinfo & NVME_PRINFO_PRCHK_APP) {
26044219b60SNaveen Nagar         trace_pci_nvme_dif_prchk_apptag(be16_to_cpu(dif->g64.apptag), apptag,
26144219b60SNaveen Nagar                                         appmask);
26244219b60SNaveen Nagar 
26344219b60SNaveen Nagar         if ((be16_to_cpu(dif->g64.apptag) & appmask) != (apptag & appmask)) {
26444219b60SNaveen Nagar             return NVME_E2E_APP_ERROR;
26544219b60SNaveen Nagar         }
26644219b60SNaveen Nagar     }
26744219b60SNaveen Nagar 
26844219b60SNaveen Nagar     if (prinfo & NVME_PRINFO_PRCHK_REF) {
26944219b60SNaveen Nagar         trace_pci_nvme_dif_prchk_reftag_crc64(r, reftag);
27044219b60SNaveen Nagar 
27144219b60SNaveen Nagar         if (r != reftag) {
27244219b60SNaveen Nagar             return NVME_E2E_REF_ERROR;
27344219b60SNaveen Nagar         }
27444219b60SNaveen Nagar     }
27544219b60SNaveen Nagar 
27644219b60SNaveen Nagar     return NVME_SUCCESS;
27744219b60SNaveen Nagar }
27844219b60SNaveen Nagar 
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)27944219b60SNaveen Nagar static uint16_t nvme_dif_prchk(NvmeNamespace *ns, NvmeDifTuple *dif,
28044219b60SNaveen Nagar                                uint8_t *buf, uint8_t *mbuf, size_t pil,
28144219b60SNaveen Nagar                                uint8_t prinfo, uint16_t apptag,
28244219b60SNaveen Nagar                                uint16_t appmask, uint64_t reftag)
28344219b60SNaveen Nagar {
28444219b60SNaveen Nagar     switch (ns->pif) {
28544219b60SNaveen Nagar     case NVME_PI_GUARD_16:
28644219b60SNaveen Nagar         return nvme_dif_prchk_crc16(ns, dif, buf, mbuf, pil, prinfo, apptag,
28744219b60SNaveen Nagar                                     appmask, reftag);
28844219b60SNaveen Nagar     case NVME_PI_GUARD_64:
28944219b60SNaveen Nagar         return nvme_dif_prchk_crc64(ns, dif, buf, mbuf, pil, prinfo, apptag,
29044219b60SNaveen Nagar                                     appmask, reftag);
29144219b60SNaveen Nagar     }
29244219b60SNaveen Nagar 
29344219b60SNaveen Nagar     abort();
29444219b60SNaveen Nagar }
29544219b60SNaveen Nagar 
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)29688eea45cSKlaus Jensen uint16_t nvme_dif_check(NvmeNamespace *ns, uint8_t *buf, size_t len,
2972a132309SKlaus Jensen                         uint8_t *mbuf, size_t mlen, uint8_t prinfo,
29888eea45cSKlaus Jensen                         uint64_t slba, uint16_t apptag,
29944219b60SNaveen Nagar                         uint16_t appmask, uint64_t *reftag)
30088eea45cSKlaus Jensen {
30144219b60SNaveen Nagar     uint8_t *bufp, *end = buf + len;
30288eea45cSKlaus Jensen     int16_t pil = 0;
30388eea45cSKlaus Jensen     uint16_t status;
30488eea45cSKlaus Jensen 
3052a132309SKlaus Jensen     status = nvme_check_prinfo(ns, prinfo, slba, *reftag);
30688eea45cSKlaus Jensen     if (status) {
30788eea45cSKlaus Jensen         return status;
30888eea45cSKlaus Jensen     }
30988eea45cSKlaus Jensen 
31088eea45cSKlaus Jensen     if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
311ac0b34c5SKlaus Jensen         pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
31288eea45cSKlaus Jensen     }
31388eea45cSKlaus Jensen 
3142a132309SKlaus Jensen     trace_pci_nvme_dif_check(prinfo, ns->lbasz + pil);
31588eea45cSKlaus Jensen 
31644219b60SNaveen Nagar     for (bufp = buf; bufp < end; bufp += ns->lbasz, mbuf += ns->lbaf.ms) {
31788eea45cSKlaus Jensen         NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
31844219b60SNaveen Nagar         status = nvme_dif_prchk(ns, dif, bufp, mbuf, pil, prinfo, apptag,
3190ca5c3ccSKlaus Jensen                                 appmask, *reftag);
32088eea45cSKlaus Jensen         if (status) {
32144219b60SNaveen Nagar             /*
32244219b60SNaveen Nagar              * The first block of a 'raw' image is always allocated, so we
32344219b60SNaveen Nagar              * cannot reliably know if the block is all zeroes or not. For
32444219b60SNaveen Nagar              * CRC16 this works fine because the T10 CRC16 is 0x0 for all
32544219b60SNaveen Nagar              * zeroes, but the Rocksoft CRC64 is not. Thus, if a guard error is
32644219b60SNaveen Nagar              * detected for the first block, check if it is zeroed and manually
32744219b60SNaveen Nagar              * set the protection information to all ones to disable protection
32844219b60SNaveen Nagar              * information checking.
32944219b60SNaveen Nagar              */
33044219b60SNaveen Nagar             if (status == NVME_E2E_GUARD_ERROR && slba == 0x0 && bufp == buf) {
33144219b60SNaveen Nagar                 g_autofree uint8_t *zeroes = g_malloc0(ns->lbasz);
33244219b60SNaveen Nagar 
33344219b60SNaveen Nagar                 if (memcmp(bufp, zeroes, ns->lbasz) == 0) {
33444219b60SNaveen Nagar                     memset(mbuf + pil, 0xff, nvme_pi_tuple_size(ns));
33544219b60SNaveen Nagar                 }
33644219b60SNaveen Nagar             } else {
33788eea45cSKlaus Jensen                 return status;
33888eea45cSKlaus Jensen             }
33944219b60SNaveen Nagar         }
34088eea45cSKlaus Jensen 
34188eea45cSKlaus Jensen         if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
3420ca5c3ccSKlaus Jensen             (*reftag)++;
34388eea45cSKlaus Jensen         }
34488eea45cSKlaus Jensen     }
34588eea45cSKlaus Jensen 
34688eea45cSKlaus Jensen     return NVME_SUCCESS;
34788eea45cSKlaus Jensen }
34888eea45cSKlaus Jensen 
nvme_dif_mangle_mdata(NvmeNamespace * ns,uint8_t * mbuf,size_t mlen,uint64_t slba)34988eea45cSKlaus Jensen uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen,
35088eea45cSKlaus Jensen                                uint64_t slba)
35188eea45cSKlaus Jensen {
35288eea45cSKlaus Jensen     BlockBackend *blk = ns->blkconf.blk;
35388eea45cSKlaus Jensen     BlockDriverState *bs = blk_bs(blk);
35488eea45cSKlaus Jensen 
35588eea45cSKlaus Jensen     int64_t moffset = 0, offset = nvme_l2b(ns, slba);
35688eea45cSKlaus Jensen     uint8_t *mbufp, *end;
35788eea45cSKlaus Jensen     bool zeroed;
35888eea45cSKlaus Jensen     int16_t pil = 0;
35988eea45cSKlaus Jensen     int64_t bytes = (mlen / ns->lbaf.ms) << ns->lbaf.ds;
36088eea45cSKlaus Jensen     int64_t pnum = 0;
36188eea45cSKlaus Jensen 
36288eea45cSKlaus Jensen     Error *err = NULL;
36388eea45cSKlaus Jensen 
36488eea45cSKlaus Jensen 
36588eea45cSKlaus Jensen     if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
366ac0b34c5SKlaus Jensen         pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
36788eea45cSKlaus Jensen     }
36888eea45cSKlaus Jensen 
36988eea45cSKlaus Jensen     do {
37088eea45cSKlaus Jensen         int ret;
37188eea45cSKlaus Jensen 
37288eea45cSKlaus Jensen         bytes -= pnum;
37388eea45cSKlaus Jensen 
37488eea45cSKlaus Jensen         ret = bdrv_block_status(bs, offset, bytes, &pnum, NULL, NULL);
37588eea45cSKlaus Jensen         if (ret < 0) {
37688eea45cSKlaus Jensen             error_setg_errno(&err, -ret, "unable to get block status");
37788eea45cSKlaus Jensen             error_report_err(err);
37888eea45cSKlaus Jensen 
37988eea45cSKlaus Jensen             return NVME_INTERNAL_DEV_ERROR;
38088eea45cSKlaus Jensen         }
38188eea45cSKlaus Jensen 
38288eea45cSKlaus Jensen         zeroed = !!(ret & BDRV_BLOCK_ZERO);
38388eea45cSKlaus Jensen 
38488eea45cSKlaus Jensen         trace_pci_nvme_block_status(offset, bytes, pnum, ret, zeroed);
38588eea45cSKlaus Jensen 
38688eea45cSKlaus Jensen         if (zeroed) {
38788eea45cSKlaus Jensen             mbufp = mbuf + moffset;
38888eea45cSKlaus Jensen             mlen = (pnum >> ns->lbaf.ds) * ns->lbaf.ms;
38988eea45cSKlaus Jensen             end = mbufp + mlen;
39088eea45cSKlaus Jensen 
39188eea45cSKlaus Jensen             for (; mbufp < end; mbufp += ns->lbaf.ms) {
392ac0b34c5SKlaus Jensen                 memset(mbufp + pil, 0xff, nvme_pi_tuple_size(ns));
39388eea45cSKlaus Jensen             }
39488eea45cSKlaus Jensen         }
39588eea45cSKlaus Jensen 
39688eea45cSKlaus Jensen         moffset += (pnum >> ns->lbaf.ds) * ns->lbaf.ms;
39788eea45cSKlaus Jensen         offset += pnum;
39888eea45cSKlaus Jensen     } while (pnum != bytes);
39988eea45cSKlaus Jensen 
40088eea45cSKlaus Jensen     return NVME_SUCCESS;
40188eea45cSKlaus Jensen }
40288eea45cSKlaus Jensen 
nvme_dif_rw_cb(void * opaque,int ret)40388eea45cSKlaus Jensen static void nvme_dif_rw_cb(void *opaque, int ret)
40488eea45cSKlaus Jensen {
40588eea45cSKlaus Jensen     NvmeBounceContext *ctx = opaque;
40688eea45cSKlaus Jensen     NvmeRequest *req = ctx->req;
40788eea45cSKlaus Jensen     NvmeNamespace *ns = req->ns;
40888eea45cSKlaus Jensen     BlockBackend *blk = ns->blkconf.blk;
40988eea45cSKlaus Jensen 
41088eea45cSKlaus Jensen     trace_pci_nvme_dif_rw_cb(nvme_cid(req), blk_name(blk));
41188eea45cSKlaus Jensen 
41288eea45cSKlaus Jensen     qemu_iovec_destroy(&ctx->data.iov);
41388eea45cSKlaus Jensen     g_free(ctx->data.bounce);
41488eea45cSKlaus Jensen 
41588eea45cSKlaus Jensen     qemu_iovec_destroy(&ctx->mdata.iov);
41688eea45cSKlaus Jensen     g_free(ctx->mdata.bounce);
41788eea45cSKlaus Jensen 
41888eea45cSKlaus Jensen     g_free(ctx);
41988eea45cSKlaus Jensen 
42088eea45cSKlaus Jensen     nvme_rw_complete_cb(req, ret);
42188eea45cSKlaus Jensen }
42288eea45cSKlaus Jensen 
nvme_dif_rw_check_cb(void * opaque,int ret)42388eea45cSKlaus Jensen static void nvme_dif_rw_check_cb(void *opaque, int ret)
42488eea45cSKlaus Jensen {
42588eea45cSKlaus Jensen     NvmeBounceContext *ctx = opaque;
42688eea45cSKlaus Jensen     NvmeRequest *req = ctx->req;
42788eea45cSKlaus Jensen     NvmeNamespace *ns = req->ns;
42888eea45cSKlaus Jensen     NvmeCtrl *n = nvme_ctrl(req);
42988eea45cSKlaus Jensen     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
43088eea45cSKlaus Jensen     uint64_t slba = le64_to_cpu(rw->slba);
4312a132309SKlaus Jensen     uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
43288eea45cSKlaus Jensen     uint16_t apptag = le16_to_cpu(rw->apptag);
43388eea45cSKlaus Jensen     uint16_t appmask = le16_to_cpu(rw->appmask);
43444219b60SNaveen Nagar     uint64_t reftag = le32_to_cpu(rw->reftag);
43544219b60SNaveen Nagar     uint64_t cdw3 = le32_to_cpu(rw->cdw3);
43688eea45cSKlaus Jensen     uint16_t status;
43788eea45cSKlaus Jensen 
43844219b60SNaveen Nagar     reftag |= cdw3 << 32;
43944219b60SNaveen Nagar 
4402a132309SKlaus Jensen     trace_pci_nvme_dif_rw_check_cb(nvme_cid(req), prinfo, apptag, appmask,
4412a132309SKlaus Jensen                                    reftag);
44288eea45cSKlaus Jensen 
44388eea45cSKlaus Jensen     if (ret) {
44488eea45cSKlaus Jensen         goto out;
44588eea45cSKlaus Jensen     }
44688eea45cSKlaus Jensen 
44788eea45cSKlaus Jensen     status = nvme_dif_mangle_mdata(ns, ctx->mdata.bounce, ctx->mdata.iov.size,
44888eea45cSKlaus Jensen                                    slba);
44988eea45cSKlaus Jensen     if (status) {
45088eea45cSKlaus Jensen         req->status = status;
45188eea45cSKlaus Jensen         goto out;
45288eea45cSKlaus Jensen     }
45388eea45cSKlaus Jensen 
45488eea45cSKlaus Jensen     status = nvme_dif_check(ns, ctx->data.bounce, ctx->data.iov.size,
4552a132309SKlaus Jensen                             ctx->mdata.bounce, ctx->mdata.iov.size, prinfo,
4560ca5c3ccSKlaus Jensen                             slba, apptag, appmask, &reftag);
45788eea45cSKlaus Jensen     if (status) {
45888eea45cSKlaus Jensen         req->status = status;
45988eea45cSKlaus Jensen         goto out;
46088eea45cSKlaus Jensen     }
46188eea45cSKlaus Jensen 
46288eea45cSKlaus Jensen     status = nvme_bounce_data(n, ctx->data.bounce, ctx->data.iov.size,
46388eea45cSKlaus Jensen                               NVME_TX_DIRECTION_FROM_DEVICE, req);
46488eea45cSKlaus Jensen     if (status) {
46588eea45cSKlaus Jensen         req->status = status;
46688eea45cSKlaus Jensen         goto out;
46788eea45cSKlaus Jensen     }
46888eea45cSKlaus Jensen 
469ac0b34c5SKlaus Jensen     if (prinfo & NVME_PRINFO_PRACT && ns->lbaf.ms == nvme_pi_tuple_size(ns)) {
47088eea45cSKlaus Jensen         goto out;
47188eea45cSKlaus Jensen     }
47288eea45cSKlaus Jensen 
47388eea45cSKlaus Jensen     status = nvme_bounce_mdata(n, ctx->mdata.bounce, ctx->mdata.iov.size,
47488eea45cSKlaus Jensen                                NVME_TX_DIRECTION_FROM_DEVICE, req);
47588eea45cSKlaus Jensen     if (status) {
47688eea45cSKlaus Jensen         req->status = status;
47788eea45cSKlaus Jensen     }
47888eea45cSKlaus Jensen 
47988eea45cSKlaus Jensen out:
48088eea45cSKlaus Jensen     nvme_dif_rw_cb(ctx, ret);
48188eea45cSKlaus Jensen }
48288eea45cSKlaus Jensen 
nvme_dif_rw_mdata_in_cb(void * opaque,int ret)48388eea45cSKlaus Jensen static void nvme_dif_rw_mdata_in_cb(void *opaque, int ret)
48488eea45cSKlaus Jensen {
48588eea45cSKlaus Jensen     NvmeBounceContext *ctx = opaque;
48688eea45cSKlaus Jensen     NvmeRequest *req = ctx->req;
48788eea45cSKlaus Jensen     NvmeNamespace *ns = req->ns;
48888eea45cSKlaus Jensen     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
48988eea45cSKlaus Jensen     uint64_t slba = le64_to_cpu(rw->slba);
49088eea45cSKlaus Jensen     uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
49188eea45cSKlaus Jensen     size_t mlen = nvme_m2b(ns, nlb);
49288eea45cSKlaus Jensen     uint64_t offset = nvme_moff(ns, slba);
49388eea45cSKlaus Jensen     BlockBackend *blk = ns->blkconf.blk;
49488eea45cSKlaus Jensen 
49588eea45cSKlaus Jensen     trace_pci_nvme_dif_rw_mdata_in_cb(nvme_cid(req), blk_name(blk));
49688eea45cSKlaus Jensen 
49788eea45cSKlaus Jensen     if (ret) {
49888eea45cSKlaus Jensen         goto out;
49988eea45cSKlaus Jensen     }
50088eea45cSKlaus Jensen 
50188eea45cSKlaus Jensen     ctx->mdata.bounce = g_malloc(mlen);
50288eea45cSKlaus Jensen 
50388eea45cSKlaus Jensen     qemu_iovec_reset(&ctx->mdata.iov);
50488eea45cSKlaus Jensen     qemu_iovec_add(&ctx->mdata.iov, ctx->mdata.bounce, mlen);
50588eea45cSKlaus Jensen 
50688eea45cSKlaus Jensen     req->aiocb = blk_aio_preadv(blk, offset, &ctx->mdata.iov, 0,
50788eea45cSKlaus Jensen                                 nvme_dif_rw_check_cb, ctx);
50888eea45cSKlaus Jensen     return;
50988eea45cSKlaus Jensen 
51088eea45cSKlaus Jensen out:
51188eea45cSKlaus Jensen     nvme_dif_rw_cb(ctx, ret);
51288eea45cSKlaus Jensen }
51388eea45cSKlaus Jensen 
nvme_dif_rw_mdata_out_cb(void * opaque,int ret)51488eea45cSKlaus Jensen static void nvme_dif_rw_mdata_out_cb(void *opaque, int ret)
51588eea45cSKlaus Jensen {
51688eea45cSKlaus Jensen     NvmeBounceContext *ctx = opaque;
51788eea45cSKlaus Jensen     NvmeRequest *req = ctx->req;
51888eea45cSKlaus Jensen     NvmeNamespace *ns = req->ns;
51988eea45cSKlaus Jensen     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
52088eea45cSKlaus Jensen     uint64_t slba = le64_to_cpu(rw->slba);
52188eea45cSKlaus Jensen     uint64_t offset = nvme_moff(ns, slba);
52288eea45cSKlaus Jensen     BlockBackend *blk = ns->blkconf.blk;
52388eea45cSKlaus Jensen 
52488eea45cSKlaus Jensen     trace_pci_nvme_dif_rw_mdata_out_cb(nvme_cid(req), blk_name(blk));
52588eea45cSKlaus Jensen 
52688eea45cSKlaus Jensen     if (ret) {
52788eea45cSKlaus Jensen         goto out;
52888eea45cSKlaus Jensen     }
52988eea45cSKlaus Jensen 
53088eea45cSKlaus Jensen     req->aiocb = blk_aio_pwritev(blk, offset, &ctx->mdata.iov, 0,
53188eea45cSKlaus Jensen                                  nvme_dif_rw_cb, ctx);
53288eea45cSKlaus Jensen     return;
53388eea45cSKlaus Jensen 
53488eea45cSKlaus Jensen out:
53588eea45cSKlaus Jensen     nvme_dif_rw_cb(ctx, ret);
53688eea45cSKlaus Jensen }
53788eea45cSKlaus Jensen 
nvme_dif_rw(NvmeCtrl * n,NvmeRequest * req)53888eea45cSKlaus Jensen uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
53988eea45cSKlaus Jensen {
54088eea45cSKlaus Jensen     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
54188eea45cSKlaus Jensen     NvmeNamespace *ns = req->ns;
54288eea45cSKlaus Jensen     BlockBackend *blk = ns->blkconf.blk;
54388eea45cSKlaus Jensen     bool wrz = rw->opcode == NVME_CMD_WRITE_ZEROES;
54488eea45cSKlaus Jensen     uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
54588eea45cSKlaus Jensen     uint64_t slba = le64_to_cpu(rw->slba);
54688eea45cSKlaus Jensen     size_t len = nvme_l2b(ns, nlb);
54788eea45cSKlaus Jensen     size_t mlen = nvme_m2b(ns, nlb);
54888eea45cSKlaus Jensen     size_t mapped_len = len;
54988eea45cSKlaus Jensen     int64_t offset = nvme_l2b(ns, slba);
5502a132309SKlaus Jensen     uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
55188eea45cSKlaus Jensen     uint16_t apptag = le16_to_cpu(rw->apptag);
55288eea45cSKlaus Jensen     uint16_t appmask = le16_to_cpu(rw->appmask);
55344219b60SNaveen Nagar     uint64_t reftag = le32_to_cpu(rw->reftag);
55444219b60SNaveen Nagar     uint64_t cdw3 = le32_to_cpu(rw->cdw3);
5552a132309SKlaus Jensen     bool pract = !!(prinfo & NVME_PRINFO_PRACT);
55688eea45cSKlaus Jensen     NvmeBounceContext *ctx;
55788eea45cSKlaus Jensen     uint16_t status;
55888eea45cSKlaus Jensen 
55944219b60SNaveen Nagar     reftag |= cdw3 << 32;
56044219b60SNaveen Nagar 
5612a132309SKlaus Jensen     trace_pci_nvme_dif_rw(pract, prinfo);
56288eea45cSKlaus Jensen 
56388eea45cSKlaus Jensen     ctx = g_new0(NvmeBounceContext, 1);
56488eea45cSKlaus Jensen     ctx->req = req;
56588eea45cSKlaus Jensen 
56688eea45cSKlaus Jensen     if (wrz) {
56788eea45cSKlaus Jensen         BdrvRequestFlags flags = BDRV_REQ_MAY_UNMAP;
56888eea45cSKlaus Jensen 
5692a132309SKlaus Jensen         if (prinfo & NVME_PRINFO_PRCHK_MASK) {
57088eea45cSKlaus Jensen             status = NVME_INVALID_PROT_INFO | NVME_DNR;
57188eea45cSKlaus Jensen             goto err;
57288eea45cSKlaus Jensen         }
57388eea45cSKlaus Jensen 
57488eea45cSKlaus Jensen         if (pract) {
57588eea45cSKlaus Jensen             uint8_t *mbuf, *end;
576ac0b34c5SKlaus Jensen             int16_t pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
57788eea45cSKlaus Jensen 
57888eea45cSKlaus Jensen             flags = 0;
57988eea45cSKlaus Jensen 
58088eea45cSKlaus Jensen             ctx->mdata.bounce = g_malloc0(mlen);
58188eea45cSKlaus Jensen 
58288eea45cSKlaus Jensen             qemu_iovec_init(&ctx->mdata.iov, 1);
58388eea45cSKlaus Jensen             qemu_iovec_add(&ctx->mdata.iov, ctx->mdata.bounce, mlen);
58488eea45cSKlaus Jensen 
58588eea45cSKlaus Jensen             mbuf = ctx->mdata.bounce;
58688eea45cSKlaus Jensen             end = mbuf + mlen;
58788eea45cSKlaus Jensen 
58888eea45cSKlaus Jensen             if (ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT) {
58988eea45cSKlaus Jensen                 pil = 0;
59088eea45cSKlaus Jensen             }
59188eea45cSKlaus Jensen 
59288eea45cSKlaus Jensen             for (; mbuf < end; mbuf += ns->lbaf.ms) {
59388eea45cSKlaus Jensen                 NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
59488eea45cSKlaus Jensen 
59544219b60SNaveen Nagar                 switch (ns->pif) {
59644219b60SNaveen Nagar                 case NVME_PI_GUARD_16:
59744219b60SNaveen Nagar                     dif->g16.apptag = cpu_to_be16(apptag);
59844219b60SNaveen Nagar                     dif->g16.reftag = cpu_to_be32(reftag);
59944219b60SNaveen Nagar 
60044219b60SNaveen Nagar                     break;
60144219b60SNaveen Nagar 
60244219b60SNaveen Nagar                 case NVME_PI_GUARD_64:
60344219b60SNaveen Nagar                     dif->g64.guard = cpu_to_be64(0x6482d367eb22b64e);
60444219b60SNaveen Nagar                     dif->g64.apptag = cpu_to_be16(apptag);
60544219b60SNaveen Nagar 
60644219b60SNaveen Nagar                     dif->g64.sr[0] = reftag >> 40;
60744219b60SNaveen Nagar                     dif->g64.sr[1] = reftag >> 32;
60844219b60SNaveen Nagar                     dif->g64.sr[2] = reftag >> 24;
60944219b60SNaveen Nagar                     dif->g64.sr[3] = reftag >> 16;
61044219b60SNaveen Nagar                     dif->g64.sr[4] = reftag >> 8;
61144219b60SNaveen Nagar                     dif->g64.sr[5] = reftag;
61244219b60SNaveen Nagar 
61344219b60SNaveen Nagar                     break;
61444219b60SNaveen Nagar 
61544219b60SNaveen Nagar                 default:
61644219b60SNaveen Nagar                     abort();
61744219b60SNaveen Nagar                 }
61888eea45cSKlaus Jensen 
61988eea45cSKlaus Jensen                 switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
62088eea45cSKlaus Jensen                 case NVME_ID_NS_DPS_TYPE_1:
62188eea45cSKlaus Jensen                 case NVME_ID_NS_DPS_TYPE_2:
62288eea45cSKlaus Jensen                     reftag++;
62388eea45cSKlaus Jensen                 }
62488eea45cSKlaus Jensen             }
62588eea45cSKlaus Jensen         }
62688eea45cSKlaus Jensen 
62788eea45cSKlaus Jensen         req->aiocb = blk_aio_pwrite_zeroes(blk, offset, len, flags,
62888eea45cSKlaus Jensen                                            nvme_dif_rw_mdata_out_cb, ctx);
62988eea45cSKlaus Jensen         return NVME_NO_COMPLETE;
63088eea45cSKlaus Jensen     }
63188eea45cSKlaus Jensen 
632ac0b34c5SKlaus Jensen     if (nvme_ns_ext(ns) && !(pract && ns->lbaf.ms == nvme_pi_tuple_size(ns))) {
63388eea45cSKlaus Jensen         mapped_len += mlen;
63488eea45cSKlaus Jensen     }
63588eea45cSKlaus Jensen 
63688eea45cSKlaus Jensen     status = nvme_map_dptr(n, &req->sg, mapped_len, &req->cmd);
63788eea45cSKlaus Jensen     if (status) {
63888eea45cSKlaus Jensen         goto err;
63988eea45cSKlaus Jensen     }
64088eea45cSKlaus Jensen 
64188eea45cSKlaus Jensen     ctx->data.bounce = g_malloc(len);
64288eea45cSKlaus Jensen 
64388eea45cSKlaus Jensen     qemu_iovec_init(&ctx->data.iov, 1);
64488eea45cSKlaus Jensen     qemu_iovec_add(&ctx->data.iov, ctx->data.bounce, len);
64588eea45cSKlaus Jensen 
64688eea45cSKlaus Jensen     if (req->cmd.opcode == NVME_CMD_READ) {
64788eea45cSKlaus Jensen         block_acct_start(blk_get_stats(blk), &req->acct, ctx->data.iov.size,
64888eea45cSKlaus Jensen                          BLOCK_ACCT_READ);
64988eea45cSKlaus Jensen 
65088eea45cSKlaus Jensen         req->aiocb = blk_aio_preadv(ns->blkconf.blk, offset, &ctx->data.iov, 0,
65188eea45cSKlaus Jensen                                     nvme_dif_rw_mdata_in_cb, ctx);
65288eea45cSKlaus Jensen         return NVME_NO_COMPLETE;
65388eea45cSKlaus Jensen     }
65488eea45cSKlaus Jensen 
65588eea45cSKlaus Jensen     status = nvme_bounce_data(n, ctx->data.bounce, ctx->data.iov.size,
65688eea45cSKlaus Jensen                               NVME_TX_DIRECTION_TO_DEVICE, req);
65788eea45cSKlaus Jensen     if (status) {
65888eea45cSKlaus Jensen         goto err;
65988eea45cSKlaus Jensen     }
66088eea45cSKlaus Jensen 
66188eea45cSKlaus Jensen     ctx->mdata.bounce = g_malloc(mlen);
66288eea45cSKlaus Jensen 
66388eea45cSKlaus Jensen     qemu_iovec_init(&ctx->mdata.iov, 1);
66488eea45cSKlaus Jensen     qemu_iovec_add(&ctx->mdata.iov, ctx->mdata.bounce, mlen);
66588eea45cSKlaus Jensen 
666ac0b34c5SKlaus Jensen     if (!(pract && ns->lbaf.ms == nvme_pi_tuple_size(ns))) {
66788eea45cSKlaus Jensen         status = nvme_bounce_mdata(n, ctx->mdata.bounce, ctx->mdata.iov.size,
66888eea45cSKlaus Jensen                                    NVME_TX_DIRECTION_TO_DEVICE, req);
66988eea45cSKlaus Jensen         if (status) {
67088eea45cSKlaus Jensen             goto err;
67188eea45cSKlaus Jensen         }
67288eea45cSKlaus Jensen     }
67388eea45cSKlaus Jensen 
6742a132309SKlaus Jensen     status = nvme_check_prinfo(ns, prinfo, slba, reftag);
67588eea45cSKlaus Jensen     if (status) {
67688eea45cSKlaus Jensen         goto err;
67788eea45cSKlaus Jensen     }
67888eea45cSKlaus Jensen 
67988eea45cSKlaus Jensen     if (pract) {
68088eea45cSKlaus Jensen         /* splice generated protection information into the buffer */
68188eea45cSKlaus Jensen         nvme_dif_pract_generate_dif(ns, ctx->data.bounce, ctx->data.iov.size,
68288eea45cSKlaus Jensen                                     ctx->mdata.bounce, ctx->mdata.iov.size,
6830ca5c3ccSKlaus Jensen                                     apptag, &reftag);
68488eea45cSKlaus Jensen     } else {
68588eea45cSKlaus Jensen         status = nvme_dif_check(ns, ctx->data.bounce, ctx->data.iov.size,
6862a132309SKlaus Jensen                                 ctx->mdata.bounce, ctx->mdata.iov.size, prinfo,
6870ca5c3ccSKlaus Jensen                                 slba, apptag, appmask, &reftag);
68888eea45cSKlaus Jensen         if (status) {
68988eea45cSKlaus Jensen             goto err;
69088eea45cSKlaus Jensen         }
69188eea45cSKlaus Jensen     }
69288eea45cSKlaus Jensen 
69388eea45cSKlaus Jensen     block_acct_start(blk_get_stats(blk), &req->acct, ctx->data.iov.size,
69488eea45cSKlaus Jensen                      BLOCK_ACCT_WRITE);
69588eea45cSKlaus Jensen 
69688eea45cSKlaus Jensen     req->aiocb = blk_aio_pwritev(ns->blkconf.blk, offset, &ctx->data.iov, 0,
69788eea45cSKlaus Jensen                                  nvme_dif_rw_mdata_out_cb, ctx);
69888eea45cSKlaus Jensen 
69988eea45cSKlaus Jensen     return NVME_NO_COMPLETE;
70088eea45cSKlaus Jensen 
70188eea45cSKlaus Jensen err:
70288eea45cSKlaus Jensen     qemu_iovec_destroy(&ctx->data.iov);
70388eea45cSKlaus Jensen     g_free(ctx->data.bounce);
70488eea45cSKlaus Jensen 
70588eea45cSKlaus Jensen     qemu_iovec_destroy(&ctx->mdata.iov);
70688eea45cSKlaus Jensen     g_free(ctx->mdata.bounce);
70788eea45cSKlaus Jensen 
70888eea45cSKlaus Jensen     g_free(ctx);
70988eea45cSKlaus Jensen 
71088eea45cSKlaus Jensen     return status;
71188eea45cSKlaus Jensen }
712