#include "qemu-timer.h"
#include "sysemu.h"
#include "ppc_mac.h"
+#include "sh.h"
/* debug IDE devices */
//#define DEBUG_IDE
#define ASC_ILLEGAL_OPCODE 0x20
#define ASC_LOGICAL_BLOCK_OOR 0x21
#define ASC_INV_FIELD_IN_CMD_PACKET 0x24
+#define ASC_MEDIUM_MAY_HAVE_CHANGED 0x28
#define ASC_INCOMPATIBLE_FORMAT 0x30
#define ASC_MEDIUM_NOT_PRESENT 0x3a
#define ASC_SAVING_PARAMETERS_NOT_SUPPORTED 0x39
PCIDevice *pci_dev;
struct BMDMAState *bmdma;
int drive_serial;
+ char drive_serial_str[21];
/* ide regs */
uint8_t feature;
uint8_t error;
{
uint16_t *p;
unsigned int oldsize;
- char buf[20];
if (s->identify_set) {
memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data));
put_le16(p + 4, 512 * s->sectors); /* XXX: retired, remove ? */
put_le16(p + 5, 512); /* XXX: retired, remove ? */
put_le16(p + 6, s->sectors);
- snprintf(buf, sizeof(buf), "QM%05d", s->drive_serial);
- padstr((char *)(p + 10), buf, 20); /* serial number */
+ padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */
put_le16(p + 20, 3); /* XXX: retired, remove ? */
put_le16(p + 21, 512); /* cache size in sectors */
put_le16(p + 22, 4); /* ecc bytes */
static void ide_atapi_identify(IDEState *s)
{
uint16_t *p;
- char buf[20];
if (s->identify_set) {
memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data));
p = (uint16_t *)s->io_buffer;
/* Removable CDROM, 50us response, 12 byte packets */
put_le16(p + 0, (2 << 14) | (5 << 8) | (1 << 7) | (2 << 5) | (0 << 0));
- snprintf(buf, sizeof(buf), "QM%05d", s->drive_serial);
- padstr((char *)(p + 10), buf, 20); /* serial number */
+ padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */
put_le16(p + 20, 3); /* buffer type */
put_le16(p + 21, 512); /* cache size in sectors */
put_le16(p + 22, 4); /* ecc bytes */
{
uint16_t *p;
uint32_t cur_sec;
- char buf[20];
p = (uint16_t *) s->identify_data;
if (s->identify_set)
put_le16(p + 6, s->sectors); /* Default sectors per track */
put_le16(p + 7, s->nb_sectors >> 16); /* Sectors per card */
put_le16(p + 8, s->nb_sectors); /* Sectors per card */
- snprintf(buf, sizeof(buf), "QM%05d", s->drive_serial);
- padstr((char *)(p + 10), buf, 20); /* Serial number in ASCII */
+ padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */
put_le16(p + 22, 0x0004); /* ECC bytes */
padstr((char *) (p + 23), QEMU_VERSION, 8); /* Firmware Revision */
padstr((char *) (p + 27), "QEMU MICRODRIVE", 40);/* Model number */
s->io_buffer_index = 0;
s->io_buffer_size = n * 512;
#ifdef DEBUG_AIO
- printf("aio_read: sector_num=%lld n=%d\n", sector_num, n);
+ printf("aio_read: sector_num=%" PRId64 " n=%d\n", sector_num, n);
#endif
bm->aiocb = bdrv_aio_read(s->bs, sector_num, s->io_buffer, n,
ide_read_dma_cb, bm);
if (dma_buf_rw(bm, 0) == 0)
goto eot;
#ifdef DEBUG_AIO
- printf("aio_write: sector_num=%lld n=%d\n", sector_num, n);
+ printf("aio_write: sector_num=%" PRId64 " n=%d\n", sector_num, n);
#endif
bm->aiocb = bdrv_aio_write(s->bs, sector_num, s->io_buffer, n,
ide_write_dma_cb, bm);
ide_set_irq(s);
}
+static void ide_atapi_cmd_check_status(IDEState *s)
+{
+#ifdef DEBUG_IDE_ATAPI
+ printf("atapi_cmd_check_status\n");
+#endif
+ s->error = MC_ERR | (SENSE_UNIT_ATTENTION << 4);
+ s->status = ERR_STAT;
+ s->nsector = 0;
+ ide_set_irq(s);
+}
+
static inline void cpu_to_ube16(uint8_t *buf, int val)
{
buf[0] = val >> 8;
printf("\n");
}
#endif
+ /* If there's a UNIT_ATTENTION condition pending, only
+ REQUEST_SENSE and INQUIRY commands are allowed to complete. */
+ if (s->sense_key == SENSE_UNIT_ATTENTION &&
+ s->io_buffer[0] != GPCMD_REQUEST_SENSE &&
+ s->io_buffer[0] != GPCMD_INQUIRY) {
+ ide_atapi_cmd_check_status(s);
+ return;
+ }
switch(s->io_buffer[0]) {
case GPCMD_TEST_UNIT_READY:
if (bdrv_is_inserted(s->bs)) {
buf[10] = 0x00;
buf[11] = 0x00;
- buf[12] = 0x70;
+ /* Claim PLAY_AUDIO capability (0x01) since some Linux
+ code checks for this to automount media. */
+ buf[12] = 0x71;
buf[13] = 3 << 5;
buf[14] = (1 << 0) | (1 << 3) | (1 << 5);
if (bdrv_is_locked(s->bs))
buf[2] = s->sense_key;
buf[7] = 10;
buf[12] = s->asc;
+ if (s->sense_key == SENSE_UNIT_ATTENTION)
+ s->sense_key = SENSE_NONE;
ide_atapi_cmd_reply(s, 18, max_len);
break;
case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
IDEState *s = opaque;
uint64_t nb_sectors;
- /* XXX: send interrupt too */
bdrv_get_geometry(s->bs, &nb_sectors);
s->nb_sectors = nb_sectors;
+
+ s->sense_key = SENSE_UNIT_ATTENTION;
+ s->asc = ASC_MEDIUM_MAY_HAVE_CHANGED;
+
+ ide_set_irq(s);
}
static void ide_cmd_lba48_transform(IDEState *s, int lba48)
s->status = READY_STAT;
ide_set_irq(s);
break;
+ case WIN_SEEK:
+ if(s->is_cdrom)
+ goto abort_cmd;
+ /* XXX: Check that seek is within bounds */
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s);
+ break;
/* ATAPI commands */
case WIN_PIDENTIFY:
if (s->is_cdrom) {
ret = 0xff;
break;
case 1:
- if (!ide_if[0].bs && !ide_if[1].bs)
+ if ((!ide_if[0].bs && !ide_if[1].bs) ||
+ (s != ide_if && !s->bs))
ret = 0;
else if (!hob)
ret = s->error;
s->media_changed = 0;
}
-struct partition {
- uint8_t boot_ind; /* 0x80 - active */
- uint8_t head; /* starting head */
- uint8_t sector; /* starting sector */
- uint8_t cyl; /* starting cylinder */
- uint8_t sys_ind; /* What partition type */
- uint8_t end_head; /* end head */
- uint8_t end_sector; /* end sector */
- uint8_t end_cyl; /* end cylinder */
- uint32_t start_sect; /* starting sector counting from 0 */
- uint32_t nr_sects; /* nr of sectors in partition */
-} __attribute__((packed));
-
-/* try to guess the disk logical geometry from the MSDOS partition table. Return 0 if OK, -1 if could not guess */
-static int guess_disk_lchs(IDEState *s,
- int *pcylinders, int *pheads, int *psectors)
-{
- uint8_t *buf = s->io_buffer;
- int ret, i, heads, sectors, cylinders;
- struct partition *p;
- uint32_t nr_sects;
-
- ret = bdrv_read(s->bs, 0, buf, 1);
- if (ret < 0) {
- return -1;
- }
- /* test msdos magic */
- if (buf[510] != 0x55 || buf[511] != 0xaa) {
- return -1;
- }
- for(i = 0; i < 4; i++) {
- p = ((struct partition *)(buf + 0x1be)) + i;
- nr_sects = le32_to_cpu(p->nr_sects);
- if (nr_sects && p->end_head) {
- /* We make the assumption that the partition terminates on
- a cylinder boundary */
- heads = p->end_head + 1;
- sectors = p->end_sector & 63;
- if (sectors == 0)
- continue;
- cylinders = s->nb_sectors / (heads * sectors);
- if (cylinders < 1 || cylinders > 16383)
- continue;
- *pheads = heads;
- *psectors = sectors;
- *pcylinders = cylinders;
-#if 0
- printf("guessed geometry: LCHS=%d %d %d\n",
- cylinders, heads, sectors);
-#endif
- return 0;
- }
- }
- return -1;
-}
-
static void ide_init2(IDEState *ide_state,
BlockDriverState *hd0, BlockDriverState *hd1,
qemu_irq irq)
{
IDEState *s;
static int drive_serial = 1;
- int i, cylinders, heads, secs, translation, lba_detected = 0;
+ int i, cylinders, heads, secs;
uint64_t nb_sectors;
for(i = 0; i < 2; i++) {
s->bs = hd1;
if (s->bs) {
bdrv_get_geometry(s->bs, &nb_sectors);
+ bdrv_guess_geometry(s->bs, &cylinders, &heads, &secs);
+ s->cylinders = cylinders;
+ s->heads = heads;
+ s->sectors = secs;
s->nb_sectors = nb_sectors;
- /* if a geometry hint is available, use it */
- bdrv_get_geometry_hint(s->bs, &cylinders, &heads, &secs);
- translation = bdrv_get_translation_hint(s->bs);
- if (cylinders != 0) {
- s->cylinders = cylinders;
- s->heads = heads;
- s->sectors = secs;
- } else {
- if (guess_disk_lchs(s, &cylinders, &heads, &secs) == 0) {
- if (heads > 16) {
- /* if heads > 16, it means that a BIOS LBA
- translation was active, so the default
- hardware geometry is OK */
- lba_detected = 1;
- goto default_geometry;
- } else {
- s->cylinders = cylinders;
- s->heads = heads;
- s->sectors = secs;
- /* disable any translation to be in sync with
- the logical geometry */
- if (translation == BIOS_ATA_TRANSLATION_AUTO) {
- bdrv_set_translation_hint(s->bs,
- BIOS_ATA_TRANSLATION_NONE);
- }
- }
- } else {
- default_geometry:
- /* if no geometry, use a standard physical disk geometry */
- 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 ((lba_detected == 1) && (translation == BIOS_ATA_TRANSLATION_AUTO)) {
- if ((s->cylinders * s->heads) <= 131072) {
- bdrv_set_translation_hint(s->bs,
- BIOS_ATA_TRANSLATION_LARGE);
- } else {
- bdrv_set_translation_hint(s->bs,
- BIOS_ATA_TRANSLATION_LBA);
- }
- }
- }
- bdrv_set_geometry_hint(s->bs, s->cylinders, s->heads, s->sectors);
- }
+
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->drive_serial = drive_serial++;
+ strncpy(s->drive_serial_str, drive_get_serial(s->bs),
+ sizeof(s->drive_serial_str));
+ if (strlen(s->drive_serial_str) == 0)
+ snprintf(s->drive_serial_str, sizeof(s->drive_serial_str),
+ "QM%05d", s->drive_serial);
s->irq = irq;
s->sector_write_timer = qemu_new_timer(vm_clock,
ide_sector_write_timer_cb, s);
}
}
+static void pci_ide_save(QEMUFile* f, void *opaque)
+{
+ PCIIDEState *d = opaque;
+ int i;
+
+ pci_device_save(&d->dev, f);
+
+ for(i = 0; i < 2; i++) {
+ BMDMAState *bm = &d->bmdma[i];
+ qemu_put_8s(f, &bm->cmd);
+ qemu_put_8s(f, &bm->status);
+ qemu_put_be32s(f, &bm->addr);
+ /* XXX: if a transfer is pending, we do not save it yet */
+ }
+
+ /* per IDE interface data */
+ for(i = 0; i < 2; i++) {
+ IDEState *s = &d->ide_if[i * 2];
+ uint8_t drive1_selected;
+ qemu_put_8s(f, &s->cmd);
+ drive1_selected = (s->cur_drive != s);
+ qemu_put_8s(f, &drive1_selected);
+ }
+
+ /* per IDE drive data */
+ for(i = 0; i < 4; i++) {
+ ide_save(f, &d->ide_if[i]);
+ }
+}
+
+static int pci_ide_load(QEMUFile* f, void *opaque, int version_id)
+{
+ PCIIDEState *d = opaque;
+ int ret, i;
+
+ if (version_id != 1)
+ return -EINVAL;
+ ret = pci_device_load(&d->dev, f);
+ if (ret < 0)
+ return ret;
+
+ for(i = 0; i < 2; i++) {
+ BMDMAState *bm = &d->bmdma[i];
+ qemu_get_8s(f, &bm->cmd);
+ qemu_get_8s(f, &bm->status);
+ qemu_get_be32s(f, &bm->addr);
+ /* XXX: if a transfer is pending, we do not save it yet */
+ }
+
+ /* per IDE interface data */
+ for(i = 0; i < 2; i++) {
+ IDEState *s = &d->ide_if[i * 2];
+ uint8_t drive1_selected;
+ qemu_get_8s(f, &s->cmd);
+ qemu_get_8s(f, &drive1_selected);
+ s->cur_drive = &d->ide_if[i * 2 + (drive1_selected != 0)];
+ }
+
+ /* per IDE drive data */
+ for(i = 0; i < 4; i++) {
+ ide_load(f, &d->ide_if[i]);
+ }
+ return 0;
+}
+
/* XXX: call it also when the MRDMODE is changed from the PCI config
registers */
static void cmd646_update_irq(PCIIDEState *d)
cmd646_update_irq(d);
}
+static void cmd646_reset(void *opaque)
+{
+ PCIIDEState *d = opaque;
+ unsigned int i;
+
+ for (i = 0; i < 2; i++)
+ ide_dma_cancel(&d->bmdma[i]);
+}
+
/* CMD646 PCI IDE controller */
void pci_cmd646_ide_init(PCIBus *bus, BlockDriverState **hd_table,
int secondary_ide_enabled)
pci_conf[0x0b] = 0x01; // class_base = PCI_mass_storage
pci_conf[0x0e] = 0x00; // header_type
+ pci_conf[0x51] = 0x04; // enable IDE0
if (secondary_ide_enabled) {
/* XXX: if not enabled, really disable the seconday IDE controller */
- pci_conf[0x51] = 0x80; /* enable IDE1 */
+ pci_conf[0x51] |= 0x08; /* enable IDE1 */
}
pci_register_io_region((PCIDevice *)d, 0, 0x8,
irq = qemu_allocate_irqs(cmd646_set_irq, d, 2);
ide_init2(&d->ide_if[0], hd_table[0], hd_table[1], irq[0]);
ide_init2(&d->ide_if[2], hd_table[2], hd_table[3], irq[1]);
-}
-
-static void pci_ide_save(QEMUFile* f, void *opaque)
-{
- PCIIDEState *d = opaque;
- int i;
-
- pci_device_save(&d->dev, f);
- for(i = 0; i < 2; i++) {
- BMDMAState *bm = &d->bmdma[i];
- qemu_put_8s(f, &bm->cmd);
- qemu_put_8s(f, &bm->status);
- qemu_put_be32s(f, &bm->addr);
- /* XXX: if a transfer is pending, we do not save it yet */
- }
-
- /* per IDE interface data */
- for(i = 0; i < 2; i++) {
- IDEState *s = &d->ide_if[i * 2];
- uint8_t drive1_selected;
- qemu_put_8s(f, &s->cmd);
- drive1_selected = (s->cur_drive != s);
- qemu_put_8s(f, &drive1_selected);
- }
-
- /* per IDE drive data */
- for(i = 0; i < 4; i++) {
- ide_save(f, &d->ide_if[i]);
- }
-}
-
-static int pci_ide_load(QEMUFile* f, void *opaque, int version_id)
-{
- PCIIDEState *d = opaque;
- int ret, i;
-
- if (version_id != 1)
- return -EINVAL;
- ret = pci_device_load(&d->dev, f);
- if (ret < 0)
- return ret;
-
- for(i = 0; i < 2; i++) {
- BMDMAState *bm = &d->bmdma[i];
- qemu_get_8s(f, &bm->cmd);
- qemu_get_8s(f, &bm->status);
- qemu_get_be32s(f, &bm->addr);
- /* XXX: if a transfer is pending, we do not save it yet */
- }
-
- /* per IDE interface data */
- for(i = 0; i < 2; i++) {
- IDEState *s = &d->ide_if[i * 2];
- uint8_t drive1_selected;
- qemu_get_8s(f, &s->cmd);
- qemu_get_8s(f, &drive1_selected);
- s->cur_drive = &d->ide_if[i * 2 + (drive1_selected != 0)];
- }
-
- /* per IDE drive data */
- for(i = 0; i < 4; i++) {
- ide_load(f, &d->ide_if[i]);
- }
- return 0;
+ register_savevm("ide", 0, 1, pci_ide_save, pci_ide_load, d);
+ qemu_register_reset(cmd646_reset, d);
+ cmd646_reset(d);
}
static void piix3_reset(void *opaque)
pmac_ide_readl,
};
+static void pmac_ide_save(QEMUFile *f, void *opaque)
+{
+ IDEState *s = (IDEState *)opaque;
+ uint8_t drive1_selected;
+ unsigned int i;
+
+ /* per IDE interface data */
+ qemu_put_8s(f, &s->cmd);
+ drive1_selected = (s->cur_drive != s);
+ qemu_put_8s(f, &drive1_selected);
+
+ /* per IDE drive data */
+ for(i = 0; i < 2; i++) {
+ ide_save(f, &s[i]);
+ }
+}
+
+static int pmac_ide_load(QEMUFile *f, void *opaque, int version_id)
+{
+ IDEState *s = (IDEState *)opaque;
+ uint8_t drive1_selected;
+ unsigned int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ /* per IDE interface data */
+ qemu_get_8s(f, &s->cmd);
+ qemu_get_8s(f, &drive1_selected);
+ s->cur_drive = &s[(drive1_selected != 0)];
+
+ /* per IDE drive data */
+ for(i = 0; i < 2; i++) {
+ ide_load(f, &s[i]);
+ }
+ return 0;
+}
+
+static void pmac_ide_reset(void *opaque)
+{
+ IDEState *s = (IDEState *)opaque;
+
+ ide_reset(&s[0]);
+ ide_reset(&s[1]);
+}
+
/* hd_table must contain 4 block drivers */
/* PowerMac uses memory mapped registers, not I/O. Return the memory
I/O index to access the ide. */
pmac_ide_memory = cpu_register_io_memory(0, pmac_ide_read,
pmac_ide_write, &ide_if[0]);
+ register_savevm("ide", 0, 1, pmac_ide_save, pmac_ide_load, &ide_if[0]);
+ qemu_register_reset(pmac_ide_reset, &ide_if[0]);
+ pmac_ide_reset(&ide_if[0]);
return pmac_ide_memory;
}
+/***********************************************************/
+/* MMIO based ide port
+ * This emulates IDE device connected directly to the CPU bus without
+ * dedicated ide controller, which is often seen on embedded boards.
+ */
+
+typedef struct {
+ void *dev;
+ int shift;
+} MMIOState;
+
+static uint32_t mmio_ide_read (void *opaque, target_phys_addr_t addr)
+{
+ MMIOState *s = (MMIOState*)opaque;
+ IDEState *ide = (IDEState*)s->dev;
+ addr >>= s->shift;
+ if (addr & 7)
+ return ide_ioport_read(ide, addr);
+ else
+ return ide_data_readw(ide, 0);
+}
+
+static void mmio_ide_write (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ MMIOState *s = (MMIOState*)opaque;
+ IDEState *ide = (IDEState*)s->dev;
+ addr >>= s->shift;
+ if (addr & 7)
+ ide_ioport_write(ide, addr, val);
+ else
+ ide_data_writew(ide, 0, val);
+}
+
+static CPUReadMemoryFunc *mmio_ide_reads[] = {
+ mmio_ide_read,
+ mmio_ide_read,
+ mmio_ide_read,
+};
+
+static CPUWriteMemoryFunc *mmio_ide_writes[] = {
+ mmio_ide_write,
+ mmio_ide_write,
+ mmio_ide_write,
+};
+
+static uint32_t mmio_ide_status_read (void *opaque, target_phys_addr_t addr)
+{
+ MMIOState *s= (MMIOState*)opaque;
+ IDEState *ide = (IDEState*)s->dev;
+ return ide_status_read(ide, 0);
+}
+
+static void mmio_ide_cmd_write (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ MMIOState *s = (MMIOState*)opaque;
+ IDEState *ide = (IDEState*)s->dev;
+ ide_cmd_write(ide, 0, val);
+}
+
+static CPUReadMemoryFunc *mmio_ide_status[] = {
+ mmio_ide_status_read,
+ mmio_ide_status_read,
+ mmio_ide_status_read,
+};
+
+static CPUWriteMemoryFunc *mmio_ide_cmd[] = {
+ mmio_ide_cmd_write,
+ mmio_ide_cmd_write,
+ mmio_ide_cmd_write,
+};
+
+void mmio_ide_init (target_phys_addr_t membase, target_phys_addr_t membase2,
+ qemu_irq irq, int shift,
+ BlockDriverState *hd0, BlockDriverState *hd1)
+{
+ MMIOState *s = qemu_mallocz(sizeof(MMIOState));
+ IDEState *ide = qemu_mallocz(sizeof(IDEState) * 2);
+ int mem1, mem2;
+
+ ide_init2(ide, hd0, hd1, irq);
+
+ s->dev = ide;
+ s->shift = shift;
+
+ mem1 = cpu_register_io_memory(0, mmio_ide_reads, mmio_ide_writes, s);
+ mem2 = cpu_register_io_memory(0, mmio_ide_status, mmio_ide_cmd, s);
+ cpu_register_physical_memory(membase, 16 << shift, mem1);
+ cpu_register_physical_memory(membase2, 2 << shift, mem2);
+}
+
/***********************************************************/
/* CF-ATA Microdrive */