diff mbox series

[v3,3/3] hw/nvme: add nvme management interface model

Message ID 20230531114744.9946-4-its@irrelevant.dk (mailing list archive)
State New, archived
Headers show
Series hw/{i2c, nvme}: mctp endpoint, nvme management interface model | expand

Commit Message

Klaus Jensen May 31, 2023, 11:47 a.m. UTC
From: Klaus Jensen <k.jensen@samsung.com>

Add the 'nmi-i2c' device that emulates an NVMe Management Interface
controller.

Initial support is very basic (Read NMI DS, Configuration Get).

This is based on previously posted code by Padmakar Kalghatgi, Arun
Kumar Agasar and Saurav Kumar.

Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/meson.build  |   1 +
 hw/nvme/nmi-i2c.c    | 367 +++++++++++++++++++++++++++++++++++++++++++
 hw/nvme/trace-events |   6 +
 3 files changed, 374 insertions(+)
 create mode 100644 hw/nvme/nmi-i2c.c

Comments

Jonathan Cameron May 31, 2023, 3:39 p.m. UTC | #1
On Wed, 31 May 2023 13:47:44 +0200
Klaus Jensen <its@irrelevant.dk> wrote:

> From: Klaus Jensen <k.jensen@samsung.com>
> 
> Add the 'nmi-i2c' device that emulates an NVMe Management Interface
> controller.
> 
> Initial support is very basic (Read NMI DS, Configuration Get).
> 
> This is based on previously posted code by Padmakar Kalghatgi, Arun
> Kumar Agasar and Saurav Kumar.
> 
> Signed-off-by: Klaus Jensen <k.jensen@samsung.com>

A few comments inline - I dug into the specs this time so some
new questions.

Jonathan

> diff --git a/hw/nvme/nmi-i2c.c b/hw/nvme/nmi-i2c.c
> new file mode 100644
> index 000000000000..38e43e48fa51
> --- /dev/null
> +++ b/hw/nvme/nmi-i2c.c
> @@ -0,0 +1,367 @@
> +/*
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * SPDX-FileCopyrightText: Copyright (c) 2022 Samsung Electronics Co., Ltd.

Update to 2023?

> +typedef struct NMIRequest {
> +   uint8_t opc;
> +   uint8_t rsvd1[3];
> +   uint32_t dw0;
> +   uint32_t dw1;
> +   uint32_t mic;
> +} NMIRequest;
> +
> +typedef struct NMIResponse {
> +    uint8_t status;
> +    uint8_t response[3];
> +    uint8_t payload[]; /* includes the Message Integrity Check */
> +} NMIResponse;

Not used.

> +
> +typedef enum NMIReadDSType {
> +    NMI_CMD_READ_NMI_DS_SUBSYSTEM       = 0x0,
> +    NMI_CMD_READ_NMI_DS_PORTS           = 0x1,
> +    NMI_CMD_READ_NMI_DS_CTRL_LIST       = 0x2,
> +    NMI_CMD_READ_NMI_DS_CTRL_INFO       = 0x3,
> +    NMI_CMD_READ_NMI_DS_CMD_SUPPORT     = 0x4,
> +    NMI_CMD_READ_NMI_DS_MEB_CMD_SUPPORT = 0x5,
> +} NMIReadDSType;
> +
> +static void nmi_set_parameter_error(NMIDevice *nmi, uint8_t bit, uint16_t byte)
> +{
> +    nmi->scratch[nmi->pos++] = 0x4;
> +    nmi->scratch[nmi->pos++] = bit;

Mask bit to only 3 bits?

> +    nmi->scratch[nmi->pos++] = (byte >> 4) & 0xf;
> +    nmi->scratch[nmi->pos++] = byte & 0xf;

Not following how byte is split up.  Figure 29 in 1.1d suggests it should be
in bits 23:08 and this doesn't match that.  Perhaps a reference given I guess
I'm looking at wrong thing.

> +}

