1*533affcbSRobert Mustacchi /* 2*533affcbSRobert Mustacchi * This file and its contents are supplied under the terms of the 3*533affcbSRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0. 4*533affcbSRobert Mustacchi * You may only use this file in accordance with the terms of version 5*533affcbSRobert Mustacchi * 1.0 of the CDDL. 6*533affcbSRobert Mustacchi * 7*533affcbSRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this 8*533affcbSRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at 9*533affcbSRobert Mustacchi * http://www.illumos.org/license/CDDL. 10*533affcbSRobert Mustacchi */ 11*533affcbSRobert Mustacchi 12*533affcbSRobert Mustacchi /* 13*533affcbSRobert Mustacchi * Copyright 2024 Oxide Computer Company 14*533affcbSRobert Mustacchi */ 15*533affcbSRobert Mustacchi 16*533affcbSRobert Mustacchi /* 17*533affcbSRobert Mustacchi * This file deals with validating and issuing various log page requests to an 18*533affcbSRobert Mustacchi * NVMe target. This contains information about all spec-based log pages. The 19*533affcbSRobert Mustacchi * get log page command has added a number of fields that have evolved over 20*533affcbSRobert Mustacchi * time. We validate that we're only sending commands to a device that we expect 21*533affcbSRobert Mustacchi * it to have a chance of understanding. In general, we only allow through 22*533affcbSRobert Mustacchi * unknown log pages that correspond to vendor-specific commands. 23*533affcbSRobert Mustacchi * 24*533affcbSRobert Mustacchi * We have two different tables of information that we use to drive and validate 25*533affcbSRobert Mustacchi * things here: 26*533affcbSRobert Mustacchi * 27*533affcbSRobert Mustacchi * 1) We have a list of fields that exist which include minimum controller 28*533affcbSRobert Mustacchi * versions and related functionality validation routines that operate off of 29*533affcbSRobert Mustacchi * the nvme_t. While tihs list includes things like the CSI and LID, these are 30*533affcbSRobert Mustacchi * things that may only be specified when we have a non-standard log page. 31*533affcbSRobert Mustacchi * 32*533affcbSRobert Mustacchi * 2) We then have a table of log pages that are supported which list which 33*533affcbSRobert Mustacchi * fields we allow for the device. Not all of this can be static. 34*533affcbSRobert Mustacchi * 35*533affcbSRobert Mustacchi * This file has been designed to be shareable between both userland and the 36*533affcbSRobert Mustacchi * kernel since the logic that libnvme wants to use is quite similar. 37*533affcbSRobert Mustacchi */ 38*533affcbSRobert Mustacchi 39*533affcbSRobert Mustacchi #include "nvme_common.h" 40*533affcbSRobert Mustacchi 41*533affcbSRobert Mustacchi #include <sys/sysmacros.h> 42*533affcbSRobert Mustacchi #ifdef _KERNEL 43*533affcbSRobert Mustacchi #include <sys/sunddi.h> 44*533affcbSRobert Mustacchi #include <sys/stdint.h> 45*533affcbSRobert Mustacchi #else 46*533affcbSRobert Mustacchi #include <stdio.h> 47*533affcbSRobert Mustacchi #include <inttypes.h> 48*533affcbSRobert Mustacchi #endif 49*533affcbSRobert Mustacchi 50*533affcbSRobert Mustacchi static bool 51*533affcbSRobert Mustacchi nvme_log_field_valid_lsp(const nvme_field_info_t *field, 52*533affcbSRobert Mustacchi const nvme_valid_ctrl_data_t *data, uint64_t lsp, char *msg, size_t msglen) 53*533affcbSRobert Mustacchi { 54*533affcbSRobert Mustacchi uint64_t max; 55*533affcbSRobert Mustacchi 56*533affcbSRobert Mustacchi if (nvme_field_atleast(data, &nvme_vers_2v0)) { 57*533affcbSRobert Mustacchi max = NVME_LOG_MAX_LSP_2v0; 58*533affcbSRobert Mustacchi } else { 59*533affcbSRobert Mustacchi max = NVME_LOG_MAX_LSP; 60*533affcbSRobert Mustacchi } 61*533affcbSRobert Mustacchi 62*533affcbSRobert Mustacchi return (nvme_field_range_check(field, 0, max, msg, msglen, lsp)); 63*533affcbSRobert Mustacchi } 64*533affcbSRobert Mustacchi 65*533affcbSRobert Mustacchi static bool 66*533affcbSRobert Mustacchi nvme_log_field_supported_offset(const nvme_field_info_t *field, 67*533affcbSRobert Mustacchi const nvme_valid_ctrl_data_t *data, char *msg, size_t msglen) 68*533affcbSRobert Mustacchi { 69*533affcbSRobert Mustacchi if (data->vcd_id->id_lpa.lp_extsup != 0) { 70*533affcbSRobert Mustacchi return (true); 71*533affcbSRobert Mustacchi } 72*533affcbSRobert Mustacchi 73*533affcbSRobert Mustacchi (void) snprintf(msg, msglen, "controller does not support field %s " 74*533affcbSRobert Mustacchi "(%s): missing extended data support in Log Page Attributes (LPA)", 75*533affcbSRobert Mustacchi field->nlfi_human, field->nlfi_spec); 76*533affcbSRobert Mustacchi return (false); 77*533affcbSRobert Mustacchi } 78*533affcbSRobert Mustacchi 79*533affcbSRobert Mustacchi /* 80*533affcbSRobert Mustacchi * The offset is a full 64-bit byte value; however, it must be 4-byte aligned. 81*533affcbSRobert Mustacchi */ 82*533affcbSRobert Mustacchi static bool 83*533affcbSRobert Mustacchi nvme_log_field_valid_offset(const nvme_field_info_t *field, 84*533affcbSRobert Mustacchi const nvme_valid_ctrl_data_t *data, uint64_t size, char *msg, size_t msglen) 85*533affcbSRobert Mustacchi { 86*533affcbSRobert Mustacchi if ((size % NVME_DWORD_SIZE) != 0) { 87*533affcbSRobert Mustacchi (void) snprintf(msg, msglen, "%s (%s) value 0x%" PRIx64 " is " 88*533affcbSRobert Mustacchi "invalid: value must be %u-byte aligned", field->nlfi_human, 89*533affcbSRobert Mustacchi field->nlfi_spec, size, NVME_DWORD_SIZE); 90*533affcbSRobert Mustacchi return (false); 91*533affcbSRobert Mustacchi } 92*533affcbSRobert Mustacchi 93*533affcbSRobert Mustacchi return (true); 94*533affcbSRobert Mustacchi } 95*533affcbSRobert Mustacchi 96*533affcbSRobert Mustacchi static bool 97*533affcbSRobert Mustacchi nvme_log_field_valid_size(const nvme_field_info_t *field, 98*533affcbSRobert Mustacchi const nvme_valid_ctrl_data_t *data, uint64_t size, char *msg, size_t msglen) 99*533affcbSRobert Mustacchi { 100*533affcbSRobert Mustacchi uint64_t max = NVME_LOG_MAX_SIZE; 101*533affcbSRobert Mustacchi 102*533affcbSRobert Mustacchi if (nvme_field_atleast(data, &nvme_vers_1v2) && 103*533affcbSRobert Mustacchi data->vcd_id->id_lpa.lp_extsup != 0) { 104*533affcbSRobert Mustacchi max = NVME_LOG_MAX_SIZE_1v2; 105*533affcbSRobert Mustacchi } 106*533affcbSRobert Mustacchi 107*533affcbSRobert Mustacchi /* 108*533affcbSRobert Mustacchi * The NVMe specification operates in terms of uint32_t (dword) units. 109*533affcbSRobert Mustacchi * Make sure that we are operating within that constraint. 110*533affcbSRobert Mustacchi */ 111*533affcbSRobert Mustacchi if ((size % 4) != 0) { 112*533affcbSRobert Mustacchi (void) snprintf(msg, msglen, "%s (%s) value 0x%" PRIx64 " is " 113*533affcbSRobert Mustacchi "invalid: value must be 4-byte aligned", field->nlfi_human, 114*533affcbSRobert Mustacchi field->nlfi_spec, size); 115*533affcbSRobert Mustacchi return (false); 116*533affcbSRobert Mustacchi } 117*533affcbSRobert Mustacchi 118*533affcbSRobert Mustacchi return (nvme_field_range_check(field, 4, max, msg, msglen, size)); 119*533affcbSRobert Mustacchi } 120*533affcbSRobert Mustacchi 121*533affcbSRobert Mustacchi const nvme_field_info_t nvme_log_fields[] = { 122*533affcbSRobert Mustacchi [NVME_LOG_REQ_FIELD_LID] = { 123*533affcbSRobert Mustacchi .nlfi_vers = &nvme_vers_1v0, 124*533affcbSRobert Mustacchi .nlfi_max_size = NVME_LOG_MAX_LID, 125*533affcbSRobert Mustacchi .nlfi_spec = "lid", 126*533affcbSRobert Mustacchi .nlfi_human = "log ID", 127*533affcbSRobert Mustacchi .nlfi_def_req = true, 128*533affcbSRobert Mustacchi .nlfi_def_allow = true 129*533affcbSRobert Mustacchi }, 130*533affcbSRobert Mustacchi [NVME_LOG_REQ_FIELD_LSP] = { 131*533affcbSRobert Mustacchi .nlfi_vers = &nvme_vers_1v3, 132*533affcbSRobert Mustacchi .nlfi_valid = nvme_log_field_valid_lsp, 133*533affcbSRobert Mustacchi .nlfi_spec = "lsp", 134*533affcbSRobert Mustacchi .nlfi_human = "log specific field", 135*533affcbSRobert Mustacchi .nlfi_def_req = false, 136*533affcbSRobert Mustacchi .nlfi_def_allow = true 137*533affcbSRobert Mustacchi }, 138*533affcbSRobert Mustacchi [NVME_LOG_REQ_FIELD_LSI] = { 139*533affcbSRobert Mustacchi .nlfi_vers = &nvme_vers_1v4, 140*533affcbSRobert Mustacchi .nlfi_max_size = NVME_LOG_MAX_LSI, 141*533affcbSRobert Mustacchi .nlfi_spec = "lsi", 142*533affcbSRobert Mustacchi .nlfi_human = "log specific ID", 143*533affcbSRobert Mustacchi .nlfi_def_req = false, 144*533affcbSRobert Mustacchi .nlfi_def_allow = true 145*533affcbSRobert Mustacchi }, 146*533affcbSRobert Mustacchi [NVME_LOG_REQ_FIELD_SIZE] = { 147*533affcbSRobert Mustacchi .nlfi_vers = &nvme_vers_1v0, 148*533affcbSRobert Mustacchi .nlfi_valid = nvme_log_field_valid_size, 149*533affcbSRobert Mustacchi .nlfi_spec = "dptr/numd", 150*533affcbSRobert Mustacchi .nlfi_human = "output", 151*533affcbSRobert Mustacchi .nlfi_def_req = true, 152*533affcbSRobert Mustacchi .nlfi_def_allow = true 153*533affcbSRobert Mustacchi }, 154*533affcbSRobert Mustacchi [NVME_LOG_REQ_FIELD_CSI] = { 155*533affcbSRobert Mustacchi .nlfi_vers = &nvme_vers_2v0, 156*533affcbSRobert Mustacchi /* 157*533affcbSRobert Mustacchi * This has the field's maximum range right now, though NVMe 2.0 158*533affcbSRobert Mustacchi * only defines a few values. Because the kernel only allows 159*533affcbSRobert Mustacchi * through known log pages, we don't really bother to check the 160*533affcbSRobert Mustacchi * condensed range and allow vendor-specific logs to go wild. 161*533affcbSRobert Mustacchi */ 162*533affcbSRobert Mustacchi .nlfi_max_size = NVME_LOG_MAX_CSI, 163*533affcbSRobert Mustacchi .nlfi_spec = "csi", 164*533affcbSRobert Mustacchi .nlfi_human = "command set ID", 165*533affcbSRobert Mustacchi .nlfi_def_req = false, 166*533affcbSRobert Mustacchi .nlfi_def_allow = true 167*533affcbSRobert Mustacchi }, 168*533affcbSRobert Mustacchi [NVME_LOG_REQ_FIELD_RAE] = { 169*533affcbSRobert Mustacchi .nlfi_vers = &nvme_vers_1v3, 170*533affcbSRobert Mustacchi .nlfi_max_size = NVME_LOG_MAX_RAE, 171*533affcbSRobert Mustacchi .nlfi_spec = "rae", 172*533affcbSRobert Mustacchi .nlfi_human = "retain asynchronous event", 173*533affcbSRobert Mustacchi .nlfi_def_req = false, 174*533affcbSRobert Mustacchi .nlfi_def_allow = true 175*533affcbSRobert Mustacchi }, 176*533affcbSRobert Mustacchi [NVME_LOG_REQ_FIELD_OFFSET] = { 177*533affcbSRobert Mustacchi .nlfi_vers = &nvme_vers_1v2, 178*533affcbSRobert Mustacchi .nlfi_sup = nvme_log_field_supported_offset, 179*533affcbSRobert Mustacchi .nlfi_valid = nvme_log_field_valid_offset, 180*533affcbSRobert Mustacchi .nlfi_max_size = NVME_LOG_MAX_OFFSET, 181*533affcbSRobert Mustacchi .nlfi_spec = "lpo", 182*533affcbSRobert Mustacchi .nlfi_human = "log offset", 183*533affcbSRobert Mustacchi .nlfi_def_req = false, 184*533affcbSRobert Mustacchi .nlfi_def_allow = true 185*533affcbSRobert Mustacchi }, 186*533affcbSRobert Mustacchi [NVME_LOG_REQ_FIELD_NSID] = { 187*533affcbSRobert Mustacchi .nlfi_vers = &nvme_vers_1v2, 188*533affcbSRobert Mustacchi .nlfi_sup = nvme_field_supported_nsid, 189*533affcbSRobert Mustacchi .nlfi_valid = nvme_field_valid_nsid, 190*533affcbSRobert Mustacchi .nlfi_spec = "nsid", 191*533affcbSRobert Mustacchi .nlfi_human = "namespace ID", 192*533affcbSRobert Mustacchi .nlfi_def_req = false, 193*533affcbSRobert Mustacchi .nlfi_def_allow = true 194*533affcbSRobert Mustacchi } 195*533affcbSRobert Mustacchi }; 196*533affcbSRobert Mustacchi 197*533affcbSRobert Mustacchi size_t nvme_log_nfields = ARRAY_SIZE(nvme_log_fields); 198*533affcbSRobert Mustacchi 199*533affcbSRobert Mustacchi static uint64_t 200*533affcbSRobert Mustacchi nvme_lpd_error_len(const nvme_valid_ctrl_data_t *data, 201*533affcbSRobert Mustacchi const nvme_log_page_info_t *lpi) 202*533affcbSRobert Mustacchi { 203*533affcbSRobert Mustacchi const uint64_t nents = data->vcd_id->id_elpe + 1; 204*533affcbSRobert Mustacchi const uint64_t logsz = nents * sizeof (nvme_error_log_entry_t); 205*533affcbSRobert Mustacchi 206*533affcbSRobert Mustacchi return (logsz); 207*533affcbSRobert Mustacchi } 208*533affcbSRobert Mustacchi 209*533affcbSRobert Mustacchi static nvme_log_disc_scope_t 210*533affcbSRobert Mustacchi nvme_lpd_health_scope(const nvme_valid_ctrl_data_t *data, 211*533affcbSRobert Mustacchi const nvme_log_page_info_t *lpi) 212*533affcbSRobert Mustacchi { 213*533affcbSRobert Mustacchi nvme_log_disc_scope_t ret = NVME_LOG_SCOPE_CTRL; 214*533affcbSRobert Mustacchi 215*533affcbSRobert Mustacchi if (nvme_field_atleast(data, &nvme_vers_1v0) && 216*533affcbSRobert Mustacchi data->vcd_id->id_lpa.lp_smart != 0) { 217*533affcbSRobert Mustacchi ret |= NVME_LOG_SCOPE_NS; 218*533affcbSRobert Mustacchi } 219*533affcbSRobert Mustacchi 220*533affcbSRobert Mustacchi return (ret); 221*533affcbSRobert Mustacchi } 222*533affcbSRobert Mustacchi 223*533affcbSRobert Mustacchi static bool 224*533affcbSRobert Mustacchi nvme_lpd_changens_sup(const nvme_valid_ctrl_data_t *data, 225*533affcbSRobert Mustacchi const nvme_log_page_info_t *lpi) 226*533affcbSRobert Mustacchi { 227*533affcbSRobert Mustacchi return (nvme_field_atleast(data, &nvme_vers_1v2) && 228*533affcbSRobert Mustacchi data->vcd_id->id_oaes.oaes_nsan != 0); 229*533affcbSRobert Mustacchi } 230*533affcbSRobert Mustacchi 231*533affcbSRobert Mustacchi /* 232*533affcbSRobert Mustacchi * The short names here correspond to the well defined names in nvmeadm(8) and 233*533affcbSRobert Mustacchi * libnvme(3LIB) that users expect to be able to use. Please do not change them 234*533affcbSRobert Mustacchi * without accounting for aliases and backwards compatibility. 235*533affcbSRobert Mustacchi */ 236*533affcbSRobert Mustacchi const nvme_log_page_info_t nvme_std_log_pages[] = { { 237*533affcbSRobert Mustacchi .nlpi_short = "error", 238*533affcbSRobert Mustacchi .nlpi_human = "Error information", 239*533affcbSRobert Mustacchi .nlpi_lid = NVME_LOGPAGE_ERROR, 240*533affcbSRobert Mustacchi .nlpi_csi = NVME_CSI_NVM, 241*533affcbSRobert Mustacchi .nlpi_vers = &nvme_vers_1v0, 242*533affcbSRobert Mustacchi .nlpi_kind = NVME_LOG_ID_MANDATORY, 243*533affcbSRobert Mustacchi .nlpi_source = NVME_LOG_DISC_S_SPEC, 244*533affcbSRobert Mustacchi .nlpi_disc = NVME_LOG_DISC_F_NEED_RAE, 245*533affcbSRobert Mustacchi .nlpi_scope = NVME_LOG_SCOPE_CTRL, 246*533affcbSRobert Mustacchi .nlpi_len_func = nvme_lpd_error_len 247*533affcbSRobert Mustacchi }, { 248*533affcbSRobert Mustacchi .nlpi_short = "health", 249*533affcbSRobert Mustacchi .nlpi_human = "SMART / Health information", 250*533affcbSRobert Mustacchi .nlpi_lid = NVME_LOGPAGE_HEALTH, 251*533affcbSRobert Mustacchi .nlpi_csi = NVME_CSI_NVM, 252*533affcbSRobert Mustacchi .nlpi_vers = &nvme_vers_1v0, 253*533affcbSRobert Mustacchi .nlpi_kind = NVME_LOG_ID_MANDATORY, 254*533affcbSRobert Mustacchi .nlpi_source = NVME_LOG_DISC_S_SPEC | NVME_LOG_DISC_S_ID_CTRL, 255*533affcbSRobert Mustacchi .nlpi_disc = NVME_LOG_DISC_F_NEED_RAE, 256*533affcbSRobert Mustacchi .nlpi_scope_func = nvme_lpd_health_scope, 257*533affcbSRobert Mustacchi .nlpi_len = sizeof (nvme_health_log_t) 258*533affcbSRobert Mustacchi }, { 259*533affcbSRobert Mustacchi .nlpi_short = "firmware", 260*533affcbSRobert Mustacchi .nlpi_human = "Firmware Slot Information", 261*533affcbSRobert Mustacchi .nlpi_lid = NVME_LOGPAGE_FWSLOT, 262*533affcbSRobert Mustacchi .nlpi_csi = NVME_CSI_NVM, 263*533affcbSRobert Mustacchi .nlpi_vers = &nvme_vers_1v0, 264*533affcbSRobert Mustacchi .nlpi_kind = NVME_LOG_ID_MANDATORY, 265*533affcbSRobert Mustacchi .nlpi_source = NVME_LOG_DISC_S_SPEC, 266*533affcbSRobert Mustacchi .nlpi_disc = 0, 267*533affcbSRobert Mustacchi .nlpi_scope = NVME_LOG_SCOPE_NVM, 268*533affcbSRobert Mustacchi .nlpi_len = sizeof (nvme_fwslot_log_t), 269*533affcbSRobert Mustacchi }, { 270*533affcbSRobert Mustacchi .nlpi_short = "changens", 271*533affcbSRobert Mustacchi .nlpi_human = "changed namespaces", 272*533affcbSRobert Mustacchi .nlpi_lid = NVME_LOGPAGE_NSCHANGE, 273*533affcbSRobert Mustacchi .nlpi_csi = NVME_CSI_NVM, 274*533affcbSRobert Mustacchi .nlpi_vers = &nvme_vers_1v2, 275*533affcbSRobert Mustacchi .nlpi_sup_func = nvme_lpd_changens_sup, 276*533affcbSRobert Mustacchi .nlpi_kind = NVME_LOG_ID_OPTIONAL, 277*533affcbSRobert Mustacchi .nlpi_source = NVME_LOG_DISC_S_ID_CTRL, 278*533affcbSRobert Mustacchi .nlpi_disc = NVME_LOG_DISC_F_NEED_RAE, 279*533affcbSRobert Mustacchi .nlpi_scope = NVME_LOG_SCOPE_CTRL, 280*533affcbSRobert Mustacchi .nlpi_len = sizeof (nvme_nschange_list_t) 281*533affcbSRobert Mustacchi } }; 282*533affcbSRobert Mustacchi 283*533affcbSRobert Mustacchi size_t nvme_std_log_npages = ARRAY_SIZE(nvme_std_log_pages); 284*533affcbSRobert Mustacchi 285*533affcbSRobert Mustacchi nvme_log_disc_scope_t 286*533affcbSRobert Mustacchi nvme_log_page_info_scope(const nvme_log_page_info_t *info, 287*533affcbSRobert Mustacchi const nvme_valid_ctrl_data_t *data) 288*533affcbSRobert Mustacchi { 289*533affcbSRobert Mustacchi if (info->nlpi_scope_func != NULL) { 290*533affcbSRobert Mustacchi return (info->nlpi_scope_func(data, info)); 291*533affcbSRobert Mustacchi } else { 292*533affcbSRobert Mustacchi return (info->nlpi_scope); 293*533affcbSRobert Mustacchi } 294*533affcbSRobert Mustacchi } 295*533affcbSRobert Mustacchi 296*533affcbSRobert Mustacchi uint64_t 297*533affcbSRobert Mustacchi nvme_log_page_info_size(const nvme_log_page_info_t *info, 298*533affcbSRobert Mustacchi const nvme_valid_ctrl_data_t *data, bool *var) 299*533affcbSRobert Mustacchi { 300*533affcbSRobert Mustacchi *var = info->nlpi_var_func != NULL; 301*533affcbSRobert Mustacchi 302*533affcbSRobert Mustacchi if (info->nlpi_len_func != NULL) { 303*533affcbSRobert Mustacchi return (info->nlpi_len_func(data, info)); 304*533affcbSRobert Mustacchi } else { 305*533affcbSRobert Mustacchi return (info->nlpi_len); 306*533affcbSRobert Mustacchi } 307*533affcbSRobert Mustacchi } 308*533affcbSRobert Mustacchi 309*533affcbSRobert Mustacchi bool 310*533affcbSRobert Mustacchi nvme_log_page_info_supported(const nvme_log_page_info_t *info, 311*533affcbSRobert Mustacchi const nvme_valid_ctrl_data_t *data) 312*533affcbSRobert Mustacchi { 313*533affcbSRobert Mustacchi bool vers, sup_func; 314*533affcbSRobert Mustacchi 315*533affcbSRobert Mustacchi if (info->nlpi_vers != NULL) { 316*533affcbSRobert Mustacchi vers = nvme_field_atleast(data, info->nlpi_vers); 317*533affcbSRobert Mustacchi } else { 318*533affcbSRobert Mustacchi vers = true; 319*533affcbSRobert Mustacchi } 320*533affcbSRobert Mustacchi 321*533affcbSRobert Mustacchi if (info->nlpi_sup_func != NULL) { 322*533affcbSRobert Mustacchi sup_func = info->nlpi_sup_func(data, info); 323*533affcbSRobert Mustacchi } else { 324*533affcbSRobert Mustacchi sup_func = true; 325*533affcbSRobert Mustacchi } 326*533affcbSRobert Mustacchi 327*533affcbSRobert Mustacchi return (vers && sup_func); 328*533affcbSRobert Mustacchi } 329