1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2023-2024 Chelsio Communications, Inc. 5 * Written by: John Baldwin <jhb@FreeBSD.org> 6 */ 7 8 #include <sys/types.h> 9 #ifdef _KERNEL 10 #include <sys/libkern.h> 11 #else 12 #include <stdbool.h> 13 #include <stdio.h> 14 #include <string.h> 15 #endif 16 17 #include <dev/nvmf/nvmf_proto.h> 18 19 #include "nvmft_subr.h" 20 21 bool 22 nvmf_nqn_valid(const char *nqn) 23 { 24 size_t len; 25 26 len = strnlen(nqn, NVME_NQN_FIELD_SIZE); 27 if (len == 0 || len > NVMF_NQN_MAX_LEN) 28 return (false); 29 return (true); 30 } 31 32 uint64_t 33 _nvmf_controller_cap(uint32_t max_io_qsize, uint8_t enable_timeout) 34 { 35 uint32_t caphi, caplo; 36 u_int mps; 37 38 caphi = NVMEF(NVME_CAP_HI_REG_CMBS, 0) | 39 NVMEF(NVME_CAP_HI_REG_PMRS, 0); 40 if (max_io_qsize != 0) { 41 mps = ffs(PAGE_SIZE) - 1; 42 if (mps < NVME_MPS_SHIFT) 43 mps = 0; 44 else 45 mps -= NVME_MPS_SHIFT; 46 caphi |= NVMEF(NVME_CAP_HI_REG_MPSMAX, mps) | 47 NVMEF(NVME_CAP_HI_REG_MPSMIN, mps); 48 } 49 caphi |= NVMEF(NVME_CAP_HI_REG_BPS, 0) | 50 NVMEF(NVME_CAP_HI_REG_CSS, NVME_CAP_HI_REG_CSS_NVM_MASK) | 51 NVMEF(NVME_CAP_HI_REG_NSSRS, 0) | 52 NVMEF(NVME_CAP_HI_REG_DSTRD, 0); 53 54 caplo = NVMEF(NVME_CAP_LO_REG_TO, enable_timeout) | 55 NVMEF(NVME_CAP_LO_REG_AMS, 0) | 56 NVMEF(NVME_CAP_LO_REG_CQR, 1); 57 58 if (max_io_qsize != 0) 59 caplo |= NVMEF(NVME_CAP_LO_REG_MQES, max_io_qsize - 1); 60 61 return ((uint64_t)caphi << 32 | caplo); 62 } 63 64 bool 65 _nvmf_validate_cc(uint32_t max_io_qsize __unused, uint64_t cap, uint32_t old_cc, 66 uint32_t new_cc) 67 { 68 uint32_t caphi, changes, field; 69 70 changes = old_cc ^ new_cc; 71 field = NVMEV(NVME_CC_REG_IOCQES, new_cc); 72 if (field != 0) { 73 /* 74 * XXX: Linux's initiator writes a non-zero value to 75 * IOCQES when connecting to a discovery controller. 76 */ 77 #ifdef STRICT_CHECKS 78 if (max_io_qsize == 0) 79 return (false); 80 #endif 81 if (field != 4) 82 return (false); 83 } 84 field = NVMEV(NVME_CC_REG_IOSQES, new_cc); 85 if (field != 0) { 86 /* 87 * XXX: Linux's initiator writes a non-zero value to 88 * IOCQES when connecting to a discovery controller. 89 */ 90 #ifdef STRICT_CHECKS 91 if (max_io_qsize == 0) 92 return (false); 93 #endif 94 if (field != 6) 95 return (false); 96 } 97 field = NVMEV(NVME_CC_REG_SHN, new_cc); 98 if (field == 3) 99 return (false); 100 101 field = NVMEV(NVME_CC_REG_AMS, new_cc); 102 if (field != 0) 103 return (false); 104 105 caphi = cap >> 32; 106 field = NVMEV(NVME_CC_REG_MPS, new_cc); 107 if (field < NVMEV(NVME_CAP_HI_REG_MPSMAX, caphi) || 108 field > NVMEV(NVME_CAP_HI_REG_MPSMIN, caphi)) 109 return (false); 110 111 field = NVMEV(NVME_CC_REG_CSS, new_cc); 112 if (field != 0 && field != 0x7) 113 return (false); 114 115 /* AMS, MPS, and CSS can only be changed while CC.EN is 0. */ 116 if (NVMEV(NVME_CC_REG_EN, old_cc) != 0 && 117 (NVMEV(NVME_CC_REG_AMS, changes) != 0 || 118 NVMEV(NVME_CC_REG_MPS, changes) != 0 || 119 NVMEV(NVME_CC_REG_CSS, changes) != 0)) 120 return (false); 121 122 return (true); 123 } 124 125 void 126 nvmf_controller_serial(char *buf, size_t len, u_long hostid) 127 { 128 snprintf(buf, len, "HI:%lu", hostid); 129 } 130 131 void 132 nvmf_strpad(char *dst, const char *src, size_t len) 133 { 134 while (len > 0 && *src != '\0') { 135 *dst++ = *src++; 136 len--; 137 } 138 memset(dst, ' ', len); 139 } 140 141 void 142 _nvmf_init_io_controller_data(uint16_t cntlid, uint32_t max_io_qsize, 143 const char *serial, const char *model, const char *firmware_version, 144 const char *subnqn, int nn, uint32_t ioccsz, uint32_t iorcsz, 145 struct nvme_controller_data *cdata) 146 { 147 char *cp; 148 149 nvmf_strpad(cdata->sn, serial, sizeof(cdata->sn)); 150 nvmf_strpad(cdata->mn, model, sizeof(cdata->mn)); 151 nvmf_strpad(cdata->fr, firmware_version, sizeof(cdata->fr)); 152 cp = memchr(cdata->fr, '-', sizeof(cdata->fr)); 153 if (cp != NULL) 154 memset(cp, ' ', sizeof(cdata->fr) - (cp - (char *)cdata->fr)); 155 156 /* FreeBSD OUI */ 157 cdata->ieee[0] = 0xfc; 158 cdata->ieee[1] = 0x9c; 159 cdata->ieee[2] = 0x58; 160 161 cdata->ctrlr_id = htole16(cntlid); 162 cdata->ver = htole32(NVME_REV(1, 4)); 163 cdata->ctratt = htole32( 164 NVMEF(NVME_CTRLR_DATA_CTRATT_128BIT_HOSTID, 1) | 165 NVMEF(NVME_CTRLR_DATA_CTRATT_TBKAS, 1)); 166 cdata->cntrltype = 1; 167 cdata->acl = 3; 168 cdata->aerl = 3; 169 170 /* 1 read-only firmware slot */ 171 cdata->frmw = NVMEF(NVME_CTRLR_DATA_FRMW_SLOT1_RO, 1) | 172 NVMEF(NVME_CTRLR_DATA_FRMW_NUM_SLOTS, 1); 173 174 cdata->lpa = NVMEF(NVME_CTRLR_DATA_LPA_EXT_DATA, 1); 175 176 /* Single power state */ 177 cdata->npss = 0; 178 179 /* 180 * 1.2+ require a non-zero value for these even though it makes 181 * no sense for Fabrics. 182 */ 183 cdata->wctemp = htole16(0x0157); 184 cdata->cctemp = cdata->wctemp; 185 186 /* 1 second granularity for KeepAlive */ 187 cdata->kas = htole16(10); 188 189 cdata->sqes = NVMEF(NVME_CTRLR_DATA_SQES_MAX, 6) | 190 NVMEF(NVME_CTRLR_DATA_SQES_MIN, 6); 191 cdata->cqes = NVMEF(NVME_CTRLR_DATA_CQES_MAX, 4) | 192 NVMEF(NVME_CTRLR_DATA_CQES_MIN, 4); 193 194 cdata->maxcmd = htole16(max_io_qsize); 195 cdata->nn = htole32(nn); 196 197 cdata->vwc = 198 NVMEF(NVME_CTRLR_DATA_VWC_ALL, NVME_CTRLR_DATA_VWC_ALL_NO) | 199 NVMEM(NVME_CTRLR_DATA_VWC_PRESENT); 200 201 /* Transport-specific? */ 202 cdata->sgls = htole32( 203 NVMEF(NVME_CTRLR_DATA_SGLS_TRANSPORT_DATA_BLOCK, 1) | 204 NVMEF(NVME_CTRLR_DATA_SGLS_ADDRESS_AS_OFFSET, 1) | 205 NVMEF(NVME_CTRLR_DATA_SGLS_NVM_COMMAND_SET, 1)); 206 207 strlcpy(cdata->subnqn, subnqn, sizeof(cdata->subnqn)); 208 209 cdata->ioccsz = htole32(ioccsz / 16); 210 cdata->iorcsz = htole32(iorcsz / 16); 211 212 /* Transport-specific? */ 213 cdata->icdoff = 0; 214 215 cdata->fcatt = 0; 216 217 /* Transport-specific? */ 218 cdata->msdbd = 1; 219 } 220