1256056Sgrehan/*-
2256056Sgrehan * Copyright (c) 2013  Zhixiang Yu <zcore@freebsd.org>
3256056Sgrehan * All rights reserved.
4256056Sgrehan *
5256056Sgrehan * Redistribution and use in source and binary forms, with or without
6256056Sgrehan * modification, are permitted provided that the following conditions
7256056Sgrehan * are met:
8256056Sgrehan * 1. Redistributions of source code must retain the above copyright
9256056Sgrehan *    notice, this list of conditions and the following disclaimer.
10256056Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
11256056Sgrehan *    notice, this list of conditions and the following disclaimer in the
12256056Sgrehan *    documentation and/or other materials provided with the distribution.
13256056Sgrehan *
14256056Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
15256056Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16256056Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17256056Sgrehan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18256056Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19256056Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20256056Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21256056Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22256056Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23256056Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24256056Sgrehan * SUCH DAMAGE.
25256056Sgrehan *
26256056Sgrehan * $FreeBSD: releng/11.0/usr.sbin/bhyve/pci_ahci.c 303138 2016-07-21 11:57:41Z mav $
27256056Sgrehan */
28256056Sgrehan
29256056Sgrehan#include <sys/cdefs.h>
30256056Sgrehan__FBSDID("$FreeBSD: releng/11.0/usr.sbin/bhyve/pci_ahci.c 303138 2016-07-21 11:57:41Z mav $");
31256056Sgrehan
32256056Sgrehan#include <sys/param.h>
33256056Sgrehan#include <sys/linker_set.h>
34256056Sgrehan#include <sys/stat.h>
35256056Sgrehan#include <sys/uio.h>
36256056Sgrehan#include <sys/ioctl.h>
37256056Sgrehan#include <sys/disk.h>
38256056Sgrehan#include <sys/ata.h>
39256056Sgrehan#include <sys/endian.h>
40256056Sgrehan
41256056Sgrehan#include <errno.h>
42256056Sgrehan#include <fcntl.h>
43256056Sgrehan#include <stdio.h>
44256056Sgrehan#include <stdlib.h>
45256056Sgrehan#include <stdint.h>
46256056Sgrehan#include <string.h>
47256056Sgrehan#include <strings.h>
48256056Sgrehan#include <unistd.h>
49256056Sgrehan#include <assert.h>
50256056Sgrehan#include <pthread.h>
51273212Stychon#include <pthread_np.h>
52256056Sgrehan#include <inttypes.h>
53280040Smav#include <md5.h>
54256056Sgrehan
55256056Sgrehan#include "bhyverun.h"
56256056Sgrehan#include "pci_emul.h"
57256056Sgrehan#include "ahci.h"
58256056Sgrehan#include "block_if.h"
59256056Sgrehan
60256056Sgrehan#define	MAX_PORTS	6	/* Intel ICH8 AHCI supports 6 ports */
61256056Sgrehan
62256056Sgrehan#define	PxSIG_ATA	0x00000101 /* ATA drive */
63256056Sgrehan#define	PxSIG_ATAPI	0xeb140101 /* ATAPI drive */
64256056Sgrehan
65256056Sgrehanenum sata_fis_type {
66256056Sgrehan	FIS_TYPE_REGH2D		= 0x27,	/* Register FIS - host to device */
67256056Sgrehan	FIS_TYPE_REGD2H		= 0x34,	/* Register FIS - device to host */
68256056Sgrehan	FIS_TYPE_DMAACT		= 0x39,	/* DMA activate FIS - device to host */
69256056Sgrehan	FIS_TYPE_DMASETUP	= 0x41,	/* DMA setup FIS - bidirectional */
70256056Sgrehan	FIS_TYPE_DATA		= 0x46,	/* Data FIS - bidirectional */
71256056Sgrehan	FIS_TYPE_BIST		= 0x58,	/* BIST activate FIS - bidirectional */
72256056Sgrehan	FIS_TYPE_PIOSETUP	= 0x5F,	/* PIO setup FIS - device to host */
73256056Sgrehan	FIS_TYPE_SETDEVBITS	= 0xA1,	/* Set dev bits FIS - device to host */
74256056Sgrehan};
75256056Sgrehan
76256056Sgrehan/*
77256056Sgrehan * SCSI opcodes
78256056Sgrehan */
79256056Sgrehan#define	TEST_UNIT_READY		0x00
80256056Sgrehan#define	REQUEST_SENSE		0x03
81256056Sgrehan#define	INQUIRY			0x12
82256056Sgrehan#define	START_STOP_UNIT		0x1B
83256056Sgrehan#define	PREVENT_ALLOW		0x1E
84256056Sgrehan#define	READ_CAPACITY		0x25
85256056Sgrehan#define	READ_10			0x28
86256056Sgrehan#define	POSITION_TO_ELEMENT	0x2B
87256056Sgrehan#define	READ_TOC		0x43
88256056Sgrehan#define	GET_EVENT_STATUS_NOTIFICATION 0x4A
89256056Sgrehan#define	MODE_SENSE_10		0x5A
90279979Smav#define	REPORT_LUNS		0xA0
91256056Sgrehan#define	READ_12			0xA8
92256056Sgrehan#define	READ_CD			0xBE
93256056Sgrehan
94256056Sgrehan/*
95256056Sgrehan * SCSI mode page codes
96256056Sgrehan */
97256056Sgrehan#define	MODEPAGE_RW_ERROR_RECOVERY	0x01
98256056Sgrehan#define	MODEPAGE_CD_CAPABILITIES	0x2A
99256056Sgrehan
100256056Sgrehan/*
101263238Stychon * ATA commands
102263238Stychon */
103263238Stychon#define	ATA_SF_ENAB_SATA_SF		0x10
104263238Stychon#define		ATA_SATA_SF_AN		0x05
105263238Stychon#define	ATA_SF_DIS_SATA_SF		0x90
106263238Stychon
107263238Stychon/*
108256056Sgrehan * Debug printf
109256056Sgrehan */
110256056Sgrehan#ifdef AHCI_DEBUG
111256056Sgrehanstatic FILE *dbg;
112256056Sgrehan#define DPRINTF(format, arg...)	do{fprintf(dbg, format, ##arg);fflush(dbg);}while(0)
113256056Sgrehan#else
114256056Sgrehan#define DPRINTF(format, arg...)
115256056Sgrehan#endif
116256056Sgrehan#define WPRINTF(format, arg...) printf(format, ##arg)
117256056Sgrehan
118256056Sgrehanstruct ahci_ioreq {
119256056Sgrehan	struct blockif_req io_req;
120256056Sgrehan	struct ahci_port *io_pr;
121273212Stychon	STAILQ_ENTRY(ahci_ioreq) io_flist;
122273212Stychon	TAILQ_ENTRY(ahci_ioreq) io_blist;
123256056Sgrehan	uint8_t *cfis;
124256056Sgrehan	uint32_t len;
125256056Sgrehan	uint32_t done;
126256056Sgrehan	int slot;
127281666Smav	int more;
128256056Sgrehan};
129256056Sgrehan
130256056Sgrehanstruct ahci_port {
131256056Sgrehan	struct blockif_ctxt *bctx;
132256056Sgrehan	struct pci_ahci_softc *pr_sc;
133256164Sdim	uint8_t *cmd_lst;
134256164Sdim	uint8_t *rfis;
135280040Smav	char ident[20 + 1];
136256056Sgrehan	int atapi;
137256056Sgrehan	int reset;
138282524Smav	int waitforclear;
139256056Sgrehan	int mult_sectors;
140256056Sgrehan	uint8_t xfermode;
141279975Smav	uint8_t err_cfis[20];
142256056Sgrehan	uint8_t sense_key;
143256056Sgrehan	uint8_t asc;
144282429Smav	u_int ccs;
145263322Stychon	uint32_t pending;
146256056Sgrehan
147256056Sgrehan	uint32_t clb;
148256056Sgrehan	uint32_t clbu;
149256056Sgrehan	uint32_t fb;
150256056Sgrehan	uint32_t fbu;
151256056Sgrehan	uint32_t is;
152256056Sgrehan	uint32_t ie;
153256056Sgrehan	uint32_t cmd;
154256056Sgrehan	uint32_t unused0;
155256056Sgrehan	uint32_t tfd;
156256056Sgrehan	uint32_t sig;
157256056Sgrehan	uint32_t ssts;
158256056Sgrehan	uint32_t sctl;
159256056Sgrehan	uint32_t serr;
160256056Sgrehan	uint32_t sact;
161256056Sgrehan	uint32_t ci;
162256056Sgrehan	uint32_t sntf;
163256056Sgrehan	uint32_t fbs;
164256056Sgrehan
165256056Sgrehan	/*
166256056Sgrehan	 * i/o request info
167256056Sgrehan	 */
168256056Sgrehan	struct ahci_ioreq *ioreq;
169256056Sgrehan	int ioqsz;
170256056Sgrehan	STAILQ_HEAD(ahci_fhead, ahci_ioreq) iofhd;
171273212Stychon	TAILQ_HEAD(ahci_bhead, ahci_ioreq) iobhd;
172256056Sgrehan};
173256056Sgrehan
174256056Sgrehanstruct ahci_cmd_hdr {
175256056Sgrehan	uint16_t flags;
176256056Sgrehan	uint16_t prdtl;
177256056Sgrehan	uint32_t prdbc;
178256056Sgrehan	uint64_t ctba;
179256056Sgrehan	uint32_t reserved[4];
180256056Sgrehan};
181256056Sgrehan
182256056Sgrehanstruct ahci_prdt_entry {
183256056Sgrehan	uint64_t dba;
184256056Sgrehan	uint32_t reserved;
185258614Sgrehan#define	DBCMASK		0x3fffff
186256056Sgrehan	uint32_t dbc;
187256056Sgrehan};
188256056Sgrehan
189256056Sgrehanstruct pci_ahci_softc {
190256056Sgrehan	struct pci_devinst *asc_pi;
191256056Sgrehan	pthread_mutex_t	mtx;
192256056Sgrehan	int ports;
193256056Sgrehan	uint32_t cap;
194256056Sgrehan	uint32_t ghc;
195256056Sgrehan	uint32_t is;
196256056Sgrehan	uint32_t pi;
197256056Sgrehan	uint32_t vs;
198256056Sgrehan	uint32_t ccc_ctl;
199256056Sgrehan	uint32_t ccc_pts;
200256056Sgrehan	uint32_t em_loc;
201256056Sgrehan	uint32_t em_ctl;
202256056Sgrehan	uint32_t cap2;
203256056Sgrehan	uint32_t bohc;
204265058Sgrehan	uint32_t lintr;
205256056Sgrehan	struct ahci_port port[MAX_PORTS];
206256056Sgrehan};
207256056Sgrehan#define	ahci_ctx(sc)	((sc)->asc_pi->pi_vmctx)
208256056Sgrehan
209282429Smavstatic void ahci_handle_port(struct ahci_port *p);
210282429Smav
211256056Sgrehanstatic inline void lba_to_msf(uint8_t *buf, int lba)
212256056Sgrehan{
213256056Sgrehan	lba += 150;
214256056Sgrehan	buf[0] = (lba / 75) / 60;
215256056Sgrehan	buf[1] = (lba / 75) % 60;
216256056Sgrehan	buf[2] = lba % 75;
217256056Sgrehan}
218256056Sgrehan
219256056Sgrehan/*
220256056Sgrehan * generate HBA intr depending on whether or not ports within
221256056Sgrehan * the controller have an interrupt pending.
222256056Sgrehan */
223256056Sgrehanstatic void
224256056Sgrehanahci_generate_intr(struct pci_ahci_softc *sc)
225256056Sgrehan{
226265058Sgrehan	struct pci_devinst *pi;
227256056Sgrehan	int i;
228256056Sgrehan
229265058Sgrehan	pi = sc->asc_pi;
230265058Sgrehan
231256056Sgrehan	for (i = 0; i < sc->ports; i++) {
232256056Sgrehan		struct ahci_port *pr;
233256056Sgrehan		pr = &sc->port[i];
234256056Sgrehan		if (pr->is & pr->ie)
235256056Sgrehan			sc->is |= (1 << i);
236256056Sgrehan	}
237256056Sgrehan
238256056Sgrehan	DPRINTF("%s %x\n", __func__, sc->is);
239256056Sgrehan
240265058Sgrehan	if (sc->is && (sc->ghc & AHCI_GHC_IE)) {
241265058Sgrehan		if (pci_msi_enabled(pi)) {
242265058Sgrehan			/*
243265058Sgrehan			 * Generate an MSI interrupt on every edge
244265058Sgrehan			 */
245265058Sgrehan			pci_generate_msi(pi, 0);
246265058Sgrehan		} else if (!sc->lintr) {
247265058Sgrehan			/*
248265058Sgrehan			 * Only generate a pin-based interrupt if one wasn't
249265058Sgrehan			 * in progress
250265058Sgrehan			 */
251265058Sgrehan			sc->lintr = 1;
252265058Sgrehan			pci_lintr_assert(pi);
253265058Sgrehan		}
254265058Sgrehan	} else if (sc->lintr) {
255265058Sgrehan		/*
256265058Sgrehan		 * No interrupts: deassert pin-based signal if it had
257265058Sgrehan		 * been asserted
258265058Sgrehan		 */
259265058Sgrehan		pci_lintr_deassert(pi);
260265058Sgrehan		sc->lintr = 0;
261265058Sgrehan	}
262256056Sgrehan}
263256056Sgrehan
264256056Sgrehanstatic void
265256056Sgrehanahci_write_fis(struct ahci_port *p, enum sata_fis_type ft, uint8_t *fis)
266256056Sgrehan{
267256056Sgrehan	int offset, len, irq;
268256056Sgrehan
269256164Sdim	if (p->rfis == NULL || !(p->cmd & AHCI_P_CMD_FRE))
270256056Sgrehan		return;
271256056Sgrehan
272256056Sgrehan	switch (ft) {
273256056Sgrehan	case FIS_TYPE_REGD2H:
274256056Sgrehan		offset = 0x40;
275256056Sgrehan		len = 20;
276282364Smav		irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_DHR : 0;
277256056Sgrehan		break;
278256056Sgrehan	case FIS_TYPE_SETDEVBITS:
279256056Sgrehan		offset = 0x58;
280256056Sgrehan		len = 8;
281282364Smav		irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_SDB : 0;
282256056Sgrehan		break;
283256056Sgrehan	case FIS_TYPE_PIOSETUP:
284256056Sgrehan		offset = 0x20;
285256056Sgrehan		len = 20;
286282364Smav		irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_PS : 0;
287256056Sgrehan		break;
288256056Sgrehan	default:
289256056Sgrehan		WPRINTF("unsupported fis type %d\n", ft);
290256056Sgrehan		return;
291256056Sgrehan	}
292282524Smav	if (fis[2] & ATA_S_ERROR) {
293282524Smav		p->waitforclear = 1;
294282364Smav		irq |= AHCI_P_IX_TFE;
295282524Smav	}
296256056Sgrehan	memcpy(p->rfis + offset, fis, len);
297256056Sgrehan	if (irq) {
298256056Sgrehan		p->is |= irq;
299256056Sgrehan		ahci_generate_intr(p->pr_sc);
300256056Sgrehan	}
301256056Sgrehan}
302256056Sgrehan
303256056Sgrehanstatic void
304261785Stychonahci_write_fis_piosetup(struct ahci_port *p)
305261785Stychon{
306261785Stychon	uint8_t fis[20];
307261785Stychon
308261785Stychon	memset(fis, 0, sizeof(fis));
309261785Stychon	fis[0] = FIS_TYPE_PIOSETUP;
310261785Stychon	ahci_write_fis(p, FIS_TYPE_PIOSETUP, fis);
311261785Stychon}
312261785Stychon
313261785Stychonstatic void
314279975Smavahci_write_fis_sdb(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t tfd)
315256056Sgrehan{
316256056Sgrehan	uint8_t fis[8];
317256056Sgrehan	uint8_t error;
318256056Sgrehan
319256056Sgrehan	error = (tfd >> 8) & 0xff;
320282364Smav	tfd &= 0x77;
321256056Sgrehan	memset(fis, 0, sizeof(fis));
322279975Smav	fis[0] = FIS_TYPE_SETDEVBITS;
323279975Smav	fis[1] = (1 << 6);
324282364Smav	fis[2] = tfd;
325279975Smav	fis[3] = error;
326279975Smav	if (fis[2] & ATA_S_ERROR) {
327279975Smav		p->err_cfis[0] = slot;
328282364Smav		p->err_cfis[2] = tfd;
329279975Smav		p->err_cfis[3] = error;
330279975Smav		memcpy(&p->err_cfis[4], cfis + 4, 16);
331279975Smav	} else {
332279975Smav		*(uint32_t *)(fis + 4) = (1 << slot);
333279975Smav		p->sact &= ~(1 << slot);
334279975Smav	}
335282364Smav	p->tfd &= ~0x77;
336282364Smav	p->tfd |= tfd;
337256056Sgrehan	ahci_write_fis(p, FIS_TYPE_SETDEVBITS, fis);
338256056Sgrehan}
339256056Sgrehan
340256056Sgrehanstatic void
341256056Sgrehanahci_write_fis_d2h(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t tfd)
342256056Sgrehan{
343256056Sgrehan	uint8_t fis[20];
344256056Sgrehan	uint8_t error;
345256056Sgrehan
346256056Sgrehan	error = (tfd >> 8) & 0xff;
347256056Sgrehan	memset(fis, 0, sizeof(fis));
348256056Sgrehan	fis[0] = FIS_TYPE_REGD2H;
349256056Sgrehan	fis[1] = (1 << 6);
350256056Sgrehan	fis[2] = tfd & 0xff;
351256056Sgrehan	fis[3] = error;
352256056Sgrehan	fis[4] = cfis[4];
353256056Sgrehan	fis[5] = cfis[5];
354256056Sgrehan	fis[6] = cfis[6];
355256056Sgrehan	fis[7] = cfis[7];
356256056Sgrehan	fis[8] = cfis[8];
357256056Sgrehan	fis[9] = cfis[9];
358256056Sgrehan	fis[10] = cfis[10];
359256056Sgrehan	fis[11] = cfis[11];
360256056Sgrehan	fis[12] = cfis[12];
361256056Sgrehan	fis[13] = cfis[13];
362279975Smav	if (fis[2] & ATA_S_ERROR) {
363279975Smav		p->err_cfis[0] = 0x80;
364279975Smav		p->err_cfis[2] = tfd & 0xff;
365279975Smav		p->err_cfis[3] = error;
366279975Smav		memcpy(&p->err_cfis[4], cfis + 4, 16);
367279975Smav	} else
368269317Stychon		p->ci &= ~(1 << slot);
369256056Sgrehan	p->tfd = tfd;
370256056Sgrehan	ahci_write_fis(p, FIS_TYPE_REGD2H, fis);
371256056Sgrehan}
372256056Sgrehan
373256056Sgrehanstatic void
374282364Smavahci_write_fis_d2h_ncq(struct ahci_port *p, int slot)
375282364Smav{
376282364Smav	uint8_t fis[20];
377282364Smav
378282364Smav	p->tfd = ATA_S_READY | ATA_S_DSC;
379282364Smav	memset(fis, 0, sizeof(fis));
380282364Smav	fis[0] = FIS_TYPE_REGD2H;
381282364Smav	fis[1] = 0;			/* No interrupt */
382282364Smav	fis[2] = p->tfd;		/* Status */
383282364Smav	fis[3] = 0;			/* No error */
384282364Smav	p->ci &= ~(1 << slot);
385282364Smav	ahci_write_fis(p, FIS_TYPE_REGD2H, fis);
386282364Smav}
387282364Smav
388282364Smavstatic void
389256056Sgrehanahci_write_reset_fis_d2h(struct ahci_port *p)
390256056Sgrehan{
391256056Sgrehan	uint8_t fis[20];
392256056Sgrehan
393256056Sgrehan	memset(fis, 0, sizeof(fis));
394256056Sgrehan	fis[0] = FIS_TYPE_REGD2H;
395256056Sgrehan	fis[3] = 1;
396256056Sgrehan	fis[4] = 1;
397256056Sgrehan	if (p->atapi) {
398256056Sgrehan		fis[5] = 0x14;
399256056Sgrehan		fis[6] = 0xeb;
400256056Sgrehan	}
401256056Sgrehan	fis[12] = 1;
402256056Sgrehan	ahci_write_fis(p, FIS_TYPE_REGD2H, fis);
403256056Sgrehan}
404256056Sgrehan
405256056Sgrehanstatic void
406273212Stychonahci_check_stopped(struct ahci_port *p)
407273212Stychon{
408273212Stychon	/*
409273212Stychon	 * If we are no longer processing the command list and nothing
410274045Stychon	 * is in-flight, clear the running bit, the current command
411274045Stychon	 * slot, the command issue and active bits.
412273212Stychon	 */
413273212Stychon	if (!(p->cmd & AHCI_P_CMD_ST)) {
414274045Stychon		if (p->pending == 0) {
415282429Smav			p->ccs = 0;
416273212Stychon			p->cmd &= ~(AHCI_P_CMD_CR | AHCI_P_CMD_CCS_MASK);
417274045Stychon			p->ci = 0;
418274045Stychon			p->sact = 0;
419282524Smav			p->waitforclear = 0;
420274045Stychon		}
421273212Stychon	}
422273212Stychon}
423273212Stychon
424273212Stychonstatic void
425273212Stychonahci_port_stop(struct ahci_port *p)
426273212Stychon{
427273212Stychon	struct ahci_ioreq *aior;
428273212Stychon	uint8_t *cfis;
429273212Stychon	int slot;
430273212Stychon	int error;
431273212Stychon
432273212Stychon	assert(pthread_mutex_isowned_np(&p->pr_sc->mtx));
433273212Stychon
434273212Stychon	TAILQ_FOREACH(aior, &p->iobhd, io_blist) {
435273212Stychon		/*
436273212Stychon		 * Try to cancel the outstanding blockif request.
437273212Stychon		 */
438273212Stychon		error = blockif_cancel(p->bctx, &aior->io_req);
439273212Stychon		if (error != 0)
440273212Stychon			continue;
441273212Stychon
442273212Stychon		slot = aior->slot;
443273212Stychon		cfis = aior->cfis;
444273212Stychon		if (cfis[2] == ATA_WRITE_FPDMA_QUEUED ||
445282344Smav		    cfis[2] == ATA_READ_FPDMA_QUEUED ||
446282344Smav		    cfis[2] == ATA_SEND_FPDMA_QUEUED)
447297589Spfg			p->sact &= ~(1 << slot);	/* NCQ */
448273212Stychon		else
449273212Stychon			p->ci &= ~(1 << slot);
450273212Stychon
451273212Stychon		/*
452273212Stychon		 * This command is now done.
453273212Stychon		 */
454273212Stychon		p->pending &= ~(1 << slot);
455273212Stychon
456273212Stychon		/*
457273212Stychon		 * Delete the blockif request from the busy list
458273212Stychon		 */
459273212Stychon		TAILQ_REMOVE(&p->iobhd, aior, io_blist);
460273212Stychon
461273212Stychon		/*
462273212Stychon		 * Move the blockif request back to the free list
463273212Stychon		 */
464273212Stychon		STAILQ_INSERT_TAIL(&p->iofhd, aior, io_flist);
465273212Stychon	}
466273212Stychon
467273212Stychon	ahci_check_stopped(p);
468273212Stychon}
469273212Stychon
470273212Stychonstatic void
471256056Sgrehanahci_port_reset(struct ahci_port *pr)
472256056Sgrehan{
473256056Sgrehan	pr->serr = 0;
474256056Sgrehan	pr->sact = 0;
475256056Sgrehan	pr->xfermode = ATA_UDMA6;
476256056Sgrehan	pr->mult_sectors = 128;
477256056Sgrehan
478256056Sgrehan	if (!pr->bctx) {
479256056Sgrehan		pr->ssts = ATA_SS_DET_NO_DEVICE;
480256056Sgrehan		pr->sig = 0xFFFFFFFF;
481256056Sgrehan		pr->tfd = 0x7F;
482256056Sgrehan		return;
483256056Sgrehan	}
484279965Smav	pr->ssts = ATA_SS_DET_PHY_ONLINE | ATA_SS_IPM_ACTIVE;
485279965Smav	if (pr->sctl & ATA_SC_SPD_MASK)
486279965Smav		pr->ssts |= (pr->sctl & ATA_SC_SPD_MASK);
487279965Smav	else
488279965Smav		pr->ssts |= ATA_SS_SPD_GEN3;
489256056Sgrehan	pr->tfd = (1 << 8) | ATA_S_DSC | ATA_S_DMA;
490256056Sgrehan	if (!pr->atapi) {
491256056Sgrehan		pr->sig = PxSIG_ATA;
492256056Sgrehan		pr->tfd |= ATA_S_READY;
493256056Sgrehan	} else
494256056Sgrehan		pr->sig = PxSIG_ATAPI;
495256056Sgrehan	ahci_write_reset_fis_d2h(pr);
496256056Sgrehan}
497256056Sgrehan
498256056Sgrehanstatic void
499256056Sgrehanahci_reset(struct pci_ahci_softc *sc)
500256056Sgrehan{
501256056Sgrehan	int i;
502256056Sgrehan
503256056Sgrehan	sc->ghc = AHCI_GHC_AE;
504256056Sgrehan	sc->is = 0;
505265058Sgrehan
506265058Sgrehan	if (sc->lintr) {
507265058Sgrehan		pci_lintr_deassert(sc->asc_pi);
508265058Sgrehan		sc->lintr = 0;
509265058Sgrehan	}
510265058Sgrehan
511256056Sgrehan	for (i = 0; i < sc->ports; i++) {
512256056Sgrehan		sc->port[i].ie = 0;
513256056Sgrehan		sc->port[i].is = 0;
514282345Smav		sc->port[i].cmd = (AHCI_P_CMD_SUD | AHCI_P_CMD_POD);
515282345Smav		if (sc->port[i].bctx)
516282345Smav			sc->port[i].cmd |= AHCI_P_CMD_CPS;
517279965Smav		sc->port[i].sctl = 0;
518256056Sgrehan		ahci_port_reset(&sc->port[i]);
519256056Sgrehan	}
520256056Sgrehan}
521256056Sgrehan
522256056Sgrehanstatic void
523256056Sgrehanata_string(uint8_t *dest, const char *src, int len)
524256056Sgrehan{
525256056Sgrehan	int i;
526256056Sgrehan
527256056Sgrehan	for (i = 0; i < len; i++) {
528256056Sgrehan		if (*src)
529256056Sgrehan			dest[i ^ 1] = *src++;
530256056Sgrehan		else
531256056Sgrehan			dest[i ^ 1] = ' ';
532256056Sgrehan	}
533256056Sgrehan}
534256056Sgrehan
535256056Sgrehanstatic void
536256056Sgrehanatapi_string(uint8_t *dest, const char *src, int len)
537256056Sgrehan{
538256056Sgrehan	int i;
539256056Sgrehan
540256056Sgrehan	for (i = 0; i < len; i++) {
541256056Sgrehan		if (*src)
542256056Sgrehan			dest[i] = *src++;
543256056Sgrehan		else
544256056Sgrehan			dest[i] = ' ';
545256056Sgrehan	}
546256056Sgrehan}
547256056Sgrehan
548281666Smav/*
549281666Smav * Build up the iovec based on the PRDT, 'done' and 'len'.
550281666Smav */
551256056Sgrehanstatic void
552281666Smavahci_build_iov(struct ahci_port *p, struct ahci_ioreq *aior,
553281666Smav    struct ahci_prdt_entry *prdt, uint16_t prdtl)
554256056Sgrehan{
555281666Smav	struct blockif_req *breq = &aior->io_req;
556281666Smav	int i, j, skip, todo, left, extra;
557281666Smav	uint32_t dbcsz;
558281666Smav
559281666Smav	/* Copy part of PRDT between 'done' and 'len' bytes into the iov. */
560281666Smav	skip = aior->done;
561281666Smav	left = aior->len - aior->done;
562281666Smav	todo = 0;
563281666Smav	for (i = 0, j = 0; i < prdtl && j < BLOCKIF_IOV_MAX && left > 0;
564281666Smav	    i++, prdt++) {
565281666Smav		dbcsz = (prdt->dbc & DBCMASK) + 1;
566281666Smav		/* Skip already done part of the PRDT */
567281666Smav		if (dbcsz <= skip) {
568281666Smav			skip -= dbcsz;
569281666Smav			continue;
570281666Smav		}
571281666Smav		dbcsz -= skip;
572281666Smav		if (dbcsz > left)
573281666Smav			dbcsz = left;
574281666Smav		breq->br_iov[j].iov_base = paddr_guest2host(ahci_ctx(p->pr_sc),
575281666Smav		    prdt->dba + skip, dbcsz);
576281666Smav		breq->br_iov[j].iov_len = dbcsz;
577281666Smav		todo += dbcsz;
578281666Smav		left -= dbcsz;
579281666Smav		skip = 0;
580281666Smav		j++;
581281666Smav	}
582281666Smav
583281666Smav	/* If we got limited by IOV length, round I/O down to sector size. */
584281666Smav	if (j == BLOCKIF_IOV_MAX) {
585281666Smav		extra = todo % blockif_sectsz(p->bctx);
586281666Smav		todo -= extra;
587281666Smav		assert(todo > 0);
588281666Smav		while (extra > 0) {
589281666Smav			if (breq->br_iov[j - 1].iov_len > extra) {
590281666Smav				breq->br_iov[j - 1].iov_len -= extra;
591281666Smav				break;
592281666Smav			}
593281666Smav			extra -= breq->br_iov[j - 1].iov_len;
594281666Smav			j--;
595281666Smav		}
596281666Smav	}
597281666Smav
598281666Smav	breq->br_iovcnt = j;
599281700Smav	breq->br_resid = todo;
600281666Smav	aior->done += todo;
601281666Smav	aior->more = (aior->done < aior->len && i < prdtl);
602281666Smav}
603281666Smav
604281666Smavstatic void
605281666Smavahci_handle_rw(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done)
606281666Smav{
607256056Sgrehan	struct ahci_ioreq *aior;
608256056Sgrehan	struct blockif_req *breq;
609256056Sgrehan	struct ahci_prdt_entry *prdt;
610256056Sgrehan	struct ahci_cmd_hdr *hdr;
611256056Sgrehan	uint64_t lba;
612256056Sgrehan	uint32_t len;
613282364Smav	int err, first, ncq, readop;
614256056Sgrehan
615256056Sgrehan	prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
616256164Sdim	hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
617256056Sgrehan	ncq = 0;
618256056Sgrehan	readop = 1;
619282364Smav	first = (done == 0);
620256056Sgrehan
621279960Smav	if (cfis[2] == ATA_WRITE || cfis[2] == ATA_WRITE48 ||
622279960Smav	    cfis[2] == ATA_WRITE_MUL || cfis[2] == ATA_WRITE_MUL48 ||
623279960Smav	    cfis[2] == ATA_WRITE_DMA || cfis[2] == ATA_WRITE_DMA48 ||
624279960Smav	    cfis[2] == ATA_WRITE_FPDMA_QUEUED)
625256056Sgrehan		readop = 0;
626256056Sgrehan
627256056Sgrehan	if (cfis[2] == ATA_WRITE_FPDMA_QUEUED ||
628279960Smav	    cfis[2] == ATA_READ_FPDMA_QUEUED) {
629256056Sgrehan		lba = ((uint64_t)cfis[10] << 40) |
630256056Sgrehan			((uint64_t)cfis[9] << 32) |
631256056Sgrehan			((uint64_t)cfis[8] << 24) |
632256056Sgrehan			((uint64_t)cfis[6] << 16) |
633256056Sgrehan			((uint64_t)cfis[5] << 8) |
634256056Sgrehan			cfis[4];
635256056Sgrehan		len = cfis[11] << 8 | cfis[3];
636256056Sgrehan		if (!len)
637256056Sgrehan			len = 65536;
638256056Sgrehan		ncq = 1;
639279960Smav	} else if (cfis[2] == ATA_READ48 || cfis[2] == ATA_WRITE48 ||
640279960Smav	    cfis[2] == ATA_READ_MUL48 || cfis[2] == ATA_WRITE_MUL48 ||
641279960Smav	    cfis[2] == ATA_READ_DMA48 || cfis[2] == ATA_WRITE_DMA48) {
642256056Sgrehan		lba = ((uint64_t)cfis[10] << 40) |
643256056Sgrehan			((uint64_t)cfis[9] << 32) |
644256056Sgrehan			((uint64_t)cfis[8] << 24) |
645256056Sgrehan			((uint64_t)cfis[6] << 16) |
646256056Sgrehan			((uint64_t)cfis[5] << 8) |
647256056Sgrehan			cfis[4];
648256056Sgrehan		len = cfis[13] << 8 | cfis[12];
649256056Sgrehan		if (!len)
650256056Sgrehan			len = 65536;
651256056Sgrehan	} else {
652256056Sgrehan		lba = ((cfis[7] & 0xf) << 24) | (cfis[6] << 16) |
653256056Sgrehan			(cfis[5] << 8) | cfis[4];
654256056Sgrehan		len = cfis[12];
655256056Sgrehan		if (!len)
656256056Sgrehan			len = 256;
657256056Sgrehan	}
658256056Sgrehan	lba *= blockif_sectsz(p->bctx);
659256056Sgrehan	len *= blockif_sectsz(p->bctx);
660256056Sgrehan
661281666Smav	/* Pull request off free list */
662256056Sgrehan	aior = STAILQ_FIRST(&p->iofhd);
663256056Sgrehan	assert(aior != NULL);
664273212Stychon	STAILQ_REMOVE_HEAD(&p->iofhd, io_flist);
665281666Smav
666256056Sgrehan	aior->cfis = cfis;
667256056Sgrehan	aior->slot = slot;
668256056Sgrehan	aior->len = len;
669256056Sgrehan	aior->done = done;
670256056Sgrehan	breq = &aior->io_req;
671256056Sgrehan	breq->br_offset = lba + done;
672281666Smav	ahci_build_iov(p, aior, prdt, hdr->prdtl);
673256056Sgrehan
674281666Smav	/* Mark this command in-flight. */
675273212Stychon	p->pending |= 1 << slot;
676273212Stychon
677281666Smav	/* Stuff request onto busy list. */
678273212Stychon	TAILQ_INSERT_HEAD(&p->iobhd, aior, io_blist);
679273212Stychon
680282364Smav	if (ncq && first)
681282364Smav		ahci_write_fis_d2h_ncq(p, slot);
682282364Smav
683256056Sgrehan	if (readop)
684256056Sgrehan		err = blockif_read(p->bctx, breq);
685256056Sgrehan	else
686256056Sgrehan		err = blockif_write(p->bctx, breq);
687256056Sgrehan	assert(err == 0);
688256056Sgrehan}
689256056Sgrehan
690256056Sgrehanstatic void
691256056Sgrehanahci_handle_flush(struct ahci_port *p, int slot, uint8_t *cfis)
692256056Sgrehan{
693256056Sgrehan	struct ahci_ioreq *aior;
694256056Sgrehan	struct blockif_req *breq;
695256056Sgrehan	int err;
696256056Sgrehan
697256056Sgrehan	/*
698256056Sgrehan	 * Pull request off free list
699256056Sgrehan	 */
700256056Sgrehan	aior = STAILQ_FIRST(&p->iofhd);
701256056Sgrehan	assert(aior != NULL);
702273212Stychon	STAILQ_REMOVE_HEAD(&p->iofhd, io_flist);
703256056Sgrehan	aior->cfis = cfis;
704256056Sgrehan	aior->slot = slot;
705256056Sgrehan	aior->len = 0;
706261000Stychon	aior->done = 0;
707281666Smav	aior->more = 0;
708256056Sgrehan	breq = &aior->io_req;
709256056Sgrehan
710273212Stychon	/*
711273212Stychon	 * Mark this command in-flight.
712273212Stychon	 */
713273212Stychon	p->pending |= 1 << slot;
714273212Stychon
715273212Stychon	/*
716273212Stychon	 * Stuff request onto busy list
717273212Stychon	 */
718273212Stychon	TAILQ_INSERT_HEAD(&p->iobhd, aior, io_blist);
719273212Stychon
720256056Sgrehan	err = blockif_flush(p->bctx, breq);
721256056Sgrehan	assert(err == 0);
722256056Sgrehan}
723256056Sgrehan
724256056Sgrehanstatic inline void
725279957Smavread_prdt(struct ahci_port *p, int slot, uint8_t *cfis,
726279957Smav		void *buf, int size)
727279957Smav{
728279957Smav	struct ahci_cmd_hdr *hdr;
729279957Smav	struct ahci_prdt_entry *prdt;
730279957Smav	void *to;
731279957Smav	int i, len;
732279957Smav
733279957Smav	hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
734279957Smav	len = size;
735279957Smav	to = buf;
736279957Smav	prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
737279957Smav	for (i = 0; i < hdr->prdtl && len; i++) {
738279957Smav		uint8_t *ptr;
739279957Smav		uint32_t dbcsz;
740279957Smav		int sublen;
741279957Smav
742279957Smav		dbcsz = (prdt->dbc & DBCMASK) + 1;
743279957Smav		ptr = paddr_guest2host(ahci_ctx(p->pr_sc), prdt->dba, dbcsz);
744298454Saraujo		sublen = MIN(len, dbcsz);
745279957Smav		memcpy(to, ptr, sublen);
746279957Smav		len -= sublen;
747279957Smav		to += sublen;
748279957Smav		prdt++;
749279957Smav	}
750279957Smav}
751279957Smav
752279957Smavstatic void
753279957Smavahci_handle_dsm_trim(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done)
754279957Smav{
755279957Smav	struct ahci_ioreq *aior;
756279957Smav	struct blockif_req *breq;
757279957Smav	uint8_t *entry;
758279957Smav	uint64_t elba;
759279957Smav	uint32_t len, elen;
760282364Smav	int err, first, ncq;
761279957Smav	uint8_t buf[512];
762279957Smav
763282364Smav	first = (done == 0);
764279976Smav	if (cfis[2] == ATA_DATA_SET_MANAGEMENT) {
765279976Smav		len = (uint16_t)cfis[13] << 8 | cfis[12];
766279976Smav		len *= 512;
767282364Smav		ncq = 0;
768279976Smav	} else { /* ATA_SEND_FPDMA_QUEUED */
769279976Smav		len = (uint16_t)cfis[11] << 8 | cfis[3];
770279976Smav		len *= 512;
771282364Smav		ncq = 1;
772279976Smav	}
773279957Smav	read_prdt(p, slot, cfis, buf, sizeof(buf));
774279957Smav
775279957Smavnext:
776279957Smav	entry = &buf[done];
777279957Smav	elba = ((uint64_t)entry[5] << 40) |
778279957Smav		((uint64_t)entry[4] << 32) |
779279957Smav		((uint64_t)entry[3] << 24) |
780279957Smav		((uint64_t)entry[2] << 16) |
781279957Smav		((uint64_t)entry[1] << 8) |
782279957Smav		entry[0];
783279957Smav	elen = (uint16_t)entry[7] << 8 | entry[6];
784279957Smav	done += 8;
785279957Smav	if (elen == 0) {
786279957Smav		if (done >= len) {
787303138Smav			if (ncq) {
788303138Smav				if (first)
789303138Smav					ahci_write_fis_d2h_ncq(p, slot);
790303138Smav				ahci_write_fis_sdb(p, slot, cfis,
791303138Smav				    ATA_S_READY | ATA_S_DSC);
792303138Smav			} else {
793303138Smav				ahci_write_fis_d2h(p, slot, cfis,
794303138Smav				    ATA_S_READY | ATA_S_DSC);
795303138Smav			}
796279957Smav			p->pending &= ~(1 << slot);
797279957Smav			ahci_check_stopped(p);
798282429Smav			if (!first)
799282429Smav				ahci_handle_port(p);
800279957Smav			return;
801279957Smav		}
802279957Smav		goto next;
803279957Smav	}
804279957Smav
805279957Smav	/*
806279957Smav	 * Pull request off free list
807279957Smav	 */
808279957Smav	aior = STAILQ_FIRST(&p->iofhd);
809279957Smav	assert(aior != NULL);
810279957Smav	STAILQ_REMOVE_HEAD(&p->iofhd, io_flist);
811279957Smav	aior->cfis = cfis;
812279957Smav	aior->slot = slot;
813279957Smav	aior->len = len;
814279957Smav	aior->done = done;
815281666Smav	aior->more = (len != done);
816279957Smav
817279957Smav	breq = &aior->io_req;
818279957Smav	breq->br_offset = elba * blockif_sectsz(p->bctx);
819281700Smav	breq->br_resid = elen * blockif_sectsz(p->bctx);
820279957Smav
821279957Smav	/*
822279957Smav	 * Mark this command in-flight.
823279957Smav	 */
824279957Smav	p->pending |= 1 << slot;
825279957Smav
826279957Smav	/*
827279957Smav	 * Stuff request onto busy list
828279957Smav	 */
829279957Smav	TAILQ_INSERT_HEAD(&p->iobhd, aior, io_blist);
830279957Smav
831282364Smav	if (ncq && first)
832282364Smav		ahci_write_fis_d2h_ncq(p, slot);
833282364Smav
834279957Smav	err = blockif_delete(p->bctx, breq);
835279957Smav	assert(err == 0);
836279957Smav}
837279957Smav
838279957Smavstatic inline void
839256056Sgrehanwrite_prdt(struct ahci_port *p, int slot, uint8_t *cfis,
840256056Sgrehan		void *buf, int size)
841256056Sgrehan{
842256056Sgrehan	struct ahci_cmd_hdr *hdr;
843256056Sgrehan	struct ahci_prdt_entry *prdt;
844256056Sgrehan	void *from;
845256056Sgrehan	int i, len;
846256056Sgrehan
847256164Sdim	hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
848256056Sgrehan	len = size;
849256056Sgrehan	from = buf;
850256056Sgrehan	prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
851256056Sgrehan	for (i = 0; i < hdr->prdtl && len; i++) {
852258614Sgrehan		uint8_t *ptr;
853258614Sgrehan		uint32_t dbcsz;
854264302Stychon		int sublen;
855258614Sgrehan
856258614Sgrehan		dbcsz = (prdt->dbc & DBCMASK) + 1;
857258614Sgrehan		ptr = paddr_guest2host(ahci_ctx(p->pr_sc), prdt->dba, dbcsz);
858298454Saraujo		sublen = MIN(len, dbcsz);
859264302Stychon		memcpy(ptr, from, sublen);
860264302Stychon		len -= sublen;
861264302Stychon		from += sublen;
862256056Sgrehan		prdt++;
863256056Sgrehan	}
864256056Sgrehan	hdr->prdbc = size - len;
865256056Sgrehan}
866256056Sgrehan
867256056Sgrehanstatic void
868279987Smavahci_checksum(uint8_t *buf, int size)
869279987Smav{
870279987Smav	int i;
871279987Smav	uint8_t sum = 0;
872279987Smav
873279987Smav	for (i = 0; i < size - 1; i++)
874279987Smav		sum += buf[i];
875279987Smav	buf[size - 1] = 0x100 - sum;
876279987Smav}
877279987Smav
878279987Smavstatic void
879279975Smavahci_handle_read_log(struct ahci_port *p, int slot, uint8_t *cfis)
880279975Smav{
881279975Smav	struct ahci_cmd_hdr *hdr;
882279975Smav	uint8_t buf[512];
883279975Smav
884279975Smav	hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
885279975Smav	if (p->atapi || hdr->prdtl == 0 || cfis[4] != 0x10 ||
886279975Smav	    cfis[5] != 0 || cfis[9] != 0 || cfis[12] != 1 || cfis[13] != 0) {
887279975Smav		ahci_write_fis_d2h(p, slot, cfis,
888279975Smav		    (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
889279975Smav		return;
890279975Smav	}
891279975Smav
892279975Smav	memset(buf, 0, sizeof(buf));
893279975Smav	memcpy(buf, p->err_cfis, sizeof(p->err_cfis));
894279987Smav	ahci_checksum(buf, sizeof(buf));
895279975Smav
896279975Smav	if (cfis[2] == ATA_READ_LOG_EXT)
897279975Smav		ahci_write_fis_piosetup(p);
898279975Smav	write_prdt(p, slot, cfis, (void *)buf, sizeof(buf));
899279975Smav	ahci_write_fis_d2h(p, slot, cfis, ATA_S_DSC | ATA_S_READY);
900279975Smav}
901279975Smav
902279975Smavstatic void
903256056Sgrehanhandle_identify(struct ahci_port *p, int slot, uint8_t *cfis)
904256056Sgrehan{
905256056Sgrehan	struct ahci_cmd_hdr *hdr;
906256056Sgrehan
907256164Sdim	hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
908256056Sgrehan	if (p->atapi || hdr->prdtl == 0) {
909279959Smav		ahci_write_fis_d2h(p, slot, cfis,
910279959Smav		    (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
911256056Sgrehan	} else {
912256056Sgrehan		uint16_t buf[256];
913256056Sgrehan		uint64_t sectors;
914280017Smav		int sectsz, psectsz, psectoff, candelete, ro;
915268639Sgrehan		uint16_t cyl;
916268639Sgrehan		uint8_t sech, heads;
917256056Sgrehan
918280017Smav		ro = blockif_is_ro(p->bctx);
919279957Smav		candelete = blockif_candelete(p->bctx);
920279654Smav		sectsz = blockif_sectsz(p->bctx);
921279654Smav		sectors = blockif_size(p->bctx) / sectsz;
922268639Sgrehan		blockif_chs(p->bctx, &cyl, &heads, &sech);
923279654Smav		blockif_psectsz(p->bctx, &psectsz, &psectoff);
924256056Sgrehan		memset(buf, 0, sizeof(buf));
925256056Sgrehan		buf[0] = 0x0040;
926268639Sgrehan		buf[1] = cyl;
927268639Sgrehan		buf[3] = heads;
928268639Sgrehan		buf[6] = sech;
929280040Smav		ata_string((uint8_t *)(buf+10), p->ident, 20);
930256056Sgrehan		ata_string((uint8_t *)(buf+23), "001", 8);
931256056Sgrehan		ata_string((uint8_t *)(buf+27), "BHYVE SATA DISK", 40);
932256056Sgrehan		buf[47] = (0x8000 | 128);
933288826Sgrehan		buf[48] = 0;
934256056Sgrehan		buf[49] = (1 << 8 | 1 << 9 | 1 << 11);
935256056Sgrehan		buf[50] = (1 << 14);
936256056Sgrehan		buf[53] = (1 << 1 | 1 << 2);
937256056Sgrehan		if (p->mult_sectors)
938256056Sgrehan			buf[59] = (0x100 | p->mult_sectors);
939279965Smav		if (sectors <= 0x0fffffff) {
940279965Smav			buf[60] = sectors;
941279965Smav			buf[61] = (sectors >> 16);
942279965Smav		} else {
943279965Smav			buf[60] = 0xffff;
944279965Smav			buf[61] = 0x0fff;
945279965Smav		}
946256056Sgrehan		buf[63] = 0x7;
947256056Sgrehan		if (p->xfermode & ATA_WDMA0)
948256056Sgrehan			buf[63] |= (1 << ((p->xfermode & 7) + 8));
949256056Sgrehan		buf[64] = 0x3;
950279965Smav		buf[65] = 120;
951279965Smav		buf[66] = 120;
952279965Smav		buf[67] = 120;
953279965Smav		buf[68] = 120;
954279957Smav		buf[69] = 0;
955256056Sgrehan		buf[75] = 31;
956279965Smav		buf[76] = (ATA_SATA_GEN1 | ATA_SATA_GEN2 | ATA_SATA_GEN3 |
957279965Smav			   ATA_SUPPORT_NCQ);
958279976Smav		buf[77] = (ATA_SUPPORT_RCVSND_FPDMA_QUEUED |
959279976Smav			   (p->ssts & ATA_SS_SPD_MASK) >> 3);
960279979Smav		buf[80] = 0x3f0;
961256056Sgrehan		buf[81] = 0x28;
962279965Smav		buf[82] = (ATA_SUPPORT_POWERMGT | ATA_SUPPORT_WRITECACHE|
963279965Smav			   ATA_SUPPORT_LOOKAHEAD | ATA_SUPPORT_NOP);
964279965Smav		buf[83] = (ATA_SUPPORT_ADDRESS48 | ATA_SUPPORT_FLUSHCACHE |
965279965Smav			   ATA_SUPPORT_FLUSHCACHE48 | 1 << 14);
966256056Sgrehan		buf[84] = (1 << 14);
967279965Smav		buf[85] = (ATA_SUPPORT_POWERMGT | ATA_SUPPORT_WRITECACHE|
968279965Smav			   ATA_SUPPORT_LOOKAHEAD | ATA_SUPPORT_NOP);
969279965Smav		buf[86] = (ATA_SUPPORT_ADDRESS48 | ATA_SUPPORT_FLUSHCACHE |
970279975Smav			   ATA_SUPPORT_FLUSHCACHE48 | 1 << 15);
971256056Sgrehan		buf[87] = (1 << 14);
972256056Sgrehan		buf[88] = 0x7f;
973256056Sgrehan		if (p->xfermode & ATA_UDMA0)
974256056Sgrehan			buf[88] |= (1 << ((p->xfermode & 7) + 8));
975256056Sgrehan		buf[100] = sectors;
976256056Sgrehan		buf[101] = (sectors >> 16);
977256056Sgrehan		buf[102] = (sectors >> 32);
978256056Sgrehan		buf[103] = (sectors >> 48);
979280017Smav		if (candelete && !ro) {
980279957Smav			buf[69] |= ATA_SUPPORT_RZAT | ATA_SUPPORT_DRAT;
981279957Smav			buf[105] = 1;
982279957Smav			buf[169] = ATA_SUPPORT_DSM_TRIM;
983279957Smav		}
984279654Smav		buf[106] = 0x4000;
985279654Smav		buf[209] = 0x4000;
986279654Smav		if (psectsz > sectsz) {
987279654Smav			buf[106] |= 0x2000;
988279654Smav			buf[106] |= ffsl(psectsz / sectsz) - 1;
989279654Smav			buf[209] |= (psectoff / sectsz);
990279654Smav		}
991279654Smav		if (sectsz > 512) {
992279654Smav			buf[106] |= 0x1000;
993279654Smav			buf[117] = sectsz / 2;
994279654Smav			buf[118] = ((sectsz / 2) >> 16);
995279654Smav		}
996279975Smav		buf[119] = (ATA_SUPPORT_RWLOGDMAEXT | 1 << 14);
997279975Smav		buf[120] = (ATA_SUPPORT_RWLOGDMAEXT | 1 << 14);
998279965Smav		buf[222] = 0x1020;
999279987Smav		buf[255] = 0x00a5;
1000279987Smav		ahci_checksum((uint8_t *)buf, sizeof(buf));
1001261785Stychon		ahci_write_fis_piosetup(p);
1002256056Sgrehan		write_prdt(p, slot, cfis, (void *)buf, sizeof(buf));
1003279959Smav		ahci_write_fis_d2h(p, slot, cfis, ATA_S_DSC | ATA_S_READY);
1004256056Sgrehan	}
1005256056Sgrehan}
1006256056Sgrehan
1007256056Sgrehanstatic void
1008256056Sgrehanhandle_atapi_identify(struct ahci_port *p, int slot, uint8_t *cfis)
1009256056Sgrehan{
1010256056Sgrehan	if (!p->atapi) {
1011279959Smav		ahci_write_fis_d2h(p, slot, cfis,
1012279959Smav		    (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
1013256056Sgrehan	} else {
1014256056Sgrehan		uint16_t buf[256];
1015256056Sgrehan
1016256056Sgrehan		memset(buf, 0, sizeof(buf));
1017256056Sgrehan		buf[0] = (2 << 14 | 5 << 8 | 1 << 7 | 2 << 5);
1018280040Smav		ata_string((uint8_t *)(buf+10), p->ident, 20);
1019256056Sgrehan		ata_string((uint8_t *)(buf+23), "001", 8);
1020256056Sgrehan		ata_string((uint8_t *)(buf+27), "BHYVE SATA DVD ROM", 40);
1021256056Sgrehan		buf[49] = (1 << 9 | 1 << 8);
1022256056Sgrehan		buf[50] = (1 << 14 | 1);
1023256056Sgrehan		buf[53] = (1 << 2 | 1 << 1);
1024256056Sgrehan		buf[62] = 0x3f;
1025256056Sgrehan		buf[63] = 7;
1026279979Smav		if (p->xfermode & ATA_WDMA0)
1027279979Smav			buf[63] |= (1 << ((p->xfermode & 7) + 8));
1028256056Sgrehan		buf[64] = 3;
1029279979Smav		buf[65] = 120;
1030279979Smav		buf[66] = 120;
1031279979Smav		buf[67] = 120;
1032279979Smav		buf[68] = 120;
1033279979Smav		buf[76] = (ATA_SATA_GEN1 | ATA_SATA_GEN2 | ATA_SATA_GEN3);
1034279979Smav		buf[77] = ((p->ssts & ATA_SS_SPD_MASK) >> 3);
1035256056Sgrehan		buf[78] = (1 << 5);
1036279979Smav		buf[80] = 0x3f0;
1037279979Smav		buf[82] = (ATA_SUPPORT_POWERMGT | ATA_SUPPORT_PACKET |
1038279979Smav			   ATA_SUPPORT_RESET | ATA_SUPPORT_NOP);
1039256056Sgrehan		buf[83] = (1 << 14);
1040256056Sgrehan		buf[84] = (1 << 14);
1041279979Smav		buf[85] = (ATA_SUPPORT_POWERMGT | ATA_SUPPORT_PACKET |
1042279979Smav			   ATA_SUPPORT_RESET | ATA_SUPPORT_NOP);
1043256056Sgrehan		buf[87] = (1 << 14);
1044279979Smav		buf[88] = 0x7f;
1045279979Smav		if (p->xfermode & ATA_UDMA0)
1046279979Smav			buf[88] |= (1 << ((p->xfermode & 7) + 8));
1047279979Smav		buf[222] = 0x1020;
1048279987Smav		buf[255] = 0x00a5;
1049279987Smav		ahci_checksum((uint8_t *)buf, sizeof(buf));
1050261785Stychon		ahci_write_fis_piosetup(p);
1051256056Sgrehan		write_prdt(p, slot, cfis, (void *)buf, sizeof(buf));
1052279959Smav		ahci_write_fis_d2h(p, slot, cfis, ATA_S_DSC | ATA_S_READY);
1053256056Sgrehan	}
1054256056Sgrehan}
1055256056Sgrehan
1056256056Sgrehanstatic void
1057256056Sgrehanatapi_inquiry(struct ahci_port *p, int slot, uint8_t *cfis)
1058256056Sgrehan{
1059256056Sgrehan	uint8_t buf[36];
1060256056Sgrehan	uint8_t *acmd;
1061256056Sgrehan	int len;
1062279979Smav	uint32_t tfd;
1063256056Sgrehan
1064256056Sgrehan	acmd = cfis + 0x40;
1065256056Sgrehan
1066279979Smav	if (acmd[1] & 1) {		/* VPD */
1067279979Smav		if (acmd[2] == 0) {	/* Supported VPD pages */
1068279979Smav			buf[0] = 0x05;
1069279979Smav			buf[1] = 0;
1070279979Smav			buf[2] = 0;
1071279979Smav			buf[3] = 1;
1072279979Smav			buf[4] = 0;
1073279979Smav			len = 4 + buf[3];
1074279979Smav		} else {
1075279979Smav			p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
1076279979Smav			p->asc = 0x24;
1077279979Smav			tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR;
1078279979Smav			cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1079279979Smav			ahci_write_fis_d2h(p, slot, cfis, tfd);
1080279979Smav			return;
1081279979Smav		}
1082279979Smav	} else {
1083279979Smav		buf[0] = 0x05;
1084279979Smav		buf[1] = 0x80;
1085279979Smav		buf[2] = 0x00;
1086279979Smav		buf[3] = 0x21;
1087279979Smav		buf[4] = 31;
1088279979Smav		buf[5] = 0;
1089279979Smav		buf[6] = 0;
1090279979Smav		buf[7] = 0;
1091279979Smav		atapi_string(buf + 8, "BHYVE", 8);
1092279979Smav		atapi_string(buf + 16, "BHYVE DVD-ROM", 16);
1093279979Smav		atapi_string(buf + 32, "001", 4);
1094279979Smav		len = sizeof(buf);
1095279979Smav	}
1096256056Sgrehan
1097256056Sgrehan	if (len > acmd[4])
1098256056Sgrehan		len = acmd[4];
1099256056Sgrehan	cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1100256056Sgrehan	write_prdt(p, slot, cfis, buf, len);
1101256056Sgrehan	ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1102256056Sgrehan}
1103256056Sgrehan
1104256056Sgrehanstatic void
1105256056Sgrehanatapi_read_capacity(struct ahci_port *p, int slot, uint8_t *cfis)
1106256056Sgrehan{
1107256056Sgrehan	uint8_t buf[8];
1108256056Sgrehan	uint64_t sectors;
1109256056Sgrehan
1110256926Sgrehan	sectors = blockif_size(p->bctx) / 2048;
1111256056Sgrehan	be32enc(buf, sectors - 1);
1112256056Sgrehan	be32enc(buf + 4, 2048);
1113256056Sgrehan	cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1114256056Sgrehan	write_prdt(p, slot, cfis, buf, sizeof(buf));
1115256056Sgrehan	ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1116256056Sgrehan}
1117256056Sgrehan
1118256056Sgrehanstatic void
1119256056Sgrehanatapi_read_toc(struct ahci_port *p, int slot, uint8_t *cfis)
1120256056Sgrehan{
1121256056Sgrehan	uint8_t *acmd;
1122256056Sgrehan	uint8_t format;
1123256056Sgrehan	int len;
1124256056Sgrehan
1125256056Sgrehan	acmd = cfis + 0x40;
1126256056Sgrehan
1127256056Sgrehan	len = be16dec(acmd + 7);
1128256056Sgrehan	format = acmd[9] >> 6;
1129256056Sgrehan	switch (format) {
1130256056Sgrehan	case 0:
1131256056Sgrehan	{
1132256056Sgrehan		int msf, size;
1133256056Sgrehan		uint64_t sectors;
1134256056Sgrehan		uint8_t start_track, buf[20], *bp;
1135256056Sgrehan
1136256056Sgrehan		msf = (acmd[1] >> 1) & 1;
1137256056Sgrehan		start_track = acmd[6];
1138256056Sgrehan		if (start_track > 1 && start_track != 0xaa) {
1139256056Sgrehan			uint32_t tfd;
1140256056Sgrehan			p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
1141256056Sgrehan			p->asc = 0x24;
1142256056Sgrehan			tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR;
1143256056Sgrehan			cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1144256056Sgrehan			ahci_write_fis_d2h(p, slot, cfis, tfd);
1145256056Sgrehan			return;
1146256056Sgrehan		}
1147256056Sgrehan		bp = buf + 2;
1148256056Sgrehan		*bp++ = 1;
1149256056Sgrehan		*bp++ = 1;
1150256056Sgrehan		if (start_track <= 1) {
1151256056Sgrehan			*bp++ = 0;
1152256056Sgrehan			*bp++ = 0x14;
1153256056Sgrehan			*bp++ = 1;
1154256056Sgrehan			*bp++ = 0;
1155256056Sgrehan			if (msf) {
1156256056Sgrehan				*bp++ = 0;
1157256056Sgrehan				lba_to_msf(bp, 0);
1158256056Sgrehan				bp += 3;
1159256056Sgrehan			} else {
1160256056Sgrehan				*bp++ = 0;
1161256056Sgrehan				*bp++ = 0;
1162256056Sgrehan				*bp++ = 0;
1163256056Sgrehan				*bp++ = 0;
1164256056Sgrehan			}
1165256056Sgrehan		}
1166256056Sgrehan		*bp++ = 0;
1167256056Sgrehan		*bp++ = 0x14;
1168256056Sgrehan		*bp++ = 0xaa;
1169256056Sgrehan		*bp++ = 0;
1170256056Sgrehan		sectors = blockif_size(p->bctx) / blockif_sectsz(p->bctx);
1171256056Sgrehan		sectors >>= 2;
1172256056Sgrehan		if (msf) {
1173256056Sgrehan			*bp++ = 0;
1174256056Sgrehan			lba_to_msf(bp, sectors);
1175256056Sgrehan			bp += 3;
1176256056Sgrehan		} else {
1177256056Sgrehan			be32enc(bp, sectors);
1178256056Sgrehan			bp += 4;
1179256056Sgrehan		}
1180256056Sgrehan		size = bp - buf;
1181256056Sgrehan		be16enc(buf, size - 2);
1182256056Sgrehan		if (len > size)
1183256056Sgrehan			len = size;
1184256056Sgrehan		write_prdt(p, slot, cfis, buf, len);
1185256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1186256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1187256056Sgrehan		break;
1188256056Sgrehan	}
1189256056Sgrehan	case 1:
1190256056Sgrehan	{
1191256056Sgrehan		uint8_t buf[12];
1192256056Sgrehan
1193256056Sgrehan		memset(buf, 0, sizeof(buf));
1194256056Sgrehan		buf[1] = 0xa;
1195256056Sgrehan		buf[2] = 0x1;
1196256056Sgrehan		buf[3] = 0x1;
1197256056Sgrehan		if (len > sizeof(buf))
1198256056Sgrehan			len = sizeof(buf);
1199256056Sgrehan		write_prdt(p, slot, cfis, buf, len);
1200256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1201256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1202256056Sgrehan		break;
1203256056Sgrehan	}
1204256056Sgrehan	case 2:
1205256056Sgrehan	{
1206256056Sgrehan		int msf, size;
1207256056Sgrehan		uint64_t sectors;
1208294774Saraujo		uint8_t *bp, buf[50];
1209256056Sgrehan
1210256056Sgrehan		msf = (acmd[1] >> 1) & 1;
1211256056Sgrehan		bp = buf + 2;
1212256056Sgrehan		*bp++ = 1;
1213256056Sgrehan		*bp++ = 1;
1214256056Sgrehan
1215256056Sgrehan		*bp++ = 1;
1216256056Sgrehan		*bp++ = 0x14;
1217256056Sgrehan		*bp++ = 0;
1218256056Sgrehan		*bp++ = 0xa0;
1219256056Sgrehan		*bp++ = 0;
1220256056Sgrehan		*bp++ = 0;
1221256056Sgrehan		*bp++ = 0;
1222256056Sgrehan		*bp++ = 0;
1223256056Sgrehan		*bp++ = 1;
1224256056Sgrehan		*bp++ = 0;
1225256056Sgrehan		*bp++ = 0;
1226256056Sgrehan
1227256056Sgrehan		*bp++ = 1;
1228256056Sgrehan		*bp++ = 0x14;
1229256056Sgrehan		*bp++ = 0;
1230256056Sgrehan		*bp++ = 0xa1;
1231256056Sgrehan		*bp++ = 0;
1232256056Sgrehan		*bp++ = 0;
1233256056Sgrehan		*bp++ = 0;
1234256056Sgrehan		*bp++ = 0;
1235256056Sgrehan		*bp++ = 1;
1236256056Sgrehan		*bp++ = 0;
1237256056Sgrehan		*bp++ = 0;
1238256056Sgrehan
1239256056Sgrehan		*bp++ = 1;
1240256056Sgrehan		*bp++ = 0x14;
1241256056Sgrehan		*bp++ = 0;
1242256056Sgrehan		*bp++ = 0xa2;
1243256056Sgrehan		*bp++ = 0;
1244256056Sgrehan		*bp++ = 0;
1245256056Sgrehan		*bp++ = 0;
1246256056Sgrehan		sectors = blockif_size(p->bctx) / blockif_sectsz(p->bctx);
1247256056Sgrehan		sectors >>= 2;
1248256056Sgrehan		if (msf) {
1249256056Sgrehan			*bp++ = 0;
1250256056Sgrehan			lba_to_msf(bp, sectors);
1251256056Sgrehan			bp += 3;
1252256056Sgrehan		} else {
1253256056Sgrehan			be32enc(bp, sectors);
1254256056Sgrehan			bp += 4;
1255256056Sgrehan		}
1256256056Sgrehan
1257256056Sgrehan		*bp++ = 1;
1258256056Sgrehan		*bp++ = 0x14;
1259256056Sgrehan		*bp++ = 0;
1260256056Sgrehan		*bp++ = 1;
1261256056Sgrehan		*bp++ = 0;
1262256056Sgrehan		*bp++ = 0;
1263256056Sgrehan		*bp++ = 0;
1264256056Sgrehan		if (msf) {
1265256056Sgrehan			*bp++ = 0;
1266256056Sgrehan			lba_to_msf(bp, 0);
1267256056Sgrehan			bp += 3;
1268256056Sgrehan		} else {
1269256056Sgrehan			*bp++ = 0;
1270256056Sgrehan			*bp++ = 0;
1271256056Sgrehan			*bp++ = 0;
1272256056Sgrehan			*bp++ = 0;
1273256056Sgrehan		}
1274256056Sgrehan
1275256056Sgrehan		size = bp - buf;
1276256056Sgrehan		be16enc(buf, size - 2);
1277256056Sgrehan		if (len > size)
1278256056Sgrehan			len = size;
1279256056Sgrehan		write_prdt(p, slot, cfis, buf, len);
1280256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1281256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1282256056Sgrehan		break;
1283256056Sgrehan	}
1284256056Sgrehan	default:
1285256056Sgrehan	{
1286256056Sgrehan		uint32_t tfd;
1287256056Sgrehan
1288256056Sgrehan		p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
1289256056Sgrehan		p->asc = 0x24;
1290256056Sgrehan		tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR;
1291256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1292256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, tfd);
1293256056Sgrehan		break;
1294256056Sgrehan	}
1295256056Sgrehan	}
1296256056Sgrehan}
1297256056Sgrehan
1298256056Sgrehanstatic void
1299279979Smavatapi_report_luns(struct ahci_port *p, int slot, uint8_t *cfis)
1300279979Smav{
1301279979Smav	uint8_t buf[16];
1302279979Smav
1303279979Smav	memset(buf, 0, sizeof(buf));
1304279979Smav	buf[3] = 8;
1305279979Smav
1306279979Smav	cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1307279979Smav	write_prdt(p, slot, cfis, buf, sizeof(buf));
1308279979Smav	ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1309279979Smav}
1310279979Smav
1311279979Smavstatic void
1312281666Smavatapi_read(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done)
1313256056Sgrehan{
1314256056Sgrehan	struct ahci_ioreq *aior;
1315256056Sgrehan	struct ahci_cmd_hdr *hdr;
1316256056Sgrehan	struct ahci_prdt_entry *prdt;
1317256056Sgrehan	struct blockif_req *breq;
1318256056Sgrehan	uint8_t *acmd;
1319256056Sgrehan	uint64_t lba;
1320256056Sgrehan	uint32_t len;
1321281666Smav	int err;
1322256056Sgrehan
1323256056Sgrehan	acmd = cfis + 0x40;
1324256164Sdim	hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
1325256056Sgrehan	prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
1326256056Sgrehan
1327256056Sgrehan	lba = be32dec(acmd + 2);
1328256056Sgrehan	if (acmd[0] == READ_10)
1329256056Sgrehan		len = be16dec(acmd + 7);
1330256056Sgrehan	else
1331256056Sgrehan		len = be32dec(acmd + 6);
1332256056Sgrehan	if (len == 0) {
1333256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1334256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1335256056Sgrehan	}
1336256056Sgrehan	lba *= 2048;
1337256056Sgrehan	len *= 2048;
1338256056Sgrehan
1339256056Sgrehan	/*
1340256056Sgrehan	 * Pull request off free list
1341256056Sgrehan	 */
1342256056Sgrehan	aior = STAILQ_FIRST(&p->iofhd);
1343256056Sgrehan	assert(aior != NULL);
1344273212Stychon	STAILQ_REMOVE_HEAD(&p->iofhd, io_flist);
1345256056Sgrehan	aior->cfis = cfis;
1346256056Sgrehan	aior->slot = slot;
1347256056Sgrehan	aior->len = len;
1348256056Sgrehan	aior->done = done;
1349256056Sgrehan	breq = &aior->io_req;
1350256056Sgrehan	breq->br_offset = lba + done;
1351281666Smav	ahci_build_iov(p, aior, prdt, hdr->prdtl);
1352256056Sgrehan
1353281666Smav	/* Mark this command in-flight. */
1354273212Stychon	p->pending |= 1 << slot;
1355273212Stychon
1356281666Smav	/* Stuff request onto busy list. */
1357273212Stychon	TAILQ_INSERT_HEAD(&p->iobhd, aior, io_blist);
1358273212Stychon
1359256056Sgrehan	err = blockif_read(p->bctx, breq);
1360256056Sgrehan	assert(err == 0);
1361256056Sgrehan}
1362256056Sgrehan
1363256056Sgrehanstatic void
1364256056Sgrehanatapi_request_sense(struct ahci_port *p, int slot, uint8_t *cfis)
1365256056Sgrehan{
1366256056Sgrehan	uint8_t buf[64];
1367256056Sgrehan	uint8_t *acmd;
1368256056Sgrehan	int len;
1369256056Sgrehan
1370256056Sgrehan	acmd = cfis + 0x40;
1371256056Sgrehan	len = acmd[4];
1372256056Sgrehan	if (len > sizeof(buf))
1373256056Sgrehan		len = sizeof(buf);
1374256056Sgrehan	memset(buf, 0, len);
1375256056Sgrehan	buf[0] = 0x70 | (1 << 7);
1376256056Sgrehan	buf[2] = p->sense_key;
1377256056Sgrehan	buf[7] = 10;
1378256056Sgrehan	buf[12] = p->asc;
1379256056Sgrehan	write_prdt(p, slot, cfis, buf, len);
1380256056Sgrehan	cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1381256056Sgrehan	ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1382256056Sgrehan}
1383256056Sgrehan
1384256056Sgrehanstatic void
1385256056Sgrehanatapi_start_stop_unit(struct ahci_port *p, int slot, uint8_t *cfis)
1386256056Sgrehan{
1387256056Sgrehan	uint8_t *acmd = cfis + 0x40;
1388256056Sgrehan	uint32_t tfd;
1389256056Sgrehan
1390256056Sgrehan	switch (acmd[4] & 3) {
1391256056Sgrehan	case 0:
1392256056Sgrehan	case 1:
1393256056Sgrehan	case 3:
1394256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1395256056Sgrehan		tfd = ATA_S_READY | ATA_S_DSC;
1396256056Sgrehan		break;
1397256056Sgrehan	case 2:
1398256056Sgrehan		/* TODO eject media */
1399256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1400256056Sgrehan		p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
1401256056Sgrehan		p->asc = 0x53;
1402256056Sgrehan		tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR;
1403256056Sgrehan		break;
1404256056Sgrehan	}
1405256056Sgrehan	ahci_write_fis_d2h(p, slot, cfis, tfd);
1406256056Sgrehan}
1407256056Sgrehan
1408256056Sgrehanstatic void
1409256056Sgrehanatapi_mode_sense(struct ahci_port *p, int slot, uint8_t *cfis)
1410256056Sgrehan{
1411256056Sgrehan	uint8_t *acmd;
1412256056Sgrehan	uint32_t tfd;
1413256056Sgrehan	uint8_t pc, code;
1414256056Sgrehan	int len;
1415256056Sgrehan
1416256056Sgrehan	acmd = cfis + 0x40;
1417256056Sgrehan	len = be16dec(acmd + 7);
1418256056Sgrehan	pc = acmd[2] >> 6;
1419256056Sgrehan	code = acmd[2] & 0x3f;
1420256056Sgrehan
1421256056Sgrehan	switch (pc) {
1422256056Sgrehan	case 0:
1423256056Sgrehan		switch (code) {
1424256056Sgrehan		case MODEPAGE_RW_ERROR_RECOVERY:
1425256056Sgrehan		{
1426256056Sgrehan			uint8_t buf[16];
1427256056Sgrehan
1428256056Sgrehan			if (len > sizeof(buf))
1429256056Sgrehan				len = sizeof(buf);
1430256056Sgrehan
1431256056Sgrehan			memset(buf, 0, sizeof(buf));
1432256056Sgrehan			be16enc(buf, 16 - 2);
1433256056Sgrehan			buf[2] = 0x70;
1434256056Sgrehan			buf[8] = 0x01;
1435256056Sgrehan			buf[9] = 16 - 10;
1436256056Sgrehan			buf[11] = 0x05;
1437256056Sgrehan			write_prdt(p, slot, cfis, buf, len);
1438256056Sgrehan			tfd = ATA_S_READY | ATA_S_DSC;
1439256056Sgrehan			break;
1440256056Sgrehan		}
1441256056Sgrehan		case MODEPAGE_CD_CAPABILITIES:
1442256056Sgrehan		{
1443256056Sgrehan			uint8_t buf[30];
1444256056Sgrehan
1445256056Sgrehan			if (len > sizeof(buf))
1446256056Sgrehan				len = sizeof(buf);
1447256056Sgrehan
1448256056Sgrehan			memset(buf, 0, sizeof(buf));
1449256056Sgrehan			be16enc(buf, 30 - 2);
1450256056Sgrehan			buf[2] = 0x70;
1451256056Sgrehan			buf[8] = 0x2A;
1452256056Sgrehan			buf[9] = 30 - 10;
1453256056Sgrehan			buf[10] = 0x08;
1454256056Sgrehan			buf[12] = 0x71;
1455256056Sgrehan			be16enc(&buf[18], 2);
1456256056Sgrehan			be16enc(&buf[20], 512);
1457256056Sgrehan			write_prdt(p, slot, cfis, buf, len);
1458256056Sgrehan			tfd = ATA_S_READY | ATA_S_DSC;
1459256056Sgrehan			break;
1460256056Sgrehan		}
1461256056Sgrehan		default:
1462256056Sgrehan			goto error;
1463256056Sgrehan			break;
1464256056Sgrehan		}
1465256056Sgrehan		break;
1466256056Sgrehan	case 3:
1467256056Sgrehan		p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
1468256056Sgrehan		p->asc = 0x39;
1469256056Sgrehan		tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR;
1470256056Sgrehan		break;
1471256056Sgrehanerror:
1472256056Sgrehan	case 1:
1473256056Sgrehan	case 2:
1474256056Sgrehan		p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
1475256056Sgrehan		p->asc = 0x24;
1476256056Sgrehan		tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR;
1477256056Sgrehan		break;
1478256056Sgrehan	}
1479256056Sgrehan	cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1480256056Sgrehan	ahci_write_fis_d2h(p, slot, cfis, tfd);
1481256056Sgrehan}
1482256056Sgrehan
1483256056Sgrehanstatic void
1484256056Sgrehanatapi_get_event_status_notification(struct ahci_port *p, int slot,
1485256056Sgrehan    uint8_t *cfis)
1486256056Sgrehan{
1487256056Sgrehan	uint8_t *acmd;
1488256056Sgrehan	uint32_t tfd;
1489256056Sgrehan
1490256056Sgrehan	acmd = cfis + 0x40;
1491256056Sgrehan
1492256056Sgrehan	/* we don't support asynchronous operation */
1493256056Sgrehan	if (!(acmd[1] & 1)) {
1494256056Sgrehan		p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
1495256056Sgrehan		p->asc = 0x24;
1496256056Sgrehan		tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR;
1497256056Sgrehan	} else {
1498256056Sgrehan		uint8_t buf[8];
1499256056Sgrehan		int len;
1500256056Sgrehan
1501256056Sgrehan		len = be16dec(acmd + 7);
1502256056Sgrehan		if (len > sizeof(buf))
1503256056Sgrehan			len = sizeof(buf);
1504256056Sgrehan
1505256056Sgrehan		memset(buf, 0, sizeof(buf));
1506256056Sgrehan		be16enc(buf, 8 - 2);
1507256056Sgrehan		buf[2] = 0x04;
1508256056Sgrehan		buf[3] = 0x10;
1509256056Sgrehan		buf[5] = 0x02;
1510256056Sgrehan		write_prdt(p, slot, cfis, buf, len);
1511256056Sgrehan		tfd = ATA_S_READY | ATA_S_DSC;
1512256056Sgrehan	}
1513256056Sgrehan	cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1514256056Sgrehan	ahci_write_fis_d2h(p, slot, cfis, tfd);
1515256056Sgrehan}
1516256056Sgrehan
1517256056Sgrehanstatic void
1518256056Sgrehanhandle_packet_cmd(struct ahci_port *p, int slot, uint8_t *cfis)
1519256056Sgrehan{
1520256056Sgrehan	uint8_t *acmd;
1521256056Sgrehan
1522256056Sgrehan	acmd = cfis + 0x40;
1523256056Sgrehan
1524256056Sgrehan#ifdef AHCI_DEBUG
1525256056Sgrehan	{
1526256056Sgrehan		int i;
1527256056Sgrehan		DPRINTF("ACMD:");
1528256056Sgrehan		for (i = 0; i < 16; i++)
1529256056Sgrehan			DPRINTF("%02x ", acmd[i]);
1530256056Sgrehan		DPRINTF("\n");
1531256056Sgrehan	}
1532256056Sgrehan#endif
1533256056Sgrehan
1534256056Sgrehan	switch (acmd[0]) {
1535256056Sgrehan	case TEST_UNIT_READY:
1536256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1537256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1538256056Sgrehan		break;
1539256056Sgrehan	case INQUIRY:
1540256056Sgrehan		atapi_inquiry(p, slot, cfis);
1541256056Sgrehan		break;
1542256056Sgrehan	case READ_CAPACITY:
1543256056Sgrehan		atapi_read_capacity(p, slot, cfis);
1544256056Sgrehan		break;
1545256056Sgrehan	case PREVENT_ALLOW:
1546256056Sgrehan		/* TODO */
1547256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1548256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1549256056Sgrehan		break;
1550256056Sgrehan	case READ_TOC:
1551256056Sgrehan		atapi_read_toc(p, slot, cfis);
1552256056Sgrehan		break;
1553279979Smav	case REPORT_LUNS:
1554279979Smav		atapi_report_luns(p, slot, cfis);
1555279979Smav		break;
1556256056Sgrehan	case READ_10:
1557256056Sgrehan	case READ_12:
1558281666Smav		atapi_read(p, slot, cfis, 0);
1559256056Sgrehan		break;
1560256056Sgrehan	case REQUEST_SENSE:
1561256056Sgrehan		atapi_request_sense(p, slot, cfis);
1562256056Sgrehan		break;
1563256056Sgrehan	case START_STOP_UNIT:
1564256056Sgrehan		atapi_start_stop_unit(p, slot, cfis);
1565256056Sgrehan		break;
1566256056Sgrehan	case MODE_SENSE_10:
1567256056Sgrehan		atapi_mode_sense(p, slot, cfis);
1568256056Sgrehan		break;
1569256056Sgrehan	case GET_EVENT_STATUS_NOTIFICATION:
1570256056Sgrehan		atapi_get_event_status_notification(p, slot, cfis);
1571256056Sgrehan		break;
1572256056Sgrehan	default:
1573256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1574256056Sgrehan		p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
1575256056Sgrehan		p->asc = 0x20;
1576256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, (p->sense_key << 12) |
1577256056Sgrehan				ATA_S_READY | ATA_S_ERROR);
1578256056Sgrehan		break;
1579256056Sgrehan	}
1580256056Sgrehan}
1581256056Sgrehan
1582256056Sgrehanstatic void
1583256056Sgrehanahci_handle_cmd(struct ahci_port *p, int slot, uint8_t *cfis)
1584256056Sgrehan{
1585256056Sgrehan
1586282364Smav	p->tfd |= ATA_S_BUSY;
1587256056Sgrehan	switch (cfis[2]) {
1588256056Sgrehan	case ATA_ATA_IDENTIFY:
1589256056Sgrehan		handle_identify(p, slot, cfis);
1590256056Sgrehan		break;
1591256056Sgrehan	case ATA_SETFEATURES:
1592256056Sgrehan	{
1593256056Sgrehan		switch (cfis[3]) {
1594263238Stychon		case ATA_SF_ENAB_SATA_SF:
1595263238Stychon			switch (cfis[12]) {
1596263238Stychon			case ATA_SATA_SF_AN:
1597263238Stychon				p->tfd = ATA_S_DSC | ATA_S_READY;
1598263238Stychon				break;
1599263238Stychon			default:
1600263238Stychon				p->tfd = ATA_S_ERROR | ATA_S_READY;
1601263238Stychon				p->tfd |= (ATA_ERROR_ABORT << 8);
1602263238Stychon				break;
1603263238Stychon			}
1604263238Stychon			break;
1605256056Sgrehan		case ATA_SF_ENAB_WCACHE:
1606256056Sgrehan		case ATA_SF_DIS_WCACHE:
1607256056Sgrehan		case ATA_SF_ENAB_RCACHE:
1608256056Sgrehan		case ATA_SF_DIS_RCACHE:
1609256056Sgrehan			p->tfd = ATA_S_DSC | ATA_S_READY;
1610256056Sgrehan			break;
1611256056Sgrehan		case ATA_SF_SETXFER:
1612256056Sgrehan		{
1613256056Sgrehan			switch (cfis[12] & 0xf8) {
1614256056Sgrehan			case ATA_PIO:
1615256056Sgrehan			case ATA_PIO0:
1616256056Sgrehan				break;
1617256056Sgrehan			case ATA_WDMA0:
1618256056Sgrehan			case ATA_UDMA0:
1619256056Sgrehan				p->xfermode = (cfis[12] & 0x7);
1620256056Sgrehan				break;
1621256056Sgrehan			}
1622256056Sgrehan			p->tfd = ATA_S_DSC | ATA_S_READY;
1623256056Sgrehan			break;
1624256056Sgrehan		}
1625256056Sgrehan		default:
1626256056Sgrehan			p->tfd = ATA_S_ERROR | ATA_S_READY;
1627256056Sgrehan			p->tfd |= (ATA_ERROR_ABORT << 8);
1628256056Sgrehan			break;
1629256056Sgrehan		}
1630261785Stychon		ahci_write_fis_d2h(p, slot, cfis, p->tfd);
1631256056Sgrehan		break;
1632256056Sgrehan	}
1633256056Sgrehan	case ATA_SET_MULTI:
1634256056Sgrehan		if (cfis[12] != 0 &&
1635256164Sdim			(cfis[12] > 128 || (cfis[12] & (cfis[12] - 1)))) {
1636256056Sgrehan			p->tfd = ATA_S_ERROR | ATA_S_READY;
1637256056Sgrehan			p->tfd |= (ATA_ERROR_ABORT << 8);
1638256056Sgrehan		} else {
1639256056Sgrehan			p->mult_sectors = cfis[12];
1640256056Sgrehan			p->tfd = ATA_S_DSC | ATA_S_READY;
1641256056Sgrehan		}
1642279959Smav		ahci_write_fis_d2h(p, slot, cfis, p->tfd);
1643256056Sgrehan		break;
1644279960Smav	case ATA_READ:
1645279960Smav	case ATA_WRITE:
1646279960Smav	case ATA_READ48:
1647279960Smav	case ATA_WRITE48:
1648279960Smav	case ATA_READ_MUL:
1649279960Smav	case ATA_WRITE_MUL:
1650279960Smav	case ATA_READ_MUL48:
1651279960Smav	case ATA_WRITE_MUL48:
1652256056Sgrehan	case ATA_READ_DMA:
1653256056Sgrehan	case ATA_WRITE_DMA:
1654256056Sgrehan	case ATA_READ_DMA48:
1655256056Sgrehan	case ATA_WRITE_DMA48:
1656256056Sgrehan	case ATA_READ_FPDMA_QUEUED:
1657256056Sgrehan	case ATA_WRITE_FPDMA_QUEUED:
1658281666Smav		ahci_handle_rw(p, slot, cfis, 0);
1659256056Sgrehan		break;
1660256056Sgrehan	case ATA_FLUSHCACHE:
1661256056Sgrehan	case ATA_FLUSHCACHE48:
1662256056Sgrehan		ahci_handle_flush(p, slot, cfis);
1663256056Sgrehan		break;
1664279957Smav	case ATA_DATA_SET_MANAGEMENT:
1665279957Smav		if (cfis[11] == 0 && cfis[3] == ATA_DSM_TRIM &&
1666279957Smav		    cfis[13] == 0 && cfis[12] == 1) {
1667279957Smav			ahci_handle_dsm_trim(p, slot, cfis, 0);
1668279957Smav			break;
1669279957Smav		}
1670279957Smav		ahci_write_fis_d2h(p, slot, cfis,
1671279957Smav		    (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
1672279957Smav		break;
1673279976Smav	case ATA_SEND_FPDMA_QUEUED:
1674279976Smav		if ((cfis[13] & 0x1f) == ATA_SFPDMA_DSM &&
1675279976Smav		    cfis[17] == 0 && cfis[16] == ATA_DSM_TRIM &&
1676303138Smav		    cfis[11] == 0 && cfis[3] == 1) {
1677279976Smav			ahci_handle_dsm_trim(p, slot, cfis, 0);
1678279976Smav			break;
1679279976Smav		}
1680279976Smav		ahci_write_fis_d2h(p, slot, cfis,
1681279976Smav		    (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
1682279976Smav		break;
1683279975Smav	case ATA_READ_LOG_EXT:
1684279975Smav	case ATA_READ_LOG_DMA_EXT:
1685279975Smav		ahci_handle_read_log(p, slot, cfis);
1686279975Smav		break;
1687288826Sgrehan	case ATA_SECURITY_FREEZE_LOCK:
1688288826Sgrehan	case ATA_SMART_CMD:
1689279977Smav	case ATA_NOP:
1690279977Smav		ahci_write_fis_d2h(p, slot, cfis,
1691279977Smav		    (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
1692279977Smav		break;
1693286838Sgrehan	case ATA_CHECK_POWER_MODE:
1694286838Sgrehan		cfis[12] = 0xff;	/* always on */
1695286838Sgrehan		ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1696286838Sgrehan		break;
1697256056Sgrehan	case ATA_STANDBY_CMD:
1698256056Sgrehan	case ATA_STANDBY_IMMEDIATE:
1699279977Smav	case ATA_IDLE_CMD:
1700256056Sgrehan	case ATA_IDLE_IMMEDIATE:
1701256056Sgrehan	case ATA_SLEEP:
1702286838Sgrehan	case ATA_READ_VERIFY:
1703286838Sgrehan	case ATA_READ_VERIFY48:
1704256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1705256056Sgrehan		break;
1706256056Sgrehan	case ATA_ATAPI_IDENTIFY:
1707256056Sgrehan		handle_atapi_identify(p, slot, cfis);
1708256056Sgrehan		break;
1709256056Sgrehan	case ATA_PACKET_CMD:
1710256056Sgrehan		if (!p->atapi) {
1711279959Smav			ahci_write_fis_d2h(p, slot, cfis,
1712279959Smav			    (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
1713256056Sgrehan		} else
1714256056Sgrehan			handle_packet_cmd(p, slot, cfis);
1715256056Sgrehan		break;
1716256056Sgrehan	default:
1717256056Sgrehan		WPRINTF("Unsupported cmd:%02x\n", cfis[2]);
1718279959Smav		ahci_write_fis_d2h(p, slot, cfis,
1719279959Smav		    (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
1720256056Sgrehan		break;
1721256056Sgrehan	}
1722256056Sgrehan}
1723256056Sgrehan
1724256056Sgrehanstatic void
1725256056Sgrehanahci_handle_slot(struct ahci_port *p, int slot)
1726256056Sgrehan{
1727256056Sgrehan	struct ahci_cmd_hdr *hdr;
1728302363Sngie#ifdef AHCI_DEBUG
1729256056Sgrehan	struct ahci_prdt_entry *prdt;
1730302363Sngie#endif
1731256056Sgrehan	struct pci_ahci_softc *sc;
1732256056Sgrehan	uint8_t *cfis;
1733302363Sngie#ifdef AHCI_DEBUG
1734256056Sgrehan	int cfl;
1735302363Sngie#endif
1736256056Sgrehan
1737256056Sgrehan	sc = p->pr_sc;
1738256164Sdim	hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
1739302363Sngie#ifdef AHCI_DEBUG
1740256056Sgrehan	cfl = (hdr->flags & 0x1f) * 4;
1741302363Sngie#endif
1742256056Sgrehan	cfis = paddr_guest2host(ahci_ctx(sc), hdr->ctba,
1743256056Sgrehan			0x80 + hdr->prdtl * sizeof(struct ahci_prdt_entry));
1744302363Sngie#ifdef AHCI_DEBUG
1745256056Sgrehan	prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
1746256056Sgrehan
1747256056Sgrehan	DPRINTF("\ncfis:");
1748256056Sgrehan	for (i = 0; i < cfl; i++) {
1749256056Sgrehan		if (i % 10 == 0)
1750256056Sgrehan			DPRINTF("\n");
1751256056Sgrehan		DPRINTF("%02x ", cfis[i]);
1752256056Sgrehan	}
1753256056Sgrehan	DPRINTF("\n");
1754256056Sgrehan
1755256056Sgrehan	for (i = 0; i < hdr->prdtl; i++) {
1756256056Sgrehan		DPRINTF("%d@%08"PRIx64"\n", prdt->dbc & 0x3fffff, prdt->dba);
1757256056Sgrehan		prdt++;
1758256056Sgrehan	}
1759256056Sgrehan#endif
1760256056Sgrehan
1761256056Sgrehan	if (cfis[0] != FIS_TYPE_REGH2D) {
1762256056Sgrehan		WPRINTF("Not a H2D FIS:%02x\n", cfis[0]);
1763256056Sgrehan		return;
1764256056Sgrehan	}
1765256056Sgrehan
1766256056Sgrehan	if (cfis[1] & 0x80) {
1767256056Sgrehan		ahci_handle_cmd(p, slot, cfis);
1768256056Sgrehan	} else {
1769256056Sgrehan		if (cfis[15] & (1 << 2))
1770256056Sgrehan			p->reset = 1;
1771256056Sgrehan		else if (p->reset) {
1772256056Sgrehan			p->reset = 0;
1773256056Sgrehan			ahci_port_reset(p);
1774256056Sgrehan		}
1775256056Sgrehan		p->ci &= ~(1 << slot);
1776256056Sgrehan	}
1777256056Sgrehan}
1778256056Sgrehan
1779256056Sgrehanstatic void
1780256056Sgrehanahci_handle_port(struct ahci_port *p)
1781256056Sgrehan{
1782256056Sgrehan
1783256056Sgrehan	if (!(p->cmd & AHCI_P_CMD_ST))
1784256056Sgrehan		return;
1785256056Sgrehan
1786263322Stychon	/*
1787263322Stychon	 * Search for any new commands to issue ignoring those that
1788282429Smav	 * are already in-flight.  Stop if device is busy or in error.
1789263322Stychon	 */
1790282429Smav	for (; (p->ci & ~p->pending) != 0; p->ccs = ((p->ccs + 1) & 31)) {
1791282524Smav		if ((p->tfd & (ATA_S_BUSY | ATA_S_DRQ)) != 0)
1792282429Smav			break;
1793282524Smav		if (p->waitforclear)
1794282524Smav			break;
1795282429Smav		if ((p->ci & ~p->pending & (1 << p->ccs)) != 0) {
1796269317Stychon			p->cmd &= ~AHCI_P_CMD_CCS_MASK;
1797282429Smav			p->cmd |= p->ccs << AHCI_P_CMD_CCS_SHIFT;
1798282429Smav			ahci_handle_slot(p, p->ccs);
1799269317Stychon		}
1800256056Sgrehan	}
1801256056Sgrehan}
1802256056Sgrehan
1803256056Sgrehan/*
1804256056Sgrehan * blockif callback routine - this runs in the context of the blockif
1805256056Sgrehan * i/o thread, so the mutex needs to be acquired.
1806256056Sgrehan */
1807256056Sgrehanstatic void
1808256056Sgrehanata_ioreq_cb(struct blockif_req *br, int err)
1809256056Sgrehan{
1810256056Sgrehan	struct ahci_cmd_hdr *hdr;
1811256056Sgrehan	struct ahci_ioreq *aior;
1812256056Sgrehan	struct ahci_port *p;
1813256056Sgrehan	struct pci_ahci_softc *sc;
1814256056Sgrehan	uint32_t tfd;
1815256056Sgrehan	uint8_t *cfis;
1816281666Smav	int slot, ncq, dsm;
1817256056Sgrehan
1818256056Sgrehan	DPRINTF("%s %d\n", __func__, err);
1819256056Sgrehan
1820280293Smav	ncq = dsm = 0;
1821256056Sgrehan	aior = br->br_param;
1822256056Sgrehan	p = aior->io_pr;
1823256056Sgrehan	cfis = aior->cfis;
1824256056Sgrehan	slot = aior->slot;
1825256056Sgrehan	sc = p->pr_sc;
1826256164Sdim	hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
1827256056Sgrehan
1828256056Sgrehan	if (cfis[2] == ATA_WRITE_FPDMA_QUEUED ||
1829279976Smav	    cfis[2] == ATA_READ_FPDMA_QUEUED ||
1830279976Smav	    cfis[2] == ATA_SEND_FPDMA_QUEUED)
1831256056Sgrehan		ncq = 1;
1832279976Smav	if (cfis[2] == ATA_DATA_SET_MANAGEMENT ||
1833279976Smav	    (cfis[2] == ATA_SEND_FPDMA_QUEUED &&
1834279976Smav	     (cfis[13] & 0x1f) == ATA_SFPDMA_DSM))
1835279957Smav		dsm = 1;
1836256056Sgrehan
1837256056Sgrehan	pthread_mutex_lock(&sc->mtx);
1838256056Sgrehan
1839256056Sgrehan	/*
1840273212Stychon	 * Delete the blockif request from the busy list
1841273212Stychon	 */
1842273212Stychon	TAILQ_REMOVE(&p->iobhd, aior, io_blist);
1843273212Stychon
1844273212Stychon	/*
1845256056Sgrehan	 * Move the blockif request back to the free list
1846256056Sgrehan	 */
1847273212Stychon	STAILQ_INSERT_TAIL(&p->iofhd, aior, io_flist);
1848256056Sgrehan
1849279967Smav	if (!err)
1850279967Smav		hdr->prdbc = aior->done;
1851279967Smav
1852281666Smav	if (!err && aior->more) {
1853281666Smav		if (dsm)
1854279957Smav			ahci_handle_dsm_trim(p, slot, cfis, aior->done);
1855281666Smav		else
1856281666Smav			ahci_handle_rw(p, slot, cfis, aior->done);
1857281666Smav		goto out;
1858256056Sgrehan	}
1859256056Sgrehan
1860281666Smav	if (!err)
1861256056Sgrehan		tfd = ATA_S_READY | ATA_S_DSC;
1862281666Smav	else
1863256056Sgrehan		tfd = (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR;
1864279975Smav	if (ncq)
1865279975Smav		ahci_write_fis_sdb(p, slot, cfis, tfd);
1866279975Smav	else
1867256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, tfd);
1868256056Sgrehan
1869273212Stychon	/*
1870273212Stychon	 * This command is now complete.
1871273212Stychon	 */
1872273212Stychon	p->pending &= ~(1 << slot);
1873273212Stychon
1874273212Stychon	ahci_check_stopped(p);
1875282429Smav	ahci_handle_port(p);
1876256056Sgrehanout:
1877256056Sgrehan	pthread_mutex_unlock(&sc->mtx);
1878256056Sgrehan	DPRINTF("%s exit\n", __func__);
1879256056Sgrehan}
1880256056Sgrehan
1881256056Sgrehanstatic void
1882256056Sgrehanatapi_ioreq_cb(struct blockif_req *br, int err)
1883256056Sgrehan{
1884256056Sgrehan	struct ahci_cmd_hdr *hdr;
1885256056Sgrehan	struct ahci_ioreq *aior;
1886256056Sgrehan	struct ahci_port *p;
1887256056Sgrehan	struct pci_ahci_softc *sc;
1888256056Sgrehan	uint8_t *cfis;
1889256056Sgrehan	uint32_t tfd;
1890281666Smav	int slot;
1891256056Sgrehan
1892256056Sgrehan	DPRINTF("%s %d\n", __func__, err);
1893256056Sgrehan
1894256056Sgrehan	aior = br->br_param;
1895256056Sgrehan	p = aior->io_pr;
1896256056Sgrehan	cfis = aior->cfis;
1897256056Sgrehan	slot = aior->slot;
1898256056Sgrehan	sc = p->pr_sc;
1899256164Sdim	hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + aior->slot * AHCI_CL_SIZE);
1900256056Sgrehan
1901256056Sgrehan	pthread_mutex_lock(&sc->mtx);
1902256056Sgrehan
1903256056Sgrehan	/*
1904273212Stychon	 * Delete the blockif request from the busy list
1905273212Stychon	 */
1906273212Stychon	TAILQ_REMOVE(&p->iobhd, aior, io_blist);
1907273212Stychon
1908273212Stychon	/*
1909256056Sgrehan	 * Move the blockif request back to the free list
1910256056Sgrehan	 */
1911273212Stychon	STAILQ_INSERT_TAIL(&p->iofhd, aior, io_flist);
1912256056Sgrehan
1913279967Smav	if (!err)
1914279967Smav		hdr->prdbc = aior->done;
1915279967Smav
1916281666Smav	if (!err && aior->more) {
1917281666Smav		atapi_read(p, slot, cfis, aior->done);
1918256056Sgrehan		goto out;
1919256056Sgrehan	}
1920256056Sgrehan
1921281666Smav	if (!err) {
1922256056Sgrehan		tfd = ATA_S_READY | ATA_S_DSC;
1923256056Sgrehan	} else {
1924256056Sgrehan		p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
1925256056Sgrehan		p->asc = 0x21;
1926256056Sgrehan		tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR;
1927256056Sgrehan	}
1928256056Sgrehan	cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1929256056Sgrehan	ahci_write_fis_d2h(p, slot, cfis, tfd);
1930256056Sgrehan
1931273212Stychon	/*
1932273212Stychon	 * This command is now complete.
1933273212Stychon	 */
1934273212Stychon	p->pending &= ~(1 << slot);
1935273212Stychon
1936273212Stychon	ahci_check_stopped(p);
1937282429Smav	ahci_handle_port(p);
1938256056Sgrehanout:
1939256056Sgrehan	pthread_mutex_unlock(&sc->mtx);
1940256056Sgrehan	DPRINTF("%s exit\n", __func__);
1941256056Sgrehan}
1942256056Sgrehan
1943256056Sgrehanstatic void
1944256056Sgrehanpci_ahci_ioreq_init(struct ahci_port *pr)
1945256056Sgrehan{
1946256056Sgrehan	struct ahci_ioreq *vr;
1947256056Sgrehan	int i;
1948256056Sgrehan
1949256056Sgrehan	pr->ioqsz = blockif_queuesz(pr->bctx);
1950256056Sgrehan	pr->ioreq = calloc(pr->ioqsz, sizeof(struct ahci_ioreq));
1951256056Sgrehan	STAILQ_INIT(&pr->iofhd);
1952256056Sgrehan
1953256056Sgrehan	/*
1954256056Sgrehan	 * Add all i/o request entries to the free queue
1955256056Sgrehan	 */
1956256056Sgrehan	for (i = 0; i < pr->ioqsz; i++) {
1957256056Sgrehan		vr = &pr->ioreq[i];
1958256056Sgrehan		vr->io_pr = pr;
1959256056Sgrehan		if (!pr->atapi)
1960256056Sgrehan			vr->io_req.br_callback = ata_ioreq_cb;
1961256056Sgrehan		else
1962256056Sgrehan			vr->io_req.br_callback = atapi_ioreq_cb;
1963256056Sgrehan		vr->io_req.br_param = vr;
1964273212Stychon		STAILQ_INSERT_TAIL(&pr->iofhd, vr, io_flist);
1965256056Sgrehan	}
1966273212Stychon
1967273212Stychon	TAILQ_INIT(&pr->iobhd);
1968256056Sgrehan}
1969256056Sgrehan
1970256056Sgrehanstatic void
1971256056Sgrehanpci_ahci_port_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value)
1972256056Sgrehan{
1973256056Sgrehan	int port = (offset - AHCI_OFFSET) / AHCI_STEP;
1974256056Sgrehan	offset = (offset - AHCI_OFFSET) % AHCI_STEP;
1975256056Sgrehan	struct ahci_port *p = &sc->port[port];
1976256056Sgrehan
1977256056Sgrehan	DPRINTF("pci_ahci_port %d: write offset 0x%"PRIx64" value 0x%"PRIx64"\n",
1978256056Sgrehan		port, offset, value);
1979256056Sgrehan
1980256056Sgrehan	switch (offset) {
1981256056Sgrehan	case AHCI_P_CLB:
1982256056Sgrehan		p->clb = value;
1983256056Sgrehan		break;
1984256056Sgrehan	case AHCI_P_CLBU:
1985256056Sgrehan		p->clbu = value;
1986256056Sgrehan		break;
1987256056Sgrehan	case AHCI_P_FB:
1988256056Sgrehan		p->fb = value;
1989256056Sgrehan		break;
1990256056Sgrehan	case AHCI_P_FBU:
1991256056Sgrehan		p->fbu = value;
1992256056Sgrehan		break;
1993256056Sgrehan	case AHCI_P_IS:
1994256056Sgrehan		p->is &= ~value;
1995256056Sgrehan		break;
1996256056Sgrehan	case AHCI_P_IE:
1997256056Sgrehan		p->ie = value & 0xFDC000FF;
1998256056Sgrehan		ahci_generate_intr(sc);
1999256056Sgrehan		break;
2000256056Sgrehan	case AHCI_P_CMD:
2001256056Sgrehan	{
2002282345Smav		p->cmd &= ~(AHCI_P_CMD_ST | AHCI_P_CMD_SUD | AHCI_P_CMD_POD |
2003282345Smav		    AHCI_P_CMD_CLO | AHCI_P_CMD_FRE | AHCI_P_CMD_APSTE |
2004282345Smav		    AHCI_P_CMD_ATAPI | AHCI_P_CMD_DLAE | AHCI_P_CMD_ALPE |
2005282345Smav		    AHCI_P_CMD_ASP | AHCI_P_CMD_ICC_MASK);
2006282345Smav		p->cmd |= (AHCI_P_CMD_ST | AHCI_P_CMD_SUD | AHCI_P_CMD_POD |
2007282345Smav		    AHCI_P_CMD_CLO | AHCI_P_CMD_FRE | AHCI_P_CMD_APSTE |
2008282345Smav		    AHCI_P_CMD_ATAPI | AHCI_P_CMD_DLAE | AHCI_P_CMD_ALPE |
2009282345Smav		    AHCI_P_CMD_ASP | AHCI_P_CMD_ICC_MASK) & value;
2010282345Smav
2011256056Sgrehan		if (!(value & AHCI_P_CMD_ST)) {
2012273212Stychon			ahci_port_stop(p);
2013256056Sgrehan		} else {
2014256056Sgrehan			uint64_t clb;
2015256056Sgrehan
2016256056Sgrehan			p->cmd |= AHCI_P_CMD_CR;
2017256056Sgrehan			clb = (uint64_t)p->clbu << 32 | p->clb;
2018256056Sgrehan			p->cmd_lst = paddr_guest2host(ahci_ctx(sc), clb,
2019256056Sgrehan					AHCI_CL_SIZE * AHCI_MAX_SLOTS);
2020256056Sgrehan		}
2021256056Sgrehan
2022256056Sgrehan		if (value & AHCI_P_CMD_FRE) {
2023256056Sgrehan			uint64_t fb;
2024256056Sgrehan
2025256056Sgrehan			p->cmd |= AHCI_P_CMD_FR;
2026256056Sgrehan			fb = (uint64_t)p->fbu << 32 | p->fb;
2027256056Sgrehan			/* we don't support FBSCP, so rfis size is 256Bytes */
2028256056Sgrehan			p->rfis = paddr_guest2host(ahci_ctx(sc), fb, 256);
2029256056Sgrehan		} else {
2030256056Sgrehan			p->cmd &= ~AHCI_P_CMD_FR;
2031256056Sgrehan		}
2032256056Sgrehan
2033256056Sgrehan		if (value & AHCI_P_CMD_CLO) {
2034282524Smav			p->tfd &= ~(ATA_S_BUSY | ATA_S_DRQ);
2035256056Sgrehan			p->cmd &= ~AHCI_P_CMD_CLO;
2036256056Sgrehan		}
2037256056Sgrehan
2038282345Smav		if (value & AHCI_P_CMD_ICC_MASK) {
2039282345Smav			p->cmd &= ~AHCI_P_CMD_ICC_MASK;
2040282345Smav		}
2041282345Smav
2042256056Sgrehan		ahci_handle_port(p);
2043256056Sgrehan		break;
2044256056Sgrehan	}
2045256056Sgrehan	case AHCI_P_TFD:
2046256056Sgrehan	case AHCI_P_SIG:
2047256056Sgrehan	case AHCI_P_SSTS:
2048256056Sgrehan		WPRINTF("pci_ahci_port: read only registers 0x%"PRIx64"\n", offset);
2049256056Sgrehan		break;
2050256056Sgrehan	case AHCI_P_SCTL:
2051279965Smav		p->sctl = value;
2052256056Sgrehan		if (!(p->cmd & AHCI_P_CMD_ST)) {
2053256056Sgrehan			if (value & ATA_SC_DET_RESET)
2054256056Sgrehan				ahci_port_reset(p);
2055256056Sgrehan		}
2056256056Sgrehan		break;
2057256056Sgrehan	case AHCI_P_SERR:
2058256056Sgrehan		p->serr &= ~value;
2059256056Sgrehan		break;
2060256056Sgrehan	case AHCI_P_SACT:
2061256056Sgrehan		p->sact |= value;
2062256056Sgrehan		break;
2063256056Sgrehan	case AHCI_P_CI:
2064256056Sgrehan		p->ci |= value;
2065256056Sgrehan		ahci_handle_port(p);
2066256056Sgrehan		break;
2067256056Sgrehan	case AHCI_P_SNTF:
2068256056Sgrehan	case AHCI_P_FBS:
2069256056Sgrehan	default:
2070256056Sgrehan		break;
2071256056Sgrehan	}
2072256056Sgrehan}
2073256056Sgrehan
2074256056Sgrehanstatic void
2075256056Sgrehanpci_ahci_host_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value)
2076256056Sgrehan{
2077256056Sgrehan	DPRINTF("pci_ahci_host: write offset 0x%"PRIx64" value 0x%"PRIx64"\n",
2078256056Sgrehan		offset, value);
2079256056Sgrehan
2080256056Sgrehan	switch (offset) {
2081256056Sgrehan	case AHCI_CAP:
2082256056Sgrehan	case AHCI_PI:
2083256056Sgrehan	case AHCI_VS:
2084256056Sgrehan	case AHCI_CAP2:
2085256709Sgrehan		DPRINTF("pci_ahci_host: read only registers 0x%"PRIx64"\n", offset);
2086256056Sgrehan		break;
2087256056Sgrehan	case AHCI_GHC:
2088256056Sgrehan		if (value & AHCI_GHC_HR)
2089256056Sgrehan			ahci_reset(sc);
2090256056Sgrehan		else if (value & AHCI_GHC_IE) {
2091256056Sgrehan			sc->ghc |= AHCI_GHC_IE;
2092256056Sgrehan			ahci_generate_intr(sc);
2093256056Sgrehan		}
2094256056Sgrehan		break;
2095256056Sgrehan	case AHCI_IS:
2096256056Sgrehan		sc->is &= ~value;
2097256056Sgrehan		ahci_generate_intr(sc);
2098256056Sgrehan		break;
2099256056Sgrehan	default:
2100256056Sgrehan		break;
2101256056Sgrehan	}
2102256056Sgrehan}
2103256056Sgrehan
2104256056Sgrehanstatic void
2105256056Sgrehanpci_ahci_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
2106256056Sgrehan		int baridx, uint64_t offset, int size, uint64_t value)
2107256056Sgrehan{
2108256056Sgrehan	struct pci_ahci_softc *sc = pi->pi_arg;
2109256056Sgrehan
2110256056Sgrehan	assert(baridx == 5);
2111282595Sneel	assert((offset % 4) == 0 && size == 4);
2112256056Sgrehan
2113256056Sgrehan	pthread_mutex_lock(&sc->mtx);
2114256056Sgrehan
2115256056Sgrehan	if (offset < AHCI_OFFSET)
2116256056Sgrehan		pci_ahci_host_write(sc, offset, value);
2117256056Sgrehan	else if (offset < AHCI_OFFSET + sc->ports * AHCI_STEP)
2118256056Sgrehan		pci_ahci_port_write(sc, offset, value);
2119256056Sgrehan	else
2120256056Sgrehan		WPRINTF("pci_ahci: unknown i/o write offset 0x%"PRIx64"\n", offset);
2121256056Sgrehan
2122256056Sgrehan	pthread_mutex_unlock(&sc->mtx);
2123256056Sgrehan}
2124256056Sgrehan
2125256056Sgrehanstatic uint64_t
2126256056Sgrehanpci_ahci_host_read(struct pci_ahci_softc *sc, uint64_t offset)
2127256056Sgrehan{
2128256056Sgrehan	uint32_t value;
2129256056Sgrehan
2130256056Sgrehan	switch (offset) {
2131256056Sgrehan	case AHCI_CAP:
2132256056Sgrehan	case AHCI_GHC:
2133256056Sgrehan	case AHCI_IS:
2134256056Sgrehan	case AHCI_PI:
2135256056Sgrehan	case AHCI_VS:
2136256056Sgrehan	case AHCI_CCCC:
2137256056Sgrehan	case AHCI_CCCP:
2138256056Sgrehan	case AHCI_EM_LOC:
2139256056Sgrehan	case AHCI_EM_CTL:
2140256056Sgrehan	case AHCI_CAP2:
2141256056Sgrehan	{
2142256056Sgrehan		uint32_t *p = &sc->cap;
2143256056Sgrehan		p += (offset - AHCI_CAP) / sizeof(uint32_t);
2144256056Sgrehan		value = *p;
2145256056Sgrehan		break;
2146256056Sgrehan	}
2147256056Sgrehan	default:
2148256056Sgrehan		value = 0;
2149256056Sgrehan		break;
2150256056Sgrehan	}
2151256056Sgrehan	DPRINTF("pci_ahci_host: read offset 0x%"PRIx64" value 0x%x\n",
2152256056Sgrehan		offset, value);
2153256056Sgrehan
2154256056Sgrehan	return (value);
2155256056Sgrehan}
2156256056Sgrehan
2157256056Sgrehanstatic uint64_t
2158256056Sgrehanpci_ahci_port_read(struct pci_ahci_softc *sc, uint64_t offset)
2159256056Sgrehan{
2160256056Sgrehan	uint32_t value;
2161256056Sgrehan	int port = (offset - AHCI_OFFSET) / AHCI_STEP;
2162256056Sgrehan	offset = (offset - AHCI_OFFSET) % AHCI_STEP;
2163256056Sgrehan
2164256056Sgrehan	switch (offset) {
2165256056Sgrehan	case AHCI_P_CLB:
2166256056Sgrehan	case AHCI_P_CLBU:
2167256056Sgrehan	case AHCI_P_FB:
2168256056Sgrehan	case AHCI_P_FBU:
2169256056Sgrehan	case AHCI_P_IS:
2170256056Sgrehan	case AHCI_P_IE:
2171256056Sgrehan	case AHCI_P_CMD:
2172256056Sgrehan	case AHCI_P_TFD:
2173256056Sgrehan	case AHCI_P_SIG:
2174256056Sgrehan	case AHCI_P_SSTS:
2175256056Sgrehan	case AHCI_P_SCTL:
2176256056Sgrehan	case AHCI_P_SERR:
2177256056Sgrehan	case AHCI_P_SACT:
2178256056Sgrehan	case AHCI_P_CI:
2179256056Sgrehan	case AHCI_P_SNTF:
2180256056Sgrehan	case AHCI_P_FBS:
2181256056Sgrehan	{
2182256056Sgrehan		uint32_t *p= &sc->port[port].clb;
2183256056Sgrehan		p += (offset - AHCI_P_CLB) / sizeof(uint32_t);
2184256056Sgrehan		value = *p;
2185256056Sgrehan		break;
2186256056Sgrehan	}
2187256056Sgrehan	default:
2188256056Sgrehan		value = 0;
2189256056Sgrehan		break;
2190256056Sgrehan	}
2191256056Sgrehan
2192256056Sgrehan	DPRINTF("pci_ahci_port %d: read offset 0x%"PRIx64" value 0x%x\n",
2193256056Sgrehan		port, offset, value);
2194256056Sgrehan
2195256056Sgrehan	return value;
2196256056Sgrehan}
2197256056Sgrehan
2198256056Sgrehanstatic uint64_t
2199256056Sgrehanpci_ahci_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
2200282595Sneel    uint64_t regoff, int size)
2201256056Sgrehan{
2202256056Sgrehan	struct pci_ahci_softc *sc = pi->pi_arg;
2203282595Sneel	uint64_t offset;
2204256056Sgrehan	uint32_t value;
2205256056Sgrehan
2206256056Sgrehan	assert(baridx == 5);
2207282595Sneel	assert(size == 1 || size == 2 || size == 4);
2208282595Sneel	assert((regoff & (size - 1)) == 0);
2209256056Sgrehan
2210256056Sgrehan	pthread_mutex_lock(&sc->mtx);
2211256056Sgrehan
2212282595Sneel	offset = regoff & ~0x3;	    /* round down to a multiple of 4 bytes */
2213256056Sgrehan	if (offset < AHCI_OFFSET)
2214256056Sgrehan		value = pci_ahci_host_read(sc, offset);
2215256056Sgrehan	else if (offset < AHCI_OFFSET + sc->ports * AHCI_STEP)
2216256056Sgrehan		value = pci_ahci_port_read(sc, offset);
2217256056Sgrehan	else {
2218256056Sgrehan		value = 0;
2219282595Sneel		WPRINTF("pci_ahci: unknown i/o read offset 0x%"PRIx64"\n",
2220282595Sneel		    regoff);
2221256056Sgrehan	}
2222282595Sneel	value >>= 8 * (regoff & 0x3);
2223256056Sgrehan
2224256056Sgrehan	pthread_mutex_unlock(&sc->mtx);
2225256056Sgrehan
2226256056Sgrehan	return (value);
2227256056Sgrehan}
2228256056Sgrehan
2229256056Sgrehanstatic int
2230256056Sgrehanpci_ahci_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts, int atapi)
2231256056Sgrehan{
2232256056Sgrehan	char bident[sizeof("XX:X:X")];
2233256056Sgrehan	struct blockif_ctxt *bctxt;
2234256056Sgrehan	struct pci_ahci_softc *sc;
2235256056Sgrehan	int ret, slots;
2236280040Smav	MD5_CTX mdctx;
2237280040Smav	u_char digest[16];
2238256056Sgrehan
2239256056Sgrehan	ret = 0;
2240256056Sgrehan
2241256056Sgrehan	if (opts == NULL) {
2242256056Sgrehan		fprintf(stderr, "pci_ahci: backing device required\n");
2243256056Sgrehan		return (1);
2244256056Sgrehan	}
2245256056Sgrehan
2246256056Sgrehan#ifdef AHCI_DEBUG
2247256056Sgrehan	dbg = fopen("/tmp/log", "w+");
2248256056Sgrehan#endif
2249256056Sgrehan
2250264770Sdelphij	sc = calloc(1, sizeof(struct pci_ahci_softc));
2251256056Sgrehan	pi->pi_arg = sc;
2252256056Sgrehan	sc->asc_pi = pi;
2253256056Sgrehan	sc->ports = MAX_PORTS;
2254256056Sgrehan
2255256056Sgrehan	/*
2256256056Sgrehan	 * Only use port 0 for a backing device. All other ports will be
2257256056Sgrehan	 * marked as unused
2258256056Sgrehan	 */
2259256056Sgrehan	sc->port[0].atapi = atapi;
2260256056Sgrehan
2261256056Sgrehan	/*
2262256056Sgrehan	 * Attempt to open the backing image. Use the PCI
2263257729Sgrehan	 * slot/func for the identifier string.
2264256056Sgrehan	 */
2265257729Sgrehan	snprintf(bident, sizeof(bident), "%d:%d", pi->pi_slot, pi->pi_func);
2266256056Sgrehan	bctxt = blockif_open(opts, bident);
2267256056Sgrehan	if (bctxt == NULL) {
2268256056Sgrehan		ret = 1;
2269256056Sgrehan		goto open_fail;
2270256056Sgrehan	}
2271256056Sgrehan	sc->port[0].bctx = bctxt;
2272256056Sgrehan	sc->port[0].pr_sc = sc;
2273256056Sgrehan
2274256056Sgrehan	/*
2275280040Smav	 * Create an identifier for the backing file. Use parts of the
2276280040Smav	 * md5 sum of the filename
2277280040Smav	 */
2278280040Smav	MD5Init(&mdctx);
2279280040Smav	MD5Update(&mdctx, opts, strlen(opts));
2280280040Smav	MD5Final(digest, &mdctx);
2281280040Smav	sprintf(sc->port[0].ident, "BHYVE-%02X%02X-%02X%02X-%02X%02X",
2282280040Smav	    digest[0], digest[1], digest[2], digest[3], digest[4], digest[5]);
2283280040Smav
2284280040Smav	/*
2285256056Sgrehan	 * Allocate blockif request structures and add them
2286256056Sgrehan	 * to the free list
2287256056Sgrehan	 */
2288256056Sgrehan	pci_ahci_ioreq_init(&sc->port[0]);
2289256056Sgrehan
2290256056Sgrehan	pthread_mutex_init(&sc->mtx, NULL);
2291256056Sgrehan
2292256056Sgrehan	/* Intel ICH8 AHCI */
2293256056Sgrehan	slots = sc->port[0].ioqsz;
2294256056Sgrehan	if (slots > 32)
2295256056Sgrehan		slots = 32;
2296256056Sgrehan	--slots;
2297256056Sgrehan	sc->cap = AHCI_CAP_64BIT | AHCI_CAP_SNCQ | AHCI_CAP_SSNTF |
2298256056Sgrehan	    AHCI_CAP_SMPS | AHCI_CAP_SSS | AHCI_CAP_SALP |
2299256056Sgrehan	    AHCI_CAP_SAL | AHCI_CAP_SCLO | (0x3 << AHCI_CAP_ISS_SHIFT)|
2300256056Sgrehan	    AHCI_CAP_PMD | AHCI_CAP_SSC | AHCI_CAP_PSC |
2301256056Sgrehan	    (slots << AHCI_CAP_NCS_SHIFT) | AHCI_CAP_SXS | (sc->ports - 1);
2302256056Sgrehan
2303256056Sgrehan	/* Only port 0 implemented */
2304256056Sgrehan	sc->pi = 1;
2305256056Sgrehan	sc->vs = 0x10300;
2306256056Sgrehan	sc->cap2 = AHCI_CAP2_APST;
2307256056Sgrehan	ahci_reset(sc);
2308256056Sgrehan
2309256056Sgrehan	pci_set_cfgdata16(pi, PCIR_DEVICE, 0x2821);
2310256056Sgrehan	pci_set_cfgdata16(pi, PCIR_VENDOR, 0x8086);
2311256056Sgrehan	pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE);
2312256056Sgrehan	pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_STORAGE_SATA);
2313256056Sgrehan	pci_set_cfgdata8(pi, PCIR_PROGIF, PCIP_STORAGE_SATA_AHCI_1_0);
2314256056Sgrehan	pci_emul_add_msicap(pi, 1);
2315256056Sgrehan	pci_emul_alloc_bar(pi, 5, PCIBAR_MEM32,
2316256056Sgrehan	    AHCI_OFFSET + sc->ports * AHCI_STEP);
2317256056Sgrehan
2318265058Sgrehan	pci_lintr_request(pi);
2319265058Sgrehan
2320256056Sgrehanopen_fail:
2321256056Sgrehan	if (ret) {
2322279220Sgrehan		if (sc->port[0].bctx != NULL)
2323279220Sgrehan			blockif_close(sc->port[0].bctx);
2324256056Sgrehan		free(sc);
2325256056Sgrehan	}
2326256056Sgrehan
2327256056Sgrehan	return (ret);
2328256056Sgrehan}
2329256056Sgrehan
2330256056Sgrehanstatic int
2331256056Sgrehanpci_ahci_hd_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
2332256056Sgrehan{
2333256056Sgrehan
2334256056Sgrehan	return (pci_ahci_init(ctx, pi, opts, 0));
2335256056Sgrehan}
2336256056Sgrehan
2337256056Sgrehanstatic int
2338256056Sgrehanpci_ahci_atapi_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
2339256056Sgrehan{
2340256056Sgrehan
2341256056Sgrehan	return (pci_ahci_init(ctx, pi, opts, 1));
2342256056Sgrehan}
2343256056Sgrehan
2344256056Sgrehan/*
2345256056Sgrehan * Use separate emulation names to distinguish drive and atapi devices
2346256056Sgrehan */
2347256056Sgrehanstruct pci_devemu pci_de_ahci_hd = {
2348256056Sgrehan	.pe_emu =	"ahci-hd",
2349256056Sgrehan	.pe_init =	pci_ahci_hd_init,
2350256056Sgrehan	.pe_barwrite =	pci_ahci_write,
2351256056Sgrehan	.pe_barread =	pci_ahci_read
2352256056Sgrehan};
2353256056SgrehanPCI_EMUL_SET(pci_de_ahci_hd);
2354256056Sgrehan
2355256056Sgrehanstruct pci_devemu pci_de_ahci_cd = {
2356256056Sgrehan	.pe_emu =	"ahci-cd",
2357256056Sgrehan	.pe_init =	pci_ahci_atapi_init,
2358256056Sgrehan	.pe_barwrite =	pci_ahci_write,
2359256056Sgrehan	.pe_barread =	pci_ahci_read
2360256056Sgrehan};
2361256056SgrehanPCI_EMUL_SET(pci_de_ahci_cd);
2362