xref: /netbsd-src/sys/dev/hyperv/hvs.c (revision c7fb772b85b2b5d4cfb282f868f454b4701534fd)
1*c7fb772bSthorpej /*	$NetBSD: hvs.c,v 1.7 2021/08/07 16:19:11 thorpej Exp $	*/
250517e57Snonaka /*	$OpenBSD: hvs.c,v 1.17 2017/08/10 17:22:48 mikeb Exp $	*/
350517e57Snonaka 
450517e57Snonaka /*-
550517e57Snonaka  * Copyright (c) 2009-2012,2016 Microsoft Corp.
650517e57Snonaka  * Copyright (c) 2012 NetApp Inc.
750517e57Snonaka  * Copyright (c) 2012 Citrix Inc.
850517e57Snonaka  * Copyright (c) 2017 Mike Belopuhov <mike@esdenera.com>
950517e57Snonaka  * All rights reserved.
1050517e57Snonaka  *
1150517e57Snonaka  * Redistribution and use in source and binary forms, with or without
1250517e57Snonaka  * modification, are permitted provided that the following conditions
1350517e57Snonaka  * are met:
1450517e57Snonaka  * 1. Redistributions of source code must retain the above copyright
1550517e57Snonaka  *    notice unmodified, this list of conditions, and the following
1650517e57Snonaka  *    disclaimer.
1750517e57Snonaka  * 2. Redistributions in binary form must reproduce the above copyright
1850517e57Snonaka  *    notice, this list of conditions and the following disclaimer in the
1950517e57Snonaka  *    documentation and/or other materials provided with the distribution.
2050517e57Snonaka  *
2150517e57Snonaka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2250517e57Snonaka  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2350517e57Snonaka  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2450517e57Snonaka  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2550517e57Snonaka  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2650517e57Snonaka  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2750517e57Snonaka  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2850517e57Snonaka  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2950517e57Snonaka  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3050517e57Snonaka  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3150517e57Snonaka  */
3250517e57Snonaka 
3350517e57Snonaka /*
3450517e57Snonaka  * The OpenBSD port was done under funding by Esdenera Networks GmbH.
3550517e57Snonaka  */
3650517e57Snonaka 
3750517e57Snonaka /* #define HVS_DEBUG_IO */
3850517e57Snonaka 
3950517e57Snonaka #include <sys/cdefs.h>
40*c7fb772bSthorpej __KERNEL_RCSID(0, "$NetBSD: hvs.c,v 1.7 2021/08/07 16:19:11 thorpej Exp $");
4150517e57Snonaka 
4250517e57Snonaka #include <sys/param.h>
4350517e57Snonaka #include <sys/systm.h>
4450517e57Snonaka #include <sys/kernel.h>
4550517e57Snonaka #include <sys/device.h>
4650517e57Snonaka #include <sys/buf.h>
4750517e57Snonaka #include <sys/bus.h>
4850517e57Snonaka #include <sys/kmem.h>
4950517e57Snonaka #include <sys/mutex.h>
5050517e57Snonaka 
5150517e57Snonaka #include <uvm/uvm_extern.h>
5250517e57Snonaka 
5350517e57Snonaka #include <dev/hyperv/vmbusvar.h>
5450517e57Snonaka 
5550517e57Snonaka #include <dev/scsipi/scsi_all.h>
5650517e57Snonaka #include <dev/scsipi/scsiconf.h>
5750517e57Snonaka #include <dev/scsipi/scsipi_all.h>
5850517e57Snonaka 
5950517e57Snonaka #define HVS_PROTO_VERSION_WIN6		0x200
6050517e57Snonaka #define HVS_PROTO_VERSION_WIN7		0x402
6150517e57Snonaka #define HVS_PROTO_VERSION_WIN8		0x501
6250517e57Snonaka #define HVS_PROTO_VERSION_WIN8_1	0x600
6350517e57Snonaka #define HVS_PROTO_VERSION_WIN10		0x602
6450517e57Snonaka 
6550517e57Snonaka #define HVS_MSG_IODONE			0x01
6650517e57Snonaka #define HVS_MSG_DEVGONE			0x02
6750517e57Snonaka #define HVS_MSG_ENUMERATE		0x0b
6850517e57Snonaka 
6950517e57Snonaka #define HVS_REQ_SCSIIO			0x03
7050517e57Snonaka #define HVS_REQ_STARTINIT		0x07
7150517e57Snonaka #define HVS_REQ_FINISHINIT		0x08
7250517e57Snonaka #define HVS_REQ_QUERYPROTO		0x09
7350517e57Snonaka #define HVS_REQ_QUERYPROPS		0x0a
7450517e57Snonaka #define HVS_REQ_CREATEMULTICHANNELS	0x0d
7550517e57Snonaka 
7650517e57Snonaka struct hvs_cmd_hdr {
7750517e57Snonaka 	uint32_t		hdr_op;
7850517e57Snonaka 	uint32_t		hdr_flags;
7950517e57Snonaka 	uint32_t		hdr_status;
8050517e57Snonaka #define cmd_op			cmd_hdr.hdr_op
8150517e57Snonaka #define cmd_flags		cmd_hdr.hdr_flags
8250517e57Snonaka #define cmd_status		cmd_hdr.hdr_status
8350517e57Snonaka } __packed;
8450517e57Snonaka 
8550517e57Snonaka /* Negotiate version */
8650517e57Snonaka struct hvs_cmd_ver {
8750517e57Snonaka 	struct hvs_cmd_hdr	cmd_hdr;
8850517e57Snonaka 	uint16_t		cmd_ver;
8950517e57Snonaka 	uint16_t		cmd_rev;
9050517e57Snonaka } __packed;
9150517e57Snonaka 
9250517e57Snonaka /* Query channel properties */
9350517e57Snonaka struct hvs_chp {
9450517e57Snonaka 	uint16_t		chp_proto;
9550517e57Snonaka 	uint8_t			chp_path;
9650517e57Snonaka 	uint8_t			chp_target;
9750517e57Snonaka 	uint16_t		chp_maxchan;
9850517e57Snonaka 	uint16_t		chp_port;
9950517e57Snonaka 	uint32_t		chp_chflags;
10050517e57Snonaka #define CHP_CHFLAGS_MULTI_CHANNEL	0x1
10150517e57Snonaka 	uint32_t		chp_maxfer;
10250517e57Snonaka 	uint64_t		chp_chanid;
10350517e57Snonaka } __packed;
10450517e57Snonaka 
10550517e57Snonaka struct hvs_cmd_chp {
10650517e57Snonaka 	struct hvs_cmd_hdr	cmd_hdr;
10750517e57Snonaka 	struct hvs_chp		cmd_chp;
10850517e57Snonaka } __packed;
10950517e57Snonaka 
11050517e57Snonaka #define SENSE_DATA_LEN_WIN7		18
11150517e57Snonaka #define SENSE_DATA_LEN			20
11250517e57Snonaka #define MAX_SRB_DATA			20
11350517e57Snonaka 
11450517e57Snonaka /* SCSI Request Block */
11550517e57Snonaka struct hvs_srb {
11650517e57Snonaka 	uint16_t		srb_reqlen;
11750517e57Snonaka 	uint8_t			srb_iostatus;
11850517e57Snonaka 	uint8_t			srb_scsistatus;
11950517e57Snonaka 
12050517e57Snonaka 	uint8_t			srb_initiator;
12150517e57Snonaka 	uint8_t			srb_bus;
12250517e57Snonaka 	uint8_t			srb_target;
12350517e57Snonaka 	uint8_t			srb_lun;
12450517e57Snonaka 
12550517e57Snonaka 	uint8_t			srb_cdblen;
12650517e57Snonaka 	uint8_t			srb_senselen;
12750517e57Snonaka 	uint8_t			srb_direction;
12850517e57Snonaka 	uint8_t			_reserved;
12950517e57Snonaka 
13050517e57Snonaka 	uint32_t		srb_datalen;
13150517e57Snonaka 	uint8_t			srb_data[MAX_SRB_DATA];
13250517e57Snonaka } __packed;
13350517e57Snonaka 
13450517e57Snonaka #define SRB_DATA_WRITE			0
13550517e57Snonaka #define SRB_DATA_READ			1
13650517e57Snonaka #define SRB_DATA_NONE			2
13750517e57Snonaka 
13850517e57Snonaka #define SRB_STATUS_PENDING		0x00
13950517e57Snonaka #define SRB_STATUS_SUCCESS		0x01
14050517e57Snonaka #define SRB_STATUS_ABORTED		0x02
14150517e57Snonaka #define SRB_STATUS_ERROR		0x04
14250517e57Snonaka #define SRB_STATUS_INVALID_LUN		0x20
14350517e57Snonaka #define SRB_STATUS_QUEUE_FROZEN		0x40
14450517e57Snonaka #define SRB_STATUS_AUTOSENSE_VALID	0x80
14550517e57Snonaka 
14650517e57Snonaka #define SRB_FLAGS_QUEUE_ACTION_ENABLE		0x00000002
14750517e57Snonaka #define SRB_FLAGS_DISABLE_DISCONNECT		0x00000004
14850517e57Snonaka #define SRB_FLAGS_DISABLE_SYNCH_TRANSFER	0x00000008
14950517e57Snonaka #define SRB_FLAGS_BYPASS_FROZEN_QUEUE		0x00000010
15050517e57Snonaka #define SRB_FLAGS_DISABLE_AUTOSENSE		0x00000020
15150517e57Snonaka #define SRB_FLAGS_DATA_IN			0x00000040
15250517e57Snonaka #define SRB_FLAGS_DATA_OUT			0x00000080
15350517e57Snonaka #define SRB_FLAGS_NO_DATA_TRANSFER		0x00000000
15450517e57Snonaka #define SRB_FLAGS_NO_QUEUE_FREEZE		0x00000100
15550517e57Snonaka #define SRB_FLAGS_ADAPTER_CACHE_ENABLE		0x00000200
15650517e57Snonaka #define SRB_FLAGS_FREE_SENSE_BUFFER		0x00000400
15750517e57Snonaka 
15850517e57Snonaka struct hvs_cmd_io {
15950517e57Snonaka 	struct hvs_cmd_hdr	cmd_hdr;
16050517e57Snonaka 	struct hvs_srb		cmd_srb;
16150517e57Snonaka 	/* Win8 extensions */
16250517e57Snonaka 	uint16_t		_reserved;
16350517e57Snonaka 	uint8_t			cmd_qtag;
16450517e57Snonaka 	uint8_t			cmd_qaction;
16550517e57Snonaka 	uint32_t		cmd_srbflags;
16650517e57Snonaka 	uint32_t		cmd_timeout;
16750517e57Snonaka 	uint32_t		cmd_qsortkey;
16850517e57Snonaka } __packed;
16950517e57Snonaka 
17050517e57Snonaka #define HVS_CMD_SIZE			64
17150517e57Snonaka 
17250517e57Snonaka union hvs_cmd {
17350517e57Snonaka 	struct hvs_cmd_hdr	cmd_hdr;
17450517e57Snonaka 	struct hvs_cmd_ver	ver;
17550517e57Snonaka 	struct hvs_cmd_chp	chp;
17650517e57Snonaka 	struct hvs_cmd_io	io;
17750517e57Snonaka 	uint16_t		multi_chans_cnt;
17850517e57Snonaka 	uint8_t			pad[HVS_CMD_SIZE];
17950517e57Snonaka } __packed;
18050517e57Snonaka 
18150517e57Snonaka #define HVS_RING_SIZE			(20 * PAGE_SIZE)
18250517e57Snonaka #define HVS_MAX_CCB			128
183a08e08e6Snonaka #define HVS_MAX_SGE			(howmany(MAXPHYS, PAGE_SIZE) + 1)
18450517e57Snonaka 
18550517e57Snonaka struct hvs_softc;
18650517e57Snonaka 
18750517e57Snonaka struct hvs_ccb {
18850517e57Snonaka 	struct scsipi_xfer	*ccb_xfer;  /* associated transfer */
18950517e57Snonaka 	union hvs_cmd		*ccb_cmd;   /* associated command */
19050517e57Snonaka 	union hvs_cmd		ccb_rsp;    /* response */
19150517e57Snonaka 	bus_dmamap_t		ccb_dmap;   /* transfer map */
19250517e57Snonaka 	uint64_t		ccb_rid;    /* request id */
19350517e57Snonaka 	struct vmbus_gpa_range	*ccb_sgl;
19450517e57Snonaka 	int			ccb_nsge;
19550517e57Snonaka 	void			(*ccb_done)(struct hvs_ccb *);
19650517e57Snonaka 	void			*ccb_cookie;
19750517e57Snonaka 	SIMPLEQ_ENTRY(hvs_ccb)	ccb_link;
19850517e57Snonaka };
19950517e57Snonaka SIMPLEQ_HEAD(hvs_ccb_queue, hvs_ccb);
20050517e57Snonaka 
20150517e57Snonaka struct hvs_config;
20250517e57Snonaka 
20350517e57Snonaka struct hvs_softc {
20450517e57Snonaka 	device_t		sc_dev;
20550517e57Snonaka 	bus_dma_tag_t		sc_dmat;
20650517e57Snonaka 
20750517e57Snonaka 	struct vmbus_channel	*sc_chan;
20850517e57Snonaka 
20950517e57Snonaka 	const struct hvs_config	*sc_config;
21050517e57Snonaka 
21150517e57Snonaka 	struct hvs_chp		sc_props;
21250517e57Snonaka 
21350517e57Snonaka 	/* CCBs */
21450517e57Snonaka 	int			sc_nccb;
21550517e57Snonaka 	struct hvs_ccb		*sc_ccbs;
21650517e57Snonaka 	struct hvs_ccb_queue	sc_ccb_fq;  /* free queue */
21750517e57Snonaka 	kmutex_t		sc_ccb_fqlck;
21850517e57Snonaka 
21950517e57Snonaka 	int			sc_bus;
22050517e57Snonaka 
22150517e57Snonaka 	struct scsipi_adapter	sc_adapter;
22250517e57Snonaka 	struct scsipi_channel	sc_channel;
22350517e57Snonaka 	device_t		sc_scsibus;
22450517e57Snonaka #if notyet /* XXX subchannel */
22550517e57Snonaka 	u_int			sc_nchan;
22650517e57Snonaka 	struct vmbus_channel	*sc_sel_chan[MAXCPUS];
22750517e57Snonaka #endif
22850517e57Snonaka };
22950517e57Snonaka 
23050517e57Snonaka static int	hvs_match(device_t, cfdata_t, void *);
23150517e57Snonaka static void	hvs_attach(device_t, device_t, void *);
23250517e57Snonaka static int	hvs_detach(device_t, int);
23350517e57Snonaka 
23450517e57Snonaka CFATTACH_DECL_NEW(hvs, sizeof(struct hvs_softc),
23550517e57Snonaka     hvs_match, hvs_attach, hvs_detach, NULL);
23650517e57Snonaka 
23750517e57Snonaka static void	hvs_scsipi_request(struct scsipi_channel *,
23850517e57Snonaka 		    scsipi_adapter_req_t, void *);
23950517e57Snonaka static void	hvs_scsi_cmd_done(struct hvs_ccb *);
24050517e57Snonaka static int	hvs_start(struct hvs_softc *, struct vmbus_channel *,
24150517e57Snonaka 		    struct hvs_ccb *);
24250517e57Snonaka static int	hvs_poll(struct hvs_softc *, struct vmbus_channel *,
24350517e57Snonaka 		    struct hvs_ccb *);
24450517e57Snonaka static void	hvs_poll_done(struct hvs_ccb *);
24550517e57Snonaka static void	hvs_intr(void *);
24650517e57Snonaka static void	hvs_scsi_probe(void *arg);
24750517e57Snonaka static void	hvs_scsi_done(struct scsipi_xfer *, int);
24850517e57Snonaka 
24950517e57Snonaka static int	hvs_connect(struct hvs_softc *);
25050517e57Snonaka static void	hvs_empty_done(struct hvs_ccb *);
25150517e57Snonaka 
25250517e57Snonaka static int	hvs_alloc_ccbs(struct hvs_softc *);
25350517e57Snonaka static void	hvs_free_ccbs(struct hvs_softc *);
25450517e57Snonaka static struct hvs_ccb *
25550517e57Snonaka 		hvs_get_ccb(struct hvs_softc *);
25650517e57Snonaka static void	hvs_put_ccb(struct hvs_softc *, struct hvs_ccb *);
25750517e57Snonaka 
25850517e57Snonaka static const struct hvs_config {
25950517e57Snonaka 	uint32_t	proto_version;
26050517e57Snonaka 	uint16_t	reqlen;
26150517e57Snonaka 	uint8_t		senselen;
26250517e57Snonaka 	bool		fixup_wrong_response;
26350517e57Snonaka 	bool		upgrade_spc2_to_spc3;
26450517e57Snonaka 	bool		use_win8ext_flags;
26550517e57Snonaka } hvs_config_list[] = {
26650517e57Snonaka 	{
26750517e57Snonaka 		.proto_version = HVS_PROTO_VERSION_WIN10,
26850517e57Snonaka 		.reqlen = sizeof(struct hvs_cmd_io),
26950517e57Snonaka 		.senselen = SENSE_DATA_LEN,
27050517e57Snonaka 		.fixup_wrong_response = false,
27150517e57Snonaka 		.upgrade_spc2_to_spc3 = false,
27250517e57Snonaka 		.use_win8ext_flags = true,
27350517e57Snonaka 	},
27450517e57Snonaka 	{
27550517e57Snonaka 		.proto_version = HVS_PROTO_VERSION_WIN8_1,
27650517e57Snonaka 		.reqlen = sizeof(struct hvs_cmd_io),
27750517e57Snonaka 		.senselen = SENSE_DATA_LEN,
27850517e57Snonaka 		.fixup_wrong_response = true,
27950517e57Snonaka 		.upgrade_spc2_to_spc3 = true,
28050517e57Snonaka 		.use_win8ext_flags = true,
28150517e57Snonaka 	},
28250517e57Snonaka 	{
28350517e57Snonaka 		.proto_version = HVS_PROTO_VERSION_WIN8,
28450517e57Snonaka 		.reqlen = sizeof(struct hvs_cmd_io),
28550517e57Snonaka 		.senselen = SENSE_DATA_LEN,
28650517e57Snonaka 		.fixup_wrong_response = true,
28750517e57Snonaka 		.upgrade_spc2_to_spc3 = true,
28850517e57Snonaka 		.use_win8ext_flags = true,
28950517e57Snonaka 	},
29050517e57Snonaka 	{
29150517e57Snonaka 		.proto_version = HVS_PROTO_VERSION_WIN7,
29250517e57Snonaka 		.reqlen = offsetof(struct hvs_cmd_io, _reserved),
29350517e57Snonaka 		.senselen = SENSE_DATA_LEN_WIN7,
29450517e57Snonaka 		.fixup_wrong_response = true,
29550517e57Snonaka 		.upgrade_spc2_to_spc3 = false,
29650517e57Snonaka 		.use_win8ext_flags = false,
29750517e57Snonaka 	},
29850517e57Snonaka 	{
29950517e57Snonaka 		.proto_version = HVS_PROTO_VERSION_WIN6,
30050517e57Snonaka 		.reqlen = offsetof(struct hvs_cmd_io, _reserved),
30150517e57Snonaka 		.senselen = SENSE_DATA_LEN_WIN7,
30250517e57Snonaka 		.fixup_wrong_response = false,
30350517e57Snonaka 		.upgrade_spc2_to_spc3 = false,
30450517e57Snonaka 		.use_win8ext_flags = false,
30550517e57Snonaka 	},
30650517e57Snonaka };
30750517e57Snonaka 
30850517e57Snonaka #if notyet /* XXX subchannel */
30950517e57Snonaka static int hvs_chan_cnt;
31050517e57Snonaka #endif
31150517e57Snonaka 
31250517e57Snonaka static int
hvs_match(device_t parent,cfdata_t cf,void * aux)31350517e57Snonaka hvs_match(device_t parent, cfdata_t cf, void *aux)
31450517e57Snonaka {
31550517e57Snonaka 	struct vmbus_attach_args *aa = aux;
31650517e57Snonaka 
31750517e57Snonaka 	if (memcmp(aa->aa_type, &hyperv_guid_ide, sizeof(*aa->aa_type)) != 0 &&
31850517e57Snonaka 	    memcmp(aa->aa_type, &hyperv_guid_scsi, sizeof(*aa->aa_type)) != 0)
31950517e57Snonaka 		return 0;
32050517e57Snonaka 	return 1;
32150517e57Snonaka }
32250517e57Snonaka 
32350517e57Snonaka static void
hvs_attach(device_t parent,device_t self,void * aux)32450517e57Snonaka hvs_attach(device_t parent, device_t self, void *aux)
32550517e57Snonaka {
32650517e57Snonaka 	extern struct cfdata cfdata[];
32750517e57Snonaka 	struct hvs_softc *sc = device_private(self);
32850517e57Snonaka 	struct vmbus_attach_args *aa = aux;
32950517e57Snonaka 	struct scsipi_adapter *adapt = &sc->sc_adapter;
33050517e57Snonaka 	struct scsipi_channel *chan = &sc->sc_channel;
33150517e57Snonaka 	const char *bus;
33250517e57Snonaka 	bool is_scsi;
33350517e57Snonaka 
33450517e57Snonaka 	sc->sc_dev = self;
33550517e57Snonaka 	sc->sc_chan = aa->aa_chan;
33650517e57Snonaka 	sc->sc_dmat = sc->sc_chan->ch_sc->sc_dmat;
33750517e57Snonaka #if notyet /* XXX subchannel */
33850517e57Snonaka 	sc->sc_nchan = 1;
33950517e57Snonaka 	sc->sc_sel_chan[0] = sc->sc_chan;
34050517e57Snonaka #endif
34150517e57Snonaka 
34250517e57Snonaka 	if (memcmp(aa->aa_type, &hyperv_guid_scsi, sizeof(*aa->aa_type)) == 0) {
34350517e57Snonaka 		is_scsi = true;
34450517e57Snonaka 		bus = "SCSI";
34550517e57Snonaka 	} else {
34650517e57Snonaka 		is_scsi = false;
34750517e57Snonaka 		bus = "IDE";
34850517e57Snonaka 	}
34950517e57Snonaka 
35050517e57Snonaka 	aprint_naive("\n");
35150517e57Snonaka 	aprint_normal(": Hyper-V StorVSC %s\n", bus);
35250517e57Snonaka 
35350517e57Snonaka 	if (vmbus_channel_setdeferred(sc->sc_chan, device_xname(self))) {
35450517e57Snonaka 		aprint_error_dev(self,
35550517e57Snonaka 		    "failed to create the interrupt thread\n");
35650517e57Snonaka 		return;
35750517e57Snonaka 	}
35850517e57Snonaka 
35950517e57Snonaka 	if (vmbus_channel_open(sc->sc_chan, HVS_RING_SIZE, &sc->sc_props,
36050517e57Snonaka 	    sizeof(sc->sc_props), hvs_intr, sc)) {
36150517e57Snonaka 		aprint_error_dev(self, "failed to open channel\n");
36250517e57Snonaka 		return;
36350517e57Snonaka 	}
36450517e57Snonaka 
36550517e57Snonaka 	if (hvs_alloc_ccbs(sc))
36650517e57Snonaka 		return;
36750517e57Snonaka 
36850517e57Snonaka 	if (hvs_connect(sc))
36950517e57Snonaka 		return;
37050517e57Snonaka 
37150517e57Snonaka 	aprint_normal_dev(self, "protocol %u.%u\n",
37250517e57Snonaka 	    (sc->sc_config->proto_version >> 8) & 0xff,
37350517e57Snonaka 	    sc->sc_config->proto_version & 0xff);
37450517e57Snonaka 
37550517e57Snonaka 	adapt = &sc->sc_adapter;
37650517e57Snonaka 	adapt->adapt_dev = self;
37750517e57Snonaka 	adapt->adapt_nchannels = 1;
37850517e57Snonaka 	adapt->adapt_openings = sc->sc_nccb;
37950517e57Snonaka 	adapt->adapt_max_periph = adapt->adapt_openings;
38050517e57Snonaka 	adapt->adapt_request = hvs_scsipi_request;
38150517e57Snonaka 	adapt->adapt_minphys = minphys;
38250517e57Snonaka 	adapt->adapt_flags = SCSIPI_ADAPT_MPSAFE;
38350517e57Snonaka 
38450517e57Snonaka 	chan = &sc->sc_channel;
38550517e57Snonaka 	chan->chan_adapter = adapt;
38650517e57Snonaka 	chan->chan_bustype = &scsi_bustype;	/* XXX IDE/ATAPI */
38750517e57Snonaka 	chan->chan_channel = 0;
38850517e57Snonaka 	chan->chan_ntargets = 2;
38950517e57Snonaka 	chan->chan_nluns = is_scsi ? 64 : 1;
39050517e57Snonaka 	chan->chan_id = 0;
39150517e57Snonaka 	chan->chan_flags = SCSIPI_CHAN_NOSETTLE;
39250517e57Snonaka 	chan->chan_defquirks |= PQUIRK_ONLYBIG;
39350517e57Snonaka 
3942685996bSthorpej 	sc->sc_scsibus = config_found(self, &sc->sc_channel, scsiprint,
395*c7fb772bSthorpej 	    CFARGS_NONE);
39650517e57Snonaka 
39750517e57Snonaka 	/*
39850517e57Snonaka 	 * If the driver has successfully attached to an IDE device,
39950517e57Snonaka 	 * we need to make sure that the same disk is not available to
40050517e57Snonaka 	 * the system via pciide(4) or piixide(4) causing DUID conflicts
40150517e57Snonaka 	 * and preventing system from booting.
40250517e57Snonaka 	 */
40350517e57Snonaka 	if (!is_scsi && sc->sc_scsibus != NULL) {
40450517e57Snonaka 		static const char *disable_devices[] = {
40550517e57Snonaka 			"wd",
40650517e57Snonaka 		};
40750517e57Snonaka 		size_t j;
40850517e57Snonaka 
40950517e57Snonaka 		for (j = 0; j < __arraycount(disable_devices); j++) {
41050517e57Snonaka 			const char *dev = disable_devices[j];
41150517e57Snonaka 			size_t len = strlen(dev);
41250517e57Snonaka 			int devno;
41350517e57Snonaka 
41450517e57Snonaka 			for (devno = 0; cfdata[devno].cf_name != NULL; devno++) {
41550517e57Snonaka 				cfdata_t cf = &cfdata[devno];
41650517e57Snonaka 
41750517e57Snonaka 				if (strlen(cf->cf_name) != len ||
41850517e57Snonaka 				    strncasecmp(dev, cf->cf_name, len) != 0 ||
41950517e57Snonaka 				    cf->cf_fstate != FSTATE_STAR)
42050517e57Snonaka 					continue;
42150517e57Snonaka 
42250517e57Snonaka 				cf->cf_fstate = FSTATE_DSTAR;
42350517e57Snonaka 			}
42450517e57Snonaka 		}
42550517e57Snonaka 	}
42650517e57Snonaka }
42750517e57Snonaka 
42850517e57Snonaka static int
hvs_detach(device_t self,int flags)42950517e57Snonaka hvs_detach(device_t self, int flags)
43050517e57Snonaka {
43150517e57Snonaka 
43250517e57Snonaka 	/* XXX detach */
43350517e57Snonaka 
43450517e57Snonaka 	return 0;
43550517e57Snonaka }
43650517e57Snonaka 
43750517e57Snonaka #define XS2DMA(xs) \
43850517e57Snonaka     ((((xs)->xs_control & XS_CTL_DATA_IN) ? BUS_DMA_READ : BUS_DMA_WRITE) | \
43950517e57Snonaka     (((xs)->xs_control & XS_CTL_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK) | \
44050517e57Snonaka     BUS_DMA_STREAMING)
44150517e57Snonaka 
44250517e57Snonaka #define XS2DMAPRE(xs) (((xs)->xs_control & XS_CTL_DATA_IN) ? \
44350517e57Snonaka     BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE)
44450517e57Snonaka 
44550517e57Snonaka #define XS2DMAPOST(xs) (((xs)->xs_control & XS_CTL_DATA_IN) ? \
44650517e57Snonaka     BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE)
44750517e57Snonaka 
44850517e57Snonaka static void
hvs_scsipi_request(struct scsipi_channel * chan,scsipi_adapter_req_t request,void * arg)44950517e57Snonaka hvs_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t request,
45050517e57Snonaka     void *arg)
45150517e57Snonaka {
45250517e57Snonaka 	struct scsipi_adapter *adapt = chan->chan_adapter;
45350517e57Snonaka 	struct hvs_softc *sc = device_private(adapt->adapt_dev);
45450517e57Snonaka 	struct scsipi_xfer *xs;
45550517e57Snonaka 	struct scsipi_xfer_mode *xm;
45650517e57Snonaka 	struct scsipi_periph *periph;
45750517e57Snonaka 	struct hvs_ccb *ccb;
45850517e57Snonaka 	union hvs_cmd cmd;
45950517e57Snonaka 	struct hvs_cmd_io *io = &cmd.io;
46050517e57Snonaka 	struct hvs_srb *srb = &io->cmd_srb;
46150517e57Snonaka 	int i, error;
46250517e57Snonaka 
46350517e57Snonaka 	switch (request) {
46450517e57Snonaka 	default:
46550517e57Snonaka 		device_printf(sc->sc_dev,
46650517e57Snonaka 		    "%s: unhandled request %u\n", __func__, request);
46750517e57Snonaka 		return;
46850517e57Snonaka 
46950517e57Snonaka 	case ADAPTER_REQ_GROW_RESOURCES:
47050517e57Snonaka 		/* Not supported. */
47150517e57Snonaka 		return;
47250517e57Snonaka 
47350517e57Snonaka 	case ADAPTER_REQ_SET_XFER_MODE:
47450517e57Snonaka 		xm = arg;
47550517e57Snonaka 		xm->xm_mode = PERIPH_CAP_TQING;
47650517e57Snonaka 		xm->xm_period = 0;
47750517e57Snonaka 		xm->xm_offset = 0;
47850517e57Snonaka 		scsipi_async_event(chan, ASYNC_EVENT_XFER_MODE, xm);
47950517e57Snonaka 		return;
48050517e57Snonaka 
48150517e57Snonaka 	case ADAPTER_REQ_RUN_XFER:
48250517e57Snonaka 		break;
48350517e57Snonaka 	}
48450517e57Snonaka 
48550517e57Snonaka 	xs = arg;
48650517e57Snonaka 
48750517e57Snonaka 	if (xs->cmdlen > MAX_SRB_DATA) {
48850517e57Snonaka 		device_printf(sc->sc_dev, "CDB is too big: %d\n",
48950517e57Snonaka 		    xs->cmdlen);
49050517e57Snonaka 		memset(&xs->sense, 0, sizeof(xs->sense));
49150517e57Snonaka 		xs->sense.scsi_sense.response_code =
49250517e57Snonaka 		    SSD_RCODE_VALID | SSD_RCODE_CURRENT;
49350517e57Snonaka 		xs->sense.scsi_sense.flags = SSD_ILI;
49450517e57Snonaka 		xs->sense.scsi_sense.asc = 0x20;
49550517e57Snonaka 		hvs_scsi_done(xs, XS_SENSE);
49650517e57Snonaka 		return;
49750517e57Snonaka 	}
49850517e57Snonaka 
49950517e57Snonaka 	ccb = hvs_get_ccb(sc);
50050517e57Snonaka 	if (ccb == NULL) {
50150517e57Snonaka 		device_printf(sc->sc_dev, "failed to allocate ccb\n");
50250517e57Snonaka 		hvs_scsi_done(xs, XS_RESOURCE_SHORTAGE);
50350517e57Snonaka 		return;
50450517e57Snonaka 	}
50550517e57Snonaka 
50650517e57Snonaka 	periph = xs->xs_periph;
50750517e57Snonaka 
50850517e57Snonaka 	memset(&cmd, 0, sizeof(cmd));
50950517e57Snonaka 
51050517e57Snonaka 	srb->srb_initiator = chan->chan_id;
51150517e57Snonaka 	srb->srb_bus = sc->sc_bus;
51250517e57Snonaka 	srb->srb_target = periph->periph_target - 1;
51350517e57Snonaka 	srb->srb_lun = periph->periph_lun;
51450517e57Snonaka 	srb->srb_cdblen = xs->cmdlen;
51550517e57Snonaka 	memcpy(srb->srb_data, xs->cmd, xs->cmdlen);
51650517e57Snonaka 
51750517e57Snonaka 	if (sc->sc_config->use_win8ext_flags) {
51850517e57Snonaka 		io->cmd_timeout = 60;
51950517e57Snonaka 		SET(io->cmd_srbflags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
52050517e57Snonaka 	}
52150517e57Snonaka 
52250517e57Snonaka 	switch (xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT)) {
52350517e57Snonaka 	case XS_CTL_DATA_IN:
52450517e57Snonaka 		srb->srb_direction = SRB_DATA_READ;
52550517e57Snonaka 		if (sc->sc_config->use_win8ext_flags)
52650517e57Snonaka 			SET(io->cmd_srbflags, SRB_FLAGS_DATA_IN);
52750517e57Snonaka 		break;
52850517e57Snonaka 	case XS_CTL_DATA_OUT:
52950517e57Snonaka 		srb->srb_direction = SRB_DATA_WRITE;
53050517e57Snonaka 		if (sc->sc_config->use_win8ext_flags)
53150517e57Snonaka 			SET(io->cmd_srbflags, SRB_FLAGS_DATA_OUT);
53250517e57Snonaka 		break;
53350517e57Snonaka 	default:
53450517e57Snonaka 		srb->srb_direction = SRB_DATA_NONE;
53550517e57Snonaka 		if (sc->sc_config->use_win8ext_flags)
53650517e57Snonaka 			SET(io->cmd_srbflags, SRB_FLAGS_NO_DATA_TRANSFER);
53750517e57Snonaka 		break;
53850517e57Snonaka 	}
53950517e57Snonaka 
54050517e57Snonaka 	srb->srb_datalen = xs->datalen;
54150517e57Snonaka 	srb->srb_reqlen = sc->sc_config->reqlen;
54250517e57Snonaka 	srb->srb_senselen = sc->sc_config->senselen;
54350517e57Snonaka 
54450517e57Snonaka 	cmd.cmd_op = HVS_REQ_SCSIIO;
54550517e57Snonaka 	cmd.cmd_flags = VMBUS_CHANPKT_FLAG_RC;
54650517e57Snonaka 
54750517e57Snonaka 	if (xs->datalen > 0) {
54850517e57Snonaka 		error = bus_dmamap_load(sc->sc_dmat, ccb->ccb_dmap, xs->data,
54950517e57Snonaka 		    xs->datalen, NULL, XS2DMA(xs));
55050517e57Snonaka 		if (error) {
55150517e57Snonaka 			device_printf(sc->sc_dev,
55250517e57Snonaka 			    "failed to load %d bytes (%d)\n", xs->datalen,
55350517e57Snonaka 			    error);
55450517e57Snonaka 			hvs_put_ccb(sc, ccb);
55550517e57Snonaka 			hvs_scsi_done(xs, (error == ENOMEM || error == EAGAIN) ?
55650517e57Snonaka 			    XS_RESOURCE_SHORTAGE : XS_DRIVER_STUFFUP);
55750517e57Snonaka 			return;
55850517e57Snonaka 		}
55950517e57Snonaka 		bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmap, 0, xs->datalen,
56050517e57Snonaka 		    XS2DMAPRE(xs));
56150517e57Snonaka 
56250517e57Snonaka 		ccb->ccb_sgl->gpa_len = xs->datalen;
56350517e57Snonaka 		ccb->ccb_sgl->gpa_ofs = (vaddr_t)xs->data & PAGE_MASK;
56450517e57Snonaka 		for (i = 0; i < ccb->ccb_dmap->dm_nsegs; i++)
56550517e57Snonaka 			ccb->ccb_sgl->gpa_page[i] =
56650517e57Snonaka 			    atop(ccb->ccb_dmap->dm_segs[i].ds_addr);
56750517e57Snonaka 		ccb->ccb_nsge = ccb->ccb_dmap->dm_nsegs;
56850517e57Snonaka 	} else
56950517e57Snonaka 		ccb->ccb_nsge = 0;
57050517e57Snonaka 
57150517e57Snonaka 	ccb->ccb_xfer = xs;
57250517e57Snonaka 	ccb->ccb_cmd = &cmd;
57350517e57Snonaka 	ccb->ccb_done = hvs_scsi_cmd_done;
57450517e57Snonaka 
57550517e57Snonaka #ifdef HVS_DEBUG_IO
57650517e57Snonaka 	printf("%s: %u.%u: rid %"PRIu64" opcode %#x control %#x datalen %d\n",
57750517e57Snonaka 	    device_xname(sc->sc_dev), periph->periph_target, periph->periph_lun,
57850517e57Snonaka 	    ccb->ccb_rid, xs->cmd->opcode, xs->xs_control, xs->datalen);
57950517e57Snonaka #endif
58050517e57Snonaka 
58150517e57Snonaka 	if (xs->xs_control & XS_CTL_POLL)
58250517e57Snonaka 		error = hvs_poll(sc, sc->sc_chan, ccb);
58350517e57Snonaka 	else
58450517e57Snonaka 		error = hvs_start(sc, sc->sc_chan, ccb);
58550517e57Snonaka 	if (error) {
58650517e57Snonaka 		hvs_put_ccb(sc, ccb);
58750517e57Snonaka 		hvs_scsi_done(xs, (error == ENOMEM || error == EAGAIN) ?
58850517e57Snonaka 		    XS_RESOURCE_SHORTAGE : XS_DRIVER_STUFFUP);
58950517e57Snonaka 	}
59050517e57Snonaka }
59150517e57Snonaka 
59250517e57Snonaka static int
hvs_start(struct hvs_softc * sc,struct vmbus_channel * chan,struct hvs_ccb * ccb)59350517e57Snonaka hvs_start(struct hvs_softc *sc, struct vmbus_channel *chan, struct hvs_ccb *ccb)
59450517e57Snonaka {
59550517e57Snonaka 	union hvs_cmd *cmd = ccb->ccb_cmd;
59650517e57Snonaka 	int error;
59750517e57Snonaka 
59850517e57Snonaka 	ccb->ccb_cmd = NULL;
59950517e57Snonaka 
60050517e57Snonaka 	if (ccb->ccb_nsge > 0) {
60150517e57Snonaka 		error = vmbus_channel_send_prpl(chan, ccb->ccb_sgl,
60250517e57Snonaka 		    ccb->ccb_nsge, cmd, HVS_CMD_SIZE, ccb->ccb_rid);
60350517e57Snonaka 		if (error) {
60450517e57Snonaka 			device_printf(sc->sc_dev,
60550517e57Snonaka 			    "failed to submit operation %x via prpl\n",
60650517e57Snonaka 			    cmd->cmd_op);
60750517e57Snonaka 			bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmap);
60850517e57Snonaka 		}
60950517e57Snonaka 	} else {
61050517e57Snonaka 		error = vmbus_channel_send(chan, cmd, HVS_CMD_SIZE,
61150517e57Snonaka 		    ccb->ccb_rid, VMBUS_CHANPKT_TYPE_INBAND,
61250517e57Snonaka 		    VMBUS_CHANPKT_FLAG_RC);
61350517e57Snonaka 		if (error)
61450517e57Snonaka 			device_printf(sc->sc_dev,
61550517e57Snonaka 			    "failed to submit operation %x\n", cmd->cmd_op);
61650517e57Snonaka 	}
61750517e57Snonaka 
61850517e57Snonaka 	return error;
61950517e57Snonaka }
62050517e57Snonaka 
62150517e57Snonaka static void
hvs_poll_done(struct hvs_ccb * ccb)62250517e57Snonaka hvs_poll_done(struct hvs_ccb *ccb)
62350517e57Snonaka {
62450517e57Snonaka 	int *rv = ccb->ccb_cookie;
62550517e57Snonaka 
62650517e57Snonaka 	if (ccb->ccb_cmd) {
62750517e57Snonaka 		memcpy(&ccb->ccb_rsp, ccb->ccb_cmd, HVS_CMD_SIZE);
62850517e57Snonaka 		ccb->ccb_cmd = &ccb->ccb_rsp;
62950517e57Snonaka 	} else
63050517e57Snonaka 		memset(&ccb->ccb_rsp, 0, HVS_CMD_SIZE);
63150517e57Snonaka 
63250517e57Snonaka 	*rv = 0;
63350517e57Snonaka }
63450517e57Snonaka 
63550517e57Snonaka static int
hvs_poll(struct hvs_softc * sc,struct vmbus_channel * chan,struct hvs_ccb * ccb)63650517e57Snonaka hvs_poll(struct hvs_softc *sc, struct vmbus_channel *chan, struct hvs_ccb *ccb)
63750517e57Snonaka {
63850517e57Snonaka 	void (*done)(struct hvs_ccb *);
63950517e57Snonaka 	void *cookie;
64050517e57Snonaka 	int s, rv = 1;
64150517e57Snonaka 
64250517e57Snonaka 	done = ccb->ccb_done;
64350517e57Snonaka 	cookie = ccb->ccb_cookie;
64450517e57Snonaka 
64550517e57Snonaka 	ccb->ccb_done = hvs_poll_done;
64650517e57Snonaka 	ccb->ccb_cookie = &rv;
64750517e57Snonaka 
64850517e57Snonaka 	if (hvs_start(sc, chan, ccb)) {
64950517e57Snonaka 		ccb->ccb_cookie = cookie;
65050517e57Snonaka 		ccb->ccb_done = done;
65150517e57Snonaka 		return -1;
65250517e57Snonaka 	}
65350517e57Snonaka 
65450517e57Snonaka 	while (rv == 1) {
65550517e57Snonaka 		delay(10);
65650517e57Snonaka 		s = splbio();
65750517e57Snonaka 		hvs_intr(sc);
65850517e57Snonaka 		splx(s);
65950517e57Snonaka 	}
66050517e57Snonaka 
66150517e57Snonaka 	ccb->ccb_cookie = cookie;
66250517e57Snonaka 	ccb->ccb_done = done;
66350517e57Snonaka 	ccb->ccb_done(ccb);
66450517e57Snonaka 
66550517e57Snonaka 	return 0;
66650517e57Snonaka }
66750517e57Snonaka 
66850517e57Snonaka static void
hvs_intr(void * xsc)66950517e57Snonaka hvs_intr(void *xsc)
67050517e57Snonaka {
67150517e57Snonaka 	struct hvs_softc *sc = xsc;
67250517e57Snonaka 	struct hvs_ccb *ccb;
67350517e57Snonaka 	union hvs_cmd cmd;
67450517e57Snonaka 	uint64_t rid;
67550517e57Snonaka 	uint32_t rlen;
67650517e57Snonaka 	int error;
67750517e57Snonaka 
67850517e57Snonaka 	for (;;) {
67950517e57Snonaka 		error = vmbus_channel_recv(sc->sc_chan, &cmd, sizeof(cmd),
68050517e57Snonaka 		    &rlen, &rid, 0);
68150517e57Snonaka 		switch (error) {
68250517e57Snonaka 		case 0:
68350517e57Snonaka 			break;
68450517e57Snonaka 		case EAGAIN:
68550517e57Snonaka 			/* No more messages to process */
68650517e57Snonaka 			return;
68750517e57Snonaka 		default:
68850517e57Snonaka 			device_printf(sc->sc_dev,
68950517e57Snonaka 			    "error %d while receiving a reply\n", error);
69050517e57Snonaka 			return;
69150517e57Snonaka 		}
69250517e57Snonaka 		if (rlen != sizeof(cmd)) {
69350517e57Snonaka 			device_printf(sc->sc_dev, "short read: %u\n", rlen);
69450517e57Snonaka 			return;
69550517e57Snonaka 		}
69650517e57Snonaka 
69750517e57Snonaka #ifdef HVS_DEBUG_IO
69850517e57Snonaka 		printf("%s: rid %"PRIu64" operation %u flags %#x status %#x\n",
69950517e57Snonaka 		    device_xname(sc->sc_dev), rid, cmd.cmd_op, cmd.cmd_flags,
70050517e57Snonaka 		    cmd.cmd_status);
70150517e57Snonaka #endif
70250517e57Snonaka 
70350517e57Snonaka 		switch (cmd.cmd_op) {
70450517e57Snonaka 		case HVS_MSG_IODONE:
70550517e57Snonaka 			if (rid >= sc->sc_nccb) {
70650517e57Snonaka 				device_printf(sc->sc_dev,
70750517e57Snonaka 				    "invalid response %#"PRIx64"\n", rid);
70850517e57Snonaka 				continue;
70950517e57Snonaka 			}
71050517e57Snonaka 			ccb = &sc->sc_ccbs[rid];
71150517e57Snonaka 			ccb->ccb_cmd = &cmd;
71250517e57Snonaka 			ccb->ccb_done(ccb);
71350517e57Snonaka 			break;
71450517e57Snonaka 		case HVS_MSG_ENUMERATE:
71550517e57Snonaka 			hvs_scsi_probe(sc);
71650517e57Snonaka 			break;
71750517e57Snonaka 		default:
71850517e57Snonaka 			device_printf(sc->sc_dev,
71950517e57Snonaka 			    "operation %u is not implemented\n", cmd.cmd_op);
72050517e57Snonaka 			break;
72150517e57Snonaka 		}
72250517e57Snonaka 	}
72350517e57Snonaka }
72450517e57Snonaka 
72550517e57Snonaka static int
is_inquiry_valid(struct scsipi_inquiry_data * inq)72650517e57Snonaka is_inquiry_valid(struct scsipi_inquiry_data *inq)
72750517e57Snonaka {
72850517e57Snonaka 
72950517e57Snonaka 	if ((inq->device & SID_TYPE) == T_NODEVICE)
73050517e57Snonaka 		return 0;
73150517e57Snonaka 	if ((inq->device & SID_QUAL) == SID_QUAL_LU_NOT_SUPP)
73250517e57Snonaka 		return 0;
73350517e57Snonaka 	return 1;
73450517e57Snonaka }
73550517e57Snonaka 
73650517e57Snonaka static void
fixup_inquiry(struct scsipi_xfer * xs,struct hvs_srb * srb)73750517e57Snonaka fixup_inquiry(struct scsipi_xfer *xs, struct hvs_srb *srb)
73850517e57Snonaka {
73950517e57Snonaka 	struct scsipi_periph *periph = xs->xs_periph;
74050517e57Snonaka 	struct scsipi_channel *chan = periph->periph_channel;
74150517e57Snonaka 	struct scsipi_adapter *adapt = chan->chan_adapter;
74250517e57Snonaka 	struct hvs_softc *sc = device_private(adapt->adapt_dev);
74350517e57Snonaka 	struct scsipi_inquiry_data *inq = (void *)xs->data;
74450517e57Snonaka 	int datalen, resplen;
74550517e57Snonaka 	char vendor[8];
74650517e57Snonaka 
74750517e57Snonaka 	resplen = srb->srb_datalen >= 5 ? inq->additional_length + 5 : 0;
74850517e57Snonaka 	datalen = MIN(resplen, srb->srb_datalen);
74950517e57Snonaka 
75050517e57Snonaka 	/* Fixup wrong response from WS2012 */
75150517e57Snonaka 	if (sc->sc_config->fixup_wrong_response &&
75250517e57Snonaka 	    !is_inquiry_valid(inq) && datalen >= 4 &&
75350517e57Snonaka 	    (inq->version == 0 || inq->response_format == 0)) {
75450517e57Snonaka 		inq->version = 0x05; /* SPC-3 */
75550517e57Snonaka 		inq->response_format = SID_FORMAT_ISO;
75650517e57Snonaka 	} else if (datalen >= SCSIPI_INQUIRY_LENGTH_SCSI2) {
75750517e57Snonaka 		/*
75850517e57Snonaka 		 * Upgrade SPC2 to SPC3 if host is Win8 or WS2012 R2
75950517e57Snonaka 		 * to support UNMAP feature.
76050517e57Snonaka 		 */
76150517e57Snonaka 		strnvisx(vendor, sizeof(vendor), inq->vendor, sizeof(vendor),
76250517e57Snonaka 		    VIS_TRIM|VIS_SAFE|VIS_OCTAL);
76350517e57Snonaka 		if (sc->sc_config->upgrade_spc2_to_spc3 &&
76450517e57Snonaka 		    (inq->version & SID_ANSII) == 0x04 /* SPC-2 */ &&
76550517e57Snonaka 		    !strncmp(vendor, "Msft", 4))
76650517e57Snonaka 			inq->version = 0x05; /* SPC-3 */
76750517e57Snonaka 	}
76850517e57Snonaka }
76950517e57Snonaka 
77050517e57Snonaka static void
hvs_scsi_cmd_done(struct hvs_ccb * ccb)77150517e57Snonaka hvs_scsi_cmd_done(struct hvs_ccb *ccb)
77250517e57Snonaka {
77350517e57Snonaka 	struct scsipi_xfer *xs = ccb->ccb_xfer;
77450517e57Snonaka 	struct scsipi_periph *periph = xs->xs_periph;
77550517e57Snonaka 	struct scsipi_channel *chan = periph->periph_channel;
77650517e57Snonaka 	struct scsipi_adapter *adapt = chan->chan_adapter;
77750517e57Snonaka 	struct hvs_softc *sc = device_private(adapt->adapt_dev);
77850517e57Snonaka 	union hvs_cmd *cmd = ccb->ccb_cmd;
77950517e57Snonaka 	struct hvs_srb *srb;
78050517e57Snonaka 	bus_dmamap_t map;
78150517e57Snonaka 	int error;
78250517e57Snonaka 
78350517e57Snonaka 	map = ccb->ccb_dmap;
78450517e57Snonaka 	bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize, XS2DMAPOST(xs));
78550517e57Snonaka 	bus_dmamap_unload(sc->sc_dmat, map);
78650517e57Snonaka 
78750517e57Snonaka 	xs = ccb->ccb_xfer;
78850517e57Snonaka 	srb = &cmd->io.cmd_srb;
78950517e57Snonaka 
79050517e57Snonaka 	xs->status = srb->srb_scsistatus & 0xff;
79150517e57Snonaka 
79250517e57Snonaka 	switch (xs->status) {
79350517e57Snonaka 	case SCSI_OK:
79450517e57Snonaka 		if ((srb->srb_iostatus & ~(SRB_STATUS_AUTOSENSE_VALID |
79550517e57Snonaka 		    SRB_STATUS_QUEUE_FROZEN)) != SRB_STATUS_SUCCESS)
79650517e57Snonaka 			error = XS_SELTIMEOUT;
79750517e57Snonaka 		else
79850517e57Snonaka 			error = XS_NOERROR;
79950517e57Snonaka 		break;
80050517e57Snonaka 	case SCSI_BUSY:
80150517e57Snonaka 	case SCSI_QUEUE_FULL:
80250517e57Snonaka 		device_printf(sc->sc_dev, "status %#x iostatus %#x (busy)\n",
80350517e57Snonaka 		    srb->srb_scsistatus, srb->srb_iostatus);
80450517e57Snonaka 		error = XS_BUSY;
80550517e57Snonaka 		break;
80650517e57Snonaka 	case SCSI_CHECK:
80750517e57Snonaka 		if (srb->srb_iostatus & SRB_STATUS_AUTOSENSE_VALID) {
80850517e57Snonaka 			memcpy(&xs->sense, srb->srb_data,
80950517e57Snonaka 			    MIN(sizeof(xs->sense), srb->srb_senselen));
81050517e57Snonaka 			error = XS_SENSE;
81150517e57Snonaka 			break;
81250517e57Snonaka 		}
81350517e57Snonaka 		/* FALLTHROUGH */
81450517e57Snonaka 	default:
81550517e57Snonaka 		error = XS_DRIVER_STUFFUP;
81650517e57Snonaka 		break;
81750517e57Snonaka 	}
81850517e57Snonaka 
81950517e57Snonaka 	if (error == XS_NOERROR) {
82050517e57Snonaka 		if (xs->cmd->opcode == INQUIRY)
82150517e57Snonaka 			fixup_inquiry(xs, srb);
82250517e57Snonaka 		else if (srb->srb_direction != SRB_DATA_NONE)
82350517e57Snonaka 			xs->resid = xs->datalen - srb->srb_datalen;
82450517e57Snonaka 	}
82550517e57Snonaka 
82650517e57Snonaka 	hvs_put_ccb(sc, ccb);
82750517e57Snonaka 	hvs_scsi_done(xs, error);
82850517e57Snonaka }
82950517e57Snonaka 
83050517e57Snonaka static void
hvs_scsi_probe(void * arg)83150517e57Snonaka hvs_scsi_probe(void *arg)
83250517e57Snonaka {
83350517e57Snonaka 	struct hvs_softc *sc = arg;
83450517e57Snonaka 
83550517e57Snonaka 	if (sc->sc_scsibus != NULL)
8366374f2d5Snonaka 		scsi_probe_bus(device_private(sc->sc_scsibus), -1, -1);
83750517e57Snonaka }
83850517e57Snonaka 
83950517e57Snonaka static void
hvs_scsi_done(struct scsipi_xfer * xs,int error)84050517e57Snonaka hvs_scsi_done(struct scsipi_xfer *xs, int error)
84150517e57Snonaka {
84250517e57Snonaka 
84350517e57Snonaka 	xs->error = error;
84450517e57Snonaka 	scsipi_done(xs);
84550517e57Snonaka }
84650517e57Snonaka 
84750517e57Snonaka static int
hvs_connect(struct hvs_softc * sc)84850517e57Snonaka hvs_connect(struct hvs_softc *sc)
84950517e57Snonaka {
85050517e57Snonaka 	union hvs_cmd ucmd;
85150517e57Snonaka 	struct hvs_cmd_ver *cmd;
85250517e57Snonaka 	struct hvs_chp *chp;
85350517e57Snonaka 	struct hvs_ccb *ccb;
85450517e57Snonaka #if notyet /* XXX subchannel */
85550517e57Snonaka 	struct vmbus_softc *vsc;
85650517e57Snonaka 	struct vmbus_channel **subchan;
85750517e57Snonaka 	uint32_t version;
85850517e57Snonaka 	uint16_t max_subch, req_subch;
85950517e57Snonaka 	bool support_multichannel = false;
86050517e57Snonaka #endif
86150517e57Snonaka 	int i;
86250517e57Snonaka 
86350517e57Snonaka 	ccb = hvs_get_ccb(sc);
86450517e57Snonaka 	if (ccb == NULL) {
86550517e57Snonaka 		aprint_error_dev(sc->sc_dev, "failed to allocate ccb\n");
86650517e57Snonaka 		return -1;
86750517e57Snonaka 	}
86850517e57Snonaka 
86950517e57Snonaka 	ccb->ccb_done = hvs_empty_done;
87050517e57Snonaka 
87150517e57Snonaka 	cmd = (struct hvs_cmd_ver *)&ucmd;
87250517e57Snonaka 
87350517e57Snonaka 	/*
87450517e57Snonaka 	 * Begin initialization
87550517e57Snonaka 	 */
87650517e57Snonaka 
87750517e57Snonaka 	memset(&ucmd, 0, sizeof(ucmd));
87850517e57Snonaka 
87950517e57Snonaka 	cmd->cmd_op = HVS_REQ_STARTINIT;
88050517e57Snonaka 	cmd->cmd_flags = VMBUS_CHANPKT_FLAG_RC;
88150517e57Snonaka 
88250517e57Snonaka 	ccb->ccb_cmd = &ucmd;
88350517e57Snonaka 	if (hvs_poll(sc, sc->sc_chan, ccb)) {
88450517e57Snonaka 		aprint_error_dev(sc->sc_dev,
88550517e57Snonaka 		    "failed to send initialization command\n");
88650517e57Snonaka 		goto error;
88750517e57Snonaka 	}
88850517e57Snonaka 	if (ccb->ccb_rsp.cmd_status != 0) {
88950517e57Snonaka 		aprint_error_dev(sc->sc_dev,
89050517e57Snonaka 		    "failed to initialize, status %#x\n",
89150517e57Snonaka 		    ccb->ccb_rsp.cmd_status);
89250517e57Snonaka 		goto error;
89350517e57Snonaka 	}
89450517e57Snonaka 
89550517e57Snonaka 	/*
89650517e57Snonaka 	 * Negotiate protocol version
89750517e57Snonaka 	 */
89850517e57Snonaka 
89950517e57Snonaka 	memset(&ucmd, 0, sizeof(ucmd));
90050517e57Snonaka 
90150517e57Snonaka 	cmd->cmd_op = HVS_REQ_QUERYPROTO;
90250517e57Snonaka 	cmd->cmd_flags = VMBUS_CHANPKT_FLAG_RC;
90350517e57Snonaka 
90450517e57Snonaka 	for (i = 0; i < __arraycount(hvs_config_list); i++) {
90550517e57Snonaka 		cmd->cmd_ver = hvs_config_list[i].proto_version;
90650517e57Snonaka 
90750517e57Snonaka 		ccb->ccb_cmd = &ucmd;
90850517e57Snonaka 		if (hvs_poll(sc, sc->sc_chan, ccb)) {
90950517e57Snonaka 			aprint_error_dev(sc->sc_dev,
91050517e57Snonaka 			    "failed to send protocol query\n");
91150517e57Snonaka 			goto error;
91250517e57Snonaka 		}
91350517e57Snonaka 		if (ccb->ccb_rsp.cmd_status == 0) {
91450517e57Snonaka 			sc->sc_config = &hvs_config_list[i];
91550517e57Snonaka 			break;
91650517e57Snonaka 		}
91750517e57Snonaka 	}
91850517e57Snonaka 	if (sc->sc_config == NULL) {
91950517e57Snonaka 		aprint_error_dev(sc->sc_dev,
92050517e57Snonaka 		    "failed to negotiate protocol version\n");
92150517e57Snonaka 		goto error;
92250517e57Snonaka 	}
92350517e57Snonaka 
92450517e57Snonaka 	/*
92550517e57Snonaka 	 * Query channel properties
92650517e57Snonaka 	 */
92750517e57Snonaka 
92850517e57Snonaka 	memset(&ucmd, 0, sizeof(ucmd));
92950517e57Snonaka 
93050517e57Snonaka 	cmd->cmd_op = HVS_REQ_QUERYPROPS;
93150517e57Snonaka 	cmd->cmd_flags = VMBUS_CHANPKT_FLAG_RC;
93250517e57Snonaka 
93350517e57Snonaka 	ccb->ccb_cmd = &ucmd;
93450517e57Snonaka 	if (hvs_poll(sc, sc->sc_chan, ccb)) {
93550517e57Snonaka 		aprint_error_dev(sc->sc_dev,
93650517e57Snonaka 		    "failed to send channel properties query\n");
93750517e57Snonaka 		goto error;
93850517e57Snonaka 	}
93950517e57Snonaka 	if (ccb->ccb_rsp.cmd_op != HVS_MSG_IODONE ||
94050517e57Snonaka 	    ccb->ccb_rsp.cmd_status != 0) {
94150517e57Snonaka 		aprint_error_dev(sc->sc_dev,
94250517e57Snonaka 		    "failed to obtain channel properties, status %#x\n",
94350517e57Snonaka 		    ccb->ccb_rsp.cmd_status);
94450517e57Snonaka 		goto error;
94550517e57Snonaka 	}
94650517e57Snonaka 	chp = &ccb->ccb_rsp.chp.cmd_chp;
94750517e57Snonaka 
94850517e57Snonaka 	DPRINTF("%s: proto %#x path %u target %u maxchan %u port %u "
94950517e57Snonaka 	    "chflags %#x maxfer %u chanid %#"PRIx64"\n",
95050517e57Snonaka 	    device_xname(sc->sc_dev), chp->chp_proto, chp->chp_path,
95150517e57Snonaka 	    chp->chp_target, chp->chp_maxchan, chp->chp_port,
95250517e57Snonaka 	    chp->chp_chflags, chp->chp_maxfer, chp->chp_chanid);
95350517e57Snonaka 
95450517e57Snonaka #if notyet /* XXX subchannel */
95550517e57Snonaka 	max_subch = chp->chp_maxchan;
95650517e57Snonaka 	if (hvs_chan_cnt > 0 && hvs_chan_cnt < (max_subch + 1))
95750517e57Snonaka 		max_subch = hvs_chan_cnt - 1;
95850517e57Snonaka 
95950517e57Snonaka 	/* multi-channels feature is supported by WIN8 and above version */
96050517e57Snonaka 	version = sc->sc_chan->ch_sc->sc_proto;
96150517e57Snonaka 	if (version != VMBUS_VERSION_WIN7 && version != VMBUS_VERSION_WS2008 &&
96250517e57Snonaka 	    ISSET(chp->chp_chflags, CHP_CHFLAGS_MULTI_CHANNEL))
96350517e57Snonaka 		support_multichannel = true;
96450517e57Snonaka #endif
96550517e57Snonaka 
96650517e57Snonaka 	/* XXX */
96750517e57Snonaka 	sc->sc_bus = chp->chp_path;
96850517e57Snonaka 	sc->sc_channel.chan_id = chp->chp_target;
96950517e57Snonaka 
97050517e57Snonaka 	/*
97150517e57Snonaka 	 * Finish initialization
97250517e57Snonaka 	 */
97350517e57Snonaka 
97450517e57Snonaka 	memset(&ucmd, 0, sizeof(ucmd));
97550517e57Snonaka 
97650517e57Snonaka 	cmd->cmd_op = HVS_REQ_FINISHINIT;
97750517e57Snonaka 	cmd->cmd_flags = VMBUS_CHANPKT_FLAG_RC;
97850517e57Snonaka 
97950517e57Snonaka 	ccb->ccb_cmd = &ucmd;
98050517e57Snonaka 	if (hvs_poll(sc, sc->sc_chan, ccb)) {
98150517e57Snonaka 		aprint_error_dev(sc->sc_dev,
98250517e57Snonaka 		    "failed to send initialization finish\n");
98350517e57Snonaka 		goto error;
98450517e57Snonaka 	}
98550517e57Snonaka 	if (ccb->ccb_rsp.cmd_op != HVS_MSG_IODONE ||
98650517e57Snonaka 	    ccb->ccb_rsp.cmd_status != 0) {
98750517e57Snonaka 		aprint_error_dev(sc->sc_dev,
98850517e57Snonaka 		    "failed to finish initialization, status %#x\n",
98950517e57Snonaka 		    ccb->ccb_rsp.cmd_status);
99050517e57Snonaka 		goto error;
99150517e57Snonaka 	}
99250517e57Snonaka 
99350517e57Snonaka #if notyet /* XXX subchannel */
99450517e57Snonaka 	if (support_multichannel && max_subch > 0 && ncpu > 1) {
99550517e57Snonaka 		req_subch = MIN(max_subch, ncpu - 1);
99650517e57Snonaka 
99750517e57Snonaka 		memset(&ucmd, 0, sizeof(ucmd));
99850517e57Snonaka 
99950517e57Snonaka 		cmd->cmd_op = HVS_REQ_CREATEMULTICHANNELS;
100050517e57Snonaka 		cmd->cmd_flags = VMBUS_CHANPKT_FLAG_RC;
100150517e57Snonaka 		cmd->u.multi_chans_cnt = req_subch;
100250517e57Snonaka 
100350517e57Snonaka 		ccb->ccb_cmd = &ucmd;
100450517e57Snonaka 		if (hvs_poll(sc, sc->sc_chan, ccb)) {
100550517e57Snonaka 			aprint_error_dev(sc->sc_dev,
100650517e57Snonaka 			    "failed to send create multi-channel\n");
100750517e57Snonaka 			goto out;
100850517e57Snonaka 		}
100950517e57Snonaka 		if (ccb->ccb_rsp.cmd_op != HVS_MSG_IODONE ||
101050517e57Snonaka 		    ccb->ccb_rsp.cmd_status != 0) {
101150517e57Snonaka 			aprint_error_dev(sc->sc_dev,
101250517e57Snonaka 			    "failed to create multi-channel, status %#x\n",
101350517e57Snonaka 			    ccb->ccb_rsp.cmd_status);
101450517e57Snonaka 			goto out;
101550517e57Snonaka 		}
101650517e57Snonaka 
101750517e57Snonaka 		sc->sc_nchan = req_subch + 1;
101850517e57Snonaka 		subchan = vmbus_subchan_get(sc->sc_chan, req_subch);
101950517e57Snonaka 		for (i = 0; i < req_subch; i++)
102050517e57Snonaka 			hsv_subchan_attach(sc, subchan[i]);
102150517e57Snonaka 		vmbus_subchan_rel(subchan, req_subch);
102250517e57Snonaka 		aprint_normal_dev(sc->sc_dev, "using %u channels\n",
102350517e57Snonaka 		    sc->sc_nchan);
102450517e57Snonaka 	}
102550517e57Snonaka out:
102650517e57Snonaka #endif
102750517e57Snonaka 	hvs_put_ccb(sc, ccb);
102850517e57Snonaka 	return 0;
102950517e57Snonaka 
103050517e57Snonaka error:
103150517e57Snonaka 	hvs_put_ccb(sc, ccb);
103250517e57Snonaka 	return -1;
103350517e57Snonaka }
103450517e57Snonaka 
103550517e57Snonaka static void
hvs_empty_done(struct hvs_ccb * ccb)103650517e57Snonaka hvs_empty_done(struct hvs_ccb *ccb)
103750517e57Snonaka {
103850517e57Snonaka 	/* nothing */
103950517e57Snonaka }
104050517e57Snonaka 
104150517e57Snonaka static int
hvs_alloc_ccbs(struct hvs_softc * sc)104250517e57Snonaka hvs_alloc_ccbs(struct hvs_softc *sc)
104350517e57Snonaka {
104450517e57Snonaka 	const int dmaflags = cold ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK;
104550517e57Snonaka 	int i, error;
104650517e57Snonaka 
104750517e57Snonaka 	SIMPLEQ_INIT(&sc->sc_ccb_fq);
104850517e57Snonaka 	mutex_init(&sc->sc_ccb_fqlck, MUTEX_DEFAULT, IPL_BIO);
104950517e57Snonaka 
105050517e57Snonaka 	sc->sc_nccb = HVS_MAX_CCB;
105150517e57Snonaka 	sc->sc_ccbs = kmem_zalloc(sc->sc_nccb * sizeof(struct hvs_ccb),
105202991323Schs 	    KM_SLEEP);
105350517e57Snonaka 
105450517e57Snonaka 	for (i = 0; i < sc->sc_nccb; i++) {
105550517e57Snonaka 		error = bus_dmamap_create(sc->sc_dmat, MAXPHYS, HVS_MAX_SGE,
105650517e57Snonaka 		    PAGE_SIZE, PAGE_SIZE, dmaflags, &sc->sc_ccbs[i].ccb_dmap);
105750517e57Snonaka 		if (error) {
105850517e57Snonaka 			aprint_error_dev(sc->sc_dev,
105950517e57Snonaka 			    "failed to create a CCB memory map (%d)\n", error);
106050517e57Snonaka 			goto errout;
106150517e57Snonaka 		}
106250517e57Snonaka 
106350517e57Snonaka 		sc->sc_ccbs[i].ccb_sgl = kmem_zalloc(
106450517e57Snonaka 		    sizeof(struct vmbus_gpa_range) * (HVS_MAX_SGE + 1),
106502991323Schs 		    KM_SLEEP);
106650517e57Snonaka 		sc->sc_ccbs[i].ccb_rid = i;
106750517e57Snonaka 		hvs_put_ccb(sc, &sc->sc_ccbs[i]);
106850517e57Snonaka 	}
106950517e57Snonaka 
107050517e57Snonaka 	return 0;
107150517e57Snonaka 
107250517e57Snonaka  errout:
107350517e57Snonaka 	hvs_free_ccbs(sc);
107450517e57Snonaka 	return -1;
107550517e57Snonaka }
107650517e57Snonaka 
107750517e57Snonaka static void
hvs_free_ccbs(struct hvs_softc * sc)107850517e57Snonaka hvs_free_ccbs(struct hvs_softc *sc)
107950517e57Snonaka {
108050517e57Snonaka 	struct hvs_ccb *ccb;
108150517e57Snonaka 	int i;
108250517e57Snonaka 
108350517e57Snonaka 	for (i = 0; i < sc->sc_nccb; i++) {
108450517e57Snonaka 		ccb = &sc->sc_ccbs[i];
108550517e57Snonaka 		if (ccb->ccb_dmap == NULL)
108650517e57Snonaka 			continue;
108750517e57Snonaka 
108860a4c738Snonaka 		bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmap,
108960a4c738Snonaka 		    0, ccb->ccb_dmap->dm_mapsize,
109050517e57Snonaka 		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
109150517e57Snonaka 		bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmap);
109250517e57Snonaka 		bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmap);
109350517e57Snonaka 
109450517e57Snonaka 		kmem_free(ccb->ccb_sgl,
109550517e57Snonaka 		    sizeof(struct vmbus_gpa_range) * (HVS_MAX_SGE + 1));
109650517e57Snonaka 	}
109750517e57Snonaka 
109850517e57Snonaka 	kmem_free(sc->sc_ccbs, sc->sc_nccb * sizeof(struct hvs_ccb));
109950517e57Snonaka 	sc->sc_ccbs = NULL;
110050517e57Snonaka 	sc->sc_nccb = 0;
110150517e57Snonaka }
110250517e57Snonaka 
110350517e57Snonaka static struct hvs_ccb *
hvs_get_ccb(struct hvs_softc * sc)110450517e57Snonaka hvs_get_ccb(struct hvs_softc *sc)
110550517e57Snonaka {
110650517e57Snonaka 	struct hvs_ccb *ccb;
110750517e57Snonaka 
110850517e57Snonaka 	mutex_enter(&sc->sc_ccb_fqlck);
110950517e57Snonaka 	ccb = SIMPLEQ_FIRST(&sc->sc_ccb_fq);
111050517e57Snonaka 	if (ccb != NULL)
111150517e57Snonaka 		SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_fq, ccb_link);
111250517e57Snonaka 	mutex_exit(&sc->sc_ccb_fqlck);
111350517e57Snonaka 
111450517e57Snonaka 	return ccb;
111550517e57Snonaka }
111650517e57Snonaka 
111750517e57Snonaka static void
hvs_put_ccb(struct hvs_softc * sc,struct hvs_ccb * ccb)111850517e57Snonaka hvs_put_ccb(struct hvs_softc *sc, struct hvs_ccb *ccb)
111950517e57Snonaka {
112050517e57Snonaka 
112150517e57Snonaka 	ccb->ccb_cmd = NULL;
112250517e57Snonaka 	ccb->ccb_xfer = NULL;
112350517e57Snonaka 	ccb->ccb_done = NULL;
112450517e57Snonaka 	ccb->ccb_cookie = NULL;
112550517e57Snonaka 	ccb->ccb_nsge = 0;
112650517e57Snonaka 
112750517e57Snonaka 	mutex_enter(&sc->sc_ccb_fqlck);
112850517e57Snonaka 	SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_fq, ccb, ccb_link);
112950517e57Snonaka 	mutex_exit(&sc->sc_ccb_fqlck);
113050517e57Snonaka }
1131