iateaca updated this revision to Diff 32081.
iateaca added a comment.

  add Copyright and license

CHANGES SINCE LAST UPDATE
  https://reviews.freebsd.org/D5473?vs=13829&id=32081

REVISION DETAIL
  https://reviews.freebsd.org/D5473

AFFECTED FILES
  Makefile
  ata.c
  pci_lpc.c

EMAIL PREFERENCES
  https://reviews.freebsd.org/settings/panel/emailpreferences/

To: iateaca, grehan, tychon, neel, mav
Cc: editor_callfortesting.org, freebsd-virtualization-list
diff --git a/pci_lpc.c b/pci_lpc.c
--- a/pci_lpc.c
+++ b/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);
 }
 
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -11,6 +11,7 @@
 SRCS=	\
 	atkbdc.c		\
 	acpi.c			\
+	ata.c			\
 	bhyverun.c		\
 	block_if.c		\
 	consport.c		\
diff --git a/ata.c b/ata.c
--- a/ata.c
+++ b/ata.c
@@ -0,0 +1,2606 @@
+/*-
+ * Copyright (c) 2016 Alex Teaca <iate...@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 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, &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];
+
+	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, &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;
+}
+

_______________________________________________
freebsd-virtualization@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/freebsd-virtualization
To unsubscribe, send any mail to 
"freebsd-virtualization-unsubscr...@freebsd.org"

Reply via email to