> +static void nmi_handle_mi_read_nmi_ds(NMIDevice *nmi, NMIRequest *request)
> +{
> +    I2CSlave *i2c = I2C_SLAVE(nmi);
> +
> +    uint32_t dw0 = le32_to_cpu(request->dw0);
> +    uint8_t dtyp = (dw0 >> 24) & 0xf;
> +    uint8_t *buf;
> +    size_t len;
> +
> +    trace_nmi_handle_mi_read_nmi_ds(dtyp);
> +
> +    static uint8_t nmi_ds_subsystem[36] = {
> +        0x00,       /* success */
> +        0x20,       /* response data length */

Not the easiest datasheet to read, but I think length is 2 bytes.
Figure 93 in the 1.2b NMI spec has it as bits 15:00 in
a 24 bit field. Ah. I see this is 1.1 definition, in that it's
the same but in figure 89.


> +        0x00, 0x00, /* reserved */
> +        0x00,       /* number of ports */
> +        0x01,       /* major version */
> +        0x01,       /* minor version */
> +    };
> +
> +    static uint8_t nmi_ds_ports[36] = {
> +        0x00,       /* success */
> +        0x20,       /* response data length */

As above - this is 2 bytes.

> +        0x00, 0x00, /* reserved */
> +        0x02,       /* port type (smbus) */
> +        0x00,       /* reserved */
> +        0x40, 0x00, /* maximum mctp transission unit size (64 bytes) */
> +        0x00, 0x00, 0x00, 0x00, /* management endpoint buffer size */
> +        0x00, 0x00, /* vpd i2c address/freq */

Give separate bytes for the two things, why not just have two lines and
a comment for each one?

> +        0x00, 0x01, /* management endpoint i2c address/freq */

Same here though second byte isn't anything to do with frequency. Documnted
as NVMe Basic Management and indicates if that command is supported.

> +    };
> +
> +    static uint8_t nmi_ds_empty[8] = {
> +        0x00,       /* success */
> +        0x02,       /* response data length */
> +        0x00, 0x00, /* reserved */
> +        0x00, 0x00, /* number of controllers */
A reference for this would be good as I'm not sure it's defined in the NMI spec.
Is it the controller list from 4.4.1 in the main NVME 2.0 spec?


> +        0x00, 0x00, /* padding */
> +    };
> +
> +    switch (dtyp) {
> +    case NMI_CMD_READ_NMI_DS_SUBSYSTEM:
> +        len = 36;
sizeof(nmi_ds_subsystem)  Might as well keep that 36 in just one place.
> +        buf = nmi_ds_subsystem;
> +
> +        break;
> +
> +    case NMI_CMD_READ_NMI_DS_PORTS:
> +        len = 36;
> +        buf = nmi_ds_ports;
same here?
> +
> +        /* patch in the i2c address of the endpoint */
> +        buf[14] = i2c->address;

If you have multiple instances of this as they will
race over the static buffer.  Just make it non static and add
a comment on why.

> +
> +        break;
> +
> +    case NMI_CMD_READ_NMI_DS_CTRL_LIST:
> +    case NMI_CMD_READ_NMI_DS_CMD_SUPPORT:
> +    case NMI_CMD_READ_NMI_DS_MEB_CMD_SUPPORT:
> +        len = 8;
> +        buf = nmi_ds_empty;
> +
> +        break;
> +
> +    default:
> +        nmi_set_parameter_error(nmi, offsetof(NMIRequest, dw0) + 4, 0);
> +
> +        return;
> +    }
> +
> +    memcpy(nmi->scratch + nmi->pos, buf, len);
> +    nmi->pos += len;
> +}
> +
> +enum {
> +    NMI_CMD_CONFIGURATION_GET_SMBUS_FREQ                = 0x1,
> +    NMI_CMD_CONFIGURATION_GET_HEALTH_STATUS_CHANGE      = 0x2,
> +    NMI_CMD_CONFIGURATION_GET_MCTP_TRANSMISSION_UNIT    = 0x3,
> +};
> +
> +static void nmi_handle_mi_config_get(NMIDevice *nmi, NMIRequest *request)
> +{
> +    uint32_t dw0 = le32_to_cpu(request->dw0);
> +    uint8_t identifier = dw0 & 0xff;
> +    uint8_t *buf;
> +
> +    static uint8_t smbus_freq[4] = {
> +        0x00,               /* success */
> +        0x01, 0x00, 0x00,   /* 100 kHz */
> +    };
> +
> +    static uint8_t mtu[4] = {
> +        0x00,               /* success */
> +        0x40, 0x00, 0x00,   /* 64 */

2 bytes only I think with another reserved.
Figure 66 in 1.1d

> +    };
> +
> +    trace_nmi_handle_mi_config_get(identifier);
> +
> +    switch (identifier) {
> +    case NMI_CMD_CONFIGURATION_GET_SMBUS_FREQ:
> +        buf = smbus_freq;
> +        break;
> +
> +    case NMI_CMD_CONFIGURATION_GET_MCTP_TRANSMISSION_UNIT:
> +        buf = mtu;
> +        break;
> +
> +    default:
> +        nmi_set_parameter_error(nmi, 0x0, offsetof(NMIRequest, dw0));
> +        return;
> +    }
> +
> +    memcpy(nmi->scratch + nmi->pos, buf, 4);
> +    nmi->pos += 4;
> +}
> +
> +enum {
> +    NMI_CMD_READ_NMI_DS         = 0x0,
> +    NMI_CMD_CONFIGURATION_GET   = 0x4,
> +};
> +
> +static void nmi_handle_mi(NMIDevice *nmi, NMIMessage *msg)
> +{
> +    NMIRequest *request = (NMIRequest *)msg->payload;
> +
> +    trace_nmi_handle_mi(request->opc);
> +
> +    switch (request->opc) {
> +    case NMI_CMD_READ_NMI_DS:
> +        nmi_handle_mi_read_nmi_ds(nmi, request);
> +        break;
> +
> +    case NMI_CMD_CONFIGURATION_GET:
> +        nmi_handle_mi_config_get(nmi, request);
> +        break;
> +
> +    default:
> +        nmi_set_parameter_error(nmi, 0x0, 0x0);
> +        fprintf(stderr, "nmi command 0x%x not handled\n", request->opc);
> +
> +        break;
> +    }
> +}
> +
> +enum {
> +    NMI_MESSAGE_TYPE_NMI = 0x1,
> +};
> +



