All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/6] hw/nvme: enhanced protection information (64-bit guard)
@ 2022-03-01 10:44 Klaus Jensen
  2022-03-01 10:44 ` [PATCH v2 1/6] hw/nvme: move dif/pi prototypes into dif.h Klaus Jensen
                   ` (6 more replies)
  0 siblings, 7 replies; 9+ messages in thread
From: Klaus Jensen @ 2022-03-01 10:44 UTC (permalink / raw)
  To: qemu-devel, Keith Busch
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Klaus Jensen, Naveen Nagar,
	Philippe Mathieu-Daudé,
	Hanna Reitz, Stefan Hajnoczi, Klaus Jensen

From: Klaus Jensen <k.jensen@samsung.com>

This adds support for one possible new protection information format
introduced in TP4068 (and integrated in NVMe 2.0): the 64-bit CRC guard
and 48-bit reference tag. This version does not support storage tags.

Like the CRC16 support already present, this uses a software
implementation of CRC64 (so it is naturally pretty slow). But its good
enough for verification purposes.

This goes hand-in-hand with the support that Keith submitted for the
Linux kernel[1].

  [1]: https://lore.kernel.org/linux-nvme/20220201190128.3075065-1-kbusch@kernel.org/

Changes since v1
~~~~~~~~~~~~~~~~
- Check metadata size depending on pi guard type selected. (Keith)

Klaus Jensen (3):
  hw/nvme: move dif/pi prototypes into dif.h
  hw/nvme: move format parameter parsing
  hw/nvme: add pi tuple size helper

Naveen Nagar (3):
  hw/nvme: add host behavior support feature
  hw/nvme: add support for the lbafee hbs feature
  hw/nvme: 64-bit pi support

 hw/nvme/ctrl.c       | 235 +++++++++++++++++++++------
 hw/nvme/dif.c        | 378 +++++++++++++++++++++++++++++++++----------
 hw/nvme/dif.h        | 191 ++++++++++++++++++++++
 hw/nvme/ns.c         |  50 ++++--
 hw/nvme/nvme.h       |  58 +------
 hw/nvme/trace-events |  12 +-
 include/block/nvme.h |  81 ++++++++--
 7 files changed, 793 insertions(+), 212 deletions(-)
 create mode 100644 hw/nvme/dif.h

-- 
2.35.1



^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v2 1/6] hw/nvme: move dif/pi prototypes into dif.h
  2022-03-01 10:44 [PATCH v2 0/6] hw/nvme: enhanced protection information (64-bit guard) Klaus Jensen
@ 2022-03-01 10:44 ` Klaus Jensen
  2022-03-01 10:44 ` [PATCH v2 2/6] hw/nvme: add host behavior support feature Klaus Jensen
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Klaus Jensen @ 2022-03-01 10:44 UTC (permalink / raw)
  To: qemu-devel, Keith Busch
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Klaus Jensen, Naveen Nagar,
	Philippe Mathieu-Daudé,
	Hanna Reitz, Stefan Hajnoczi, Klaus Jensen

From: Klaus Jensen <k.jensen@samsung.com>

Move dif/pi data structures and inlines to dif.h.

Reviewed-by: Keith Busch <kbusch@kernel.org>
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/ctrl.c |  1 +
 hw/nvme/dif.c  |  1 +
 hw/nvme/dif.h  | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/nvme/nvme.h | 50 -----------------------------------------------
 4 files changed, 55 insertions(+), 50 deletions(-)
 create mode 100644 hw/nvme/dif.h

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 98aac98bef5f..d08af3bdc1a2 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -163,6 +163,7 @@
 #include "migration/vmstate.h"
 
 #include "nvme.h"
+#include "dif.h"
 #include "trace.h"
 
 #define NVME_MAX_IOQPAIRS 0xffff
diff --git a/hw/nvme/dif.c b/hw/nvme/dif.c
index 5dbd18b2a4a5..cd0cea2b5ebd 100644
--- a/hw/nvme/dif.c
+++ b/hw/nvme/dif.c
@@ -13,6 +13,7 @@
 #include "sysemu/block-backend.h"
 
 #include "nvme.h"
+#include "dif.h"
 #include "trace.h"
 
 uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba,
diff --git a/hw/nvme/dif.h b/hw/nvme/dif.h
new file mode 100644
index 000000000000..e36fea30e71e
--- /dev/null
+++ b/hw/nvme/dif.h
@@ -0,0 +1,53 @@
+#ifndef HW_NVME_DIF_H
+#define HW_NVME_DIF_H
+
+/* from Linux kernel (crypto/crct10dif_common.c) */
+static const uint16_t t10_dif_crc_table[256] = {
+    0x0000, 0x8BB7, 0x9CD9, 0x176E, 0xB205, 0x39B2, 0x2EDC, 0xA56B,
+    0xEFBD, 0x640A, 0x7364, 0xF8D3, 0x5DB8, 0xD60F, 0xC161, 0x4AD6,
+    0x54CD, 0xDF7A, 0xC814, 0x43A3, 0xE6C8, 0x6D7F, 0x7A11, 0xF1A6,
+    0xBB70, 0x30C7, 0x27A9, 0xAC1E, 0x0975, 0x82C2, 0x95AC, 0x1E1B,
+    0xA99A, 0x222D, 0x3543, 0xBEF4, 0x1B9F, 0x9028, 0x8746, 0x0CF1,
+    0x4627, 0xCD90, 0xDAFE, 0x5149, 0xF422, 0x7F95, 0x68FB, 0xE34C,
+    0xFD57, 0x76E0, 0x618E, 0xEA39, 0x4F52, 0xC4E5, 0xD38B, 0x583C,
+    0x12EA, 0x995D, 0x8E33, 0x0584, 0xA0EF, 0x2B58, 0x3C36, 0xB781,
+    0xD883, 0x5334, 0x445A, 0xCFED, 0x6A86, 0xE131, 0xF65F, 0x7DE8,
+    0x373E, 0xBC89, 0xABE7, 0x2050, 0x853B, 0x0E8C, 0x19E2, 0x9255,
+    0x8C4E, 0x07F9, 0x1097, 0x9B20, 0x3E4B, 0xB5FC, 0xA292, 0x2925,
+    0x63F3, 0xE844, 0xFF2A, 0x749D, 0xD1F6, 0x5A41, 0x4D2F, 0xC698,
+    0x7119, 0xFAAE, 0xEDC0, 0x6677, 0xC31C, 0x48AB, 0x5FC5, 0xD472,
+    0x9EA4, 0x1513, 0x027D, 0x89CA, 0x2CA1, 0xA716, 0xB078, 0x3BCF,
+    0x25D4, 0xAE63, 0xB90D, 0x32BA, 0x97D1, 0x1C66, 0x0B08, 0x80BF,
+    0xCA69, 0x41DE, 0x56B0, 0xDD07, 0x786C, 0xF3DB, 0xE4B5, 0x6F02,
+    0x3AB1, 0xB106, 0xA668, 0x2DDF, 0x88B4, 0x0303, 0x146D, 0x9FDA,
+    0xD50C, 0x5EBB, 0x49D5, 0xC262, 0x6709, 0xECBE, 0xFBD0, 0x7067,
+    0x6E7C, 0xE5CB, 0xF2A5, 0x7912, 0xDC79, 0x57CE, 0x40A0, 0xCB17,
+    0x81C1, 0x0A76, 0x1D18, 0x96AF, 0x33C4, 0xB873, 0xAF1D, 0x24AA,
+    0x932B, 0x189C, 0x0FF2, 0x8445, 0x212E, 0xAA99, 0xBDF7, 0x3640,
+    0x7C96, 0xF721, 0xE04F, 0x6BF8, 0xCE93, 0x4524, 0x524A, 0xD9FD,
+    0xC7E6, 0x4C51, 0x5B3F, 0xD088, 0x75E3, 0xFE54, 0xE93A, 0x628D,
+    0x285B, 0xA3EC, 0xB482, 0x3F35, 0x9A5E, 0x11E9, 0x0687, 0x8D30,
+    0xE232, 0x6985, 0x7EEB, 0xF55C, 0x5037, 0xDB80, 0xCCEE, 0x4759,
+    0x0D8F, 0x8638, 0x9156, 0x1AE1, 0xBF8A, 0x343D, 0x2353, 0xA8E4,
+    0xB6FF, 0x3D48, 0x2A26, 0xA191, 0x04FA, 0x8F4D, 0x9823, 0x1394,
+    0x5942, 0xD2F5, 0xC59B, 0x4E2C, 0xEB47, 0x60F0, 0x779E, 0xFC29,
+    0x4BA8, 0xC01F, 0xD771, 0x5CC6, 0xF9AD, 0x721A, 0x6574, 0xEEC3,
+    0xA415, 0x2FA2, 0x38CC, 0xB37B, 0x1610, 0x9DA7, 0x8AC9, 0x017E,
+    0x1F65, 0x94D2, 0x83BC, 0x080B, 0xAD60, 0x26D7, 0x31B9, 0xBA0E,
+    0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3
+};
+
+uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba,
+                           uint32_t reftag);
+uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen,
+                               uint64_t slba);
+void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len,
+                                 uint8_t *mbuf, size_t mlen, uint16_t apptag,
+                                 uint32_t *reftag);
+uint16_t 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, uint32_t *reftag);
+uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req);
+
+#endif /* HW_NVME_DIF_H */
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index 90c0bb7ce236..801176a2bd5e 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -513,54 +513,4 @@ void nvme_rw_complete_cb(void *opaque, int ret);
 uint16_t nvme_map_dptr(NvmeCtrl *n, NvmeSg *sg, size_t len,
                        NvmeCmd *cmd);
 
-/* from Linux kernel (crypto/crct10dif_common.c) */
-static const uint16_t t10_dif_crc_table[256] = {
-    0x0000, 0x8BB7, 0x9CD9, 0x176E, 0xB205, 0x39B2, 0x2EDC, 0xA56B,
-    0xEFBD, 0x640A, 0x7364, 0xF8D3, 0x5DB8, 0xD60F, 0xC161, 0x4AD6,
-    0x54CD, 0xDF7A, 0xC814, 0x43A3, 0xE6C8, 0x6D7F, 0x7A11, 0xF1A6,
-    0xBB70, 0x30C7, 0x27A9, 0xAC1E, 0x0975, 0x82C2, 0x95AC, 0x1E1B,
-    0xA99A, 0x222D, 0x3543, 0xBEF4, 0x1B9F, 0x9028, 0x8746, 0x0CF1,
-    0x4627, 0xCD90, 0xDAFE, 0x5149, 0xF422, 0x7F95, 0x68FB, 0xE34C,
-    0xFD57, 0x76E0, 0x618E, 0xEA39, 0x4F52, 0xC4E5, 0xD38B, 0x583C,
-    0x12EA, 0x995D, 0x8E33, 0x0584, 0xA0EF, 0x2B58, 0x3C36, 0xB781,
-    0xD883, 0x5334, 0x445A, 0xCFED, 0x6A86, 0xE131, 0xF65F, 0x7DE8,
-    0x373E, 0xBC89, 0xABE7, 0x2050, 0x853B, 0x0E8C, 0x19E2, 0x9255,
-    0x8C4E, 0x07F9, 0x1097, 0x9B20, 0x3E4B, 0xB5FC, 0xA292, 0x2925,
-    0x63F3, 0xE844, 0xFF2A, 0x749D, 0xD1F6, 0x5A41, 0x4D2F, 0xC698,
-    0x7119, 0xFAAE, 0xEDC0, 0x6677, 0xC31C, 0x48AB, 0x5FC5, 0xD472,
-    0x9EA4, 0x1513, 0x027D, 0x89CA, 0x2CA1, 0xA716, 0xB078, 0x3BCF,
-    0x25D4, 0xAE63, 0xB90D, 0x32BA, 0x97D1, 0x1C66, 0x0B08, 0x80BF,
-    0xCA69, 0x41DE, 0x56B0, 0xDD07, 0x786C, 0xF3DB, 0xE4B5, 0x6F02,
-    0x3AB1, 0xB106, 0xA668, 0x2DDF, 0x88B4, 0x0303, 0x146D, 0x9FDA,
-    0xD50C, 0x5EBB, 0x49D5, 0xC262, 0x6709, 0xECBE, 0xFBD0, 0x7067,
-    0x6E7C, 0xE5CB, 0xF2A5, 0x7912, 0xDC79, 0x57CE, 0x40A0, 0xCB17,
-    0x81C1, 0x0A76, 0x1D18, 0x96AF, 0x33C4, 0xB873, 0xAF1D, 0x24AA,
-    0x932B, 0x189C, 0x0FF2, 0x8445, 0x212E, 0xAA99, 0xBDF7, 0x3640,
-    0x7C96, 0xF721, 0xE04F, 0x6BF8, 0xCE93, 0x4524, 0x524A, 0xD9FD,
-    0xC7E6, 0x4C51, 0x5B3F, 0xD088, 0x75E3, 0xFE54, 0xE93A, 0x628D,
-    0x285B, 0xA3EC, 0xB482, 0x3F35, 0x9A5E, 0x11E9, 0x0687, 0x8D30,
-    0xE232, 0x6985, 0x7EEB, 0xF55C, 0x5037, 0xDB80, 0xCCEE, 0x4759,
-    0x0D8F, 0x8638, 0x9156, 0x1AE1, 0xBF8A, 0x343D, 0x2353, 0xA8E4,
-    0xB6FF, 0x3D48, 0x2A26, 0xA191, 0x04FA, 0x8F4D, 0x9823, 0x1394,
-    0x5942, 0xD2F5, 0xC59B, 0x4E2C, 0xEB47, 0x60F0, 0x779E, 0xFC29,
-    0x4BA8, 0xC01F, 0xD771, 0x5CC6, 0xF9AD, 0x721A, 0x6574, 0xEEC3,
-    0xA415, 0x2FA2, 0x38CC, 0xB37B, 0x1610, 0x9DA7, 0x8AC9, 0x017E,
-    0x1F65, 0x94D2, 0x83BC, 0x080B, 0xAD60, 0x26D7, 0x31B9, 0xBA0E,
-    0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3
-};
-
-uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba,
-                           uint32_t reftag);
-uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen,
-                               uint64_t slba);
-void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len,
-                                 uint8_t *mbuf, size_t mlen, uint16_t apptag,
-                                 uint32_t *reftag);
-uint16_t 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, uint32_t *reftag);
-uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req);
-
-
 #endif /* HW_NVME_INTERNAL_H */
