struct v3_ide_cd_ops {
uint32_t (*get_capacity)(void * private_data);
// Reads always operate on 2048 byte blocks
- int (*read_block)(uchar_t * buf, int offset, void * private_data);
+ int (*read)(uint8_t * buf, int lba, void * private_data);
};
--- /dev/null
+/*
+ * This file is part of the Palacios Virtual Machine Monitor developed
+ * by the V3VEE Project with funding from the United States National
+ * Science Foundation and the Department of Energy.
+ *
+ * The V3VEE Project is a joint project between Northwestern University
+ * and the University of New Mexico. You can find out more at
+ * http://www.v3vee.org
+ *
+ * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
+ * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org>
+ * All rights reserved.
+ *
+ * Author: Jack Lange <jarusl@cs.northwestern.edu>
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
+ */
+
+
+#ifndef __DEVICES_ATAPI_TYPES_H__
+#define __DEVICES_ATAPI_TYPES_H__
+
+#ifdef __V3VEE__
+
+#include <palacios/vmm_types.h>
+
+typedef enum {
+ ATAPI_SEN_NONE = 0,
+ ATAPI_SEN_NOT_RDY = 2,
+ ATAPI_SEN_ILL_REQ = 5,
+ ATAPI_SEN_UNIT_ATTNT = 6
+} atapi_sense_key_t ;
+
+typedef enum {
+ ASC_INV_CMD_FIELD = 0x24,
+ ASC_MEDIA_NOT_PRESENT = 0x3a,
+ ASC_SAVE_PARAM_NOT_SUPPORTED = 0x39,
+ ASC_LOG_BLK_OOR = 0x21 /* LOGICAL BLOCK OUT OF RANGE */
+} atapi_add_sense_code_t ;
+
+
+struct atapi_sense_data {
+ union {
+ uint8_t buf[18];
+ struct {
+ uint8_t header;
+ uint8_t rsvd1;
+ uint8_t sense_key; // atapi_sense_key_t
+ uint8_t info[4];
+ uint8_t read_len; // num bytes past this point
+ uint8_t spec_info[4];
+ uint8_t asc; // atapi_add_sense_code_t
+ uint8_t ascq; // ??
+ uint8_t fruc; // ??
+ uint8_t key_spec[3];
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+
+
+struct atapi_read10_cmd {
+ uint8_t atapi_cmd;
+ uint8_t rel_addr : 1;
+ uint8_t rsvd1 : 2;
+ uint8_t force_access : 1; // can't use cache for data
+ uint8_t disable_pg_out : 1;
+ uint8_t lun : 3;
+ uint32_t lba;
+ uint8_t rsvd2;
+ uint16_t xfer_len;
+ uint8_t ctrl;
+} __attribute__((packed));
+
+
+#endif
+
+#endif
--- /dev/null
+/*
+ * This file is part of the Palacios Virtual Machine Monitor developed
+ * by the V3VEE Project with funding from the United States National
+ * Science Foundation and the Department of Energy.
+ *
+ * The V3VEE Project is a joint project between Northwestern University
+ * and the University of New Mexico. You can find out more at
+ * http://www.v3vee.org
+ *
+ * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
+ * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org>
+ * All rights reserved.
+ *
+ * Author: Jack Lange <jarusl@cs.northwestern.edu>
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
+ */
+
+#define ATAPI_PACKET_SIZE 12
+
+#include "atapi-types.h"
+
+static void atapi_cmd_error(struct vm_device * dev, struct ide_channel * channel,
+ atapi_sense_key_t sense_key, atapi_add_sense_code_t asc) {
+ struct ide_drive * drive = get_selected_drive(channel);
+
+ // overload error register with ATAPI value
+ channel->error_reg.val = sense_key << 4;
+
+ channel->status.busy = 0;
+ channel->status.ready = 1;
+ channel->status.write_fault = 0;
+ channel->status.data_req = 0;
+ channel->status.error = 1;
+
+ drive->cd_state.sense.header = 0xf0;
+ drive->cd_state.sense.rsvd1 = 0x00;
+ drive->cd_state.sense.read_len = 0x0a;
+ drive->cd_state.sense.sense_key = sense_key;
+ drive->cd_state.sense.asc = asc;
+
+ ide_raise_irq(dev, channel);
+}
+
+
+static void atapi_cmd_nop(struct vm_device * dev, struct ide_channel * channel) {
+ channel->status.busy = 0;
+ channel->status.ready = 1;
+ channel->status.data_req = 0;
+ channel->status.error = 0;
+
+ ide_raise_irq(dev, channel);
+}
+
+
+
+static int atapi_read_chunk(struct vm_device * dev, struct ide_channel * channel) {
+ struct ide_drive * drive = get_selected_drive(channel);
+
+ int ret = drive->cd_ops->read(drive->data_buf, drive->cd_state.current_lba, drive->private_data);
+
+ if (ret == -1) {
+ PrintError("IDE: Error reading CD block (LBA=%x)\n", drive->cd_state.current_lba);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int atapi_read10(struct vm_device * dev, struct ide_channel * channel) {
+ struct ide_drive * drive = get_selected_drive(channel);
+ struct atapi_read10_cmd * cmd = (struct atapi_read10_cmd *)(drive->data_buf);
+ uint32_t lba = be_to_le_32(cmd->lba);
+ uint16_t xfer_len = be_to_le_16(cmd->xfer_len);
+
+ /* Check if cd is ready
+ * if not: atapi_cmd_error(... ATAPI_SEN_NOT_RDY, ASC_MEDIA_NOT_PRESENT)
+ */
+
+ if (xfer_len == 0) {
+ atapi_cmd_nop(dev, channel);
+ return 0;
+ }
+
+ if ((lba + xfer_len) > drive->cd_ops->get_capacity(drive->private_data)) {
+ atapi_cmd_error(dev, channel, ATAPI_SEN_ILL_REQ, ASC_LOG_BLK_OOR);
+ }
+
+ drive->cd_state.current_lba = lba;
+
+
+ if (atapi_read_chunk(dev, channel) == -1) {
+ PrintError("IDE: Could not read initial chunk from CD\n");
+ return -1;
+ }
+
+ drive->transfer_length = xfer_len;
+ drive->transfer_index = 0;
+
+ channel->status.busy = 0;
+ channel->status.data_req = 1;
+ channel->status.error = 0;
+
+ ide_raise_irq(dev, channel);
+
+ return 0;
+}
+
+
+
+static void atapi_req_sense(struct vm_device * dev, struct ide_channel * channel) {
+ struct ide_drive * drive = get_selected_drive(channel);
+
+ drive->transfer_length = 18;
+ drive->transfer_index = 0;
+ memcpy(drive->data_buf, drive->cd_state.sense.buf, sizeof(drive->cd_state.sense.buf));
+
+ ide_raise_irq(dev, channel);
+}
+
+static int atapi_handle_packet(struct vm_device * dev, struct ide_channel * channel) {
+ struct ide_drive * drive = get_selected_drive(channel);
+ uint8_t command = drive->data_buf[0];
+
+ PrintDebug("IDE: ATAPI Command %x\n", command);
+
+ switch (command) {
+ case 0x00: // test unit ready
+ atapi_cmd_nop(dev, channel);
+
+ /* if drive not ready:
+ atapi_cmd_error(... ATAPI_SEN_NOT_RDY, ASC_MEDIA_NOT_PRESENT)
+ */
+ break;
+ case 0x03: // request sense
+ atapi_req_sense(dev, channel);
+ break;
+
+ case 0x28: // read(10)
+ if (atapi_read10(dev, channel) == -1) {
+ PrintError("IDE: Error in ATAPI read (%x)\n", command);
+ return -1;
+ }
+
+ break;
+ case 0xa8: // read(12)
+
+
+ case 0x1b: // start/stop drive
+ case 0xbd: // mechanism status
+ case 0x5a: // mode sense
+ case 0x12: // inquiry
+ case 0x25: // read cdrom capacity
+ case 0xbe: // read cd
+ case 0x43: // read TOC
+
+
+ case 0x2b: // seek
+ case 0x1e: // lock door
+ case 0x42: // read sub-channel
+ case 0x51: // read disk info
+
+
+ case 0x55: // mode select
+ case 0xa6: // load/unload cd
+ case 0x4b: // pause/resume
+ case 0x45: // play audio
+ case 0x47: // play audio msf
+ case 0xbc: // play cd
+ case 0xb9: // read cd msf
+ case 0x44: // read header
+ case 0xba: // scan
+ case 0xbb: // set cd speed
+ case 0x4e: // stop play/scan
+ case 0x46: // ???
+ case 0x4a: // ???
+ default:
+ PrintError("Unhandled ATAPI command %x\n", command);
+ atapi_cmd_error(dev, channel, ATAPI_SEN_ILL_REQ, ASC_INV_CMD_FIELD);
+ ide_raise_irq(dev, channel);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void atapi_identify_device(struct ide_drive * drive) {
+ struct ide_drive_id * drive_id = (struct ide_drive_id *)(drive->data_buf);
+ const char* serial_number = " VT00001\0\0\0\0\0\0\0\0\0\0\0\0";
+ const char* firmware = "ALPHA1 ";
+
+ drive->transfer_length = 512;
+ drive->transfer_index = 0;
+ memset(drive_id->buf, 0, sizeof(drive_id->buf));
+
+ drive_id->fixed_drive = 1;
+ drive_id->removable_media = 1;
+
+ // Black magic...
+ drive_id->disk_speed1 = 1;
+ drive_id->disk_speed3 = 1;
+
+ drive_id->cdrom_flag = 1;
+
+ // These buffers do not contain a terminating "\0"
+ memcpy(drive_id->serial_num, serial_number, strlen(serial_number));
+ memcpy(drive_id->firmware_rev, firmware, strlen(firmware));
+ memcpy(drive_id->model_num, drive->model, 40);
+
+ // 32 bits access
+ drive_id->dword_io = 1;
+
+ // enable LBA access
+ drive_id->lba_enable = 1;
+
+
+ // words 64-70, 54-58 valid
+ drive_id->buf[53] = 0x0003;
+
+ // copied from CFA540A
+ drive_id->buf[63] = 0x0103; // variable (DMA stuff)
+ drive_id->buf[64] = 0x0001; // PIO
+ drive_id->buf[65] = 0x00b4;
+ drive_id->buf[66] = 0x00b4;
+ drive_id->buf[67] = 0x012c;
+ drive_id->buf[68] = 0x00b4;
+
+ drive_id->buf[71] = 30; // faked
+ drive_id->buf[72] = 30; // faked
+
+ drive_id->buf[80] = 0x1e; // supports up to ATA/ATAPI-4
+}
} __attribute__((packed));
} __attribute__((packed));
+
typedef enum {IDE_CTRL_NOT_SPECIFIED,
IDE_CTRL_SINGLE_PORT,
IDE_CTRL_DUAL_PORT,
IDE_CTRL_DUAL_PORT_CACHE} ide_controller_type;
+
struct ide_drive_id {
union {
uint16_t buf[256];
#include <palacios/vmm.h>
#include <devices/ide.h>
#include "ide-types.h"
+#include "atapi-types.h"
#define PRI_DEFAULT_IRQ 14
#define SEC_DEFAULT_IRQ 15
+
#define PRI_DATA_PORT 0x1f0
#define PRI_FEATURES_PORT 0x1f1
#define PRI_SECT_CNT_PORT 0x1f2
#define SEC_ADDR_REG_PORT 0x377
+#define DATA_BUFFER_SIZE 2048
static const char * ide_dev_type_strs[] = {"HARDDISK", "CDROM", "NONE"};
+struct ide_cd_state {
+ struct atapi_sense_data sense;
+ uint_t current_lba;
+};
+
+struct ide_hd_state {
+};
struct ide_drive {
// Command Registers
};
+ union {
+ struct ide_cd_state cd_state;
+ struct ide_hd_state hd_state;
+ };
+
char model[41];
- uint_t data_offset;
+ // Where we are in the data transfer
+ uint_t transfer_index;
+
+ // the length of a transfer
+ // calculated for easy access
+ uint_t transfer_length;
- // data buffer...
- uint8_t buffer[2048];
+ // We have a local data buffer that we use for IO port accesses
+ uint8_t data_buf[DATA_BUFFER_SIZE];
void * private_data;
- uint8_t sector_count; // 0x1f2,0x172
+ uint8_t sector_count; // 0x1f2,0x172
uint8_t sector_num; // 0x1f3,0x173
union {
uint16_t cylinder;
struct ide_drive_head_reg drive_head; // 0x1f6,0x176
struct ide_status_reg status; // [read] 0x1f7,0x177
- uint8_t command_reg; // [write] 0x1f7,0x177
+ uint8_t cmd_reg; // [write] 0x1f7,0x177
int irq; // this is temporary until we add PCI support
+static inline uint32_t be_to_le_16(const uint16_t val) {
+ uint8_t * buf = (uint8_t *)&val;
+ return (buf[0] << 8) | (buf[1]) ;
+}
+
+
+static inline uint32_t be_to_le_32(const uint32_t val) {
+ uint8_t * buf = (uint8_t *)&val;
+ return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+}
+
static inline int get_channel_index(ushort_t port) {
if (((port & 0xfff8) == 0x1f0) ||
drive->cylinder = 0x0000;
}
+
+ memset(drive->data_buf, 0, sizeof(drive->data_buf));
+ drive->transfer_length = 0;
+ drive->transfer_index = 0;
+
// Send the reset signal to the connected device callbacks
// channel->drives[0].reset();
// channel->drives[1].reset();
channel->error_reg.val = 0x01;
// clear commands
- channel->command_reg = 0x00;
+ channel->cmd_reg = 0x00;
channel->ctrl_reg.irq_disable = 0;
}
return -1;
}
- channel->command_reg = *(uint8_t *)src;
-
-
PrintDebug("IDE: Writing Command Port %x (val=%x)\n", port, *(uint8_t *)src);
-
- switch (channel->command_reg) {
+
+ channel->cmd_reg = *(uint8_t *)src;
+
+ switch (channel->cmd_reg) {
case 0xa0: // ATAPI Command Packet
if (drive->drive_type != IDE_CDROM) {
channel->status.write_fault = 0;
channel->status.data_req = 1;
channel->status.error = 0;
-
- drive->data_offset = 0;
+ // reset the data buffer...
+ drive->transfer_length = ATAPI_PACKET_SIZE;
+ drive->transfer_index = 0;
+
+ break;
case 0xa1: // ATAPI Identify Device Packet
atapi_identify_device(drive);
channel->status.val = 0x58; // ready, data_req, seek_complete
ide_raise_irq(dev, channel);
+ break;
case 0xec: // Identify Device
if (drive->drive_type != IDE_DISK) {
drive_reset(drive);
return -1;
}
break;
-
+ default:
+ PrintError("Unimplemented IDE command (%x)\n", channel->cmd_reg);
+ return -1;
}
- return -1;
+ return length;
}
static int write_data_port(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
- PrintDebug("IDE: Writing Data Port %x (val=%x)\n", port, *(uint8_t *)src);
- return -1;
+ struct ide_internal * ide = (struct ide_internal *)(dev->private_data);
+ struct ide_channel * channel = get_selected_channel(ide, port);
+ struct ide_drive * drive = get_selected_drive(channel);
+
+ PrintDebug("IDE: Writing Data Port %x (val=%x, len=%d)\n", port, *(uint32_t *)src, length);
+
+ memcpy(drive->data_buf + drive->transfer_index, src, length);
+ drive->transfer_index += length;
+
+ // Transfer is complete, dispatch the command
+ if (drive->transfer_index >= drive->transfer_length) {
+ switch (channel->cmd_reg) {
+ case 0x30: // Write Sectors
+ PrintError("Writing Data not yet implemented\n");
+ return -1;
+
+ case 0xa0: // ATAPI packet command
+ if (atapi_handle_packet(dev, channel) == -1) {
+ PrintError("Error handling ATAPI packet\n");
+ return -1;
+ }
+ break;
+ default:
+ PrintError("Unhandld IDE Command %x\n", channel->cmd_reg);
+ return -1;
+ }
+ }
+
+ return length;
}
static int read_data_port(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
+ struct ide_internal * ide = (struct ide_internal *)(dev->private_data);
+ struct ide_channel * channel = get_selected_channel(ide, port);
+ struct ide_drive * drive = get_selected_drive(channel);
+ int data_offset = drive->transfer_index % DATA_BUFFER_SIZE;
+
PrintDebug("IDE: Reading Data Port %x\n", port);
- return -1;
+
+ if (data_offset == DATA_BUFFER_SIZE) {
+ // check for more data to transfer, if there isn't any then that's a problem...
+ /*
+ * if (ide_update_buffer(drive) == -1) {
+ * return -1;
+ * }
+ */
+ return -1;
+ }
+
+
+ // check for overruns...
+ // We will return the data padded with 0's
+ if (drive->transfer_index + length > drive->transfer_length) {
+ length = drive->transfer_length - drive->transfer_index;
+ memset(dst, 0, length);
+ }
+
+ memcpy(dst, drive->data_buf + data_offset, length);
+
+ drive->transfer_index += length;
+
+
+ if (drive->transfer_index >= drive->transfer_length) {
+ channel->status.data_req = 0;
+ }
+
+ return length;
}
static int write_port_std(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
drive->drive_type = IDE_NONE;
- drive->data_offset = 0;
memset(drive->model, 0, sizeof(drive->model));
- memset(drive->buffer, 0, sizeof(drive->buffer));
+
+ drive->transfer_index = 0;
+ drive->transfer_length = 0;
+ memset(drive->data_buf, 0, sizeof(drive->data_buf));
drive->private_data = NULL;
drive->cd_ops = NULL;
channel->error_reg.val = 0x01;
channel->drive_head.val = 0x00;
channel->status.val = 0x00;
- channel->command_reg = 0x00;
+ channel->cmd_reg = 0x00;
channel->ctrl_reg.val = 0x08;