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