Index: Makefile =================================================================== --- Makefile +++ Makefile @@ -11,6 +11,7 @@ SRCS= \ atkbdc.c \ acpi.c \ + ata.c \ bhyverun.c \ block_if.c \ consport.c \ Index: ata.c =================================================================== --- ata.c +++ ata.c @@ -0,0 +1,2606 @@ +/*- + * Copyright (c) 2016 Alex Teaca + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "block_if.h" +#include "bhyverun.h" +#include "pci_emul.h" +#include "pci_irq.h" +#include "inout.h" + + +/* + * ATA Debug Log + */ +#define DEBUG_ATA 1 +#if DEBUG_ATA == 1 + +typedef enum { + LOG_DEBUG = 0, + LOG_ERR = 1, +} LOG_LEVEL; + +static FILE *dbg; + +static void ata_open_dbg_file(void) +{ + dbg = fopen("/tmp/bhyve_ata.log", "w+"); + return; +} + +#define dprint(level, format, arg...) \ + do{ \ + if (level == LOG_DEBUG) {fprintf(dbg, format, ##arg);} \ + else {fprintf(dbg, "LOG_ERR: "format, ##arg);} \ + fflush(dbg); \ + }while(0) +#else +#define ata_open_dbg_file() ; +#define dprint(level, fmt, ...) do {} while (0) +#endif + + +/* + * ATA defines + */ + +#define PCI_ATA_CH_SEP ';' + +/* ATA Channels */ +#define ATA_CHANNELS 0x02 +#define ATA_X 0x00 +#define ATA_Y 0x01 + +#define ATA_DRIVES 0x02 +#define ATA_MASTER 0x00 +#define ATA_SLAVE 0x01 + +#define ATA_DRIVE_ABSENT 0xf8 + +#define ATA_DEV(unit) ((unit > 0) ? 0x10 : 0) + +#define ATA_IOSIZE 0x08 +#define ATA_CTLIOSIZE 0x01 +#define ATA_BMIOSIZE 0x08 + +#define ATA_SECTOR_SIZE 512 +#define ATA_SECTORS_PER_BLOCK 128 +#define ATA_MAX_SEC_COUNT 256 +#define ATA_BLOCK_SIZE (ATA_SECTORS_PER_BLOCK * ATA_SECTOR_SIZE) +#define ATA_INBUF_SIZE (ATA_MAX_SEC_COUNT * ATA_SECTOR_SIZE) +#define ATA_LBA_27_24_MASK 0x0f + +#define ATAPI_PACKET_SIZE 12 +#define ATAPI_BLOCK_SIZE 2048 + +#define ATA_WRITE_MUL_COMPLETE 0 +#define ATA_READ_MUL_COMPLETE 0 + +#define ATA_DMA_MAX_PRD_COUNT 65536 +#define ATA_DMA_PRDT_SIZE 65536 +#define ATA_DMA_PRDT_EOT 0x8000 + +#define ATA_DMA_READ (0) +#define ATA_DMA_WRITE (1) + +#define ATA_W_DMA2 0x04 +#define ATA_MODE_PIO4 0x02 + +#define ATA_ATAPI_6 0x40 + +#define ATAPI_MAGIC_LSB 0x14 +#define ATAPI_MAGIC_MSB 0xeb + +/* PCI BARS idx */ +#define PCI_BAR0 0 +#define PCI_BAR1 1 +#define PCI_BAR2 2 +#define PCI_BAR3 3 +#define PCI_BAR4 4 + +/* ATA Bus Master Register Offsets and Values*/ +#define ATA_BMCMD_X_REG 0x00 +#define ATA_BMCMD_Y_REG 0x08 +#define ATA_BMCMD_START_STOP 0x01 +#define ATA_BMCMD_WRITE_READ 0x08 + +#define ATA_BMSTAT_X_REG 0x02 +#define ATA_BMSTAT_Y_REG 0x0A +#define ATA_BMSTAT_ACTIVE 0x01 +#define ATA_BMSTAT_ERROR 0x02 +#define ATA_BMSTAT_INTERRUPT 0x04 +#define ATA_BMSTAT_MASK 0x07 +#define ATA_BMSTAT_DMA_MASTER 0x20 +#define ATA_BMSTAT_DMA_SLAVE 0x40 +#define ATA_BMSTAT_DMA_SIMPLEX 0x80 + +#define ATA_BMPRDT_X_REG 0x04 +#define ATA_BMPRDT_Y_REG 0x0C + +/* The Command Block registers */ +#define ATA_DATA_REG 0x00 +#define ATA_ERROR_REG 0x01 +#define ATA_E_ILI 0x01 /* illegal length */ +#define ATA_E_ABORT 0x04 /* command aborted */ +#define ATA_FEATURES_REG 0x01 +#define ATA_SECCOUNT_REG 0x02 +#define ATA_I_CMD 0x01 /* cmd (1) | data (0) */ +#define ATA_I_IN 0x02 /* read (1) | write (0) */ +#define ATA_I_RELEASE 0x04 /* released bus (1) */ +#define ATA_LBA_LOW_REG 0x03 +#define ATA_LBA_MID_REG 0x04 +#define ATA_LBA_HIGH_REG 0x05 +#define ATA_HDDEVSEL_REG 0x06 +#define ATA_D_LBA 0x40 /* use LBA addressing */ +#define ATA_D_IBM 0xa0 /* 512 byte sectors, ECC */ +#define ATA_COMMAND_REG 0x07 +#define ATA_STATUS_REG 0x07 +#define ATA_S_BUSY 0x80 /* busy */ +#define ATA_S_READY 0x40 /* drive ready */ +#define ATA_S_DWF 0x20 /* drive write fault */ +#define ATA_S_DRQ 0x08 /* data request */ +#define ATA_S_DSC 0x10 /* drive seek completed */ +#define ATA_S_ERROR 0x01 /* error */ + +/* The Control Block registers */ +#define ATA_CONTROL_REG 0x0C +#define ATA_A_IDS 0x02 /* disable interrupts */ +#define ATA_A_RESET 0x04 /* RESET controller */ +#define ATA_ALTSTATUS_REG 0x0C + +#define READ_TOC 0x43 + +struct ata_channel; + +typedef void (*ata_intr_func_t)(void *arg); +typedef void (*ata_pio_end_transfer_func_t)(struct ata_channel *channel); + +typedef enum { + ATA_HD, + ATAPI_CD, + ATA_DRIVE_EMPTY +} ata_drive_type; + +/* + * ATA data structures + */ +struct ata_pio_buffer { + uint8_t data[ATA_INBUF_SIZE]; + uint64_t current_pos; +}; + +struct ata_pio_setup { + uint8_t use_word; + struct blockif_req breq; + struct ata_pio_buffer pio_buffer; + + uint64_t size_transfer; + ata_pio_end_transfer_func_t end_transfer; + struct ata_channel *channel; +}; + +struct prd_entry { + uint32_t prd_address; + uint16_t byte_count; + uint16_t vendor_specific; +} __attribute__((__packed__)); + +struct pci_ata_dma_transaction { + struct blockif_req breq; + uint8_t is_eot; + uint8_t started; + uint64_t offset; + uint64_t nbytes; + uint8_t op_dir; +}; + +struct ata_drive { + /* ATA Drive Registers */ + + /* the io port range size = 0x08 */ + uint8_t data; + uint8_t error; + uint8_t features; + uint8_t seccount; + uint8_t lba_low; + uint8_t lba_mid; + uint8_t lba_high; + uint8_t hddevsel; + uint8_t command; + uint8_t status; + + /* the altport range size = 0x01 */ + uint8_t altstatus; + + ata_drive_type type; + uint16_t byte_count; + uint64_t offset; + uint64_t count; + uint8_t irq_disabled; + struct blockif_ctxt *bctxt; + struct ata_pio_setup pio_setup; +}; + +struct ata_channel { + /* ATA Channel State Variables */ + uint32_t size; + uint8_t interface; + uint8_t drive; + uint8_t has_slave; + uint8_t mode; + uint8_t sectors_per_block; + uint8_t use_16bit; + + struct blockif_req flush_breq; + + struct ata_drive drives[ATA_DRIVES]; + + struct vmctx *vm_ctx; + void *pr_sc; + + /* Interrupts callbacks (PCI or LPC) */ + ata_intr_func_t intr_assert; + ata_intr_func_t intr_deassert; + uint8_t lintr; +}; + +struct pci_ata_softc { + /* ATA Bus Master Register */ + uint8_t cmd[ATA_CHANNELS]; + uint8_t stat[ATA_CHANNELS]; + uint32_t prdt[ATA_CHANNELS]; + + struct pci_ata_dma_transaction dma_transactions[ATA_CHANNELS]; + + struct pci_devinst *asc_pi; + struct ata_channel *channels[ATA_CHANNELS]; +}; + +struct lpc_ata_softc { + struct ata_channel *channel; + int irq; + int base_addr_io; + int base_addr_ioctl; + const char *name_io; + const char *name_ioctl; +}; + + +/* + * ATA module function declarations + */ +static void +ata_irq_raise(struct ata_channel *channel); +static void +ata_irq_lower(struct ata_channel *channel); + +static ata_drive_type +ata_drive_type_by_name(char *drive_name); +static int +ata_parse_params(const char *opts, int *channel, char *disk_master, + char *disk_slave, uint8_t *has_slave); +static struct ata_channel * +ata_init(struct vmctx *ctx, ata_intr_func_t intr_assert, ata_intr_func_t intr_deassert, void *pr_sc, + const char *opts, uint8_t isa_at); + +static void +ata_srst(struct ata_channel *channel); +static void +ata_initialize_ident(struct ata_channel *channel); +static void +ata_atapi_initialize_ident(struct ata_channel *channel); + +static int +ata_channel_is_ok(struct ata_channel *channel, uint8_t ch); +static int +ata_select_drive_is_ok(struct ata_channel *channel); +static void +ata_set_status_ok(struct ata_channel *channel); +static void +ata_set_data_block_ready(struct ata_channel *channel); +static void +ata_command_aborted(struct ata_channel *channel); +static void +ata_set_signature(struct ata_drive *drive); + +static void +ata_addressing_sec_count(struct ata_channel *channel, uint16_t *p_sector_count); +static void +ata_addressing_28_lba(struct ata_channel *channel, uint32_t *p_lba_address); + +static void +ata_init_block_request(struct blockif_req *breq, struct ata_channel *channel, + uint64_t offset, uint64_t nbytes, void *buffer); +static void +ata_handle_block_request(struct blockif_req *req, int err); + +static void +ata_pio_do_transfer(struct ata_pio_setup *pio_setup, uint64_t size_transfer, + ata_pio_end_transfer_func_t end_transfer, uint8_t use_word); +static int +ata_pio_in_progress(struct ata_pio_setup *pio_setup); +static uint8_t * +ata_pio_get_buffer_data(struct ata_pio_setup *pio_setup); +static void +ata_pio_check_end_transfer(struct ata_pio_setup *pio_setup); +static uint32_t +ata_pio_get_uint(struct ata_pio_setup *pio_setup); +static void +ata_pio_put_uint(struct ata_pio_setup *pio_setup, uint32_t write_value); +static void +ata_pio_generic_end_transfer(struct ata_channel *channel); + +static void +ata_read_multiple_block_done(struct ata_channel *channel); +static void +ata_write_multiple_block_done(struct ata_channel *channel); + +static void +ata_atapi_handle_read_done(struct ata_channel *channel); + +static void +ata_handle_identify(struct ata_channel *channel); +static void +ata_handle_setfeatures(struct ata_channel *channel); +static void +ata_handle_set_multi(struct ata_channel *channel); +static void +ata_handle_read_multiple(struct ata_channel *channel); +static void +ata_handle_read_verify(struct ata_channel *channel); +static void +ata_handle_write_multiple(struct ata_channel *channel); +static void +ata_handle_dma(struct ata_channel *channel, uint8_t op_dir); +static void +ata_handle_flushcache(struct ata_channel *channel); +static void +ata_handle_seek(struct ata_channel *channel); +static void +ata_handle_atapi_identify(struct ata_channel *channel); +static void +ata_handle_packet_cmd(struct ata_channel *channel); +static void +ata_handle_cmd(struct ata_channel *channel, uint8_t cmd); + +static void +ata_atapi_data_request(struct ata_channel *channel); +static void +ata_atapi_cmd_done(struct ata_channel *channel); +static void +ata_atapi_handle_inquiry(struct ata_channel *channel, uint8_t *packet); +static void +ata_atapi_handle_read_capacity(struct ata_channel *channel, uint8_t *packet); +static void +ata_atapi_handle_read_toc(struct ata_channel *channel, uint8_t *packet); +static void +ata_atapi_handle_read(struct ata_channel *channel, uint8_t *packet); +static void +ata_atapi_cmd(struct ata_channel *channel); + +static uint64_t +ata_command_block_read(struct ata_channel *channel, uint64_t offset); +static void +ata_command_block_write(struct ata_channel *channel, + uint64_t offset, int size, uint64_t value); + +static uint64_t +ata_altstatus_read(struct ata_channel *channel); +static void +ata_control_write(struct ata_channel *channel, + int size, uint64_t value); + +/* + * PCI ATA function declarations + */ +static void +pci_ata_intr_assert(void *arg); +static void +pci_ata_intr_deassert(void *arg); + +static int +pci_ata_parse_params(const char *opts, char *opt0, char *opt1, uint8_t *nr_channels); +static int +pci_ata_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts, int atapi); +static int +pci_ata_hd_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts); + +static void +pci_ata_dma_start(struct pci_ata_softc *sc, uint8_t ch); +static void +pci_ata_dma_stop(struct pci_ata_softc *sc, uint8_t ch); +static uint64_t +pci_ata_bus_master_read(struct pci_ata_softc *sc, uint64_t offset); +static void +pci_ata_bus_master_write(struct pci_ata_softc *sc, + uint64_t offset, int size, uint64_t value); + +static uint64_t +pci_ata_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, + uint64_t offset, int size); +static void +pci_ata_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int baridx, uint64_t offset, int size, uint64_t value); + +/* + * LPC ATA function declarations + */ +static void +lpc_ata_intr_assert(void *arg); +static void +lpc_ata_intr_deassert(void *arg); + +int +lpc_ata_init(struct vmctx *ctx, const char *opts); + +static int +lpc_ata_io_handler(struct vmctx *ctx, int vcpu, + int in, int port, int bytes, uint32_t *eax, void *arg); +static int +lpc_ata_ioctl_handler(struct vmctx *ctx, int vcpu, + int in, int port, int bytes, uint32_t *eax, void *arg); + + +/* + * ATA global data + */ +struct pci_devemu pci_de_ata_hd = { + .pe_emu = "ata-hd", + .pe_init = pci_ata_hd_init, + .pe_barwrite = pci_ata_write, + .pe_barread = pci_ata_read +}; +PCI_EMUL_SET(pci_de_ata_hd); + +static struct lpc_ata_softc lpc_ata_sc[ATA_CHANNELS] = { + {NULL, 14, 0x1f0, 0x3f6, "ata0_ioport", "ata0_ioctlport"}, + {NULL, 15, 0x170, 0x376, "ata1_ioport", "ata1_ioctlport"} +}; + + +/* + * ATA module function definitions + */ +static void +ata_irq_raise(struct ata_channel *channel) +{ + uint8_t irq_disabled; + + irq_disabled = channel->drives[channel->drive].irq_disabled; + + if (!irq_disabled && !channel->lintr) { + channel->lintr = 1; + channel->intr_assert(channel->pr_sc); + } + + return; +} + +static void +ata_irq_lower(struct ata_channel *channel) +{ + if (channel->lintr) { + channel->lintr = 0; + channel->intr_deassert(channel->pr_sc); + } + + return; +} + +static ata_drive_type +ata_drive_type_by_name(char *drive_name) +{ + int i, len; + char *ext = NULL; + + if (!drive_name) + return ATA_DRIVE_EMPTY; + + len = strlen(drive_name); + + for (i = len - 1; i >= 0; i--) { + if (drive_name[i] == '.') { + ext = drive_name + i + 1; + if (strcmp(ext, "iso") == 0) + return ATAPI_CD; + break; + } + } + + return ATA_HD; +} + +static int +ata_parse_params(const char *opts, int *channel, char *disk_master, + char *disk_slave, uint8_t *has_slave) +{ + uint8_t len; + char param[64]; + char *params[3]; + uint8_t i, j; + int ch; + + params[0] = NULL; + params[1] = NULL; + params[2] = NULL; + + if (!opts) { + dprint(LOG_ERR, "ata_parse_params: opts should be like: X,MASTER[,SLAVE]\n"); + return -1; + } + + len = strlen(opts); + if (len >= 64) { + dprint(LOG_ERR, "ata_parse_params: ATA string param too big\n"); + return -1; + } + + strcpy(param, opts); + + j = 0; + params[j] = param; + for (i = 0; i < len; i++) { + if (param[i] == ',') { + j++; + if (j > 2) { + dprint(LOG_ERR, "ata_parse_params: to many params\n"); + return -1; + } + param[i] = '\0'; + params[j] = param + i + 1; + } + } + + ch = atoi(params[0]); + if (ch != ATA_X && ch != ATA_Y) { + dprint(LOG_ERR, "ata_parse_params: the channel should be 0 or 1\n"); + return -1; + } + *channel = ch; + + if (params[1] == NULL || strlen(params[1]) >= 32) { + dprint(LOG_ERR, "ata_parse_params: the name of MASTER should be given\n"); + return -1; + } + strcpy(disk_master, params[1]); + + if (params[2] != NULL && strlen(params[2]) < 32) { + strcpy(disk_slave, params[2]); + *has_slave = 1; + } + + return 0; + +} + +static struct ata_channel * +ata_init(struct vmctx *ctx, ata_intr_func_t intr_assert, ata_intr_func_t intr_deassert, void *pr_sc, + const char *opts, uint8_t isa_at) +{ + char bident[sizeof("XX:X:X:X")]; + struct blockif_ctxt *bctxt = NULL, *bctxt_s = NULL; + struct ata_channel *channel = NULL; + int ret; + int ch_ata = -1; + char disk_master[32]; + char disk_slave[32]; + uint8_t has_slave = 0; + ata_drive_type master_type = ATA_DRIVE_EMPTY; + ata_drive_type slave_type = ATA_DRIVE_EMPTY; + + ret = ata_parse_params(opts, &ch_ata, disk_master, disk_slave, &has_slave); + if (ret != 0) { + dprint(LOG_ERR, "ata_init: some errors with the input checking\n"); + return NULL; + } + + dprint(LOG_DEBUG, "ata_init: ch_ata: %d\n", ch_ata); + + /* + * Attempt to open the backing images. Use the + * "PCI/ISA:ch:drive" for the identifier string. + */ + snprintf(bident, sizeof(bident), "%d:%d:%d", isa_at, ch_ata, ATA_MASTER); + bctxt = blockif_open(disk_master, bident); + if (bctxt == NULL) { + goto open_fail; + } + + master_type = ata_drive_type_by_name(disk_master); + dprint(LOG_DEBUG, "ata_init: disk_master: %s type: %s\n", + disk_master, master_type == ATA_HD ? "ata_hd" : "atapi_cd"); + + if (has_slave) { + snprintf(bident, sizeof(bident), "%d:%d:%d", isa_at, ch_ata, ATA_SLAVE); + bctxt_s = blockif_open(disk_slave, bident); + if (bctxt_s == NULL) { + ret = 1; + goto open_fail; + } + + slave_type = ata_drive_type_by_name(disk_slave); + dprint(LOG_DEBUG, "ata_init: disk_slave: %s type: %s\n", + disk_slave, slave_type == ATA_HD ? "ata_hd" : "atapi_cd"); + } + + dprint(LOG_DEBUG, "ata_init: opts: %s, atapi: %d\n", opts, 0); + + channel = calloc(1, sizeof(struct ata_channel)); + + channel->vm_ctx = ctx; + channel->interface = ch_ata; + channel->has_slave = has_slave; + + channel->drives[ATA_MASTER].type = master_type; + channel->drives[ATA_SLAVE].type = slave_type; + + channel->drives[ATA_MASTER].bctxt = bctxt; + channel->drives[ATA_SLAVE].bctxt = bctxt_s; + + channel->drives[ATA_MASTER].pio_setup.channel = channel; + channel->drives[ATA_SLAVE].pio_setup.channel = channel; + + channel->intr_assert = intr_assert; + channel->intr_deassert = intr_deassert; + channel->pr_sc = pr_sc; + + return channel; + +open_fail: + if (ret) { + blockif_close(bctxt); + if (bctxt_s != NULL) { + blockif_close(bctxt_s); + } + } + + return NULL; +} + +static void +ata_srst(struct ata_channel *channel) +{ + struct ata_drive *drive = NULL; + int dev; + + dprint(LOG_DEBUG, "ata_srst: Software Reset\n"); + + for (dev = 0; dev < ATA_DRIVES; dev++) { + drive = &channel->drives[dev]; + + if (drive->type != ATA_DRIVE_EMPTY) { + + drive->error = ATA_E_ILI; + drive->status = ATA_S_READY; + + ata_set_signature(drive); + } + } + + return; +} + +static void +ata_initialize_ident(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + struct blockif_ctxt *bctxt = NULL; + struct ata_params *ident_data = NULL; + struct ata_pio_setup *pio_setup = NULL; + + uint16_t cylinders; + uint8_t heads; + uint8_t sectors; + uint32_t total_sectors; + + bctxt = drive->bctxt; + pio_setup = &drive->pio_setup; + ident_data = (struct ata_params *)ata_pio_get_buffer_data(pio_setup); + + total_sectors = blockif_size(bctxt) / ATA_SECTOR_SIZE; + + memset(ident_data, 0, sizeof(struct ata_params)); + + /* Number of sectors per block used in read / write multi commands */ + ident_data->sectors_intr |= 0x80 << 8; + ident_data->sectors_intr |= ATA_SECTORS_PER_BLOCK; + + /* Use DMA2 mode */ + ident_data->capabilities1 |= ATA_SUPPORT_DMA; + ident_data->mwdmamodes |= ATA_W_DMA2; + + /* Use PIO4 mode */ + ident_data->atavalid |= ATA_FLAG_64_70; + ident_data->apiomodes |= ATA_MODE_PIO4; + + /* We use 28-bit LBA addressing because CHS addressing is obsolete in ATA/ATAPI 6 */ + ident_data->capabilities1 |= ATA_SUPPORT_LBA; + ident_data->lba_size_1 = total_sectors; + ident_data->lba_size_2 = total_sectors >> 16; + + /* Support write-read-verify */ + ident_data->support2 |= ATA_SUPPORT_WRITEREADVERIFY; + ident_data->enabled2 |= ATA_SUPPORT_WRITEREADVERIFY; + + /* Support ATA FLUSHCACHE */ + ident_data->support.command2 |= ATA_SUPPORT_FLUSHCACHE; + ident_data->enabled.command2 |= ATA_SUPPORT_FLUSHCACHE; + + /* Major version number = ATA/ATAPI-6 */ + ident_data->version_major |= ATA_ATAPI_6; + + /* This information are used only for printing purposes */ + blockif_chs(bctxt, &cylinders, &heads, §ors); + + ident_data->cylinders = cylinders; + ident_data->heads = heads; + ident_data->sectors = sectors; + + /* Model: BHYVE ATA IDE DISK */ + memcpy(ident_data->model, "HBVY ETA ADI EIDKS ", 40); + /* Serial number: 123456 */ + memcpy(ident_data->serial, "214365 ", 20); + /* Firmware version: 1.0 */ + memcpy(ident_data->revision, ".1 0 ", 8); + + dprint(LOG_DEBUG, "ata_initialize_ident: channel: %d, drive: %d C: %d, H: %d, S: %d, total_sectors: %d\n", + channel->interface, channel->drive, cylinders, heads, sectors, total_sectors); + + ata_pio_do_transfer(pio_setup, sizeof(struct ata_params), ata_pio_generic_end_transfer, channel->use_16bit); + + return; +} + +static void +ata_atapi_initialize_ident(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + struct ata_params *ident_data = NULL; + struct ata_pio_setup *pio_setup = NULL; + + pio_setup = &drive->pio_setup; + ident_data = (struct ata_params *)ata_pio_get_buffer_data(pio_setup); + + memset(ident_data, 0, sizeof(struct ata_params)); + + /* CDROM device */ + ident_data->config |= (ATA_PROTO_ATAPI_12 | ATA_ATAPI_TYPE_CDROM | ATA_DRQ_FAST); + + /* Use DMA2 mode */ + ident_data->capabilities1 |= ATA_SUPPORT_DMA; + ident_data->mwdmamodes |= ATA_W_DMA2; + + /* Use PIO4 mode */ + ident_data->atavalid |= ATA_FLAG_64_70; + ident_data->apiomodes |= ATA_MODE_PIO4; + + /* We use 28-bit LBA addressing because CHS addressing is obsolete in ATA/ATAPI 6 */ + ident_data->capabilities1 |= ATA_SUPPORT_LBA; + + /* Major version number = ATA/ATAPI-6 */ + ident_data->version_major |= ATA_ATAPI_6; + + /* Model: BHYVE ATAPI IDE CDROM */ + memcpy(ident_data->model, "HBVY ETAPA IDI EDCOR M ", 40); + /* Serial number: 123456 */ + memcpy(ident_data->serial, "214365 ", 20); + /* Firmware version: 1.0 */ + memcpy(ident_data->revision, ".1 0 ", 8); + + dprint(LOG_DEBUG, "ata_atapi_initialize_ident: channel: %d, drive: %d\n", + channel->interface, channel->drive); + + ata_pio_do_transfer(pio_setup, sizeof(struct ata_params), ata_pio_generic_end_transfer, channel->use_16bit); + + return; +} + +static int +ata_channel_is_ok(struct ata_channel *channel, uint8_t ch) +{ + if (channel->interface == ch) + return 1; + + return 0; +} + +static int +ata_select_drive_is_ok(struct ata_channel *channel) +{ + if (channel->drive == ATA_SLAVE && !channel->has_slave) { + return 0; + } + + return 1; +} + +static void +ata_set_status_ok(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + + drive->status &= ~ATA_S_BUSY; + drive->status |= ATA_S_READY; + drive->status &= ~ATA_S_DWF; + drive->status &= ~ATA_S_DRQ; + drive->status &= ~ATA_S_ERROR; + + drive->error = 0; + + return; +} + +static void +ata_set_data_block_ready(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + + drive->altstatus &= ~ATA_S_BUSY; + drive->altstatus |= ATA_S_DRQ; + drive->altstatus |= ATA_S_READY; + + drive->status |= ATA_S_DRQ; + + return; +} + +static void +ata_command_aborted(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + + drive->status &= ~ATA_S_BUSY; + drive->status &= ~ATA_S_DRQ; + drive->status |= ATA_S_ERROR; + + drive->error |= ATA_E_ABORT; + + return; +} + +static void +ata_set_signature(struct ata_drive *drive) +{ + drive->seccount = 0x01; + drive->lba_low = 0x01; + + if (drive->type == ATA_HD) { + drive->lba_mid = 0x00; + drive->lba_high = 0x00; + drive->hddevsel = 0x00; + } + else if (drive->type == ATAPI_CD) { + drive->lba_mid = ATAPI_MAGIC_LSB; + drive->lba_high = ATAPI_MAGIC_MSB; + drive->hddevsel &= 0x10; + } + else + assert(0); + + return; +} + +static void +ata_addressing_sec_count(struct ata_channel *channel, uint16_t *p_sector_count) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + uint16_t sector_count; + + sector_count = drive->seccount ? drive->seccount : ATA_MAX_SEC_COUNT; + *p_sector_count = sector_count; + + return; +} + +static void +ata_addressing_28_lba(struct ata_channel *channel, uint32_t *p_lba_address) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + uint32_t lba_address = 0; + + lba_address |= drive->lba_low; + lba_address |= drive->lba_mid << 8; + lba_address |= drive->lba_high << 16; + lba_address |= (drive->hddevsel & ATA_LBA_27_24_MASK) << 24; + + *p_lba_address = lba_address; + + return; +} + +static void +ata_init_block_request(struct blockif_req *breq, struct ata_channel *channel, + uint64_t offset, uint64_t nbytes, void *buffer) +{ + breq->br_param = channel; + breq->br_callback = ata_handle_block_request; + breq->br_offset = offset; + breq->br_iovcnt = 1; + breq->br_iov[0].iov_len = nbytes; + breq->br_iov[0].iov_base = buffer; + + return; +} + +static void +ata_handle_block_request(struct blockif_req *req, int err) +{ + struct ata_channel *channel = (struct ata_channel *)req->br_param; + + ata_set_status_ok(channel); + ata_set_data_block_ready(channel); + ata_irq_raise(channel); + + return; +} + +static void +ata_pio_do_transfer(struct ata_pio_setup *pio_setup, uint64_t size_transfer, + ata_pio_end_transfer_func_t end_transfer, uint8_t use_word) +{ + assert(size_transfer <= ATA_INBUF_SIZE); + assert(!ata_pio_in_progress(pio_setup)); + + pio_setup->use_word = use_word; + pio_setup->pio_buffer.current_pos = 0; + pio_setup->size_transfer = size_transfer; + pio_setup->end_transfer = end_transfer; + + return; +} + +static int +ata_pio_in_progress(struct ata_pio_setup *pio_setup) +{ + return (int)pio_setup->end_transfer; +} + +static uint8_t * +ata_pio_get_buffer_data(struct ata_pio_setup *pio_setup) +{ + return pio_setup->pio_buffer.data; +} + +static void +ata_pio_check_end_transfer(struct ata_pio_setup *pio_setup) +{ + struct ata_channel *channel = pio_setup->channel; + struct ata_pio_buffer *pio_buffer = &pio_setup->pio_buffer; + ata_pio_end_transfer_func_t end_transfer = pio_setup->end_transfer; + + if (pio_buffer->current_pos == pio_setup->size_transfer) { + pio_setup->end_transfer = NULL; + end_transfer(channel); + } + + return; +} + +static uint32_t +ata_pio_get_uint(struct ata_pio_setup *pio_setup) +{ + struct ata_pio_buffer *pio_buffer = &pio_setup->pio_buffer; + uint32_t value; + + assert(pio_buffer->current_pos < pio_setup->size_transfer); + + if (pio_setup->use_word) { + value = *(uint16_t *)(pio_buffer->data + pio_buffer->current_pos); + pio_buffer->current_pos += sizeof(uint16_t); + } + else { + value = *(uint32_t *)(pio_buffer->data + pio_buffer->current_pos); + pio_buffer->current_pos += sizeof(uint32_t); + } + + ata_pio_check_end_transfer(pio_setup); + + return value; +} + +static void +ata_pio_put_uint(struct ata_pio_setup *pio_setup, uint32_t write_value) +{ + struct ata_pio_buffer *pio_buffer = &pio_setup->pio_buffer; + + assert(pio_buffer->current_pos < pio_setup->size_transfer); + + if (pio_setup->use_word) { + *(uint16_t *)(pio_buffer->data + pio_buffer->current_pos) = (uint16_t)write_value; + pio_buffer->current_pos += sizeof(uint16_t); + } + else { + *(uint32_t *)(pio_buffer->data + pio_buffer->current_pos) = write_value; + pio_buffer->current_pos += sizeof(uint32_t); + } + + ata_pio_check_end_transfer(pio_setup); + + return; +} + +static void +ata_pio_generic_end_transfer(struct ata_channel *channel) +{ + dprint(LOG_DEBUG, "ata_pio_generic_end_transfer: ch: %d drive: %d\n", + channel->interface, channel->drive); + + return; +} + +static void +ata_read_multiple_block_done(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + struct blockif_ctxt *bctxt = NULL; + struct ata_pio_setup *pio_setup = NULL; + struct blockif_req *breq = NULL; + uint64_t count; + uint64_t size_transfer; + int err; + + bctxt = drive->bctxt; + pio_setup = &drive->pio_setup; + breq = &pio_setup->breq; + + drive->offset += pio_setup->size_transfer; + drive->count -= pio_setup->size_transfer; + + count = drive->count; + + if (count) { + size_transfer = count >= ATA_BLOCK_SIZE ? ATA_BLOCK_SIZE : count; + ata_pio_do_transfer(pio_setup, size_transfer, ata_read_multiple_block_done, channel->use_16bit); + + ata_init_block_request(breq, channel, drive->offset, + size_transfer, ata_pio_get_buffer_data(pio_setup)); + + err = blockif_read(bctxt, breq); + assert(!err); + } + else { + dprint(LOG_DEBUG, "ata_read_multiple_block_done: ATA_READ_MUL completed\n"); + } + + return; +} + +static void +ata_write_multiple_block_done(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + struct blockif_ctxt *bctxt = NULL; + struct ata_pio_setup *pio_setup = NULL; + struct blockif_req *breq = NULL; + uint64_t count; + uint64_t size_transfer; + int err; + + bctxt = drive->bctxt; + pio_setup = &drive->pio_setup; + breq = &pio_setup->breq; + + ata_init_block_request(breq, channel, drive->offset, + pio_setup->size_transfer, ata_pio_get_buffer_data(pio_setup)); + + drive->offset += pio_setup->size_transfer; + drive->count -= pio_setup->size_transfer; + + count = drive->count; + + if (count) { + size_transfer = count >= ATA_BLOCK_SIZE ? ATA_BLOCK_SIZE : count; + ata_pio_do_transfer(pio_setup, size_transfer, ata_write_multiple_block_done, channel->use_16bit); + } + else { + dprint(LOG_DEBUG, "ata_write_multiple_block_done: ATA_WRITE_MUL completed\n"); + } + + err = blockif_write(bctxt, breq); + assert(!err); + + return; +} + +static void +ata_atapi_handle_read_done(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + struct blockif_ctxt *bctxt = NULL; + struct ata_pio_setup *pio_setup = NULL; + struct blockif_req *breq = NULL; + uint64_t count; + int err; + + bctxt = drive->bctxt; + pio_setup = &drive->pio_setup; + breq = &pio_setup->breq; + + drive->offset += ATAPI_BLOCK_SIZE; + drive->count -= ATAPI_BLOCK_SIZE; + + count = drive->count; + + if (count) { + assert(count >= ATAPI_BLOCK_SIZE); + + drive->seccount = ATA_I_IN; + + ata_pio_do_transfer(pio_setup, ATAPI_BLOCK_SIZE, ata_atapi_handle_read_done, channel->use_16bit); + + ata_init_block_request(breq, channel, drive->offset, + ATAPI_BLOCK_SIZE, ata_pio_get_buffer_data(pio_setup)); + + err = blockif_read(bctxt, breq); + assert(!err); + } + else { + dprint(LOG_DEBUG, "ata_atapi_handle_read_done: READ completed\n"); + ata_atapi_cmd_done(channel); + } + + return; +} + +static void +ata_handle_identify(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + + if (drive->type == ATA_HD) { + ata_initialize_ident(channel); + + ata_set_data_block_ready(channel); + ata_set_status_ok(channel); + ata_irq_raise(channel); + } + else if (drive->type == ATAPI_CD) { + ata_command_aborted(channel); + ata_set_signature(drive); + } + else + assert(0); + + return; +} + +static void +ata_handle_setfeatures(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + + switch (drive->features) { + case ATA_SF_SETXFER: + channel->mode = drive->seccount; + dprint(LOG_DEBUG, "ata_handle_setfeatures ATA_SF_SETXFER: mode: %d\n", + channel->mode); + break; + case ATA_SF_ENAB_WCACHE: + break; + case ATA_SF_DIS_WCACHE: + break; + case ATA_SF_ENAB_PUIS: + break; + case ATA_SF_DIS_PUIS: + break; + case ATA_SF_PUIS_SPINUP: + break; + case ATA_SF_ENAB_RCACHE: + break; + case ATA_SF_DIS_RCACHE: + break; + case ATA_SF_ENAB_RELIRQ: + break; + case ATA_SF_DIS_RELIRQ: + break; + case ATA_SF_ENAB_SRVIRQ: + break; + case ATA_SF_DIS_SRVIRQ: + break; + default: + break; + } + + ata_set_status_ok(channel); + ata_irq_raise(channel); + + return; +} + +static void +ata_handle_set_multi(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + + channel->sectors_per_block = drive->seccount; + + assert(channel->sectors_per_block == ATA_SECTORS_PER_BLOCK); + + dprint(LOG_DEBUG, "ata_handle_set_multi: sectors_per_block: %d\n", + channel->sectors_per_block); + + ata_set_status_ok(channel); + ata_irq_raise(channel); + + return; +} + +static void +ata_handle_read_multiple(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + struct blockif_ctxt *bctxt = NULL; + struct ata_pio_setup *pio_setup = NULL; + struct blockif_req *breq = NULL; + + uint16_t sector_count; + uint32_t lba_address = 0; + uint64_t offset; + uint64_t count; + uint64_t size_transfer; + int err; + + bctxt = drive->bctxt; + pio_setup = &drive->pio_setup; + breq = &pio_setup->breq; + + ata_addressing_sec_count(channel, §or_count); + ata_addressing_28_lba(channel, &lba_address); + + dprint(LOG_DEBUG, "ata_handle_read_multiple: sector_count: %d, lba_address: 0x%x\n", + sector_count, lba_address); + + offset = (uint64_t)lba_address * ATA_SECTOR_SIZE; + count = (uint64_t)sector_count * ATA_SECTOR_SIZE; + + drive->count = count; + drive->offset = offset; + + size_transfer = count >= ATA_BLOCK_SIZE ? ATA_BLOCK_SIZE : count; + ata_pio_do_transfer(pio_setup, size_transfer, ata_read_multiple_block_done, channel->use_16bit); + + ata_init_block_request(breq, channel, drive->offset, + size_transfer, ata_pio_get_buffer_data(pio_setup)); + + err = blockif_read(bctxt, breq); + assert(!err); + + return; +} + +static void +ata_handle_read_verify(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + dprint(LOG_DEBUG, "ata_handle_read_verify\n"); + + ata_set_status_ok(channel); + drive->altstatus &= ~ATA_S_DRQ; + ata_irq_raise(channel); + + return; +} + +static void +ata_handle_write_multiple(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + struct blockif_ctxt *bctxt = NULL; + struct ata_pio_setup *pio_setup = NULL; + + uint16_t sector_count; + uint32_t lba_address = 0; + uint64_t offset; + uint64_t count; + uint64_t size_transfer; + + bctxt = drive->bctxt; + pio_setup = &drive->pio_setup; + + ata_addressing_sec_count(channel, §or_count); + ata_addressing_28_lba(channel, &lba_address); + + dprint(LOG_DEBUG, "ata_handle_write_multiple: sector_count: %d, lba_address: 0x%x\n", + sector_count, lba_address); + + offset = (uint64_t)lba_address * ATA_SECTOR_SIZE; + count = (uint64_t)sector_count * ATA_SECTOR_SIZE; + + drive->offset = offset; + drive->count = count; + + size_transfer = count >= ATA_BLOCK_SIZE ? ATA_BLOCK_SIZE : count; + ata_pio_do_transfer(pio_setup, size_transfer, ata_write_multiple_block_done, channel->use_16bit); + + return; +} + +static void +ata_handle_dma(struct ata_channel *channel, uint8_t op_dir) +{ + struct pci_ata_softc *sc = NULL; + struct pci_ata_dma_transaction *dma_transaction = NULL; + + uint16_t sector_count; + uint32_t lba_address = 0; + + sc = channel->pr_sc; + dma_transaction = &sc->dma_transactions[channel->interface]; + + assert(!dma_transaction->started); + + ata_addressing_sec_count(channel, §or_count); + ata_addressing_28_lba(channel, &lba_address); + + dprint(LOG_DEBUG, "ata_handle_dma: sector_count: %d, lba_address: 0x%x DIR: %d\n", + sector_count, lba_address, op_dir); + + dma_transaction->started = 1; + dma_transaction->offset = (uint64_t)lba_address * ATA_SECTOR_SIZE; + dma_transaction->nbytes = (uint64_t)sector_count * ATA_SECTOR_SIZE; + dma_transaction->op_dir = op_dir; + + return; +} + +static void +ata_handle_flushcache(struct ata_channel *channel) +{ + struct blockif_ctxt *bctxt = NULL; + struct blockif_req *breq = NULL; + int err; + + bctxt = channel->drives[channel->drive].bctxt; + breq = &channel->flush_breq; + + dprint(LOG_DEBUG, "ata_handle_flushcache\n"); + + ata_init_block_request(breq, channel, 0, 0, NULL); + + err = blockif_flush(bctxt, breq); + assert(!err); + + return; +} + +static void +ata_handle_seek(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + + dprint(LOG_DEBUG, "ata_handle_seek\n"); + + ata_set_status_ok(channel); + drive->status |= ATA_S_DSC; + ata_irq_raise(channel); + + return; +} + +static void +ata_handle_atapi_identify(struct ata_channel *channel) +{ + ata_atapi_initialize_ident(channel); + + ata_set_data_block_ready(channel); + ata_set_status_ok(channel); + ata_irq_raise(channel); + + return; +} + +static void +ata_handle_packet_cmd(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + struct ata_pio_setup *pio_setup = NULL; + + pio_setup = &drive->pio_setup; + + drive->byte_count = drive->lba_mid | (drive->lba_high << 8); + + dprint(LOG_DEBUG, "ata_handle_packet_cmd: byte_count: %d\n", drive->byte_count); + + ata_set_status_ok(channel); + drive->seccount = ATA_I_CMD; + drive->status |= ATA_S_DRQ; + + /* The Packet cmd is received as 6 words on both LPC and PCI */ + ata_pio_do_transfer(pio_setup, ATAPI_PACKET_SIZE, ata_atapi_cmd, 1); + + return; +} + +static void +ata_handle_cmd(struct ata_channel *channel, uint8_t cmd) +{ + switch (cmd) { + case ATA_ATA_IDENTIFY: + ata_handle_identify(channel); + break; + case ATA_SETFEATURES: + ata_handle_setfeatures(channel); + break; + case ATA_SET_MULTI: + ata_handle_set_multi(channel); + break; + case ATA_READ: + case ATA_READ_MUL: + ata_handle_read_multiple(channel); + break; + case ATA_READ_VERIFY: + ata_handle_read_verify(channel); + break; + case ATA_WRITE: + case ATA_WRITE_MUL: + ata_handle_write_multiple(channel); + break; + case ATA_READ_DMA: + ata_handle_dma(channel, ATA_DMA_READ); + break; + case ATA_WRITE_DMA: + ata_handle_dma(channel, ATA_DMA_WRITE); + break; + case ATA_FLUSHCACHE: + ata_handle_flushcache(channel); + break; + case ATA_SEEK: + ata_handle_seek(channel); + break; + case ATA_ATAPI_IDENTIFY: + ata_handle_atapi_identify(channel); + break; + case ATA_PACKET_CMD: + ata_handle_packet_cmd(channel); + break; + default: + dprint(LOG_ERR, "ata_handle_cmd: unsupported cmd: %02x\n", cmd); + assert(0); + break; + } + + return; +} + +/* + * ata_cpu_to_be32 translates from little endian to big endian + * the little endian format is used by the cpu + * @val - the integer value being translated + * @buf - the address where the result is stored + */ +static inline void +ata_cpu_to_be32(uint8_t *buf, uint32_t val) +{ + buf[0] = val >> 24; + buf[1] = val >> 16; + buf[2] = val >> 8; + buf[3] = val; + + return; +} + +/* + * ata_cpu_to_be16 translates from little endian to big endian + * the little endian format is used by the cpu + * @val - the word being translated + * @buf - the address where the result is stored + */ +static inline void +ata_cpu_to_be16(uint8_t *buf, uint16_t val) +{ + buf[0] = val >> 8; + buf[1] = val; + + return; +} + +/* + * ata_be32_to_cpu translates from big endian to little endian + * the little endian format is used by the cpu + * @val - the return integer value + * @buf - the address of the value being translated + */ +static inline uint32_t +ata_be32_to_cpu(uint8_t *buf) +{ + uint32_t val = 0; + + val |= buf[0] << 24; + val |= buf[1] << 16; + val |= buf[2] << 8; + val |= buf[3]; + + return val; +} + +/* + * ata_be16_to_cpu translates from big endian to little endian + * the little endian format is used by the cpu + * @val - the return word value + * @buf - the address of the value being translated + */ +static inline uint16_t +ata_be16_to_cpu(uint8_t *buf) +{ + uint16_t val = 0; + + val |= buf[0] << 8; + val |= buf[1]; + + return val; +} + +static void +ata_atapi_print_packet(uint8_t *packet) +{ + int i; + + dprint(LOG_DEBUG, "ata_atapi_print_packet cmd: 0x%x packet: ", packet[0]); + for (i = 0; i < ATAPI_PACKET_SIZE; i++) { + dprint(LOG_DEBUG, "%x ", packet[i]); + } + dprint(LOG_DEBUG, "\n"); + + return; +} + +static void +ata_atapi_data_request(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + + ata_set_status_ok(channel); + drive->seccount = ATA_I_IN; + drive->status |= ATA_S_DRQ; + + ata_irq_raise(channel); + + return; +} + +static void +ata_atapi_cmd_done(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + + ata_set_status_ok(channel); + drive->seccount = ATA_I_IN | ATA_I_CMD; + + ata_irq_raise(channel); + + return; +} + +static void +ata_atapi_handle_inquiry(struct ata_channel *channel, uint8_t *packet) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + struct ata_pio_setup *pio_setup = NULL; + struct scsi_inquiry_data *inquiry_data = (struct scsi_inquiry_data *)packet; + uint8_t max_len = 0; + + pio_setup = &drive->pio_setup; + + max_len = packet[4]; + assert(max_len >= SHORT_INQUIRY_LENGTH); + + /* for now we implement only the short inquiry cmd */ + if (packet[1] & 0x01) { + assert(0); + } else { + memset(inquiry_data, 0, SHORT_INQUIRY_LENGTH); + + inquiry_data->device = T_CDROM; + inquiry_data->dev_qual2 = SID_RMB; + inquiry_data->version = SCSI_REV_0; + inquiry_data->response_format = 2; + inquiry_data->additional_length = SHORT_INQUIRY_LENGTH - 5; + + memcpy(inquiry_data->vendor, "BHYVE ", SID_VENDOR_SIZE); + memcpy(inquiry_data->product, "ATAPI IDE CDROM ", SID_PRODUCT_SIZE); + memcpy(inquiry_data->revision, "1.1 ", SID_REVISION_SIZE); + + ata_pio_do_transfer(pio_setup, SHORT_INQUIRY_LENGTH, ata_atapi_cmd_done, channel->use_16bit); + ata_atapi_data_request(channel); + } + + return; +} + +static void +ata_atapi_handle_read_capacity(struct ata_channel *channel, uint8_t *packet) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + struct ata_pio_setup *pio_setup = NULL; + int nsectors = blockif_size(drive->bctxt) / ATAPI_BLOCK_SIZE; + + pio_setup = &drive->pio_setup; + + ata_cpu_to_be32(packet, nsectors - 1); + ata_cpu_to_be32(packet + 4, ATAPI_BLOCK_SIZE); + + ata_pio_do_transfer(pio_setup, 8, ata_atapi_cmd_done, channel->use_16bit); + ata_atapi_data_request(channel); + + return; +} + +static void lba_to_msf(uint8_t *buf, int lba) +{ + lba += 150; + buf[0] = (lba / 75) / 60; + buf[1] = (lba / 75) % 60; + buf[2] = lba % 75; + + return; +} + +static void +ata_atapi_handle_read_toc(struct ata_channel *channel, uint8_t *packet) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + struct ata_pio_setup *pio_setup = NULL; + int nsectors = blockif_size(drive->bctxt) / ATAPI_BLOCK_SIZE; + + uint8_t msf = packet[1] & 2; + uint8_t format = packet[2] & 0x0f; + uint8_t track_number = packet[6]; + uint16_t allocation_length = (packet[7] << 8) | packet[8]; + + uint16_t data_len = 0; + uint8_t *data_zone = NULL; + uint8_t *lead_out = NULL; + + pio_setup = &drive->pio_setup; + + assert(track_number <= 1 || track_number == 0xaa); + assert(!format); + + packet[2] = 1; /* First Track */ + packet[3] = 1; /* Last Track */ + + lead_out = packet + 4; + + if (track_number <= 1) { + data_zone = packet + 4; + lead_out = packet + 12; + + data_zone[0] = 0; /* Reserved */ + data_zone[1] = 0x14; /* ADR, control */ + data_zone[2] = 1; /* track number */ + data_zone[3] = 0; /* Reserved */ + if (msf) { + data_zone[4] = 0; + lba_to_msf(data_zone + 5, 0); + } + else { + ata_cpu_to_be32(data_zone + 4, 0); + } + } + + lead_out[0] = 0; /* Reserved */ + lead_out[1] = 0x16; /* ADR, control */ + lead_out[2] = 0xaa; /* track number */ + lead_out[3] = 0; /* Reserved */ + if (msf) { + lead_out[4] = 0; + lba_to_msf(lead_out + 5, nsectors); + } + else { + ata_cpu_to_be32(lead_out + 4, nsectors); + } + + data_len = (lead_out - packet) + 8; + ata_cpu_to_be16(packet, data_len - 2); + + data_len = (data_len < allocation_length) ? data_len : allocation_length; + + ata_pio_do_transfer(pio_setup, data_len, ata_atapi_cmd_done, channel->use_16bit); + ata_atapi_data_request(channel); + + return; +} + +static void +ata_atapi_handle_read(struct ata_channel *channel, uint8_t *packet) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + struct blockif_ctxt *bctxt = NULL; + struct ata_pio_setup *pio_setup = NULL; + struct blockif_req *breq = NULL; + + uint16_t sector_count = 0; + uint32_t lba_address = 0; + uint64_t offset; + uint64_t count; + int err; + + assert(drive->byte_count >= ATAPI_BLOCK_SIZE); + + bctxt = drive->bctxt; + pio_setup = &drive->pio_setup; + breq = &pio_setup->breq; + + lba_address = ata_be32_to_cpu(packet + 2); + sector_count = ata_be16_to_cpu(packet + 7); + + dprint(LOG_DEBUG, "ata_atapi_handle_read: sector_count: %d, lba_address: 0x%x\n", + sector_count, lba_address); + + offset = (uint64_t)lba_address * ATAPI_BLOCK_SIZE; + count = (uint64_t)sector_count * ATAPI_BLOCK_SIZE; + + drive->count = count; + drive->offset = offset; + + drive->lba_mid = ATAPI_BLOCK_SIZE & 0xff; + drive->lba_high = (ATAPI_BLOCK_SIZE >> 8) & 0xff; + + drive->seccount = ATA_I_IN; + + ata_pio_do_transfer(pio_setup, ATAPI_BLOCK_SIZE, ata_atapi_handle_read_done, channel->use_16bit); + + ata_init_block_request(breq, channel, drive->offset, + ATAPI_BLOCK_SIZE, ata_pio_get_buffer_data(pio_setup)); + + err = blockif_read(bctxt, breq); + assert(!err); + + return; +} + +static void +ata_atapi_cmd(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + struct ata_pio_setup *pio_setup = NULL; + uint8_t *packet = NULL; + uint8_t op_code = 0; + + pio_setup = &drive->pio_setup; + packet = ata_pio_get_buffer_data(pio_setup); + + ata_atapi_print_packet(packet); + + op_code = packet[0]; + + switch (op_code) { + case INQUIRY: + ata_atapi_handle_inquiry(channel, packet); + break; + case READ_CAPACITY: + ata_atapi_handle_read_capacity(channel, packet); + break; + case PREVENT_ALLOW: + ata_atapi_cmd_done(channel); + break; + case READ_TOC: + ata_atapi_handle_read_toc(channel, packet); + break; + case READ_10: + ata_atapi_handle_read(channel, packet); + break; + case TEST_UNIT_READY: + ata_atapi_cmd_done(channel); + break; + default: + dprint(LOG_ERR, "ata_atapi_cmd: unsupported cmd: %02x\n", op_code); + assert(0); + break; + }; + + return; +} + +static uint64_t +ata_command_block_read(struct ata_channel *channel, uint64_t offset) +{ + uint64_t value = 0; + struct ata_drive *drive = &channel->drives[channel->drive]; + struct ata_pio_setup *pio_setup = NULL; + + pio_setup = &drive->pio_setup; + + switch (offset) { + case ATA_DATA_REG: + value = ata_pio_get_uint(pio_setup); + break; + case ATA_ERROR_REG: + value = drive->error; + dprint(LOG_DEBUG, "ata_command_block_read: ATA_ERROR_REG: %lxh\n", value); + break; + case ATA_SECCOUNT_REG: + value = drive->seccount; + dprint(LOG_DEBUG, "ata_command_block_read: ATA_SECCOUNT_REG: %lxh\n", value); + break; + case ATA_LBA_LOW_REG: + value = drive->lba_low; + dprint(LOG_DEBUG, "ata_command_block_read: ATA_LBA_LOW_REG: %lxh\n", value); + break; + case ATA_LBA_MID_REG: + value = drive->lba_mid; + dprint(LOG_DEBUG, "ata_command_block_read: ATA_LBA_MID_REG: %lxh\n", value); + break; + case ATA_LBA_HIGH_REG: + value = drive->lba_high; + dprint(LOG_DEBUG, "ata_command_block_read: ATA_LBA_HIGH_REG: %lxh\n", value); + break; + case ATA_HDDEVSEL_REG: + value = drive->hddevsel; + dprint(LOG_DEBUG, "ata_command_block_read: ATA_HDDEVSEL_REG: %lxh\n", value); + break; + case ATA_STATUS_REG: + ata_irq_lower(channel); + + value = drive->status; + dprint(LOG_DEBUG, "ata_command_block_read: ATA_STATUS_REG: %lxh\n", value); + break; + default: + value = 0; + break; + } + + return value; +} + +static void +ata_command_block_write(struct ata_channel *channel, + uint64_t offset, int size, uint64_t value) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + struct ata_drive *drive0 = &channel->drives[ATA_MASTER]; + struct ata_drive *drive1 = &channel->drives[ATA_SLAVE]; + struct ata_pio_setup *pio_setup = NULL; + + uint8_t reg_value; + + pio_setup = &drive->pio_setup; + + switch (offset) { + case ATA_DATA_REG: + ata_pio_put_uint(pio_setup, value); + break; + case ATA_FEATURES_REG: + drive0->features = value; + drive1->features = value; + dprint(LOG_DEBUG, "ata_command_block_write: ATA_FEATURES_REG: %xh\n", (uint8_t)value); + break; + case ATA_SECCOUNT_REG: + drive0->seccount = value; + drive1->seccount = value; + dprint(LOG_DEBUG, "ata_command_block_write: ATA_SECCOUNT_REG: %xh\n", (uint8_t)value); + break; + case ATA_LBA_LOW_REG: + drive0->lba_low = value; + drive1->lba_low = value; + dprint(LOG_DEBUG, "ata_command_block_write: ATA_LBA_LOW_REG: %xh\n", (uint8_t)value); + break; + case ATA_LBA_MID_REG: + drive0->lba_mid = value; + drive1->lba_mid = value; + dprint(LOG_DEBUG, "ata_command_block_write: ATA_LBA_MID_REG: %xh\n", (uint8_t)value); + break; + case ATA_LBA_HIGH_REG: + drive0->lba_high = value; + drive1->lba_high = value; + dprint(LOG_DEBUG, "ata_command_block_write: ATA_LBA_HIGH_REG: %xh\n", (uint8_t)value); + break; + case ATA_HDDEVSEL_REG: + reg_value = value; + + drive0->hddevsel = value; + drive1->hddevsel = value; + dprint(LOG_DEBUG, "ata_command_block_write: ATA_HDDEVSEL_REG: %xh\n", reg_value); + + if (reg_value & ATA_D_IBM) + channel->size = ATA_SECTOR_SIZE; + if (reg_value & ATA_DEV(ATA_SLAVE)) { + dprint(LOG_DEBUG, "ata_command_block_write: select ATA_SLAVE\n"); + channel->drive = ATA_SLAVE; + } + else { + dprint(LOG_DEBUG, "ata_command_block_write: select ATA_MASTER\n"); + channel->drive = ATA_MASTER; + } + if (!ata_select_drive_is_ok(channel)) { + drive1->status = ATA_DRIVE_ABSENT; + } + break; + case ATA_COMMAND_REG: + drive0->command = value; + drive1->command = value; + dprint(LOG_DEBUG, "ata_command_block_write: ATA_COMMAND_REG: %xh\n", (uint8_t)value); + ata_handle_cmd(channel, value); + break; + default: + dprint(LOG_ERR, "ata_command_block_write: unsupported reg:%lx\n", offset); + assert(0); + break; + } + + return; +} + +static uint64_t +ata_altstatus_read(struct ata_channel *channel) +{ + struct ata_drive *drive = &channel->drives[channel->drive]; + uint64_t value = 0; + + value = drive->altstatus; + + dprint(LOG_DEBUG, "ata_altstatus_read: value: %lu\n", value); + + return value; +} + +static void +ata_control_write(struct ata_channel *channel, + int size, uint64_t value) +{ + uint8_t reg_value; + struct ata_drive *drive = &channel->drives[channel->drive]; + + reg_value = (uint8_t)value; + + dprint(LOG_DEBUG, "ata_control_write: irq_disabled: %d\n", reg_value & ATA_A_IDS); + + if (reg_value & ATA_A_IDS) + drive->irq_disabled = 1; + else + drive->irq_disabled = 0; + + if (reg_value & ATA_A_RESET) + ata_srst(channel); + + return; +} + +/* + * PCI ATA function definitions + */ +static void +pci_ata_intr_assert(void *arg) +{ + struct pci_ata_softc *sc = (struct pci_ata_softc *)arg; + struct pci_devinst *pi = sc->asc_pi; + + pci_lintr_assert(pi); + + return; +} + +static void +pci_ata_intr_deassert(void *arg) +{ + struct pci_ata_softc *sc = (struct pci_ata_softc *)arg; + struct pci_devinst *pi = sc->asc_pi; + + pci_lintr_deassert(pi); + + return; +} + +static int +pci_ata_parse_params(const char *opts, char *opt0, char *opt1, uint8_t *nr_channels) +{ + uint8_t len; + char tmp_opts[128]; + char *ch0 = NULL, *ch1 = NULL; + uint8_t i; + + if (!opts) { + dprint(LOG_ERR, "pci_ata_parse_params: opts should be like: %s\n", + "X,MASTER_X[,SLAVE_X][;Y,MASTER_Y[,SLAVE_Y]]"); + return -1; + } + + len = strlen(opts); + if (len >= 128) { + dprint(LOG_ERR, "pci_ata_parse_params: PCI ATA string params too big\n"); + return -1; + } + + strcpy(tmp_opts, opts); + + ch0 = tmp_opts; + + for (i = 0; i < len; i++) { + if (tmp_opts[i] == PCI_ATA_CH_SEP) { + if (ch1) { + dprint(LOG_ERR, "pci_ata_parse_params: to many channels\n"); + return -1; + } + tmp_opts[i] = '\0'; + ch1 = tmp_opts + i + 1; + } + } + + if (strlen(ch0) >= 64) { + dprint(LOG_ERR, "pci_ata_parse_params: the name of ATA_X channel too big\n"); + return -1; + } + strcpy(opt0, ch0); + *nr_channels = 1; + + if (ch1 != NULL && strlen(ch1) < 64) { + strcpy(opt1, ch1); + *nr_channels = 2; + } + + return 0; +} + +static int +pci_ata_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts, int atapi) +{ + struct ata_channel *channel = NULL; + struct pci_ata_softc *pci_ata_sc = NULL; + char ch_opts[2][64]; + uint8_t nr_channels = 0; + int ret, i; + + ata_open_dbg_file(); + + ret = pci_ata_parse_params(opts, ch_opts[0], ch_opts[1], &nr_channels); + if (ret) { + dprint(LOG_ERR, "pci_ata_init: some errors with the input checking\n"); + return -1; + } + + pci_ata_sc = calloc(1, sizeof(struct pci_ata_softc)); + if (!pci_ata_sc) + return -1; + + pci_ata_sc->channels[ATA_X] = NULL; + pci_ata_sc->channels[ATA_Y] = NULL; + + for (i = 0; i < nr_channels; i++) { + channel = ata_init(ctx, pci_ata_intr_assert, pci_ata_intr_deassert, + pci_ata_sc, ch_opts[i], 0); + if (!channel) + continue; + channel->use_16bit = 0; + assert(!pci_ata_sc->channels[channel->interface]); + pci_ata_sc->channels[channel->interface] = channel; + } + + /* exit if none of the channels are initialized */ + if (!pci_ata_sc->channels[ATA_X] && !pci_ata_sc->channels[ATA_Y]) { + free(pci_ata_sc); + return -1; + } + + pi->pi_arg = pci_ata_sc; + pci_ata_sc->asc_pi = pi; + + /* generic PCI ATA/ATAPI device ATA_IT8211F */ + pci_set_cfgdata16(pi, PCIR_DEVICE, 0x8211); /* ATA/ATAPI Controller */ + pci_set_cfgdata16(pi, PCIR_VENDOR, 0x1283); /* Waldo */ + + /* this is a storage class device */ + pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE); + /* this is an IDE/ATA type device */ + pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_STORAGE_IDE); + + pci_set_cfgdata8(pi, PCIR_PROGIF, PCIP_STORAGE_IDE_MASTERDEV | + PCIP_STORAGE_IDE_MODEPRIM | PCIP_STORAGE_IDE_MODESEC); + + pci_emul_alloc_bar(pi, PCI_BAR0, PCIBAR_IO, ATA_IOSIZE); + pci_emul_alloc_bar(pi, PCI_BAR1, PCIBAR_IO, ATA_CTLIOSIZE); + pci_emul_alloc_bar(pi, PCI_BAR2, PCIBAR_IO, ATA_IOSIZE); + pci_emul_alloc_bar(pi, PCI_BAR3, PCIBAR_IO, ATA_CTLIOSIZE); + pci_emul_alloc_bar(pi, PCI_BAR4, PCIBAR_IO, ATA_CHANNELS * ATA_BMIOSIZE); + + pci_lintr_request(pi); + + return 0; +} + +static int +pci_ata_hd_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) +{ + return (pci_ata_init(ctx, pi, opts, 0)); +} + +static void +pci_ata_dma_start(struct pci_ata_softc *sc, uint8_t ch) +{ + struct vmctx *ctx = NULL; + struct ata_channel *channel = NULL; + struct blockif_ctxt *bctxt = NULL; + struct pci_ata_dma_transaction *dma_transaction = NULL; + struct blockif_req *breq = NULL; + + struct prd_entry *prdt = NULL; + struct prd_entry *entry = NULL; + uint32_t prdt_address; + uint32_t index; + uint32_t byte_count; + void *entry_buff = NULL; + int err; + + channel = sc->channels[ch]; + + ctx = channel->vm_ctx; + assert(ctx != NULL); + + bctxt = channel->drives[channel->drive].bctxt; + dma_transaction = &sc->dma_transactions[ch]; + + assert(dma_transaction->started); + + assert(((sc->cmd[ch] & ATA_BMCMD_WRITE_READ) && dma_transaction->op_dir == ATA_DMA_READ) || + (!(sc->cmd[ch] & ATA_BMCMD_WRITE_READ) && dma_transaction->op_dir == ATA_DMA_WRITE)); + + sc->stat[ch] |= ATA_BMSTAT_ACTIVE; + + /* prdt_address is the physical address of the prdt in the guest memory */ + prdt_address = sc->prdt[ch]; + + /* map the guest physical address into the host virtual address */ + prdt = paddr_guest2host(ctx, prdt_address, ATA_DMA_PRDT_SIZE); + assert(prdt != NULL); + + breq = &dma_transaction->breq; + breq->br_param = channel; + breq->br_callback = ata_handle_block_request; + breq->br_offset = dma_transaction->offset; + + dma_transaction->is_eot = 0; + index = 0; + while (!dma_transaction->is_eot && dma_transaction->nbytes > 0) { + entry = prdt + index; + + entry_buff = paddr_guest2host(ctx, entry->prd_address, entry->byte_count); + byte_count = entry->byte_count ? entry->byte_count : ATA_DMA_MAX_PRD_COUNT; + + if (byte_count > dma_transaction->nbytes) { + dprint(LOG_ERR, "pci_ata_dma_start: the PRD entry contains %s", + "more bytes than the ATA transfer size\n"); + byte_count = dma_transaction->nbytes; + } + + if (index == BLOCKIF_IOV_MAX) { + dprint(LOG_ERR, "pci_ata_dma_start: the PRD Table contains %s", + "more than BLOCKIF_IOV_MAX entries\n"); + break; + } + + breq->br_iov[index].iov_len = byte_count; + breq->br_iov[index].iov_base = entry_buff; + + dma_transaction->nbytes -= byte_count; + + if (entry->vendor_specific & ATA_DMA_PRDT_EOT) + dma_transaction->is_eot = 1; + + index++; + } + + breq->br_iovcnt = index; + + sc->stat[ch] |= ATA_BMSTAT_INTERRUPT; + + /* + * Use asynchronous operations for read / write on the block; + * update the status registers and notify the guest operating + * system using an interrupt in the ata_handle_block_request callback + */ + if (dma_transaction->op_dir == ATA_DMA_READ) { + err = blockif_read(bctxt, breq); + } + else if (dma_transaction->op_dir == ATA_DMA_WRITE) { + err = blockif_write(bctxt, breq); + } + else { + assert(0); + } + + assert(!err); + + return; +} + +static void +pci_ata_dma_stop(struct pci_ata_softc *sc, uint8_t ch) +{ + struct ata_channel *channel = NULL; + struct pci_ata_dma_transaction *dma_transaction = NULL; + + channel = sc->channels[ch]; + dma_transaction = &sc->dma_transactions[ch]; + + if(!dma_transaction->started) { + return; + } + + if (dma_transaction->is_eot && dma_transaction->nbytes == 0) { + sc->stat[ch] |= ATA_BMSTAT_INTERRUPT; + sc->stat[ch] &= ~ATA_BMSTAT_ERROR; + sc->stat[ch] &= ~ATA_BMSTAT_ACTIVE; + } + else if (!dma_transaction->is_eot && dma_transaction->nbytes == 0) { + sc->stat[ch] |= ATA_BMSTAT_INTERRUPT; + sc->stat[ch] &= ~ATA_BMSTAT_ERROR; + sc->stat[ch] |= ATA_BMSTAT_ACTIVE; + dprint(LOG_ERR, "pci_ata_dma_stop: DMA PRDT transfer was %s", + "larger than the ATA transfer size\n"); + } + else if (dma_transaction->is_eot && dma_transaction->nbytes > 0) { + sc->stat[ch] &= ~ATA_BMSTAT_INTERRUPT; + sc->stat[ch] &= ~ATA_BMSTAT_ERROR; + sc->stat[ch] &= ~ATA_BMSTAT_ACTIVE; + dprint(LOG_ERR, "pci_ata_dma_stop: DMA PRDT transfer was %s", + "smaller than the ATA transfer size\n"); + } + else if (!dma_transaction->is_eot && dma_transaction->nbytes > 0) { + sc->stat[ch] |= ATA_BMSTAT_ERROR; + dprint(LOG_ERR, "pci_ata_dma_stop: the PRD Table contains %s", + "more than BLOCKIF_IOV_MAX entries\n"); + } + + dma_transaction->started = 0; + + return; +} + +static uint64_t +pci_ata_bus_master_read(struct pci_ata_softc *sc, uint64_t offset) +{ + uint64_t value = 0; + + switch (offset) { + case ATA_BMCMD_X_REG: + value = sc->cmd[ATA_X]; + dprint(LOG_DEBUG, "pci_ata_bus_master_read: ATA_BMCMD_X_REG: %lu\n", value); + break; + case ATA_BMSTAT_X_REG: + value = sc->stat[ATA_X]; + dprint(LOG_DEBUG, "pci_ata_bus_master_read: ATA_BMSTAT_X_REG: %lu\n", value); + break; + case ATA_BMPRDT_X_REG: + value = sc->prdt[ATA_X]; + dprint(LOG_DEBUG, "pci_ata_bus_master_read: ATA_BMPRDT_X_REG: %lu\n", value); + break; + case ATA_BMCMD_Y_REG: + value = sc->cmd[ATA_Y]; + dprint(LOG_DEBUG, "pci_ata_bus_master_read: ATA_BMCMD_Y_REG: %lu\n", value); + break; + case ATA_BMSTAT_Y_REG: + value = sc->stat[ATA_Y]; + dprint(LOG_DEBUG, "pci_ata_bus_master_read: ATA_BMSTAT_Y_REG: %lu\n", value); + break; + case ATA_BMPRDT_Y_REG: + value = sc->prdt[ATA_Y]; + dprint(LOG_DEBUG, "pci_ata_bus_master_read: ATA_BMPRDT_Y_REG: %lu\n", value); + break; + default: + value = 0; + break; + } + + return value; +} + +static void +pci_ata_bus_master_write(struct pci_ata_softc *sc, + uint64_t offset, int size, uint64_t value) +{ + + uint8_t reg_value; + + if (offset != ATA_BMPRDT_X_REG && offset != ATA_BMPRDT_Y_REG && size != 1) + return; + if ((offset == ATA_BMPRDT_X_REG || offset == ATA_BMPRDT_Y_REG) && size != 4) + return; + + switch (offset) { + case ATA_BMCMD_X_REG: + reg_value = sc->cmd[ATA_X] = (uint8_t)value; + + dprint(LOG_DEBUG, "pci_ata_bus_master_write[X]: ATA_BMCMD_START_STOP: %d\n", + reg_value & ATA_BMCMD_START_STOP); + if (reg_value & ATA_BMCMD_START_STOP) { + pci_ata_dma_start(sc, ATA_X); + } + else { + pci_ata_dma_stop(sc, ATA_X); + } + + dprint(LOG_DEBUG, "pci_ata_bus_master_write[X]: ATA_BMCMD_WRITE_READ: %d\n", + reg_value & ATA_BMCMD_WRITE_READ); + break; + case ATA_BMSTAT_X_REG: + reg_value = sc->stat[ATA_X] = (uint8_t)value; + dprint(LOG_DEBUG, "pci_ata_bus_master_write[X]: ATA_BMSTAT_INTERRUPT: %d, ATA_BMSTAT_ERROR: %d\n", + reg_value & ATA_BMSTAT_INTERRUPT, reg_value & ATA_BMSTAT_ERROR); + if (reg_value & ATA_BMSTAT_INTERRUPT) + sc->stat[ATA_X] &= ~ATA_BMSTAT_INTERRUPT; + if (reg_value & ATA_BMSTAT_ERROR) + sc->stat[ATA_X] &= ~ATA_BMSTAT_ERROR; + break; + case ATA_BMPRDT_X_REG: + dprint(LOG_DEBUG, "pci_ata_bus_master_write[X]: ATA_BMPRDT_X_REG: 0x%x\n", (uint32_t)value); + sc->prdt[ATA_X] = value; + break; + case ATA_BMCMD_Y_REG: + reg_value = sc->cmd[ATA_Y] = (uint8_t)value; + + dprint(LOG_DEBUG, "pci_ata_bus_master_write[Y]: ATA_BMCMD_START_STOP: %d\n", + reg_value & ATA_BMCMD_START_STOP); + if (reg_value & ATA_BMCMD_START_STOP) { + pci_ata_dma_start(sc, ATA_Y); + } + else { + pci_ata_dma_stop(sc, ATA_Y); + } + + dprint(LOG_DEBUG, "pci_ata_bus_master_write[Y]: ATA_BMCMD_WRITE_READ: %d\n", + reg_value & ATA_BMCMD_WRITE_READ); + break; + case ATA_BMSTAT_Y_REG: + reg_value = sc->stat[ATA_Y] = (uint8_t)value; + dprint(LOG_DEBUG, "pci_ata_bus_master_write[Y]: ATA_BMSTAT_INTERRUPT: %d, ATA_BMSTAT_ERROR: %d\n", + reg_value & ATA_BMSTAT_INTERRUPT, reg_value & ATA_BMSTAT_ERROR); + if (reg_value & ATA_BMSTAT_INTERRUPT) + sc->stat[ATA_Y] &= ~ATA_BMSTAT_INTERRUPT; + if (reg_value & ATA_BMSTAT_ERROR) + sc->stat[ATA_Y] &= ~ATA_BMSTAT_ERROR; + break; + case ATA_BMPRDT_Y_REG: + dprint(LOG_DEBUG, "pci_ata_bus_master_write[Y]: ATA_BMPRDT_X_REG: 0x%x\n", (uint32_t)value); + sc->prdt[ATA_Y] = (uint32_t)value; + break; + default: + break; + } + + return; +} + +static uint64_t +pci_ata_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, + uint64_t offset, int size) +{ + struct pci_ata_softc *sc = pi->pi_arg; + uint64_t value = 0; + + switch (baridx) { + case PCI_BAR0: + if (!sc->channels[ATA_X]) + break; + assert(ata_channel_is_ok(sc->channels[ATA_X], ATA_X)); + value = ata_command_block_read(sc->channels[ATA_X], offset); + break; + case PCI_BAR1: + if (!sc->channels[ATA_X]) + break; + assert(ata_channel_is_ok(sc->channels[ATA_X], ATA_X)); + value = ata_altstatus_read(sc->channels[ATA_X]); + break; + case PCI_BAR2: + if (!sc->channels[ATA_Y]) + break; + assert(ata_channel_is_ok(sc->channels[ATA_Y], ATA_Y)); + value = ata_command_block_read(sc->channels[ATA_Y], offset); + break; + case PCI_BAR3: + if (!sc->channels[ATA_Y]) + break; + assert(ata_channel_is_ok(sc->channels[ATA_Y], ATA_Y)); + value = ata_altstatus_read(sc->channels[ATA_Y]); + break; + case PCI_BAR4: + value = pci_ata_bus_master_read(sc, offset); + break; + default: + assert(0); + break; + } + + return value; +} + +static void +pci_ata_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int baridx, uint64_t offset, int size, uint64_t value) +{ + struct pci_ata_softc *sc = pi->pi_arg; + + switch (baridx) { + case PCI_BAR0: + if (!sc->channels[ATA_X]) + break; + assert(ata_channel_is_ok(sc->channels[ATA_X], ATA_X)); + ata_command_block_write(sc->channels[ATA_X], offset, size, value); + break; + case PCI_BAR1: + if (!sc->channels[ATA_X]) + break; + assert(ata_channel_is_ok(sc->channels[ATA_X], ATA_X)); + ata_control_write(sc->channels[ATA_X], size, value); + break; + case PCI_BAR2: + if (!sc->channels[ATA_Y]) + break; + assert(ata_channel_is_ok(sc->channels[ATA_Y], ATA_Y)); + ata_command_block_write(sc->channels[ATA_Y], offset, size, value); + break; + case PCI_BAR3: + if (!sc->channels[ATA_Y]) + break; + assert(ata_channel_is_ok(sc->channels[ATA_Y], ATA_Y)); + ata_control_write(sc->channels[ATA_Y], size, value); + break; + case PCI_BAR4: + pci_ata_bus_master_write(sc, offset, size, value); + break; + default: + assert(0); + break; + } + + return; +} + +/* + * LPC ATA function definitions + */ +static void +lpc_ata_intr_assert(void *arg) +{ + struct lpc_ata_softc *sc_lpc_ata = (struct lpc_ata_softc *)arg; + struct vmctx *lpc_ctx = sc_lpc_ata->channel->vm_ctx; + + vm_isa_pulse_irq(lpc_ctx, sc_lpc_ata->irq, sc_lpc_ata->irq); + + return; +} + +static void +lpc_ata_intr_deassert(void *arg) +{ + /* + * The ATA devices on the LPC bus generate edge triggered interrupts + * so nothing more to do here + */ + + return; +} + +int +lpc_ata_init(struct vmctx *ctx, const char *opts) +{ + struct ata_channel *channel = NULL; + struct inout_port iop, ioctlp; + int ch_ata = -1; + int err; + + ata_open_dbg_file(); + + assert(opts[0] == '0' || opts[0] == '1'); + ch_ata = opts[0] - '0'; + + if (lpc_ata_sc[ch_ata].channel) { + dprint(LOG_ERR, "lpc_ata_init: the LPC ata%d is initialized\n", ch_ata); + return -1; + } + + channel = ata_init(ctx, lpc_ata_intr_assert, lpc_ata_intr_deassert, &lpc_ata_sc[ch_ata], opts, 1); + if (!channel) + return -1; + + assert(ch_ata == channel->interface); + channel->use_16bit = 1; + + lpc_ata_sc[ch_ata].channel = channel; + + bzero(&iop, sizeof(struct inout_port)); + iop.name = lpc_ata_sc[ch_ata].name_io; + iop.port = lpc_ata_sc[ch_ata].base_addr_io; + iop.size = ATA_IOSIZE; + iop.flags = IOPORT_F_INOUT; + iop.handler = lpc_ata_io_handler; + iop.arg = &lpc_ata_sc[ch_ata]; + + err = register_inout(&iop); + assert(err == 0); + + bzero(&ioctlp, sizeof(struct inout_port)); + ioctlp.name = lpc_ata_sc[ch_ata].name_ioctl; + ioctlp.port = lpc_ata_sc[ch_ata].base_addr_ioctl; + ioctlp.size = ATA_CTLIOSIZE; + ioctlp.flags = IOPORT_F_INOUT; + ioctlp.handler = lpc_ata_ioctl_handler; + ioctlp.arg = &lpc_ata_sc[ch_ata]; + + err = register_inout(&ioctlp); + assert(err == 0); + + return 0; +} + +static int +lpc_ata_io_handler(struct vmctx *ctx, int vcpu, + int in, int port, int bytes, uint32_t *eax, void *arg) +{ + struct lpc_ata_softc *lpc_sc = (struct lpc_ata_softc *)arg; + struct ata_channel *channel = NULL; + uint8_t offset = 0; + + assert(lpc_sc); + channel = lpc_sc->channel; + assert(channel); + + offset = port - lpc_sc->base_addr_io; + assert(offset < ATA_IOSIZE); + + if (in) + *eax = ata_command_block_read(channel, offset); + else + ata_command_block_write(channel, offset, bytes, *eax); + + return 0; +} + +static int +lpc_ata_ioctl_handler(struct vmctx *ctx, int vcpu, + int in, int port, int bytes, uint32_t *eax, void *arg) +{ + struct lpc_ata_softc *lpc_sc = (struct lpc_ata_softc *)arg; + struct ata_channel *channel = NULL; + uint8_t offset = 0; + + assert(lpc_sc); + channel = lpc_sc->channel; + assert(channel); + + offset = port - lpc_sc->base_addr_ioctl; + assert(offset < ATA_CTLIOSIZE); + + if (in) + *eax = ata_altstatus_read(channel); + else + ata_control_write(channel, bytes, *eax); + + return 0; +} + Index: pci_lpc.c =================================================================== --- pci_lpc.c +++ pci_lpc.c @@ -73,6 +73,11 @@ static const char *lpc_uart_names[LPC_UART_NUM] = { "COM1", "COM2" }; +extern int +lpc_ata_init(struct vmctx *ctx, const char *opts); +static const char *lpc_ata0_opts = NULL; +static const char *lpc_ata1_opts = NULL; + /* * LPC device configuration is in the following form: * [,] @@ -95,6 +100,16 @@ goto done; } } + + if (strcasecmp(lpcdev, "ata-hd") == 0) { + if (str[0] == '0') + lpc_ata0_opts = str; + else if (str[0] == '1') + lpc_ata1_opts = str; + + error = 0; + goto done; + } } done: @@ -197,6 +212,11 @@ sc->enabled = 1; } + if (lpc_ata0_opts) + lpc_ata_init(lpc_bridge->pi_vmctx, lpc_ata0_opts); + if (lpc_ata1_opts) + lpc_ata_init(lpc_bridge->pi_vmctx, lpc_ata1_opts); + return (0); } @@ -379,6 +399,8 @@ return (-1); } + lpc_bridge = pi; + if (lpc_init() != 0) return (-1); @@ -388,8 +410,6 @@ pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_BRIDGE); pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_BRIDGE_ISA); - lpc_bridge = pi; - return (0); }