-- 
2.35.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 2/6] hw/nvme: add host behavior support feature
  2022-03-01 10:44 [PATCH v2 0/6] hw/nvme: enhanced protection information (64-bit guard) Klaus Jensen
  2022-03-01 10:44 ` [PATCH v2 1/6] hw/nvme: move dif/pi prototypes into dif.h Klaus Jensen
@ 2022-03-01 10:44 ` Klaus Jensen
  2022-03-01 10:44 ` [PATCH v2 3/6] hw/nvme: move format parameter parsing Klaus Jensen
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Klaus Jensen @ 2022-03-01 10:44 UTC (permalink / raw)
  To: qemu-devel, Keith Busch
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Klaus Jensen, Naveen Nagar,
	Philippe Mathieu-Daudé,
	Hanna Reitz, Stefan Hajnoczi, Klaus Jensen

From: Naveen Nagar <naveen.n1@samsung.com>

Add support for getting and setting the Host Behavior Support feature.

Reviewed-by: Keith Busch <kbusch@kernel.org>
Signed-off-by: Naveen Nagar <naveen.n1@samsung.com>
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/ctrl.c       | 8 ++++++++
 hw/nvme/nvme.h       | 4 +++-
 include/block/nvme.h | 9 +++++++++
 3 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index d08af3bdc1a2..71c60482c75f 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -196,6 +196,7 @@ static const bool nvme_feature_support[NVME_FID_MAX] = {
     [NVME_WRITE_ATOMICITY]          = true,
     [NVME_ASYNCHRONOUS_EVENT_CONF]  = true,
     [NVME_TIMESTAMP]                = true,
+    [NVME_HOST_BEHAVIOR_SUPPORT]    = true,
     [NVME_COMMAND_SET_PROFILE]      = true,
 };
 
@@ -206,6 +207,7 @@ static const uint32_t nvme_feature_cap[NVME_FID_MAX] = {
     [NVME_NUMBER_OF_QUEUES]         = NVME_FEAT_CAP_CHANGE,
     [NVME_ASYNCHRONOUS_EVENT_CONF]  = NVME_FEAT_CAP_CHANGE,
     [NVME_TIMESTAMP]                = NVME_FEAT_CAP_CHANGE,
+    [NVME_HOST_BEHAVIOR_SUPPORT]    = NVME_FEAT_CAP_CHANGE,
     [NVME_COMMAND_SET_PROFILE]      = NVME_FEAT_CAP_CHANGE,
 };
 
@@ -5091,6 +5093,9 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeRequest *req)
         goto out;
     case NVME_TIMESTAMP:
         return nvme_get_feature_timestamp(n, req);
+    case NVME_HOST_BEHAVIOR_SUPPORT:
+        return nvme_c2h(n, (uint8_t *)&n->features.hbs,
+                        sizeof(n->features.hbs), req);
     default:
         break;
     }
@@ -5281,6 +5286,9 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req)
         break;
     case NVME_TIMESTAMP:
         return nvme_set_feature_timestamp(n, req);
+    case NVME_HOST_BEHAVIOR_SUPPORT:
+        return nvme_h2c(n, (uint8_t *)&n->features.hbs,
+                        sizeof(n->features.hbs), req);
     case NVME_COMMAND_SET_PROFILE:
         if (dw11 & 0x1ff) {
             trace_pci_nvme_err_invalid_iocsci(dw11 & 0x1ff);
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index 801176a2bd5e..103407038e74 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -468,7 +468,9 @@ typedef struct NvmeCtrl {
             uint16_t temp_thresh_hi;
             uint16_t temp_thresh_low;
         };
-        uint32_t    async_config;
+
+        uint32_t                async_config;
+        NvmeHostBehaviorSupport hbs;
     } features;
 } NvmeCtrl;
 
diff --git a/include/block/nvme.h b/include/block/nvme.h
index cd068ac89142..e527c728f975 100644
--- a/include/block/nvme.h
+++ b/include/block/nvme.h
@@ -1216,6 +1216,7 @@ enum NvmeFeatureIds {
     NVME_WRITE_ATOMICITY            = 0xa,
     NVME_ASYNCHRONOUS_EVENT_CONF    = 0xb,
     NVME_TIMESTAMP                  = 0xe,
+    NVME_HOST_BEHAVIOR_SUPPORT      = 0x16,
     NVME_COMMAND_SET_PROFILE        = 0x19,
     NVME_SOFTWARE_PROGRESS_MARKER   = 0x80,
     NVME_FID_MAX                    = 0x100,
@@ -1257,6 +1258,13 @@ typedef struct QEMU_PACKED NvmeRangeType {
     uint8_t     rsvd48[16];
 } NvmeRangeType;
 
+typedef struct NvmeHostBehaviorSupport {
+    uint8_t     acre;
+    uint8_t     etdas;
+    uint8_t     lbafee;
+    uint8_t     rsvd3[509];
+} NvmeHostBehaviorSupport;
+
 typedef struct QEMU_PACKED NvmeLBAF {
     uint16_t    ms;
     uint8_t     ds;
@@ -1520,6 +1528,7 @@ static inline void _nvme_check_size(void)
     QEMU_BUILD_BUG_ON(sizeof(NvmeDsmCmd) != 64);
     QEMU_BUILD_BUG_ON(sizeof(NvmeCopyCmd) != 64);
     QEMU_BUILD_BUG_ON(sizeof(NvmeRangeType) != 64);
+    QEMU_BUILD_BUG_ON(sizeof(NvmeHostBehaviorSupport) != 512);
     QEMU_BUILD_BUG_ON(sizeof(NvmeErrorLog) != 64);
     QEMU_BUILD_BUG_ON(sizeof(NvmeFwSlotInfoLog) != 512);
     QEMU_BUILD_BUG_ON(sizeof(NvmeSmartLog) != 512);
-- 
2.35.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 3/6] hw/nvme: move format parameter parsing
  2022-03-01 10:44 [PATCH v2 0/6] hw/nvme: enhanced protection information (64-bit guard) Klaus Jensen
  2022-03-01 10:44 ` [PATCH v2 1/6] hw/nvme: move dif/pi prototypes into dif.h Klaus Jensen
  2022-03-01 10:44 ` [PATCH v2 2/6] hw/nvme: add host behavior support feature Klaus Jensen
@ 2022-03-01 10:44 ` Klaus Jensen
  2022-03-01 10:44 ` [PATCH v2 4/6] hw/nvme: add support for the lbafee hbs feature Klaus Jensen
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Klaus Jensen @ 2022-03-01 10:44 UTC (permalink / raw)
  To: qemu-devel, Keith Busch
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Klaus Jensen, Naveen Nagar,
	Philippe Mathieu-Daudé,
	Hanna Reitz, Stefan Hajnoczi, Klaus Jensen

From: Klaus Jensen <k.jensen@samsung.com>

There is no need to extract the format command parameters for each
namespace. Move it to the entry point.

Reviewed-by: Keith Busch <kbusch@kernel.org>
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/ctrl.c | 31 ++++++++++++++++++-------------
 1 file changed, 18 insertions(+), 13 deletions(-)

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 71c60482c75f..d8701ebf2fa8 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -5452,6 +5452,11 @@ typedef struct NvmeFormatAIOCB {
     uint32_t nsid;
     bool broadcast;
     int64_t offset;
+
+    uint8_t lbaf;
+    uint8_t mset;
+    uint8_t pi;
+    uint8_t pil;
 } NvmeFormatAIOCB;
 
 static void nvme_format_bh(void *opaque);
@@ -5471,14 +5476,9 @@ static const AIOCBInfo nvme_format_aiocb_info = {
     .get_aio_context = nvme_get_aio_context,
 };
 
-static void nvme_format_set(NvmeNamespace *ns, NvmeCmd *cmd)
+static void nvme_format_set(NvmeNamespace *ns, uint8_t lbaf, uint8_t mset,
+                            uint8_t pi, uint8_t pil)
 {
-    uint32_t dw10 = le32_to_cpu(cmd->cdw10);
-    uint8_t lbaf = dw10 & 0xf;
-    uint8_t pi = (dw10 >> 5) & 0x7;
-    uint8_t mset = (dw10 >> 4) & 0x1;
-    uint8_t pil = (dw10 >> 8) & 0x1;
-
     trace_pci_nvme_format_set(ns->params.nsid, lbaf, mset, pi, pil);
 
     ns->id_ns.dps = (pil << 3) | pi;
@@ -5490,7 +5490,6 @@ static void nvme_format_set(NvmeNamespace *ns, NvmeCmd *cmd)
 static void nvme_format_ns_cb(void *opaque, int ret)
 {
     NvmeFormatAIOCB *iocb = opaque;
-    NvmeRequest *req = iocb->req;
     NvmeNamespace *ns = iocb->ns;
     int bytes;
 
@@ -5512,7 +5511,7 @@ static void nvme_format_ns_cb(void *opaque, int ret)
         return;
     }
 
-    nvme_format_set(ns, &req->cmd);
+    nvme_format_set(ns, iocb->lbaf, iocb->mset, iocb->pi, iocb->pil);
     ns->status = 0x0;
     iocb->ns = NULL;
     iocb->offset = 0;
@@ -5548,9 +5547,6 @@ static void nvme_format_bh(void *opaque)
     NvmeFormatAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
     NvmeCtrl *n = nvme_ctrl(req);
-    uint32_t dw10 = le32_to_cpu(req->cmd.cdw10);
-    uint8_t lbaf = dw10 & 0xf;
-    uint8_t pi = (dw10 >> 5) & 0x7;
     uint16_t status;
     int i;
 
@@ -5572,7 +5568,7 @@ static void nvme_format_bh(void *opaque)
         goto done;
     }
 
-    status = nvme_format_check(iocb->ns, lbaf, pi);
+    status = nvme_format_check(iocb->ns, iocb->lbaf, iocb->pi);
     if (status) {
         req->status = status;
         goto done;
@@ -5595,6 +5591,11 @@ static uint16_t nvme_format(NvmeCtrl *n, NvmeRequest *req)
 {
     NvmeFormatAIOCB *iocb;
     uint32_t nsid = le32_to_cpu(req->cmd.nsid);
+    uint32_t dw10 = le32_to_cpu(req->cmd.cdw10);
+    uint8_t lbaf = dw10 & 0xf;
+    uint8_t mset = (dw10 >> 4) & 0x1;
+    uint8_t pi = (dw10 >> 5) & 0x7;
+    uint8_t pil = (dw10 >> 8) & 0x1;
     uint16_t status;
 
     iocb = qemu_aio_get(&nvme_format_aiocb_info, NULL, nvme_misc_cb, req);
@@ -5604,6 +5605,10 @@ static uint16_t nvme_format(NvmeCtrl *n, NvmeRequest *req)
     iocb->ret = 0;
     iocb->ns = NULL;
     iocb->nsid = 0;
+    iocb->lbaf = lbaf;
+    iocb->mset = mset;
+    iocb->pi = pi;
+    iocb->pil = pil;
     iocb->broadcast = (nsid == NVME_NSID_BROADCAST);
     iocb->offset = 0;
 
-- 
2.35.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 4/6] hw/nvme: add support for the lbafee hbs feature
  2022-03-01 10:44 [PATCH v2 0/6] hw/nvme: enhanced protection information (64-bit guard) Klaus Jensen
                   ` (2 preceding siblings ...)
  2022-03-01 10:44 ` [PATCH v2 3/6] hw/nvme: move format parameter parsing Klaus Jensen
@ 2022-03-01 10:44 ` Klaus Jensen
  2022-03-01 10:44 ` [PATCH v2 5/6] hw/nvme: add pi tuple size helper Klaus Jensen
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Klaus Jensen @ 2022-03-01 10:44 UTC (permalink / raw)
  To: qemu-devel, Keith Busch
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Klaus Jensen, Naveen Nagar,
	Philippe Mathieu-Daudé,
	Hanna Reitz, Stefan Hajnoczi, Klaus Jensen

From: Naveen Nagar <naveen.n1@samsung.com>

Add support for up to 64 LBA formats through the LBAFEE field of the
Host Behavior Support feature.

Reviewed-by: Keith Busch <kbusch@kernel.org>
Signed-off-by: Naveen Nagar <naveen.n1@samsung.com>
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/ctrl.c       | 34 +++++++++++++++++++++++++++++++---
 hw/nvme/ns.c         | 15 +++++++++------
 hw/nvme/nvme.h       |  1 +
 include/block/nvme.h |  7 +++++--
 4 files changed, 46 insertions(+), 11 deletions(-)

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index d8701ebf2fa8..52ab3450b975 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -5165,6 +5165,7 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req)
     uint32_t nsid = le32_to_cpu(cmd->nsid);
     uint8_t fid = NVME_GETSETFEAT_FID(dw10);
     uint8_t save = NVME_SETFEAT_SAVE(dw10);
+    uint16_t status;
     int i;
 
     trace_pci_nvme_setfeat(nvme_cid(req), nsid, fid, save, dw11);
@@ -5287,8 +5288,26 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req)
     case NVME_TIMESTAMP:
         return nvme_set_feature_timestamp(n, req);
     case NVME_HOST_BEHAVIOR_SUPPORT:
-        return nvme_h2c(n, (uint8_t *)&n->features.hbs,
-                        sizeof(n->features.hbs), req);
+        status = nvme_h2c(n, (uint8_t *)&n->features.hbs,
+                          sizeof(n->features.hbs), req);
+        if (status) {
+            return status;
+        }
+
+        for (i = 1; i <= NVME_MAX_NAMESPACES; i++) {
+            ns = nvme_ns(n, i);
+
+            if (!ns) {
+                continue;
+            }
+
+            ns->id_ns.nlbaf = ns->nlbaf - 1;
+            if (!n->features.hbs.lbafee) {
+                ns->id_ns.nlbaf = MIN(ns->id_ns.nlbaf, 15);
+            }
+        }
+
+        return status;
     case NVME_COMMAND_SET_PROFILE:
         if (dw11 & 0x1ff) {
             trace_pci_nvme_err_invalid_iocsci(dw11 & 0x1ff);
@@ -5479,10 +5498,13 @@ static const AIOCBInfo nvme_format_aiocb_info = {
 static void nvme_format_set(NvmeNamespace *ns, uint8_t lbaf, uint8_t mset,
                             uint8_t pi, uint8_t pil)
 {
+    uint8_t lbafl = lbaf & 0xf;
+    uint8_t lbafu = lbaf >> 4;
+
     trace_pci_nvme_format_set(ns->params.nsid, lbaf, mset, pi, pil);
 
     ns->id_ns.dps = (pil << 3) | pi;
-    ns->id_ns.flbas = lbaf | (mset << 4);
+    ns->id_ns.flbas = (lbafu << 5) | (mset << 4) | lbafl;
 
     nvme_ns_init_format(ns);
 }
@@ -5596,6 +5618,7 @@ static uint16_t nvme_format(NvmeCtrl *n, NvmeRequest *req)
     uint8_t mset = (dw10 >> 4) & 0x1;
     uint8_t pi = (dw10 >> 5) & 0x7;
     uint8_t pil = (dw10 >> 8) & 0x1;
+    uint8_t lbafu = (dw10 >> 12) & 0x3;
     uint16_t status;
 
     iocb = qemu_aio_get(&nvme_format_aiocb_info, NULL, nvme_misc_cb, req);
@@ -5612,6 +5635,10 @@ static uint16_t nvme_format(NvmeCtrl *n, NvmeRequest *req)
     iocb->broadcast = (nsid == NVME_NSID_BROADCAST);
     iocb->offset = 0;
 
+    if (n->features.hbs.lbafee) {
+        iocb->lbaf |= lbafu << 4;
+    }
+
     if (!iocb->broadcast) {
         if (!nvme_nsid_valid(n, nsid)) {
             status = NVME_INVALID_NSID | NVME_DNR;
@@ -6587,6 +6614,7 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev)
     id->cntlid = cpu_to_le16(n->cntlid);
 
     id->oaes = cpu_to_le32(NVME_OAES_NS_ATTR);
+    id->ctratt |= cpu_to_le32(NVME_CTRATT_ELBAS);
 
     id->rab = 6;
 
diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c
index ee673f1a5bef..8dfb55130beb 100644
--- a/hw/nvme/ns.c
+++ b/hw/nvme/ns.c
@@ -112,10 +112,11 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp)
         [7] = { .ds = 12, .ms = 64 },
     };
 
+    ns->nlbaf = 8;
+
     memcpy(&id_ns->lbaf, &lbaf, sizeof(lbaf));
-    id_ns->nlbaf = 7;
 
-    for (i = 0; i <= id_ns->nlbaf; i++) {
+    for (i = 0; i < ns->nlbaf; i++) {
         NvmeLBAF *lbaf = &id_ns->lbaf[i];
         if (lbaf->ds == ds) {
             if (lbaf->ms == ms) {
@@ -126,12 +127,14 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp)
     }
 
     /* add non-standard lba format */
-    id_ns->nlbaf++;
-    id_ns->lbaf[id_ns->nlbaf].ds = ds;
-    id_ns->lbaf[id_ns->nlbaf].ms = ms;
-    id_ns->flbas |= id_ns->nlbaf;
+    id_ns->lbaf[ns->nlbaf].ds = ds;
+    id_ns->lbaf[ns->nlbaf].ms = ms;
+    ns->nlbaf++;
+
+    id_ns->flbas |= i;
 
 lbaf_found:
+    id_ns->nlbaf = ns->nlbaf - 1;
     nvme_ns_init_format(ns);
 
     return 0;
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index 103407038e74..e715c3255a29 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -128,6 +128,7 @@ typedef struct NvmeNamespace {
     int64_t      moff;
     NvmeIdNs     id_ns;
     NvmeLBAF     lbaf;
+    unsigned int nlbaf;
     size_t       lbasz;
     const uint32_t *iocs;
     uint8_t      csi;
diff --git a/include/block/nvme.h b/include/block/nvme.h
index e527c728f975..37afc9be9b18 100644
--- a/include/block/nvme.h
+++ b/include/block/nvme.h
@@ -1111,6 +1111,10 @@ enum NvmeIdCtrlOaes {
     NVME_OAES_NS_ATTR   = 1 << 8,
 };
 
+enum NvmeIdCtrlCtratt {
+    NVME_CTRATT_ELBAS   = 1 << 15,
+};
+
 enum NvmeIdCtrlOacs {
     NVME_OACS_SECURITY  = 1 << 0,
     NVME_OACS_FORMAT    = 1 << 1,
@@ -1312,8 +1316,7 @@ typedef struct QEMU_PACKED NvmeIdNs {
     uint8_t     rsvd81[23];
     uint8_t     nguid[16];
     uint64_t    eui64;
-    NvmeLBAF    lbaf[16];
-    uint8_t     rsvd192[192];
+    NvmeLBAF    lbaf[64];
     uint8_t     vs[3712];
 } NvmeIdNs;
 
-- 
2.35.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 5/6] hw/nvme: add pi tuple size helper
  2022-03-01 10:44 [PATCH v2 0/6] hw/nvme: enhanced protection information (64-bit guard) Klaus Jensen
                   ` (3 preceding siblings ...)
  2022-03-01 10:44 ` [PATCH v2 4/6] hw/nvme: add support for the lbafee hbs feature Klaus Jensen
@ 2022-03-01 10:44 ` Klaus Jensen
  2022-03-01 10:44 ` [PATCH v2 6/6] hw/nvme: 64-bit pi support Klaus Jensen
  2022-03-01 14:54 ` [PATCH v2 0/6] hw/nvme: enhanced protection information (64-bit guard) Keith Busch
  6 siblings, 0 replies; 9+ messages in thread
