root/sys/dev/nvme/nvme_util.c
/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (C) 2013 Intel Corporation
 * Copyright (C) 1997 Justin T. Gibbs
 * All rights reserved.
 *
 * Copyright (c) 2023-2025 Chelsio Communications, Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/sbuf.h>
#include <dev/nvme/nvme.h>

#define OPC_ENTRY(x)            [NVME_OPC_ ## x] = #x

static const char *admin_opcode[256] = {
        OPC_ENTRY(DELETE_IO_SQ),
        OPC_ENTRY(CREATE_IO_SQ),
        OPC_ENTRY(GET_LOG_PAGE),
        OPC_ENTRY(DELETE_IO_CQ),
        OPC_ENTRY(CREATE_IO_CQ),
        OPC_ENTRY(IDENTIFY),
        OPC_ENTRY(ABORT),
        OPC_ENTRY(SET_FEATURES),
        OPC_ENTRY(GET_FEATURES),
        OPC_ENTRY(ASYNC_EVENT_REQUEST),
        OPC_ENTRY(NAMESPACE_MANAGEMENT),
        OPC_ENTRY(FIRMWARE_ACTIVATE),
        OPC_ENTRY(FIRMWARE_IMAGE_DOWNLOAD),
        OPC_ENTRY(DEVICE_SELF_TEST),
        OPC_ENTRY(NAMESPACE_ATTACHMENT),
        OPC_ENTRY(KEEP_ALIVE),
        OPC_ENTRY(DIRECTIVE_SEND),
        OPC_ENTRY(DIRECTIVE_RECEIVE),
        OPC_ENTRY(VIRTUALIZATION_MANAGEMENT),
        OPC_ENTRY(NVME_MI_SEND),
        OPC_ENTRY(NVME_MI_RECEIVE),
        OPC_ENTRY(CAPACITY_MANAGEMENT),
        OPC_ENTRY(LOCKDOWN),
        OPC_ENTRY(DOORBELL_BUFFER_CONFIG),
        OPC_ENTRY(FABRICS_COMMANDS),
        OPC_ENTRY(FORMAT_NVM),
        OPC_ENTRY(SECURITY_SEND),
        OPC_ENTRY(SECURITY_RECEIVE),
        OPC_ENTRY(SANITIZE),
        OPC_ENTRY(GET_LBA_STATUS),
};

static const char *nvm_opcode[256] = {
        OPC_ENTRY(FLUSH),
        OPC_ENTRY(WRITE),
        OPC_ENTRY(READ),
        OPC_ENTRY(WRITE_UNCORRECTABLE),
        OPC_ENTRY(COMPARE),
        OPC_ENTRY(WRITE_ZEROES),
        OPC_ENTRY(DATASET_MANAGEMENT),
        OPC_ENTRY(VERIFY),
        OPC_ENTRY(RESERVATION_REGISTER),
        OPC_ENTRY(RESERVATION_REPORT),
        OPC_ENTRY(RESERVATION_ACQUIRE),
        OPC_ENTRY(RESERVATION_RELEASE),
        OPC_ENTRY(COPY),
};

#define SC_ENTRY(x)             [NVME_SC_ ## x] = #x

