svn commit: r256056 - head/usr.sbin/bhyve

Peter Grehan grehan at FreeBSD.org
Fri Oct 4 18:31:39 UTC 2013


Author: grehan
Date: Fri Oct  4 18:31:38 2013
New Revision: 256056
URL: http://svnweb.freebsd.org/changeset/base/256056

Log:
  Import Zhixiang Yu's GSoC'13 AHCI emulation:
     https://wiki.freebsd.org/SummerOfCode2013/bhyveAHCI
  
  This provides ICH8 SATA disk and ATAPI ports, selectable
  via the bhyve slot command-line parameter:
  
  SATA
    -s <slot>,ahci-hd,<image-file>
  
  ATAPI
    -s <slot>,ahci-cd,<image-file>
  
  Slight modifications by:	grehan@
  Approved by:	re@ (blanket)
  Obtained from:	FreeBSD GSoC'13

Added:
  head/usr.sbin/bhyve/ahci.h   (contents, props changed)
  head/usr.sbin/bhyve/pci_ahci.c   (contents, props changed)

Added: head/usr.sbin/bhyve/ahci.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/usr.sbin/bhyve/ahci.h	Fri Oct  4 18:31:38 2013	(r256056)
@@ -0,0 +1,304 @@
+/*-
+ * Copyright (c) 1998 - 2008 Søren Schmidt <sos at FreeBSD.org>
+ * Copyright (c) 2009-2012 Alexander Motin <mav at FreeBSD.org>
+ * 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. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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$
+ */
+
+#ifndef _AHCI_H_
+#define	_AHCI_H_
+
+/* ATA register defines */
+#define ATA_DATA                        0       /* (RW) data */
+
+#define ATA_FEATURE                     1       /* (W) feature */
+#define         ATA_F_DMA               0x01    /* enable DMA */
+#define         ATA_F_OVL               0x02    /* enable overlap */
+
+#define ATA_COUNT                       2       /* (W) sector count */
+
+#define ATA_SECTOR                      3       /* (RW) sector # */
+#define ATA_CYL_LSB                     4       /* (RW) cylinder# LSB */
+#define ATA_CYL_MSB                     5       /* (RW) cylinder# MSB */
+#define ATA_DRIVE                       6       /* (W) Sector/Drive/Head */
+#define         ATA_D_LBA               0x40    /* use LBA addressing */
+#define         ATA_D_IBM               0xa0    /* 512 byte sectors, ECC */
+
+#define ATA_COMMAND                     7       /* (W) command */
+
+#define ATA_ERROR                       8       /* (R) error */
+#define         ATA_E_ILI               0x01    /* illegal length */
+#define         ATA_E_NM                0x02    /* no media */
+#define         ATA_E_ABORT             0x04    /* command aborted */
+#define         ATA_E_MCR               0x08    /* media change request */
+#define         ATA_E_IDNF              0x10    /* ID not found */
+#define         ATA_E_MC                0x20    /* media changed */
+#define         ATA_E_UNC               0x40    /* uncorrectable data */
+#define         ATA_E_ICRC              0x80    /* UDMA crc error */
+#define		ATA_E_ATAPI_SENSE_MASK	0xf0	/* ATAPI sense key mask */
+
+#define ATA_IREASON                     9       /* (R) interrupt reason */
+#define         ATA_I_CMD               0x01    /* cmd (1) | data (0) */
+#define         ATA_I_IN                0x02    /* read (1) | write (0) */
+#define         ATA_I_RELEASE           0x04    /* released bus (1) */
+#define         ATA_I_TAGMASK           0xf8    /* tag mask */
+
+#define ATA_STATUS                      10      /* (R) status */
+#define ATA_ALTSTAT                     11      /* (R) alternate status */
+#define         ATA_S_ERROR             0x01    /* error */
+#define         ATA_S_INDEX             0x02    /* index */
+#define         ATA_S_CORR              0x04    /* data corrected */
+#define         ATA_S_DRQ               0x08    /* data request */
+#define         ATA_S_DSC               0x10    /* drive seek completed */
+#define         ATA_S_SERVICE           0x10    /* drive needs service */
+#define         ATA_S_DWF               0x20    /* drive write fault */
+#define         ATA_S_DMA               0x20    /* DMA ready */
+#define         ATA_S_READY             0x40    /* drive ready */
+#define         ATA_S_BUSY              0x80    /* busy */
+
+#define ATA_CONTROL                     12      /* (W) control */
+#define         ATA_A_IDS               0x02    /* disable interrupts */
+#define         ATA_A_RESET             0x04    /* RESET controller */
+#define         ATA_A_4BIT              0x08    /* 4 head bits */
+#define         ATA_A_HOB               0x80    /* High Order Byte enable */
+
+/* SATA register defines */
+#define ATA_SSTATUS                     13
+#define         ATA_SS_DET_MASK         0x0000000f
+#define         ATA_SS_DET_NO_DEVICE    0x00000000
+#define         ATA_SS_DET_DEV_PRESENT  0x00000001
+#define         ATA_SS_DET_PHY_ONLINE   0x00000003
+#define         ATA_SS_DET_PHY_OFFLINE  0x00000004
+
+#define         ATA_SS_SPD_MASK         0x000000f0
+#define         ATA_SS_SPD_NO_SPEED     0x00000000
+#define         ATA_SS_SPD_GEN1         0x00000010
+#define         ATA_SS_SPD_GEN2         0x00000020
+#define         ATA_SS_SPD_GEN3         0x00000040
+
+#define         ATA_SS_IPM_MASK         0x00000f00
+#define         ATA_SS_IPM_NO_DEVICE    0x00000000
+#define         ATA_SS_IPM_ACTIVE       0x00000100
+#define         ATA_SS_IPM_PARTIAL      0x00000200
+#define         ATA_SS_IPM_SLUMBER      0x00000600
+
+#define ATA_SERROR                      14
+#define         ATA_SE_DATA_CORRECTED   0x00000001
+#define         ATA_SE_COMM_CORRECTED   0x00000002
+#define         ATA_SE_DATA_ERR         0x00000100
+#define         ATA_SE_COMM_ERR         0x00000200
+#define         ATA_SE_PROT_ERR         0x00000400
+#define         ATA_SE_HOST_ERR         0x00000800
+#define         ATA_SE_PHY_CHANGED      0x00010000
+#define         ATA_SE_PHY_IERROR       0x00020000
+#define         ATA_SE_COMM_WAKE        0x00040000
+#define         ATA_SE_DECODE_ERR       0x00080000
+#define         ATA_SE_PARITY_ERR       0x00100000
+#define         ATA_SE_CRC_ERR          0x00200000
+#define         ATA_SE_HANDSHAKE_ERR    0x00400000
+#define         ATA_SE_LINKSEQ_ERR      0x00800000
+#define         ATA_SE_TRANSPORT_ERR    0x01000000
+#define         ATA_SE_UNKNOWN_FIS      0x02000000
+#define         ATA_SE_EXCHANGED        0x04000000
+
+#define ATA_SCONTROL                    15
+#define         ATA_SC_DET_MASK         0x0000000f
+#define         ATA_SC_DET_IDLE         0x00000000
+#define         ATA_SC_DET_RESET        0x00000001
+#define         ATA_SC_DET_DISABLE      0x00000004
+
+#define         ATA_SC_SPD_MASK         0x000000f0
+#define         ATA_SC_SPD_NO_SPEED     0x00000000
+#define         ATA_SC_SPD_SPEED_GEN1   0x00000010
+#define         ATA_SC_SPD_SPEED_GEN2   0x00000020
+#define         ATA_SC_SPD_SPEED_GEN3   0x00000040
+
+#define         ATA_SC_IPM_MASK         0x00000f00
+#define         ATA_SC_IPM_NONE         0x00000000
+#define         ATA_SC_IPM_DIS_PARTIAL  0x00000100
+#define         ATA_SC_IPM_DIS_SLUMBER  0x00000200
+
+#define ATA_SACTIVE                     16
+
+#define AHCI_MAX_PORTS			32
+#define AHCI_MAX_SLOTS			32
+
+/* SATA AHCI v1.0 register defines */
+#define AHCI_CAP                    0x00
+#define		AHCI_CAP_NPMASK	0x0000001f
+#define		AHCI_CAP_SXS	0x00000020
+#define		AHCI_CAP_EMS	0x00000040
+#define		AHCI_CAP_CCCS	0x00000080
+#define		AHCI_CAP_NCS	0x00001F00
+#define		AHCI_CAP_NCS_SHIFT	8
+#define		AHCI_CAP_PSC	0x00002000
+#define		AHCI_CAP_SSC	0x00004000
+#define		AHCI_CAP_PMD	0x00008000
+#define		AHCI_CAP_FBSS	0x00010000
+#define		AHCI_CAP_SPM	0x00020000
+#define		AHCI_CAP_SAM	0x00080000
+#define		AHCI_CAP_ISS	0x00F00000
+#define		AHCI_CAP_ISS_SHIFT	20
+#define		AHCI_CAP_SCLO	0x01000000
+#define		AHCI_CAP_SAL	0x02000000
+#define		AHCI_CAP_SALP	0x04000000
+#define		AHCI_CAP_SSS	0x08000000
+#define		AHCI_CAP_SMPS	0x10000000
+#define		AHCI_CAP_SSNTF	0x20000000
+#define		AHCI_CAP_SNCQ	0x40000000
+#define		AHCI_CAP_64BIT	0x80000000
+
+#define AHCI_GHC                    0x04
+#define         AHCI_GHC_AE         0x80000000
+#define         AHCI_GHC_MRSM       0x00000004
+#define         AHCI_GHC_IE         0x00000002
+#define         AHCI_GHC_HR         0x00000001
+
+#define AHCI_IS                     0x08
+#define AHCI_PI                     0x0c
+#define AHCI_VS                     0x10
+
+#define AHCI_CCCC                   0x14
+#define		AHCI_CCCC_TV_MASK	0xffff0000
+#define		AHCI_CCCC_TV_SHIFT	16
+#define		AHCI_CCCC_CC_MASK	0x0000ff00
+#define		AHCI_CCCC_CC_SHIFT	8
+#define		AHCI_CCCC_INT_MASK	0x000000f8
+#define		AHCI_CCCC_INT_SHIFT	3
+#define		AHCI_CCCC_EN		0x00000001
+#define AHCI_CCCP                   0x18
+
+#define AHCI_EM_LOC                 0x1C
+#define AHCI_EM_CTL                 0x20
+#define 	AHCI_EM_MR              0x00000001
+#define 	AHCI_EM_TM              0x00000100
+#define 	AHCI_EM_RST             0x00000200
+#define 	AHCI_EM_LED             0x00010000
+#define 	AHCI_EM_SAFTE           0x00020000
+#define 	AHCI_EM_SES2            0x00040000
+#define 	AHCI_EM_SGPIO           0x00080000
+#define 	AHCI_EM_SMB             0x01000000
+#define 	AHCI_EM_XMT             0x02000000
+#define 	AHCI_EM_ALHD            0x04000000
+#define 	AHCI_EM_PM              0x08000000
+
+#define AHCI_CAP2                   0x24
+#define		AHCI_CAP2_BOH	0x00000001
+#define		AHCI_CAP2_NVMP	0x00000002
+#define		AHCI_CAP2_APST	0x00000004
+
+#define AHCI_OFFSET                 0x100
+#define AHCI_STEP                   0x80
+
+#define AHCI_P_CLB                  0x00
+#define AHCI_P_CLBU                 0x04
+#define AHCI_P_FB                   0x08
+#define AHCI_P_FBU                  0x0c
+#define AHCI_P_IS                   0x10
+#define AHCI_P_IE                   0x14
+#define         AHCI_P_IX_DHR       0x00000001
+#define         AHCI_P_IX_PS        0x00000002
+#define         AHCI_P_IX_DS        0x00000004
+#define         AHCI_P_IX_SDB       0x00000008
+#define         AHCI_P_IX_UF        0x00000010
+#define         AHCI_P_IX_DP        0x00000020
+#define         AHCI_P_IX_PC        0x00000040
+#define         AHCI_P_IX_MP        0x00000080
+
+#define         AHCI_P_IX_PRC       0x00400000
+#define         AHCI_P_IX_IPM       0x00800000
+#define         AHCI_P_IX_OF        0x01000000
+#define         AHCI_P_IX_INF       0x04000000
+#define         AHCI_P_IX_IF        0x08000000
+#define         AHCI_P_IX_HBD       0x10000000
+#define         AHCI_P_IX_HBF       0x20000000
+#define         AHCI_P_IX_TFE       0x40000000
+#define         AHCI_P_IX_CPD       0x80000000
+
+#define AHCI_P_CMD                  0x18
+#define         AHCI_P_CMD_ST       0x00000001
+#define         AHCI_P_CMD_SUD      0x00000002
+#define         AHCI_P_CMD_POD      0x00000004
+#define         AHCI_P_CMD_CLO      0x00000008
+#define         AHCI_P_CMD_FRE      0x00000010
+#define         AHCI_P_CMD_CCS_MASK 0x00001f00
+#define         AHCI_P_CMD_CCS_SHIFT 8
+#define         AHCI_P_CMD_ISS      0x00002000
+#define         AHCI_P_CMD_FR       0x00004000
+#define         AHCI_P_CMD_CR       0x00008000
+#define         AHCI_P_CMD_CPS      0x00010000
+#define         AHCI_P_CMD_PMA      0x00020000
+#define         AHCI_P_CMD_HPCP     0x00040000
+#define         AHCI_P_CMD_MPSP     0x00080000
+#define         AHCI_P_CMD_CPD      0x00100000
+#define         AHCI_P_CMD_ESP      0x00200000
+#define         AHCI_P_CMD_FBSCP    0x00400000
+#define         AHCI_P_CMD_APSTE    0x00800000
+#define         AHCI_P_CMD_ATAPI    0x01000000
+#define         AHCI_P_CMD_DLAE     0x02000000
+#define         AHCI_P_CMD_ALPE     0x04000000
+#define         AHCI_P_CMD_ASP      0x08000000
+#define         AHCI_P_CMD_ICC_MASK 0xf0000000
+#define         AHCI_P_CMD_NOOP     0x00000000
+#define         AHCI_P_CMD_ACTIVE   0x10000000
+#define         AHCI_P_CMD_PARTIAL  0x20000000
+#define         AHCI_P_CMD_SLUMBER  0x60000000
+
+#define AHCI_P_TFD                  0x20
+#define AHCI_P_SIG                  0x24
+#define AHCI_P_SSTS                 0x28
+#define AHCI_P_SCTL                 0x2c
+#define AHCI_P_SERR                 0x30
+#define AHCI_P_SACT                 0x34
+#define AHCI_P_CI                   0x38
+#define AHCI_P_SNTF                 0x3C
+#define AHCI_P_FBS                  0x40
+#define 	AHCI_P_FBS_EN       0x00000001
+#define 	AHCI_P_FBS_DEC      0x00000002
+#define 	AHCI_P_FBS_SDE      0x00000004
+#define 	AHCI_P_FBS_DEV      0x00000f00
+#define 	AHCI_P_FBS_DEV_SHIFT 8
+#define 	AHCI_P_FBS_ADO      0x0000f000
+#define 	AHCI_P_FBS_ADO_SHIFT 12
+#define 	AHCI_P_FBS_DWE      0x000f0000
+#define 	AHCI_P_FBS_DWE_SHIFT 16
+
+/* Just to be sure, if building as module. */
+#if MAXPHYS < 512 * 1024
+#undef MAXPHYS
+#define MAXPHYS				512 * 1024
+#endif
+/* Pessimistic prognosis on number of required S/G entries */
+#define AHCI_SG_ENTRIES	(roundup(btoc(MAXPHYS) + 1, 8))
+/* Command list. 32 commands. First, 1Kbyte aligned. */
+#define AHCI_CL_OFFSET              0
+#define AHCI_CL_SIZE                32
+/* Command tables. Up to 32 commands, Each, 128byte aligned. */
+#define AHCI_CT_OFFSET              (AHCI_CL_OFFSET + AHCI_CL_SIZE * AHCI_MAX_SLOTS)
+#define AHCI_CT_SIZE                (128 + AHCI_SG_ENTRIES * 16)
+/* Total main work area. */
+#define AHCI_WORK_SIZE              (AHCI_CT_OFFSET + AHCI_CT_SIZE * ch->numslots)
+
+#endif /* _AHCI_H_ */