From: Klaus Jensen @ 2022-03-01 10:44 UTC (permalink / raw)
  To: qemu-devel, Keith Busch
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Klaus Jensen, Naveen Nagar,
	Philippe Mathieu-Daudé,
	Hanna Reitz, Stefan Hajnoczi, Klaus Jensen

From: Klaus Jensen <k.jensen@samsung.com>

A subsequent patch will introduce a new tuple size; so add a helper and
use that instead of sizeof() and magic numbers.

Reviewed-by: Keith Busch <kbusch@kernel.org>
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/ctrl.c | 14 ++++++++------
 hw/nvme/dif.c  | 16 ++++++++--------
 hw/nvme/dif.h  |  5 +++++
 3 files changed, 21 insertions(+), 14 deletions(-)

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 52ab3450b975..f1683960b87e 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -1068,7 +1068,8 @@ static uint16_t nvme_map_data(NvmeCtrl *n, uint32_t nlb, NvmeRequest *req)
     size_t len = nvme_l2b(ns, nlb);
     uint16_t status;
 
-    if (nvme_ns_ext(ns) && !(pi && pract && ns->lbaf.ms == 8)) {
+    if (nvme_ns_ext(ns) &&
+        !(pi && pract && ns->lbaf.ms == nvme_pi_tuple_size(ns))) {
         NvmeSg sg;
 
         len += nvme_m2b(ns, nlb);
@@ -1247,7 +1248,8 @@ uint16_t nvme_bounce_data(NvmeCtrl *n, void *ptr, uint32_t len,
     bool pi = !!NVME_ID_NS_DPS_TYPE(ns->id_ns.dps);
     bool pract = !!(le16_to_cpu(rw->control) & NVME_RW_PRINFO_PRACT);
 
-    if (nvme_ns_ext(ns) && !(pi && pract && ns->lbaf.ms == 8)) {
+    if (nvme_ns_ext(ns) &&
+        !(pi && pract && ns->lbaf.ms == nvme_pi_tuple_size(ns))) {
         return nvme_tx_interleaved(n, &req->sg, ptr, len, ns->lbasz,
                                    ns->lbaf.ms, 0, dir);
     }
@@ -2184,7 +2186,7 @@ static void nvme_compare_mdata_cb(void *opaque, int ret)
          * tuple.
          */
         if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
-            pil = ns->lbaf.ms - sizeof(NvmeDifTuple);
+            pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
         }
 
         for (bufp = buf; mbufp < end; bufp += ns->lbaf.ms, mbufp += ns->lbaf.ms) {
@@ -3167,7 +3169,7 @@ static uint16_t nvme_read(NvmeCtrl *n, NvmeRequest *req)
         if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
             bool pract = prinfo & NVME_PRINFO_PRACT;
 
-            if (pract && ns->lbaf.ms == 8) {
+            if (pract && ns->lbaf.ms == nvme_pi_tuple_size(ns)) {
                 mapped_size = data_size;
             }
         }
@@ -3244,7 +3246,7 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append,
         if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
             bool pract = prinfo & NVME_PRINFO_PRACT;
 
-            if (pract && ns->lbaf.ms == 8) {
+            if (pract && ns->lbaf.ms == nvme_pi_tuple_size(ns)) {
                 mapped_size -= nvme_m2b(ns, nlb);
             }
         }
@@ -5553,7 +5555,7 @@ static uint16_t nvme_format_check(NvmeNamespace *ns, uint8_t lbaf, uint8_t pi)
         return NVME_INVALID_FORMAT | NVME_DNR;
     }
 
-    if (pi && (ns->id_ns.lbaf[lbaf].ms < sizeof(NvmeDifTuple))) {
+    if (pi && (ns->id_ns.lbaf[lbaf].ms < nvme_pi_tuple_size(ns))) {
         return NVME_INVALID_FORMAT | NVME_DNR;
     }
 
diff --git a/hw/nvme/dif.c b/hw/nvme/dif.c
index cd0cea2b5ebd..891385f33f20 100644
--- a/hw/nvme/dif.c
+++ b/hw/nvme/dif.c
@@ -48,7 +48,7 @@ void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len,
     int16_t pil = 0;
 
     if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
-        pil = ns->lbaf.ms - sizeof(NvmeDifTuple);
+        pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
     }
 
     trace_pci_nvme_dif_pract_generate_dif(len, ns->lbasz, ns->lbasz + pil,
@@ -145,7 +145,7 @@ uint16_t nvme_dif_check(NvmeNamespace *ns, uint8_t *buf, size_t len,
     }
 
     if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
-        pil = ns->lbaf.ms - sizeof(NvmeDifTuple);
+        pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
     }
 
     trace_pci_nvme_dif_check(prinfo, ns->lbasz + pil);
@@ -184,7 +184,7 @@ uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen,
 
 
     if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
-        pil = ns->lbaf.ms - sizeof(NvmeDifTuple);
+        pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
     }
 
     do {
@@ -210,7 +210,7 @@ uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen,
             end = mbufp + mlen;
 
             for (; mbufp < end; mbufp += ns->lbaf.ms) {
-                memset(mbufp + pil, 0xff, sizeof(NvmeDifTuple));
+                memset(mbufp + pil, 0xff, nvme_pi_tuple_size(ns));
             }
         }
 
@@ -284,7 +284,7 @@ static void nvme_dif_rw_check_cb(void *opaque, int ret)
         goto out;
     }
 
-    if (prinfo & NVME_PRINFO_PRACT && ns->lbaf.ms == 8) {
+    if (prinfo & NVME_PRINFO_PRACT && ns->lbaf.ms == nvme_pi_tuple_size(ns)) {
         goto out;
     }
 
@@ -388,7 +388,7 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
 
         if (pract) {
             uint8_t *mbuf, *end;
-            int16_t pil = ns->lbaf.ms - sizeof(NvmeDifTuple);
+            int16_t pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
 
             status = nvme_check_prinfo(ns, prinfo, slba, reftag);
             if (status) {
@@ -428,7 +428,7 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
         return NVME_NO_COMPLETE;
     }
 
-    if (nvme_ns_ext(ns) && !(pract && ns->lbaf.ms == 8)) {
+    if (nvme_ns_ext(ns) && !(pract && ns->lbaf.ms == nvme_pi_tuple_size(ns))) {
         mapped_len += mlen;
     }
 
@@ -462,7 +462,7 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
     qemu_iovec_init(&ctx->mdata.iov, 1);
     qemu_iovec_add(&ctx->mdata.iov, ctx->mdata.bounce, mlen);
 
-    if (!(pract && ns->lbaf.ms == 8)) {
+    if (!(pract && ns->lbaf.ms == nvme_pi_tuple_size(ns))) {
         status = nvme_bounce_mdata(n, ctx->mdata.bounce, ctx->mdata.iov.size,
                                    NVME_TX_DIRECTION_TO_DEVICE, req);
         if (status) {
diff --git a/hw/nvme/dif.h b/hw/nvme/dif.h
index e36fea30e71e..ab6dbb09463e 100644
--- a/hw/nvme/dif.h
+++ b/hw/nvme/dif.h
@@ -37,6 +37,11 @@ static const uint16_t t10_dif_crc_table[256] = {
     0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3
 };
 
+static inline size_t nvme_pi_tuple_size(NvmeNamespace *ns)
+{
+    return sizeof(NvmeDifTuple);
+}
+
 uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba,
                            uint32_t reftag);
 uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen,
-- 
2.35.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 6/6] hw/nvme: 64-bit pi support
  2022-03-01 10:44 [PATCH v2 0/6] hw/nvme: enhanced protection information (64-bit guard) Klaus Jensen
                   ` (4 preceding siblings ...)
  2022-03-01 10:44 ` [PATCH v2 5/6] hw/nvme: add pi tuple size helper Klaus Jensen
@ 2022-03-01 10:44 ` Klaus Jensen
  2022-03-01 14:54 ` [PATCH v2 0/6] hw/nvme: enhanced protection information (64-bit guard) Keith Busch
  6 siblings, 0 replies; 9+ messages in thread
From: Klaus Jensen @ 2022-03-01 10:44 UTC (permalink / raw)
  To: qemu-devel, Keith Busch
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Klaus Jensen, Naveen Nagar,
	Philippe Mathieu-Daudé,
	Hanna Reitz, Stefan Hajnoczi, Klaus Jensen

From: Naveen Nagar <naveen.n1@samsung.com>

This adds support for one possible new protection information format
introduced in TP4068 (and integrated in NVMe 2.0): the 64-bit CRC guard
and 48-bit reference tag. This version does not support storage tags.

Like the CRC16 support already present, this uses a software
implementation of CRC64 (so it is naturally pretty slow). But its good
enough for verification purposes.

This may go nicely hand-in-hand with the support that Keith submitted
for the Linux kernel[1].

  [1]: https://lore.kernel.org/linux-nvme/20220126165214.GA1782352@dhcp-10-100-145-180.wdc.com/T/

Signed-off-by: Naveen Nagar <naveen.n1@samsung.com>
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/ctrl.c       | 163 +++++++++++++++----
 hw/nvme/dif.c        | 363 +++++++++++++++++++++++++++++++++----------
 hw/nvme/dif.h        | 143 ++++++++++++++++-
 hw/nvme/ns.c         |  35 ++++-
 hw/nvme/nvme.h       |   3 +
 hw/nvme/trace-events |  12 +-
 include/block/nvme.h |  67 ++++++--
 7 files changed, 648 insertions(+), 138 deletions(-)

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index f1683960b87e..03760ddeae8c 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -2050,9 +2050,12 @@ static void nvme_verify_cb(void *opaque, int ret)
     uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
     uint16_t apptag = le16_to_cpu(rw->apptag);
     uint16_t appmask = le16_to_cpu(rw->appmask);
-    uint32_t reftag = le32_to_cpu(rw->reftag);
+    uint64_t reftag = le32_to_cpu(rw->reftag);
+    uint64_t cdw3 = le32_to_cpu(rw->cdw3);
     uint16_t status;
 
+    reftag |= cdw3 << 32;
+
     trace_pci_nvme_verify_cb(nvme_cid(req), prinfo, apptag, appmask, reftag);
 
     if (ret) {
@@ -2141,7 +2144,8 @@ static void nvme_compare_mdata_cb(void *opaque, int ret)
     uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
     uint16_t apptag = le16_to_cpu(rw->apptag);
     uint16_t appmask = le16_to_cpu(rw->appmask);
-    uint32_t reftag = le32_to_cpu(rw->reftag);
+    uint64_t reftag = le32_to_cpu(rw->reftag);
+    uint64_t cdw3 = le32_to_cpu(rw->cdw3);
     struct nvme_compare_ctx *ctx = req->opaque;
     g_autofree uint8_t *buf = NULL;
     BlockBackend *blk = ns->blkconf.blk;
@@ -2149,6 +2153,8 @@ static void nvme_compare_mdata_cb(void *opaque, int ret)
     BlockAcctStats *stats = blk_get_stats(blk);
     uint16_t status = NVME_SUCCESS;
 
+    reftag |= cdw3 << 32;
+
     trace_pci_nvme_compare_mdata_cb(nvme_cid(req));
 
     if (ret) {
@@ -2527,7 +2533,8 @@ typedef struct NvmeCopyAIOCB {
     QEMUBH *bh;
     int ret;
 
-    NvmeCopySourceRange *ranges;
+    void *ranges;
+    unsigned int format;
     int nr;
     int idx;
 
@@ -2538,7 +2545,7 @@ typedef struct NvmeCopyAIOCB {
         BlockAcctCookie write;
     } acct;
 
-    uint32_t reftag;
+    uint64_t reftag;
     uint64_t slba;
 
     NvmeZone *zone;
@@ -2592,13 +2599,101 @@ static void nvme_copy_bh(void *opaque)
 
 static void nvme_copy_cb(void *opaque, int ret);
 
+static void nvme_copy_source_range_parse_format0(void *ranges, int idx,
+                                                 uint64_t *slba, uint32_t *nlb,
+                                                 uint16_t *apptag,
+                                                 uint16_t *appmask,
+                                                 uint64_t *reftag)
+{
+    NvmeCopySourceRangeFormat0 *_ranges = ranges;
+
+    if (slba) {
+        *slba = le64_to_cpu(_ranges[idx].slba);
+    }
+
+    if (nlb) {
+        *nlb = le16_to_cpu(_ranges[idx].nlb) + 1;
+    }
+
+    if (apptag) {
+        *apptag = le16_to_cpu(_ranges[idx].apptag);
+    }
+
+    if (appmask) {
+        *appmask = le16_to_cpu(_ranges[idx].appmask);
+    }
+
+    if (reftag) {
+        *reftag = le32_to_cpu(_ranges[idx].reftag);
+    }
+}
+
+static void nvme_copy_source_range_parse_format1(void *ranges, int idx,
+                                                 uint64_t *slba, uint32_t *nlb,
+                                                 uint16_t *apptag,
+                                                 uint16_t *appmask,
+                                                 uint64_t *reftag)
+{
+    NvmeCopySourceRangeFormat1 *_ranges = ranges;
+
+    if (slba) {
+        *slba = le64_to_cpu(_ranges[idx].slba);
+    }
+
+    if (nlb) {
+        *nlb = le16_to_cpu(_ranges[idx].nlb) + 1;
+    }
+
+    if (apptag) {
+        *apptag = le16_to_cpu(_ranges[idx].apptag);
+    }
+
+    if (appmask) {
+        *appmask = le16_to_cpu(_ranges[idx].appmask);
+    }
+
+    if (reftag) {
+        *reftag = 0;
+
+        *reftag |= (uint64_t)_ranges[idx].sr[4] << 40;
+        *reftag |= (uint64_t)_ranges[idx].sr[5] << 32;
+        *reftag |= (uint64_t)_ranges[idx].sr[6] << 24;
+        *reftag |= (uint64_t)_ranges[idx].sr[7] << 16;
+        *reftag |= (uint64_t)_ranges[idx].sr[8] << 8;
+        *reftag |= (uint64_t)_ranges[idx].sr[9];
+    }
+}
+
+static void nvme_copy_source_range_parse(void *ranges, int idx, uint8_t format,
+                                         uint64_t *slba, uint32_t *nlb,
+                                         uint16_t *apptag, uint16_t *appmask,
+                                         uint64_t *reftag)
+{
+    switch (format) {
+    case NVME_COPY_FORMAT_0:
+        nvme_copy_source_range_parse_format0(ranges, idx, slba, nlb, apptag,
+                                             appmask, reftag);
+        break;
+
+    case NVME_COPY_FORMAT_1:
+        nvme_copy_source_range_parse_format1(ranges, idx, slba, nlb, apptag,
+                                             appmask, reftag);
+        break;
+
+    default:
+        abort();
+    }
+}
+
 static void nvme_copy_out_completed_cb(void *opaque, int ret)
 {
     NvmeCopyAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
     NvmeNamespace *ns = req->ns;
-    NvmeCopySourceRange *range = &iocb->ranges[iocb->idx];
-    uint32_t nlb = le32_to_cpu(range->nlb) + 1;
+    uint32_t nlb;
+
+    nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, NULL,
+                                 &nlb, NULL, NULL, NULL);
 
     if (ret < 0) {
         iocb->ret = ret;
@@ -2622,7 +2717,6 @@ static void nvme_copy_out_cb(void *opaque, int ret)
     NvmeCopyAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
     NvmeNamespace *ns = req->ns;
-    NvmeCopySourceRange *range;
     uint32_t nlb;
     size_t mlen;
     uint8_t *mbounce;
@@ -2639,8 +2733,8 @@ static void nvme_copy_out_cb(void *opaque, int ret)
         return;
     }
 
-    range = &iocb->ranges[iocb->idx];
-    nlb = le32_to_cpu(range->nlb) + 1;
+    nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, NULL,
+                                 &nlb, NULL, NULL, NULL);
 
     mlen = nvme_m2b(ns, nlb);
     mbounce = iocb->bounce + nvme_l2b(ns, nlb);
@@ -2663,8 +2757,10 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret)
     NvmeCopyAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
     NvmeNamespace *ns = req->ns;
-    NvmeCopySourceRange *range;
     uint32_t nlb;
+    uint64_t slba;
+    uint16_t apptag, appmask;
+    uint64_t reftag;
     size_t len;
     uint16_t status;
 
@@ -2675,8 +2771,8 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret)
         goto out;
     }
 
-    range = &iocb->ranges[iocb->idx];
-    nlb = le32_to_cpu(range->nlb) + 1;
+    nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, &slba,
+                                 &nlb, &apptag, &appmask, &reftag);
     len = nvme_l2b(ns, nlb);
 
     trace_pci_nvme_copy_out(iocb->slba, nlb);
@@ -2687,11 +2783,6 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret)
         uint16_t prinfor = ((copy->control[0] >> 4) & 0xf);
         uint16_t prinfow = ((copy->control[2] >> 2) & 0xf);
 
-        uint16_t apptag = le16_to_cpu(range->apptag);
-        uint16_t appmask = le16_to_cpu(range->appmask);
-        uint32_t reftag = le32_to_cpu(range->reftag);
-
-        uint64_t slba = le64_to_cpu(range->slba);
         size_t mlen = nvme_m2b(ns, nlb);
         uint8_t *mbounce = iocb->bounce + nvme_l2b(ns, nlb);
 
@@ -2764,7 +2855,6 @@ static void nvme_copy_in_cb(void *opaque, int ret)
     NvmeCopyAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
     NvmeNamespace *ns = req->ns;
-    NvmeCopySourceRange *range;
     uint64_t slba;
     uint32_t nlb;
 
@@ -2780,9 +2870,8 @@ static void nvme_copy_in_cb(void *opaque, int ret)
         return;
     }
 
-    range = &iocb->ranges[iocb->idx];
-    slba = le64_to_cpu(range->slba);
-    nlb = le32_to_cpu(range->nlb) + 1;
+    nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, &slba,
+                                 &nlb, NULL, NULL, NULL);
 
     qemu_iovec_reset(&iocb->iov);
     qemu_iovec_add(&iocb->iov, iocb->bounce + nvme_l2b(ns, nlb),
@@ -2802,7 +2891,6 @@ static void nvme_copy_cb(void *opaque, int ret)
     NvmeCopyAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
     NvmeNamespace *ns = req->ns;
-    NvmeCopySourceRange *range;
     uint64_t slba;
     uint32_t nlb;
     size_t len;
@@ -2819,9 +2907,8 @@ static void nvme_copy_cb(void *opaque, int ret)
         goto done;
     }
 