static const char *generic_status[256] = {
        SC_ENTRY(SUCCESS),
        SC_ENTRY(INVALID_OPCODE),
        SC_ENTRY(INVALID_FIELD),
        SC_ENTRY(COMMAND_ID_CONFLICT),
        SC_ENTRY(DATA_TRANSFER_ERROR),
        SC_ENTRY(ABORTED_POWER_LOSS),
        SC_ENTRY(INTERNAL_DEVICE_ERROR),
        SC_ENTRY(ABORTED_BY_REQUEST),
        SC_ENTRY(ABORTED_SQ_DELETION),
        SC_ENTRY(ABORTED_FAILED_FUSED),
        SC_ENTRY(ABORTED_MISSING_FUSED),
        SC_ENTRY(INVALID_NAMESPACE_OR_FORMAT),
        SC_ENTRY(COMMAND_SEQUENCE_ERROR),
        SC_ENTRY(INVALID_SGL_SEGMENT_DESCR),
        SC_ENTRY(INVALID_NUMBER_OF_SGL_DESCR),
        SC_ENTRY(DATA_SGL_LENGTH_INVALID),
        SC_ENTRY(METADATA_SGL_LENGTH_INVALID),
        SC_ENTRY(SGL_DESCRIPTOR_TYPE_INVALID),
        SC_ENTRY(INVALID_USE_OF_CMB),
        SC_ENTRY(PRP_OFFET_INVALID),
        SC_ENTRY(ATOMIC_WRITE_UNIT_EXCEEDED),
        SC_ENTRY(OPERATION_DENIED),
        SC_ENTRY(SGL_OFFSET_INVALID),
        SC_ENTRY(HOST_ID_INCONSISTENT_FORMAT),
        SC_ENTRY(KEEP_ALIVE_TIMEOUT_EXPIRED),
        SC_ENTRY(KEEP_ALIVE_TIMEOUT_INVALID),
        SC_ENTRY(ABORTED_DUE_TO_PREEMPT),
        SC_ENTRY(SANITIZE_FAILED),
        SC_ENTRY(SANITIZE_IN_PROGRESS),
        SC_ENTRY(SGL_DATA_BLOCK_GRAN_INVALID),
        SC_ENTRY(NOT_SUPPORTED_IN_CMB),
        SC_ENTRY(NAMESPACE_IS_WRITE_PROTECTED),
        SC_ENTRY(COMMAND_INTERRUPTED),
        SC_ENTRY(TRANSIENT_TRANSPORT_ERROR),

        SC_ENTRY(LBA_OUT_OF_RANGE),
        SC_ENTRY(CAPACITY_EXCEEDED),
        SC_ENTRY(NAMESPACE_NOT_READY),
        SC_ENTRY(RESERVATION_CONFLICT),
        SC_ENTRY(FORMAT_IN_PROGRESS),
};

static const char *command_specific_status[256] = {
        SC_ENTRY(COMPLETION_QUEUE_INVALID),
        SC_ENTRY(INVALID_QUEUE_IDENTIFIER),
        SC_ENTRY(MAXIMUM_QUEUE_SIZE_EXCEEDED),
        SC_ENTRY(ABORT_COMMAND_LIMIT_EXCEEDED),
        SC_ENTRY(ASYNC_EVENT_REQUEST_LIMIT_EXCEEDED),
        SC_ENTRY(INVALID_FIRMWARE_SLOT),
        SC_ENTRY(INVALID_FIRMWARE_IMAGE),
        SC_ENTRY(INVALID_INTERRUPT_VECTOR),
        SC_ENTRY(INVALID_LOG_PAGE),
        SC_ENTRY(INVALID_FORMAT),
        SC_ENTRY(FIRMWARE_REQUIRES_RESET),
        SC_ENTRY(INVALID_QUEUE_DELETION),
        SC_ENTRY(FEATURE_NOT_SAVEABLE),
        SC_ENTRY(FEATURE_NOT_CHANGEABLE),
        SC_ENTRY(FEATURE_NOT_NS_SPECIFIC),
        SC_ENTRY(FW_ACT_REQUIRES_NVMS_RESET),
        SC_ENTRY(FW_ACT_REQUIRES_RESET),
        SC_ENTRY(FW_ACT_REQUIRES_TIME),
        SC_ENTRY(FW_ACT_PROHIBITED),
        SC_ENTRY(OVERLAPPING_RANGE),
        SC_ENTRY(NS_INSUFFICIENT_CAPACITY),
        SC_ENTRY(NS_ID_UNAVAILABLE),
        SC_ENTRY(NS_ALREADY_ATTACHED),
        SC_ENTRY(NS_IS_PRIVATE),
        SC_ENTRY(NS_NOT_ATTACHED),
        SC_ENTRY(THIN_PROV_NOT_SUPPORTED),
        SC_ENTRY(CTRLR_LIST_INVALID),
        SC_ENTRY(SELF_TEST_IN_PROGRESS),
        SC_ENTRY(BOOT_PART_WRITE_PROHIB),
        SC_ENTRY(INVALID_CTRLR_ID),
        SC_ENTRY(INVALID_SEC_CTRLR_STATE),
        SC_ENTRY(INVALID_NUM_OF_CTRLR_RESRC),
        SC_ENTRY(INVALID_RESOURCE_ID),
        SC_ENTRY(SANITIZE_PROHIBITED_WPMRE),
        SC_ENTRY(ANA_GROUP_ID_INVALID),
        SC_ENTRY(ANA_ATTACH_FAILED),

        SC_ENTRY(CONFLICTING_ATTRIBUTES),
        SC_ENTRY(INVALID_PROTECTION_INFO),
        SC_ENTRY(ATTEMPTED_WRITE_TO_RO_PAGE),
};

