Index: usr.sbin/bhyve/Makefile =================================================================== --- usr.sbin/bhyve/Makefile +++ usr.sbin/bhyve/Makefile @@ -35,6 +35,7 @@ mem.c \ mevent.c \ mptbl.c \ + cam_if.c \ net_backends.c \ net_utils.c \ pci_ahci.c \ Index: usr.sbin/bhyve/cam_if.c =================================================================== --- /dev/null +++ usr.sbin/bhyve/cam_if.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 1997-2007 Kenneth D. Merry + * 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. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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 + +struct cam_devlist { + STAILQ_HEAD(, cam_devitem) dev_queue; + path_id_t path_id; +}; + + +/* + * Get a path inquiry CCB for the specified device. + */ +static int +get_cpi(struct cam_device *device, struct ccb_pathinq *cpi) +{ + union ccb *ccb; + int retval = 0; + + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("get_cpi: couldn't allocate CCB"); + return (1); + } + CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->cpi); + ccb->ccb_h.func_code = XPT_PATH_INQ; + if (cam_send_ccb(device, ccb) < 0) { + warn("get_cpi: error sending Path Inquiry CCB"); + retval = 1; + goto get_cpi_bailout; + } + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + retval = 1; + goto get_cpi_bailout; + } + bcopy(&ccb->cpi, cpi, sizeof(struct ccb_pathinq)); + +get_cpi_bailout: + cam_freeccb(ccb); + return (retval); +} + +static int +scsi_cam_pass_16_send(struct cam_device *device, union ccb *ccb) +{ + struct ata_pass_16 *ata_pass_16; + struct ata_cmd ata_cmd; + + ata_pass_16 = (struct ata_pass_16 *)ccb->csio.cdb_io.cdb_bytes; + ata_cmd.command = ata_pass_16->command; + ata_cmd.control = ata_pass_16->control; + ata_cmd.features = ata_pass_16->features; + + /* Disable freezing the device queue */ + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + if (cam_send_ccb(device, ccb) < 0) { + warn("error sending ATA %s via pass_16", ata_op_string(&ata_cmd)); + return (1); + } + + /* + * Consider any non-CAM_REQ_CMP status as error and report it here, + * unless caller set AP_FLAG_CHK_COND, in which case it is reponsible. + */ + if (!(ata_pass_16->flags & AP_FLAG_CHK_COND) && + (ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + warnx("ATA %s via pass_16 failed", ata_op_string(&ata_cmd)); + return (1); + } + + return (0); +} + + +static int +ata_cam_send(struct cam_device *device, union ccb *ccb) +{ + /* Disable freezing the device queue */ + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + if (cam_send_ccb(device, ccb) < 0) { + warn("error sending ATA %s", ata_op_string(&(ccb->ataio.cmd))); + return (1); + } + + /* + * Consider any non-CAM_REQ_CMP status as error and report it here, + * unless caller set AP_FLAG_CHK_COND, in which case it is reponsible. + */ + if (!(ccb->ataio.cmd.flags & CAM_ATAIO_NEEDRESULT) && + (ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + warnx("ATA %s failed", ata_op_string(&(ccb->ataio.cmd))); + return (1); + } + + return (0); +} + +static int +ata_do_pass_16(struct cam_device *device, union ccb *ccb, int retries, + u_int32_t flags, u_int8_t protocol, u_int8_t ata_flags, + u_int8_t tag_action, u_int8_t command, u_int16_t features, + u_int64_t lba, u_int16_t sector_count, u_int8_t *data_ptr, + u_int16_t dxfer_len, int timeout) +{ + if (data_ptr != NULL) { + if (flags & CAM_DIR_OUT) + ata_flags |= AP_FLAG_TDIR_TO_DEV; + else + ata_flags |= AP_FLAG_TDIR_FROM_DEV; + } else { + ata_flags |= AP_FLAG_TLEN_NO_DATA; + } + + CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio); + + scsi_ata_pass_16(&ccb->csio, + retries, + NULL, + flags, + tag_action, + protocol, + ata_flags, + features, + sector_count, + lba, + command, + /*control*/0, + data_ptr, + dxfer_len, + /*sense_len*/SSD_FULL_SIZE, + timeout); + + return scsi_cam_pass_16_send(device, ccb); +} + +static int +ata_try_pass_16(struct cam_device *device) +{ + struct ccb_pathinq cpi; + + if (get_cpi(device, &cpi) != 0) { + warnx("couldn't get CPI"); + return (-1); + } + + if (cpi.protocol == PROTO_SCSI) { + /* possibly compatible with pass_16 */ + return (1); + } + + /* likely not compatible with pass_16 */ + return (0); +} + +int +ata_do_cmd(struct cam_device *device, union ccb *ccb, int retries, + u_int32_t flags, u_int8_t protocol, u_int8_t ata_flags, + u_int8_t tag_action, u_int8_t command, u_int16_t features, + u_int64_t lba, u_int16_t sector_count, u_int8_t *data_ptr, + u_int16_t dxfer_len, int timeout, int force48bit) +{ + int retval; + + retval = ata_try_pass_16(device); + if (retval == -1) + return (1); + + if (retval == 1) { + return (ata_do_pass_16(device, ccb, retries, flags, protocol, + ata_flags, tag_action, command, features, + lba, sector_count, data_ptr, dxfer_len, + timeout)); + } + + CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->ataio); + cam_fill_ataio(&ccb->ataio, + retries, + NULL, + flags, + tag_action, + data_ptr, + dxfer_len, + timeout); + + if (force48bit || lba > ATA_MAX_28BIT_LBA) + ata_48bit_cmd(&ccb->ataio, command, features, lba, sector_count); + else + ata_28bit_cmd(&ccb->ataio, command, features, lba, sector_count); + + if (ata_flags & AP_FLAG_CHK_COND) + ccb->ataio.cmd.flags |= CAM_ATAIO_NEEDRESULT; + + return ata_cam_send(device, ccb); +} + Index: usr.sbin/bhyve/pci_ahci.c =================================================================== --- usr.sbin/bhyve/pci_ahci.c +++ usr.sbin/bhyve/pci_ahci.c @@ -57,6 +57,11 @@ #include #include +#include +#include +#include +#include + #include "bhyverun.h" #include "pci_emul.h" #include "ahci.h" @@ -140,6 +145,8 @@ struct blockif_ctxt *bctx; struct pci_ahci_softc *pr_sc; struct ata_params ata_ident; + struct cam_device *cdev; + uint8_t *cmd_lst; uint8_t *rfis; int port; @@ -218,6 +225,11 @@ static void ahci_handle_port(struct ahci_port *p); +int ata_do_cmd(struct cam_device *device, union ccb *ccb, int retries, + u_int32_t flags, u_int8_t protocol, u_int8_t ata_flags, u_int8_t tag_action, + u_int8_t command, u_int16_t features, u_int64_t lba, u_int16_t sector_count, + u_int8_t *data_ptr, u_int16_t dxfer_len, int timeout, int force48bit); + static inline void lba_to_msf(uint8_t *buf, int lba) { lba += 150; @@ -993,6 +1005,73 @@ } } +static void +handle_smart(struct ahci_port *p, int slot, uint8_t *cfis) +{ + union ccb *ccb; + u_int error; + uint8_t ata_smart[512]; + + if(p->cdev) { + ccb = cam_getccb(p->cdev); ; + + error = ata_do_cmd(p->cdev, ccb, + /*retries*/ 0, + /*flags*/ CAM_DIR_IN, + /*protocol*/ AP_PROTO_PIO_IN, + /*ata_flags*/ + AP_FLAG_BYT_BLOK_BLOCKS | AP_FLAG_TLEN_SECT_CNT, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*command*/ ATA_SMART_CMD, + /*features*/ 0xD0, + /*lba*/ 0xC24F0, + /*sector_count*/ 1, + /*data_ptr*/ (u_int8_t *)ata_smart, + /*dxfer_len*/ 512, + /*timeout*/ 30 * 1000, + /*force48bit*/ 0); + } + + if (p->cdev && error == 0) { + ahci_write_fis_piosetup(p); + write_prdt(p, slot, cfis, (void *)ata_smart, + sizeof(ata_smart)); + ahci_write_fis_d2h(p, slot, cfis, ATA_S_DSC | ATA_S_READY); + } else { + ahci_write_fis_d2h(p, slot, cfis, + (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR); + } +} + +static int +copy_identify(struct ahci_port* p) +{ + union ccb *ccb; + + if ((ccb = cam_getccb(p->cdev)) == NULL) { + return(1); + } + + return ata_do_cmd(p->cdev, + ccb, + /*retries*/ 0, + /*flags*/ CAM_DIR_IN, + /*protocol*/ AP_PROTO_PIO_IN, + /*ata_flags*/ + AP_FLAG_BYT_BLOK_BLOCKS | + AP_FLAG_TLEN_SECT_CNT, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*command*/ ATA_ATA_IDENTIFY, + /*features*/ 0, + /*lba*/ 0, + /*sector_count*/ sizeof(struct ata_params) / 512, + /*data_ptr*/ (u_int8_t *)&p->ata_ident, + /*dxfer_len*/ sizeof(struct ata_params), + /*timeout*/ 30 * 1000, + /*force48bit*/ 0); +} + + static void ata_identify_init(struct ahci_port* p, int atapi) { @@ -1082,11 +1161,15 @@ ata_ident->version_minor = 0x28; ata_ident->support.command1 = (ATA_SUPPORT_POWERMGT | ATA_SUPPORT_WRITECACHE | ATA_SUPPORT_LOOKAHEAD | ATA_SUPPORT_NOP); + if(p->cdev) + ata_ident->support.command1 |= ATA_SUPPORT_SMART; ata_ident->support.command2 = (ATA_SUPPORT_ADDRESS48 | ATA_SUPPORT_FLUSHCACHE | ATA_SUPPORT_FLUSHCACHE48 | 1 << 14); ata_ident->support.extension = (1 << 14); ata_ident->enabled.command1 = (ATA_SUPPORT_POWERMGT | ATA_SUPPORT_WRITECACHE | ATA_SUPPORT_LOOKAHEAD | ATA_SUPPORT_NOP); + if(p->cdev) + ata_ident->enabled.command1 |= ATA_SUPPORT_SMART; ata_ident->enabled.command2 = (ATA_SUPPORT_ADDRESS48 | ATA_SUPPORT_FLUSHCACHE | ATA_SUPPORT_FLUSHCACHE48 | 1 << 15); ata_ident->enabled.extension = (1 << 14); @@ -1768,8 +1851,10 @@ case ATA_READ_LOG_DMA_EXT: ahci_handle_read_log(p, slot, cfis); break; - case ATA_SECURITY_FREEZE_LOCK: case ATA_SMART_CMD: + handle_smart(p, slot, cfis); + break; + case ATA_SECURITY_FREEZE_LOCK: case ATA_NOP: ahci_write_fis_d2h(p, slot, cfis, (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR); @@ -2417,6 +2502,18 @@ else if (!strcmp("model", xopts)) { ata_string((uint8_t*)(&ata_ident->model), config, 40); } + else if (!strcmp("smart", xopts)) { + sc->port[p].cdev = cam_open_device(config, O_RDWR); + if(sc->port[p].cdev == NULL) { + ret = 1; + goto open_fail; + } + copy_identify(&sc->port[p]); + /* clear following capability */ + ata_ident->support_dsm = 0; + ata_ident->multi = 0; + + } else { /* Pass all other options to blockif_open. */ *--config = '=';