-    range = &iocb->ranges[iocb->idx];
-    slba = le64_to_cpu(range->slba);
-    nlb = le32_to_cpu(range->nlb) + 1;
+    nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, &slba,
+                                 &nlb, NULL, NULL, NULL);
     len = nvme_l2b(ns, nlb);
 
     trace_pci_nvme_copy_source_range(slba, nlb);
@@ -2877,6 +2964,7 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req)
     uint8_t format = copy->control[0] & 0xf;
     uint16_t prinfor = ((copy->control[0] >> 4) & 0xf);
     uint16_t prinfow = ((copy->control[2] >> 2) & 0xf);
+    size_t len = sizeof(NvmeCopySourceRangeFormat0);
 
     uint16_t status;
 
@@ -2902,10 +2990,18 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req)
         goto invalid;
     }
 
-    iocb->ranges = g_new(NvmeCopySourceRange, nr);
+    if (ns->pif && format != 0x1) {
+        status = NVME_INVALID_FORMAT | NVME_DNR;
+        goto invalid;
+    }
 
-    status = nvme_h2c(n, (uint8_t *)iocb->ranges,
-                      sizeof(NvmeCopySourceRange) * nr, req);
+    if (ns->pif) {
+        len = sizeof(NvmeCopySourceRangeFormat1);
+    }
+
+    iocb->format = format;
+    iocb->ranges = g_malloc_n(nr, len);
+    status = nvme_h2c(n, (uint8_t *)iocb->ranges, len * nr, req);
     if (status) {
         goto invalid;
     }
