Page MenuHomeFreeBSD

D5473.id32422.diff
No OneTemporary

D5473.id32422.diff

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,2603 @@
+/*-
+ * Copyright (c) 2016 Alex Teaca <iateaca@freebsd.org>
+ * 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 <sys/stat.h>
+#include <sys/ata.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <cam/scsi/scsi_all.h>
+#include <machine/vmm.h>
+#include <vmmapi.h>
+
+#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 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, &sectors);
+
+ 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];
+
+ dprint(LOG_DEBUG, "ata_handle_set_multi: drive->seccount: %d\n",
+ drive->seccount);
+
+ assert(drive->seccount <= ATA_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, &sector_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, &sector_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, &sector_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:
* <lpc_device_name>[,<options>]
@@ -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);
}

File Metadata

Mime Type
text/plain
Expires
Tue, Mar 31, 7:53 AM (30 m, 23 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30627836
Default Alt Text
D5473.id32422.diff (67 KB)

Event Timeline