1256056Sgrehan/*-
2256056Sgrehan * Copyright (c) 2013  Zhixiang Yu <zcore@freebsd.org>
3304420Smav * Copyright (c) 2015-2016 Alexander Motin <mav@FreeBSD.org>
4256056Sgrehan * All rights reserved.
5256056Sgrehan *
6256056Sgrehan * Redistribution and use in source and binary forms, with or without
7256056Sgrehan * modification, are permitted provided that the following conditions
8256056Sgrehan * are met:
9256056Sgrehan * 1. Redistributions of source code must retain the above copyright
10256056Sgrehan *    notice, this list of conditions and the following disclaimer.
11256056Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
12256056Sgrehan *    notice, this list of conditions and the following disclaimer in the
13256056Sgrehan *    documentation and/or other materials provided with the distribution.
14256056Sgrehan *
15256056Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
16256056Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17256056Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18256056Sgrehan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19256056Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20256056Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21256056Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22256056Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23256056Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24256056Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25256056Sgrehan * SUCH DAMAGE.
26256056Sgrehan *
27256056Sgrehan * $FreeBSD: stable/10/usr.sbin/bhyve/pci_ahci.c 341606 2018-12-05 21:49:39Z emaste $
28256056Sgrehan */
29256056Sgrehan
30256056Sgrehan#include <sys/cdefs.h>
31256056Sgrehan__FBSDID("$FreeBSD: stable/10/usr.sbin/bhyve/pci_ahci.c 341606 2018-12-05 21:49:39Z emaste $");
32256056Sgrehan
33256056Sgrehan#include <sys/param.h>
34256056Sgrehan#include <sys/linker_set.h>
35256056Sgrehan#include <sys/stat.h>
36256056Sgrehan#include <sys/uio.h>
37256056Sgrehan#include <sys/ioctl.h>
38256056Sgrehan#include <sys/disk.h>
39256056Sgrehan#include <sys/ata.h>
40256056Sgrehan#include <sys/endian.h>
41256056Sgrehan
42256056Sgrehan#include <errno.h>
43256056Sgrehan#include <fcntl.h>
44256056Sgrehan#include <stdio.h>
45256056Sgrehan#include <stdlib.h>
46256056Sgrehan#include <stdint.h>
47256056Sgrehan#include <string.h>
48256056Sgrehan#include <strings.h>
49256056Sgrehan#include <unistd.h>
50256056Sgrehan#include <assert.h>
51256056Sgrehan#include <pthread.h>
52276349Sneel#include <pthread_np.h>
53256056Sgrehan#include <inttypes.h>
54280745Smav#include <md5.h>
55256056Sgrehan
56256056Sgrehan#include "bhyverun.h"
57256056Sgrehan#include "pci_emul.h"
58256056Sgrehan#include "ahci.h"
59256056Sgrehan#include "block_if.h"
60256056Sgrehan
61304420Smav#define	DEF_PORTS	6	/* Intel ICH8 AHCI supports 6 ports */
62304420Smav#define	MAX_PORTS	32	/* AHCI supports 32 ports */
63256056Sgrehan
64256056Sgrehan#define	PxSIG_ATA	0x00000101 /* ATA drive */
65256056Sgrehan#define	PxSIG_ATAPI	0xeb140101 /* ATAPI drive */
66256056Sgrehan
67256056Sgrehanenum sata_fis_type {
68256056Sgrehan	FIS_TYPE_REGH2D		= 0x27,	/* Register FIS - host to device */
69256056Sgrehan	FIS_TYPE_REGD2H		= 0x34,	/* Register FIS - device to host */
70256056Sgrehan	FIS_TYPE_DMAACT		= 0x39,	/* DMA activate FIS - device to host */
71256056Sgrehan	FIS_TYPE_DMASETUP	= 0x41,	/* DMA setup FIS - bidirectional */
72256056Sgrehan	FIS_TYPE_DATA		= 0x46,	/* Data FIS - bidirectional */
73256056Sgrehan	FIS_TYPE_BIST		= 0x58,	/* BIST activate FIS - bidirectional */
74256056Sgrehan	FIS_TYPE_PIOSETUP	= 0x5F,	/* PIO setup FIS - device to host */
75256056Sgrehan	FIS_TYPE_SETDEVBITS	= 0xA1,	/* Set dev bits FIS - device to host */
76256056Sgrehan};
77256056Sgrehan
78256056Sgrehan/*
79256056Sgrehan * SCSI opcodes
80256056Sgrehan */
81256056Sgrehan#define	TEST_UNIT_READY		0x00
82256056Sgrehan#define	REQUEST_SENSE		0x03
83256056Sgrehan#define	INQUIRY			0x12
84256056Sgrehan#define	START_STOP_UNIT		0x1B
85256056Sgrehan#define	PREVENT_ALLOW		0x1E
86256056Sgrehan#define	READ_CAPACITY		0x25
87256056Sgrehan#define	READ_10			0x28
88256056Sgrehan#define	POSITION_TO_ELEMENT	0x2B
89256056Sgrehan#define	READ_TOC		0x43
90256056Sgrehan#define	GET_EVENT_STATUS_NOTIFICATION 0x4A
91256056Sgrehan#define	MODE_SENSE_10		0x5A
92280740Smav#define	REPORT_LUNS		0xA0
93256056Sgrehan#define	READ_12			0xA8
94256056Sgrehan#define	READ_CD			0xBE
95256056Sgrehan
96256056Sgrehan/*
97256056Sgrehan * SCSI mode page codes
98256056Sgrehan */
99256056Sgrehan#define	MODEPAGE_RW_ERROR_RECOVERY	0x01
100256056Sgrehan#define	MODEPAGE_CD_CAPABILITIES	0x2A
101256056Sgrehan
102256056Sgrehan/*
103267339Sjhb * ATA commands
104267339Sjhb */
105267339Sjhb#define	ATA_SF_ENAB_SATA_SF		0x10
106267339Sjhb#define		ATA_SATA_SF_AN		0x05
107267339Sjhb#define	ATA_SF_DIS_SATA_SF		0x90
108267339Sjhb
109267339Sjhb/*
110256056Sgrehan * Debug printf
111256056Sgrehan */
112256056Sgrehan#ifdef AHCI_DEBUG
113256056Sgrehanstatic FILE *dbg;
114256056Sgrehan#define DPRINTF(format, arg...)	do{fprintf(dbg, format, ##arg);fflush(dbg);}while(0)
115256056Sgrehan#else
116256056Sgrehan#define DPRINTF(format, arg...)
117256056Sgrehan#endif
118256056Sgrehan#define WPRINTF(format, arg...) printf(format, ##arg)
119256056Sgrehan
120256056Sgrehanstruct ahci_ioreq {
121256056Sgrehan	struct blockif_req io_req;
122256056Sgrehan	struct ahci_port *io_pr;
123276349Sneel	STAILQ_ENTRY(ahci_ioreq) io_flist;
124276349Sneel	TAILQ_ENTRY(ahci_ioreq) io_blist;
125256056Sgrehan	uint8_t *cfis;
126256056Sgrehan	uint32_t len;
127256056Sgrehan	uint32_t done;
128256056Sgrehan	int slot;
129282306Smav	int more;
130256056Sgrehan};
131256056Sgrehan
132256056Sgrehanstruct ahci_port {
133256056Sgrehan	struct blockif_ctxt *bctx;
134256056Sgrehan	struct pci_ahci_softc *pr_sc;
135256164Sdim	uint8_t *cmd_lst;
136256164Sdim	uint8_t *rfis;
137280745Smav	char ident[20 + 1];
138304421Smav	int port;
139256056Sgrehan	int atapi;
140256056Sgrehan	int reset;
141282846Smav	int waitforclear;
142256056Sgrehan	int mult_sectors;
143256056Sgrehan	uint8_t xfermode;
144280736Smav	uint8_t err_cfis[20];
145256056Sgrehan	uint8_t sense_key;
146256056Sgrehan	uint8_t asc;
147282846Smav	u_int ccs;
148267339Sjhb	uint32_t pending;
149256056Sgrehan
150256056Sgrehan	uint32_t clb;
151256056Sgrehan	uint32_t clbu;
152256056Sgrehan	uint32_t fb;
153256056Sgrehan	uint32_t fbu;
154256056Sgrehan	uint32_t is;
155256056Sgrehan	uint32_t ie;
156256056Sgrehan	uint32_t cmd;
157256056Sgrehan	uint32_t unused0;
158256056Sgrehan	uint32_t tfd;
159256056Sgrehan	uint32_t sig;
160256056Sgrehan	uint32_t ssts;
161256056Sgrehan	uint32_t sctl;
162256056Sgrehan	uint32_t serr;
163256056Sgrehan	uint32_t sact;
164256056Sgrehan	uint32_t ci;
165256056Sgrehan	uint32_t sntf;
166256056Sgrehan	uint32_t fbs;
167256056Sgrehan
168256056Sgrehan	/*
169256056Sgrehan	 * i/o request info
170256056Sgrehan	 */
171256056Sgrehan	struct ahci_ioreq *ioreq;
172256056Sgrehan	int ioqsz;
173256056Sgrehan	STAILQ_HEAD(ahci_fhead, ahci_ioreq) iofhd;
174276349Sneel	TAILQ_HEAD(ahci_bhead, ahci_ioreq) iobhd;
175256056Sgrehan};
176256056Sgrehan
177256056Sgrehanstruct ahci_cmd_hdr {
178256056Sgrehan	uint16_t flags;
179256056Sgrehan	uint16_t prdtl;
180256056Sgrehan	uint32_t prdbc;
181256056Sgrehan	uint64_t ctba;
182256056Sgrehan	uint32_t reserved[4];
183256056Sgrehan};
184256056Sgrehan
185256056Sgrehanstruct ahci_prdt_entry {
186256056Sgrehan	uint64_t dba;
187256056Sgrehan	uint32_t reserved;
188259301Sgrehan#define	DBCMASK		0x3fffff
189256056Sgrehan	uint32_t dbc;
190256056Sgrehan};
191256056Sgrehan
192256056Sgrehanstruct pci_ahci_softc {
193256056Sgrehan	struct pci_devinst *asc_pi;
194256056Sgrehan	pthread_mutex_t	mtx;
195256056Sgrehan	int ports;
196256056Sgrehan	uint32_t cap;
197256056Sgrehan	uint32_t ghc;
198256056Sgrehan	uint32_t is;
199256056Sgrehan	uint32_t pi;
200256056Sgrehan	uint32_t vs;
201256056Sgrehan	uint32_t ccc_ctl;
202256056Sgrehan	uint32_t ccc_pts;
203256056Sgrehan	uint32_t em_loc;
204256056Sgrehan	uint32_t em_ctl;
205256056Sgrehan	uint32_t cap2;
206256056Sgrehan	uint32_t bohc;
207267393Sjhb	uint32_t lintr;
208256056Sgrehan	struct ahci_port port[MAX_PORTS];
209256056Sgrehan};
210256056Sgrehan#define	ahci_ctx(sc)	((sc)->asc_pi->pi_vmctx)
211256056Sgrehan
212282846Smavstatic void ahci_handle_port(struct ahci_port *p);
213282846Smav
214256056Sgrehanstatic inline void lba_to_msf(uint8_t *buf, int lba)
215256056Sgrehan{
216256056Sgrehan	lba += 150;
217256056Sgrehan	buf[0] = (lba / 75) / 60;
218256056Sgrehan	buf[1] = (lba / 75) % 60;
219256056Sgrehan	buf[2] = lba % 75;
220256056Sgrehan}
221256056Sgrehan
222256056Sgrehan/*
223304421Smav * Generate HBA interrupts on global IS register write.
224256056Sgrehan */
225256056Sgrehanstatic void
226304421Smavahci_generate_intr(struct pci_ahci_softc *sc, uint32_t mask)
227256056Sgrehan{
228304421Smav	struct pci_devinst *pi = sc->asc_pi;
229304421Smav	struct ahci_port *p;
230304421Smav	int i, nmsg;
231304421Smav	uint32_t mmask;
232256056Sgrehan
233304421Smav	/* Update global IS from PxIS/PxIE. */
234256056Sgrehan	for (i = 0; i < sc->ports; i++) {
235304421Smav		p = &sc->port[i];
236304421Smav		if (p->is & p->ie)
237256056Sgrehan			sc->is |= (1 << i);
238256056Sgrehan	}
239304421Smav	DPRINTF("%s(%08x) %08x\n", __func__, mask, sc->is);
240256056Sgrehan
241304421Smav	/* If there is nothing enabled -- clear legacy interrupt and exit. */
242304421Smav	if (sc->is == 0 || (sc->ghc & AHCI_GHC_IE) == 0) {
243304421Smav		if (sc->lintr) {
244304421Smav			pci_lintr_deassert(pi);
245304421Smav			sc->lintr = 0;
246304421Smav		}
247304421Smav		return;
248304421Smav	}
249256056Sgrehan
250304421Smav	/* If there is anything and no MSI -- assert legacy interrupt. */
251304421Smav	nmsg = pci_msi_maxmsgnum(pi);
252304421Smav	if (nmsg == 0) {
253304421Smav		if (!sc->lintr) {
254267393Sjhb			sc->lintr = 1;
255267393Sjhb			pci_lintr_assert(pi);
256267393Sjhb		}
257304421Smav		return;
258267393Sjhb	}
259304421Smav
260304421Smav	/* Assert respective MSIs for ports that were touched. */
261304421Smav	for (i = 0; i < nmsg; i++) {
262304421Smav		if (sc->ports <= nmsg || i < nmsg - 1)
263304421Smav			mmask = 1 << i;
264304421Smav		else
265304421Smav			mmask = 0xffffffff << i;
266304421Smav		if (sc->is & mask && mmask & mask)
267304421Smav			pci_generate_msi(pi, i);
268304421Smav	}
269256056Sgrehan}
270256056Sgrehan
271304421Smav/*
272304421Smav * Generate HBA interrupt on specific port event.
273304421Smav */
274256056Sgrehanstatic void
275304421Smavahci_port_intr(struct ahci_port *p)
276304421Smav{
277304421Smav	struct pci_ahci_softc *sc = p->pr_sc;
278304421Smav	struct pci_devinst *pi = sc->asc_pi;
279304421Smav	int nmsg;
280304421Smav
281304421Smav	DPRINTF("%s(%d) %08x/%08x %08x\n", __func__,
282304421Smav	    p->port, p->is, p->ie, sc->is);
283304421Smav
284304421Smav	/* If there is nothing enabled -- we are done. */
285304421Smav	if ((p->is & p->ie) == 0)
286304421Smav		return;
287304421Smav
288304421Smav	/* In case of non-shared MSI always generate interrupt. */
289304421Smav	nmsg = pci_msi_maxmsgnum(pi);
290304421Smav	if (sc->ports <= nmsg || p->port < nmsg - 1) {
291304421Smav		sc->is |= (1 << p->port);
292304421Smav		if ((sc->ghc & AHCI_GHC_IE) == 0)
293304421Smav			return;
294304421Smav		pci_generate_msi(pi, p->port);
295304421Smav		return;
296304421Smav	}
297304421Smav
298304421Smav	/* If IS for this port is already set -- do nothing. */
299304421Smav	if (sc->is & (1 << p->port))
300304421Smav		return;
301304421Smav
302304421Smav	sc->is |= (1 << p->port);
303304421Smav
304304421Smav	/* If interrupts are enabled -- generate one. */
305304421Smav	if ((sc->ghc & AHCI_GHC_IE) == 0)
306304421Smav		return;
307304421Smav	if (nmsg > 0) {
308304421Smav		pci_generate_msi(pi, nmsg - 1);
309304421Smav	} else if (!sc->lintr) {
310304421Smav		sc->lintr = 1;
311304421Smav		pci_lintr_assert(pi);
312304421Smav	}
313304421Smav}
314304421Smav
315304421Smavstatic void
316256056Sgrehanahci_write_fis(struct ahci_port *p, enum sata_fis_type ft, uint8_t *fis)
317256056Sgrehan{
318256056Sgrehan	int offset, len, irq;
319256056Sgrehan
320256164Sdim	if (p->rfis == NULL || !(p->cmd & AHCI_P_CMD_FRE))
321256056Sgrehan		return;
322256056Sgrehan
323256056Sgrehan	switch (ft) {
324256056Sgrehan	case FIS_TYPE_REGD2H:
325256056Sgrehan		offset = 0x40;
326256056Sgrehan		len = 20;
327282846Smav		irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_DHR : 0;
328256056Sgrehan		break;
329256056Sgrehan	case FIS_TYPE_SETDEVBITS:
330256056Sgrehan		offset = 0x58;
331256056Sgrehan		len = 8;
332282846Smav		irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_SDB : 0;
333256056Sgrehan		break;
334256056Sgrehan	case FIS_TYPE_PIOSETUP:
335256056Sgrehan		offset = 0x20;
336256056Sgrehan		len = 20;
337282846Smav		irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_PS : 0;
338256056Sgrehan		break;
339256056Sgrehan	default:
340256056Sgrehan		WPRINTF("unsupported fis type %d\n", ft);
341256056Sgrehan		return;
342256056Sgrehan	}
343282846Smav	if (fis[2] & ATA_S_ERROR) {
344282846Smav		p->waitforclear = 1;
345282846Smav		irq |= AHCI_P_IX_TFE;
346282846Smav	}
347256056Sgrehan	memcpy(p->rfis + offset, fis, len);
348256056Sgrehan	if (irq) {
349304421Smav		if (~p->is & irq) {
350304421Smav			p->is |= irq;
351304421Smav			ahci_port_intr(p);
352304421Smav		}
353256056Sgrehan	}
354256056Sgrehan}
355256056Sgrehan
356256056Sgrehanstatic void
357267339Sjhbahci_write_fis_piosetup(struct ahci_port *p)
358267339Sjhb{
359267339Sjhb	uint8_t fis[20];
360267339Sjhb
361267339Sjhb	memset(fis, 0, sizeof(fis));
362267339Sjhb	fis[0] = FIS_TYPE_PIOSETUP;
363267339Sjhb	ahci_write_fis(p, FIS_TYPE_PIOSETUP, fis);
364267339Sjhb}
365267339Sjhb
366267339Sjhbstatic void
367280736Smavahci_write_fis_sdb(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t tfd)
368256056Sgrehan{
369256056Sgrehan	uint8_t fis[8];
370256056Sgrehan	uint8_t error;
371256056Sgrehan
372256056Sgrehan	error = (tfd >> 8) & 0xff;
373282846Smav	tfd &= 0x77;
374256056Sgrehan	memset(fis, 0, sizeof(fis));
375280736Smav	fis[0] = FIS_TYPE_SETDEVBITS;
376280736Smav	fis[1] = (1 << 6);
377282846Smav	fis[2] = tfd;
378280736Smav	fis[3] = error;
379280736Smav	if (fis[2] & ATA_S_ERROR) {
380280736Smav		p->err_cfis[0] = slot;
381282846Smav		p->err_cfis[2] = tfd;
382280736Smav		p->err_cfis[3] = error;
383280736Smav		memcpy(&p->err_cfis[4], cfis + 4, 16);
384280736Smav	} else {
385280736Smav		*(uint32_t *)(fis + 4) = (1 << slot);
386280736Smav		p->sact &= ~(1 << slot);
387280736Smav	}
388282846Smav	p->tfd &= ~0x77;
389282846Smav	p->tfd |= tfd;
390256056Sgrehan	ahci_write_fis(p, FIS_TYPE_SETDEVBITS, fis);
391256056Sgrehan}
392256056Sgrehan
393256056Sgrehanstatic void
394256056Sgrehanahci_write_fis_d2h(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t tfd)
395256056Sgrehan{
396256056Sgrehan	uint8_t fis[20];
397256056Sgrehan	uint8_t error;
398256056Sgrehan
399256056Sgrehan	error = (tfd >> 8) & 0xff;
400256056Sgrehan	memset(fis, 0, sizeof(fis));
401256056Sgrehan	fis[0] = FIS_TYPE_REGD2H;
402256056Sgrehan	fis[1] = (1 << 6);
403256056Sgrehan	fis[2] = tfd & 0xff;
404256056Sgrehan	fis[3] = error;
405256056Sgrehan	fis[4] = cfis[4];
406256056Sgrehan	fis[5] = cfis[5];
407256056Sgrehan	fis[6] = cfis[6];
408256056Sgrehan	fis[7] = cfis[7];
409256056Sgrehan	fis[8] = cfis[8];
410256056Sgrehan	fis[9] = cfis[9];
411256056Sgrehan	fis[10] = cfis[10];
412256056Sgrehan	fis[11] = cfis[11];
413256056Sgrehan	fis[12] = cfis[12];
414256056Sgrehan	fis[13] = cfis[13];
415280736Smav	if (fis[2] & ATA_S_ERROR) {
416280736Smav		p->err_cfis[0] = 0x80;
417280736Smav		p->err_cfis[2] = tfd & 0xff;
418280736Smav		p->err_cfis[3] = error;
419280736Smav		memcpy(&p->err_cfis[4], cfis + 4, 16);
420280736Smav	} else
421270159Sgrehan		p->ci &= ~(1 << slot);
422256056Sgrehan	p->tfd = tfd;
423256056Sgrehan	ahci_write_fis(p, FIS_TYPE_REGD2H, fis);
424256056Sgrehan}
425256056Sgrehan
426256056Sgrehanstatic void
427282846Smavahci_write_fis_d2h_ncq(struct ahci_port *p, int slot)
428282846Smav{
429282846Smav	uint8_t fis[20];
430282846Smav
431282846Smav	p->tfd = ATA_S_READY | ATA_S_DSC;
432282846Smav	memset(fis, 0, sizeof(fis));
433282846Smav	fis[0] = FIS_TYPE_REGD2H;
434282846Smav	fis[1] = 0;			/* No interrupt */
435282846Smav	fis[2] = p->tfd;		/* Status */
436282846Smav	fis[3] = 0;			/* No error */
437282846Smav	p->ci &= ~(1 << slot);
438282846Smav	ahci_write_fis(p, FIS_TYPE_REGD2H, fis);
439282846Smav}
440282846Smav
441282846Smavstatic void
442256056Sgrehanahci_write_reset_fis_d2h(struct ahci_port *p)
443256056Sgrehan{
444256056Sgrehan	uint8_t fis[20];
445256056Sgrehan
446256056Sgrehan	memset(fis, 0, sizeof(fis));
447256056Sgrehan	fis[0] = FIS_TYPE_REGD2H;
448256056Sgrehan	fis[3] = 1;
449256056Sgrehan	fis[4] = 1;
450256056Sgrehan	if (p->atapi) {
451256056Sgrehan		fis[5] = 0x14;
452256056Sgrehan		fis[6] = 0xeb;
453256056Sgrehan	}
454256056Sgrehan	fis[12] = 1;
455256056Sgrehan	ahci_write_fis(p, FIS_TYPE_REGD2H, fis);
456256056Sgrehan}
457256056Sgrehan
458256056Sgrehanstatic void
459276349Sneelahci_check_stopped(struct ahci_port *p)
460276349Sneel{
461276349Sneel	/*
462276349Sneel	 * If we are no longer processing the command list and nothing
463276429Sneel	 * is in-flight, clear the running bit, the current command
464276429Sneel	 * slot, the command issue and active bits.
465276349Sneel	 */
466276349Sneel	if (!(p->cmd & AHCI_P_CMD_ST)) {
467276429Sneel		if (p->pending == 0) {
468282846Smav			p->ccs = 0;
469276349Sneel			p->cmd &= ~(AHCI_P_CMD_CR | AHCI_P_CMD_CCS_MASK);
470276429Sneel			p->ci = 0;
471276429Sneel			p->sact = 0;
472282846Smav			p->waitforclear = 0;
473276429Sneel		}
474276349Sneel	}
475276349Sneel}
476276349Sneel
477276349Sneelstatic void
478276349Sneelahci_port_stop(struct ahci_port *p)
479276349Sneel{
480276349Sneel	struct ahci_ioreq *aior;
481276349Sneel	uint8_t *cfis;
482276349Sneel	int slot;
483276349Sneel	int ncq;
484276349Sneel	int error;
485276349Sneel
486276349Sneel	assert(pthread_mutex_isowned_np(&p->pr_sc->mtx));
487276349Sneel
488276349Sneel	TAILQ_FOREACH(aior, &p->iobhd, io_blist) {
489276349Sneel		/*
490276349Sneel		 * Try to cancel the outstanding blockif request.
491276349Sneel		 */
492276349Sneel		error = blockif_cancel(p->bctx, &aior->io_req);
493276349Sneel		if (error != 0)
494276349Sneel			continue;
495276349Sneel
496276349Sneel		slot = aior->slot;
497276349Sneel		cfis = aior->cfis;
498276349Sneel		if (cfis[2] == ATA_WRITE_FPDMA_QUEUED ||
499282844Smav		    cfis[2] == ATA_READ_FPDMA_QUEUED ||
500282844Smav		    cfis[2] == ATA_SEND_FPDMA_QUEUED)
501276349Sneel			ncq = 1;
502276349Sneel
503276349Sneel		if (ncq)
504276349Sneel			p->sact &= ~(1 << slot);
505276349Sneel		else
506276349Sneel			p->ci &= ~(1 << slot);
507276349Sneel
508276349Sneel		/*
509276349Sneel		 * This command is now done.
510276349Sneel		 */
511276349Sneel		p->pending &= ~(1 << slot);
512276349Sneel
513276349Sneel		/*
514276349Sneel		 * Delete the blockif request from the busy list
515276349Sneel		 */
516276349Sneel		TAILQ_REMOVE(&p->iobhd, aior, io_blist);
517276349Sneel
518276349Sneel		/*
519276349Sneel		 * Move the blockif request back to the free list
520276349Sneel		 */
521276349Sneel		STAILQ_INSERT_TAIL(&p->iofhd, aior, io_flist);
522276349Sneel	}
523276349Sneel
524276349Sneel	ahci_check_stopped(p);
525276349Sneel}
526276349Sneel
527276349Sneelstatic void
528256056Sgrehanahci_port_reset(struct ahci_port *pr)
529256056Sgrehan{
530256056Sgrehan	pr->serr = 0;
531256056Sgrehan	pr->sact = 0;
532256056Sgrehan	pr->xfermode = ATA_UDMA6;
533256056Sgrehan	pr->mult_sectors = 128;
534256056Sgrehan
535256056Sgrehan	if (!pr->bctx) {
536256056Sgrehan		pr->ssts = ATA_SS_DET_NO_DEVICE;
537256056Sgrehan		pr->sig = 0xFFFFFFFF;
538256056Sgrehan		pr->tfd = 0x7F;
539256056Sgrehan		return;
540256056Sgrehan	}
541280733Smav	pr->ssts = ATA_SS_DET_PHY_ONLINE | ATA_SS_IPM_ACTIVE;
542280733Smav	if (pr->sctl & ATA_SC_SPD_MASK)
543280733Smav		pr->ssts |= (pr->sctl & ATA_SC_SPD_MASK);
544280733Smav	else
545280733Smav		pr->ssts |= ATA_SS_SPD_GEN3;
546256056Sgrehan	pr->tfd = (1 << 8) | ATA_S_DSC | ATA_S_DMA;
547256056Sgrehan	if (!pr->atapi) {
548256056Sgrehan		pr->sig = PxSIG_ATA;
549256056Sgrehan		pr->tfd |= ATA_S_READY;
550256056Sgrehan	} else
551256056Sgrehan		pr->sig = PxSIG_ATAPI;
552256056Sgrehan	ahci_write_reset_fis_d2h(pr);
553256056Sgrehan}
554256056Sgrehan
555256056Sgrehanstatic void
556256056Sgrehanahci_reset(struct pci_ahci_softc *sc)
557256056Sgrehan{
558256056Sgrehan	int i;
559256056Sgrehan
560256056Sgrehan	sc->ghc = AHCI_GHC_AE;
561256056Sgrehan	sc->is = 0;
562267393Sjhb
563267393Sjhb	if (sc->lintr) {
564267393Sjhb		pci_lintr_deassert(sc->asc_pi);
565267393Sjhb		sc->lintr = 0;
566267393Sjhb	}
567267393Sjhb
568256056Sgrehan	for (i = 0; i < sc->ports; i++) {
569256056Sgrehan		sc->port[i].ie = 0;
570256056Sgrehan		sc->port[i].is = 0;
571282845Smav		sc->port[i].cmd = (AHCI_P_CMD_SUD | AHCI_P_CMD_POD);
572282845Smav		if (sc->port[i].bctx)
573282845Smav			sc->port[i].cmd |= AHCI_P_CMD_CPS;
574280733Smav		sc->port[i].sctl = 0;
575256056Sgrehan		ahci_port_reset(&sc->port[i]);
576256056Sgrehan	}
577256056Sgrehan}
578256056Sgrehan
579256056Sgrehanstatic void
580256056Sgrehanata_string(uint8_t *dest, const char *src, int len)
581256056Sgrehan{
582256056Sgrehan	int i;
583256056Sgrehan
584256056Sgrehan	for (i = 0; i < len; i++) {
585256056Sgrehan		if (*src)
586256056Sgrehan			dest[i ^ 1] = *src++;
587256056Sgrehan		else
588256056Sgrehan			dest[i ^ 1] = ' ';
589256056Sgrehan	}
590256056Sgrehan}
591256056Sgrehan
592256056Sgrehanstatic void
593256056Sgrehanatapi_string(uint8_t *dest, const char *src, int len)
594256056Sgrehan{
595256056Sgrehan	int i;
596256056Sgrehan
597256056Sgrehan	for (i = 0; i < len; i++) {
598256056Sgrehan		if (*src)
599256056Sgrehan			dest[i] = *src++;
600256056Sgrehan		else
601256056Sgrehan			dest[i] = ' ';
602256056Sgrehan	}
603256056Sgrehan}
604256056Sgrehan
605282306Smav/*
606282306Smav * Build up the iovec based on the PRDT, 'done' and 'len'.
607282306Smav */
608256056Sgrehanstatic void
609282306Smavahci_build_iov(struct ahci_port *p, struct ahci_ioreq *aior,
610282306Smav    struct ahci_prdt_entry *prdt, uint16_t prdtl)
611256056Sgrehan{
612282306Smav	struct blockif_req *breq = &aior->io_req;
613282306Smav	int i, j, skip, todo, left, extra;
614282306Smav	uint32_t dbcsz;
615282306Smav
616282306Smav	/* Copy part of PRDT between 'done' and 'len' bytes into the iov. */
617282306Smav	skip = aior->done;
618282306Smav	left = aior->len - aior->done;
619282306Smav	todo = 0;
620282306Smav	for (i = 0, j = 0; i < prdtl && j < BLOCKIF_IOV_MAX && left > 0;
621282306Smav	    i++, prdt++) {
622282306Smav		dbcsz = (prdt->dbc & DBCMASK) + 1;
623282306Smav		/* Skip already done part of the PRDT */
624282306Smav		if (dbcsz <= skip) {
625282306Smav			skip -= dbcsz;
626282306Smav			continue;
627282306Smav		}
628282306Smav		dbcsz -= skip;
629282306Smav		if (dbcsz > left)
630282306Smav			dbcsz = left;
631282306Smav		breq->br_iov[j].iov_base = paddr_guest2host(ahci_ctx(p->pr_sc),
632282306Smav		    prdt->dba + skip, dbcsz);
633282306Smav		breq->br_iov[j].iov_len = dbcsz;
634282306Smav		todo += dbcsz;
635282306Smav		left -= dbcsz;
636282306Smav		skip = 0;
637282306Smav		j++;
638282306Smav	}
639282306Smav
640282306Smav	/* If we got limited by IOV length, round I/O down to sector size. */
641282306Smav	if (j == BLOCKIF_IOV_MAX) {
642282306Smav		extra = todo % blockif_sectsz(p->bctx);
643282306Smav		todo -= extra;
644282306Smav		assert(todo > 0);
645282306Smav		while (extra > 0) {
646282306Smav			if (breq->br_iov[j - 1].iov_len > extra) {
647282306Smav				breq->br_iov[j - 1].iov_len -= extra;
648282306Smav				break;
649282306Smav			}
650282306Smav			extra -= breq->br_iov[j - 1].iov_len;
651282306Smav			j--;
652282306Smav		}
653282306Smav	}
654282306Smav
655282306Smav	breq->br_iovcnt = j;
656282307Smav	breq->br_resid = todo;
657282306Smav	aior->done += todo;
658282306Smav	aior->more = (aior->done < aior->len && i < prdtl);
659282306Smav}
660282306Smav
661282306Smavstatic void
662282306Smavahci_handle_rw(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done)
663282306Smav{
664256056Sgrehan	struct ahci_ioreq *aior;
665256056Sgrehan	struct blockif_req *breq;
666256056Sgrehan	struct ahci_prdt_entry *prdt;
667256056Sgrehan	struct ahci_cmd_hdr *hdr;
668256056Sgrehan	uint64_t lba;
669256056Sgrehan	uint32_t len;
670282846Smav	int err, first, ncq, readop;
671256056Sgrehan
672256056Sgrehan	prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
673256164Sdim	hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
674256056Sgrehan	ncq = 0;
675256056Sgrehan	readop = 1;
676282846Smav	first = (done == 0);
677256056Sgrehan
678280732Smav	if (cfis[2] == ATA_WRITE || cfis[2] == ATA_WRITE48 ||
679280732Smav	    cfis[2] == ATA_WRITE_MUL || cfis[2] == ATA_WRITE_MUL48 ||
680280732Smav	    cfis[2] == ATA_WRITE_DMA || cfis[2] == ATA_WRITE_DMA48 ||
681280732Smav	    cfis[2] == ATA_WRITE_FPDMA_QUEUED)
682256056Sgrehan		readop = 0;
683256056Sgrehan
684256056Sgrehan	if (cfis[2] == ATA_WRITE_FPDMA_QUEUED ||
685280732Smav	    cfis[2] == ATA_READ_FPDMA_QUEUED) {
686256056Sgrehan		lba = ((uint64_t)cfis[10] << 40) |
687256056Sgrehan			((uint64_t)cfis[9] << 32) |
688256056Sgrehan			((uint64_t)cfis[8] << 24) |
689256056Sgrehan			((uint64_t)cfis[6] << 16) |
690256056Sgrehan			((uint64_t)cfis[5] << 8) |
691256056Sgrehan			cfis[4];
692256056Sgrehan		len = cfis[11] << 8 | cfis[3];
693256056Sgrehan		if (!len)
694256056Sgrehan			len = 65536;
695256056Sgrehan		ncq = 1;
696280732Smav	} else if (cfis[2] == ATA_READ48 || cfis[2] == ATA_WRITE48 ||
697280732Smav	    cfis[2] == ATA_READ_MUL48 || cfis[2] == ATA_WRITE_MUL48 ||
698280732Smav	    cfis[2] == ATA_READ_DMA48 || cfis[2] == ATA_WRITE_DMA48) {
699256056Sgrehan		lba = ((uint64_t)cfis[10] << 40) |
700256056Sgrehan			((uint64_t)cfis[9] << 32) |
701256056Sgrehan			((uint64_t)cfis[8] << 24) |
702256056Sgrehan			((uint64_t)cfis[6] << 16) |
703256056Sgrehan			((uint64_t)cfis[5] << 8) |
704256056Sgrehan			cfis[4];
705256056Sgrehan		len = cfis[13] << 8 | cfis[12];
706256056Sgrehan		if (!len)
707256056Sgrehan			len = 65536;
708256056Sgrehan	} else {
709256056Sgrehan		lba = ((cfis[7] & 0xf) << 24) | (cfis[6] << 16) |
710256056Sgrehan			(cfis[5] << 8) | cfis[4];
711256056Sgrehan		len = cfis[12];
712256056Sgrehan		if (!len)
713256056Sgrehan			len = 256;
714256056Sgrehan	}
715256056Sgrehan	lba *= blockif_sectsz(p->bctx);
716256056Sgrehan	len *= blockif_sectsz(p->bctx);
717256056Sgrehan
718282306Smav	/* Pull request off free list */
719256056Sgrehan	aior = STAILQ_FIRST(&p->iofhd);
720256056Sgrehan	assert(aior != NULL);
721276349Sneel	STAILQ_REMOVE_HEAD(&p->iofhd, io_flist);
722282306Smav
723256056Sgrehan	aior->cfis = cfis;
724256056Sgrehan	aior->slot = slot;
725256056Sgrehan	aior->len = len;
726256056Sgrehan	aior->done = done;
727256056Sgrehan	breq = &aior->io_req;
728256056Sgrehan	breq->br_offset = lba + done;
729282306Smav	ahci_build_iov(p, aior, prdt, hdr->prdtl);
730256056Sgrehan
731282306Smav	/* Mark this command in-flight. */
732276349Sneel	p->pending |= 1 << slot;
733276349Sneel
734282306Smav	/* Stuff request onto busy list. */
735276349Sneel	TAILQ_INSERT_HEAD(&p->iobhd, aior, io_blist);
736276349Sneel
737282846Smav	if (ncq && first)
738282846Smav		ahci_write_fis_d2h_ncq(p, slot);
739282846Smav
740256056Sgrehan	if (readop)
741256056Sgrehan		err = blockif_read(p->bctx, breq);
742256056Sgrehan	else
743256056Sgrehan		err = blockif_write(p->bctx, breq);
744256056Sgrehan	assert(err == 0);
745256056Sgrehan}
746256056Sgrehan
747256056Sgrehanstatic void
748256056Sgrehanahci_handle_flush(struct ahci_port *p, int slot, uint8_t *cfis)
749256056Sgrehan{
750256056Sgrehan	struct ahci_ioreq *aior;
751256056Sgrehan	struct blockif_req *breq;
752256056Sgrehan	int err;
753256056Sgrehan
754256056Sgrehan	/*
755256056Sgrehan	 * Pull request off free list
756256056Sgrehan	 */
757256056Sgrehan	aior = STAILQ_FIRST(&p->iofhd);
758256056Sgrehan	assert(aior != NULL);
759276349Sneel	STAILQ_REMOVE_HEAD(&p->iofhd, io_flist);
760256056Sgrehan	aior->cfis = cfis;
761256056Sgrehan	aior->slot = slot;
762256056Sgrehan	aior->len = 0;
763267339Sjhb	aior->done = 0;
764282306Smav	aior->more = 0;
765256056Sgrehan	breq = &aior->io_req;
766256056Sgrehan
767276349Sneel	/*
768276349Sneel	 * Mark this command in-flight.
769276349Sneel	 */
770276349Sneel	p->pending |= 1 << slot;
771276349Sneel
772276349Sneel	/*
773276349Sneel	 * Stuff request onto busy list
774276349Sneel	 */
775276349Sneel	TAILQ_INSERT_HEAD(&p->iobhd, aior, io_blist);
776276349Sneel
777256056Sgrehan	err = blockif_flush(p->bctx, breq);
778256056Sgrehan	assert(err == 0);
779256056Sgrehan}
780256056Sgrehan
781256056Sgrehanstatic inline void
782280370Smavread_prdt(struct ahci_port *p, int slot, uint8_t *cfis,
783280370Smav		void *buf, int size)
784280370Smav{
785280370Smav	struct ahci_cmd_hdr *hdr;
786280370Smav	struct ahci_prdt_entry *prdt;
787280370Smav	void *to;
788280370Smav	int i, len;
789280370Smav
790280370Smav	hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
791280370Smav	len = size;
792280370Smav	to = buf;
793280370Smav	prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
794280370Smav	for (i = 0; i < hdr->prdtl && len; i++) {
795280370Smav		uint8_t *ptr;
796280370Smav		uint32_t dbcsz;
797280370Smav		int sublen;
798280370Smav
799280370Smav		dbcsz = (prdt->dbc & DBCMASK) + 1;
800280370Smav		ptr = paddr_guest2host(ahci_ctx(p->pr_sc), prdt->dba, dbcsz);
801341606Semaste		sublen = MIN(len, dbcsz);
802280370Smav		memcpy(to, ptr, sublen);
803280370Smav		len -= sublen;
804280370Smav		to += sublen;
805280370Smav		prdt++;
806280370Smav	}
807280370Smav}
808280370Smav
809280370Smavstatic void
810280370Smavahci_handle_dsm_trim(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done)
811280370Smav{
812280370Smav	struct ahci_ioreq *aior;
813280370Smav	struct blockif_req *breq;
814280370Smav	uint8_t *entry;
815280370Smav	uint64_t elba;
816280370Smav	uint32_t len, elen;
817282846Smav	int err, first, ncq;
818280370Smav	uint8_t buf[512];
819280370Smav
820282846Smav	first = (done == 0);
821280738Smav	if (cfis[2] == ATA_DATA_SET_MANAGEMENT) {
822280738Smav		len = (uint16_t)cfis[13] << 8 | cfis[12];
823280738Smav		len *= 512;
824282846Smav		ncq = 0;
825280738Smav	} else { /* ATA_SEND_FPDMA_QUEUED */
826280738Smav		len = (uint16_t)cfis[11] << 8 | cfis[3];
827280738Smav		len *= 512;
828282846Smav		ncq = 1;
829280738Smav	}
830280370Smav	read_prdt(p, slot, cfis, buf, sizeof(buf));
831280370Smav
832280370Smavnext:
833280370Smav	entry = &buf[done];
834280370Smav	elba = ((uint64_t)entry[5] << 40) |
835280370Smav		((uint64_t)entry[4] << 32) |
836280370Smav		((uint64_t)entry[3] << 24) |
837280370Smav		((uint64_t)entry[2] << 16) |
838280370Smav		((uint64_t)entry[1] << 8) |
839280370Smav		entry[0];
840280370Smav	elen = (uint16_t)entry[7] << 8 | entry[6];
841280370Smav	done += 8;
842280370Smav	if (elen == 0) {
843280370Smav		if (done >= len) {
844303139Smav			if (ncq) {
845303139Smav				if (first)
846303139Smav					ahci_write_fis_d2h_ncq(p, slot);
847303139Smav				ahci_write_fis_sdb(p, slot, cfis,
848303139Smav				    ATA_S_READY | ATA_S_DSC);
849303139Smav			} else {
850303139Smav				ahci_write_fis_d2h(p, slot, cfis,
851303139Smav				    ATA_S_READY | ATA_S_DSC);
852303139Smav			}
853280370Smav			p->pending &= ~(1 << slot);
854280370Smav			ahci_check_stopped(p);
855282846Smav			if (!first)
856282846Smav				ahci_handle_port(p);
857280370Smav			return;
858280370Smav		}
859280370Smav		goto next;
860280370Smav	}
861280370Smav
862280370Smav	/*
863280370Smav	 * Pull request off free list
864280370Smav	 */
865280370Smav	aior = STAILQ_FIRST(&p->iofhd);
866280370Smav	assert(aior != NULL);
867280370Smav	STAILQ_REMOVE_HEAD(&p->iofhd, io_flist);
868280370Smav	aior->cfis = cfis;
869280370Smav	aior->slot = slot;
870280370Smav	aior->len = len;
871280370Smav	aior->done = done;
872282306Smav	aior->more = (len != done);
873280370Smav
874280370Smav	breq = &aior->io_req;
875280370Smav	breq->br_offset = elba * blockif_sectsz(p->bctx);
876282307Smav	breq->br_resid = elen * blockif_sectsz(p->bctx);
877280370Smav
878280370Smav	/*
879280370Smav	 * Mark this command in-flight.
880280370Smav	 */
881280370Smav	p->pending |= 1 << slot;
882280370Smav
883280370Smav	/*
884280370Smav	 * Stuff request onto busy list
885280370Smav	 */
886280370Smav	TAILQ_INSERT_HEAD(&p->iobhd, aior, io_blist);
887280370Smav
888282846Smav	if (ncq && first)
889282846Smav		ahci_write_fis_d2h_ncq(p, slot);
890282846Smav
891280370Smav	err = blockif_delete(p->bctx, breq);
892280370Smav	assert(err == 0);
893280370Smav}
894280370Smav
895280370Smavstatic inline void
896256056Sgrehanwrite_prdt(struct ahci_port *p, int slot, uint8_t *cfis,
897256056Sgrehan		void *buf, int size)
898256056Sgrehan{
899256056Sgrehan	struct ahci_cmd_hdr *hdr;
900256056Sgrehan	struct ahci_prdt_entry *prdt;
901256056Sgrehan	void *from;
902256056Sgrehan	int i, len;
903256056Sgrehan
904256164Sdim	hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
905256056Sgrehan	len = size;
906256056Sgrehan	from = buf;
907256056Sgrehan	prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
908256056Sgrehan	for (i = 0; i < hdr->prdtl && len; i++) {
909259301Sgrehan		uint8_t *ptr;
910259301Sgrehan		uint32_t dbcsz;
911267339Sjhb		int sublen;
912259301Sgrehan
913259301Sgrehan		dbcsz = (prdt->dbc & DBCMASK) + 1;
914259301Sgrehan		ptr = paddr_guest2host(ahci_ctx(p->pr_sc), prdt->dba, dbcsz);
915341606Semaste		sublen = MIN(len, dbcsz);
916267339Sjhb		memcpy(ptr, from, sublen);
917267339Sjhb		len -= sublen;
918267339Sjhb		from += sublen;
919256056Sgrehan		prdt++;
920256056Sgrehan	}
921256056Sgrehan	hdr->prdbc = size - len;
922256056Sgrehan}
923256056Sgrehan
924256056Sgrehanstatic void
925280741Smavahci_checksum(uint8_t *buf, int size)
926280741Smav{
927280741Smav	int i;
928280741Smav	uint8_t sum = 0;
929280741Smav
930280741Smav	for (i = 0; i < size - 1; i++)
931280741Smav		sum += buf[i];
932280741Smav	buf[size - 1] = 0x100 - sum;
933280741Smav}
934280741Smav
935280741Smavstatic void
936280736Smavahci_handle_read_log(struct ahci_port *p, int slot, uint8_t *cfis)
937280736Smav{
938280736Smav	struct ahci_cmd_hdr *hdr;
939317001Smav	uint32_t buf[128];
940317001Smav	uint8_t *buf8 = (uint8_t *)buf;
941317001Smav	uint16_t *buf16 = (uint16_t *)buf;
942280736Smav
943280736Smav	hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
944317001Smav	if (p->atapi || hdr->prdtl == 0 || cfis[5] != 0 ||
945317001Smav	    cfis[9] != 0 || cfis[12] != 1 || cfis[13] != 0) {
946280736Smav		ahci_write_fis_d2h(p, slot, cfis,
947280736Smav		    (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
948280736Smav		return;
949280736Smav	}
950280736Smav
951280736Smav	memset(buf, 0, sizeof(buf));
952317001Smav	if (cfis[4] == 0x00) {	/* Log directory */
953317001Smav		buf16[0x00] = 1; /* Version -- 1 */
954317001Smav		buf16[0x10] = 1; /* NCQ Command Error Log -- 1 page */
955317001Smav		buf16[0x13] = 1; /* SATA NCQ Send and Receive Log -- 1 page */
956317001Smav	} else if (cfis[4] == 0x10) {	/* NCQ Command Error Log */
957317001Smav		memcpy(buf8, p->err_cfis, sizeof(p->err_cfis));
958317001Smav		ahci_checksum(buf8, sizeof(buf));
959317001Smav	} else if (cfis[4] == 0x13) {	/* SATA NCQ Send and Receive Log */
960317001Smav		if (blockif_candelete(p->bctx) && !blockif_is_ro(p->bctx)) {
961317001Smav			buf[0x00] = 1;	/* SFQ DSM supported */
962317001Smav			buf[0x01] = 1;	/* SFQ DSM TRIM supported */
963317001Smav		}
964317001Smav	} else {
965317001Smav		ahci_write_fis_d2h(p, slot, cfis,
966317001Smav		    (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
967317001Smav		return;
968317001Smav	}
969280736Smav
970280736Smav	if (cfis[2] == ATA_READ_LOG_EXT)
971280736Smav		ahci_write_fis_piosetup(p);
972280736Smav	write_prdt(p, slot, cfis, (void *)buf, sizeof(buf));
973280736Smav	ahci_write_fis_d2h(p, slot, cfis, ATA_S_DSC | ATA_S_READY);
974280736Smav}
975280736Smav
976280736Smavstatic void
977256056Sgrehanhandle_identify(struct ahci_port *p, int slot, uint8_t *cfis)
978256056Sgrehan{
979256056Sgrehan	struct ahci_cmd_hdr *hdr;
980256056Sgrehan
981256164Sdim	hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
982256056Sgrehan	if (p->atapi || hdr->prdtl == 0) {
983280731Smav		ahci_write_fis_d2h(p, slot, cfis,
984280731Smav		    (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
985256056Sgrehan	} else {
986256056Sgrehan		uint16_t buf[256];
987256056Sgrehan		uint64_t sectors;
988280370Smav		int sectsz, psectsz, psectoff, candelete, ro;
989270159Sgrehan		uint16_t cyl;
990270159Sgrehan		uint8_t sech, heads;
991256056Sgrehan
992280370Smav		ro = blockif_is_ro(p->bctx);
993280370Smav		candelete = blockif_candelete(p->bctx);
994280244Smav		sectsz = blockif_sectsz(p->bctx);
995280244Smav		sectors = blockif_size(p->bctx) / sectsz;
996270159Sgrehan		blockif_chs(p->bctx, &cyl, &heads, &sech);
997280244Smav		blockif_psectsz(p->bctx, &psectsz, &psectoff);
998256056Sgrehan		memset(buf, 0, sizeof(buf));
999256056Sgrehan		buf[0] = 0x0040;
1000270159Sgrehan		buf[1] = cyl;
1001270159Sgrehan		buf[3] = heads;
1002270159Sgrehan		buf[6] = sech;
1003280745Smav		ata_string((uint8_t *)(buf+10), p->ident, 20);
1004256056Sgrehan		ata_string((uint8_t *)(buf+23), "001", 8);
1005256056Sgrehan		ata_string((uint8_t *)(buf+27), "BHYVE SATA DISK", 40);
1006256056Sgrehan		buf[47] = (0x8000 | 128);
1007295124Sgrehan		buf[48] = 0;
1008256056Sgrehan		buf[49] = (1 << 8 | 1 << 9 | 1 << 11);
1009256056Sgrehan		buf[50] = (1 << 14);
1010256056Sgrehan		buf[53] = (1 << 1 | 1 << 2);
1011256056Sgrehan		if (p->mult_sectors)
1012256056Sgrehan			buf[59] = (0x100 | p->mult_sectors);
1013280733Smav		if (sectors <= 0x0fffffff) {
1014280733Smav			buf[60] = sectors;
1015280733Smav			buf[61] = (sectors >> 16);
1016280733Smav		} else {
1017280733Smav			buf[60] = 0xffff;
1018280733Smav			buf[61] = 0x0fff;
1019280733Smav		}
1020256056Sgrehan		buf[63] = 0x7;
1021256056Sgrehan		if (p->xfermode & ATA_WDMA0)
1022256056Sgrehan			buf[63] |= (1 << ((p->xfermode & 7) + 8));
1023256056Sgrehan		buf[64] = 0x3;
1024280733Smav		buf[65] = 120;
1025280733Smav		buf[66] = 120;
1026280733Smav		buf[67] = 120;
1027280733Smav		buf[68] = 120;
1028280370Smav		buf[69] = 0;
1029256056Sgrehan		buf[75] = 31;
1030280733Smav		buf[76] = (ATA_SATA_GEN1 | ATA_SATA_GEN2 | ATA_SATA_GEN3 |
1031280733Smav			   ATA_SUPPORT_NCQ);
1032280738Smav		buf[77] = (ATA_SUPPORT_RCVSND_FPDMA_QUEUED |
1033280738Smav			   (p->ssts & ATA_SS_SPD_MASK) >> 3);
1034280740Smav		buf[80] = 0x3f0;
1035256056Sgrehan		buf[81] = 0x28;
1036280733Smav		buf[82] = (ATA_SUPPORT_POWERMGT | ATA_SUPPORT_WRITECACHE|
1037280733Smav			   ATA_SUPPORT_LOOKAHEAD | ATA_SUPPORT_NOP);
1038280733Smav		buf[83] = (ATA_SUPPORT_ADDRESS48 | ATA_SUPPORT_FLUSHCACHE |
1039280733Smav			   ATA_SUPPORT_FLUSHCACHE48 | 1 << 14);
1040256056Sgrehan		buf[84] = (1 << 14);
1041280733Smav		buf[85] = (ATA_SUPPORT_POWERMGT | ATA_SUPPORT_WRITECACHE|
1042280733Smav			   ATA_SUPPORT_LOOKAHEAD | ATA_SUPPORT_NOP);
1043280733Smav		buf[86] = (ATA_SUPPORT_ADDRESS48 | ATA_SUPPORT_FLUSHCACHE |
1044280736Smav			   ATA_SUPPORT_FLUSHCACHE48 | 1 << 15);
1045256056Sgrehan		buf[87] = (1 << 14);
1046256056Sgrehan		buf[88] = 0x7f;
1047256056Sgrehan		if (p->xfermode & ATA_UDMA0)
1048256056Sgrehan			buf[88] |= (1 << ((p->xfermode & 7) + 8));
1049256056Sgrehan		buf[100] = sectors;
1050256056Sgrehan		buf[101] = (sectors >> 16);
1051256056Sgrehan		buf[102] = (sectors >> 32);
1052256056Sgrehan		buf[103] = (sectors >> 48);
1053280370Smav		if (candelete && !ro) {
1054280370Smav			buf[69] |= ATA_SUPPORT_RZAT | ATA_SUPPORT_DRAT;
1055280370Smav			buf[105] = 1;
1056280370Smav			buf[169] = ATA_SUPPORT_DSM_TRIM;
1057280370Smav		}
1058280244Smav		buf[106] = 0x4000;
1059280244Smav		buf[209] = 0x4000;
1060280244Smav		if (psectsz > sectsz) {
1061280244Smav			buf[106] |= 0x2000;
1062280244Smav			buf[106] |= ffsl(psectsz / sectsz) - 1;
1063280244Smav			buf[209] |= (psectoff / sectsz);
1064280244Smav		}
1065280244Smav		if (sectsz > 512) {
1066280244Smav			buf[106] |= 0x1000;
1067280244Smav			buf[117] = sectsz / 2;
1068280244Smav			buf[118] = ((sectsz / 2) >> 16);
1069280244Smav		}
1070280736Smav		buf[119] = (ATA_SUPPORT_RWLOGDMAEXT | 1 << 14);
1071280736Smav		buf[120] = (ATA_SUPPORT_RWLOGDMAEXT | 1 << 14);
1072280733Smav		buf[222] = 0x1020;
1073280741Smav		buf[255] = 0x00a5;
1074280741Smav		ahci_checksum((uint8_t *)buf, sizeof(buf));
1075267339Sjhb		ahci_write_fis_piosetup(p);
1076256056Sgrehan		write_prdt(p, slot, cfis, (void *)buf, sizeof(buf));
1077280731Smav		ahci_write_fis_d2h(p, slot, cfis, ATA_S_DSC | ATA_S_READY);
1078256056Sgrehan	}
1079256056Sgrehan}
1080256056Sgrehan
1081256056Sgrehanstatic void
1082256056Sgrehanhandle_atapi_identify(struct ahci_port *p, int slot, uint8_t *cfis)
1083256056Sgrehan{
1084256056Sgrehan	if (!p->atapi) {
1085280731Smav		ahci_write_fis_d2h(p, slot, cfis,
1086280731Smav		    (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
1087256056Sgrehan	} else {
1088256056Sgrehan		uint16_t buf[256];
1089256056Sgrehan
1090256056Sgrehan		memset(buf, 0, sizeof(buf));
1091256056Sgrehan		buf[0] = (2 << 14 | 5 << 8 | 1 << 7 | 2 << 5);
1092280745Smav		ata_string((uint8_t *)(buf+10), p->ident, 20);
1093256056Sgrehan		ata_string((uint8_t *)(buf+23), "001", 8);
1094256056Sgrehan		ata_string((uint8_t *)(buf+27), "BHYVE SATA DVD ROM", 40);
1095256056Sgrehan		buf[49] = (1 << 9 | 1 << 8);
1096256056Sgrehan		buf[50] = (1 << 14 | 1);
1097256056Sgrehan		buf[53] = (1 << 2 | 1 << 1);
1098256056Sgrehan		buf[62] = 0x3f;
1099256056Sgrehan		buf[63] = 7;
1100280740Smav		if (p->xfermode & ATA_WDMA0)
1101280740Smav			buf[63] |= (1 << ((p->xfermode & 7) + 8));
1102256056Sgrehan		buf[64] = 3;
1103280740Smav		buf[65] = 120;
1104280740Smav		buf[66] = 120;
1105280740Smav		buf[67] = 120;
1106280740Smav		buf[68] = 120;
1107280740Smav		buf[76] = (ATA_SATA_GEN1 | ATA_SATA_GEN2 | ATA_SATA_GEN3);
1108280740Smav		buf[77] = ((p->ssts & ATA_SS_SPD_MASK) >> 3);
1109256056Sgrehan		buf[78] = (1 << 5);
1110280740Smav		buf[80] = 0x3f0;
1111280740Smav		buf[82] = (ATA_SUPPORT_POWERMGT | ATA_SUPPORT_PACKET |
1112280740Smav			   ATA_SUPPORT_RESET | ATA_SUPPORT_NOP);
1113256056Sgrehan		buf[83] = (1 << 14);
1114256056Sgrehan		buf[84] = (1 << 14);
1115280740Smav		buf[85] = (ATA_SUPPORT_POWERMGT | ATA_SUPPORT_PACKET |
1116280740Smav			   ATA_SUPPORT_RESET | ATA_SUPPORT_NOP);
1117256056Sgrehan		buf[87] = (1 << 14);
1118280740Smav		buf[88] = 0x7f;
1119280740Smav		if (p->xfermode & ATA_UDMA0)
1120280740Smav			buf[88] |= (1 << ((p->xfermode & 7) + 8));
1121280740Smav		buf[222] = 0x1020;
1122280741Smav		buf[255] = 0x00a5;
1123280741Smav		ahci_checksum((uint8_t *)buf, sizeof(buf));
1124267339Sjhb		ahci_write_fis_piosetup(p);
1125256056Sgrehan		write_prdt(p, slot, cfis, (void *)buf, sizeof(buf));
1126280731Smav		ahci_write_fis_d2h(p, slot, cfis, ATA_S_DSC | ATA_S_READY);
1127256056Sgrehan	}
1128256056Sgrehan}
1129256056Sgrehan
1130256056Sgrehanstatic void
1131256056Sgrehanatapi_inquiry(struct ahci_port *p, int slot, uint8_t *cfis)
1132256056Sgrehan{
1133256056Sgrehan	uint8_t buf[36];
1134256056Sgrehan	uint8_t *acmd;
1135256056Sgrehan	int len;
1136280740Smav	uint32_t tfd;
1137256056Sgrehan
1138256056Sgrehan	acmd = cfis + 0x40;
1139256056Sgrehan
1140280740Smav	if (acmd[1] & 1) {		/* VPD */
1141280740Smav		if (acmd[2] == 0) {	/* Supported VPD pages */
1142280740Smav			buf[0] = 0x05;
1143280740Smav			buf[1] = 0;
1144280740Smav			buf[2] = 0;
1145280740Smav			buf[3] = 1;
1146280740Smav			buf[4] = 0;
1147280740Smav			len = 4 + buf[3];
1148280740Smav		} else {
1149280740Smav			p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
1150280740Smav			p->asc = 0x24;
1151280740Smav			tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR;
1152280740Smav			cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1153280740Smav			ahci_write_fis_d2h(p, slot, cfis, tfd);
1154280740Smav			return;
1155280740Smav		}
1156280740Smav	} else {
1157280740Smav		buf[0] = 0x05;
1158280740Smav		buf[1] = 0x80;
1159280740Smav		buf[2] = 0x00;
1160280740Smav		buf[3] = 0x21;
1161280740Smav		buf[4] = 31;
1162280740Smav		buf[5] = 0;
1163280740Smav		buf[6] = 0;
1164280740Smav		buf[7] = 0;
1165280740Smav		atapi_string(buf + 8, "BHYVE", 8);
1166280740Smav		atapi_string(buf + 16, "BHYVE DVD-ROM", 16);
1167280740Smav		atapi_string(buf + 32, "001", 4);
1168280740Smav		len = sizeof(buf);
1169280740Smav	}
1170256056Sgrehan
1171256056Sgrehan	if (len > acmd[4])
1172256056Sgrehan		len = acmd[4];
1173256056Sgrehan	cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1174256056Sgrehan	write_prdt(p, slot, cfis, buf, len);
1175256056Sgrehan	ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1176256056Sgrehan}
1177256056Sgrehan
1178256056Sgrehanstatic void
1179256056Sgrehanatapi_read_capacity(struct ahci_port *p, int slot, uint8_t *cfis)
1180256056Sgrehan{
1181256056Sgrehan	uint8_t buf[8];
1182256056Sgrehan	uint64_t sectors;
1183256056Sgrehan
1184257128Sgrehan	sectors = blockif_size(p->bctx) / 2048;
1185256056Sgrehan	be32enc(buf, sectors - 1);
1186256056Sgrehan	be32enc(buf + 4, 2048);
1187256056Sgrehan	cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1188256056Sgrehan	write_prdt(p, slot, cfis, buf, sizeof(buf));
1189256056Sgrehan	ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1190256056Sgrehan}
1191256056Sgrehan
1192256056Sgrehanstatic void
1193256056Sgrehanatapi_read_toc(struct ahci_port *p, int slot, uint8_t *cfis)
1194256056Sgrehan{
1195256056Sgrehan	uint8_t *acmd;
1196256056Sgrehan	uint8_t format;
1197256056Sgrehan	int len;
1198256056Sgrehan
1199256056Sgrehan	acmd = cfis + 0x40;
1200256056Sgrehan
1201256056Sgrehan	len = be16dec(acmd + 7);
1202256056Sgrehan	format = acmd[9] >> 6;
1203256056Sgrehan	switch (format) {
1204256056Sgrehan	case 0:
1205256056Sgrehan	{
1206256056Sgrehan		int msf, size;
1207256056Sgrehan		uint64_t sectors;
1208256056Sgrehan		uint8_t start_track, buf[20], *bp;
1209256056Sgrehan
1210256056Sgrehan		msf = (acmd[1] >> 1) & 1;
1211256056Sgrehan		start_track = acmd[6];
1212256056Sgrehan		if (start_track > 1 && start_track != 0xaa) {
1213256056Sgrehan			uint32_t tfd;
1214256056Sgrehan			p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
1215256056Sgrehan			p->asc = 0x24;
1216256056Sgrehan			tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR;
1217256056Sgrehan			cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1218256056Sgrehan			ahci_write_fis_d2h(p, slot, cfis, tfd);
1219256056Sgrehan			return;
1220256056Sgrehan		}
1221256056Sgrehan		bp = buf + 2;
1222256056Sgrehan		*bp++ = 1;
1223256056Sgrehan		*bp++ = 1;
1224256056Sgrehan		if (start_track <= 1) {
1225256056Sgrehan			*bp++ = 0;
1226256056Sgrehan			*bp++ = 0x14;
1227256056Sgrehan			*bp++ = 1;
1228256056Sgrehan			*bp++ = 0;
1229256056Sgrehan			if (msf) {
1230256056Sgrehan				*bp++ = 0;
1231256056Sgrehan				lba_to_msf(bp, 0);
1232256056Sgrehan				bp += 3;
1233256056Sgrehan			} else {
1234256056Sgrehan				*bp++ = 0;
1235256056Sgrehan				*bp++ = 0;
1236256056Sgrehan				*bp++ = 0;
1237256056Sgrehan				*bp++ = 0;
1238256056Sgrehan			}
1239256056Sgrehan		}
1240256056Sgrehan		*bp++ = 0;
1241256056Sgrehan		*bp++ = 0x14;
1242256056Sgrehan		*bp++ = 0xaa;
1243256056Sgrehan		*bp++ = 0;
1244256056Sgrehan		sectors = blockif_size(p->bctx) / blockif_sectsz(p->bctx);
1245256056Sgrehan		sectors >>= 2;
1246256056Sgrehan		if (msf) {
1247256056Sgrehan			*bp++ = 0;
1248256056Sgrehan			lba_to_msf(bp, sectors);
1249256056Sgrehan			bp += 3;
1250256056Sgrehan		} else {
1251256056Sgrehan			be32enc(bp, sectors);
1252256056Sgrehan			bp += 4;
1253256056Sgrehan		}
1254256056Sgrehan		size = bp - buf;
1255256056Sgrehan		be16enc(buf, size - 2);
1256256056Sgrehan		if (len > size)
1257256056Sgrehan			len = size;
1258256056Sgrehan		write_prdt(p, slot, cfis, buf, len);
1259256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1260256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1261256056Sgrehan		break;
1262256056Sgrehan	}
1263256056Sgrehan	case 1:
1264256056Sgrehan	{
1265256056Sgrehan		uint8_t buf[12];
1266256056Sgrehan
1267256056Sgrehan		memset(buf, 0, sizeof(buf));
1268256056Sgrehan		buf[1] = 0xa;
1269256056Sgrehan		buf[2] = 0x1;
1270256056Sgrehan		buf[3] = 0x1;
1271256056Sgrehan		if (len > sizeof(buf))
1272256056Sgrehan			len = sizeof(buf);
1273256056Sgrehan		write_prdt(p, slot, cfis, buf, len);
1274256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1275256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1276256056Sgrehan		break;
1277256056Sgrehan	}
1278256056Sgrehan	case 2:
1279256056Sgrehan	{
1280256056Sgrehan		int msf, size;
1281256056Sgrehan		uint64_t sectors;
1282256056Sgrehan		uint8_t start_track, *bp, buf[50];
1283256056Sgrehan
1284256056Sgrehan		msf = (acmd[1] >> 1) & 1;
1285256056Sgrehan		start_track = acmd[6];
1286256056Sgrehan		bp = buf + 2;
1287256056Sgrehan		*bp++ = 1;
1288256056Sgrehan		*bp++ = 1;
1289256056Sgrehan
1290256056Sgrehan		*bp++ = 1;
1291256056Sgrehan		*bp++ = 0x14;
1292256056Sgrehan		*bp++ = 0;
1293256056Sgrehan		*bp++ = 0xa0;
1294256056Sgrehan		*bp++ = 0;
1295256056Sgrehan		*bp++ = 0;
1296256056Sgrehan		*bp++ = 0;
1297256056Sgrehan		*bp++ = 0;
1298256056Sgrehan		*bp++ = 1;
1299256056Sgrehan		*bp++ = 0;
1300256056Sgrehan		*bp++ = 0;
1301256056Sgrehan
1302256056Sgrehan		*bp++ = 1;
1303256056Sgrehan		*bp++ = 0x14;
1304256056Sgrehan		*bp++ = 0;
1305256056Sgrehan		*bp++ = 0xa1;
1306256056Sgrehan		*bp++ = 0;
1307256056Sgrehan		*bp++ = 0;
1308256056Sgrehan		*bp++ = 0;
1309256056Sgrehan		*bp++ = 0;
1310256056Sgrehan		*bp++ = 1;
1311256056Sgrehan		*bp++ = 0;
1312256056Sgrehan		*bp++ = 0;
1313256056Sgrehan
1314256056Sgrehan		*bp++ = 1;
1315256056Sgrehan		*bp++ = 0x14;
1316256056Sgrehan		*bp++ = 0;
1317256056Sgrehan		*bp++ = 0xa2;
1318256056Sgrehan		*bp++ = 0;
1319256056Sgrehan		*bp++ = 0;
1320256056Sgrehan		*bp++ = 0;
1321256056Sgrehan		sectors = blockif_size(p->bctx) / blockif_sectsz(p->bctx);
1322256056Sgrehan		sectors >>= 2;
1323256056Sgrehan		if (msf) {
1324256056Sgrehan			*bp++ = 0;
1325256056Sgrehan			lba_to_msf(bp, sectors);
1326256056Sgrehan			bp += 3;
1327256056Sgrehan		} else {
1328256056Sgrehan			be32enc(bp, sectors);
1329256056Sgrehan			bp += 4;
1330256056Sgrehan		}
1331256056Sgrehan
1332256056Sgrehan		*bp++ = 1;
1333256056Sgrehan		*bp++ = 0x14;
1334256056Sgrehan		*bp++ = 0;
1335256056Sgrehan		*bp++ = 1;
1336256056Sgrehan		*bp++ = 0;
1337256056Sgrehan		*bp++ = 0;
1338256056Sgrehan		*bp++ = 0;
1339256056Sgrehan		if (msf) {
1340256056Sgrehan			*bp++ = 0;
1341256056Sgrehan			lba_to_msf(bp, 0);
1342256056Sgrehan			bp += 3;
1343256056Sgrehan		} else {
1344256056Sgrehan			*bp++ = 0;
1345256056Sgrehan			*bp++ = 0;
1346256056Sgrehan			*bp++ = 0;
1347256056Sgrehan			*bp++ = 0;
1348256056Sgrehan		}
1349256056Sgrehan
1350256056Sgrehan		size = bp - buf;
1351256056Sgrehan		be16enc(buf, size - 2);
1352256056Sgrehan		if (len > size)
1353256056Sgrehan			len = size;
1354256056Sgrehan		write_prdt(p, slot, cfis, buf, len);
1355256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1356256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1357256056Sgrehan		break;
1358256056Sgrehan	}
1359256056Sgrehan	default:
1360256056Sgrehan	{
1361256056Sgrehan		uint32_t tfd;
1362256056Sgrehan
1363256056Sgrehan		p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
1364256056Sgrehan		p->asc = 0x24;
1365256056Sgrehan		tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR;
1366256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1367256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, tfd);
1368256056Sgrehan		break;
1369256056Sgrehan	}
1370256056Sgrehan	}
1371256056Sgrehan}
1372256056Sgrehan
1373256056Sgrehanstatic void
1374280740Smavatapi_report_luns(struct ahci_port *p, int slot, uint8_t *cfis)
1375280740Smav{
1376280740Smav	uint8_t buf[16];
1377280740Smav
1378280740Smav	memset(buf, 0, sizeof(buf));
1379280740Smav	buf[3] = 8;
1380280740Smav
1381280740Smav	cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1382280740Smav	write_prdt(p, slot, cfis, buf, sizeof(buf));
1383280740Smav	ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1384280740Smav}
1385280740Smav
1386280740Smavstatic void
1387282306Smavatapi_read(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done)
1388256056Sgrehan{
1389256056Sgrehan	struct ahci_ioreq *aior;
1390256056Sgrehan	struct ahci_cmd_hdr *hdr;
1391256056Sgrehan	struct ahci_prdt_entry *prdt;
1392256056Sgrehan	struct blockif_req *breq;
1393256056Sgrehan	struct pci_ahci_softc *sc;
1394256056Sgrehan	uint8_t *acmd;
1395256056Sgrehan	uint64_t lba;
1396256056Sgrehan	uint32_t len;
1397282306Smav	int err;
1398256056Sgrehan
1399256056Sgrehan	sc = p->pr_sc;
1400256056Sgrehan	acmd = cfis + 0x40;
1401256164Sdim	hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
1402256056Sgrehan	prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
1403256056Sgrehan
1404256056Sgrehan	lba = be32dec(acmd + 2);
1405256056Sgrehan	if (acmd[0] == READ_10)
1406256056Sgrehan		len = be16dec(acmd + 7);
1407256056Sgrehan	else
1408256056Sgrehan		len = be32dec(acmd + 6);
1409256056Sgrehan	if (len == 0) {
1410256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1411256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1412256056Sgrehan	}
1413256056Sgrehan	lba *= 2048;
1414256056Sgrehan	len *= 2048;
1415256056Sgrehan
1416256056Sgrehan	/*
1417256056Sgrehan	 * Pull request off free list
1418256056Sgrehan	 */
1419256056Sgrehan	aior = STAILQ_FIRST(&p->iofhd);
1420256056Sgrehan	assert(aior != NULL);
1421276349Sneel	STAILQ_REMOVE_HEAD(&p->iofhd, io_flist);
1422256056Sgrehan	aior->cfis = cfis;
1423256056Sgrehan	aior->slot = slot;
1424256056Sgrehan	aior->len = len;
1425256056Sgrehan	aior->done = done;
1426256056Sgrehan	breq = &aior->io_req;
1427256056Sgrehan	breq->br_offset = lba + done;
1428282306Smav	ahci_build_iov(p, aior, prdt, hdr->prdtl);
1429256056Sgrehan
1430282306Smav	/* Mark this command in-flight. */
1431276349Sneel	p->pending |= 1 << slot;
1432276349Sneel
1433282306Smav	/* Stuff request onto busy list. */
1434276349Sneel	TAILQ_INSERT_HEAD(&p->iobhd, aior, io_blist);
1435276349Sneel
1436256056Sgrehan	err = blockif_read(p->bctx, breq);
1437256056Sgrehan	assert(err == 0);
1438256056Sgrehan}
1439256056Sgrehan
1440256056Sgrehanstatic void
1441256056Sgrehanatapi_request_sense(struct ahci_port *p, int slot, uint8_t *cfis)
1442256056Sgrehan{
1443256056Sgrehan	uint8_t buf[64];
1444256056Sgrehan	uint8_t *acmd;
1445256056Sgrehan	int len;
1446256056Sgrehan
1447256056Sgrehan	acmd = cfis + 0x40;
1448256056Sgrehan	len = acmd[4];
1449256056Sgrehan	if (len > sizeof(buf))
1450256056Sgrehan		len = sizeof(buf);
1451256056Sgrehan	memset(buf, 0, len);
1452256056Sgrehan	buf[0] = 0x70 | (1 << 7);
1453256056Sgrehan	buf[2] = p->sense_key;
1454256056Sgrehan	buf[7] = 10;
1455256056Sgrehan	buf[12] = p->asc;
1456256056Sgrehan	write_prdt(p, slot, cfis, buf, len);
1457256056Sgrehan	cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1458256056Sgrehan	ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1459256056Sgrehan}
1460256056Sgrehan
1461256056Sgrehanstatic void
1462256056Sgrehanatapi_start_stop_unit(struct ahci_port *p, int slot, uint8_t *cfis)
1463256056Sgrehan{
1464256056Sgrehan	uint8_t *acmd = cfis + 0x40;
1465256056Sgrehan	uint32_t tfd;
1466256056Sgrehan
1467256056Sgrehan	switch (acmd[4] & 3) {
1468256056Sgrehan	case 0:
1469256056Sgrehan	case 1:
1470256056Sgrehan	case 3:
1471256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1472256056Sgrehan		tfd = ATA_S_READY | ATA_S_DSC;
1473256056Sgrehan		break;
1474256056Sgrehan	case 2:
1475256056Sgrehan		/* TODO eject media */
1476256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1477256056Sgrehan		p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
1478256056Sgrehan		p->asc = 0x53;
1479256056Sgrehan		tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR;
1480256056Sgrehan		break;
1481256056Sgrehan	}
1482256056Sgrehan	ahci_write_fis_d2h(p, slot, cfis, tfd);
1483256056Sgrehan}
1484256056Sgrehan
1485256056Sgrehanstatic void
1486256056Sgrehanatapi_mode_sense(struct ahci_port *p, int slot, uint8_t *cfis)
1487256056Sgrehan{
1488256056Sgrehan	uint8_t *acmd;
1489256056Sgrehan	uint32_t tfd;
1490256056Sgrehan	uint8_t pc, code;
1491256056Sgrehan	int len;
1492256056Sgrehan
1493256056Sgrehan	acmd = cfis + 0x40;
1494256056Sgrehan	len = be16dec(acmd + 7);
1495256056Sgrehan	pc = acmd[2] >> 6;
1496256056Sgrehan	code = acmd[2] & 0x3f;
1497256056Sgrehan
1498256056Sgrehan	switch (pc) {
1499256056Sgrehan	case 0:
1500256056Sgrehan		switch (code) {
1501256056Sgrehan		case MODEPAGE_RW_ERROR_RECOVERY:
1502256056Sgrehan		{
1503256056Sgrehan			uint8_t buf[16];
1504256056Sgrehan
1505256056Sgrehan			if (len > sizeof(buf))
1506256056Sgrehan				len = sizeof(buf);
1507256056Sgrehan
1508256056Sgrehan			memset(buf, 0, sizeof(buf));
1509256056Sgrehan			be16enc(buf, 16 - 2);
1510256056Sgrehan			buf[2] = 0x70;
1511256056Sgrehan			buf[8] = 0x01;
1512256056Sgrehan			buf[9] = 16 - 10;
1513256056Sgrehan			buf[11] = 0x05;
1514256056Sgrehan			write_prdt(p, slot, cfis, buf, len);
1515256056Sgrehan			tfd = ATA_S_READY | ATA_S_DSC;
1516256056Sgrehan			break;
1517256056Sgrehan		}
1518256056Sgrehan		case MODEPAGE_CD_CAPABILITIES:
1519256056Sgrehan		{
1520256056Sgrehan			uint8_t buf[30];
1521256056Sgrehan
1522256056Sgrehan			if (len > sizeof(buf))
1523256056Sgrehan				len = sizeof(buf);
1524256056Sgrehan
1525256056Sgrehan			memset(buf, 0, sizeof(buf));
1526256056Sgrehan			be16enc(buf, 30 - 2);
1527256056Sgrehan			buf[2] = 0x70;
1528256056Sgrehan			buf[8] = 0x2A;
1529256056Sgrehan			buf[9] = 30 - 10;
1530256056Sgrehan			buf[10] = 0x08;
1531256056Sgrehan			buf[12] = 0x71;
1532256056Sgrehan			be16enc(&buf[18], 2);
1533256056Sgrehan			be16enc(&buf[20], 512);
1534256056Sgrehan			write_prdt(p, slot, cfis, buf, len);
1535256056Sgrehan			tfd = ATA_S_READY | ATA_S_DSC;
1536256056Sgrehan			break;
1537256056Sgrehan		}
1538256056Sgrehan		default:
1539256056Sgrehan			goto error;
1540256056Sgrehan			break;
1541256056Sgrehan		}
1542256056Sgrehan		break;
1543256056Sgrehan	case 3:
1544256056Sgrehan		p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
1545256056Sgrehan		p->asc = 0x39;
1546256056Sgrehan		tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR;
1547256056Sgrehan		break;
1548256056Sgrehanerror:
1549256056Sgrehan	case 1:
1550256056Sgrehan	case 2:
1551256056Sgrehan		p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
1552256056Sgrehan		p->asc = 0x24;
1553256056Sgrehan		tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR;
1554256056Sgrehan		break;
1555256056Sgrehan	}
1556256056Sgrehan	cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1557256056Sgrehan	ahci_write_fis_d2h(p, slot, cfis, tfd);
1558256056Sgrehan}
1559256056Sgrehan
1560256056Sgrehanstatic void
1561256056Sgrehanatapi_get_event_status_notification(struct ahci_port *p, int slot,
1562256056Sgrehan    uint8_t *cfis)
1563256056Sgrehan{
1564256056Sgrehan	uint8_t *acmd;
1565256056Sgrehan	uint32_t tfd;
1566256056Sgrehan
1567256056Sgrehan	acmd = cfis + 0x40;
1568256056Sgrehan
1569256056Sgrehan	/* we don't support asynchronous operation */
1570256056Sgrehan	if (!(acmd[1] & 1)) {
1571256056Sgrehan		p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
1572256056Sgrehan		p->asc = 0x24;
1573256056Sgrehan		tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR;
1574256056Sgrehan	} else {
1575256056Sgrehan		uint8_t buf[8];
1576256056Sgrehan		int len;
1577256056Sgrehan
1578256056Sgrehan		len = be16dec(acmd + 7);
1579256056Sgrehan		if (len > sizeof(buf))
1580256056Sgrehan			len = sizeof(buf);
1581256056Sgrehan
1582256056Sgrehan		memset(buf, 0, sizeof(buf));
1583256056Sgrehan		be16enc(buf, 8 - 2);
1584256056Sgrehan		buf[2] = 0x04;
1585256056Sgrehan		buf[3] = 0x10;
1586256056Sgrehan		buf[5] = 0x02;
1587256056Sgrehan		write_prdt(p, slot, cfis, buf, len);
1588256056Sgrehan		tfd = ATA_S_READY | ATA_S_DSC;
1589256056Sgrehan	}
1590256056Sgrehan	cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1591256056Sgrehan	ahci_write_fis_d2h(p, slot, cfis, tfd);
1592256056Sgrehan}
1593256056Sgrehan
1594256056Sgrehanstatic void
1595256056Sgrehanhandle_packet_cmd(struct ahci_port *p, int slot, uint8_t *cfis)
1596256056Sgrehan{
1597256056Sgrehan	uint8_t *acmd;
1598256056Sgrehan
1599256056Sgrehan	acmd = cfis + 0x40;
1600256056Sgrehan
1601256056Sgrehan#ifdef AHCI_DEBUG
1602256056Sgrehan	{
1603256056Sgrehan		int i;
1604256056Sgrehan		DPRINTF("ACMD:");
1605256056Sgrehan		for (i = 0; i < 16; i++)
1606256056Sgrehan			DPRINTF("%02x ", acmd[i]);
1607256056Sgrehan		DPRINTF("\n");
1608256056Sgrehan	}
1609256056Sgrehan#endif
1610256056Sgrehan
1611256056Sgrehan	switch (acmd[0]) {
1612256056Sgrehan	case TEST_UNIT_READY:
1613256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1614256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1615256056Sgrehan		break;
1616256056Sgrehan	case INQUIRY:
1617256056Sgrehan		atapi_inquiry(p, slot, cfis);
1618256056Sgrehan		break;
1619256056Sgrehan	case READ_CAPACITY:
1620256056Sgrehan		atapi_read_capacity(p, slot, cfis);
1621256056Sgrehan		break;
1622256056Sgrehan	case PREVENT_ALLOW:
1623256056Sgrehan		/* TODO */
1624256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1625256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1626256056Sgrehan		break;
1627256056Sgrehan	case READ_TOC:
1628256056Sgrehan		atapi_read_toc(p, slot, cfis);
1629256056Sgrehan		break;
1630280740Smav	case REPORT_LUNS:
1631280740Smav		atapi_report_luns(p, slot, cfis);
1632280740Smav		break;
1633256056Sgrehan	case READ_10:
1634256056Sgrehan	case READ_12:
1635282306Smav		atapi_read(p, slot, cfis, 0);
1636256056Sgrehan		break;
1637256056Sgrehan	case REQUEST_SENSE:
1638256056Sgrehan		atapi_request_sense(p, slot, cfis);
1639256056Sgrehan		break;
1640256056Sgrehan	case START_STOP_UNIT:
1641256056Sgrehan		atapi_start_stop_unit(p, slot, cfis);
1642256056Sgrehan		break;
1643256056Sgrehan	case MODE_SENSE_10:
1644256056Sgrehan		atapi_mode_sense(p, slot, cfis);
1645256056Sgrehan		break;
1646256056Sgrehan	case GET_EVENT_STATUS_NOTIFICATION:
1647256056Sgrehan		atapi_get_event_status_notification(p, slot, cfis);
1648256056Sgrehan		break;
1649256056Sgrehan	default:
1650256056Sgrehan		cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
1651256056Sgrehan		p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
1652256056Sgrehan		p->asc = 0x20;
1653256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, (p->sense_key << 12) |
1654256056Sgrehan				ATA_S_READY | ATA_S_ERROR);
1655256056Sgrehan		break;
1656256056Sgrehan	}
1657256056Sgrehan}
1658256056Sgrehan
1659256056Sgrehanstatic void
1660256056Sgrehanahci_handle_cmd(struct ahci_port *p, int slot, uint8_t *cfis)
1661256056Sgrehan{
1662256056Sgrehan
1663282846Smav	p->tfd |= ATA_S_BUSY;
1664256056Sgrehan	switch (cfis[2]) {
1665256056Sgrehan	case ATA_ATA_IDENTIFY:
1666256056Sgrehan		handle_identify(p, slot, cfis);
1667256056Sgrehan		break;
1668256056Sgrehan	case ATA_SETFEATURES:
1669256056Sgrehan	{
1670256056Sgrehan		switch (cfis[3]) {
1671267339Sjhb		case ATA_SF_ENAB_SATA_SF:
1672267339Sjhb			switch (cfis[12]) {
1673267339Sjhb			case ATA_SATA_SF_AN:
1674267339Sjhb				p->tfd = ATA_S_DSC | ATA_S_READY;
1675267339Sjhb				break;
1676267339Sjhb			default:
1677267339Sjhb				p->tfd = ATA_S_ERROR | ATA_S_READY;
1678267339Sjhb				p->tfd |= (ATA_ERROR_ABORT << 8);
1679267339Sjhb				break;
1680267339Sjhb			}
1681267339Sjhb			break;
1682256056Sgrehan		case ATA_SF_ENAB_WCACHE:
1683256056Sgrehan		case ATA_SF_DIS_WCACHE:
1684256056Sgrehan		case ATA_SF_ENAB_RCACHE:
1685256056Sgrehan		case ATA_SF_DIS_RCACHE:
1686256056Sgrehan			p->tfd = ATA_S_DSC | ATA_S_READY;
1687256056Sgrehan			break;
1688256056Sgrehan		case ATA_SF_SETXFER:
1689256056Sgrehan		{
1690256056Sgrehan			switch (cfis[12] & 0xf8) {
1691256056Sgrehan			case ATA_PIO:
1692256056Sgrehan			case ATA_PIO0:
1693256056Sgrehan				break;
1694256056Sgrehan			case ATA_WDMA0:
1695256056Sgrehan			case ATA_UDMA0:
1696256056Sgrehan				p->xfermode = (cfis[12] & 0x7);
1697256056Sgrehan				break;
1698256056Sgrehan			}
1699256056Sgrehan			p->tfd = ATA_S_DSC | ATA_S_READY;
1700256056Sgrehan			break;
1701256056Sgrehan		}
1702256056Sgrehan		default:
1703256056Sgrehan			p->tfd = ATA_S_ERROR | ATA_S_READY;
1704256056Sgrehan			p->tfd |= (ATA_ERROR_ABORT << 8);
1705256056Sgrehan			break;
1706256056Sgrehan		}
1707267339Sjhb		ahci_write_fis_d2h(p, slot, cfis, p->tfd);
1708256056Sgrehan		break;
1709256056Sgrehan	}
1710256056Sgrehan	case ATA_SET_MULTI:
1711256056Sgrehan		if (cfis[12] != 0 &&
1712256164Sdim			(cfis[12] > 128 || (cfis[12] & (cfis[12] - 1)))) {
1713256056Sgrehan			p->tfd = ATA_S_ERROR | ATA_S_READY;
1714256056Sgrehan			p->tfd |= (ATA_ERROR_ABORT << 8);
1715256056Sgrehan		} else {
1716256056Sgrehan			p->mult_sectors = cfis[12];
1717256056Sgrehan			p->tfd = ATA_S_DSC | ATA_S_READY;
1718256056Sgrehan		}
1719280731Smav		ahci_write_fis_d2h(p, slot, cfis, p->tfd);
1720256056Sgrehan		break;
1721280732Smav	case ATA_READ:
1722280732Smav	case ATA_WRITE:
1723280732Smav	case ATA_READ48:
1724280732Smav	case ATA_WRITE48:
1725280732Smav	case ATA_READ_MUL:
1726280732Smav	case ATA_WRITE_MUL:
1727280732Smav	case ATA_READ_MUL48:
1728280732Smav	case ATA_WRITE_MUL48:
1729256056Sgrehan	case ATA_READ_DMA:
1730256056Sgrehan	case ATA_WRITE_DMA:
1731256056Sgrehan	case ATA_READ_DMA48:
1732256056Sgrehan	case ATA_WRITE_DMA48:
1733256056Sgrehan	case ATA_READ_FPDMA_QUEUED:
1734256056Sgrehan	case ATA_WRITE_FPDMA_QUEUED:
1735282306Smav		ahci_handle_rw(p, slot, cfis, 0);
1736256056Sgrehan		break;
1737256056Sgrehan	case ATA_FLUSHCACHE:
1738256056Sgrehan	case ATA_FLUSHCACHE48:
1739256056Sgrehan		ahci_handle_flush(p, slot, cfis);
1740256056Sgrehan		break;
1741280370Smav	case ATA_DATA_SET_MANAGEMENT:
1742280370Smav		if (cfis[11] == 0 && cfis[3] == ATA_DSM_TRIM &&
1743280370Smav		    cfis[13] == 0 && cfis[12] == 1) {
1744280370Smav			ahci_handle_dsm_trim(p, slot, cfis, 0);
1745280370Smav			break;
1746280370Smav		}
1747280370Smav		ahci_write_fis_d2h(p, slot, cfis,
1748280370Smav		    (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
1749280370Smav		break;
1750280738Smav	case ATA_SEND_FPDMA_QUEUED:
1751280738Smav		if ((cfis[13] & 0x1f) == ATA_SFPDMA_DSM &&
1752280738Smav		    cfis[17] == 0 && cfis[16] == ATA_DSM_TRIM &&
1753303139Smav		    cfis[11] == 0 && cfis[3] == 1) {
1754280738Smav			ahci_handle_dsm_trim(p, slot, cfis, 0);
1755280738Smav			break;
1756280738Smav		}
1757280738Smav		ahci_write_fis_d2h(p, slot, cfis,
1758280738Smav		    (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
1759280738Smav		break;
1760280736Smav	case ATA_READ_LOG_EXT:
1761280736Smav	case ATA_READ_LOG_DMA_EXT:
1762280736Smav		ahci_handle_read_log(p, slot, cfis);
1763280736Smav		break;
1764295124Sgrehan	case ATA_SECURITY_FREEZE_LOCK:
1765295124Sgrehan	case ATA_SMART_CMD:
1766280739Smav	case ATA_NOP:
1767280739Smav		ahci_write_fis_d2h(p, slot, cfis,
1768280739Smav		    (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
1769280739Smav		break;
1770295124Sgrehan	case ATA_CHECK_POWER_MODE:
1771295124Sgrehan		cfis[12] = 0xff;	/* always on */
1772295124Sgrehan		ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1773295124Sgrehan		break;
1774256056Sgrehan	case ATA_STANDBY_CMD:
1775256056Sgrehan	case ATA_STANDBY_IMMEDIATE:
1776280739Smav	case ATA_IDLE_CMD:
1777256056Sgrehan	case ATA_IDLE_IMMEDIATE:
1778256056Sgrehan	case ATA_SLEEP:
1779295124Sgrehan	case ATA_READ_VERIFY:
1780295124Sgrehan	case ATA_READ_VERIFY48:
1781256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
1782256056Sgrehan		break;
1783256056Sgrehan	case ATA_ATAPI_IDENTIFY:
1784256056Sgrehan		handle_atapi_identify(p, slot, cfis);
1785256056Sgrehan		break;
1786256056Sgrehan	case ATA_PACKET_CMD:
1787256056Sgrehan		if (!p->atapi) {
1788280731Smav			ahci_write_fis_d2h(p, slot, cfis,
1789280731Smav			    (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
1790256056Sgrehan		} else
1791256056Sgrehan			handle_packet_cmd(p, slot, cfis);
1792256056Sgrehan		break;
1793256056Sgrehan	default:
1794256056Sgrehan		WPRINTF("Unsupported cmd:%02x\n", cfis[2]);
1795280731Smav		ahci_write_fis_d2h(p, slot, cfis,
1796280731Smav		    (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
1797256056Sgrehan		break;
1798256056Sgrehan	}
1799256056Sgrehan}
1800256056Sgrehan
1801256056Sgrehanstatic void
1802256056Sgrehanahci_handle_slot(struct ahci_port *p, int slot)
1803256056Sgrehan{
1804256056Sgrehan	struct ahci_cmd_hdr *hdr;
1805302705Sngie#ifdef AHCI_DEBUG
1806256056Sgrehan	struct ahci_prdt_entry *prdt;
1807302705Sngie#endif
1808256056Sgrehan	struct pci_ahci_softc *sc;
1809256056Sgrehan	uint8_t *cfis;
1810302705Sngie#ifdef AHCI_DEBUG
1811304421Smav	int cfl, i;
1812302705Sngie#endif
1813256056Sgrehan
1814256056Sgrehan	sc = p->pr_sc;
1815256164Sdim	hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
1816302705Sngie#ifdef AHCI_DEBUG
1817256056Sgrehan	cfl = (hdr->flags & 0x1f) * 4;
1818302705Sngie#endif
1819256056Sgrehan	cfis = paddr_guest2host(ahci_ctx(sc), hdr->ctba,
1820256056Sgrehan			0x80 + hdr->prdtl * sizeof(struct ahci_prdt_entry));
1821302705Sngie#ifdef AHCI_DEBUG
1822256056Sgrehan	prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
1823256056Sgrehan
1824256056Sgrehan	DPRINTF("\ncfis:");
1825256056Sgrehan	for (i = 0; i < cfl; i++) {
1826256056Sgrehan		if (i % 10 == 0)
1827256056Sgrehan			DPRINTF("\n");
1828256056Sgrehan		DPRINTF("%02x ", cfis[i]);
1829256056Sgrehan	}
1830256056Sgrehan	DPRINTF("\n");
1831256056Sgrehan
1832256056Sgrehan	for (i = 0; i < hdr->prdtl; i++) {
1833256056Sgrehan		DPRINTF("%d@%08"PRIx64"\n", prdt->dbc & 0x3fffff, prdt->dba);
1834256056Sgrehan		prdt++;
1835256056Sgrehan	}
1836256056Sgrehan#endif
1837256056Sgrehan
1838256056Sgrehan	if (cfis[0] != FIS_TYPE_REGH2D) {
1839256056Sgrehan		WPRINTF("Not a H2D FIS:%02x\n", cfis[0]);
1840256056Sgrehan		return;
1841256056Sgrehan	}
1842256056Sgrehan
1843256056Sgrehan	if (cfis[1] & 0x80) {
1844256056Sgrehan		ahci_handle_cmd(p, slot, cfis);
1845256056Sgrehan	} else {
1846256056Sgrehan		if (cfis[15] & (1 << 2))
1847256056Sgrehan			p->reset = 1;
1848256056Sgrehan		else if (p->reset) {
1849256056Sgrehan			p->reset = 0;
1850256056Sgrehan			ahci_port_reset(p);
1851256056Sgrehan		}
1852256056Sgrehan		p->ci &= ~(1 << slot);
1853256056Sgrehan	}
1854256056Sgrehan}
1855256056Sgrehan
1856256056Sgrehanstatic void
1857256056Sgrehanahci_handle_port(struct ahci_port *p)
1858256056Sgrehan{
1859256056Sgrehan
1860256056Sgrehan	if (!(p->cmd & AHCI_P_CMD_ST))
1861256056Sgrehan		return;
1862256056Sgrehan
1863267339Sjhb	/*
1864267339Sjhb	 * Search for any new commands to issue ignoring those that
1865282846Smav	 * are already in-flight.  Stop if device is busy or in error.
1866267339Sjhb	 */
1867282846Smav	for (; (p->ci & ~p->pending) != 0; p->ccs = ((p->ccs + 1) & 31)) {
1868282846Smav		if ((p->tfd & (ATA_S_BUSY | ATA_S_DRQ)) != 0)
1869282846Smav			break;
1870282846Smav		if (p->waitforclear)
1871282846Smav			break;
1872282846Smav		if ((p->ci & ~p->pending & (1 << p->ccs)) != 0) {
1873270159Sgrehan			p->cmd &= ~AHCI_P_CMD_CCS_MASK;
1874282846Smav			p->cmd |= p->ccs << AHCI_P_CMD_CCS_SHIFT;
1875282846Smav			ahci_handle_slot(p, p->ccs);
1876270159Sgrehan		}
1877256056Sgrehan	}
1878256056Sgrehan}
1879256056Sgrehan
1880256056Sgrehan/*
1881256056Sgrehan * blockif callback routine - this runs in the context of the blockif
1882256056Sgrehan * i/o thread, so the mutex needs to be acquired.
1883256056Sgrehan */
1884256056Sgrehanstatic void
1885256056Sgrehanata_ioreq_cb(struct blockif_req *br, int err)
1886256056Sgrehan{
1887256056Sgrehan	struct ahci_cmd_hdr *hdr;
1888256056Sgrehan	struct ahci_ioreq *aior;
1889256056Sgrehan	struct ahci_port *p;
1890256056Sgrehan	struct pci_ahci_softc *sc;
1891256056Sgrehan	uint32_t tfd;
1892256056Sgrehan	uint8_t *cfis;
1893282306Smav	int slot, ncq, dsm;
1894256056Sgrehan
1895256056Sgrehan	DPRINTF("%s %d\n", __func__, err);
1896256056Sgrehan
1897280363Smav	ncq = dsm = 0;
1898256056Sgrehan	aior = br->br_param;
1899256056Sgrehan	p = aior->io_pr;
1900256056Sgrehan	cfis = aior->cfis;
1901256056Sgrehan	slot = aior->slot;
1902256056Sgrehan	sc = p->pr_sc;
1903256164Sdim	hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
1904256056Sgrehan
1905256056Sgrehan	if (cfis[2] == ATA_WRITE_FPDMA_QUEUED ||
1906280738Smav	    cfis[2] == ATA_READ_FPDMA_QUEUED ||
1907280738Smav	    cfis[2] == ATA_SEND_FPDMA_QUEUED)
1908256056Sgrehan		ncq = 1;
1909280738Smav	if (cfis[2] == ATA_DATA_SET_MANAGEMENT ||
1910280738Smav	    (cfis[2] == ATA_SEND_FPDMA_QUEUED &&
1911280738Smav	     (cfis[13] & 0x1f) == ATA_SFPDMA_DSM))
1912280370Smav		dsm = 1;
1913256056Sgrehan
1914256056Sgrehan	pthread_mutex_lock(&sc->mtx);
1915256056Sgrehan
1916256056Sgrehan	/*
1917276349Sneel	 * Delete the blockif request from the busy list
1918276349Sneel	 */
1919276349Sneel	TAILQ_REMOVE(&p->iobhd, aior, io_blist);
1920276349Sneel
1921276349Sneel	/*
1922256056Sgrehan	 * Move the blockif request back to the free list
1923256056Sgrehan	 */
1924276349Sneel	STAILQ_INSERT_TAIL(&p->iofhd, aior, io_flist);
1925256056Sgrehan
1926280734Smav	if (!err)
1927280734Smav		hdr->prdbc = aior->done;
1928280734Smav
1929282306Smav	if (!err && aior->more) {
1930282306Smav		if (dsm)
1931280370Smav			ahci_handle_dsm_trim(p, slot, cfis, aior->done);
1932282306Smav		else
1933282306Smav			ahci_handle_rw(p, slot, cfis, aior->done);
1934282306Smav		goto out;
1935256056Sgrehan	}
1936256056Sgrehan
1937282306Smav	if (!err)
1938256056Sgrehan		tfd = ATA_S_READY | ATA_S_DSC;
1939282306Smav	else
1940256056Sgrehan		tfd = (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR;
1941280736Smav	if (ncq)
1942280736Smav		ahci_write_fis_sdb(p, slot, cfis, tfd);
1943280736Smav	else
1944256056Sgrehan		ahci_write_fis_d2h(p, slot, cfis, tfd);
1945256056Sgrehan
1946276349Sneel	/*
1947276349Sneel	 * This command is now complete.
1948276349Sneel	 */
1949276349Sneel	p->pending &= ~(1 << slot);
1950276349Sneel
1951276349Sneel	ahci_check_stopped(p);
1952282846Smav	ahci_handle_port(p);
1953256056Sgrehanout:
1954256056Sgrehan	pthread_mutex_unlock(&sc->mtx);
1955256056Sgrehan	DPRINTF("%s exit\n", __func__);
1956256056Sgrehan}
1957256056Sgrehan
1958256056Sgrehanstatic void
1959256056Sgrehanatapi_ioreq_cb(struct blockif_req *br, int err)
1960256056Sgrehan{
1961256056Sgrehan	struct ahci_cmd_hdr *hdr;
1962256056Sgrehan	struct ahci_ioreq *aior;
1963256056Sgrehan	struct ahci_port *p;
1964256056Sgrehan	struct pci_ahci_softc *sc;
1965256056Sgrehan	uint8_t *cfis;
1966256056Sgrehan	uint32_t tfd;
1967282306Smav	int slot;
1968256056Sgrehan
1969256056Sgrehan	DPRINTF("%s %d\n", __func__, err);
1970256056Sgrehan
1971256056Sgrehan	aior = br->br_param;
1972256056Sgrehan	p = aior->io_pr;
1973256056Sgrehan	cfis = aior->cfis;
1974256056Sgrehan	slot = aior->slot;
1975256056Sgrehan	sc = p->pr_sc;
1976256164Sdim	hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + aior->slot * AHCI_CL_SIZE);
1977256056Sgrehan
1978256056Sgrehan	pthread_mutex_lock(&sc->mtx);
1979256056Sgrehan
1980256056Sgrehan	/*
1981276349Sneel	 * Delete the blockif request from the busy list
1982276349Sneel	 */
1983276349Sneel	TAILQ_REMOVE(&p->iobhd, aior, io_blist);
1984276349Sneel
1985276349Sneel	/*
1986256056Sgrehan	 * Move the blockif request back to the free list
1987256056Sgrehan	 */
1988276349Sneel	STAILQ_INSERT_TAIL(&p->iofhd, aior, io_flist);
1989256056Sgrehan
1990280734Smav	if (!err)
1991280734Smav		hdr->prdbc = aior->done;
1992280734Smav
1993282306Smav	if (!err && aior->more) {
1994282306Smav		atapi_read(p, slot, cfis, aior->done);
1995256056Sgrehan		goto out;
1996256056Sgrehan	}
1997256056Sgrehan
1998282306Smav	if (!err) {
1999256056Sgrehan		tfd = ATA_S_READY | ATA_S_DSC;
2000256056Sgrehan	} else {
2001256056Sgrehan		p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
2002256056Sgrehan		p->asc = 0x21;
2003256056Sgrehan		tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR;
2004256056Sgrehan	}
2005256056Sgrehan	cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
2006256056Sgrehan	ahci_write_fis_d2h(p, slot, cfis, tfd);
2007256056Sgrehan
2008276349Sneel	/*
2009276349Sneel	 * This command is now complete.
2010276349Sneel	 */
2011276349Sneel	p->pending &= ~(1 << slot);
2012276349Sneel
2013276349Sneel	ahci_check_stopped(p);
2014282846Smav	ahci_handle_port(p);
2015256056Sgrehanout:
2016256056Sgrehan	pthread_mutex_unlock(&sc->mtx);
2017256056Sgrehan	DPRINTF("%s exit\n", __func__);
2018256056Sgrehan}
2019256056Sgrehan
2020256056Sgrehanstatic void
2021256056Sgrehanpci_ahci_ioreq_init(struct ahci_port *pr)
2022256056Sgrehan{
2023256056Sgrehan	struct ahci_ioreq *vr;
2024256056Sgrehan	int i;
2025256056Sgrehan
2026256056Sgrehan	pr->ioqsz = blockif_queuesz(pr->bctx);
2027256056Sgrehan	pr->ioreq = calloc(pr->ioqsz, sizeof(struct ahci_ioreq));
2028256056Sgrehan	STAILQ_INIT(&pr->iofhd);
2029256056Sgrehan
2030256056Sgrehan	/*
2031256056Sgrehan	 * Add all i/o request entries to the free queue
2032256056Sgrehan	 */
2033256056Sgrehan	for (i = 0; i < pr->ioqsz; i++) {
2034256056Sgrehan		vr = &pr->ioreq[i];
2035256056Sgrehan		vr->io_pr = pr;
2036256056Sgrehan		if (!pr->atapi)
2037256056Sgrehan			vr->io_req.br_callback = ata_ioreq_cb;
2038256056Sgrehan		else
2039256056Sgrehan			vr->io_req.br_callback = atapi_ioreq_cb;
2040256056Sgrehan		vr->io_req.br_param = vr;
2041276349Sneel		STAILQ_INSERT_TAIL(&pr->iofhd, vr, io_flist);
2042256056Sgrehan	}
2043276349Sneel
2044276349Sneel	TAILQ_INIT(&pr->iobhd);
2045256056Sgrehan}
2046256056Sgrehan
2047256056Sgrehanstatic void
2048256056Sgrehanpci_ahci_port_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value)
2049256056Sgrehan{
2050256056Sgrehan	int port = (offset - AHCI_OFFSET) / AHCI_STEP;
2051256056Sgrehan	offset = (offset - AHCI_OFFSET) % AHCI_STEP;
2052256056Sgrehan	struct ahci_port *p = &sc->port[port];
2053256056Sgrehan
2054256056Sgrehan	DPRINTF("pci_ahci_port %d: write offset 0x%"PRIx64" value 0x%"PRIx64"\n",
2055256056Sgrehan		port, offset, value);
2056256056Sgrehan
2057256056Sgrehan	switch (offset) {
2058256056Sgrehan	case AHCI_P_CLB:
2059256056Sgrehan		p->clb = value;
2060256056Sgrehan		break;
2061256056Sgrehan	case AHCI_P_CLBU:
2062256056Sgrehan		p->clbu = value;
2063256056Sgrehan		break;
2064256056Sgrehan	case AHCI_P_FB:
2065256056Sgrehan		p->fb = value;
2066256056Sgrehan		break;
2067256056Sgrehan	case AHCI_P_FBU:
2068256056Sgrehan		p->fbu = value;
2069256056Sgrehan		break;
2070256056Sgrehan	case AHCI_P_IS:
2071256056Sgrehan		p->is &= ~value;
2072304421Smav		ahci_port_intr(p);
2073256056Sgrehan		break;
2074256056Sgrehan	case AHCI_P_IE:
2075256056Sgrehan		p->ie = value & 0xFDC000FF;
2076304421Smav		ahci_port_intr(p);
2077256056Sgrehan		break;
2078256056Sgrehan	case AHCI_P_CMD:
2079256056Sgrehan	{
2080282845Smav		p->cmd &= ~(AHCI_P_CMD_ST | AHCI_P_CMD_SUD | AHCI_P_CMD_POD |
2081282845Smav		    AHCI_P_CMD_CLO | AHCI_P_CMD_FRE | AHCI_P_CMD_APSTE |
2082282845Smav		    AHCI_P_CMD_ATAPI | AHCI_P_CMD_DLAE | AHCI_P_CMD_ALPE |
2083282845Smav		    AHCI_P_CMD_ASP | AHCI_P_CMD_ICC_MASK);
2084282845Smav		p->cmd |= (AHCI_P_CMD_ST | AHCI_P_CMD_SUD | AHCI_P_CMD_POD |
2085282845Smav		    AHCI_P_CMD_CLO | AHCI_P_CMD_FRE | AHCI_P_CMD_APSTE |
2086282845Smav		    AHCI_P_CMD_ATAPI | AHCI_P_CMD_DLAE | AHCI_P_CMD_ALPE |
2087282845Smav		    AHCI_P_CMD_ASP | AHCI_P_CMD_ICC_MASK) & value;
2088282845Smav
2089256056Sgrehan		if (!(value & AHCI_P_CMD_ST)) {
2090276349Sneel			ahci_port_stop(p);
2091256056Sgrehan		} else {
2092256056Sgrehan			uint64_t clb;
2093256056Sgrehan
2094256056Sgrehan			p->cmd |= AHCI_P_CMD_CR;
2095256056Sgrehan			clb = (uint64_t)p->clbu << 32 | p->clb;
2096256056Sgrehan			p->cmd_lst = paddr_guest2host(ahci_ctx(sc), clb,
2097256056Sgrehan					AHCI_CL_SIZE * AHCI_MAX_SLOTS);
2098256056Sgrehan		}
2099256056Sgrehan
2100256056Sgrehan		if (value & AHCI_P_CMD_FRE) {
2101256056Sgrehan			uint64_t fb;
2102256056Sgrehan
2103256056Sgrehan			p->cmd |= AHCI_P_CMD_FR;
2104256056Sgrehan			fb = (uint64_t)p->fbu << 32 | p->fb;
2105256056Sgrehan			/* we don't support FBSCP, so rfis size is 256Bytes */
2106256056Sgrehan			p->rfis = paddr_guest2host(ahci_ctx(sc), fb, 256);
2107256056Sgrehan		} else {
2108256056Sgrehan			p->cmd &= ~AHCI_P_CMD_FR;
2109256056Sgrehan		}
2110256056Sgrehan
2111256056Sgrehan		if (value & AHCI_P_CMD_CLO) {
2112282846Smav			p->tfd &= ~(ATA_S_BUSY | ATA_S_DRQ);
2113256056Sgrehan			p->cmd &= ~AHCI_P_CMD_CLO;
2114256056Sgrehan		}
2115256056Sgrehan
2116282845Smav		if (value & AHCI_P_CMD_ICC_MASK) {
2117282845Smav			p->cmd &= ~AHCI_P_CMD_ICC_MASK;
2118282845Smav		}
2119282845Smav
2120256056Sgrehan		ahci_handle_port(p);
2121256056Sgrehan		break;
2122256056Sgrehan	}
2123256056Sgrehan	case AHCI_P_TFD:
2124256056Sgrehan	case AHCI_P_SIG:
2125256056Sgrehan	case AHCI_P_SSTS:
2126256056Sgrehan		WPRINTF("pci_ahci_port: read only registers 0x%"PRIx64"\n", offset);
2127256056Sgrehan		break;
2128256056Sgrehan	case AHCI_P_SCTL:
2129280733Smav		p->sctl = value;
2130256056Sgrehan		if (!(p->cmd & AHCI_P_CMD_ST)) {
2131256056Sgrehan			if (value & ATA_SC_DET_RESET)
2132256056Sgrehan				ahci_port_reset(p);
2133256056Sgrehan		}
2134256056Sgrehan		break;
2135256056Sgrehan	case AHCI_P_SERR:
2136256056Sgrehan		p->serr &= ~value;
2137256056Sgrehan		break;
2138256056Sgrehan	case AHCI_P_SACT:
2139256056Sgrehan		p->sact |= value;
2140256056Sgrehan		break;
2141256056Sgrehan	case AHCI_P_CI:
2142256056Sgrehan		p->ci |= value;
2143256056Sgrehan		ahci_handle_port(p);
2144256056Sgrehan		break;
2145256056Sgrehan	case AHCI_P_SNTF:
2146256056Sgrehan	case AHCI_P_FBS:
2147256056Sgrehan	default:
2148256056Sgrehan		break;
2149256056Sgrehan	}
2150256056Sgrehan}
2151256056Sgrehan
2152256056Sgrehanstatic void
2153256056Sgrehanpci_ahci_host_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value)
2154256056Sgrehan{
2155256056Sgrehan	DPRINTF("pci_ahci_host: write offset 0x%"PRIx64" value 0x%"PRIx64"\n",
2156256056Sgrehan		offset, value);
2157256056Sgrehan
2158256056Sgrehan	switch (offset) {
2159256056Sgrehan	case AHCI_CAP:
2160256056Sgrehan	case AHCI_PI:
2161256056Sgrehan	case AHCI_VS:
2162256056Sgrehan	case AHCI_CAP2:
2163256754Sgrehan		DPRINTF("pci_ahci_host: read only registers 0x%"PRIx64"\n", offset);
2164256056Sgrehan		break;
2165256056Sgrehan	case AHCI_GHC:
2166304421Smav		if (value & AHCI_GHC_HR) {
2167256056Sgrehan			ahci_reset(sc);
2168304421Smav			break;
2169304421Smav		}
2170304421Smav		if (value & AHCI_GHC_IE)
2171256056Sgrehan			sc->ghc |= AHCI_GHC_IE;
2172304421Smav		else
2173304421Smav			sc->ghc &= ~AHCI_GHC_IE;
2174304421Smav		ahci_generate_intr(sc, 0xffffffff);
2175256056Sgrehan		break;
2176256056Sgrehan	case AHCI_IS:
2177256056Sgrehan		sc->is &= ~value;
2178304421Smav		ahci_generate_intr(sc, value);
2179256056Sgrehan		break;
2180256056Sgrehan	default:
2181256056Sgrehan		break;
2182256056Sgrehan	}
2183256056Sgrehan}
2184256056Sgrehan
2185256056Sgrehanstatic void
2186256056Sgrehanpci_ahci_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
2187256056Sgrehan		int baridx, uint64_t offset, int size, uint64_t value)
2188256056Sgrehan{
2189256056Sgrehan	struct pci_ahci_softc *sc = pi->pi_arg;
2190256056Sgrehan
2191256056Sgrehan	assert(baridx == 5);
2192284900Sneel	assert((offset % 4) == 0 && size == 4);
2193256056Sgrehan
2194256056Sgrehan	pthread_mutex_lock(&sc->mtx);
2195256056Sgrehan
2196256056Sgrehan	if (offset < AHCI_OFFSET)
2197256056Sgrehan		pci_ahci_host_write(sc, offset, value);
2198256056Sgrehan	else if (offset < AHCI_OFFSET + sc->ports * AHCI_STEP)
2199256056Sgrehan		pci_ahci_port_write(sc, offset, value);
2200256056Sgrehan	else
2201256056Sgrehan		WPRINTF("pci_ahci: unknown i/o write offset 0x%"PRIx64"\n", offset);
2202256056Sgrehan
2203256056Sgrehan	pthread_mutex_unlock(&sc->mtx);
2204256056Sgrehan}
2205256056Sgrehan
2206256056Sgrehanstatic uint64_t
2207256056Sgrehanpci_ahci_host_read(struct pci_ahci_softc *sc, uint64_t offset)
2208256056Sgrehan{
2209256056Sgrehan	uint32_t value;
2210256056Sgrehan
2211256056Sgrehan	switch (offset) {
2212256056Sgrehan	case AHCI_CAP:
2213256056Sgrehan	case AHCI_GHC:
2214256056Sgrehan	case AHCI_IS:
2215256056Sgrehan	case AHCI_PI:
2216256056Sgrehan	case AHCI_VS:
2217256056Sgrehan	case AHCI_CCCC:
2218256056Sgrehan	case AHCI_CCCP:
2219256056Sgrehan	case AHCI_EM_LOC:
2220256056Sgrehan	case AHCI_EM_CTL:
2221256056Sgrehan	case AHCI_CAP2:
2222256056Sgrehan	{
2223256056Sgrehan		uint32_t *p = &sc->cap;
2224256056Sgrehan		p += (offset - AHCI_CAP) / sizeof(uint32_t);
2225256056Sgrehan		value = *p;
2226256056Sgrehan		break;
2227256056Sgrehan	}
2228256056Sgrehan	default:
2229256056Sgrehan		value = 0;
2230256056Sgrehan		break;
2231256056Sgrehan	}
2232256056Sgrehan	DPRINTF("pci_ahci_host: read offset 0x%"PRIx64" value 0x%x\n",
2233256056Sgrehan		offset, value);
2234256056Sgrehan
2235256056Sgrehan	return (value);
2236256056Sgrehan}
2237256056Sgrehan
2238256056Sgrehanstatic uint64_t
2239256056Sgrehanpci_ahci_port_read(struct pci_ahci_softc *sc, uint64_t offset)
2240256056Sgrehan{
2241256056Sgrehan	uint32_t value;
2242256056Sgrehan	int port = (offset - AHCI_OFFSET) / AHCI_STEP;
2243256056Sgrehan	offset = (offset - AHCI_OFFSET) % AHCI_STEP;
2244256056Sgrehan
2245256056Sgrehan	switch (offset) {
2246256056Sgrehan	case AHCI_P_CLB:
2247256056Sgrehan	case AHCI_P_CLBU:
2248256056Sgrehan	case AHCI_P_FB:
2249256056Sgrehan	case AHCI_P_FBU:
2250256056Sgrehan	case AHCI_P_IS:
2251256056Sgrehan	case AHCI_P_IE:
2252256056Sgrehan	case AHCI_P_CMD:
2253256056Sgrehan	case AHCI_P_TFD:
2254256056Sgrehan	case AHCI_P_SIG:
2255256056Sgrehan	case AHCI_P_SSTS:
2256256056Sgrehan	case AHCI_P_SCTL:
2257256056Sgrehan	case AHCI_P_SERR:
2258256056Sgrehan	case AHCI_P_SACT:
2259256056Sgrehan	case AHCI_P_CI:
2260256056Sgrehan	case AHCI_P_SNTF:
2261256056Sgrehan	case AHCI_P_FBS:
2262256056Sgrehan	{
2263256056Sgrehan		uint32_t *p= &sc->port[port].clb;
2264256056Sgrehan		p += (offset - AHCI_P_CLB) / sizeof(uint32_t);
2265256056Sgrehan		value = *p;
2266256056Sgrehan		break;
2267256056Sgrehan	}
2268256056Sgrehan	default:
2269256056Sgrehan		value = 0;
2270256056Sgrehan		break;
2271256056Sgrehan	}
2272256056Sgrehan
2273256056Sgrehan	DPRINTF("pci_ahci_port %d: read offset 0x%"PRIx64" value 0x%x\n",
2274256056Sgrehan		port, offset, value);
2275256056Sgrehan
2276256056Sgrehan	return value;
2277256056Sgrehan}
2278256056Sgrehan
2279256056Sgrehanstatic uint64_t
2280256056Sgrehanpci_ahci_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
2281284900Sneel    uint64_t regoff, int size)
2282256056Sgrehan{
2283256056Sgrehan	struct pci_ahci_softc *sc = pi->pi_arg;
2284284900Sneel	uint64_t offset;
2285256056Sgrehan	uint32_t value;
2286256056Sgrehan
2287256056Sgrehan	assert(baridx == 5);
2288284900Sneel	assert(size == 1 || size == 2 || size == 4);
2289284900Sneel	assert((regoff & (size - 1)) == 0);
2290256056Sgrehan
2291256056Sgrehan	pthread_mutex_lock(&sc->mtx);
2292256056Sgrehan
2293284900Sneel	offset = regoff & ~0x3;	    /* round down to a multiple of 4 bytes */
2294256056Sgrehan	if (offset < AHCI_OFFSET)
2295256056Sgrehan		value = pci_ahci_host_read(sc, offset);
2296256056Sgrehan	else if (offset < AHCI_OFFSET + sc->ports * AHCI_STEP)
2297256056Sgrehan		value = pci_ahci_port_read(sc, offset);
2298256056Sgrehan	else {
2299256056Sgrehan		value = 0;
2300284900Sneel		WPRINTF("pci_ahci: unknown i/o read offset 0x%"PRIx64"\n",
2301284900Sneel		    regoff);
2302256056Sgrehan	}
2303284900Sneel	value >>= 8 * (regoff & 0x3);
2304256056Sgrehan
2305256056Sgrehan	pthread_mutex_unlock(&sc->mtx);
2306256056Sgrehan
2307256056Sgrehan	return (value);
2308256056Sgrehan}
2309256056Sgrehan
2310256056Sgrehanstatic int
2311256056Sgrehanpci_ahci_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts, int atapi)
2312256056Sgrehan{
2313304420Smav	char bident[sizeof("XX:XX:XX")];
2314256056Sgrehan	struct blockif_ctxt *bctxt;
2315256056Sgrehan	struct pci_ahci_softc *sc;
2316304420Smav	int ret, slots, p;
2317280745Smav	MD5_CTX mdctx;
2318280745Smav	u_char digest[16];
2319304420Smav	char *next, *next2;
2320256056Sgrehan
2321256056Sgrehan	ret = 0;
2322256056Sgrehan
2323256056Sgrehan#ifdef AHCI_DEBUG
2324256056Sgrehan	dbg = fopen("/tmp/log", "w+");
2325256056Sgrehan#endif
2326256056Sgrehan
2327268953Sjhb	sc = calloc(1, sizeof(struct pci_ahci_softc));
2328256056Sgrehan	pi->pi_arg = sc;
2329256056Sgrehan	sc->asc_pi = pi;
2330304420Smav	pthread_mutex_init(&sc->mtx, NULL);
2331304420Smav	sc->ports = 0;
2332304420Smav	sc->pi = 0;
2333304420Smav	slots = 32;
2334256056Sgrehan
2335304420Smav	for (p = 0; p < MAX_PORTS && opts != NULL; p++, opts = next) {
2336304420Smav		/* Identify and cut off type of present port. */
2337304420Smav		if (strncmp(opts, "hd:", 3) == 0) {
2338304420Smav			atapi = 0;
2339304420Smav			opts += 3;
2340304420Smav		} else if (strncmp(opts, "cd:", 3) == 0) {
2341304420Smav			atapi = 1;
2342304420Smav			opts += 3;
2343304420Smav		}
2344256056Sgrehan
2345304420Smav		/* Find and cut off the next port options. */
2346304420Smav		next = strstr(opts, ",hd:");
2347304420Smav		next2 = strstr(opts, ",cd:");
2348304420Smav		if (next == NULL || (next2 != NULL && next2 < next))
2349304420Smav			next = next2;
2350304420Smav		if (next != NULL) {
2351304420Smav			next[0] = 0;
2352304420Smav			next++;
2353304420Smav		}
2354256056Sgrehan
2355304420Smav		if (opts[0] == 0)
2356304420Smav			continue;
2357280745Smav
2358304420Smav		/*
2359304420Smav		 * Attempt to open the backing image. Use the PCI slot/func
2360304420Smav		 * and the port number for the identifier string.
2361304420Smav		 */
2362304420Smav		snprintf(bident, sizeof(bident), "%d:%d:%d", pi->pi_slot,
2363304420Smav		    pi->pi_func, p);
2364304420Smav		bctxt = blockif_open(opts, bident);
2365304420Smav		if (bctxt == NULL) {
2366304420Smav			sc->ports = p;
2367304420Smav			ret = 1;
2368304420Smav			goto open_fail;
2369304420Smav		}
2370304420Smav		sc->port[p].bctx = bctxt;
2371304420Smav		sc->port[p].pr_sc = sc;
2372304421Smav		sc->port[p].port = p;
2373304420Smav		sc->port[p].atapi = atapi;
2374256056Sgrehan
2375304420Smav		/*
2376304420Smav		 * Create an identifier for the backing file.
2377304420Smav		 * Use parts of the md5 sum of the filename
2378304420Smav		 */
2379304420Smav		MD5Init(&mdctx);
2380304420Smav		MD5Update(&mdctx, opts, strlen(opts));
2381304420Smav		MD5Final(digest, &mdctx);
2382304420Smav		sprintf(sc->port[p].ident, "BHYVE-%02X%02X-%02X%02X-%02X%02X",
2383304420Smav		    digest[0], digest[1], digest[2], digest[3], digest[4],
2384304420Smav		    digest[5]);
2385256056Sgrehan
2386304420Smav		/*
2387304420Smav		 * Allocate blockif request structures and add them
2388304420Smav		 * to the free list
2389304420Smav		 */
2390304420Smav		pci_ahci_ioreq_init(&sc->port[p]);
2391304420Smav
2392304420Smav		sc->pi |= (1 << p);
2393304420Smav		if (sc->port[p].ioqsz < slots)
2394304420Smav			slots = sc->port[p].ioqsz;
2395304420Smav	}
2396304420Smav	sc->ports = p;
2397304420Smav
2398256056Sgrehan	/* Intel ICH8 AHCI */
2399256056Sgrehan	--slots;
2400304420Smav	if (sc->ports < DEF_PORTS)
2401304420Smav		sc->ports = DEF_PORTS;
2402256056Sgrehan	sc->cap = AHCI_CAP_64BIT | AHCI_CAP_SNCQ | AHCI_CAP_SSNTF |
2403256056Sgrehan	    AHCI_CAP_SMPS | AHCI_CAP_SSS | AHCI_CAP_SALP |
2404256056Sgrehan	    AHCI_CAP_SAL | AHCI_CAP_SCLO | (0x3 << AHCI_CAP_ISS_SHIFT)|
2405256056Sgrehan	    AHCI_CAP_PMD | AHCI_CAP_SSC | AHCI_CAP_PSC |
2406256056Sgrehan	    (slots << AHCI_CAP_NCS_SHIFT) | AHCI_CAP_SXS | (sc->ports - 1);
2407256056Sgrehan
2408256056Sgrehan	sc->vs = 0x10300;
2409256056Sgrehan	sc->cap2 = AHCI_CAP2_APST;
2410256056Sgrehan	ahci_reset(sc);
2411256056Sgrehan
2412256056Sgrehan	pci_set_cfgdata16(pi, PCIR_DEVICE, 0x2821);
2413256056Sgrehan	pci_set_cfgdata16(pi, PCIR_VENDOR, 0x8086);
2414256056Sgrehan	pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE);
2415256056Sgrehan	pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_STORAGE_SATA);
2416256056Sgrehan	pci_set_cfgdata8(pi, PCIR_PROGIF, PCIP_STORAGE_SATA_AHCI_1_0);
2417304421Smav	p = MIN(sc->ports, 16);
2418304421Smav	p = flsl(p) - ((p & (p - 1)) ? 0 : 1);
2419304421Smav	pci_emul_add_msicap(pi, 1 << p);
2420256056Sgrehan	pci_emul_alloc_bar(pi, 5, PCIBAR_MEM32,
2421256056Sgrehan	    AHCI_OFFSET + sc->ports * AHCI_STEP);
2422256056Sgrehan
2423267393Sjhb	pci_lintr_request(pi);
2424267393Sjhb
2425256056Sgrehanopen_fail:
2426256056Sgrehan	if (ret) {
2427304420Smav		for (p = 0; p < sc->ports; p++) {
2428304420Smav			if (sc->port[p].bctx != NULL)
2429304420Smav				blockif_close(sc->port[p].bctx);
2430304420Smav		}
2431256056Sgrehan		free(sc);
2432256056Sgrehan	}
2433256056Sgrehan
2434256056Sgrehan	return (ret);
2435256056Sgrehan}
2436256056Sgrehan
2437256056Sgrehanstatic int
2438256056Sgrehanpci_ahci_hd_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
2439256056Sgrehan{
2440256056Sgrehan
2441256056Sgrehan	return (pci_ahci_init(ctx, pi, opts, 0));
2442256056Sgrehan}
2443256056Sgrehan
2444256056Sgrehanstatic int
2445256056Sgrehanpci_ahci_atapi_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
2446256056Sgrehan{
2447256056Sgrehan
2448256056Sgrehan	return (pci_ahci_init(ctx, pi, opts, 1));
2449256056Sgrehan}
2450256056Sgrehan
2451256056Sgrehan/*
2452256056Sgrehan * Use separate emulation names to distinguish drive and atapi devices
2453256056Sgrehan */
2454304420Smavstruct pci_devemu pci_de_ahci = {
2455304420Smav	.pe_emu =	"ahci",
2456304420Smav	.pe_init =	pci_ahci_hd_init,
2457304420Smav	.pe_barwrite =	pci_ahci_write,
2458304420Smav	.pe_barread =	pci_ahci_read
2459304420Smav};
2460304420SmavPCI_EMUL_SET(pci_de_ahci);
2461304420Smav
2462256056Sgrehanstruct pci_devemu pci_de_ahci_hd = {
2463256056Sgrehan	.pe_emu =	"ahci-hd",
2464256056Sgrehan	.pe_init =	pci_ahci_hd_init,
2465256056Sgrehan	.pe_barwrite =	pci_ahci_write,
2466256056Sgrehan	.pe_barread =	pci_ahci_read
2467256056Sgrehan};
2468256056SgrehanPCI_EMUL_SET(pci_de_ahci_hd);
2469256056Sgrehan
2470256056Sgrehanstruct pci_devemu pci_de_ahci_cd = {
2471256056Sgrehan	.pe_emu =	"ahci-cd",
2472256056Sgrehan	.pe_init =	pci_ahci_atapi_init,
2473256056Sgrehan	.pe_barwrite =	pci_ahci_write,
2474256056Sgrehan	.pe_barread =	pci_ahci_read
2475256056Sgrehan};
2476256056SgrehanPCI_EMUL_SET(pci_de_ahci_cd);
2477