@@ -2931,6 +3027,7 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req)
     iocb->nr = nr;
     iocb->idx = 0;
     iocb->reftag = le32_to_cpu(copy->reftag);
+    iocb->reftag |= (uint64_t)le32_to_cpu(copy->cdw3) << 32;
     iocb->bounce = g_malloc_n(le16_to_cpu(ns->id_ns.mssrl),
                               ns->lbasz + ns->lbaf.ms);
 
@@ -4717,7 +4814,8 @@ static uint16_t nvme_identify_ns_csi(NvmeCtrl *n, NvmeRequest *req,
     }
 
     if (c->csi == NVME_CSI_NVM) {
-        return nvme_rpt_empty_id_struct(n, req);
+        return nvme_c2h(n, (uint8_t *)&ns->id_ns_nvm, sizeof(NvmeIdNsNvm),
+                        req);
     } else if (c->csi == NVME_CSI_ZONED && ns->csi == NVME_CSI_ZONED) {
         return nvme_c2h(n, (uint8_t *)ns->id_ns_zoned, sizeof(NvmeIdNsZoned),
                         req);
@@ -5571,6 +5669,9 @@ static void nvme_format_bh(void *opaque)
     NvmeFormatAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
     NvmeCtrl *n = nvme_ctrl(req);
+    uint32_t dw10 = le32_to_cpu(req->cmd.cdw10);
+    uint8_t lbaf = dw10 & 0xf;
+    uint8_t pi = (dw10 >> 5) & 0x7;
     uint16_t status;
     int i;
 
@@ -5592,7 +5693,7 @@ static void nvme_format_bh(void *opaque)
         goto done;
     }
 
-    status = nvme_format_check(iocb->ns, iocb->lbaf, iocb->pi);
+    status = nvme_format_check(iocb->ns, lbaf, pi);
     if (status) {
         req->status = status;
         goto done;
@@ -6671,7 +6772,7 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev)
      */
     id->vwc = NVME_VWC_NSID_BROADCAST_SUPPORT | NVME_VWC_PRESENT;
 
-    id->ocfs = cpu_to_le16(NVME_OCFS_COPY_FORMAT_0);
+    id->ocfs = cpu_to_le16(NVME_OCFS_COPY_FORMAT_0 | NVME_OCFS_COPY_FORMAT_1);
     id->sgls = cpu_to_le32(NVME_CTRL_SGLS_SUPPORT_NO_ALIGN |
                            NVME_CTRL_SGLS_BITBUCKET);
 
diff --git a/hw/nvme/dif.c b/hw/nvme/dif.c
index 891385f33f20..62d885f83ea4 100644
--- a/hw/nvme/dif.c
+++ b/hw/nvme/dif.c
@@ -17,10 +17,12 @@
 #include "trace.h"
 
 uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba,
-                           uint32_t reftag)
+                           uint64_t reftag)
 {
+    uint64_t mask = ns->pif ? 0xffffffffffff : 0xffffffff;
+
     if ((NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) == NVME_ID_NS_DPS_TYPE_1) &&
-        (prinfo & NVME_PRINFO_PRCHK_REF) && (slba & 0xffffffff) != reftag) {
+        (prinfo & NVME_PRINFO_PRCHK_REF) && (slba & mask) != reftag) {
         return NVME_INVALID_PROT_INFO | NVME_DNR;
     }
 
@@ -28,114 +30,270 @@ uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba,
 }
 
 /* from Linux kernel (crypto/crct10dif_common.c) */
-static uint16_t crc_t10dif(uint16_t crc, const unsigned char *buffer,
-                           size_t len)
+static uint16_t crc16_t10dif(uint16_t crc, const unsigned char *buffer,
+                             size_t len)
 {
     unsigned int i;
 
     for (i = 0; i < len; i++) {
-        crc = (crc << 8) ^ t10_dif_crc_table[((crc >> 8) ^ buffer[i]) & 0xff];
+        crc = (crc << 8) ^ crc16_t10dif_table[((crc >> 8) ^ buffer[i]) & 0xff];
     }
 
     return crc;
 }
 
+/* from Linux kernel (lib/crc64.c) */
+static uint64_t crc64_nvme(uint64_t crc, const unsigned char *buffer,
+                           size_t len)
+{
+    size_t i;
+
+    for (i = 0; i < len; i++) {
+        crc = (crc >> 8) ^ crc64_nvme_table[(crc & 0xff) ^ buffer[i]];
+    }
+
+    return crc ^ (uint64_t)~0;
+}
+
+static void 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)
+{
+    uint8_t *end = buf + len;
+    int16_t pil = 0;
+
+    if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
+        pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
+    }
+
+    trace_pci_nvme_dif_pract_generate_dif_crc16(len, ns->lbasz,
+                                                ns->lbasz + pil, apptag,
+                                                *reftag);
+
+    for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) {
+        NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
+        uint16_t crc = crc16_t10dif(0x0, buf, ns->lbasz);
+
+        if (pil) {
+            crc = crc16_t10dif(crc, mbuf, pil);
+        }
+
+        dif->g16.guard = cpu_to_be16(crc);
+        dif->g16.apptag = cpu_to_be16(apptag);
+        dif->g16.reftag = cpu_to_be32(*reftag);
+
+        if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
+            (*reftag)++;
+        }
+    }
+}
+
+static void 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)
+{
+    uint8_t *end = buf + len;
+    int16_t pil = 0;
+
+    if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
+        pil = ns->lbaf.ms - 16;
+    }
+
+    trace_pci_nvme_dif_pract_generate_dif_crc64(len, ns->lbasz,
+                                                ns->lbasz + pil, apptag,
+                                                *reftag);
+
+    for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) {
+        NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
+        uint64_t crc = crc64_nvme(~0ULL, buf, ns->lbasz);
+
+        if (pil) {
+            crc = crc64_nvme(crc, mbuf, pil);
+        }
+
+        dif->g64.guard = cpu_to_be64(crc);
+        dif->g64.apptag = cpu_to_be16(apptag);
+
+        dif->g64.sr[0] = *reftag >> 40;
+        dif->g64.sr[1] = *reftag >> 32;
+        dif->g64.sr[2] = *reftag >> 24;
+        dif->g64.sr[3] = *reftag >> 16;
+        dif->g64.sr[4] = *reftag >> 8;
+        dif->g64.sr[5] = *reftag;
+
+        if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
+            (*reftag)++;
+        }
+    }
+}
+
 void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len,
                                  uint8_t *mbuf, size_t mlen, uint16_t apptag,
-                                 uint32_t *reftag)
+                                 uint64_t *reftag)
 {
-    uint8_t *end = buf + len;
-    int16_t pil = 0;
+    switch (ns->pif) {
+    case NVME_PI_GUARD_16:
+        return nvme_dif_pract_generate_dif_crc16(ns, buf, len, mbuf, mlen,
+                                                 apptag, reftag);
+    case NVME_PI_GUARD_64:
+        return nvme_dif_pract_generate_dif_crc64(ns, buf, len, mbuf, mlen,
+                                                 apptag, reftag);
+    }
+
+    abort();
+}
+
+static uint16_t 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)
+{
+    switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
+    case NVME_ID_NS_DPS_TYPE_3:
+        if (be32_to_cpu(dif->g16.reftag) != 0xffffffff) {
+            break;
+        }
+
+        /* fallthrough */
+    case NVME_ID_NS_DPS_TYPE_1:
+    case NVME_ID_NS_DPS_TYPE_2:
+        if (be16_to_cpu(dif->g16.apptag) != 0xffff) {
+            break;
+        }
+
+        trace_pci_nvme_dif_prchk_disabled_crc16(be16_to_cpu(dif->g16.apptag),
+                                                be32_to_cpu(dif->g16.reftag));
+
+        return NVME_SUCCESS;
+    }
+
+    if (prinfo & NVME_PRINFO_PRCHK_GUARD) {
+        uint16_t crc = crc16_t10dif(0x0, buf, ns->lbasz);
 
-    if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
-        pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
+        if (pil) {
+            crc = crc16_t10dif(crc, mbuf, pil);
+        }
+
+        trace_pci_nvme_dif_prchk_guard_crc16(be16_to_cpu(dif->g16.guard), crc);
+
+        if (be16_to_cpu(dif->g16.guard) != crc) {
+            return NVME_E2E_GUARD_ERROR;
+        }
     }
 
-    trace_pci_nvme_dif_pract_generate_dif(len, ns->lbasz, ns->lbasz + pil,
-                                          apptag, *reftag);
+    if (prinfo & NVME_PRINFO_PRCHK_APP) {
+        trace_pci_nvme_dif_prchk_apptag(be16_to_cpu(dif->g16.apptag), apptag,
+                                        appmask);
 
-    for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) {
-        NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
-        uint16_t crc = crc_t10dif(0x0, buf, ns->lbasz);
+        if ((be16_to_cpu(dif->g16.apptag) & appmask) != (apptag & appmask)) {
+            return NVME_E2E_APP_ERROR;
+        }
+    }
+
+    if (prinfo & NVME_PRINFO_PRCHK_REF) {
+        trace_pci_nvme_dif_prchk_reftag_crc16(be32_to_cpu(dif->g16.reftag),
+                                              reftag);
+
+        if (be32_to_cpu(dif->g16.reftag) != reftag) {
+            return NVME_E2E_REF_ERROR;
+        }
+    }
+
+    return NVME_SUCCESS;
+}
+
+static uint16_t 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)
+{
+    uint64_t r = 0;
+
+    r |= (uint64_t)dif->g64.sr[0] << 40;
+    r |= (uint64_t)dif->g64.sr[1] << 32;
+    r |= (uint64_t)dif->g64.sr[2] << 24;
+    r |= (uint64_t)dif->g64.sr[3] << 16;
+    r |= (uint64_t)dif->g64.sr[4] << 8;
+    r |= (uint64_t)dif->g64.sr[5];
+
+    switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
+    case NVME_ID_NS_DPS_TYPE_3:
+        if (r != 0xffffffffffff) {
+            break;
+        }
+
+        /* fallthrough */
+    case NVME_ID_NS_DPS_TYPE_1:
+    case NVME_ID_NS_DPS_TYPE_2:
+        if (be16_to_cpu(dif->g64.apptag) != 0xffff) {
+            break;
+        }
+
+        trace_pci_nvme_dif_prchk_disabled_crc64(be16_to_cpu(dif->g16.apptag),
+                                                r);
+
+        return NVME_SUCCESS;
+    }
+
+    if (prinfo & NVME_PRINFO_PRCHK_GUARD) {
+        uint64_t crc = crc64_nvme(~0ULL, buf, ns->lbasz);
 
         if (pil) {
-            crc = crc_t10dif(crc, mbuf, pil);
+            crc = crc64_nvme(crc, mbuf, pil);
+        }
+
+        trace_pci_nvme_dif_prchk_guard_crc64(be64_to_cpu(dif->g64.guard), crc);
+
+        if (be64_to_cpu(dif->g64.guard) != crc) {
+            return NVME_E2E_GUARD_ERROR;
+        }
+    }
+
+    if (prinfo & NVME_PRINFO_PRCHK_APP) {
+        trace_pci_nvme_dif_prchk_apptag(be16_to_cpu(dif->g64.apptag), apptag,
+                                        appmask);
+
+        if ((be16_to_cpu(dif->g64.apptag) & appmask) != (apptag & appmask)) {
+            return NVME_E2E_APP_ERROR;
         }
+    }
 
-        dif->guard = cpu_to_be16(crc);
-        dif->apptag = cpu_to_be16(apptag);
-        dif->reftag = cpu_to_be32(*reftag);
+    if (prinfo & NVME_PRINFO_PRCHK_REF) {
+        trace_pci_nvme_dif_prchk_reftag_crc64(r, reftag);
 
-        if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
-            (*reftag)++;
+        if (r != reftag) {
+            return NVME_E2E_REF_ERROR;
         }
     }
+
+    return NVME_SUCCESS;
 }
 
 static uint16_t 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, uint32_t reftag)
