* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#include <getopt.h>
-#include <inttypes.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <time.h>
-#include <sys/time.h>
-#include <malloc.h>
-#include <termios.h>
-#include <sys/poll.h>
-#include <errno.h>
-#include <sys/wait.h>
-#include <netinet/in.h>
-
-#define NO_THUNK_TYPE_SIZE
-#include "thunk.h"
-
-#include "cpu.h"
-#include "exec-all.h"
-
#include "vl.h"
/* debug IDE devices */
#define ATAPI_INT_REASON_TAG 0xf8
/* same constants as bochs */
+#define ASC_ILLEGAL_OPCODE 0x20
#define ASC_LOGICAL_BLOCK_OOR 0x21
#define ASC_INV_FIELD_IN_CMD_PACKET 0x24
#define ASC_MEDIUM_NOT_PRESENT 0x3a
typedef void EndTransferFunc(struct IDEState *);
+/* NOTE: IDEState represents in fact one drive */
typedef struct IDEState {
/* ide config */
int is_cdrom;
- int cdrom_locked;
int cylinders, heads, sectors;
int64_t nb_sectors;
int mult_sectors;
uint8_t io_buffer[MAX_MULT_SECTORS*512 + 4];
} IDEState;
-IDEState ide_state[MAX_DISKS];
-IDEState *ide_table[0x400 >> 3];
-
-static inline IDEState *get_ide_interface(uint32_t addr)
-{
- return ide_table[addr >> 3];
-}
-
static void padstr(char *str, const char *src, int len)
{
int i, v;
}
}
+static void put_le16(uint16_t *p, unsigned int v)
+{
+#ifdef WORDS_BIGENDIAN
+ *p = bswap16(v);
+#else
+ *p = v;
+#endif
+}
+
static void ide_identify(IDEState *s)
{
uint16_t *p;
memset(s->io_buffer, 0, 512);
p = (uint16_t *)s->io_buffer;
- stw_raw(p + 0, 0x0040);
- stw_raw(p + 1, s->cylinders);
- stw_raw(p + 3, s->heads);
- stw_raw(p + 4, 512 * s->sectors); /* sectors */
- stw_raw(p + 5, 512); /* sector size */
- stw_raw(p + 6, s->sectors);
+ put_le16(p + 0, 0x0040);
+ put_le16(p + 1, s->cylinders);
+ put_le16(p + 3, s->heads);
+ put_le16(p + 4, 512 * s->sectors); /* XXX: retired, remove ? */
+ put_le16(p + 5, 512); /* XXX: retired, remove ? */
+ put_le16(p + 6, s->sectors);
padstr((uint8_t *)(p + 10), "QM00001", 20); /* serial number */
- stw_raw(p + 20, 3); /* buffer type */
- stw_raw(p + 21, 512); /* cache size in sectors */
- stw_raw(p + 22, 4); /* ecc bytes */
+ put_le16(p + 20, 3); /* XXX: retired, remove ? */
+ put_le16(p + 21, 512); /* cache size in sectors */
+ put_le16(p + 22, 4); /* ecc bytes */
padstr((uint8_t *)(p + 23), QEMU_VERSION, 8); /* firmware version */
padstr((uint8_t *)(p + 27), "QEMU HARDDISK", 40); /* model */
#if MAX_MULT_SECTORS > 1
- stw_raw(p + 47, MAX_MULT_SECTORS);
+ put_le16(p + 47, 0x8000 | MAX_MULT_SECTORS);
#endif
- stw_raw(p + 48, 1); /* dword I/O */
- stw_raw(p + 49, 1 << 9); /* LBA supported, no DMA */
- stw_raw(p + 51, 0x200); /* PIO transfer cycle */
- stw_raw(p + 52, 0x200); /* DMA transfer cycle */
- stw_raw(p + 54, s->cylinders);
- stw_raw(p + 55, s->heads);
- stw_raw(p + 56, s->sectors);
+ put_le16(p + 48, 1); /* dword I/O */
+ put_le16(p + 49, 1 << 9); /* LBA supported, no DMA */
+ put_le16(p + 51, 0x200); /* PIO transfer cycle */
+ put_le16(p + 52, 0x200); /* DMA transfer cycle */
+ put_le16(p + 53, 1); /* words 54-58 are valid */
+ put_le16(p + 54, s->cylinders);
+ put_le16(p + 55, s->heads);
+ put_le16(p + 56, s->sectors);
oldsize = s->cylinders * s->heads * s->sectors;
- stw_raw(p + 57, oldsize);
- stw_raw(p + 58, oldsize >> 16);
+ put_le16(p + 57, oldsize);
+ put_le16(p + 58, oldsize >> 16);
if (s->mult_sectors)
- stw_raw(p + 59, 0x100 | s->mult_sectors);
- stw_raw(p + 60, s->nb_sectors);
- stw_raw(p + 61, s->nb_sectors >> 16);
- stw_raw(p + 80, (1 << 1) | (1 << 2));
- stw_raw(p + 82, (1 << 14));
- stw_raw(p + 83, (1 << 14));
- stw_raw(p + 84, (1 << 14));
- stw_raw(p + 85, (1 << 14));
- stw_raw(p + 86, 0);
- stw_raw(p + 87, (1 << 14));
+ put_le16(p + 59, 0x100 | s->mult_sectors);
+ put_le16(p + 60, s->nb_sectors);
+ put_le16(p + 61, s->nb_sectors >> 16);
+ put_le16(p + 80, (1 << 1) | (1 << 2));
+ put_le16(p + 82, (1 << 14));
+ put_le16(p + 83, (1 << 14));
+ put_le16(p + 84, (1 << 14));
+ put_le16(p + 85, (1 << 14));
+ put_le16(p + 86, 0);
+ put_le16(p + 87, (1 << 14));
}
static void ide_atapi_identify(IDEState *s)
memset(s->io_buffer, 0, 512);
p = (uint16_t *)s->io_buffer;
/* Removable CDROM, 50us response, 12 byte packets */
- stw_raw(p + 0, (2 << 14) | (5 << 8) | (1 << 7) | (2 << 5) | (0 << 0));
- stw_raw(p + 1, s->cylinders);
- stw_raw(p + 3, s->heads);
- stw_raw(p + 4, 512 * s->sectors); /* sectors */
- stw_raw(p + 5, 512); /* sector size */
- stw_raw(p + 6, s->sectors);
+ put_le16(p + 0, (2 << 14) | (5 << 8) | (1 << 7) | (2 << 5) | (0 << 0));
+ put_le16(p + 1, s->cylinders);
+ put_le16(p + 3, s->heads);
+ put_le16(p + 4, 512 * s->sectors); /* sectors */
+ put_le16(p + 5, 512); /* sector size */
+ put_le16(p + 6, s->sectors);
padstr((uint8_t *)(p + 10), "QM00001", 20); /* serial number */
- stw_raw(p + 20, 3); /* buffer type */
- stw_raw(p + 21, 512); /* cache size in sectors */
- stw_raw(p + 22, 4); /* ecc bytes */
+ put_le16(p + 20, 3); /* buffer type */
+ put_le16(p + 21, 512); /* cache size in sectors */
+ put_le16(p + 22, 4); /* ecc bytes */
padstr((uint8_t *)(p + 23), QEMU_VERSION, 8); /* firmware version */
padstr((uint8_t *)(p + 27), "QEMU CD-ROM", 40); /* model */
- stw_raw(p + 48, 1); /* dword I/O (XXX: should not be set on CDROM) */
- stw_raw(p + 49, 1 << 9); /* LBA supported, no DMA */
- stw_raw(p + 53, 3); /* words 64-70, 54-58 valid */
- stw_raw(p + 63, 0x103); /* DMA modes XXX: may be incorrect */
- stw_raw(p + 64, 1); /* PIO modes */
- stw_raw(p + 65, 0xb4); /* minimum DMA multiword tx cycle time */
- stw_raw(p + 66, 0xb4); /* recommended DMA multiword tx cycle time */
- stw_raw(p + 67, 0x12c); /* minimum PIO cycle time without flow control */
- stw_raw(p + 68, 0xb4); /* minimum PIO cycle time with IORDY flow control */
+ put_le16(p + 48, 1); /* dword I/O (XXX: should not be set on CDROM) */
+ put_le16(p + 49, 1 << 9); /* LBA supported, no DMA */
+ put_le16(p + 53, 3); /* words 64-70, 54-58 valid */
+ put_le16(p + 63, 0x103); /* DMA modes XXX: may be incorrect */
+ put_le16(p + 64, 1); /* PIO modes */
+ put_le16(p + 65, 0xb4); /* minimum DMA multiword tx cycle time */
+ put_le16(p + 66, 0xb4); /* recommended DMA multiword tx cycle time */
+ put_le16(p + 67, 0x12c); /* minimum PIO cycle time without flow control */
+ put_le16(p + 68, 0xb4); /* minimum PIO cycle time with IORDY flow control */
- stw_raw(p + 71, 30); /* in ns */
- stw_raw(p + 72, 30); /* in ns */
+ put_le16(p + 71, 30); /* in ns */
+ put_le16(p + 72, 30); /* in ns */
- stw_raw(p + 80, 0x1e); /* support up to ATA/ATAPI-4 */
+ put_le16(p + 80, 0x1e); /* support up to ATA/ATAPI-4 */
}
static void ide_set_signature(IDEState *s)
int ret, n;
s->status = READY_STAT | SEEK_STAT;
+ s->error = 0; /* not needed by IDE spec, but needed by Windows */
sector_num = ide_get_sector(s);
n = s->nsector;
if (n == 0) {
if (byte_count_limit & 1)
byte_count_limit--;
size = byte_count_limit;
- } else {
- s->lcyl = size;
- s->hcyl = size >> 8;
}
+ s->lcyl = size;
+ s->hcyl = size >> 8;
s->elementary_transfer_size = size;
/* we cannot transmit more than one sector at a time */
if (s->lba != -1) {
#endif
switch(s->io_buffer[0]) {
case GPCMD_TEST_UNIT_READY:
- if (s->bs) {
+ if (bdrv_is_inserted(s->bs)) {
ide_atapi_cmd_ok(s);
} else {
ide_atapi_cmd_error(s, SENSE_NOT_READY,
buf[12] = 0x70;
buf[13] = 3 << 5;
buf[14] = (1 << 0) | (1 << 3) | (1 << 5);
- if (s->cdrom_locked)
+ if (bdrv_is_locked(s->bs))
buf[6] |= 1 << 1;
buf[15] = 0x00;
cpu_to_ube16(&buf[16], 706);
ide_atapi_cmd_reply(s, 18, max_len);
break;
case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
- if (s->bs) {
- s->cdrom_locked = packet[4] & 1;
+ if (bdrv_is_inserted(s->bs)) {
+ bdrv_set_locked(s->bs, packet[4] & 1);
ide_atapi_cmd_ok(s);
} else {
ide_atapi_cmd_error(s, SENSE_NOT_READY,
{
int nb_sectors, lba;
- if (!s->bs) {
+ if (!bdrv_is_inserted(s->bs)) {
ide_atapi_cmd_error(s, SENSE_NOT_READY,
ASC_MEDIUM_NOT_PRESENT);
break;
case GPCMD_SEEK:
{
int lba;
- if (!s->bs) {
+ if (!bdrv_is_inserted(s->bs)) {
ide_atapi_cmd_error(s, SENSE_NOT_READY,
ASC_MEDIUM_NOT_PRESENT);
break;
start = packet[4] & 1;
eject = (packet[4] >> 1) & 1;
- /* XXX: currently none implemented */
+ if (eject && !start) {
+ /* eject the disk */
+ bdrv_close(s->bs);
+ }
ide_atapi_cmd_ok(s);
}
break;
{
int format, msf, start_track, len;
- if (!s->bs) {
+ if (!bdrv_is_inserted(s->bs)) {
ide_atapi_cmd_error(s, SENSE_NOT_READY,
ASC_MEDIUM_NOT_PRESENT);
break;
ide_atapi_cmd_reply(s, 12, max_len);
break;
default:
- goto error_cmd;
+ error_cmd:
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_INV_FIELD_IN_CMD_PACKET);
+ break;
}
}
break;
case GPCMD_READ_CDVD_CAPACITY:
- if (!s->bs) {
+ if (!bdrv_is_inserted(s->bs)) {
ide_atapi_cmd_error(s, SENSE_NOT_READY,
ASC_MEDIUM_NOT_PRESENT);
break;
ide_atapi_cmd_reply(s, 36, max_len);
break;
default:
- error_cmd:
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
- ASC_INV_FIELD_IN_CMD_PACKET);
+ ASC_ILLEGAL_OPCODE);
break;
}
}
-static void ide_ioport_write(CPUState *env, uint32_t addr, uint32_t val)
+/* called when the inserted state of the media has changed */
+static void cdrom_change_cb(void *opaque)
{
- IDEState *ide_if = get_ide_interface(addr);
- IDEState *s = ide_if->cur_drive;
+ IDEState *s = opaque;
+ int64_t nb_sectors;
+
+ /* XXX: send interrupt too */
+ bdrv_get_geometry(s->bs, &nb_sectors);
+ s->nb_sectors = nb_sectors;
+}
+
+static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ IDEState *ide_if = opaque;
+ IDEState *s;
int unit, n;
#ifdef DEBUG_IDE
case 0:
break;
case 1:
- s->feature = val;
+ /* NOTE: data is written to the two drives */
+ ide_if[0].feature = val;
+ ide_if[1].feature = val;
break;
case 2:
if (val == 0)
val = 256;
- s->nsector = val;
+ ide_if[0].nsector = val;
+ ide_if[1].nsector = val;
break;
case 3:
- s->sector = val;
+ ide_if[0].sector = val;
+ ide_if[1].sector = val;
break;
case 4:
- s->lcyl = val;
+ ide_if[0].lcyl = val;
+ ide_if[1].lcyl = val;
break;
case 5:
- s->hcyl = val;
+ ide_if[0].hcyl = val;
+ ide_if[1].hcyl = val;
break;
case 6:
+ ide_if[0].select = (val & ~0x10) | 0xa0;
+ ide_if[1].select = (val | 0x10) | 0xa0;
/* select drive */
unit = (val >> 4) & 1;
s = ide_if + unit;
ide_if->cur_drive = s;
- s->select = val;
break;
default:
case 7:
#if defined(DEBUG_IDE)
printf("ide: CMD=%02x\n", val);
#endif
+ s = ide_if->cur_drive;
switch(val) {
case WIN_IDENTIFY:
if (s->bs && !s->is_cdrom) {
break;
case WIN_SPECIFY:
case WIN_RECAL:
+ s->error = 0;
s->status = READY_STAT;
ide_set_irq(s);
break;
break;
case WIN_WRITE:
case WIN_WRITE_ONCE:
+ s->error = 0;
s->status = SEEK_STAT;
s->req_nb_sectors = 1;
ide_transfer_start(s, s->io_buffer, 512, ide_sector_write);
case WIN_MULTWRITE:
if (!s->mult_sectors)
goto abort_cmd;
+ s->error = 0;
s->status = SEEK_STAT;
s->req_nb_sectors = s->mult_sectors;
n = s->nsector;
s->status = READY_STAT;
ide_set_irq(s);
break;
+ case WIN_CHECKPOWERMODE1:
+ s->nsector = 0xff; /* device active or idle */
+ s->status = READY_STAT;
+ ide_set_irq(s);
+ break;
/* ATAPI commands */
case WIN_PIDENTIFY:
}
}
-static uint32_t ide_ioport_read(CPUState *env, uint32_t addr1)
+static uint32_t ide_ioport_read(void *opaque, uint32_t addr1)
{
- IDEState *s = get_ide_interface(addr1)->cur_drive;
+ IDEState *ide_if = opaque;
+ IDEState *s = ide_if->cur_drive;
uint32_t addr;
int ret;
ret = 0xff;
break;
case 1:
- ret = s->error;
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else
+ ret = s->error;
break;
case 2:
- ret = s->nsector & 0xff;
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else
+ ret = s->nsector & 0xff;
break;
case 3:
- ret = s->sector;
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else
+ ret = s->sector;
break;
case 4:
- ret = s->lcyl;
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else
+ ret = s->lcyl;
break;
case 5:
- ret = s->hcyl;
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else
+ ret = s->hcyl;
break;
case 6:
- ret = s->select;
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else
+ ret = s->select;
break;
default:
case 7:
- ret = s->status;
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else
+ ret = s->status;
pic_set_irq(s->irq, 0);
break;
}
return ret;
}
-static uint32_t ide_status_read(CPUState *env, uint32_t addr)
+static uint32_t ide_status_read(void *opaque, uint32_t addr)
{
- IDEState *s = get_ide_interface(addr)->cur_drive;
+ IDEState *ide_if = opaque;
+ IDEState *s = ide_if->cur_drive;
int ret;
- ret = s->status;
+
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else
+ ret = s->status;
#ifdef DEBUG_IDE
printf("ide: read status addr=0x%x val=%02x\n", addr, ret);
#endif
return ret;
}
-static void ide_cmd_write(CPUState *env, uint32_t addr, uint32_t val)
+static void ide_cmd_write(void *opaque, uint32_t addr, uint32_t val)
{
- IDEState *ide_if = get_ide_interface(addr);
+ IDEState *ide_if = opaque;
IDEState *s;
int i;
if (s->is_cdrom)
s->status = 0x00; /* NOTE: READY is _not_ set */
else
- s->status = READY_STAT;
+ s->status = READY_STAT | SEEK_STAT;
ide_set_signature(s);
}
}
ide_if[1].cmd = val;
}
-static void ide_data_writew(CPUState *env, uint32_t addr, uint32_t val)
+static void ide_data_writew(void *opaque, uint32_t addr, uint32_t val)
{
- IDEState *s = get_ide_interface(addr)->cur_drive;
+ IDEState *s = ((IDEState *)opaque)->cur_drive;
uint8_t *p;
p = s->data_ptr;
s->end_transfer_func(s);
}
-static uint32_t ide_data_readw(CPUState *env, uint32_t addr)
+static uint32_t ide_data_readw(void *opaque, uint32_t addr)
{
- IDEState *s = get_ide_interface(addr)->cur_drive;
+ IDEState *s = ((IDEState *)opaque)->cur_drive;
uint8_t *p;
int ret;
p = s->data_ptr;
return ret;
}
-static void ide_data_writel(CPUState *env, uint32_t addr, uint32_t val)
+static void ide_data_writel(void *opaque, uint32_t addr, uint32_t val)
{
- IDEState *s = get_ide_interface(addr)->cur_drive;
+ IDEState *s = ((IDEState *)opaque)->cur_drive;
uint8_t *p;
p = s->data_ptr;
s->end_transfer_func(s);
}
-static uint32_t ide_data_readl(CPUState *env, uint32_t addr)
+static uint32_t ide_data_readl(void *opaque, uint32_t addr)
{
- IDEState *s = get_ide_interface(addr)->cur_drive;
+ IDEState *s = ((IDEState *)opaque)->cur_drive;
uint8_t *p;
int ret;
}
}
-void ide_init(void)
+void ide_init(int iobase, int iobase2, int irq,
+ BlockDriverState *hd0, BlockDriverState *hd1)
{
- IDEState *s;
- int i, cylinders, iobase, iobase2;
+ IDEState *s, *ide_state;
+ int i, cylinders, heads, secs;
int64_t nb_sectors;
- static const int ide_iobase[2] = { 0x1f0, 0x170 };
- static const int ide_iobase2[2] = { 0x3f6, 0x376 };
- static const int ide_irq[2] = { 14, 15 };
- for(i = 0; i < MAX_DISKS; i++) {
- s = &ide_state[i];
- s->bs = bs_table[i];
+ ide_state = qemu_mallocz(sizeof(IDEState) * 2);
+ if (!ide_state)
+ return;
+
+ for(i = 0; i < 2; i++) {
+ s = ide_state + i;
+ if (i == 0)
+ s->bs = hd0;
+ else
+ s->bs = hd1;
if (s->bs) {
bdrv_get_geometry(s->bs, &nb_sectors);
s->nb_sectors = nb_sectors;
- ide_guess_geometry(s);
- if (s->cylinders == 0) {
- /* if no geometry, use a LBA compatible one */
- cylinders = nb_sectors / (16 * 63);
- if (cylinders > 16383)
- cylinders = 16383;
- else if (cylinders < 2)
- cylinders = 2;
+ /* if a geometry hint is available, use it */
+ bdrv_get_geometry_hint(s->bs, &cylinders, &heads, &secs);
+ if (cylinders != 0) {
s->cylinders = cylinders;
- s->heads = 16;
- s->sectors = 63;
+ s->heads = heads;
+ s->sectors = secs;
+ } else {
+ ide_guess_geometry(s);
+ if (s->cylinders == 0) {
+ /* if no geometry, use a LBA compatible one */
+ cylinders = nb_sectors / (16 * 63);
+ if (cylinders > 16383)
+ cylinders = 16383;
+ else if (cylinders < 2)
+ cylinders = 2;
+ s->cylinders = cylinders;
+ s->heads = 16;
+ s->sectors = 63;
+ }
+ }
+ if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) {
+ s->is_cdrom = 1;
+ bdrv_set_change_cb(s->bs, cdrom_change_cb, s);
}
}
- s->irq = ide_irq[i >> 1];
+ s->irq = irq;
ide_reset(s);
}
- for(i = 0; i < (MAX_DISKS / 2); i++) {
- iobase = ide_iobase[i];
- iobase2 = ide_iobase2[i];
- ide_table[iobase >> 3] = &ide_state[2 * i];
- if (ide_iobase2[i])
- ide_table[iobase2 >> 3] = &ide_state[2 * i];
- register_ioport_write(iobase, 8, ide_ioport_write, 1);
- register_ioport_read(iobase, 8, ide_ioport_read, 1);
- register_ioport_read(iobase2, 1, ide_status_read, 1);
- register_ioport_write(iobase2, 1, ide_cmd_write, 1);
-
- /* data ports */
- register_ioport_write(iobase, 2, ide_data_writew, 2);
- register_ioport_read(iobase, 2, ide_data_readw, 2);
- register_ioport_write(iobase, 4, ide_data_writel, 4);
- register_ioport_read(iobase, 4, ide_data_readl, 4);
+ register_ioport_write(iobase, 8, 1, ide_ioport_write, ide_state);
+ register_ioport_read(iobase, 8, 1, ide_ioport_read, ide_state);
+ if (iobase2) {
+ register_ioport_read(iobase2, 1, 1, ide_status_read, ide_state);
+ register_ioport_write(iobase2, 1, 1, ide_cmd_write, ide_state);
}
-}
-
-void ide_set_geometry(int n, int cyls, int heads, int secs)
-{
- ide_state[n].cylinders = cyls;
- ide_state[n].heads = heads;
- ide_state[n].sectors = secs;
-}
-
-void ide_set_cdrom(int n, int is_cdrom)
-{
- ide_state[n].is_cdrom = is_cdrom;
+
+ /* data ports */
+ register_ioport_write(iobase, 2, 2, ide_data_writew, ide_state);
+ register_ioport_read(iobase, 2, 2, ide_data_readw, ide_state);
+ register_ioport_write(iobase, 4, 4, ide_data_writel, ide_state);
+ register_ioport_read(iobase, 4, 4, ide_data_readl, ide_state);
}