static const char *media_error_status[256] = {
        SC_ENTRY(WRITE_FAULTS),
        SC_ENTRY(UNRECOVERED_READ_ERROR),
        SC_ENTRY(GUARD_CHECK_ERROR),
        SC_ENTRY(APPLICATION_TAG_CHECK_ERROR),
        SC_ENTRY(REFERENCE_TAG_CHECK_ERROR),
        SC_ENTRY(COMPARE_FAILURE),
        SC_ENTRY(ACCESS_DENIED),
        SC_ENTRY(DEALLOCATED_OR_UNWRITTEN),
};

static const char *path_related_status[256] = {
        SC_ENTRY(INTERNAL_PATH_ERROR),
        SC_ENTRY(ASYMMETRIC_ACCESS_PERSISTENT_LOSS),
        SC_ENTRY(ASYMMETRIC_ACCESS_INACCESSIBLE),
        SC_ENTRY(ASYMMETRIC_ACCESS_TRANSITION),
        SC_ENTRY(CONTROLLER_PATHING_ERROR),
        SC_ENTRY(HOST_PATHING_ERROR),
        SC_ENTRY(COMMAND_ABORTED_BY_HOST),
};

void
nvme_opcode_sbuf(bool admin, uint8_t opc, struct sbuf *sb)
{
        const char *s, *type;

        if (admin) {
                s = admin_opcode[opc];
                type = "ADMIN";
        } else {
                s = nvm_opcode[opc];
                type = "NVM";
        }
        if (s == NULL)
                sbuf_printf(sb, "%s (%02x)", type, opc);
        else
                sbuf_printf(sb, "%s (%02x)", s, opc);
}

void
nvme_sc_sbuf(const struct nvme_completion *cpl, struct sbuf *sb)
{
        const char *s, *type;
        uint16_t status, sc, sct;

        status = le16toh(cpl->status);
        sc = NVME_STATUS_GET_SC(status);
        sct = NVME_STATUS_GET_SCT(status);
        switch (sct) {
        case NVME_SCT_GENERIC:
                s = generic_status[sc];
                type = "GENERIC";
                break;
        case NVME_SCT_COMMAND_SPECIFIC:
                s = command_specific_status[sc];
                type = "COMMAND SPECIFIC";
                break;
        case NVME_SCT_MEDIA_ERROR:
                s = media_error_status[sc];
                type = "MEDIA ERROR";
                break;
        case NVME_SCT_PATH_RELATED:
                s = path_related_status[sc];
                type = "PATH RELATED";
                break;
        case NVME_SCT_VENDOR_SPECIFIC:
                s = NULL;
                type = "VENDOR SPECIFIC";
                break;
        default:
                s = NULL;
                type = NULL;
                break;
        }

        if (type == NULL)
                sbuf_printf(sb, "RESERVED (%02x/%02x)", sct, sc);
        else if (s == NULL)
                sbuf_printf(sb, "%s (%02x/%02x)", type, sct, sc);
        else
                sbuf_printf(sb, "%s (%02x/%02x)", s, sct, sc);
}

void
nvme_cpl_sbuf(const struct nvme_completion *cpl, struct sbuf *sb)
{
        uint16_t status;

        status = le16toh(cpl->status);
        nvme_sc_sbuf(cpl, sb);
        if (NVME_STATUS_GET_M(status) != 0)
                sbuf_printf(sb, " M");
        if (NVME_STATUS_GET_DNR(status) != 0)
                sbuf_printf(sb, " DNR");
}

void
nvme_strvis(uint8_t *dst, const uint8_t *src, int dstlen, int srclen)
{
        uint8_t *cur_pos;

        /* Trim leading/trailing spaces, nulls. */
        while (srclen > 0 && src[0] == ' ')
                src++, srclen--;
        while (srclen > 0
            && (src[srclen - 1] == ' ' || src[srclen - 1] == '\0'))
                srclen--;

        while (srclen > 0 && dstlen > 1) {
                cur_pos = dst;

                /* Show '?' for non-printable characters. */
                if (*src < 0x20 || *src >= 0x7F)
                        *cur_pos++ = '?';
                else
                        *cur_pos++ = *src;
                src++;
                srclen--;
                dstlen -= cur_pos - dst;
                dst = cur_pos;
        }
        *dst = '\0';
}