+                               uint16_t appmask, uint64_t reftag)
 {
-    switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
-    case NVME_ID_NS_DPS_TYPE_3:
-        if (be32_to_cpu(dif->reftag) != 0xffffffff) {
-            break;
-        }
-
-        /* fallthrough */
-    case NVME_ID_NS_DPS_TYPE_1:
-    case NVME_ID_NS_DPS_TYPE_2:
-        if (be16_to_cpu(dif->apptag) != 0xffff) {
-            break;
-        }
-
-        trace_pci_nvme_dif_prchk_disabled(be16_to_cpu(dif->apptag),
-                                          be32_to_cpu(dif->reftag));
-
-        return NVME_SUCCESS;
-    }
-
-    if (prinfo & NVME_PRINFO_PRCHK_GUARD) {
-        uint16_t crc = crc_t10dif(0x0, buf, ns->lbasz);
-
-        if (pil) {
-            crc = crc_t10dif(crc, mbuf, pil);
-        }
-
-        trace_pci_nvme_dif_prchk_guard(be16_to_cpu(dif->guard), crc);
-
-        if (be16_to_cpu(dif->guard) != crc) {
-            return NVME_E2E_GUARD_ERROR;
-        }
-    }
-
-    if (prinfo & NVME_PRINFO_PRCHK_APP) {
-        trace_pci_nvme_dif_prchk_apptag(be16_to_cpu(dif->apptag), apptag,
-                                        appmask);
-
-        if ((be16_to_cpu(dif->apptag) & appmask) != (apptag & appmask)) {
-            return NVME_E2E_APP_ERROR;
-        }
-    }
-
-    if (prinfo & NVME_PRINFO_PRCHK_REF) {
-        trace_pci_nvme_dif_prchk_reftag(be32_to_cpu(dif->reftag), reftag);
-
-        if (be32_to_cpu(dif->reftag) != reftag) {
-            return NVME_E2E_REF_ERROR;
-        }
+    switch (ns->pif) {
+    case NVME_PI_GUARD_16:
+        return nvme_dif_prchk_crc16(ns, dif, buf, mbuf, pil, prinfo, apptag,
+                                    appmask, reftag);
+    case NVME_PI_GUARD_64:
+        return nvme_dif_prchk_crc64(ns, dif, buf, mbuf, pil, prinfo, apptag,
+                                    appmask, reftag);
     }
 
-    return NVME_SUCCESS;
+    abort();
 }
 
 uint16_t 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, uint32_t *reftag)
+                        uint16_t appmask, uint64_t *reftag)
 {
-    uint8_t *end = buf + len;
+    uint8_t *bufp, *end = buf + len;
     int16_t pil = 0;
     uint16_t status;
 
@@ -150,13 +308,29 @@ uint16_t nvme_dif_check(NvmeNamespace *ns, uint8_t *buf, size_t len,
 
     trace_pci_nvme_dif_check(prinfo, ns->lbasz + pil);
 
-    for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) {
+    for (bufp = buf; bufp < end; bufp += ns->lbasz, mbuf += ns->lbaf.ms) {
         NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
-
-        status = nvme_dif_prchk(ns, dif, buf, mbuf, pil, prinfo, apptag,
+        status = nvme_dif_prchk(ns, dif, bufp, mbuf, pil, prinfo, apptag,
                                 appmask, *reftag);
         if (status) {
-            return status;
+            /*
+             * The first block of a 'raw' image is always allocated, so we
+             * cannot reliably know if the block is all zeroes or not. For
+             * CRC16 this works fine because the T10 CRC16 is 0x0 for all
+             * zeroes, but the Rocksoft CRC64 is not. Thus, if a guard error is
+             * detected for the first block, check if it is zeroed and manually
+             * set the protection information to all ones to disable protection
+             * information checking.
+             */
+            if (status == NVME_E2E_GUARD_ERROR && slba == 0x0 && bufp == buf) {
+                g_autofree uint8_t *zeroes = g_malloc0(ns->lbasz);
+
+                if (memcmp(bufp, zeroes, ns->lbasz) == 0) {
+                    memset(mbuf + pil, 0xff, nvme_pi_tuple_size(ns));
+                }
+            } else {
+                return status;
+            }
         }
 
         if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
@@ -252,9 +426,12 @@ static void nvme_dif_rw_check_cb(void *opaque, int ret)
     uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
     uint16_t apptag = le16_to_cpu(rw->apptag);
     uint16_t appmask = le16_to_cpu(rw->appmask);
-    uint32_t reftag = le32_to_cpu(rw->reftag);
+    uint64_t reftag = le32_to_cpu(rw->reftag);
+    uint64_t cdw3 = le32_to_cpu(rw->cdw3);
     uint16_t status;
 
+    reftag |= cdw3 << 32;
+
     trace_pci_nvme_dif_rw_check_cb(nvme_cid(req), prinfo, apptag, appmask,
                                    reftag);
 
@@ -368,11 +545,14 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
     uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
     uint16_t apptag = le16_to_cpu(rw->apptag);
     uint16_t appmask = le16_to_cpu(rw->appmask);
-    uint32_t reftag = le32_to_cpu(rw->reftag);
+    uint64_t reftag = le32_to_cpu(rw->reftag);
+    uint64_t cdw3 = le32_to_cpu(rw->cdw3);
     bool pract = !!(prinfo & NVME_PRINFO_PRACT);
     NvmeBounceContext *ctx;
     uint16_t status;
 
+    reftag |= cdw3 << 32;
+
     trace_pci_nvme_dif_rw(pract, prinfo);
 
     ctx = g_new0(NvmeBounceContext, 1);
@@ -412,8 +592,29 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
             for (; mbuf < end; mbuf += ns->lbaf.ms) {
                 NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
 
-                dif->apptag = cpu_to_be16(apptag);
-                dif->reftag = cpu_to_be32(reftag);
+                switch (ns->pif) {
+                case NVME_PI_GUARD_16:
+                    dif->g16.apptag = cpu_to_be16(apptag);
+                    dif->g16.reftag = cpu_to_be32(reftag);
+
+                    break;
+
+                case NVME_PI_GUARD_64:
+                    dif->g64.guard = cpu_to_be64(0x6482d367eb22b64e);
+                    dif->g64.apptag = cpu_to_be16(apptag);
+
+                    dif->g64.sr[0] = reftag >> 40;
+                    dif->g64.sr[1] = reftag >> 32;
+                    dif->g64.sr[2] = reftag >> 24;
+                    dif->g64.sr[3] = reftag >> 16;
+                    dif->g64.sr[4] = reftag >> 8;
+                    dif->g64.sr[5] = reftag;
+
+                    break;
+
+                default:
+                    abort();
+                }
 
                 switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
                 case NVME_ID_NS_DPS_TYPE_1:
diff --git a/hw/nvme/dif.h b/hw/nvme/dif.h
index ab6dbb09463e..f12e312250bb 100644
--- a/hw/nvme/dif.h
+++ b/hw/nvme/dif.h
@@ -2,7 +2,7 @@
 #define HW_NVME_DIF_H
 
 /* from Linux kernel (crypto/crct10dif_common.c) */
-static const uint16_t t10_dif_crc_table[256] = {
+static const uint16_t crc16_t10dif_table[256] = {
     0x0000, 0x8BB7, 0x9CD9, 0x176E, 0xB205, 0x39B2, 0x2EDC, 0xA56B,
     0xEFBD, 0x640A, 0x7364, 0xF8D3, 0x5DB8, 0xD60F, 0xC161, 0x4AD6,
     0x54CD, 0xDF7A, 0xC814, 0x43A3, 0xE6C8, 0x6D7F, 0x7A11, 0xF1A6,
@@ -37,22 +37,155 @@ static const uint16_t t10_dif_crc_table[256] = {
     0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3
 };
 
+#define CRC64_NVME_POLY 0x9A6C9329AC4BC9B5ULL
+
+static const uint64_t crc64_nvme_table[] = {
+    0x0000000000000000ULL, 0x7F6EF0C830358979ULL,
+    0xFEDDE190606B12F2ULL, 0x81B31158505E9B8BULL,
+    0xC962E5739841B68FULL, 0xB60C15BBA8743FF6ULL,
+    0x37BF04E3F82AA47DULL, 0x48D1F42BC81F2D04ULL,
+    0xA61CECB46814FE75ULL, 0xD9721C7C5821770CULL,
+    0x58C10D24087FEC87ULL, 0x27AFFDEC384A65FEULL,
+    0x6F7E09C7F05548FAULL, 0x1010F90FC060C183ULL,
+    0x91A3E857903E5A08ULL, 0xEECD189FA00BD371ULL,
+    0x78E0FF3B88BE6F81ULL, 0x078E0FF3B88BE6F8ULL,
+    0x863D1EABE8D57D73ULL, 0xF953EE63D8E0F40AULL,
+    0xB1821A4810FFD90EULL, 0xCEECEA8020CA5077ULL,
+    0x4F5FFBD87094CBFCULL, 0x30310B1040A14285ULL,
+    0xDEFC138FE0AA91F4ULL, 0xA192E347D09F188DULL,
+    0x2021F21F80C18306ULL, 0x5F4F02D7B0F40A7FULL,
+    0x179EF6FC78EB277BULL, 0x68F0063448DEAE02ULL,
+    0xE943176C18803589ULL, 0x962DE7A428B5BCF0ULL,
+    0xF1C1FE77117CDF02ULL, 0x8EAF0EBF2149567BULL,
+    0x0F1C1FE77117CDF0ULL, 0x7072EF2F41224489ULL,
+    0x38A31B04893D698DULL, 0x47CDEBCCB908E0F4ULL,
+    0xC67EFA94E9567B7FULL, 0xB9100A5CD963F206ULL,
+    0x57DD12C379682177ULL, 0x28B3E20B495DA80EULL,
+    0xA900F35319033385ULL, 0xD66E039B2936BAFCULL,
+    0x9EBFF7B0E12997F8ULL, 0xE1D10778D11C1E81ULL,
+    0x606216208142850AULL, 0x1F0CE6E8B1770C73ULL,
+    0x8921014C99C2B083ULL, 0xF64FF184A9F739FAULL,
+    0x77FCE0DCF9A9A271ULL, 0x08921014C99C2B08ULL,
+    0x4043E43F0183060CULL, 0x3F2D14F731B68F75ULL,
+    0xBE9E05AF61E814FEULL, 0xC1F0F56751DD9D87ULL,
+    0x2F3DEDF8F1D64EF6ULL, 0x50531D30C1E3C78FULL,
+    0xD1E00C6891BD5C04ULL, 0xAE8EFCA0A188D57DULL,
+    0xE65F088B6997F879ULL, 0x9931F84359A27100ULL,
+    0x1882E91B09FCEA8BULL, 0x67EC19D339C963F2ULL,
+    0xD75ADABD7A6E2D6FULL, 0xA8342A754A5BA416ULL,
+    0x29873B2D1A053F9DULL, 0x56E9CBE52A30B6E4ULL,
+    0x1E383FCEE22F9BE0ULL, 0x6156CF06D21A1299ULL,
+    0xE0E5DE5E82448912ULL, 0x9F8B2E96B271006BULL,
+    0x71463609127AD31AULL, 0x0E28C6C1224F5A63ULL,
+    0x8F9BD7997211C1E8ULL, 0xF0F5275142244891ULL,
+    0xB824D37A8A3B6595ULL, 0xC74A23B2BA0EECECULL,
+    0x46F932EAEA507767ULL, 0x3997C222DA65FE1EULL,
+    0xAFBA2586F2D042EEULL, 0xD0D4D54EC2E5CB97ULL,
+    0x5167C41692BB501CULL, 0x2E0934DEA28ED965ULL,
+    0x66D8C0F56A91F461ULL, 0x19B6303D5AA47D18ULL,
+    0x980521650AFAE693ULL, 0xE76BD1AD3ACF6FEAULL,
+    0x09A6C9329AC4BC9BULL, 0x76C839FAAAF135E2ULL,
+    0xF77B28A2FAAFAE69ULL, 0x8815D86ACA9A2710ULL,
+    0xC0C42C4102850A14ULL, 0xBFAADC8932B0836DULL,
+    0x3E19CDD162EE18E6ULL, 0x41773D1952DB919FULL,
+    0x269B24CA6B12F26DULL, 0x59F5D4025B277B14ULL,
+    0xD846C55A0B79E09FULL, 0xA72835923B4C69E6ULL,
+    0xEFF9C1B9F35344E2ULL, 0x90973171C366CD9BULL,
+    0x1124202993385610ULL, 0x6E4AD0E1A30DDF69ULL,
+    0x8087C87E03060C18ULL, 0xFFE938B633338561ULL,
+    0x7E5A29EE636D1EEAULL, 0x0134D92653589793ULL,
+    0x49E52D0D9B47BA97ULL, 0x368BDDC5AB7233EEULL,
+    0xB738CC9DFB2CA865ULL, 0xC8563C55CB19211CULL,
+    0x5E7BDBF1E3AC9DECULL, 0x21152B39D3991495ULL,
+    0xA0A63A6183C78F1EULL, 0xDFC8CAA9B3F20667ULL,
+    0x97193E827BED2B63ULL, 0xE877CE4A4BD8A21AULL,
+    0x69C4DF121B863991ULL, 0x16AA2FDA2BB3B0E8ULL,
+    0xF86737458BB86399ULL, 0x8709C78DBB8DEAE0ULL,
+    0x06BAD6D5EBD3716BULL, 0x79D4261DDBE6F812ULL,
+    0x3105D23613F9D516ULL, 0x4E6B22FE23CC5C6FULL,
+    0xCFD833A67392C7E4ULL, 0xB0B6C36E43A74E9DULL,
+    0x9A6C9329AC4BC9B5ULL, 0xE50263E19C7E40CCULL,
+    0x64B172B9CC20DB47ULL, 0x1BDF8271FC15523EULL,
+    0x530E765A340A7F3AULL, 0x2C608692043FF643ULL,
+    0xADD397CA54616DC8ULL, 0xD2BD67026454E4B1ULL,
+    0x3C707F9DC45F37C0ULL, 0x431E8F55F46ABEB9ULL,
+    0xC2AD9E0DA4342532ULL, 0xBDC36EC59401AC4BULL,
+    0xF5129AEE5C1E814FULL, 0x8A7C6A266C2B0836ULL,
+    0x0BCF7B7E3C7593BDULL, 0x74A18BB60C401AC4ULL,
+    0xE28C6C1224F5A634ULL, 0x9DE29CDA14C02F4DULL,
+    0x1C518D82449EB4C6ULL, 0x633F7D4A74AB3DBFULL,
+    0x2BEE8961BCB410BBULL, 0x548079A98C8199C2ULL,
+    0xD53368F1DCDF0249ULL, 0xAA5D9839ECEA8B30ULL,
+    0x449080A64CE15841ULL, 0x3BFE706E7CD4D138ULL,
+    0xBA4D61362C8A4AB3ULL, 0xC52391FE1CBFC3CAULL,
+    0x8DF265D5D4A0EECEULL, 0xF29C951DE49567B7ULL,
+    0x732F8445B4CBFC3CULL, 0x0C41748D84FE7545ULL,
+    0x6BAD6D5EBD3716B7ULL, 0x14C39D968D029FCEULL,
+    0x95708CCEDD5C0445ULL, 0xEA1E7C06ED698D3CULL,
+    0xA2CF882D2576A038ULL, 0xDDA178E515432941ULL,
+    0x5C1269BD451DB2CAULL, 0x237C997575283BB3ULL,
+    0xCDB181EAD523E8C2ULL, 0xB2DF7122E51661BBULL,
+    0x336C607AB548FA30ULL, 0x4C0290B2857D7349ULL,
+    0x04D364994D625E4DULL, 0x7BBD94517D57D734ULL,
+    0xFA0E85092D094CBFULL, 0x856075C11D3CC5C6ULL,
+    0x134D926535897936ULL, 0x6C2362AD05BCF04FULL,
+    0xED9073F555E26BC4ULL, 0x92FE833D65D7E2BDULL,
+    0xDA2F7716ADC8CFB9ULL, 0xA54187DE9DFD46C0ULL,
+    0x24F29686CDA3DD4BULL, 0x5B9C664EFD965432ULL,
+    0xB5517ED15D9D8743ULL, 0xCA3F8E196DA80E3AULL,
+    0x4B8C9F413DF695B1ULL, 0x34E26F890DC31CC8ULL,
+    0x7C339BA2C5DC31CCULL, 0x035D6B6AF5E9B8B5ULL,
+    0x82EE7A32A5B7233EULL, 0xFD808AFA9582AA47ULL,
+    0x4D364994D625E4DAULL, 0x3258B95CE6106DA3ULL,
+    0xB3EBA804B64EF628ULL, 0xCC8558CC867B7F51ULL,
+    0x8454ACE74E645255ULL, 0xFB3A5C2F7E51DB2CULL,
+    0x7A894D772E0F40A7ULL, 0x05E7BDBF1E3AC9DEULL,
+    0xEB2AA520BE311AAFULL, 0x944455E88E0493D6ULL,
+    0x15F744B0DE5A085DULL, 0x6A99B478EE6F8124ULL,
+    0x224840532670AC20ULL, 0x5D26B09B16452559ULL,
+    0xDC95A1C3461BBED2ULL, 0xA3FB510B762E37ABULL,
+    0x35D6B6AF5E9B8B5BULL, 0x4AB846676EAE0222ULL,
+    0xCB0B573F3EF099A9ULL, 0xB465A7F70EC510D0ULL,
+    0xFCB453DCC6DA3DD4ULL, 0x83DAA314F6EFB4ADULL,
+    0x0269B24CA6B12F26ULL, 0x7D0742849684A65FULL,
+    0x93CA5A1B368F752EULL, 0xECA4AAD306BAFC57ULL,
+    0x6D17BB8B56E467DCULL, 0x12794B4366D1EEA5ULL,
+    0x5AA8BF68AECEC3A1ULL, 0x25C64FA09EFB4AD8ULL,
+    0xA4755EF8CEA5D153ULL, 0xDB1BAE30FE90582AULL,
+    0xBCF7B7E3C7593BD8ULL, 0xC399472BF76CB2A1ULL,
+    0x422A5673A732292AULL, 0x3D44A6BB9707A053ULL,
+    0x759552905F188D57ULL, 0x0AFBA2586F2D042EULL,
+    0x8B48B3003F739FA5ULL, 0xF42643C80F4616DCULL,
+    0x1AEB5B57AF4DC5ADULL, 0x6585AB9F9F784CD4ULL,
+    0xE436BAC7CF26D75FULL, 0x9B584A0FFF135E26ULL,
+    0xD389BE24370C7322ULL, 0xACE74EEC0739FA5BULL,
+    0x2D545FB4576761D0ULL, 0x523AAF7C6752E8A9ULL,
+    0xC41748D84FE75459ULL, 0xBB79B8107FD2DD20ULL,
+    0x3ACAA9482F8C46ABULL, 0x45A459801FB9CFD2ULL,
+    0x0D75ADABD7A6E2D6ULL, 0x721B5D63E7936BAFULL,
+    0xF3A84C3BB7CDF024ULL, 0x8CC6BCF387F8795DULL,
+    0x620BA46C27F3AA2CULL, 0x1D6554A417C62355ULL,
+    0x9CD645FC4798B8DEULL, 0xE3B8B53477AD31A7ULL,
+    0xAB69411FBFB21CA3ULL, 0xD407B1D78F8795DAULL,
+    0x55B4A08FDFD90E51ULL, 0x2ADA5047EFEC8728ULL,
+};
+
 static inline size_t nvme_pi_tuple_size(NvmeNamespace *ns)
 {
-    return sizeof(NvmeDifTuple);
+    return ns->pif ? 16 : 8;
 }
 
 uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba,
-                           uint32_t reftag);
+                           uint64_t reftag);
 uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen,
                                uint64_t slba);
 void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len,
                                  uint8_t *mbuf, size_t mlen, uint16_t apptag,
-                                 uint32_t *reftag);
+                                 uint64_t *reftag);
 uint16_t 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, uint32_t *reftag);
+                        uint16_t appmask, uint64_t *reftag);
 uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req);
 
 #endif /* HW_NVME_DIF_H */
diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c
index 8dfb55130beb..8a3613d9ab0e 100644
--- a/hw/nvme/ns.c
+++ b/hw/nvme/ns.c
@@ -58,6 +58,7 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp)
 {
     static uint64_t ns_count;
     NvmeIdNs *id_ns = &ns->id_ns;
+    NvmeIdNsNvm *id_ns_nvm = &ns->id_ns_nvm;
     uint8_t ds;
     uint16_t ms;
     int i;
@@ -101,6 +102,8 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp)
         id_ns->dps |= NVME_ID_NS_DPS_FIRST_EIGHT;
     }
 
+    ns->pif = ns->params.pif;
+
     static const NvmeLBAF lbaf[16] = {
         [0] = { .ds =  9           },
         [1] = { .ds =  9, .ms =  8 },
@@ -133,7 +136,9 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp)
 
     id_ns->flbas |= i;
 
+
 lbaf_found:
+    id_ns_nvm->elbaf[i] = (ns->pif & 0x3) << 7;
     id_ns->nlbaf = ns->nlbaf - 1;
     nvme_ns_init_format(ns);
 
@@ -373,15 +378,36 @@ static void nvme_zoned_ns_shutdown(NvmeNamespace *ns)
 
 static int nvme_ns_check_constraints(NvmeNamespace *ns, Error **errp)
 {
+    unsigned int pi_size;
+
     if (!ns->blkconf.blk) {
         error_setg(errp, "block backend not configured");
         return -1;
     }
 
-    if (ns->params.pi && ns->params.ms < 8) {
-        error_setg(errp, "at least 8 bytes of metadata required to enable "
-                   "protection information");
-        return -1;
+    if (ns->params.pi) {
+        if (ns->params.pi > NVME_ID_NS_DPS_TYPE_3) {
+            error_setg(errp, "invalid 'pi' value");
+            return -1;
+        }
+
+        switch (ns->params.pif) {
+        case NVME_PI_GUARD_16:
+            pi_size = 8;
+            break;
+        case NVME_PI_GUARD_64:
+            pi_size = 16;
+            break;
+        default:
+            error_setg(errp, "invalid 'pif'");
+            return -1;
+        }
+
+        if (ns->params.ms < pi_size) {
+            error_setg(errp, "at least %u bytes of metadata required to "
+                       "enable protection information", pi_size);
+            return -1;
+        }
     }
 
     if (ns->params.nsid > NVME_MAX_NAMESPACES) {
@@ -593,6 +619,7 @@ static Property nvme_ns_props[] = {
     DEFINE_PROP_UINT8("mset", NvmeNamespace, params.mset, 0),
     DEFINE_PROP_UINT8("pi", NvmeNamespace, params.pi, 0),
     DEFINE_PROP_UINT8("pil", NvmeNamespace, params.pil, 0),
+    DEFINE_PROP_UINT8("pif", NvmeNamespace, params.pif, 0),
     DEFINE_PROP_UINT16("mssrl", NvmeNamespace, params.mssrl, 128),
     DEFINE_PROP_UINT32("mcl", NvmeNamespace, params.mcl, 128),
     DEFINE_PROP_UINT8("msrc", NvmeNamespace, params.msrc, 127),
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index e715c3255a29..739c8b8f7962 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -102,6 +102,7 @@ typedef struct NvmeNamespaceParams {
     uint8_t  mset;
     uint8_t  pi;
     uint8_t  pil;
+    uint8_t  pif;
 
     uint16_t mssrl;
     uint32_t mcl;
@@ -127,6 +128,7 @@ typedef struct NvmeNamespace {
     int64_t      size;
     int64_t      moff;
     NvmeIdNs     id_ns;
+    NvmeIdNsNvm  id_ns_nvm;
     NvmeLBAF     lbaf;
     unsigned int nlbaf;
     size_t       lbasz;
@@ -134,6 +136,7 @@ typedef struct NvmeNamespace {
     uint8_t      csi;
     uint16_t     status;
     int          attached;
+    uint8_t      pif;
 
     struct {
         uint16_t zrwas;
diff --git a/hw/nvme/trace-events b/hw/nvme/trace-events
index 90730d802fe3..ff1b4589692b 100644
--- a/hw/nvme/trace-events
+++ b/hw/nvme/trace-events
@@ -20,12 +20,16 @@ pci_nvme_dif_rw_cb(uint16_t cid, const char *blkname) "cid %"PRIu16" blk '%s'"
 pci_nvme_dif_rw_mdata_in_cb(uint16_t cid, const char *blkname) "cid %"PRIu16" blk '%s'"
 pci_nvme_dif_rw_mdata_out_cb(uint16_t cid, const char *blkname) "cid %"PRIu16" blk '%s'"
 pci_nvme_dif_rw_check_cb(uint16_t cid, uint8_t prinfo, uint16_t apptag, uint16_t appmask, uint32_t reftag) "cid %"PRIu16" prinfo 0x%"PRIx8" apptag 0x%"PRIx16" appmask 0x%"PRIx16" reftag 0x%"PRIx32""
-pci_nvme_dif_pract_generate_dif(size_t len, size_t lba_size, size_t chksum_len, uint16_t apptag, uint32_t reftag) "len %zu lba_size %zu chksum_len %zu apptag 0x%"PRIx16" reftag 0x%"PRIx32""
+pci_nvme_dif_pract_generate_dif_crc16(size_t len, size_t lba_size, size_t chksum_len, uint16_t apptag, uint32_t reftag) "len %zu lba_size %zu chksum_len %zu apptag 0x%"PRIx16" reftag 0x%"PRIx32""
+pci_nvme_dif_pract_generate_dif_crc64(size_t len, size_t lba_size, size_t chksum_len, uint16_t apptag, uint64_t reftag) "len %zu lba_size %zu chksum_len %zu apptag 0x%"PRIx16" reftag 0x%"PRIx64""
 pci_nvme_dif_check(uint8_t prinfo, uint16_t chksum_len) "prinfo 0x%"PRIx8" chksum_len %"PRIu16""
-pci_nvme_dif_prchk_disabled(uint16_t apptag, uint32_t reftag) "apptag 0x%"PRIx16" reftag 0x%"PRIx32""
-pci_nvme_dif_prchk_guard(uint16_t guard, uint16_t crc) "guard 0x%"PRIx16" crc 0x%"PRIx16""
+pci_nvme_dif_prchk_disabled_crc16(uint16_t apptag, uint32_t reftag) "apptag 0x%"PRIx16" reftag 0x%"PRIx32""
+pci_nvme_dif_prchk_disabled_crc64(uint16_t apptag, uint64_t reftag) "apptag 0x%"PRIx16" reftag 0x%"PRIx64""
+pci_nvme_dif_prchk_guard_crc16(uint16_t guard, uint16_t crc) "guard 0x%"PRIx16" crc 0x%"PRIx16""
+pci_nvme_dif_prchk_guard_crc64(uint64_t guard, uint64_t crc) "guard 0x%"PRIx64" crc 0x%"PRIx64""
 pci_nvme_dif_prchk_apptag(uint16_t apptag, uint16_t elbat, uint16_t elbatm) "apptag 0x%"PRIx16" elbat 0x%"PRIx16" elbatm 0x%"PRIx16""
-pci_nvme_dif_prchk_reftag(uint32_t reftag, uint32_t elbrt) "reftag 0x%"PRIx32" elbrt 0x%"PRIx32""
+pci_nvme_dif_prchk_reftag_crc16(uint32_t reftag, uint32_t elbrt) "reftag 0x%"PRIx32" elbrt 0x%"PRIx32""
+pci_nvme_dif_prchk_reftag_crc64(uint64_t reftag, uint64_t elbrt) "reftag 0x%"PRIx64" elbrt 0x%"PRIx64""
 pci_nvme_copy(uint16_t cid, uint32_t nsid, uint16_t nr, uint8_t format) "cid %"PRIu16" nsid %"PRIu32" nr %"PRIu16" format 0x%"PRIx8""
 pci_nvme_copy_source_range(uint64_t slba, uint32_t nlb) "slba 0x%"PRIx64" nlb %"PRIu32""
 pci_nvme_copy_out(uint64_t slba, uint32_t nlb) "slba 0x%"PRIx64" nlb %"PRIu32""
diff --git a/include/block/nvme.h b/include/block/nvme.h
index 37afc9be9b18..3737351cc815 100644
--- a/include/block/nvme.h
+++ b/include/block/nvme.h
@@ -695,7 +695,8 @@ typedef struct QEMU_PACKED NvmeRwCmd {
     uint8_t     flags;
     uint16_t    cid;
     uint32_t    nsid;
-    uint64_t    rsvd2;
+    uint32_t    cdw2;
+    uint32_t    cdw3;
     uint64_t    mptr;
     NvmeCmdDptr dptr;
     uint64_t    slba;
@@ -731,7 +732,6 @@ enum {
     NVME_RW_PRINFO_PRCHK_APP    = 1 << 11,
     NVME_RW_PRINFO_PRCHK_REF    = 1 << 10,
     NVME_RW_PRINFO_PRCHK_MASK   = 7 << 10,
-
 };
 
 #define NVME_RW_PRINFO(control) ((control >> 10) & 0xf)
@@ -770,6 +770,7 @@ typedef struct QEMU_PACKED NvmeDsmRange {
 
 enum {
     NVME_COPY_FORMAT_0 = 0x0,
+    NVME_COPY_FORMAT_1 = 0x1,
 };
 
 typedef struct QEMU_PACKED NvmeCopyCmd {
@@ -777,7 +778,9 @@ typedef struct QEMU_PACKED NvmeCopyCmd {
     uint8_t     flags;
     uint16_t    cid;
     uint32_t    nsid;
-    uint32_t    rsvd2[4];
+    uint32_t    cdw2;
+    uint32_t    cdw3;
+    uint32_t    rsvd2[2];
     NvmeCmdDptr dptr;
     uint64_t    sdlba;
     uint8_t     nr;
@@ -789,7 +792,7 @@ typedef struct QEMU_PACKED NvmeCopyCmd {
     uint16_t    appmask;
 } NvmeCopyCmd;
 
-typedef struct QEMU_PACKED NvmeCopySourceRange {
+typedef struct QEMU_PACKED NvmeCopySourceRangeFormat0 {
     uint8_t  rsvd0[8];
     uint64_t slba;
     uint16_t nlb;
@@ -797,7 +800,17 @@ typedef struct QEMU_PACKED NvmeCopySourceRange {
     uint32_t reftag;
     uint16_t apptag;
     uint16_t appmask;
-} NvmeCopySourceRange;
+} NvmeCopySourceRangeFormat0;
+
+typedef struct QEMU_PACKED NvmeCopySourceRangeFormat1 {
+    uint8_t  rsvd0[8];
+    uint64_t slba;
+    uint16_t nlb;
+    uint8_t  rsvd18[8];
+    uint8_t  sr[10];
+    uint16_t apptag;
+    uint16_t appmask;
+} NvmeCopySourceRangeFormat1;
 
 enum NvmeAsyncEventRequest {
     NVME_AER_TYPE_ERROR                     = 0,
@@ -908,6 +921,7 @@ enum NvmeStatusCodes {
     NVME_CMP_FAILURE            = 0x0285,
     NVME_ACCESS_DENIED          = 0x0286,
     NVME_DULB                   = 0x0287,
+    NVME_E2E_STORAGE_TAG_ERROR  = 0x0288,
     NVME_MORE                   = 0x2000,
     NVME_DNR                    = 0x4000,
     NVME_NO_COMPLETE            = 0xffff,
@@ -1135,7 +1149,8 @@ enum NvmeIdCtrlOncs {
 };
 
 enum NvmeIdCtrlOcfs {
-    NVME_OCFS_COPY_FORMAT_0 = 1 << 0,
+    NVME_OCFS_COPY_FORMAT_0 = 1 << NVME_COPY_FORMAT_0,
+    NVME_OCFS_COPY_FORMAT_1 = 1 << NVME_COPY_FORMAT_1,
 };
 
 enum NvmeIdctrlVwc {
@@ -1282,6 +1297,7 @@ typedef struct QEMU_PACKED NvmeLBAFE {
 } NvmeLBAFE;
 
 #define NVME_NSID_BROADCAST 0xffffffff
+#define NVME_MAX_NLBAF 64
 
 typedef struct QEMU_PACKED NvmeIdNs {
     uint64_t    nsze;
@@ -1316,10 +1332,20 @@ typedef struct QEMU_PACKED NvmeIdNs {
     uint8_t     rsvd81[23];
     uint8_t     nguid[16];
     uint64_t    eui64;
-    NvmeLBAF    lbaf[64];
+    NvmeLBAF    lbaf[NVME_MAX_NLBAF];
     uint8_t     vs[3712];
 } NvmeIdNs;
 
+#define NVME_ID_NS_NVM_ELBAF_PIF(elbaf) (((elbaf) >> 7) & 0x3)
+
+typedef struct QEMU_PACKED NvmeIdNsNvm {
+    uint64_t    lbstm;
+    uint8_t     pic;
+    uint8_t     rsvd9[3];
+    uint32_t    elbaf[NVME_MAX_NLBAF];
+    uint8_t     rsvd268[3828];
+} NvmeIdNsNvm;
+
 typedef struct QEMU_PACKED NvmeIdNsDescr {
     uint8_t nidt;
     uint8_t nidl;
@@ -1421,10 +1447,23 @@ enum NvmeIdNsMc {
 
 #define NVME_ID_NS_DPS_TYPE(dps) (dps & NVME_ID_NS_DPS_TYPE_MASK)
 
-typedef struct NvmeDifTuple {
-    uint16_t guard;
-    uint16_t apptag;
-    uint32_t reftag;
+enum NvmePIFormat {
+    NVME_PI_GUARD_16                 = 0,
+    NVME_PI_GUARD_64                 = 2,
+};
+
+typedef union NvmeDifTuple {
+    struct {
+        uint16_t guard;
+        uint16_t apptag;
+        uint32_t reftag;
+    } g16;
+
+    struct {
+        uint64_t guard;
+        uint16_t apptag;
+        uint8_t  sr[6];
+    } g64;
 } NvmeDifTuple;
 
 enum NvmeZoneAttr {
@@ -1521,7 +1560,8 @@ static inline void _nvme_check_size(void)
     QEMU_BUILD_BUG_ON(sizeof(NvmeZonedResult) != 8);
     QEMU_BUILD_BUG_ON(sizeof(NvmeCqe) != 16);
     QEMU_BUILD_BUG_ON(sizeof(NvmeDsmRange) != 16);
-    QEMU_BUILD_BUG_ON(sizeof(NvmeCopySourceRange) != 32);
+    QEMU_BUILD_BUG_ON(sizeof(NvmeCopySourceRangeFormat0) != 32);
+    QEMU_BUILD_BUG_ON(sizeof(NvmeCopySourceRangeFormat1) != 40);
     QEMU_BUILD_BUG_ON(sizeof(NvmeCmd) != 64);
     QEMU_BUILD_BUG_ON(sizeof(NvmeDeleteQ) != 64);
     QEMU_BUILD_BUG_ON(sizeof(NvmeCreateCq) != 64);
@@ -1542,10 +1582,11 @@ static inline void _nvme_check_size(void)
     QEMU_BUILD_BUG_ON(sizeof(NvmeLBAF) != 4);
     QEMU_BUILD_BUG_ON(sizeof(NvmeLBAFE) != 16);
     QEMU_BUILD_BUG_ON(sizeof(NvmeIdNs) != 4096);
+    QEMU_BUILD_BUG_ON(sizeof(NvmeIdNsNvm) != 4096);
     QEMU_BUILD_BUG_ON(sizeof(NvmeIdNsZoned) != 4096);
     QEMU_BUILD_BUG_ON(sizeof(NvmeSglDescriptor) != 16);
     QEMU_BUILD_BUG_ON(sizeof(NvmeIdNsDescr) != 4);
     QEMU_BUILD_BUG_ON(sizeof(NvmeZoneDescr) != 64);
-    QEMU_BUILD_BUG_ON(sizeof(NvmeDifTuple) != 8);
+    QEMU_BUILD_BUG_ON(sizeof(NvmeDifTuple) != 16);
 }
 #endif
-- 
2.35.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 0/6] hw/nvme: enhanced protection information (64-bit guard)
  2022-03-01 10:44 [PATCH v2 0/6] hw/nvme: enhanced protection information (64-bit guard) Klaus Jensen
                   ` (5 preceding siblings ...)
  2022-03-01 10:44 ` [PATCH v2 6/6] hw/nvme: 64-bit pi support Klaus Jensen
@ 2022-03-01 14:54 ` Keith Busch
  2022-03-03 11:24   ` Klaus Jensen
  6 siblings, 1 reply; 9+ messages in thread
From: Keith Busch @ 2022-03-01 14:54 UTC (permalink / raw)
  To: Klaus Jensen
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Klaus Jensen, Naveen Nagar,
	qemu-devel, Philippe Mathieu-Daudé,
	Hanna Reitz, Stefan Hajnoczi

On Tue, Mar 01, 2022 at 11:44:22AM +0100, Klaus Jensen wrote:
> From: Klaus Jensen <k.jensen@samsung.com>
> 
> This adds support for one possible new protection information format
> introduced in TP4068 (and integrated in NVMe 2.0): the 64-bit CRC guard
> and 48-bit reference tag. This version does not support storage tags.
> 
> Like the CRC16 support already present, this uses a software
> implementation of CRC64 (so it is naturally pretty slow). But its good
> enough for verification purposes.
> 
> This goes hand-in-hand with the support that Keith submitted for the
> Linux kernel[1].
> 
>   [1]: https://lore.kernel.org/linux-nvme/20220201190128.3075065-1-kbusch@kernel.org/

Thanks Klaus, this looks good to me.

Reviewed-by: Keith Busch <kbusch@kernel.org>


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 0/6] hw/nvme: enhanced protection information (64-bit guard)
  2022-03-01 14:54 ` [PATCH v2 0/6] hw/nvme: enhanced protection information (64-bit guard) Keith Busch
@ 2022-03-03 11:24   ` Klaus Jensen
  0 siblings, 0 replies; 9+ messages in thread
From: Klaus Jensen @ 2022-03-03 11:24 UTC (permalink / raw)
  To: Keith Busch
  Cc: Kevin Wolf, Fam Zheng, qemu-block, Klaus Jensen, Naveen Nagar,
	qemu-devel, Philippe Mathieu-Daudé,
	Hanna Reitz, Stefan Hajnoczi

[-- Attachment #1: Type: text/plain, Size: 899 bytes --]

On Mar  1 06:54, Keith Busch wrote:
> On Tue, Mar 01, 2022 at 11:44:22AM +0100, Klaus Jensen wrote:
> > From: Klaus Jensen <k.jensen@samsung.com>
> > 
> > This adds support for one possible new protection information format
> > introduced in TP4068 (and integrated in NVMe 2.0): the 64-bit CRC guard
> > and 48-bit reference tag. This version does not support storage tags.
> > 
> > Like the CRC16 support already present, this uses a software
> > implementation of CRC64 (so it is naturally pretty slow). But its good
> > enough for verification purposes.
> > 
> > This goes hand-in-hand with the support that Keith submitted for the
> > Linux kernel[1].
> > 
> >   [1]: https://lore.kernel.org/linux-nvme/20220201190128.3075065-1-kbusch@kernel.org/
> 
> Thanks Klaus, this looks good to me.
> 
> Reviewed-by: Keith Busch <kbusch@kernel.org>

Thanks,

Applied to nvme-next!

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2022-03-03 11:29 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-01 10:44 [PATCH v2 0/6] hw/nvme: enhanced protection information (64-bit guard) Klaus Jensen
2022-03-01 10:44 ` [PATCH v2 1/6] hw/nvme: move dif/pi prototypes into dif.h Klaus Jensen
2022-03-01 10:44 ` [PATCH v2 2/6] hw/nvme: add host behavior support feature Klaus Jensen
2022-03-01 10:44 ` [PATCH v2 3/6] hw/nvme: move format parameter parsing Klaus Jensen
2022-03-01 10:44 ` [PATCH v2 4/6] hw/nvme: add support for the lbafee hbs feature Klaus Jensen
2022-03-01 10:44 ` [PATCH v2 5/6] hw/nvme: add pi tuple size helper Klaus Jensen
2022-03-01 10:44 ` [PATCH v2 6/6] hw/nvme: 64-bit pi support Klaus Jensen
2022-03-01 14:54 ` [PATCH v2 0/6] hw/nvme: enhanced protection information (64-bit guard) Keith Busch
2022-03-03 11:24   ` Klaus Jensen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.