diff --git a/sys/cam/scsi/scsi_enc.c b/sys/cam/scsi/scsi_enc.c index e01f613514fb..b8afdf938a3a 100644 --- a/sys/cam/scsi/scsi_enc.c +++ b/sys/cam/scsi/scsi_enc.c @@ -1,1030 +1,1030 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2000 Matthew Jacob * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "opt_ses.h" MALLOC_DEFINE(M_SCSIENC, "SCSI ENC", "SCSI ENC buffers"); /* Enclosure type independent driver */ static d_open_t enc_open; static d_close_t enc_close; static d_ioctl_t enc_ioctl; static periph_init_t enc_init; static periph_ctor_t enc_ctor; static periph_oninv_t enc_oninvalidate; static periph_dtor_t enc_dtor; static void enc_async(void *, uint32_t, struct cam_path *, void *); static enctyp enc_type(struct ccb_getdev *); SYSCTL_NODE(_kern_cam, OID_AUTO, enc, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "CAM Enclosure Services driver"); #if defined(DEBUG) || defined(ENC_DEBUG) int enc_verbose = 1; #else int enc_verbose = 0; #endif SYSCTL_INT(_kern_cam_enc, OID_AUTO, verbose, CTLFLAG_RWTUN, &enc_verbose, 0, "Enable verbose logging"); const char *elm_type_names[] = ELM_TYPE_NAMES; CTASSERT(nitems(elm_type_names) - 1 == ELMTYP_LAST); static struct periph_driver encdriver = { enc_init, "ses", TAILQ_HEAD_INITIALIZER(encdriver.units), /* generation */ 0 }; PERIPHDRIVER_DECLARE(enc, encdriver); static struct cdevsw enc_cdevsw = { .d_version = D_VERSION, .d_open = enc_open, .d_close = enc_close, .d_ioctl = enc_ioctl, .d_name = "ses", .d_flags = D_TRACKCLOSE, }; static void enc_init(void) { cam_status status; /* * Install a global async callback. This callback will * receive async callbacks like "new device found". */ status = xpt_register_async(AC_FOUND_DEVICE, enc_async, NULL, NULL); if (status != CAM_REQ_CMP) { printf("enc: Failed to attach master async callback " "due to status 0x%x!\n", status); } } static void enc_devgonecb(void *arg) { struct cam_periph *periph; struct enc_softc *enc; struct mtx *mtx; int i; periph = (struct cam_periph *)arg; mtx = cam_periph_mtx(periph); mtx_lock(mtx); enc = (struct enc_softc *)periph->softc; /* * When we get this callback, we will get no more close calls from * devfs. So if we have any dangling opens, we need to release the * reference held for that particular context. */ for (i = 0; i < enc->open_count; i++) cam_periph_release_locked(periph); enc->open_count = 0; /* * Release the reference held for the device node, it is gone now. */ cam_periph_release_locked(periph); /* * We reference the lock directly here, instead of using * cam_periph_unlock(). The reason is that the final call to * cam_periph_release_locked() above could result in the periph * getting freed. If that is the case, dereferencing the periph * with a cam_periph_unlock() call would cause a page fault. */ mtx_unlock(mtx); } static void enc_oninvalidate(struct cam_periph *periph) { struct enc_softc *enc; enc = periph->softc; enc->enc_flags |= ENC_FLAG_INVALID; /* If the sub-driver has an invalidate routine, call it */ if (enc->enc_vec.softc_invalidate != NULL) enc->enc_vec.softc_invalidate(enc); /* * Unregister any async callbacks. */ xpt_register_async(0, enc_async, periph, periph->path); /* * Shutdown our daemon. */ enc->enc_flags |= ENC_FLAG_SHUTDOWN; if (enc->enc_daemon != NULL) { /* Signal the ses daemon to terminate. */ wakeup(enc->enc_daemon); } callout_drain(&enc->status_updater); destroy_dev_sched_cb(enc->enc_dev, enc_devgonecb, periph); } static void enc_dtor(struct cam_periph *periph) { struct enc_softc *enc; enc = periph->softc; /* If the sub-driver has a cleanup routine, call it */ if (enc->enc_vec.softc_cleanup != NULL) enc->enc_vec.softc_cleanup(enc); root_mount_rel(&enc->enc_rootmount); ENC_FREE(enc); } static void enc_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg) { struct cam_periph *periph; periph = (struct cam_periph *)callback_arg; switch(code) { case AC_FOUND_DEVICE: { struct ccb_getdev *cgd; cam_status status; path_id_t path_id; cgd = (struct ccb_getdev *)arg; if (arg == NULL) { break; } if (enc_type(cgd) == ENC_NONE) { /* * Schedule announcement of the ENC bindings for * this device if it is managed by a SEP. */ path_id = xpt_path_path_id(path); xpt_lock_buses(); TAILQ_FOREACH(periph, &encdriver.units, unit_links) { struct enc_softc *softc; softc = (struct enc_softc *)periph->softc; /* Check this SEP is ready. */ if (softc == NULL || (softc->enc_flags & ENC_FLAG_INITIALIZED) == 0 || softc->enc_vec.device_found == NULL) continue; /* Check this SEP may manage this device. */ if (xpt_path_path_id(periph->path) != path_id && (softc->enc_type != ENC_SEMB_SES || cgd->protocol != PROTO_ATA)) continue; softc->enc_vec.device_found(softc); } xpt_unlock_buses(); return; } status = cam_periph_alloc(enc_ctor, enc_oninvalidate, enc_dtor, NULL, "ses", CAM_PERIPH_BIO, path, enc_async, AC_FOUND_DEVICE, cgd); if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) { printf("enc_async: Unable to probe new device due to " "status 0x%x\n", status); } break; } default: cam_periph_async(periph, code, path, arg); break; } } static int enc_open(struct cdev *dev, int flags, int fmt, struct thread *td) { struct cam_periph *periph; struct enc_softc *softc; int error = 0; periph = (struct cam_periph *)dev->si_drv1; if (cam_periph_acquire(periph) != 0) return (ENXIO); cam_periph_lock(periph); softc = (struct enc_softc *)periph->softc; if ((softc->enc_flags & ENC_FLAG_INITIALIZED) == 0) { error = ENXIO; goto out; } if (softc->enc_flags & ENC_FLAG_INVALID) { error = ENXIO; goto out; } out: if (error != 0) cam_periph_release_locked(periph); else softc->open_count++; cam_periph_unlock(periph); return (error); } static int enc_close(struct cdev *dev, int flag, int fmt, struct thread *td) { struct cam_periph *periph; struct enc_softc *enc; struct mtx *mtx; periph = (struct cam_periph *)dev->si_drv1; mtx = cam_periph_mtx(periph); mtx_lock(mtx); enc = periph->softc; enc->open_count--; cam_periph_release_locked(periph); /* * We reference the lock directly here, instead of using * cam_periph_unlock(). The reason is that the call to * cam_periph_release_locked() above could result in the periph * getting freed. If that is the case, dereferencing the periph * with a cam_periph_unlock() call would cause a page fault. * * cam_periph_release() avoids this problem using the same method, * but we're manually acquiring and dropping the lock here to * protect the open count and avoid another lock acquisition and * release. */ mtx_unlock(mtx); return (0); } int enc_error(union ccb *ccb, uint32_t cflags, uint32_t sflags) { return (cam_periph_error(ccb, cflags, sflags)); } static int enc_ioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag, struct thread *td) { struct cam_periph *periph; - encioc_enc_status_t tmp; - encioc_string_t sstr; - encioc_elm_status_t elms; - encioc_elm_desc_t elmd; - encioc_elm_devnames_t elmdn; - encioc_element_t *uelm; enc_softc_t *enc; enc_cache_t *cache; void *addr; int error, i; #ifdef COMPAT_FREEBSD32 if (SV_PROC_FLAG(td->td_proc, SV_ILP32)) return (ENOTTY); #endif if (arg_addr) addr = *((caddr_t *) arg_addr); else addr = NULL; periph = (struct cam_periph *)dev->si_drv1; CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering encioctl\n")); cam_periph_lock(periph); enc = (struct enc_softc *)periph->softc; cache = &enc->enc_cache; /* * Now check to see whether we're initialized or not. * This actually should never fail as we're not supposed * to get past enc_open w/o successfully initializing * things. */ if ((enc->enc_flags & ENC_FLAG_INITIALIZED) == 0) { cam_periph_unlock(periph); return (ENXIO); } cam_periph_unlock(periph); error = 0; CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("trying to do ioctl %#lx\n", cmd)); /* * If this command can change the device's state, * we must have the device open for writing. * * For commands that get information about the * device- we don't need to lock the peripheral * if we aren't running a command. The periph * also can't go away while a user process has * it open. */ switch (cmd) { case ENCIOC_GETNELM: case ENCIOC_GETELMMAP: case ENCIOC_GETENCSTAT: case ENCIOC_GETELMSTAT: case ENCIOC_GETELMDESC: case ENCIOC_GETELMDEVNAMES: case ENCIOC_GETENCNAME: case ENCIOC_GETENCID: break; default: if ((flag & FWRITE) == 0) { return (EBADF); } } /* * XXX The values read here are only valid for the current * configuration generation. We need these ioctls * to also pass in/out a generation number. */ sx_slock(&enc->enc_cache_lock); switch (cmd) { case ENCIOC_GETNELM: error = copyout(&cache->nelms, addr, sizeof (cache->nelms)); break; - case ENCIOC_GETELMMAP: + case ENCIOC_GETELMMAP: { + encioc_element_t *uelm; + for (uelm = addr, i = 0; i != cache->nelms; i++) { encioc_element_t kelm; kelm.elm_idx = i; kelm.elm_subenc_id = cache->elm_map[i].subenclosure; kelm.elm_type = cache->elm_map[i].elm_type; error = copyout(&kelm, &uelm[i], sizeof(kelm)); if (error) break; } break; - - case ENCIOC_GETENCSTAT: - cam_periph_lock(periph); - error = enc->enc_vec.get_enc_status(enc, 1); - if (error) { - cam_periph_unlock(periph); - break; - } - tmp = cache->enc_status; - cam_periph_unlock(periph); - error = copyout(&tmp, addr, sizeof(tmp)); - cache->enc_status = tmp; + } + case ENCIOC_GETENCSTAT: { + error = copyout(&cache->enc_status, addr, + sizeof(cache->enc_status)); break; + } + case ENCIOC_SETENCSTAT: { + encioc_enc_status_t tmp; - case ENCIOC_SETENCSTAT: error = copyin(addr, &tmp, sizeof(tmp)); if (error) break; cam_periph_lock(periph); error = enc->enc_vec.set_enc_status(enc, tmp, 1); cam_periph_unlock(periph); break; - + } case ENCIOC_GETSTRING: case ENCIOC_SETSTRING: case ENCIOC_GETENCNAME: - case ENCIOC_GETENCID: + case ENCIOC_GETENCID: { + encioc_string_t sstr; + if (enc->enc_vec.handle_string == NULL) { error = EINVAL; break; } error = copyin(addr, &sstr, sizeof(sstr)); if (error) break; cam_periph_lock(periph); error = enc->enc_vec.handle_string(enc, &sstr, cmd); cam_periph_unlock(periph); if (error == 0 || error == ENOMEM) (void)copyout(&sstr.bufsiz, &((encioc_string_t *)addr)->bufsiz, sizeof(sstr.bufsiz)); break; + } + case ENCIOC_GETELMSTAT: { + encioc_elm_status_t elms; - case ENCIOC_GETELMSTAT: error = copyin(addr, &elms, sizeof(elms)); if (error) break; if (elms.elm_idx >= cache->nelms) { error = EINVAL; break; } cam_periph_lock(periph); error = enc->enc_vec.get_elm_status(enc, &elms, 1); cam_periph_unlock(periph); if (error) break; error = copyout(&elms, addr, sizeof(elms)); break; + } + case ENCIOC_GETELMDESC: { + encioc_elm_desc_t elmd; - case ENCIOC_GETELMDESC: error = copyin(addr, &elmd, sizeof(elmd)); if (error) break; if (elmd.elm_idx >= cache->nelms) { error = EINVAL; break; } if (enc->enc_vec.get_elm_desc != NULL) { error = enc->enc_vec.get_elm_desc(enc, &elmd); if (error) break; } else elmd.elm_desc_len = 0; error = copyout(&elmd, addr, sizeof(elmd)); break; + } + case ENCIOC_GETELMDEVNAMES: { + encioc_elm_devnames_t elmdn; - case ENCIOC_GETELMDEVNAMES: if (enc->enc_vec.get_elm_devnames == NULL) { error = EINVAL; break; } error = copyin(addr, &elmdn, sizeof(elmdn)); if (error) break; if (elmdn.elm_idx >= cache->nelms) { error = EINVAL; break; } cam_periph_lock(periph); error = (*enc->enc_vec.get_elm_devnames)(enc, &elmdn); cam_periph_unlock(periph); if (error) break; error = copyout(&elmdn, addr, sizeof(elmdn)); break; + } + case ENCIOC_SETELMSTAT: { + encioc_elm_status_t elms; - case ENCIOC_SETELMSTAT: error = copyin(addr, &elms, sizeof(elms)); if (error) break; if (elms.elm_idx >= cache->nelms) { error = EINVAL; break; } cam_periph_lock(periph); error = enc->enc_vec.set_elm_status(enc, &elms, 1); cam_periph_unlock(periph); break; - + } case ENCIOC_INIT: cam_periph_lock(periph); error = enc->enc_vec.init_enc(enc); cam_periph_unlock(periph); break; default: cam_periph_lock(periph); error = cam_periph_ioctl(periph, cmd, arg_addr, enc_error); cam_periph_unlock(periph); break; } sx_sunlock(&enc->enc_cache_lock); return (error); } int enc_runcmd(struct enc_softc *enc, char *cdb, int cdbl, char *dptr, int *dlenp) { int error, dlen, tdlen; ccb_flags ddf; union ccb *ccb; CAM_DEBUG(enc->periph->path, CAM_DEBUG_TRACE, ("entering enc_runcmd\n")); if (dptr) { if ((dlen = *dlenp) < 0) { dlen = -dlen; ddf = CAM_DIR_OUT; } else { ddf = CAM_DIR_IN; } } else { dlen = 0; ddf = CAM_DIR_NONE; } if (cdbl > IOCDBLEN) { cdbl = IOCDBLEN; } ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL); if (enc->enc_type == ENC_SEMB_SES || enc->enc_type == ENC_SEMB_SAFT) { tdlen = min(dlen, 1020); tdlen = (tdlen + 3) & ~3; cam_fill_ataio(&ccb->ataio, 0, NULL, ddf, 0, dptr, tdlen, 30 * 1000); if (cdb[0] == RECEIVE_DIAGNOSTIC) ata_28bit_cmd(&ccb->ataio, ATA_SEP_ATTN, cdb[2], 0x02, tdlen / 4); else if (cdb[0] == SEND_DIAGNOSTIC) ata_28bit_cmd(&ccb->ataio, ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0, 0x82, tdlen / 4); else if (cdb[0] == READ_BUFFER) ata_28bit_cmd(&ccb->ataio, ATA_SEP_ATTN, cdb[2], 0x00, tdlen / 4); else ata_28bit_cmd(&ccb->ataio, ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0, 0x80, tdlen / 4); } else { tdlen = dlen; cam_fill_csio(&ccb->csio, 0, NULL, ddf, MSG_SIMPLE_Q_TAG, dptr, dlen, sizeof (struct scsi_sense_data), cdbl, 60 * 1000); bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cdbl); } error = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS, NULL); if (error) { if (dptr) { *dlenp = dlen; } } else { if (dptr) { if (ccb->ccb_h.func_code == XPT_ATA_IO) *dlenp = ccb->ataio.resid; else *dlenp = ccb->csio.resid; *dlenp += tdlen - dlen; } } xpt_release_ccb(ccb); CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, ("exiting enc_runcmd: *dlenp = %d\n", *dlenp)); return (error); } void enc_log(struct enc_softc *enc, const char *fmt, ...) { va_list ap; printf("%s%d: ", enc->periph->periph_name, enc->periph->unit_number); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } /* * The code after this point runs on many platforms, * so forgive the slightly awkward and nonconforming * appearance. */ /* * Is this a device that supports enclosure services? * * It's a pretty simple ruleset- if it is device type * 0x0D (13), it's an ENCLOSURE device. */ #define SAFTE_START 44 #define SAFTE_END 50 #define SAFTE_LEN SAFTE_END-SAFTE_START static enctyp enc_type(struct ccb_getdev *cgd) { int buflen; unsigned char *iqd; if (cgd->protocol == PROTO_SEMB) { iqd = (unsigned char *)&cgd->ident_data; if (STRNCMP(iqd + 43, "S-E-S", 5) == 0) return (ENC_SEMB_SES); else if (STRNCMP(iqd + 43, "SAF-TE", 6) == 0) return (ENC_SEMB_SAFT); return (ENC_NONE); } else if (cgd->protocol != PROTO_SCSI) return (ENC_NONE); iqd = (unsigned char *)&cgd->inq_data; buflen = min(sizeof(cgd->inq_data), SID_ADDITIONAL_LENGTH(&cgd->inq_data)); if ((iqd[0] & 0x1f) == T_ENCLOSURE) return (ENC_SES); #ifdef SES_ENABLE_PASSTHROUGH if ((iqd[6] & 0x40) && (iqd[2] & 0x7) >= 2) { /* * PassThrough Device. */ return (ENC_SES_PASSTHROUGH); } #endif /* * The comparison is short for a reason- * some vendors were chopping it short. */ if (buflen < SAFTE_END - 2) { return (ENC_NONE); } if (STRNCMP((char *)&iqd[SAFTE_START], "SAF-TE", SAFTE_LEN - 2) == 0) { return (ENC_SAFT); } return (ENC_NONE); } /*================== Enclosure Monitoring/Processing Daemon ==================*/ /** * \brief Queue an update request for a given action, if needed. * * \param enc SES softc to queue the request for. * \param action Action requested. */ void enc_update_request(enc_softc_t *enc, uint32_t action) { if ((enc->pending_actions & (0x1 << action)) == 0) { enc->pending_actions |= (0x1 << action); ENC_DLOG(enc, "%s: queing requested action %d\n", __func__, action); if (enc->current_action == ENC_UPDATE_NONE) wakeup(enc->enc_daemon); } else { ENC_DLOG(enc, "%s: ignoring requested action %d - " "Already queued\n", __func__, action); } } /** * \brief Invoke the handler of the highest priority pending * state in the SES state machine. * * \param enc The SES instance invoking the state machine. */ static void enc_fsm_step(enc_softc_t *enc) { union ccb *ccb; uint8_t *buf; struct enc_fsm_state *cur_state; int error; uint32_t xfer_len; ENC_DLOG(enc, "%s enter %p\n", __func__, enc); enc->current_action = ffs(enc->pending_actions) - 1; enc->pending_actions &= ~(0x1 << enc->current_action); cur_state = &enc->enc_fsm_states[enc->current_action]; buf = NULL; if (cur_state->buf_size != 0) { cam_periph_unlock(enc->periph); buf = malloc(cur_state->buf_size, M_SCSIENC, M_WAITOK|M_ZERO); cam_periph_lock(enc->periph); } error = 0; ccb = NULL; if (cur_state->fill != NULL) { ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL); error = cur_state->fill(enc, cur_state, ccb, buf); if (error != 0) goto done; error = cam_periph_runccb(ccb, cur_state->error, ENC_CFLAGS, ENC_FLAGS|SF_QUIET_IR, NULL); } if (ccb != NULL) { if (ccb->ccb_h.func_code == XPT_ATA_IO) xfer_len = ccb->ataio.dxfer_len - ccb->ataio.resid; else xfer_len = ccb->csio.dxfer_len - ccb->csio.resid; } else xfer_len = 0; cam_periph_unlock(enc->periph); cur_state->done(enc, cur_state, ccb, &buf, error, xfer_len); cam_periph_lock(enc->periph); done: ENC_DLOG(enc, "%s exit - result %d\n", __func__, error); ENC_FREE_AND_NULL(buf); if (ccb != NULL) xpt_release_ccb(ccb); } /** * \invariant Called with cam_periph mutex held. */ static void enc_status_updater(void *arg) { enc_softc_t *enc; enc = arg; if (enc->enc_vec.poll_status != NULL) enc->enc_vec.poll_status(enc); } static void enc_daemon(void *arg) { enc_softc_t *enc; enc = arg; cam_periph_lock(enc->periph); while ((enc->enc_flags & ENC_FLAG_SHUTDOWN) == 0) { if (enc->pending_actions == 0) { /* * Reset callout and msleep, or * issue timed task completion * status command. */ enc->current_action = ENC_UPDATE_NONE; /* * We've been through our state machine at least * once. Allow the transition to userland. */ root_mount_rel(&enc->enc_rootmount); callout_reset_sbt(&enc->status_updater, 60 * SBT_1S, 0, enc_status_updater, enc, C_PREL(1)); cam_periph_sleep(enc->periph, enc->enc_daemon, PUSER, "idle", 0); } else { enc_fsm_step(enc); } } enc->enc_daemon = NULL; cam_periph_unlock(enc->periph); cam_periph_release(enc->periph); kproc_exit(0); } static int enc_kproc_init(enc_softc_t *enc) { int result; callout_init_mtx(&enc->status_updater, cam_periph_mtx(enc->periph), 0); if (cam_periph_acquire(enc->periph) != 0) return (ENXIO); result = kproc_create(enc_daemon, enc, &enc->enc_daemon, /*flags*/0, /*stackpgs*/0, "enc_daemon%d", enc->periph->unit_number); if (result == 0) { /* Do an initial load of all page data. */ cam_periph_lock(enc->periph); enc->enc_vec.poll_status(enc); cam_periph_unlock(enc->periph); } else cam_periph_release(enc->periph); return (result); } static cam_status enc_ctor(struct cam_periph *periph, void *arg) { cam_status status = CAM_REQ_CMP_ERR; int err; enc_softc_t *enc; struct ccb_getdev *cgd; char *tname; struct make_dev_args args; struct sbuf sb; cgd = (struct ccb_getdev *)arg; if (cgd == NULL) { printf("enc_ctor: no getdev CCB, can't register device\n"); goto out; } enc = ENC_MALLOCZ(sizeof(*enc)); if (enc == NULL) { printf("enc_ctor: Unable to probe new device. " "Unable to allocate enc\n"); goto out; } enc->periph = periph; enc->current_action = ENC_UPDATE_INVALID; enc->enc_type = enc_type(cgd); sx_init(&enc->enc_cache_lock, "enccache"); switch (enc->enc_type) { case ENC_SES: case ENC_SES_PASSTHROUGH: case ENC_SEMB_SES: err = ses_softc_init(enc); break; case ENC_SAFT: case ENC_SEMB_SAFT: err = safte_softc_init(enc); break; case ENC_NONE: default: ENC_FREE(enc); return (CAM_REQ_CMP_ERR); } if (err) { xpt_print(periph->path, "error %d initializing\n", err); goto out; } /* * Hold off userland until we have made at least one pass * through our state machine so that physical path data is * present. */ if (enc->enc_vec.poll_status != NULL) { root_mount_hold_token(periph->periph_name, &enc->enc_rootmount); } /* * The softc field is set only once the enc is fully initialized * so that we can rely on this field to detect partially * initialized periph objects in the AC_FOUND_DEVICE handler. */ periph->softc = enc; cam_periph_unlock(periph); if (enc->enc_vec.poll_status != NULL) { err = enc_kproc_init(enc); if (err) { xpt_print(periph->path, "error %d starting enc_daemon\n", err); goto out; } } /* * Acquire a reference to the periph before we create the devfs * instance for it. We'll release this reference once the devfs * instance has been freed. */ if (cam_periph_acquire(periph) != 0) { xpt_print(periph->path, "%s: lost periph during " "registration!\n", __func__); cam_periph_lock(periph); return (CAM_REQ_CMP_ERR); } make_dev_args_init(&args); args.mda_devsw = &enc_cdevsw; args.mda_unit = periph->unit_number; args.mda_uid = UID_ROOT; args.mda_gid = GID_OPERATOR; args.mda_mode = 0600; args.mda_si_drv1 = periph; err = make_dev_s(&args, &enc->enc_dev, "%s%d", periph->periph_name, periph->unit_number); cam_periph_lock(periph); if (err != 0) { cam_periph_release_locked(periph); return (CAM_REQ_CMP_ERR); } enc->enc_flags |= ENC_FLAG_INITIALIZED; /* * Add an async callback so that we get notified if this * device goes away. */ xpt_register_async(AC_LOST_DEVICE, enc_async, periph, periph->path); switch (enc->enc_type) { default: case ENC_NONE: tname = "No ENC device"; break; case ENC_SES: tname = "SES Device"; break; case ENC_SES_PASSTHROUGH: tname = "SES Passthrough Device"; break; case ENC_SAFT: tname = "SAF-TE Device"; break; case ENC_SEMB_SES: tname = "SEMB SES Device"; break; case ENC_SEMB_SAFT: tname = "SEMB SAF-TE Device"; break; } sbuf_new(&sb, enc->announce_buf, ENC_ANNOUNCE_SZ, SBUF_FIXEDLEN); xpt_announce_periph_sbuf(periph, &sb, tname); sbuf_finish(&sb); sbuf_putbuf(&sb); status = CAM_REQ_CMP; out: if (status != CAM_REQ_CMP) enc_dtor(periph); return (status); } diff --git a/sys/cam/scsi/scsi_enc.h b/sys/cam/scsi/scsi_enc.h index b816a6a5d4cd..f53a99f2b233 100644 --- a/sys/cam/scsi/scsi_enc.h +++ b/sys/cam/scsi/scsi_enc.h @@ -1,257 +1,257 @@ /* $FreeBSD$ */ /*- * SPDX-License-Identifier: (BSD-2-Clause-FreeBSD OR GPL-2.0) * * Copyright (c) 2000 by Matthew Jacob * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * the GNU Public License ("GPL"). * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef _SCSI_ENC_H_ #define _SCSI_ENC_H_ #include #define ENCIOC ('s' - 040) #define ENCIOC_GETNELM _IO(ENCIOC, 1) #define ENCIOC_GETELMMAP _IO(ENCIOC, 2) #define ENCIOC_GETENCSTAT _IO(ENCIOC, 3) #define ENCIOC_SETENCSTAT _IO(ENCIOC, 4) #define ENCIOC_GETELMSTAT _IO(ENCIOC, 5) #define ENCIOC_SETELMSTAT _IO(ENCIOC, 6) #define ENCIOC_GETTEXT _IO(ENCIOC, 7) #define ENCIOC_INIT _IO(ENCIOC, 8) #define ENCIOC_GETELMDESC _IO(ENCIOC, 9) #define ENCIOC_GETELMDEVNAMES _IO(ENCIOC, 10) #define ENCIOC_GETSTRING _IO(ENCIOC, 11) #define ENCIOC_SETSTRING _IO(ENCIOC, 12) #define ENCIOC_GETENCNAME _IO(ENCIOC, 13) #define ENCIOC_GETENCID _IO(ENCIOC, 14) /* * Platform Independent Definitions for enclosure devices. */ /* * SCSI Based Environmental Services Application Defines * * Based almost entirely on SCSI-3 ENC Revision 8A specification, * but slightly abstracted as the underlying device may in fact * be a SAF-TE or vendor unique device. */ /* * ENC Driver Operations: * (The defines themselves are platform and access method specific) * * ENCIOC_GETNELM * ENCIOC_GETELMMAP * ENCIOC_GETENCSTAT * ENCIOC_SETENCSTAT * ENCIOC_GETELMSTAT * ENCIOC_SETELMSTAT * ENCIOC_INIT * * * An application finds out how many elements an enclosure instance - * is managing by performing a ENCIOC_GETNELM operation. It then - * performs a ENCIOC_GETELMMAP to get the map that contains the - * elment identifiers for all elements (see encioc_element_t below). + * manages by performing a ENCIOC_GETNELM operation. It then + * performs an ENCIOC_GETELMMAP to get the map that contains the + * element identifiers for all elements (see encioc_element_t below). * This information is static. * * The application may perform ENCIOC_GETELMSTAT operations to retrieve - * status on an element (see the enc_elm_status_t structure below), - * ENCIOC_SETELMSTAT operations to set status for an element. + * status on an element (see the encioc_elm_status_t structure below), + * and ENCIOC_SETELMSTAT operations to set status for an element. * - * Similarly, overall enclosure status me be fetched or set via - * ENCIOC_GETENCSTAT or ENCIOC_SETENCSTAT operations (see encioc_enc_status_t + * Similarly, overall enclosure status may be fetched or set via + * ENCIOC_GETENCSTAT or ENCIOC_SETENCSTAT operations (see encioc_enc_status_t * below). * * Readers should note that there is nothing that requires either a set * or a clear operation to actually latch and do anything in the target. * - * A ENCIOC_INIT operation causes the enclosure to be initialized. + * An ENCIOC_INIT operation causes the enclosure to be initialized. */ /* Element Types */ typedef enum { ELMTYP_UNSPECIFIED = 0x00, ELMTYP_DEVICE = 0x01, ELMTYP_POWER = 0x02, ELMTYP_FAN = 0x03, ELMTYP_THERM = 0x04, ELMTYP_DOORLOCK = 0x05, ELMTYP_ALARM = 0x06, ELMTYP_ESCC = 0x07, /* Enclosure SCC */ ELMTYP_SCC = 0x08, /* SCC */ ELMTYP_NVRAM = 0x09, ELMTYP_INV_OP_REASON = 0x0a, ELMTYP_UPS = 0x0b, ELMTYP_DISPLAY = 0x0c, ELMTYP_KEYPAD = 0x0d, ELMTYP_ENCLOSURE = 0x0e, ELMTYP_SCSIXVR = 0x0f, ELMTYP_LANGUAGE = 0x10, ELMTYP_COMPORT = 0x11, ELMTYP_VOM = 0x12, ELMTYP_AMMETER = 0x13, ELMTYP_SCSI_TGT = 0x14, ELMTYP_SCSI_INI = 0x15, ELMTYP_SUBENC = 0x16, ELMTYP_ARRAY_DEV = 0x17, ELMTYP_SAS_EXP = 0x18, /* SAS Expander */ ELMTYP_SAS_CONN = 0x19, /* SAS Connector */ ELMTYP_LAST = ELMTYP_SAS_CONN } elm_type_t; #define ELM_TYPE_NAMES { \ "Unspecified", \ "Device Slot", \ "Power Supply", \ "Cooling", \ "Temperature Sensor", \ "Door", \ "Audible alarm", \ "Enclosure Services Controller Electronics", \ "SCC Controller Electronics", \ "Nonvolatile Cache", \ "Invalid Operation Reason", \ "Uninterruptible Power Supply", \ "Display", \ "Key Pad Entry", \ "Enclosure", \ "SCSI Port/Transceiver", \ "Language", \ "Communication Port", \ "Voltage Sensor", \ "Current Sensor", \ "SCSI Target Port", \ "SCSI Initiator Port", \ "Simple Subenclosure", \ "Array Device Slot", \ "SAS Expander", \ "SAS Connector" \ } #ifdef _KERNEL extern const char *elm_type_names[]; #endif typedef struct encioc_element { /* Element Index */ unsigned int elm_idx; /* ID of SubEnclosure containing Element*/ unsigned int elm_subenc_id; /* Element Type */ elm_type_t elm_type; } encioc_element_t; /* * Overall Enclosure Status */ typedef unsigned char encioc_enc_status_t; /* * Element Status */ typedef struct encioc_elm_status { unsigned int elm_idx; unsigned char cstat[4]; } encioc_elm_status_t; /* * ENC String structure, for StringIn and StringOut commands; use this with * the ENCIOC_GETSTRING and ENCIOC_SETSTRING ioctls. */ typedef struct encioc_string { size_t bufsiz; /* IN/OUT: length of string provided/returned */ #define ENC_STRING_MAX 0xffff uint8_t *buf; /* IN/OUT: string */ } encioc_string_t; /*============================================================================*/ /* * SES v2 r20 6.1.10 (pg 39) - Element Descriptor diagnostic page * Tables 21, 22, and 23 */ typedef struct encioc_elm_desc { unsigned int elm_idx; /* IN: elment requested */ uint16_t elm_desc_len; /* IN: buffer size; OUT: bytes written */ char *elm_desc_str; /* IN/OUT: buffer for descriptor data */ } encioc_elm_desc_t; /* * ENCIOC_GETELMDEVNAMES: * ioctl structure to get an element's device names, if available */ typedef struct encioc_elm_devnames { unsigned int elm_idx; /* IN: element index */ size_t elm_names_size;/* IN: size of elm_devnames */ size_t elm_names_len; /* OUT: actual size returned */ /* * IN/OUT: comma separated list of peripheral driver * instances servicing this element. */ char *elm_devnames; } encioc_elm_devnames_t; /* ioctl structure for requesting FC info for a port */ typedef struct encioc_elm_fc_port { unsigned int elm_idx; unsigned int port_idx; struct ses_elm_fc_port port_data; } encioc_elm_fc_port_t; /* ioctl structure for requesting SAS info for element phys */ typedef struct encioc_elm_sas_device_phy { unsigned int elm_idx; unsigned int phy_idx; struct ses_elm_sas_device_phy phy_data; } enioc_elm_sas_phy_t; /* ioctl structure for requesting SAS info for an expander phy */ typedef struct encioc_elm_sas_expander_phy { unsigned int elm_idx; unsigned int phy_idx; struct ses_elm_sas_expander_phy phy_data; } encioc_elm_sas_expander_phy_t; /* ioctl structure for requesting SAS info for a port phy */ typedef struct encioc_elm_sas_port_phy { unsigned int elm_idx; unsigned int phy_idx; struct ses_elm_sas_port_phy phy_data; } enioc_elm_sas_port_phy_t; /* ioctl structure for requesting additional status for an element */ typedef struct encioc_addl_status { unsigned int elm_idx; union ses_elm_addlstatus_descr_hdr addl_hdr; union ses_elm_addlstatus_proto_hdr proto_hdr; } enioc_addl_status_t; #endif /* _SCSI_ENC_H_ */ diff --git a/sys/cam/scsi/scsi_enc_internal.h b/sys/cam/scsi/scsi_enc_internal.h index cea5eebfeb5f..e57634d065df 100644 --- a/sys/cam/scsi/scsi_enc_internal.h +++ b/sys/cam/scsi/scsi_enc_internal.h @@ -1,233 +1,232 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2000 Matthew Jacob * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * This file contains definitions only intended for use within * sys/cam/scsi/scsi_enc*.c, and not in other kernel components. */ #ifndef __SCSI_ENC_INTERNAL_H__ #define __SCSI_ENC_INTERNAL_H__ #include typedef struct enc_element { u_int elm_idx; /* index of element */ uint8_t elm_type; /* element type */ uint8_t subenclosure; /* subenclosure id */ uint8_t type_elm_idx; /* index of element within type */ uint8_t svalid; /* enclosure information valid */ uint8_t encstat[4]; /* state && stats */ u_int physical_path_len; /* Length of device path data. */ uint8_t *physical_path; /* Device physical path data. */ void *elm_private; /* per-type object data */ uint16_t priv; } enc_element_t; typedef enum { ENC_NONE, ENC_SES, ENC_SES_PASSTHROUGH, ENC_SAFT, ENC_SEMB_SES, ENC_SEMB_SAFT } enctyp; /* Platform Independent Driver Internal Definitions for enclosure devices. */ typedef struct enc_softc enc_softc_t; struct enc_fsm_state; typedef int fsm_fill_handler_t(enc_softc_t *ssc, struct enc_fsm_state *state, union ccb *ccb, uint8_t *buf); typedef int fsm_error_handler_t(union ccb *ccb, uint32_t cflags, uint32_t sflags); typedef int fsm_done_handler_t(enc_softc_t *ssc, struct enc_fsm_state *state, union ccb *ccb, uint8_t **bufp, int error, int xfer_len); struct enc_fsm_state { const char *name; int page_code; size_t buf_size; uint32_t timeout; fsm_fill_handler_t *fill; fsm_done_handler_t *done; fsm_error_handler_t *error; }; typedef int (enc_softc_init_t)(enc_softc_t *); typedef void (enc_softc_invalidate_t)(enc_softc_t *); typedef void (enc_softc_cleanup_t)(enc_softc_t *); typedef int (enc_init_enc_t)(enc_softc_t *); -typedef int (enc_get_enc_status_t)(enc_softc_t *, int); typedef int (enc_set_enc_status_t)(enc_softc_t *, encioc_enc_status_t, int); typedef int (enc_get_elm_status_t)(enc_softc_t *, encioc_elm_status_t *, int); typedef int (enc_set_elm_status_t)(enc_softc_t *, encioc_elm_status_t *, int); typedef int (enc_get_elm_desc_t)(enc_softc_t *, encioc_elm_desc_t *); typedef int (enc_get_elm_devnames_t)(enc_softc_t *, encioc_elm_devnames_t *); -typedef int (enc_handle_string_t)(enc_softc_t *, encioc_string_t *, int); +typedef int (enc_handle_string_t)(enc_softc_t *, encioc_string_t *, + unsigned long); typedef void (enc_device_found_t)(enc_softc_t *); typedef void (enc_poll_status_t)(enc_softc_t *); struct enc_vec { enc_softc_invalidate_t *softc_invalidate; enc_softc_cleanup_t *softc_cleanup; enc_init_enc_t *init_enc; - enc_get_enc_status_t *get_enc_status; enc_set_enc_status_t *set_enc_status; enc_get_elm_status_t *get_elm_status; enc_set_elm_status_t *set_elm_status; enc_get_elm_desc_t *get_elm_desc; enc_get_elm_devnames_t *get_elm_devnames; enc_handle_string_t *handle_string; enc_device_found_t *device_found; enc_poll_status_t *poll_status; }; typedef struct enc_cache { enc_element_t *elm_map; /* objects */ int nelms; /* number of objects */ encioc_enc_status_t enc_status; /* overall status */ void *private; /* per-type private data */ } enc_cache_t; /* Enclosure instance toplevel structure */ struct enc_softc { enctyp enc_type; /* type of enclosure */ struct enc_vec enc_vec; /* vector to handlers */ void *enc_private; /* per-type private data */ /** * "Published" configuration and state data available to * external consumers. */ enc_cache_t enc_cache; /** * Configuration and state data being actively updated * by the enclosure daemon. */ enc_cache_t enc_daemon_cache; struct sx enc_cache_lock; uint8_t enc_flags; #define ENC_FLAG_INVALID 0x01 #define ENC_FLAG_INITIALIZED 0x02 #define ENC_FLAG_SHUTDOWN 0x04 struct cdev *enc_dev; struct cam_periph *periph; int open_count; /* Bitmap of pending operations. */ uint32_t pending_actions; /* The action on which the state machine is currently working. */ uint32_t current_action; #define ENC_UPDATE_NONE 0x00 #define ENC_UPDATE_INVALID 0xff /* Callout for auto-updating enclosure status */ struct callout status_updater; struct proc *enc_daemon; struct enc_fsm_state *enc_fsm_states; struct root_hold_token enc_rootmount; #define ENC_ANNOUNCE_SZ 400 char announce_buf[ENC_ANNOUNCE_SZ]; }; static inline enc_cache_t * enc_other_cache(enc_softc_t *enc, enc_cache_t *primary) { return (primary == &enc->enc_cache ? &enc->enc_daemon_cache : &enc->enc_cache); } /* SES Management mode page - SES2r20 Table 59 */ struct ses_mgmt_mode_page { struct scsi_mode_header_6 header; struct scsi_mode_blk_desc blk_desc; uint8_t byte0; /* ps : 1, spf : 1, page_code : 6 */ #define SES_MGMT_MODE_PAGE_CODE 0x14 uint8_t length; #define SES_MGMT_MODE_PAGE_LEN 6 uint8_t reserved[3]; uint8_t byte5; /* reserved : 7, enbltc : 1 */ #define SES_MGMT_TIMED_COMP_EN 0x1 uint8_t max_comp_time[2]; }; /* Enclosure core interface for sub-drivers */ int enc_runcmd(struct enc_softc *, char *, int, char *, int *); void enc_log(struct enc_softc *, const char *, ...); int enc_error(union ccb *, uint32_t, uint32_t); void enc_update_request(enc_softc_t *, uint32_t); /* SES Native interface */ enc_softc_init_t ses_softc_init; /* SAF-TE interface */ enc_softc_init_t safte_softc_init; SYSCTL_DECL(_kern_cam_enc); extern int enc_verbose; /* Helper macros */ MALLOC_DECLARE(M_SCSIENC); #define ENC_CFLAGS CAM_RETRY_SELTO #define ENC_FLAGS SF_NO_PRINT | SF_RETRY_UA #define STRNCMP strncmp #define PRINTF printf #define ENC_LOG enc_log #if defined(DEBUG) || defined(ENC_DEBUG) #define ENC_DLOG enc_log #else #define ENC_DLOG if (0) enc_log #endif #define ENC_VLOG if (enc_verbose) enc_log #define ENC_MALLOC(amt) malloc(amt, M_SCSIENC, M_NOWAIT) #define ENC_MALLOCZ(amt) malloc(amt, M_SCSIENC, M_ZERO|M_NOWAIT) /* Cast away const avoiding GCC warnings. */ #define ENC_FREE(ptr) free((void *)((uintptr_t)ptr), M_SCSIENC) #define ENC_FREE_AND_NULL(ptr) do { \ if (ptr != NULL) { \ ENC_FREE(ptr); \ ptr = NULL; \ } \ } while(0) #define MEMZERO bzero #define MEMCPY(dest, src, amt) bcopy(src, dest, amt) #endif /* __SCSI_ENC_INTERNAL_H__ */ diff --git a/sys/cam/scsi/scsi_enc_safte.c b/sys/cam/scsi/scsi_enc_safte.c index 200c32073644..7539531302f9 100644 --- a/sys/cam/scsi/scsi_enc_safte.c +++ b/sys/cam/scsi/scsi_enc_safte.c @@ -1,1131 +1,1123 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2000 Matthew Jacob * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * SAF-TE Type Device Emulation */ static int safte_set_enc_status(enc_softc_t *enc, uint8_t encstat, int slpflag); #define ALL_ENC_STAT (SES_ENCSTAT_CRITICAL | SES_ENCSTAT_UNRECOV | \ SES_ENCSTAT_NONCRITICAL | SES_ENCSTAT_INFO) /* * SAF-TE specific defines- Mandatory ones only... */ /* * READ BUFFER ('get' commands) IDs- placed in offset 2 of cdb */ #define SAFTE_RD_RDCFG 0x00 /* read enclosure configuration */ #define SAFTE_RD_RDESTS 0x01 /* read enclosure status */ #define SAFTE_RD_RDDSTS 0x04 /* read drive slot status */ #define SAFTE_RD_RDGFLG 0x05 /* read global flags */ /* * WRITE BUFFER ('set' commands) IDs- placed in offset 0 of databuf */ #define SAFTE_WT_DSTAT 0x10 /* write device slot status */ #define SAFTE_WT_SLTOP 0x12 /* perform slot operation */ #define SAFTE_WT_FANSPD 0x13 /* set fan speed */ #define SAFTE_WT_ACTPWS 0x14 /* turn on/off power supply */ #define SAFTE_WT_GLOBAL 0x15 /* send global command */ #define SAFT_SCRATCH 64 #define SCSZ 0x8000 typedef enum { SAFTE_UPDATE_NONE, SAFTE_UPDATE_READCONFIG, SAFTE_UPDATE_READGFLAGS, SAFTE_UPDATE_READENCSTATUS, SAFTE_UPDATE_READSLOTSTATUS, SAFTE_PROCESS_CONTROL_REQS, SAFTE_NUM_UPDATE_STATES } safte_update_action; static fsm_fill_handler_t safte_fill_read_buf_io; static fsm_fill_handler_t safte_fill_control_request; static fsm_done_handler_t safte_process_config; static fsm_done_handler_t safte_process_gflags; static fsm_done_handler_t safte_process_status; static fsm_done_handler_t safte_process_slotstatus; static fsm_done_handler_t safte_process_control_request; static struct enc_fsm_state enc_fsm_states[SAFTE_NUM_UPDATE_STATES] = { { "SAFTE_UPDATE_NONE", 0, 0, 0, NULL, NULL, NULL }, { "SAFTE_UPDATE_READCONFIG", SAFTE_RD_RDCFG, SAFT_SCRATCH, 60 * 1000, safte_fill_read_buf_io, safte_process_config, enc_error }, { "SAFTE_UPDATE_READGFLAGS", SAFTE_RD_RDGFLG, 16, 60 * 1000, safte_fill_read_buf_io, safte_process_gflags, enc_error }, { "SAFTE_UPDATE_READENCSTATUS", SAFTE_RD_RDESTS, SCSZ, 60 * 1000, safte_fill_read_buf_io, safte_process_status, enc_error }, { "SAFTE_UPDATE_READSLOTSTATUS", SAFTE_RD_RDDSTS, SCSZ, 60 * 1000, safte_fill_read_buf_io, safte_process_slotstatus, enc_error }, { "SAFTE_PROCESS_CONTROL_REQS", 0, SCSZ, 60 * 1000, safte_fill_control_request, safte_process_control_request, enc_error } }; typedef struct safte_control_request { int elm_idx; uint8_t elm_stat[4]; int result; TAILQ_ENTRY(safte_control_request) links; } safte_control_request_t; TAILQ_HEAD(safte_control_reqlist, safte_control_request); typedef struct safte_control_reqlist safte_control_reqlist_t; enum { SES_SETSTATUS_ENC_IDX = -1 }; static void safte_terminate_control_requests(safte_control_reqlist_t *reqlist, int result) { safte_control_request_t *req; while ((req = TAILQ_FIRST(reqlist)) != NULL) { TAILQ_REMOVE(reqlist, req, links); req->result = result; wakeup(req); } } struct scfg { /* * Cached Configuration */ uint8_t Nfans; /* Number of Fans */ uint8_t Npwr; /* Number of Power Supplies */ uint8_t Nslots; /* Number of Device Slots */ uint8_t DoorLock; /* Door Lock Installed */ uint8_t Ntherm; /* Number of Temperature Sensors */ uint8_t Nspkrs; /* Number of Speakers */ uint8_t Ntstats; /* Number of Thermostats */ /* * Cached Flag Bytes for Global Status */ uint8_t flag1; uint8_t flag2; /* * What object index ID is where various slots start. */ uint8_t pwroff; uint8_t slotoff; #define SAFT_ALARM_OFFSET(cc) (cc)->slotoff - 1 encioc_enc_status_t adm_status; encioc_enc_status_t enc_status; encioc_enc_status_t slot_status; safte_control_reqlist_t requests; safte_control_request_t *current_request; int current_request_stage; int current_request_stages; }; #define SAFT_FLG1_ALARM 0x1 #define SAFT_FLG1_GLOBFAIL 0x2 #define SAFT_FLG1_GLOBWARN 0x4 #define SAFT_FLG1_ENCPWROFF 0x8 #define SAFT_FLG1_ENCFANFAIL 0x10 #define SAFT_FLG1_ENCPWRFAIL 0x20 #define SAFT_FLG1_ENCDRVFAIL 0x40 #define SAFT_FLG1_ENCDRVWARN 0x80 #define SAFT_FLG2_LOCKDOOR 0x4 #define SAFT_PRIVATE sizeof (struct scfg) static char *safte_2little = "Too Little Data Returned (%d) at line %d\n"; #define SAFT_BAIL(r, x) \ if ((r) >= (x)) { \ ENC_VLOG(enc, safte_2little, x, __LINE__);\ return (EIO); \ } int emulate_array_devices = 1; SYSCTL_INT(_kern_cam_enc, OID_AUTO, emulate_array_devices, CTLFLAG_RWTUN, &emulate_array_devices, 0, "Emulate Array Devices for SAF-TE"); static int safte_fill_read_buf_io(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t *buf) { if (state->page_code != SAFTE_RD_RDCFG && enc->enc_cache.nelms == 0) { enc_update_request(enc, SAFTE_UPDATE_READCONFIG); return (-1); } if (enc->enc_type == ENC_SEMB_SAFT) { semb_read_buffer(&ccb->ataio, /*retries*/5, NULL, MSG_SIMPLE_Q_TAG, state->page_code, buf, state->buf_size, state->timeout); } else { scsi_read_buffer(&ccb->csio, /*retries*/5, NULL, MSG_SIMPLE_Q_TAG, 1, state->page_code, 0, buf, state->buf_size, SSD_FULL_SIZE, state->timeout); } return (0); } static int safte_process_config(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { struct scfg *cfg; uint8_t *buf = *bufp; int i, r; cfg = enc->enc_private; if (cfg == NULL) return (ENXIO); if (error != 0) return (error); if (xfer_len < 6) { ENC_VLOG(enc, "too little data (%d) for configuration\n", xfer_len); return (EIO); } cfg->Nfans = buf[0]; cfg->Npwr = buf[1]; cfg->Nslots = buf[2]; cfg->DoorLock = buf[3]; cfg->Ntherm = buf[4]; cfg->Nspkrs = buf[5]; if (xfer_len >= 7) cfg->Ntstats = buf[6] & 0x0f; else cfg->Ntstats = 0; ENC_VLOG(enc, "Nfans %d Npwr %d Nslots %d Lck %d Ntherm %d Nspkrs %d " "Ntstats %d\n", cfg->Nfans, cfg->Npwr, cfg->Nslots, cfg->DoorLock, cfg->Ntherm, cfg->Nspkrs, cfg->Ntstats); enc->enc_cache.nelms = cfg->Nfans + cfg->Npwr + cfg->Nslots + cfg->DoorLock + cfg->Ntherm + cfg->Nspkrs + cfg->Ntstats + 1; ENC_FREE_AND_NULL(enc->enc_cache.elm_map); enc->enc_cache.elm_map = malloc(enc->enc_cache.nelms * sizeof(enc_element_t), M_SCSIENC, M_WAITOK|M_ZERO); r = 0; /* * Note that this is all arranged for the convenience * in later fetches of status. */ for (i = 0; i < cfg->Nfans; i++) enc->enc_cache.elm_map[r++].elm_type = ELMTYP_FAN; cfg->pwroff = (uint8_t) r; for (i = 0; i < cfg->Npwr; i++) enc->enc_cache.elm_map[r++].elm_type = ELMTYP_POWER; for (i = 0; i < cfg->DoorLock; i++) enc->enc_cache.elm_map[r++].elm_type = ELMTYP_DOORLOCK; if (cfg->Nspkrs > 0) enc->enc_cache.elm_map[r++].elm_type = ELMTYP_ALARM; for (i = 0; i < cfg->Ntherm; i++) enc->enc_cache.elm_map[r++].elm_type = ELMTYP_THERM; for (i = 0; i <= cfg->Ntstats; i++) enc->enc_cache.elm_map[r++].elm_type = ELMTYP_THERM; cfg->slotoff = (uint8_t) r; for (i = 0; i < cfg->Nslots; i++) enc->enc_cache.elm_map[r++].elm_type = emulate_array_devices ? ELMTYP_ARRAY_DEV : ELMTYP_DEVICE; enc_update_request(enc, SAFTE_UPDATE_READGFLAGS); enc_update_request(enc, SAFTE_UPDATE_READENCSTATUS); enc_update_request(enc, SAFTE_UPDATE_READSLOTSTATUS); return (0); } static int safte_process_gflags(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { struct scfg *cfg; uint8_t *buf = *bufp; cfg = enc->enc_private; if (cfg == NULL) return (ENXIO); if (error != 0) return (error); SAFT_BAIL(3, xfer_len); cfg->flag1 = buf[1]; cfg->flag2 = buf[2]; cfg->adm_status = 0; if (cfg->flag1 & SAFT_FLG1_GLOBFAIL) cfg->adm_status |= SES_ENCSTAT_CRITICAL; else if (cfg->flag1 & SAFT_FLG1_GLOBWARN) cfg->adm_status |= SES_ENCSTAT_NONCRITICAL; return (0); } static int safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { struct scfg *cfg; uint8_t *buf = *bufp; int oid, r, i, nitems; uint16_t tempflags; enc_cache_t *cache = &enc->enc_cache; cfg = enc->enc_private; if (cfg == NULL) return (ENXIO); if (error != 0) return (error); oid = r = 0; cfg->enc_status = 0; for (nitems = i = 0; i < cfg->Nfans; i++) { SAFT_BAIL(r, xfer_len); /* * 0 = Fan Operational * 1 = Fan is malfunctioning * 2 = Fan is not present * 0x80 = Unknown or Not Reportable Status */ cache->elm_map[oid].encstat[1] = 0; /* resvd */ cache->elm_map[oid].encstat[2] = 0; /* resvd */ if (cfg->flag1 & SAFT_FLG1_ENCFANFAIL) cache->elm_map[oid].encstat[3] |= 0x40; else cache->elm_map[oid].encstat[3] &= ~0x40; switch ((int)buf[r]) { case 0: nitems++; cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; if ((cache->elm_map[oid].encstat[3] & 0x37) == 0) cache->elm_map[oid].encstat[3] |= 0x27; break; case 1: cache->elm_map[oid].encstat[0] = SES_OBJSTAT_CRIT; /* * FAIL and FAN STOPPED synthesized */ cache->elm_map[oid].encstat[3] |= 0x10; cache->elm_map[oid].encstat[3] &= ~0x07; /* * Enclosure marked with CRITICAL error * if only one fan or no thermometers, * else the NONCRITICAL error is set. */ if (cfg->Nfans == 1 || (cfg->Ntherm + cfg->Ntstats) == 0) cfg->enc_status |= SES_ENCSTAT_CRITICAL; else cfg->enc_status |= SES_ENCSTAT_NONCRITICAL; break; case 2: cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NOTINSTALLED; cache->elm_map[oid].encstat[3] |= 0x10; cache->elm_map[oid].encstat[3] &= ~0x07; /* * Enclosure marked with CRITICAL error * if only one fan or no thermometers, * else the NONCRITICAL error is set. */ if (cfg->Nfans == 1) cfg->enc_status |= SES_ENCSTAT_CRITICAL; else cfg->enc_status |= SES_ENCSTAT_NONCRITICAL; break; case 0x80: cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; cache->elm_map[oid].encstat[3] = 0; cfg->enc_status |= SES_ENCSTAT_INFO; break; default: cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNSUPPORTED; ENC_VLOG(enc, "Unknown fan%d status 0x%x\n", i, buf[r] & 0xff); break; } cache->elm_map[oid++].svalid = 1; r++; } /* * No matter how you cut it, no cooling elements when there * should be some there is critical. */ if (cfg->Nfans && nitems == 0) cfg->enc_status |= SES_ENCSTAT_CRITICAL; for (i = 0; i < cfg->Npwr; i++) { SAFT_BAIL(r, xfer_len); cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; cache->elm_map[oid].encstat[1] = 0; /* resvd */ cache->elm_map[oid].encstat[2] = 0; /* resvd */ cache->elm_map[oid].encstat[3] = 0x20; /* requested on */ switch (buf[r]) { case 0x00: /* pws operational and on */ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; break; case 0x01: /* pws operational and off */ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; cache->elm_map[oid].encstat[3] = 0x10; cfg->enc_status |= SES_ENCSTAT_INFO; break; case 0x10: /* pws is malfunctioning and commanded on */ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_CRIT; cache->elm_map[oid].encstat[3] = 0x61; cfg->enc_status |= SES_ENCSTAT_NONCRITICAL; break; case 0x11: /* pws is malfunctioning and commanded off */ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NONCRIT; cache->elm_map[oid].encstat[3] = 0x51; cfg->enc_status |= SES_ENCSTAT_NONCRITICAL; break; case 0x20: /* pws is not present */ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NOTINSTALLED; cache->elm_map[oid].encstat[3] = 0; cfg->enc_status |= SES_ENCSTAT_INFO; break; case 0x21: /* pws is present */ /* * This is for enclosures that cannot tell whether the * device is on or malfunctioning, but know that it is * present. Just fall through. */ /* FALLTHROUGH */ case 0x80: /* Unknown or Not Reportable Status */ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; cache->elm_map[oid].encstat[3] = 0; cfg->enc_status |= SES_ENCSTAT_INFO; break; default: ENC_VLOG(enc, "unknown power supply %d status (0x%x)\n", i, buf[r] & 0xff); break; } enc->enc_cache.elm_map[oid++].svalid = 1; r++; } /* * Copy Slot SCSI IDs */ for (i = 0; i < cfg->Nslots; i++) { SAFT_BAIL(r, xfer_len); if (cache->elm_map[cfg->slotoff + i].elm_type == ELMTYP_DEVICE) cache->elm_map[cfg->slotoff + i].encstat[1] = buf[r]; r++; } /* * We always have doorlock status, no matter what, * but we only save the status if we have one. */ SAFT_BAIL(r, xfer_len); if (cfg->DoorLock) { /* * 0 = Door Locked * 1 = Door Unlocked, or no Lock Installed * 0x80 = Unknown or Not Reportable Status */ cache->elm_map[oid].encstat[1] = 0; cache->elm_map[oid].encstat[2] = 0; switch (buf[r]) { case 0: cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; cache->elm_map[oid].encstat[3] = 0; break; case 1: cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; cache->elm_map[oid].encstat[3] = 1; break; case 0x80: cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; cache->elm_map[oid].encstat[3] = 0; cfg->enc_status |= SES_ENCSTAT_INFO; break; default: cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNSUPPORTED; ENC_VLOG(enc, "unknown lock status 0x%x\n", buf[r] & 0xff); break; } cache->elm_map[oid++].svalid = 1; } r++; /* * We always have speaker status, no matter what, * but we only save the status if we have one. */ SAFT_BAIL(r, xfer_len); if (cfg->Nspkrs) { cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; cache->elm_map[oid].encstat[1] = 0; cache->elm_map[oid].encstat[2] = 0; if (buf[r] == 0) { cache->elm_map[oid].encstat[0] |= SESCTL_DISABLE; cache->elm_map[oid].encstat[3] |= 0x40; } cache->elm_map[oid++].svalid = 1; } r++; /* * Now, for "pseudo" thermometers, we have two bytes * of information in enclosure status- 16 bits. Actually, * the MSB is a single TEMP ALERT flag indicating whether * any other bits are set, but, thanks to fuzzy thinking, * in the SAF-TE spec, this can also be set even if no * other bits are set, thus making this really another * binary temperature sensor. */ SAFT_BAIL(r + cfg->Ntherm, xfer_len); tempflags = buf[r + cfg->Ntherm]; SAFT_BAIL(r + cfg->Ntherm + 1, xfer_len); tempflags |= (tempflags << 8) | buf[r + cfg->Ntherm + 1]; for (i = 0; i < cfg->Ntherm; i++) { SAFT_BAIL(r, xfer_len); /* * Status is a range from -10 to 245 deg Celsius, * which we need to normalize to -20 to -245 according * to the latest SCSI spec, which makes little * sense since this would overflow an 8bit value. * Well, still, the base normalization is -20, * not -10, so we have to adjust. * * So what's over and under temperature? * Hmm- we'll state that 'normal' operating * is 10 to 40 deg Celsius. */ /* * Actually.... All of the units that people out in the world * seem to have do not come even close to setting a value that * complies with this spec. * * The closest explanation I could find was in an * LSI-Logic manual, which seemed to indicate that * this value would be set by whatever the I2C code * would interpolate from the output of an LM75 * temperature sensor. * * This means that it is impossible to use the actual * numeric value to predict anything. But we don't want * to lose the value. So, we'll propagate the *uncorrected* * value and set SES_OBJSTAT_NOTAVAIL. We'll depend on the * temperature flags for warnings. */ if (tempflags & (1 << i)) { cache->elm_map[oid].encstat[0] = SES_OBJSTAT_CRIT; cfg->enc_status |= SES_ENCSTAT_CRITICAL; } else cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; cache->elm_map[oid].encstat[1] = 0; cache->elm_map[oid].encstat[2] = buf[r]; cache->elm_map[oid].encstat[3] = 0; cache->elm_map[oid++].svalid = 1; r++; } for (i = 0; i <= cfg->Ntstats; i++) { cache->elm_map[oid].encstat[1] = 0; if (tempflags & (1 << ((i == cfg->Ntstats) ? 15 : (cfg->Ntherm + i)))) { cache->elm_map[oid].encstat[0] = SES_OBJSTAT_CRIT; cache->elm_map[4].encstat[2] = 0xff; /* * Set 'over temperature' failure. */ cache->elm_map[oid].encstat[3] = 8; cfg->enc_status |= SES_ENCSTAT_CRITICAL; } else { /* * We used to say 'not available' and synthesize a * nominal 30 deg (C)- that was wrong. Actually, * Just say 'OK', and use the reserved value of * zero. */ if ((cfg->Ntherm + cfg->Ntstats) == 0) cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NOTAVAIL; else cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; cache->elm_map[oid].encstat[2] = 0; cache->elm_map[oid].encstat[3] = 0; } cache->elm_map[oid++].svalid = 1; } r += 2; cache->enc_status = cfg->enc_status | cfg->slot_status | cfg->adm_status; return (0); } static int safte_process_slotstatus(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { struct scfg *cfg; uint8_t *buf = *bufp; enc_cache_t *cache = &enc->enc_cache; int oid, r, i; cfg = enc->enc_private; if (cfg == NULL) return (ENXIO); if (error != 0) return (error); cfg->slot_status = 0; oid = cfg->slotoff; for (r = i = 0; i < cfg->Nslots; i++, r += 4) { SAFT_BAIL(r+3, xfer_len); if (cache->elm_map[oid].elm_type == ELMTYP_ARRAY_DEV) cache->elm_map[oid].encstat[1] = 0; cache->elm_map[oid].encstat[2] &= SESCTL_RQSID; cache->elm_map[oid].encstat[3] = 0; if ((buf[r+3] & 0x01) == 0) { /* no device */ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NOTINSTALLED; } else if (buf[r+0] & 0x02) { cache->elm_map[oid].encstat[0] = SES_OBJSTAT_CRIT; cfg->slot_status |= SES_ENCSTAT_CRITICAL; } else if (buf[r+0] & 0x40) { cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NONCRIT; cfg->slot_status |= SES_ENCSTAT_NONCRITICAL; } else { cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; } if (buf[r+3] & 0x2) { if (buf[r+3] & 0x01) cache->elm_map[oid].encstat[2] |= SESCTL_RQSRMV; else cache->elm_map[oid].encstat[2] |= SESCTL_RQSINS; } if ((buf[r+3] & 0x04) == 0) cache->elm_map[oid].encstat[3] |= SESCTL_DEVOFF; if (buf[r+0] & 0x02) cache->elm_map[oid].encstat[3] |= SESCTL_RQSFLT; if (buf[r+0] & 0x40) cache->elm_map[oid].encstat[0] |= SESCTL_PRDFAIL; if (cache->elm_map[oid].elm_type == ELMTYP_ARRAY_DEV) { if (buf[r+0] & 0x01) cache->elm_map[oid].encstat[1] |= 0x80; if (buf[r+0] & 0x04) cache->elm_map[oid].encstat[1] |= 0x02; if (buf[r+0] & 0x08) cache->elm_map[oid].encstat[1] |= 0x04; if (buf[r+0] & 0x10) cache->elm_map[oid].encstat[1] |= 0x08; if (buf[r+0] & 0x20) cache->elm_map[oid].encstat[1] |= 0x10; if (buf[r+1] & 0x01) cache->elm_map[oid].encstat[1] |= 0x20; if (buf[r+1] & 0x02) cache->elm_map[oid].encstat[1] |= 0x01; } cache->elm_map[oid++].svalid = 1; } cache->enc_status = cfg->enc_status | cfg->slot_status | cfg->adm_status; return (0); } static int safte_fill_control_request(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t *buf) { struct scfg *cfg; enc_element_t *ep, *ep1; safte_control_request_t *req; int i, idx, xfer_len; cfg = enc->enc_private; if (cfg == NULL) return (ENXIO); if (enc->enc_cache.nelms == 0) { enc_update_request(enc, SAFTE_UPDATE_READCONFIG); return (-1); } if (cfg->current_request == NULL) { cfg->current_request = TAILQ_FIRST(&cfg->requests); TAILQ_REMOVE(&cfg->requests, cfg->current_request, links); cfg->current_request_stage = 0; cfg->current_request_stages = 1; } req = cfg->current_request; idx = (int)req->elm_idx; if (req->elm_idx == SES_SETSTATUS_ENC_IDX) { cfg->adm_status = req->elm_stat[0] & ALL_ENC_STAT; cfg->flag1 &= ~(SAFT_FLG1_GLOBFAIL|SAFT_FLG1_GLOBWARN); if (req->elm_stat[0] & (SES_ENCSTAT_CRITICAL|SES_ENCSTAT_UNRECOV)) cfg->flag1 |= SAFT_FLG1_GLOBFAIL; else if (req->elm_stat[0] & SES_ENCSTAT_NONCRITICAL) cfg->flag1 |= SAFT_FLG1_GLOBWARN; buf[0] = SAFTE_WT_GLOBAL; buf[1] = cfg->flag1; buf[2] = cfg->flag2; buf[3] = 0; xfer_len = 16; } else { ep = &enc->enc_cache.elm_map[idx]; switch (ep->elm_type) { case ELMTYP_DEVICE: case ELMTYP_ARRAY_DEV: switch (cfg->current_request_stage) { case 0: ep->priv = 0; if (req->elm_stat[0] & SESCTL_PRDFAIL) ep->priv |= 0x40; if (req->elm_stat[3] & SESCTL_RQSFLT) ep->priv |= 0x02; if (ep->elm_type == ELMTYP_ARRAY_DEV) { if (req->elm_stat[1] & 0x01) ep->priv |= 0x200; if (req->elm_stat[1] & 0x02) ep->priv |= 0x04; if (req->elm_stat[1] & 0x04) ep->priv |= 0x08; if (req->elm_stat[1] & 0x08) ep->priv |= 0x10; if (req->elm_stat[1] & 0x10) ep->priv |= 0x20; if (req->elm_stat[1] & 0x20) ep->priv |= 0x100; if (req->elm_stat[1] & 0x80) ep->priv |= 0x01; } if (ep->priv == 0) ep->priv |= 0x01; /* no errors */ buf[0] = SAFTE_WT_DSTAT; for (i = 0; i < cfg->Nslots; i++) { ep1 = &enc->enc_cache.elm_map[cfg->slotoff + i]; buf[1 + (3 * i)] = ep1->priv; buf[2 + (3 * i)] = ep1->priv >> 8; } xfer_len = cfg->Nslots * 3 + 1; #define DEVON(x) (!(((x)[2] & SESCTL_RQSINS) | \ ((x)[2] & SESCTL_RQSRMV) | \ ((x)[3] & SESCTL_DEVOFF))) if (DEVON(req->elm_stat) != DEVON(ep->encstat)) cfg->current_request_stages++; #define IDON(x) (!!((x)[2] & SESCTL_RQSID)) if (IDON(req->elm_stat) != IDON(ep->encstat)) cfg->current_request_stages++; break; case 1: case 2: buf[0] = SAFTE_WT_SLTOP; buf[1] = idx - cfg->slotoff; if (cfg->current_request_stage == 1 && DEVON(req->elm_stat) != DEVON(ep->encstat)) { if (DEVON(req->elm_stat)) buf[2] = 0x01; else buf[2] = 0x02; } else { if (IDON(req->elm_stat)) buf[2] = 0x04; else buf[2] = 0x00; ep->encstat[2] &= ~SESCTL_RQSID; ep->encstat[2] |= req->elm_stat[2] & SESCTL_RQSID; } xfer_len = 64; break; default: return (EINVAL); } break; case ELMTYP_POWER: cfg->current_request_stages = 2; switch (cfg->current_request_stage) { case 0: if (req->elm_stat[3] & SESCTL_RQSTFAIL) { cfg->flag1 |= SAFT_FLG1_ENCPWRFAIL; } else { cfg->flag1 &= ~SAFT_FLG1_ENCPWRFAIL; } buf[0] = SAFTE_WT_GLOBAL; buf[1] = cfg->flag1; buf[2] = cfg->flag2; buf[3] = 0; xfer_len = 16; break; case 1: buf[0] = SAFTE_WT_ACTPWS; buf[1] = idx - cfg->pwroff; if (req->elm_stat[3] & SESCTL_RQSTON) buf[2] = 0x01; else buf[2] = 0x00; buf[3] = 0; xfer_len = 16; default: return (EINVAL); } break; case ELMTYP_FAN: if ((req->elm_stat[3] & 0x7) != 0) cfg->current_request_stages = 2; switch (cfg->current_request_stage) { case 0: if (req->elm_stat[3] & SESCTL_RQSTFAIL) cfg->flag1 |= SAFT_FLG1_ENCFANFAIL; else cfg->flag1 &= ~SAFT_FLG1_ENCFANFAIL; buf[0] = SAFTE_WT_GLOBAL; buf[1] = cfg->flag1; buf[2] = cfg->flag2; buf[3] = 0; xfer_len = 16; break; case 1: buf[0] = SAFTE_WT_FANSPD; buf[1] = idx; if (req->elm_stat[3] & SESCTL_RQSTON) { if ((req->elm_stat[3] & 0x7) == 7) buf[2] = 4; else if ((req->elm_stat[3] & 0x7) >= 5) buf[2] = 3; else if ((req->elm_stat[3] & 0x7) >= 3) buf[2] = 2; else buf[2] = 1; } else buf[2] = 0; buf[3] = 0; xfer_len = 16; ep->encstat[3] = req->elm_stat[3] & 0x67; default: return (EINVAL); } break; case ELMTYP_DOORLOCK: if (req->elm_stat[3] & 0x1) cfg->flag2 &= ~SAFT_FLG2_LOCKDOOR; else cfg->flag2 |= SAFT_FLG2_LOCKDOOR; buf[0] = SAFTE_WT_GLOBAL; buf[1] = cfg->flag1; buf[2] = cfg->flag2; buf[3] = 0; xfer_len = 16; break; case ELMTYP_ALARM: if ((req->elm_stat[0] & SESCTL_DISABLE) || (req->elm_stat[3] & 0x40)) { cfg->flag2 &= ~SAFT_FLG1_ALARM; } else if ((req->elm_stat[3] & 0x0f) != 0) { cfg->flag2 |= SAFT_FLG1_ALARM; } else { cfg->flag2 &= ~SAFT_FLG1_ALARM; } buf[0] = SAFTE_WT_GLOBAL; buf[1] = cfg->flag1; buf[2] = cfg->flag2; buf[3] = 0; xfer_len = 16; ep->encstat[3] = req->elm_stat[3]; break; default: return (EINVAL); } } if (enc->enc_type == ENC_SEMB_SAFT) { semb_write_buffer(&ccb->ataio, /*retries*/5, NULL, MSG_SIMPLE_Q_TAG, buf, xfer_len, state->timeout); } else { scsi_write_buffer(&ccb->csio, /*retries*/5, NULL, MSG_SIMPLE_Q_TAG, 1, 0, 0, buf, xfer_len, SSD_FULL_SIZE, state->timeout); } return (0); } static int safte_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { struct scfg *cfg; safte_control_request_t *req; int idx, type; cfg = enc->enc_private; if (cfg == NULL) return (ENXIO); req = cfg->current_request; if (req->result == 0) req->result = error; if (++cfg->current_request_stage >= cfg->current_request_stages) { idx = req->elm_idx; if (idx == SES_SETSTATUS_ENC_IDX) type = -1; else type = enc->enc_cache.elm_map[idx].elm_type; if (type == ELMTYP_DEVICE || type == ELMTYP_ARRAY_DEV) enc_update_request(enc, SAFTE_UPDATE_READSLOTSTATUS); else enc_update_request(enc, SAFTE_UPDATE_READENCSTATUS); cfg->current_request = NULL; wakeup(req); } else { enc_update_request(enc, SAFTE_PROCESS_CONTROL_REQS); } return (0); } static void safte_softc_invalidate(enc_softc_t *enc) { struct scfg *cfg; cfg = enc->enc_private; safte_terminate_control_requests(&cfg->requests, ENXIO); } static void safte_softc_cleanup(enc_softc_t *enc) { ENC_FREE_AND_NULL(enc->enc_cache.elm_map); ENC_FREE_AND_NULL(enc->enc_private); enc->enc_cache.nelms = 0; } static int safte_init_enc(enc_softc_t *enc) { struct scfg *cfg; int err; static char cdb0[6] = { SEND_DIAGNOSTIC }; cfg = enc->enc_private; if (cfg == NULL) return (ENXIO); err = enc_runcmd(enc, cdb0, 6, NULL, 0); if (err) { return (err); } DELAY(5000); cfg->flag1 = 0; cfg->flag2 = 0; err = safte_set_enc_status(enc, 0, 1); return (err); } -static int -safte_get_enc_status(enc_softc_t *enc, int slpflg) -{ - - return (0); -} - static int safte_set_enc_status(enc_softc_t *enc, uint8_t encstat, int slpflag) { struct scfg *cfg; safte_control_request_t req; cfg = enc->enc_private; if (cfg == NULL) return (ENXIO); req.elm_idx = SES_SETSTATUS_ENC_IDX; req.elm_stat[0] = encstat & 0xf; req.result = 0; TAILQ_INSERT_TAIL(&cfg->requests, &req, links); enc_update_request(enc, SAFTE_PROCESS_CONTROL_REQS); cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0); return (req.result); } static int safte_get_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflg) { int i = (int)elms->elm_idx; elms->cstat[0] = enc->enc_cache.elm_map[i].encstat[0]; elms->cstat[1] = enc->enc_cache.elm_map[i].encstat[1]; elms->cstat[2] = enc->enc_cache.elm_map[i].encstat[2]; elms->cstat[3] = enc->enc_cache.elm_map[i].encstat[3]; return (0); } static int safte_set_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflag) { struct scfg *cfg; safte_control_request_t req; cfg = enc->enc_private; if (cfg == NULL) return (ENXIO); /* If this is clear, we don't do diddly. */ if ((elms->cstat[0] & SESCTL_CSEL) == 0) return (0); req.elm_idx = elms->elm_idx; memcpy(&req.elm_stat, elms->cstat, sizeof(req.elm_stat)); req.result = 0; TAILQ_INSERT_TAIL(&cfg->requests, &req, links); enc_update_request(enc, SAFTE_PROCESS_CONTROL_REQS); cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0); return (req.result); } static void safte_poll_status(enc_softc_t *enc) { enc_update_request(enc, SAFTE_UPDATE_READENCSTATUS); enc_update_request(enc, SAFTE_UPDATE_READSLOTSTATUS); } static struct enc_vec safte_enc_vec = { .softc_invalidate = safte_softc_invalidate, .softc_cleanup = safte_softc_cleanup, .init_enc = safte_init_enc, - .get_enc_status = safte_get_enc_status, .set_enc_status = safte_set_enc_status, .get_elm_status = safte_get_elm_status, .set_elm_status = safte_set_elm_status, .poll_status = safte_poll_status }; int safte_softc_init(enc_softc_t *enc) { struct scfg *cfg; enc->enc_vec = safte_enc_vec; enc->enc_fsm_states = enc_fsm_states; if (enc->enc_private == NULL) { enc->enc_private = ENC_MALLOCZ(SAFT_PRIVATE); if (enc->enc_private == NULL) return (ENOMEM); } cfg = enc->enc_private; enc->enc_cache.nelms = 0; enc->enc_cache.enc_status = 0; TAILQ_INIT(&cfg->requests); return (0); } diff --git a/sys/cam/scsi/scsi_enc_ses.c b/sys/cam/scsi/scsi_enc_ses.c index 803630f31f54..557038d9b9f6 100644 --- a/sys/cam/scsi/scsi_enc_ses.c +++ b/sys/cam/scsi/scsi_enc_ses.c @@ -1,3053 +1,3045 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2000 Matthew Jacob * Copyright (c) 2010 Spectra Logic Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /** * \file scsi_enc_ses.c * * Structures and routines specific && private to SES only */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* SES Native Type Device Support */ /* SES Diagnostic Page Codes */ typedef enum { SesSupportedPages = 0x0, SesConfigPage = 0x1, SesControlPage = 0x2, SesStatusPage = SesControlPage, SesHelpTxt = 0x3, SesStringOut = 0x4, SesStringIn = SesStringOut, SesThresholdOut = 0x5, SesThresholdIn = SesThresholdOut, SesArrayControl = 0x6, /* Obsolete in SES v2 */ SesArrayStatus = SesArrayControl, SesElementDescriptor = 0x7, SesShortStatus = 0x8, SesEnclosureBusy = 0x9, SesAddlElementStatus = 0xa } SesDiagPageCodes; typedef struct ses_type { const struct ses_elm_type_desc *hdr; const char *text; } ses_type_t; typedef struct ses_comstat { uint8_t comstatus; uint8_t comstat[3]; } ses_comstat_t; typedef union ses_addl_data { struct ses_elm_sas_device_phy *sasdev_phys; struct ses_elm_sas_expander_phy *sasexp_phys; struct ses_elm_sas_port_phy *sasport_phys; struct ses_fcobj_port *fc_ports; } ses_add_data_t; typedef struct ses_addl_status { struct ses_elm_addlstatus_base_hdr *hdr; union { union ses_fcobj_hdr *fc; union ses_elm_sas_hdr *sas; struct ses_elm_ata_hdr *ata; } proto_hdr; union ses_addl_data proto_data; /* array sizes stored in header */ } ses_add_status_t; typedef struct ses_element { uint8_t eip; /* eip bit is set */ uint16_t descr_len; /* length of the descriptor */ const char *descr; /* descriptor for this object */ struct ses_addl_status addl; /* additional status info */ } ses_element_t; typedef struct ses_control_request { int elm_idx; ses_comstat_t elm_stat; int result; TAILQ_ENTRY(ses_control_request) links; } ses_control_request_t; TAILQ_HEAD(ses_control_reqlist, ses_control_request); typedef struct ses_control_reqlist ses_control_reqlist_t; enum { SES_SETSTATUS_ENC_IDX = -1 }; static void ses_terminate_control_requests(ses_control_reqlist_t *reqlist, int result) { ses_control_request_t *req; while ((req = TAILQ_FIRST(reqlist)) != NULL) { TAILQ_REMOVE(reqlist, req, links); req->result = result; wakeup(req); } } enum ses_iter_index_values { /** * \brief Value of an initialized but invalid index * in a ses_iterator object. * * This value is used for the individual_element_index of * overal status elements and for all index types when * an iterator is first initialized. */ ITERATOR_INDEX_INVALID = -1, /** * \brief Value of an index in a ses_iterator object * when the iterator has traversed past the last * valid element.. */ ITERATOR_INDEX_END = INT_MAX }; /** * \brief Structure encapsulating all data necessary to traverse the * elements of a SES configuration. * * The ses_iterator object simplifies the task of iterating through all * elements detected via the SES configuration page by tracking the numerous * element indexes that, instead of memoizing in the softc, we calculate * on the fly during the traversal of the element objects. The various * indexes are necessary due to the varying needs of matching objects in * the different SES pages. Some pages (e.g. Status/Control) contain all * elements, while others (e.g. Additional Element Status) only contain * individual elements (no overal status elements) of particular types. * * To use an iterator, initialize it with ses_iter_init(), and then * use ses_iter_next() to traverse the elements (including the first) in * the configuration. Once an iterator is initiailized with ses_iter_init(), * you may also seek to any particular element by either it's global or * individual element index via the ses_iter_seek_to() function. You may * also return an iterator to the position just before the first element * (i.e. the same state as after an ses_iter_init()), with ses_iter_reset(). */ struct ses_iterator { /** * \brief Backlink to the overal software configuration structure. * * This is included for convenience so the iteration functions * need only take a single, struct ses_iterator *, argument. */ enc_softc_t *enc; enc_cache_t *cache; /** * \brief Index of the type of the current element within the * ses_cache's ses_types array. */ int type_index; /** * \brief The position (0 based) of this element relative to all other * elements of this type. * * This index resets to zero every time the iterator transitions * to elements of a new type in the configuration. */ int type_element_index; /** * \brief The position (0 based) of this element relative to all * other individual status elements in the configuration. * * This index ranges from 0 through the number of individual * elements in the configuration. When the iterator returns * an overall status element, individual_element_index is * set to ITERATOR_INDEX_INVALID, to indicate that it does * not apply to the current element. */ int individual_element_index; /** * \brief The position (0 based) of this element relative to * all elements in the configration. * * This index is appropriate for indexing into enc->ses_elm_map. */ int global_element_index; /** * \brief The last valid individual element index of this * iterator. * * When an iterator traverses an overal status element, the * individual element index is reset to ITERATOR_INDEX_INVALID * to prevent unintential use of the individual_element_index * field. The saved_individual_element_index allows the iterator * to restore it's position in the individual elements upon * reaching the next individual element. */ int saved_individual_element_index; }; typedef enum { SES_UPDATE_NONE, SES_UPDATE_PAGES, SES_UPDATE_GETCONFIG, SES_UPDATE_GETSTATUS, SES_UPDATE_GETELMDESCS, SES_UPDATE_GETELMADDLSTATUS, SES_PROCESS_CONTROL_REQS, SES_PUBLISH_PHYSPATHS, SES_PUBLISH_CACHE, SES_NUM_UPDATE_STATES } ses_update_action; static enc_softc_cleanup_t ses_softc_cleanup; #define SCSZ 0x8000 static fsm_fill_handler_t ses_fill_rcv_diag_io; static fsm_fill_handler_t ses_fill_control_request; static fsm_done_handler_t ses_process_pages; static fsm_done_handler_t ses_process_config; static fsm_done_handler_t ses_process_status; static fsm_done_handler_t ses_process_elm_descs; static fsm_done_handler_t ses_process_elm_addlstatus; static fsm_done_handler_t ses_process_control_request; static fsm_done_handler_t ses_publish_physpaths; static fsm_done_handler_t ses_publish_cache; static struct enc_fsm_state enc_fsm_states[SES_NUM_UPDATE_STATES] = { { "SES_UPDATE_NONE", 0, 0, 0, NULL, NULL, NULL }, { "SES_UPDATE_PAGES", SesSupportedPages, SCSZ, 60 * 1000, ses_fill_rcv_diag_io, ses_process_pages, enc_error }, { "SES_UPDATE_GETCONFIG", SesConfigPage, SCSZ, 60 * 1000, ses_fill_rcv_diag_io, ses_process_config, enc_error }, { "SES_UPDATE_GETSTATUS", SesStatusPage, SCSZ, 60 * 1000, ses_fill_rcv_diag_io, ses_process_status, enc_error }, { "SES_UPDATE_GETELMDESCS", SesElementDescriptor, SCSZ, 60 * 1000, ses_fill_rcv_diag_io, ses_process_elm_descs, enc_error }, { "SES_UPDATE_GETELMADDLSTATUS", SesAddlElementStatus, SCSZ, 60 * 1000, ses_fill_rcv_diag_io, ses_process_elm_addlstatus, enc_error }, { "SES_PROCESS_CONTROL_REQS", SesControlPage, SCSZ, 60 * 1000, ses_fill_control_request, ses_process_control_request, enc_error }, { "SES_PUBLISH_PHYSPATHS", 0, 0, 0, NULL, ses_publish_physpaths, NULL }, { "SES_PUBLISH_CACHE", 0, 0, 0, NULL, ses_publish_cache, NULL } }; typedef struct ses_cache { /* Source for all the configuration data pointers */ const struct ses_cfg_page *cfg_page; /* References into the config page. */ int ses_nsubencs; const struct ses_enc_desc * const *subencs; int ses_ntypes; const ses_type_t *ses_types; /* Source for all the status pointers */ const struct ses_status_page *status_page; /* Source for all the object descriptor pointers */ const struct ses_elem_descr_page *elm_descs_page; /* Source for all the additional object status pointers */ const struct ses_addl_elem_status_page *elm_addlstatus_page; } ses_cache_t; typedef struct ses_softc { uint32_t ses_flags; #define SES_FLAG_TIMEDCOMP 0x01 #define SES_FLAG_ADDLSTATUS 0x02 #define SES_FLAG_DESC 0x04 ses_control_reqlist_t ses_requests; ses_control_reqlist_t ses_pending_requests; } ses_softc_t; static int ses_search_globally = 0; SYSCTL_INT(_kern_cam_enc, OID_AUTO, search_globally, CTLFLAG_RWTUN, &ses_search_globally, 0, "Search for disks on other buses"); /** * \brief Reset a SES iterator to just before the first element * in the configuration. * * \param iter The iterator object to reset. * * The indexes within a reset iterator are invalid and will only * become valid upon completion of a ses_iter_seek_to() or a * ses_iter_next(). */ static void ses_iter_reset(struct ses_iterator *iter) { /* * Set our indexes to just before the first valid element * of the first type (ITERATOR_INDEX_INVALID == -1). This * simplifies the implementation of ses_iter_next(). */ iter->type_index = 0; iter->type_element_index = ITERATOR_INDEX_INVALID; iter->global_element_index = ITERATOR_INDEX_INVALID; iter->individual_element_index = ITERATOR_INDEX_INVALID; iter->saved_individual_element_index = ITERATOR_INDEX_INVALID; } /** * \brief Initialize the storage of a SES iterator and reset it to * the position just before the first element of the * configuration. * * \param enc The SES softc for the SES instance whose configuration * will be enumerated by this iterator. * \param iter The iterator object to initialize. */ static void ses_iter_init(enc_softc_t *enc, enc_cache_t *cache, struct ses_iterator *iter) { iter->enc = enc; iter->cache = cache; ses_iter_reset(iter); } /** * \brief Traverse the provided SES iterator to the next element * within the configuration. * * \param iter The iterator to move. * * \return If a valid next element exists, a pointer to it's enc_element_t. * Otherwise NULL. */ static enc_element_t * ses_iter_next(struct ses_iterator *iter) { ses_cache_t *ses_cache; const ses_type_t *element_type; ses_cache = iter->cache->private; /* * Note: Treat nelms as signed, so we will hit this case * and immediately terminate the iteration if the * configuration has 0 objects. */ if (iter->global_element_index >= (int)iter->cache->nelms - 1) { /* Elements exhausted. */ iter->type_index = ITERATOR_INDEX_END; iter->type_element_index = ITERATOR_INDEX_END; iter->global_element_index = ITERATOR_INDEX_END; iter->individual_element_index = ITERATOR_INDEX_END; iter->saved_individual_element_index = ITERATOR_INDEX_END; return (NULL); } KASSERT((iter->type_index < ses_cache->ses_ntypes), ("Corrupted element iterator. %d not less than %d", iter->type_index, ses_cache->ses_ntypes)); element_type = &ses_cache->ses_types[iter->type_index]; iter->global_element_index++; iter->type_element_index++; /* * There is an object for overal type status in addition * to one for each allowed element, but only if the element * count is non-zero. */ if (iter->type_element_index > element_type->hdr->etype_maxelt) { /* * We've exhausted the elements of this type. * This next element belongs to the next type. */ iter->type_index++; iter->type_element_index = 0; iter->individual_element_index = ITERATOR_INDEX_INVALID; } if (iter->type_element_index > 0) { iter->individual_element_index = ++iter->saved_individual_element_index; } return (&iter->cache->elm_map[iter->global_element_index]); } /** * Element index types tracked by a SES iterator. */ typedef enum { /** * Index relative to all elements (overall and individual) * in the system. */ SES_ELEM_INDEX_GLOBAL, /** * \brief Index relative to all individual elements in the system. * * This index counts only individual elements, skipping overall * status elements. This is the index space of the additional * element status page (page 0xa). */ SES_ELEM_INDEX_INDIVIDUAL } ses_elem_index_type_t; /** * \brief Move the provided iterator forwards or backwards to the object * having the give index. * * \param iter The iterator on which to perform the seek. * \param element_index The index of the element to find. * \param index_type The type (global or individual) of element_index. * * \return If the element is found, a pointer to it's enc_element_t. * Otherwise NULL. */ static enc_element_t * ses_iter_seek_to(struct ses_iterator *iter, int element_index, ses_elem_index_type_t index_type) { enc_element_t *element; int *cur_index; if (index_type == SES_ELEM_INDEX_GLOBAL) cur_index = &iter->global_element_index; else cur_index = &iter->individual_element_index; if (*cur_index == element_index) { /* Already there. */ return (&iter->cache->elm_map[iter->global_element_index]); } ses_iter_reset(iter); while ((element = ses_iter_next(iter)) != NULL && *cur_index != element_index) ; if (*cur_index != element_index) return (NULL); return (element); } #if 0 static int ses_encode(enc_softc_t *, uint8_t *, int, int, struct ses_comstat *); #endif static int ses_set_timed_completion(enc_softc_t *, uint8_t); #if 0 static int ses_putstatus(enc_softc_t *, int, struct ses_comstat *); #endif static void ses_poll_status(enc_softc_t *); static void ses_print_addl_data(enc_softc_t *, enc_element_t *); /*=========================== SES cleanup routines ===========================*/ static void ses_cache_free_elm_addlstatus(enc_softc_t *enc, enc_cache_t *cache) { ses_cache_t *ses_cache; ses_cache_t *other_ses_cache; enc_element_t *cur_elm; enc_element_t *last_elm; ENC_DLOG(enc, "%s: enter\n", __func__); ses_cache = cache->private; if (ses_cache->elm_addlstatus_page == NULL) return; for (cur_elm = cache->elm_map, last_elm = &cache->elm_map[cache->nelms]; cur_elm != last_elm; cur_elm++) { ses_element_t *elmpriv; elmpriv = cur_elm->elm_private; /* Clear references to the additional status page. */ bzero(&elmpriv->addl, sizeof(elmpriv->addl)); } other_ses_cache = enc_other_cache(enc, cache)->private; if (other_ses_cache->elm_addlstatus_page != ses_cache->elm_addlstatus_page) ENC_FREE(ses_cache->elm_addlstatus_page); ses_cache->elm_addlstatus_page = NULL; } static void ses_cache_free_elm_descs(enc_softc_t *enc, enc_cache_t *cache) { ses_cache_t *ses_cache; ses_cache_t *other_ses_cache; enc_element_t *cur_elm; enc_element_t *last_elm; ENC_DLOG(enc, "%s: enter\n", __func__); ses_cache = cache->private; if (ses_cache->elm_descs_page == NULL) return; for (cur_elm = cache->elm_map, last_elm = &cache->elm_map[cache->nelms]; cur_elm != last_elm; cur_elm++) { ses_element_t *elmpriv; elmpriv = cur_elm->elm_private; elmpriv->descr_len = 0; elmpriv->descr = NULL; } other_ses_cache = enc_other_cache(enc, cache)->private; if (other_ses_cache->elm_descs_page != ses_cache->elm_descs_page) ENC_FREE(ses_cache->elm_descs_page); ses_cache->elm_descs_page = NULL; } static void ses_cache_free_status(enc_softc_t *enc, enc_cache_t *cache) { ses_cache_t *ses_cache; ses_cache_t *other_ses_cache; ENC_DLOG(enc, "%s: enter\n", __func__); ses_cache = cache->private; if (ses_cache->status_page == NULL) return; other_ses_cache = enc_other_cache(enc, cache)->private; if (other_ses_cache->status_page != ses_cache->status_page) ENC_FREE(ses_cache->status_page); ses_cache->status_page = NULL; } static void ses_cache_free_elm_map(enc_softc_t *enc, enc_cache_t *cache) { enc_element_t *cur_elm; enc_element_t *last_elm; ENC_DLOG(enc, "%s: enter\n", __func__); if (cache->elm_map == NULL) return; ses_cache_free_elm_descs(enc, cache); ses_cache_free_elm_addlstatus(enc, cache); for (cur_elm = cache->elm_map, last_elm = &cache->elm_map[cache->nelms]; cur_elm != last_elm; cur_elm++) { ENC_FREE_AND_NULL(cur_elm->elm_private); } ENC_FREE_AND_NULL(cache->elm_map); cache->nelms = 0; ENC_DLOG(enc, "%s: exit\n", __func__); } static void ses_cache_free(enc_softc_t *enc, enc_cache_t *cache) { ses_cache_t *other_ses_cache; ses_cache_t *ses_cache; ENC_DLOG(enc, "%s: enter\n", __func__); ses_cache_free_elm_addlstatus(enc, cache); ses_cache_free_status(enc, cache); ses_cache_free_elm_map(enc, cache); ses_cache = cache->private; ses_cache->ses_ntypes = 0; other_ses_cache = enc_other_cache(enc, cache)->private; if (other_ses_cache->subencs != ses_cache->subencs) ENC_FREE(ses_cache->subencs); ses_cache->subencs = NULL; if (other_ses_cache->ses_types != ses_cache->ses_types) ENC_FREE(ses_cache->ses_types); ses_cache->ses_types = NULL; if (other_ses_cache->cfg_page != ses_cache->cfg_page) ENC_FREE(ses_cache->cfg_page); ses_cache->cfg_page = NULL; ENC_DLOG(enc, "%s: exit\n", __func__); } static void ses_cache_clone(enc_softc_t *enc, enc_cache_t *src, enc_cache_t *dst) { ses_cache_t *dst_ses_cache; ses_cache_t *src_ses_cache; enc_element_t *src_elm; enc_element_t *dst_elm; enc_element_t *last_elm; ses_cache_free(enc, dst); src_ses_cache = src->private; dst_ses_cache = dst->private; /* * The cloned enclosure cache and ses specific cache are * mostly identical to the source. */ *dst = *src; *dst_ses_cache = *src_ses_cache; /* * But the ses cache storage is still independent. Restore * the pointer that was clobbered by the structure copy above. */ dst->private = dst_ses_cache; /* * The element map is independent even though it starts out * pointing to the same constant page data. */ dst->elm_map = malloc(dst->nelms * sizeof(enc_element_t), M_SCSIENC, M_WAITOK); memcpy(dst->elm_map, src->elm_map, dst->nelms * sizeof(enc_element_t)); for (dst_elm = dst->elm_map, src_elm = src->elm_map, last_elm = &src->elm_map[src->nelms]; src_elm != last_elm; src_elm++, dst_elm++) { dst_elm->elm_private = malloc(sizeof(ses_element_t), M_SCSIENC, M_WAITOK); memcpy(dst_elm->elm_private, src_elm->elm_private, sizeof(ses_element_t)); } } /* Structure accessors. These are strongly typed to avoid errors. */ int ses_elm_sas_descr_type(union ses_elm_sas_hdr *obj) { return ((obj)->base_hdr.byte1 >> 6); } int ses_elm_addlstatus_proto(struct ses_elm_addlstatus_base_hdr *hdr) { return ((hdr)->byte0 & 0xf); } int ses_elm_addlstatus_eip(struct ses_elm_addlstatus_base_hdr *hdr) { - return ((hdr)->byte0 >> 4) & 0x1; + return ((hdr)->byte0 >> 4 & 0x1); } int ses_elm_addlstatus_invalid(struct ses_elm_addlstatus_base_hdr *hdr) { return ((hdr)->byte0 >> 7); } int ses_elm_sas_type0_not_all_phys(union ses_elm_sas_hdr *hdr) { return ((hdr)->type0_noneip.byte1 & 0x1); } int ses_elm_sas_dev_phy_sata_dev(struct ses_elm_sas_device_phy *phy) { return ((phy)->target_ports & 0x1); } int ses_elm_sas_dev_phy_sata_port(struct ses_elm_sas_device_phy *phy) { return ((phy)->target_ports >> 7); } int ses_elm_sas_dev_phy_dev_type(struct ses_elm_sas_device_phy *phy) { return (((phy)->byte0 >> 4) & 0x7); } /** * \brief Verify that the cached configuration data in our softc * is valid for processing the page data corresponding to * the provided page header. * * \param ses_cache The SES cache to validate. * \param gen_code The 4 byte generation code from a SES diagnostic * page header. * * \return non-zero if true, 0 if false. */ static int ses_config_cache_valid(ses_cache_t *ses_cache, const uint8_t *gen_code) { uint32_t cache_gc; uint32_t cur_gc; if (ses_cache->cfg_page == NULL) return (0); cache_gc = scsi_4btoul(ses_cache->cfg_page->hdr.gen_code); cur_gc = scsi_4btoul(gen_code); return (cache_gc == cur_gc); } /** * Function signature for consumers of the ses_devids_iter() interface. */ typedef void ses_devid_callback_t(enc_softc_t *, enc_element_t *, struct scsi_vpd_id_descriptor *, void *); /** * \brief Iterate over and create vpd device id records from the * additional element status data for elm, passing that data * to the provided callback. * * \param enc SES instance containing elm * \param elm Element for which to extract device ID data. * \param callback The callback function to invoke on each generated * device id descriptor for elm. * \param callback_arg Argument passed through to callback on each invocation. */ static void ses_devids_iter(enc_softc_t *enc, enc_element_t *elm, ses_devid_callback_t *callback, void *callback_arg) { ses_element_t *elmpriv; struct ses_addl_status *addl; u_int i; size_t devid_record_size; elmpriv = elm->elm_private; addl = &(elmpriv->addl); devid_record_size = SVPD_DEVICE_ID_DESC_HDR_LEN + sizeof(struct scsi_vpd_id_naa_ieee_reg); for (i = 0; i < addl->proto_hdr.sas->base_hdr.num_phys; i++) { uint8_t devid_buf[devid_record_size]; struct scsi_vpd_id_descriptor *devid; uint8_t *phy_addr; devid = (struct scsi_vpd_id_descriptor *)devid_buf; phy_addr = addl->proto_data.sasdev_phys[i].phy_addr; devid->proto_codeset = (SCSI_PROTO_SAS << SVPD_ID_PROTO_SHIFT) | SVPD_ID_CODESET_BINARY; devid->id_type = SVPD_ID_PIV | SVPD_ID_ASSOC_PORT | SVPD_ID_TYPE_NAA; devid->reserved = 0; devid->length = sizeof(struct scsi_vpd_id_naa_ieee_reg); memcpy(devid->identifier, phy_addr, devid->length); callback(enc, elm, devid, callback_arg); } } /** * Function signature for consumers of the ses_paths_iter() interface. */ typedef void ses_path_callback_t(enc_softc_t *, enc_element_t *, struct cam_path *, void *); /** * Argument package passed through ses_devids_iter() by * ses_paths_iter() to ses_path_iter_devid_callback(). */ typedef struct ses_path_iter_args { ses_path_callback_t *callback; void *callback_arg; } ses_path_iter_args_t; /** * ses_devids_iter() callback function used by ses_paths_iter() * to map device ids to peripheral driver instances. * * \param enc SES instance containing elm * \param elm Element on which device ID matching is active. * \param periph A device ID corresponding to elm. * \param arg Argument passed through to callback on each invocation. */ static void ses_path_iter_devid_callback(enc_softc_t *enc, enc_element_t *elem, struct scsi_vpd_id_descriptor *devid, void *arg) { struct ccb_dev_match cdm; struct dev_match_pattern match_pattern; struct dev_match_result match_result; struct device_match_result *device_match; struct device_match_pattern *device_pattern; ses_path_iter_args_t *args; struct cam_path *path; args = (ses_path_iter_args_t *)arg; match_pattern.type = DEV_MATCH_DEVICE; device_pattern = &match_pattern.pattern.device_pattern; device_pattern->flags = DEV_MATCH_DEVID; device_pattern->data.devid_pat.id_len = offsetof(struct scsi_vpd_id_descriptor, identifier) + devid->length; memcpy(device_pattern->data.devid_pat.id, devid, device_pattern->data.devid_pat.id_len); if (!ses_search_globally) { device_pattern->flags |= DEV_MATCH_PATH; device_pattern->path_id = xpt_path_path_id(enc->periph->path); } memset(&cdm, 0, sizeof(cdm)); if (xpt_create_path(&cdm.ccb_h.path, /*periph*/NULL, CAM_XPT_PATH_ID, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) return; cdm.ccb_h.func_code = XPT_DEV_MATCH; cdm.num_patterns = 1; cdm.patterns = &match_pattern; cdm.pattern_buf_len = sizeof(match_pattern); cdm.match_buf_len = sizeof(match_result); cdm.matches = &match_result; do { xpt_action((union ccb *)&cdm); if ((cdm.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP || (cdm.status != CAM_DEV_MATCH_LAST && cdm.status != CAM_DEV_MATCH_MORE) || cdm.num_matches == 0) break; device_match = &match_result.result.device_result; if (xpt_create_path(&path, /*periph*/NULL, device_match->path_id, device_match->target_id, device_match->target_lun) == CAM_REQ_CMP) { args->callback(enc, elem, path, args->callback_arg); xpt_free_path(path); } } while (cdm.status == CAM_DEV_MATCH_MORE); xpt_free_path(cdm.ccb_h.path); } /** * \brief Iterate over and find the matching periph objects for the * specified element. * * \param enc SES instance containing elm * \param elm Element for which to perform periph object matching. * \param callback The callback function to invoke with each matching * periph object. * \param callback_arg Argument passed through to callback on each invocation. */ static void ses_paths_iter(enc_softc_t *enc, enc_element_t *elm, ses_path_callback_t *callback, void *callback_arg) { ses_element_t *elmpriv; struct ses_addl_status *addl; elmpriv = elm->elm_private; addl = &(elmpriv->addl); if (addl->hdr == NULL) return; switch(ses_elm_addlstatus_proto(addl->hdr)) { case SPSP_PROTO_SAS: if (addl->proto_hdr.sas != NULL && addl->proto_data.sasdev_phys != NULL) { ses_path_iter_args_t args; args.callback = callback; args.callback_arg = callback_arg; ses_devids_iter(enc, elm, ses_path_iter_devid_callback, &args); } break; case SPSP_PROTO_ATA: if (addl->proto_hdr.ata != NULL) { struct cam_path *path; struct ccb_getdev cgd; if (xpt_create_path(&path, /*periph*/NULL, scsi_4btoul(addl->proto_hdr.ata->bus), scsi_4btoul(addl->proto_hdr.ata->target), 0) != CAM_REQ_CMP) return; memset(&cgd, 0, sizeof(cgd)); xpt_setup_ccb(&cgd.ccb_h, path, CAM_PRIORITY_NORMAL); cgd.ccb_h.func_code = XPT_GDEV_TYPE; xpt_action((union ccb *)&cgd); if (cgd.ccb_h.status == CAM_REQ_CMP) callback(enc, elm, path, callback_arg); xpt_free_path(path); } break; } } /** * ses_paths_iter() callback function used by ses_get_elmdevname() * to record periph driver instance strings corresponding to a SES * element. * * \param enc SES instance containing elm * \param elm Element on which periph matching is active. * \param periph A periph instance that matches elm. * \param arg Argument passed through to callback on each invocation. */ static void ses_elmdevname_callback(enc_softc_t *enc, enc_element_t *elem, struct cam_path *path, void *arg) { struct sbuf *sb; sb = (struct sbuf *)arg; cam_periph_list(path, sb); } /** * Argument package passed through ses_paths_iter() to * ses_getcampath_callback. */ typedef struct ses_setphyspath_callback_args { struct sbuf *physpath; int num_set; } ses_setphyspath_callback_args_t; /** * \brief ses_paths_iter() callback to set the physical path on the * CAM EDT entries corresponding to a given SES element. * * \param enc SES instance containing elm * \param elm Element on which periph matching is active. * \param periph A periph instance that matches elm. * \param arg Argument passed through to callback on each invocation. */ static void ses_setphyspath_callback(enc_softc_t *enc, enc_element_t *elm, struct cam_path *path, void *arg) { struct ccb_dev_advinfo cdai; ses_setphyspath_callback_args_t *args; char *old_physpath; args = (ses_setphyspath_callback_args_t *)arg; old_physpath = malloc(MAXPATHLEN, M_SCSIENC, M_WAITOK|M_ZERO); xpt_path_lock(path); memset(&cdai, 0, sizeof(cdai)); xpt_setup_ccb(&cdai.ccb_h, path, CAM_PRIORITY_NORMAL); cdai.ccb_h.func_code = XPT_DEV_ADVINFO; cdai.buftype = CDAI_TYPE_PHYS_PATH; cdai.flags = CDAI_FLAG_NONE; cdai.bufsiz = MAXPATHLEN; cdai.buf = old_physpath; xpt_action((union ccb *)&cdai); if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); if (strcmp(old_physpath, sbuf_data(args->physpath)) != 0) { xpt_setup_ccb(&cdai.ccb_h, path, CAM_PRIORITY_NORMAL); cdai.ccb_h.func_code = XPT_DEV_ADVINFO; cdai.buftype = CDAI_TYPE_PHYS_PATH; cdai.flags = CDAI_FLAG_STORE; cdai.bufsiz = sbuf_len(args->physpath); cdai.buf = sbuf_data(args->physpath); xpt_action((union ccb *)&cdai); if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); if (cdai.ccb_h.status == CAM_REQ_CMP) args->num_set++; } xpt_path_unlock(path); free(old_physpath, M_SCSIENC); } /** * \brief Set a device's physical path string in CAM XPT. * * \param enc SES instance containing elm * \param elm Element to publish physical path string for * \param iter Iterator whose state corresponds to elm * * \return 0 on success, errno otherwise. */ static int ses_set_physpath(enc_softc_t *enc, enc_element_t *elm, struct ses_iterator *iter) { struct ccb_dev_advinfo cdai; ses_setphyspath_callback_args_t args; int i, ret; struct sbuf sb; struct scsi_vpd_id_descriptor *idd; uint8_t *devid; ses_element_t *elmpriv; const char *c; ret = EIO; devid = NULL; elmpriv = elm->elm_private; if (elmpriv->addl.hdr == NULL) goto out; /* * Assemble the components of the physical path starting with * the device ID of the enclosure itself. */ memset(&cdai, 0, sizeof(cdai)); xpt_setup_ccb(&cdai.ccb_h, enc->periph->path, CAM_PRIORITY_NORMAL); cdai.ccb_h.func_code = XPT_DEV_ADVINFO; cdai.flags = CDAI_FLAG_NONE; cdai.buftype = CDAI_TYPE_SCSI_DEVID; cdai.bufsiz = CAM_SCSI_DEVID_MAXLEN; cdai.buf = devid = malloc(cdai.bufsiz, M_SCSIENC, M_WAITOK|M_ZERO); cam_periph_lock(enc->periph); xpt_action((union ccb *)&cdai); if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); cam_periph_unlock(enc->periph); if (cdai.ccb_h.status != CAM_REQ_CMP) goto out; idd = scsi_get_devid((struct scsi_vpd_device_id *)cdai.buf, cdai.provsiz, scsi_devid_is_naa_ieee_reg); if (idd == NULL) goto out; if (sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND) == NULL) { ret = ENOMEM; goto out; } /* Next, generate the physical path string */ sbuf_printf(&sb, "id1,enc@n%jx/type@%x/slot@%x", scsi_8btou64(idd->identifier), iter->type_index, iter->type_element_index); /* Append the element descriptor if one exists */ if (elmpriv->descr != NULL && elmpriv->descr_len > 0) { sbuf_cat(&sb, "/elmdesc@"); for (i = 0, c = elmpriv->descr; i < elmpriv->descr_len; i++, c++) { if (!isprint(*c) || isspace(*c) || *c == '/') sbuf_putc(&sb, '_'); else sbuf_putc(&sb, *c); } } sbuf_finish(&sb); /* * Set this physical path on any CAM devices with a device ID * descriptor that matches one created from the SES additional * status data for this element. */ args.physpath= &sb; args.num_set = 0; ses_paths_iter(enc, elm, ses_setphyspath_callback, &args); sbuf_delete(&sb); ret = args.num_set == 0 ? ENOENT : 0; out: if (devid != NULL) ENC_FREE(devid); return (ret); } /** * \brief Helper to set the CDB fields appropriately. * * \param cdb Buffer containing the cdb. * \param pagenum SES diagnostic page to query for. * \param dir Direction of query. */ static void ses_page_cdb(char *cdb, int bufsiz, SesDiagPageCodes pagenum, int dir) { /* Ref: SPC-4 r25 Section 6.20 Table 223 */ if (dir == CAM_DIR_IN) { cdb[0] = RECEIVE_DIAGNOSTIC; cdb[1] = 1; /* Set page code valid bit */ cdb[2] = pagenum; } else { cdb[0] = SEND_DIAGNOSTIC; cdb[1] = 0x10; cdb[2] = pagenum; } cdb[3] = bufsiz >> 8; /* high bits */ cdb[4] = bufsiz & 0xff; /* low bits */ cdb[5] = 0; } /** * \brief Discover whether this instance supports timed completion of a * RECEIVE DIAGNOSTIC RESULTS command requesting the Enclosure Status * page, and store the result in the softc, updating if necessary. * * \param enc SES instance to query and update. * \param tc_en Value of timed completion to set (see \return). * * \return 1 if timed completion enabled, 0 otherwise. */ static int ses_set_timed_completion(enc_softc_t *enc, uint8_t tc_en) { union ccb *ccb; struct cam_periph *periph; struct ses_mgmt_mode_page *mgmt; uint8_t *mode_buf; size_t mode_buf_len; ses_softc_t *ses; periph = enc->periph; ses = enc->enc_private; ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); mode_buf_len = sizeof(struct ses_mgmt_mode_page); mode_buf = ENC_MALLOCZ(mode_buf_len); if (mode_buf == NULL) goto out; scsi_mode_sense(&ccb->csio, /*retries*/4, NULL, MSG_SIMPLE_Q_TAG, /*dbd*/FALSE, SMS_PAGE_CTRL_CURRENT, SES_MGMT_MODE_PAGE_CODE, mode_buf, mode_buf_len, SSD_FULL_SIZE, /*timeout*/60 * 1000); /* * Ignore illegal request errors, as they are quite common and we * will print something out in that case anyway. */ cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS|SF_QUIET_IR, NULL); if (ccb->ccb_h.status != CAM_REQ_CMP) { ENC_VLOG(enc, "Timed Completion Unsupported\n"); goto release; } /* Skip the mode select if the desired value is already set */ mgmt = (struct ses_mgmt_mode_page *)mode_buf; if ((mgmt->byte5 & SES_MGMT_TIMED_COMP_EN) == tc_en) goto done; /* Value is not what we wanted, set it */ if (tc_en) mgmt->byte5 |= SES_MGMT_TIMED_COMP_EN; else mgmt->byte5 &= ~SES_MGMT_TIMED_COMP_EN; /* SES2r20: a completion time of zero means as long as possible */ bzero(&mgmt->max_comp_time, sizeof(mgmt->max_comp_time)); scsi_mode_select(&ccb->csio, 5, NULL, MSG_SIMPLE_Q_TAG, /*page_fmt*/FALSE, /*save_pages*/TRUE, mode_buf, mode_buf_len, SSD_FULL_SIZE, /*timeout*/60 * 1000); cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS, NULL); if (ccb->ccb_h.status != CAM_REQ_CMP) { ENC_VLOG(enc, "Timed Completion Set Failed\n"); goto release; } done: if ((mgmt->byte5 & SES_MGMT_TIMED_COMP_EN) != 0) { ENC_LOG(enc, "Timed Completion Enabled\n"); ses->ses_flags |= SES_FLAG_TIMEDCOMP; } else { ENC_LOG(enc, "Timed Completion Disabled\n"); ses->ses_flags &= ~SES_FLAG_TIMEDCOMP; } release: ENC_FREE(mode_buf); xpt_release_ccb(ccb); out: return (ses->ses_flags & SES_FLAG_TIMEDCOMP); } /** * \brief Process the list of supported pages and update flags. * * \param enc SES device to query. * \param buf Buffer containing the config page. * \param xfer_len Length of the config page in the buffer. * * \return 0 on success, errno otherwise. */ static int ses_process_pages(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { ses_softc_t *ses; struct scsi_diag_page *page; int err, i, length; CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, ("entering %s(%p, %d)\n", __func__, bufp, xfer_len)); ses = enc->enc_private; err = -1; if (error != 0) { err = error; goto out; } if (xfer_len < sizeof(*page)) { ENC_VLOG(enc, "Unable to parse Diag Pages List Header\n"); err = EIO; goto out; } page = (struct scsi_diag_page *)*bufp; length = scsi_2btoul(page->length); if (length + offsetof(struct scsi_diag_page, params) > xfer_len) { ENC_VLOG(enc, "Diag Pages List Too Long\n"); goto out; } ENC_DLOG(enc, "%s: page length %d, xfer_len %d\n", __func__, length, xfer_len); err = 0; for (i = 0; i < length; i++) { if (page->params[i] == SesElementDescriptor) ses->ses_flags |= SES_FLAG_DESC; else if (page->params[i] == SesAddlElementStatus) ses->ses_flags |= SES_FLAG_ADDLSTATUS; } out: ENC_DLOG(enc, "%s: exiting with err %d\n", __func__, err); return (err); } /** * \brief Process the config page and update associated structures. * * \param enc SES device to query. * \param buf Buffer containing the config page. * \param xfer_len Length of the config page in the buffer. * * \return 0 on success, errno otherwise. */ static int ses_process_config(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { struct ses_iterator iter; enc_cache_t *enc_cache; ses_cache_t *ses_cache; uint8_t *buf; int length; int err; int nelm; int ntype; struct ses_cfg_page *cfg_page; struct ses_enc_desc *buf_subenc; const struct ses_enc_desc **subencs; const struct ses_enc_desc **cur_subenc; const struct ses_enc_desc **last_subenc; ses_type_t *ses_types; ses_type_t *sestype; const struct ses_elm_type_desc *cur_buf_type; const struct ses_elm_type_desc *last_buf_type; uint8_t *last_valid_byte; enc_element_t *element; const char *type_text; CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, ("entering %s(%p, %d)\n", __func__, bufp, xfer_len)); enc_cache = &enc->enc_daemon_cache; ses_cache = enc_cache->private; buf = *bufp; err = -1; if (error != 0) { err = error; goto out; } if (xfer_len < sizeof(cfg_page->hdr)) { ENC_VLOG(enc, "Unable to parse SES Config Header\n"); err = EIO; goto out; } cfg_page = (struct ses_cfg_page *)buf; length = ses_page_length(&cfg_page->hdr); if (length > xfer_len) { ENC_VLOG(enc, "Enclosure Config Page Too Long\n"); goto out; } last_valid_byte = &buf[length - 1]; ENC_DLOG(enc, "%s: total page length %d, xfer_len %d\n", __func__, length, xfer_len); err = 0; if (ses_config_cache_valid(ses_cache, cfg_page->hdr.gen_code)) { /* Our cache is still valid. Proceed to fetching status. */ goto out; } /* Cache is no longer valid. Free old data to make way for new. */ ses_cache_free(enc, enc_cache); ENC_VLOG(enc, "Generation Code 0x%x has %d SubEnclosures\n", scsi_4btoul(cfg_page->hdr.gen_code), ses_cfg_page_get_num_subenc(cfg_page)); /* Take ownership of the buffer. */ ses_cache->cfg_page = cfg_page; *bufp = NULL; /* * Now waltz through all the subenclosures summing the number of * types available in each. */ subencs = malloc(ses_cfg_page_get_num_subenc(cfg_page) * sizeof(*subencs), M_SCSIENC, M_WAITOK|M_ZERO); /* * Sub-enclosure data is const after construction (i.e. when * accessed via our cache object. * * The cast here is not required in C++ but C99 is not so * sophisticated (see C99 6.5.16.1(1)). */ ses_cache->ses_nsubencs = ses_cfg_page_get_num_subenc(cfg_page); ses_cache->subencs = subencs; buf_subenc = cfg_page->subencs; cur_subenc = subencs; last_subenc = &subencs[ses_cache->ses_nsubencs - 1]; ntype = 0; while (cur_subenc <= last_subenc) { if (!ses_enc_desc_is_complete(buf_subenc, last_valid_byte)) { ENC_VLOG(enc, "Enclosure %d Beyond End of " "Descriptors\n", cur_subenc - subencs); err = EIO; goto out; } ENC_VLOG(enc, " SubEnclosure ID %d, %d Types With this ID, " "Descriptor Length %d, offset %d\n", buf_subenc->subenc_id, buf_subenc->num_types, buf_subenc->length, &buf_subenc->byte0 - buf); ENC_VLOG(enc, "WWN: %jx\n", (uintmax_t)scsi_8btou64(buf_subenc->logical_id)); ntype += buf_subenc->num_types; *cur_subenc = buf_subenc; cur_subenc++; buf_subenc = ses_enc_desc_next(buf_subenc); } /* Process the type headers. */ ses_types = malloc(ntype * sizeof(*ses_types), M_SCSIENC, M_WAITOK|M_ZERO); /* * Type data is const after construction (i.e. when accessed via * our cache object. */ ses_cache->ses_ntypes = ntype; ses_cache->ses_types = ses_types; cur_buf_type = (const struct ses_elm_type_desc *) (&(*last_subenc)->length + (*last_subenc)->length + 1); last_buf_type = cur_buf_type + ntype - 1; type_text = (const uint8_t *)(last_buf_type + 1); nelm = 0; sestype = ses_types; while (cur_buf_type <= last_buf_type) { if (&cur_buf_type->etype_txt_len > last_valid_byte) { ENC_VLOG(enc, "Runt Enclosure Type Header %d\n", sestype - ses_types); err = EIO; goto out; } sestype->hdr = cur_buf_type; sestype->text = type_text; type_text += cur_buf_type->etype_txt_len; ENC_VLOG(enc, " Type Desc[%d]: Type 0x%x, MaxElt %d, In Subenc " "%d, Text Length %d: %.*s\n", sestype - ses_types, sestype->hdr->etype_elm_type, sestype->hdr->etype_maxelt, sestype->hdr->etype_subenc, sestype->hdr->etype_txt_len, sestype->hdr->etype_txt_len, sestype->text); nelm += sestype->hdr->etype_maxelt + /*overall status element*/1; sestype++; cur_buf_type++; } /* Create the object map. */ enc_cache->elm_map = malloc(nelm * sizeof(enc_element_t), M_SCSIENC, M_WAITOK|M_ZERO); enc_cache->nelms = nelm; ses_iter_init(enc, enc_cache, &iter); while ((element = ses_iter_next(&iter)) != NULL) { const struct ses_elm_type_desc *thdr; ENC_DLOG(enc, "%s: checking obj %d(%d,%d)\n", __func__, iter.global_element_index, iter.type_index, nelm, iter.type_element_index); thdr = ses_cache->ses_types[iter.type_index].hdr; element->elm_idx = iter.global_element_index; element->elm_type = thdr->etype_elm_type; element->subenclosure = thdr->etype_subenc; element->type_elm_idx = iter.type_element_index; element->elm_private = malloc(sizeof(ses_element_t), M_SCSIENC, M_WAITOK|M_ZERO); ENC_DLOG(enc, "%s: creating elmpriv %d(%d,%d) subenc %d " "type 0x%x\n", __func__, iter.global_element_index, iter.type_index, iter.type_element_index, thdr->etype_subenc, thdr->etype_elm_type); } err = 0; out: if (err) ses_cache_free(enc, enc_cache); else { ses_poll_status(enc); enc_update_request(enc, SES_PUBLISH_CACHE); } ENC_DLOG(enc, "%s: exiting with err %d\n", __func__, err); return (err); } /** * \brief Update the status page and associated structures. * * \param enc SES softc to update for. * \param buf Buffer containing the status page. * \param bufsz Amount of data in the buffer. * * \return 0 on success, errno otherwise. */ static int ses_process_status(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { struct ses_iterator iter; enc_element_t *element; ses_softc_t *ses; enc_cache_t *enc_cache; ses_cache_t *ses_cache; uint8_t *buf; int err = -1; int length; struct ses_status_page *page; union ses_status_element *cur_stat; union ses_status_element *last_stat; ses = enc->enc_private; enc_cache = &enc->enc_daemon_cache; ses_cache = enc_cache->private; buf = *bufp; ENC_DLOG(enc, "%s: enter (%p, %p, %d)\n", __func__, enc, buf, xfer_len); page = (struct ses_status_page *)buf; length = ses_page_length(&page->hdr); if (error != 0) { err = error; goto out; } /* * Make sure the length fits in the buffer. * * XXX all this means is that the page is larger than the space * we allocated. Since we use a statically sized buffer, this * could happen... Need to use dynamic discovery of the size. */ if (length > xfer_len) { ENC_VLOG(enc, "Enclosure Status Page Too Long\n"); goto out; } /* Check for simple enclosure reporting short enclosure status. */ if (length >= 4 && page->hdr.page_code == SesShortStatus) { ENC_DLOG(enc, "Got Short Enclosure Status page\n"); ses->ses_flags &= ~(SES_FLAG_ADDLSTATUS | SES_FLAG_DESC); ses_cache_free(enc, enc_cache); enc_cache->enc_status = page->hdr.page_specific_flags; enc_update_request(enc, SES_PUBLISH_CACHE); err = 0; goto out; } /* Make sure the length contains at least one header and status */ if (length < (sizeof(*page) + sizeof(*page->elements))) { ENC_VLOG(enc, "Enclosure Status Page Too Short\n"); goto out; } if (!ses_config_cache_valid(ses_cache, page->hdr.gen_code)) { ENC_DLOG(enc, "%s: Generation count change detected\n", __func__); enc_update_request(enc, SES_UPDATE_GETCONFIG); goto out; } ses_cache_free_status(enc, enc_cache); ses_cache->status_page = page; *bufp = NULL; enc_cache->enc_status = page->hdr.page_specific_flags; /* * Read in individual element status. The element order * matches the order reported in the config page (i.e. the * order of an unfiltered iteration of the config objects).. */ ses_iter_init(enc, enc_cache, &iter); cur_stat = page->elements; last_stat = (union ses_status_element *) &buf[length - sizeof(*last_stat)]; ENC_DLOG(enc, "%s: total page length %d, xfer_len %d\n", __func__, length, xfer_len); while (cur_stat <= last_stat && (element = ses_iter_next(&iter)) != NULL) { ENC_DLOG(enc, "%s: obj %d(%d,%d) off=0x%tx status=%jx\n", __func__, iter.global_element_index, iter.type_index, iter.type_element_index, (uint8_t *)cur_stat - buf, scsi_4btoul(cur_stat->bytes)); memcpy(&element->encstat, cur_stat, sizeof(element->encstat)); element->svalid = 1; cur_stat++; } if (ses_iter_next(&iter) != NULL) { ENC_VLOG(enc, "Status page, length insufficient for " "expected number of objects\n"); } else { if (cur_stat <= last_stat) ENC_VLOG(enc, "Status page, exhausted objects before " "exhausing page\n"); enc_update_request(enc, SES_PUBLISH_CACHE); err = 0; } out: ENC_DLOG(enc, "%s: exiting with error %d\n", __func__, err); return (err); } typedef enum { /** * The enclosure should not provide additional element * status for this element type in page 0x0A. * * \note This status is returned for any types not * listed SES3r02. Further types added in a * future specification will be incorrectly * classified. */ TYPE_ADDLSTATUS_NONE, /** * The element type provides additional element status * in page 0x0A. */ TYPE_ADDLSTATUS_MANDATORY, /** * The element type may provide additional element status * in page 0x0A, but i */ TYPE_ADDLSTATUS_OPTIONAL } ses_addlstatus_avail_t; /** * \brief Check to see whether a given type (as obtained via type headers) is * supported by the additional status command. * * \param enc SES softc to check. * \param typidx Type index to check for. * * \return An enumeration indicating if additional status is mandatory, * optional, or not required for this type. */ static ses_addlstatus_avail_t ses_typehasaddlstatus(enc_softc_t *enc, uint8_t typidx) { enc_cache_t *enc_cache; ses_cache_t *ses_cache; enc_cache = &enc->enc_daemon_cache; ses_cache = enc_cache->private; switch(ses_cache->ses_types[typidx].hdr->etype_elm_type) { case ELMTYP_DEVICE: case ELMTYP_ARRAY_DEV: case ELMTYP_SAS_EXP: return (TYPE_ADDLSTATUS_MANDATORY); case ELMTYP_SCSI_INI: case ELMTYP_SCSI_TGT: case ELMTYP_ESCC: return (TYPE_ADDLSTATUS_OPTIONAL); default: /* No additional status information available. */ break; } return (TYPE_ADDLSTATUS_NONE); } static int ses_get_elm_addlstatus_fc(enc_softc_t *, enc_cache_t *, uint8_t *, int); static int ses_get_elm_addlstatus_sas(enc_softc_t *, enc_cache_t *, uint8_t *, int, int, int, int); static int ses_get_elm_addlstatus_ata(enc_softc_t *, enc_cache_t *, uint8_t *, int, int, int, int); /** * \brief Parse the additional status element data for each object. * * \param enc The SES softc to update. * \param buf The buffer containing the additional status * element response. * \param xfer_len Size of the buffer. * * \return 0 on success, errno otherwise. */ static int ses_process_elm_addlstatus(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { struct ses_iterator iter, titer; int eip; int err; int length; int offset; enc_cache_t *enc_cache; ses_cache_t *ses_cache; uint8_t *buf; ses_element_t *elmpriv; const struct ses_page_hdr *hdr; enc_element_t *element, *telement; enc_cache = &enc->enc_daemon_cache; ses_cache = enc_cache->private; buf = *bufp; err = -1; if (error != 0) { err = error; goto out; } ses_cache_free_elm_addlstatus(enc, enc_cache); ses_cache->elm_addlstatus_page = (struct ses_addl_elem_status_page *)buf; *bufp = NULL; /* * The objects appear in the same order here as in Enclosure Status, * which itself is ordered by the Type Descriptors from the Config * page. However, it is necessary to skip elements that are not * supported by this page when counting them. */ hdr = &ses_cache->elm_addlstatus_page->hdr; length = ses_page_length(hdr); ENC_DLOG(enc, "Additional Element Status Page Length 0x%x\n", length); /* Make sure the length includes at least one header. */ if (length < sizeof(*hdr)+sizeof(struct ses_elm_addlstatus_base_hdr)) { ENC_VLOG(enc, "Runt Additional Element Status Page\n"); goto out; } if (length > xfer_len) { ENC_VLOG(enc, "Additional Element Status Page Too Long\n"); goto out; } if (!ses_config_cache_valid(ses_cache, hdr->gen_code)) { ENC_DLOG(enc, "%s: Generation count change detected\n", __func__); enc_update_request(enc, SES_UPDATE_GETCONFIG); goto out; } offset = sizeof(struct ses_page_hdr); ses_iter_init(enc, enc_cache, &iter); while (offset < length && (element = ses_iter_next(&iter)) != NULL) { struct ses_elm_addlstatus_base_hdr *elm_hdr; int proto_info_len; ses_addlstatus_avail_t status_type; /* * Additional element status is only provided for * individual elements (i.e. overal status elements * are excluded) and those of the types specified * in the SES spec. */ status_type = ses_typehasaddlstatus(enc, iter.type_index); if (iter.individual_element_index == ITERATOR_INDEX_INVALID || status_type == TYPE_ADDLSTATUS_NONE) continue; elm_hdr = (struct ses_elm_addlstatus_base_hdr *)&buf[offset]; eip = ses_elm_addlstatus_eip(elm_hdr); if (eip) { struct ses_elm_addlstatus_eip_hdr *eip_hdr; int expected_index, index; ses_elem_index_type_t index_type; eip_hdr = (struct ses_elm_addlstatus_eip_hdr *)elm_hdr; if (SES_ADDL_EIP_EIIOE_EI_GLOB(eip_hdr->byte2)) { index_type = SES_ELEM_INDEX_GLOBAL; expected_index = iter.global_element_index; } else { index_type = SES_ELEM_INDEX_INDIVIDUAL; expected_index = iter.individual_element_index; } if (eip_hdr->element_index < expected_index) { ENC_VLOG(enc, "%s: provided %selement index " "%d is lower then expected %d\n", __func__, SES_ADDL_EIP_EIIOE_EI_GLOB( eip_hdr->byte2) ? "global " : "", eip_hdr->element_index, expected_index); goto badindex; } titer = iter; telement = ses_iter_seek_to(&titer, eip_hdr->element_index, index_type); if (telement == NULL) { ENC_VLOG(enc, "%s: provided %selement index " "%d does not exist\n", __func__, SES_ADDL_EIP_EIIOE_EI_GLOB(eip_hdr->byte2) ? "global " : "", eip_hdr->element_index); goto badindex; } if (ses_typehasaddlstatus(enc, titer.type_index) == TYPE_ADDLSTATUS_NONE) { ENC_VLOG(enc, "%s: provided %selement index " "%d can't have additional status\n", __func__, SES_ADDL_EIP_EIIOE_EI_GLOB(eip_hdr->byte2) ? "global " : "", eip_hdr->element_index); badindex: /* * If we expected mandatory element, we may * guess it was just a wrong index and we may * use the status. If element was optional, * then we have no idea where status belongs. */ if (status_type == TYPE_ADDLSTATUS_OPTIONAL) break; } else { iter = titer; element = telement; } if (SES_ADDL_EIP_EIIOE_EI_GLOB(eip_hdr->byte2)) index = iter.global_element_index; else index = iter.individual_element_index; if (index > expected_index && status_type == TYPE_ADDLSTATUS_MANDATORY) { ENC_VLOG(enc, "%s: provided %s element" "index %d skips mandatory status " " element at index %d\n", __func__, SES_ADDL_EIP_EIIOE_EI_GLOB( eip_hdr->byte2) ? "global " : "", index, expected_index); } } elmpriv = element->elm_private; ENC_DLOG(enc, "%s: global element index=%d, type index=%d " "type element index=%d, offset=0x%x, " "byte0=0x%x, length=0x%x\n", __func__, iter.global_element_index, iter.type_index, iter.type_element_index, offset, elm_hdr->byte0, elm_hdr->length); /* Skip to after the length field */ offset += sizeof(struct ses_elm_addlstatus_base_hdr); /* Make sure the descriptor is within bounds */ if ((offset + elm_hdr->length) > length) { ENC_VLOG(enc, "Element %d Beyond End " "of Additional Element Status Descriptors\n", iter.global_element_index); break; } /* Skip elements marked as invalid. */ if (ses_elm_addlstatus_invalid(elm_hdr)) { offset += elm_hdr->length; continue; } elmpriv->addl.hdr = elm_hdr; /* Advance to the protocol data, skipping eip bytes if needed */ offset += (eip * SES_EIP_HDR_EXTRA_LEN); proto_info_len = elm_hdr->length - (eip * SES_EIP_HDR_EXTRA_LEN); /* Errors in this block are ignored as they are non-fatal */ switch(ses_elm_addlstatus_proto(elm_hdr)) { case SPSP_PROTO_FC: if (elm_hdr->length == 0) break; ses_get_elm_addlstatus_fc(enc, enc_cache, &buf[offset], proto_info_len); break; case SPSP_PROTO_SAS: if (elm_hdr->length <= 2) break; ses_get_elm_addlstatus_sas(enc, enc_cache, &buf[offset], proto_info_len, eip, iter.type_index, iter.global_element_index); break; case SPSP_PROTO_ATA: ses_get_elm_addlstatus_ata(enc, enc_cache, &buf[offset], proto_info_len, eip, iter.type_index, iter.global_element_index); break; default: ENC_VLOG(enc, "Element %d: Unknown Additional Element " "Protocol 0x%x\n", iter.global_element_index, ses_elm_addlstatus_proto(elm_hdr)); break; } offset += proto_info_len; } err = 0; out: if (err) ses_cache_free_elm_addlstatus(enc, enc_cache); enc_update_request(enc, SES_PUBLISH_PHYSPATHS); enc_update_request(enc, SES_PUBLISH_CACHE); return (err); } static int ses_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { ses_softc_t *ses; ses = enc->enc_private; /* * Possible errors: * o Generation count wrong. * o Some SCSI status error. */ ses_terminate_control_requests(&ses->ses_pending_requests, error); ses_poll_status(enc); return (0); } static int ses_publish_physpaths(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { struct ses_iterator iter; enc_cache_t *enc_cache; enc_element_t *element; enc_cache = &enc->enc_daemon_cache; ses_iter_init(enc, enc_cache, &iter); while ((element = ses_iter_next(&iter)) != NULL) { /* * ses_set_physpath() returns success if we changed * the physpath of any element. This allows us to * only announce devices once regardless of how * many times we process additional element status. */ if (ses_set_physpath(enc, element, &iter) == 0) ses_print_addl_data(enc, element); } return (0); } static int ses_publish_cache(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { sx_xlock(&enc->enc_cache_lock); ses_cache_clone(enc, /*src*/&enc->enc_daemon_cache, /*dst*/&enc->enc_cache); sx_xunlock(&enc->enc_cache_lock); return (0); } /* * \brief Sanitize an element descriptor * * The SES4r3 standard, sections 3.1.2 and 6.1.10, specifies that element * descriptors may only contain ASCII characters in the range 0x20 to 0x7e. * But some vendors violate that rule. Ensure that we only expose compliant * descriptors to userland. * * \param desc SES element descriptor as reported by the hardware * \param len Length of desc in bytes, not necessarily including * trailing NUL. It will be modified if desc is invalid. */ static const char* ses_sanitize_elm_desc(const char *desc, uint16_t *len) { const char *invalid = ""; int i; for (i = 0; i < *len; i++) { if (desc[i] == 0) { break; } else if (desc[i] < 0x20 || desc[i] > 0x7e) { *len = strlen(invalid); return (invalid); } } return (desc); } /** * \brief Parse the descriptors for each object. * * \param enc The SES softc to update. * \param buf The buffer containing the descriptor list response. * \param xfer_len Size of the buffer. * * \return 0 on success, errno otherwise. */ static int ses_process_elm_descs(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { ses_softc_t *ses; struct ses_iterator iter; enc_element_t *element; int err; int offset; u_long length, plength; enc_cache_t *enc_cache; ses_cache_t *ses_cache; uint8_t *buf; ses_element_t *elmpriv; const struct ses_page_hdr *phdr; const struct ses_elm_desc_hdr *hdr; ses = enc->enc_private; enc_cache = &enc->enc_daemon_cache; ses_cache = enc_cache->private; buf = *bufp; err = -1; if (error != 0) { err = error; goto out; } ses_cache_free_elm_descs(enc, enc_cache); ses_cache->elm_descs_page = (struct ses_elem_descr_page *)buf; *bufp = NULL; phdr = &ses_cache->elm_descs_page->hdr; plength = ses_page_length(phdr); if (xfer_len < sizeof(struct ses_page_hdr)) { ENC_VLOG(enc, "Runt Element Descriptor Page\n"); goto out; } if (plength > xfer_len) { ENC_VLOG(enc, "Element Descriptor Page Too Long\n"); goto out; } if (!ses_config_cache_valid(ses_cache, phdr->gen_code)) { ENC_VLOG(enc, "%s: Generation count change detected\n", __func__); enc_update_request(enc, SES_UPDATE_GETCONFIG); goto out; } offset = sizeof(struct ses_page_hdr); ses_iter_init(enc, enc_cache, &iter); while (offset < plength && (element = ses_iter_next(&iter)) != NULL) { if ((offset + sizeof(struct ses_elm_desc_hdr)) > plength) { ENC_VLOG(enc, "Element %d Descriptor Header Past " "End of Buffer\n", iter.global_element_index); goto out; } hdr = (struct ses_elm_desc_hdr *)&buf[offset]; length = scsi_2btoul(hdr->length); ENC_DLOG(enc, "%s: obj %d(%d,%d) length=%d off=%d\n", __func__, iter.global_element_index, iter.type_index, iter.type_element_index, length, offset); if ((offset + sizeof(*hdr) + length) > plength) { ENC_VLOG(enc, "Element%d Descriptor Past " "End of Buffer\n", iter.global_element_index); goto out; } offset += sizeof(*hdr); if (length > 0) { elmpriv = element->elm_private; elmpriv->descr_len = length; elmpriv->descr = ses_sanitize_elm_desc(&buf[offset], &elmpriv->descr_len); } /* skip over the descriptor itself */ offset += length; } err = 0; out: if (err == 0) { if (ses->ses_flags & SES_FLAG_ADDLSTATUS) enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS); } enc_update_request(enc, SES_PUBLISH_CACHE); return (err); } static int ses_fill_rcv_diag_io(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t *buf) { if (enc->enc_type == ENC_SEMB_SES) { semb_receive_diagnostic_results(&ccb->ataio, /*retries*/5, NULL, MSG_SIMPLE_Q_TAG, /*pcv*/1, state->page_code, buf, state->buf_size, state->timeout); } else { scsi_receive_diagnostic_results(&ccb->csio, /*retries*/5, NULL, MSG_SIMPLE_Q_TAG, /*pcv*/1, state->page_code, buf, state->buf_size, SSD_FULL_SIZE, state->timeout); } return (0); } /** * \brief Encode the object status into the response buffer, which is * expected to contain the current enclosure status. This function * turns off all the 'select' bits for the objects except for the * object specified, then sends it back to the enclosure. * * \param enc SES enclosure the change is being applied to. * \param buf Buffer containing the current enclosure status response. * \param amt Length of the response in the buffer. * \param req The control request to be applied to buf. * * \return 0 on success, errno otherwise. */ static int ses_encode(enc_softc_t *enc, uint8_t *buf, int amt, ses_control_request_t *req) { struct ses_iterator iter; enc_element_t *element; int offset; struct ses_control_page_hdr *hdr; ses_iter_init(enc, &enc->enc_cache, &iter); hdr = (struct ses_control_page_hdr *)buf; if (req->elm_idx == -1) { /* for enclosure status, at least 2 bytes are needed */ if (amt < 2) return EIO; hdr->control_flags = req->elm_stat.comstatus & SES_SET_STATUS_MASK; ENC_DLOG(enc, "Set EncStat %x\n", hdr->control_flags); return (0); } element = ses_iter_seek_to(&iter, req->elm_idx, SES_ELEM_INDEX_GLOBAL); if (element == NULL) return (ENXIO); /* * Seek to the type set that corresponds to the requested object. * The +1 is for the overall status element for the type. */ offset = sizeof(struct ses_control_page_hdr) + (iter.global_element_index * sizeof(struct ses_comstat)); /* Check for buffer overflow. */ if (offset + sizeof(struct ses_comstat) > amt) return (EIO); /* Set the status. */ memcpy(&buf[offset], &req->elm_stat, sizeof(struct ses_comstat)); ENC_DLOG(enc, "Set Type 0x%x Obj 0x%x (offset %d) with %x %x %x %x\n", iter.type_index, iter.global_element_index, offset, req->elm_stat.comstatus, req->elm_stat.comstat[0], req->elm_stat.comstat[1], req->elm_stat.comstat[2]); return (0); } static int ses_fill_control_request(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t *buf) { ses_softc_t *ses; enc_cache_t *enc_cache; ses_cache_t *ses_cache; struct ses_control_page_hdr *hdr; ses_control_request_t *req; size_t plength; size_t offset; ses = enc->enc_private; enc_cache = &enc->enc_daemon_cache; ses_cache = enc_cache->private; hdr = (struct ses_control_page_hdr *)buf; if (ses_cache->status_page == NULL) { ses_terminate_control_requests(&ses->ses_requests, EIO); return (EIO); } plength = ses_page_length(&ses_cache->status_page->hdr); memcpy(buf, ses_cache->status_page, plength); /* Disable the select bits in all status entries. */ offset = sizeof(struct ses_control_page_hdr); for (offset = sizeof(struct ses_control_page_hdr); offset < plength; offset += sizeof(struct ses_comstat)) { buf[offset] &= ~SESCTL_CSEL; } /* And make sure the INVOP bit is clear. */ hdr->control_flags &= ~SES_ENCSTAT_INVOP; /* Apply incoming requests. */ while ((req = TAILQ_FIRST(&ses->ses_requests)) != NULL) { TAILQ_REMOVE(&ses->ses_requests, req, links); req->result = ses_encode(enc, buf, plength, req); if (req->result != 0) { wakeup(req); continue; } TAILQ_INSERT_TAIL(&ses->ses_pending_requests, req, links); } if (TAILQ_EMPTY(&ses->ses_pending_requests) != 0) return (ENOENT); /* Fill out the ccb */ if (enc->enc_type == ENC_SEMB_SES) { semb_send_diagnostic(&ccb->ataio, /*retries*/5, NULL, MSG_SIMPLE_Q_TAG, buf, ses_page_length(&ses_cache->status_page->hdr), state->timeout); } else { scsi_send_diagnostic(&ccb->csio, /*retries*/5, NULL, MSG_SIMPLE_Q_TAG, /*unit_offline*/0, /*device_offline*/0, /*self_test*/0, /*page_format*/1, /*self_test_code*/0, buf, ses_page_length(&ses_cache->status_page->hdr), SSD_FULL_SIZE, state->timeout); } return (0); } static int ses_get_elm_addlstatus_fc(enc_softc_t *enc, enc_cache_t *enc_cache, uint8_t *buf, int bufsiz) { ENC_VLOG(enc, "FC Device Support Stubbed in Additional Status Page\n"); return (ENODEV); } #define SES_PRINT_PORTS(p, type) do { \ if (((p) & SES_SASOBJ_DEV_PHY_PROTOMASK) != 0) { \ sbuf_printf(sbp, " %s (", type); \ if ((p) & SES_SASOBJ_DEV_PHY_SMP) \ sbuf_printf(sbp, " SMP"); \ if ((p) & SES_SASOBJ_DEV_PHY_STP) \ sbuf_printf(sbp, " STP"); \ if ((p) & SES_SASOBJ_DEV_PHY_SSP) \ sbuf_printf(sbp, " SSP"); \ sbuf_printf(sbp, " )"); \ } \ } while(0) /** * \brief Print the additional element status data for this object, for SAS * type 0 objects. See SES2 r20 Section 6.1.13.3.2. * * \param sesname SES device name associated with the object. * \param sbp Sbuf to print to. * \param obj The object to print the data for. */ static void ses_print_addl_data_sas_type0(char *sesname, struct sbuf *sbp, enc_element_t *obj) { int i; ses_element_t *elmpriv; struct ses_addl_status *addl; struct ses_elm_sas_device_phy *phy; elmpriv = obj->elm_private; addl = &(elmpriv->addl); sbuf_printf(sbp, ", SAS Slot: %d%s phys", addl->proto_hdr.sas->base_hdr.num_phys, ses_elm_sas_type0_not_all_phys(addl->proto_hdr.sas) ? "+" : ""); if (ses_elm_addlstatus_eip(addl->hdr)) sbuf_printf(sbp, " at slot %d", addl->proto_hdr.sas->type0_eip.dev_slot_num); sbuf_printf(sbp, "\n"); if (addl->proto_data.sasdev_phys == NULL) return; for (i = 0;i < addl->proto_hdr.sas->base_hdr.num_phys;i++) { phy = &addl->proto_data.sasdev_phys[i]; sbuf_printf(sbp, "%s: phy %d:", sesname, i); if (ses_elm_sas_dev_phy_sata_dev(phy)) /* Spec says all other fields are specific values */ sbuf_printf(sbp, " SATA device\n"); else { sbuf_printf(sbp, " SAS device type %d phy %d", ses_elm_sas_dev_phy_dev_type(phy), phy->phy_id); SES_PRINT_PORTS(phy->initiator_ports, "Initiator"); SES_PRINT_PORTS(phy->target_ports, "Target"); sbuf_printf(sbp, "\n"); } sbuf_printf(sbp, "%s: phy %d: parent %jx addr %jx\n", sesname, i, (uintmax_t)scsi_8btou64(phy->parent_addr), (uintmax_t)scsi_8btou64(phy->phy_addr)); } } #undef SES_PRINT_PORTS /** * \brief Print the additional element status data for this object, for SAS * type 1 objects. See SES2 r20 Sections 6.1.13.3.3 and 6.1.13.3.4. * * \param sesname SES device name associated with the object. * \param sbp Sbuf to print to. * \param obj The object to print the data for. */ static void ses_print_addl_data_sas_type1(char *sesname, struct sbuf *sbp, enc_element_t *obj) { int i, num_phys; ses_element_t *elmpriv; struct ses_addl_status *addl; struct ses_elm_sas_expander_phy *exp_phy; struct ses_elm_sas_port_phy *port_phy; elmpriv = obj->elm_private; addl = &(elmpriv->addl); sbuf_printf(sbp, ", SAS "); if (obj->elm_type == ELMTYP_SAS_EXP) { num_phys = addl->proto_hdr.sas->base_hdr.num_phys; sbuf_printf(sbp, "Expander: %d phys", num_phys); if (addl->proto_data.sasexp_phys == NULL) return; for (i = 0;i < num_phys;i++) { exp_phy = &addl->proto_data.sasexp_phys[i]; sbuf_printf(sbp, "%s: phy %d: connector %d other %d\n", sesname, i, exp_phy->connector_index, exp_phy->other_index); } } else { num_phys = addl->proto_hdr.sas->base_hdr.num_phys; sbuf_printf(sbp, "Port: %d phys", num_phys); if (addl->proto_data.sasport_phys == NULL) return; for (i = 0;i < num_phys;i++) { port_phy = &addl->proto_data.sasport_phys[i]; sbuf_printf(sbp, "%s: phy %d: id %d connector %d other %d\n", sesname, i, port_phy->phy_id, port_phy->connector_index, port_phy->other_index); sbuf_printf(sbp, "%s: phy %d: addr %jx\n", sesname, i, (uintmax_t)scsi_8btou64(port_phy->phy_addr)); } } } /** * \brief Print the additional element status data for this object, for * ATA objects. * * \param sbp Sbuf to print to. * \param obj The object to print the data for. */ static void ses_print_addl_data_ata(struct sbuf *sbp, enc_element_t *obj) { ses_element_t *elmpriv = obj->elm_private; struct ses_addl_status *addl = &elmpriv->addl; struct ses_elm_ata_hdr *ata = addl->proto_hdr.ata; sbuf_printf(sbp, ", SATA Slot: scbus%d target %d\n", scsi_4btoul(ata->bus), scsi_4btoul(ata->target)); } /** * \brief Print the additional element status data for this object. * * \param enc SES softc associated with the object. * \param obj The object to print the data for. */ static void ses_print_addl_data(enc_softc_t *enc, enc_element_t *obj) { ses_element_t *elmpriv; struct ses_addl_status *addl; struct sbuf sesname, name, out; elmpriv = obj->elm_private; if (elmpriv == NULL) return; addl = &(elmpriv->addl); if (addl->hdr == NULL) return; sbuf_new(&sesname, NULL, 16, SBUF_AUTOEXTEND); sbuf_new(&name, NULL, 16, SBUF_AUTOEXTEND); sbuf_new(&out, NULL, 512, SBUF_AUTOEXTEND); ses_paths_iter(enc, obj, ses_elmdevname_callback, &name); if (sbuf_len(&name) == 0) sbuf_printf(&name, "(none)"); sbuf_finish(&name); sbuf_printf(&sesname, "%s%d", enc->periph->periph_name, enc->periph->unit_number); sbuf_finish(&sesname); sbuf_printf(&out, "%s: %s in ", sbuf_data(&sesname), sbuf_data(&name)); if (elmpriv->descr != NULL) sbuf_printf(&out, "'%s'", elmpriv->descr); else { if (obj->elm_type <= ELMTYP_LAST) sbuf_cat(&out, elm_type_names[obj->elm_type]); else sbuf_printf(&out, "", obj->elm_type); sbuf_printf(&out, " %d", obj->type_elm_idx); if (obj->subenclosure != 0) sbuf_printf(&out, " of subenc %d", obj->subenclosure); } switch(ses_elm_addlstatus_proto(addl->hdr)) { case SPSP_PROTO_FC: goto noaddl; /* stubbed for now */ case SPSP_PROTO_SAS: if (addl->proto_hdr.sas == NULL) goto noaddl; switch(ses_elm_sas_descr_type(addl->proto_hdr.sas)) { case SES_SASOBJ_TYPE_SLOT: ses_print_addl_data_sas_type0(sbuf_data(&sesname), &out, obj); break; case SES_SASOBJ_TYPE_OTHER: ses_print_addl_data_sas_type1(sbuf_data(&sesname), &out, obj); break; default: goto noaddl; } break; case SPSP_PROTO_ATA: if (addl->proto_hdr.ata == NULL) goto noaddl; ses_print_addl_data_ata(&out, obj); break; default: noaddl: sbuf_cat(&out, "\n"); break; } sbuf_finish(&out); printf("%s", sbuf_data(&out)); sbuf_delete(&out); sbuf_delete(&name); sbuf_delete(&sesname); } /** * \brief Update the softc with the additional element status data for this * object, for SAS type 0 objects. * * \param enc SES softc to be updated. * \param buf The additional element status response buffer. * \param bufsiz Size of the response buffer. * \param eip The EIP bit value. * \param nobj Number of objects attached to the SES softc. * * \return 0 on success, errno otherwise. */ static int ses_get_elm_addlstatus_sas_type0(enc_softc_t *enc, enc_cache_t *enc_cache, uint8_t *buf, int bufsiz, int eip, int nobj) { int err, offset, physz; enc_element_t *obj; ses_element_t *elmpriv; struct ses_addl_status *addl; err = offset = 0; /* basic object setup */ obj = &(enc_cache->elm_map[nobj]); elmpriv = obj->elm_private; addl = &(elmpriv->addl); addl->proto_hdr.sas = (union ses_elm_sas_hdr *)&buf[offset]; /* Don't assume this object has any phys */ bzero(&addl->proto_data, sizeof(addl->proto_data)); if (addl->proto_hdr.sas->base_hdr.num_phys == 0) goto out; /* Skip forward to the phy list */ if (eip) offset += sizeof(struct ses_elm_sas_type0_eip_hdr); else offset += sizeof(struct ses_elm_sas_type0_base_hdr); /* Make sure the phy list fits in the buffer */ physz = addl->proto_hdr.sas->base_hdr.num_phys; physz *= sizeof(struct ses_elm_sas_device_phy); if (physz > (bufsiz - offset + 4)) { ENC_VLOG(enc, "Element %d Device Phy List Beyond End Of Buffer\n", nobj); err = EIO; goto out; } /* Point to the phy list */ addl->proto_data.sasdev_phys = (struct ses_elm_sas_device_phy *)&buf[offset]; out: return (err); } /** * \brief Update the softc with the additional element status data for this * object, for SAS type 1 objects. * * \param enc SES softc to be updated. * \param buf The additional element status response buffer. * \param bufsiz Size of the response buffer. * \param eip The EIP bit value. * \param nobj Number of objects attached to the SES softc. * * \return 0 on success, errno otherwise. */ static int ses_get_elm_addlstatus_sas_type1(enc_softc_t *enc, enc_cache_t *enc_cache, uint8_t *buf, int bufsiz, int eip, int nobj) { int err, offset, physz; enc_element_t *obj; ses_element_t *elmpriv; struct ses_addl_status *addl; err = offset = 0; /* basic object setup */ obj = &(enc_cache->elm_map[nobj]); elmpriv = obj->elm_private; addl = &(elmpriv->addl); addl->proto_hdr.sas = (union ses_elm_sas_hdr *)&buf[offset]; /* Don't assume this object has any phys */ bzero(&addl->proto_data, sizeof(addl->proto_data)); if (addl->proto_hdr.sas->base_hdr.num_phys == 0) goto out; /* Process expanders differently from other type1 cases */ if (obj->elm_type == ELMTYP_SAS_EXP) { offset += sizeof(struct ses_elm_sas_type1_expander_hdr); physz = addl->proto_hdr.sas->base_hdr.num_phys * sizeof(struct ses_elm_sas_expander_phy); if (physz > (bufsiz - offset)) { ENC_VLOG(enc, "Element %d: Expander Phy List Beyond " "End Of Buffer\n", nobj); err = EIO; goto out; } addl->proto_data.sasexp_phys = (struct ses_elm_sas_expander_phy *)&buf[offset]; } else { offset += sizeof(struct ses_elm_sas_type1_nonexpander_hdr); physz = addl->proto_hdr.sas->base_hdr.num_phys * sizeof(struct ses_elm_sas_port_phy); if (physz > (bufsiz - offset + 4)) { ENC_VLOG(enc, "Element %d: Port Phy List Beyond End " "Of Buffer\n", nobj); err = EIO; goto out; } addl->proto_data.sasport_phys = (struct ses_elm_sas_port_phy *)&buf[offset]; } out: return (err); } /** * \brief Update the softc with the additional element status data for this * object, for SAS objects. * * \param enc SES softc to be updated. * \param buf The additional element status response buffer. * \param bufsiz Size of the response buffer. * \param eip The EIP bit value. * \param tidx Type index for this object. * \param nobj Number of objects attached to the SES softc. * * \return 0 on success, errno otherwise. */ static int ses_get_elm_addlstatus_sas(enc_softc_t *enc, enc_cache_t *enc_cache, uint8_t *buf, int bufsiz, int eip, int tidx, int nobj) { int dtype, err; ses_cache_t *ses_cache; union ses_elm_sas_hdr *hdr; /* Need to be able to read the descriptor type! */ if (bufsiz < sizeof(union ses_elm_sas_hdr)) { err = EIO; goto out; } ses_cache = enc_cache->private; hdr = (union ses_elm_sas_hdr *)buf; dtype = ses_elm_sas_descr_type(hdr); switch(dtype) { case SES_SASOBJ_TYPE_SLOT: switch(ses_cache->ses_types[tidx].hdr->etype_elm_type) { case ELMTYP_DEVICE: case ELMTYP_ARRAY_DEV: break; default: ENC_VLOG(enc, "Element %d has Additional Status type 0, " "invalid for SES element type 0x%x\n", nobj, ses_cache->ses_types[tidx].hdr->etype_elm_type); err = ENODEV; goto out; } err = ses_get_elm_addlstatus_sas_type0(enc, enc_cache, buf, bufsiz, eip, nobj); break; case SES_SASOBJ_TYPE_OTHER: switch(ses_cache->ses_types[tidx].hdr->etype_elm_type) { case ELMTYP_SAS_EXP: case ELMTYP_SCSI_INI: case ELMTYP_SCSI_TGT: case ELMTYP_ESCC: break; default: ENC_VLOG(enc, "Element %d has Additional Status type 1, " "invalid for SES element type 0x%x\n", nobj, ses_cache->ses_types[tidx].hdr->etype_elm_type); err = ENODEV; goto out; } err = ses_get_elm_addlstatus_sas_type1(enc, enc_cache, buf, bufsiz, eip, nobj); break; default: ENC_VLOG(enc, "Element %d of type 0x%x has Additional Status " "of unknown type 0x%x\n", nobj, ses_cache->ses_types[tidx].hdr->etype_elm_type, dtype); err = ENODEV; break; } out: return (err); } /** * \brief Update the softc with the additional element status data for this * object, for ATA objects. * * \param enc SES softc to be updated. * \param buf The additional element status response buffer. * \param bufsiz Size of the response buffer. * \param eip The EIP bit value. * \param tidx Type index for this object. * \param nobj Number of objects attached to the SES softc. * * \return 0 on success, errno otherwise. */ static int ses_get_elm_addlstatus_ata(enc_softc_t *enc, enc_cache_t *enc_cache, uint8_t *buf, int bufsiz, int eip, int tidx, int nobj) { int err; ses_cache_t *ses_cache; if (bufsiz < sizeof(struct ses_elm_ata_hdr)) { err = EIO; goto out; } ses_cache = enc_cache->private; switch(ses_cache->ses_types[tidx].hdr->etype_elm_type) { case ELMTYP_DEVICE: case ELMTYP_ARRAY_DEV: break; default: ENC_VLOG(enc, "Element %d has Additional Status, " "invalid for SES element type 0x%x\n", nobj, ses_cache->ses_types[tidx].hdr->etype_elm_type); err = ENODEV; goto out; } ((ses_element_t *)enc_cache->elm_map[nobj].elm_private) ->addl.proto_hdr.ata = (struct ses_elm_ata_hdr *)buf; err = 0; out: return (err); } static void ses_softc_invalidate(enc_softc_t *enc) { ses_softc_t *ses; ses = enc->enc_private; ses_terminate_control_requests(&ses->ses_requests, ENXIO); } static void ses_softc_cleanup(enc_softc_t *enc) { ses_cache_free(enc, &enc->enc_cache); ses_cache_free(enc, &enc->enc_daemon_cache); ENC_FREE_AND_NULL(enc->enc_private); ENC_FREE_AND_NULL(enc->enc_cache.private); ENC_FREE_AND_NULL(enc->enc_daemon_cache.private); } static int ses_init_enc(enc_softc_t *enc) { return (0); } -static int -ses_get_enc_status(enc_softc_t *enc, int slpflag) -{ - /* Automatically updated, caller checks enc_cache->encstat itself */ - return (0); -} - static int ses_set_enc_status(enc_softc_t *enc, uint8_t encstat, int slpflag) { ses_control_request_t req; ses_softc_t *ses; ses = enc->enc_private; req.elm_idx = SES_SETSTATUS_ENC_IDX; req.elm_stat.comstatus = encstat & 0xf; TAILQ_INSERT_TAIL(&ses->ses_requests, &req, links); enc_update_request(enc, SES_PROCESS_CONTROL_REQS); cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0); return (req.result); } static int ses_get_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflag) { unsigned int i = elms->elm_idx; memcpy(elms->cstat, &enc->enc_cache.elm_map[i].encstat, 4); return (0); } static int ses_set_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflag) { ses_control_request_t req; ses_softc_t *ses; /* If this is clear, we don't do diddly. */ if ((elms->cstat[0] & SESCTL_CSEL) == 0) return (0); ses = enc->enc_private; req.elm_idx = elms->elm_idx; memcpy(&req.elm_stat, elms->cstat, sizeof(req.elm_stat)); TAILQ_INSERT_TAIL(&ses->ses_requests, &req, links); enc_update_request(enc, SES_PROCESS_CONTROL_REQS); cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0); return (req.result); } static int ses_get_elm_desc(enc_softc_t *enc, encioc_elm_desc_t *elmd) { int i = (int)elmd->elm_idx; ses_element_t *elmpriv; /* Assume caller has already checked obj_id validity */ elmpriv = enc->enc_cache.elm_map[i].elm_private; /* object might not have a descriptor */ if (elmpriv == NULL || elmpriv->descr == NULL) { elmd->elm_desc_len = 0; return (0); } if (elmd->elm_desc_len > elmpriv->descr_len) elmd->elm_desc_len = elmpriv->descr_len; copyout(elmpriv->descr, elmd->elm_desc_str, elmd->elm_desc_len); return (0); } /** * \brief Respond to ENCIOC_GETELMDEVNAME, providing a device name for the * given object id if one is available. * * \param enc SES softc to examine. * \param objdn ioctl structure to read/write device name info. * * \return 0 on success, errno otherwise. */ static int ses_get_elm_devnames(enc_softc_t *enc, encioc_elm_devnames_t *elmdn) { struct sbuf sb; int len; len = elmdn->elm_names_size; if (len < 0) return (EINVAL); cam_periph_unlock(enc->periph); sbuf_new(&sb, NULL, len, SBUF_FIXEDLEN); ses_paths_iter(enc, &enc->enc_cache.elm_map[elmdn->elm_idx], ses_elmdevname_callback, &sb); sbuf_finish(&sb); elmdn->elm_names_len = sbuf_len(&sb); copyout(sbuf_data(&sb), elmdn->elm_devnames, elmdn->elm_names_len + 1); sbuf_delete(&sb); cam_periph_lock(enc->periph); return (elmdn->elm_names_len > 0 ? 0 : ENODEV); } /** * \brief Send a string to the primary subenclosure using the String Out * SES diagnostic page. * * \param enc SES enclosure to run the command on. * \param sstr SES string structure to operate on * \param ioc Ioctl being performed * * \return 0 on success, errno otherwise. */ static int -ses_handle_string(enc_softc_t *enc, encioc_string_t *sstr, int ioc) +ses_handle_string(enc_softc_t *enc, encioc_string_t *sstr, unsigned long ioc) { enc_cache_t *enc_cache; ses_cache_t *ses_cache; const struct ses_enc_desc *enc_desc; int amt, payload, ret; char cdb[6]; char str[32]; char vendor[9]; char product[17]; char rev[5]; uint8_t *buf; size_t size, rsize; enc_cache = &enc->enc_daemon_cache; ses_cache = enc_cache->private; /* Implement SES2r20 6.1.6 */ - if (sstr->bufsiz > 0xffff) + if (sstr->bufsiz > ENC_STRING_MAX) return (EINVAL); /* buffer size too large */ switch (ioc) { case ENCIOC_SETSTRING: payload = sstr->bufsiz + 4; /* header for SEND DIAGNOSTIC */ amt = 0 - payload; buf = ENC_MALLOC(payload); if (buf == NULL) return (ENOMEM); ses_page_cdb(cdb, payload, 0, CAM_DIR_OUT); /* Construct the page request */ buf[0] = SesStringOut; buf[1] = 0; buf[2] = sstr->bufsiz >> 8; buf[3] = sstr->bufsiz & 0xff; ret = copyin(sstr->buf, &buf[4], sstr->bufsiz); if (ret != 0) { ENC_FREE(buf); return (ret); } break; case ENCIOC_GETSTRING: payload = sstr->bufsiz; amt = payload; buf = ENC_MALLOC(payload); if (buf == NULL) return (ENOMEM); ses_page_cdb(cdb, payload, SesStringIn, CAM_DIR_IN); break; case ENCIOC_GETENCNAME: if (ses_cache->ses_nsubencs < 1) return (ENODEV); enc_desc = ses_cache->subencs[0]; cam_strvis(vendor, enc_desc->vendor_id, sizeof(enc_desc->vendor_id), sizeof(vendor)); cam_strvis(product, enc_desc->product_id, sizeof(enc_desc->product_id), sizeof(product)); cam_strvis(rev, enc_desc->product_rev, sizeof(enc_desc->product_rev), sizeof(rev)); rsize = snprintf(str, sizeof(str), "%s %s %s", vendor, product, rev) + 1; if (rsize > sizeof(str)) rsize = sizeof(str); size = rsize; if (size > sstr->bufsiz) size = sstr->bufsiz; copyout(str, sstr->buf, size); sstr->bufsiz = rsize; return (size == rsize ? 0 : ENOMEM); case ENCIOC_GETENCID: if (ses_cache->ses_nsubencs < 1) return (ENODEV); enc_desc = ses_cache->subencs[0]; rsize = snprintf(str, sizeof(str), "%16jx", scsi_8btou64(enc_desc->logical_id)) + 1; if (rsize > sizeof(str)) rsize = sizeof(str); size = rsize; if (size > sstr->bufsiz) size = sstr->bufsiz; copyout(str, sstr->buf, size); sstr->bufsiz = rsize; return (size == rsize ? 0 : ENOMEM); default: return (EINVAL); } ret = enc_runcmd(enc, cdb, 6, buf, &amt); if (ret == 0 && ioc == ENCIOC_GETSTRING) ret = copyout(buf, sstr->buf, sstr->bufsiz); if (ioc == ENCIOC_SETSTRING || ioc == ENCIOC_GETSTRING) ENC_FREE(buf); return (ret); } /** * \invariant Called with cam_periph mutex held. */ static void ses_poll_status(enc_softc_t *enc) { ses_softc_t *ses; ses = enc->enc_private; enc_update_request(enc, SES_UPDATE_GETSTATUS); if (ses->ses_flags & SES_FLAG_DESC) enc_update_request(enc, SES_UPDATE_GETELMDESCS); if (ses->ses_flags & SES_FLAG_ADDLSTATUS) enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS); } /** * \brief Notification received when CAM detects a new device in the * SCSI domain in which this SEP resides. * * \param enc SES enclosure instance. */ static void ses_device_found(enc_softc_t *enc) { ses_poll_status(enc); enc_update_request(enc, SES_PUBLISH_PHYSPATHS); } static struct enc_vec ses_enc_vec = { .softc_invalidate = ses_softc_invalidate, .softc_cleanup = ses_softc_cleanup, .init_enc = ses_init_enc, - .get_enc_status = ses_get_enc_status, .set_enc_status = ses_set_enc_status, .get_elm_status = ses_get_elm_status, .set_elm_status = ses_set_elm_status, .get_elm_desc = ses_get_elm_desc, .get_elm_devnames = ses_get_elm_devnames, .handle_string = ses_handle_string, .device_found = ses_device_found, .poll_status = ses_poll_status }; /** * \brief Initialize a new SES instance. * * \param enc SES softc structure to set up the instance in. * \param doinit Do the initialization (see main driver). * * \return 0 on success, errno otherwise. */ int ses_softc_init(enc_softc_t *enc) { ses_softc_t *ses_softc; CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, ("entering enc_softc_init(%p)\n", enc)); enc->enc_vec = ses_enc_vec; enc->enc_fsm_states = enc_fsm_states; if (enc->enc_private == NULL) enc->enc_private = ENC_MALLOCZ(sizeof(ses_softc_t)); if (enc->enc_cache.private == NULL) enc->enc_cache.private = ENC_MALLOCZ(sizeof(ses_cache_t)); if (enc->enc_daemon_cache.private == NULL) enc->enc_daemon_cache.private = ENC_MALLOCZ(sizeof(ses_cache_t)); if (enc->enc_private == NULL || enc->enc_cache.private == NULL || enc->enc_daemon_cache.private == NULL) { ENC_FREE_AND_NULL(enc->enc_private); ENC_FREE_AND_NULL(enc->enc_cache.private); ENC_FREE_AND_NULL(enc->enc_daemon_cache.private); return (ENOMEM); } ses_softc = enc->enc_private; TAILQ_INIT(&ses_softc->ses_requests); TAILQ_INIT(&ses_softc->ses_pending_requests); enc_update_request(enc, SES_UPDATE_PAGES); // XXX: Move this to the FSM so it doesn't hang init if (0) (void) ses_set_timed_completion(enc, 1); return (0); }