diff options
author | RajithaY <rajithax.yerrumsetty@intel.com> | 2017-04-25 03:31:15 -0700 |
---|---|---|
committer | Rajitha Yerrumchetty <rajithax.yerrumsetty@intel.com> | 2017-05-22 06:48:08 +0000 |
commit | bb756eebdac6fd24e8919e2c43f7d2c8c4091f59 (patch) | |
tree | ca11e03542edf2d8f631efeca5e1626d211107e3 /qemu/hw/block | |
parent | a14b48d18a9ed03ec191cf16b162206998a895ce (diff) |
Adding qemu as a submodule of KVMFORNFV
This Patch includes the changes to add qemu as a submodule to
kvmfornfv repo and make use of the updated latest qemu for the
execution of all testcase
Change-Id: I1280af507a857675c7f81d30c95255635667bdd7
Signed-off-by:RajithaY<rajithax.yerrumsetty@intel.com>
Diffstat (limited to 'qemu/hw/block')
-rw-r--r-- | qemu/hw/block/Makefile.objs | 15 | ||||
-rw-r--r-- | qemu/hw/block/block.c | 93 | ||||
-rw-r--r-- | qemu/hw/block/cdrom.c | 156 | ||||
-rw-r--r-- | qemu/hw/block/dataplane/Makefile.objs | 1 | ||||
-rw-r--r-- | qemu/hw/block/dataplane/virtio-blk.c | 290 | ||||
-rw-r--r-- | qemu/hw/block/dataplane/virtio-blk.h | 31 | ||||
-rw-r--r-- | qemu/hw/block/ecc.c | 91 | ||||
-rw-r--r-- | qemu/hw/block/fdc.c | 2738 | ||||
-rw-r--r-- | qemu/hw/block/hd-geometry.c | 166 | ||||
-rw-r--r-- | qemu/hw/block/m25p80.c | 1005 | ||||
-rw-r--r-- | qemu/hw/block/nand.c | 801 | ||||
-rw-r--r-- | qemu/hw/block/nvme.c | 942 | ||||
-rw-r--r-- | qemu/hw/block/nvme.h | 713 | ||||
-rw-r--r-- | qemu/hw/block/onenand.c | 850 | ||||
-rw-r--r-- | qemu/hw/block/pflash_cfi01.c | 970 | ||||
-rw-r--r-- | qemu/hw/block/pflash_cfi02.c | 797 | ||||
-rw-r--r-- | qemu/hw/block/tc58128.c | 181 | ||||
-rw-r--r-- | qemu/hw/block/virtio-blk.c | 980 | ||||
-rw-r--r-- | qemu/hw/block/xen_blkif.h | 119 | ||||
-rw-r--r-- | qemu/hw/block/xen_disk.c | 1121 |
20 files changed, 0 insertions, 12060 deletions
diff --git a/qemu/hw/block/Makefile.objs b/qemu/hw/block/Makefile.objs deleted file mode 100644 index d4c3ab758..000000000 --- a/qemu/hw/block/Makefile.objs +++ /dev/null @@ -1,15 +0,0 @@ -common-obj-y += block.o cdrom.o hd-geometry.o -common-obj-$(CONFIG_FDC) += fdc.o -common-obj-$(CONFIG_SSI_M25P80) += m25p80.o -common-obj-$(CONFIG_NAND) += nand.o -common-obj-$(CONFIG_PFLASH_CFI01) += pflash_cfi01.o -common-obj-$(CONFIG_PFLASH_CFI02) += pflash_cfi02.o -common-obj-$(CONFIG_XEN_BACKEND) += xen_disk.o -common-obj-$(CONFIG_ECC) += ecc.o -common-obj-$(CONFIG_ONENAND) += onenand.o -common-obj-$(CONFIG_NVME_PCI) += nvme.o - -obj-$(CONFIG_SH4) += tc58128.o - -obj-$(CONFIG_VIRTIO) += virtio-blk.o -obj-$(CONFIG_VIRTIO) += dataplane/ diff --git a/qemu/hw/block/block.c b/qemu/hw/block/block.c deleted file mode 100644 index 97a59d4fa..000000000 --- a/qemu/hw/block/block.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Common code for block device models - * - * Copyright (C) 2012 Red Hat, Inc. - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * later. See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "sysemu/blockdev.h" -#include "sysemu/block-backend.h" -#include "hw/block/block.h" -#include "qapi/error.h" -#include "qemu/error-report.h" - -void blkconf_serial(BlockConf *conf, char **serial) -{ - DriveInfo *dinfo; - - if (!*serial) { - /* try to fall back to value set with legacy -drive serial=... */ - dinfo = blk_legacy_dinfo(conf->blk); - if (dinfo) { - *serial = g_strdup(dinfo->serial); - } - } -} - -void blkconf_blocksizes(BlockConf *conf) -{ - BlockBackend *blk = conf->blk; - BlockSizes blocksizes; - int backend_ret; - - backend_ret = blk_probe_blocksizes(blk, &blocksizes); - /* fill in detected values if they are not defined via qemu command line */ - if (!conf->physical_block_size) { - if (!backend_ret) { - conf->physical_block_size = blocksizes.phys; - } else { - conf->physical_block_size = BDRV_SECTOR_SIZE; - } - } - if (!conf->logical_block_size) { - if (!backend_ret) { - conf->logical_block_size = blocksizes.log; - } else { - conf->logical_block_size = BDRV_SECTOR_SIZE; - } - } -} - -void blkconf_geometry(BlockConf *conf, int *ptrans, - unsigned cyls_max, unsigned heads_max, unsigned secs_max, - Error **errp) -{ - DriveInfo *dinfo; - - if (!conf->cyls && !conf->heads && !conf->secs) { - /* try to fall back to value set with legacy -drive cyls=... */ - dinfo = blk_legacy_dinfo(conf->blk); - if (dinfo) { - conf->cyls = dinfo->cyls; - conf->heads = dinfo->heads; - conf->secs = dinfo->secs; - if (ptrans) { - *ptrans = dinfo->trans; - } - } - } - if (!conf->cyls && !conf->heads && !conf->secs) { - hd_geometry_guess(conf->blk, - &conf->cyls, &conf->heads, &conf->secs, - ptrans); - } else if (ptrans && *ptrans == BIOS_ATA_TRANSLATION_AUTO) { - *ptrans = hd_bios_chs_auto_trans(conf->cyls, conf->heads, conf->secs); - } - if (conf->cyls || conf->heads || conf->secs) { - if (conf->cyls < 1 || conf->cyls > cyls_max) { - error_setg(errp, "cyls must be between 1 and %u", cyls_max); - return; - } - if (conf->heads < 1 || conf->heads > heads_max) { - error_setg(errp, "heads must be between 1 and %u", heads_max); - return; - } - if (conf->secs < 1 || conf->secs > secs_max) { - error_setg(errp, "secs must be between 1 and %u", secs_max); - return; - } - } -} diff --git a/qemu/hw/block/cdrom.c b/qemu/hw/block/cdrom.c deleted file mode 100644 index da937fe33..000000000 --- a/qemu/hw/block/cdrom.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * QEMU ATAPI CD-ROM Emulator - * - * Copyright (c) 2006 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* ??? Most of the ATAPI emulation is still in ide.c. It should be moved - here. */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "hw/scsi/scsi.h" - -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; -} - -/* same toc as bochs. Return -1 if error or the toc length */ -/* XXX: check this */ -int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track) -{ - uint8_t *q; - int len; - - if (start_track > 1 && start_track != 0xaa) - return -1; - q = buf + 2; - *q++ = 1; /* first session */ - *q++ = 1; /* last session */ - if (start_track <= 1) { - *q++ = 0; /* reserved */ - *q++ = 0x14; /* ADR, control */ - *q++ = 1; /* track number */ - *q++ = 0; /* reserved */ - if (msf) { - *q++ = 0; /* reserved */ - lba_to_msf(q, 0); - q += 3; - } else { - /* sector 0 */ - stl_be_p(q, 0); - q += 4; - } - } - /* lead out track */ - *q++ = 0; /* reserved */ - *q++ = 0x16; /* ADR, control */ - *q++ = 0xaa; /* track number */ - *q++ = 0; /* reserved */ - if (msf) { - *q++ = 0; /* reserved */ - lba_to_msf(q, nb_sectors); - q += 3; - } else { - stl_be_p(q, nb_sectors); - q += 4; - } - len = q - buf; - stw_be_p(buf, len - 2); - return len; -} - -/* mostly same info as PearPc */ -int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num) -{ - uint8_t *q; - int len; - - q = buf + 2; - *q++ = 1; /* first session */ - *q++ = 1; /* last session */ - - *q++ = 1; /* session number */ - *q++ = 0x14; /* data track */ - *q++ = 0; /* track number */ - *q++ = 0xa0; /* lead-in */ - *q++ = 0; /* min */ - *q++ = 0; /* sec */ - *q++ = 0; /* frame */ - *q++ = 0; - *q++ = 1; /* first track */ - *q++ = 0x00; /* disk type */ - *q++ = 0x00; - - *q++ = 1; /* session number */ - *q++ = 0x14; /* data track */ - *q++ = 0; /* track number */ - *q++ = 0xa1; - *q++ = 0; /* min */ - *q++ = 0; /* sec */ - *q++ = 0; /* frame */ - *q++ = 0; - *q++ = 1; /* last track */ - *q++ = 0x00; - *q++ = 0x00; - - *q++ = 1; /* session number */ - *q++ = 0x14; /* data track */ - *q++ = 0; /* track number */ - *q++ = 0xa2; /* lead-out */ - *q++ = 0; /* min */ - *q++ = 0; /* sec */ - *q++ = 0; /* frame */ - if (msf) { - *q++ = 0; /* reserved */ - lba_to_msf(q, nb_sectors); - q += 3; - } else { - stl_be_p(q, nb_sectors); - q += 4; - } - - *q++ = 1; /* session number */ - *q++ = 0x14; /* ADR, control */ - *q++ = 0; /* track number */ - *q++ = 1; /* point */ - *q++ = 0; /* min */ - *q++ = 0; /* sec */ - *q++ = 0; /* frame */ - if (msf) { - *q++ = 0; - lba_to_msf(q, 0); - q += 3; - } else { - *q++ = 0; - *q++ = 0; - *q++ = 0; - *q++ = 0; - } - - len = q - buf; - stw_be_p(buf, len - 2); - return len; -} diff --git a/qemu/hw/block/dataplane/Makefile.objs b/qemu/hw/block/dataplane/Makefile.objs deleted file mode 100644 index e786f6642..000000000 --- a/qemu/hw/block/dataplane/Makefile.objs +++ /dev/null @@ -1 +0,0 @@ -obj-y += virtio-blk.o diff --git a/qemu/hw/block/dataplane/virtio-blk.c b/qemu/hw/block/dataplane/virtio-blk.c deleted file mode 100644 index 3cb97c9a2..000000000 --- a/qemu/hw/block/dataplane/virtio-blk.c +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Dedicated thread for virtio-blk I/O processing - * - * Copyright 2012 IBM, Corp. - * Copyright 2012 Red Hat, Inc. and/or its affiliates - * - * Authors: - * Stefan Hajnoczi <stefanha@redhat.com> - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "trace.h" -#include "qemu/iov.h" -#include "qemu/thread.h" -#include "qemu/error-report.h" -#include "hw/virtio/virtio-access.h" -#include "sysemu/block-backend.h" -#include "hw/virtio/virtio-blk.h" -#include "virtio-blk.h" -#include "block/aio.h" -#include "hw/virtio/virtio-bus.h" -#include "qom/object_interfaces.h" - -struct VirtIOBlockDataPlane { - bool starting; - bool stopping; - - VirtIOBlkConf *conf; - - VirtIODevice *vdev; - VirtQueue *vq; /* virtqueue vring */ - EventNotifier *guest_notifier; /* irq */ - QEMUBH *bh; /* bh for guest notification */ - - Notifier insert_notifier, remove_notifier; - - /* Note that these EventNotifiers are assigned by value. This is - * fine as long as you do not call event_notifier_cleanup on them - * (because you don't own the file descriptor or handle; you just - * use it). - */ - IOThread *iothread; - AioContext *ctx; - - /* Operation blocker on BDS */ - Error *blocker; -}; - -/* Raise an interrupt to signal guest, if necessary */ -void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s) -{ - qemu_bh_schedule(s->bh); -} - -static void notify_guest_bh(void *opaque) -{ - VirtIOBlockDataPlane *s = opaque; - - if (!virtio_should_notify(s->vdev, s->vq)) { - return; - } - - event_notifier_set(s->guest_notifier); -} - -static void data_plane_set_up_op_blockers(VirtIOBlockDataPlane *s) -{ - assert(!s->blocker); - error_setg(&s->blocker, "block device is in use by data plane"); - blk_op_block_all(s->conf->conf.blk, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_BACKUP_SOURCE, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_CHANGE, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_COMMIT_SOURCE, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_COMMIT_TARGET, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_EJECT, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, - s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, - s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, - s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_MIRROR_SOURCE, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_STREAM, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_REPLACE, s->blocker); -} - -static void data_plane_remove_op_blockers(VirtIOBlockDataPlane *s) -{ - if (s->blocker) { - blk_op_unblock_all(s->conf->conf.blk, s->blocker); - error_free(s->blocker); - s->blocker = NULL; - } -} - -static void data_plane_blk_insert_notifier(Notifier *n, void *data) -{ - VirtIOBlockDataPlane *s = container_of(n, VirtIOBlockDataPlane, - insert_notifier); - assert(s->conf->conf.blk == data); - data_plane_set_up_op_blockers(s); -} - -static void data_plane_blk_remove_notifier(Notifier *n, void *data) -{ - VirtIOBlockDataPlane *s = container_of(n, VirtIOBlockDataPlane, - remove_notifier); - assert(s->conf->conf.blk == data); - data_plane_remove_op_blockers(s); -} - -/* Context: QEMU global mutex held */ -void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, - VirtIOBlockDataPlane **dataplane, - Error **errp) -{ - VirtIOBlockDataPlane *s; - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - - *dataplane = NULL; - - if (!conf->iothread) { - return; - } - - /* Don't try if transport does not support notifiers. */ - if (!k->set_guest_notifiers || !k->set_host_notifier) { - error_setg(errp, - "device is incompatible with dataplane " - "(transport does not support notifiers)"); - return; - } - - /* If dataplane is (re-)enabled while the guest is running there could be - * block jobs that can conflict. - */ - if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { - error_prepend(errp, "cannot start dataplane thread: "); - return; - } - - s = g_new0(VirtIOBlockDataPlane, 1); - s->vdev = vdev; - s->conf = conf; - - if (conf->iothread) { - s->iothread = conf->iothread; - object_ref(OBJECT(s->iothread)); - } - s->ctx = iothread_get_aio_context(s->iothread); - s->bh = aio_bh_new(s->ctx, notify_guest_bh, s); - - s->insert_notifier.notify = data_plane_blk_insert_notifier; - s->remove_notifier.notify = data_plane_blk_remove_notifier; - blk_add_insert_bs_notifier(conf->conf.blk, &s->insert_notifier); - blk_add_remove_bs_notifier(conf->conf.blk, &s->remove_notifier); - - data_plane_set_up_op_blockers(s); - - *dataplane = s; -} - -/* Context: QEMU global mutex held */ -void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s) -{ - if (!s) { - return; - } - - virtio_blk_data_plane_stop(s); - data_plane_remove_op_blockers(s); - notifier_remove(&s->insert_notifier); - notifier_remove(&s->remove_notifier); - qemu_bh_delete(s->bh); - object_unref(OBJECT(s->iothread)); - g_free(s); -} - -static void virtio_blk_data_plane_handle_output(VirtIODevice *vdev, - VirtQueue *vq) -{ - VirtIOBlock *s = (VirtIOBlock *)vdev; - - assert(s->dataplane); - assert(s->dataplane_started); - - virtio_blk_handle_vq(s, vq); -} - -/* Context: QEMU global mutex held */ -void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s) -{ - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - VirtIOBlock *vblk = VIRTIO_BLK(s->vdev); - int r; - - if (vblk->dataplane_started || s->starting) { - return; - } - - s->starting = true; - s->vq = virtio_get_queue(s->vdev, 0); - - /* Set up guest notifier (irq) */ - r = k->set_guest_notifiers(qbus->parent, 1, true); - if (r != 0) { - fprintf(stderr, "virtio-blk failed to set guest notifier (%d), " - "ensure -enable-kvm is set\n", r); - goto fail_guest_notifiers; - } - s->guest_notifier = virtio_queue_get_guest_notifier(s->vq); - - /* Set up virtqueue notify */ - r = k->set_host_notifier(qbus->parent, 0, true); - if (r != 0) { - fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r); - goto fail_host_notifier; - } - - s->starting = false; - vblk->dataplane_started = true; - trace_virtio_blk_data_plane_start(s); - - blk_set_aio_context(s->conf->conf.blk, s->ctx); - - /* Kick right away to begin processing requests already in vring */ - event_notifier_set(virtio_queue_get_host_notifier(s->vq)); - - /* Get this show started by hooking up our callbacks */ - aio_context_acquire(s->ctx); - virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, - virtio_blk_data_plane_handle_output); - aio_context_release(s->ctx); - return; - - fail_host_notifier: - k->set_guest_notifiers(qbus->parent, 1, false); - fail_guest_notifiers: - vblk->dataplane_disabled = true; - s->starting = false; - vblk->dataplane_started = true; -} - -/* Context: QEMU global mutex held */ -void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s) -{ - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - VirtIOBlock *vblk = VIRTIO_BLK(s->vdev); - - if (!vblk->dataplane_started || s->stopping) { - return; - } - - /* Better luck next time. */ - if (vblk->dataplane_disabled) { - vblk->dataplane_disabled = false; - vblk->dataplane_started = false; - return; - } - s->stopping = true; - trace_virtio_blk_data_plane_stop(s); - - aio_context_acquire(s->ctx); - - /* Stop notifications for new requests from guest */ - virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, NULL); - - /* Drain and switch bs back to the QEMU main loop */ - blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context()); - - aio_context_release(s->ctx); - - k->set_host_notifier(qbus->parent, 0, false); - - /* Clean up guest notifier (irq) */ - k->set_guest_notifiers(qbus->parent, 1, false); - - vblk->dataplane_started = false; - s->stopping = false; -} diff --git a/qemu/hw/block/dataplane/virtio-blk.h b/qemu/hw/block/dataplane/virtio-blk.h deleted file mode 100644 index 0714c11a2..000000000 --- a/qemu/hw/block/dataplane/virtio-blk.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Dedicated thread for virtio-blk I/O processing - * - * Copyright 2012 IBM, Corp. - * Copyright 2012 Red Hat, Inc. and/or its affiliates - * - * Authors: - * Stefan Hajnoczi <stefanha@redhat.com> - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef HW_DATAPLANE_VIRTIO_BLK_H -#define HW_DATAPLANE_VIRTIO_BLK_H - -#include "hw/virtio/virtio.h" - -typedef struct VirtIOBlockDataPlane VirtIOBlockDataPlane; - -void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, - VirtIOBlockDataPlane **dataplane, - Error **errp); -void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s); -void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s); -void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s); -void virtio_blk_data_plane_drain(VirtIOBlockDataPlane *s); -void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s); - -#endif /* HW_DATAPLANE_VIRTIO_BLK_H */ diff --git a/qemu/hw/block/ecc.c b/qemu/hw/block/ecc.c deleted file mode 100644 index 48311d260..000000000 --- a/qemu/hw/block/ecc.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Calculate Error-correcting Codes. Used by NAND Flash controllers - * (not by NAND chips). - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski <balrog@zabor.org> - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/block/flash.h" - -/* - * Pre-calculated 256-way 1 byte column parity. Table borrowed from Linux. - */ -static const uint8_t nand_ecc_precalc_table[] = { - 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, - 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, - 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, - 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, - 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, - 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, - 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, - 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, - 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, - 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, - 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, - 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, - 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, - 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, - 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, - 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, - 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, - 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, - 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, - 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, - 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, - 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, - 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, - 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, - 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, - 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, - 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, - 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, - 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, - 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, - 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, - 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, -}; - -/* Update ECC parity count. */ -uint8_t ecc_digest(ECCState *s, uint8_t sample) -{ - uint8_t idx = nand_ecc_precalc_table[sample]; - - s->cp ^= idx & 0x3f; - if (idx & 0x40) { - s->lp[0] ^= ~s->count; - s->lp[1] ^= s->count; - } - s->count ++; - - return sample; -} - -/* Reinitialise the counters. */ -void ecc_reset(ECCState *s) -{ - s->lp[0] = 0x0000; - s->lp[1] = 0x0000; - s->cp = 0x00; - s->count = 0; -} - -/* Save/restore */ -VMStateDescription vmstate_ecc_state = { - .name = "ecc-state", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT8(cp, ECCState), - VMSTATE_UINT16_ARRAY(lp, ECCState, 2), - VMSTATE_UINT16(count, ECCState), - VMSTATE_END_OF_LIST(), - }, -}; diff --git a/qemu/hw/block/fdc.c b/qemu/hw/block/fdc.c deleted file mode 100644 index 372227569..000000000 --- a/qemu/hw/block/fdc.c +++ /dev/null @@ -1,2738 +0,0 @@ -/* - * QEMU Floppy disk emulator (Intel 82078) - * - * Copyright (c) 2003, 2007 Jocelyn Mayer - * Copyright (c) 2008 Hervé Poussineau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -/* - * The controller is used in Sun4m systems in a slightly different - * way. There are changes in DOR register and DMA is not available. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/block/fdc.h" -#include "qapi/error.h" -#include "qemu/error-report.h" -#include "qemu/timer.h" -#include "hw/isa/isa.h" -#include "hw/sysbus.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" -#include "qemu/log.h" - -/********************************************************/ -/* debug Floppy devices */ - -#define DEBUG_FLOPPY 0 - -#define FLOPPY_DPRINTF(fmt, ...) \ - do { \ - if (DEBUG_FLOPPY) { \ - fprintf(stderr, "FLOPPY: " fmt , ## __VA_ARGS__); \ - } \ - } while (0) - -/********************************************************/ -/* Floppy drive emulation */ - -typedef enum FDriveRate { - FDRIVE_RATE_500K = 0x00, /* 500 Kbps */ - FDRIVE_RATE_300K = 0x01, /* 300 Kbps */ - FDRIVE_RATE_250K = 0x02, /* 250 Kbps */ - FDRIVE_RATE_1M = 0x03, /* 1 Mbps */ -} FDriveRate; - -typedef enum FDriveSize { - FDRIVE_SIZE_UNKNOWN, - FDRIVE_SIZE_350, - FDRIVE_SIZE_525, -} FDriveSize; - -typedef struct FDFormat { - FloppyDriveType drive; - uint8_t last_sect; - uint8_t max_track; - uint8_t max_head; - FDriveRate rate; -} FDFormat; - -/* In many cases, the total sector size of a format is enough to uniquely - * identify it. However, there are some total sector collisions between - * formats of different physical size, and these are noted below by - * highlighting the total sector size for entries with collisions. */ -static const FDFormat fd_formats[] = { - /* First entry is default format */ - /* 1.44 MB 3"1/2 floppy disks */ - { FLOPPY_DRIVE_TYPE_144, 18, 80, 1, FDRIVE_RATE_500K, }, /* 3.5" 2880 */ - { FLOPPY_DRIVE_TYPE_144, 20, 80, 1, FDRIVE_RATE_500K, }, /* 3.5" 3200 */ - { FLOPPY_DRIVE_TYPE_144, 21, 80, 1, FDRIVE_RATE_500K, }, - { FLOPPY_DRIVE_TYPE_144, 21, 82, 1, FDRIVE_RATE_500K, }, - { FLOPPY_DRIVE_TYPE_144, 21, 83, 1, FDRIVE_RATE_500K, }, - { FLOPPY_DRIVE_TYPE_144, 22, 80, 1, FDRIVE_RATE_500K, }, - { FLOPPY_DRIVE_TYPE_144, 23, 80, 1, FDRIVE_RATE_500K, }, - { FLOPPY_DRIVE_TYPE_144, 24, 80, 1, FDRIVE_RATE_500K, }, - /* 2.88 MB 3"1/2 floppy disks */ - { FLOPPY_DRIVE_TYPE_288, 36, 80, 1, FDRIVE_RATE_1M, }, - { FLOPPY_DRIVE_TYPE_288, 39, 80, 1, FDRIVE_RATE_1M, }, - { FLOPPY_DRIVE_TYPE_288, 40, 80, 1, FDRIVE_RATE_1M, }, - { FLOPPY_DRIVE_TYPE_288, 44, 80, 1, FDRIVE_RATE_1M, }, - { FLOPPY_DRIVE_TYPE_288, 48, 80, 1, FDRIVE_RATE_1M, }, - /* 720 kB 3"1/2 floppy disks */ - { FLOPPY_DRIVE_TYPE_144, 9, 80, 1, FDRIVE_RATE_250K, }, /* 3.5" 1440 */ - { FLOPPY_DRIVE_TYPE_144, 10, 80, 1, FDRIVE_RATE_250K, }, - { FLOPPY_DRIVE_TYPE_144, 10, 82, 1, FDRIVE_RATE_250K, }, - { FLOPPY_DRIVE_TYPE_144, 10, 83, 1, FDRIVE_RATE_250K, }, - { FLOPPY_DRIVE_TYPE_144, 13, 80, 1, FDRIVE_RATE_250K, }, - { FLOPPY_DRIVE_TYPE_144, 14, 80, 1, FDRIVE_RATE_250K, }, - /* 1.2 MB 5"1/4 floppy disks */ - { FLOPPY_DRIVE_TYPE_120, 15, 80, 1, FDRIVE_RATE_500K, }, - { FLOPPY_DRIVE_TYPE_120, 18, 80, 1, FDRIVE_RATE_500K, }, /* 5.25" 2880 */ - { FLOPPY_DRIVE_TYPE_120, 18, 82, 1, FDRIVE_RATE_500K, }, - { FLOPPY_DRIVE_TYPE_120, 18, 83, 1, FDRIVE_RATE_500K, }, - { FLOPPY_DRIVE_TYPE_120, 20, 80, 1, FDRIVE_RATE_500K, }, /* 5.25" 3200 */ - /* 720 kB 5"1/4 floppy disks */ - { FLOPPY_DRIVE_TYPE_120, 9, 80, 1, FDRIVE_RATE_250K, }, /* 5.25" 1440 */ - { FLOPPY_DRIVE_TYPE_120, 11, 80, 1, FDRIVE_RATE_250K, }, - /* 360 kB 5"1/4 floppy disks */ - { FLOPPY_DRIVE_TYPE_120, 9, 40, 1, FDRIVE_RATE_300K, }, /* 5.25" 720 */ - { FLOPPY_DRIVE_TYPE_120, 9, 40, 0, FDRIVE_RATE_300K, }, - { FLOPPY_DRIVE_TYPE_120, 10, 41, 1, FDRIVE_RATE_300K, }, - { FLOPPY_DRIVE_TYPE_120, 10, 42, 1, FDRIVE_RATE_300K, }, - /* 320 kB 5"1/4 floppy disks */ - { FLOPPY_DRIVE_TYPE_120, 8, 40, 1, FDRIVE_RATE_250K, }, - { FLOPPY_DRIVE_TYPE_120, 8, 40, 0, FDRIVE_RATE_250K, }, - /* 360 kB must match 5"1/4 better than 3"1/2... */ - { FLOPPY_DRIVE_TYPE_144, 9, 80, 0, FDRIVE_RATE_250K, }, /* 3.5" 720 */ - /* end */ - { FLOPPY_DRIVE_TYPE_NONE, -1, -1, 0, 0, }, -}; - -static FDriveSize drive_size(FloppyDriveType drive) -{ - switch (drive) { - case FLOPPY_DRIVE_TYPE_120: - return FDRIVE_SIZE_525; - case FLOPPY_DRIVE_TYPE_144: - case FLOPPY_DRIVE_TYPE_288: - return FDRIVE_SIZE_350; - default: - return FDRIVE_SIZE_UNKNOWN; - } -} - -#define GET_CUR_DRV(fdctrl) ((fdctrl)->cur_drv) -#define SET_CUR_DRV(fdctrl, drive) ((fdctrl)->cur_drv = (drive)) - -/* Will always be a fixed parameter for us */ -#define FD_SECTOR_LEN 512 -#define FD_SECTOR_SC 2 /* Sector size code */ -#define FD_RESET_SENSEI_COUNT 4 /* Number of sense interrupts on RESET */ - -typedef struct FDCtrl FDCtrl; - -/* Floppy disk drive emulation */ -typedef enum FDiskFlags { - FDISK_DBL_SIDES = 0x01, -} FDiskFlags; - -typedef struct FDrive { - FDCtrl *fdctrl; - BlockBackend *blk; - /* Drive status */ - FloppyDriveType drive; /* CMOS drive type */ - uint8_t perpendicular; /* 2.88 MB access mode */ - /* Position */ - uint8_t head; - uint8_t track; - uint8_t sect; - /* Media */ - FloppyDriveType disk; /* Current disk type */ - FDiskFlags flags; - uint8_t last_sect; /* Nb sector per track */ - uint8_t max_track; /* Nb of tracks */ - uint16_t bps; /* Bytes per sector */ - uint8_t ro; /* Is read-only */ - uint8_t media_changed; /* Is media changed */ - uint8_t media_rate; /* Data rate of medium */ - - bool media_validated; /* Have we validated the media? */ -} FDrive; - - -static FloppyDriveType get_fallback_drive_type(FDrive *drv); - -/* Hack: FD_SEEK is expected to work on empty drives. However, QEMU - * currently goes through some pains to keep seeks within the bounds - * established by last_sect and max_track. Correcting this is difficult, - * as refactoring FDC code tends to expose nasty bugs in the Linux kernel. - * - * For now: allow empty drives to have large bounds so we can seek around, - * with the understanding that when a diskette is inserted, the bounds will - * properly tighten to match the geometry of that inserted medium. - */ -static void fd_empty_seek_hack(FDrive *drv) -{ - drv->last_sect = 0xFF; - drv->max_track = 0xFF; -} - -static void fd_init(FDrive *drv) -{ - /* Drive */ - drv->perpendicular = 0; - /* Disk */ - drv->disk = FLOPPY_DRIVE_TYPE_NONE; - drv->last_sect = 0; - drv->max_track = 0; - drv->ro = true; - drv->media_changed = 1; -} - -#define NUM_SIDES(drv) ((drv)->flags & FDISK_DBL_SIDES ? 2 : 1) - -static int fd_sector_calc(uint8_t head, uint8_t track, uint8_t sect, - uint8_t last_sect, uint8_t num_sides) -{ - return (((track * num_sides) + head) * last_sect) + sect - 1; -} - -/* Returns current position, in sectors, for given drive */ -static int fd_sector(FDrive *drv) -{ - return fd_sector_calc(drv->head, drv->track, drv->sect, drv->last_sect, - NUM_SIDES(drv)); -} - -/* Seek to a new position: - * returns 0 if already on right track - * returns 1 if track changed - * returns 2 if track is invalid - * returns 3 if sector is invalid - * returns 4 if seek is disabled - */ -static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect, - int enable_seek) -{ - uint32_t sector; - int ret; - - if (track > drv->max_track || - (head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) { - FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n", - head, track, sect, 1, - (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1, - drv->max_track, drv->last_sect); - return 2; - } - if (sect > drv->last_sect) { - FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n", - head, track, sect, 1, - (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1, - drv->max_track, drv->last_sect); - return 3; - } - sector = fd_sector_calc(head, track, sect, drv->last_sect, NUM_SIDES(drv)); - ret = 0; - if (sector != fd_sector(drv)) { -#if 0 - if (!enable_seek) { - FLOPPY_DPRINTF("error: no implicit seek %d %02x %02x" - " (max=%d %02x %02x)\n", - head, track, sect, 1, drv->max_track, - drv->last_sect); - return 4; - } -#endif - drv->head = head; - if (drv->track != track) { - if (drv->blk != NULL && blk_is_inserted(drv->blk)) { - drv->media_changed = 0; - } - ret = 1; - } - drv->track = track; - drv->sect = sect; - } - - if (drv->blk == NULL || !blk_is_inserted(drv->blk)) { - ret = 2; - } - - return ret; -} - -/* Set drive back to track 0 */ -static void fd_recalibrate(FDrive *drv) -{ - FLOPPY_DPRINTF("recalibrate\n"); - fd_seek(drv, 0, 0, 1, 1); -} - -/** - * Determine geometry based on inserted diskette. - * Will not operate on an empty drive. - * - * @return: 0 on success, -1 if the drive is empty. - */ -static int pick_geometry(FDrive *drv) -{ - BlockBackend *blk = drv->blk; - const FDFormat *parse; - uint64_t nb_sectors, size; - int i; - int match, size_match, type_match; - bool magic = drv->drive == FLOPPY_DRIVE_TYPE_AUTO; - - /* We can only pick a geometry if we have a diskette. */ - if (!drv->blk || !blk_is_inserted(drv->blk) || - drv->drive == FLOPPY_DRIVE_TYPE_NONE) - { - return -1; - } - - /* We need to determine the likely geometry of the inserted medium. - * In order of preference, we look for: - * (1) The same drive type and number of sectors, - * (2) The same diskette size and number of sectors, - * (3) The same drive type. - * - * In all cases, matches that occur higher in the drive table will take - * precedence over matches that occur later in the table. - */ - blk_get_geometry(blk, &nb_sectors); - match = size_match = type_match = -1; - for (i = 0; ; i++) { - parse = &fd_formats[i]; - if (parse->drive == FLOPPY_DRIVE_TYPE_NONE) { - break; - } - size = (parse->max_head + 1) * parse->max_track * parse->last_sect; - if (nb_sectors == size) { - if (magic || parse->drive == drv->drive) { - /* (1) perfect match -- nb_sectors and drive type */ - goto out; - } else if (drive_size(parse->drive) == drive_size(drv->drive)) { - /* (2) size match -- nb_sectors and physical medium size */ - match = (match == -1) ? i : match; - } else { - /* This is suspicious -- Did the user misconfigure? */ - size_match = (size_match == -1) ? i : size_match; - } - } else if (type_match == -1) { - if ((parse->drive == drv->drive) || - (magic && (parse->drive == get_fallback_drive_type(drv)))) { - /* (3) type match -- nb_sectors mismatch, but matches the type - * specified explicitly by the user, or matches the fallback - * default type when using the drive autodetect mechanism */ - type_match = i; - } - } - } - - /* No exact match found */ - if (match == -1) { - if (size_match != -1) { - parse = &fd_formats[size_match]; - FLOPPY_DPRINTF("User requested floppy drive type '%s', " - "but inserted medium appears to be a " - "%"PRId64" sector '%s' type\n", - FloppyDriveType_lookup[drv->drive], - nb_sectors, - FloppyDriveType_lookup[parse->drive]); - } - match = type_match; - } - - /* No match of any kind found -- fd_format is misconfigured, abort. */ - if (match == -1) { - error_setg(&error_abort, "No candidate geometries present in table " - " for floppy drive type '%s'", - FloppyDriveType_lookup[drv->drive]); - } - - parse = &(fd_formats[match]); - - out: - if (parse->max_head == 0) { - drv->flags &= ~FDISK_DBL_SIDES; - } else { - drv->flags |= FDISK_DBL_SIDES; - } - drv->max_track = parse->max_track; - drv->last_sect = parse->last_sect; - drv->disk = parse->drive; - drv->media_rate = parse->rate; - return 0; -} - -static void pick_drive_type(FDrive *drv) -{ - if (drv->drive != FLOPPY_DRIVE_TYPE_AUTO) { - return; - } - - if (pick_geometry(drv) == 0) { - drv->drive = drv->disk; - } else { - drv->drive = get_fallback_drive_type(drv); - } - - g_assert(drv->drive != FLOPPY_DRIVE_TYPE_AUTO); -} - -/* Revalidate a disk drive after a disk change */ -static void fd_revalidate(FDrive *drv) -{ - int rc; - - FLOPPY_DPRINTF("revalidate\n"); - if (drv->blk != NULL) { - drv->ro = blk_is_read_only(drv->blk); - if (!blk_is_inserted(drv->blk)) { - FLOPPY_DPRINTF("No disk in drive\n"); - drv->disk = FLOPPY_DRIVE_TYPE_NONE; - fd_empty_seek_hack(drv); - } else if (!drv->media_validated) { - rc = pick_geometry(drv); - if (rc) { - FLOPPY_DPRINTF("Could not validate floppy drive media"); - } else { - drv->media_validated = true; - FLOPPY_DPRINTF("Floppy disk (%d h %d t %d s) %s\n", - (drv->flags & FDISK_DBL_SIDES) ? 2 : 1, - drv->max_track, drv->last_sect, - drv->ro ? "ro" : "rw"); - } - } - } else { - FLOPPY_DPRINTF("No drive connected\n"); - drv->last_sect = 0; - drv->max_track = 0; - drv->flags &= ~FDISK_DBL_SIDES; - drv->drive = FLOPPY_DRIVE_TYPE_NONE; - drv->disk = FLOPPY_DRIVE_TYPE_NONE; - } -} - -/********************************************************/ -/* Intel 82078 floppy disk controller emulation */ - -static void fdctrl_reset(FDCtrl *fdctrl, int do_irq); -static void fdctrl_to_command_phase(FDCtrl *fdctrl); -static int fdctrl_transfer_handler (void *opaque, int nchan, - int dma_pos, int dma_len); -static void fdctrl_raise_irq(FDCtrl *fdctrl); -static FDrive *get_cur_drv(FDCtrl *fdctrl); - -static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl); -static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl); -static uint32_t fdctrl_read_dor(FDCtrl *fdctrl); -static void fdctrl_write_dor(FDCtrl *fdctrl, uint32_t value); -static uint32_t fdctrl_read_tape(FDCtrl *fdctrl); -static void fdctrl_write_tape(FDCtrl *fdctrl, uint32_t value); -static uint32_t fdctrl_read_main_status(FDCtrl *fdctrl); -static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value); -static uint32_t fdctrl_read_data(FDCtrl *fdctrl); -static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value); -static uint32_t fdctrl_read_dir(FDCtrl *fdctrl); -static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value); - -enum { - FD_DIR_WRITE = 0, - FD_DIR_READ = 1, - FD_DIR_SCANE = 2, - FD_DIR_SCANL = 3, - FD_DIR_SCANH = 4, - FD_DIR_VERIFY = 5, -}; - -enum { - FD_STATE_MULTI = 0x01, /* multi track flag */ - FD_STATE_FORMAT = 0x02, /* format flag */ -}; - -enum { - FD_REG_SRA = 0x00, - FD_REG_SRB = 0x01, - FD_REG_DOR = 0x02, - FD_REG_TDR = 0x03, - FD_REG_MSR = 0x04, - FD_REG_DSR = 0x04, - FD_REG_FIFO = 0x05, - FD_REG_DIR = 0x07, - FD_REG_CCR = 0x07, -}; - -enum { - FD_CMD_READ_TRACK = 0x02, - FD_CMD_SPECIFY = 0x03, - FD_CMD_SENSE_DRIVE_STATUS = 0x04, - FD_CMD_WRITE = 0x05, - FD_CMD_READ = 0x06, - FD_CMD_RECALIBRATE = 0x07, - FD_CMD_SENSE_INTERRUPT_STATUS = 0x08, - FD_CMD_WRITE_DELETED = 0x09, - FD_CMD_READ_ID = 0x0a, - FD_CMD_READ_DELETED = 0x0c, - FD_CMD_FORMAT_TRACK = 0x0d, - FD_CMD_DUMPREG = 0x0e, - FD_CMD_SEEK = 0x0f, - FD_CMD_VERSION = 0x10, - FD_CMD_SCAN_EQUAL = 0x11, - FD_CMD_PERPENDICULAR_MODE = 0x12, - FD_CMD_CONFIGURE = 0x13, - FD_CMD_LOCK = 0x14, - FD_CMD_VERIFY = 0x16, - FD_CMD_POWERDOWN_MODE = 0x17, - FD_CMD_PART_ID = 0x18, - FD_CMD_SCAN_LOW_OR_EQUAL = 0x19, - FD_CMD_SCAN_HIGH_OR_EQUAL = 0x1d, - FD_CMD_SAVE = 0x2e, - FD_CMD_OPTION = 0x33, - FD_CMD_RESTORE = 0x4e, - FD_CMD_DRIVE_SPECIFICATION_COMMAND = 0x8e, - FD_CMD_RELATIVE_SEEK_OUT = 0x8f, - FD_CMD_FORMAT_AND_WRITE = 0xcd, - FD_CMD_RELATIVE_SEEK_IN = 0xcf, -}; - -enum { - FD_CONFIG_PRETRK = 0xff, /* Pre-compensation set to track 0 */ - FD_CONFIG_FIFOTHR = 0x0f, /* FIFO threshold set to 1 byte */ - FD_CONFIG_POLL = 0x10, /* Poll enabled */ - FD_CONFIG_EFIFO = 0x20, /* FIFO disabled */ - FD_CONFIG_EIS = 0x40, /* No implied seeks */ -}; - -enum { - FD_SR0_DS0 = 0x01, - FD_SR0_DS1 = 0x02, - FD_SR0_HEAD = 0x04, - FD_SR0_EQPMT = 0x10, - FD_SR0_SEEK = 0x20, - FD_SR0_ABNTERM = 0x40, - FD_SR0_INVCMD = 0x80, - FD_SR0_RDYCHG = 0xc0, -}; - -enum { - FD_SR1_MA = 0x01, /* Missing address mark */ - FD_SR1_NW = 0x02, /* Not writable */ - FD_SR1_EC = 0x80, /* End of cylinder */ -}; - -enum { - FD_SR2_SNS = 0x04, /* Scan not satisfied */ - FD_SR2_SEH = 0x08, /* Scan equal hit */ -}; - -enum { - FD_SRA_DIR = 0x01, - FD_SRA_nWP = 0x02, - FD_SRA_nINDX = 0x04, - FD_SRA_HDSEL = 0x08, - FD_SRA_nTRK0 = 0x10, - FD_SRA_STEP = 0x20, - FD_SRA_nDRV2 = 0x40, - FD_SRA_INTPEND = 0x80, -}; - -enum { - FD_SRB_MTR0 = 0x01, - FD_SRB_MTR1 = 0x02, - FD_SRB_WGATE = 0x04, - FD_SRB_RDATA = 0x08, - FD_SRB_WDATA = 0x10, - FD_SRB_DR0 = 0x20, -}; - -enum { -#if MAX_FD == 4 - FD_DOR_SELMASK = 0x03, -#else - FD_DOR_SELMASK = 0x01, -#endif - FD_DOR_nRESET = 0x04, - FD_DOR_DMAEN = 0x08, - FD_DOR_MOTEN0 = 0x10, - FD_DOR_MOTEN1 = 0x20, - FD_DOR_MOTEN2 = 0x40, - FD_DOR_MOTEN3 = 0x80, -}; - -enum { -#if MAX_FD == 4 - FD_TDR_BOOTSEL = 0x0c, -#else - FD_TDR_BOOTSEL = 0x04, -#endif -}; - -enum { - FD_DSR_DRATEMASK= 0x03, - FD_DSR_PWRDOWN = 0x40, - FD_DSR_SWRESET = 0x80, -}; - -enum { - FD_MSR_DRV0BUSY = 0x01, - FD_MSR_DRV1BUSY = 0x02, - FD_MSR_DRV2BUSY = 0x04, - FD_MSR_DRV3BUSY = 0x08, - FD_MSR_CMDBUSY = 0x10, - FD_MSR_NONDMA = 0x20, - FD_MSR_DIO = 0x40, - FD_MSR_RQM = 0x80, -}; - -enum { - FD_DIR_DSKCHG = 0x80, -}; - -/* - * See chapter 5.0 "Controller phases" of the spec: - * - * Command phase: - * The host writes a command and its parameters into the FIFO. The command - * phase is completed when all parameters for the command have been supplied, - * and execution phase is entered. - * - * Execution phase: - * Data transfers, either DMA or non-DMA. For non-DMA transfers, the FIFO - * contains the payload now, otherwise it's unused. When all bytes of the - * required data have been transferred, the state is switched to either result - * phase (if the command produces status bytes) or directly back into the - * command phase for the next command. - * - * Result phase: - * The host reads out the FIFO, which contains one or more result bytes now. - */ -enum { - /* Only for migration: reconstruct phase from registers like qemu 2.3 */ - FD_PHASE_RECONSTRUCT = 0, - - FD_PHASE_COMMAND = 1, - FD_PHASE_EXECUTION = 2, - FD_PHASE_RESULT = 3, -}; - -#define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI) -#define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT) - -struct FDCtrl { - MemoryRegion iomem; - qemu_irq irq; - /* Controller state */ - QEMUTimer *result_timer; - int dma_chann; - uint8_t phase; - IsaDma *dma; - /* Controller's identification */ - uint8_t version; - /* HW */ - uint8_t sra; - uint8_t srb; - uint8_t dor; - uint8_t dor_vmstate; /* only used as temp during vmstate */ - uint8_t tdr; - uint8_t dsr; - uint8_t msr; - uint8_t cur_drv; - uint8_t status0; - uint8_t status1; - uint8_t status2; - /* Command FIFO */ - uint8_t *fifo; - int32_t fifo_size; - uint32_t data_pos; - uint32_t data_len; - uint8_t data_state; - uint8_t data_dir; - uint8_t eot; /* last wanted sector */ - /* States kept only to be returned back */ - /* precompensation */ - uint8_t precomp_trk; - uint8_t config; - uint8_t lock; - /* Power down config (also with status regB access mode */ - uint8_t pwrd; - /* Floppy drives */ - uint8_t num_floppies; - FDrive drives[MAX_FD]; - int reset_sensei; - uint32_t check_media_rate; - FloppyDriveType fallback; /* type=auto failure fallback */ - /* Timers state */ - uint8_t timer0; - uint8_t timer1; -}; - -static FloppyDriveType get_fallback_drive_type(FDrive *drv) -{ - return drv->fdctrl->fallback; -} - -#define TYPE_SYSBUS_FDC "base-sysbus-fdc" -#define SYSBUS_FDC(obj) OBJECT_CHECK(FDCtrlSysBus, (obj), TYPE_SYSBUS_FDC) - -typedef struct FDCtrlSysBus { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - struct FDCtrl state; -} FDCtrlSysBus; - -#define ISA_FDC(obj) OBJECT_CHECK(FDCtrlISABus, (obj), TYPE_ISA_FDC) - -typedef struct FDCtrlISABus { - ISADevice parent_obj; - - uint32_t iobase; - uint32_t irq; - uint32_t dma; - struct FDCtrl state; - int32_t bootindexA; - int32_t bootindexB; -} FDCtrlISABus; - -static uint32_t fdctrl_read (void *opaque, uint32_t reg) -{ - FDCtrl *fdctrl = opaque; - uint32_t retval; - - reg &= 7; - switch (reg) { - case FD_REG_SRA: - retval = fdctrl_read_statusA(fdctrl); - break; - case FD_REG_SRB: - retval = fdctrl_read_statusB(fdctrl); - break; - case FD_REG_DOR: - retval = fdctrl_read_dor(fdctrl); - break; - case FD_REG_TDR: - retval = fdctrl_read_tape(fdctrl); - break; - case FD_REG_MSR: - retval = fdctrl_read_main_status(fdctrl); - break; - case FD_REG_FIFO: - retval = fdctrl_read_data(fdctrl); - break; - case FD_REG_DIR: - retval = fdctrl_read_dir(fdctrl); - break; - default: - retval = (uint32_t)(-1); - break; - } - FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval); - - return retval; -} - -static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value) -{ - FDCtrl *fdctrl = opaque; - - FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value); - - reg &= 7; - switch (reg) { - case FD_REG_DOR: - fdctrl_write_dor(fdctrl, value); - break; - case FD_REG_TDR: - fdctrl_write_tape(fdctrl, value); - break; - case FD_REG_DSR: - fdctrl_write_rate(fdctrl, value); - break; - case FD_REG_FIFO: - fdctrl_write_data(fdctrl, value); - break; - case FD_REG_CCR: - fdctrl_write_ccr(fdctrl, value); - break; - default: - break; - } -} - -static uint64_t fdctrl_read_mem (void *opaque, hwaddr reg, - unsigned ize) -{ - return fdctrl_read(opaque, (uint32_t)reg); -} - -static void fdctrl_write_mem (void *opaque, hwaddr reg, - uint64_t value, unsigned size) -{ - fdctrl_write(opaque, (uint32_t)reg, value); -} - -static const MemoryRegionOps fdctrl_mem_ops = { - .read = fdctrl_read_mem, - .write = fdctrl_write_mem, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const MemoryRegionOps fdctrl_mem_strict_ops = { - .read = fdctrl_read_mem, - .write = fdctrl_write_mem, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static bool fdrive_media_changed_needed(void *opaque) -{ - FDrive *drive = opaque; - - return (drive->blk != NULL && drive->media_changed != 1); -} - -static const VMStateDescription vmstate_fdrive_media_changed = { - .name = "fdrive/media_changed", - .version_id = 1, - .minimum_version_id = 1, - .needed = fdrive_media_changed_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(media_changed, FDrive), - VMSTATE_END_OF_LIST() - } -}; - -static bool fdrive_media_rate_needed(void *opaque) -{ - FDrive *drive = opaque; - - return drive->fdctrl->check_media_rate; -} - -static const VMStateDescription vmstate_fdrive_media_rate = { - .name = "fdrive/media_rate", - .version_id = 1, - .minimum_version_id = 1, - .needed = fdrive_media_rate_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(media_rate, FDrive), - VMSTATE_END_OF_LIST() - } -}; - -static bool fdrive_perpendicular_needed(void *opaque) -{ - FDrive *drive = opaque; - - return drive->perpendicular != 0; -} - -static const VMStateDescription vmstate_fdrive_perpendicular = { - .name = "fdrive/perpendicular", - .version_id = 1, - .minimum_version_id = 1, - .needed = fdrive_perpendicular_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(perpendicular, FDrive), - VMSTATE_END_OF_LIST() - } -}; - -static int fdrive_post_load(void *opaque, int version_id) -{ - fd_revalidate(opaque); - return 0; -} - -static const VMStateDescription vmstate_fdrive = { - .name = "fdrive", - .version_id = 1, - .minimum_version_id = 1, - .post_load = fdrive_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(head, FDrive), - VMSTATE_UINT8(track, FDrive), - VMSTATE_UINT8(sect, FDrive), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_fdrive_media_changed, - &vmstate_fdrive_media_rate, - &vmstate_fdrive_perpendicular, - NULL - } -}; - -/* - * Reconstructs the phase from register values according to the logic that was - * implemented in qemu 2.3. This is the default value that is used if the phase - * subsection is not present on migration. - * - * Don't change this function to reflect newer qemu versions, it is part of - * the migration ABI. - */ -static int reconstruct_phase(FDCtrl *fdctrl) -{ - if (fdctrl->msr & FD_MSR_NONDMA) { - return FD_PHASE_EXECUTION; - } else if ((fdctrl->msr & FD_MSR_RQM) == 0) { - /* qemu 2.3 disabled RQM only during DMA transfers */ - return FD_PHASE_EXECUTION; - } else if (fdctrl->msr & FD_MSR_DIO) { - return FD_PHASE_RESULT; - } else { - return FD_PHASE_COMMAND; - } -} - -static void fdc_pre_save(void *opaque) -{ - FDCtrl *s = opaque; - - s->dor_vmstate = s->dor | GET_CUR_DRV(s); -} - -static int fdc_pre_load(void *opaque) -{ - FDCtrl *s = opaque; - s->phase = FD_PHASE_RECONSTRUCT; - return 0; -} - -static int fdc_post_load(void *opaque, int version_id) -{ - FDCtrl *s = opaque; - - SET_CUR_DRV(s, s->dor_vmstate & FD_DOR_SELMASK); - s->dor = s->dor_vmstate & ~FD_DOR_SELMASK; - - if (s->phase == FD_PHASE_RECONSTRUCT) { - s->phase = reconstruct_phase(s); - } - - return 0; -} - -static bool fdc_reset_sensei_needed(void *opaque) -{ - FDCtrl *s = opaque; - - return s->reset_sensei != 0; -} - -static const VMStateDescription vmstate_fdc_reset_sensei = { - .name = "fdc/reset_sensei", - .version_id = 1, - .minimum_version_id = 1, - .needed = fdc_reset_sensei_needed, - .fields = (VMStateField[]) { - VMSTATE_INT32(reset_sensei, FDCtrl), - VMSTATE_END_OF_LIST() - } -}; - -static bool fdc_result_timer_needed(void *opaque) -{ - FDCtrl *s = opaque; - - return timer_pending(s->result_timer); -} - -static const VMStateDescription vmstate_fdc_result_timer = { - .name = "fdc/result_timer", - .version_id = 1, - .minimum_version_id = 1, - .needed = fdc_result_timer_needed, - .fields = (VMStateField[]) { - VMSTATE_TIMER_PTR(result_timer, FDCtrl), - VMSTATE_END_OF_LIST() - } -}; - -static bool fdc_phase_needed(void *opaque) -{ - FDCtrl *fdctrl = opaque; - - return reconstruct_phase(fdctrl) != fdctrl->phase; -} - -static const VMStateDescription vmstate_fdc_phase = { - .name = "fdc/phase", - .version_id = 1, - .minimum_version_id = 1, - .needed = fdc_phase_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(phase, FDCtrl), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_fdc = { - .name = "fdc", - .version_id = 2, - .minimum_version_id = 2, - .pre_save = fdc_pre_save, - .pre_load = fdc_pre_load, - .post_load = fdc_post_load, - .fields = (VMStateField[]) { - /* Controller State */ - VMSTATE_UINT8(sra, FDCtrl), - VMSTATE_UINT8(srb, FDCtrl), - VMSTATE_UINT8(dor_vmstate, FDCtrl), - VMSTATE_UINT8(tdr, FDCtrl), - VMSTATE_UINT8(dsr, FDCtrl), - VMSTATE_UINT8(msr, FDCtrl), - VMSTATE_UINT8(status0, FDCtrl), - VMSTATE_UINT8(status1, FDCtrl), - VMSTATE_UINT8(status2, FDCtrl), - /* Command FIFO */ - VMSTATE_VARRAY_INT32(fifo, FDCtrl, fifo_size, 0, vmstate_info_uint8, - uint8_t), - VMSTATE_UINT32(data_pos, FDCtrl), - VMSTATE_UINT32(data_len, FDCtrl), - VMSTATE_UINT8(data_state, FDCtrl), - VMSTATE_UINT8(data_dir, FDCtrl), - VMSTATE_UINT8(eot, FDCtrl), - /* States kept only to be returned back */ - VMSTATE_UINT8(timer0, FDCtrl), - VMSTATE_UINT8(timer1, FDCtrl), - VMSTATE_UINT8(precomp_trk, FDCtrl), - VMSTATE_UINT8(config, FDCtrl), - VMSTATE_UINT8(lock, FDCtrl), - VMSTATE_UINT8(pwrd, FDCtrl), - VMSTATE_UINT8_EQUAL(num_floppies, FDCtrl), - VMSTATE_STRUCT_ARRAY(drives, FDCtrl, MAX_FD, 1, - vmstate_fdrive, FDrive), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_fdc_reset_sensei, - &vmstate_fdc_result_timer, - &vmstate_fdc_phase, - NULL - } -}; - -static void fdctrl_external_reset_sysbus(DeviceState *d) -{ - FDCtrlSysBus *sys = SYSBUS_FDC(d); - FDCtrl *s = &sys->state; - - fdctrl_reset(s, 0); -} - -static void fdctrl_external_reset_isa(DeviceState *d) -{ - FDCtrlISABus *isa = ISA_FDC(d); - FDCtrl *s = &isa->state; - - fdctrl_reset(s, 0); -} - -static void fdctrl_handle_tc(void *opaque, int irq, int level) -{ - //FDCtrl *s = opaque; - - if (level) { - // XXX - FLOPPY_DPRINTF("TC pulsed\n"); - } -} - -/* Change IRQ state */ -static void fdctrl_reset_irq(FDCtrl *fdctrl) -{ - fdctrl->status0 = 0; - if (!(fdctrl->sra & FD_SRA_INTPEND)) - return; - FLOPPY_DPRINTF("Reset interrupt\n"); - qemu_set_irq(fdctrl->irq, 0); - fdctrl->sra &= ~FD_SRA_INTPEND; -} - -static void fdctrl_raise_irq(FDCtrl *fdctrl) -{ - if (!(fdctrl->sra & FD_SRA_INTPEND)) { - qemu_set_irq(fdctrl->irq, 1); - fdctrl->sra |= FD_SRA_INTPEND; - } - - fdctrl->reset_sensei = 0; - FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", fdctrl->status0); -} - -/* Reset controller */ -static void fdctrl_reset(FDCtrl *fdctrl, int do_irq) -{ - int i; - - FLOPPY_DPRINTF("reset controller\n"); - fdctrl_reset_irq(fdctrl); - /* Initialise controller */ - fdctrl->sra = 0; - fdctrl->srb = 0xc0; - if (!fdctrl->drives[1].blk) { - fdctrl->sra |= FD_SRA_nDRV2; - } - fdctrl->cur_drv = 0; - fdctrl->dor = FD_DOR_nRESET; - fdctrl->dor |= (fdctrl->dma_chann != -1) ? FD_DOR_DMAEN : 0; - fdctrl->msr = FD_MSR_RQM; - fdctrl->reset_sensei = 0; - timer_del(fdctrl->result_timer); - /* FIFO state */ - fdctrl->data_pos = 0; - fdctrl->data_len = 0; - fdctrl->data_state = 0; - fdctrl->data_dir = FD_DIR_WRITE; - for (i = 0; i < MAX_FD; i++) - fd_recalibrate(&fdctrl->drives[i]); - fdctrl_to_command_phase(fdctrl); - if (do_irq) { - fdctrl->status0 |= FD_SR0_RDYCHG; - fdctrl_raise_irq(fdctrl); - fdctrl->reset_sensei = FD_RESET_SENSEI_COUNT; - } -} - -static inline FDrive *drv0(FDCtrl *fdctrl) -{ - return &fdctrl->drives[(fdctrl->tdr & FD_TDR_BOOTSEL) >> 2]; -} - -static inline FDrive *drv1(FDCtrl *fdctrl) -{ - if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (1 << 2)) - return &fdctrl->drives[1]; - else - return &fdctrl->drives[0]; -} - -#if MAX_FD == 4 -static inline FDrive *drv2(FDCtrl *fdctrl) -{ - if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (2 << 2)) - return &fdctrl->drives[2]; - else - return &fdctrl->drives[1]; -} - -static inline FDrive *drv3(FDCtrl *fdctrl) -{ - if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (3 << 2)) - return &fdctrl->drives[3]; - else - return &fdctrl->drives[2]; -} -#endif - -static FDrive *get_cur_drv(FDCtrl *fdctrl) -{ - switch (fdctrl->cur_drv) { - case 0: return drv0(fdctrl); - case 1: return drv1(fdctrl); -#if MAX_FD == 4 - case 2: return drv2(fdctrl); - case 3: return drv3(fdctrl); -#endif - default: return NULL; - } -} - -/* Status A register : 0x00 (read-only) */ -static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl) -{ - uint32_t retval = fdctrl->sra; - - FLOPPY_DPRINTF("status register A: 0x%02x\n", retval); - - return retval; -} - -/* Status B register : 0x01 (read-only) */ -static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl) -{ - uint32_t retval = fdctrl->srb; - - FLOPPY_DPRINTF("status register B: 0x%02x\n", retval); - - return retval; -} - -/* Digital output register : 0x02 */ -static uint32_t fdctrl_read_dor(FDCtrl *fdctrl) -{ - uint32_t retval = fdctrl->dor; - - /* Selected drive */ - retval |= fdctrl->cur_drv; - FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval); - - return retval; -} - -static void fdctrl_write_dor(FDCtrl *fdctrl, uint32_t value) -{ - FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value); - - /* Motors */ - if (value & FD_DOR_MOTEN0) - fdctrl->srb |= FD_SRB_MTR0; - else - fdctrl->srb &= ~FD_SRB_MTR0; - if (value & FD_DOR_MOTEN1) - fdctrl->srb |= FD_SRB_MTR1; - else - fdctrl->srb &= ~FD_SRB_MTR1; - - /* Drive */ - if (value & 1) - fdctrl->srb |= FD_SRB_DR0; - else - fdctrl->srb &= ~FD_SRB_DR0; - - /* Reset */ - if (!(value & FD_DOR_nRESET)) { - if (fdctrl->dor & FD_DOR_nRESET) { - FLOPPY_DPRINTF("controller enter RESET state\n"); - } - } else { - if (!(fdctrl->dor & FD_DOR_nRESET)) { - FLOPPY_DPRINTF("controller out of RESET state\n"); - fdctrl_reset(fdctrl, 1); - fdctrl->dsr &= ~FD_DSR_PWRDOWN; - } - } - /* Selected drive */ - fdctrl->cur_drv = value & FD_DOR_SELMASK; - - fdctrl->dor = value; -} - -/* Tape drive register : 0x03 */ -static uint32_t fdctrl_read_tape(FDCtrl *fdctrl) -{ - uint32_t retval = fdctrl->tdr; - - FLOPPY_DPRINTF("tape drive register: 0x%02x\n", retval); - - return retval; -} - -static void fdctrl_write_tape(FDCtrl *fdctrl, uint32_t value) -{ - /* Reset mode */ - if (!(fdctrl->dor & FD_DOR_nRESET)) { - FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); - return; - } - FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value); - /* Disk boot selection indicator */ - fdctrl->tdr = value & FD_TDR_BOOTSEL; - /* Tape indicators: never allow */ -} - -/* Main status register : 0x04 (read) */ -static uint32_t fdctrl_read_main_status(FDCtrl *fdctrl) -{ - uint32_t retval = fdctrl->msr; - - fdctrl->dsr &= ~FD_DSR_PWRDOWN; - fdctrl->dor |= FD_DOR_nRESET; - - FLOPPY_DPRINTF("main status register: 0x%02x\n", retval); - - return retval; -} - -/* Data select rate register : 0x04 (write) */ -static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value) -{ - /* Reset mode */ - if (!(fdctrl->dor & FD_DOR_nRESET)) { - FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); - return; - } - FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value); - /* Reset: autoclear */ - if (value & FD_DSR_SWRESET) { - fdctrl->dor &= ~FD_DOR_nRESET; - fdctrl_reset(fdctrl, 1); - fdctrl->dor |= FD_DOR_nRESET; - } - if (value & FD_DSR_PWRDOWN) { - fdctrl_reset(fdctrl, 1); - } - fdctrl->dsr = value; -} - -/* Configuration control register: 0x07 (write) */ -static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value) -{ - /* Reset mode */ - if (!(fdctrl->dor & FD_DOR_nRESET)) { - FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); - return; - } - FLOPPY_DPRINTF("configuration control register set to 0x%02x\n", value); - - /* Only the rate selection bits used in AT mode, and we - * store those in the DSR. - */ - fdctrl->dsr = (fdctrl->dsr & ~FD_DSR_DRATEMASK) | - (value & FD_DSR_DRATEMASK); -} - -static int fdctrl_media_changed(FDrive *drv) -{ - return drv->media_changed; -} - -/* Digital input register : 0x07 (read-only) */ -static uint32_t fdctrl_read_dir(FDCtrl *fdctrl) -{ - uint32_t retval = 0; - - if (fdctrl_media_changed(get_cur_drv(fdctrl))) { - retval |= FD_DIR_DSKCHG; - } - if (retval != 0) { - FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval); - } - - return retval; -} - -/* Clear the FIFO and update the state for receiving the next command */ -static void fdctrl_to_command_phase(FDCtrl *fdctrl) -{ - fdctrl->phase = FD_PHASE_COMMAND; - fdctrl->data_dir = FD_DIR_WRITE; - fdctrl->data_pos = 0; - fdctrl->data_len = 1; /* Accept command byte, adjust for params later */ - fdctrl->msr &= ~(FD_MSR_CMDBUSY | FD_MSR_DIO); - fdctrl->msr |= FD_MSR_RQM; -} - -/* Update the state to allow the guest to read out the command status. - * @fifo_len is the number of result bytes to be read out. */ -static void fdctrl_to_result_phase(FDCtrl *fdctrl, int fifo_len) -{ - fdctrl->phase = FD_PHASE_RESULT; - fdctrl->data_dir = FD_DIR_READ; - fdctrl->data_len = fifo_len; - fdctrl->data_pos = 0; - fdctrl->msr |= FD_MSR_CMDBUSY | FD_MSR_RQM | FD_MSR_DIO; -} - -/* Set an error: unimplemented/unknown command */ -static void fdctrl_unimplemented(FDCtrl *fdctrl, int direction) -{ - qemu_log_mask(LOG_UNIMP, "fdc: unimplemented command 0x%02x\n", - fdctrl->fifo[0]); - fdctrl->fifo[0] = FD_SR0_INVCMD; - fdctrl_to_result_phase(fdctrl, 1); -} - -/* Seek to next sector - * returns 0 when end of track reached (for DBL_SIDES on head 1) - * otherwise returns 1 - */ -static int fdctrl_seek_to_next_sect(FDCtrl *fdctrl, FDrive *cur_drv) -{ - FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d)\n", - cur_drv->head, cur_drv->track, cur_drv->sect, - fd_sector(cur_drv)); - /* XXX: cur_drv->sect >= cur_drv->last_sect should be an - error in fact */ - uint8_t new_head = cur_drv->head; - uint8_t new_track = cur_drv->track; - uint8_t new_sect = cur_drv->sect; - - int ret = 1; - - if (new_sect >= cur_drv->last_sect || - new_sect == fdctrl->eot) { - new_sect = 1; - if (FD_MULTI_TRACK(fdctrl->data_state)) { - if (new_head == 0 && - (cur_drv->flags & FDISK_DBL_SIDES) != 0) { - new_head = 1; - } else { - new_head = 0; - new_track++; - fdctrl->status0 |= FD_SR0_SEEK; - if ((cur_drv->flags & FDISK_DBL_SIDES) == 0) { - ret = 0; - } - } - } else { - fdctrl->status0 |= FD_SR0_SEEK; - new_track++; - ret = 0; - } - if (ret == 1) { - FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n", - new_head, new_track, new_sect, fd_sector(cur_drv)); - } - } else { - new_sect++; - } - fd_seek(cur_drv, new_head, new_track, new_sect, 1); - return ret; -} - -/* Callback for transfer end (stop or abort) */ -static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0, - uint8_t status1, uint8_t status2) -{ - FDrive *cur_drv; - cur_drv = get_cur_drv(fdctrl); - - fdctrl->status0 &= ~(FD_SR0_DS0 | FD_SR0_DS1 | FD_SR0_HEAD); - fdctrl->status0 |= GET_CUR_DRV(fdctrl); - if (cur_drv->head) { - fdctrl->status0 |= FD_SR0_HEAD; - } - fdctrl->status0 |= status0; - - FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n", - status0, status1, status2, fdctrl->status0); - fdctrl->fifo[0] = fdctrl->status0; - fdctrl->fifo[1] = status1; - fdctrl->fifo[2] = status2; - fdctrl->fifo[3] = cur_drv->track; - fdctrl->fifo[4] = cur_drv->head; - fdctrl->fifo[5] = cur_drv->sect; - fdctrl->fifo[6] = FD_SECTOR_SC; - fdctrl->data_dir = FD_DIR_READ; - if (!(fdctrl->msr & FD_MSR_NONDMA)) { - IsaDmaClass *k = ISADMA_GET_CLASS(fdctrl->dma); - k->release_DREQ(fdctrl->dma, fdctrl->dma_chann); - } - fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO; - fdctrl->msr &= ~FD_MSR_NONDMA; - - fdctrl_to_result_phase(fdctrl, 7); - fdctrl_raise_irq(fdctrl); -} - -/* Prepare a data transfer (either DMA or FIFO) */ -static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv; - uint8_t kh, kt, ks; - - SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); - cur_drv = get_cur_drv(fdctrl); - kt = fdctrl->fifo[2]; - kh = fdctrl->fifo[3]; - ks = fdctrl->fifo[4]; - FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n", - GET_CUR_DRV(fdctrl), kh, kt, ks, - fd_sector_calc(kh, kt, ks, cur_drv->last_sect, - NUM_SIDES(cur_drv))); - switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) { - case 2: - /* sect too big */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); - fdctrl->fifo[3] = kt; - fdctrl->fifo[4] = kh; - fdctrl->fifo[5] = ks; - return; - case 3: - /* track too big */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00); - fdctrl->fifo[3] = kt; - fdctrl->fifo[4] = kh; - fdctrl->fifo[5] = ks; - return; - case 4: - /* No seek enabled */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); - fdctrl->fifo[3] = kt; - fdctrl->fifo[4] = kh; - fdctrl->fifo[5] = ks; - return; - case 1: - fdctrl->status0 |= FD_SR0_SEEK; - break; - default: - break; - } - - /* Check the data rate. If the programmed data rate does not match - * the currently inserted medium, the operation has to fail. */ - if (fdctrl->check_media_rate && - (fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) { - FLOPPY_DPRINTF("data rate mismatch (fdc=%d, media=%d)\n", - fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate); - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00); - fdctrl->fifo[3] = kt; - fdctrl->fifo[4] = kh; - fdctrl->fifo[5] = ks; - return; - } - - /* Set the FIFO state */ - fdctrl->data_dir = direction; - fdctrl->data_pos = 0; - assert(fdctrl->msr & FD_MSR_CMDBUSY); - if (fdctrl->fifo[0] & 0x80) - fdctrl->data_state |= FD_STATE_MULTI; - else - fdctrl->data_state &= ~FD_STATE_MULTI; - if (fdctrl->fifo[5] == 0) { - fdctrl->data_len = fdctrl->fifo[8]; - } else { - int tmp; - fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]); - tmp = (fdctrl->fifo[6] - ks + 1); - if (fdctrl->fifo[0] & 0x80) - tmp += fdctrl->fifo[6]; - fdctrl->data_len *= tmp; - } - fdctrl->eot = fdctrl->fifo[6]; - if (fdctrl->dor & FD_DOR_DMAEN) { - IsaDmaTransferMode dma_mode; - IsaDmaClass *k = ISADMA_GET_CLASS(fdctrl->dma); - bool dma_mode_ok; - /* DMA transfer are enabled. Check if DMA channel is well programmed */ - dma_mode = k->get_transfer_mode(fdctrl->dma, fdctrl->dma_chann); - FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n", - dma_mode, direction, - (128 << fdctrl->fifo[5]) * - (cur_drv->last_sect - ks + 1), fdctrl->data_len); - switch (direction) { - case FD_DIR_SCANE: - case FD_DIR_SCANL: - case FD_DIR_SCANH: - dma_mode_ok = (dma_mode == ISADMA_TRANSFER_VERIFY); - break; - case FD_DIR_WRITE: - dma_mode_ok = (dma_mode == ISADMA_TRANSFER_WRITE); - break; - case FD_DIR_READ: - dma_mode_ok = (dma_mode == ISADMA_TRANSFER_READ); - break; - case FD_DIR_VERIFY: - dma_mode_ok = true; - break; - default: - dma_mode_ok = false; - break; - } - if (dma_mode_ok) { - /* No access is allowed until DMA transfer has completed */ - fdctrl->msr &= ~FD_MSR_RQM; - if (direction != FD_DIR_VERIFY) { - /* Now, we just have to wait for the DMA controller to - * recall us... - */ - k->hold_DREQ(fdctrl->dma, fdctrl->dma_chann); - k->schedule(fdctrl->dma); - } else { - /* Start transfer */ - fdctrl_transfer_handler(fdctrl, fdctrl->dma_chann, 0, - fdctrl->data_len); - } - return; - } else { - FLOPPY_DPRINTF("bad dma_mode=%d direction=%d\n", dma_mode, - direction); - } - } - FLOPPY_DPRINTF("start non-DMA transfer\n"); - fdctrl->msr |= FD_MSR_NONDMA | FD_MSR_RQM; - if (direction != FD_DIR_WRITE) - fdctrl->msr |= FD_MSR_DIO; - /* IO based transfer: calculate len */ - fdctrl_raise_irq(fdctrl); -} - -/* Prepare a transfer of deleted data */ -static void fdctrl_start_transfer_del(FDCtrl *fdctrl, int direction) -{ - qemu_log_mask(LOG_UNIMP, "fdctrl_start_transfer_del() unimplemented\n"); - - /* We don't handle deleted data, - * so we don't return *ANYTHING* - */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); -} - -/* handlers for DMA transfers */ -static int fdctrl_transfer_handler (void *opaque, int nchan, - int dma_pos, int dma_len) -{ - FDCtrl *fdctrl; - FDrive *cur_drv; - int len, start_pos, rel_pos; - uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00; - IsaDmaClass *k; - - fdctrl = opaque; - if (fdctrl->msr & FD_MSR_RQM) { - FLOPPY_DPRINTF("Not in DMA transfer mode !\n"); - return 0; - } - k = ISADMA_GET_CLASS(fdctrl->dma); - cur_drv = get_cur_drv(fdctrl); - if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL || - fdctrl->data_dir == FD_DIR_SCANH) - status2 = FD_SR2_SNS; - if (dma_len > fdctrl->data_len) - dma_len = fdctrl->data_len; - if (cur_drv->blk == NULL) { - if (fdctrl->data_dir == FD_DIR_WRITE) - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); - else - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); - len = 0; - goto transfer_error; - } - rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; - for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) { - len = dma_len - fdctrl->data_pos; - if (len + rel_pos > FD_SECTOR_LEN) - len = FD_SECTOR_LEN - rel_pos; - FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x " - "(%d-0x%08x 0x%08x)\n", len, dma_len, fdctrl->data_pos, - fdctrl->data_len, GET_CUR_DRV(fdctrl), cur_drv->head, - cur_drv->track, cur_drv->sect, fd_sector(cur_drv), - fd_sector(cur_drv) * FD_SECTOR_LEN); - if (fdctrl->data_dir != FD_DIR_WRITE || - len < FD_SECTOR_LEN || rel_pos != 0) { - /* READ & SCAN commands and realign to a sector for WRITE */ - if (blk_read(cur_drv->blk, fd_sector(cur_drv), - fdctrl->fifo, 1) < 0) { - FLOPPY_DPRINTF("Floppy: error getting sector %d\n", - fd_sector(cur_drv)); - /* Sure, image size is too small... */ - memset(fdctrl->fifo, 0, FD_SECTOR_LEN); - } - } - switch (fdctrl->data_dir) { - case FD_DIR_READ: - /* READ commands */ - k->write_memory(fdctrl->dma, nchan, fdctrl->fifo + rel_pos, - fdctrl->data_pos, len); - break; - case FD_DIR_WRITE: - /* WRITE commands */ - if (cur_drv->ro) { - /* Handle readonly medium early, no need to do DMA, touch the - * LED or attempt any writes. A real floppy doesn't attempt - * to write to readonly media either. */ - fdctrl_stop_transfer(fdctrl, - FD_SR0_ABNTERM | FD_SR0_SEEK, FD_SR1_NW, - 0x00); - goto transfer_error; - } - - k->read_memory(fdctrl->dma, nchan, fdctrl->fifo + rel_pos, - fdctrl->data_pos, len); - if (blk_write(cur_drv->blk, fd_sector(cur_drv), - fdctrl->fifo, 1) < 0) { - FLOPPY_DPRINTF("error writing sector %d\n", - fd_sector(cur_drv)); - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); - goto transfer_error; - } - break; - case FD_DIR_VERIFY: - /* VERIFY commands */ - break; - default: - /* SCAN commands */ - { - uint8_t tmpbuf[FD_SECTOR_LEN]; - int ret; - k->read_memory(fdctrl->dma, nchan, tmpbuf, fdctrl->data_pos, - len); - ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len); - if (ret == 0) { - status2 = FD_SR2_SEH; - goto end_transfer; - } - if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) || - (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) { - status2 = 0x00; - goto end_transfer; - } - } - break; - } - fdctrl->data_pos += len; - rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; - if (rel_pos == 0) { - /* Seek to next sector */ - if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) - break; - } - } - end_transfer: - len = fdctrl->data_pos - start_pos; - FLOPPY_DPRINTF("end transfer %d %d %d\n", - fdctrl->data_pos, len, fdctrl->data_len); - if (fdctrl->data_dir == FD_DIR_SCANE || - fdctrl->data_dir == FD_DIR_SCANL || - fdctrl->data_dir == FD_DIR_SCANH) - status2 = FD_SR2_SEH; - fdctrl->data_len -= len; - fdctrl_stop_transfer(fdctrl, status0, status1, status2); - transfer_error: - - return len; -} - -/* Data register : 0x05 */ -static uint32_t fdctrl_read_data(FDCtrl *fdctrl) -{ - FDrive *cur_drv; - uint32_t retval = 0; - uint32_t pos; - - cur_drv = get_cur_drv(fdctrl); - fdctrl->dsr &= ~FD_DSR_PWRDOWN; - if (!(fdctrl->msr & FD_MSR_RQM) || !(fdctrl->msr & FD_MSR_DIO)) { - FLOPPY_DPRINTF("error: controller not ready for reading\n"); - return 0; - } - - /* If data_len spans multiple sectors, the current position in the FIFO - * wraps around while fdctrl->data_pos is the real position in the whole - * request. */ - pos = fdctrl->data_pos; - pos %= FD_SECTOR_LEN; - - switch (fdctrl->phase) { - case FD_PHASE_EXECUTION: - assert(fdctrl->msr & FD_MSR_NONDMA); - if (pos == 0) { - if (fdctrl->data_pos != 0) - if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) { - FLOPPY_DPRINTF("error seeking to next sector %d\n", - fd_sector(cur_drv)); - return 0; - } - if (blk_read(cur_drv->blk, fd_sector(cur_drv), fdctrl->fifo, 1) - < 0) { - FLOPPY_DPRINTF("error getting sector %d\n", - fd_sector(cur_drv)); - /* Sure, image size is too small... */ - memset(fdctrl->fifo, 0, FD_SECTOR_LEN); - } - } - - if (++fdctrl->data_pos == fdctrl->data_len) { - fdctrl->msr &= ~FD_MSR_RQM; - fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); - } - break; - - case FD_PHASE_RESULT: - assert(!(fdctrl->msr & FD_MSR_NONDMA)); - if (++fdctrl->data_pos == fdctrl->data_len) { - fdctrl->msr &= ~FD_MSR_RQM; - fdctrl_to_command_phase(fdctrl); - fdctrl_reset_irq(fdctrl); - } - break; - - case FD_PHASE_COMMAND: - default: - abort(); - } - - retval = fdctrl->fifo[pos]; - FLOPPY_DPRINTF("data register: 0x%02x\n", retval); - - return retval; -} - -static void fdctrl_format_sector(FDCtrl *fdctrl) -{ - FDrive *cur_drv; - uint8_t kh, kt, ks; - - SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); - cur_drv = get_cur_drv(fdctrl); - kt = fdctrl->fifo[6]; - kh = fdctrl->fifo[7]; - ks = fdctrl->fifo[8]; - FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n", - GET_CUR_DRV(fdctrl), kh, kt, ks, - fd_sector_calc(kh, kt, ks, cur_drv->last_sect, - NUM_SIDES(cur_drv))); - switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) { - case 2: - /* sect too big */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); - fdctrl->fifo[3] = kt; - fdctrl->fifo[4] = kh; - fdctrl->fifo[5] = ks; - return; - case 3: - /* track too big */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00); - fdctrl->fifo[3] = kt; - fdctrl->fifo[4] = kh; - fdctrl->fifo[5] = ks; - return; - case 4: - /* No seek enabled */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); - fdctrl->fifo[3] = kt; - fdctrl->fifo[4] = kh; - fdctrl->fifo[5] = ks; - return; - case 1: - fdctrl->status0 |= FD_SR0_SEEK; - break; - default: - break; - } - memset(fdctrl->fifo, 0, FD_SECTOR_LEN); - if (cur_drv->blk == NULL || - blk_write(cur_drv->blk, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { - FLOPPY_DPRINTF("error formatting sector %d\n", fd_sector(cur_drv)); - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); - } else { - if (cur_drv->sect == cur_drv->last_sect) { - fdctrl->data_state &= ~FD_STATE_FORMAT; - /* Last sector done */ - fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); - } else { - /* More to do */ - fdctrl->data_pos = 0; - fdctrl->data_len = 4; - } - } -} - -static void fdctrl_handle_lock(FDCtrl *fdctrl, int direction) -{ - fdctrl->lock = (fdctrl->fifo[0] & 0x80) ? 1 : 0; - fdctrl->fifo[0] = fdctrl->lock << 4; - fdctrl_to_result_phase(fdctrl, 1); -} - -static void fdctrl_handle_dumpreg(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv = get_cur_drv(fdctrl); - - /* Drives position */ - fdctrl->fifo[0] = drv0(fdctrl)->track; - fdctrl->fifo[1] = drv1(fdctrl)->track; -#if MAX_FD == 4 - fdctrl->fifo[2] = drv2(fdctrl)->track; - fdctrl->fifo[3] = drv3(fdctrl)->track; -#else - fdctrl->fifo[2] = 0; - fdctrl->fifo[3] = 0; -#endif - /* timers */ - fdctrl->fifo[4] = fdctrl->timer0; - fdctrl->fifo[5] = (fdctrl->timer1 << 1) | (fdctrl->dor & FD_DOR_DMAEN ? 1 : 0); - fdctrl->fifo[6] = cur_drv->last_sect; - fdctrl->fifo[7] = (fdctrl->lock << 7) | - (cur_drv->perpendicular << 2); - fdctrl->fifo[8] = fdctrl->config; - fdctrl->fifo[9] = fdctrl->precomp_trk; - fdctrl_to_result_phase(fdctrl, 10); -} - -static void fdctrl_handle_version(FDCtrl *fdctrl, int direction) -{ - /* Controller's version */ - fdctrl->fifo[0] = fdctrl->version; - fdctrl_to_result_phase(fdctrl, 1); -} - -static void fdctrl_handle_partid(FDCtrl *fdctrl, int direction) -{ - fdctrl->fifo[0] = 0x41; /* Stepping 1 */ - fdctrl_to_result_phase(fdctrl, 1); -} - -static void fdctrl_handle_restore(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv = get_cur_drv(fdctrl); - - /* Drives position */ - drv0(fdctrl)->track = fdctrl->fifo[3]; - drv1(fdctrl)->track = fdctrl->fifo[4]; -#if MAX_FD == 4 - drv2(fdctrl)->track = fdctrl->fifo[5]; - drv3(fdctrl)->track = fdctrl->fifo[6]; -#endif - /* timers */ - fdctrl->timer0 = fdctrl->fifo[7]; - fdctrl->timer1 = fdctrl->fifo[8]; - cur_drv->last_sect = fdctrl->fifo[9]; - fdctrl->lock = fdctrl->fifo[10] >> 7; - cur_drv->perpendicular = (fdctrl->fifo[10] >> 2) & 0xF; - fdctrl->config = fdctrl->fifo[11]; - fdctrl->precomp_trk = fdctrl->fifo[12]; - fdctrl->pwrd = fdctrl->fifo[13]; - fdctrl_to_command_phase(fdctrl); -} - -static void fdctrl_handle_save(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv = get_cur_drv(fdctrl); - - fdctrl->fifo[0] = 0; - fdctrl->fifo[1] = 0; - /* Drives position */ - fdctrl->fifo[2] = drv0(fdctrl)->track; - fdctrl->fifo[3] = drv1(fdctrl)->track; -#if MAX_FD == 4 - fdctrl->fifo[4] = drv2(fdctrl)->track; - fdctrl->fifo[5] = drv3(fdctrl)->track; -#else - fdctrl->fifo[4] = 0; - fdctrl->fifo[5] = 0; -#endif - /* timers */ - fdctrl->fifo[6] = fdctrl->timer0; - fdctrl->fifo[7] = fdctrl->timer1; - fdctrl->fifo[8] = cur_drv->last_sect; - fdctrl->fifo[9] = (fdctrl->lock << 7) | - (cur_drv->perpendicular << 2); - fdctrl->fifo[10] = fdctrl->config; - fdctrl->fifo[11] = fdctrl->precomp_trk; - fdctrl->fifo[12] = fdctrl->pwrd; - fdctrl->fifo[13] = 0; - fdctrl->fifo[14] = 0; - fdctrl_to_result_phase(fdctrl, 15); -} - -static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv = get_cur_drv(fdctrl); - - cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; - timer_mod(fdctrl->result_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / 50)); -} - -static void fdctrl_handle_format_track(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv; - - SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); - cur_drv = get_cur_drv(fdctrl); - fdctrl->data_state |= FD_STATE_FORMAT; - if (fdctrl->fifo[0] & 0x80) - fdctrl->data_state |= FD_STATE_MULTI; - else - fdctrl->data_state &= ~FD_STATE_MULTI; - cur_drv->bps = - fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2]; -#if 0 - cur_drv->last_sect = - cur_drv->flags & FDISK_DBL_SIDES ? fdctrl->fifo[3] : - fdctrl->fifo[3] / 2; -#else - cur_drv->last_sect = fdctrl->fifo[3]; -#endif - /* TODO: implement format using DMA expected by the Bochs BIOS - * and Linux fdformat (read 3 bytes per sector via DMA and fill - * the sector with the specified fill byte - */ - fdctrl->data_state &= ~FD_STATE_FORMAT; - fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); -} - -static void fdctrl_handle_specify(FDCtrl *fdctrl, int direction) -{ - fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF; - fdctrl->timer1 = fdctrl->fifo[2] >> 1; - if (fdctrl->fifo[2] & 1) - fdctrl->dor &= ~FD_DOR_DMAEN; - else - fdctrl->dor |= FD_DOR_DMAEN; - /* No result back */ - fdctrl_to_command_phase(fdctrl); -} - -static void fdctrl_handle_sense_drive_status(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv; - - SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); - cur_drv = get_cur_drv(fdctrl); - cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; - /* 1 Byte status back */ - fdctrl->fifo[0] = (cur_drv->ro << 6) | - (cur_drv->track == 0 ? 0x10 : 0x00) | - (cur_drv->head << 2) | - GET_CUR_DRV(fdctrl) | - 0x28; - fdctrl_to_result_phase(fdctrl, 1); -} - -static void fdctrl_handle_recalibrate(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv; - - SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); - cur_drv = get_cur_drv(fdctrl); - fd_recalibrate(cur_drv); - fdctrl_to_command_phase(fdctrl); - /* Raise Interrupt */ - fdctrl->status0 |= FD_SR0_SEEK; - fdctrl_raise_irq(fdctrl); -} - -static void fdctrl_handle_sense_interrupt_status(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv = get_cur_drv(fdctrl); - - if (fdctrl->reset_sensei > 0) { - fdctrl->fifo[0] = - FD_SR0_RDYCHG + FD_RESET_SENSEI_COUNT - fdctrl->reset_sensei; - fdctrl->reset_sensei--; - } else if (!(fdctrl->sra & FD_SRA_INTPEND)) { - fdctrl->fifo[0] = FD_SR0_INVCMD; - fdctrl_to_result_phase(fdctrl, 1); - return; - } else { - fdctrl->fifo[0] = - (fdctrl->status0 & ~(FD_SR0_HEAD | FD_SR0_DS1 | FD_SR0_DS0)) - | GET_CUR_DRV(fdctrl); - } - - fdctrl->fifo[1] = cur_drv->track; - fdctrl_to_result_phase(fdctrl, 2); - fdctrl_reset_irq(fdctrl); - fdctrl->status0 = FD_SR0_RDYCHG; -} - -static void fdctrl_handle_seek(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv; - - SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); - cur_drv = get_cur_drv(fdctrl); - fdctrl_to_command_phase(fdctrl); - /* The seek command just sends step pulses to the drive and doesn't care if - * there is a medium inserted of if it's banging the head against the drive. - */ - fd_seek(cur_drv, cur_drv->head, fdctrl->fifo[2], cur_drv->sect, 1); - /* Raise Interrupt */ - fdctrl->status0 |= FD_SR0_SEEK; - fdctrl_raise_irq(fdctrl); -} - -static void fdctrl_handle_perpendicular_mode(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv = get_cur_drv(fdctrl); - - if (fdctrl->fifo[1] & 0x80) - cur_drv->perpendicular = fdctrl->fifo[1] & 0x7; - /* No result back */ - fdctrl_to_command_phase(fdctrl); -} - -static void fdctrl_handle_configure(FDCtrl *fdctrl, int direction) -{ - fdctrl->config = fdctrl->fifo[2]; - fdctrl->precomp_trk = fdctrl->fifo[3]; - /* No result back */ - fdctrl_to_command_phase(fdctrl); -} - -static void fdctrl_handle_powerdown_mode(FDCtrl *fdctrl, int direction) -{ - fdctrl->pwrd = fdctrl->fifo[1]; - fdctrl->fifo[0] = fdctrl->fifo[1]; - fdctrl_to_result_phase(fdctrl, 1); -} - -static void fdctrl_handle_option(FDCtrl *fdctrl, int direction) -{ - /* No result back */ - fdctrl_to_command_phase(fdctrl); -} - -static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv = get_cur_drv(fdctrl); - uint32_t pos; - - pos = fdctrl->data_pos - 1; - pos %= FD_SECTOR_LEN; - if (fdctrl->fifo[pos] & 0x80) { - /* Command parameters done */ - if (fdctrl->fifo[pos] & 0x40) { - fdctrl->fifo[0] = fdctrl->fifo[1]; - fdctrl->fifo[2] = 0; - fdctrl->fifo[3] = 0; - fdctrl_to_result_phase(fdctrl, 4); - } else { - fdctrl_to_command_phase(fdctrl); - } - } else if (fdctrl->data_len > 7) { - /* ERROR */ - fdctrl->fifo[0] = 0x80 | - (cur_drv->head << 2) | GET_CUR_DRV(fdctrl); - fdctrl_to_result_phase(fdctrl, 1); - } -} - -static void fdctrl_handle_relative_seek_in(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv; - - SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); - cur_drv = get_cur_drv(fdctrl); - if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) { - fd_seek(cur_drv, cur_drv->head, cur_drv->max_track - 1, - cur_drv->sect, 1); - } else { - fd_seek(cur_drv, cur_drv->head, - cur_drv->track + fdctrl->fifo[2], cur_drv->sect, 1); - } - fdctrl_to_command_phase(fdctrl); - /* Raise Interrupt */ - fdctrl->status0 |= FD_SR0_SEEK; - fdctrl_raise_irq(fdctrl); -} - -static void fdctrl_handle_relative_seek_out(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv; - - SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); - cur_drv = get_cur_drv(fdctrl); - if (fdctrl->fifo[2] > cur_drv->track) { - fd_seek(cur_drv, cur_drv->head, 0, cur_drv->sect, 1); - } else { - fd_seek(cur_drv, cur_drv->head, - cur_drv->track - fdctrl->fifo[2], cur_drv->sect, 1); - } - fdctrl_to_command_phase(fdctrl); - /* Raise Interrupt */ - fdctrl->status0 |= FD_SR0_SEEK; - fdctrl_raise_irq(fdctrl); -} - -/* - * Handlers for the execution phase of each command - */ -typedef struct FDCtrlCommand { - uint8_t value; - uint8_t mask; - const char* name; - int parameters; - void (*handler)(FDCtrl *fdctrl, int direction); - int direction; -} FDCtrlCommand; - -static const FDCtrlCommand handlers[] = { - { FD_CMD_READ, 0x1f, "READ", 8, fdctrl_start_transfer, FD_DIR_READ }, - { FD_CMD_WRITE, 0x3f, "WRITE", 8, fdctrl_start_transfer, FD_DIR_WRITE }, - { FD_CMD_SEEK, 0xff, "SEEK", 2, fdctrl_handle_seek }, - { FD_CMD_SENSE_INTERRUPT_STATUS, 0xff, "SENSE INTERRUPT STATUS", 0, fdctrl_handle_sense_interrupt_status }, - { FD_CMD_RECALIBRATE, 0xff, "RECALIBRATE", 1, fdctrl_handle_recalibrate }, - { FD_CMD_FORMAT_TRACK, 0xbf, "FORMAT TRACK", 5, fdctrl_handle_format_track }, - { FD_CMD_READ_TRACK, 0xbf, "READ TRACK", 8, fdctrl_start_transfer, FD_DIR_READ }, - { FD_CMD_RESTORE, 0xff, "RESTORE", 17, fdctrl_handle_restore }, /* part of READ DELETED DATA */ - { FD_CMD_SAVE, 0xff, "SAVE", 0, fdctrl_handle_save }, /* part of READ DELETED DATA */ - { FD_CMD_READ_DELETED, 0x1f, "READ DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_READ }, - { FD_CMD_SCAN_EQUAL, 0x1f, "SCAN EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANE }, - { FD_CMD_VERIFY, 0x1f, "VERIFY", 8, fdctrl_start_transfer, FD_DIR_VERIFY }, - { FD_CMD_SCAN_LOW_OR_EQUAL, 0x1f, "SCAN LOW OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANL }, - { FD_CMD_SCAN_HIGH_OR_EQUAL, 0x1f, "SCAN HIGH OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANH }, - { FD_CMD_WRITE_DELETED, 0x3f, "WRITE DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_WRITE }, - { FD_CMD_READ_ID, 0xbf, "READ ID", 1, fdctrl_handle_readid }, - { FD_CMD_SPECIFY, 0xff, "SPECIFY", 2, fdctrl_handle_specify }, - { FD_CMD_SENSE_DRIVE_STATUS, 0xff, "SENSE DRIVE STATUS", 1, fdctrl_handle_sense_drive_status }, - { FD_CMD_PERPENDICULAR_MODE, 0xff, "PERPENDICULAR MODE", 1, fdctrl_handle_perpendicular_mode }, - { FD_CMD_CONFIGURE, 0xff, "CONFIGURE", 3, fdctrl_handle_configure }, - { FD_CMD_POWERDOWN_MODE, 0xff, "POWERDOWN MODE", 2, fdctrl_handle_powerdown_mode }, - { FD_CMD_OPTION, 0xff, "OPTION", 1, fdctrl_handle_option }, - { FD_CMD_DRIVE_SPECIFICATION_COMMAND, 0xff, "DRIVE SPECIFICATION COMMAND", 5, fdctrl_handle_drive_specification_command }, - { FD_CMD_RELATIVE_SEEK_OUT, 0xff, "RELATIVE SEEK OUT", 2, fdctrl_handle_relative_seek_out }, - { FD_CMD_FORMAT_AND_WRITE, 0xff, "FORMAT AND WRITE", 10, fdctrl_unimplemented }, - { FD_CMD_RELATIVE_SEEK_IN, 0xff, "RELATIVE SEEK IN", 2, fdctrl_handle_relative_seek_in }, - { FD_CMD_LOCK, 0x7f, "LOCK", 0, fdctrl_handle_lock }, - { FD_CMD_DUMPREG, 0xff, "DUMPREG", 0, fdctrl_handle_dumpreg }, - { FD_CMD_VERSION, 0xff, "VERSION", 0, fdctrl_handle_version }, - { FD_CMD_PART_ID, 0xff, "PART ID", 0, fdctrl_handle_partid }, - { FD_CMD_WRITE, 0x1f, "WRITE (BeOS)", 8, fdctrl_start_transfer, FD_DIR_WRITE }, /* not in specification ; BeOS 4.5 bug */ - { 0, 0, "unknown", 0, fdctrl_unimplemented }, /* default handler */ -}; -/* Associate command to an index in the 'handlers' array */ -static uint8_t command_to_handler[256]; - -static const FDCtrlCommand *get_command(uint8_t cmd) -{ - int idx; - - idx = command_to_handler[cmd]; - FLOPPY_DPRINTF("%s command\n", handlers[idx].name); - return &handlers[idx]; -} - -static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value) -{ - FDrive *cur_drv; - const FDCtrlCommand *cmd; - uint32_t pos; - - /* Reset mode */ - if (!(fdctrl->dor & FD_DOR_nRESET)) { - FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); - return; - } - if (!(fdctrl->msr & FD_MSR_RQM) || (fdctrl->msr & FD_MSR_DIO)) { - FLOPPY_DPRINTF("error: controller not ready for writing\n"); - return; - } - fdctrl->dsr &= ~FD_DSR_PWRDOWN; - - FLOPPY_DPRINTF("%s: %02x\n", __func__, value); - - /* If data_len spans multiple sectors, the current position in the FIFO - * wraps around while fdctrl->data_pos is the real position in the whole - * request. */ - pos = fdctrl->data_pos++; - pos %= FD_SECTOR_LEN; - fdctrl->fifo[pos] = value; - - if (fdctrl->data_pos == fdctrl->data_len) { - fdctrl->msr &= ~FD_MSR_RQM; - } - - switch (fdctrl->phase) { - case FD_PHASE_EXECUTION: - /* For DMA requests, RQM should be cleared during execution phase, so - * we would have errored out above. */ - assert(fdctrl->msr & FD_MSR_NONDMA); - - /* FIFO data write */ - if (pos == FD_SECTOR_LEN - 1 || - fdctrl->data_pos == fdctrl->data_len) { - cur_drv = get_cur_drv(fdctrl); - if (blk_write(cur_drv->blk, fd_sector(cur_drv), fdctrl->fifo, 1) - < 0) { - FLOPPY_DPRINTF("error writing sector %d\n", - fd_sector(cur_drv)); - break; - } - if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) { - FLOPPY_DPRINTF("error seeking to next sector %d\n", - fd_sector(cur_drv)); - break; - } - } - - /* Switch to result phase when done with the transfer */ - if (fdctrl->data_pos == fdctrl->data_len) { - fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); - } - break; - - case FD_PHASE_COMMAND: - assert(!(fdctrl->msr & FD_MSR_NONDMA)); - assert(fdctrl->data_pos < FD_SECTOR_LEN); - - if (pos == 0) { - /* The first byte specifies the command. Now we start reading - * as many parameters as this command requires. */ - cmd = get_command(value); - fdctrl->data_len = cmd->parameters + 1; - if (cmd->parameters) { - fdctrl->msr |= FD_MSR_RQM; - } - fdctrl->msr |= FD_MSR_CMDBUSY; - } - - if (fdctrl->data_pos == fdctrl->data_len) { - /* We have all parameters now, execute the command */ - fdctrl->phase = FD_PHASE_EXECUTION; - - if (fdctrl->data_state & FD_STATE_FORMAT) { - fdctrl_format_sector(fdctrl); - break; - } - - cmd = get_command(fdctrl->fifo[0]); - FLOPPY_DPRINTF("Calling handler for '%s'\n", cmd->name); - cmd->handler(fdctrl, cmd->direction); - } - break; - - case FD_PHASE_RESULT: - default: - abort(); - } -} - -static void fdctrl_result_timer(void *opaque) -{ - FDCtrl *fdctrl = opaque; - FDrive *cur_drv = get_cur_drv(fdctrl); - - /* Pretend we are spinning. - * This is needed for Coherent, which uses READ ID to check for - * sector interleaving. - */ - if (cur_drv->last_sect != 0) { - cur_drv->sect = (cur_drv->sect % cur_drv->last_sect) + 1; - } - /* READ_ID can't automatically succeed! */ - if (fdctrl->check_media_rate && - (fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) { - FLOPPY_DPRINTF("read id rate mismatch (fdc=%d, media=%d)\n", - fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate); - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00); - } else { - fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); - } -} - -static void fdctrl_change_cb(void *opaque, bool load) -{ - FDrive *drive = opaque; - - drive->media_changed = 1; - drive->media_validated = false; - fd_revalidate(drive); -} - -static const BlockDevOps fdctrl_block_ops = { - .change_media_cb = fdctrl_change_cb, -}; - -/* Init functions */ -static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp) -{ - unsigned int i; - FDrive *drive; - - for (i = 0; i < MAX_FD; i++) { - drive = &fdctrl->drives[i]; - drive->fdctrl = fdctrl; - - if (drive->blk) { - if (blk_get_on_error(drive->blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC) { - error_setg(errp, "fdc doesn't support drive option werror"); - return; - } - if (blk_get_on_error(drive->blk, 1) != BLOCKDEV_ON_ERROR_REPORT) { - error_setg(errp, "fdc doesn't support drive option rerror"); - return; - } - } - - fd_init(drive); - if (drive->blk) { - blk_set_dev_ops(drive->blk, &fdctrl_block_ops, drive); - pick_drive_type(drive); - } - fd_revalidate(drive); - } -} - -ISADevice *fdctrl_init_isa(ISABus *bus, DriveInfo **fds) -{ - DeviceState *dev; - ISADevice *isadev; - - isadev = isa_try_create(bus, TYPE_ISA_FDC); - if (!isadev) { - return NULL; - } - dev = DEVICE(isadev); - - if (fds[0]) { - qdev_prop_set_drive(dev, "driveA", blk_by_legacy_dinfo(fds[0]), - &error_fatal); - } - if (fds[1]) { - qdev_prop_set_drive(dev, "driveB", blk_by_legacy_dinfo(fds[1]), - &error_fatal); - } - qdev_init_nofail(dev); - - return isadev; -} - -void fdctrl_init_sysbus(qemu_irq irq, int dma_chann, - hwaddr mmio_base, DriveInfo **fds) -{ - FDCtrl *fdctrl; - DeviceState *dev; - SysBusDevice *sbd; - FDCtrlSysBus *sys; - - dev = qdev_create(NULL, "sysbus-fdc"); - sys = SYSBUS_FDC(dev); - fdctrl = &sys->state; - fdctrl->dma_chann = dma_chann; /* FIXME */ - if (fds[0]) { - qdev_prop_set_drive(dev, "driveA", blk_by_legacy_dinfo(fds[0]), - &error_fatal); - } - if (fds[1]) { - qdev_prop_set_drive(dev, "driveB", blk_by_legacy_dinfo(fds[1]), - &error_fatal); - } - qdev_init_nofail(dev); - sbd = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(sbd, 0, irq); - sysbus_mmio_map(sbd, 0, mmio_base); -} - -void sun4m_fdctrl_init(qemu_irq irq, hwaddr io_base, - DriveInfo **fds, qemu_irq *fdc_tc) -{ - DeviceState *dev; - FDCtrlSysBus *sys; - - dev = qdev_create(NULL, "SUNW,fdtwo"); - if (fds[0]) { - qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(fds[0]), - &error_fatal); - } - qdev_init_nofail(dev); - sys = SYSBUS_FDC(dev); - sysbus_connect_irq(SYS_BUS_DEVICE(sys), 0, irq); - sysbus_mmio_map(SYS_BUS_DEVICE(sys), 0, io_base); - *fdc_tc = qdev_get_gpio_in(dev, 0); -} - -static void fdctrl_realize_common(FDCtrl *fdctrl, Error **errp) -{ - int i, j; - static int command_tables_inited = 0; - - if (fdctrl->fallback == FLOPPY_DRIVE_TYPE_AUTO) { - error_setg(errp, "Cannot choose a fallback FDrive type of 'auto'"); - } - - /* Fill 'command_to_handler' lookup table */ - if (!command_tables_inited) { - command_tables_inited = 1; - for (i = ARRAY_SIZE(handlers) - 1; i >= 0; i--) { - for (j = 0; j < sizeof(command_to_handler); j++) { - if ((j & handlers[i].mask) == handlers[i].value) { - command_to_handler[j] = i; - } - } - } - } - - FLOPPY_DPRINTF("init controller\n"); - fdctrl->fifo = qemu_memalign(512, FD_SECTOR_LEN); - fdctrl->fifo_size = 512; - fdctrl->result_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - fdctrl_result_timer, fdctrl); - - fdctrl->version = 0x90; /* Intel 82078 controller */ - fdctrl->config = FD_CONFIG_EIS | FD_CONFIG_EFIFO; /* Implicit seek, polling & FIFO enabled */ - fdctrl->num_floppies = MAX_FD; - - if (fdctrl->dma_chann != -1) { - IsaDmaClass *k; - assert(fdctrl->dma); - k = ISADMA_GET_CLASS(fdctrl->dma); - k->register_channel(fdctrl->dma, fdctrl->dma_chann, - &fdctrl_transfer_handler, fdctrl); - } - fdctrl_connect_drives(fdctrl, errp); -} - -static const MemoryRegionPortio fdc_portio_list[] = { - { 1, 5, 1, .read = fdctrl_read, .write = fdctrl_write }, - { 7, 1, 1, .read = fdctrl_read, .write = fdctrl_write }, - PORTIO_END_OF_LIST(), -}; - -static void isabus_fdc_realize(DeviceState *dev, Error **errp) -{ - ISADevice *isadev = ISA_DEVICE(dev); - FDCtrlISABus *isa = ISA_FDC(dev); - FDCtrl *fdctrl = &isa->state; - Error *err = NULL; - - isa_register_portio_list(isadev, isa->iobase, fdc_portio_list, fdctrl, - "fdc"); - - isa_init_irq(isadev, &fdctrl->irq, isa->irq); - fdctrl->dma_chann = isa->dma; - if (fdctrl->dma_chann != -1) { - fdctrl->dma = isa_get_dma(isa_bus_from_device(isadev), isa->dma); - assert(fdctrl->dma); - } - - qdev_set_legacy_instance_id(dev, isa->iobase, 2); - fdctrl_realize_common(fdctrl, &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } -} - -static void sysbus_fdc_initfn(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - FDCtrlSysBus *sys = SYSBUS_FDC(obj); - FDCtrl *fdctrl = &sys->state; - - fdctrl->dma_chann = -1; - - memory_region_init_io(&fdctrl->iomem, obj, &fdctrl_mem_ops, fdctrl, - "fdc", 0x08); - sysbus_init_mmio(sbd, &fdctrl->iomem); -} - -static void sun4m_fdc_initfn(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - FDCtrlSysBus *sys = SYSBUS_FDC(obj); - FDCtrl *fdctrl = &sys->state; - - fdctrl->dma_chann = -1; - - memory_region_init_io(&fdctrl->iomem, obj, &fdctrl_mem_strict_ops, - fdctrl, "fdctrl", 0x08); - sysbus_init_mmio(sbd, &fdctrl->iomem); -} - -static void sysbus_fdc_common_initfn(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - FDCtrlSysBus *sys = SYSBUS_FDC(obj); - FDCtrl *fdctrl = &sys->state; - - qdev_set_legacy_instance_id(dev, 0 /* io */, 2); /* FIXME */ - - sysbus_init_irq(sbd, &fdctrl->irq); - qdev_init_gpio_in(dev, fdctrl_handle_tc, 1); -} - -static void sysbus_fdc_common_realize(DeviceState *dev, Error **errp) -{ - FDCtrlSysBus *sys = SYSBUS_FDC(dev); - FDCtrl *fdctrl = &sys->state; - - fdctrl_realize_common(fdctrl, errp); -} - -FloppyDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i) -{ - FDCtrlISABus *isa = ISA_FDC(fdc); - - return isa->state.drives[i].drive; -} - -void isa_fdc_get_drive_max_chs(FloppyDriveType type, - uint8_t *maxc, uint8_t *maxh, uint8_t *maxs) -{ - const FDFormat *fdf; - - *maxc = *maxh = *maxs = 0; - for (fdf = fd_formats; fdf->drive != FLOPPY_DRIVE_TYPE_NONE; fdf++) { - if (fdf->drive != type) { - continue; - } - if (*maxc < fdf->max_track) { - *maxc = fdf->max_track; - } - if (*maxh < fdf->max_head) { - *maxh = fdf->max_head; - } - if (*maxs < fdf->last_sect) { - *maxs = fdf->last_sect; - } - } - (*maxc)--; -} - -static const VMStateDescription vmstate_isa_fdc ={ - .name = "fdc", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(state, FDCtrlISABus, 0, vmstate_fdc, FDCtrl), - VMSTATE_END_OF_LIST() - } -}; - -static Property isa_fdc_properties[] = { - DEFINE_PROP_UINT32("iobase", FDCtrlISABus, iobase, 0x3f0), - DEFINE_PROP_UINT32("irq", FDCtrlISABus, irq, 6), - DEFINE_PROP_UINT32("dma", FDCtrlISABus, dma, 2), - DEFINE_PROP_DRIVE("driveA", FDCtrlISABus, state.drives[0].blk), - DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.drives[1].blk), - DEFINE_PROP_BIT("check_media_rate", FDCtrlISABus, state.check_media_rate, - 0, true), - DEFINE_PROP_DEFAULT("fdtypeA", FDCtrlISABus, state.drives[0].drive, - FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, - FloppyDriveType), - DEFINE_PROP_DEFAULT("fdtypeB", FDCtrlISABus, state.drives[1].drive, - FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, - FloppyDriveType), - DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback, - FLOPPY_DRIVE_TYPE_288, qdev_prop_fdc_drive_type, - FloppyDriveType), - DEFINE_PROP_END_OF_LIST(), -}; - -static void isabus_fdc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = isabus_fdc_realize; - dc->fw_name = "fdc"; - dc->reset = fdctrl_external_reset_isa; - dc->vmsd = &vmstate_isa_fdc; - dc->props = isa_fdc_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static void isabus_fdc_instance_init(Object *obj) -{ - FDCtrlISABus *isa = ISA_FDC(obj); - - device_add_bootindex_property(obj, &isa->bootindexA, - "bootindexA", "/floppy@0", - DEVICE(obj), NULL); - device_add_bootindex_property(obj, &isa->bootindexB, - "bootindexB", "/floppy@1", - DEVICE(obj), NULL); -} - -static const TypeInfo isa_fdc_info = { - .name = TYPE_ISA_FDC, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(FDCtrlISABus), - .class_init = isabus_fdc_class_init, - .instance_init = isabus_fdc_instance_init, -}; - -static const VMStateDescription vmstate_sysbus_fdc ={ - .name = "fdc", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(state, FDCtrlSysBus, 0, vmstate_fdc, FDCtrl), - VMSTATE_END_OF_LIST() - } -}; - -static Property sysbus_fdc_properties[] = { - DEFINE_PROP_DRIVE("driveA", FDCtrlSysBus, state.drives[0].blk), - DEFINE_PROP_DRIVE("driveB", FDCtrlSysBus, state.drives[1].blk), - DEFINE_PROP_DEFAULT("fdtypeA", FDCtrlSysBus, state.drives[0].drive, - FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, - FloppyDriveType), - DEFINE_PROP_DEFAULT("fdtypeB", FDCtrlSysBus, state.drives[1].drive, - FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, - FloppyDriveType), - DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback, - FLOPPY_DRIVE_TYPE_144, qdev_prop_fdc_drive_type, - FloppyDriveType), - DEFINE_PROP_END_OF_LIST(), -}; - -static void sysbus_fdc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->props = sysbus_fdc_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo sysbus_fdc_info = { - .name = "sysbus-fdc", - .parent = TYPE_SYSBUS_FDC, - .instance_init = sysbus_fdc_initfn, - .class_init = sysbus_fdc_class_init, -}; - -static Property sun4m_fdc_properties[] = { - DEFINE_PROP_DRIVE("drive", FDCtrlSysBus, state.drives[0].blk), - DEFINE_PROP_DEFAULT("fdtype", FDCtrlSysBus, state.drives[0].drive, - FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, - FloppyDriveType), - DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback, - FLOPPY_DRIVE_TYPE_144, qdev_prop_fdc_drive_type, - FloppyDriveType), - DEFINE_PROP_END_OF_LIST(), -}; - -static void sun4m_fdc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->props = sun4m_fdc_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo sun4m_fdc_info = { - .name = "SUNW,fdtwo", - .parent = TYPE_SYSBUS_FDC, - .instance_init = sun4m_fdc_initfn, - .class_init = sun4m_fdc_class_init, -}; - -static void sysbus_fdc_common_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = sysbus_fdc_common_realize; - dc->reset = fdctrl_external_reset_sysbus; - dc->vmsd = &vmstate_sysbus_fdc; -} - -static const TypeInfo sysbus_fdc_type_info = { - .name = TYPE_SYSBUS_FDC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(FDCtrlSysBus), - .instance_init = sysbus_fdc_common_initfn, - .abstract = true, - .class_init = sysbus_fdc_common_class_init, -}; - -static void fdc_register_types(void) -{ - type_register_static(&isa_fdc_info); - type_register_static(&sysbus_fdc_type_info); - type_register_static(&sysbus_fdc_info); - type_register_static(&sun4m_fdc_info); -} - -type_init(fdc_register_types) diff --git a/qemu/hw/block/hd-geometry.c b/qemu/hw/block/hd-geometry.c deleted file mode 100644 index 6d02192db..000000000 --- a/qemu/hw/block/hd-geometry.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Hard disk geometry utilities - * - * Copyright (C) 2012 Red Hat, Inc. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright (c) 2003 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "sysemu/block-backend.h" -#include "hw/block/block.h" -#include "trace.h" - -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 */ -} QEMU_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(BlockBackend *blk, - int *pcylinders, int *pheads, int *psectors) -{ - uint8_t buf[BDRV_SECTOR_SIZE]; - int i, heads, sectors, cylinders; - struct partition *p; - uint32_t nr_sects; - uint64_t nb_sectors; - - blk_get_geometry(blk, &nb_sectors); - - /** - * The function will be invoked during startup not only in sync I/O mode, - * but also in async I/O mode. So the I/O throttling function has to - * be disabled temporarily here, not permanently. - */ - if (blk_read_unthrottled(blk, 0, buf, 1) < 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 = nb_sectors / (heads * sectors); - if (cylinders < 1 || cylinders > 16383) { - continue; - } - *pheads = heads; - *psectors = sectors; - *pcylinders = cylinders; - trace_hd_geometry_lchs_guess(blk, cylinders, heads, sectors); - return 0; - } - } - return -1; -} - -static void guess_chs_for_size(BlockBackend *blk, - uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs) -{ - uint64_t nb_sectors; - int cylinders; - - blk_get_geometry(blk, &nb_sectors); - - cylinders = nb_sectors / (16 * 63); - if (cylinders > 16383) { - cylinders = 16383; - } else if (cylinders < 2) { - cylinders = 2; - } - *pcyls = cylinders; - *pheads = 16; - *psecs = 63; -} - -void hd_geometry_guess(BlockBackend *blk, - uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs, - int *ptrans) -{ - int cylinders, heads, secs, translation; - HDGeometry geo; - - /* Try to probe the backing device geometry, otherwise fallback - to the old logic. (as of 12/2014 probing only succeeds on DASDs) */ - if (blk_probe_geometry(blk, &geo) == 0) { - *pcyls = geo.cylinders; - *psecs = geo.sectors; - *pheads = geo.heads; - translation = BIOS_ATA_TRANSLATION_NONE; - } else if (guess_disk_lchs(blk, &cylinders, &heads, &secs) < 0) { - /* no LCHS guess: use a standard physical disk geometry */ - guess_chs_for_size(blk, pcyls, pheads, psecs); - translation = hd_bios_chs_auto_trans(*pcyls, *pheads, *psecs); - } else if (heads > 16) { - /* LCHS guess with heads > 16 means that a BIOS LBA - translation was active, so a standard physical disk - geometry is OK */ - guess_chs_for_size(blk, pcyls, pheads, psecs); - translation = *pcyls * *pheads <= 131072 - ? BIOS_ATA_TRANSLATION_LARGE - : BIOS_ATA_TRANSLATION_LBA; - } else { - /* LCHS guess with heads <= 16: use as physical geometry */ - *pcyls = cylinders; - *pheads = heads; - *psecs = secs; - /* disable any translation to be in sync with - the logical geometry */ - translation = BIOS_ATA_TRANSLATION_NONE; - } - if (ptrans) { - *ptrans = translation; - } - trace_hd_geometry_guess(blk, *pcyls, *pheads, *psecs, translation); -} - -int hd_bios_chs_auto_trans(uint32_t cyls, uint32_t heads, uint32_t secs) -{ - return cyls <= 1024 && heads <= 16 && secs <= 63 - ? BIOS_ATA_TRANSLATION_NONE - : BIOS_ATA_TRANSLATION_LBA; -} diff --git a/qemu/hw/block/m25p80.c b/qemu/hw/block/m25p80.c deleted file mode 100644 index 906b71257..000000000 --- a/qemu/hw/block/m25p80.c +++ /dev/null @@ -1,1005 +0,0 @@ -/* - * ST M25P80 emulator. Emulate all SPI flash devices based on the m25p80 command - * set. Known devices table current as of Jun/2012 and taken from linux. - * See drivers/mtd/devices/m25p80.c. - * - * Copyright (C) 2011 Edgar E. Iglesias <edgar.iglesias@gmail.com> - * Copyright (C) 2012 Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com> - * Copyright (C) 2012 PetaLogix - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) a later version of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "hw/ssi/ssi.h" -#include "qemu/bitops.h" - -#ifndef M25P80_ERR_DEBUG -#define M25P80_ERR_DEBUG 0 -#endif - -#define DB_PRINT_L(level, ...) do { \ - if (M25P80_ERR_DEBUG > (level)) { \ - fprintf(stderr, ": %s: ", __func__); \ - fprintf(stderr, ## __VA_ARGS__); \ - } \ -} while (0); - -/* Fields for FlashPartInfo->flags */ - -/* erase capabilities */ -#define ER_4K 1 -#define ER_32K 2 -/* set to allow the page program command to write 0s back to 1. Useful for - * modelling EEPROM with SPI flash command set - */ -#define EEPROM 0x100 - -/* 16 MiB max in 3 byte address mode */ -#define MAX_3BYTES_SIZE 0x1000000 - -typedef struct FlashPartInfo { - const char *part_name; - /* jedec code. (jedec >> 16) & 0xff is the 1st byte, >> 8 the 2nd etc */ - uint32_t jedec; - /* extended jedec code */ - uint16_t ext_jedec; - /* there is confusion between manufacturers as to what a sector is. In this - * device model, a "sector" is the size that is erased by the ERASE_SECTOR - * command (opcode 0xd8). - */ - uint32_t sector_size; - uint32_t n_sectors; - uint32_t page_size; - uint16_t flags; -} FlashPartInfo; - -/* adapted from linux */ - -#define INFO(_part_name, _jedec, _ext_jedec, _sector_size, _n_sectors, _flags)\ - .part_name = (_part_name),\ - .jedec = (_jedec),\ - .ext_jedec = (_ext_jedec),\ - .sector_size = (_sector_size),\ - .n_sectors = (_n_sectors),\ - .page_size = 256,\ - .flags = (_flags),\ - -#define JEDEC_NUMONYX 0x20 -#define JEDEC_WINBOND 0xEF -#define JEDEC_SPANSION 0x01 - -/* Numonyx (Micron) Configuration register macros */ -#define VCFG_DUMMY 0x1 -#define VCFG_WRAP_SEQUENTIAL 0x2 -#define NVCFG_XIP_MODE_DISABLED (7 << 9) -#define NVCFG_XIP_MODE_MASK (7 << 9) -#define VCFG_XIP_MODE_ENABLED (1 << 3) -#define CFG_DUMMY_CLK_LEN 4 -#define NVCFG_DUMMY_CLK_POS 12 -#define VCFG_DUMMY_CLK_POS 4 -#define EVCFG_OUT_DRIVER_STRENGHT_DEF 7 -#define EVCFG_VPP_ACCELERATOR (1 << 3) -#define EVCFG_RESET_HOLD_ENABLED (1 << 4) -#define NVCFG_DUAL_IO_MASK (1 << 2) -#define EVCFG_DUAL_IO_ENABLED (1 << 6) -#define NVCFG_QUAD_IO_MASK (1 << 3) -#define EVCFG_QUAD_IO_ENABLED (1 << 7) -#define NVCFG_4BYTE_ADDR_MASK (1 << 0) -#define NVCFG_LOWER_SEGMENT_MASK (1 << 1) -#define CFG_UPPER_128MB_SEG_ENABLED 0x3 - -/* Numonyx (Micron) Flag Status Register macros */ -#define FSR_4BYTE_ADDR_MODE_ENABLED 0x1 -#define FSR_FLASH_READY (1 << 7) - -static const FlashPartInfo known_devices[] = { - /* Atmel -- some are (confusingly) marketed as "DataFlash" */ - { INFO("at25fs010", 0x1f6601, 0, 32 << 10, 4, ER_4K) }, - { INFO("at25fs040", 0x1f6604, 0, 64 << 10, 8, ER_4K) }, - - { INFO("at25df041a", 0x1f4401, 0, 64 << 10, 8, ER_4K) }, - { INFO("at25df321a", 0x1f4701, 0, 64 << 10, 64, ER_4K) }, - { INFO("at25df641", 0x1f4800, 0, 64 << 10, 128, ER_4K) }, - - { INFO("at26f004", 0x1f0400, 0, 64 << 10, 8, ER_4K) }, - { INFO("at26df081a", 0x1f4501, 0, 64 << 10, 16, ER_4K) }, - { INFO("at26df161a", 0x1f4601, 0, 64 << 10, 32, ER_4K) }, - { INFO("at26df321", 0x1f4700, 0, 64 << 10, 64, ER_4K) }, - - { INFO("at45db081d", 0x1f2500, 0, 64 << 10, 16, ER_4K) }, - - /* Atmel EEPROMS - it is assumed, that don't care bit in command - * is set to 0. Block protection is not supported. - */ - { INFO("at25128a-nonjedec", 0x0, 0, 1, 131072, EEPROM) }, - { INFO("at25256a-nonjedec", 0x0, 0, 1, 262144, EEPROM) }, - - /* EON -- en25xxx */ - { INFO("en25f32", 0x1c3116, 0, 64 << 10, 64, ER_4K) }, - { INFO("en25p32", 0x1c2016, 0, 64 << 10, 64, 0) }, - { INFO("en25q32b", 0x1c3016, 0, 64 << 10, 64, 0) }, - { INFO("en25p64", 0x1c2017, 0, 64 << 10, 128, 0) }, - { INFO("en25q64", 0x1c3017, 0, 64 << 10, 128, ER_4K) }, - - /* GigaDevice */ - { INFO("gd25q32", 0xc84016, 0, 64 << 10, 64, ER_4K) }, - { INFO("gd25q64", 0xc84017, 0, 64 << 10, 128, ER_4K) }, - - /* Intel/Numonyx -- xxxs33b */ - { INFO("160s33b", 0x898911, 0, 64 << 10, 32, 0) }, - { INFO("320s33b", 0x898912, 0, 64 << 10, 64, 0) }, - { INFO("640s33b", 0x898913, 0, 64 << 10, 128, 0) }, - { INFO("n25q064", 0x20ba17, 0, 64 << 10, 128, 0) }, - - /* Macronix */ - { INFO("mx25l2005a", 0xc22012, 0, 64 << 10, 4, ER_4K) }, - { INFO("mx25l4005a", 0xc22013, 0, 64 << 10, 8, ER_4K) }, - { INFO("mx25l8005", 0xc22014, 0, 64 << 10, 16, 0) }, - { INFO("mx25l1606e", 0xc22015, 0, 64 << 10, 32, ER_4K) }, - { INFO("mx25l3205d", 0xc22016, 0, 64 << 10, 64, 0) }, - { INFO("mx25l6405d", 0xc22017, 0, 64 << 10, 128, 0) }, - { INFO("mx25l12805d", 0xc22018, 0, 64 << 10, 256, 0) }, - { INFO("mx25l12855e", 0xc22618, 0, 64 << 10, 256, 0) }, - { INFO("mx25l25635e", 0xc22019, 0, 64 << 10, 512, 0) }, - { INFO("mx25l25655e", 0xc22619, 0, 64 << 10, 512, 0) }, - - /* Micron */ - { INFO("n25q032a11", 0x20bb16, 0, 64 << 10, 64, ER_4K) }, - { INFO("n25q032a13", 0x20ba16, 0, 64 << 10, 64, ER_4K) }, - { INFO("n25q064a11", 0x20bb17, 0, 64 << 10, 128, ER_4K) }, - { INFO("n25q064a13", 0x20ba17, 0, 64 << 10, 128, ER_4K) }, - { INFO("n25q128a11", 0x20bb18, 0, 64 << 10, 256, ER_4K) }, - { INFO("n25q128a13", 0x20ba18, 0, 64 << 10, 256, ER_4K) }, - { INFO("n25q256a11", 0x20bb19, 0, 64 << 10, 512, ER_4K) }, - { INFO("n25q256a13", 0x20ba19, 0, 64 << 10, 512, ER_4K) }, - - /* Spansion -- single (large) sector size only, at least - * for the chips listed here (without boot sectors). - */ - { INFO("s25sl032p", 0x010215, 0x4d00, 64 << 10, 64, ER_4K) }, - { INFO("s25sl064p", 0x010216, 0x4d00, 64 << 10, 128, ER_4K) }, - { INFO("s25fl256s0", 0x010219, 0x4d00, 256 << 10, 128, 0) }, - { INFO("s25fl256s1", 0x010219, 0x4d01, 64 << 10, 512, 0) }, - { INFO("s25fl512s", 0x010220, 0x4d00, 256 << 10, 256, 0) }, - { INFO("s70fl01gs", 0x010221, 0x4d00, 256 << 10, 256, 0) }, - { INFO("s25sl12800", 0x012018, 0x0300, 256 << 10, 64, 0) }, - { INFO("s25sl12801", 0x012018, 0x0301, 64 << 10, 256, 0) }, - { INFO("s25fl129p0", 0x012018, 0x4d00, 256 << 10, 64, 0) }, - { INFO("s25fl129p1", 0x012018, 0x4d01, 64 << 10, 256, 0) }, - { INFO("s25sl004a", 0x010212, 0, 64 << 10, 8, 0) }, - { INFO("s25sl008a", 0x010213, 0, 64 << 10, 16, 0) }, - { INFO("s25sl016a", 0x010214, 0, 64 << 10, 32, 0) }, - { INFO("s25sl032a", 0x010215, 0, 64 << 10, 64, 0) }, - { INFO("s25sl064a", 0x010216, 0, 64 << 10, 128, 0) }, - { INFO("s25fl016k", 0xef4015, 0, 64 << 10, 32, ER_4K | ER_32K) }, - { INFO("s25fl064k", 0xef4017, 0, 64 << 10, 128, ER_4K | ER_32K) }, - - /* SST -- large erase sizes are "overlays", "sectors" are 4<< 10 */ - { INFO("sst25vf040b", 0xbf258d, 0, 64 << 10, 8, ER_4K) }, - { INFO("sst25vf080b", 0xbf258e, 0, 64 << 10, 16, ER_4K) }, - { INFO("sst25vf016b", 0xbf2541, 0, 64 << 10, 32, ER_4K) }, - { INFO("sst25vf032b", 0xbf254a, 0, 64 << 10, 64, ER_4K) }, - { INFO("sst25wf512", 0xbf2501, 0, 64 << 10, 1, ER_4K) }, - { INFO("sst25wf010", 0xbf2502, 0, 64 << 10, 2, ER_4K) }, - { INFO("sst25wf020", 0xbf2503, 0, 64 << 10, 4, ER_4K) }, - { INFO("sst25wf040", 0xbf2504, 0, 64 << 10, 8, ER_4K) }, - { INFO("sst25wf080", 0xbf2505, 0, 64 << 10, 16, ER_4K) }, - - /* ST Microelectronics -- newer production may have feature updates */ - { INFO("m25p05", 0x202010, 0, 32 << 10, 2, 0) }, - { INFO("m25p10", 0x202011, 0, 32 << 10, 4, 0) }, - { INFO("m25p20", 0x202012, 0, 64 << 10, 4, 0) }, - { INFO("m25p40", 0x202013, 0, 64 << 10, 8, 0) }, - { INFO("m25p80", 0x202014, 0, 64 << 10, 16, 0) }, - { INFO("m25p16", 0x202015, 0, 64 << 10, 32, 0) }, - { INFO("m25p32", 0x202016, 0, 64 << 10, 64, 0) }, - { INFO("m25p64", 0x202017, 0, 64 << 10, 128, 0) }, - { INFO("m25p128", 0x202018, 0, 256 << 10, 64, 0) }, - { INFO("n25q032", 0x20ba16, 0, 64 << 10, 64, 0) }, - - { INFO("m45pe10", 0x204011, 0, 64 << 10, 2, 0) }, - { INFO("m45pe80", 0x204014, 0, 64 << 10, 16, 0) }, - { INFO("m45pe16", 0x204015, 0, 64 << 10, 32, 0) }, - - { INFO("m25pe20", 0x208012, 0, 64 << 10, 4, 0) }, - { INFO("m25pe80", 0x208014, 0, 64 << 10, 16, 0) }, - { INFO("m25pe16", 0x208015, 0, 64 << 10, 32, ER_4K) }, - - { INFO("m25px32", 0x207116, 0, 64 << 10, 64, ER_4K) }, - { INFO("m25px32-s0", 0x207316, 0, 64 << 10, 64, ER_4K) }, - { INFO("m25px32-s1", 0x206316, 0, 64 << 10, 64, ER_4K) }, - { INFO("m25px64", 0x207117, 0, 64 << 10, 128, 0) }, - - /* Winbond -- w25x "blocks" are 64k, "sectors" are 4KiB */ - { INFO("w25x10", 0xef3011, 0, 64 << 10, 2, ER_4K) }, - { INFO("w25x20", 0xef3012, 0, 64 << 10, 4, ER_4K) }, - { INFO("w25x40", 0xef3013, 0, 64 << 10, 8, ER_4K) }, - { INFO("w25x80", 0xef3014, 0, 64 << 10, 16, ER_4K) }, - { INFO("w25x16", 0xef3015, 0, 64 << 10, 32, ER_4K) }, - { INFO("w25x32", 0xef3016, 0, 64 << 10, 64, ER_4K) }, - { INFO("w25q32", 0xef4016, 0, 64 << 10, 64, ER_4K) }, - { INFO("w25q32dw", 0xef6016, 0, 64 << 10, 64, ER_4K) }, - { INFO("w25x64", 0xef3017, 0, 64 << 10, 128, ER_4K) }, - { INFO("w25q64", 0xef4017, 0, 64 << 10, 128, ER_4K) }, - { INFO("w25q80", 0xef5014, 0, 64 << 10, 16, ER_4K) }, - { INFO("w25q80bl", 0xef4014, 0, 64 << 10, 16, ER_4K) }, - { INFO("w25q256", 0xef4019, 0, 64 << 10, 512, ER_4K) }, - - { INFO("n25q128", 0x20ba18, 0, 64 << 10, 256, 0) }, - { INFO("n25q256a", 0x20ba19, 0, 64 << 10, 512, ER_4K) }, - { INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) }, -}; - -typedef enum { - NOP = 0, - WRSR = 0x1, - WRDI = 0x4, - RDSR = 0x5, - WREN = 0x6, - JEDEC_READ = 0x9f, - BULK_ERASE = 0xc7, - READ_FSR = 0x70, - - READ = 0x03, - READ4 = 0x13, - FAST_READ = 0x0b, - FAST_READ4 = 0x0c, - DOR = 0x3b, - DOR4 = 0x3c, - QOR = 0x6b, - QOR4 = 0x6c, - DIOR = 0xbb, - DIOR4 = 0xbc, - QIOR = 0xeb, - QIOR4 = 0xec, - - PP = 0x02, - PP4 = 0x12, - DPP = 0xa2, - QPP = 0x32, - - ERASE_4K = 0x20, - ERASE4_4K = 0x21, - ERASE_32K = 0x52, - ERASE_SECTOR = 0xd8, - ERASE4_SECTOR = 0xdc, - - EN_4BYTE_ADDR = 0xB7, - EX_4BYTE_ADDR = 0xE9, - - EXTEND_ADDR_READ = 0xC8, - EXTEND_ADDR_WRITE = 0xC5, - - RESET_ENABLE = 0x66, - RESET_MEMORY = 0x99, - - RNVCR = 0xB5, - WNVCR = 0xB1, - - RVCR = 0x85, - WVCR = 0x81, - - REVCR = 0x65, - WEVCR = 0x61, -} FlashCMD; - -typedef enum { - STATE_IDLE, - STATE_PAGE_PROGRAM, - STATE_READ, - STATE_COLLECTING_DATA, - STATE_READING_DATA, -} CMDState; - -typedef struct Flash { - SSISlave parent_obj; - - BlockBackend *blk; - - uint8_t *storage; - uint32_t size; - int page_size; - - uint8_t state; - uint8_t data[16]; - uint32_t len; - uint32_t pos; - uint8_t needed_bytes; - uint8_t cmd_in_progress; - uint64_t cur_addr; - uint32_t nonvolatile_cfg; - uint32_t volatile_cfg; - uint32_t enh_volatile_cfg; - bool write_enable; - bool four_bytes_address_mode; - bool reset_enable; - uint8_t ear; - - int64_t dirty_page; - - const FlashPartInfo *pi; - -} Flash; - -typedef struct M25P80Class { - SSISlaveClass parent_class; - FlashPartInfo *pi; -} M25P80Class; - -#define TYPE_M25P80 "m25p80-generic" -#define M25P80(obj) \ - OBJECT_CHECK(Flash, (obj), TYPE_M25P80) -#define M25P80_CLASS(klass) \ - OBJECT_CLASS_CHECK(M25P80Class, (klass), TYPE_M25P80) -#define M25P80_GET_CLASS(obj) \ - OBJECT_GET_CLASS(M25P80Class, (obj), TYPE_M25P80) - -static void blk_sync_complete(void *opaque, int ret) -{ - /* do nothing. Masters do not directly interact with the backing store, - * only the working copy so no mutexing required. - */ -} - -static void flash_sync_page(Flash *s, int page) -{ - int blk_sector, nb_sectors; - QEMUIOVector iov; - - if (!s->blk || blk_is_read_only(s->blk)) { - return; - } - - blk_sector = (page * s->pi->page_size) / BDRV_SECTOR_SIZE; - nb_sectors = DIV_ROUND_UP(s->pi->page_size, BDRV_SECTOR_SIZE); - qemu_iovec_init(&iov, 1); - qemu_iovec_add(&iov, s->storage + blk_sector * BDRV_SECTOR_SIZE, - nb_sectors * BDRV_SECTOR_SIZE); - blk_aio_writev(s->blk, blk_sector, &iov, nb_sectors, blk_sync_complete, - NULL); -} - -static inline void flash_sync_area(Flash *s, int64_t off, int64_t len) -{ - int64_t start, end, nb_sectors; - QEMUIOVector iov; - - if (!s->blk || blk_is_read_only(s->blk)) { - return; - } - - assert(!(len % BDRV_SECTOR_SIZE)); - start = off / BDRV_SECTOR_SIZE; - end = (off + len) / BDRV_SECTOR_SIZE; - nb_sectors = end - start; - qemu_iovec_init(&iov, 1); - qemu_iovec_add(&iov, s->storage + (start * BDRV_SECTOR_SIZE), - nb_sectors * BDRV_SECTOR_SIZE); - blk_aio_writev(s->blk, start, &iov, nb_sectors, blk_sync_complete, NULL); -} - -static void flash_erase(Flash *s, int offset, FlashCMD cmd) -{ - uint32_t len; - uint8_t capa_to_assert = 0; - - switch (cmd) { - case ERASE_4K: - case ERASE4_4K: - len = 4 << 10; - capa_to_assert = ER_4K; - break; - case ERASE_32K: - len = 32 << 10; - capa_to_assert = ER_32K; - break; - case ERASE_SECTOR: - case ERASE4_SECTOR: - len = s->pi->sector_size; - break; - case BULK_ERASE: - len = s->size; - break; - default: - abort(); - } - - DB_PRINT_L(0, "offset = %#x, len = %d\n", offset, len); - if ((s->pi->flags & capa_to_assert) != capa_to_assert) { - qemu_log_mask(LOG_GUEST_ERROR, "M25P80: %d erase size not supported by" - " device\n", len); - } - - if (!s->write_enable) { - qemu_log_mask(LOG_GUEST_ERROR, "M25P80: erase with write protect!\n"); - return; - } - memset(s->storage + offset, 0xff, len); - flash_sync_area(s, offset, len); -} - -static inline void flash_sync_dirty(Flash *s, int64_t newpage) -{ - if (s->dirty_page >= 0 && s->dirty_page != newpage) { - flash_sync_page(s, s->dirty_page); - s->dirty_page = newpage; - } -} - -static inline -void flash_write8(Flash *s, uint64_t addr, uint8_t data) -{ - int64_t page = addr / s->pi->page_size; - uint8_t prev = s->storage[s->cur_addr]; - - if (!s->write_enable) { - qemu_log_mask(LOG_GUEST_ERROR, "M25P80: write with write protect!\n"); - } - - if ((prev ^ data) & data) { - DB_PRINT_L(1, "programming zero to one! addr=%" PRIx64 " %" PRIx8 - " -> %" PRIx8 "\n", addr, prev, data); - } - - if (s->pi->flags & EEPROM) { - s->storage[s->cur_addr] = data; - } else { - s->storage[s->cur_addr] &= data; - } - - flash_sync_dirty(s, page); - s->dirty_page = page; -} - -static inline int get_addr_length(Flash *s) -{ - /* check if eeprom is in use */ - if (s->pi->flags == EEPROM) { - return 2; - } - - switch (s->cmd_in_progress) { - case PP4: - case READ4: - case QIOR4: - case ERASE4_4K: - case ERASE4_SECTOR: - case FAST_READ4: - case DOR4: - case QOR4: - case DIOR4: - return 4; - default: - return s->four_bytes_address_mode ? 4 : 3; - } -} - -static void complete_collecting_data(Flash *s) -{ - int i; - - s->cur_addr = 0; - - for (i = 0; i < get_addr_length(s); ++i) { - s->cur_addr <<= 8; - s->cur_addr |= s->data[i]; - } - - if (get_addr_length(s) == 3) { - s->cur_addr += (s->ear & 0x3) * MAX_3BYTES_SIZE; - } - - s->state = STATE_IDLE; - - switch (s->cmd_in_progress) { - case DPP: - case QPP: - case PP: - case PP4: - s->state = STATE_PAGE_PROGRAM; - break; - case READ: - case READ4: - case FAST_READ: - case FAST_READ4: - case DOR: - case DOR4: - case QOR: - case QOR4: - case DIOR: - case DIOR4: - case QIOR: - case QIOR4: - s->state = STATE_READ; - break; - case ERASE_4K: - case ERASE4_4K: - case ERASE_32K: - case ERASE_SECTOR: - case ERASE4_SECTOR: - flash_erase(s, s->cur_addr, s->cmd_in_progress); - break; - case WRSR: - if (s->write_enable) { - s->write_enable = false; - } - break; - case EXTEND_ADDR_WRITE: - s->ear = s->data[0]; - break; - case WNVCR: - s->nonvolatile_cfg = s->data[0] | (s->data[1] << 8); - break; - case WVCR: - s->volatile_cfg = s->data[0]; - break; - case WEVCR: - s->enh_volatile_cfg = s->data[0]; - break; - default: - break; - } -} - -static void reset_memory(Flash *s) -{ - s->cmd_in_progress = NOP; - s->cur_addr = 0; - s->ear = 0; - s->four_bytes_address_mode = false; - s->len = 0; - s->needed_bytes = 0; - s->pos = 0; - s->state = STATE_IDLE; - s->write_enable = false; - s->reset_enable = false; - - if (((s->pi->jedec >> 16) & 0xFF) == JEDEC_NUMONYX) { - s->volatile_cfg = 0; - s->volatile_cfg |= VCFG_DUMMY; - s->volatile_cfg |= VCFG_WRAP_SEQUENTIAL; - if ((s->nonvolatile_cfg & NVCFG_XIP_MODE_MASK) - != NVCFG_XIP_MODE_DISABLED) { - s->volatile_cfg |= VCFG_XIP_MODE_ENABLED; - } - s->volatile_cfg |= deposit32(s->volatile_cfg, - VCFG_DUMMY_CLK_POS, - CFG_DUMMY_CLK_LEN, - extract32(s->nonvolatile_cfg, - NVCFG_DUMMY_CLK_POS, - CFG_DUMMY_CLK_LEN) - ); - - s->enh_volatile_cfg = 0; - s->enh_volatile_cfg |= EVCFG_OUT_DRIVER_STRENGHT_DEF; - s->enh_volatile_cfg |= EVCFG_VPP_ACCELERATOR; - s->enh_volatile_cfg |= EVCFG_RESET_HOLD_ENABLED; - if (s->nonvolatile_cfg & NVCFG_DUAL_IO_MASK) { - s->enh_volatile_cfg |= EVCFG_DUAL_IO_ENABLED; - } - if (s->nonvolatile_cfg & NVCFG_QUAD_IO_MASK) { - s->enh_volatile_cfg |= EVCFG_QUAD_IO_ENABLED; - } - if (!(s->nonvolatile_cfg & NVCFG_4BYTE_ADDR_MASK)) { - s->four_bytes_address_mode = true; - } - if (!(s->nonvolatile_cfg & NVCFG_LOWER_SEGMENT_MASK)) { - s->ear = CFG_UPPER_128MB_SEG_ENABLED; - } - } - - DB_PRINT_L(0, "Reset done.\n"); -} - -static void decode_new_cmd(Flash *s, uint32_t value) -{ - s->cmd_in_progress = value; - DB_PRINT_L(0, "decoded new command:%x\n", value); - - if (value != RESET_MEMORY) { - s->reset_enable = false; - } - - switch (value) { - - case ERASE_4K: - case ERASE4_4K: - case ERASE_32K: - case ERASE_SECTOR: - case ERASE4_SECTOR: - case READ: - case READ4: - case DPP: - case QPP: - case PP: - case PP4: - s->needed_bytes = get_addr_length(s); - s->pos = 0; - s->len = 0; - s->state = STATE_COLLECTING_DATA; - break; - - case FAST_READ: - case FAST_READ4: - case DOR: - case DOR4: - case QOR: - case QOR4: - s->needed_bytes = get_addr_length(s); - if (((s->pi->jedec >> 16) & 0xFF) == JEDEC_NUMONYX) { - /* Dummy cycles modeled with bytes writes instead of bits */ - s->needed_bytes += extract32(s->volatile_cfg, 4, 4); - } - s->pos = 0; - s->len = 0; - s->state = STATE_COLLECTING_DATA; - break; - - case DIOR: - case DIOR4: - switch ((s->pi->jedec >> 16) & 0xFF) { - case JEDEC_WINBOND: - case JEDEC_SPANSION: - s->needed_bytes = 4; - break; - default: - s->needed_bytes = get_addr_length(s); - /* Dummy cycles modeled with bytes writes instead of bits */ - s->needed_bytes += extract32(s->volatile_cfg, 4, 4); - } - s->pos = 0; - s->len = 0; - s->state = STATE_COLLECTING_DATA; - break; - - case QIOR: - case QIOR4: - switch ((s->pi->jedec >> 16) & 0xFF) { - case JEDEC_WINBOND: - case JEDEC_SPANSION: - s->needed_bytes = 6; - break; - default: - s->needed_bytes = get_addr_length(s); - /* Dummy cycles modeled with bytes writes instead of bits */ - s->needed_bytes += extract32(s->volatile_cfg, 4, 4); - } - s->pos = 0; - s->len = 0; - s->state = STATE_COLLECTING_DATA; - break; - - case WRSR: - if (s->write_enable) { - s->needed_bytes = 1; - s->pos = 0; - s->len = 0; - s->state = STATE_COLLECTING_DATA; - } - break; - - case WRDI: - s->write_enable = false; - break; - case WREN: - s->write_enable = true; - break; - - case RDSR: - s->data[0] = (!!s->write_enable) << 1; - s->pos = 0; - s->len = 1; - s->state = STATE_READING_DATA; - break; - - case READ_FSR: - s->data[0] = FSR_FLASH_READY; - if (s->four_bytes_address_mode) { - s->data[0] |= FSR_4BYTE_ADDR_MODE_ENABLED; - } - s->pos = 0; - s->len = 1; - s->state = STATE_READING_DATA; - break; - - case JEDEC_READ: - DB_PRINT_L(0, "populated jedec code\n"); - s->data[0] = (s->pi->jedec >> 16) & 0xff; - s->data[1] = (s->pi->jedec >> 8) & 0xff; - s->data[2] = s->pi->jedec & 0xff; - if (s->pi->ext_jedec) { - s->data[3] = (s->pi->ext_jedec >> 8) & 0xff; - s->data[4] = s->pi->ext_jedec & 0xff; - s->len = 5; - } else { - s->len = 3; - } - s->pos = 0; - s->state = STATE_READING_DATA; - break; - - case BULK_ERASE: - if (s->write_enable) { - DB_PRINT_L(0, "chip erase\n"); - flash_erase(s, 0, BULK_ERASE); - } else { - qemu_log_mask(LOG_GUEST_ERROR, "M25P80: chip erase with write " - "protect!\n"); - } - break; - case NOP: - break; - case EN_4BYTE_ADDR: - s->four_bytes_address_mode = true; - break; - case EX_4BYTE_ADDR: - s->four_bytes_address_mode = false; - break; - case EXTEND_ADDR_READ: - s->data[0] = s->ear; - s->pos = 0; - s->len = 1; - s->state = STATE_READING_DATA; - break; - case EXTEND_ADDR_WRITE: - if (s->write_enable) { - s->needed_bytes = 1; - s->pos = 0; - s->len = 0; - s->state = STATE_COLLECTING_DATA; - } - break; - case RNVCR: - s->data[0] = s->nonvolatile_cfg & 0xFF; - s->data[1] = (s->nonvolatile_cfg >> 8) & 0xFF; - s->pos = 0; - s->len = 2; - s->state = STATE_READING_DATA; - break; - case WNVCR: - if (s->write_enable) { - s->needed_bytes = 2; - s->pos = 0; - s->len = 0; - s->state = STATE_COLLECTING_DATA; - } - break; - case RVCR: - s->data[0] = s->volatile_cfg & 0xFF; - s->pos = 0; - s->len = 1; - s->state = STATE_READING_DATA; - break; - case WVCR: - if (s->write_enable) { - s->needed_bytes = 1; - s->pos = 0; - s->len = 0; - s->state = STATE_COLLECTING_DATA; - } - break; - case REVCR: - s->data[0] = s->enh_volatile_cfg & 0xFF; - s->pos = 0; - s->len = 1; - s->state = STATE_READING_DATA; - break; - case WEVCR: - if (s->write_enable) { - s->needed_bytes = 1; - s->pos = 0; - s->len = 0; - s->state = STATE_COLLECTING_DATA; - } - break; - case RESET_ENABLE: - s->reset_enable = true; - break; - case RESET_MEMORY: - if (s->reset_enable) { - reset_memory(s); - } - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value); - break; - } -} - -static int m25p80_cs(SSISlave *ss, bool select) -{ - Flash *s = M25P80(ss); - - if (select) { - s->len = 0; - s->pos = 0; - s->state = STATE_IDLE; - flash_sync_dirty(s, -1); - } - - DB_PRINT_L(0, "%sselect\n", select ? "de" : ""); - - return 0; -} - -static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) -{ - Flash *s = M25P80(ss); - uint32_t r = 0; - - switch (s->state) { - - case STATE_PAGE_PROGRAM: - DB_PRINT_L(1, "page program cur_addr=%#" PRIx64 " data=%" PRIx8 "\n", - s->cur_addr, (uint8_t)tx); - flash_write8(s, s->cur_addr, (uint8_t)tx); - s->cur_addr++; - break; - - case STATE_READ: - r = s->storage[s->cur_addr]; - DB_PRINT_L(1, "READ 0x%" PRIx64 "=%" PRIx8 "\n", s->cur_addr, - (uint8_t)r); - s->cur_addr = (s->cur_addr + 1) % s->size; - break; - - case STATE_COLLECTING_DATA: - s->data[s->len] = (uint8_t)tx; - s->len++; - - if (s->len == s->needed_bytes) { - complete_collecting_data(s); - } - break; - - case STATE_READING_DATA: - r = s->data[s->pos]; - s->pos++; - if (s->pos == s->len) { - s->pos = 0; - s->state = STATE_IDLE; - } - break; - - default: - case STATE_IDLE: - decode_new_cmd(s, (uint8_t)tx); - break; - } - - return r; -} - -static int m25p80_init(SSISlave *ss) -{ - DriveInfo *dinfo; - Flash *s = M25P80(ss); - M25P80Class *mc = M25P80_GET_CLASS(s); - - s->pi = mc->pi; - - s->size = s->pi->sector_size * s->pi->n_sectors; - s->dirty_page = -1; - - /* FIXME use a qdev drive property instead of drive_get_next() */ - dinfo = drive_get_next(IF_MTD); - - if (dinfo) { - DB_PRINT_L(0, "Binding to IF_MTD drive\n"); - s->blk = blk_by_legacy_dinfo(dinfo); - blk_attach_dev_nofail(s->blk, s); - - s->storage = blk_blockalign(s->blk, s->size); - - /* FIXME: Move to late init */ - if (blk_read(s->blk, 0, s->storage, - DIV_ROUND_UP(s->size, BDRV_SECTOR_SIZE))) { - fprintf(stderr, "Failed to initialize SPI flash!\n"); - return 1; - } - } else { - DB_PRINT_L(0, "No BDRV - binding to RAM\n"); - s->storage = blk_blockalign(NULL, s->size); - memset(s->storage, 0xFF, s->size); - } - - return 0; -} - -static void m25p80_reset(DeviceState *d) -{ - Flash *s = M25P80(d); - - reset_memory(s); -} - -static void m25p80_pre_save(void *opaque) -{ - flash_sync_dirty((Flash *)opaque, -1); -} - -static Property m25p80_properties[] = { - DEFINE_PROP_UINT32("nonvolatile-cfg", Flash, nonvolatile_cfg, 0x8FFF), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription vmstate_m25p80 = { - .name = "xilinx_spi", - .version_id = 2, - .minimum_version_id = 1, - .pre_save = m25p80_pre_save, - .fields = (VMStateField[]) { - VMSTATE_UINT8(state, Flash), - VMSTATE_UINT8_ARRAY(data, Flash, 16), - VMSTATE_UINT32(len, Flash), - VMSTATE_UINT32(pos, Flash), - VMSTATE_UINT8(needed_bytes, Flash), - VMSTATE_UINT8(cmd_in_progress, Flash), - VMSTATE_UINT64(cur_addr, Flash), - VMSTATE_BOOL(write_enable, Flash), - VMSTATE_BOOL_V(reset_enable, Flash, 2), - VMSTATE_UINT8_V(ear, Flash, 2), - VMSTATE_BOOL_V(four_bytes_address_mode, Flash, 2), - VMSTATE_UINT32_V(nonvolatile_cfg, Flash, 2), - VMSTATE_UINT32_V(volatile_cfg, Flash, 2), - VMSTATE_UINT32_V(enh_volatile_cfg, Flash, 2), - VMSTATE_END_OF_LIST() - } -}; - -static void m25p80_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - M25P80Class *mc = M25P80_CLASS(klass); - - k->init = m25p80_init; - k->transfer = m25p80_transfer8; - k->set_cs = m25p80_cs; - k->cs_polarity = SSI_CS_LOW; - dc->vmsd = &vmstate_m25p80; - dc->props = m25p80_properties; - dc->reset = m25p80_reset; - mc->pi = data; -} - -static const TypeInfo m25p80_info = { - .name = TYPE_M25P80, - .parent = TYPE_SSI_SLAVE, - .instance_size = sizeof(Flash), - .class_size = sizeof(M25P80Class), - .abstract = true, -}; - -static void m25p80_register_types(void) -{ - int i; - - type_register_static(&m25p80_info); - for (i = 0; i < ARRAY_SIZE(known_devices); ++i) { - TypeInfo ti = { - .name = known_devices[i].part_name, - .parent = TYPE_M25P80, - .class_init = m25p80_class_init, - .class_data = (void *)&known_devices[i], - }; - type_register(&ti); - } -} - -type_init(m25p80_register_types) diff --git a/qemu/hw/block/nand.c b/qemu/hw/block/nand.c deleted file mode 100644 index 29c659681..000000000 --- a/qemu/hw/block/nand.c +++ /dev/null @@ -1,801 +0,0 @@ -/* - * Flash NAND memory emulation. Based on "16M x 8 Bit NAND Flash - * Memory" datasheet for the KM29U128AT / K9F2808U0A chips from - * Samsung Electronic. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski <balrog@zabor.org> - * - * Support for additional features based on "MT29F2G16ABCWP 2Gx16" - * datasheet from Micron Technology and "NAND02G-B2C" datasheet - * from ST Microelectronics. - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#ifndef NAND_IO - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/block/flash.h" -#include "sysemu/block-backend.h" -#include "hw/qdev.h" -#include "qapi/error.h" -#include "qemu/error-report.h" - -# define NAND_CMD_READ0 0x00 -# define NAND_CMD_READ1 0x01 -# define NAND_CMD_READ2 0x50 -# define NAND_CMD_LPREAD2 0x30 -# define NAND_CMD_NOSERIALREAD2 0x35 -# define NAND_CMD_RANDOMREAD1 0x05 -# define NAND_CMD_RANDOMREAD2 0xe0 -# define NAND_CMD_READID 0x90 -# define NAND_CMD_RESET 0xff -# define NAND_CMD_PAGEPROGRAM1 0x80 -# define NAND_CMD_PAGEPROGRAM2 0x10 -# define NAND_CMD_CACHEPROGRAM2 0x15 -# define NAND_CMD_BLOCKERASE1 0x60 -# define NAND_CMD_BLOCKERASE2 0xd0 -# define NAND_CMD_READSTATUS 0x70 -# define NAND_CMD_COPYBACKPRG1 0x85 - -# define NAND_IOSTATUS_ERROR (1 << 0) -# define NAND_IOSTATUS_PLANE0 (1 << 1) -# define NAND_IOSTATUS_PLANE1 (1 << 2) -# define NAND_IOSTATUS_PLANE2 (1 << 3) -# define NAND_IOSTATUS_PLANE3 (1 << 4) -# define NAND_IOSTATUS_READY (1 << 6) -# define NAND_IOSTATUS_UNPROTCT (1 << 7) - -# define MAX_PAGE 0x800 -# define MAX_OOB 0x40 - -typedef struct NANDFlashState NANDFlashState; -struct NANDFlashState { - DeviceState parent_obj; - - uint8_t manf_id, chip_id; - uint8_t buswidth; /* in BYTES */ - int size, pages; - int page_shift, oob_shift, erase_shift, addr_shift; - uint8_t *storage; - BlockBackend *blk; - int mem_oob; - - uint8_t cle, ale, ce, wp, gnd; - - uint8_t io[MAX_PAGE + MAX_OOB + 0x400]; - uint8_t *ioaddr; - int iolen; - - uint32_t cmd; - uint64_t addr; - int addrlen; - int status; - int offset; - - void (*blk_write)(NANDFlashState *s); - void (*blk_erase)(NANDFlashState *s); - void (*blk_load)(NANDFlashState *s, uint64_t addr, int offset); - - uint32_t ioaddr_vmstate; -}; - -#define TYPE_NAND "nand" - -#define NAND(obj) \ - OBJECT_CHECK(NANDFlashState, (obj), TYPE_NAND) - -static void mem_and(uint8_t *dest, const uint8_t *src, size_t n) -{ - /* Like memcpy() but we logical-AND the data into the destination */ - int i; - for (i = 0; i < n; i++) { - dest[i] &= src[i]; - } -} - -# define NAND_NO_AUTOINCR 0x00000001 -# define NAND_BUSWIDTH_16 0x00000002 -# define NAND_NO_PADDING 0x00000004 -# define NAND_CACHEPRG 0x00000008 -# define NAND_COPYBACK 0x00000010 -# define NAND_IS_AND 0x00000020 -# define NAND_4PAGE_ARRAY 0x00000040 -# define NAND_NO_READRDY 0x00000100 -# define NAND_SAMSUNG_LP (NAND_NO_PADDING | NAND_COPYBACK) - -# define NAND_IO - -# define PAGE(addr) ((addr) >> ADDR_SHIFT) -# define PAGE_START(page) (PAGE(page) * (PAGE_SIZE + OOB_SIZE)) -# define PAGE_MASK ((1 << ADDR_SHIFT) - 1) -# define OOB_SHIFT (PAGE_SHIFT - 5) -# define OOB_SIZE (1 << OOB_SHIFT) -# define SECTOR(addr) ((addr) >> (9 + ADDR_SHIFT - PAGE_SHIFT)) -# define SECTOR_OFFSET(addr) ((addr) & ((511 >> PAGE_SHIFT) << 8)) - -# define PAGE_SIZE 256 -# define PAGE_SHIFT 8 -# define PAGE_SECTORS 1 -# define ADDR_SHIFT 8 -# include "nand.c" -# define PAGE_SIZE 512 -# define PAGE_SHIFT 9 -# define PAGE_SECTORS 1 -# define ADDR_SHIFT 8 -# include "nand.c" -# define PAGE_SIZE 2048 -# define PAGE_SHIFT 11 -# define PAGE_SECTORS 4 -# define ADDR_SHIFT 16 -# include "nand.c" - -/* Information based on Linux drivers/mtd/nand/nand_ids.c */ -static const struct { - int size; - int width; - int page_shift; - int erase_shift; - uint32_t options; -} nand_flash_ids[0x100] = { - [0 ... 0xff] = { 0 }, - - [0x6e] = { 1, 8, 8, 4, 0 }, - [0x64] = { 2, 8, 8, 4, 0 }, - [0x6b] = { 4, 8, 9, 4, 0 }, - [0xe8] = { 1, 8, 8, 4, 0 }, - [0xec] = { 1, 8, 8, 4, 0 }, - [0xea] = { 2, 8, 8, 4, 0 }, - [0xd5] = { 4, 8, 9, 4, 0 }, - [0xe3] = { 4, 8, 9, 4, 0 }, - [0xe5] = { 4, 8, 9, 4, 0 }, - [0xd6] = { 8, 8, 9, 4, 0 }, - - [0x39] = { 8, 8, 9, 4, 0 }, - [0xe6] = { 8, 8, 9, 4, 0 }, - [0x49] = { 8, 16, 9, 4, NAND_BUSWIDTH_16 }, - [0x59] = { 8, 16, 9, 4, NAND_BUSWIDTH_16 }, - - [0x33] = { 16, 8, 9, 5, 0 }, - [0x73] = { 16, 8, 9, 5, 0 }, - [0x43] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x53] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 }, - - [0x35] = { 32, 8, 9, 5, 0 }, - [0x75] = { 32, 8, 9, 5, 0 }, - [0x45] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x55] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 }, - - [0x36] = { 64, 8, 9, 5, 0 }, - [0x76] = { 64, 8, 9, 5, 0 }, - [0x46] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x56] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 }, - - [0x78] = { 128, 8, 9, 5, 0 }, - [0x39] = { 128, 8, 9, 5, 0 }, - [0x79] = { 128, 8, 9, 5, 0 }, - [0x72] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x49] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x74] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x59] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, - - [0x71] = { 256, 8, 9, 5, 0 }, - - /* - * These are the new chips with large page size. The pagesize and the - * erasesize is determined from the extended id bytes - */ -# define LP_OPTIONS (NAND_SAMSUNG_LP | NAND_NO_READRDY | NAND_NO_AUTOINCR) -# define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) - - /* 512 Megabit */ - [0xa2] = { 64, 8, 0, 0, LP_OPTIONS }, - [0xf2] = { 64, 8, 0, 0, LP_OPTIONS }, - [0xb2] = { 64, 16, 0, 0, LP_OPTIONS16 }, - [0xc2] = { 64, 16, 0, 0, LP_OPTIONS16 }, - - /* 1 Gigabit */ - [0xa1] = { 128, 8, 0, 0, LP_OPTIONS }, - [0xf1] = { 128, 8, 0, 0, LP_OPTIONS }, - [0xb1] = { 128, 16, 0, 0, LP_OPTIONS16 }, - [0xc1] = { 128, 16, 0, 0, LP_OPTIONS16 }, - - /* 2 Gigabit */ - [0xaa] = { 256, 8, 0, 0, LP_OPTIONS }, - [0xda] = { 256, 8, 0, 0, LP_OPTIONS }, - [0xba] = { 256, 16, 0, 0, LP_OPTIONS16 }, - [0xca] = { 256, 16, 0, 0, LP_OPTIONS16 }, - - /* 4 Gigabit */ - [0xac] = { 512, 8, 0, 0, LP_OPTIONS }, - [0xdc] = { 512, 8, 0, 0, LP_OPTIONS }, - [0xbc] = { 512, 16, 0, 0, LP_OPTIONS16 }, - [0xcc] = { 512, 16, 0, 0, LP_OPTIONS16 }, - - /* 8 Gigabit */ - [0xa3] = { 1024, 8, 0, 0, LP_OPTIONS }, - [0xd3] = { 1024, 8, 0, 0, LP_OPTIONS }, - [0xb3] = { 1024, 16, 0, 0, LP_OPTIONS16 }, - [0xc3] = { 1024, 16, 0, 0, LP_OPTIONS16 }, - - /* 16 Gigabit */ - [0xa5] = { 2048, 8, 0, 0, LP_OPTIONS }, - [0xd5] = { 2048, 8, 0, 0, LP_OPTIONS }, - [0xb5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, - [0xc5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, -}; - -static void nand_reset(DeviceState *dev) -{ - NANDFlashState *s = NAND(dev); - s->cmd = NAND_CMD_READ0; - s->addr = 0; - s->addrlen = 0; - s->iolen = 0; - s->offset = 0; - s->status &= NAND_IOSTATUS_UNPROTCT; - s->status |= NAND_IOSTATUS_READY; -} - -static inline void nand_pushio_byte(NANDFlashState *s, uint8_t value) -{ - s->ioaddr[s->iolen++] = value; - for (value = s->buswidth; --value;) { - s->ioaddr[s->iolen++] = 0; - } -} - -static void nand_command(NANDFlashState *s) -{ - unsigned int offset; - switch (s->cmd) { - case NAND_CMD_READ0: - s->iolen = 0; - break; - - case NAND_CMD_READID: - s->ioaddr = s->io; - s->iolen = 0; - nand_pushio_byte(s, s->manf_id); - nand_pushio_byte(s, s->chip_id); - nand_pushio_byte(s, 'Q'); /* Don't-care byte (often 0xa5) */ - if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { - /* Page Size, Block Size, Spare Size; bit 6 indicates - * 8 vs 16 bit width NAND. - */ - nand_pushio_byte(s, (s->buswidth == 2) ? 0x55 : 0x15); - } else { - nand_pushio_byte(s, 0xc0); /* Multi-plane */ - } - break; - - case NAND_CMD_RANDOMREAD2: - case NAND_CMD_NOSERIALREAD2: - if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)) - break; - offset = s->addr & ((1 << s->addr_shift) - 1); - s->blk_load(s, s->addr, offset); - if (s->gnd) - s->iolen = (1 << s->page_shift) - offset; - else - s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset; - break; - - case NAND_CMD_RESET: - nand_reset(DEVICE(s)); - break; - - case NAND_CMD_PAGEPROGRAM1: - s->ioaddr = s->io; - s->iolen = 0; - break; - - case NAND_CMD_PAGEPROGRAM2: - if (s->wp) { - s->blk_write(s); - } - break; - - case NAND_CMD_BLOCKERASE1: - break; - - case NAND_CMD_BLOCKERASE2: - s->addr &= (1ull << s->addrlen * 8) - 1; - s->addr <<= nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP ? - 16 : 8; - - if (s->wp) { - s->blk_erase(s); - } - break; - - case NAND_CMD_READSTATUS: - s->ioaddr = s->io; - s->iolen = 0; - nand_pushio_byte(s, s->status); - break; - - default: - printf("%s: Unknown NAND command 0x%02x\n", __FUNCTION__, s->cmd); - } -} - -static void nand_pre_save(void *opaque) -{ - NANDFlashState *s = NAND(opaque); - - s->ioaddr_vmstate = s->ioaddr - s->io; -} - -static int nand_post_load(void *opaque, int version_id) -{ - NANDFlashState *s = NAND(opaque); - - if (s->ioaddr_vmstate > sizeof(s->io)) { - return -EINVAL; - } - s->ioaddr = s->io + s->ioaddr_vmstate; - - return 0; -} - -static const VMStateDescription vmstate_nand = { - .name = "nand", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = nand_pre_save, - .post_load = nand_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(cle, NANDFlashState), - VMSTATE_UINT8(ale, NANDFlashState), - VMSTATE_UINT8(ce, NANDFlashState), - VMSTATE_UINT8(wp, NANDFlashState), - VMSTATE_UINT8(gnd, NANDFlashState), - VMSTATE_BUFFER(io, NANDFlashState), - VMSTATE_UINT32(ioaddr_vmstate, NANDFlashState), - VMSTATE_INT32(iolen, NANDFlashState), - VMSTATE_UINT32(cmd, NANDFlashState), - VMSTATE_UINT64(addr, NANDFlashState), - VMSTATE_INT32(addrlen, NANDFlashState), - VMSTATE_INT32(status, NANDFlashState), - VMSTATE_INT32(offset, NANDFlashState), - /* XXX: do we want to save s->storage too? */ - VMSTATE_END_OF_LIST() - } -}; - -static void nand_realize(DeviceState *dev, Error **errp) -{ - int pagesize; - NANDFlashState *s = NAND(dev); - - s->buswidth = nand_flash_ids[s->chip_id].width >> 3; - s->size = nand_flash_ids[s->chip_id].size << 20; - if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { - s->page_shift = 11; - s->erase_shift = 6; - } else { - s->page_shift = nand_flash_ids[s->chip_id].page_shift; - s->erase_shift = nand_flash_ids[s->chip_id].erase_shift; - } - - switch (1 << s->page_shift) { - case 256: - nand_init_256(s); - break; - case 512: - nand_init_512(s); - break; - case 2048: - nand_init_2048(s); - break; - default: - error_setg(errp, "Unsupported NAND block size %#x", - 1 << s->page_shift); - return; - } - - pagesize = 1 << s->oob_shift; - s->mem_oob = 1; - if (s->blk) { - if (blk_is_read_only(s->blk)) { - error_setg(errp, "Can't use a read-only drive"); - return; - } - if (blk_getlength(s->blk) >= - (s->pages << s->page_shift) + (s->pages << s->oob_shift)) { - pagesize = 0; - s->mem_oob = 0; - } - } else { - pagesize += 1 << s->page_shift; - } - if (pagesize) { - s->storage = (uint8_t *) memset(g_malloc(s->pages * pagesize), - 0xff, s->pages * pagesize); - } - /* Give s->ioaddr a sane value in case we save state before it is used. */ - s->ioaddr = s->io; -} - -static Property nand_properties[] = { - DEFINE_PROP_UINT8("manufacturer_id", NANDFlashState, manf_id, 0), - DEFINE_PROP_UINT8("chip_id", NANDFlashState, chip_id, 0), - DEFINE_PROP_DRIVE("drive", NANDFlashState, blk), - DEFINE_PROP_END_OF_LIST(), -}; - -static void nand_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = nand_realize; - dc->reset = nand_reset; - dc->vmsd = &vmstate_nand; - dc->props = nand_properties; -} - -static const TypeInfo nand_info = { - .name = TYPE_NAND, - .parent = TYPE_DEVICE, - .instance_size = sizeof(NANDFlashState), - .class_init = nand_class_init, -}; - -static void nand_register_types(void) -{ - type_register_static(&nand_info); -} - -/* - * Chip inputs are CLE, ALE, CE, WP, GND and eight I/O pins. Chip - * outputs are R/B and eight I/O pins. - * - * CE, WP and R/B are active low. - */ -void nand_setpins(DeviceState *dev, uint8_t cle, uint8_t ale, - uint8_t ce, uint8_t wp, uint8_t gnd) -{ - NANDFlashState *s = NAND(dev); - - s->cle = cle; - s->ale = ale; - s->ce = ce; - s->wp = wp; - s->gnd = gnd; - if (wp) { - s->status |= NAND_IOSTATUS_UNPROTCT; - } else { - s->status &= ~NAND_IOSTATUS_UNPROTCT; - } -} - -void nand_getpins(DeviceState *dev, int *rb) -{ - *rb = 1; -} - -void nand_setio(DeviceState *dev, uint32_t value) -{ - int i; - NANDFlashState *s = NAND(dev); - - if (!s->ce && s->cle) { - if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { - if (s->cmd == NAND_CMD_READ0 && value == NAND_CMD_LPREAD2) - return; - if (value == NAND_CMD_RANDOMREAD1) { - s->addr &= ~((1 << s->addr_shift) - 1); - s->addrlen = 0; - return; - } - } - if (value == NAND_CMD_READ0) { - s->offset = 0; - } else if (value == NAND_CMD_READ1) { - s->offset = 0x100; - value = NAND_CMD_READ0; - } else if (value == NAND_CMD_READ2) { - s->offset = 1 << s->page_shift; - value = NAND_CMD_READ0; - } - - s->cmd = value; - - if (s->cmd == NAND_CMD_READSTATUS || - s->cmd == NAND_CMD_PAGEPROGRAM2 || - s->cmd == NAND_CMD_BLOCKERASE1 || - s->cmd == NAND_CMD_BLOCKERASE2 || - s->cmd == NAND_CMD_NOSERIALREAD2 || - s->cmd == NAND_CMD_RANDOMREAD2 || - s->cmd == NAND_CMD_RESET) { - nand_command(s); - } - - if (s->cmd != NAND_CMD_RANDOMREAD2) { - s->addrlen = 0; - } - } - - if (s->ale) { - unsigned int shift = s->addrlen * 8; - uint64_t mask = ~(0xffull << shift); - uint64_t v = (uint64_t)value << shift; - - s->addr = (s->addr & mask) | v; - s->addrlen ++; - - switch (s->addrlen) { - case 1: - if (s->cmd == NAND_CMD_READID) { - nand_command(s); - } - break; - case 2: /* fix cache address as a byte address */ - s->addr <<= (s->buswidth - 1); - break; - case 3: - if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && - (s->cmd == NAND_CMD_READ0 || - s->cmd == NAND_CMD_PAGEPROGRAM1)) { - nand_command(s); - } - break; - case 4: - if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && - nand_flash_ids[s->chip_id].size < 256 && /* 1Gb or less */ - (s->cmd == NAND_CMD_READ0 || - s->cmd == NAND_CMD_PAGEPROGRAM1)) { - nand_command(s); - } - break; - case 5: - if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && - nand_flash_ids[s->chip_id].size >= 256 && /* 2Gb or more */ - (s->cmd == NAND_CMD_READ0 || - s->cmd == NAND_CMD_PAGEPROGRAM1)) { - nand_command(s); - } - break; - default: - break; - } - } - - if (!s->cle && !s->ale && s->cmd == NAND_CMD_PAGEPROGRAM1) { - if (s->iolen < (1 << s->page_shift) + (1 << s->oob_shift)) { - for (i = s->buswidth; i--; value >>= 8) { - s->io[s->iolen ++] = (uint8_t) (value & 0xff); - } - } - } else if (!s->cle && !s->ale && s->cmd == NAND_CMD_COPYBACKPRG1) { - if ((s->addr & ((1 << s->addr_shift) - 1)) < - (1 << s->page_shift) + (1 << s->oob_shift)) { - for (i = s->buswidth; i--; s->addr++, value >>= 8) { - s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] = - (uint8_t) (value & 0xff); - } - } - } -} - -uint32_t nand_getio(DeviceState *dev) -{ - int offset; - uint32_t x = 0; - NANDFlashState *s = NAND(dev); - - /* Allow sequential reading */ - if (!s->iolen && s->cmd == NAND_CMD_READ0) { - offset = (int) (s->addr & ((1 << s->addr_shift) - 1)) + s->offset; - s->offset = 0; - - s->blk_load(s, s->addr, offset); - if (s->gnd) - s->iolen = (1 << s->page_shift) - offset; - else - s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset; - } - - if (s->ce || s->iolen <= 0) { - return 0; - } - - for (offset = s->buswidth; offset--;) { - x |= s->ioaddr[offset] << (offset << 3); - } - /* after receiving READ STATUS command all subsequent reads will - * return the status register value until another command is issued - */ - if (s->cmd != NAND_CMD_READSTATUS) { - s->addr += s->buswidth; - s->ioaddr += s->buswidth; - s->iolen -= s->buswidth; - } - return x; -} - -uint32_t nand_getbuswidth(DeviceState *dev) -{ - NANDFlashState *s = (NANDFlashState *) dev; - return s->buswidth << 3; -} - -DeviceState *nand_init(BlockBackend *blk, int manf_id, int chip_id) -{ - DeviceState *dev; - - if (nand_flash_ids[chip_id].size == 0) { - hw_error("%s: Unsupported NAND chip ID.\n", __FUNCTION__); - } - dev = DEVICE(object_new(TYPE_NAND)); - qdev_prop_set_uint8(dev, "manufacturer_id", manf_id); - qdev_prop_set_uint8(dev, "chip_id", chip_id); - if (blk) { - qdev_prop_set_drive(dev, "drive", blk, &error_fatal); - } - - qdev_init_nofail(dev); - return dev; -} - -type_init(nand_register_types) - -#else - -/* Program a single page */ -static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s) -{ - uint64_t off, page, sector, soff; - uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200]; - if (PAGE(s->addr) >= s->pages) - return; - - if (!s->blk) { - mem_and(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) + - s->offset, s->io, s->iolen); - } else if (s->mem_oob) { - sector = SECTOR(s->addr); - off = (s->addr & PAGE_MASK) + s->offset; - soff = SECTOR_OFFSET(s->addr); - if (blk_read(s->blk, sector, iobuf, PAGE_SECTORS) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", __func__, sector); - return; - } - - mem_and(iobuf + (soff | off), s->io, MIN(s->iolen, PAGE_SIZE - off)); - if (off + s->iolen > PAGE_SIZE) { - page = PAGE(s->addr); - mem_and(s->storage + (page << OOB_SHIFT), s->io + PAGE_SIZE - off, - MIN(OOB_SIZE, off + s->iolen - PAGE_SIZE)); - } - - if (blk_write(s->blk, sector, iobuf, PAGE_SECTORS) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, sector); - } - } else { - off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset; - sector = off >> 9; - soff = off & 0x1ff; - if (blk_read(s->blk, sector, iobuf, PAGE_SECTORS + 2) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", __func__, sector); - return; - } - - mem_and(iobuf + soff, s->io, s->iolen); - - if (blk_write(s->blk, sector, iobuf, PAGE_SECTORS + 2) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, sector); - } - } - s->offset = 0; -} - -/* Erase a single block */ -static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s) -{ - uint64_t i, page, addr; - uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, }; - addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1); - - if (PAGE(addr) >= s->pages) { - return; - } - - if (!s->blk) { - memset(s->storage + PAGE_START(addr), - 0xff, (PAGE_SIZE + OOB_SIZE) << s->erase_shift); - } else if (s->mem_oob) { - memset(s->storage + (PAGE(addr) << OOB_SHIFT), - 0xff, OOB_SIZE << s->erase_shift); - i = SECTOR(addr); - page = SECTOR(addr + (1 << (ADDR_SHIFT + s->erase_shift))); - for (; i < page; i ++) - if (blk_write(s->blk, i, iobuf, 1) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, i); - } - } else { - addr = PAGE_START(addr); - page = addr >> 9; - if (blk_read(s->blk, page, iobuf, 1) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", __func__, page); - } - memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1); - if (blk_write(s->blk, page, iobuf, 1) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, page); - } - - memset(iobuf, 0xff, 0x200); - i = (addr & ~0x1ff) + 0x200; - for (addr += ((PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200; - i < addr; i += 0x200) { - if (blk_write(s->blk, i >> 9, iobuf, 1) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", - __func__, i >> 9); - } - } - - page = i >> 9; - if (blk_read(s->blk, page, iobuf, 1) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", __func__, page); - } - memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1); - if (blk_write(s->blk, page, iobuf, 1) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, page); - } - } -} - -static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s, - uint64_t addr, int offset) -{ - if (PAGE(addr) >= s->pages) { - return; - } - - if (s->blk) { - if (s->mem_oob) { - if (blk_read(s->blk, SECTOR(addr), s->io, PAGE_SECTORS) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", - __func__, SECTOR(addr)); - } - memcpy(s->io + SECTOR_OFFSET(s->addr) + PAGE_SIZE, - s->storage + (PAGE(s->addr) << OOB_SHIFT), - OOB_SIZE); - s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset; - } else { - if (blk_read(s->blk, PAGE_START(addr) >> 9, - s->io, (PAGE_SECTORS + 2)) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", - __func__, PAGE_START(addr) >> 9); - } - s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset; - } - } else { - memcpy(s->io, s->storage + PAGE_START(s->addr) + - offset, PAGE_SIZE + OOB_SIZE - offset); - s->ioaddr = s->io; - } -} - -static void glue(nand_init_, PAGE_SIZE)(NANDFlashState *s) -{ - s->oob_shift = PAGE_SHIFT - 5; - s->pages = s->size >> PAGE_SHIFT; - s->addr_shift = ADDR_SHIFT; - - s->blk_erase = glue(nand_blk_erase_, PAGE_SIZE); - s->blk_write = glue(nand_blk_write_, PAGE_SIZE); - s->blk_load = glue(nand_blk_load_, PAGE_SIZE); -} - -# undef PAGE_SIZE -# undef PAGE_SHIFT -# undef PAGE_SECTORS -# undef ADDR_SHIFT -#endif /* NAND_IO */ diff --git a/qemu/hw/block/nvme.c b/qemu/hw/block/nvme.c deleted file mode 100644 index 173988ee8..000000000 --- a/qemu/hw/block/nvme.c +++ /dev/null @@ -1,942 +0,0 @@ -/* - * QEMU NVM Express Controller - * - * Copyright (c) 2012, Intel Corporation - * - * Written by Keith Busch <keith.busch@intel.com> - * - * This code is licensed under the GNU GPL v2 or later. - */ - -/** - * Reference Specs: http://www.nvmexpress.org, 1.1, 1.0e - * - * http://www.nvmexpress.org/resources/ - */ - -/** - * Usage: add options: - * -drive file=<file>,if=none,id=<drive_id> - * -device nvme,drive=<drive_id>,serial=<serial>,id=<id[optional]> - */ - -#include "qemu/osdep.h" -#include <hw/block/block.h> -#include <hw/hw.h> -#include <hw/pci/msix.h> -#include <hw/pci/pci.h> -#include "sysemu/sysemu.h" -#include "qapi/error.h" -#include "qapi/visitor.h" -#include "sysemu/block-backend.h" - -#include "nvme.h" - -static void nvme_process_sq(void *opaque); - -static int nvme_check_sqid(NvmeCtrl *n, uint16_t sqid) -{ - return sqid < n->num_queues && n->sq[sqid] != NULL ? 0 : -1; -} - -static int nvme_check_cqid(NvmeCtrl *n, uint16_t cqid) -{ - return cqid < n->num_queues && n->cq[cqid] != NULL ? 0 : -1; -} - -static void nvme_inc_cq_tail(NvmeCQueue *cq) -{ - cq->tail++; - if (cq->tail >= cq->size) { - cq->tail = 0; - cq->phase = !cq->phase; - } -} - -static void nvme_inc_sq_head(NvmeSQueue *sq) -{ - sq->head = (sq->head + 1) % sq->size; -} - -static uint8_t nvme_cq_full(NvmeCQueue *cq) -{ - return (cq->tail + 1) % cq->size == cq->head; -} - -static uint8_t nvme_sq_empty(NvmeSQueue *sq) -{ - return sq->head == sq->tail; -} - -static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq) -{ - if (cq->irq_enabled) { - if (msix_enabled(&(n->parent_obj))) { - msix_notify(&(n->parent_obj), cq->vector); - } else { - pci_irq_pulse(&n->parent_obj); - } - } -} - -static uint16_t nvme_map_prp(QEMUSGList *qsg, uint64_t prp1, uint64_t prp2, - uint32_t len, NvmeCtrl *n) -{ - hwaddr trans_len = n->page_size - (prp1 % n->page_size); - trans_len = MIN(len, trans_len); - int num_prps = (len >> n->page_bits) + 1; - - if (!prp1) { - return NVME_INVALID_FIELD | NVME_DNR; - } - - pci_dma_sglist_init(qsg, &n->parent_obj, num_prps); - qemu_sglist_add(qsg, prp1, trans_len); - len -= trans_len; - if (len) { - if (!prp2) { - goto unmap; - } - if (len > n->page_size) { - uint64_t prp_list[n->max_prp_ents]; - uint32_t nents, prp_trans; - int i = 0; - - nents = (len + n->page_size - 1) >> n->page_bits; - prp_trans = MIN(n->max_prp_ents, nents) * sizeof(uint64_t); - pci_dma_read(&n->parent_obj, prp2, (void *)prp_list, prp_trans); - while (len != 0) { - uint64_t prp_ent = le64_to_cpu(prp_list[i]); - - if (i == n->max_prp_ents - 1 && len > n->page_size) { - if (!prp_ent || prp_ent & (n->page_size - 1)) { - goto unmap; - } - - i = 0; - nents = (len + n->page_size - 1) >> n->page_bits; - prp_trans = MIN(n->max_prp_ents, nents) * sizeof(uint64_t); - pci_dma_read(&n->parent_obj, prp_ent, (void *)prp_list, - prp_trans); - prp_ent = le64_to_cpu(prp_list[i]); - } - - if (!prp_ent || prp_ent & (n->page_size - 1)) { - goto unmap; - } - - trans_len = MIN(len, n->page_size); - qemu_sglist_add(qsg, prp_ent, trans_len); - len -= trans_len; - i++; - } - } else { - if (prp2 & (n->page_size - 1)) { - goto unmap; - } - qemu_sglist_add(qsg, prp2, len); - } - } - return NVME_SUCCESS; - - unmap: - qemu_sglist_destroy(qsg); - return NVME_INVALID_FIELD | NVME_DNR; -} - -static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len, - uint64_t prp1, uint64_t prp2) -{ - QEMUSGList qsg; - - if (nvme_map_prp(&qsg, prp1, prp2, len, n)) { - return NVME_INVALID_FIELD | NVME_DNR; - } - if (dma_buf_read(ptr, len, &qsg)) { - qemu_sglist_destroy(&qsg); - return NVME_INVALID_FIELD | NVME_DNR; - } - qemu_sglist_destroy(&qsg); - return NVME_SUCCESS; -} - -static void nvme_post_cqes(void *opaque) -{ - NvmeCQueue *cq = opaque; - NvmeCtrl *n = cq->ctrl; - NvmeRequest *req, *next; - - QTAILQ_FOREACH_SAFE(req, &cq->req_list, entry, next) { - NvmeSQueue *sq; - hwaddr addr; - - if (nvme_cq_full(cq)) { - break; - } - - QTAILQ_REMOVE(&cq->req_list, req, entry); - sq = req->sq; - req->cqe.status = cpu_to_le16((req->status << 1) | cq->phase); - req->cqe.sq_id = cpu_to_le16(sq->sqid); - req->cqe.sq_head = cpu_to_le16(sq->head); - addr = cq->dma_addr + cq->tail * n->cqe_size; - nvme_inc_cq_tail(cq); - pci_dma_write(&n->parent_obj, addr, (void *)&req->cqe, - sizeof(req->cqe)); - QTAILQ_INSERT_TAIL(&sq->req_list, req, entry); - } - nvme_isr_notify(n, cq); -} - -static void nvme_enqueue_req_completion(NvmeCQueue *cq, NvmeRequest *req) -{ - assert(cq->cqid == req->sq->cqid); - QTAILQ_REMOVE(&req->sq->out_req_list, req, entry); - QTAILQ_INSERT_TAIL(&cq->req_list, req, entry); - timer_mod(cq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500); -} - -static void nvme_rw_cb(void *opaque, int ret) -{ - NvmeRequest *req = opaque; - NvmeSQueue *sq = req->sq; - NvmeCtrl *n = sq->ctrl; - NvmeCQueue *cq = n->cq[sq->cqid]; - - if (!ret) { - block_acct_done(blk_get_stats(n->conf.blk), &req->acct); - req->status = NVME_SUCCESS; - } else { - block_acct_failed(blk_get_stats(n->conf.blk), &req->acct); - req->status = NVME_INTERNAL_DEV_ERROR; - } - if (req->has_sg) { - qemu_sglist_destroy(&req->qsg); - } - nvme_enqueue_req_completion(cq, req); -} - -static uint16_t nvme_flush(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, - NvmeRequest *req) -{ - req->has_sg = false; - block_acct_start(blk_get_stats(n->conf.blk), &req->acct, 0, - BLOCK_ACCT_FLUSH); - req->aiocb = blk_aio_flush(n->conf.blk, nvme_rw_cb, req); - - return NVME_NO_COMPLETE; -} - -static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, - NvmeRequest *req) -{ - NvmeRwCmd *rw = (NvmeRwCmd *)cmd; - uint32_t nlb = le32_to_cpu(rw->nlb) + 1; - uint64_t slba = le64_to_cpu(rw->slba); - uint64_t prp1 = le64_to_cpu(rw->prp1); - uint64_t prp2 = le64_to_cpu(rw->prp2); - - uint8_t lba_index = NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas); - uint8_t data_shift = ns->id_ns.lbaf[lba_index].ds; - uint64_t data_size = (uint64_t)nlb << data_shift; - uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS); - int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0; - enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ; - - if ((slba + nlb) > ns->id_ns.nsze) { - block_acct_invalid(blk_get_stats(n->conf.blk), acct); - return NVME_LBA_RANGE | NVME_DNR; - } - - if (nvme_map_prp(&req->qsg, prp1, prp2, data_size, n)) { - block_acct_invalid(blk_get_stats(n->conf.blk), acct); - return NVME_INVALID_FIELD | NVME_DNR; - } - - assert((nlb << data_shift) == req->qsg.size); - - req->has_sg = true; - dma_acct_start(n->conf.blk, &req->acct, &req->qsg, acct); - req->aiocb = is_write ? - dma_blk_write(n->conf.blk, &req->qsg, aio_slba, nvme_rw_cb, req) : - dma_blk_read(n->conf.blk, &req->qsg, aio_slba, nvme_rw_cb, req); - - return NVME_NO_COMPLETE; -} - -static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) -{ - NvmeNamespace *ns; - uint32_t nsid = le32_to_cpu(cmd->nsid); - - if (nsid == 0 || nsid > n->num_namespaces) { - return NVME_INVALID_NSID | NVME_DNR; - } - - ns = &n->namespaces[nsid - 1]; - switch (cmd->opcode) { - case NVME_CMD_FLUSH: - return nvme_flush(n, ns, cmd, req); - case NVME_CMD_WRITE: - case NVME_CMD_READ: - return nvme_rw(n, ns, cmd, req); - default: - return NVME_INVALID_OPCODE | NVME_DNR; - } -} - -static void nvme_free_sq(NvmeSQueue *sq, NvmeCtrl *n) -{ - n->sq[sq->sqid] = NULL; - timer_del(sq->timer); - timer_free(sq->timer); - g_free(sq->io_req); - if (sq->sqid) { - g_free(sq); - } -} - -static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeCmd *cmd) -{ - NvmeDeleteQ *c = (NvmeDeleteQ *)cmd; - NvmeRequest *req, *next; - NvmeSQueue *sq; - NvmeCQueue *cq; - uint16_t qid = le16_to_cpu(c->qid); - - if (!qid || nvme_check_sqid(n, qid)) { - return NVME_INVALID_QID | NVME_DNR; - } - - sq = n->sq[qid]; - while (!QTAILQ_EMPTY(&sq->out_req_list)) { - req = QTAILQ_FIRST(&sq->out_req_list); - assert(req->aiocb); - blk_aio_cancel(req->aiocb); - } - if (!nvme_check_cqid(n, sq->cqid)) { - cq = n->cq[sq->cqid]; - QTAILQ_REMOVE(&cq->sq_list, sq, entry); - - nvme_post_cqes(cq); - QTAILQ_FOREACH_SAFE(req, &cq->req_list, entry, next) { - if (req->sq == sq) { - QTAILQ_REMOVE(&cq->req_list, req, entry); - QTAILQ_INSERT_TAIL(&sq->req_list, req, entry); - } - } - } - - nvme_free_sq(sq, n); - return NVME_SUCCESS; -} - -static void nvme_init_sq(NvmeSQueue *sq, NvmeCtrl *n, uint64_t dma_addr, - uint16_t sqid, uint16_t cqid, uint16_t size) -{ - int i; - NvmeCQueue *cq; - - sq->ctrl = n; - sq->dma_addr = dma_addr; - sq->sqid = sqid; - sq->size = size; - sq->cqid = cqid; - sq->head = sq->tail = 0; - sq->io_req = g_new(NvmeRequest, sq->size); - - QTAILQ_INIT(&sq->req_list); - QTAILQ_INIT(&sq->out_req_list); - for (i = 0; i < sq->size; i++) { - sq->io_req[i].sq = sq; - QTAILQ_INSERT_TAIL(&(sq->req_list), &sq->io_req[i], entry); - } - sq->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, nvme_process_sq, sq); - - assert(n->cq[cqid]); - cq = n->cq[cqid]; - QTAILQ_INSERT_TAIL(&(cq->sq_list), sq, entry); - n->sq[sqid] = sq; -} - -static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeCmd *cmd) -{ - NvmeSQueue *sq; - NvmeCreateSq *c = (NvmeCreateSq *)cmd; - - uint16_t cqid = le16_to_cpu(c->cqid); - uint16_t sqid = le16_to_cpu(c->sqid); - uint16_t qsize = le16_to_cpu(c->qsize); - uint16_t qflags = le16_to_cpu(c->sq_flags); - uint64_t prp1 = le64_to_cpu(c->prp1); - - if (!cqid || nvme_check_cqid(n, cqid)) { - return NVME_INVALID_CQID | NVME_DNR; - } - if (!sqid || (sqid && !nvme_check_sqid(n, sqid))) { - return NVME_INVALID_QID | NVME_DNR; - } - if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) { - return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR; - } - if (!prp1 || prp1 & (n->page_size - 1)) { - return NVME_INVALID_FIELD | NVME_DNR; - } - if (!(NVME_SQ_FLAGS_PC(qflags))) { - return NVME_INVALID_FIELD | NVME_DNR; - } - sq = g_malloc0(sizeof(*sq)); - nvme_init_sq(sq, n, prp1, sqid, cqid, qsize + 1); - return NVME_SUCCESS; -} - -static void nvme_free_cq(NvmeCQueue *cq, NvmeCtrl *n) -{ - n->cq[cq->cqid] = NULL; - timer_del(cq->timer); - timer_free(cq->timer); - msix_vector_unuse(&n->parent_obj, cq->vector); - if (cq->cqid) { - g_free(cq); - } -} - -static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeCmd *cmd) -{ - NvmeDeleteQ *c = (NvmeDeleteQ *)cmd; - NvmeCQueue *cq; - uint16_t qid = le16_to_cpu(c->qid); - - if (!qid || nvme_check_cqid(n, qid)) { - return NVME_INVALID_CQID | NVME_DNR; - } - - cq = n->cq[qid]; - if (!QTAILQ_EMPTY(&cq->sq_list)) { - return NVME_INVALID_QUEUE_DEL; - } - nvme_free_cq(cq, n); - return NVME_SUCCESS; -} - -static void nvme_init_cq(NvmeCQueue *cq, NvmeCtrl *n, uint64_t dma_addr, - uint16_t cqid, uint16_t vector, uint16_t size, uint16_t irq_enabled) -{ - cq->ctrl = n; - cq->cqid = cqid; - cq->size = size; - cq->dma_addr = dma_addr; - cq->phase = 1; - cq->irq_enabled = irq_enabled; - cq->vector = vector; - cq->head = cq->tail = 0; - QTAILQ_INIT(&cq->req_list); - QTAILQ_INIT(&cq->sq_list); - msix_vector_use(&n->parent_obj, cq->vector); - n->cq[cqid] = cq; - cq->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, nvme_post_cqes, cq); -} - -static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeCmd *cmd) -{ - NvmeCQueue *cq; - NvmeCreateCq *c = (NvmeCreateCq *)cmd; - uint16_t cqid = le16_to_cpu(c->cqid); - uint16_t vector = le16_to_cpu(c->irq_vector); - uint16_t qsize = le16_to_cpu(c->qsize); - uint16_t qflags = le16_to_cpu(c->cq_flags); - uint64_t prp1 = le64_to_cpu(c->prp1); - - if (!cqid || (cqid && !nvme_check_cqid(n, cqid))) { - return NVME_INVALID_CQID | NVME_DNR; - } - if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) { - return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR; - } - if (!prp1) { - return NVME_INVALID_FIELD | NVME_DNR; - } - if (vector > n->num_queues) { - return NVME_INVALID_IRQ_VECTOR | NVME_DNR; - } - if (!(NVME_CQ_FLAGS_PC(qflags))) { - return NVME_INVALID_FIELD | NVME_DNR; - } - - cq = g_malloc0(sizeof(*cq)); - nvme_init_cq(cq, n, prp1, cqid, vector, qsize + 1, - NVME_CQ_FLAGS_IEN(qflags)); - return NVME_SUCCESS; -} - -static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd) -{ - NvmeNamespace *ns; - NvmeIdentify *c = (NvmeIdentify *)cmd; - uint32_t cns = le32_to_cpu(c->cns); - uint32_t nsid = le32_to_cpu(c->nsid); - uint64_t prp1 = le64_to_cpu(c->prp1); - uint64_t prp2 = le64_to_cpu(c->prp2); - - if (cns) { - return nvme_dma_read_prp(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl), - prp1, prp2); - } - if (nsid == 0 || nsid > n->num_namespaces) { - return NVME_INVALID_NSID | NVME_DNR; - } - - ns = &n->namespaces[nsid - 1]; - return nvme_dma_read_prp(n, (uint8_t *)&ns->id_ns, sizeof(ns->id_ns), - prp1, prp2); -} - -static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) -{ - uint32_t dw10 = le32_to_cpu(cmd->cdw10); - uint32_t result; - - switch (dw10) { - case NVME_VOLATILE_WRITE_CACHE: - result = blk_enable_write_cache(n->conf.blk); - break; - case NVME_NUMBER_OF_QUEUES: - result = cpu_to_le32((n->num_queues - 1) | ((n->num_queues - 1) << 16)); - break; - default: - return NVME_INVALID_FIELD | NVME_DNR; - } - - req->cqe.result = result; - return NVME_SUCCESS; -} - -static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) -{ - uint32_t dw10 = le32_to_cpu(cmd->cdw10); - uint32_t dw11 = le32_to_cpu(cmd->cdw11); - - switch (dw10) { - case NVME_VOLATILE_WRITE_CACHE: - blk_set_enable_write_cache(n->conf.blk, dw11 & 1); - break; - case NVME_NUMBER_OF_QUEUES: - req->cqe.result = - cpu_to_le32((n->num_queues - 1) | ((n->num_queues - 1) << 16)); - break; - default: - return NVME_INVALID_FIELD | NVME_DNR; - } - return NVME_SUCCESS; -} - -static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) -{ - switch (cmd->opcode) { - case NVME_ADM_CMD_DELETE_SQ: - return nvme_del_sq(n, cmd); - case NVME_ADM_CMD_CREATE_SQ: - return nvme_create_sq(n, cmd); - case NVME_ADM_CMD_DELETE_CQ: - return nvme_del_cq(n, cmd); - case NVME_ADM_CMD_CREATE_CQ: - return nvme_create_cq(n, cmd); - case NVME_ADM_CMD_IDENTIFY: - return nvme_identify(n, cmd); - case NVME_ADM_CMD_SET_FEATURES: - return nvme_set_feature(n, cmd, req); - case NVME_ADM_CMD_GET_FEATURES: - return nvme_get_feature(n, cmd, req); - default: - return NVME_INVALID_OPCODE | NVME_DNR; - } -} - -static void nvme_process_sq(void *opaque) -{ - NvmeSQueue *sq = opaque; - NvmeCtrl *n = sq->ctrl; - NvmeCQueue *cq = n->cq[sq->cqid]; - - uint16_t status; - hwaddr addr; - NvmeCmd cmd; - NvmeRequest *req; - - while (!(nvme_sq_empty(sq) || QTAILQ_EMPTY(&sq->req_list))) { - addr = sq->dma_addr + sq->head * n->sqe_size; - pci_dma_read(&n->parent_obj, addr, (void *)&cmd, sizeof(cmd)); - nvme_inc_sq_head(sq); - - req = QTAILQ_FIRST(&sq->req_list); - QTAILQ_REMOVE(&sq->req_list, req, entry); - QTAILQ_INSERT_TAIL(&sq->out_req_list, req, entry); - memset(&req->cqe, 0, sizeof(req->cqe)); - req->cqe.cid = cmd.cid; - - status = sq->sqid ? nvme_io_cmd(n, &cmd, req) : - nvme_admin_cmd(n, &cmd, req); - if (status != NVME_NO_COMPLETE) { - req->status = status; - nvme_enqueue_req_completion(cq, req); - } - } -} - -static void nvme_clear_ctrl(NvmeCtrl *n) -{ - int i; - - for (i = 0; i < n->num_queues; i++) { - if (n->sq[i] != NULL) { - nvme_free_sq(n->sq[i], n); - } - } - for (i = 0; i < n->num_queues; i++) { - if (n->cq[i] != NULL) { - nvme_free_cq(n->cq[i], n); - } - } - - blk_flush(n->conf.blk); - n->bar.cc = 0; -} - -static int nvme_start_ctrl(NvmeCtrl *n) -{ - uint32_t page_bits = NVME_CC_MPS(n->bar.cc) + 12; - uint32_t page_size = 1 << page_bits; - - if (n->cq[0] || n->sq[0] || !n->bar.asq || !n->bar.acq || - n->bar.asq & (page_size - 1) || n->bar.acq & (page_size - 1) || - NVME_CC_MPS(n->bar.cc) < NVME_CAP_MPSMIN(n->bar.cap) || - NVME_CC_MPS(n->bar.cc) > NVME_CAP_MPSMAX(n->bar.cap) || - NVME_CC_IOCQES(n->bar.cc) < NVME_CTRL_CQES_MIN(n->id_ctrl.cqes) || - NVME_CC_IOCQES(n->bar.cc) > NVME_CTRL_CQES_MAX(n->id_ctrl.cqes) || - NVME_CC_IOSQES(n->bar.cc) < NVME_CTRL_SQES_MIN(n->id_ctrl.sqes) || - NVME_CC_IOSQES(n->bar.cc) > NVME_CTRL_SQES_MAX(n->id_ctrl.sqes) || - !NVME_AQA_ASQS(n->bar.aqa) || !NVME_AQA_ACQS(n->bar.aqa)) { - return -1; - } - - n->page_bits = page_bits; - n->page_size = page_size; - n->max_prp_ents = n->page_size / sizeof(uint64_t); - n->cqe_size = 1 << NVME_CC_IOCQES(n->bar.cc); - n->sqe_size = 1 << NVME_CC_IOSQES(n->bar.cc); - nvme_init_cq(&n->admin_cq, n, n->bar.acq, 0, 0, - NVME_AQA_ACQS(n->bar.aqa) + 1, 1); - nvme_init_sq(&n->admin_sq, n, n->bar.asq, 0, 0, - NVME_AQA_ASQS(n->bar.aqa) + 1); - - return 0; -} - -static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data, - unsigned size) -{ - switch (offset) { - case 0xc: - n->bar.intms |= data & 0xffffffff; - n->bar.intmc = n->bar.intms; - break; - case 0x10: - n->bar.intms &= ~(data & 0xffffffff); - n->bar.intmc = n->bar.intms; - break; - case 0x14: - /* Windows first sends data, then sends enable bit */ - if (!NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc) && - !NVME_CC_SHN(data) && !NVME_CC_SHN(n->bar.cc)) - { - n->bar.cc = data; - } - - if (NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc)) { - n->bar.cc = data; - if (nvme_start_ctrl(n)) { - n->bar.csts = NVME_CSTS_FAILED; - } else { - n->bar.csts = NVME_CSTS_READY; - } - } else if (!NVME_CC_EN(data) && NVME_CC_EN(n->bar.cc)) { - nvme_clear_ctrl(n); - n->bar.csts &= ~NVME_CSTS_READY; - } - if (NVME_CC_SHN(data) && !(NVME_CC_SHN(n->bar.cc))) { - nvme_clear_ctrl(n); - n->bar.cc = data; - n->bar.csts |= NVME_CSTS_SHST_COMPLETE; - } else if (!NVME_CC_SHN(data) && NVME_CC_SHN(n->bar.cc)) { - n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE; - n->bar.cc = data; - } - break; - case 0x24: - n->bar.aqa = data & 0xffffffff; - break; - case 0x28: - n->bar.asq = data; - break; - case 0x2c: - n->bar.asq |= data << 32; - break; - case 0x30: - n->bar.acq = data; - break; - case 0x34: - n->bar.acq |= data << 32; - break; - default: - break; - } -} - -static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size) -{ - NvmeCtrl *n = (NvmeCtrl *)opaque; - uint8_t *ptr = (uint8_t *)&n->bar; - uint64_t val = 0; - - if (addr < sizeof(n->bar)) { - memcpy(&val, ptr + addr, size); - } - return val; -} - -static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val) -{ - uint32_t qid; - - if (addr & ((1 << 2) - 1)) { - return; - } - - if (((addr - 0x1000) >> 2) & 1) { - uint16_t new_head = val & 0xffff; - int start_sqs; - NvmeCQueue *cq; - - qid = (addr - (0x1000 + (1 << 2))) >> 3; - if (nvme_check_cqid(n, qid)) { - return; - } - - cq = n->cq[qid]; - if (new_head >= cq->size) { - return; - } - - start_sqs = nvme_cq_full(cq) ? 1 : 0; - cq->head = new_head; - if (start_sqs) { - NvmeSQueue *sq; - QTAILQ_FOREACH(sq, &cq->sq_list, entry) { - timer_mod(sq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500); - } - timer_mod(cq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500); - } - - if (cq->tail != cq->head) { - nvme_isr_notify(n, cq); - } - } else { - uint16_t new_tail = val & 0xffff; - NvmeSQueue *sq; - - qid = (addr - 0x1000) >> 3; - if (nvme_check_sqid(n, qid)) { - return; - } - - sq = n->sq[qid]; - if (new_tail >= sq->size) { - return; - } - - sq->tail = new_tail; - timer_mod(sq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500); - } -} - -static void nvme_mmio_write(void *opaque, hwaddr addr, uint64_t data, - unsigned size) -{ - NvmeCtrl *n = (NvmeCtrl *)opaque; - if (addr < sizeof(n->bar)) { - nvme_write_bar(n, addr, data, size); - } else if (addr >= 0x1000) { - nvme_process_db(n, addr, data); - } -} - -static const MemoryRegionOps nvme_mmio_ops = { - .read = nvme_mmio_read, - .write = nvme_mmio_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 2, - .max_access_size = 8, - }, -}; - -static int nvme_init(PCIDevice *pci_dev) -{ - NvmeCtrl *n = NVME(pci_dev); - NvmeIdCtrl *id = &n->id_ctrl; - - int i; - int64_t bs_size; - uint8_t *pci_conf; - - if (!n->conf.blk) { - return -1; - } - - bs_size = blk_getlength(n->conf.blk); - if (bs_size < 0) { - return -1; - } - - blkconf_serial(&n->conf, &n->serial); - if (!n->serial) { - return -1; - } - blkconf_blocksizes(&n->conf); - - pci_conf = pci_dev->config; - pci_conf[PCI_INTERRUPT_PIN] = 1; - pci_config_set_prog_interface(pci_dev->config, 0x2); - pci_config_set_class(pci_dev->config, PCI_CLASS_STORAGE_EXPRESS); - pcie_endpoint_cap_init(&n->parent_obj, 0x80); - - n->num_namespaces = 1; - n->num_queues = 64; - n->reg_size = pow2ceil(0x1004 + 2 * (n->num_queues + 1) * 4); - n->ns_size = bs_size / (uint64_t)n->num_namespaces; - - n->namespaces = g_new0(NvmeNamespace, n->num_namespaces); - n->sq = g_new0(NvmeSQueue *, n->num_queues); - n->cq = g_new0(NvmeCQueue *, n->num_queues); - - memory_region_init_io(&n->iomem, OBJECT(n), &nvme_mmio_ops, n, - "nvme", n->reg_size); - pci_register_bar(&n->parent_obj, 0, - PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, - &n->iomem); - msix_init_exclusive_bar(&n->parent_obj, n->num_queues, 4); - - id->vid = cpu_to_le16(pci_get_word(pci_conf + PCI_VENDOR_ID)); - id->ssvid = cpu_to_le16(pci_get_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID)); - strpadcpy((char *)id->mn, sizeof(id->mn), "QEMU NVMe Ctrl", ' '); - strpadcpy((char *)id->fr, sizeof(id->fr), "1.0", ' '); - strpadcpy((char *)id->sn, sizeof(id->sn), n->serial, ' '); - id->rab = 6; - id->ieee[0] = 0x00; - id->ieee[1] = 0x02; - id->ieee[2] = 0xb3; - id->oacs = cpu_to_le16(0); - id->frmw = 7 << 1; - id->lpa = 1 << 0; - id->sqes = (0x6 << 4) | 0x6; - id->cqes = (0x4 << 4) | 0x4; - id->nn = cpu_to_le32(n->num_namespaces); - id->psd[0].mp = cpu_to_le16(0x9c4); - id->psd[0].enlat = cpu_to_le32(0x10); - id->psd[0].exlat = cpu_to_le32(0x4); - if (blk_enable_write_cache(n->conf.blk)) { - id->vwc = 1; - } - - n->bar.cap = 0; - NVME_CAP_SET_MQES(n->bar.cap, 0x7ff); - NVME_CAP_SET_CQR(n->bar.cap, 1); - NVME_CAP_SET_AMS(n->bar.cap, 1); - NVME_CAP_SET_TO(n->bar.cap, 0xf); - NVME_CAP_SET_CSS(n->bar.cap, 1); - NVME_CAP_SET_MPSMAX(n->bar.cap, 4); - - n->bar.vs = 0x00010100; - n->bar.intmc = n->bar.intms = 0; - - for (i = 0; i < n->num_namespaces; i++) { - NvmeNamespace *ns = &n->namespaces[i]; - NvmeIdNs *id_ns = &ns->id_ns; - id_ns->nsfeat = 0; - id_ns->nlbaf = 0; - id_ns->flbas = 0; - id_ns->mc = 0; - id_ns->dpc = 0; - id_ns->dps = 0; - id_ns->lbaf[0].ds = BDRV_SECTOR_BITS; - id_ns->ncap = id_ns->nuse = id_ns->nsze = - cpu_to_le64(n->ns_size >> - id_ns->lbaf[NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas)].ds); - } - return 0; -} - -static void nvme_exit(PCIDevice *pci_dev) -{ - NvmeCtrl *n = NVME(pci_dev); - - nvme_clear_ctrl(n); - g_free(n->namespaces); - g_free(n->cq); - g_free(n->sq); - msix_uninit_exclusive_bar(pci_dev); -} - -static Property nvme_props[] = { - DEFINE_BLOCK_PROPERTIES(NvmeCtrl, conf), - DEFINE_PROP_STRING("serial", NvmeCtrl, serial), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription nvme_vmstate = { - .name = "nvme", - .unmigratable = 1, -}; - -static void nvme_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); - - pc->init = nvme_init; - pc->exit = nvme_exit; - pc->class_id = PCI_CLASS_STORAGE_EXPRESS; - pc->vendor_id = PCI_VENDOR_ID_INTEL; - pc->device_id = 0x5845; - pc->revision = 1; - pc->is_express = 1; - - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->desc = "Non-Volatile Memory Express"; - dc->props = nvme_props; - dc->vmsd = &nvme_vmstate; -} - -static void nvme_instance_init(Object *obj) -{ - NvmeCtrl *s = NVME(obj); - - device_add_bootindex_property(obj, &s->conf.bootindex, - "bootindex", "/namespace@1,0", - DEVICE(obj), &error_abort); -} - -static const TypeInfo nvme_info = { - .name = "nvme", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(NvmeCtrl), - .class_init = nvme_class_init, - .instance_init = nvme_instance_init, -}; - -static void nvme_register_types(void) -{ - type_register_static(&nvme_info); -} - -type_init(nvme_register_types) diff --git a/qemu/hw/block/nvme.h b/qemu/hw/block/nvme.h deleted file mode 100644 index 8fb0c1075..000000000 --- a/qemu/hw/block/nvme.h +++ /dev/null @@ -1,713 +0,0 @@ -#ifndef HW_NVME_H -#define HW_NVME_H -#include "qemu/cutils.h" - -typedef struct NvmeBar { - uint64_t cap; - uint32_t vs; - uint32_t intms; - uint32_t intmc; - uint32_t cc; - uint32_t rsvd1; - uint32_t csts; - uint32_t nssrc; - uint32_t aqa; - uint64_t asq; - uint64_t acq; -} NvmeBar; - -enum NvmeCapShift { - CAP_MQES_SHIFT = 0, - CAP_CQR_SHIFT = 16, - CAP_AMS_SHIFT = 17, - CAP_TO_SHIFT = 24, - CAP_DSTRD_SHIFT = 32, - CAP_NSSRS_SHIFT = 33, - CAP_CSS_SHIFT = 37, - CAP_MPSMIN_SHIFT = 48, - CAP_MPSMAX_SHIFT = 52, -}; - -enum NvmeCapMask { - CAP_MQES_MASK = 0xffff, - CAP_CQR_MASK = 0x1, - CAP_AMS_MASK = 0x3, - CAP_TO_MASK = 0xff, - CAP_DSTRD_MASK = 0xf, - CAP_NSSRS_MASK = 0x1, - CAP_CSS_MASK = 0xff, - CAP_MPSMIN_MASK = 0xf, - CAP_MPSMAX_MASK = 0xf, -}; - -#define NVME_CAP_MQES(cap) (((cap) >> CAP_MQES_SHIFT) & CAP_MQES_MASK) -#define NVME_CAP_CQR(cap) (((cap) >> CAP_CQR_SHIFT) & CAP_CQR_MASK) -#define NVME_CAP_AMS(cap) (((cap) >> CAP_AMS_SHIFT) & CAP_AMS_MASK) -#define NVME_CAP_TO(cap) (((cap) >> CAP_TO_SHIFT) & CAP_TO_MASK) -#define NVME_CAP_DSTRD(cap) (((cap) >> CAP_DSTRD_SHIFT) & CAP_DSTRD_MASK) -#define NVME_CAP_NSSRS(cap) (((cap) >> CAP_NSSRS_SHIFT) & CAP_NSSRS_MASK) -#define NVME_CAP_CSS(cap) (((cap) >> CAP_CSS_SHIFT) & CAP_CSS_MASK) -#define NVME_CAP_MPSMIN(cap)(((cap) >> CAP_MPSMIN_SHIFT) & CAP_MPSMIN_MASK) -#define NVME_CAP_MPSMAX(cap)(((cap) >> CAP_MPSMAX_SHIFT) & CAP_MPSMAX_MASK) - -#define NVME_CAP_SET_MQES(cap, val) (cap |= (uint64_t)(val & CAP_MQES_MASK) \ - << CAP_MQES_SHIFT) -#define NVME_CAP_SET_CQR(cap, val) (cap |= (uint64_t)(val & CAP_CQR_MASK) \ - << CAP_CQR_SHIFT) -#define NVME_CAP_SET_AMS(cap, val) (cap |= (uint64_t)(val & CAP_AMS_MASK) \ - << CAP_AMS_SHIFT) -#define NVME_CAP_SET_TO(cap, val) (cap |= (uint64_t)(val & CAP_TO_MASK) \ - << CAP_TO_SHIFT) -#define NVME_CAP_SET_DSTRD(cap, val) (cap |= (uint64_t)(val & CAP_DSTRD_MASK) \ - << CAP_DSTRD_SHIFT) -#define NVME_CAP_SET_NSSRS(cap, val) (cap |= (uint64_t)(val & CAP_NSSRS_MASK) \ - << CAP_NSSRS_SHIFT) -#define NVME_CAP_SET_CSS(cap, val) (cap |= (uint64_t)(val & CAP_CSS_MASK) \ - << CAP_CSS_SHIFT) -#define NVME_CAP_SET_MPSMIN(cap, val) (cap |= (uint64_t)(val & CAP_MPSMIN_MASK)\ - << CAP_MPSMIN_SHIFT) -#define NVME_CAP_SET_MPSMAX(cap, val) (cap |= (uint64_t)(val & CAP_MPSMAX_MASK)\ - << CAP_MPSMAX_SHIFT) - -enum NvmeCcShift { - CC_EN_SHIFT = 0, - CC_CSS_SHIFT = 4, - CC_MPS_SHIFT = 7, - CC_AMS_SHIFT = 11, - CC_SHN_SHIFT = 14, - CC_IOSQES_SHIFT = 16, - CC_IOCQES_SHIFT = 20, -}; - -enum NvmeCcMask { - CC_EN_MASK = 0x1, - CC_CSS_MASK = 0x7, - CC_MPS_MASK = 0xf, - CC_AMS_MASK = 0x7, - CC_SHN_MASK = 0x3, - CC_IOSQES_MASK = 0xf, - CC_IOCQES_MASK = 0xf, -}; - -#define NVME_CC_EN(cc) ((cc >> CC_EN_SHIFT) & CC_EN_MASK) -#define NVME_CC_CSS(cc) ((cc >> CC_CSS_SHIFT) & CC_CSS_MASK) -#define NVME_CC_MPS(cc) ((cc >> CC_MPS_SHIFT) & CC_MPS_MASK) -#define NVME_CC_AMS(cc) ((cc >> CC_AMS_SHIFT) & CC_AMS_MASK) -#define NVME_CC_SHN(cc) ((cc >> CC_SHN_SHIFT) & CC_SHN_MASK) -#define NVME_CC_IOSQES(cc) ((cc >> CC_IOSQES_SHIFT) & CC_IOSQES_MASK) -#define NVME_CC_IOCQES(cc) ((cc >> CC_IOCQES_SHIFT) & CC_IOCQES_MASK) - -enum NvmeCstsShift { - CSTS_RDY_SHIFT = 0, - CSTS_CFS_SHIFT = 1, - CSTS_SHST_SHIFT = 2, - CSTS_NSSRO_SHIFT = 4, -}; - -enum NvmeCstsMask { - CSTS_RDY_MASK = 0x1, - CSTS_CFS_MASK = 0x1, - CSTS_SHST_MASK = 0x3, - CSTS_NSSRO_MASK = 0x1, -}; - -enum NvmeCsts { - NVME_CSTS_READY = 1 << CSTS_RDY_SHIFT, - NVME_CSTS_FAILED = 1 << CSTS_CFS_SHIFT, - NVME_CSTS_SHST_NORMAL = 0 << CSTS_SHST_SHIFT, - NVME_CSTS_SHST_PROGRESS = 1 << CSTS_SHST_SHIFT, - NVME_CSTS_SHST_COMPLETE = 2 << CSTS_SHST_SHIFT, - NVME_CSTS_NSSRO = 1 << CSTS_NSSRO_SHIFT, -}; - -#define NVME_CSTS_RDY(csts) ((csts >> CSTS_RDY_SHIFT) & CSTS_RDY_MASK) -#define NVME_CSTS_CFS(csts) ((csts >> CSTS_CFS_SHIFT) & CSTS_CFS_MASK) -#define NVME_CSTS_SHST(csts) ((csts >> CSTS_SHST_SHIFT) & CSTS_SHST_MASK) -#define NVME_CSTS_NSSRO(csts) ((csts >> CSTS_NSSRO_SHIFT) & CSTS_NSSRO_MASK) - -enum NvmeAqaShift { - AQA_ASQS_SHIFT = 0, - AQA_ACQS_SHIFT = 16, -}; - -enum NvmeAqaMask { - AQA_ASQS_MASK = 0xfff, - AQA_ACQS_MASK = 0xfff, -}; - -#define NVME_AQA_ASQS(aqa) ((aqa >> AQA_ASQS_SHIFT) & AQA_ASQS_MASK) -#define NVME_AQA_ACQS(aqa) ((aqa >> AQA_ACQS_SHIFT) & AQA_ACQS_MASK) - -typedef struct NvmeCmd { - uint8_t opcode; - uint8_t fuse; - uint16_t cid; - uint32_t nsid; - uint64_t res1; - uint64_t mptr; - uint64_t prp1; - uint64_t prp2; - uint32_t cdw10; - uint32_t cdw11; - uint32_t cdw12; - uint32_t cdw13; - uint32_t cdw14; - uint32_t cdw15; -} NvmeCmd; - -enum NvmeAdminCommands { - NVME_ADM_CMD_DELETE_SQ = 0x00, - NVME_ADM_CMD_CREATE_SQ = 0x01, - NVME_ADM_CMD_GET_LOG_PAGE = 0x02, - NVME_ADM_CMD_DELETE_CQ = 0x04, - NVME_ADM_CMD_CREATE_CQ = 0x05, - NVME_ADM_CMD_IDENTIFY = 0x06, - NVME_ADM_CMD_ABORT = 0x08, - NVME_ADM_CMD_SET_FEATURES = 0x09, - NVME_ADM_CMD_GET_FEATURES = 0x0a, - NVME_ADM_CMD_ASYNC_EV_REQ = 0x0c, - NVME_ADM_CMD_ACTIVATE_FW = 0x10, - NVME_ADM_CMD_DOWNLOAD_FW = 0x11, - NVME_ADM_CMD_FORMAT_NVM = 0x80, - NVME_ADM_CMD_SECURITY_SEND = 0x81, - NVME_ADM_CMD_SECURITY_RECV = 0x82, -}; - -enum NvmeIoCommands { - NVME_CMD_FLUSH = 0x00, - NVME_CMD_WRITE = 0x01, - NVME_CMD_READ = 0x02, - NVME_CMD_WRITE_UNCOR = 0x04, - NVME_CMD_COMPARE = 0x05, - NVME_CMD_DSM = 0x09, -}; - -typedef struct NvmeDeleteQ { - uint8_t opcode; - uint8_t flags; - uint16_t cid; - uint32_t rsvd1[9]; - uint16_t qid; - uint16_t rsvd10; - uint32_t rsvd11[5]; -} NvmeDeleteQ; - -typedef struct NvmeCreateCq { - uint8_t opcode; - uint8_t flags; - uint16_t cid; - uint32_t rsvd1[5]; - uint64_t prp1; - uint64_t rsvd8; - uint16_t cqid; - uint16_t qsize; - uint16_t cq_flags; - uint16_t irq_vector; - uint32_t rsvd12[4]; -} NvmeCreateCq; - -#define NVME_CQ_FLAGS_PC(cq_flags) (cq_flags & 0x1) -#define NVME_CQ_FLAGS_IEN(cq_flags) ((cq_flags >> 1) & 0x1) - -typedef struct NvmeCreateSq { - uint8_t opcode; - uint8_t flags; - uint16_t cid; - uint32_t rsvd1[5]; - uint64_t prp1; - uint64_t rsvd8; - uint16_t sqid; - uint16_t qsize; - uint16_t sq_flags; - uint16_t cqid; - uint32_t rsvd12[4]; -} NvmeCreateSq; - -#define NVME_SQ_FLAGS_PC(sq_flags) (sq_flags & 0x1) -#define NVME_SQ_FLAGS_QPRIO(sq_flags) ((sq_flags >> 1) & 0x3) - -enum NvmeQueueFlags { - NVME_Q_PC = 1, - NVME_Q_PRIO_URGENT = 0, - NVME_Q_PRIO_HIGH = 1, - NVME_Q_PRIO_NORMAL = 2, - NVME_Q_PRIO_LOW = 3, -}; - -typedef struct NvmeIdentify { - uint8_t opcode; - uint8_t flags; - uint16_t cid; - uint32_t nsid; - uint64_t rsvd2[2]; - uint64_t prp1; - uint64_t prp2; - uint32_t cns; - uint32_t rsvd11[5]; -} NvmeIdentify; - -typedef struct NvmeRwCmd { - uint8_t opcode; - uint8_t flags; - uint16_t cid; - uint32_t nsid; - uint64_t rsvd2; - uint64_t mptr; - uint64_t prp1; - uint64_t prp2; - uint64_t slba; - uint16_t nlb; - uint16_t control; - uint32_t dsmgmt; - uint32_t reftag; - uint16_t apptag; - uint16_t appmask; -} NvmeRwCmd; - -enum { - NVME_RW_LR = 1 << 15, - NVME_RW_FUA = 1 << 14, - NVME_RW_DSM_FREQ_UNSPEC = 0, - NVME_RW_DSM_FREQ_TYPICAL = 1, - NVME_RW_DSM_FREQ_RARE = 2, - NVME_RW_DSM_FREQ_READS = 3, - NVME_RW_DSM_FREQ_WRITES = 4, - NVME_RW_DSM_FREQ_RW = 5, - NVME_RW_DSM_FREQ_ONCE = 6, - NVME_RW_DSM_FREQ_PREFETCH = 7, - NVME_RW_DSM_FREQ_TEMP = 8, - NVME_RW_DSM_LATENCY_NONE = 0 << 4, - NVME_RW_DSM_LATENCY_IDLE = 1 << 4, - NVME_RW_DSM_LATENCY_NORM = 2 << 4, - NVME_RW_DSM_LATENCY_LOW = 3 << 4, - NVME_RW_DSM_SEQ_REQ = 1 << 6, - NVME_RW_DSM_COMPRESSED = 1 << 7, - NVME_RW_PRINFO_PRACT = 1 << 13, - NVME_RW_PRINFO_PRCHK_GUARD = 1 << 12, - NVME_RW_PRINFO_PRCHK_APP = 1 << 11, - NVME_RW_PRINFO_PRCHK_REF = 1 << 10, -}; - -typedef struct NvmeDsmCmd { - uint8_t opcode; - uint8_t flags; - uint16_t cid; - uint32_t nsid; - uint64_t rsvd2[2]; - uint64_t prp1; - uint64_t prp2; - uint32_t nr; - uint32_t attributes; - uint32_t rsvd12[4]; -} NvmeDsmCmd; - -enum { - NVME_DSMGMT_IDR = 1 << 0, - NVME_DSMGMT_IDW = 1 << 1, - NVME_DSMGMT_AD = 1 << 2, -}; - -typedef struct NvmeDsmRange { - uint32_t cattr; - uint32_t nlb; - uint64_t slba; -} NvmeDsmRange; - -enum NvmeAsyncEventRequest { - NVME_AER_TYPE_ERROR = 0, - NVME_AER_TYPE_SMART = 1, - NVME_AER_TYPE_IO_SPECIFIC = 6, - NVME_AER_TYPE_VENDOR_SPECIFIC = 7, - NVME_AER_INFO_ERR_INVALID_SQ = 0, - NVME_AER_INFO_ERR_INVALID_DB = 1, - NVME_AER_INFO_ERR_DIAG_FAIL = 2, - NVME_AER_INFO_ERR_PERS_INTERNAL_ERR = 3, - NVME_AER_INFO_ERR_TRANS_INTERNAL_ERR = 4, - NVME_AER_INFO_ERR_FW_IMG_LOAD_ERR = 5, - NVME_AER_INFO_SMART_RELIABILITY = 0, - NVME_AER_INFO_SMART_TEMP_THRESH = 1, - NVME_AER_INFO_SMART_SPARE_THRESH = 2, -}; - -typedef struct NvmeAerResult { - uint8_t event_type; - uint8_t event_info; - uint8_t log_page; - uint8_t resv; -} NvmeAerResult; - -typedef struct NvmeCqe { - uint32_t result; - uint32_t rsvd; - uint16_t sq_head; - uint16_t sq_id; - uint16_t cid; - uint16_t status; -} NvmeCqe; - -enum NvmeStatusCodes { - NVME_SUCCESS = 0x0000, - NVME_INVALID_OPCODE = 0x0001, - NVME_INVALID_FIELD = 0x0002, - NVME_CID_CONFLICT = 0x0003, - NVME_DATA_TRAS_ERROR = 0x0004, - NVME_POWER_LOSS_ABORT = 0x0005, - NVME_INTERNAL_DEV_ERROR = 0x0006, - NVME_CMD_ABORT_REQ = 0x0007, - NVME_CMD_ABORT_SQ_DEL = 0x0008, - NVME_CMD_ABORT_FAILED_FUSE = 0x0009, - NVME_CMD_ABORT_MISSING_FUSE = 0x000a, - NVME_INVALID_NSID = 0x000b, - NVME_CMD_SEQ_ERROR = 0x000c, - NVME_LBA_RANGE = 0x0080, - NVME_CAP_EXCEEDED = 0x0081, - NVME_NS_NOT_READY = 0x0082, - NVME_NS_RESV_CONFLICT = 0x0083, - NVME_INVALID_CQID = 0x0100, - NVME_INVALID_QID = 0x0101, - NVME_MAX_QSIZE_EXCEEDED = 0x0102, - NVME_ACL_EXCEEDED = 0x0103, - NVME_RESERVED = 0x0104, - NVME_AER_LIMIT_EXCEEDED = 0x0105, - NVME_INVALID_FW_SLOT = 0x0106, - NVME_INVALID_FW_IMAGE = 0x0107, - NVME_INVALID_IRQ_VECTOR = 0x0108, - NVME_INVALID_LOG_ID = 0x0109, - NVME_INVALID_FORMAT = 0x010a, - NVME_FW_REQ_RESET = 0x010b, - NVME_INVALID_QUEUE_DEL = 0x010c, - NVME_FID_NOT_SAVEABLE = 0x010d, - NVME_FID_NOT_NSID_SPEC = 0x010f, - NVME_FW_REQ_SUSYSTEM_RESET = 0x0110, - NVME_CONFLICTING_ATTRS = 0x0180, - NVME_INVALID_PROT_INFO = 0x0181, - NVME_WRITE_TO_RO = 0x0182, - NVME_WRITE_FAULT = 0x0280, - NVME_UNRECOVERED_READ = 0x0281, - NVME_E2E_GUARD_ERROR = 0x0282, - NVME_E2E_APP_ERROR = 0x0283, - NVME_E2E_REF_ERROR = 0x0284, - NVME_CMP_FAILURE = 0x0285, - NVME_ACCESS_DENIED = 0x0286, - NVME_MORE = 0x2000, - NVME_DNR = 0x4000, - NVME_NO_COMPLETE = 0xffff, -}; - -typedef struct NvmeFwSlotInfoLog { - uint8_t afi; - uint8_t reserved1[7]; - uint8_t frs1[8]; - uint8_t frs2[8]; - uint8_t frs3[8]; - uint8_t frs4[8]; - uint8_t frs5[8]; - uint8_t frs6[8]; - uint8_t frs7[8]; - uint8_t reserved2[448]; -} NvmeFwSlotInfoLog; - -typedef struct NvmeErrorLog { - uint64_t error_count; - uint16_t sqid; - uint16_t cid; - uint16_t status_field; - uint16_t param_error_location; - uint64_t lba; - uint32_t nsid; - uint8_t vs; - uint8_t resv[35]; -} NvmeErrorLog; - -typedef struct NvmeSmartLog { - uint8_t critical_warning; - uint8_t temperature[2]; - uint8_t available_spare; - uint8_t available_spare_threshold; - uint8_t percentage_used; - uint8_t reserved1[26]; - uint64_t data_units_read[2]; - uint64_t data_units_written[2]; - uint64_t host_read_commands[2]; - uint64_t host_write_commands[2]; - uint64_t controller_busy_time[2]; - uint64_t power_cycles[2]; - uint64_t power_on_hours[2]; - uint64_t unsafe_shutdowns[2]; - uint64_t media_errors[2]; - uint64_t number_of_error_log_entries[2]; - uint8_t reserved2[320]; -} NvmeSmartLog; - -enum NvmeSmartWarn { - NVME_SMART_SPARE = 1 << 0, - NVME_SMART_TEMPERATURE = 1 << 1, - NVME_SMART_RELIABILITY = 1 << 2, - NVME_SMART_MEDIA_READ_ONLY = 1 << 3, - NVME_SMART_FAILED_VOLATILE_MEDIA = 1 << 4, -}; - -enum LogIdentifier { - NVME_LOG_ERROR_INFO = 0x01, - NVME_LOG_SMART_INFO = 0x02, - NVME_LOG_FW_SLOT_INFO = 0x03, -}; - -typedef struct NvmePSD { - uint16_t mp; - uint16_t reserved; - uint32_t enlat; - uint32_t exlat; - uint8_t rrt; - uint8_t rrl; - uint8_t rwt; - uint8_t rwl; - uint8_t resv[16]; -} NvmePSD; - -typedef struct NvmeIdCtrl { - uint16_t vid; - uint16_t ssvid; - uint8_t sn[20]; - uint8_t mn[40]; - uint8_t fr[8]; - uint8_t rab; - uint8_t ieee[3]; - uint8_t cmic; - uint8_t mdts; - uint8_t rsvd255[178]; - uint16_t oacs; - uint8_t acl; - uint8_t aerl; - uint8_t frmw; - uint8_t lpa; - uint8_t elpe; - uint8_t npss; - uint8_t rsvd511[248]; - uint8_t sqes; - uint8_t cqes; - uint16_t rsvd515; - uint32_t nn; - uint16_t oncs; - uint16_t fuses; - uint8_t fna; - uint8_t vwc; - uint16_t awun; - uint16_t awupf; - uint8_t rsvd703[174]; - uint8_t rsvd2047[1344]; - NvmePSD psd[32]; - uint8_t vs[1024]; -} NvmeIdCtrl; - -enum NvmeIdCtrlOacs { - NVME_OACS_SECURITY = 1 << 0, - NVME_OACS_FORMAT = 1 << 1, - NVME_OACS_FW = 1 << 2, -}; - -enum NvmeIdCtrlOncs { - NVME_ONCS_COMPARE = 1 << 0, - NVME_ONCS_WRITE_UNCORR = 1 << 1, - NVME_ONCS_DSM = 1 << 2, - NVME_ONCS_WRITE_ZEROS = 1 << 3, - NVME_ONCS_FEATURES = 1 << 4, - NVME_ONCS_RESRVATIONS = 1 << 5, -}; - -#define NVME_CTRL_SQES_MIN(sqes) ((sqes) & 0xf) -#define NVME_CTRL_SQES_MAX(sqes) (((sqes) >> 4) & 0xf) -#define NVME_CTRL_CQES_MIN(cqes) ((cqes) & 0xf) -#define NVME_CTRL_CQES_MAX(cqes) (((cqes) >> 4) & 0xf) - -typedef struct NvmeFeatureVal { - uint32_t arbitration; - uint32_t power_mgmt; - uint32_t temp_thresh; - uint32_t err_rec; - uint32_t volatile_wc; - uint32_t num_queues; - uint32_t int_coalescing; - uint32_t *int_vector_config; - uint32_t write_atomicity; - uint32_t async_config; - uint32_t sw_prog_marker; -} NvmeFeatureVal; - -#define NVME_ARB_AB(arb) (arb & 0x7) -#define NVME_ARB_LPW(arb) ((arb >> 8) & 0xff) -#define NVME_ARB_MPW(arb) ((arb >> 16) & 0xff) -#define NVME_ARB_HPW(arb) ((arb >> 24) & 0xff) - -#define NVME_INTC_THR(intc) (intc & 0xff) -#define NVME_INTC_TIME(intc) ((intc >> 8) & 0xff) - -enum NvmeFeatureIds { - NVME_ARBITRATION = 0x1, - NVME_POWER_MANAGEMENT = 0x2, - NVME_LBA_RANGE_TYPE = 0x3, - NVME_TEMPERATURE_THRESHOLD = 0x4, - NVME_ERROR_RECOVERY = 0x5, - NVME_VOLATILE_WRITE_CACHE = 0x6, - NVME_NUMBER_OF_QUEUES = 0x7, - NVME_INTERRUPT_COALESCING = 0x8, - NVME_INTERRUPT_VECTOR_CONF = 0x9, - NVME_WRITE_ATOMICITY = 0xa, - NVME_ASYNCHRONOUS_EVENT_CONF = 0xb, - NVME_SOFTWARE_PROGRESS_MARKER = 0x80 -}; - -typedef struct NvmeRangeType { - uint8_t type; - uint8_t attributes; - uint8_t rsvd2[14]; - uint64_t slba; - uint64_t nlb; - uint8_t guid[16]; - uint8_t rsvd48[16]; -} NvmeRangeType; - -typedef struct NvmeLBAF { - uint16_t ms; - uint8_t ds; - uint8_t rp; -} NvmeLBAF; - -typedef struct NvmeIdNs { - uint64_t nsze; - uint64_t ncap; - uint64_t nuse; - uint8_t nsfeat; - uint8_t nlbaf; - uint8_t flbas; - uint8_t mc; - uint8_t dpc; - uint8_t dps; - uint8_t res30[98]; - NvmeLBAF lbaf[16]; - uint8_t res192[192]; - uint8_t vs[3712]; -} NvmeIdNs; - -#define NVME_ID_NS_NSFEAT_THIN(nsfeat) ((nsfeat & 0x1)) -#define NVME_ID_NS_FLBAS_EXTENDED(flbas) ((flbas >> 4) & 0x1) -#define NVME_ID_NS_FLBAS_INDEX(flbas) ((flbas & 0xf)) -#define NVME_ID_NS_MC_SEPARATE(mc) ((mc >> 1) & 0x1) -#define NVME_ID_NS_MC_EXTENDED(mc) ((mc & 0x1)) -#define NVME_ID_NS_DPC_LAST_EIGHT(dpc) ((dpc >> 4) & 0x1) -#define NVME_ID_NS_DPC_FIRST_EIGHT(dpc) ((dpc >> 3) & 0x1) -#define NVME_ID_NS_DPC_TYPE_3(dpc) ((dpc >> 2) & 0x1) -#define NVME_ID_NS_DPC_TYPE_2(dpc) ((dpc >> 1) & 0x1) -#define NVME_ID_NS_DPC_TYPE_1(dpc) ((dpc & 0x1)) -#define NVME_ID_NS_DPC_TYPE_MASK 0x7 - -enum NvmeIdNsDps { - DPS_TYPE_NONE = 0, - DPS_TYPE_1 = 1, - DPS_TYPE_2 = 2, - DPS_TYPE_3 = 3, - DPS_TYPE_MASK = 0x7, - DPS_FIRST_EIGHT = 8, -}; - -static inline void _nvme_check_size(void) -{ - QEMU_BUILD_BUG_ON(sizeof(NvmeAerResult) != 4); - QEMU_BUILD_BUG_ON(sizeof(NvmeCqe) != 16); - QEMU_BUILD_BUG_ON(sizeof(NvmeDsmRange) != 16); - QEMU_BUILD_BUG_ON(sizeof(NvmeCmd) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeDeleteQ) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeCreateCq) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeCreateSq) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeIdentify) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeRwCmd) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeDsmCmd) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeRangeType) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeErrorLog) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeFwSlotInfoLog) != 512); - QEMU_BUILD_BUG_ON(sizeof(NvmeSmartLog) != 512); - QEMU_BUILD_BUG_ON(sizeof(NvmeIdCtrl) != 4096); - QEMU_BUILD_BUG_ON(sizeof(NvmeIdNs) != 4096); -} - -typedef struct NvmeAsyncEvent { - QSIMPLEQ_ENTRY(NvmeAsyncEvent) entry; - NvmeAerResult result; -} NvmeAsyncEvent; - -typedef struct NvmeRequest { - struct NvmeSQueue *sq; - BlockAIOCB *aiocb; - uint16_t status; - bool has_sg; - NvmeCqe cqe; - BlockAcctCookie acct; - QEMUSGList qsg; - QTAILQ_ENTRY(NvmeRequest)entry; -} NvmeRequest; - -typedef struct NvmeSQueue { - struct NvmeCtrl *ctrl; - uint16_t sqid; - uint16_t cqid; - uint32_t head; - uint32_t tail; - uint32_t size; - uint64_t dma_addr; - QEMUTimer *timer; - NvmeRequest *io_req; - QTAILQ_HEAD(sq_req_list, NvmeRequest) req_list; - QTAILQ_HEAD(out_req_list, NvmeRequest) out_req_list; - QTAILQ_ENTRY(NvmeSQueue) entry; -} NvmeSQueue; - -typedef struct NvmeCQueue { - struct NvmeCtrl *ctrl; - uint8_t phase; - uint16_t cqid; - uint16_t irq_enabled; - uint32_t head; - uint32_t tail; - uint32_t vector; - uint32_t size; - uint64_t dma_addr; - QEMUTimer *timer; - QTAILQ_HEAD(sq_list, NvmeSQueue) sq_list; - QTAILQ_HEAD(cq_req_list, NvmeRequest) req_list; -} NvmeCQueue; - -typedef struct NvmeNamespace { - NvmeIdNs id_ns; -} NvmeNamespace; - -#define TYPE_NVME "nvme" -#define NVME(obj) \ - OBJECT_CHECK(NvmeCtrl, (obj), TYPE_NVME) - -typedef struct NvmeCtrl { - PCIDevice parent_obj; - MemoryRegion iomem; - NvmeBar bar; - BlockConf conf; - - uint32_t page_size; - uint16_t page_bits; - uint16_t max_prp_ents; - uint16_t cqe_size; - uint16_t sqe_size; - uint32_t reg_size; - uint32_t num_namespaces; - uint32_t num_queues; - uint32_t max_q_ents; - uint64_t ns_size; - - char *serial; - NvmeNamespace *namespaces; - NvmeSQueue **sq; - NvmeCQueue **cq; - NvmeSQueue admin_sq; - NvmeCQueue admin_cq; - NvmeIdCtrl id_ctrl; -} NvmeCtrl; - -#endif /* HW_NVME_H */ diff --git a/qemu/hw/block/onenand.c b/qemu/hw/block/onenand.c deleted file mode 100644 index 883f4b1fa..000000000 --- a/qemu/hw/block/onenand.c +++ /dev/null @@ -1,850 +0,0 @@ -/* - * OneNAND flash memories emulation. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski <andrew@openedhand.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "hw/hw.h" -#include "hw/block/flash.h" -#include "hw/irq.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" -#include "hw/sysbus.h" -#include "qemu/error-report.h" - -/* 11 for 2kB-page OneNAND ("2nd generation") and 10 for 1kB-page chips */ -#define PAGE_SHIFT 11 - -/* Fixed */ -#define BLOCK_SHIFT (PAGE_SHIFT + 6) - -#define TYPE_ONE_NAND "onenand" -#define ONE_NAND(obj) OBJECT_CHECK(OneNANDState, (obj), TYPE_ONE_NAND) - -typedef struct OneNANDState { - SysBusDevice parent_obj; - - struct { - uint16_t man; - uint16_t dev; - uint16_t ver; - } id; - int shift; - hwaddr base; - qemu_irq intr; - qemu_irq rdy; - BlockBackend *blk; - BlockBackend *blk_cur; - uint8_t *image; - uint8_t *otp; - uint8_t *current; - MemoryRegion ram; - MemoryRegion mapped_ram; - uint8_t current_direction; - uint8_t *boot[2]; - uint8_t *data[2][2]; - MemoryRegion iomem; - MemoryRegion container; - int cycle; - int otpmode; - - uint16_t addr[8]; - uint16_t unladdr[8]; - int bufaddr; - int count; - uint16_t command; - uint16_t config[2]; - uint16_t status; - uint16_t intstatus; - uint16_t wpstatus; - - ECCState ecc; - - int density_mask; - int secs; - int secs_cur; - int blocks; - uint8_t *blockwp; -} OneNANDState; - -enum { - ONEN_BUF_BLOCK = 0, - ONEN_BUF_BLOCK2 = 1, - ONEN_BUF_DEST_BLOCK = 2, - ONEN_BUF_DEST_PAGE = 3, - ONEN_BUF_PAGE = 7, -}; - -enum { - ONEN_ERR_CMD = 1 << 10, - ONEN_ERR_ERASE = 1 << 11, - ONEN_ERR_PROG = 1 << 12, - ONEN_ERR_LOAD = 1 << 13, -}; - -enum { - ONEN_INT_RESET = 1 << 4, - ONEN_INT_ERASE = 1 << 5, - ONEN_INT_PROG = 1 << 6, - ONEN_INT_LOAD = 1 << 7, - ONEN_INT = 1 << 15, -}; - -enum { - ONEN_LOCK_LOCKTIGHTEN = 1 << 0, - ONEN_LOCK_LOCKED = 1 << 1, - ONEN_LOCK_UNLOCKED = 1 << 2, -}; - -static void onenand_mem_setup(OneNANDState *s) -{ - /* XXX: We should use IO_MEM_ROMD but we broke it earlier... - * Both 0x0000 ... 0x01ff and 0x8000 ... 0x800f can be used to - * write boot commands. Also take note of the BWPS bit. */ - memory_region_init(&s->container, OBJECT(s), "onenand", - 0x10000 << s->shift); - memory_region_add_subregion(&s->container, 0, &s->iomem); - memory_region_init_alias(&s->mapped_ram, OBJECT(s), "onenand-mapped-ram", - &s->ram, 0x0200 << s->shift, - 0xbe00 << s->shift); - memory_region_add_subregion_overlap(&s->container, - 0x0200 << s->shift, - &s->mapped_ram, - 1); -} - -static void onenand_intr_update(OneNANDState *s) -{ - qemu_set_irq(s->intr, ((s->intstatus >> 15) ^ (~s->config[0] >> 6)) & 1); -} - -static void onenand_pre_save(void *opaque) -{ - OneNANDState *s = opaque; - if (s->current == s->otp) { - s->current_direction = 1; - } else if (s->current == s->image) { - s->current_direction = 2; - } else { - s->current_direction = 0; - } -} - -static int onenand_post_load(void *opaque, int version_id) -{ - OneNANDState *s = opaque; - switch (s->current_direction) { - case 0: - break; - case 1: - s->current = s->otp; - break; - case 2: - s->current = s->image; - break; - default: - return -1; - } - onenand_intr_update(s); - return 0; -} - -static const VMStateDescription vmstate_onenand = { - .name = "onenand", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = onenand_pre_save, - .post_load = onenand_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(current_direction, OneNANDState), - VMSTATE_INT32(cycle, OneNANDState), - VMSTATE_INT32(otpmode, OneNANDState), - VMSTATE_UINT16_ARRAY(addr, OneNANDState, 8), - VMSTATE_UINT16_ARRAY(unladdr, OneNANDState, 8), - VMSTATE_INT32(bufaddr, OneNANDState), - VMSTATE_INT32(count, OneNANDState), - VMSTATE_UINT16(command, OneNANDState), - VMSTATE_UINT16_ARRAY(config, OneNANDState, 2), - VMSTATE_UINT16(status, OneNANDState), - VMSTATE_UINT16(intstatus, OneNANDState), - VMSTATE_UINT16(wpstatus, OneNANDState), - VMSTATE_INT32(secs_cur, OneNANDState), - VMSTATE_PARTIAL_VBUFFER(blockwp, OneNANDState, blocks), - VMSTATE_UINT8(ecc.cp, OneNANDState), - VMSTATE_UINT16_ARRAY(ecc.lp, OneNANDState, 2), - VMSTATE_UINT16(ecc.count, OneNANDState), - VMSTATE_BUFFER_POINTER_UNSAFE(otp, OneNANDState, 0, - ((64 + 2) << PAGE_SHIFT)), - VMSTATE_END_OF_LIST() - } -}; - -/* Hot reset (Reset OneNAND command) or warm reset (RP pin low) */ -static void onenand_reset(OneNANDState *s, int cold) -{ - memset(&s->addr, 0, sizeof(s->addr)); - s->command = 0; - s->count = 1; - s->bufaddr = 0; - s->config[0] = 0x40c0; - s->config[1] = 0x0000; - onenand_intr_update(s); - qemu_irq_raise(s->rdy); - s->status = 0x0000; - s->intstatus = cold ? 0x8080 : 0x8010; - s->unladdr[0] = 0; - s->unladdr[1] = 0; - s->wpstatus = 0x0002; - s->cycle = 0; - s->otpmode = 0; - s->blk_cur = s->blk; - s->current = s->image; - s->secs_cur = s->secs; - - if (cold) { - /* Lock the whole flash */ - memset(s->blockwp, ONEN_LOCK_LOCKED, s->blocks); - - if (s->blk_cur && blk_read(s->blk_cur, 0, s->boot[0], 8) < 0) { - hw_error("%s: Loading the BootRAM failed.\n", __func__); - } - } -} - -static void onenand_system_reset(DeviceState *dev) -{ - OneNANDState *s = ONE_NAND(dev); - - onenand_reset(s, 1); -} - -static inline int onenand_load_main(OneNANDState *s, int sec, int secn, - void *dest) -{ - if (s->blk_cur) { - return blk_read(s->blk_cur, sec, dest, secn) < 0; - } else if (sec + secn > s->secs_cur) { - return 1; - } - - memcpy(dest, s->current + (sec << 9), secn << 9); - - return 0; -} - -static inline int onenand_prog_main(OneNANDState *s, int sec, int secn, - void *src) -{ - int result = 0; - - if (secn > 0) { - uint32_t size = (uint32_t)secn * 512; - const uint8_t *sp = (const uint8_t *)src; - uint8_t *dp = 0; - if (s->blk_cur) { - dp = g_malloc(size); - if (!dp || blk_read(s->blk_cur, sec, dp, secn) < 0) { - result = 1; - } - } else { - if (sec + secn > s->secs_cur) { - result = 1; - } else { - dp = (uint8_t *)s->current + (sec << 9); - } - } - if (!result) { - uint32_t i; - for (i = 0; i < size; i++) { - dp[i] &= sp[i]; - } - if (s->blk_cur) { - result = blk_write(s->blk_cur, sec, dp, secn) < 0; - } - } - if (dp && s->blk_cur) { - g_free(dp); - } - } - - return result; -} - -static inline int onenand_load_spare(OneNANDState *s, int sec, int secn, - void *dest) -{ - uint8_t buf[512]; - - if (s->blk_cur) { - if (blk_read(s->blk_cur, s->secs_cur + (sec >> 5), buf, 1) < 0) { - return 1; - } - memcpy(dest, buf + ((sec & 31) << 4), secn << 4); - } else if (sec + secn > s->secs_cur) { - return 1; - } else { - memcpy(dest, s->current + (s->secs_cur << 9) + (sec << 4), secn << 4); - } - - return 0; -} - -static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn, - void *src) -{ - int result = 0; - if (secn > 0) { - const uint8_t *sp = (const uint8_t *)src; - uint8_t *dp = 0, *dpp = 0; - if (s->blk_cur) { - dp = g_malloc(512); - if (!dp - || blk_read(s->blk_cur, s->secs_cur + (sec >> 5), dp, 1) < 0) { - result = 1; - } else { - dpp = dp + ((sec & 31) << 4); - } - } else { - if (sec + secn > s->secs_cur) { - result = 1; - } else { - dpp = s->current + (s->secs_cur << 9) + (sec << 4); - } - } - if (!result) { - uint32_t i; - for (i = 0; i < (secn << 4); i++) { - dpp[i] &= sp[i]; - } - if (s->blk_cur) { - result = blk_write(s->blk_cur, s->secs_cur + (sec >> 5), - dp, 1) < 0; - } - } - g_free(dp); - } - return result; -} - -static inline int onenand_erase(OneNANDState *s, int sec, int num) -{ - uint8_t *blankbuf, *tmpbuf; - - blankbuf = g_malloc(512); - tmpbuf = g_malloc(512); - memset(blankbuf, 0xff, 512); - for (; num > 0; num--, sec++) { - if (s->blk_cur) { - int erasesec = s->secs_cur + (sec >> 5); - if (blk_write(s->blk_cur, sec, blankbuf, 1) < 0) { - goto fail; - } - if (blk_read(s->blk_cur, erasesec, tmpbuf, 1) < 0) { - goto fail; - } - memcpy(tmpbuf + ((sec & 31) << 4), blankbuf, 1 << 4); - if (blk_write(s->blk_cur, erasesec, tmpbuf, 1) < 0) { - goto fail; - } - } else { - if (sec + 1 > s->secs_cur) { - goto fail; - } - memcpy(s->current + (sec << 9), blankbuf, 512); - memcpy(s->current + (s->secs_cur << 9) + (sec << 4), - blankbuf, 1 << 4); - } - } - - g_free(tmpbuf); - g_free(blankbuf); - return 0; - -fail: - g_free(tmpbuf); - g_free(blankbuf); - return 1; -} - -static void onenand_command(OneNANDState *s) -{ - int b; - int sec; - void *buf; -#define SETADDR(block, page) \ - sec = (s->addr[page] & 3) + \ - ((((s->addr[page] >> 2) & 0x3f) + \ - (((s->addr[block] & 0xfff) | \ - (s->addr[block] >> 15 ? \ - s->density_mask : 0)) << 6)) << (PAGE_SHIFT - 9)); -#define SETBUF_M() \ - buf = (s->bufaddr & 8) ? \ - s->data[(s->bufaddr >> 2) & 1][0] : s->boot[0]; \ - buf += (s->bufaddr & 3) << 9; -#define SETBUF_S() \ - buf = (s->bufaddr & 8) ? \ - s->data[(s->bufaddr >> 2) & 1][1] : s->boot[1]; \ - buf += (s->bufaddr & 3) << 4; - - switch (s->command) { - case 0x00: /* Load single/multiple sector data unit into buffer */ - SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) - - SETBUF_M() - if (onenand_load_main(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD; - -#if 0 - SETBUF_S() - if (onenand_load_spare(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD; -#endif - - /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) - * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) - * then we need two split the read/write into two chunks. - */ - s->intstatus |= ONEN_INT | ONEN_INT_LOAD; - break; - case 0x13: /* Load single/multiple spare sector into buffer */ - SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) - - SETBUF_S() - if (onenand_load_spare(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD; - - /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) - * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) - * then we need two split the read/write into two chunks. - */ - s->intstatus |= ONEN_INT | ONEN_INT_LOAD; - break; - case 0x80: /* Program single/multiple sector data unit from buffer */ - SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) - - SETBUF_M() - if (onenand_prog_main(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; - -#if 0 - SETBUF_S() - if (onenand_prog_spare(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; -#endif - - /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) - * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) - * then we need two split the read/write into two chunks. - */ - s->intstatus |= ONEN_INT | ONEN_INT_PROG; - break; - case 0x1a: /* Program single/multiple spare area sector from buffer */ - SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) - - SETBUF_S() - if (onenand_prog_spare(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; - - /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) - * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) - * then we need two split the read/write into two chunks. - */ - s->intstatus |= ONEN_INT | ONEN_INT_PROG; - break; - case 0x1b: /* Copy-back program */ - SETBUF_S() - - SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) - if (onenand_load_main(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; - - SETADDR(ONEN_BUF_DEST_BLOCK, ONEN_BUF_DEST_PAGE) - if (onenand_prog_main(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; - - /* TODO: spare areas */ - - s->intstatus |= ONEN_INT | ONEN_INT_PROG; - break; - - case 0x23: /* Unlock NAND array block(s) */ - s->intstatus |= ONEN_INT; - - /* XXX the previous (?) area should be locked automatically */ - for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) { - if (b >= s->blocks) { - s->status |= ONEN_ERR_CMD; - break; - } - if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN) - break; - - s->wpstatus = s->blockwp[b] = ONEN_LOCK_UNLOCKED; - } - break; - case 0x27: /* Unlock All NAND array blocks */ - s->intstatus |= ONEN_INT; - - for (b = 0; b < s->blocks; b ++) { - if (b >= s->blocks) { - s->status |= ONEN_ERR_CMD; - break; - } - if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN) - break; - - s->wpstatus = s->blockwp[b] = ONEN_LOCK_UNLOCKED; - } - break; - - case 0x2a: /* Lock NAND array block(s) */ - s->intstatus |= ONEN_INT; - - for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) { - if (b >= s->blocks) { - s->status |= ONEN_ERR_CMD; - break; - } - if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN) - break; - - s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKED; - } - break; - case 0x2c: /* Lock-tight NAND array block(s) */ - s->intstatus |= ONEN_INT; - - for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) { - if (b >= s->blocks) { - s->status |= ONEN_ERR_CMD; - break; - } - if (s->blockwp[b] == ONEN_LOCK_UNLOCKED) - continue; - - s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKTIGHTEN; - } - break; - - case 0x71: /* Erase-Verify-Read */ - s->intstatus |= ONEN_INT; - break; - case 0x95: /* Multi-block erase */ - qemu_irq_pulse(s->intr); - /* Fall through. */ - case 0x94: /* Block erase */ - sec = ((s->addr[ONEN_BUF_BLOCK] & 0xfff) | - (s->addr[ONEN_BUF_BLOCK] >> 15 ? s->density_mask : 0)) - << (BLOCK_SHIFT - 9); - if (onenand_erase(s, sec, 1 << (BLOCK_SHIFT - 9))) - s->status |= ONEN_ERR_CMD | ONEN_ERR_ERASE; - - s->intstatus |= ONEN_INT | ONEN_INT_ERASE; - break; - case 0xb0: /* Erase suspend */ - break; - case 0x30: /* Erase resume */ - s->intstatus |= ONEN_INT | ONEN_INT_ERASE; - break; - - case 0xf0: /* Reset NAND Flash core */ - onenand_reset(s, 0); - break; - case 0xf3: /* Reset OneNAND */ - onenand_reset(s, 0); - break; - - case 0x65: /* OTP Access */ - s->intstatus |= ONEN_INT; - s->blk_cur = NULL; - s->current = s->otp; - s->secs_cur = 1 << (BLOCK_SHIFT - 9); - s->addr[ONEN_BUF_BLOCK] = 0; - s->otpmode = 1; - break; - - default: - s->status |= ONEN_ERR_CMD; - s->intstatus |= ONEN_INT; - fprintf(stderr, "%s: unknown OneNAND command %x\n", - __func__, s->command); - } - - onenand_intr_update(s); -} - -static uint64_t onenand_read(void *opaque, hwaddr addr, - unsigned size) -{ - OneNANDState *s = (OneNANDState *) opaque; - int offset = addr >> s->shift; - - switch (offset) { - case 0x0000 ... 0xc000: - return lduw_le_p(s->boot[0] + addr); - - case 0xf000: /* Manufacturer ID */ - return s->id.man; - case 0xf001: /* Device ID */ - return s->id.dev; - case 0xf002: /* Version ID */ - return s->id.ver; - /* TODO: get the following values from a real chip! */ - case 0xf003: /* Data Buffer size */ - return 1 << PAGE_SHIFT; - case 0xf004: /* Boot Buffer size */ - return 0x200; - case 0xf005: /* Amount of buffers */ - return 1 | (2 << 8); - case 0xf006: /* Technology */ - return 0; - - case 0xf100 ... 0xf107: /* Start addresses */ - return s->addr[offset - 0xf100]; - - case 0xf200: /* Start buffer */ - return (s->bufaddr << 8) | ((s->count - 1) & (1 << (PAGE_SHIFT - 10))); - - case 0xf220: /* Command */ - return s->command; - case 0xf221: /* System Configuration 1 */ - return s->config[0] & 0xffe0; - case 0xf222: /* System Configuration 2 */ - return s->config[1]; - - case 0xf240: /* Controller Status */ - return s->status; - case 0xf241: /* Interrupt */ - return s->intstatus; - case 0xf24c: /* Unlock Start Block Address */ - return s->unladdr[0]; - case 0xf24d: /* Unlock End Block Address */ - return s->unladdr[1]; - case 0xf24e: /* Write Protection Status */ - return s->wpstatus; - - case 0xff00: /* ECC Status */ - return 0x00; - case 0xff01: /* ECC Result of main area data */ - case 0xff02: /* ECC Result of spare area data */ - case 0xff03: /* ECC Result of main area data */ - case 0xff04: /* ECC Result of spare area data */ - hw_error("%s: imeplement ECC\n", __FUNCTION__); - return 0x0000; - } - - fprintf(stderr, "%s: unknown OneNAND register %x\n", - __FUNCTION__, offset); - return 0; -} - -static void onenand_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - OneNANDState *s = (OneNANDState *) opaque; - int offset = addr >> s->shift; - int sec; - - switch (offset) { - case 0x0000 ... 0x01ff: - case 0x8000 ... 0x800f: - if (s->cycle) { - s->cycle = 0; - - if (value == 0x0000) { - SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) - onenand_load_main(s, sec, - 1 << (PAGE_SHIFT - 9), s->data[0][0]); - s->addr[ONEN_BUF_PAGE] += 4; - s->addr[ONEN_BUF_PAGE] &= 0xff; - } - break; - } - - switch (value) { - case 0x00f0: /* Reset OneNAND */ - onenand_reset(s, 0); - break; - - case 0x00e0: /* Load Data into Buffer */ - s->cycle = 1; - break; - - case 0x0090: /* Read Identification Data */ - memset(s->boot[0], 0, 3 << s->shift); - s->boot[0][0 << s->shift] = s->id.man & 0xff; - s->boot[0][1 << s->shift] = s->id.dev & 0xff; - s->boot[0][2 << s->shift] = s->wpstatus & 0xff; - break; - - default: - fprintf(stderr, "%s: unknown OneNAND boot command %"PRIx64"\n", - __FUNCTION__, value); - } - break; - - case 0xf100 ... 0xf107: /* Start addresses */ - s->addr[offset - 0xf100] = value; - break; - - case 0xf200: /* Start buffer */ - s->bufaddr = (value >> 8) & 0xf; - if (PAGE_SHIFT == 11) - s->count = (value & 3) ?: 4; - else if (PAGE_SHIFT == 10) - s->count = (value & 1) ?: 2; - break; - - case 0xf220: /* Command */ - if (s->intstatus & (1 << 15)) - break; - s->command = value; - onenand_command(s); - break; - case 0xf221: /* System Configuration 1 */ - s->config[0] = value; - onenand_intr_update(s); - qemu_set_irq(s->rdy, (s->config[0] >> 7) & 1); - break; - case 0xf222: /* System Configuration 2 */ - s->config[1] = value; - break; - - case 0xf241: /* Interrupt */ - s->intstatus &= value; - if ((1 << 15) & ~s->intstatus) - s->status &= ~(ONEN_ERR_CMD | ONEN_ERR_ERASE | - ONEN_ERR_PROG | ONEN_ERR_LOAD); - onenand_intr_update(s); - break; - case 0xf24c: /* Unlock Start Block Address */ - s->unladdr[0] = value & (s->blocks - 1); - /* For some reason we have to set the end address to by default - * be same as start because the software forgets to write anything - * in there. */ - s->unladdr[1] = value & (s->blocks - 1); - break; - case 0xf24d: /* Unlock End Block Address */ - s->unladdr[1] = value & (s->blocks - 1); - break; - - default: - fprintf(stderr, "%s: unknown OneNAND register %x\n", - __FUNCTION__, offset); - } -} - -static const MemoryRegionOps onenand_ops = { - .read = onenand_read, - .write = onenand_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int onenand_initfn(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - OneNANDState *s = ONE_NAND(dev); - uint32_t size = 1 << (24 + ((s->id.dev >> 4) & 7)); - void *ram; - - s->base = (hwaddr)-1; - s->rdy = NULL; - s->blocks = size >> BLOCK_SHIFT; - s->secs = size >> 9; - s->blockwp = g_malloc(s->blocks); - s->density_mask = (s->id.dev & 0x08) - ? (1 << (6 + ((s->id.dev >> 4) & 7))) : 0; - memory_region_init_io(&s->iomem, OBJECT(s), &onenand_ops, s, "onenand", - 0x10000 << s->shift); - if (!s->blk) { - s->image = memset(g_malloc(size + (size >> 5)), - 0xff, size + (size >> 5)); - } else { - if (blk_is_read_only(s->blk)) { - error_report("Can't use a read-only drive"); - return -1; - } - s->blk_cur = s->blk; - } - s->otp = memset(g_malloc((64 + 2) << PAGE_SHIFT), - 0xff, (64 + 2) << PAGE_SHIFT); - memory_region_init_ram(&s->ram, OBJECT(s), "onenand.ram", - 0xc000 << s->shift, &error_fatal); - vmstate_register_ram_global(&s->ram); - ram = memory_region_get_ram_ptr(&s->ram); - s->boot[0] = ram + (0x0000 << s->shift); - s->boot[1] = ram + (0x8000 << s->shift); - s->data[0][0] = ram + ((0x0200 + (0 << (PAGE_SHIFT - 1))) << s->shift); - s->data[0][1] = ram + ((0x8010 + (0 << (PAGE_SHIFT - 6))) << s->shift); - s->data[1][0] = ram + ((0x0200 + (1 << (PAGE_SHIFT - 1))) << s->shift); - s->data[1][1] = ram + ((0x8010 + (1 << (PAGE_SHIFT - 6))) << s->shift); - onenand_mem_setup(s); - sysbus_init_irq(sbd, &s->intr); - sysbus_init_mmio(sbd, &s->container); - vmstate_register(dev, - ((s->shift & 0x7f) << 24) - | ((s->id.man & 0xff) << 16) - | ((s->id.dev & 0xff) << 8) - | (s->id.ver & 0xff), - &vmstate_onenand, s); - return 0; -} - -static Property onenand_properties[] = { - DEFINE_PROP_UINT16("manufacturer_id", OneNANDState, id.man, 0), - DEFINE_PROP_UINT16("device_id", OneNANDState, id.dev, 0), - DEFINE_PROP_UINT16("version_id", OneNANDState, id.ver, 0), - DEFINE_PROP_INT32("shift", OneNANDState, shift, 0), - DEFINE_PROP_DRIVE("drive", OneNANDState, blk), - DEFINE_PROP_END_OF_LIST(), -}; - -static void onenand_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = onenand_initfn; - dc->reset = onenand_system_reset; - dc->props = onenand_properties; -} - -static const TypeInfo onenand_info = { - .name = TYPE_ONE_NAND, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(OneNANDState), - .class_init = onenand_class_init, -}; - -static void onenand_register_types(void) -{ - type_register_static(&onenand_info); -} - -void *onenand_raw_otp(DeviceState *onenand_device) -{ - OneNANDState *s = ONE_NAND(onenand_device); - - return s->otp; -} - -type_init(onenand_register_types) diff --git a/qemu/hw/block/pflash_cfi01.c b/qemu/hw/block/pflash_cfi01.c deleted file mode 100644 index 106a77523..000000000 --- a/qemu/hw/block/pflash_cfi01.c +++ /dev/null @@ -1,970 +0,0 @@ -/* - * CFI parallel flash with Intel command set emulation - * - * Copyright (c) 2006 Thorsten Zitterell - * Copyright (c) 2005 Jocelyn Mayer - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -/* - * For now, this code can emulate flashes of 1, 2 or 4 bytes width. - * Supported commands/modes are: - * - flash read - * - flash write - * - flash ID read - * - sector erase - * - CFI queries - * - * It does not support timings - * It does not support flash interleaving - * It does not implement software data protection as found in many real chips - * It does not implement erase suspend/resume commands - * It does not implement multiple sectors erase - * - * It does not implement much more ... - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/block/flash.h" -#include "sysemu/block-backend.h" -#include "qapi/error.h" -#include "qemu/timer.h" -#include "qemu/bitops.h" -#include "exec/address-spaces.h" -#include "qemu/host-utils.h" -#include "hw/sysbus.h" -#include "sysemu/sysemu.h" - -#define PFLASH_BUG(fmt, ...) \ -do { \ - fprintf(stderr, "PFLASH: Possible BUG - " fmt, ## __VA_ARGS__); \ - exit(1); \ -} while(0) - -/* #define PFLASH_DEBUG */ -#ifdef PFLASH_DEBUG -#define DPRINTF(fmt, ...) \ -do { \ - fprintf(stderr, "PFLASH: " fmt , ## __VA_ARGS__); \ -} while (0) -#else -#define DPRINTF(fmt, ...) do { } while (0) -#endif - -#define TYPE_CFI_PFLASH01 "cfi.pflash01" -#define CFI_PFLASH01(obj) OBJECT_CHECK(pflash_t, (obj), TYPE_CFI_PFLASH01) - -#define PFLASH_BE 0 -#define PFLASH_SECURE 1 - -struct pflash_t { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - BlockBackend *blk; - uint32_t nb_blocs; - uint64_t sector_len; - uint8_t bank_width; - uint8_t device_width; /* If 0, device width not specified. */ - uint8_t max_device_width; /* max device width in bytes */ - uint32_t features; - uint8_t wcycle; /* if 0, the flash is read normally */ - int ro; - uint8_t cmd; - uint8_t status; - uint16_t ident0; - uint16_t ident1; - uint16_t ident2; - uint16_t ident3; - uint8_t cfi_len; - uint8_t cfi_table[0x52]; - uint64_t counter; - unsigned int writeblock_size; - QEMUTimer *timer; - MemoryRegion mem; - char *name; - void *storage; - VMChangeStateEntry *vmstate; -}; - -static int pflash_post_load(void *opaque, int version_id); - -static const VMStateDescription vmstate_pflash = { - .name = "pflash_cfi01", - .version_id = 1, - .minimum_version_id = 1, - .post_load = pflash_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(wcycle, pflash_t), - VMSTATE_UINT8(cmd, pflash_t), - VMSTATE_UINT8(status, pflash_t), - VMSTATE_UINT64(counter, pflash_t), - VMSTATE_END_OF_LIST() - } -}; - -static void pflash_timer (void *opaque) -{ - pflash_t *pfl = opaque; - - DPRINTF("%s: command %02x done\n", __func__, pfl->cmd); - /* Reset flash */ - pfl->status ^= 0x80; - memory_region_rom_device_set_romd(&pfl->mem, true); - pfl->wcycle = 0; - pfl->cmd = 0; -} - -/* Perform a CFI query based on the bank width of the flash. - * If this code is called we know we have a device_width set for - * this flash. - */ -static uint32_t pflash_cfi_query(pflash_t *pfl, hwaddr offset) -{ - int i; - uint32_t resp = 0; - hwaddr boff; - - /* Adjust incoming offset to match expected device-width - * addressing. CFI query addresses are always specified in terms of - * the maximum supported width of the device. This means that x8 - * devices and x8/x16 devices in x8 mode behave differently. For - * devices that are not used at their max width, we will be - * provided with addresses that use higher address bits than - * expected (based on the max width), so we will shift them lower - * so that they will match the addresses used when - * device_width==max_device_width. - */ - boff = offset >> (ctz32(pfl->bank_width) + - ctz32(pfl->max_device_width) - ctz32(pfl->device_width)); - - if (boff > pfl->cfi_len) { - return 0; - } - /* Now we will construct the CFI response generated by a single - * device, then replicate that for all devices that make up the - * bus. For wide parts used in x8 mode, CFI query responses - * are different than native byte-wide parts. - */ - resp = pfl->cfi_table[boff]; - if (pfl->device_width != pfl->max_device_width) { - /* The only case currently supported is x8 mode for a - * wider part. - */ - if (pfl->device_width != 1 || pfl->bank_width > 4) { - DPRINTF("%s: Unsupported device configuration: " - "device_width=%d, max_device_width=%d\n", - __func__, pfl->device_width, - pfl->max_device_width); - return 0; - } - /* CFI query data is repeated, rather than zero padded for - * wide devices used in x8 mode. - */ - for (i = 1; i < pfl->max_device_width; i++) { - resp = deposit32(resp, 8 * i, 8, pfl->cfi_table[boff]); - } - } - /* Replicate responses for each device in bank. */ - if (pfl->device_width < pfl->bank_width) { - for (i = pfl->device_width; - i < pfl->bank_width; i += pfl->device_width) { - resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp); - } - } - - return resp; -} - - - -/* Perform a device id query based on the bank width of the flash. */ -static uint32_t pflash_devid_query(pflash_t *pfl, hwaddr offset) -{ - int i; - uint32_t resp; - hwaddr boff; - - /* Adjust incoming offset to match expected device-width - * addressing. Device ID read addresses are always specified in - * terms of the maximum supported width of the device. This means - * that x8 devices and x8/x16 devices in x8 mode behave - * differently. For devices that are not used at their max width, - * we will be provided with addresses that use higher address bits - * than expected (based on the max width), so we will shift them - * lower so that they will match the addresses used when - * device_width==max_device_width. - */ - boff = offset >> (ctz32(pfl->bank_width) + - ctz32(pfl->max_device_width) - ctz32(pfl->device_width)); - - /* Mask off upper bits which may be used in to query block - * or sector lock status at other addresses. - * Offsets 2/3 are block lock status, is not emulated. - */ - switch (boff & 0xFF) { - case 0: - resp = pfl->ident0; - DPRINTF("%s: Manufacturer Code %04x\n", __func__, resp); - break; - case 1: - resp = pfl->ident1; - DPRINTF("%s: Device ID Code %04x\n", __func__, resp); - break; - default: - DPRINTF("%s: Read Device Information offset=%x\n", __func__, - (unsigned)offset); - return 0; - break; - } - /* Replicate responses for each device in bank. */ - if (pfl->device_width < pfl->bank_width) { - for (i = pfl->device_width; - i < pfl->bank_width; i += pfl->device_width) { - resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp); - } - } - - return resp; -} - -static uint32_t pflash_data_read(pflash_t *pfl, hwaddr offset, - int width, int be) -{ - uint8_t *p; - uint32_t ret; - - p = pfl->storage; - switch (width) { - case 1: - ret = p[offset]; - DPRINTF("%s: data offset " TARGET_FMT_plx " %02x\n", - __func__, offset, ret); - break; - case 2: - if (be) { - ret = p[offset] << 8; - ret |= p[offset + 1]; - } else { - ret = p[offset]; - ret |= p[offset + 1] << 8; - } - DPRINTF("%s: data offset " TARGET_FMT_plx " %04x\n", - __func__, offset, ret); - break; - case 4: - if (be) { - ret = p[offset] << 24; - ret |= p[offset + 1] << 16; - ret |= p[offset + 2] << 8; - ret |= p[offset + 3]; - } else { - ret = p[offset]; - ret |= p[offset + 1] << 8; - ret |= p[offset + 2] << 16; - ret |= p[offset + 3] << 24; - } - DPRINTF("%s: data offset " TARGET_FMT_plx " %08x\n", - __func__, offset, ret); - break; - default: - DPRINTF("BUG in %s\n", __func__); - abort(); - } - return ret; -} - -static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, - int width, int be) -{ - hwaddr boff; - uint32_t ret; - - ret = -1; - -#if 0 - DPRINTF("%s: reading offset " TARGET_FMT_plx " under cmd %02x width %d\n", - __func__, offset, pfl->cmd, width); -#endif - switch (pfl->cmd) { - default: - /* This should never happen : reset state & treat it as a read */ - DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd); - pfl->wcycle = 0; - pfl->cmd = 0; - /* fall through to read code */ - case 0x00: - /* Flash area read */ - ret = pflash_data_read(pfl, offset, width, be); - break; - case 0x10: /* Single byte program */ - case 0x20: /* Block erase */ - case 0x28: /* Block erase */ - case 0x40: /* single byte program */ - case 0x50: /* Clear status register */ - case 0x60: /* Block /un)lock */ - case 0x70: /* Status Register */ - case 0xe8: /* Write block */ - /* Status register read. Return status from each device in - * bank. - */ - ret = pfl->status; - if (pfl->device_width && width > pfl->device_width) { - int shift = pfl->device_width * 8; - while (shift + pfl->device_width * 8 <= width * 8) { - ret |= pfl->status << shift; - shift += pfl->device_width * 8; - } - } else if (!pfl->device_width && width > 2) { - /* Handle 32 bit flash cases where device width is not - * set. (Existing behavior before device width added.) - */ - ret |= pfl->status << 16; - } - DPRINTF("%s: status %x\n", __func__, ret); - break; - case 0x90: - if (!pfl->device_width) { - /* Preserve old behavior if device width not specified */ - boff = offset & 0xFF; - if (pfl->bank_width == 2) { - boff = boff >> 1; - } else if (pfl->bank_width == 4) { - boff = boff >> 2; - } - - switch (boff) { - case 0: - ret = pfl->ident0 << 8 | pfl->ident1; - DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret); - break; - case 1: - ret = pfl->ident2 << 8 | pfl->ident3; - DPRINTF("%s: Device ID Code %04x\n", __func__, ret); - break; - default: - DPRINTF("%s: Read Device Information boff=%x\n", __func__, - (unsigned)boff); - ret = 0; - break; - } - } else { - /* If we have a read larger than the bank_width, combine multiple - * manufacturer/device ID queries into a single response. - */ - int i; - for (i = 0; i < width; i += pfl->bank_width) { - ret = deposit32(ret, i * 8, pfl->bank_width * 8, - pflash_devid_query(pfl, - offset + i * pfl->bank_width)); - } - } - break; - case 0x98: /* Query mode */ - if (!pfl->device_width) { - /* Preserve old behavior if device width not specified */ - boff = offset & 0xFF; - if (pfl->bank_width == 2) { - boff = boff >> 1; - } else if (pfl->bank_width == 4) { - boff = boff >> 2; - } - - if (boff > pfl->cfi_len) { - ret = 0; - } else { - ret = pfl->cfi_table[boff]; - } - } else { - /* If we have a read larger than the bank_width, combine multiple - * CFI queries into a single response. - */ - int i; - for (i = 0; i < width; i += pfl->bank_width) { - ret = deposit32(ret, i * 8, pfl->bank_width * 8, - pflash_cfi_query(pfl, - offset + i * pfl->bank_width)); - } - } - - break; - } - return ret; -} - -/* update flash content on disk */ -static void pflash_update(pflash_t *pfl, int offset, - int size) -{ - int offset_end; - if (pfl->blk) { - offset_end = offset + size; - /* round to sectors */ - offset = offset >> 9; - offset_end = (offset_end + 511) >> 9; - blk_write(pfl->blk, offset, pfl->storage + (offset << 9), - offset_end - offset); - } -} - -static inline void pflash_data_write(pflash_t *pfl, hwaddr offset, - uint32_t value, int width, int be) -{ - uint8_t *p = pfl->storage; - - DPRINTF("%s: block write offset " TARGET_FMT_plx - " value %x counter %016" PRIx64 "\n", - __func__, offset, value, pfl->counter); - switch (width) { - case 1: - p[offset] = value; - break; - case 2: - if (be) { - p[offset] = value >> 8; - p[offset + 1] = value; - } else { - p[offset] = value; - p[offset + 1] = value >> 8; - } - break; - case 4: - if (be) { - p[offset] = value >> 24; - p[offset + 1] = value >> 16; - p[offset + 2] = value >> 8; - p[offset + 3] = value; - } else { - p[offset] = value; - p[offset + 1] = value >> 8; - p[offset + 2] = value >> 16; - p[offset + 3] = value >> 24; - } - break; - } - -} - -static void pflash_write(pflash_t *pfl, hwaddr offset, - uint32_t value, int width, int be) -{ - uint8_t *p; - uint8_t cmd; - - cmd = value; - - DPRINTF("%s: writing offset " TARGET_FMT_plx " value %08x width %d wcycle 0x%x\n", - __func__, offset, value, width, pfl->wcycle); - - if (!pfl->wcycle) { - /* Set the device in I/O access mode */ - memory_region_rom_device_set_romd(&pfl->mem, false); - } - - switch (pfl->wcycle) { - case 0: - /* read mode */ - switch (cmd) { - case 0x00: /* ??? */ - goto reset_flash; - case 0x10: /* Single Byte Program */ - case 0x40: /* Single Byte Program */ - DPRINTF("%s: Single Byte Program\n", __func__); - break; - case 0x20: /* Block erase */ - p = pfl->storage; - offset &= ~(pfl->sector_len - 1); - - DPRINTF("%s: block erase at " TARGET_FMT_plx " bytes %x\n", - __func__, offset, (unsigned)pfl->sector_len); - - if (!pfl->ro) { - memset(p + offset, 0xff, pfl->sector_len); - pflash_update(pfl, offset, pfl->sector_len); - } else { - pfl->status |= 0x20; /* Block erase error */ - } - pfl->status |= 0x80; /* Ready! */ - break; - case 0x50: /* Clear status bits */ - DPRINTF("%s: Clear status bits\n", __func__); - pfl->status = 0x0; - goto reset_flash; - case 0x60: /* Block (un)lock */ - DPRINTF("%s: Block unlock\n", __func__); - break; - case 0x70: /* Status Register */ - DPRINTF("%s: Read status register\n", __func__); - pfl->cmd = cmd; - return; - case 0x90: /* Read Device ID */ - DPRINTF("%s: Read Device information\n", __func__); - pfl->cmd = cmd; - return; - case 0x98: /* CFI query */ - DPRINTF("%s: CFI query\n", __func__); - break; - case 0xe8: /* Write to buffer */ - DPRINTF("%s: Write to buffer\n", __func__); - pfl->status |= 0x80; /* Ready! */ - break; - case 0xf0: /* Probe for AMD flash */ - DPRINTF("%s: Probe for AMD flash\n", __func__); - goto reset_flash; - case 0xff: /* Read array mode */ - DPRINTF("%s: Read array mode\n", __func__); - goto reset_flash; - default: - goto error_flash; - } - pfl->wcycle++; - pfl->cmd = cmd; - break; - case 1: - switch (pfl->cmd) { - case 0x10: /* Single Byte Program */ - case 0x40: /* Single Byte Program */ - DPRINTF("%s: Single Byte Program\n", __func__); - if (!pfl->ro) { - pflash_data_write(pfl, offset, value, width, be); - pflash_update(pfl, offset, width); - } else { - pfl->status |= 0x10; /* Programming error */ - } - pfl->status |= 0x80; /* Ready! */ - pfl->wcycle = 0; - break; - case 0x20: /* Block erase */ - case 0x28: - if (cmd == 0xd0) { /* confirm */ - pfl->wcycle = 0; - pfl->status |= 0x80; - } else if (cmd == 0xff) { /* read array mode */ - goto reset_flash; - } else - goto error_flash; - - break; - case 0xe8: - /* Mask writeblock size based on device width, or bank width if - * device width not specified. - */ - if (pfl->device_width) { - value = extract32(value, 0, pfl->device_width * 8); - } else { - value = extract32(value, 0, pfl->bank_width * 8); - } - DPRINTF("%s: block write of %x bytes\n", __func__, value); - pfl->counter = value; - pfl->wcycle++; - break; - case 0x60: - if (cmd == 0xd0) { - pfl->wcycle = 0; - pfl->status |= 0x80; - } else if (cmd == 0x01) { - pfl->wcycle = 0; - pfl->status |= 0x80; - } else if (cmd == 0xff) { - goto reset_flash; - } else { - DPRINTF("%s: Unknown (un)locking command\n", __func__); - goto reset_flash; - } - break; - case 0x98: - if (cmd == 0xff) { - goto reset_flash; - } else { - DPRINTF("%s: leaving query mode\n", __func__); - } - break; - default: - goto error_flash; - } - break; - case 2: - switch (pfl->cmd) { - case 0xe8: /* Block write */ - if (!pfl->ro) { - pflash_data_write(pfl, offset, value, width, be); - } else { - pfl->status |= 0x10; /* Programming error */ - } - - pfl->status |= 0x80; - - if (!pfl->counter) { - hwaddr mask = pfl->writeblock_size - 1; - mask = ~mask; - - DPRINTF("%s: block write finished\n", __func__); - pfl->wcycle++; - if (!pfl->ro) { - /* Flush the entire write buffer onto backing storage. */ - pflash_update(pfl, offset & mask, pfl->writeblock_size); - } else { - pfl->status |= 0x10; /* Programming error */ - } - } - - pfl->counter--; - break; - default: - goto error_flash; - } - break; - case 3: /* Confirm mode */ - switch (pfl->cmd) { - case 0xe8: /* Block write */ - if (cmd == 0xd0) { - pfl->wcycle = 0; - pfl->status |= 0x80; - } else { - DPRINTF("%s: unknown command for \"write block\"\n", __func__); - PFLASH_BUG("Write block confirm"); - goto reset_flash; - } - break; - default: - goto error_flash; - } - break; - default: - /* Should never happen */ - DPRINTF("%s: invalid write state\n", __func__); - goto reset_flash; - } - return; - - error_flash: - qemu_log_mask(LOG_UNIMP, "%s: Unimplemented flash cmd sequence " - "(offset " TARGET_FMT_plx ", wcycle 0x%x cmd 0x%x value 0x%x)" - "\n", __func__, offset, pfl->wcycle, pfl->cmd, value); - - reset_flash: - memory_region_rom_device_set_romd(&pfl->mem, true); - - pfl->wcycle = 0; - pfl->cmd = 0; -} - - -static MemTxResult pflash_mem_read_with_attrs(void *opaque, hwaddr addr, uint64_t *value, - unsigned len, MemTxAttrs attrs) -{ - pflash_t *pfl = opaque; - bool be = !!(pfl->features & (1 << PFLASH_BE)); - - if ((pfl->features & (1 << PFLASH_SECURE)) && !attrs.secure) { - *value = pflash_data_read(opaque, addr, len, be); - } else { - *value = pflash_read(opaque, addr, len, be); - } - return MEMTX_OK; -} - -static MemTxResult pflash_mem_write_with_attrs(void *opaque, hwaddr addr, uint64_t value, - unsigned len, MemTxAttrs attrs) -{ - pflash_t *pfl = opaque; - bool be = !!(pfl->features & (1 << PFLASH_BE)); - - if ((pfl->features & (1 << PFLASH_SECURE)) && !attrs.secure) { - return MEMTX_ERROR; - } else { - pflash_write(opaque, addr, value, len, be); - return MEMTX_OK; - } -} - -static const MemoryRegionOps pflash_cfi01_ops = { - .read_with_attrs = pflash_mem_read_with_attrs, - .write_with_attrs = pflash_mem_write_with_attrs, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pflash_cfi01_realize(DeviceState *dev, Error **errp) -{ - pflash_t *pfl = CFI_PFLASH01(dev); - uint64_t total_len; - int ret; - uint64_t blocks_per_device, device_len; - int num_devices; - Error *local_err = NULL; - - total_len = pfl->sector_len * pfl->nb_blocs; - - /* These are only used to expose the parameters of each device - * in the cfi_table[]. - */ - num_devices = pfl->device_width ? (pfl->bank_width / pfl->device_width) : 1; - blocks_per_device = pfl->nb_blocs / num_devices; - device_len = pfl->sector_len * blocks_per_device; - - /* XXX: to be fixed */ -#if 0 - if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) && - total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024)) - return NULL; -#endif - - memory_region_init_rom_device( - &pfl->mem, OBJECT(dev), - &pflash_cfi01_ops, - pfl, - pfl->name, total_len, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - vmstate_register_ram(&pfl->mem, DEVICE(pfl)); - pfl->storage = memory_region_get_ram_ptr(&pfl->mem); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem); - - if (pfl->blk) { - /* read the initial flash content */ - ret = blk_read(pfl->blk, 0, pfl->storage, total_len >> 9); - - if (ret < 0) { - vmstate_unregister_ram(&pfl->mem, DEVICE(pfl)); - error_setg(errp, "failed to read the initial flash content"); - return; - } - } - - if (pfl->blk) { - pfl->ro = blk_is_read_only(pfl->blk); - } else { - pfl->ro = 0; - } - - /* Default to devices being used at their maximum device width. This was - * assumed before the device_width support was added. - */ - if (!pfl->max_device_width) { - pfl->max_device_width = pfl->device_width; - } - - pfl->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pflash_timer, pfl); - pfl->wcycle = 0; - pfl->cmd = 0; - pfl->status = 0; - /* Hardcoded CFI table */ - pfl->cfi_len = 0x52; - /* Standard "QRY" string */ - pfl->cfi_table[0x10] = 'Q'; - pfl->cfi_table[0x11] = 'R'; - pfl->cfi_table[0x12] = 'Y'; - /* Command set (Intel) */ - pfl->cfi_table[0x13] = 0x01; - pfl->cfi_table[0x14] = 0x00; - /* Primary extended table address (none) */ - pfl->cfi_table[0x15] = 0x31; - pfl->cfi_table[0x16] = 0x00; - /* Alternate command set (none) */ - pfl->cfi_table[0x17] = 0x00; - pfl->cfi_table[0x18] = 0x00; - /* Alternate extended table (none) */ - pfl->cfi_table[0x19] = 0x00; - pfl->cfi_table[0x1A] = 0x00; - /* Vcc min */ - pfl->cfi_table[0x1B] = 0x45; - /* Vcc max */ - pfl->cfi_table[0x1C] = 0x55; - /* Vpp min (no Vpp pin) */ - pfl->cfi_table[0x1D] = 0x00; - /* Vpp max (no Vpp pin) */ - pfl->cfi_table[0x1E] = 0x00; - /* Reserved */ - pfl->cfi_table[0x1F] = 0x07; - /* Timeout for min size buffer write */ - pfl->cfi_table[0x20] = 0x07; - /* Typical timeout for block erase */ - pfl->cfi_table[0x21] = 0x0a; - /* Typical timeout for full chip erase (4096 ms) */ - pfl->cfi_table[0x22] = 0x00; - /* Reserved */ - pfl->cfi_table[0x23] = 0x04; - /* Max timeout for buffer write */ - pfl->cfi_table[0x24] = 0x04; - /* Max timeout for block erase */ - pfl->cfi_table[0x25] = 0x04; - /* Max timeout for chip erase */ - pfl->cfi_table[0x26] = 0x00; - /* Device size */ - pfl->cfi_table[0x27] = ctz32(device_len); /* + 1; */ - /* Flash device interface (8 & 16 bits) */ - pfl->cfi_table[0x28] = 0x02; - pfl->cfi_table[0x29] = 0x00; - /* Max number of bytes in multi-bytes write */ - if (pfl->bank_width == 1) { - pfl->cfi_table[0x2A] = 0x08; - } else { - pfl->cfi_table[0x2A] = 0x0B; - } - pfl->writeblock_size = 1 << pfl->cfi_table[0x2A]; - - pfl->cfi_table[0x2B] = 0x00; - /* Number of erase block regions (uniform) */ - pfl->cfi_table[0x2C] = 0x01; - /* Erase block region 1 */ - pfl->cfi_table[0x2D] = blocks_per_device - 1; - pfl->cfi_table[0x2E] = (blocks_per_device - 1) >> 8; - pfl->cfi_table[0x2F] = pfl->sector_len >> 8; - pfl->cfi_table[0x30] = pfl->sector_len >> 16; - - /* Extended */ - pfl->cfi_table[0x31] = 'P'; - pfl->cfi_table[0x32] = 'R'; - pfl->cfi_table[0x33] = 'I'; - - pfl->cfi_table[0x34] = '1'; - pfl->cfi_table[0x35] = '0'; - - pfl->cfi_table[0x36] = 0x00; - pfl->cfi_table[0x37] = 0x00; - pfl->cfi_table[0x38] = 0x00; - pfl->cfi_table[0x39] = 0x00; - - pfl->cfi_table[0x3a] = 0x00; - - pfl->cfi_table[0x3b] = 0x00; - pfl->cfi_table[0x3c] = 0x00; - - pfl->cfi_table[0x3f] = 0x01; /* Number of protection fields */ -} - -static Property pflash_cfi01_properties[] = { - DEFINE_PROP_DRIVE("drive", struct pflash_t, blk), - /* num-blocks is the number of blocks actually visible to the guest, - * ie the total size of the device divided by the sector length. - * If we're emulating flash devices wired in parallel the actual - * number of blocks per indvidual device will differ. - */ - DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0), - DEFINE_PROP_UINT64("sector-length", struct pflash_t, sector_len, 0), - /* width here is the overall width of this QEMU device in bytes. - * The QEMU device may be emulating a number of flash devices - * wired up in parallel; the width of each individual flash - * device should be specified via device-width. If the individual - * devices have a maximum width which is greater than the width - * they are being used for, this maximum width should be set via - * max-device-width (which otherwise defaults to device-width). - * So for instance a 32-bit wide QEMU flash device made from four - * 16-bit flash devices used in 8-bit wide mode would be configured - * with width = 4, device-width = 1, max-device-width = 2. - * - * If device-width is not specified we default to backwards - * compatible behaviour which is a bad emulation of two - * 16 bit devices making up a 32 bit wide QEMU device. This - * is deprecated for new uses of this device. - */ - DEFINE_PROP_UINT8("width", struct pflash_t, bank_width, 0), - DEFINE_PROP_UINT8("device-width", struct pflash_t, device_width, 0), - DEFINE_PROP_UINT8("max-device-width", struct pflash_t, max_device_width, 0), - DEFINE_PROP_BIT("big-endian", struct pflash_t, features, PFLASH_BE, 0), - DEFINE_PROP_BIT("secure", struct pflash_t, features, PFLASH_SECURE, 0), - DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0), - DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0), - DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0), - DEFINE_PROP_UINT16("id3", struct pflash_t, ident3, 0), - DEFINE_PROP_STRING("name", struct pflash_t, name), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pflash_cfi01_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = pflash_cfi01_realize; - dc->props = pflash_cfi01_properties; - dc->vmsd = &vmstate_pflash; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - - -static const TypeInfo pflash_cfi01_info = { - .name = TYPE_CFI_PFLASH01, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct pflash_t), - .class_init = pflash_cfi01_class_init, -}; - -static void pflash_cfi01_register_types(void) -{ - type_register_static(&pflash_cfi01_info); -} - -type_init(pflash_cfi01_register_types) - -pflash_t *pflash_cfi01_register(hwaddr base, - DeviceState *qdev, const char *name, - hwaddr size, - BlockBackend *blk, - uint32_t sector_len, int nb_blocs, - int bank_width, uint16_t id0, uint16_t id1, - uint16_t id2, uint16_t id3, int be) -{ - DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH01); - - if (blk) { - qdev_prop_set_drive(dev, "drive", blk, &error_abort); - } - qdev_prop_set_uint32(dev, "num-blocks", nb_blocs); - qdev_prop_set_uint64(dev, "sector-length", sector_len); - qdev_prop_set_uint8(dev, "width", bank_width); - qdev_prop_set_bit(dev, "big-endian", !!be); - qdev_prop_set_uint16(dev, "id0", id0); - qdev_prop_set_uint16(dev, "id1", id1); - qdev_prop_set_uint16(dev, "id2", id2); - qdev_prop_set_uint16(dev, "id3", id3); - qdev_prop_set_string(dev, "name", name); - qdev_init_nofail(dev); - - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - return CFI_PFLASH01(dev); -} - -MemoryRegion *pflash_cfi01_get_memory(pflash_t *fl) -{ - return &fl->mem; -} - -static void postload_update_cb(void *opaque, int running, RunState state) -{ - pflash_t *pfl = opaque; - - /* This is called after bdrv_invalidate_cache_all. */ - qemu_del_vm_change_state_handler(pfl->vmstate); - pfl->vmstate = NULL; - - DPRINTF("%s: updating bdrv for %s\n", __func__, pfl->name); - pflash_update(pfl, 0, pfl->sector_len * pfl->nb_blocs); -} - -static int pflash_post_load(void *opaque, int version_id) -{ - pflash_t *pfl = opaque; - - if (!pfl->ro) { - pfl->vmstate = qemu_add_vm_change_state_handler(postload_update_cb, - pfl); - } - return 0; -} diff --git a/qemu/hw/block/pflash_cfi02.c b/qemu/hw/block/pflash_cfi02.c deleted file mode 100644 index b13172c6e..000000000 --- a/qemu/hw/block/pflash_cfi02.c +++ /dev/null @@ -1,797 +0,0 @@ -/* - * CFI parallel flash with AMD command set emulation - * - * Copyright (c) 2005 Jocelyn Mayer - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -/* - * For now, this code can emulate flashes of 1, 2 or 4 bytes width. - * Supported commands/modes are: - * - flash read - * - flash write - * - flash ID read - * - sector erase - * - chip erase - * - unlock bypass command - * - CFI queries - * - * It does not support flash interleaving. - * It does not implement boot blocs with reduced size - * It does not implement software data protection as found in many real chips - * It does not implement erase suspend/resume commands - * It does not implement multiple sectors erase - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/block/flash.h" -#include "qapi/error.h" -#include "qemu/timer.h" -#include "sysemu/block-backend.h" -#include "exec/address-spaces.h" -#include "qemu/host-utils.h" -#include "hw/sysbus.h" - -//#define PFLASH_DEBUG -#ifdef PFLASH_DEBUG -#define DPRINTF(fmt, ...) \ -do { \ - fprintf(stderr, "PFLASH: " fmt , ## __VA_ARGS__); \ -} while (0) -#else -#define DPRINTF(fmt, ...) do { } while (0) -#endif - -#define PFLASH_LAZY_ROMD_THRESHOLD 42 - -#define TYPE_CFI_PFLASH02 "cfi.pflash02" -#define CFI_PFLASH02(obj) OBJECT_CHECK(pflash_t, (obj), TYPE_CFI_PFLASH02) - -struct pflash_t { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - BlockBackend *blk; - uint32_t sector_len; - uint32_t nb_blocs; - uint32_t chip_len; - uint8_t mappings; - uint8_t width; - uint8_t be; - int wcycle; /* if 0, the flash is read normally */ - int bypass; - int ro; - uint8_t cmd; - uint8_t status; - /* FIXME: implement array device properties */ - uint16_t ident0; - uint16_t ident1; - uint16_t ident2; - uint16_t ident3; - uint16_t unlock_addr0; - uint16_t unlock_addr1; - uint8_t cfi_len; - uint8_t cfi_table[0x52]; - QEMUTimer *timer; - /* The device replicates the flash memory across its memory space. Emulate - * that by having a container (.mem) filled with an array of aliases - * (.mem_mappings) pointing to the flash memory (.orig_mem). - */ - MemoryRegion mem; - MemoryRegion *mem_mappings; /* array; one per mapping */ - MemoryRegion orig_mem; - int rom_mode; - int read_counter; /* used for lazy switch-back to rom mode */ - char *name; - void *storage; -}; - -/* - * Set up replicated mappings of the same region. - */ -static void pflash_setup_mappings(pflash_t *pfl) -{ - unsigned i; - hwaddr size = memory_region_size(&pfl->orig_mem); - - memory_region_init(&pfl->mem, OBJECT(pfl), "pflash", pfl->mappings * size); - pfl->mem_mappings = g_new(MemoryRegion, pfl->mappings); - for (i = 0; i < pfl->mappings; ++i) { - memory_region_init_alias(&pfl->mem_mappings[i], OBJECT(pfl), - "pflash-alias", &pfl->orig_mem, 0, size); - memory_region_add_subregion(&pfl->mem, i * size, &pfl->mem_mappings[i]); - } -} - -static void pflash_register_memory(pflash_t *pfl, int rom_mode) -{ - memory_region_rom_device_set_romd(&pfl->orig_mem, rom_mode); - pfl->rom_mode = rom_mode; -} - -static void pflash_timer (void *opaque) -{ - pflash_t *pfl = opaque; - - DPRINTF("%s: command %02x done\n", __func__, pfl->cmd); - /* Reset flash */ - pfl->status ^= 0x80; - if (pfl->bypass) { - pfl->wcycle = 2; - } else { - pflash_register_memory(pfl, 1); - pfl->wcycle = 0; - } - pfl->cmd = 0; -} - -static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, - int width, int be) -{ - hwaddr boff; - uint32_t ret; - uint8_t *p; - - DPRINTF("%s: offset " TARGET_FMT_plx "\n", __func__, offset); - ret = -1; - /* Lazy reset to ROMD mode after a certain amount of read accesses */ - if (!pfl->rom_mode && pfl->wcycle == 0 && - ++pfl->read_counter > PFLASH_LAZY_ROMD_THRESHOLD) { - pflash_register_memory(pfl, 1); - } - offset &= pfl->chip_len - 1; - boff = offset & 0xFF; - if (pfl->width == 2) - boff = boff >> 1; - else if (pfl->width == 4) - boff = boff >> 2; - switch (pfl->cmd) { - default: - /* This should never happen : reset state & treat it as a read*/ - DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd); - pfl->wcycle = 0; - pfl->cmd = 0; - /* fall through to the read code */ - case 0x80: - /* We accept reads during second unlock sequence... */ - case 0x00: - flash_read: - /* Flash area read */ - p = pfl->storage; - switch (width) { - case 1: - ret = p[offset]; -// DPRINTF("%s: data offset %08x %02x\n", __func__, offset, ret); - break; - case 2: - if (be) { - ret = p[offset] << 8; - ret |= p[offset + 1]; - } else { - ret = p[offset]; - ret |= p[offset + 1] << 8; - } -// DPRINTF("%s: data offset %08x %04x\n", __func__, offset, ret); - break; - case 4: - if (be) { - ret = p[offset] << 24; - ret |= p[offset + 1] << 16; - ret |= p[offset + 2] << 8; - ret |= p[offset + 3]; - } else { - ret = p[offset]; - ret |= p[offset + 1] << 8; - ret |= p[offset + 2] << 16; - ret |= p[offset + 3] << 24; - } -// DPRINTF("%s: data offset %08x %08x\n", __func__, offset, ret); - break; - } - break; - case 0x90: - /* flash ID read */ - switch (boff) { - case 0x00: - case 0x01: - ret = boff & 0x01 ? pfl->ident1 : pfl->ident0; - break; - case 0x02: - ret = 0x00; /* Pretend all sectors are unprotected */ - break; - case 0x0E: - case 0x0F: - ret = boff & 0x01 ? pfl->ident3 : pfl->ident2; - if (ret == (uint8_t)-1) { - goto flash_read; - } - break; - default: - goto flash_read; - } - DPRINTF("%s: ID " TARGET_FMT_plx " %x\n", __func__, boff, ret); - break; - case 0xA0: - case 0x10: - case 0x30: - /* Status register read */ - ret = pfl->status; - DPRINTF("%s: status %x\n", __func__, ret); - /* Toggle bit 6 */ - pfl->status ^= 0x40; - break; - case 0x98: - /* CFI query mode */ - if (boff > pfl->cfi_len) - ret = 0; - else - ret = pfl->cfi_table[boff]; - break; - } - - return ret; -} - -/* update flash content on disk */ -static void pflash_update(pflash_t *pfl, int offset, - int size) -{ - int offset_end; - if (pfl->blk) { - offset_end = offset + size; - /* round to sectors */ - offset = offset >> 9; - offset_end = (offset_end + 511) >> 9; - blk_write(pfl->blk, offset, pfl->storage + (offset << 9), - offset_end - offset); - } -} - -static void pflash_write (pflash_t *pfl, hwaddr offset, - uint32_t value, int width, int be) -{ - hwaddr boff; - uint8_t *p; - uint8_t cmd; - - cmd = value; - if (pfl->cmd != 0xA0 && cmd == 0xF0) { -#if 0 - DPRINTF("%s: flash reset asked (%02x %02x)\n", - __func__, pfl->cmd, cmd); -#endif - goto reset_flash; - } - DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d %d\n", __func__, - offset, value, width, pfl->wcycle); - offset &= pfl->chip_len - 1; - - DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d\n", __func__, - offset, value, width); - boff = offset & (pfl->sector_len - 1); - if (pfl->width == 2) - boff = boff >> 1; - else if (pfl->width == 4) - boff = boff >> 2; - switch (pfl->wcycle) { - case 0: - /* Set the device in I/O access mode if required */ - if (pfl->rom_mode) - pflash_register_memory(pfl, 0); - pfl->read_counter = 0; - /* We're in read mode */ - check_unlock0: - if (boff == 0x55 && cmd == 0x98) { - enter_CFI_mode: - /* Enter CFI query mode */ - pfl->wcycle = 7; - pfl->cmd = 0x98; - return; - } - if (boff != pfl->unlock_addr0 || cmd != 0xAA) { - DPRINTF("%s: unlock0 failed " TARGET_FMT_plx " %02x %04x\n", - __func__, boff, cmd, pfl->unlock_addr0); - goto reset_flash; - } - DPRINTF("%s: unlock sequence started\n", __func__); - break; - case 1: - /* We started an unlock sequence */ - check_unlock1: - if (boff != pfl->unlock_addr1 || cmd != 0x55) { - DPRINTF("%s: unlock1 failed " TARGET_FMT_plx " %02x\n", __func__, - boff, cmd); - goto reset_flash; - } - DPRINTF("%s: unlock sequence done\n", __func__); - break; - case 2: - /* We finished an unlock sequence */ - if (!pfl->bypass && boff != pfl->unlock_addr0) { - DPRINTF("%s: command failed " TARGET_FMT_plx " %02x\n", __func__, - boff, cmd); - goto reset_flash; - } - switch (cmd) { - case 0x20: - pfl->bypass = 1; - goto do_bypass; - case 0x80: - case 0x90: - case 0xA0: - pfl->cmd = cmd; - DPRINTF("%s: starting command %02x\n", __func__, cmd); - break; - default: - DPRINTF("%s: unknown command %02x\n", __func__, cmd); - goto reset_flash; - } - break; - case 3: - switch (pfl->cmd) { - case 0x80: - /* We need another unlock sequence */ - goto check_unlock0; - case 0xA0: - DPRINTF("%s: write data offset " TARGET_FMT_plx " %08x %d\n", - __func__, offset, value, width); - p = pfl->storage; - if (!pfl->ro) { - switch (width) { - case 1: - p[offset] &= value; - pflash_update(pfl, offset, 1); - break; - case 2: - if (be) { - p[offset] &= value >> 8; - p[offset + 1] &= value; - } else { - p[offset] &= value; - p[offset + 1] &= value >> 8; - } - pflash_update(pfl, offset, 2); - break; - case 4: - if (be) { - p[offset] &= value >> 24; - p[offset + 1] &= value >> 16; - p[offset + 2] &= value >> 8; - p[offset + 3] &= value; - } else { - p[offset] &= value; - p[offset + 1] &= value >> 8; - p[offset + 2] &= value >> 16; - p[offset + 3] &= value >> 24; - } - pflash_update(pfl, offset, 4); - break; - } - } - pfl->status = 0x00 | ~(value & 0x80); - /* Let's pretend write is immediate */ - if (pfl->bypass) - goto do_bypass; - goto reset_flash; - case 0x90: - if (pfl->bypass && cmd == 0x00) { - /* Unlock bypass reset */ - goto reset_flash; - } - /* We can enter CFI query mode from autoselect mode */ - if (boff == 0x55 && cmd == 0x98) - goto enter_CFI_mode; - /* No break here */ - default: - DPRINTF("%s: invalid write for command %02x\n", - __func__, pfl->cmd); - goto reset_flash; - } - case 4: - switch (pfl->cmd) { - case 0xA0: - /* Ignore writes while flash data write is occurring */ - /* As we suppose write is immediate, this should never happen */ - return; - case 0x80: - goto check_unlock1; - default: - /* Should never happen */ - DPRINTF("%s: invalid command state %02x (wc 4)\n", - __func__, pfl->cmd); - goto reset_flash; - } - break; - case 5: - switch (cmd) { - case 0x10: - if (boff != pfl->unlock_addr0) { - DPRINTF("%s: chip erase: invalid address " TARGET_FMT_plx "\n", - __func__, offset); - goto reset_flash; - } - /* Chip erase */ - DPRINTF("%s: start chip erase\n", __func__); - if (!pfl->ro) { - memset(pfl->storage, 0xFF, pfl->chip_len); - pflash_update(pfl, 0, pfl->chip_len); - } - pfl->status = 0x00; - /* Let's wait 5 seconds before chip erase is done */ - timer_mod(pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND * 5)); - break; - case 0x30: - /* Sector erase */ - p = pfl->storage; - offset &= ~(pfl->sector_len - 1); - DPRINTF("%s: start sector erase at " TARGET_FMT_plx "\n", __func__, - offset); - if (!pfl->ro) { - memset(p + offset, 0xFF, pfl->sector_len); - pflash_update(pfl, offset, pfl->sector_len); - } - pfl->status = 0x00; - /* Let's wait 1/2 second before sector erase is done */ - timer_mod(pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / 2)); - break; - default: - DPRINTF("%s: invalid command %02x (wc 5)\n", __func__, cmd); - goto reset_flash; - } - pfl->cmd = cmd; - break; - case 6: - switch (pfl->cmd) { - case 0x10: - /* Ignore writes during chip erase */ - return; - case 0x30: - /* Ignore writes during sector erase */ - return; - default: - /* Should never happen */ - DPRINTF("%s: invalid command state %02x (wc 6)\n", - __func__, pfl->cmd); - goto reset_flash; - } - break; - case 7: /* Special value for CFI queries */ - DPRINTF("%s: invalid write in CFI query mode\n", __func__); - goto reset_flash; - default: - /* Should never happen */ - DPRINTF("%s: invalid write state (wc 7)\n", __func__); - goto reset_flash; - } - pfl->wcycle++; - - return; - - /* Reset flash */ - reset_flash: - pfl->bypass = 0; - pfl->wcycle = 0; - pfl->cmd = 0; - return; - - do_bypass: - pfl->wcycle = 2; - pfl->cmd = 0; -} - - -static uint32_t pflash_readb_be(void *opaque, hwaddr addr) -{ - return pflash_read(opaque, addr, 1, 1); -} - -static uint32_t pflash_readb_le(void *opaque, hwaddr addr) -{ - return pflash_read(opaque, addr, 1, 0); -} - -static uint32_t pflash_readw_be(void *opaque, hwaddr addr) -{ - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 2, 1); -} - -static uint32_t pflash_readw_le(void *opaque, hwaddr addr) -{ - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 2, 0); -} - -static uint32_t pflash_readl_be(void *opaque, hwaddr addr) -{ - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 4, 1); -} - -static uint32_t pflash_readl_le(void *opaque, hwaddr addr) -{ - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 4, 0); -} - -static void pflash_writeb_be(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_write(opaque, addr, value, 1, 1); -} - -static void pflash_writeb_le(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_write(opaque, addr, value, 1, 0); -} - -static void pflash_writew_be(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 2, 1); -} - -static void pflash_writew_le(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 2, 0); -} - -static void pflash_writel_be(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 4, 1); -} - -static void pflash_writel_le(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 4, 0); -} - -static const MemoryRegionOps pflash_cfi02_ops_be = { - .old_mmio = { - .read = { pflash_readb_be, pflash_readw_be, pflash_readl_be, }, - .write = { pflash_writeb_be, pflash_writew_be, pflash_writel_be, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const MemoryRegionOps pflash_cfi02_ops_le = { - .old_mmio = { - .read = { pflash_readb_le, pflash_readw_le, pflash_readl_le, }, - .write = { pflash_writeb_le, pflash_writew_le, pflash_writel_le, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pflash_cfi02_realize(DeviceState *dev, Error **errp) -{ - pflash_t *pfl = CFI_PFLASH02(dev); - uint32_t chip_len; - int ret; - Error *local_err = NULL; - - chip_len = pfl->sector_len * pfl->nb_blocs; - /* XXX: to be fixed */ -#if 0 - if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) && - total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024)) - return NULL; -#endif - - memory_region_init_rom_device(&pfl->orig_mem, OBJECT(pfl), pfl->be ? - &pflash_cfi02_ops_be : &pflash_cfi02_ops_le, - pfl, pfl->name, chip_len, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - vmstate_register_ram(&pfl->orig_mem, DEVICE(pfl)); - pfl->storage = memory_region_get_ram_ptr(&pfl->orig_mem); - pfl->chip_len = chip_len; - if (pfl->blk) { - /* read the initial flash content */ - ret = blk_read(pfl->blk, 0, pfl->storage, chip_len >> 9); - if (ret < 0) { - vmstate_unregister_ram(&pfl->orig_mem, DEVICE(pfl)); - error_setg(errp, "failed to read the initial flash content"); - return; - } - } - - pflash_setup_mappings(pfl); - pfl->rom_mode = 1; - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem); - - if (pfl->blk) { - pfl->ro = blk_is_read_only(pfl->blk); - } else { - pfl->ro = 0; - } - - pfl->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pflash_timer, pfl); - pfl->wcycle = 0; - pfl->cmd = 0; - pfl->status = 0; - /* Hardcoded CFI table (mostly from SG29 Spansion flash) */ - pfl->cfi_len = 0x52; - /* Standard "QRY" string */ - pfl->cfi_table[0x10] = 'Q'; - pfl->cfi_table[0x11] = 'R'; - pfl->cfi_table[0x12] = 'Y'; - /* Command set (AMD/Fujitsu) */ - pfl->cfi_table[0x13] = 0x02; - pfl->cfi_table[0x14] = 0x00; - /* Primary extended table address */ - pfl->cfi_table[0x15] = 0x31; - pfl->cfi_table[0x16] = 0x00; - /* Alternate command set (none) */ - pfl->cfi_table[0x17] = 0x00; - pfl->cfi_table[0x18] = 0x00; - /* Alternate extended table (none) */ - pfl->cfi_table[0x19] = 0x00; - pfl->cfi_table[0x1A] = 0x00; - /* Vcc min */ - pfl->cfi_table[0x1B] = 0x27; - /* Vcc max */ - pfl->cfi_table[0x1C] = 0x36; - /* Vpp min (no Vpp pin) */ - pfl->cfi_table[0x1D] = 0x00; - /* Vpp max (no Vpp pin) */ - pfl->cfi_table[0x1E] = 0x00; - /* Reserved */ - pfl->cfi_table[0x1F] = 0x07; - /* Timeout for min size buffer write (NA) */ - pfl->cfi_table[0x20] = 0x00; - /* Typical timeout for block erase (512 ms) */ - pfl->cfi_table[0x21] = 0x09; - /* Typical timeout for full chip erase (4096 ms) */ - pfl->cfi_table[0x22] = 0x0C; - /* Reserved */ - pfl->cfi_table[0x23] = 0x01; - /* Max timeout for buffer write (NA) */ - pfl->cfi_table[0x24] = 0x00; - /* Max timeout for block erase */ - pfl->cfi_table[0x25] = 0x0A; - /* Max timeout for chip erase */ - pfl->cfi_table[0x26] = 0x0D; - /* Device size */ - pfl->cfi_table[0x27] = ctz32(chip_len); - /* Flash device interface (8 & 16 bits) */ - pfl->cfi_table[0x28] = 0x02; - pfl->cfi_table[0x29] = 0x00; - /* Max number of bytes in multi-bytes write */ - /* XXX: disable buffered write as it's not supported */ - // pfl->cfi_table[0x2A] = 0x05; - pfl->cfi_table[0x2A] = 0x00; - pfl->cfi_table[0x2B] = 0x00; - /* Number of erase block regions (uniform) */ - pfl->cfi_table[0x2C] = 0x01; - /* Erase block region 1 */ - pfl->cfi_table[0x2D] = pfl->nb_blocs - 1; - pfl->cfi_table[0x2E] = (pfl->nb_blocs - 1) >> 8; - pfl->cfi_table[0x2F] = pfl->sector_len >> 8; - pfl->cfi_table[0x30] = pfl->sector_len >> 16; - - /* Extended */ - pfl->cfi_table[0x31] = 'P'; - pfl->cfi_table[0x32] = 'R'; - pfl->cfi_table[0x33] = 'I'; - - pfl->cfi_table[0x34] = '1'; - pfl->cfi_table[0x35] = '0'; - - pfl->cfi_table[0x36] = 0x00; - pfl->cfi_table[0x37] = 0x00; - pfl->cfi_table[0x38] = 0x00; - pfl->cfi_table[0x39] = 0x00; - - pfl->cfi_table[0x3a] = 0x00; - - pfl->cfi_table[0x3b] = 0x00; - pfl->cfi_table[0x3c] = 0x00; -} - -static Property pflash_cfi02_properties[] = { - DEFINE_PROP_DRIVE("drive", struct pflash_t, blk), - DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0), - DEFINE_PROP_UINT32("sector-length", struct pflash_t, sector_len, 0), - DEFINE_PROP_UINT8("width", struct pflash_t, width, 0), - DEFINE_PROP_UINT8("mappings", struct pflash_t, mappings, 0), - DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0), - DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0), - DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0), - DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0), - DEFINE_PROP_UINT16("id3", struct pflash_t, ident3, 0), - DEFINE_PROP_UINT16("unlock-addr0", struct pflash_t, unlock_addr0, 0), - DEFINE_PROP_UINT16("unlock-addr1", struct pflash_t, unlock_addr1, 0), - DEFINE_PROP_STRING("name", struct pflash_t, name), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pflash_cfi02_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = pflash_cfi02_realize; - dc->props = pflash_cfi02_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo pflash_cfi02_info = { - .name = TYPE_CFI_PFLASH02, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct pflash_t), - .class_init = pflash_cfi02_class_init, -}; - -static void pflash_cfi02_register_types(void) -{ - type_register_static(&pflash_cfi02_info); -} - -type_init(pflash_cfi02_register_types) - -pflash_t *pflash_cfi02_register(hwaddr base, - DeviceState *qdev, const char *name, - hwaddr size, - BlockBackend *blk, uint32_t sector_len, - int nb_blocs, int nb_mappings, int width, - uint16_t id0, uint16_t id1, - uint16_t id2, uint16_t id3, - uint16_t unlock_addr0, uint16_t unlock_addr1, - int be) -{ - DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH02); - - if (blk) { - qdev_prop_set_drive(dev, "drive", blk, &error_abort); - } - qdev_prop_set_uint32(dev, "num-blocks", nb_blocs); - qdev_prop_set_uint32(dev, "sector-length", sector_len); - qdev_prop_set_uint8(dev, "width", width); - qdev_prop_set_uint8(dev, "mappings", nb_mappings); - qdev_prop_set_uint8(dev, "big-endian", !!be); - qdev_prop_set_uint16(dev, "id0", id0); - qdev_prop_set_uint16(dev, "id1", id1); - qdev_prop_set_uint16(dev, "id2", id2); - qdev_prop_set_uint16(dev, "id3", id3); - qdev_prop_set_uint16(dev, "unlock-addr0", unlock_addr0); - qdev_prop_set_uint16(dev, "unlock-addr1", unlock_addr1); - qdev_prop_set_string(dev, "name", name); - qdev_init_nofail(dev); - - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - return CFI_PFLASH02(dev); -} diff --git a/qemu/hw/block/tc58128.c b/qemu/hw/block/tc58128.c deleted file mode 100644 index 7909d5041..000000000 --- a/qemu/hw/block/tc58128.c +++ /dev/null @@ -1,181 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sh4/sh.h" -#include "hw/loader.h" -#include "sysemu/qtest.h" -#include "qemu/error-report.h" - -#define CE1 0x0100 -#define CE2 0x0200 -#define RE 0x0400 -#define WE 0x0800 -#define ALE 0x1000 -#define CLE 0x2000 -#define RDY1 0x4000 -#define RDY2 0x8000 -#define RDY(n) ((n) == 0 ? RDY1 : RDY2) - -typedef enum { WAIT, READ1, READ2, READ3 } state_t; - -typedef struct { - uint8_t *flash_contents; - state_t state; - uint32_t address; - uint8_t address_cycle; -} tc58128_dev; - -static tc58128_dev tc58128_devs[2]; - -#define FLASH_SIZE (16*1024*1024) - -static void init_dev(tc58128_dev * dev, const char *filename) -{ - int ret, blocks; - - dev->state = WAIT; - dev->flash_contents = g_malloc(FLASH_SIZE); - memset(dev->flash_contents, 0xff, FLASH_SIZE); - if (filename) { - /* Load flash image skipping the first block */ - ret = load_image(filename, dev->flash_contents + 528 * 32); - if (ret < 0) { - if (!qtest_enabled()) { - error_report("Could not load flash image %s", filename); - exit(1); - } - } else { - /* Build first block with number of blocks */ - blocks = (ret + 528 * 32 - 1) / (528 * 32); - dev->flash_contents[0] = blocks & 0xff; - dev->flash_contents[1] = (blocks >> 8) & 0xff; - dev->flash_contents[2] = (blocks >> 16) & 0xff; - dev->flash_contents[3] = (blocks >> 24) & 0xff; - fprintf(stderr, "loaded %d bytes for %s into flash\n", ret, - filename); - } - } -} - -static void handle_command(tc58128_dev * dev, uint8_t command) -{ - switch (command) { - case 0xff: - fprintf(stderr, "reset flash device\n"); - dev->state = WAIT; - break; - case 0x00: - fprintf(stderr, "read mode 1\n"); - dev->state = READ1; - dev->address_cycle = 0; - break; - case 0x01: - fprintf(stderr, "read mode 2\n"); - dev->state = READ2; - dev->address_cycle = 0; - break; - case 0x50: - fprintf(stderr, "read mode 3\n"); - dev->state = READ3; - dev->address_cycle = 0; - break; - default: - fprintf(stderr, "unknown flash command 0x%02x\n", command); - abort(); - } -} - -static void handle_address(tc58128_dev * dev, uint8_t data) -{ - switch (dev->state) { - case READ1: - case READ2: - case READ3: - switch (dev->address_cycle) { - case 0: - dev->address = data; - if (dev->state == READ2) - dev->address |= 0x100; - else if (dev->state == READ3) - dev->address |= 0x200; - break; - case 1: - dev->address += data * 528 * 0x100; - break; - case 2: - dev->address += data * 528; - fprintf(stderr, "address pointer in flash: 0x%08x\n", - dev->address); - break; - default: - /* Invalid data */ - abort(); - } - dev->address_cycle++; - break; - default: - abort(); - } -} - -static uint8_t handle_read(tc58128_dev * dev) -{ -#if 0 - if (dev->address % 0x100000 == 0) - fprintf(stderr, "reading flash at address 0x%08x\n", dev->address); -#endif - return dev->flash_contents[dev->address++]; -} - -/* We never mark the device as busy, so interrupts cannot be triggered - XXXXX */ - -static int tc58128_cb(uint16_t porta, uint16_t portb, - uint16_t * periph_pdtra, uint16_t * periph_portadir, - uint16_t * periph_pdtrb, uint16_t * periph_portbdir) -{ - int dev; - - if ((porta & CE1) == 0) - dev = 0; - else if ((porta & CE2) == 0) - dev = 1; - else - return 0; /* No device selected */ - - if ((porta & RE) && (porta & WE)) { - /* Nothing to do, assert ready and return to input state */ - *periph_portadir &= 0xff00; - *periph_portadir |= RDY(dev); - *periph_pdtra |= RDY(dev); - return 1; - } - - if (porta & CLE) { - /* Command */ - assert((porta & WE) == 0); - handle_command(&tc58128_devs[dev], porta & 0x00ff); - } else if (porta & ALE) { - assert((porta & WE) == 0); - handle_address(&tc58128_devs[dev], porta & 0x00ff); - } else if ((porta & RE) == 0) { - *periph_portadir |= 0x00ff; - *periph_pdtra &= 0xff00; - *periph_pdtra |= handle_read(&tc58128_devs[dev]); - } else { - abort(); - } - return 1; -} - -static sh7750_io_device tc58128 = { - RE | WE, /* Port A triggers */ - 0, /* Port B triggers */ - tc58128_cb /* Callback */ -}; - -int tc58128_init(struct SH7750State *s, const char *zone1, const char *zone2) -{ - init_dev(&tc58128_devs[0], zone1); - init_dev(&tc58128_devs[1], zone2); - return sh7750_register_io_device(s, &tc58128); -} diff --git a/qemu/hw/block/virtio-blk.c b/qemu/hw/block/virtio-blk.c deleted file mode 100644 index 3f88f8cf5..000000000 --- a/qemu/hw/block/virtio-blk.c +++ /dev/null @@ -1,980 +0,0 @@ -/* - * Virtio Block Device - * - * Copyright IBM, Corp. 2007 - * - * Authors: - * Anthony Liguori <aliguori@us.ibm.com> - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "qemu/iov.h" -#include "qemu/error-report.h" -#include "trace.h" -#include "hw/block/block.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "hw/virtio/virtio-blk.h" -#include "dataplane/virtio-blk.h" -#include "block/scsi.h" -#ifdef __linux__ -# include <scsi/sg.h> -#endif -#include "hw/virtio/virtio-bus.h" -#include "hw/virtio/virtio-access.h" - -void virtio_blk_init_request(VirtIOBlock *s, VirtIOBlockReq *req) -{ - req->dev = s; - req->qiov.size = 0; - req->in_len = 0; - req->next = NULL; - req->mr_next = NULL; -} - -void virtio_blk_free_request(VirtIOBlockReq *req) -{ - if (req) { - g_free(req); - } -} - -static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) -{ - VirtIOBlock *s = req->dev; - VirtIODevice *vdev = VIRTIO_DEVICE(s); - - trace_virtio_blk_req_complete(req, status); - - stb_p(&req->in->status, status); - virtqueue_push(s->vq, &req->elem, req->in_len); - if (s->dataplane_started && !s->dataplane_disabled) { - virtio_blk_data_plane_notify(s->dataplane); - } else { - virtio_notify(vdev, s->vq); - } -} - -static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, - bool is_read) -{ - BlockErrorAction action = blk_get_error_action(req->dev->blk, - is_read, error); - VirtIOBlock *s = req->dev; - - if (action == BLOCK_ERROR_ACTION_STOP) { - /* Break the link as the next request is going to be parsed from the - * ring again. Otherwise we may end up doing a double completion! */ - req->mr_next = NULL; - req->next = s->rq; - s->rq = req; - } else if (action == BLOCK_ERROR_ACTION_REPORT) { - virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); - block_acct_failed(blk_get_stats(s->blk), &req->acct); - virtio_blk_free_request(req); - } - - blk_error_action(s->blk, action, is_read, error); - return action != BLOCK_ERROR_ACTION_IGNORE; -} - -static void virtio_blk_rw_complete(void *opaque, int ret) -{ - VirtIOBlockReq *next = opaque; - - while (next) { - VirtIOBlockReq *req = next; - next = req->mr_next; - trace_virtio_blk_rw_complete(req, ret); - - if (req->qiov.nalloc != -1) { - /* If nalloc is != 1 req->qiov is a local copy of the original - * external iovec. It was allocated in submit_merged_requests - * to be able to merge requests. */ - qemu_iovec_destroy(&req->qiov); - } - - if (ret) { - int p = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); - bool is_read = !(p & VIRTIO_BLK_T_OUT); - /* Note that memory may be dirtied on read failure. If the - * virtio request is not completed here, as is the case for - * BLOCK_ERROR_ACTION_STOP, the memory may not be copied - * correctly during live migration. While this is ugly, - * it is acceptable because the device is free to write to - * the memory until the request is completed (which will - * happen on the other side of the migration). - */ - if (virtio_blk_handle_rw_error(req, -ret, is_read)) { - continue; - } - } - - virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); - block_acct_done(blk_get_stats(req->dev->blk), &req->acct); - virtio_blk_free_request(req); - } -} - -static void virtio_blk_flush_complete(void *opaque, int ret) -{ - VirtIOBlockReq *req = opaque; - - if (ret) { - if (virtio_blk_handle_rw_error(req, -ret, 0)) { - return; - } - } - - virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); - block_acct_done(blk_get_stats(req->dev->blk), &req->acct); - virtio_blk_free_request(req); -} - -#ifdef __linux__ - -typedef struct { - VirtIOBlockReq *req; - struct sg_io_hdr hdr; -} VirtIOBlockIoctlReq; - -static void virtio_blk_ioctl_complete(void *opaque, int status) -{ - VirtIOBlockIoctlReq *ioctl_req = opaque; - VirtIOBlockReq *req = ioctl_req->req; - VirtIODevice *vdev = VIRTIO_DEVICE(req->dev); - struct virtio_scsi_inhdr *scsi; - struct sg_io_hdr *hdr; - - scsi = (void *)req->elem.in_sg[req->elem.in_num - 2].iov_base; - - if (status) { - status = VIRTIO_BLK_S_UNSUPP; - virtio_stl_p(vdev, &scsi->errors, 255); - goto out; - } - - hdr = &ioctl_req->hdr; - /* - * From SCSI-Generic-HOWTO: "Some lower level drivers (e.g. ide-scsi) - * clear the masked_status field [hence status gets cleared too, see - * block/scsi_ioctl.c] even when a CHECK_CONDITION or COMMAND_TERMINATED - * status has occurred. However they do set DRIVER_SENSE in driver_status - * field. Also a (sb_len_wr > 0) indicates there is a sense buffer. - */ - if (hdr->status == 0 && hdr->sb_len_wr > 0) { - hdr->status = CHECK_CONDITION; - } - - virtio_stl_p(vdev, &scsi->errors, - hdr->status | (hdr->msg_status << 8) | - (hdr->host_status << 16) | (hdr->driver_status << 24)); - virtio_stl_p(vdev, &scsi->residual, hdr->resid); - virtio_stl_p(vdev, &scsi->sense_len, hdr->sb_len_wr); - virtio_stl_p(vdev, &scsi->data_len, hdr->dxfer_len); - -out: - virtio_blk_req_complete(req, status); - virtio_blk_free_request(req); - g_free(ioctl_req); -} - -#endif - -static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s) -{ - VirtIOBlockReq *req = virtqueue_pop(s->vq, sizeof(VirtIOBlockReq)); - - if (req) { - virtio_blk_init_request(s, req); - } - return req; -} - -static int virtio_blk_handle_scsi_req(VirtIOBlockReq *req) -{ - int status = VIRTIO_BLK_S_OK; - struct virtio_scsi_inhdr *scsi = NULL; - VirtIODevice *vdev = VIRTIO_DEVICE(req->dev); - VirtQueueElement *elem = &req->elem; - VirtIOBlock *blk = req->dev; - -#ifdef __linux__ - int i; - VirtIOBlockIoctlReq *ioctl_req; - BlockAIOCB *acb; -#endif - - /* - * We require at least one output segment each for the virtio_blk_outhdr - * and the SCSI command block. - * - * We also at least require the virtio_blk_inhdr, the virtio_scsi_inhdr - * and the sense buffer pointer in the input segments. - */ - if (elem->out_num < 2 || elem->in_num < 3) { - status = VIRTIO_BLK_S_IOERR; - goto fail; - } - - /* - * The scsi inhdr is placed in the second-to-last input segment, just - * before the regular inhdr. - */ - scsi = (void *)elem->in_sg[elem->in_num - 2].iov_base; - - if (!blk->conf.scsi) { - status = VIRTIO_BLK_S_UNSUPP; - goto fail; - } - - /* - * No support for bidirection commands yet. - */ - if (elem->out_num > 2 && elem->in_num > 3) { - status = VIRTIO_BLK_S_UNSUPP; - goto fail; - } - -#ifdef __linux__ - ioctl_req = g_new0(VirtIOBlockIoctlReq, 1); - ioctl_req->req = req; - ioctl_req->hdr.interface_id = 'S'; - ioctl_req->hdr.cmd_len = elem->out_sg[1].iov_len; - ioctl_req->hdr.cmdp = elem->out_sg[1].iov_base; - ioctl_req->hdr.dxfer_len = 0; - - if (elem->out_num > 2) { - /* - * If there are more than the minimally required 2 output segments - * there is write payload starting from the third iovec. - */ - ioctl_req->hdr.dxfer_direction = SG_DXFER_TO_DEV; - ioctl_req->hdr.iovec_count = elem->out_num - 2; - - for (i = 0; i < ioctl_req->hdr.iovec_count; i++) { - ioctl_req->hdr.dxfer_len += elem->out_sg[i + 2].iov_len; - } - - ioctl_req->hdr.dxferp = elem->out_sg + 2; - - } else if (elem->in_num > 3) { - /* - * If we have more than 3 input segments the guest wants to actually - * read data. - */ - ioctl_req->hdr.dxfer_direction = SG_DXFER_FROM_DEV; - ioctl_req->hdr.iovec_count = elem->in_num - 3; - for (i = 0; i < ioctl_req->hdr.iovec_count; i++) { - ioctl_req->hdr.dxfer_len += elem->in_sg[i].iov_len; - } - - ioctl_req->hdr.dxferp = elem->in_sg; - } else { - /* - * Some SCSI commands don't actually transfer any data. - */ - ioctl_req->hdr.dxfer_direction = SG_DXFER_NONE; - } - - ioctl_req->hdr.sbp = elem->in_sg[elem->in_num - 3].iov_base; - ioctl_req->hdr.mx_sb_len = elem->in_sg[elem->in_num - 3].iov_len; - - acb = blk_aio_ioctl(blk->blk, SG_IO, &ioctl_req->hdr, - virtio_blk_ioctl_complete, ioctl_req); - if (!acb) { - g_free(ioctl_req); - status = VIRTIO_BLK_S_UNSUPP; - goto fail; - } - return -EINPROGRESS; -#else - abort(); -#endif - -fail: - /* Just put anything nonzero so that the ioctl fails in the guest. */ - if (scsi) { - virtio_stl_p(vdev, &scsi->errors, 255); - } - return status; -} - -static void virtio_blk_handle_scsi(VirtIOBlockReq *req) -{ - int status; - - status = virtio_blk_handle_scsi_req(req); - if (status != -EINPROGRESS) { - virtio_blk_req_complete(req, status); - virtio_blk_free_request(req); - } -} - -static inline void submit_requests(BlockBackend *blk, MultiReqBuffer *mrb, - int start, int num_reqs, int niov) -{ - QEMUIOVector *qiov = &mrb->reqs[start]->qiov; - int64_t sector_num = mrb->reqs[start]->sector_num; - int nb_sectors = mrb->reqs[start]->qiov.size / BDRV_SECTOR_SIZE; - bool is_write = mrb->is_write; - - if (num_reqs > 1) { - int i; - struct iovec *tmp_iov = qiov->iov; - int tmp_niov = qiov->niov; - - /* mrb->reqs[start]->qiov was initialized from external so we can't - * modifiy it here. We need to initialize it locally and then add the - * external iovecs. */ - qemu_iovec_init(qiov, niov); - - for (i = 0; i < tmp_niov; i++) { - qemu_iovec_add(qiov, tmp_iov[i].iov_base, tmp_iov[i].iov_len); - } - - for (i = start + 1; i < start + num_reqs; i++) { - qemu_iovec_concat(qiov, &mrb->reqs[i]->qiov, 0, - mrb->reqs[i]->qiov.size); - mrb->reqs[i - 1]->mr_next = mrb->reqs[i]; - nb_sectors += mrb->reqs[i]->qiov.size / BDRV_SECTOR_SIZE; - } - assert(nb_sectors == qiov->size / BDRV_SECTOR_SIZE); - - trace_virtio_blk_submit_multireq(mrb, start, num_reqs, sector_num, - nb_sectors, is_write); - block_acct_merge_done(blk_get_stats(blk), - is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ, - num_reqs - 1); - } - - if (is_write) { - blk_aio_writev(blk, sector_num, qiov, nb_sectors, - virtio_blk_rw_complete, mrb->reqs[start]); - } else { - blk_aio_readv(blk, sector_num, qiov, nb_sectors, - virtio_blk_rw_complete, mrb->reqs[start]); - } -} - -static int multireq_compare(const void *a, const void *b) -{ - const VirtIOBlockReq *req1 = *(VirtIOBlockReq **)a, - *req2 = *(VirtIOBlockReq **)b; - - /* - * Note that we can't simply subtract sector_num1 from sector_num2 - * here as that could overflow the return value. - */ - if (req1->sector_num > req2->sector_num) { - return 1; - } else if (req1->sector_num < req2->sector_num) { - return -1; - } else { - return 0; - } -} - -void virtio_blk_submit_multireq(BlockBackend *blk, MultiReqBuffer *mrb) -{ - int i = 0, start = 0, num_reqs = 0, niov = 0, nb_sectors = 0; - int max_xfer_len = 0; - int64_t sector_num = 0; - - if (mrb->num_reqs == 1) { - submit_requests(blk, mrb, 0, 1, -1); - mrb->num_reqs = 0; - return; - } - - max_xfer_len = blk_get_max_transfer_length(mrb->reqs[0]->dev->blk); - max_xfer_len = MIN_NON_ZERO(max_xfer_len, BDRV_REQUEST_MAX_SECTORS); - - qsort(mrb->reqs, mrb->num_reqs, sizeof(*mrb->reqs), - &multireq_compare); - - for (i = 0; i < mrb->num_reqs; i++) { - VirtIOBlockReq *req = mrb->reqs[i]; - if (num_reqs > 0) { - /* - * NOTE: We cannot merge the requests in below situations: - * 1. requests are not sequential - * 2. merge would exceed maximum number of IOVs - * 3. merge would exceed maximum transfer length of backend device - */ - if (sector_num + nb_sectors != req->sector_num || - niov > blk_get_max_iov(blk) - req->qiov.niov || - req->qiov.size / BDRV_SECTOR_SIZE > max_xfer_len || - nb_sectors > max_xfer_len - req->qiov.size / BDRV_SECTOR_SIZE) { - submit_requests(blk, mrb, start, num_reqs, niov); - num_reqs = 0; - } - } - - if (num_reqs == 0) { - sector_num = req->sector_num; - nb_sectors = niov = 0; - start = i; - } - - nb_sectors += req->qiov.size / BDRV_SECTOR_SIZE; - niov += req->qiov.niov; - num_reqs++; - } - - submit_requests(blk, mrb, start, num_reqs, niov); - mrb->num_reqs = 0; -} - -static void virtio_blk_handle_flush(VirtIOBlockReq *req, MultiReqBuffer *mrb) -{ - block_acct_start(blk_get_stats(req->dev->blk), &req->acct, 0, - BLOCK_ACCT_FLUSH); - - /* - * Make sure all outstanding writes are posted to the backing device. - */ - if (mrb->is_write && mrb->num_reqs > 0) { - virtio_blk_submit_multireq(req->dev->blk, mrb); - } - blk_aio_flush(req->dev->blk, virtio_blk_flush_complete, req); -} - -static bool virtio_blk_sect_range_ok(VirtIOBlock *dev, - uint64_t sector, size_t size) -{ - uint64_t nb_sectors = size >> BDRV_SECTOR_BITS; - uint64_t total_sectors; - - if (nb_sectors > BDRV_REQUEST_MAX_SECTORS) { - return false; - } - if (sector & dev->sector_mask) { - return false; - } - if (size % dev->conf.conf.logical_block_size) { - return false; - } - blk_get_geometry(dev->blk, &total_sectors); - if (sector > total_sectors || nb_sectors > total_sectors - sector) { - return false; - } - return true; -} - -void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) -{ - uint32_t type; - struct iovec *in_iov = req->elem.in_sg; - struct iovec *iov = req->elem.out_sg; - unsigned in_num = req->elem.in_num; - unsigned out_num = req->elem.out_num; - - if (req->elem.out_num < 1 || req->elem.in_num < 1) { - error_report("virtio-blk missing headers"); - exit(1); - } - - if (unlikely(iov_to_buf(iov, out_num, 0, &req->out, - sizeof(req->out)) != sizeof(req->out))) { - error_report("virtio-blk request outhdr too short"); - exit(1); - } - - iov_discard_front(&iov, &out_num, sizeof(req->out)); - - if (in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) { - error_report("virtio-blk request inhdr too short"); - exit(1); - } - - /* We always touch the last byte, so just see how big in_iov is. */ - req->in_len = iov_size(in_iov, in_num); - req->in = (void *)in_iov[in_num - 1].iov_base - + in_iov[in_num - 1].iov_len - - sizeof(struct virtio_blk_inhdr); - iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr)); - - type = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); - - /* VIRTIO_BLK_T_OUT defines the command direction. VIRTIO_BLK_T_BARRIER - * is an optional flag. Although a guest should not send this flag if - * not negotiated we ignored it in the past. So keep ignoring it. */ - switch (type & ~(VIRTIO_BLK_T_OUT | VIRTIO_BLK_T_BARRIER)) { - case VIRTIO_BLK_T_IN: - { - bool is_write = type & VIRTIO_BLK_T_OUT; - req->sector_num = virtio_ldq_p(VIRTIO_DEVICE(req->dev), - &req->out.sector); - - if (is_write) { - qemu_iovec_init_external(&req->qiov, iov, out_num); - trace_virtio_blk_handle_write(req, req->sector_num, - req->qiov.size / BDRV_SECTOR_SIZE); - } else { - qemu_iovec_init_external(&req->qiov, in_iov, in_num); - trace_virtio_blk_handle_read(req, req->sector_num, - req->qiov.size / BDRV_SECTOR_SIZE); - } - - if (!virtio_blk_sect_range_ok(req->dev, req->sector_num, - req->qiov.size)) { - virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); - block_acct_invalid(blk_get_stats(req->dev->blk), - is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ); - virtio_blk_free_request(req); - return; - } - - block_acct_start(blk_get_stats(req->dev->blk), - &req->acct, req->qiov.size, - is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ); - - /* merge would exceed maximum number of requests or IO direction - * changes */ - if (mrb->num_reqs > 0 && (mrb->num_reqs == VIRTIO_BLK_MAX_MERGE_REQS || - is_write != mrb->is_write || - !req->dev->conf.request_merging)) { - virtio_blk_submit_multireq(req->dev->blk, mrb); - } - - assert(mrb->num_reqs < VIRTIO_BLK_MAX_MERGE_REQS); - mrb->reqs[mrb->num_reqs++] = req; - mrb->is_write = is_write; - break; - } - case VIRTIO_BLK_T_FLUSH: - virtio_blk_handle_flush(req, mrb); - break; - case VIRTIO_BLK_T_SCSI_CMD: - virtio_blk_handle_scsi(req); - break; - case VIRTIO_BLK_T_GET_ID: - { - VirtIOBlock *s = req->dev; - - /* - * NB: per existing s/n string convention the string is - * terminated by '\0' only when shorter than buffer. - */ - const char *serial = s->conf.serial ? s->conf.serial : ""; - size_t size = MIN(strlen(serial) + 1, - MIN(iov_size(in_iov, in_num), - VIRTIO_BLK_ID_BYTES)); - iov_from_buf(in_iov, in_num, 0, serial, size); - virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); - virtio_blk_free_request(req); - break; - } - default: - virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); - virtio_blk_free_request(req); - } -} - -void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq) -{ - VirtIOBlockReq *req; - MultiReqBuffer mrb = {}; - - blk_io_plug(s->blk); - - while ((req = virtio_blk_get_request(s))) { - virtio_blk_handle_request(req, &mrb); - } - - if (mrb.num_reqs) { - virtio_blk_submit_multireq(s->blk, &mrb); - } - - blk_io_unplug(s->blk); -} - -static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIOBlock *s = (VirtIOBlock *)vdev; - - if (s->dataplane) { - /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start - * dataplane here instead of waiting for .set_status(). - */ - virtio_blk_data_plane_start(s->dataplane); - if (!s->dataplane_disabled) { - return; - } - } - virtio_blk_handle_vq(s, vq); -} - -static void virtio_blk_dma_restart_bh(void *opaque) -{ - VirtIOBlock *s = opaque; - VirtIOBlockReq *req = s->rq; - MultiReqBuffer mrb = {}; - - qemu_bh_delete(s->bh); - s->bh = NULL; - - s->rq = NULL; - - while (req) { - VirtIOBlockReq *next = req->next; - virtio_blk_handle_request(req, &mrb); - req = next; - } - - if (mrb.num_reqs) { - virtio_blk_submit_multireq(s->blk, &mrb); - } -} - -static void virtio_blk_dma_restart_cb(void *opaque, int running, - RunState state) -{ - VirtIOBlock *s = opaque; - - if (!running) { - return; - } - - if (!s->bh) { - s->bh = aio_bh_new(blk_get_aio_context(s->conf.conf.blk), - virtio_blk_dma_restart_bh, s); - qemu_bh_schedule(s->bh); - } -} - -static void virtio_blk_reset(VirtIODevice *vdev) -{ - VirtIOBlock *s = VIRTIO_BLK(vdev); - AioContext *ctx; - - /* - * This should cancel pending requests, but can't do nicely until there - * are per-device request lists. - */ - ctx = blk_get_aio_context(s->blk); - aio_context_acquire(ctx); - blk_drain(s->blk); - - if (s->dataplane) { - virtio_blk_data_plane_stop(s->dataplane); - } - aio_context_release(ctx); - - blk_set_enable_write_cache(s->blk, s->original_wce); -} - -/* coalesce internal state, copy to pci i/o region 0 - */ -static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) -{ - VirtIOBlock *s = VIRTIO_BLK(vdev); - BlockConf *conf = &s->conf.conf; - struct virtio_blk_config blkcfg; - uint64_t capacity; - int blk_size = conf->logical_block_size; - - blk_get_geometry(s->blk, &capacity); - memset(&blkcfg, 0, sizeof(blkcfg)); - virtio_stq_p(vdev, &blkcfg.capacity, capacity); - virtio_stl_p(vdev, &blkcfg.seg_max, 128 - 2); - virtio_stw_p(vdev, &blkcfg.geometry.cylinders, conf->cyls); - virtio_stl_p(vdev, &blkcfg.blk_size, blk_size); - virtio_stw_p(vdev, &blkcfg.min_io_size, conf->min_io_size / blk_size); - virtio_stw_p(vdev, &blkcfg.opt_io_size, conf->opt_io_size / blk_size); - blkcfg.geometry.heads = conf->heads; - /* - * We must ensure that the block device capacity is a multiple of - * the logical block size. If that is not the case, let's use - * sector_mask to adopt the geometry to have a correct picture. - * For those devices where the capacity is ok for the given geometry - * we don't touch the sector value of the geometry, since some devices - * (like s390 dasd) need a specific value. Here the capacity is already - * cyls*heads*secs*blk_size and the sector value is not block size - * divided by 512 - instead it is the amount of blk_size blocks - * per track (cylinder). - */ - if (blk_getlength(s->blk) / conf->heads / conf->secs % blk_size) { - blkcfg.geometry.sectors = conf->secs & ~s->sector_mask; - } else { - blkcfg.geometry.sectors = conf->secs; - } - blkcfg.size_max = 0; - blkcfg.physical_block_exp = get_physical_block_exp(conf); - blkcfg.alignment_offset = 0; - blkcfg.wce = blk_enable_write_cache(s->blk); - memcpy(config, &blkcfg, sizeof(struct virtio_blk_config)); -} - -static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config) -{ - VirtIOBlock *s = VIRTIO_BLK(vdev); - struct virtio_blk_config blkcfg; - - memcpy(&blkcfg, config, sizeof(blkcfg)); - - aio_context_acquire(blk_get_aio_context(s->blk)); - blk_set_enable_write_cache(s->blk, blkcfg.wce != 0); - aio_context_release(blk_get_aio_context(s->blk)); -} - -static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features, - Error **errp) -{ - VirtIOBlock *s = VIRTIO_BLK(vdev); - - virtio_add_feature(&features, VIRTIO_BLK_F_SEG_MAX); - virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY); - virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY); - virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE); - if (virtio_has_feature(features, VIRTIO_F_VERSION_1)) { - if (s->conf.scsi) { - error_setg(errp, "Please set scsi=off for virtio-blk devices in order to use virtio 1.0"); - return 0; - } - } else { - virtio_clear_feature(&features, VIRTIO_F_ANY_LAYOUT); - virtio_add_feature(&features, VIRTIO_BLK_F_SCSI); - } - - if (s->conf.config_wce) { - virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE); - } - if (blk_enable_write_cache(s->blk)) { - virtio_add_feature(&features, VIRTIO_BLK_F_WCE); - } - if (blk_is_read_only(s->blk)) { - virtio_add_feature(&features, VIRTIO_BLK_F_RO); - } - - return features; -} - -static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) -{ - VirtIOBlock *s = VIRTIO_BLK(vdev); - - if (s->dataplane && !(status & (VIRTIO_CONFIG_S_DRIVER | - VIRTIO_CONFIG_S_DRIVER_OK))) { - virtio_blk_data_plane_stop(s->dataplane); - } - - if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) { - return; - } - - /* A guest that supports VIRTIO_BLK_F_CONFIG_WCE must be able to send - * cache flushes. Thus, the "auto writethrough" behavior is never - * necessary for guests that support the VIRTIO_BLK_F_CONFIG_WCE feature. - * Leaving it enabled would break the following sequence: - * - * Guest started with "-drive cache=writethrough" - * Guest sets status to 0 - * Guest sets DRIVER bit in status field - * Guest reads host features (WCE=0, CONFIG_WCE=1) - * Guest writes guest features (WCE=0, CONFIG_WCE=1) - * Guest writes 1 to the WCE configuration field (writeback mode) - * Guest sets DRIVER_OK bit in status field - * - * s->blk would erroneously be placed in writethrough mode. - */ - if (!virtio_vdev_has_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE)) { - aio_context_acquire(blk_get_aio_context(s->blk)); - blk_set_enable_write_cache(s->blk, - virtio_vdev_has_feature(vdev, - VIRTIO_BLK_F_WCE)); - aio_context_release(blk_get_aio_context(s->blk)); - } -} - -static void virtio_blk_save(QEMUFile *f, void *opaque) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(opaque); - VirtIOBlock *s = VIRTIO_BLK(vdev); - - if (s->dataplane) { - virtio_blk_data_plane_stop(s->dataplane); - } - - virtio_save(vdev, f); -} - -static void virtio_blk_save_device(VirtIODevice *vdev, QEMUFile *f) -{ - VirtIOBlock *s = VIRTIO_BLK(vdev); - VirtIOBlockReq *req = s->rq; - - while (req) { - qemu_put_sbyte(f, 1); - qemu_put_virtqueue_element(f, &req->elem); - req = req->next; - } - qemu_put_sbyte(f, 0); -} - -static int virtio_blk_load(QEMUFile *f, void *opaque, int version_id) -{ - VirtIOBlock *s = opaque; - VirtIODevice *vdev = VIRTIO_DEVICE(s); - - if (version_id != 2) - return -EINVAL; - - return virtio_load(vdev, f, version_id); -} - -static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f, - int version_id) -{ - VirtIOBlock *s = VIRTIO_BLK(vdev); - - while (qemu_get_sbyte(f)) { - VirtIOBlockReq *req; - req = qemu_get_virtqueue_element(f, sizeof(VirtIOBlockReq)); - virtio_blk_init_request(s, req); - req->next = s->rq; - s->rq = req; - } - - return 0; -} - -static void virtio_blk_resize(void *opaque) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(opaque); - - virtio_notify_config(vdev); -} - -static const BlockDevOps virtio_block_ops = { - .resize_cb = virtio_blk_resize, -}; - -static void virtio_blk_device_realize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIOBlock *s = VIRTIO_BLK(dev); - VirtIOBlkConf *conf = &s->conf; - Error *err = NULL; - static int virtio_blk_id; - - if (!conf->conf.blk) { - error_setg(errp, "drive property not set"); - return; - } - if (!blk_is_inserted(conf->conf.blk)) { - error_setg(errp, "Device needs media, but drive is empty"); - return; - } - - blkconf_serial(&conf->conf, &conf->serial); - s->original_wce = blk_enable_write_cache(conf->conf.blk); - blkconf_geometry(&conf->conf, NULL, 65535, 255, 255, &err); - if (err) { - error_propagate(errp, err); - return; - } - blkconf_blocksizes(&conf->conf); - - virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, - sizeof(struct virtio_blk_config)); - - s->blk = conf->conf.blk; - s->rq = NULL; - s->sector_mask = (s->conf.conf.logical_block_size / BDRV_SECTOR_SIZE) - 1; - - s->vq = virtio_add_queue(vdev, 128, virtio_blk_handle_output); - virtio_blk_data_plane_create(vdev, conf, &s->dataplane, &err); - if (err != NULL) { - error_propagate(errp, err); - virtio_cleanup(vdev); - return; - } - - s->change = qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s); - register_savevm(dev, "virtio-blk", virtio_blk_id++, 2, - virtio_blk_save, virtio_blk_load, s); - blk_set_dev_ops(s->blk, &virtio_block_ops, s); - blk_set_guest_block_size(s->blk, s->conf.conf.logical_block_size); - - blk_iostatus_enable(s->blk); -} - -static void virtio_blk_device_unrealize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIOBlock *s = VIRTIO_BLK(dev); - - virtio_blk_data_plane_destroy(s->dataplane); - s->dataplane = NULL; - qemu_del_vm_change_state_handler(s->change); - unregister_savevm(dev, "virtio-blk", s); - blockdev_mark_auto_del(s->blk); - virtio_cleanup(vdev); -} - -static void virtio_blk_instance_init(Object *obj) -{ - VirtIOBlock *s = VIRTIO_BLK(obj); - - object_property_add_link(obj, "iothread", TYPE_IOTHREAD, - (Object **)&s->conf.iothread, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); - device_add_bootindex_property(obj, &s->conf.conf.bootindex, - "bootindex", "/disk@0,0", - DEVICE(obj), NULL); -} - -static Property virtio_blk_properties[] = { - DEFINE_BLOCK_PROPERTIES(VirtIOBlock, conf.conf), - DEFINE_BLOCK_CHS_PROPERTIES(VirtIOBlock, conf.conf), - DEFINE_PROP_STRING("serial", VirtIOBlock, conf.serial), - DEFINE_PROP_BIT("config-wce", VirtIOBlock, conf.config_wce, 0, true), -#ifdef __linux__ - DEFINE_PROP_BIT("scsi", VirtIOBlock, conf.scsi, 0, false), -#endif - DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, - true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_blk_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - - dc->props = virtio_blk_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - vdc->realize = virtio_blk_device_realize; - vdc->unrealize = virtio_blk_device_unrealize; - vdc->get_config = virtio_blk_update_config; - vdc->set_config = virtio_blk_set_config; - vdc->get_features = virtio_blk_get_features; - vdc->set_status = virtio_blk_set_status; - vdc->reset = virtio_blk_reset; - vdc->save = virtio_blk_save_device; - vdc->load = virtio_blk_load_device; -} - -static const TypeInfo virtio_device_info = { - .name = TYPE_VIRTIO_BLK, - .parent = TYPE_VIRTIO_DEVICE, - .instance_size = sizeof(VirtIOBlock), - .instance_init = virtio_blk_instance_init, - .class_init = virtio_blk_class_init, -}; - -static void virtio_register_types(void) -{ - type_register_static(&virtio_device_info); -} - -type_init(virtio_register_types) diff --git a/qemu/hw/block/xen_blkif.h b/qemu/hw/block/xen_blkif.h deleted file mode 100644 index c68487cb3..000000000 --- a/qemu/hw/block/xen_blkif.h +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef __XEN_BLKIF_H__ -#define __XEN_BLKIF_H__ - -#include <xen/io/ring.h> -#include <xen/io/blkif.h> -#include <xen/io/protocols.h> - -/* Not a real protocol. Used to generate ring structs which contain - * the elements common to all protocols only. This way we get a - * compiler-checkable way to use common struct elements, so we can - * avoid using switch(protocol) in a number of places. */ -struct blkif_common_request { - char dummy; -}; -struct blkif_common_response { - char dummy; -}; - -/* i386 protocol version */ -#pragma pack(push, 4) -struct blkif_x86_32_request { - uint8_t operation; /* BLKIF_OP_??? */ - uint8_t nr_segments; /* number of segments */ - blkif_vdev_t handle; /* only for read/write requests */ - uint64_t id; /* private guest value, echoed in resp */ - blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ - struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; -}; -struct blkif_x86_32_response { - uint64_t id; /* copied from request */ - uint8_t operation; /* copied from request */ - int16_t status; /* BLKIF_RSP_??? */ -}; -typedef struct blkif_x86_32_request blkif_x86_32_request_t; -typedef struct blkif_x86_32_response blkif_x86_32_response_t; -#pragma pack(pop) - -/* x86_64 protocol version */ -struct blkif_x86_64_request { - uint8_t operation; /* BLKIF_OP_??? */ - uint8_t nr_segments; /* number of segments */ - blkif_vdev_t handle; /* only for read/write requests */ - uint64_t __attribute__((__aligned__(8))) id; - blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ - struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; -}; -struct blkif_x86_64_response { - uint64_t __attribute__((__aligned__(8))) id; - uint8_t operation; /* copied from request */ - int16_t status; /* BLKIF_RSP_??? */ -}; -typedef struct blkif_x86_64_request blkif_x86_64_request_t; -typedef struct blkif_x86_64_response blkif_x86_64_response_t; - -DEFINE_RING_TYPES(blkif_common, struct blkif_common_request, struct blkif_common_response); -DEFINE_RING_TYPES(blkif_x86_32, struct blkif_x86_32_request, struct blkif_x86_32_response); -DEFINE_RING_TYPES(blkif_x86_64, struct blkif_x86_64_request, struct blkif_x86_64_response); - -union blkif_back_rings { - blkif_back_ring_t native; - blkif_common_back_ring_t common; - blkif_x86_32_back_ring_t x86_32_part; - blkif_x86_64_back_ring_t x86_64_part; -}; -typedef union blkif_back_rings blkif_back_rings_t; - -enum blkif_protocol { - BLKIF_PROTOCOL_NATIVE = 1, - BLKIF_PROTOCOL_X86_32 = 2, - BLKIF_PROTOCOL_X86_64 = 3, -}; - -static inline void blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_request_t *src) -{ - int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; - - dst->operation = src->operation; - dst->nr_segments = src->nr_segments; - dst->handle = src->handle; - dst->id = src->id; - dst->sector_number = src->sector_number; - if (src->operation == BLKIF_OP_DISCARD) { - struct blkif_request_discard *s = (void *)src; - struct blkif_request_discard *d = (void *)dst; - d->nr_sectors = s->nr_sectors; - return; - } - /* prevent the compiler from optimizing the code and using src->nr_segments instead */ - barrier(); - if (n > dst->nr_segments) - n = dst->nr_segments; - for (i = 0; i < n; i++) - dst->seg[i] = src->seg[i]; -} - -static inline void blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_request_t *src) -{ - int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; - - dst->operation = src->operation; - dst->nr_segments = src->nr_segments; - dst->handle = src->handle; - dst->id = src->id; - dst->sector_number = src->sector_number; - if (src->operation == BLKIF_OP_DISCARD) { - struct blkif_request_discard *s = (void *)src; - struct blkif_request_discard *d = (void *)dst; - d->nr_sectors = s->nr_sectors; - return; - } - /* prevent the compiler from optimizing the code and using src->nr_segments instead */ - barrier(); - if (n > dst->nr_segments) - n = dst->nr_segments; - for (i = 0; i < n; i++) - dst->seg[i] = src->seg[i]; -} - -#endif /* __XEN_BLKIF_H__ */ diff --git a/qemu/hw/block/xen_disk.c b/qemu/hw/block/xen_disk.c deleted file mode 100644 index d4ce380fe..000000000 --- a/qemu/hw/block/xen_disk.c +++ /dev/null @@ -1,1121 +0,0 @@ -/* - * xen paravirt block device backend - * - * (c) Gerd Hoffmann <kraxel@redhat.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; under version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#include "qemu/osdep.h" -#include <sys/ioctl.h> -#include <sys/mman.h> -#include <sys/uio.h> - -#include "hw/hw.h" -#include "hw/xen/xen_backend.h" -#include "xen_blkif.h" -#include "sysemu/blockdev.h" -#include "sysemu/block-backend.h" -#include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" - -/* ------------------------------------------------------------- */ - -static int batch_maps = 0; - -static int max_requests = 32; - -/* ------------------------------------------------------------- */ - -#define BLOCK_SIZE 512 -#define IOCB_COUNT (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2) - -struct PersistentGrant { - void *page; - struct XenBlkDev *blkdev; -}; - -typedef struct PersistentGrant PersistentGrant; - -struct PersistentRegion { - void *addr; - int num; -}; - -typedef struct PersistentRegion PersistentRegion; - -struct ioreq { - blkif_request_t req; - int16_t status; - - /* parsed request */ - off_t start; - QEMUIOVector v; - int presync; - uint8_t mapped; - - /* grant mapping */ - uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - int prot; - void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - void *pages; - int num_unmap; - - /* aio status */ - int aio_inflight; - int aio_errors; - - struct XenBlkDev *blkdev; - QLIST_ENTRY(ioreq) list; - BlockAcctCookie acct; -}; - -struct XenBlkDev { - struct XenDevice xendev; /* must be first */ - char *params; - char *mode; - char *type; - char *dev; - char *devtype; - bool directiosafe; - const char *fileproto; - const char *filename; - int ring_ref; - void *sring; - int64_t file_blk; - int64_t file_size; - int protocol; - blkif_back_rings_t rings; - int more_work; - int cnt_map; - - /* request lists */ - QLIST_HEAD(inflight_head, ioreq) inflight; - QLIST_HEAD(finished_head, ioreq) finished; - QLIST_HEAD(freelist_head, ioreq) freelist; - int requests_total; - int requests_inflight; - int requests_finished; - - /* Persistent grants extension */ - gboolean feature_discard; - gboolean feature_persistent; - GTree *persistent_gnts; - GSList *persistent_regions; - unsigned int persistent_gnt_count; - unsigned int max_grants; - - /* qemu block driver */ - DriveInfo *dinfo; - BlockBackend *blk; - QEMUBH *bh; -}; - -/* ------------------------------------------------------------- */ - -static void ioreq_reset(struct ioreq *ioreq) -{ - memset(&ioreq->req, 0, sizeof(ioreq->req)); - ioreq->status = 0; - ioreq->start = 0; - ioreq->presync = 0; - ioreq->mapped = 0; - - memset(ioreq->domids, 0, sizeof(ioreq->domids)); - memset(ioreq->refs, 0, sizeof(ioreq->refs)); - ioreq->prot = 0; - memset(ioreq->page, 0, sizeof(ioreq->page)); - ioreq->pages = NULL; - - ioreq->aio_inflight = 0; - ioreq->aio_errors = 0; - - ioreq->blkdev = NULL; - memset(&ioreq->list, 0, sizeof(ioreq->list)); - memset(&ioreq->acct, 0, sizeof(ioreq->acct)); - - qemu_iovec_reset(&ioreq->v); -} - -static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data) -{ - uint ua = GPOINTER_TO_UINT(a); - uint ub = GPOINTER_TO_UINT(b); - return (ua > ub) - (ua < ub); -} - -static void destroy_grant(gpointer pgnt) -{ - PersistentGrant *grant = pgnt; - xengnttab_handle *gnt = grant->blkdev->xendev.gnttabdev; - - if (xengnttab_unmap(gnt, grant->page, 1) != 0) { - xen_be_printf(&grant->blkdev->xendev, 0, - "xengnttab_unmap failed: %s\n", - strerror(errno)); - } - grant->blkdev->persistent_gnt_count--; - xen_be_printf(&grant->blkdev->xendev, 3, - "unmapped grant %p\n", grant->page); - g_free(grant); -} - -static void remove_persistent_region(gpointer data, gpointer dev) -{ - PersistentRegion *region = data; - struct XenBlkDev *blkdev = dev; - xengnttab_handle *gnt = blkdev->xendev.gnttabdev; - - if (xengnttab_unmap(gnt, region->addr, region->num) != 0) { - xen_be_printf(&blkdev->xendev, 0, - "xengnttab_unmap region %p failed: %s\n", - region->addr, strerror(errno)); - } - xen_be_printf(&blkdev->xendev, 3, - "unmapped grant region %p with %d pages\n", - region->addr, region->num); - g_free(region); -} - -static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) -{ - struct ioreq *ioreq = NULL; - - if (QLIST_EMPTY(&blkdev->freelist)) { - if (blkdev->requests_total >= max_requests) { - goto out; - } - /* allocate new struct */ - ioreq = g_malloc0(sizeof(*ioreq)); - ioreq->blkdev = blkdev; - blkdev->requests_total++; - qemu_iovec_init(&ioreq->v, BLKIF_MAX_SEGMENTS_PER_REQUEST); - } else { - /* get one from freelist */ - ioreq = QLIST_FIRST(&blkdev->freelist); - QLIST_REMOVE(ioreq, list); - } - QLIST_INSERT_HEAD(&blkdev->inflight, ioreq, list); - blkdev->requests_inflight++; - -out: - return ioreq; -} - -static void ioreq_finish(struct ioreq *ioreq) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - - QLIST_REMOVE(ioreq, list); - QLIST_INSERT_HEAD(&blkdev->finished, ioreq, list); - blkdev->requests_inflight--; - blkdev->requests_finished++; -} - -static void ioreq_release(struct ioreq *ioreq, bool finish) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - - QLIST_REMOVE(ioreq, list); - ioreq_reset(ioreq); - ioreq->blkdev = blkdev; - QLIST_INSERT_HEAD(&blkdev->freelist, ioreq, list); - if (finish) { - blkdev->requests_finished--; - } else { - blkdev->requests_inflight--; - } -} - -/* - * translate request into iovec + start offset - * do sanity checks along the way - */ -static int ioreq_parse(struct ioreq *ioreq) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - uintptr_t mem; - size_t len; - int i; - - xen_be_printf(&blkdev->xendev, 3, - "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n", - ioreq->req.operation, ioreq->req.nr_segments, - ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); - switch (ioreq->req.operation) { - case BLKIF_OP_READ: - ioreq->prot = PROT_WRITE; /* to memory */ - break; - case BLKIF_OP_FLUSH_DISKCACHE: - ioreq->presync = 1; - if (!ioreq->req.nr_segments) { - return 0; - } - /* fall through */ - case BLKIF_OP_WRITE: - ioreq->prot = PROT_READ; /* from memory */ - break; - case BLKIF_OP_DISCARD: - return 0; - default: - xen_be_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n", - ioreq->req.operation); - goto err; - }; - - if (ioreq->req.operation != BLKIF_OP_READ && blkdev->mode[0] != 'w') { - xen_be_printf(&blkdev->xendev, 0, "error: write req for ro device\n"); - goto err; - } - - ioreq->start = ioreq->req.sector_number * blkdev->file_blk; - for (i = 0; i < ioreq->req.nr_segments; i++) { - if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { - xen_be_printf(&blkdev->xendev, 0, "error: nr_segments too big\n"); - goto err; - } - if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) { - xen_be_printf(&blkdev->xendev, 0, "error: first > last sector\n"); - goto err; - } - if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) { - xen_be_printf(&blkdev->xendev, 0, "error: page crossing\n"); - goto err; - } - - ioreq->domids[i] = blkdev->xendev.dom; - ioreq->refs[i] = ioreq->req.seg[i].gref; - - mem = ioreq->req.seg[i].first_sect * blkdev->file_blk; - len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk; - qemu_iovec_add(&ioreq->v, (void*)mem, len); - } - if (ioreq->start + ioreq->v.size > blkdev->file_size) { - xen_be_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); - goto err; - } - return 0; - -err: - ioreq->status = BLKIF_RSP_ERROR; - return -1; -} - -static void ioreq_unmap(struct ioreq *ioreq) -{ - xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev; - int i; - - if (ioreq->num_unmap == 0 || ioreq->mapped == 0) { - return; - } - if (batch_maps) { - if (!ioreq->pages) { - return; - } - if (xengnttab_unmap(gnt, ioreq->pages, ioreq->num_unmap) != 0) { - xen_be_printf(&ioreq->blkdev->xendev, 0, - "xengnttab_unmap failed: %s\n", - strerror(errno)); - } - ioreq->blkdev->cnt_map -= ioreq->num_unmap; - ioreq->pages = NULL; - } else { - for (i = 0; i < ioreq->num_unmap; i++) { - if (!ioreq->page[i]) { - continue; - } - if (xengnttab_unmap(gnt, ioreq->page[i], 1) != 0) { - xen_be_printf(&ioreq->blkdev->xendev, 0, - "xengnttab_unmap failed: %s\n", - strerror(errno)); - } - ioreq->blkdev->cnt_map--; - ioreq->page[i] = NULL; - } - } - ioreq->mapped = 0; -} - -static int ioreq_map(struct ioreq *ioreq) -{ - xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev; - uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - int i, j, new_maps = 0; - PersistentGrant *grant; - PersistentRegion *region; - /* domids and refs variables will contain the information necessary - * to map the grants that are needed to fulfill this request. - * - * After mapping the needed grants, the page array will contain the - * memory address of each granted page in the order specified in ioreq - * (disregarding if it's a persistent grant or not). - */ - - if (ioreq->v.niov == 0 || ioreq->mapped == 1) { - return 0; - } - if (ioreq->blkdev->feature_persistent) { - for (i = 0; i < ioreq->v.niov; i++) { - grant = g_tree_lookup(ioreq->blkdev->persistent_gnts, - GUINT_TO_POINTER(ioreq->refs[i])); - - if (grant != NULL) { - page[i] = grant->page; - xen_be_printf(&ioreq->blkdev->xendev, 3, - "using persistent-grant %" PRIu32 "\n", - ioreq->refs[i]); - } else { - /* Add the grant to the list of grants that - * should be mapped - */ - domids[new_maps] = ioreq->domids[i]; - refs[new_maps] = ioreq->refs[i]; - page[i] = NULL; - new_maps++; - } - } - /* Set the protection to RW, since grants may be reused later - * with a different protection than the one needed for this request - */ - ioreq->prot = PROT_WRITE | PROT_READ; - } else { - /* All grants in the request should be mapped */ - memcpy(refs, ioreq->refs, sizeof(refs)); - memcpy(domids, ioreq->domids, sizeof(domids)); - memset(page, 0, sizeof(page)); - new_maps = ioreq->v.niov; - } - - if (batch_maps && new_maps) { - ioreq->pages = xengnttab_map_grant_refs - (gnt, new_maps, domids, refs, ioreq->prot); - if (ioreq->pages == NULL) { - xen_be_printf(&ioreq->blkdev->xendev, 0, - "can't map %d grant refs (%s, %d maps)\n", - new_maps, strerror(errno), ioreq->blkdev->cnt_map); - return -1; - } - for (i = 0, j = 0; i < ioreq->v.niov; i++) { - if (page[i] == NULL) { - page[i] = ioreq->pages + (j++) * XC_PAGE_SIZE; - } - } - ioreq->blkdev->cnt_map += new_maps; - } else if (new_maps) { - for (i = 0; i < new_maps; i++) { - ioreq->page[i] = xengnttab_map_grant_ref - (gnt, domids[i], refs[i], ioreq->prot); - if (ioreq->page[i] == NULL) { - xen_be_printf(&ioreq->blkdev->xendev, 0, - "can't map grant ref %d (%s, %d maps)\n", - refs[i], strerror(errno), ioreq->blkdev->cnt_map); - ioreq->mapped = 1; - ioreq_unmap(ioreq); - return -1; - } - ioreq->blkdev->cnt_map++; - } - for (i = 0, j = 0; i < ioreq->v.niov; i++) { - if (page[i] == NULL) { - page[i] = ioreq->page[j++]; - } - } - } - if (ioreq->blkdev->feature_persistent && new_maps != 0 && - (!batch_maps || (ioreq->blkdev->persistent_gnt_count + new_maps <= - ioreq->blkdev->max_grants))) { - /* - * If we are using persistent grants and batch mappings only - * add the new maps to the list of persistent grants if the whole - * area can be persistently mapped. - */ - if (batch_maps) { - region = g_malloc0(sizeof(*region)); - region->addr = ioreq->pages; - region->num = new_maps; - ioreq->blkdev->persistent_regions = g_slist_append( - ioreq->blkdev->persistent_regions, - region); - } - while ((ioreq->blkdev->persistent_gnt_count < ioreq->blkdev->max_grants) - && new_maps) { - /* Go through the list of newly mapped grants and add as many - * as possible to the list of persistently mapped grants. - * - * Since we start at the end of ioreq->page(s), we only need - * to decrease new_maps to prevent this granted pages from - * being unmapped in ioreq_unmap. - */ - grant = g_malloc0(sizeof(*grant)); - new_maps--; - if (batch_maps) { - grant->page = ioreq->pages + (new_maps) * XC_PAGE_SIZE; - } else { - grant->page = ioreq->page[new_maps]; - } - grant->blkdev = ioreq->blkdev; - xen_be_printf(&ioreq->blkdev->xendev, 3, - "adding grant %" PRIu32 " page: %p\n", - refs[new_maps], grant->page); - g_tree_insert(ioreq->blkdev->persistent_gnts, - GUINT_TO_POINTER(refs[new_maps]), - grant); - ioreq->blkdev->persistent_gnt_count++; - } - assert(!batch_maps || new_maps == 0); - } - for (i = 0; i < ioreq->v.niov; i++) { - ioreq->v.iov[i].iov_base += (uintptr_t)page[i]; - } - ioreq->mapped = 1; - ioreq->num_unmap = new_maps; - return 0; -} - -static int ioreq_runio_qemu_aio(struct ioreq *ioreq); - -static void qemu_aio_complete(void *opaque, int ret) -{ - struct ioreq *ioreq = opaque; - - if (ret != 0) { - xen_be_printf(&ioreq->blkdev->xendev, 0, "%s I/O error\n", - ioreq->req.operation == BLKIF_OP_READ ? "read" : "write"); - ioreq->aio_errors++; - } - - ioreq->aio_inflight--; - if (ioreq->presync) { - ioreq->presync = 0; - ioreq_runio_qemu_aio(ioreq); - return; - } - if (ioreq->aio_inflight > 0) { - return; - } - - ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY; - ioreq_unmap(ioreq); - ioreq_finish(ioreq); - switch (ioreq->req.operation) { - case BLKIF_OP_WRITE: - case BLKIF_OP_FLUSH_DISKCACHE: - if (!ioreq->req.nr_segments) { - break; - } - case BLKIF_OP_READ: - if (ioreq->status == BLKIF_RSP_OKAY) { - block_acct_done(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct); - } else { - block_acct_failed(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct); - } - break; - case BLKIF_OP_DISCARD: - default: - break; - } - qemu_bh_schedule(ioreq->blkdev->bh); -} - -static int ioreq_runio_qemu_aio(struct ioreq *ioreq) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - - if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) { - goto err_no_map; - } - - ioreq->aio_inflight++; - if (ioreq->presync) { - blk_aio_flush(ioreq->blkdev->blk, qemu_aio_complete, ioreq); - return 0; - } - - switch (ioreq->req.operation) { - case BLKIF_OP_READ: - block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct, - ioreq->v.size, BLOCK_ACCT_READ); - ioreq->aio_inflight++; - blk_aio_readv(blkdev->blk, ioreq->start / BLOCK_SIZE, - &ioreq->v, ioreq->v.size / BLOCK_SIZE, - qemu_aio_complete, ioreq); - break; - case BLKIF_OP_WRITE: - case BLKIF_OP_FLUSH_DISKCACHE: - if (!ioreq->req.nr_segments) { - break; - } - - block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct, - ioreq->v.size, - ioreq->req.operation == BLKIF_OP_WRITE ? - BLOCK_ACCT_WRITE : BLOCK_ACCT_FLUSH); - ioreq->aio_inflight++; - blk_aio_writev(blkdev->blk, ioreq->start / BLOCK_SIZE, - &ioreq->v, ioreq->v.size / BLOCK_SIZE, - qemu_aio_complete, ioreq); - break; - case BLKIF_OP_DISCARD: - { - struct blkif_request_discard *discard_req = (void *)&ioreq->req; - ioreq->aio_inflight++; - blk_aio_discard(blkdev->blk, - discard_req->sector_number, discard_req->nr_sectors, - qemu_aio_complete, ioreq); - break; - } - default: - /* unknown operation (shouldn't happen -- parse catches this) */ - goto err; - } - - qemu_aio_complete(ioreq, 0); - - return 0; - -err: - ioreq_unmap(ioreq); -err_no_map: - ioreq_finish(ioreq); - ioreq->status = BLKIF_RSP_ERROR; - return -1; -} - -static int blk_send_response_one(struct ioreq *ioreq) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - int send_notify = 0; - int have_requests = 0; - blkif_response_t resp; - void *dst; - - resp.id = ioreq->req.id; - resp.operation = ioreq->req.operation; - resp.status = ioreq->status; - - /* Place on the response ring for the relevant domain. */ - switch (blkdev->protocol) { - case BLKIF_PROTOCOL_NATIVE: - dst = RING_GET_RESPONSE(&blkdev->rings.native, blkdev->rings.native.rsp_prod_pvt); - break; - case BLKIF_PROTOCOL_X86_32: - dst = RING_GET_RESPONSE(&blkdev->rings.x86_32_part, - blkdev->rings.x86_32_part.rsp_prod_pvt); - break; - case BLKIF_PROTOCOL_X86_64: - dst = RING_GET_RESPONSE(&blkdev->rings.x86_64_part, - blkdev->rings.x86_64_part.rsp_prod_pvt); - break; - default: - dst = NULL; - return 0; - } - memcpy(dst, &resp, sizeof(resp)); - blkdev->rings.common.rsp_prod_pvt++; - - RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify); - if (blkdev->rings.common.rsp_prod_pvt == blkdev->rings.common.req_cons) { - /* - * Tail check for pending requests. Allows frontend to avoid - * notifications if requests are already in flight (lower - * overheads and promotes batching). - */ - RING_FINAL_CHECK_FOR_REQUESTS(&blkdev->rings.common, have_requests); - } else if (RING_HAS_UNCONSUMED_REQUESTS(&blkdev->rings.common)) { - have_requests = 1; - } - - if (have_requests) { - blkdev->more_work++; - } - return send_notify; -} - -/* walk finished list, send outstanding responses, free requests */ -static void blk_send_response_all(struct XenBlkDev *blkdev) -{ - struct ioreq *ioreq; - int send_notify = 0; - - while (!QLIST_EMPTY(&blkdev->finished)) { - ioreq = QLIST_FIRST(&blkdev->finished); - send_notify += blk_send_response_one(ioreq); - ioreq_release(ioreq, true); - } - if (send_notify) { - xen_be_send_notify(&blkdev->xendev); - } -} - -static int blk_get_request(struct XenBlkDev *blkdev, struct ioreq *ioreq, RING_IDX rc) -{ - switch (blkdev->protocol) { - case BLKIF_PROTOCOL_NATIVE: - memcpy(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.native, rc), - sizeof(ioreq->req)); - break; - case BLKIF_PROTOCOL_X86_32: - blkif_get_x86_32_req(&ioreq->req, - RING_GET_REQUEST(&blkdev->rings.x86_32_part, rc)); - break; - case BLKIF_PROTOCOL_X86_64: - blkif_get_x86_64_req(&ioreq->req, - RING_GET_REQUEST(&blkdev->rings.x86_64_part, rc)); - break; - } - return 0; -} - -static void blk_handle_requests(struct XenBlkDev *blkdev) -{ - RING_IDX rc, rp; - struct ioreq *ioreq; - - blkdev->more_work = 0; - - rc = blkdev->rings.common.req_cons; - rp = blkdev->rings.common.sring->req_prod; - xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ - - blk_send_response_all(blkdev); - while (rc != rp) { - /* pull request from ring */ - if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc)) { - break; - } - ioreq = ioreq_start(blkdev); - if (ioreq == NULL) { - blkdev->more_work++; - break; - } - blk_get_request(blkdev, ioreq, rc); - blkdev->rings.common.req_cons = ++rc; - - /* parse them */ - if (ioreq_parse(ioreq) != 0) { - - switch (ioreq->req.operation) { - case BLKIF_OP_READ: - block_acct_invalid(blk_get_stats(blkdev->blk), - BLOCK_ACCT_READ); - break; - case BLKIF_OP_WRITE: - block_acct_invalid(blk_get_stats(blkdev->blk), - BLOCK_ACCT_WRITE); - break; - case BLKIF_OP_FLUSH_DISKCACHE: - block_acct_invalid(blk_get_stats(blkdev->blk), - BLOCK_ACCT_FLUSH); - default: - break; - }; - - if (blk_send_response_one(ioreq)) { - xen_be_send_notify(&blkdev->xendev); - } - ioreq_release(ioreq, false); - continue; - } - - ioreq_runio_qemu_aio(ioreq); - } - - if (blkdev->more_work && blkdev->requests_inflight < max_requests) { - qemu_bh_schedule(blkdev->bh); - } -} - -/* ------------------------------------------------------------- */ - -static void blk_bh(void *opaque) -{ - struct XenBlkDev *blkdev = opaque; - blk_handle_requests(blkdev); -} - -/* - * We need to account for the grant allocations requiring contiguous - * chunks; the worst case number would be - * max_req * max_seg + (max_req - 1) * (max_seg - 1) + 1, - * but in order to keep things simple just use - * 2 * max_req * max_seg. - */ -#define MAX_GRANTS(max_req, max_seg) (2 * (max_req) * (max_seg)) - -static void blk_alloc(struct XenDevice *xendev) -{ - struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - - QLIST_INIT(&blkdev->inflight); - QLIST_INIT(&blkdev->finished); - QLIST_INIT(&blkdev->freelist); - blkdev->bh = qemu_bh_new(blk_bh, blkdev); - if (xen_mode != XEN_EMULATE) { - batch_maps = 1; - } - if (xengnttab_set_max_grants(xendev->gnttabdev, - MAX_GRANTS(max_requests, BLKIF_MAX_SEGMENTS_PER_REQUEST)) < 0) { - xen_be_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", - strerror(errno)); - } -} - -static void blk_parse_discard(struct XenBlkDev *blkdev) -{ - int enable; - - blkdev->feature_discard = true; - - if (xenstore_read_be_int(&blkdev->xendev, "discard-enable", &enable) == 0) { - blkdev->feature_discard = !!enable; - } - - if (blkdev->feature_discard) { - xenstore_write_be_int(&blkdev->xendev, "feature-discard", 1); - } -} - -static int blk_init(struct XenDevice *xendev) -{ - struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - int info = 0; - char *directiosafe = NULL; - - /* read xenstore entries */ - if (blkdev->params == NULL) { - char *h = NULL; - blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); - if (blkdev->params != NULL) { - h = strchr(blkdev->params, ':'); - } - if (h != NULL) { - blkdev->fileproto = blkdev->params; - blkdev->filename = h+1; - *h = 0; - } else { - blkdev->fileproto = "<unset>"; - blkdev->filename = blkdev->params; - } - } - if (!strcmp("aio", blkdev->fileproto)) { - blkdev->fileproto = "raw"; - } - if (!strcmp("vhd", blkdev->fileproto)) { - blkdev->fileproto = "vpc"; - } - if (blkdev->mode == NULL) { - blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode"); - } - if (blkdev->type == NULL) { - blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type"); - } - if (blkdev->dev == NULL) { - blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev"); - } - if (blkdev->devtype == NULL) { - blkdev->devtype = xenstore_read_be_str(&blkdev->xendev, "device-type"); - } - directiosafe = xenstore_read_be_str(&blkdev->xendev, "direct-io-safe"); - blkdev->directiosafe = (directiosafe && atoi(directiosafe)); - - /* do we have all we need? */ - if (blkdev->params == NULL || - blkdev->mode == NULL || - blkdev->type == NULL || - blkdev->dev == NULL) { - goto out_error; - } - - /* read-only ? */ - if (strcmp(blkdev->mode, "w")) { - info |= VDISK_READONLY; - } - - /* cdrom ? */ - if (blkdev->devtype && !strcmp(blkdev->devtype, "cdrom")) { - info |= VDISK_CDROM; - } - - blkdev->file_blk = BLOCK_SIZE; - - /* fill info - * blk_connect supplies sector-size and sectors - */ - xenstore_write_be_int(&blkdev->xendev, "feature-flush-cache", 1); - xenstore_write_be_int(&blkdev->xendev, "feature-persistent", 1); - xenstore_write_be_int(&blkdev->xendev, "info", info); - - blk_parse_discard(blkdev); - - g_free(directiosafe); - return 0; - -out_error: - g_free(blkdev->params); - blkdev->params = NULL; - g_free(blkdev->mode); - blkdev->mode = NULL; - g_free(blkdev->type); - blkdev->type = NULL; - g_free(blkdev->dev); - blkdev->dev = NULL; - g_free(blkdev->devtype); - blkdev->devtype = NULL; - g_free(directiosafe); - blkdev->directiosafe = false; - return -1; -} - -static int blk_connect(struct XenDevice *xendev) -{ - struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - int pers, index, qflags; - bool readonly = true; - bool writethrough = true; - - /* read-only ? */ - if (blkdev->directiosafe) { - qflags = BDRV_O_NOCACHE | BDRV_O_NATIVE_AIO; - } else { - qflags = 0; - writethrough = false; - } - if (strcmp(blkdev->mode, "w") == 0) { - qflags |= BDRV_O_RDWR; - readonly = false; - } - if (blkdev->feature_discard) { - qflags |= BDRV_O_UNMAP; - } - - /* init qemu block driver */ - index = (blkdev->xendev.dev - 202 * 256) / 16; - blkdev->dinfo = drive_get(IF_XEN, 0, index); - if (!blkdev->dinfo) { - Error *local_err = NULL; - QDict *options = NULL; - - if (strcmp(blkdev->fileproto, "<unset>")) { - options = qdict_new(); - qdict_put(options, "driver", qstring_from_str(blkdev->fileproto)); - } - - /* setup via xenbus -> create new block driver instance */ - xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); - blkdev->blk = blk_new_open(blkdev->filename, NULL, options, - qflags, &local_err); - if (!blkdev->blk) { - xen_be_printf(&blkdev->xendev, 0, "error: %s\n", - error_get_pretty(local_err)); - error_free(local_err); - return -1; - } - blk_set_enable_write_cache(blkdev->blk, !writethrough); - } else { - /* setup via qemu cmdline -> already setup for us */ - xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n"); - blkdev->blk = blk_by_legacy_dinfo(blkdev->dinfo); - if (blk_is_read_only(blkdev->blk) && !readonly) { - xen_be_printf(&blkdev->xendev, 0, "Unexpected read-only drive"); - blkdev->blk = NULL; - return -1; - } - /* blkdev->blk is not create by us, we get a reference - * so we can blk_unref() unconditionally */ - blk_ref(blkdev->blk); - } - blk_attach_dev_nofail(blkdev->blk, blkdev); - blkdev->file_size = blk_getlength(blkdev->blk); - if (blkdev->file_size < 0) { - BlockDriverState *bs = blk_bs(blkdev->blk); - const char *drv_name = bs ? bdrv_get_format_name(bs) : NULL; - xen_be_printf(&blkdev->xendev, 1, "blk_getlength: %d (%s) | drv %s\n", - (int)blkdev->file_size, strerror(-blkdev->file_size), - drv_name ?: "-"); - blkdev->file_size = 0; - } - - xen_be_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\"," - " size %" PRId64 " (%" PRId64 " MB)\n", - blkdev->type, blkdev->fileproto, blkdev->filename, - blkdev->file_size, blkdev->file_size >> 20); - - /* Fill in number of sector size and number of sectors */ - xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); - xenstore_write_be_int64(&blkdev->xendev, "sectors", - blkdev->file_size / blkdev->file_blk); - - if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", &blkdev->ring_ref) == -1) { - return -1; - } - if (xenstore_read_fe_int(&blkdev->xendev, "event-channel", - &blkdev->xendev.remote_port) == -1) { - return -1; - } - if (xenstore_read_fe_int(&blkdev->xendev, "feature-persistent", &pers)) { - blkdev->feature_persistent = FALSE; - } else { - blkdev->feature_persistent = !!pers; - } - - blkdev->protocol = BLKIF_PROTOCOL_NATIVE; - if (blkdev->xendev.protocol) { - if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32) == 0) { - blkdev->protocol = BLKIF_PROTOCOL_X86_32; - } - if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64) == 0) { - blkdev->protocol = BLKIF_PROTOCOL_X86_64; - } - } - - blkdev->sring = xengnttab_map_grant_ref(blkdev->xendev.gnttabdev, - blkdev->xendev.dom, - blkdev->ring_ref, - PROT_READ | PROT_WRITE); - if (!blkdev->sring) { - return -1; - } - blkdev->cnt_map++; - - switch (blkdev->protocol) { - case BLKIF_PROTOCOL_NATIVE: - { - blkif_sring_t *sring_native = blkdev->sring; - BACK_RING_INIT(&blkdev->rings.native, sring_native, XC_PAGE_SIZE); - break; - } - case BLKIF_PROTOCOL_X86_32: - { - blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring; - - BACK_RING_INIT(&blkdev->rings.x86_32_part, sring_x86_32, XC_PAGE_SIZE); - break; - } - case BLKIF_PROTOCOL_X86_64: - { - blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring; - - BACK_RING_INIT(&blkdev->rings.x86_64_part, sring_x86_64, XC_PAGE_SIZE); - break; - } - } - - if (blkdev->feature_persistent) { - /* Init persistent grants */ - blkdev->max_grants = max_requests * BLKIF_MAX_SEGMENTS_PER_REQUEST; - blkdev->persistent_gnts = g_tree_new_full((GCompareDataFunc)int_cmp, - NULL, NULL, - batch_maps ? - (GDestroyNotify)g_free : - (GDestroyNotify)destroy_grant); - blkdev->persistent_regions = NULL; - blkdev->persistent_gnt_count = 0; - } - - xen_be_bind_evtchn(&blkdev->xendev); - - xen_be_printf(&blkdev->xendev, 1, "ok: proto %s, ring-ref %d, " - "remote port %d, local port %d\n", - blkdev->xendev.protocol, blkdev->ring_ref, - blkdev->xendev.remote_port, blkdev->xendev.local_port); - return 0; -} - -static void blk_disconnect(struct XenDevice *xendev) -{ - struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - - if (blkdev->blk) { - blk_detach_dev(blkdev->blk, blkdev); - blk_unref(blkdev->blk); - blkdev->blk = NULL; - } - xen_be_unbind_evtchn(&blkdev->xendev); - - if (blkdev->sring) { - xengnttab_unmap(blkdev->xendev.gnttabdev, blkdev->sring, 1); - blkdev->cnt_map--; - blkdev->sring = NULL; - } - - /* - * Unmap persistent grants before switching to the closed state - * so the frontend can free them. - * - * In the !batch_maps case g_tree_destroy will take care of unmapping - * the grant, but in the batch_maps case we need to iterate over every - * region in persistent_regions and unmap it. - */ - if (blkdev->feature_persistent) { - g_tree_destroy(blkdev->persistent_gnts); - assert(batch_maps || blkdev->persistent_gnt_count == 0); - if (batch_maps) { - blkdev->persistent_gnt_count = 0; - g_slist_foreach(blkdev->persistent_regions, - (GFunc)remove_persistent_region, blkdev); - g_slist_free(blkdev->persistent_regions); - } - blkdev->feature_persistent = false; - } -} - -static int blk_free(struct XenDevice *xendev) -{ - struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - struct ioreq *ioreq; - - if (blkdev->blk || blkdev->sring) { - blk_disconnect(xendev); - } - - while (!QLIST_EMPTY(&blkdev->freelist)) { - ioreq = QLIST_FIRST(&blkdev->freelist); - QLIST_REMOVE(ioreq, list); - qemu_iovec_destroy(&ioreq->v); - g_free(ioreq); - } - - g_free(blkdev->params); - g_free(blkdev->mode); - g_free(blkdev->type); - g_free(blkdev->dev); - g_free(blkdev->devtype); - qemu_bh_delete(blkdev->bh); - return 0; -} - -static void blk_event(struct XenDevice *xendev) -{ - struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - - qemu_bh_schedule(blkdev->bh); -} - -struct XenDevOps xen_blkdev_ops = { - .size = sizeof(struct XenBlkDev), - .flags = DEVOPS_FLAG_NEED_GNTDEV, - .alloc = blk_alloc, - .init = blk_init, - .initialise = blk_connect, - .disconnect = blk_disconnect, - .event = blk_event, - .free = blk_free, -}; |