1*a006a8cdSkettenis /* $OpenBSD: nvme.c,v 1.124 2024/10/08 19:41:23 kettenis Exp $ */ 2282c0692Sdlg 3282c0692Sdlg /* 4282c0692Sdlg * Copyright (c) 2014 David Gwynne <dlg@openbsd.org> 5282c0692Sdlg * 6282c0692Sdlg * Permission to use, copy, modify, and distribute this software for any 7282c0692Sdlg * purpose with or without fee is hereby granted, provided that the above 8282c0692Sdlg * copyright notice and this permission notice appear in all copies. 9282c0692Sdlg * 10282c0692Sdlg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11282c0692Sdlg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12282c0692Sdlg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13282c0692Sdlg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14282c0692Sdlg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15282c0692Sdlg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16282c0692Sdlg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17282c0692Sdlg */ 18282c0692Sdlg 197f4636ceSkrw #include "bio.h" 207f4636ceSkrw 21282c0692Sdlg #include <sys/param.h> 227f4636ceSkrw #include <sys/ioctl.h> 23282c0692Sdlg #include <sys/systm.h> 24282c0692Sdlg #include <sys/buf.h> 25282c0692Sdlg #include <sys/kernel.h> 26282c0692Sdlg #include <sys/malloc.h> 27282c0692Sdlg #include <sys/device.h> 28282c0692Sdlg #include <sys/queue.h> 29282c0692Sdlg #include <sys/mutex.h> 30282c0692Sdlg #include <sys/pool.h> 317f4636ceSkrw #include <sys/disk.h> 32282c0692Sdlg 3303d86467Sjmatthew #include <sys/atomic.h> 3403d86467Sjmatthew 35282c0692Sdlg #include <machine/bus.h> 36282c0692Sdlg 37282c0692Sdlg #include <scsi/scsi_all.h> 38c947e044Sdlg #include <scsi/scsi_disk.h> 39282c0692Sdlg #include <scsi/scsiconf.h> 407f4636ceSkrw #include <scsi/sdvar.h> 41282c0692Sdlg 427f4636ceSkrw #include <dev/biovar.h> 43448b3c09Sdlg #include <dev/ic/nvmereg.h> 44448b3c09Sdlg #include <dev/ic/nvmevar.h> 454e9514d6Skrw #include <dev/ic/nvmeio.h> 46448b3c09Sdlg 47282c0692Sdlg struct cfdriver nvme_cd = { 48282c0692Sdlg NULL, 49282c0692Sdlg "nvme", 50282c0692Sdlg DV_DULL 51282c0692Sdlg }; 52282c0692Sdlg 53282c0692Sdlg int nvme_ready(struct nvme_softc *, u_int32_t); 54ee8b2d53Skrw int nvme_enable(struct nvme_softc *); 55282c0692Sdlg int nvme_disable(struct nvme_softc *); 565313ab17Sdlg int nvme_shutdown(struct nvme_softc *); 57aa5ddcf9Ssf int nvme_resume(struct nvme_softc *); 58282c0692Sdlg 59282c0692Sdlg void nvme_dumpregs(struct nvme_softc *); 6088aa9192Sdlg int nvme_identify(struct nvme_softc *, u_int); 61e623ca5aSdlg void nvme_fill_identify(struct nvme_softc *, struct nvme_ccb *, void *); 62282c0692Sdlg 633800fc35Sjmatthew #ifndef SMALL_KERNEL 643800fc35Sjmatthew void nvme_refresh_sensors(void *); 653800fc35Sjmatthew #endif 663800fc35Sjmatthew 67448b3c09Sdlg int nvme_ccbs_alloc(struct nvme_softc *, u_int); 68a1141c40Stedu void nvme_ccbs_free(struct nvme_softc *, u_int); 69448b3c09Sdlg 70448b3c09Sdlg void * nvme_ccb_get(void *); 71448b3c09Sdlg void nvme_ccb_put(void *, void *); 72448b3c09Sdlg 73e623ca5aSdlg int nvme_poll(struct nvme_softc *, struct nvme_queue *, struct nvme_ccb *, 7454904088Skrw void (*)(struct nvme_softc *, struct nvme_ccb *, void *), u_int32_t); 75e623ca5aSdlg void nvme_poll_fill(struct nvme_softc *, struct nvme_ccb *, void *); 76e623ca5aSdlg void nvme_poll_done(struct nvme_softc *, struct nvme_ccb *, 77e623ca5aSdlg struct nvme_cqe *); 78ad9e5681Sdlg void nvme_sqe_fill(struct nvme_softc *, struct nvme_ccb *, void *); 79e623ca5aSdlg void nvme_empty_done(struct nvme_softc *, struct nvme_ccb *, 80e623ca5aSdlg struct nvme_cqe *); 81448b3c09Sdlg 82e623ca5aSdlg struct nvme_queue * 8359968badSdlg nvme_q_alloc(struct nvme_softc *, u_int16_t, u_int, u_int); 84cd15a86fSdlg int nvme_q_create(struct nvme_softc *, struct nvme_queue *); 850abee971Sdv int nvme_q_reset(struct nvme_softc *, struct nvme_queue *); 865313ab17Sdlg int nvme_q_delete(struct nvme_softc *, struct nvme_queue *); 87448b3c09Sdlg void nvme_q_submit(struct nvme_softc *, 88448b3c09Sdlg struct nvme_queue *, struct nvme_ccb *, 89e623ca5aSdlg void (*)(struct nvme_softc *, struct nvme_ccb *, void *)); 90e623ca5aSdlg int nvme_q_complete(struct nvme_softc *, struct nvme_queue *); 91e623ca5aSdlg void nvme_q_free(struct nvme_softc *, struct nvme_queue *); 92282c0692Sdlg 9311624e4cSdlg void nvme_scsi_cmd(struct scsi_xfer *); 94767e8532Skrw void nvme_minphys(struct buf *, struct scsi_link *); 9511624e4cSdlg int nvme_scsi_probe(struct scsi_link *); 9611624e4cSdlg void nvme_scsi_free(struct scsi_link *); 977f4636ceSkrw uint64_t nvme_scsi_size(const struct nvm_identify_namespace *); 984e9514d6Skrw int nvme_scsi_ioctl(struct scsi_link *, u_long, caddr_t, int); 994e9514d6Skrw int nvme_passthrough_cmd(struct nvme_softc *, struct nvme_pt_cmd *, 1004e9514d6Skrw int, int); 10111624e4cSdlg 10203d86467Sjmatthew #ifdef HIBERNATE 10303d86467Sjmatthew #include <uvm/uvm_extern.h> 10403d86467Sjmatthew #include <sys/hibernate.h> 10503d86467Sjmatthew #include <sys/disklabel.h> 10603d86467Sjmatthew 10703d86467Sjmatthew int nvme_hibernate_io(dev_t, daddr_t, vaddr_t, size_t, int, void *); 10803d86467Sjmatthew #endif 10903d86467Sjmatthew 1107f4636ceSkrw #if NBIO > 0 1117f4636ceSkrw void nvme_bio_status(struct bio_status *, const char *, ...); 1127f4636ceSkrw 1137f4636ceSkrw const char *nvme_bioctl_sdname(const struct nvme_softc *, int); 1147f4636ceSkrw 1157f4636ceSkrw int nvme_bioctl(struct device *, u_long, caddr_t); 1167f4636ceSkrw int nvme_bioctl_inq(struct nvme_softc *, struct bioc_inq *); 1177f4636ceSkrw int nvme_bioctl_vol(struct nvme_softc *, struct bioc_vol *); 1187f4636ceSkrw int nvme_bioctl_disk(struct nvme_softc *, struct bioc_disk *); 1197f4636ceSkrw #endif /* NBIO > 0 */ 1207f4636ceSkrw 121a454aff3Snaddy const struct scsi_adapter nvme_switch = { 1224e9514d6Skrw nvme_scsi_cmd, nvme_minphys, nvme_scsi_probe, nvme_scsi_free, 1234e9514d6Skrw nvme_scsi_ioctl 12411624e4cSdlg }; 12511624e4cSdlg 1261eef25a1Sdlg void nvme_scsi_io(struct scsi_xfer *, int); 1271eef25a1Sdlg void nvme_scsi_io_fill(struct nvme_softc *, struct nvme_ccb *, void *); 128a1e87500Sdlg void nvme_scsi_io_done(struct nvme_softc *, struct nvme_ccb *, 129a1e87500Sdlg struct nvme_cqe *); 130a1e87500Sdlg 131689c2c68Sdlg void nvme_scsi_sync(struct scsi_xfer *); 132689c2c68Sdlg void nvme_scsi_sync_fill(struct nvme_softc *, struct nvme_ccb *, void *); 133689c2c68Sdlg void nvme_scsi_sync_done(struct nvme_softc *, struct nvme_ccb *, 134689c2c68Sdlg struct nvme_cqe *); 135689c2c68Sdlg 1369856392eSdlg void nvme_scsi_inq(struct scsi_xfer *); 1379856392eSdlg void nvme_scsi_inquiry(struct scsi_xfer *); 138c947e044Sdlg void nvme_scsi_capacity16(struct scsi_xfer *); 139c947e044Sdlg void nvme_scsi_capacity(struct scsi_xfer *); 1409856392eSdlg 1417599295eSdlg uint32_t nvme_op_sq_enter(struct nvme_softc *, 1427599295eSdlg struct nvme_queue *, struct nvme_ccb *); 1437599295eSdlg void nvme_op_sq_leave(struct nvme_softc *, 1447599295eSdlg struct nvme_queue *, struct nvme_ccb *); 1457599295eSdlg uint32_t nvme_op_sq_enter_locked(struct nvme_softc *, 1467599295eSdlg struct nvme_queue *, struct nvme_ccb *); 1477599295eSdlg void nvme_op_sq_leave_locked(struct nvme_softc *, 1487599295eSdlg struct nvme_queue *, struct nvme_ccb *); 1497599295eSdlg 1507599295eSdlg void nvme_op_cq_done(struct nvme_softc *, 1517599295eSdlg struct nvme_queue *, struct nvme_ccb *); 1527599295eSdlg 1537599295eSdlg static const struct nvme_ops nvme_ops = { 1547599295eSdlg .op_sq_enter = nvme_op_sq_enter, 1557599295eSdlg .op_sq_leave = nvme_op_sq_leave, 1567599295eSdlg .op_sq_enter_locked = nvme_op_sq_enter_locked, 1577599295eSdlg .op_sq_leave_locked = nvme_op_sq_leave_locked, 1587599295eSdlg 1597599295eSdlg .op_cq_done = nvme_op_cq_done, 1607599295eSdlg }; 1617599295eSdlg 16254904088Skrw #define NVME_TIMO_QOP 5000 /* ms to create/delete queue */ 1631488c4e9Skrw #define NVME_TIMO_PT 5000 /* ms to complete passthrough */ 16454904088Skrw #define NVME_TIMO_IDENT 10000 /* ms to probe/identify */ 1653800fc35Sjmatthew #define NVME_TIMO_LOG_PAGE 5000 /* ms to read log pages */ 16654904088Skrw #define NVME_TIMO_DELAYNS 10 /* ns to delay() in poll loop */ 16754904088Skrw 168343e9e5aSmpi /* 169343e9e5aSmpi * Some controllers, at least Apple NVMe, always require split 170343e9e5aSmpi * transfers, so don't use bus_space_{read,write}_8() on LP64. 171343e9e5aSmpi */ 172a30550dbSdlg u_int64_t 173282c0692Sdlg nvme_read8(struct nvme_softc *sc, bus_size_t r) 174282c0692Sdlg { 175282c0692Sdlg u_int64_t v; 176282c0692Sdlg 1777bd1ac27Sdlg v = (u_int64_t)nvme_read4(sc, r) | 1787bd1ac27Sdlg (u_int64_t)nvme_read4(sc, r + 4) << 32; 179282c0692Sdlg 180282c0692Sdlg return (v); 181282c0692Sdlg } 182282c0692Sdlg 183a30550dbSdlg void 184282c0692Sdlg nvme_write8(struct nvme_softc *sc, bus_size_t r, u_int64_t v) 185282c0692Sdlg { 1867bd1ac27Sdlg nvme_write4(sc, r, v); 1877bd1ac27Sdlg nvme_write4(sc, r + 4, v >> 32); 188282c0692Sdlg } 189282c0692Sdlg 190282c0692Sdlg void 191282c0692Sdlg nvme_dumpregs(struct nvme_softc *sc) 192282c0692Sdlg { 193282c0692Sdlg u_int64_t r8; 194282c0692Sdlg u_int32_t r4; 195282c0692Sdlg 196282c0692Sdlg r8 = nvme_read8(sc, NVME_CAP); 197282c0692Sdlg printf("%s: cap 0x%016llx\n", DEVNAME(sc), nvme_read8(sc, NVME_CAP)); 198282c0692Sdlg printf("%s: mpsmax %u (%u)\n", DEVNAME(sc), 199282c0692Sdlg (u_int)NVME_CAP_MPSMAX(r8), (1 << NVME_CAP_MPSMAX(r8))); 200282c0692Sdlg printf("%s: mpsmin %u (%u)\n", DEVNAME(sc), 201282c0692Sdlg (u_int)NVME_CAP_MPSMIN(r8), (1 << NVME_CAP_MPSMIN(r8))); 202282c0692Sdlg printf("%s: css %llu\n", DEVNAME(sc), NVME_CAP_CSS(r8)); 203282c0692Sdlg printf("%s: nssrs %llu\n", DEVNAME(sc), NVME_CAP_NSSRS(r8)); 204a7f13332Sdlg printf("%s: dstrd %u\n", DEVNAME(sc), NVME_CAP_DSTRD(r8)); 205282c0692Sdlg printf("%s: to %llu msec\n", DEVNAME(sc), NVME_CAP_TO(r8)); 206282c0692Sdlg printf("%s: ams %llu\n", DEVNAME(sc), NVME_CAP_AMS(r8)); 207282c0692Sdlg printf("%s: cqr %llu\n", DEVNAME(sc), NVME_CAP_CQR(r8)); 208282c0692Sdlg printf("%s: mqes %llu\n", DEVNAME(sc), NVME_CAP_MQES(r8)); 209282c0692Sdlg 210a7f13332Sdlg printf("%s: vs 0x%04x\n", DEVNAME(sc), nvme_read4(sc, NVME_VS)); 211282c0692Sdlg 212282c0692Sdlg r4 = nvme_read4(sc, NVME_CC); 213a7f13332Sdlg printf("%s: cc 0x%04x\n", DEVNAME(sc), r4); 214282c0692Sdlg printf("%s: iocqes %u\n", DEVNAME(sc), NVME_CC_IOCQES_R(r4)); 215282c0692Sdlg printf("%s: iosqes %u\n", DEVNAME(sc), NVME_CC_IOSQES_R(r4)); 216282c0692Sdlg printf("%s: shn %u\n", DEVNAME(sc), NVME_CC_SHN_R(r4)); 217282c0692Sdlg printf("%s: ams %u\n", DEVNAME(sc), NVME_CC_AMS_R(r4)); 218282c0692Sdlg printf("%s: mps %u\n", DEVNAME(sc), NVME_CC_MPS_R(r4)); 219282c0692Sdlg printf("%s: css %u\n", DEVNAME(sc), NVME_CC_CSS_R(r4)); 220282c0692Sdlg printf("%s: en %u\n", DEVNAME(sc), ISSET(r4, NVME_CC_EN)); 221282c0692Sdlg 222a7f13332Sdlg printf("%s: csts 0x%08x\n", DEVNAME(sc), nvme_read4(sc, NVME_CSTS)); 223a7f13332Sdlg printf("%s: aqa 0x%08x\n", DEVNAME(sc), nvme_read4(sc, NVME_AQA)); 224282c0692Sdlg printf("%s: asq 0x%016llx\n", DEVNAME(sc), nvme_read8(sc, NVME_ASQ)); 225282c0692Sdlg printf("%s: acq 0x%016llx\n", DEVNAME(sc), nvme_read8(sc, NVME_ACQ)); 226282c0692Sdlg } 227282c0692Sdlg 228282c0692Sdlg int 229282c0692Sdlg nvme_ready(struct nvme_softc *sc, u_int32_t rdy) 230282c0692Sdlg { 2319fb4d6d3Skrw u_int i = 0; 232282c0692Sdlg 2339fb4d6d3Skrw while ((nvme_read4(sc, NVME_CSTS) & NVME_CSTS_RDY) != rdy) { 2349fb4d6d3Skrw if (i++ > sc->sc_rdy_to) 2359fb4d6d3Skrw return (1); 236282c0692Sdlg 237282c0692Sdlg delay(1000); 238282c0692Sdlg nvme_barrier(sc, NVME_CSTS, 4, BUS_SPACE_BARRIER_READ); 239282c0692Sdlg } 240282c0692Sdlg 2419fb4d6d3Skrw return (0); 242282c0692Sdlg } 243282c0692Sdlg 244282c0692Sdlg int 245ee8b2d53Skrw nvme_enable(struct nvme_softc *sc) 246282c0692Sdlg { 2479fb4d6d3Skrw u_int32_t cc; 248282c0692Sdlg 249282c0692Sdlg cc = nvme_read4(sc, NVME_CC); 2509fb4d6d3Skrw if (ISSET(cc, NVME_CC_EN)) 251282c0692Sdlg return (nvme_ready(sc, NVME_CSTS_RDY)); 252282c0692Sdlg 2537599295eSdlg if (sc->sc_ops->op_enable != NULL) 2547599295eSdlg sc->sc_ops->op_enable(sc); 2557599295eSdlg 25683d8579cSdlg nvme_write4(sc, NVME_AQA, NVME_AQA_ACQS(sc->sc_admin_q->q_entries) | 25783d8579cSdlg NVME_AQA_ASQS(sc->sc_admin_q->q_entries)); 25883d8579cSdlg nvme_barrier(sc, 0, sc->sc_ios, BUS_SPACE_BARRIER_WRITE); 25983d8579cSdlg 260282c0692Sdlg nvme_write8(sc, NVME_ASQ, NVME_DMA_DVA(sc->sc_admin_q->q_sq_dmamem)); 261282c0692Sdlg nvme_barrier(sc, 0, sc->sc_ios, BUS_SPACE_BARRIER_WRITE); 262282c0692Sdlg nvme_write8(sc, NVME_ACQ, NVME_DMA_DVA(sc->sc_admin_q->q_cq_dmamem)); 263282c0692Sdlg nvme_barrier(sc, 0, sc->sc_ios, BUS_SPACE_BARRIER_WRITE); 264282c0692Sdlg 265282c0692Sdlg CLR(cc, NVME_CC_IOCQES_MASK | NVME_CC_IOSQES_MASK | NVME_CC_SHN_MASK | 266282c0692Sdlg NVME_CC_AMS_MASK | NVME_CC_MPS_MASK | NVME_CC_CSS_MASK); 2670f19ad86Skrw SET(cc, NVME_CC_IOSQES(6)); /* Submission queue size == 2**6 (64) */ 2680f19ad86Skrw SET(cc, NVME_CC_IOCQES(4)); /* Completion queue size == 2**4 (16) */ 269282c0692Sdlg SET(cc, NVME_CC_SHN(NVME_CC_SHN_NONE)); 270282c0692Sdlg SET(cc, NVME_CC_CSS(NVME_CC_CSS_NVM)); 271282c0692Sdlg SET(cc, NVME_CC_AMS(NVME_CC_AMS_RR)); 2720f19ad86Skrw SET(cc, NVME_CC_MPS(ffs(sc->sc_mps) - 1)); 273282c0692Sdlg SET(cc, NVME_CC_EN); 274282c0692Sdlg 275282c0692Sdlg nvme_write4(sc, NVME_CC, cc); 276282c0692Sdlg nvme_barrier(sc, 0, sc->sc_ios, 277282c0692Sdlg BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 278282c0692Sdlg 27983d8579cSdlg return (nvme_ready(sc, NVME_CSTS_RDY)); 280282c0692Sdlg } 281282c0692Sdlg 282282c0692Sdlg int 283282c0692Sdlg nvme_disable(struct nvme_softc *sc) 284282c0692Sdlg { 285282c0692Sdlg u_int32_t cc, csts; 286282c0692Sdlg 287282c0692Sdlg cc = nvme_read4(sc, NVME_CC); 2889fb4d6d3Skrw if (ISSET(cc, NVME_CC_EN)) { 289282c0692Sdlg csts = nvme_read4(sc, NVME_CSTS); 2909fb4d6d3Skrw if (!ISSET(csts, NVME_CSTS_CFS) && 2919fb4d6d3Skrw nvme_ready(sc, NVME_CSTS_RDY) != 0) 292282c0692Sdlg return (1); 293282c0692Sdlg } 294282c0692Sdlg 295282c0692Sdlg CLR(cc, NVME_CC_EN); 296282c0692Sdlg 297282c0692Sdlg nvme_write4(sc, NVME_CC, cc); 298282c0692Sdlg nvme_barrier(sc, 0, sc->sc_ios, 299282c0692Sdlg BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 300282c0692Sdlg 301282c0692Sdlg return (nvme_ready(sc, 0)); 302282c0692Sdlg } 303282c0692Sdlg 304282c0692Sdlg int 305282c0692Sdlg nvme_attach(struct nvme_softc *sc) 306282c0692Sdlg { 30701b059acSdlg struct scsibus_attach_args saa; 308282c0692Sdlg u_int64_t cap; 309282c0692Sdlg u_int32_t reg; 310a1141c40Stedu u_int nccbs = 0; 311282c0692Sdlg 3123f4555f0Sdlg mtx_init(&sc->sc_ccb_mtx, IPL_BIO); 3137f4636ceSkrw rw_init(&sc->sc_lock, "nvme_lock"); 3143f4555f0Sdlg SIMPLEQ_INIT(&sc->sc_ccb_list); 3153f4555f0Sdlg scsi_iopool_init(&sc->sc_iopool, sc, nvme_ccb_get, nvme_ccb_put); 3167599295eSdlg if (sc->sc_ops == NULL) 3177599295eSdlg sc->sc_ops = &nvme_ops; 318e9317b4bSkettenis if (sc->sc_openings == 0) 319e9317b4bSkettenis sc->sc_openings = 64; 3203f4555f0Sdlg 321282c0692Sdlg reg = nvme_read4(sc, NVME_VS); 32298f39564Skrw if (reg == 0xffffffff) { 3237317d3e7Sdlg printf("invalid mapping\n"); 324282c0692Sdlg return (1); 325282c0692Sdlg } 326282c0692Sdlg 3277317d3e7Sdlg printf("NVMe %d.%d\n", NVME_VS_MJR(reg), NVME_VS_MNR(reg)); 328282c0692Sdlg 329282c0692Sdlg cap = nvme_read8(sc, NVME_CAP); 330aa5ddcf9Ssf sc->sc_dstrd = NVME_CAP_DSTRD(cap); 3311be1268fSdlg if (NVME_CAP_MPSMIN(cap) > PAGE_SHIFT) { 3321be1268fSdlg printf("%s: NVMe minimum page size %u " 3331be1268fSdlg "is greater than CPU page size %u\n", DEVNAME(sc), 3341be1268fSdlg 1 << NVME_CAP_MPSMIN(cap), 1 << PAGE_SHIFT); 3351be1268fSdlg return (1); 3361be1268fSdlg } 33794a36263Skmos if (NVME_CAP_MPSMAX(cap) < PAGE_SHIFT) 33894a36263Skmos sc->sc_mps = 1 << NVME_CAP_MPSMAX(cap); 33994a36263Skmos else 34094a36263Skmos sc->sc_mps = 1 << PAGE_SHIFT; 341282c0692Sdlg 342282c0692Sdlg sc->sc_rdy_to = NVME_CAP_TO(cap); 343448b3c09Sdlg sc->sc_mdts = MAXPHYS; 344a8751b7cSkrw sc->sc_max_prpl = sc->sc_mdts / sc->sc_mps; 3453f4555f0Sdlg 3463f4555f0Sdlg if (nvme_disable(sc) != 0) { 3473f4555f0Sdlg printf("%s: unable to disable controller\n", DEVNAME(sc)); 3483f4555f0Sdlg return (1); 3493f4555f0Sdlg } 350282c0692Sdlg 351aa5ddcf9Ssf sc->sc_admin_q = nvme_q_alloc(sc, NVME_ADMIN_Q, 128, sc->sc_dstrd); 352282c0692Sdlg if (sc->sc_admin_q == NULL) { 353282c0692Sdlg printf("%s: unable to allocate admin queue\n", DEVNAME(sc)); 354282c0692Sdlg return (1); 355282c0692Sdlg } 356282c0692Sdlg 357448b3c09Sdlg if (nvme_ccbs_alloc(sc, 16) != 0) { 358448b3c09Sdlg printf("%s: unable to allocate initial ccbs\n", DEVNAME(sc)); 359282c0692Sdlg goto free_admin_q; 360282c0692Sdlg } 361a1141c40Stedu nccbs = 16; 362282c0692Sdlg 363ee8b2d53Skrw if (nvme_enable(sc) != 0) { 364448b3c09Sdlg printf("%s: unable to enable controller\n", DEVNAME(sc)); 365448b3c09Sdlg goto free_ccbs; 366448b3c09Sdlg } 367448b3c09Sdlg 36888aa9192Sdlg if (nvme_identify(sc, NVME_CAP_MPSMIN(cap)) != 0) { 369448b3c09Sdlg printf("%s: unable to identify controller\n", DEVNAME(sc)); 370448b3c09Sdlg goto disable; 371448b3c09Sdlg } 372282c0692Sdlg 373a8751b7cSkrw /* We now know the real values of sc_mdts and sc_max_prpl. */ 374a1141c40Stedu nvme_ccbs_free(sc, nccbs); 3751926545cSdlg if (nvme_ccbs_alloc(sc, 64) != 0) { 3761926545cSdlg printf("%s: unable to allocate ccbs\n", DEVNAME(sc)); 3771926545cSdlg goto free_admin_q; 3781926545cSdlg } 379a1141c40Stedu nccbs = 64; 3801926545cSdlg 38103d86467Sjmatthew sc->sc_q = nvme_q_alloc(sc, NVME_IO_Q, 128, sc->sc_dstrd); 38260c144b3Sdlg if (sc->sc_q == NULL) { 38360c144b3Sdlg printf("%s: unable to allocate io q\n", DEVNAME(sc)); 38460c144b3Sdlg goto disable; 38560c144b3Sdlg } 38660c144b3Sdlg 38760c144b3Sdlg if (nvme_q_create(sc, sc->sc_q) != 0) { 38860c144b3Sdlg printf("%s: unable to create io q\n", DEVNAME(sc)); 38960c144b3Sdlg goto free_q; 39060c144b3Sdlg } 39160c144b3Sdlg 3924d4525deSkettenis #ifdef HIBERNATE 39303d86467Sjmatthew sc->sc_hib_q = nvme_q_alloc(sc, NVME_HIB_Q, 4, sc->sc_dstrd); 39403d86467Sjmatthew if (sc->sc_hib_q == NULL) { 39503d86467Sjmatthew printf("%s: unable to allocate hibernate io queue\n", DEVNAME(sc)); 39689715fd5Sjsg goto free_q; 39703d86467Sjmatthew } 3984d4525deSkettenis #endif 39903d86467Sjmatthew 400ffeaebd1Sdlg nvme_write4(sc, NVME_INTMC, 1); 401ffeaebd1Sdlg 402397f5692Skettenis sc->sc_namespaces = mallocarray(sc->sc_nn + 1, 403397f5692Skettenis sizeof(*sc->sc_namespaces), M_DEVBUF, M_WAITOK|M_ZERO); 404bc407f60Sdlg 405ead808c4Skrw saa.saa_adapter = &nvme_switch; 406ead808c4Skrw saa.saa_adapter_softc = sc; 407ead808c4Skrw saa.saa_adapter_buswidth = sc->sc_nn + 1; 408ead808c4Skrw saa.saa_luns = 1; 409ead808c4Skrw saa.saa_adapter_target = 0; 410e9317b4bSkettenis saa.saa_openings = sc->sc_openings; 411e5eae15dSkrw saa.saa_pool = &sc->sc_iopool; 412e5eae15dSkrw saa.saa_quirks = saa.saa_flags = 0; 413e5eae15dSkrw saa.saa_wwpn = saa.saa_wwnn = 0; 41401b059acSdlg 4153800fc35Sjmatthew strlcpy(sc->sc_sensordev.xname, DEVNAME(sc), sizeof(sc->sc_sensordev.xname)); 4163800fc35Sjmatthew 4173800fc35Sjmatthew #ifndef SMALL_KERNEL 4183800fc35Sjmatthew sc->sc_temp_sensor.type = SENSOR_TEMP; 4193800fc35Sjmatthew sc->sc_temp_sensor.status = SENSOR_S_UNKNOWN; 4203800fc35Sjmatthew sensor_attach(&sc->sc_sensordev, &sc->sc_temp_sensor); 4213800fc35Sjmatthew 4223800fc35Sjmatthew sc->sc_usage_sensor.type = SENSOR_PERCENT; 4233800fc35Sjmatthew sc->sc_usage_sensor.status = SENSOR_S_UNKNOWN; 4243800fc35Sjmatthew strlcpy(sc->sc_usage_sensor.desc, "endurance used", 4253800fc35Sjmatthew sizeof(sc->sc_usage_sensor.desc)); 4263800fc35Sjmatthew sensor_attach(&sc->sc_sensordev, &sc->sc_usage_sensor); 4273800fc35Sjmatthew 4283800fc35Sjmatthew sc->sc_spare_sensor.type = SENSOR_PERCENT; 4293800fc35Sjmatthew sc->sc_spare_sensor.status = SENSOR_S_UNKNOWN; 4303800fc35Sjmatthew strlcpy(sc->sc_spare_sensor.desc, "available spare", 4313800fc35Sjmatthew sizeof(sc->sc_spare_sensor.desc)); 4323800fc35Sjmatthew sensor_attach(&sc->sc_sensordev, &sc->sc_spare_sensor); 4333800fc35Sjmatthew 4343800fc35Sjmatthew if (sensor_task_register(sc, nvme_refresh_sensors, 60) == NULL) 4353800fc35Sjmatthew goto free_q; 4363800fc35Sjmatthew 4373800fc35Sjmatthew sensordev_install(&sc->sc_sensordev); 4383800fc35Sjmatthew #endif 4393800fc35Sjmatthew 4407f4636ceSkrw sc->sc_scsibus = (struct scsibus_softc *)config_found(&sc->sc_dev, 4417f4636ceSkrw &saa, scsiprint); 4427f4636ceSkrw #if NBIO > 0 4437f4636ceSkrw if (bio_register(&sc->sc_dev, nvme_bioctl) != 0) 4447f4636ceSkrw printf("%s: unable to register bioctl\n", DEVNAME(sc)); 4457f4636ceSkrw #endif /* NBIO > 0 */ 44601b059acSdlg 447282c0692Sdlg return (0); 448282c0692Sdlg 44960c144b3Sdlg free_q: 45060c144b3Sdlg nvme_q_free(sc, sc->sc_q); 451448b3c09Sdlg disable: 452448b3c09Sdlg nvme_disable(sc); 453448b3c09Sdlg free_ccbs: 454a1141c40Stedu nvme_ccbs_free(sc, nccbs); 455282c0692Sdlg free_admin_q: 456448b3c09Sdlg nvme_q_free(sc, sc->sc_admin_q); 457282c0692Sdlg 458282c0692Sdlg return (1); 459282c0692Sdlg } 460282c0692Sdlg 46111624e4cSdlg int 462aa5ddcf9Ssf nvme_resume(struct nvme_softc *sc) 463aa5ddcf9Ssf { 464aa5ddcf9Ssf if (nvme_disable(sc) != 0) { 465aa5ddcf9Ssf printf("%s: unable to disable controller\n", DEVNAME(sc)); 466aa5ddcf9Ssf return (1); 467aa5ddcf9Ssf } 468aa5ddcf9Ssf 4690abee971Sdv if (nvme_q_reset(sc, sc->sc_admin_q) != 0) { 4700abee971Sdv printf("%s: unable to reset admin queue\n", DEVNAME(sc)); 4710abee971Sdv return (1); 4720abee971Sdv } 473aa5ddcf9Ssf 474ee8b2d53Skrw if (nvme_enable(sc) != 0) { 475aa5ddcf9Ssf printf("%s: unable to enable controller\n", DEVNAME(sc)); 476aa5ddcf9Ssf return (1); 477aa5ddcf9Ssf } 478aa5ddcf9Ssf 4790abee971Sdv sc->sc_q = nvme_q_alloc(sc, NVME_IO_Q, 128, sc->sc_dstrd); 4800abee971Sdv if (sc->sc_q == NULL) { 4810abee971Sdv printf("%s: unable to allocate io q\n", DEVNAME(sc)); 4820abee971Sdv goto disable; 4830abee971Sdv } 4840abee971Sdv 485aa5ddcf9Ssf if (nvme_q_create(sc, sc->sc_q) != 0) { 486aa5ddcf9Ssf printf("%s: unable to create io q\n", DEVNAME(sc)); 4870abee971Sdv goto free_q; 488aa5ddcf9Ssf } 489aa5ddcf9Ssf 490aa5ddcf9Ssf nvme_write4(sc, NVME_INTMC, 1); 491aa5ddcf9Ssf 492aa5ddcf9Ssf return (0); 493aa5ddcf9Ssf 4940abee971Sdv free_q: 4950abee971Sdv nvme_q_free(sc, sc->sc_q); 496aa5ddcf9Ssf disable: 497aa5ddcf9Ssf nvme_disable(sc); 498aa5ddcf9Ssf 499aa5ddcf9Ssf return (1); 500aa5ddcf9Ssf } 501aa5ddcf9Ssf 502aa5ddcf9Ssf int 50311624e4cSdlg nvme_scsi_probe(struct scsi_link *link) 50411624e4cSdlg { 5050b29cb40Skrw struct nvme_softc *sc = link->bus->sb_adapter_softc; 50665dd51f8Sdlg struct nvme_sqe sqe; 50765dd51f8Sdlg struct nvm_identify_namespace *identify; 50865dd51f8Sdlg struct nvme_dmamem *mem; 50965dd51f8Sdlg struct nvme_ccb *ccb; 51065dd51f8Sdlg int rv; 51165dd51f8Sdlg 51265dd51f8Sdlg ccb = scsi_io_get(&sc->sc_iopool, 0); 51365dd51f8Sdlg KASSERT(ccb != NULL); 51465dd51f8Sdlg 51565dd51f8Sdlg mem = nvme_dmamem_alloc(sc, sizeof(*identify)); 51665dd51f8Sdlg if (mem == NULL) 51765dd51f8Sdlg return (ENOMEM); 51865dd51f8Sdlg 51965dd51f8Sdlg memset(&sqe, 0, sizeof(sqe)); 52065dd51f8Sdlg sqe.opcode = NVM_ADMIN_IDENTIFY; 521397f5692Skettenis htolem32(&sqe.nsid, link->target); 52265dd51f8Sdlg htolem64(&sqe.entry.prp[0], NVME_DMA_DVA(mem)); 52365dd51f8Sdlg htolem32(&sqe.cdw10, 0); 52465dd51f8Sdlg 52565dd51f8Sdlg ccb->ccb_done = nvme_empty_done; 52665dd51f8Sdlg ccb->ccb_cookie = &sqe; 52765dd51f8Sdlg 52865dd51f8Sdlg nvme_dmamem_sync(sc, mem, BUS_DMASYNC_PREREAD); 52954904088Skrw rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_IDENT); 53065dd51f8Sdlg nvme_dmamem_sync(sc, mem, BUS_DMASYNC_POSTREAD); 53165dd51f8Sdlg 53265dd51f8Sdlg scsi_io_put(&sc->sc_iopool, ccb); 53365dd51f8Sdlg 5343f48a60eSkrw identify = NVME_DMA_KVA(mem); 535badf3925Sjan if (rv == 0) { 536daef0c50Skrw if (nvme_scsi_size(identify) > 0) { 5373f48a60eSkrw /* Commit namespace if it has a size greater than zero. */ 5383f48a60eSkrw identify = malloc(sizeof(*identify), M_DEVBUF, M_WAITOK); 5393f48a60eSkrw memcpy(identify, NVME_DMA_KVA(mem), sizeof(*identify)); 5403f48a60eSkrw sc->sc_namespaces[link->target].ident = identify; 541badf3925Sjan } else { 542badf3925Sjan /* Don't attach a namespace if its size is zero. */ 543badf3925Sjan rv = ENXIO; 544badf3925Sjan } 54565dd51f8Sdlg } 54665dd51f8Sdlg 54765dd51f8Sdlg nvme_dmamem_free(sc, mem); 54865dd51f8Sdlg 54965dd51f8Sdlg return (rv); 55011624e4cSdlg } 55111624e4cSdlg 5525313ab17Sdlg int 5535313ab17Sdlg nvme_shutdown(struct nvme_softc *sc) 5545313ab17Sdlg { 5555313ab17Sdlg u_int32_t cc, csts; 5565313ab17Sdlg int i; 5575313ab17Sdlg 5585313ab17Sdlg nvme_write4(sc, NVME_INTMC, 0); 5595313ab17Sdlg 5605313ab17Sdlg if (nvme_q_delete(sc, sc->sc_q) != 0) { 5615313ab17Sdlg printf("%s: unable to delete q, disabling\n", DEVNAME(sc)); 5625313ab17Sdlg goto disable; 5635313ab17Sdlg } 5645313ab17Sdlg 5655313ab17Sdlg cc = nvme_read4(sc, NVME_CC); 5665313ab17Sdlg CLR(cc, NVME_CC_SHN_MASK); 5675313ab17Sdlg SET(cc, NVME_CC_SHN(NVME_CC_SHN_NORMAL)); 5685313ab17Sdlg nvme_write4(sc, NVME_CC, cc); 5695313ab17Sdlg 5705313ab17Sdlg for (i = 0; i < 4000; i++) { 5715313ab17Sdlg nvme_barrier(sc, 0, sc->sc_ios, 5725313ab17Sdlg BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 5735313ab17Sdlg csts = nvme_read4(sc, NVME_CSTS); 5745313ab17Sdlg if ((csts & NVME_CSTS_SHST_MASK) == NVME_CSTS_SHST_DONE) 5755313ab17Sdlg return (0); 5765313ab17Sdlg 5775313ab17Sdlg delay(1000); 5785313ab17Sdlg } 5795313ab17Sdlg 5807f0b5f10Sderaadt printf("%s: unable to shutdown, disabling\n", DEVNAME(sc)); 5815313ab17Sdlg 5825313ab17Sdlg disable: 5835313ab17Sdlg nvme_disable(sc); 5845313ab17Sdlg return (0); 5855313ab17Sdlg } 5865313ab17Sdlg 5875313ab17Sdlg int 5885313ab17Sdlg nvme_activate(struct nvme_softc *sc, int act) 5895313ab17Sdlg { 5905313ab17Sdlg int rv; 5915313ab17Sdlg 5925313ab17Sdlg switch (act) { 5935313ab17Sdlg case DVACT_POWERDOWN: 5945313ab17Sdlg rv = config_activate_children(&sc->sc_dev, act); 5955313ab17Sdlg nvme_shutdown(sc); 5965313ab17Sdlg break; 597aa5ddcf9Ssf case DVACT_RESUME: 598aa5ddcf9Ssf rv = nvme_resume(sc); 599aa5ddcf9Ssf if (rv == 0) 600aa5ddcf9Ssf rv = config_activate_children(&sc->sc_dev, act); 601aa5ddcf9Ssf break; 6025313ab17Sdlg default: 6035313ab17Sdlg rv = config_activate_children(&sc->sc_dev, act); 6045313ab17Sdlg break; 6055313ab17Sdlg } 6065313ab17Sdlg 6075313ab17Sdlg return (rv); 6085313ab17Sdlg } 6095313ab17Sdlg 61011624e4cSdlg void 61111624e4cSdlg nvme_scsi_cmd(struct scsi_xfer *xs) 61211624e4cSdlg { 613664c6166Skrw switch (xs->cmd.opcode) { 614a1e87500Sdlg case READ_COMMAND: 615eccd596dSkrw case READ_10: 616a1e87500Sdlg case READ_12: 617a1e87500Sdlg case READ_16: 6181eef25a1Sdlg nvme_scsi_io(xs, SCSI_DATA_IN); 619a1e87500Sdlg return; 620a1e87500Sdlg case WRITE_COMMAND: 621eccd596dSkrw case WRITE_10: 622a1e87500Sdlg case WRITE_12: 623a1e87500Sdlg case WRITE_16: 6241eef25a1Sdlg nvme_scsi_io(xs, SCSI_DATA_OUT); 625d3f19a0bSdlg return; 626a1e87500Sdlg 627689c2c68Sdlg case SYNCHRONIZE_CACHE: 628689c2c68Sdlg nvme_scsi_sync(xs); 629689c2c68Sdlg return; 630689c2c68Sdlg 6319856392eSdlg case INQUIRY: 6329856392eSdlg nvme_scsi_inq(xs); 6339856392eSdlg return; 634c947e044Sdlg case READ_CAPACITY_16: 635c947e044Sdlg nvme_scsi_capacity16(xs); 636c947e044Sdlg return; 637c947e044Sdlg case READ_CAPACITY: 638c947e044Sdlg nvme_scsi_capacity(xs); 639c947e044Sdlg return; 640c947e044Sdlg 641a50b57eaSdlg case TEST_UNIT_READY: 642a50b57eaSdlg case PREVENT_ALLOW: 643a50b57eaSdlg case START_STOP: 644a50b57eaSdlg xs->error = XS_NOERROR; 645a50b57eaSdlg scsi_done(xs); 646a50b57eaSdlg return; 647a50b57eaSdlg 6489856392eSdlg default: 6499856392eSdlg break; 6509856392eSdlg } 6519856392eSdlg 65211624e4cSdlg xs->error = XS_DRIVER_STUFFUP; 65311624e4cSdlg scsi_done(xs); 65411624e4cSdlg } 65511624e4cSdlg 65611624e4cSdlg void 657767e8532Skrw nvme_minphys(struct buf *bp, struct scsi_link *link) 658767e8532Skrw { 6590b29cb40Skrw struct nvme_softc *sc = link->bus->sb_adapter_softc; 660767e8532Skrw 661767e8532Skrw if (bp->b_bcount > sc->sc_mdts) 662767e8532Skrw bp->b_bcount = sc->sc_mdts; 663767e8532Skrw } 664767e8532Skrw 665767e8532Skrw void 6661eef25a1Sdlg nvme_scsi_io(struct scsi_xfer *xs, int dir) 667a1e87500Sdlg { 668a1e87500Sdlg struct scsi_link *link = xs->sc_link; 6690b29cb40Skrw struct nvme_softc *sc = link->bus->sb_adapter_softc; 670a1e87500Sdlg struct nvme_ccb *ccb = xs->io; 671a1e87500Sdlg bus_dmamap_t dmap = ccb->ccb_dmamap; 672ecfae151Sdlg int i; 673a1e87500Sdlg 6741eef25a1Sdlg if ((xs->flags & (SCSI_DATA_IN|SCSI_DATA_OUT)) != dir) 6751eef25a1Sdlg goto stuffup; 6761eef25a1Sdlg 677a1e87500Sdlg ccb->ccb_done = nvme_scsi_io_done; 678a1e87500Sdlg ccb->ccb_cookie = xs; 679a1e87500Sdlg 680a1e87500Sdlg if (bus_dmamap_load(sc->sc_dmat, dmap, 681a1e87500Sdlg xs->data, xs->datalen, NULL, ISSET(xs->flags, SCSI_NOSLEEP) ? 682a1e87500Sdlg BUS_DMA_NOWAIT : BUS_DMA_WAITOK) != 0) 683a1e87500Sdlg goto stuffup; 684a1e87500Sdlg 685a1e87500Sdlg bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize, 686a1e87500Sdlg ISSET(xs->flags, SCSI_DATA_IN) ? 687a1e87500Sdlg BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); 688a1e87500Sdlg 689ecfae151Sdlg if (dmap->dm_nsegs > 2) { 690ecfae151Sdlg for (i = 1; i < dmap->dm_nsegs; i++) { 691ecfae151Sdlg htolem64(&ccb->ccb_prpl[i - 1], 692ecfae151Sdlg dmap->dm_segs[i].ds_addr); 693ecfae151Sdlg } 694ecfae151Sdlg bus_dmamap_sync(sc->sc_dmat, 695ecfae151Sdlg NVME_DMA_MAP(sc->sc_ccb_prpls), 696ecfae151Sdlg ccb->ccb_prpl_off, 697e4727810Spatrick sizeof(*ccb->ccb_prpl) * (dmap->dm_nsegs - 1), 698ecfae151Sdlg BUS_DMASYNC_PREWRITE); 699ecfae151Sdlg } 700ecfae151Sdlg 701a1e87500Sdlg if (ISSET(xs->flags, SCSI_POLL)) { 70254904088Skrw nvme_poll(sc, sc->sc_q, ccb, nvme_scsi_io_fill, xs->timeout); 703a1e87500Sdlg return; 704a1e87500Sdlg } 705a1e87500Sdlg 7061eef25a1Sdlg nvme_q_submit(sc, sc->sc_q, ccb, nvme_scsi_io_fill); 707a1e87500Sdlg return; 708a1e87500Sdlg 709a1e87500Sdlg stuffup: 710a1e87500Sdlg xs->error = XS_DRIVER_STUFFUP; 711a1e87500Sdlg scsi_done(xs); 712a1e87500Sdlg } 713a1e87500Sdlg 714a1e87500Sdlg void 7151eef25a1Sdlg nvme_scsi_io_fill(struct nvme_softc *sc, struct nvme_ccb *ccb, void *slot) 716a1e87500Sdlg { 717a1e87500Sdlg struct nvme_sqe_io *sqe = slot; 718a1e87500Sdlg struct scsi_xfer *xs = ccb->ccb_cookie; 719a1e87500Sdlg struct scsi_link *link = xs->sc_link; 720a1e87500Sdlg bus_dmamap_t dmap = ccb->ccb_dmamap; 721a1e87500Sdlg u_int64_t lba; 722a1e87500Sdlg u_int32_t blocks; 723a1e87500Sdlg 724664c6166Skrw scsi_cmd_rw_decode(&xs->cmd, &lba, &blocks); 725a1e87500Sdlg 7261eef25a1Sdlg sqe->opcode = ISSET(xs->flags, SCSI_DATA_IN) ? 7271eef25a1Sdlg NVM_CMD_READ : NVM_CMD_WRITE; 728397f5692Skettenis htolem32(&sqe->nsid, link->target); 729a1e87500Sdlg 730a1e87500Sdlg htolem64(&sqe->entry.prp[0], dmap->dm_segs[0].ds_addr); 731a1e87500Sdlg switch (dmap->dm_nsegs) { 732a1e87500Sdlg case 1: 733a1e87500Sdlg break; 734a1e87500Sdlg case 2: 735a1e87500Sdlg htolem64(&sqe->entry.prp[1], dmap->dm_segs[1].ds_addr); 736a1e87500Sdlg break; 737a1e87500Sdlg default: 738ecfae151Sdlg /* the prp list is already set up and synced */ 739ecfae151Sdlg htolem64(&sqe->entry.prp[1], ccb->ccb_prpl_dva); 740ecfae151Sdlg break; 741a1e87500Sdlg } 742a1e87500Sdlg 743a1e87500Sdlg htolem64(&sqe->slba, lba); 744a1e87500Sdlg htolem16(&sqe->nlb, blocks - 1); 745a1e87500Sdlg } 746a1e87500Sdlg 747a1e87500Sdlg void 748a1e87500Sdlg nvme_scsi_io_done(struct nvme_softc *sc, struct nvme_ccb *ccb, 749a1e87500Sdlg struct nvme_cqe *cqe) 750a1e87500Sdlg { 751a1e87500Sdlg struct scsi_xfer *xs = ccb->ccb_cookie; 752a1e87500Sdlg bus_dmamap_t dmap = ccb->ccb_dmamap; 753a1e87500Sdlg u_int16_t flags; 754a1e87500Sdlg 755ecfae151Sdlg if (dmap->dm_nsegs > 2) { 756ecfae151Sdlg bus_dmamap_sync(sc->sc_dmat, 757ecfae151Sdlg NVME_DMA_MAP(sc->sc_ccb_prpls), 758ecfae151Sdlg ccb->ccb_prpl_off, 759e4727810Spatrick sizeof(*ccb->ccb_prpl) * (dmap->dm_nsegs - 1), 760ecfae151Sdlg BUS_DMASYNC_POSTWRITE); 761ecfae151Sdlg } 762ecfae151Sdlg 763a1e87500Sdlg bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize, 764a1e87500Sdlg ISSET(xs->flags, SCSI_DATA_IN) ? 765a1e87500Sdlg BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); 766a1e87500Sdlg 767a1e87500Sdlg bus_dmamap_unload(sc->sc_dmat, dmap); 768a1e87500Sdlg 769a1e87500Sdlg flags = lemtoh16(&cqe->flags); 770a1e87500Sdlg 771a1e87500Sdlg xs->error = (NVME_CQE_SC(flags) == NVME_CQE_SC_SUCCESS) ? 772a1e87500Sdlg XS_NOERROR : XS_DRIVER_STUFFUP; 7738cfbc7f4Sdlg xs->status = SCSI_OK; 774a1e87500Sdlg xs->resid = 0; 775a1e87500Sdlg scsi_done(xs); 776a1e87500Sdlg } 777a1e87500Sdlg 778a1e87500Sdlg void 779689c2c68Sdlg nvme_scsi_sync(struct scsi_xfer *xs) 780689c2c68Sdlg { 781689c2c68Sdlg struct scsi_link *link = xs->sc_link; 7820b29cb40Skrw struct nvme_softc *sc = link->bus->sb_adapter_softc; 783689c2c68Sdlg struct nvme_ccb *ccb = xs->io; 784689c2c68Sdlg 785689c2c68Sdlg ccb->ccb_done = nvme_scsi_sync_done; 786689c2c68Sdlg ccb->ccb_cookie = xs; 787689c2c68Sdlg 788689c2c68Sdlg if (ISSET(xs->flags, SCSI_POLL)) { 78954904088Skrw nvme_poll(sc, sc->sc_q, ccb, nvme_scsi_sync_fill, xs->timeout); 790689c2c68Sdlg return; 791689c2c68Sdlg } 792689c2c68Sdlg 793689c2c68Sdlg nvme_q_submit(sc, sc->sc_q, ccb, nvme_scsi_sync_fill); 794689c2c68Sdlg } 795689c2c68Sdlg 796689c2c68Sdlg void 797689c2c68Sdlg nvme_scsi_sync_fill(struct nvme_softc *sc, struct nvme_ccb *ccb, void *slot) 798689c2c68Sdlg { 799689c2c68Sdlg struct nvme_sqe *sqe = slot; 800689c2c68Sdlg struct scsi_xfer *xs = ccb->ccb_cookie; 801689c2c68Sdlg struct scsi_link *link = xs->sc_link; 802689c2c68Sdlg 803689c2c68Sdlg sqe->opcode = NVM_CMD_FLUSH; 804397f5692Skettenis htolem32(&sqe->nsid, link->target); 805689c2c68Sdlg } 806689c2c68Sdlg 807689c2c68Sdlg void 808689c2c68Sdlg nvme_scsi_sync_done(struct nvme_softc *sc, struct nvme_ccb *ccb, 809689c2c68Sdlg struct nvme_cqe *cqe) 810689c2c68Sdlg { 811689c2c68Sdlg struct scsi_xfer *xs = ccb->ccb_cookie; 812689c2c68Sdlg u_int16_t flags; 813689c2c68Sdlg 814689c2c68Sdlg flags = lemtoh16(&cqe->flags); 815689c2c68Sdlg 816689c2c68Sdlg xs->error = (NVME_CQE_SC(flags) == NVME_CQE_SC_SUCCESS) ? 817689c2c68Sdlg XS_NOERROR : XS_DRIVER_STUFFUP; 818689c2c68Sdlg xs->status = SCSI_OK; 819689c2c68Sdlg xs->resid = 0; 820689c2c68Sdlg scsi_done(xs); 821689c2c68Sdlg } 822689c2c68Sdlg 823689c2c68Sdlg void 8249856392eSdlg nvme_scsi_inq(struct scsi_xfer *xs) 8259856392eSdlg { 826664c6166Skrw struct scsi_inquiry *inq = (struct scsi_inquiry *)&xs->cmd; 8279856392eSdlg 8289856392eSdlg if (!ISSET(inq->flags, SI_EVPD)) { 8299856392eSdlg nvme_scsi_inquiry(xs); 8309856392eSdlg return; 8319856392eSdlg } 8329856392eSdlg 8339856392eSdlg switch (inq->pagecode) { 8349856392eSdlg default: 8359856392eSdlg /* printf("%s: %d\n", __func__, inq->pagecode); */ 8369856392eSdlg break; 8379856392eSdlg } 8389856392eSdlg 8399856392eSdlg xs->error = XS_DRIVER_STUFFUP; 8409856392eSdlg scsi_done(xs); 8419856392eSdlg } 8429856392eSdlg 8439856392eSdlg void 8449856392eSdlg nvme_scsi_inquiry(struct scsi_xfer *xs) 8459856392eSdlg { 8469856392eSdlg struct scsi_inquiry_data inq; 8479856392eSdlg struct scsi_link *link = xs->sc_link; 8480b29cb40Skrw struct nvme_softc *sc = link->bus->sb_adapter_softc; 8499856392eSdlg struct nvm_identify_namespace *ns; 8509856392eSdlg 8519856392eSdlg ns = sc->sc_namespaces[link->target].ident; 8529856392eSdlg 8539856392eSdlg memset(&inq, 0, sizeof(inq)); 8549856392eSdlg 8559856392eSdlg inq.device = T_DIRECT; 856b291b595Skrw inq.version = SCSI_REV_SPC4; 857bb4b71ebSkrw inq.response_format = SID_SCSI2_RESPONSE; 8583ca8dabfSkrw inq.additional_length = SID_SCSI2_ALEN; 8599856392eSdlg inq.flags |= SID_CmdQue; 8609856392eSdlg memcpy(inq.vendor, "NVMe ", sizeof(inq.vendor)); 8619856392eSdlg memcpy(inq.product, sc->sc_identify.mn, sizeof(inq.product)); 8629856392eSdlg memcpy(inq.revision, sc->sc_identify.fr, sizeof(inq.revision)); 8639856392eSdlg 864a83ec286Skrw scsi_copy_internal_data(xs, &inq, sizeof(inq)); 8659856392eSdlg 8669856392eSdlg xs->error = XS_NOERROR; 8679856392eSdlg scsi_done(xs); 8689856392eSdlg } 8699856392eSdlg 8709856392eSdlg void 871c947e044Sdlg nvme_scsi_capacity16(struct scsi_xfer *xs) 872c947e044Sdlg { 873c947e044Sdlg struct scsi_read_cap_data_16 rcd; 874c947e044Sdlg struct scsi_link *link = xs->sc_link; 8750b29cb40Skrw struct nvme_softc *sc = link->bus->sb_adapter_softc; 876c947e044Sdlg struct nvm_identify_namespace *ns; 877c947e044Sdlg struct nvm_namespace_format *f; 878daef0c50Skrw u_int64_t addr; 879c947e044Sdlg u_int16_t tpe = READ_CAP_16_TPE; 880c947e044Sdlg 881c947e044Sdlg ns = sc->sc_namespaces[link->target].ident; 882c947e044Sdlg 883c947e044Sdlg if (xs->cmdlen != sizeof(struct scsi_read_capacity_16)) { 884c947e044Sdlg xs->error = XS_DRIVER_STUFFUP; 885c947e044Sdlg scsi_done(xs); 886c947e044Sdlg return; 887c947e044Sdlg } 888c947e044Sdlg 889daef0c50Skrw addr = nvme_scsi_size(ns) - 1; 890c947e044Sdlg f = &ns->lbaf[NVME_ID_NS_FLBAS(ns->flbas)]; 891c947e044Sdlg 892c947e044Sdlg memset(&rcd, 0, sizeof(rcd)); 893daef0c50Skrw _lto8b(addr, rcd.addr); 894c947e044Sdlg _lto4b(1 << f->lbads, rcd.length); 895c947e044Sdlg _lto2b(tpe, rcd.lowest_aligned); 896c947e044Sdlg 897c947e044Sdlg memcpy(xs->data, &rcd, MIN(sizeof(rcd), xs->datalen)); 898c947e044Sdlg 899c947e044Sdlg xs->error = XS_NOERROR; 900c947e044Sdlg scsi_done(xs); 901c947e044Sdlg } 902c947e044Sdlg 903c947e044Sdlg void 904c947e044Sdlg nvme_scsi_capacity(struct scsi_xfer *xs) 905c947e044Sdlg { 906c947e044Sdlg struct scsi_read_cap_data rcd; 907c947e044Sdlg struct scsi_link *link = xs->sc_link; 9080b29cb40Skrw struct nvme_softc *sc = link->bus->sb_adapter_softc; 909c947e044Sdlg struct nvm_identify_namespace *ns; 910c947e044Sdlg struct nvm_namespace_format *f; 911daef0c50Skrw u_int64_t addr; 912c947e044Sdlg 913c947e044Sdlg ns = sc->sc_namespaces[link->target].ident; 914c947e044Sdlg 915c947e044Sdlg if (xs->cmdlen != sizeof(struct scsi_read_capacity)) { 916c947e044Sdlg xs->error = XS_DRIVER_STUFFUP; 917c947e044Sdlg scsi_done(xs); 918c947e044Sdlg return; 919c947e044Sdlg } 920c947e044Sdlg 921daef0c50Skrw addr = nvme_scsi_size(ns) - 1; 922daef0c50Skrw if (addr > 0xffffffff) 923daef0c50Skrw addr = 0xffffffff; 924c947e044Sdlg 925c947e044Sdlg f = &ns->lbaf[NVME_ID_NS_FLBAS(ns->flbas)]; 926c947e044Sdlg 927c947e044Sdlg memset(&rcd, 0, sizeof(rcd)); 928daef0c50Skrw _lto4b(addr, rcd.addr); 929c947e044Sdlg _lto4b(1 << f->lbads, rcd.length); 930c947e044Sdlg 931c947e044Sdlg memcpy(xs->data, &rcd, MIN(sizeof(rcd), xs->datalen)); 932c947e044Sdlg 933c947e044Sdlg xs->error = XS_NOERROR; 934c947e044Sdlg scsi_done(xs); 935c947e044Sdlg } 936c947e044Sdlg 937c947e044Sdlg void 93811624e4cSdlg nvme_scsi_free(struct scsi_link *link) 93911624e4cSdlg { 9400b29cb40Skrw struct nvme_softc *sc = link->bus->sb_adapter_softc; 94165dd51f8Sdlg struct nvm_identify_namespace *identify; 94211624e4cSdlg 94365dd51f8Sdlg identify = sc->sc_namespaces[link->target].ident; 94465dd51f8Sdlg sc->sc_namespaces[link->target].ident = NULL; 94565dd51f8Sdlg 94665dd51f8Sdlg free(identify, M_DEVBUF, sizeof(*identify)); 94711624e4cSdlg } 94811624e4cSdlg 949daef0c50Skrw uint64_t 9507f4636ceSkrw nvme_scsi_size(const struct nvm_identify_namespace *ns) 951daef0c50Skrw { 952daef0c50Skrw uint64_t ncap, nsze; 953daef0c50Skrw 954daef0c50Skrw ncap = lemtoh64(&ns->ncap); /* Max allowed allocation. */ 955daef0c50Skrw nsze = lemtoh64(&ns->nsze); 956daef0c50Skrw 957daef0c50Skrw if ((ns->nsfeat & NVME_ID_NS_NSFEAT_THIN_PROV) && ncap < nsze) 958daef0c50Skrw return ncap; 959daef0c50Skrw else 960daef0c50Skrw return nsze; 961daef0c50Skrw } 962daef0c50Skrw 9634e9514d6Skrw int 9644e9514d6Skrw nvme_passthrough_cmd(struct nvme_softc *sc, struct nvme_pt_cmd *pt, int dv_unit, 9654e9514d6Skrw int nsid) 9664e9514d6Skrw { 9674e9514d6Skrw struct nvme_pt_status pt_status; 9684e9514d6Skrw struct nvme_sqe sqe; 9694e9514d6Skrw struct nvme_dmamem *mem = NULL; 9704e9514d6Skrw struct nvme_ccb *ccb = NULL; 9714e9514d6Skrw int flags; 9724e9514d6Skrw int rv = 0; 9734e9514d6Skrw 9744e9514d6Skrw ccb = nvme_ccb_get(sc); 9754e9514d6Skrw if (ccb == NULL) 9764e9514d6Skrw panic("nvme_passthrough_cmd: nvme_ccb_get returned NULL"); 9774e9514d6Skrw 9784e9514d6Skrw memset(&sqe, 0, sizeof(sqe)); 9794e9514d6Skrw sqe.opcode = pt->pt_opcode; 9804e9514d6Skrw htolem32(&sqe.nsid, pt->pt_nsid); 9814e9514d6Skrw htolem32(&sqe.cdw10, pt->pt_cdw10); 9824e9514d6Skrw htolem32(&sqe.cdw11, pt->pt_cdw11); 9834e9514d6Skrw htolem32(&sqe.cdw12, pt->pt_cdw12); 9844e9514d6Skrw htolem32(&sqe.cdw13, pt->pt_cdw13); 9854e9514d6Skrw htolem32(&sqe.cdw14, pt->pt_cdw14); 9864e9514d6Skrw htolem32(&sqe.cdw15, pt->pt_cdw15); 9874e9514d6Skrw 9884e9514d6Skrw ccb->ccb_done = nvme_empty_done; 9894e9514d6Skrw ccb->ccb_cookie = &sqe; 9904e9514d6Skrw 9914e9514d6Skrw switch (pt->pt_opcode) { 9924e9514d6Skrw case NVM_ADMIN_IDENTIFY: 9934e9514d6Skrw case NVM_ADMIN_GET_LOG_PG: 9944e9514d6Skrw case NVM_ADMIN_SELFTEST: 9954e9514d6Skrw break; 9964e9514d6Skrw 9974e9514d6Skrw default: 9984e9514d6Skrw rv = ENOTTY; 9994e9514d6Skrw goto done; 10004e9514d6Skrw } 10014e9514d6Skrw 10024e9514d6Skrw if (pt->pt_databuflen > 0) { 10034e9514d6Skrw mem = nvme_dmamem_alloc(sc, pt->pt_databuflen); 10044e9514d6Skrw if (mem == NULL) { 10054e9514d6Skrw rv = ENOMEM; 10064e9514d6Skrw goto done; 10074e9514d6Skrw } 10084e9514d6Skrw htolem64(&sqe.entry.prp[0], NVME_DMA_DVA(mem)); 10094e9514d6Skrw nvme_dmamem_sync(sc, mem, BUS_DMASYNC_PREREAD); 10104e9514d6Skrw } 10114e9514d6Skrw 10121488c4e9Skrw flags = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_PT); 10134e9514d6Skrw 10144e9514d6Skrw if (pt->pt_databuflen > 0) { 10154e9514d6Skrw nvme_dmamem_sync(sc, mem, BUS_DMASYNC_POSTREAD); 10164e9514d6Skrw if (flags == 0) 10174e9514d6Skrw rv = copyout(NVME_DMA_KVA(mem), pt->pt_databuf, 10184e9514d6Skrw pt->pt_databuflen); 10194e9514d6Skrw } 10204e9514d6Skrw 10214e9514d6Skrw if (rv == 0 && pt->pt_statuslen > 0) { 10224e9514d6Skrw pt_status.ps_dv_unit = dv_unit; 10234e9514d6Skrw pt_status.ps_nsid = nsid; 10244e9514d6Skrw pt_status.ps_flags = flags; 10254e9514d6Skrw pt_status.ps_cc = nvme_read4(sc, NVME_CC); 10264e9514d6Skrw pt_status.ps_csts = nvme_read4(sc, NVME_CSTS); 10274e9514d6Skrw rv = copyout(&pt_status, pt->pt_status, pt->pt_statuslen); 10284e9514d6Skrw } 10294e9514d6Skrw 10304e9514d6Skrw done: 10314e9514d6Skrw if (mem) 10324e9514d6Skrw nvme_dmamem_free(sc, mem); 10334e9514d6Skrw if (ccb) 10344e9514d6Skrw nvme_ccb_put(sc, ccb); 10354e9514d6Skrw 10364e9514d6Skrw return rv; 10374e9514d6Skrw } 10384e9514d6Skrw 10394e9514d6Skrw int 10404e9514d6Skrw nvme_scsi_ioctl(struct scsi_link *link, u_long cmd, caddr_t addr, int flag) 10414e9514d6Skrw { 10424e9514d6Skrw struct nvme_softc *sc = link->bus->sb_adapter_softc; 10434e9514d6Skrw struct nvme_pt_cmd *pt = (struct nvme_pt_cmd *)addr; 10444e9514d6Skrw int rv; 10454e9514d6Skrw 10464e9514d6Skrw switch (cmd) { 10474e9514d6Skrw case NVME_PASSTHROUGH_CMD: 10484e9514d6Skrw break; 10494e9514d6Skrw default: 10504e9514d6Skrw return ENOTTY; 10514e9514d6Skrw } 10524e9514d6Skrw 10534e9514d6Skrw if ((pt->pt_cdw10 & 0xff) == 0) 10544e9514d6Skrw pt->pt_nsid = link->target; 10554e9514d6Skrw 10564e9514d6Skrw rv = nvme_passthrough_cmd(sc, pt, sc->sc_dev.dv_unit, link->target); 10574e9514d6Skrw if (rv) 10584e9514d6Skrw goto done; 10594e9514d6Skrw 10604e9514d6Skrw done: 10614e9514d6Skrw return rv; 10624e9514d6Skrw } 10634e9514d6Skrw 10647599295eSdlg uint32_t 10657599295eSdlg nvme_op_sq_enter(struct nvme_softc *sc, 10667599295eSdlg struct nvme_queue *q, struct nvme_ccb *ccb) 10677599295eSdlg { 10687599295eSdlg mtx_enter(&q->q_sq_mtx); 10697599295eSdlg return (nvme_op_sq_enter_locked(sc, q, ccb)); 10707599295eSdlg } 10717599295eSdlg 10727599295eSdlg uint32_t 10737599295eSdlg nvme_op_sq_enter_locked(struct nvme_softc *sc, 10747599295eSdlg struct nvme_queue *q, struct nvme_ccb *ccb) 10757599295eSdlg { 10767599295eSdlg return (q->q_sq_tail); 10777599295eSdlg } 10787599295eSdlg 10797599295eSdlg void 10807599295eSdlg nvme_op_sq_leave_locked(struct nvme_softc *sc, 10817599295eSdlg struct nvme_queue *q, struct nvme_ccb *ccb) 10827599295eSdlg { 10837599295eSdlg uint32_t tail; 10847599295eSdlg 10857599295eSdlg tail = ++q->q_sq_tail; 10867599295eSdlg if (tail >= q->q_entries) 10877599295eSdlg tail = 0; 10887599295eSdlg q->q_sq_tail = tail; 10897599295eSdlg nvme_write4(sc, q->q_sqtdbl, tail); 10907599295eSdlg } 10917599295eSdlg 10927599295eSdlg void 10937599295eSdlg nvme_op_sq_leave(struct nvme_softc *sc, 10947599295eSdlg struct nvme_queue *q, struct nvme_ccb *ccb) 10957599295eSdlg { 10967599295eSdlg nvme_op_sq_leave_locked(sc, q, ccb); 10977599295eSdlg mtx_leave(&q->q_sq_mtx); 10987599295eSdlg } 10997599295eSdlg 1100448b3c09Sdlg void 1101448b3c09Sdlg nvme_q_submit(struct nvme_softc *sc, struct nvme_queue *q, struct nvme_ccb *ccb, 1102448b3c09Sdlg void (*fill)(struct nvme_softc *, struct nvme_ccb *, void *)) 1103448b3c09Sdlg { 1104448b3c09Sdlg struct nvme_sqe *sqe = NVME_DMA_KVA(q->q_sq_dmamem); 1105448b3c09Sdlg u_int32_t tail; 1106448b3c09Sdlg 11077599295eSdlg tail = sc->sc_ops->op_sq_enter(sc, q, ccb); 1108448b3c09Sdlg 1109448b3c09Sdlg sqe += tail; 1110448b3c09Sdlg 1111448b3c09Sdlg bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_sq_dmamem), 1112448b3c09Sdlg sizeof(*sqe) * tail, sizeof(*sqe), BUS_DMASYNC_POSTWRITE); 1113448b3c09Sdlg memset(sqe, 0, sizeof(*sqe)); 1114448b3c09Sdlg (*fill)(sc, ccb, sqe); 111524aede5fSdlg sqe->cid = ccb->ccb_id; 1116448b3c09Sdlg bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_sq_dmamem), 1117448b3c09Sdlg sizeof(*sqe) * tail, sizeof(*sqe), BUS_DMASYNC_PREWRITE); 1118448b3c09Sdlg 11197599295eSdlg sc->sc_ops->op_sq_leave(sc, q, ccb); 1120448b3c09Sdlg } 1121448b3c09Sdlg 1122448b3c09Sdlg struct nvme_poll_state { 1123448b3c09Sdlg struct nvme_sqe s; 1124448b3c09Sdlg struct nvme_cqe c; 1125448b3c09Sdlg }; 1126448b3c09Sdlg 1127448b3c09Sdlg int 1128448b3c09Sdlg nvme_poll(struct nvme_softc *sc, struct nvme_queue *q, struct nvme_ccb *ccb, 112954904088Skrw void (*fill)(struct nvme_softc *, struct nvme_ccb *, void *), u_int32_t ms) 1130448b3c09Sdlg { 1131448b3c09Sdlg struct nvme_poll_state state; 1132448b3c09Sdlg void (*done)(struct nvme_softc *, struct nvme_ccb *, struct nvme_cqe *); 1133448b3c09Sdlg void *cookie; 113454904088Skrw int64_t us; 11359f81ea3bSdlg u_int16_t flags; 1136448b3c09Sdlg 1137448b3c09Sdlg memset(&state, 0, sizeof(state)); 1138448b3c09Sdlg (*fill)(sc, ccb, &state.s); 1139448b3c09Sdlg 1140448b3c09Sdlg done = ccb->ccb_done; 1141448b3c09Sdlg cookie = ccb->ccb_cookie; 1142448b3c09Sdlg 1143448b3c09Sdlg ccb->ccb_done = nvme_poll_done; 1144448b3c09Sdlg ccb->ccb_cookie = &state; 1145448b3c09Sdlg 1146448b3c09Sdlg nvme_q_submit(sc, q, ccb, nvme_poll_fill); 114754904088Skrw for (us = ms * 1000; ms == 0 || us > 0; us -= NVME_TIMO_DELAYNS) { 114854904088Skrw if (ISSET(state.c.flags, htole16(NVME_CQE_PHASE))) 114954904088Skrw break; 11505fac9627Sdlg if (nvme_q_complete(sc, q) == 0) 115154904088Skrw delay(NVME_TIMO_DELAYNS); 1152b6ac76ceSkrw nvme_barrier(sc, NVME_CSTS, 4, BUS_SPACE_BARRIER_READ); 1153448b3c09Sdlg } 1154448b3c09Sdlg 1155448b3c09Sdlg ccb->ccb_cookie = cookie; 1156448b3c09Sdlg done(sc, ccb, &state.c); 1157448b3c09Sdlg 11589f81ea3bSdlg flags = lemtoh16(&state.c.flags); 11599f81ea3bSdlg 1160c05818fdSdlg return (flags & ~NVME_CQE_PHASE); 1161448b3c09Sdlg } 1162448b3c09Sdlg 1163448b3c09Sdlg void 1164448b3c09Sdlg nvme_poll_fill(struct nvme_softc *sc, struct nvme_ccb *ccb, void *slot) 1165448b3c09Sdlg { 1166448b3c09Sdlg struct nvme_sqe *sqe = slot; 1167448b3c09Sdlg struct nvme_poll_state *state = ccb->ccb_cookie; 1168448b3c09Sdlg 1169448b3c09Sdlg *sqe = state->s; 1170448b3c09Sdlg } 1171448b3c09Sdlg 1172448b3c09Sdlg void 1173448b3c09Sdlg nvme_poll_done(struct nvme_softc *sc, struct nvme_ccb *ccb, 1174448b3c09Sdlg struct nvme_cqe *cqe) 1175448b3c09Sdlg { 1176448b3c09Sdlg struct nvme_poll_state *state = ccb->ccb_cookie; 1177448b3c09Sdlg 1178448b3c09Sdlg state->c = *cqe; 1179300a229bSyasuoka SET(state->c.flags, htole16(NVME_CQE_PHASE)); 1180448b3c09Sdlg } 1181448b3c09Sdlg 11829f81ea3bSdlg void 1183ad9e5681Sdlg nvme_sqe_fill(struct nvme_softc *sc, struct nvme_ccb *ccb, void *slot) 1184ad9e5681Sdlg { 1185ad9e5681Sdlg struct nvme_sqe *src = ccb->ccb_cookie; 1186ad9e5681Sdlg struct nvme_sqe *dst = slot; 1187ad9e5681Sdlg 1188ad9e5681Sdlg *dst = *src; 1189ad9e5681Sdlg } 1190ad9e5681Sdlg 1191ad9e5681Sdlg void 11929f81ea3bSdlg nvme_empty_done(struct nvme_softc *sc, struct nvme_ccb *ccb, 11939f81ea3bSdlg struct nvme_cqe *cqe) 11949f81ea3bSdlg { 11959f81ea3bSdlg } 11969f81ea3bSdlg 11977599295eSdlg void 11987599295eSdlg nvme_op_cq_done(struct nvme_softc *sc, 11997599295eSdlg struct nvme_queue *q, struct nvme_ccb *ccb) 12007599295eSdlg { 12017599295eSdlg /* nop */ 12027599295eSdlg } 12037599295eSdlg 1204448b3c09Sdlg int 1205448b3c09Sdlg nvme_q_complete(struct nvme_softc *sc, struct nvme_queue *q) 1206448b3c09Sdlg { 1207448b3c09Sdlg struct nvme_ccb *ccb; 1208448b3c09Sdlg struct nvme_cqe *ring = NVME_DMA_KVA(q->q_cq_dmamem), *cqe; 1209448b3c09Sdlg u_int32_t head; 1210448b3c09Sdlg u_int16_t flags; 1211448b3c09Sdlg int rv = 0; 1212448b3c09Sdlg 1213448b3c09Sdlg if (!mtx_enter_try(&q->q_cq_mtx)) 1214448b3c09Sdlg return (-1); 1215448b3c09Sdlg 1216448b3c09Sdlg head = q->q_cq_head; 12170ee935ccSdlg 12180ee935ccSdlg nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_POSTREAD); 1219448b3c09Sdlg for (;;) { 1220448b3c09Sdlg cqe = &ring[head]; 1221448b3c09Sdlg flags = lemtoh16(&cqe->flags); 1222448b3c09Sdlg if ((flags & NVME_CQE_PHASE) != q->q_cq_phase) 1223448b3c09Sdlg break; 1224448b3c09Sdlg 1225ee570821Sjmatthew membar_consumer(); 1226ee570821Sjmatthew 1227448b3c09Sdlg ccb = &sc->sc_ccbs[cqe->cid]; 12287599295eSdlg sc->sc_ops->op_cq_done(sc, q, ccb); 1229448b3c09Sdlg ccb->ccb_done(sc, ccb, cqe); 1230448b3c09Sdlg 1231448b3c09Sdlg if (++head >= q->q_entries) { 1232448b3c09Sdlg head = 0; 1233448b3c09Sdlg q->q_cq_phase ^= NVME_CQE_PHASE; 1234448b3c09Sdlg } 1235448b3c09Sdlg 1236448b3c09Sdlg rv = 1; 1237448b3c09Sdlg } 12380ee935ccSdlg nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_PREREAD); 1239448b3c09Sdlg 1240448b3c09Sdlg if (rv) 124120386a6cSdlg nvme_write4(sc, q->q_cqhdbl, q->q_cq_head = head); 1242448b3c09Sdlg mtx_leave(&q->q_cq_mtx); 1243448b3c09Sdlg 1244448b3c09Sdlg return (rv); 1245448b3c09Sdlg } 1246448b3c09Sdlg 1247448b3c09Sdlg int 124898b2ae82Skrw nvme_identify(struct nvme_softc *sc, u_int mpsmin) 1249448b3c09Sdlg { 12509f81ea3bSdlg char sn[41], mn[81], fr[17]; 12519f81ea3bSdlg struct nvm_identify_controller *identify; 1252448b3c09Sdlg struct nvme_dmamem *mem; 12539f81ea3bSdlg struct nvme_ccb *ccb; 1254448b3c09Sdlg int rv = 1; 1255448b3c09Sdlg 1256448b3c09Sdlg ccb = nvme_ccb_get(sc); 1257448b3c09Sdlg if (ccb == NULL) 1258448b3c09Sdlg panic("nvme_identify: nvme_ccb_get returned NULL"); 1259448b3c09Sdlg 12609f81ea3bSdlg mem = nvme_dmamem_alloc(sc, sizeof(*identify)); 1261448b3c09Sdlg if (mem == NULL) 1262448b3c09Sdlg return (1); 1263448b3c09Sdlg 12649f81ea3bSdlg ccb->ccb_done = nvme_empty_done; 1265448b3c09Sdlg ccb->ccb_cookie = mem; 1266448b3c09Sdlg 12670ee935ccSdlg nvme_dmamem_sync(sc, mem, BUS_DMASYNC_PREREAD); 126854904088Skrw rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_fill_identify, 126954904088Skrw NVME_TIMO_IDENT); 12700ee935ccSdlg nvme_dmamem_sync(sc, mem, BUS_DMASYNC_POSTREAD); 1271448b3c09Sdlg 1272b62ffe02Sdlg nvme_ccb_put(sc, ccb); 1273b62ffe02Sdlg 12749f81ea3bSdlg if (rv != 0) 12759f81ea3bSdlg goto done; 12769f81ea3bSdlg 127717756a2bSdlg identify = NVME_DMA_KVA(mem); 127817756a2bSdlg 12799f81ea3bSdlg scsi_strvis(sn, identify->sn, sizeof(identify->sn)); 12809f81ea3bSdlg scsi_strvis(mn, identify->mn, sizeof(identify->mn)); 12819f81ea3bSdlg scsi_strvis(fr, identify->fr, sizeof(identify->fr)); 12829f81ea3bSdlg 12839f81ea3bSdlg printf("%s: %s, firmware %s, serial %s\n", DEVNAME(sc), mn, fr, sn); 12849f81ea3bSdlg 128588aa9192Sdlg if (identify->mdts > 0) { 128698b2ae82Skrw sc->sc_mdts = (1 << identify->mdts) * (1 << mpsmin); 1287767e8532Skrw if (sc->sc_mdts > NVME_MAXPHYS) 1288767e8532Skrw sc->sc_mdts = NVME_MAXPHYS; 1289a8751b7cSkrw sc->sc_max_prpl = sc->sc_mdts / sc->sc_mps; 129088aa9192Sdlg } 129188aa9192Sdlg 129217756a2bSdlg sc->sc_nn = lemtoh32(&identify->nn); 129317756a2bSdlg 1294e276f6b1Sjcs /* 1295e276f6b1Sjcs * At least one Apple NVMe device presents a second, bogus disk that is 1296e276f6b1Sjcs * inaccessible, so cap targets at 1. 1297e276f6b1Sjcs * 1298397f5692Skettenis * sd1 at scsibus1 targ 2 lun 0: <NVMe, APPLE SSD AP0512, 16.1> [..] 1299e276f6b1Sjcs * sd1: 0MB, 4096 bytes/sector, 2 sectors 1300e276f6b1Sjcs */ 1301e276f6b1Sjcs if (sc->sc_nn > 1 && 1302e276f6b1Sjcs mn[0] == 'A' && mn[1] == 'P' && mn[2] == 'P' && mn[3] == 'L' && 1303e276f6b1Sjcs mn[4] == 'E') 1304e276f6b1Sjcs sc->sc_nn = 1; 1305e276f6b1Sjcs 130617756a2bSdlg memcpy(&sc->sc_identify, identify, sizeof(sc->sc_identify)); 130717756a2bSdlg 13089f81ea3bSdlg done: 1309448b3c09Sdlg nvme_dmamem_free(sc, mem); 1310448b3c09Sdlg 1311448b3c09Sdlg return (rv); 1312448b3c09Sdlg } 1313448b3c09Sdlg 1314cd15a86fSdlg int 1315cd15a86fSdlg nvme_q_create(struct nvme_softc *sc, struct nvme_queue *q) 1316cd15a86fSdlg { 1317cd15a86fSdlg struct nvme_sqe_q sqe; 1318cd15a86fSdlg struct nvme_ccb *ccb; 1319cd15a86fSdlg int rv; 1320cd15a86fSdlg 1321cd15a86fSdlg ccb = scsi_io_get(&sc->sc_iopool, 0); 1322cd15a86fSdlg KASSERT(ccb != NULL); 1323cd15a86fSdlg 1324cd15a86fSdlg ccb->ccb_done = nvme_empty_done; 1325cd15a86fSdlg ccb->ccb_cookie = &sqe; 1326cd15a86fSdlg 1327cd15a86fSdlg memset(&sqe, 0, sizeof(sqe)); 1328cd15a86fSdlg sqe.opcode = NVM_ADMIN_ADD_IOCQ; 1329cd15a86fSdlg htolem64(&sqe.prp1, NVME_DMA_DVA(q->q_cq_dmamem)); 1330cd15a86fSdlg htolem16(&sqe.qsize, q->q_entries - 1); 1331cd15a86fSdlg htolem16(&sqe.qid, q->q_id); 1332cd15a86fSdlg sqe.qflags = NVM_SQE_CQ_IEN | NVM_SQE_Q_PC; 1333cd15a86fSdlg 133454904088Skrw rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_QOP); 1335cd15a86fSdlg if (rv != 0) 1336cd15a86fSdlg goto fail; 1337cd15a86fSdlg 1338cd15a86fSdlg ccb->ccb_done = nvme_empty_done; 1339cd15a86fSdlg ccb->ccb_cookie = &sqe; 1340cd15a86fSdlg 1341cd15a86fSdlg memset(&sqe, 0, sizeof(sqe)); 1342cd15a86fSdlg sqe.opcode = NVM_ADMIN_ADD_IOSQ; 1343cd15a86fSdlg htolem64(&sqe.prp1, NVME_DMA_DVA(q->q_sq_dmamem)); 1344cd15a86fSdlg htolem16(&sqe.qsize, q->q_entries - 1); 1345cd15a86fSdlg htolem16(&sqe.qid, q->q_id); 1346cd15a86fSdlg htolem16(&sqe.cqid, q->q_id); 1347cd15a86fSdlg sqe.qflags = NVM_SQE_Q_PC; 1348cd15a86fSdlg 134954904088Skrw rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_QOP); 1350cd15a86fSdlg if (rv != 0) 1351cd15a86fSdlg goto fail; 1352cd15a86fSdlg 1353cd15a86fSdlg fail: 1354cd15a86fSdlg scsi_io_put(&sc->sc_iopool, ccb); 1355cd15a86fSdlg return (rv); 1356cd15a86fSdlg } 1357cd15a86fSdlg 13585313ab17Sdlg int 13595313ab17Sdlg nvme_q_delete(struct nvme_softc *sc, struct nvme_queue *q) 13605313ab17Sdlg { 13615313ab17Sdlg struct nvme_sqe_q sqe; 13625313ab17Sdlg struct nvme_ccb *ccb; 13635313ab17Sdlg int rv; 13645313ab17Sdlg 13655313ab17Sdlg ccb = scsi_io_get(&sc->sc_iopool, 0); 13665313ab17Sdlg KASSERT(ccb != NULL); 13675313ab17Sdlg 13685313ab17Sdlg ccb->ccb_done = nvme_empty_done; 13695313ab17Sdlg ccb->ccb_cookie = &sqe; 13705313ab17Sdlg 13715313ab17Sdlg memset(&sqe, 0, sizeof(sqe)); 13725313ab17Sdlg sqe.opcode = NVM_ADMIN_DEL_IOSQ; 13735313ab17Sdlg htolem16(&sqe.qid, q->q_id); 13745313ab17Sdlg 137554904088Skrw rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_QOP); 13765313ab17Sdlg if (rv != 0) 13775313ab17Sdlg goto fail; 13785313ab17Sdlg 13795313ab17Sdlg ccb->ccb_done = nvme_empty_done; 13805313ab17Sdlg ccb->ccb_cookie = &sqe; 13815313ab17Sdlg 13825313ab17Sdlg memset(&sqe, 0, sizeof(sqe)); 13835313ab17Sdlg sqe.opcode = NVM_ADMIN_DEL_IOCQ; 13845313ab17Sdlg htolem16(&sqe.qid, q->q_id); 13855313ab17Sdlg 138654904088Skrw rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_QOP); 13875313ab17Sdlg if (rv != 0) 13885313ab17Sdlg goto fail; 13895313ab17Sdlg 13900abee971Sdv nvme_q_free(sc, q); 1391aa5ddcf9Ssf 13925313ab17Sdlg fail: 13935313ab17Sdlg scsi_io_put(&sc->sc_iopool, ccb); 13945313ab17Sdlg return (rv); 13955313ab17Sdlg 13965313ab17Sdlg } 13975313ab17Sdlg 1398448b3c09Sdlg void 1399448b3c09Sdlg nvme_fill_identify(struct nvme_softc *sc, struct nvme_ccb *ccb, void *slot) 1400448b3c09Sdlg { 1401448b3c09Sdlg struct nvme_sqe *sqe = slot; 1402448b3c09Sdlg struct nvme_dmamem *mem = ccb->ccb_cookie; 1403448b3c09Sdlg 1404448b3c09Sdlg sqe->opcode = NVM_ADMIN_IDENTIFY; 1405448b3c09Sdlg htolem64(&sqe->entry.prp[0], NVME_DMA_DVA(mem)); 1406448b3c09Sdlg htolem32(&sqe->cdw10, 1); 1407448b3c09Sdlg } 1408448b3c09Sdlg 1409448b3c09Sdlg int 1410448b3c09Sdlg nvme_ccbs_alloc(struct nvme_softc *sc, u_int nccbs) 1411448b3c09Sdlg { 1412448b3c09Sdlg struct nvme_ccb *ccb; 1413b1699ccdSdlg bus_addr_t off; 1414b1699ccdSdlg u_int64_t *prpl; 1415448b3c09Sdlg u_int i; 1416448b3c09Sdlg 14179f6fb5c7Sderaadt sc->sc_ccbs = mallocarray(nccbs, sizeof(*ccb), M_DEVBUF, 1418448b3c09Sdlg M_WAITOK | M_CANFAIL); 1419448b3c09Sdlg if (sc->sc_ccbs == NULL) 1420448b3c09Sdlg return (1); 1421448b3c09Sdlg 1422b1699ccdSdlg sc->sc_ccb_prpls = nvme_dmamem_alloc(sc, 1423a8751b7cSkrw sizeof(*prpl) * sc->sc_max_prpl * nccbs); 1424b1699ccdSdlg 1425b1699ccdSdlg prpl = NVME_DMA_KVA(sc->sc_ccb_prpls); 1426b1699ccdSdlg off = 0; 1427b1699ccdSdlg 1428448b3c09Sdlg for (i = 0; i < nccbs; i++) { 1429448b3c09Sdlg ccb = &sc->sc_ccbs[i]; 1430448b3c09Sdlg 1431b1699ccdSdlg if (bus_dmamap_create(sc->sc_dmat, sc->sc_mdts, 1432a8751b7cSkrw sc->sc_max_prpl + 1, /* we get a free prp in the sqe */ 14332a4e68fdSdlg sc->sc_mps, sc->sc_mps, 14342a4e68fdSdlg BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW | BUS_DMA_64BIT, 1435448b3c09Sdlg &ccb->ccb_dmamap) != 0) 1436448b3c09Sdlg goto free_maps; 1437448b3c09Sdlg 1438448b3c09Sdlg ccb->ccb_id = i; 1439b1699ccdSdlg ccb->ccb_prpl = prpl; 1440b1699ccdSdlg ccb->ccb_prpl_off = off; 1441b1699ccdSdlg ccb->ccb_prpl_dva = NVME_DMA_DVA(sc->sc_ccb_prpls) + off; 1442b1699ccdSdlg 1443448b3c09Sdlg SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_list, ccb, ccb_entry); 1444b1699ccdSdlg 1445a8751b7cSkrw prpl += sc->sc_max_prpl; 1446a8751b7cSkrw off += sizeof(*prpl) * sc->sc_max_prpl; 1447448b3c09Sdlg } 1448448b3c09Sdlg 1449448b3c09Sdlg return (0); 1450448b3c09Sdlg 1451448b3c09Sdlg free_maps: 1452a1141c40Stedu nvme_ccbs_free(sc, nccbs); 1453448b3c09Sdlg return (1); 1454448b3c09Sdlg } 1455448b3c09Sdlg 1456448b3c09Sdlg void * 1457448b3c09Sdlg nvme_ccb_get(void *cookie) 1458448b3c09Sdlg { 1459448b3c09Sdlg struct nvme_softc *sc = cookie; 1460448b3c09Sdlg struct nvme_ccb *ccb; 1461448b3c09Sdlg 1462448b3c09Sdlg mtx_enter(&sc->sc_ccb_mtx); 1463448b3c09Sdlg ccb = SIMPLEQ_FIRST(&sc->sc_ccb_list); 1464448b3c09Sdlg if (ccb != NULL) 1465448b3c09Sdlg SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_list, ccb_entry); 1466448b3c09Sdlg mtx_leave(&sc->sc_ccb_mtx); 1467448b3c09Sdlg 1468448b3c09Sdlg return (ccb); 1469448b3c09Sdlg } 1470448b3c09Sdlg 1471448b3c09Sdlg void 1472448b3c09Sdlg nvme_ccb_put(void *cookie, void *io) 1473448b3c09Sdlg { 1474448b3c09Sdlg struct nvme_softc *sc = cookie; 1475448b3c09Sdlg struct nvme_ccb *ccb = io; 1476448b3c09Sdlg 1477448b3c09Sdlg mtx_enter(&sc->sc_ccb_mtx); 1478448b3c09Sdlg SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_list, ccb, ccb_entry); 1479448b3c09Sdlg mtx_leave(&sc->sc_ccb_mtx); 1480448b3c09Sdlg } 1481448b3c09Sdlg 1482448b3c09Sdlg void 1483a1141c40Stedu nvme_ccbs_free(struct nvme_softc *sc, unsigned int nccbs) 1484448b3c09Sdlg { 1485448b3c09Sdlg struct nvme_ccb *ccb; 1486448b3c09Sdlg 1487448b3c09Sdlg while ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_list)) != NULL) { 1488448b3c09Sdlg SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_list, ccb_entry); 1489448b3c09Sdlg bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap); 1490448b3c09Sdlg } 1491448b3c09Sdlg 1492b1699ccdSdlg nvme_dmamem_free(sc, sc->sc_ccb_prpls); 1493a1141c40Stedu free(sc->sc_ccbs, M_DEVBUF, nccbs * sizeof(*ccb)); 1494448b3c09Sdlg } 1495448b3c09Sdlg 1496282c0692Sdlg struct nvme_queue * 149759968badSdlg nvme_q_alloc(struct nvme_softc *sc, u_int16_t id, u_int entries, u_int dstrd) 1498282c0692Sdlg { 1499282c0692Sdlg struct nvme_queue *q; 1500282c0692Sdlg 1501282c0692Sdlg q = malloc(sizeof(*q), M_DEVBUF, M_WAITOK | M_CANFAIL); 1502282c0692Sdlg if (q == NULL) 1503282c0692Sdlg return (NULL); 1504282c0692Sdlg 1505448b3c09Sdlg q->q_sq_dmamem = nvme_dmamem_alloc(sc, 150669136b8eSdlg sizeof(struct nvme_sqe) * entries); 1507282c0692Sdlg if (q->q_sq_dmamem == NULL) 1508282c0692Sdlg goto free; 1509282c0692Sdlg 1510448b3c09Sdlg q->q_cq_dmamem = nvme_dmamem_alloc(sc, 151169136b8eSdlg sizeof(struct nvme_cqe) * entries); 1512638d48daSdlg if (q->q_cq_dmamem == NULL) 1513282c0692Sdlg goto free_sq; 1514282c0692Sdlg 1515448b3c09Sdlg memset(NVME_DMA_KVA(q->q_sq_dmamem), 0, NVME_DMA_LEN(q->q_sq_dmamem)); 1516448b3c09Sdlg memset(NVME_DMA_KVA(q->q_cq_dmamem), 0, NVME_DMA_LEN(q->q_cq_dmamem)); 1517448b3c09Sdlg 1518448b3c09Sdlg mtx_init(&q->q_sq_mtx, IPL_BIO); 1519448b3c09Sdlg mtx_init(&q->q_cq_mtx, IPL_BIO); 152059968badSdlg q->q_sqtdbl = NVME_SQTDBL(id, dstrd); 152159968badSdlg q->q_cqhdbl = NVME_CQHDBL(id, dstrd); 1522aa5ddcf9Ssf 15238f4e0057Sdlg q->q_id = id; 1524282c0692Sdlg q->q_entries = entries; 1525448b3c09Sdlg q->q_sq_tail = 0; 1526448b3c09Sdlg q->q_cq_head = 0; 1527448b3c09Sdlg q->q_cq_phase = NVME_CQE_PHASE; 1528448b3c09Sdlg 15297599295eSdlg if (sc->sc_ops->op_q_alloc != NULL) { 15307599295eSdlg if (sc->sc_ops->op_q_alloc(sc, q) != 0) 15317599295eSdlg goto free_cq; 15327599295eSdlg } 15337599295eSdlg 15340ee935ccSdlg nvme_dmamem_sync(sc, q->q_sq_dmamem, BUS_DMASYNC_PREWRITE); 15350ee935ccSdlg nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_PREREAD); 1536282c0692Sdlg 1537282c0692Sdlg return (q); 1538282c0692Sdlg 15397599295eSdlg free_cq: 15407599295eSdlg nvme_dmamem_free(sc, q->q_cq_dmamem); 1541282c0692Sdlg free_sq: 1542282c0692Sdlg nvme_dmamem_free(sc, q->q_sq_dmamem); 1543282c0692Sdlg free: 1544234dfda1Sderaadt free(q, M_DEVBUF, sizeof *q); 1545282c0692Sdlg 1546282c0692Sdlg return (NULL); 1547282c0692Sdlg } 1548282c0692Sdlg 15490abee971Sdv int 1550aa5ddcf9Ssf nvme_q_reset(struct nvme_softc *sc, struct nvme_queue *q) 1551aa5ddcf9Ssf { 1552aa5ddcf9Ssf memset(NVME_DMA_KVA(q->q_sq_dmamem), 0, NVME_DMA_LEN(q->q_sq_dmamem)); 1553aa5ddcf9Ssf memset(NVME_DMA_KVA(q->q_cq_dmamem), 0, NVME_DMA_LEN(q->q_cq_dmamem)); 1554aa5ddcf9Ssf 1555aa5ddcf9Ssf q->q_sq_tail = 0; 1556aa5ddcf9Ssf q->q_cq_head = 0; 1557aa5ddcf9Ssf q->q_cq_phase = NVME_CQE_PHASE; 1558aa5ddcf9Ssf 1559aa5ddcf9Ssf nvme_dmamem_sync(sc, q->q_sq_dmamem, BUS_DMASYNC_PREWRITE); 1560aa5ddcf9Ssf nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_PREREAD); 15610abee971Sdv 15620abee971Sdv return (0); 1563aa5ddcf9Ssf } 1564aa5ddcf9Ssf 1565282c0692Sdlg void 1566448b3c09Sdlg nvme_q_free(struct nvme_softc *sc, struct nvme_queue *q) 1567282c0692Sdlg { 15680ee935ccSdlg nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_POSTREAD); 15690ee935ccSdlg nvme_dmamem_sync(sc, q->q_sq_dmamem, BUS_DMASYNC_POSTWRITE); 15707599295eSdlg 1571e163d0c4Skrw if (sc->sc_ops->op_q_free != NULL) 15727599295eSdlg sc->sc_ops->op_q_free(sc, q); 15737599295eSdlg 1574282c0692Sdlg nvme_dmamem_free(sc, q->q_cq_dmamem); 1575282c0692Sdlg nvme_dmamem_free(sc, q->q_sq_dmamem); 1576234dfda1Sderaadt free(q, M_DEVBUF, sizeof *q); 1577282c0692Sdlg } 1578282c0692Sdlg 1579282c0692Sdlg int 1580282c0692Sdlg nvme_intr(void *xsc) 1581282c0692Sdlg { 1582448b3c09Sdlg struct nvme_softc *sc = xsc; 158387cbc23fSdlg int rv = 0; 1584448b3c09Sdlg 158587cbc23fSdlg if (nvme_q_complete(sc, sc->sc_q)) 158687cbc23fSdlg rv = 1; 158787cbc23fSdlg if (nvme_q_complete(sc, sc->sc_admin_q)) 158887cbc23fSdlg rv = 1; 158987cbc23fSdlg 159087cbc23fSdlg return (rv); 1591282c0692Sdlg } 1592282c0692Sdlg 1593eb77e636Sdlg int 1594eb77e636Sdlg nvme_intr_intx(void *xsc) 1595eb77e636Sdlg { 1596eb77e636Sdlg struct nvme_softc *sc = xsc; 1597eb77e636Sdlg int rv; 1598eb77e636Sdlg 1599eb77e636Sdlg nvme_write4(sc, NVME_INTMS, 1); 1600eb77e636Sdlg rv = nvme_intr(sc); 1601eb77e636Sdlg nvme_write4(sc, NVME_INTMC, 1); 1602eb77e636Sdlg 1603eb77e636Sdlg return (rv); 1604eb77e636Sdlg } 1605eb77e636Sdlg 1606282c0692Sdlg struct nvme_dmamem * 1607282c0692Sdlg nvme_dmamem_alloc(struct nvme_softc *sc, size_t size) 1608282c0692Sdlg { 1609282c0692Sdlg struct nvme_dmamem *ndm; 1610282c0692Sdlg int nsegs; 1611282c0692Sdlg 161272f0e87bSdlg ndm = malloc(sizeof(*ndm), M_DEVBUF, M_WAITOK | M_ZERO); 1613282c0692Sdlg if (ndm == NULL) 1614282c0692Sdlg return (NULL); 1615282c0692Sdlg 1616282c0692Sdlg ndm->ndm_size = size; 1617282c0692Sdlg 1618282c0692Sdlg if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, 16192a4e68fdSdlg BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW | BUS_DMA_64BIT, 16202a4e68fdSdlg &ndm->ndm_map) != 0) 1621282c0692Sdlg goto ndmfree; 1622282c0692Sdlg 1623448b3c09Sdlg if (bus_dmamem_alloc(sc->sc_dmat, size, sc->sc_mps, 0, &ndm->ndm_seg, 1624*a006a8cdSkettenis 1, &nsegs, BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_64BIT) != 0) 1625282c0692Sdlg goto destroy; 1626282c0692Sdlg 1627282c0692Sdlg if (bus_dmamem_map(sc->sc_dmat, &ndm->ndm_seg, nsegs, size, 162872f0e87bSdlg &ndm->ndm_kva, BUS_DMA_WAITOK) != 0) 1629282c0692Sdlg goto free; 1630282c0692Sdlg 1631282c0692Sdlg if (bus_dmamap_load(sc->sc_dmat, ndm->ndm_map, ndm->ndm_kva, size, 163272f0e87bSdlg NULL, BUS_DMA_WAITOK) != 0) 1633282c0692Sdlg goto unmap; 1634282c0692Sdlg 1635282c0692Sdlg return (ndm); 1636282c0692Sdlg 1637282c0692Sdlg unmap: 1638282c0692Sdlg bus_dmamem_unmap(sc->sc_dmat, ndm->ndm_kva, size); 1639282c0692Sdlg free: 1640282c0692Sdlg bus_dmamem_free(sc->sc_dmat, &ndm->ndm_seg, 1); 1641282c0692Sdlg destroy: 1642282c0692Sdlg bus_dmamap_destroy(sc->sc_dmat, ndm->ndm_map); 1643282c0692Sdlg ndmfree: 1644234dfda1Sderaadt free(ndm, M_DEVBUF, sizeof *ndm); 1645282c0692Sdlg 1646282c0692Sdlg return (NULL); 1647282c0692Sdlg } 1648282c0692Sdlg 1649282c0692Sdlg void 16500ee935ccSdlg nvme_dmamem_sync(struct nvme_softc *sc, struct nvme_dmamem *mem, int ops) 16510ee935ccSdlg { 16520ee935ccSdlg bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(mem), 16530ee935ccSdlg 0, NVME_DMA_LEN(mem), ops); 16540ee935ccSdlg } 16550ee935ccSdlg 16560ee935ccSdlg void 1657282c0692Sdlg nvme_dmamem_free(struct nvme_softc *sc, struct nvme_dmamem *ndm) 1658282c0692Sdlg { 1659282c0692Sdlg bus_dmamap_unload(sc->sc_dmat, ndm->ndm_map); 1660282c0692Sdlg bus_dmamem_unmap(sc->sc_dmat, ndm->ndm_kva, ndm->ndm_size); 1661282c0692Sdlg bus_dmamem_free(sc->sc_dmat, &ndm->ndm_seg, 1); 1662282c0692Sdlg bus_dmamap_destroy(sc->sc_dmat, ndm->ndm_map); 1663234dfda1Sderaadt free(ndm, M_DEVBUF, sizeof *ndm); 1664282c0692Sdlg } 1665282c0692Sdlg 166603d86467Sjmatthew #ifdef HIBERNATE 166703d86467Sjmatthew 166803d86467Sjmatthew int 166903d86467Sjmatthew nvme_hibernate_admin_cmd(struct nvme_softc *sc, struct nvme_sqe *sqe, 167003d86467Sjmatthew struct nvme_cqe *cqe, int cid) 167103d86467Sjmatthew { 167203d86467Sjmatthew struct nvme_sqe *asqe = NVME_DMA_KVA(sc->sc_admin_q->q_sq_dmamem); 167303d86467Sjmatthew struct nvme_cqe *acqe = NVME_DMA_KVA(sc->sc_admin_q->q_cq_dmamem); 167403d86467Sjmatthew struct nvme_queue *q = sc->sc_admin_q; 167503d86467Sjmatthew int tail; 167603d86467Sjmatthew u_int16_t flags; 167703d86467Sjmatthew 167803d86467Sjmatthew /* submit command */ 16797599295eSdlg tail = sc->sc_ops->op_sq_enter_locked(sc, q, /* XXX ccb */ NULL); 168003d86467Sjmatthew 168103d86467Sjmatthew asqe += tail; 168203d86467Sjmatthew bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_sq_dmamem), 168303d86467Sjmatthew sizeof(*sqe) * tail, sizeof(*sqe), BUS_DMASYNC_POSTWRITE); 168403d86467Sjmatthew *asqe = *sqe; 168503d86467Sjmatthew asqe->cid = cid; 168603d86467Sjmatthew bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_sq_dmamem), 168703d86467Sjmatthew sizeof(*sqe) * tail, sizeof(*sqe), BUS_DMASYNC_PREWRITE); 168803d86467Sjmatthew 16897599295eSdlg sc->sc_ops->op_sq_leave_locked(sc, q, /* XXX ccb */ NULL); 169003d86467Sjmatthew 169103d86467Sjmatthew /* wait for completion */ 169203d86467Sjmatthew acqe += q->q_cq_head; 169303d86467Sjmatthew for (;;) { 169403d86467Sjmatthew nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_POSTREAD); 169503d86467Sjmatthew flags = lemtoh16(&acqe->flags); 169603d86467Sjmatthew if ((flags & NVME_CQE_PHASE) == q->q_cq_phase) 169703d86467Sjmatthew break; 169803d86467Sjmatthew 169903d86467Sjmatthew delay(10); 170003d86467Sjmatthew } 170103d86467Sjmatthew 170203d86467Sjmatthew if (++q->q_cq_head >= q->q_entries) { 170303d86467Sjmatthew q->q_cq_head = 0; 170403d86467Sjmatthew q->q_cq_phase ^= NVME_CQE_PHASE; 170503d86467Sjmatthew } 170603d86467Sjmatthew nvme_write4(sc, q->q_cqhdbl, q->q_cq_head); 170703d86467Sjmatthew if ((NVME_CQE_SC(flags) != NVME_CQE_SC_SUCCESS) || (acqe->cid != cid)) 170803d86467Sjmatthew return (EIO); 170903d86467Sjmatthew 171003d86467Sjmatthew return (0); 171103d86467Sjmatthew } 171203d86467Sjmatthew 171303d86467Sjmatthew int 171403d86467Sjmatthew nvme_hibernate_io(dev_t dev, daddr_t blkno, vaddr_t addr, size_t size, 171503d86467Sjmatthew int op, void *page) 171603d86467Sjmatthew { 171703d86467Sjmatthew struct nvme_hibernate_page { 171803d86467Sjmatthew u_int64_t prpl[MAXPHYS / PAGE_SIZE]; 171903d86467Sjmatthew 172003d86467Sjmatthew struct nvme_softc *sc; 172103d86467Sjmatthew int nsid; 172203d86467Sjmatthew int sq_tail; 172303d86467Sjmatthew int cq_head; 172403d86467Sjmatthew int cqe_phase; 172503d86467Sjmatthew 172603d86467Sjmatthew daddr_t poffset; 172703d86467Sjmatthew size_t psize; 1728fc6d48fdSkrw u_int32_t secsize; 172903d86467Sjmatthew } *my = page; 173003d86467Sjmatthew struct nvme_sqe_io *isqe; 173103d86467Sjmatthew struct nvme_cqe *icqe; 173203d86467Sjmatthew paddr_t data_phys, page_phys; 173303d86467Sjmatthew u_int64_t data_bus_phys, page_bus_phys; 173403d86467Sjmatthew u_int16_t flags; 173503d86467Sjmatthew int i; 1736cad24394Sdlg int error; 173703d86467Sjmatthew 173803d86467Sjmatthew if (op == HIB_INIT) { 173903d86467Sjmatthew struct device *disk; 174003d86467Sjmatthew struct device *scsibus; 1741fc6d48fdSkrw struct nvm_identify_namespace *ns; 1742fc6d48fdSkrw struct nvm_namespace_format *f; 174303d86467Sjmatthew extern struct cfdriver sd_cd; 174403d86467Sjmatthew struct scsi_link *link; 174503d86467Sjmatthew struct scsibus_softc *bus_sc; 174603d86467Sjmatthew struct nvme_sqe_q qsqe; 174703d86467Sjmatthew struct nvme_cqe qcqe; 174803d86467Sjmatthew 174903d86467Sjmatthew /* find nvme softc */ 175003d86467Sjmatthew disk = disk_lookup(&sd_cd, DISKUNIT(dev)); 175103d86467Sjmatthew scsibus = disk->dv_parent; 175203d86467Sjmatthew my->sc = (struct nvme_softc *)disk->dv_parent->dv_parent; 175303d86467Sjmatthew 175403d86467Sjmatthew /* find scsi_link, which tells us the target */ 175503d86467Sjmatthew my->nsid = 0; 175603d86467Sjmatthew bus_sc = (struct scsibus_softc *)scsibus; 175703d86467Sjmatthew SLIST_FOREACH(link, &bus_sc->sc_link_list, bus_list) { 175803d86467Sjmatthew if (link->device_softc == disk) { 1759397f5692Skettenis my->nsid = link->target; 176003d86467Sjmatthew break; 176103d86467Sjmatthew } 176203d86467Sjmatthew } 176303d86467Sjmatthew if (my->nsid == 0) 176403d86467Sjmatthew return (EIO); 1765fc6d48fdSkrw ns = my->sc->sc_namespaces[my->nsid].ident; 1766fc6d48fdSkrw f = &ns->lbaf[NVME_ID_NS_FLBAS(ns->flbas)]; 176703d86467Sjmatthew 176803d86467Sjmatthew my->poffset = blkno; 176903d86467Sjmatthew my->psize = size; 1770fc6d48fdSkrw my->secsize = 1 << f->lbads; 177103d86467Sjmatthew 177203d86467Sjmatthew memset(NVME_DMA_KVA(my->sc->sc_hib_q->q_cq_dmamem), 0, 177303d86467Sjmatthew my->sc->sc_hib_q->q_entries * sizeof(struct nvme_cqe)); 177403d86467Sjmatthew memset(NVME_DMA_KVA(my->sc->sc_hib_q->q_sq_dmamem), 0, 177503d86467Sjmatthew my->sc->sc_hib_q->q_entries * sizeof(struct nvme_sqe)); 177603d86467Sjmatthew 177703d86467Sjmatthew my->sq_tail = 0; 177803d86467Sjmatthew my->cq_head = 0; 177903d86467Sjmatthew my->cqe_phase = NVME_CQE_PHASE; 178003d86467Sjmatthew 178103d86467Sjmatthew memset(&qsqe, 0, sizeof(qsqe)); 178203d86467Sjmatthew qsqe.opcode = NVM_ADMIN_ADD_IOCQ; 178303d86467Sjmatthew htolem64(&qsqe.prp1, 178403d86467Sjmatthew NVME_DMA_DVA(my->sc->sc_hib_q->q_cq_dmamem)); 178503d86467Sjmatthew htolem16(&qsqe.qsize, my->sc->sc_hib_q->q_entries - 1); 178603d86467Sjmatthew htolem16(&qsqe.qid, my->sc->sc_hib_q->q_id); 178703d86467Sjmatthew qsqe.qflags = NVM_SQE_CQ_IEN | NVM_SQE_Q_PC; 178803d86467Sjmatthew if (nvme_hibernate_admin_cmd(my->sc, (struct nvme_sqe *)&qsqe, 178903d86467Sjmatthew &qcqe, 1) != 0) 179003d86467Sjmatthew return (EIO); 179103d86467Sjmatthew 179203d86467Sjmatthew memset(&qsqe, 0, sizeof(qsqe)); 179303d86467Sjmatthew qsqe.opcode = NVM_ADMIN_ADD_IOSQ; 179403d86467Sjmatthew htolem64(&qsqe.prp1, 179503d86467Sjmatthew NVME_DMA_DVA(my->sc->sc_hib_q->q_sq_dmamem)); 179603d86467Sjmatthew htolem16(&qsqe.qsize, my->sc->sc_hib_q->q_entries - 1); 179703d86467Sjmatthew htolem16(&qsqe.qid, my->sc->sc_hib_q->q_id); 179803d86467Sjmatthew htolem16(&qsqe.cqid, my->sc->sc_hib_q->q_id); 179903d86467Sjmatthew qsqe.qflags = NVM_SQE_Q_PC; 180003d86467Sjmatthew if (nvme_hibernate_admin_cmd(my->sc, (struct nvme_sqe *)&qsqe, 180103d86467Sjmatthew &qcqe, 2) != 0) 180203d86467Sjmatthew return (EIO); 180303d86467Sjmatthew 180403d86467Sjmatthew return (0); 180503d86467Sjmatthew } 180603d86467Sjmatthew 180703d86467Sjmatthew if (op != HIB_W) 180803d86467Sjmatthew return (0); 180903d86467Sjmatthew 1810fc6d48fdSkrw if (blkno + (size / DEV_BSIZE) > my->psize) 1811fc6d48fdSkrw return E2BIG; 1812fc6d48fdSkrw 181303d86467Sjmatthew isqe = NVME_DMA_KVA(my->sc->sc_hib_q->q_sq_dmamem); 181403d86467Sjmatthew isqe += my->sq_tail; 181503d86467Sjmatthew if (++my->sq_tail == my->sc->sc_hib_q->q_entries) 181603d86467Sjmatthew my->sq_tail = 0; 181703d86467Sjmatthew 181803d86467Sjmatthew memset(isqe, 0, sizeof(*isqe)); 181903d86467Sjmatthew isqe->opcode = NVM_CMD_WRITE; 182003d86467Sjmatthew htolem32(&isqe->nsid, my->nsid); 182103d86467Sjmatthew 182203d86467Sjmatthew pmap_extract(pmap_kernel(), addr, &data_phys); 182303d86467Sjmatthew data_bus_phys = data_phys; 182403d86467Sjmatthew htolem64(&isqe->entry.prp[0], data_bus_phys); 182503d86467Sjmatthew if ((size > my->sc->sc_mps) && (size <= my->sc->sc_mps * 2)) { 182603d86467Sjmatthew htolem64(&isqe->entry.prp[1], data_bus_phys + my->sc->sc_mps); 182703d86467Sjmatthew } else if (size > my->sc->sc_mps * 2) { 182803d86467Sjmatthew pmap_extract(pmap_kernel(), (vaddr_t)page, &page_phys); 182903d86467Sjmatthew page_bus_phys = page_phys; 183003d86467Sjmatthew htolem64(&isqe->entry.prp[1], page_bus_phys + 183103d86467Sjmatthew offsetof(struct nvme_hibernate_page, prpl)); 18324e79e485Skrw for (i = 1; i < howmany(size, my->sc->sc_mps); i++) { 183303d86467Sjmatthew htolem64(&my->prpl[i - 1], data_bus_phys + 183403d86467Sjmatthew (i * my->sc->sc_mps)); 183503d86467Sjmatthew } 183603d86467Sjmatthew } 183703d86467Sjmatthew 1838fc6d48fdSkrw isqe->slba = (blkno + my->poffset) / (my->secsize / DEV_BSIZE); 1839fc6d48fdSkrw isqe->nlb = (size / my->secsize) - 1; 184003d86467Sjmatthew isqe->cid = blkno % 0xffff; 184103d86467Sjmatthew 184203d86467Sjmatthew nvme_write4(my->sc, NVME_SQTDBL(NVME_HIB_Q, my->sc->sc_dstrd), 184303d86467Sjmatthew my->sq_tail); 1844cad24394Sdlg nvme_barrier(my->sc, NVME_SQTDBL(NVME_HIB_Q, my->sc->sc_dstrd), 4, 1845cad24394Sdlg BUS_SPACE_BARRIER_WRITE); 1846cad24394Sdlg 1847cad24394Sdlg error = 0; 184803d86467Sjmatthew 184903d86467Sjmatthew icqe = NVME_DMA_KVA(my->sc->sc_hib_q->q_cq_dmamem); 185003d86467Sjmatthew icqe += my->cq_head; 1851cad24394Sdlg 1852cad24394Sdlg nvme_dmamem_sync(my->sc, my->sc->sc_hib_q->q_cq_dmamem, 1853cad24394Sdlg BUS_DMASYNC_POSTREAD); 185403d86467Sjmatthew for (;;) { 185503d86467Sjmatthew flags = lemtoh16(&icqe->flags); 1856cad24394Sdlg if ((flags & NVME_CQE_PHASE) == my->cqe_phase) { 1857cad24394Sdlg if ((NVME_CQE_SC(flags) != NVME_CQE_SC_SUCCESS) || 1858cad24394Sdlg (icqe->cid != blkno % 0xffff)) 1859cad24394Sdlg error = EIO; 186003d86467Sjmatthew 1861cad24394Sdlg break; 186203d86467Sjmatthew } 186303d86467Sjmatthew 1864cad24394Sdlg delay(1); 1865cad24394Sdlg nvme_dmamem_sync(my->sc, my->sc->sc_hib_q->q_cq_dmamem, 1866cad24394Sdlg BUS_DMASYNC_PREREAD|BUS_DMASYNC_POSTREAD); 1867cad24394Sdlg } 1868cad24394Sdlg nvme_dmamem_sync(my->sc, my->sc->sc_hib_q->q_cq_dmamem, 1869cad24394Sdlg BUS_DMASYNC_PREREAD); 1870cad24394Sdlg 187103d86467Sjmatthew if (++my->cq_head == my->sc->sc_hib_q->q_entries) { 187203d86467Sjmatthew my->cq_head = 0; 187303d86467Sjmatthew my->cqe_phase ^= NVME_CQE_PHASE; 187403d86467Sjmatthew } 1875cad24394Sdlg 187603d86467Sjmatthew nvme_write4(my->sc, NVME_CQHDBL(NVME_HIB_Q, my->sc->sc_dstrd), 187703d86467Sjmatthew my->cq_head); 1878cad24394Sdlg nvme_barrier(my->sc, NVME_CQHDBL(NVME_HIB_Q, my->sc->sc_dstrd), 4, 1879cad24394Sdlg BUS_SPACE_BARRIER_WRITE); 188003d86467Sjmatthew 1881cad24394Sdlg return (error); 188203d86467Sjmatthew } 188303d86467Sjmatthew 188403d86467Sjmatthew #endif 18857f4636ceSkrw 18867f4636ceSkrw #if NBIO > 0 18877f4636ceSkrw int 18887f4636ceSkrw nvme_bioctl(struct device *self, u_long cmd, caddr_t data) 18897f4636ceSkrw { 18907f4636ceSkrw struct nvme_softc *sc = (struct nvme_softc *)self; 18914e9514d6Skrw struct nvme_pt_cmd *pt; 18927f4636ceSkrw int error = 0; 18937f4636ceSkrw 18947f4636ceSkrw rw_enter_write(&sc->sc_lock); 18957f4636ceSkrw 18967f4636ceSkrw switch (cmd) { 18977f4636ceSkrw case BIOCINQ: 18987f4636ceSkrw error = nvme_bioctl_inq(sc, (struct bioc_inq *)data); 18997f4636ceSkrw break; 19007f4636ceSkrw case BIOCVOL: 19017f4636ceSkrw error = nvme_bioctl_vol(sc, (struct bioc_vol *)data); 19027f4636ceSkrw break; 19037f4636ceSkrw case BIOCDISK: 19047f4636ceSkrw error = nvme_bioctl_disk(sc, (struct bioc_disk *)data); 19057f4636ceSkrw break; 19064e9514d6Skrw case NVME_PASSTHROUGH_CMD: 19074e9514d6Skrw pt = (struct nvme_pt_cmd *)data; 19084e9514d6Skrw error = nvme_passthrough_cmd(sc, pt, sc->sc_dev.dv_unit, -1); 19094e9514d6Skrw break; 19107f4636ceSkrw default: 19117f4636ceSkrw printf("nvme_bioctl() Unknown command (%lu)\n", cmd); 19127f4636ceSkrw error = ENOTTY; 19137f4636ceSkrw } 19147f4636ceSkrw 19157f4636ceSkrw rw_exit_write(&sc->sc_lock); 19167f4636ceSkrw 19177f4636ceSkrw return error; 19187f4636ceSkrw } 19197f4636ceSkrw 19207f4636ceSkrw void 19217f4636ceSkrw nvme_bio_status(struct bio_status *bs, const char *fmt, ...) 19227f4636ceSkrw { 19237f4636ceSkrw va_list ap; 19247f4636ceSkrw 19257f4636ceSkrw va_start(ap, fmt); 19267f4636ceSkrw bio_status(bs, 0, BIO_MSG_INFO, fmt, &ap); 19277f4636ceSkrw va_end(ap); 19287f4636ceSkrw } 19297f4636ceSkrw 19307f4636ceSkrw const char * 19317f4636ceSkrw nvme_bioctl_sdname(const struct nvme_softc *sc, int target) 19327f4636ceSkrw { 19337f4636ceSkrw const struct scsi_link *link; 19347f4636ceSkrw const struct sd_softc *sd; 19357f4636ceSkrw 19367f4636ceSkrw link = scsi_get_link(sc->sc_scsibus, target, 0); 19379a0b8a7eSjsg if (link == NULL) 19389a0b8a7eSjsg return NULL; 19397f4636ceSkrw sd = (struct sd_softc *)(link->device_softc); 19407f4636ceSkrw if (ISSET(link->state, SDEV_S_DYING) || sd == NULL || 19417f4636ceSkrw ISSET(sd->flags, SDF_DYING)) 19427f4636ceSkrw return NULL; 19437f4636ceSkrw 194498f39564Skrw if (nvme_read4(sc, NVME_VS) == 0xffffffff) 19457f4636ceSkrw return NULL; 19467f4636ceSkrw 19477f4636ceSkrw return DEVNAME(sd); 19487f4636ceSkrw } 19497f4636ceSkrw 19507f4636ceSkrw int 19517f4636ceSkrw nvme_bioctl_inq(struct nvme_softc *sc, struct bioc_inq *bi) 19527f4636ceSkrw { 19537f4636ceSkrw char sn[41], mn[81], fr[17]; 19547f4636ceSkrw struct nvm_identify_controller *idctrl = &sc->sc_identify; 19557f4636ceSkrw struct bio_status *bs; 19567f4636ceSkrw unsigned int nn; 19577f4636ceSkrw uint32_t cc, csts, vs; 19587f4636ceSkrw 19597f4636ceSkrw /* Don't tell bioctl about namespaces > last configured namespace. */ 19607f4636ceSkrw for (nn = sc->sc_nn; nn > 0; nn--) { 19617f4636ceSkrw if (sc->sc_namespaces[nn].ident) 19627f4636ceSkrw break; 19637f4636ceSkrw } 19647f4636ceSkrw bi->bi_novol = bi->bi_nodisk = nn; 19657f4636ceSkrw strlcpy(bi->bi_dev, DEVNAME(sc), sizeof(bi->bi_dev)); 19667f4636ceSkrw 19677f4636ceSkrw bs = &bi->bi_bio.bio_status; 19687f4636ceSkrw bio_status_init(bs, &sc->sc_dev); 19697f4636ceSkrw bs->bs_status = BIO_STATUS_SUCCESS; 19707f4636ceSkrw 19717f4636ceSkrw scsi_strvis(sn, idctrl->sn, sizeof(idctrl->sn)); 19727f4636ceSkrw scsi_strvis(mn, idctrl->mn, sizeof(idctrl->mn)); 19737f4636ceSkrw scsi_strvis(fr, idctrl->fr, sizeof(idctrl->fr)); 19747f4636ceSkrw 19757f4636ceSkrw nvme_bio_status(bs, "%s, %s, %s", mn, fr, sn); 19767f4636ceSkrw nvme_bio_status(bs, "Max i/o %zu bytes%s%s%s, Sanitize 0x%b", 19777f4636ceSkrw sc->sc_mdts, 19787f4636ceSkrw ISSET(idctrl->lpa, NVM_ID_CTRL_LPA_PE) ? 197954fbbda3Sjsg ", Persistent Event Log" : "", 19807f4636ceSkrw ISSET(idctrl->fna, NVM_ID_CTRL_FNA_CRYPTOFORMAT) ? 19817f4636ceSkrw ", CryptoFormat" : "", 19827f4636ceSkrw ISSET(idctrl->vwc, NVM_ID_CTRL_VWC_PRESENT) ? 19837f4636ceSkrw ", Volatile Write Cache" : "", 19847f4636ceSkrw lemtoh32(&idctrl->sanicap), NVM_ID_CTRL_SANICAP_FMT 19857f4636ceSkrw ); 19867f4636ceSkrw 19877f4636ceSkrw if (idctrl->ctratt != 0) 19887f4636ceSkrw nvme_bio_status(bs, "Features 0x%b", lemtoh32(&idctrl->ctratt), 19897f4636ceSkrw NVM_ID_CTRL_CTRATT_FMT); 19907f4636ceSkrw 19917f4636ceSkrw if (idctrl->oacs || idctrl->oncs) { 19927f4636ceSkrw nvme_bio_status(bs, "Admin commands 0x%b, NVM commands 0x%b", 19937f4636ceSkrw lemtoh16(&idctrl->oacs), NVM_ID_CTRL_OACS_FMT, 19947f4636ceSkrw lemtoh16(&idctrl->oncs), NVM_ID_CTRL_ONCS_FMT); 19957f4636ceSkrw } 19967f4636ceSkrw 19977f4636ceSkrw cc = nvme_read4(sc, NVME_CC); 19987f4636ceSkrw csts = nvme_read4(sc, NVME_CSTS); 19997f4636ceSkrw vs = nvme_read4(sc, NVME_VS); 20007f4636ceSkrw 200198f39564Skrw if (vs == 0xffffffff) { 20027f4636ceSkrw nvme_bio_status(bs, "Invalid PCIe register mapping"); 20037f4636ceSkrw return 0; 20047f4636ceSkrw } 20057f4636ceSkrw 20067f4636ceSkrw nvme_bio_status(bs, "NVMe %u.%u%s%s%sabled, %sReady%s%s%s%s", 20077f4636ceSkrw NVME_VS_MJR(vs), NVME_VS_MNR(vs), 20087f4636ceSkrw (NVME_CC_CSS_R(cc) == NVME_CC_CSS_NVM) ? ", NVM I/O command set" : "", 20097f4636ceSkrw (NVME_CC_CSS_R(cc) == 0x7) ? ", Admin command set only" : "", 20107f4636ceSkrw ISSET(cc, NVME_CC_EN) ? ", En" : "Dis", 20117f4636ceSkrw ISSET(csts, NVME_CSTS_RDY) ? "" : "Not ", 20127f4636ceSkrw ISSET(csts, NVME_CSTS_CFS) ? ", Fatal Error, " : "", 20137f4636ceSkrw (NVME_CC_SHN_R(cc) == NVME_CC_SHN_NORMAL) ? ", Normal shutdown" : "", 20147f4636ceSkrw (NVME_CC_SHN_R(cc) == NVME_CC_SHN_ABRUPT) ? ", Abrupt shutdown" : "", 20157f4636ceSkrw ISSET(csts, NVME_CSTS_SHST_DONE) ? " complete" : ""); 20167f4636ceSkrw 20177f4636ceSkrw return 0; 20187f4636ceSkrw } 20197f4636ceSkrw 20207f4636ceSkrw int 20217f4636ceSkrw nvme_bioctl_vol(struct nvme_softc *sc, struct bioc_vol *bv) 20227f4636ceSkrw { 20237f4636ceSkrw const struct nvm_identify_namespace *idns; 20247f4636ceSkrw const char *sd; 20257f4636ceSkrw int target; 20267f4636ceSkrw unsigned int lbaf; 20277f4636ceSkrw 20287f4636ceSkrw target = bv->bv_volid + 1; 20297f4636ceSkrw if (target > sc->sc_nn) { 20307f4636ceSkrw bv->bv_status = BIOC_SVINVALID; 20317f4636ceSkrw return 0; 20327f4636ceSkrw } 20337f4636ceSkrw 20347f4636ceSkrw bv->bv_level = 'c'; 20357f4636ceSkrw bv->bv_nodisk = 1; 20367f4636ceSkrw 20377f4636ceSkrw idns = sc->sc_namespaces[target].ident; 20387f4636ceSkrw if (idns == NULL) { 20397f4636ceSkrw bv->bv_status = BIOC_SVINVALID; 20407f4636ceSkrw return 0; 20417f4636ceSkrw } 20427f4636ceSkrw 20437f4636ceSkrw lbaf = NVME_ID_NS_FLBAS(idns->flbas); 20447f4636ceSkrw if (idns->nlbaf > 16) 20457f4636ceSkrw lbaf |= (idns->flbas >> 1) & 0x3f; 20467f4636ceSkrw bv->bv_size = nvme_scsi_size(idns) << idns->lbaf[lbaf].lbads; 20477f4636ceSkrw 20487f4636ceSkrw sd = nvme_bioctl_sdname(sc, target); 20497f4636ceSkrw if (sd) { 20507f4636ceSkrw strlcpy(bv->bv_dev, sd, sizeof(bv->bv_dev)); 20517f4636ceSkrw bv->bv_status = BIOC_SVONLINE; 20527f4636ceSkrw } else 20537f4636ceSkrw bv->bv_status = BIOC_SVOFFLINE; 20547f4636ceSkrw 20557f4636ceSkrw return 0; 20567f4636ceSkrw } 20577f4636ceSkrw 20587f4636ceSkrw int 20597f4636ceSkrw nvme_bioctl_disk(struct nvme_softc *sc, struct bioc_disk *bd) 20607f4636ceSkrw { 20617f4636ceSkrw const char *rpdesc[4] = { 20627f4636ceSkrw " (Best)", 20637f4636ceSkrw " (Better)", 20647f4636ceSkrw " (Good)", 20657f4636ceSkrw " (Degraded)" 20667f4636ceSkrw }; 20677f4636ceSkrw const char *protection[4] = { 20687f4636ceSkrw "not enabled", 20697f4636ceSkrw "Type 1", 20707f4636ceSkrw "Type 2", 20717f4636ceSkrw "Type 3", 20727f4636ceSkrw }; 20737f4636ceSkrw char buf[32], msg[BIO_MSG_LEN]; 20747f4636ceSkrw struct nvm_identify_namespace *idns; 20757f4636ceSkrw struct bio_status *bs; 20767f4636ceSkrw uint64_t id1, id2; 20777f4636ceSkrw unsigned int i, lbaf, target; 20787f4636ceSkrw uint16_t ms; 20797f4636ceSkrw uint8_t dps; 20807f4636ceSkrw 20817f4636ceSkrw target = bd->bd_volid + 1; 20827f4636ceSkrw if (target > sc->sc_nn) 20837f4636ceSkrw return EINVAL; 20847f4636ceSkrw bd->bd_channel = sc->sc_scsibus->sc_dev.dv_unit; 20857f4636ceSkrw bd->bd_target = target; 20867f4636ceSkrw bd->bd_lun = 0; 20877f4636ceSkrw snprintf(bd->bd_procdev, sizeof(bd->bd_procdev), "Namespace %u", target); 20887f4636ceSkrw 20897f4636ceSkrw bs = &bd->bd_bio.bio_status; 20907f4636ceSkrw bs->bs_status = BIO_STATUS_SUCCESS; 20917f4636ceSkrw snprintf(bs->bs_controller, sizeof(bs->bs_controller), "%11u", 20927f4636ceSkrw bd->bd_diskid); 20937f4636ceSkrw 20947f4636ceSkrw idns = sc->sc_namespaces[target].ident; 20957f4636ceSkrw if (idns == NULL) { 20967f4636ceSkrw bd->bd_status = BIOC_SDUNUSED; 20977f4636ceSkrw return 0; 20987f4636ceSkrw } 20997f4636ceSkrw 21007f4636ceSkrw lbaf = NVME_ID_NS_FLBAS(idns->flbas); 21017f4636ceSkrw if (idns->nlbaf > nitems(idns->lbaf)) 21027f4636ceSkrw lbaf |= (idns->flbas >> 1) & 0x3f; 21037f4636ceSkrw bd->bd_size = lemtoh64(&idns->nsze) << idns->lbaf[lbaf].lbads; 21047f4636ceSkrw 21057f4636ceSkrw if (memcmp(idns->nguid, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16)) { 21067f4636ceSkrw memcpy(&id1, idns->nguid, sizeof(uint64_t)); 21077f4636ceSkrw memcpy(&id2, idns->nguid + sizeof(uint64_t), sizeof(uint64_t)); 21087f4636ceSkrw snprintf(bd->bd_serial, sizeof(bd->bd_serial), "%08llx%08llx", 21097f4636ceSkrw id1, id2); 21107f4636ceSkrw } else if (memcmp(idns->eui64, "\0\0\0\0\0\0\0\0", 8)) { 21117f4636ceSkrw memcpy(&id1, idns->eui64, sizeof(uint64_t)); 21127f4636ceSkrw snprintf(bd->bd_serial, sizeof(bd->bd_serial), "%08llx", id1); 21137f4636ceSkrw } 21147f4636ceSkrw 21157f4636ceSkrw msg[0] = '\0'; 21167f4636ceSkrw for (i = 0; i <= idns->nlbaf; i++) { 21177f4636ceSkrw if (idns->lbaf[i].lbads == 0) 21187f4636ceSkrw continue; 21197f4636ceSkrw snprintf(buf, sizeof(buf), "%s%s%u", 21207f4636ceSkrw strlen(msg) ? ", " : "", (i == lbaf) ? "*" : "", 21217f4636ceSkrw 1 << idns->lbaf[i].lbads); 21227f4636ceSkrw strlcat(msg, buf, sizeof(msg)); 21237f4636ceSkrw ms = lemtoh16(&idns->lbaf[i].ms); 21247f4636ceSkrw if (ms) { 21257f4636ceSkrw snprintf(buf, sizeof(buf), "+%u", ms); 21267f4636ceSkrw strlcat(msg, buf, sizeof(msg)); 21277f4636ceSkrw } 21287f4636ceSkrw strlcat(msg, rpdesc[idns->lbaf[i].rp], sizeof(msg)); 21297f4636ceSkrw } 21307f4636ceSkrw nvme_bio_status(bs, "Formats %s", msg); 21317f4636ceSkrw 21327f4636ceSkrw if (idns->nsfeat) 21337f4636ceSkrw nvme_bio_status(bs, "Features 0x%b", idns->nsfeat, 21347f4636ceSkrw NVME_ID_NS_NSFEAT_FMT); 21357f4636ceSkrw 21367f4636ceSkrw if (idns->dps) { 21377f4636ceSkrw dps = idns->dps; 21387f4636ceSkrw snprintf(msg, sizeof(msg), "Data Protection (0x%02x) " 21397f4636ceSkrw "Protection Data in ", dps); 21407f4636ceSkrw if (ISSET(dps, NVME_ID_NS_DPS_PIP)) 21417f4636ceSkrw strlcat(msg, "first", sizeof(msg)); 21427f4636ceSkrw else 21437f4636ceSkrw strlcat(msg, "last", sizeof(msg)); 21447f4636ceSkrw strlcat(msg, "bytes of metadata, Protection ", sizeof(msg)); 21457f4636ceSkrw if (NVME_ID_NS_DPS_TYPE(dps) >= nitems(protection)) 21467f4636ceSkrw strlcat(msg, "Type unknown", sizeof(msg)); 21477f4636ceSkrw else 21487f4636ceSkrw strlcat(msg, protection[NVME_ID_NS_DPS_TYPE(dps)], 21497f4636ceSkrw sizeof(msg)); 21507f4636ceSkrw nvme_bio_status(bs, "%s", msg); 21517f4636ceSkrw } 21527f4636ceSkrw 21537f4636ceSkrw if (nvme_bioctl_sdname(sc, target) == NULL) 21547f4636ceSkrw bd->bd_status = BIOC_SDOFFLINE; 21557f4636ceSkrw else 21567f4636ceSkrw bd->bd_status = BIOC_SDONLINE; 21577f4636ceSkrw 21587f4636ceSkrw return 0; 21597f4636ceSkrw } 21607f4636ceSkrw #endif /* NBIO > 0 */ 21613800fc35Sjmatthew 21623800fc35Sjmatthew #ifndef SMALL_KERNEL 21633800fc35Sjmatthew void 21643800fc35Sjmatthew nvme_refresh_sensors(void *arg) 21653800fc35Sjmatthew { 21663800fc35Sjmatthew struct nvme_softc *sc = arg; 21673800fc35Sjmatthew struct nvme_sqe sqe; 21683800fc35Sjmatthew struct nvme_dmamem *mem = NULL; 21693800fc35Sjmatthew struct nvme_ccb *ccb = NULL; 21703800fc35Sjmatthew struct nvm_smart_health *health; 21713800fc35Sjmatthew uint32_t dwlen; 21723800fc35Sjmatthew uint8_t cw; 21733800fc35Sjmatthew int flags; 21743800fc35Sjmatthew int64_t temp; 21753800fc35Sjmatthew 21763800fc35Sjmatthew ccb = nvme_ccb_get(sc); 21773800fc35Sjmatthew if (ccb == NULL) 21783800fc35Sjmatthew goto failed; 21793800fc35Sjmatthew 21803800fc35Sjmatthew mem = nvme_dmamem_alloc(sc, sizeof(*health)); 21813800fc35Sjmatthew if (mem == NULL) 21823800fc35Sjmatthew goto failed; 21833800fc35Sjmatthew nvme_dmamem_sync(sc, mem, BUS_DMASYNC_PREREAD); 21843800fc35Sjmatthew 21853800fc35Sjmatthew dwlen = (sizeof(*health) >> 2) - 1; 21863800fc35Sjmatthew memset(&sqe, 0, sizeof(sqe)); 21873800fc35Sjmatthew sqe.opcode = NVM_ADMIN_GET_LOG_PG; 21883800fc35Sjmatthew htolem32(&sqe.nsid, 0xffffffff); 21893800fc35Sjmatthew htolem32(&sqe.cdw10, (dwlen << 16 | NVM_LOG_PAGE_SMART_HEALTH)); 21903800fc35Sjmatthew htolem64(&sqe.entry.prp[0], NVME_DMA_DVA(mem)); 21913800fc35Sjmatthew 21923800fc35Sjmatthew ccb->ccb_done = nvme_empty_done; 21933800fc35Sjmatthew ccb->ccb_cookie = &sqe; 21943800fc35Sjmatthew flags = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_LOG_PAGE); 21953800fc35Sjmatthew 21963800fc35Sjmatthew nvme_dmamem_sync(sc, mem, BUS_DMASYNC_POSTREAD); 21973800fc35Sjmatthew 21983800fc35Sjmatthew if (flags != 0) 21993800fc35Sjmatthew goto failed; 22003800fc35Sjmatthew 22013800fc35Sjmatthew health = NVME_DMA_KVA(mem); 22023800fc35Sjmatthew cw = health->critical_warning; 22033800fc35Sjmatthew 22043800fc35Sjmatthew sc->sc_temp_sensor.status = (cw & NVM_HEALTH_CW_TEMP) ? 22053800fc35Sjmatthew SENSOR_S_CRIT : SENSOR_S_OK; 22063800fc35Sjmatthew temp = letoh16(health->temperature); 22073800fc35Sjmatthew sc->sc_temp_sensor.value = (temp * 1000000) + 150000; 22083800fc35Sjmatthew 22093800fc35Sjmatthew sc->sc_spare_sensor.status = (cw & NVM_HEALTH_CW_SPARE) ? 22103800fc35Sjmatthew SENSOR_S_CRIT : SENSOR_S_OK; 22113800fc35Sjmatthew sc->sc_spare_sensor.value = health->avail_spare * 1000; 22123800fc35Sjmatthew 22133800fc35Sjmatthew sc->sc_usage_sensor.status = SENSOR_S_OK; 22143800fc35Sjmatthew sc->sc_usage_sensor.value = health->percent_used * 1000; 22153800fc35Sjmatthew goto done; 22163800fc35Sjmatthew 22173800fc35Sjmatthew failed: 22183800fc35Sjmatthew sc->sc_temp_sensor.status = SENSOR_S_UNKNOWN; 22193800fc35Sjmatthew sc->sc_usage_sensor.status = SENSOR_S_UNKNOWN; 22203800fc35Sjmatthew sc->sc_spare_sensor.status = SENSOR_S_UNKNOWN; 22213800fc35Sjmatthew done: 22223800fc35Sjmatthew if (mem != NULL) 22233800fc35Sjmatthew nvme_dmamem_free(sc, mem); 22243800fc35Sjmatthew if (ccb != NULL) 22253800fc35Sjmatthew nvme_ccb_put(sc, ccb); 22263800fc35Sjmatthew } 22273800fc35Sjmatthew #endif /* SMALL_KERNEL */ 2228