> +static size_t nmi_get_types(MCTPI2CEndpoint *mctp, const uint8_t **data)
> +{
> +    static const uint8_t buf[] = {
> +        0x0, 0x1, MCTP_MESSAGE_TYPE_NMI,
> +    };

Perhaps a comment that the 0x1 is the length.  Or you could define
a structure for this with a variable length final field?

Also, should the control type be reported?  I couldn't find a statement
that it shouldn't and it has a message ID.

> +
> +    *data = buf;
> +
> +    return sizeof(buf);
> +}
> +
diff mbox series

Patch

diff --git a/hw/nvme/meson.build b/hw/nvme/meson.build
index 3cf40046eea9..b231e3fa12c2 100644
--- a/hw/nvme/meson.build
+++ b/hw/nvme/meson.build
@@ -1 +1,2 @@ 
 softmmu_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('ctrl.c', 'dif.c', 'ns.c', 'subsys.c'))
+softmmu_ss.add(when: 'CONFIG_MCTP_I2C', if_true: files('nmi-i2c.c'))
diff --git a/hw/nvme/nmi-i2c.c b/hw/nvme/nmi-i2c.c
new file mode 100644
index 000000000000..38e43e48fa51
--- /dev/null
+++ b/hw/nvme/nmi-i2c.c
@@ -0,0 +1,367 @@ 
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * SPDX-FileCopyrightText: Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * SPDX-FileContributor: Padmakar Kalghatgi <p.kalghatgi@samsung.com>
+ * SPDX-FileContributor: Arun Kumar Agasar <arun.kka@samsung.com>
+ * SPDX-FileContributor: Saurav Kumar <saurav.29@partner.samsung.com>
+ * SPDX-FileContributor: Klaus Jensen <k.jensen@samsung.com>
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/crc32c.h"
+#include "hw/i2c/i2c.h"
+#include "hw/registerfields.h"
+#include "hw/i2c/mctp.h"
+#include "trace.h"
+
+#define MCTP_MESSAGE_TYPE_NMI 0x4
+
+#define NMI_MAX_MESSAGE_LENGTH 4224
+
+#define TYPE_NMI_I2C_DEVICE "nmi-i2c"
+OBJECT_DECLARE_SIMPLE_TYPE(NMIDevice, NMI_I2C_DEVICE)
+
+typedef struct NMIDevice {
+    MCTPI2CEndpoint mctp;
+
+    uint8_t buffer[NMI_MAX_MESSAGE_LENGTH];
+    uint8_t scratch[NMI_MAX_MESSAGE_LENGTH];
+
+    size_t  len;
+    int64_t pos;
+} NMIDevice;
+
+FIELD(NMI_NMP, ROR, 7, 1)
+FIELD(NMI_NMP, NMIMT, 3, 4)
+
+#define NMI_NMP_NMIMT_NMI_CMD 0x1
+#define NMI_NMP_NMIMT_NM_ADMIN 0x2
+
+typedef struct NMIMessage {
+    uint8_t mctpd;
+    uint8_t nmp;
+    uint8_t rsvd2[2];
+    uint8_t payload[]; /* includes the Message Integrity Check */
+} NMIMessage;
+
+typedef struct NMIRequest {
+   uint8_t opc;
+   uint8_t rsvd1[3];
+   uint32_t dw0;
+   uint32_t dw1;
+   uint32_t mic;
+} NMIRequest;
+
+typedef struct NMIResponse {
+    uint8_t status;
+    uint8_t response[3];
+    uint8_t payload[]; /* includes the Message Integrity Check */
+} NMIResponse;
+
+typedef enum NMIReadDSType {
+    NMI_CMD_READ_NMI_DS_SUBSYSTEM       = 0x0,
+    NMI_CMD_READ_NMI_DS_PORTS           = 0x1,
+    NMI_CMD_READ_NMI_DS_CTRL_LIST       = 0x2,
+    NMI_CMD_READ_NMI_DS_CTRL_INFO       = 0x3,
+    NMI_CMD_READ_NMI_DS_CMD_SUPPORT     = 0x4,
+    NMI_CMD_READ_NMI_DS_MEB_CMD_SUPPORT = 0x5,
+} NMIReadDSType;
+
+static void nmi_set_parameter_error(NMIDevice *nmi, uint8_t bit, uint16_t byte)
+{
+    nmi->scratch[nmi->pos++] = 0x4;
+    nmi->scratch[nmi->pos++] = bit;
+    nmi->scratch[nmi->pos++] = (byte >> 4) & 0xf;
+    nmi->scratch[nmi->pos++] = byte & 0xf;
+}
+
+static void nmi_set_error(NMIDevice *nmi, uint8_t status)
+{
+    uint8_t buf[4] = {};
+
+    buf[0] = status;
+
+    memcpy(nmi->scratch + nmi->pos, buf, 4);
+    nmi->pos += 4;
+}
+
+static void nmi_handle_mi_read_nmi_ds(NMIDevice *nmi, NMIRequest *request)
+{
+    I2CSlave *i2c = I2C_SLAVE(nmi);
+
+    uint32_t dw0 = le32_to_cpu(request->dw0);
+    uint8_t dtyp = (dw0 >> 24) & 0xf;
+    uint8_t *buf;
+    size_t len;
+
+    trace_nmi_handle_mi_read_nmi_ds(dtyp);
+
+    static uint8_t nmi_ds_subsystem[36] = {
+        0x00,       /* success */
+        0x20,       /* response data length */
+        0x00, 0x00, /* reserved */
+        0x00,       /* number of ports */
+        0x01,       /* major version */
+        0x01,       /* minor version */
+    };
+
+    static uint8_t nmi_ds_ports[36] = {
+        0x00,       /* success */
+        0x20,       /* response data length */
+        0x00, 0x00, /* reserved */
+        0x02,       /* port type (smbus) */
+        0x00,       /* reserved */
+        0x40, 0x00, /* maximum mctp transission unit size (64 bytes) */
+        0x00, 0x00, 0x00, 0x00, /* management endpoint buffer size */
+        0x00, 0x00, /* vpd i2c address/freq */
+        0x00, 0x01, /* management endpoint i2c address/freq */
+    };
+
+    static uint8_t nmi_ds_empty[8] = {
+        0x00,       /* success */
+        0x02,       /* response data length */
+        0x00, 0x00, /* reserved */
+        0x00, 0x00, /* number of controllers */
+        0x00, 0x00, /* padding */
+    };
+
+    switch (dtyp) {
+    case NMI_CMD_READ_NMI_DS_SUBSYSTEM:
+        len = 36;
+        buf = nmi_ds_subsystem;
+
+        break;
+
+    case NMI_CMD_READ_NMI_DS_PORTS:
+        len = 36;
+        buf = nmi_ds_ports;
+
+        /* patch in the i2c address of the endpoint */
+        buf[14] = i2c->address;
+
+        break;
+
+    case NMI_CMD_READ_NMI_DS_CTRL_LIST:
+    case NMI_CMD_READ_NMI_DS_CMD_SUPPORT:
+    case NMI_CMD_READ_NMI_DS_MEB_CMD_SUPPORT:
+        len = 8;
+        buf = nmi_ds_empty;
+
+        break;
+
+    default:
+        nmi_set_parameter_error(nmi, offsetof(NMIRequest, dw0) + 4, 0);
+
+        return;
+    }
+
+    memcpy(nmi->scratch + nmi->pos, buf, len);
+    nmi->pos += len;
+}
+
+enum {
+    NMI_CMD_CONFIGURATION_GET_SMBUS_FREQ                = 0x1,
+    NMI_CMD_CONFIGURATION_GET_HEALTH_STATUS_CHANGE      = 0x2,
+    NMI_CMD_CONFIGURATION_GET_MCTP_TRANSMISSION_UNIT    = 0x3,
+};
+
+static void nmi_handle_mi_config_get(NMIDevice *nmi, NMIRequest *request)
+{
+    uint32_t dw0 = le32_to_cpu(request->dw0);
+    uint8_t identifier = dw0 & 0xff;
+    uint8_t *buf;
+
+    static uint8_t smbus_freq[4] = {
+        0x00,               /* success */
+        0x01, 0x00, 0x00,   /* 100 kHz */
+    };
+
+    static uint8_t mtu[4] = {
+        0x00,               /* success */
+        0x40, 0x00, 0x00,   /* 64 */
+    };
+
+    trace_nmi_handle_mi_config_get(identifier);
+
+    switch (identifier) {
+    case NMI_CMD_CONFIGURATION_GET_SMBUS_FREQ:
+        buf = smbus_freq;
+        break;
+
+    case NMI_CMD_CONFIGURATION_GET_MCTP_TRANSMISSION_UNIT:
+        buf = mtu;
+        break;
+
+    default:
+        nmi_set_parameter_error(nmi, 0x0, offsetof(NMIRequest, dw0));
+        return;
+    }
+
+    memcpy(nmi->scratch + nmi->pos, buf, 4);
+    nmi->pos += 4;
+}
+
+enum {
+    NMI_CMD_READ_NMI_DS         = 0x0,
+    NMI_CMD_CONFIGURATION_GET   = 0x4,
+};
+
+static void nmi_handle_mi(NMIDevice *nmi, NMIMessage *msg)
+{
+    NMIRequest *request = (NMIRequest *)msg->payload;
+
+    trace_nmi_handle_mi(request->opc);
+
+    switch (request->opc) {
+    case NMI_CMD_READ_NMI_DS:
+        nmi_handle_mi_read_nmi_ds(nmi, request);
+        break;
+
+    case NMI_CMD_CONFIGURATION_GET:
+        nmi_handle_mi_config_get(nmi, request);
+        break;
+
+    default:
+        nmi_set_parameter_error(nmi, 0x0, 0x0);
+        fprintf(stderr, "nmi command 0x%x not handled\n", request->opc);
+
+        break;
+    }
+}
+
+enum {
+    NMI_MESSAGE_TYPE_NMI = 0x1,
+};
+
+static void nmi_handle(MCTPI2CEndpoint *mctp)
+{
+    NMIDevice *nmi = NMI_I2C_DEVICE(mctp);
+    NMIMessage *msg = (NMIMessage *)nmi->buffer;
+    uint32_t crc;
+    uint8_t nmimt;
+
+    uint8_t buf[] = {
+        MCTP_MESSAGE_TYPE_NMI | MCTP_MESSAGE_IC,
+        FIELD_DP8(msg->nmp, NMI_NMP, ROR, 1),
+        0x0, 0x0,
+    };
+
+    memcpy(nmi->scratch, buf, sizeof(buf));
+    nmi->pos = sizeof(buf);
+
+    nmimt = FIELD_EX8(msg->nmp, NMI_NMP, NMIMT);
+
+    trace_nmi_handle_msg(nmimt);
+
+    switch (nmimt) {
+    case NMI_MESSAGE_TYPE_NMI:
+        nmi_handle_mi(nmi, msg);
+        break;
+
+    default:
+        fprintf(stderr, "nmi message type 0x%x not handled\n", nmimt);
+
+        nmi_set_error(nmi, 0x3);
+
+        break;
+    }
+
+    /* add message integrity check */
+    memset(nmi->scratch + nmi->pos, 0x0, sizeof(crc));
+
+    crc = crc32c(0xffffffff, nmi->scratch, nmi->pos);
+    memcpy(nmi->scratch + nmi->pos, &crc, sizeof(crc));
+
+    nmi->len = nmi->pos + sizeof(crc);
+    nmi->pos = 0;
+
+    i2c_mctp_schedule_send(mctp);
+}
+
+static size_t nmi_get_buf(MCTPI2CEndpoint *mctp, const uint8_t **buf,
+                          size_t maxlen, uint8_t *mctp_flags)
+{
+    NMIDevice *nmi = NMI_I2C_DEVICE(mctp);
+    size_t len;
+
+    len = MIN(maxlen, nmi->len - nmi->pos);
+
+    if (len == 0) {
+        return 0;
+    }
+
+    if (nmi->pos == 0) {
+        *mctp_flags |= MCTP_H_FLAGS_SOM;
+    }
+
+    *buf = nmi->scratch + nmi->pos;
+    nmi->pos += len;
+
+    if (nmi->pos == nmi->len) {
+        *mctp_flags |= MCTP_H_FLAGS_EOM;
+
+        nmi->pos = nmi->len = 0;
+    }
+
+    return len;
+}
+
+static int nmi_put_buf(MCTPI2CEndpoint *mctp, uint8_t *buf, size_t len)
+{
+    NMIDevice *nmi = NMI_I2C_DEVICE(mctp);
+
+    if (nmi->len + len > NMI_MAX_MESSAGE_LENGTH) {
+        return -1;
+    }
+
+    memcpy(nmi->buffer + nmi->len, buf, len);
+    nmi->len += len;
+
+    return 0;
+}
+
+static void nmi_reset(MCTPI2CEndpoint *mctp)
+{
+    NMIDevice *nmi = NMI_I2C_DEVICE(mctp);
+    nmi->len = 0;
+}
+
+static size_t nmi_get_types(MCTPI2CEndpoint *mctp, const uint8_t **data)
+{
+    static const uint8_t buf[] = {
+        0x0, 0x1, MCTP_MESSAGE_TYPE_NMI,
+    };
+
+    *data = buf;
+
+    return sizeof(buf);
+}
+
+static void nvme_mi_class_init(ObjectClass *oc, void *data)
+{
+    MCTPI2CEndpointClass *mc = MCTP_I2C_ENDPOINT_CLASS(oc);
+
+    mc->get_types = nmi_get_types;
+
+    mc->get_buf = nmi_get_buf;
+    mc->put_buf = nmi_put_buf;
+
+    mc->handle = nmi_handle;
+    mc->reset = nmi_reset;
+}
+
+static const TypeInfo nvme_mi = {
+    .name = TYPE_NMI_I2C_DEVICE,
+    .parent = TYPE_MCTP_I2C_ENDPOINT,
+    .instance_size = sizeof(NMIDevice),
+    .class_init = nvme_mi_class_init,
+};
+
+static void register_types(void)
+{
+    type_register_static(&nvme_mi);
+}
+
+type_init(register_types);
diff --git a/hw/nvme/trace-events b/hw/nvme/trace-events
index 9afddf3b951c..e71171c539bd 100644
--- a/hw/nvme/trace-events
+++ b/hw/nvme/trace-events
@@ -215,3 +215,9 @@  pci_nvme_ub_db_wr_invalid_sq(uint32_t qid) "submission queue doorbell write for
 pci_nvme_ub_db_wr_invalid_sqtail(uint32_t qid, uint16_t new_tail) "submission queue doorbell write value beyond queue size, sqid=%"PRIu32", new_head=%"PRIu16", ignoring"
 pci_nvme_ub_unknown_css_value(void) "unknown value in cc.css field"
 pci_nvme_ub_too_many_mappings(void) "too many prp/sgl mappings"
+
+# nmi-i2c
+nmi_handle_mi_read_nmi_ds(uint8_t dtyp) "dtyp 0x%"PRIx8""
+nmi_handle_mi_config_get(uint8_t identifier) "identifier 0x%"PRIx8""
+nmi_handle_mi(uint8_t opc) "opc 0x%"PRIx8""
+nmi_handle_msg(uint8_t nmint) "nmint 0x%"PRIx8""