Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F150214239
D5473.id32422.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
67 KB
Referenced Files
None
Subscribers
None
D5473.id32422.diff
View Options
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, §ors);
+
+ ident_data->cylinders = cylinders;
+ ident_data->heads = heads;
+ ident_data->sectors = sectors;
+
+ /* Model: BHYVE ATA IDE DISK */
+ memcpy(ident_data->model, "HBVY ETA ADI EIDKS ", 40);
+ /* Serial number: 123456 */
+ memcpy(ident_data->serial, "214365 ", 20);
+ /* Firmware version: 1.0 */
+ memcpy(ident_data->revision, ".1 0 ", 8);
+
+ dprint(LOG_DEBUG, "ata_initialize_ident: channel: %d, drive: %d C: %d, H: %d, S: %d, total_sectors: %d\n",
+ channel->interface, channel->drive, cylinders, heads, sectors, total_sectors);
+
+ ata_pio_do_transfer(pio_setup, sizeof(struct ata_params), ata_pio_generic_end_transfer, channel->use_16bit);
+
+ return;
+}
+
+static void
+ata_atapi_initialize_ident(struct ata_channel *channel)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+ struct ata_params *ident_data = NULL;
+ struct ata_pio_setup *pio_setup = NULL;
+
+ pio_setup = &drive->pio_setup;
+ ident_data = (struct ata_params *)ata_pio_get_buffer_data(pio_setup);
+
+ memset(ident_data, 0, sizeof(struct ata_params));
+
+ /* CDROM device */
+ ident_data->config |= (ATA_PROTO_ATAPI_12 | ATA_ATAPI_TYPE_CDROM | ATA_DRQ_FAST);
+
+ /* Use DMA2 mode */
+ ident_data->capabilities1 |= ATA_SUPPORT_DMA;
+ ident_data->mwdmamodes |= ATA_W_DMA2;
+
+ /* Use PIO4 mode */
+ ident_data->atavalid |= ATA_FLAG_64_70;
+ ident_data->apiomodes |= ATA_MODE_PIO4;
+
+ /* We use 28-bit LBA addressing because CHS addressing is obsolete in ATA/ATAPI 6 */
+ ident_data->capabilities1 |= ATA_SUPPORT_LBA;
+
+ /* Major version number = ATA/ATAPI-6 */
+ ident_data->version_major |= ATA_ATAPI_6;
+
+ /* Model: BHYVE ATAPI IDE CDROM */
+ memcpy(ident_data->model, "HBVY ETAPA IDI EDCOR M ", 40);
+ /* Serial number: 123456 */
+ memcpy(ident_data->serial, "214365 ", 20);
+ /* Firmware version: 1.0 */
+ memcpy(ident_data->revision, ".1 0 ", 8);
+
+ dprint(LOG_DEBUG, "ata_atapi_initialize_ident: channel: %d, drive: %d\n",
+ channel->interface, channel->drive);
+
+ ata_pio_do_transfer(pio_setup, sizeof(struct ata_params), ata_pio_generic_end_transfer, channel->use_16bit);
+
+ return;
+}
+
+static int
+ata_channel_is_ok(struct ata_channel *channel, uint8_t ch)
+{
+ if (channel->interface == ch)
+ return 1;
+
+ return 0;
+}
+
+static int
+ata_select_drive_is_ok(struct ata_channel *channel)
+{
+ if (channel->drive == ATA_SLAVE && !channel->has_slave) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+ata_set_status_ok(struct ata_channel *channel)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+
+ drive->status &= ~ATA_S_BUSY;
+ drive->status |= ATA_S_READY;
+ drive->status &= ~ATA_S_DWF;
+ drive->status &= ~ATA_S_DRQ;
+ drive->status &= ~ATA_S_ERROR;
+
+ drive->error = 0;
+
+ return;
+}
+
+static void
+ata_set_data_block_ready(struct ata_channel *channel)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+
+ drive->altstatus &= ~ATA_S_BUSY;
+ drive->altstatus |= ATA_S_DRQ;
+ drive->altstatus |= ATA_S_READY;
+
+ drive->status |= ATA_S_DRQ;
+
+ return;
+}
+
+static void
+ata_command_aborted(struct ata_channel *channel)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+
+ drive->status &= ~ATA_S_BUSY;
+ drive->status &= ~ATA_S_DRQ;
+ drive->status |= ATA_S_ERROR;
+
+ drive->error |= ATA_E_ABORT;
+
+ return;
+}
+
+static void
+ata_set_signature(struct ata_drive *drive)
+{
+ drive->seccount = 0x01;
+ drive->lba_low = 0x01;
+
+ if (drive->type == ATA_HD) {
+ drive->lba_mid = 0x00;
+ drive->lba_high = 0x00;
+ drive->hddevsel = 0x00;
+ }
+ else if (drive->type == ATAPI_CD) {
+ drive->lba_mid = ATAPI_MAGIC_LSB;
+ drive->lba_high = ATAPI_MAGIC_MSB;
+ drive->hddevsel &= 0x10;
+ }
+ else
+ assert(0);
+
+ return;
+}
+
+static void
+ata_addressing_sec_count(struct ata_channel *channel, uint16_t *p_sector_count)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+ uint16_t sector_count;
+
+ sector_count = drive->seccount ? drive->seccount : ATA_MAX_SEC_COUNT;
+ *p_sector_count = sector_count;
+
+ return;
+}
+
+static void
+ata_addressing_28_lba(struct ata_channel *channel, uint32_t *p_lba_address)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+ uint32_t lba_address = 0;
+
+ lba_address |= drive->lba_low;
+ lba_address |= drive->lba_mid << 8;
+ lba_address |= drive->lba_high << 16;
+ lba_address |= (drive->hddevsel & ATA_LBA_27_24_MASK) << 24;
+
+ *p_lba_address = lba_address;
+
+ return;
+}
+
+static void
+ata_init_block_request(struct blockif_req *breq, struct ata_channel *channel,
+ uint64_t offset, uint64_t nbytes, void *buffer)
+{
+ breq->br_param = channel;
+ breq->br_callback = ata_handle_block_request;
+ breq->br_offset = offset;
+ breq->br_iovcnt = 1;
+ breq->br_iov[0].iov_len = nbytes;
+ breq->br_iov[0].iov_base = buffer;
+
+ return;
+}
+
+static void
+ata_handle_block_request(struct blockif_req *req, int err)
+{
+ struct ata_channel *channel = (struct ata_channel *)req->br_param;
+
+ ata_set_status_ok(channel);
+ ata_set_data_block_ready(channel);
+ ata_irq_raise(channel);
+
+ return;
+}
+
+static void
+ata_pio_do_transfer(struct ata_pio_setup *pio_setup, uint64_t size_transfer,
+ ata_pio_end_transfer_func_t end_transfer, uint8_t use_word)
+{
+ assert(size_transfer <= ATA_INBUF_SIZE);
+ assert(!ata_pio_in_progress(pio_setup));
+
+ pio_setup->use_word = use_word;
+ pio_setup->pio_buffer.current_pos = 0;
+ pio_setup->size_transfer = size_transfer;
+ pio_setup->end_transfer = end_transfer;
+
+ return;
+}
+
+static int
+ata_pio_in_progress(struct ata_pio_setup *pio_setup)
+{
+ return (int)pio_setup->end_transfer;
+}
+
+static uint8_t *
+ata_pio_get_buffer_data(struct ata_pio_setup *pio_setup)
+{
+ return pio_setup->pio_buffer.data;
+}
+
+static void
+ata_pio_check_end_transfer(struct ata_pio_setup *pio_setup)
+{
+ struct ata_channel *channel = pio_setup->channel;
+ struct ata_pio_buffer *pio_buffer = &pio_setup->pio_buffer;
+ ata_pio_end_transfer_func_t end_transfer = pio_setup->end_transfer;
+
+ if (pio_buffer->current_pos == pio_setup->size_transfer) {
+ pio_setup->end_transfer = NULL;
+ end_transfer(channel);
+ }
+
+ return;
+}
+
+static uint32_t
+ata_pio_get_uint(struct ata_pio_setup *pio_setup)
+{
+ struct ata_pio_buffer *pio_buffer = &pio_setup->pio_buffer;
+ uint32_t value;
+
+ assert(pio_buffer->current_pos < pio_setup->size_transfer);
+
+ if (pio_setup->use_word) {
+ value = *(uint16_t *)(pio_buffer->data + pio_buffer->current_pos);
+ pio_buffer->current_pos += sizeof(uint16_t);
+ }
+ else {
+ value = *(uint32_t *)(pio_buffer->data + pio_buffer->current_pos);
+ pio_buffer->current_pos += sizeof(uint32_t);
+ }
+
+ ata_pio_check_end_transfer(pio_setup);
+
+ return value;
+}
+
+static void
+ata_pio_put_uint(struct ata_pio_setup *pio_setup, uint32_t write_value)
+{
+ struct ata_pio_buffer *pio_buffer = &pio_setup->pio_buffer;
+
+ assert(pio_buffer->current_pos < pio_setup->size_transfer);
+
+ if (pio_setup->use_word) {
+ *(uint16_t *)(pio_buffer->data + pio_buffer->current_pos) = (uint16_t)write_value;
+ pio_buffer->current_pos += sizeof(uint16_t);
+ }
+ else {
+ *(uint32_t *)(pio_buffer->data + pio_buffer->current_pos) = write_value;
+ pio_buffer->current_pos += sizeof(uint32_t);
+ }
+
+ ata_pio_check_end_transfer(pio_setup);
+
+ return;
+}
+
+static void
+ata_pio_generic_end_transfer(struct ata_channel *channel)
+{
+ dprint(LOG_DEBUG, "ata_pio_generic_end_transfer: ch: %d drive: %d\n",
+ channel->interface, channel->drive);
+
+ return;
+}
+
+static void
+ata_read_multiple_block_done(struct ata_channel *channel)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+ struct blockif_ctxt *bctxt = NULL;
+ struct ata_pio_setup *pio_setup = NULL;
+ struct blockif_req *breq = NULL;
+ uint64_t count;
+ uint64_t size_transfer;
+ int err;
+
+ bctxt = drive->bctxt;
+ pio_setup = &drive->pio_setup;
+ breq = &pio_setup->breq;
+
+ drive->offset += pio_setup->size_transfer;
+ drive->count -= pio_setup->size_transfer;
+
+ count = drive->count;
+
+ if (count) {
+ size_transfer = count >= ATA_BLOCK_SIZE ? ATA_BLOCK_SIZE : count;
+ ata_pio_do_transfer(pio_setup, size_transfer, ata_read_multiple_block_done, channel->use_16bit);
+
+ ata_init_block_request(breq, channel, drive->offset,
+ size_transfer, ata_pio_get_buffer_data(pio_setup));
+
+ err = blockif_read(bctxt, breq);
+ assert(!err);
+ }
+ else {
+ dprint(LOG_DEBUG, "ata_read_multiple_block_done: ATA_READ_MUL completed\n");
+ }
+
+ return;
+}
+
+static void
+ata_write_multiple_block_done(struct ata_channel *channel)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+ struct blockif_ctxt *bctxt = NULL;
+ struct ata_pio_setup *pio_setup = NULL;
+ struct blockif_req *breq = NULL;
+ uint64_t count;
+ uint64_t size_transfer;
+ int err;
+
+ bctxt = drive->bctxt;
+ pio_setup = &drive->pio_setup;
+ breq = &pio_setup->breq;
+
+ ata_init_block_request(breq, channel, drive->offset,
+ pio_setup->size_transfer, ata_pio_get_buffer_data(pio_setup));
+
+ drive->offset += pio_setup->size_transfer;
+ drive->count -= pio_setup->size_transfer;
+
+ count = drive->count;
+
+ if (count) {
+ size_transfer = count >= ATA_BLOCK_SIZE ? ATA_BLOCK_SIZE : count;
+ ata_pio_do_transfer(pio_setup, size_transfer, ata_write_multiple_block_done, channel->use_16bit);
+ }
+ else {
+ dprint(LOG_DEBUG, "ata_write_multiple_block_done: ATA_WRITE_MUL completed\n");
+ }
+
+ err = blockif_write(bctxt, breq);
+ assert(!err);
+
+ return;
+}
+
+static void
+ata_atapi_handle_read_done(struct ata_channel *channel)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+ struct blockif_ctxt *bctxt = NULL;
+ struct ata_pio_setup *pio_setup = NULL;
+ struct blockif_req *breq = NULL;
+ uint64_t count;
+ int err;
+
+ bctxt = drive->bctxt;
+ pio_setup = &drive->pio_setup;
+ breq = &pio_setup->breq;
+
+ drive->offset += ATAPI_BLOCK_SIZE;
+ drive->count -= ATAPI_BLOCK_SIZE;
+
+ count = drive->count;
+
+ if (count) {
+ assert(count >= ATAPI_BLOCK_SIZE);
+
+ drive->seccount = ATA_I_IN;
+
+ ata_pio_do_transfer(pio_setup, ATAPI_BLOCK_SIZE, ata_atapi_handle_read_done, channel->use_16bit);
+
+ ata_init_block_request(breq, channel, drive->offset,
+ ATAPI_BLOCK_SIZE, ata_pio_get_buffer_data(pio_setup));
+
+ err = blockif_read(bctxt, breq);
+ assert(!err);
+ }
+ else {
+ dprint(LOG_DEBUG, "ata_atapi_handle_read_done: READ completed\n");
+ ata_atapi_cmd_done(channel);
+ }
+
+ return;
+}
+
+static void
+ata_handle_identify(struct ata_channel *channel)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+
+ if (drive->type == ATA_HD) {
+ ata_initialize_ident(channel);
+
+ ata_set_data_block_ready(channel);
+ ata_set_status_ok(channel);
+ ata_irq_raise(channel);
+ }
+ else if (drive->type == ATAPI_CD) {
+ ata_command_aborted(channel);
+ ata_set_signature(drive);
+ }
+ else
+ assert(0);
+
+ return;
+}
+
+static void
+ata_handle_setfeatures(struct ata_channel *channel)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+
+ switch (drive->features) {
+ case ATA_SF_SETXFER:
+ channel->mode = drive->seccount;
+ dprint(LOG_DEBUG, "ata_handle_setfeatures ATA_SF_SETXFER: mode: %d\n",
+ channel->mode);
+ break;
+ case ATA_SF_ENAB_WCACHE:
+ break;
+ case ATA_SF_DIS_WCACHE:
+ break;
+ case ATA_SF_ENAB_PUIS:
+ break;
+ case ATA_SF_DIS_PUIS:
+ break;
+ case ATA_SF_PUIS_SPINUP:
+ break;
+ case ATA_SF_ENAB_RCACHE:
+ break;
+ case ATA_SF_DIS_RCACHE:
+ break;
+ case ATA_SF_ENAB_RELIRQ:
+ break;
+ case ATA_SF_DIS_RELIRQ:
+ break;
+ case ATA_SF_ENAB_SRVIRQ:
+ break;
+ case ATA_SF_DIS_SRVIRQ:
+ break;
+ default:
+ break;
+ }
+
+ ata_set_status_ok(channel);
+ ata_irq_raise(channel);
+
+ return;
+}
+
+static void
+ata_handle_set_multi(struct ata_channel *channel)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+
+ 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, §or_count);
+ ata_addressing_28_lba(channel, &lba_address);
+
+ dprint(LOG_DEBUG, "ata_handle_read_multiple: sector_count: %d, lba_address: 0x%x\n",
+ sector_count, lba_address);
+
+ offset = (uint64_t)lba_address * ATA_SECTOR_SIZE;
+ count = (uint64_t)sector_count * ATA_SECTOR_SIZE;
+
+ drive->count = count;
+ drive->offset = offset;
+
+ size_transfer = count >= ATA_BLOCK_SIZE ? ATA_BLOCK_SIZE : count;
+ ata_pio_do_transfer(pio_setup, size_transfer, ata_read_multiple_block_done, channel->use_16bit);
+
+ ata_init_block_request(breq, channel, drive->offset,
+ size_transfer, ata_pio_get_buffer_data(pio_setup));
+
+ err = blockif_read(bctxt, breq);
+ assert(!err);
+
+ return;
+}
+
+static void
+ata_handle_read_verify(struct ata_channel *channel)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+ dprint(LOG_DEBUG, "ata_handle_read_verify\n");
+
+ ata_set_status_ok(channel);
+ drive->altstatus &= ~ATA_S_DRQ;
+ ata_irq_raise(channel);
+
+ return;
+}
+
+static void
+ata_handle_write_multiple(struct ata_channel *channel)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+ struct blockif_ctxt *bctxt = NULL;
+ struct ata_pio_setup *pio_setup = NULL;
+
+ uint16_t sector_count;
+ uint32_t lba_address = 0;
+ uint64_t offset;
+ uint64_t count;
+ uint64_t size_transfer;
+
+ bctxt = drive->bctxt;
+ pio_setup = &drive->pio_setup;
+
+ ata_addressing_sec_count(channel, §or_count);
+ ata_addressing_28_lba(channel, &lba_address);
+
+ dprint(LOG_DEBUG, "ata_handle_write_multiple: sector_count: %d, lba_address: 0x%x\n",
+ sector_count, lba_address);
+
+ offset = (uint64_t)lba_address * ATA_SECTOR_SIZE;
+ count = (uint64_t)sector_count * ATA_SECTOR_SIZE;
+
+ drive->offset = offset;
+ drive->count = count;
+
+ size_transfer = count >= ATA_BLOCK_SIZE ? ATA_BLOCK_SIZE : count;
+ ata_pio_do_transfer(pio_setup, size_transfer, ata_write_multiple_block_done, channel->use_16bit);
+
+ return;
+}
+
+static void
+ata_handle_dma(struct ata_channel *channel, uint8_t op_dir)
+{
+ struct pci_ata_softc *sc = NULL;
+ struct pci_ata_dma_transaction *dma_transaction = NULL;
+
+ uint16_t sector_count;
+ uint32_t lba_address = 0;
+
+ sc = channel->pr_sc;
+ dma_transaction = &sc->dma_transactions[channel->interface];
+
+ assert(!dma_transaction->started);
+
+ ata_addressing_sec_count(channel, §or_count);
+ ata_addressing_28_lba(channel, &lba_address);
+
+ dprint(LOG_DEBUG, "ata_handle_dma: sector_count: %d, lba_address: 0x%x DIR: %d\n",
+ sector_count, lba_address, op_dir);
+
+ dma_transaction->started = 1;
+ dma_transaction->offset = (uint64_t)lba_address * ATA_SECTOR_SIZE;
+ dma_transaction->nbytes = (uint64_t)sector_count * ATA_SECTOR_SIZE;
+ dma_transaction->op_dir = op_dir;
+
+ return;
+}
+
+static void
+ata_handle_flushcache(struct ata_channel *channel)
+{
+ struct blockif_ctxt *bctxt = NULL;
+ struct blockif_req *breq = NULL;
+ int err;
+
+ bctxt = channel->drives[channel->drive].bctxt;
+ breq = &channel->flush_breq;
+
+ dprint(LOG_DEBUG, "ata_handle_flushcache\n");
+
+ ata_init_block_request(breq, channel, 0, 0, NULL);
+
+ err = blockif_flush(bctxt, breq);
+ assert(!err);
+
+ return;
+}
+
+static void
+ata_handle_seek(struct ata_channel *channel)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+
+ dprint(LOG_DEBUG, "ata_handle_seek\n");
+
+ ata_set_status_ok(channel);
+ drive->status |= ATA_S_DSC;
+ ata_irq_raise(channel);
+
+ return;
+}
+
+static void
+ata_handle_atapi_identify(struct ata_channel *channel)
+{
+ ata_atapi_initialize_ident(channel);
+
+ ata_set_data_block_ready(channel);
+ ata_set_status_ok(channel);
+ ata_irq_raise(channel);
+
+ return;
+}
+
+static void
+ata_handle_packet_cmd(struct ata_channel *channel)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+ struct ata_pio_setup *pio_setup = NULL;
+
+ pio_setup = &drive->pio_setup;
+
+ drive->byte_count = drive->lba_mid | (drive->lba_high << 8);
+
+ dprint(LOG_DEBUG, "ata_handle_packet_cmd: byte_count: %d\n", drive->byte_count);
+
+ ata_set_status_ok(channel);
+ drive->seccount = ATA_I_CMD;
+ drive->status |= ATA_S_DRQ;
+
+ /* The Packet cmd is received as 6 words on both LPC and PCI */
+ ata_pio_do_transfer(pio_setup, ATAPI_PACKET_SIZE, ata_atapi_cmd, 1);
+
+ return;
+}
+
+static void
+ata_handle_cmd(struct ata_channel *channel, uint8_t cmd)
+{
+ switch (cmd) {
+ case ATA_ATA_IDENTIFY:
+ ata_handle_identify(channel);
+ break;
+ case ATA_SETFEATURES:
+ ata_handle_setfeatures(channel);
+ break;
+ case ATA_SET_MULTI:
+ ata_handle_set_multi(channel);
+ break;
+ case ATA_READ:
+ case ATA_READ_MUL:
+ ata_handle_read_multiple(channel);
+ break;
+ case ATA_READ_VERIFY:
+ ata_handle_read_verify(channel);
+ break;
+ case ATA_WRITE:
+ case ATA_WRITE_MUL:
+ ata_handle_write_multiple(channel);
+ break;
+ case ATA_READ_DMA:
+ ata_handle_dma(channel, ATA_DMA_READ);
+ break;
+ case ATA_WRITE_DMA:
+ ata_handle_dma(channel, ATA_DMA_WRITE);
+ break;
+ case ATA_FLUSHCACHE:
+ ata_handle_flushcache(channel);
+ break;
+ case ATA_SEEK:
+ ata_handle_seek(channel);
+ break;
+ case ATA_ATAPI_IDENTIFY:
+ ata_handle_atapi_identify(channel);
+ break;
+ case ATA_PACKET_CMD:
+ ata_handle_packet_cmd(channel);
+ break;
+ default:
+ dprint(LOG_ERR, "ata_handle_cmd: unsupported cmd: %02x\n", cmd);
+ assert(0);
+ break;
+ }
+
+ return;
+}
+
+/*
+ * ata_cpu_to_be32 translates from little endian to big endian
+ * the little endian format is used by the cpu
+ * @val - the integer value being translated
+ * @buf - the address where the result is stored
+ */
+static inline void
+ata_cpu_to_be32(uint8_t *buf, uint32_t val)
+{
+ buf[0] = val >> 24;
+ buf[1] = val >> 16;
+ buf[2] = val >> 8;
+ buf[3] = val;
+
+ return;
+}
+
+/*
+ * ata_cpu_to_be16 translates from little endian to big endian
+ * the little endian format is used by the cpu
+ * @val - the word being translated
+ * @buf - the address where the result is stored
+ */
+static inline void
+ata_cpu_to_be16(uint8_t *buf, uint16_t val)
+{
+ buf[0] = val >> 8;
+ buf[1] = val;
+
+ return;
+}
+
+/*
+ * ata_be32_to_cpu translates from big endian to little endian
+ * the little endian format is used by the cpu
+ * @val - the return integer value
+ * @buf - the address of the value being translated
+ */
+static inline uint32_t
+ata_be32_to_cpu(uint8_t *buf)
+{
+ uint32_t val = 0;
+
+ val |= buf[0] << 24;
+ val |= buf[1] << 16;
+ val |= buf[2] << 8;
+ val |= buf[3];
+
+ return val;
+}
+
+/*
+ * ata_be16_to_cpu translates from big endian to little endian
+ * the little endian format is used by the cpu
+ * @val - the return word value
+ * @buf - the address of the value being translated
+ */
+static inline uint16_t
+ata_be16_to_cpu(uint8_t *buf)
+{
+ uint16_t val = 0;
+
+ val |= buf[0] << 8;
+ val |= buf[1];
+
+ return val;
+}
+
+static void
+ata_atapi_print_packet(uint8_t *packet)
+{
+ int i;
+
+ dprint(LOG_DEBUG, "ata_atapi_print_packet cmd: 0x%x packet: ", packet[0]);
+ for (i = 0; i < ATAPI_PACKET_SIZE; i++) {
+ dprint(LOG_DEBUG, "%x ", packet[i]);
+ }
+ dprint(LOG_DEBUG, "\n");
+
+ return;
+}
+
+static void
+ata_atapi_data_request(struct ata_channel *channel)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+
+ ata_set_status_ok(channel);
+ drive->seccount = ATA_I_IN;
+ drive->status |= ATA_S_DRQ;
+
+ ata_irq_raise(channel);
+
+ return;
+}
+
+static void
+ata_atapi_cmd_done(struct ata_channel *channel)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+
+ ata_set_status_ok(channel);
+ drive->seccount = ATA_I_IN | ATA_I_CMD;
+
+ ata_irq_raise(channel);
+
+ return;
+}
+
+static void
+ata_atapi_handle_inquiry(struct ata_channel *channel, uint8_t *packet)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+ struct ata_pio_setup *pio_setup = NULL;
+ struct scsi_inquiry_data *inquiry_data = (struct scsi_inquiry_data *)packet;
+ uint8_t max_len = 0;
+
+ pio_setup = &drive->pio_setup;
+
+ max_len = packet[4];
+ assert(max_len >= SHORT_INQUIRY_LENGTH);
+
+ /* for now we implement only the short inquiry cmd */
+ if (packet[1] & 0x01) {
+ assert(0);
+ } else {
+ memset(inquiry_data, 0, SHORT_INQUIRY_LENGTH);
+
+ inquiry_data->device = T_CDROM;
+ inquiry_data->dev_qual2 = SID_RMB;
+ inquiry_data->version = SCSI_REV_0;
+ inquiry_data->response_format = 2;
+ inquiry_data->additional_length = SHORT_INQUIRY_LENGTH - 5;
+
+ memcpy(inquiry_data->vendor, "BHYVE ", SID_VENDOR_SIZE);
+ memcpy(inquiry_data->product, "ATAPI IDE CDROM ", SID_PRODUCT_SIZE);
+ memcpy(inquiry_data->revision, "1.1 ", SID_REVISION_SIZE);
+
+ ata_pio_do_transfer(pio_setup, SHORT_INQUIRY_LENGTH, ata_atapi_cmd_done, channel->use_16bit);
+ ata_atapi_data_request(channel);
+ }
+
+ return;
+}
+
+static void
+ata_atapi_handle_read_capacity(struct ata_channel *channel, uint8_t *packet)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+ struct ata_pio_setup *pio_setup = NULL;
+ int nsectors = blockif_size(drive->bctxt) / ATAPI_BLOCK_SIZE;
+
+ pio_setup = &drive->pio_setup;
+
+ ata_cpu_to_be32(packet, nsectors - 1);
+ ata_cpu_to_be32(packet + 4, ATAPI_BLOCK_SIZE);
+
+ ata_pio_do_transfer(pio_setup, 8, ata_atapi_cmd_done, channel->use_16bit);
+ ata_atapi_data_request(channel);
+
+ return;
+}
+
+static void lba_to_msf(uint8_t *buf, int lba)
+{
+ lba += 150;
+ buf[0] = (lba / 75) / 60;
+ buf[1] = (lba / 75) % 60;
+ buf[2] = lba % 75;
+
+ return;
+}
+
+static void
+ata_atapi_handle_read_toc(struct ata_channel *channel, uint8_t *packet)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+ struct ata_pio_setup *pio_setup = NULL;
+ int nsectors = blockif_size(drive->bctxt) / ATAPI_BLOCK_SIZE;
+
+ uint8_t msf = packet[1] & 2;
+ uint8_t format = packet[2] & 0x0f;
+ uint8_t track_number = packet[6];
+ uint16_t allocation_length = (packet[7] << 8) | packet[8];
+
+ uint16_t data_len = 0;
+ uint8_t *data_zone = NULL;
+ uint8_t *lead_out = NULL;
+
+ pio_setup = &drive->pio_setup;
+
+ assert(track_number <= 1 || track_number == 0xaa);
+ assert(!format);
+
+ packet[2] = 1; /* First Track */
+ packet[3] = 1; /* Last Track */
+
+ lead_out = packet + 4;
+
+ if (track_number <= 1) {
+ data_zone = packet + 4;
+ lead_out = packet + 12;
+
+ data_zone[0] = 0; /* Reserved */
+ data_zone[1] = 0x14; /* ADR, control */
+ data_zone[2] = 1; /* track number */
+ data_zone[3] = 0; /* Reserved */
+ if (msf) {
+ data_zone[4] = 0;
+ lba_to_msf(data_zone + 5, 0);
+ }
+ else {
+ ata_cpu_to_be32(data_zone + 4, 0);
+ }
+ }
+
+ lead_out[0] = 0; /* Reserved */
+ lead_out[1] = 0x16; /* ADR, control */
+ lead_out[2] = 0xaa; /* track number */
+ lead_out[3] = 0; /* Reserved */
+ if (msf) {
+ lead_out[4] = 0;
+ lba_to_msf(lead_out + 5, nsectors);
+ }
+ else {
+ ata_cpu_to_be32(lead_out + 4, nsectors);
+ }
+
+ data_len = (lead_out - packet) + 8;
+ ata_cpu_to_be16(packet, data_len - 2);
+
+ data_len = (data_len < allocation_length) ? data_len : allocation_length;
+
+ ata_pio_do_transfer(pio_setup, data_len, ata_atapi_cmd_done, channel->use_16bit);
+ ata_atapi_data_request(channel);
+
+ return;
+}
+
+static void
+ata_atapi_handle_read(struct ata_channel *channel, uint8_t *packet)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+ struct blockif_ctxt *bctxt = NULL;
+ struct ata_pio_setup *pio_setup = NULL;
+ struct blockif_req *breq = NULL;
+
+ uint16_t sector_count = 0;
+ uint32_t lba_address = 0;
+ uint64_t offset;
+ uint64_t count;
+ int err;
+
+ assert(drive->byte_count >= ATAPI_BLOCK_SIZE);
+
+ bctxt = drive->bctxt;
+ pio_setup = &drive->pio_setup;
+ breq = &pio_setup->breq;
+
+ lba_address = ata_be32_to_cpu(packet + 2);
+ sector_count = ata_be16_to_cpu(packet + 7);
+
+ dprint(LOG_DEBUG, "ata_atapi_handle_read: sector_count: %d, lba_address: 0x%x\n",
+ sector_count, lba_address);
+
+ offset = (uint64_t)lba_address * ATAPI_BLOCK_SIZE;
+ count = (uint64_t)sector_count * ATAPI_BLOCK_SIZE;
+
+ drive->count = count;
+ drive->offset = offset;
+
+ drive->lba_mid = ATAPI_BLOCK_SIZE & 0xff;
+ drive->lba_high = (ATAPI_BLOCK_SIZE >> 8) & 0xff;
+
+ drive->seccount = ATA_I_IN;
+
+ ata_pio_do_transfer(pio_setup, ATAPI_BLOCK_SIZE, ata_atapi_handle_read_done, channel->use_16bit);
+
+ ata_init_block_request(breq, channel, drive->offset,
+ ATAPI_BLOCK_SIZE, ata_pio_get_buffer_data(pio_setup));
+
+ err = blockif_read(bctxt, breq);
+ assert(!err);
+
+ return;
+}
+
+static void
+ata_atapi_cmd(struct ata_channel *channel)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+ struct ata_pio_setup *pio_setup = NULL;
+ uint8_t *packet = NULL;
+ uint8_t op_code = 0;
+
+ pio_setup = &drive->pio_setup;
+ packet = ata_pio_get_buffer_data(pio_setup);
+
+ ata_atapi_print_packet(packet);
+
+ op_code = packet[0];
+
+ switch (op_code) {
+ case INQUIRY:
+ ata_atapi_handle_inquiry(channel, packet);
+ break;
+ case READ_CAPACITY:
+ ata_atapi_handle_read_capacity(channel, packet);
+ break;
+ case PREVENT_ALLOW:
+ ata_atapi_cmd_done(channel);
+ break;
+ case READ_TOC:
+ ata_atapi_handle_read_toc(channel, packet);
+ break;
+ case READ_10:
+ ata_atapi_handle_read(channel, packet);
+ break;
+ case TEST_UNIT_READY:
+ ata_atapi_cmd_done(channel);
+ break;
+ default:
+ dprint(LOG_ERR, "ata_atapi_cmd: unsupported cmd: %02x\n", op_code);
+ assert(0);
+ break;
+ };
+
+ return;
+}
+
+static uint64_t
+ata_command_block_read(struct ata_channel *channel, uint64_t offset)
+{
+ uint64_t value = 0;
+ struct ata_drive *drive = &channel->drives[channel->drive];
+ struct ata_pio_setup *pio_setup = NULL;
+
+ pio_setup = &drive->pio_setup;
+
+ switch (offset) {
+ case ATA_DATA_REG:
+ value = ata_pio_get_uint(pio_setup);
+ break;
+ case ATA_ERROR_REG:
+ value = drive->error;
+ dprint(LOG_DEBUG, "ata_command_block_read: ATA_ERROR_REG: %lxh\n", value);
+ break;
+ case ATA_SECCOUNT_REG:
+ value = drive->seccount;
+ dprint(LOG_DEBUG, "ata_command_block_read: ATA_SECCOUNT_REG: %lxh\n", value);
+ break;
+ case ATA_LBA_LOW_REG:
+ value = drive->lba_low;
+ dprint(LOG_DEBUG, "ata_command_block_read: ATA_LBA_LOW_REG: %lxh\n", value);
+ break;
+ case ATA_LBA_MID_REG:
+ value = drive->lba_mid;
+ dprint(LOG_DEBUG, "ata_command_block_read: ATA_LBA_MID_REG: %lxh\n", value);
+ break;
+ case ATA_LBA_HIGH_REG:
+ value = drive->lba_high;
+ dprint(LOG_DEBUG, "ata_command_block_read: ATA_LBA_HIGH_REG: %lxh\n", value);
+ break;
+ case ATA_HDDEVSEL_REG:
+ value = drive->hddevsel;
+ dprint(LOG_DEBUG, "ata_command_block_read: ATA_HDDEVSEL_REG: %lxh\n", value);
+ break;
+ case ATA_STATUS_REG:
+ ata_irq_lower(channel);
+
+ value = drive->status;
+ dprint(LOG_DEBUG, "ata_command_block_read: ATA_STATUS_REG: %lxh\n", value);
+ break;
+ default:
+ value = 0;
+ break;
+ }
+
+ return value;
+}
+
+static void
+ata_command_block_write(struct ata_channel *channel,
+ uint64_t offset, int size, uint64_t value)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+ struct ata_drive *drive0 = &channel->drives[ATA_MASTER];
+ struct ata_drive *drive1 = &channel->drives[ATA_SLAVE];
+ struct ata_pio_setup *pio_setup = NULL;
+
+ uint8_t reg_value;
+
+ pio_setup = &drive->pio_setup;
+
+ switch (offset) {
+ case ATA_DATA_REG:
+ ata_pio_put_uint(pio_setup, value);
+ break;
+ case ATA_FEATURES_REG:
+ drive0->features = value;
+ drive1->features = value;
+ dprint(LOG_DEBUG, "ata_command_block_write: ATA_FEATURES_REG: %xh\n", (uint8_t)value);
+ break;
+ case ATA_SECCOUNT_REG:
+ drive0->seccount = value;
+ drive1->seccount = value;
+ dprint(LOG_DEBUG, "ata_command_block_write: ATA_SECCOUNT_REG: %xh\n", (uint8_t)value);
+ break;
+ case ATA_LBA_LOW_REG:
+ drive0->lba_low = value;
+ drive1->lba_low = value;
+ dprint(LOG_DEBUG, "ata_command_block_write: ATA_LBA_LOW_REG: %xh\n", (uint8_t)value);
+ break;
+ case ATA_LBA_MID_REG:
+ drive0->lba_mid = value;
+ drive1->lba_mid = value;
+ dprint(LOG_DEBUG, "ata_command_block_write: ATA_LBA_MID_REG: %xh\n", (uint8_t)value);
+ break;
+ case ATA_LBA_HIGH_REG:
+ drive0->lba_high = value;
+ drive1->lba_high = value;
+ dprint(LOG_DEBUG, "ata_command_block_write: ATA_LBA_HIGH_REG: %xh\n", (uint8_t)value);
+ break;
+ case ATA_HDDEVSEL_REG:
+ reg_value = value;
+
+ drive0->hddevsel = value;
+ drive1->hddevsel = value;
+ dprint(LOG_DEBUG, "ata_command_block_write: ATA_HDDEVSEL_REG: %xh\n", reg_value);
+
+ if (reg_value & ATA_D_IBM)
+ channel->size = ATA_SECTOR_SIZE;
+ if (reg_value & ATA_DEV(ATA_SLAVE)) {
+ dprint(LOG_DEBUG, "ata_command_block_write: select ATA_SLAVE\n");
+ channel->drive = ATA_SLAVE;
+ }
+ else {
+ dprint(LOG_DEBUG, "ata_command_block_write: select ATA_MASTER\n");
+ channel->drive = ATA_MASTER;
+ }
+ if (!ata_select_drive_is_ok(channel)) {
+ drive1->status = ATA_DRIVE_ABSENT;
+ }
+ break;
+ case ATA_COMMAND_REG:
+ drive0->command = value;
+ drive1->command = value;
+ dprint(LOG_DEBUG, "ata_command_block_write: ATA_COMMAND_REG: %xh\n", (uint8_t)value);
+ ata_handle_cmd(channel, value);
+ break;
+ default:
+ dprint(LOG_ERR, "ata_command_block_write: unsupported reg:%lx\n", offset);
+ assert(0);
+ break;
+ }
+
+ return;
+}
+
+static uint64_t
+ata_altstatus_read(struct ata_channel *channel)
+{
+ struct ata_drive *drive = &channel->drives[channel->drive];
+ uint64_t value = 0;
+
+ value = drive->altstatus;
+
+ dprint(LOG_DEBUG, "ata_altstatus_read: value: %lu\n", value);
+
+ return value;
+}
+
+static void
+ata_control_write(struct ata_channel *channel,
+ int size, uint64_t value)
+{
+ uint8_t reg_value;
+ struct ata_drive *drive = &channel->drives[channel->drive];
+
+ reg_value = (uint8_t)value;
+
+ dprint(LOG_DEBUG, "ata_control_write: irq_disabled: %d\n", reg_value & ATA_A_IDS);
+
+ if (reg_value & ATA_A_IDS)
+ drive->irq_disabled = 1;
+ else
+ drive->irq_disabled = 0;
+
+ if (reg_value & ATA_A_RESET)
+ ata_srst(channel);
+
+ return;
+}
+
+/*
+ * PCI ATA function definitions
+ */
+static void
+pci_ata_intr_assert(void *arg)
+{
+ struct pci_ata_softc *sc = (struct pci_ata_softc *)arg;
+ struct pci_devinst *pi = sc->asc_pi;
+
+ pci_lintr_assert(pi);
+
+ return;
+}
+
+static void
+pci_ata_intr_deassert(void *arg)
+{
+ struct pci_ata_softc *sc = (struct pci_ata_softc *)arg;
+ struct pci_devinst *pi = sc->asc_pi;
+
+ pci_lintr_deassert(pi);
+
+ return;
+}
+
+static int
+pci_ata_parse_params(const char *opts, char *opt0, char *opt1, uint8_t *nr_channels)
+{
+ uint8_t len;
+ char tmp_opts[128];
+ char *ch0 = NULL, *ch1 = NULL;
+ uint8_t i;
+
+ if (!opts) {
+ dprint(LOG_ERR, "pci_ata_parse_params: opts should be like: %s\n",
+ "X,MASTER_X[,SLAVE_X][;Y,MASTER_Y[,SLAVE_Y]]");
+ return -1;
+ }
+
+ len = strlen(opts);
+ if (len >= 128) {
+ dprint(LOG_ERR, "pci_ata_parse_params: PCI ATA string params too big\n");
+ return -1;
+ }
+
+ strcpy(tmp_opts, opts);
+
+ ch0 = tmp_opts;
+
+ for (i = 0; i < len; i++) {
+ if (tmp_opts[i] == PCI_ATA_CH_SEP) {
+ if (ch1) {
+ dprint(LOG_ERR, "pci_ata_parse_params: to many channels\n");
+ return -1;
+ }
+ tmp_opts[i] = '\0';
+ ch1 = tmp_opts + i + 1;
+ }
+ }
+
+ if (strlen(ch0) >= 64) {
+ dprint(LOG_ERR, "pci_ata_parse_params: the name of ATA_X channel too big\n");
+ return -1;
+ }
+ strcpy(opt0, ch0);
+ *nr_channels = 1;
+
+ if (ch1 != NULL && strlen(ch1) < 64) {
+ strcpy(opt1, ch1);
+ *nr_channels = 2;
+ }
+
+ return 0;
+}
+
+static int
+pci_ata_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts, int atapi)
+{
+ struct ata_channel *channel = NULL;
+ struct pci_ata_softc *pci_ata_sc = NULL;
+ char ch_opts[2][64];
+ uint8_t nr_channels = 0;
+ int ret, i;
+
+ ata_open_dbg_file();
+
+ ret = pci_ata_parse_params(opts, ch_opts[0], ch_opts[1], &nr_channels);
+ if (ret) {
+ dprint(LOG_ERR, "pci_ata_init: some errors with the input checking\n");
+ return -1;
+ }
+
+ pci_ata_sc = calloc(1, sizeof(struct pci_ata_softc));
+ if (!pci_ata_sc)
+ return -1;
+
+ pci_ata_sc->channels[ATA_X] = NULL;
+ pci_ata_sc->channels[ATA_Y] = NULL;
+
+ for (i = 0; i < nr_channels; i++) {
+ channel = ata_init(ctx, pci_ata_intr_assert, pci_ata_intr_deassert,
+ pci_ata_sc, ch_opts[i], 0);
+ if (!channel)
+ continue;
+ channel->use_16bit = 0;
+ assert(!pci_ata_sc->channels[channel->interface]);
+ pci_ata_sc->channels[channel->interface] = channel;
+ }
+
+ /* exit if none of the channels are initialized */
+ if (!pci_ata_sc->channels[ATA_X] && !pci_ata_sc->channels[ATA_Y]) {
+ free(pci_ata_sc);
+ return -1;
+ }
+
+ pi->pi_arg = pci_ata_sc;
+ pci_ata_sc->asc_pi = pi;
+
+ /* generic PCI ATA/ATAPI device ATA_IT8211F */
+ pci_set_cfgdata16(pi, PCIR_DEVICE, 0x8211); /* ATA/ATAPI Controller */
+ pci_set_cfgdata16(pi, PCIR_VENDOR, 0x1283); /* Waldo */
+
+ /* this is a storage class device */
+ pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE);
+ /* this is an IDE/ATA type device */
+ pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_STORAGE_IDE);
+
+ pci_set_cfgdata8(pi, PCIR_PROGIF, PCIP_STORAGE_IDE_MASTERDEV |
+ PCIP_STORAGE_IDE_MODEPRIM | PCIP_STORAGE_IDE_MODESEC);
+
+ pci_emul_alloc_bar(pi, PCI_BAR0, PCIBAR_IO, ATA_IOSIZE);
+ pci_emul_alloc_bar(pi, PCI_BAR1, PCIBAR_IO, ATA_CTLIOSIZE);
+ pci_emul_alloc_bar(pi, PCI_BAR2, PCIBAR_IO, ATA_IOSIZE);
+ pci_emul_alloc_bar(pi, PCI_BAR3, PCIBAR_IO, ATA_CTLIOSIZE);
+ pci_emul_alloc_bar(pi, PCI_BAR4, PCIBAR_IO, ATA_CHANNELS * ATA_BMIOSIZE);
+
+ pci_lintr_request(pi);
+
+ return 0;
+}
+
+static int
+pci_ata_hd_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+{
+ return (pci_ata_init(ctx, pi, opts, 0));
+}
+
+static void
+pci_ata_dma_start(struct pci_ata_softc *sc, uint8_t ch)
+{
+ struct vmctx *ctx = NULL;
+ struct ata_channel *channel = NULL;
+ struct blockif_ctxt *bctxt = NULL;
+ struct pci_ata_dma_transaction *dma_transaction = NULL;
+ struct blockif_req *breq = NULL;
+
+ struct prd_entry *prdt = NULL;
+ struct prd_entry *entry = NULL;
+ uint32_t prdt_address;
+ uint32_t index;
+ uint32_t byte_count;
+ void *entry_buff = NULL;
+ int err;
+
+ channel = sc->channels[ch];
+
+ ctx = channel->vm_ctx;
+ assert(ctx != NULL);
+
+ bctxt = channel->drives[channel->drive].bctxt;
+ dma_transaction = &sc->dma_transactions[ch];
+
+ assert(dma_transaction->started);
+
+ assert(((sc->cmd[ch] & ATA_BMCMD_WRITE_READ) && dma_transaction->op_dir == ATA_DMA_READ) ||
+ (!(sc->cmd[ch] & ATA_BMCMD_WRITE_READ) && dma_transaction->op_dir == ATA_DMA_WRITE));
+
+ sc->stat[ch] |= ATA_BMSTAT_ACTIVE;
+
+ /* prdt_address is the physical address of the prdt in the guest memory */
+ prdt_address = sc->prdt[ch];
+
+ /* map the guest physical address into the host virtual address */
+ prdt = paddr_guest2host(ctx, prdt_address, ATA_DMA_PRDT_SIZE);
+ assert(prdt != NULL);
+
+ breq = &dma_transaction->breq;
+ breq->br_param = channel;
+ breq->br_callback = ata_handle_block_request;
+ breq->br_offset = dma_transaction->offset;
+
+ dma_transaction->is_eot = 0;
+ index = 0;
+ while (!dma_transaction->is_eot && dma_transaction->nbytes > 0) {
+ entry = prdt + index;
+
+ entry_buff = paddr_guest2host(ctx, entry->prd_address, entry->byte_count);
+ byte_count = entry->byte_count ? entry->byte_count : ATA_DMA_MAX_PRD_COUNT;
+
+ if (byte_count > dma_transaction->nbytes) {
+ dprint(LOG_ERR, "pci_ata_dma_start: the PRD entry contains %s",
+ "more bytes than the ATA transfer size\n");
+ byte_count = dma_transaction->nbytes;
+ }
+
+ if (index == BLOCKIF_IOV_MAX) {
+ dprint(LOG_ERR, "pci_ata_dma_start: the PRD Table contains %s",
+ "more than BLOCKIF_IOV_MAX entries\n");
+ break;
+ }
+
+ breq->br_iov[index].iov_len = byte_count;
+ breq->br_iov[index].iov_base = entry_buff;
+
+ dma_transaction->nbytes -= byte_count;
+
+ if (entry->vendor_specific & ATA_DMA_PRDT_EOT)
+ dma_transaction->is_eot = 1;
+
+ index++;
+ }
+
+ breq->br_iovcnt = index;
+
+ sc->stat[ch] |= ATA_BMSTAT_INTERRUPT;
+
+ /*
+ * Use asynchronous operations for read / write on the block;
+ * update the status registers and notify the guest operating
+ * system using an interrupt in the ata_handle_block_request callback
+ */
+ if (dma_transaction->op_dir == ATA_DMA_READ) {
+ err = blockif_read(bctxt, breq);
+ }
+ else if (dma_transaction->op_dir == ATA_DMA_WRITE) {
+ err = blockif_write(bctxt, breq);
+ }
+ else {
+ assert(0);
+ }
+
+ assert(!err);
+
+ return;
+}
+
+static void
+pci_ata_dma_stop(struct pci_ata_softc *sc, uint8_t ch)
+{
+ struct ata_channel *channel = NULL;
+ struct pci_ata_dma_transaction *dma_transaction = NULL;
+
+ channel = sc->channels[ch];
+ dma_transaction = &sc->dma_transactions[ch];
+
+ if(!dma_transaction->started) {
+ return;
+ }
+
+ if (dma_transaction->is_eot && dma_transaction->nbytes == 0) {
+ sc->stat[ch] |= ATA_BMSTAT_INTERRUPT;
+ sc->stat[ch] &= ~ATA_BMSTAT_ERROR;
+ sc->stat[ch] &= ~ATA_BMSTAT_ACTIVE;
+ }
+ else if (!dma_transaction->is_eot && dma_transaction->nbytes == 0) {
+ sc->stat[ch] |= ATA_BMSTAT_INTERRUPT;
+ sc->stat[ch] &= ~ATA_BMSTAT_ERROR;
+ sc->stat[ch] |= ATA_BMSTAT_ACTIVE;
+ dprint(LOG_ERR, "pci_ata_dma_stop: DMA PRDT transfer was %s",
+ "larger than the ATA transfer size\n");
+ }
+ else if (dma_transaction->is_eot && dma_transaction->nbytes > 0) {
+ sc->stat[ch] &= ~ATA_BMSTAT_INTERRUPT;
+ sc->stat[ch] &= ~ATA_BMSTAT_ERROR;
+ sc->stat[ch] &= ~ATA_BMSTAT_ACTIVE;
+ dprint(LOG_ERR, "pci_ata_dma_stop: DMA PRDT transfer was %s",
+ "smaller than the ATA transfer size\n");
+ }
+ else if (!dma_transaction->is_eot && dma_transaction->nbytes > 0) {
+ sc->stat[ch] |= ATA_BMSTAT_ERROR;
+ dprint(LOG_ERR, "pci_ata_dma_stop: the PRD Table contains %s",
+ "more than BLOCKIF_IOV_MAX entries\n");
+ }
+
+ dma_transaction->started = 0;
+
+ return;
+}
+
+static uint64_t
+pci_ata_bus_master_read(struct pci_ata_softc *sc, uint64_t offset)
+{
+ uint64_t value = 0;
+
+ switch (offset) {
+ case ATA_BMCMD_X_REG:
+ value = sc->cmd[ATA_X];
+ dprint(LOG_DEBUG, "pci_ata_bus_master_read: ATA_BMCMD_X_REG: %lu\n", value);
+ break;
+ case ATA_BMSTAT_X_REG:
+ value = sc->stat[ATA_X];
+ dprint(LOG_DEBUG, "pci_ata_bus_master_read: ATA_BMSTAT_X_REG: %lu\n", value);
+ break;
+ case ATA_BMPRDT_X_REG:
+ value = sc->prdt[ATA_X];
+ dprint(LOG_DEBUG, "pci_ata_bus_master_read: ATA_BMPRDT_X_REG: %lu\n", value);
+ break;
+ case ATA_BMCMD_Y_REG:
+ value = sc->cmd[ATA_Y];
+ dprint(LOG_DEBUG, "pci_ata_bus_master_read: ATA_BMCMD_Y_REG: %lu\n", value);
+ break;
+ case ATA_BMSTAT_Y_REG:
+ value = sc->stat[ATA_Y];
+ dprint(LOG_DEBUG, "pci_ata_bus_master_read: ATA_BMSTAT_Y_REG: %lu\n", value);
+ break;
+ case ATA_BMPRDT_Y_REG:
+ value = sc->prdt[ATA_Y];
+ dprint(LOG_DEBUG, "pci_ata_bus_master_read: ATA_BMPRDT_Y_REG: %lu\n", value);
+ break;
+ default:
+ value = 0;
+ break;
+ }
+
+ return value;
+}
+
+static void
+pci_ata_bus_master_write(struct pci_ata_softc *sc,
+ uint64_t offset, int size, uint64_t value)
+{
+
+ uint8_t reg_value;
+
+ if (offset != ATA_BMPRDT_X_REG && offset != ATA_BMPRDT_Y_REG && size != 1)
+ return;
+ if ((offset == ATA_BMPRDT_X_REG || offset == ATA_BMPRDT_Y_REG) && size != 4)
+ return;
+
+ switch (offset) {
+ case ATA_BMCMD_X_REG:
+ reg_value = sc->cmd[ATA_X] = (uint8_t)value;
+
+ dprint(LOG_DEBUG, "pci_ata_bus_master_write[X]: ATA_BMCMD_START_STOP: %d\n",
+ reg_value & ATA_BMCMD_START_STOP);
+ if (reg_value & ATA_BMCMD_START_STOP) {
+ pci_ata_dma_start(sc, ATA_X);
+ }
+ else {
+ pci_ata_dma_stop(sc, ATA_X);
+ }
+
+ dprint(LOG_DEBUG, "pci_ata_bus_master_write[X]: ATA_BMCMD_WRITE_READ: %d\n",
+ reg_value & ATA_BMCMD_WRITE_READ);
+ break;
+ case ATA_BMSTAT_X_REG:
+ reg_value = sc->stat[ATA_X] = (uint8_t)value;
+ dprint(LOG_DEBUG, "pci_ata_bus_master_write[X]: ATA_BMSTAT_INTERRUPT: %d, ATA_BMSTAT_ERROR: %d\n",
+ reg_value & ATA_BMSTAT_INTERRUPT, reg_value & ATA_BMSTAT_ERROR);
+ if (reg_value & ATA_BMSTAT_INTERRUPT)
+ sc->stat[ATA_X] &= ~ATA_BMSTAT_INTERRUPT;
+ if (reg_value & ATA_BMSTAT_ERROR)
+ sc->stat[ATA_X] &= ~ATA_BMSTAT_ERROR;
+ break;
+ case ATA_BMPRDT_X_REG:
+ dprint(LOG_DEBUG, "pci_ata_bus_master_write[X]: ATA_BMPRDT_X_REG: 0x%x\n", (uint32_t)value);
+ sc->prdt[ATA_X] = value;
+ break;
+ case ATA_BMCMD_Y_REG:
+ reg_value = sc->cmd[ATA_Y] = (uint8_t)value;
+
+ dprint(LOG_DEBUG, "pci_ata_bus_master_write[Y]: ATA_BMCMD_START_STOP: %d\n",
+ reg_value & ATA_BMCMD_START_STOP);
+ if (reg_value & ATA_BMCMD_START_STOP) {
+ pci_ata_dma_start(sc, ATA_Y);
+ }
+ else {
+ pci_ata_dma_stop(sc, ATA_Y);
+ }
+
+ dprint(LOG_DEBUG, "pci_ata_bus_master_write[Y]: ATA_BMCMD_WRITE_READ: %d\n",
+ reg_value & ATA_BMCMD_WRITE_READ);
+ break;
+ case ATA_BMSTAT_Y_REG:
+ reg_value = sc->stat[ATA_Y] = (uint8_t)value;
+ dprint(LOG_DEBUG, "pci_ata_bus_master_write[Y]: ATA_BMSTAT_INTERRUPT: %d, ATA_BMSTAT_ERROR: %d\n",
+ reg_value & ATA_BMSTAT_INTERRUPT, reg_value & ATA_BMSTAT_ERROR);
+ if (reg_value & ATA_BMSTAT_INTERRUPT)
+ sc->stat[ATA_Y] &= ~ATA_BMSTAT_INTERRUPT;
+ if (reg_value & ATA_BMSTAT_ERROR)
+ sc->stat[ATA_Y] &= ~ATA_BMSTAT_ERROR;
+ break;
+ case ATA_BMPRDT_Y_REG:
+ dprint(LOG_DEBUG, "pci_ata_bus_master_write[Y]: ATA_BMPRDT_X_REG: 0x%x\n", (uint32_t)value);
+ sc->prdt[ATA_Y] = (uint32_t)value;
+ break;
+ default:
+ break;
+ }
+
+ return;
+}
+
+static uint64_t
+pci_ata_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
+ uint64_t offset, int size)
+{
+ struct pci_ata_softc *sc = pi->pi_arg;
+ uint64_t value = 0;
+
+ switch (baridx) {
+ case PCI_BAR0:
+ if (!sc->channels[ATA_X])
+ break;
+ assert(ata_channel_is_ok(sc->channels[ATA_X], ATA_X));
+ value = ata_command_block_read(sc->channels[ATA_X], offset);
+ break;
+ case PCI_BAR1:
+ if (!sc->channels[ATA_X])
+ break;
+ assert(ata_channel_is_ok(sc->channels[ATA_X], ATA_X));
+ value = ata_altstatus_read(sc->channels[ATA_X]);
+ break;
+ case PCI_BAR2:
+ if (!sc->channels[ATA_Y])
+ break;
+ assert(ata_channel_is_ok(sc->channels[ATA_Y], ATA_Y));
+ value = ata_command_block_read(sc->channels[ATA_Y], offset);
+ break;
+ case PCI_BAR3:
+ if (!sc->channels[ATA_Y])
+ break;
+ assert(ata_channel_is_ok(sc->channels[ATA_Y], ATA_Y));
+ value = ata_altstatus_read(sc->channels[ATA_Y]);
+ break;
+ case PCI_BAR4:
+ value = pci_ata_bus_master_read(sc, offset);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ return value;
+}
+
+static void
+pci_ata_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
+ int baridx, uint64_t offset, int size, uint64_t value)
+{
+ struct pci_ata_softc *sc = pi->pi_arg;
+
+ switch (baridx) {
+ case PCI_BAR0:
+ if (!sc->channels[ATA_X])
+ break;
+ assert(ata_channel_is_ok(sc->channels[ATA_X], ATA_X));
+ ata_command_block_write(sc->channels[ATA_X], offset, size, value);
+ break;
+ case PCI_BAR1:
+ if (!sc->channels[ATA_X])
+ break;
+ assert(ata_channel_is_ok(sc->channels[ATA_X], ATA_X));
+ ata_control_write(sc->channels[ATA_X], size, value);
+ break;
+ case PCI_BAR2:
+ if (!sc->channels[ATA_Y])
+ break;
+ assert(ata_channel_is_ok(sc->channels[ATA_Y], ATA_Y));
+ ata_command_block_write(sc->channels[ATA_Y], offset, size, value);
+ break;
+ case PCI_BAR3:
+ if (!sc->channels[ATA_Y])
+ break;
+ assert(ata_channel_is_ok(sc->channels[ATA_Y], ATA_Y));
+ ata_control_write(sc->channels[ATA_Y], size, value);
+ break;
+ case PCI_BAR4:
+ pci_ata_bus_master_write(sc, offset, size, value);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ return;
+}
+
+/*
+ * LPC ATA function definitions
+ */
+static void
+lpc_ata_intr_assert(void *arg)
+{
+ struct lpc_ata_softc *sc_lpc_ata = (struct lpc_ata_softc *)arg;
+ struct vmctx *lpc_ctx = sc_lpc_ata->channel->vm_ctx;
+
+ vm_isa_pulse_irq(lpc_ctx, sc_lpc_ata->irq, sc_lpc_ata->irq);
+
+ return;
+}
+
+static void
+lpc_ata_intr_deassert(void *arg)
+{
+ /*
+ * The ATA devices on the LPC bus generate edge triggered interrupts
+ * so nothing more to do here
+ */
+
+ return;
+}
+
+int
+lpc_ata_init(struct vmctx *ctx, const char *opts)
+{
+ struct ata_channel *channel = NULL;
+ struct inout_port iop, ioctlp;
+ int ch_ata = -1;
+ int err;
+
+ ata_open_dbg_file();
+
+ assert(opts[0] == '0' || opts[0] == '1');
+ ch_ata = opts[0] - '0';
+
+ if (lpc_ata_sc[ch_ata].channel) {
+ dprint(LOG_ERR, "lpc_ata_init: the LPC ata%d is initialized\n", ch_ata);
+ return -1;
+ }
+
+ channel = ata_init(ctx, lpc_ata_intr_assert, lpc_ata_intr_deassert, &lpc_ata_sc[ch_ata], opts, 1);
+ if (!channel)
+ return -1;
+
+ assert(ch_ata == channel->interface);
+ channel->use_16bit = 1;
+
+ lpc_ata_sc[ch_ata].channel = channel;
+
+ bzero(&iop, sizeof(struct inout_port));
+ iop.name = lpc_ata_sc[ch_ata].name_io;
+ iop.port = lpc_ata_sc[ch_ata].base_addr_io;
+ iop.size = ATA_IOSIZE;
+ iop.flags = IOPORT_F_INOUT;
+ iop.handler = lpc_ata_io_handler;
+ iop.arg = &lpc_ata_sc[ch_ata];
+
+ err = register_inout(&iop);
+ assert(err == 0);
+
+ bzero(&ioctlp, sizeof(struct inout_port));
+ ioctlp.name = lpc_ata_sc[ch_ata].name_ioctl;
+ ioctlp.port = lpc_ata_sc[ch_ata].base_addr_ioctl;
+ ioctlp.size = ATA_CTLIOSIZE;
+ ioctlp.flags = IOPORT_F_INOUT;
+ ioctlp.handler = lpc_ata_ioctl_handler;
+ ioctlp.arg = &lpc_ata_sc[ch_ata];
+
+ err = register_inout(&ioctlp);
+ assert(err == 0);
+
+ return 0;
+}
+
+static int
+lpc_ata_io_handler(struct vmctx *ctx, int vcpu,
+ int in, int port, int bytes, uint32_t *eax, void *arg)
+{
+ struct lpc_ata_softc *lpc_sc = (struct lpc_ata_softc *)arg;
+ struct ata_channel *channel = NULL;
+ uint8_t offset = 0;
+
+ assert(lpc_sc);
+ channel = lpc_sc->channel;
+ assert(channel);
+
+ offset = port - lpc_sc->base_addr_io;
+ assert(offset < ATA_IOSIZE);
+
+ if (in)
+ *eax = ata_command_block_read(channel, offset);
+ else
+ ata_command_block_write(channel, offset, bytes, *eax);
+
+ return 0;
+}
+
+static int
+lpc_ata_ioctl_handler(struct vmctx *ctx, int vcpu,
+ int in, int port, int bytes, uint32_t *eax, void *arg)
+{
+ struct lpc_ata_softc *lpc_sc = (struct lpc_ata_softc *)arg;
+ struct ata_channel *channel = NULL;
+ uint8_t offset = 0;
+
+ assert(lpc_sc);
+ channel = lpc_sc->channel;
+ assert(channel);
+
+ offset = port - lpc_sc->base_addr_ioctl;
+ assert(offset < ATA_CTLIOSIZE);
+
+ if (in)
+ *eax = ata_altstatus_read(channel);
+ else
+ ata_control_write(channel, bytes, *eax);
+
+ return 0;
+}
+
Index: pci_lpc.c
===================================================================
--- pci_lpc.c
+++ pci_lpc.c
@@ -73,6 +73,11 @@
static const char *lpc_uart_names[LPC_UART_NUM] = { "COM1", "COM2" };
+extern int
+lpc_ata_init(struct vmctx *ctx, const char *opts);
+static const char *lpc_ata0_opts = NULL;
+static const char *lpc_ata1_opts = NULL;
+
/*
* LPC device configuration is in the following form:
* <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
Details
Attached
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)
Attached To
Mode
D5473: ATA/ATAPI6 device emulation in bhyve
Attached
Detach File
Event Timeline
Log In to Comment