Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


added atapi handlers
Jack Lange [Thu, 19 Mar 2009 20:35:12 +0000 (15:35 -0500)]
palacios/include/devices/ide.h
palacios/src/devices/atapi-types.h [new file with mode: 0644]
palacios/src/devices/atapi.h [new file with mode: 0644]
palacios/src/devices/ide-types.h
palacios/src/devices/ide.c
palacios/src/devices/ramdisk.c

index a5b6ea0..d70ac4b 100644 (file)
@@ -29,7 +29,7 @@ typedef enum {IDE_DISK, IDE_CDROM, IDE_NONE} v3_ide_dev_type_t;
 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);
 
 };
 
diff --git a/palacios/src/devices/atapi-types.h b/palacios/src/devices/atapi-types.h
new file mode 100644 (file)
index 0000000..1f4df14
--- /dev/null
@@ -0,0 +1,80 @@
+/* 
+ * 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
diff --git a/palacios/src/devices/atapi.h b/palacios/src/devices/atapi.h
new file mode 100644 (file)
index 0000000..e10d4e8
--- /dev/null
@@ -0,0 +1,234 @@
+/* 
+ * 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
+}
index fab4fa0..caf1ac2 100644 (file)
@@ -92,11 +92,13 @@ struct ide_features_reg {
     } __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];
index 7534f1e..0d6e0b2 100644 (file)
 #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
@@ -47,6 +49,7 @@
 #define SEC_ADDR_REG_PORT     0x377
 
 
+#define DATA_BUFFER_SIZE 2048
 
 static const char * ide_dev_type_strs[] = {"HARDDISK", "CDROM", "NONE"};
 
@@ -61,7 +64,14 @@ static inline const char * device_type_to_str(v3_ide_dev_type_t type) {
 
 
 
+struct ide_cd_state {
+    struct atapi_sense_data sense;
+    uint_t current_lba;
+};
+
+struct ide_hd_state {
 
+};
 
 struct ide_drive {
     // Command Registers
@@ -74,16 +84,26 @@ struct ide_drive {
     };
 
 
+    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;
@@ -109,7 +129,7 @@ struct ide_channel {
     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
 
@@ -125,6 +145,17 @@ struct ide_internal {
 
 
 
+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) ||
@@ -170,6 +201,11 @@ static void drive_reset(struct ide_drive * drive) {
        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();
@@ -184,7 +220,7 @@ static void channel_reset(struct ide_channel * channel) {
     channel->error_reg.val = 0x01;
 
     // clear commands
-    channel->command_reg = 0x00;
+    channel->cmd_reg = 0x00;
 
     channel->ctrl_reg.irq_disable = 0;
 }
@@ -226,12 +262,11 @@ static int write_cmd_port(ushort_t port, void * src, uint_t length, struct vm_de
        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) {
@@ -244,9 +279,12 @@ static int write_cmd_port(ushort_t port, void * src, uint_t length, struct vm_de
            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);
 
@@ -254,6 +292,7 @@ static int write_cmd_port(ushort_t port, void * src, uint_t length, struct vm_de
            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);
@@ -265,22 +304,84 @@ static int write_cmd_port(ushort_t port, void * src, uint_t length, struct vm_de
                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) {
@@ -458,9 +559,11 @@ static void init_drive(struct ide_drive * drive) {
 
     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;
@@ -472,7 +575,7 @@ static void init_channel(struct ide_channel * channel) {
     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;
 
 
index d2a14ce..3595c36 100644 (file)
@@ -2240,7 +2240,6 @@ void rd_identify_ATAPI_drive(struct vm_device * dev, struct channel_t * channel)
     }
 
 
-
     return;
 }