Added: head/usr.sbin/bhyve/pci_ahci.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/usr.sbin/bhyve/pci_ahci.c	Fri Oct  4 18:31:38 2013	(r256056)
@@ -0,0 +1,1805 @@
+/*-
+ * Copyright (c) 2013  Zhixiang Yu <zcore at freebsd.org>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/linker_set.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <sys/disk.h>
+#include <sys/ata.h>
+#include <sys/endian.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <assert.h>
+#include <pthread.h>
+#include <inttypes.h>
+
+#include "bhyverun.h"
+#include "pci_emul.h"
+#include "ahci.h"
+#include "block_if.h"
+
+#define	MAX_PORTS	6	/* Intel ICH8 AHCI supports 6 ports */
+
+#define	PxSIG_ATA	0x00000101 /* ATA drive */
+#define	PxSIG_ATAPI	0xeb140101 /* ATAPI drive */
+
+enum sata_fis_type {
+	FIS_TYPE_REGH2D		= 0x27,	/* Register FIS - host to device */
+	FIS_TYPE_REGD2H		= 0x34,	/* Register FIS - device to host */
+	FIS_TYPE_DMAACT		= 0x39,	/* DMA activate FIS - device to host */
+	FIS_TYPE_DMASETUP	= 0x41,	/* DMA setup FIS - bidirectional */
+	FIS_TYPE_DATA		= 0x46,	/* Data FIS - bidirectional */
+	FIS_TYPE_BIST		= 0x58,	/* BIST activate FIS - bidirectional */
+	FIS_TYPE_PIOSETUP	= 0x5F,	/* PIO setup FIS - device to host */
+	FIS_TYPE_SETDEVBITS	= 0xA1,	/* Set dev bits FIS - device to host */
+};
+
+/*
+ * SCSI opcodes
+ */
+#define	TEST_UNIT_READY		0x00
+#define	REQUEST_SENSE		0x03
+#define	INQUIRY			0x12
+#define	START_STOP_UNIT		0x1B
+#define	PREVENT_ALLOW		0x1E
+#define	READ_CAPACITY		0x25
+#define	READ_10			0x28
+#define	POSITION_TO_ELEMENT	0x2B
+#define	READ_TOC		0x43
+#define	GET_EVENT_STATUS_NOTIFICATION 0x4A
+#define	MODE_SENSE_10		0x5A
+#define	READ_12			0xA8
+#define	READ_CD			0xBE
+
+/*
+ * SCSI mode page codes
+ */
+#define	MODEPAGE_RW_ERROR_RECOVERY	0x01
+#define	MODEPAGE_CD_CAPABILITIES	0x2A
+
+/*
+ * Debug printf
+ */
+#ifdef AHCI_DEBUG
+static FILE *dbg;
+#define DPRINTF(format, arg...)	do{fprintf(dbg, format, ##arg);fflush(dbg);}while(0)
+#else
+#define DPRINTF(format, arg...)
+#endif
+#define WPRINTF(format, arg...) printf(format, ##arg)
+
+struct ahci_ioreq {
+	struct blockif_req io_req;
+	struct ahci_port *io_pr;
+	STAILQ_ENTRY(ahci_ioreq) io_list;
+	uint8_t *cfis;
+	uint32_t len;
+	uint32_t done;
+	int slot;
+	int prdtl;
+};
+
+struct ahci_port {
+	struct blockif_ctxt *bctx;
+	struct pci_ahci_softc *pr_sc;
+	uint64_t cmd_lst;
+	uint64_t rfis;
+	int atapi;
+	int reset;
+	int mult_sectors;
+	uint8_t xfermode;
+	uint8_t sense_key;
+	uint8_t asc;
+
+	uint32_t clb;
+	uint32_t clbu;
+	uint32_t fb;
+	uint32_t fbu;
+	uint32_t is;
+	uint32_t ie;
+	uint32_t cmd;
+	uint32_t unused0;
+	uint32_t tfd;
+	uint32_t sig;
+	uint32_t ssts;
+	uint32_t sctl;
+	uint32_t serr;
+	uint32_t sact;
+	uint32_t ci;
+	uint32_t sntf;
+	uint32_t fbs;
+
+	/*
+	 * i/o request info
+	 */
+	struct ahci_ioreq *ioreq;
+	int ioqsz;
+	STAILQ_HEAD(ahci_fhead, ahci_ioreq) iofhd;
+};
+
+struct ahci_cmd_hdr {
+	uint16_t flags;
+	uint16_t prdtl;
+	uint32_t prdbc;
+	uint64_t ctba;
+	uint32_t reserved[4];
+};
+
+struct ahci_prdt_entry {
+	uint64_t dba;
+	uint32_t reserved;
+	uint32_t dbc;
+};
+
+struct pci_ahci_softc {
+	struct pci_devinst *asc_pi;
+	pthread_mutex_t	mtx;
+	int ports;
+	uint32_t cap;
+	uint32_t ghc;
+	uint32_t is;
+	uint32_t pi;
+	uint32_t vs;
+	uint32_t ccc_ctl;
+	uint32_t ccc_pts;
+	uint32_t em_loc;
+	uint32_t em_ctl;
+	uint32_t cap2;
+	uint32_t bohc;
+	struct ahci_port port[MAX_PORTS];
+};
+#define	ahci_ctx(sc)	((sc)->asc_pi->pi_vmctx)
+
+static inline void lba_to_msf(uint8_t *buf, int lba)
+{
+	lba += 150;
+	buf[0] = (lba / 75) / 60;
+	buf[1] = (lba / 75) % 60;
+	buf[2] = lba % 75;
+}
+
+/*
+ * generate HBA intr depending on whether or not ports within
+ * the controller have an interrupt pending.
+ */
+static void
+ahci_generate_intr(struct pci_ahci_softc *sc)
+{
+	int i;
+
+	for (i = 0; i < sc->ports; i++) {
+		struct ahci_port *pr;
+		pr = &sc->port[i];
+		if (pr->is & pr->ie)
+			sc->is |= (1 << i);
+	}
+
+	DPRINTF("%s %x\n", __func__, sc->is);
+
+	if (sc->is && (sc->ghc & AHCI_GHC_IE))
+		pci_generate_msi(sc->asc_pi, 0);
+}
+
+static void
+ahci_write_fis(struct ahci_port *p, enum sata_fis_type ft, uint8_t *fis)
+{
+	int offset, len, irq;
+
+	if (p->rfis == 0 || !(p->cmd & AHCI_P_CMD_FRE))
+		return;
+
+	switch (ft) {
+	case FIS_TYPE_REGD2H:
+		offset = 0x40;
+		len = 20;
+		irq = AHCI_P_IX_DHR;
+		break;
+	case FIS_TYPE_SETDEVBITS:
+		offset = 0x58;
+		len = 8;
+		irq = AHCI_P_IX_SDB;
+		break;
+	case FIS_TYPE_PIOSETUP:
+		offset = 0x20;
+		len = 20;
+		irq = 0;
+		break;
+	default:
+		WPRINTF("unsupported fis type %d\n", ft);
+		return;
+	}
+	memcpy(p->rfis + offset, fis, len);
+	if (irq) {
+		p->is |= irq;
+		ahci_generate_intr(p->pr_sc);
+	}
+}
+
+static void
+ahci_write_fis_sdb(struct ahci_port *p, int slot, uint32_t tfd)
+{
+	uint8_t fis[8];
+	uint8_t error;
+
+	error = (tfd >> 8) & 0xff;
+	memset(fis, 0, sizeof(fis));
+	fis[0] = error;
+	fis[2] = tfd & 0x77;
+	*(uint32_t *)(fis + 4) = (1 << slot);
+	if (fis[2] & ATA_S_ERROR)
+		p->is |= AHCI_P_IX_TFE;
+	p->tfd = tfd;
+	ahci_write_fis(p, FIS_TYPE_SETDEVBITS, fis);
+}
+
+static void
+ahci_write_fis_d2h(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t tfd)
+{
+	uint8_t fis[20];
+	uint8_t error;
+
+	error = (tfd >> 8) & 0xff;
+	memset(fis, 0, sizeof(fis));
+	fis[0] = FIS_TYPE_REGD2H;
+	fis[1] = (1 << 6);
+	fis[2] = tfd & 0xff;
+	fis[3] = error;
+	fis[4] = cfis[4];
+	fis[5] = cfis[5];
+	fis[6] = cfis[6];
+	fis[7] = cfis[7];
+	fis[8] = cfis[8];
+	fis[9] = cfis[9];
+	fis[10] = cfis[10];
+	fis[11] = cfis[11];
+	fis[12] = cfis[12];
+	fis[13] = cfis[13];
+	if (fis[2] & ATA_S_ERROR)
+		p->is |= AHCI_P_IX_TFE;
+	p->tfd = tfd;
+	p->ci &= ~(1 << slot);
+	ahci_write_fis(p, FIS_TYPE_REGD2H, fis);
+}
+
+static void
+ahci_write_reset_fis_d2h(struct ahci_port *p)
+{
+	uint8_t fis[20];
+
+	memset(fis, 0, sizeof(fis));
+	fis[0] = FIS_TYPE_REGD2H;
+	fis[3] = 1;
+	fis[4] = 1;
+	if (p->atapi) {
+		fis[5] = 0x14;
+		fis[6] = 0xeb;
+	}
+	fis[12] = 1;
+	ahci_write_fis(p, FIS_TYPE_REGD2H, fis);
+}
+
+static void
+ahci_port_reset(struct ahci_port *pr)
+{
+	pr->sctl = 0;
+	pr->serr = 0;
+	pr->sact = 0;
+	pr->xfermode = ATA_UDMA6;
+	pr->mult_sectors = 128;
+
+	if (!pr->bctx) {
+		pr->ssts = ATA_SS_DET_NO_DEVICE;
+		pr->sig = 0xFFFFFFFF;
+		pr->tfd = 0x7F;
+		return;
+	}
+	pr->ssts = ATA_SS_DET_PHY_ONLINE | ATA_SS_SPD_GEN2 |
+		ATA_SS_IPM_ACTIVE;
+	pr->tfd = (1 << 8) | ATA_S_DSC | ATA_S_DMA;
+	if (!pr->atapi) {
+		pr->sig = PxSIG_ATA;
+		pr->tfd |= ATA_S_READY;
+	} else
+		pr->sig = PxSIG_ATAPI;
+	ahci_write_reset_fis_d2h(pr);
+}
+
+static void
+ahci_reset(struct pci_ahci_softc *sc)
+{
+	int i;
+
+	sc->ghc = AHCI_GHC_AE;
+	sc->is = 0;
+	for (i = 0; i < sc->ports; i++) {
+		sc->port[i].ie = 0;
+		sc->port[i].is = 0;
+		ahci_port_reset(&sc->port[i]);
+	}
+}
+
+static void
+ata_string(uint8_t *dest, const char *src, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		if (*src)
+			dest[i ^ 1] = *src++;
+		else
+			dest[i ^ 1] = ' ';
+	}
+}
+
+static void
+atapi_string(uint8_t *dest, const char *src, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		if (*src)
+			dest[i] = *src++;
+		else
+			dest[i] = ' ';
+	}
+}
+
+static void
+ahci_handle_dma(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done,
+    int seek)
+{
+	struct ahci_ioreq *aior;
+	struct blockif_req *breq;
+	struct pci_ahci_softc *sc;
+	struct ahci_prdt_entry *prdt;
+	struct ahci_cmd_hdr *hdr;
+	uint64_t lba;
+	uint32_t len;
+	int i, err, iovcnt, ncq, readop;
+
+	sc = p->pr_sc;
+	prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
+	hdr = p->cmd_lst + slot * AHCI_CL_SIZE;
+	ncq = 0;
+	readop = 1;
+
+	prdt += seek;
+	if (cfis[2] == ATA_WRITE_DMA || cfis[2] == ATA_WRITE_DMA48 ||
+			cfis[2] == ATA_WRITE_FPDMA_QUEUED)
+		readop = 0;
+
+	if (cfis[2] == ATA_WRITE_FPDMA_QUEUED ||
+			cfis[2] == ATA_READ_FPDMA_QUEUED) {
+		lba = ((uint64_t)cfis[10] << 40) |
+			((uint64_t)cfis[9] << 32) |
+			((uint64_t)cfis[8] << 24) |
+			((uint64_t)cfis[6] << 16) |
+			((uint64_t)cfis[5] << 8) |
+			cfis[4];
+		len = cfis[11] << 8 | cfis[3];
+		if (!len)
+			len = 65536;
+		ncq = 1;
+	} else if (cfis[2] == ATA_READ_DMA48 || cfis[2] == ATA_WRITE_DMA48) {
+		lba = ((uint64_t)cfis[10] << 40) |
+			((uint64_t)cfis[9] << 32) |
+			((uint64_t)cfis[8] << 24) |
+			((uint64_t)cfis[6] << 16) |
+			((uint64_t)cfis[5] << 8) |
+			cfis[4];
+		len = cfis[13] << 8 | cfis[12];
+		if (!len)
+			len = 65536;
+	} else {
+		lba = ((cfis[7] & 0xf) << 24) | (cfis[6] << 16) |
+			(cfis[5] << 8) | cfis[4];
+		len = cfis[12];
+		if (!len)
+			len = 256;
+	}
+	lba *= blockif_sectsz(p->bctx);
+	len *= blockif_sectsz(p->bctx);
+
+	/*
+	 * Pull request off free list
+	 */
+	aior = STAILQ_FIRST(&p->iofhd);
+	assert(aior != NULL);
+	STAILQ_REMOVE_HEAD(&p->iofhd, io_list);
+	aior->cfis = cfis;
+	aior->slot = slot;
+	aior->len = len;
+	aior->done = done;
+	breq = &aior->io_req;
+	breq->br_offset = lba + done;
+	iovcnt = hdr->prdtl - seek;
+	if (iovcnt > BLOCKIF_IOV_MAX) {
+		aior->prdtl = iovcnt - BLOCKIF_IOV_MAX;
+		iovcnt = BLOCKIF_IOV_MAX;
+	} else
+		aior->prdtl = 0;
+	breq->br_iovcnt = iovcnt;
+
+	/*
+	 * Build up the iovec based on the prdt
+	 */
+	for (i = 0; i < iovcnt; i++) {
+		breq->br_iov[i].iov_base = paddr_guest2host(ahci_ctx(sc),
+				prdt->dba, prdt->dbc + 1);
+		breq->br_iov[i].iov_len = prdt->dbc + 1;
+		aior->done += (prdt->dbc + 1);
+		prdt++;
+	}
+	if (readop)
+		err = blockif_read(p->bctx, breq);
+	else
+		err = blockif_write(p->bctx, breq);
+	assert(err == 0);
+
+	if (!aior->prdtl && ncq)
+		p->ci &= ~(1 << slot);
+}
+
+static void
+ahci_handle_flush(struct ahci_port *p, int slot, uint8_t *cfis)
+{
+	struct ahci_ioreq *aior;
+	struct blockif_req *breq;
+	int err;
+
+	/*
+	 * Pull request off free list
+	 */
+	aior = STAILQ_FIRST(&p->iofhd);
+	assert(aior != NULL);
+	STAILQ_REMOVE_HEAD(&p->iofhd, io_list);
+	aior->cfis = cfis;
+	aior->slot = slot;
+	aior->len = 0;
+	breq = &aior->io_req;
+
+	err = blockif_flush(p->bctx, breq);
+	assert(err == 0);
+}
+
+static inline void
+write_prdt(struct ahci_port *p, int slot, uint8_t *cfis,
+		void *buf, int size)
+{
+	struct ahci_cmd_hdr *hdr;
+	struct ahci_prdt_entry *prdt;
+	void *from;
+	int i, len;
+
+	hdr = p->cmd_lst + slot * AHCI_CL_SIZE;
+	len = size;
+	from = buf;
+	prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
+	for (i = 0; i < hdr->prdtl && len; i++) {
+		uint8_t *ptr = paddr_guest2host(ahci_ctx(p->pr_sc),
+				prdt->dba, prdt->dbc + 1);
+		memcpy(ptr, from, prdt->dbc + 1);
+		len -= (prdt->dbc + 1);
+		from += (prdt->dbc + 1);
+		prdt++;
+	}
+	hdr->prdbc = size - len;
+}
+
+static void
+handle_identify(struct ahci_port *p, int slot, uint8_t *cfis)
+{
+	struct ahci_cmd_hdr *hdr;
+
+	hdr = p->cmd_lst + slot * AHCI_CL_SIZE;
+	if (p->atapi || hdr->prdtl == 0) {
+		p->tfd = (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR;
+		p->is |= AHCI_P_IX_TFE;
+	} else {
+		uint16_t buf[256];
+		uint64_t sectors;
+
+		sectors = blockif_size(p->bctx) / blockif_sectsz(p->bctx);
+		memset(buf, 0, sizeof(buf));
+		buf[0] = 0x0040;
+		/* TODO emulate different serial? */
+		ata_string((uint8_t *)(buf+10), "123456", 20);
+		ata_string((uint8_t *)(buf+23), "001", 8);
+		ata_string((uint8_t *)(buf+27), "BHYVE SATA DISK", 40);
+		buf[47] = (0x8000 | 128);
+		buf[48] = 0x1;
+		buf[49] = (1 << 8 | 1 << 9 | 1 << 11);
+		buf[50] = (1 << 14);
+		buf[53] = (1 << 1 | 1 << 2);
+		if (p->mult_sectors)
+			buf[59] = (0x100 | p->mult_sectors);
+		buf[60] = sectors;
+		buf[61] = (sectors >> 16);
+		buf[63] = 0x7;
+		if (p->xfermode & ATA_WDMA0)
+			buf[63] |= (1 << ((p->xfermode & 7) + 8));
+		buf[64] = 0x3;
+		buf[65] = 100;
+		buf[66] = 100;
+		buf[67] = 100;
+		buf[68] = 100;
+		buf[75] = 31;
+		buf[76] = (1 << 8 | 1 << 2);
+		buf[80] = 0x1f0;
+		buf[81] = 0x28;
+		buf[82] = (1 << 5 | 1 << 14);
+		buf[83] = (1 << 10 | 1 << 12 | 1 << 13 | 1 << 14);
+		buf[84] = (1 << 14);
+		buf[85] = (1 << 5 | 1 << 14);
+		buf[86] = (1 << 10 | 1 << 12 | 1 << 13);
+		buf[87] = (1 << 14);
+		buf[88] = 0x7f;
+		if (p->xfermode & ATA_UDMA0)
+			buf[88] |= (1 << ((p->xfermode & 7) + 8));
+		buf[93] = (1 | 1 <<14);
+		buf[100] = sectors;
+		buf[101] = (sectors >> 16);
+		buf[102] = (sectors >> 32);
+		buf[103] = (sectors >> 48);
+		write_prdt(p, slot, cfis, (void *)buf, sizeof(buf));
+		p->tfd = ATA_S_DSC | ATA_S_READY;
+		p->is |= AHCI_P_IX_DP;
+	}
+	p->ci &= ~(1 << slot);
+	ahci_generate_intr(p->pr_sc);
+}
+
+static void
+handle_atapi_identify(struct ahci_port *p, int slot, uint8_t *cfis)
+{
+	if (!p->atapi) {
+		p->tfd = (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR;
+		p->is |= AHCI_P_IX_TFE;
+	} else {
+		uint16_t buf[256];
+
+		memset(buf, 0, sizeof(buf));
+		buf[0] = (2 << 14 | 5 << 8 | 1 << 7 | 2 << 5);
+		/* TODO emulate different serial? */
+		ata_string((uint8_t *)(buf+10), "123456", 20);
+		ata_string((uint8_t *)(buf+23), "001", 8);
+		ata_string((uint8_t *)(buf+27), "BHYVE SATA DVD ROM", 40);
+		buf[49] = (1 << 9 | 1 << 8);
+		buf[50] = (1 << 14 | 1);
+		buf[53] = (1 << 2 | 1 << 1);
+		buf[62] = 0x3f;
+		buf[63] = 7;
+		buf[64] = 3;
+		buf[65] = 100;
+		buf[66] = 100;
+		buf[67] = 100;
+		buf[68] = 100;
+		buf[76] = (1 << 2 | 1 << 1);
+		buf[78] = (1 << 5);
+		buf[80] = (0x1f << 4);
+		buf[82] = (1 << 4);
+		buf[83] = (1 << 14);
+		buf[84] = (1 << 14);
+		buf[85] = (1 << 4);
+		buf[87] = (1 << 14);
+		buf[88] = (1 << 14 | 0x7f);
+		write_prdt(p, slot, cfis, (void *)buf, sizeof(buf));
+		p->tfd = ATA_S_DSC | ATA_S_READY;
+		p->is |= AHCI_P_IX_DHR;
+	}
+	p->ci &= ~(1 << slot);
+	ahci_generate_intr(p->pr_sc);
+}
+
+static void
+atapi_inquiry(struct ahci_port *p, int slot, uint8_t *cfis)
+{
+	uint8_t buf[36];
+	uint8_t *acmd;
+	int len;
+
+	acmd = cfis + 0x40;
+
+	buf[0] = 0x05;
+	buf[1] = 0x80;
+	buf[2] = 0x00;
+	buf[3] = 0x21;
+	buf[4] = 31;
+	buf[5] = 0;
+	buf[6] = 0;
+	buf[7] = 0;
+	atapi_string(buf + 8, "BHYVE", 8);
+	atapi_string(buf + 16, "BHYVE DVD-ROM", 16);
+	atapi_string(buf + 32, "001", 4);
+
+	len = sizeof(buf);
+	if (len > acmd[4])
+		len = acmd[4];
+	cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
+	write_prdt(p, slot, cfis, buf, len);
+	ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
+}
+
+static void
+atapi_read_capacity(struct ahci_port *p, int slot, uint8_t *cfis)
+{
+	uint8_t buf[8];
+	uint64_t sectors;
+
+	sectors = blockif_size(p->bctx) / blockif_sectsz(p->bctx);
+	sectors >>= 2;
+	be32enc(buf, sectors - 1);
+	be32enc(buf + 4, 2048);
+	cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
+	write_prdt(p, slot, cfis, buf, sizeof(buf));
+	ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
+}
+
+static void
+atapi_read_toc(struct ahci_port *p, int slot, uint8_t *cfis)
+{
+	uint8_t *acmd;
+	uint8_t format;
+	int len;
+
+	acmd = cfis + 0x40;
+
+	len = be16dec(acmd + 7);
+	format = acmd[9] >> 6;
+	switch (format) {
+	case 0:
+	{
+		int msf, size;
+		uint64_t sectors;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-all mailing list