diff options
Diffstat (limited to 'hw/block')
| -rw-r--r-- | hw/block/dataplane/virtio-blk.c | 83 | ||||
| -rw-r--r-- | hw/block/dataplane/virtio-blk.h | 2 | ||||
| -rw-r--r-- | hw/block/m25p80.c | 398 | ||||
| -rw-r--r-- | hw/block/virtio-blk.c | 57 |
4 files changed, 433 insertions, 107 deletions
diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index 2073f9a270..54b9ac1da6 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -31,11 +31,9 @@ struct VirtIOBlockDataPlane { bool stopping; VirtIOBlkConf *conf; - VirtIODevice *vdev; - VirtQueue *vq; /* virtqueue vring */ - EventNotifier *guest_notifier; /* irq */ QEMUBH *bh; /* bh for guest notification */ + unsigned long *batch_notify_vqs; /* Note that these EventNotifiers are assigned by value. This is * fine as long as you do not call event_notifier_cleanup on them @@ -47,20 +45,36 @@ struct VirtIOBlockDataPlane { }; /* Raise an interrupt to signal guest, if necessary */ -void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s) +void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s, VirtQueue *vq) { + set_bit(virtio_get_queue_index(vq), s->batch_notify_vqs); qemu_bh_schedule(s->bh); } static void notify_guest_bh(void *opaque) { VirtIOBlockDataPlane *s = opaque; + unsigned nvqs = s->conf->num_queues; + unsigned long bitmap[BITS_TO_LONGS(nvqs)]; + unsigned j; - if (!virtio_should_notify(s->vdev, s->vq)) { - return; - } + memcpy(bitmap, s->batch_notify_vqs, sizeof(bitmap)); + memset(s->batch_notify_vqs, 0, sizeof(bitmap)); + + for (j = 0; j < nvqs; j += BITS_PER_LONG) { + unsigned long bits = bitmap[j]; - event_notifier_set(s->guest_notifier); + while (bits != 0) { + unsigned i = j + ctzl(bits); + VirtQueue *vq = virtio_get_queue(s->vdev, i); + + if (virtio_should_notify(s->vdev, vq)) { + event_notifier_set(virtio_queue_get_guest_notifier(vq)); + } + + bits &= bits - 1; /* clear right-most bit */ + } + } } /* Context: QEMU global mutex held */ @@ -79,7 +93,7 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, } /* Don't try if transport does not support notifiers. */ - if (!k->set_guest_notifiers || !k->set_host_notifier) { + if (!k->set_guest_notifiers || !k->ioeventfd_started) { error_setg(errp, "device is incompatible with dataplane " "(transport does not support notifiers)"); @@ -104,6 +118,7 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, } s->ctx = iothread_get_aio_context(s->iothread); s->bh = aio_bh_new(s->ctx, notify_guest_bh, s); + s->batch_notify_vqs = bitmap_new(conf->num_queues); *dataplane = s; } @@ -116,6 +131,7 @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s) } virtio_blk_data_plane_stop(s); + g_free(s->batch_notify_vqs); qemu_bh_delete(s->bh); object_unref(OBJECT(s->iothread)); g_free(s); @@ -138,6 +154,8 @@ 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); + unsigned i; + unsigned nvqs = s->conf->num_queues; int r; if (vblk->dataplane_started || s->starting) { @@ -145,22 +163,25 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s) } 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); + r = k->set_guest_notifiers(qbus->parent, nvqs, 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; + for (i = 0; i < nvqs; i++) { + r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, true); + if (r != 0) { + fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r); + while (i--) { + virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); + } + goto fail_guest_notifiers; + } } s->starting = false; @@ -170,17 +191,23 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *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)); + for (i = 0; i < nvqs; i++) { + VirtQueue *vq = virtio_get_queue(s->vdev, i); + + event_notifier_set(virtio_queue_get_host_notifier(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); + for (i = 0; i < nvqs; i++) { + VirtQueue *vq = virtio_get_queue(s->vdev, i); + + virtio_queue_aio_set_host_notifier_handler(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; @@ -193,6 +220,8 @@ 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); + unsigned i; + unsigned nvqs = s->conf->num_queues; if (!vblk->dataplane_started || s->stopping) { return; @@ -210,17 +239,23 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *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); + for (i = 0; i < nvqs; i++) { + VirtQueue *vq = virtio_get_queue(s->vdev, i); + + virtio_queue_aio_set_host_notifier_handler(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); + for (i = 0; i < nvqs; i++) { + virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); + } /* Clean up guest notifier (irq) */ - k->set_guest_notifiers(qbus->parent, 1, false); + k->set_guest_notifiers(qbus->parent, nvqs, false); vblk->dataplane_started = false; s->stopping = false; diff --git a/hw/block/dataplane/virtio-blk.h b/hw/block/dataplane/virtio-blk.h index 0714c11a2b..b1f0b95b32 100644 --- a/hw/block/dataplane/virtio-blk.h +++ b/hw/block/dataplane/virtio-blk.h @@ -26,6 +26,6 @@ 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); +void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s, VirtQueue *vq); #endif /* HW_DATAPLANE_VIRTIO_BLK_H */ diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 51d8596056..326b688e83 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -53,12 +53,17 @@ /* 16 MiB max in 3 byte address mode */ #define MAX_3BYTES_SIZE 0x1000000 +#define SPI_NOR_MAX_ID_LEN 6 + 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; + /* + * This array stores the ID bytes. + * The first three bytes are the JEDIC ID. + * JEDEC ID zero means "no ID" (mostly older chips). + */ + uint8_t id[SPI_NOR_MAX_ID_LEN]; + uint8_t id_len; /* 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). @@ -70,11 +75,33 @@ typedef struct FlashPartInfo { } 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),\ +/* Used when the "_ext_id" is two bytes at most */ +#define INFO(_part_name, _jedec_id, _ext_id, _sector_size, _n_sectors, _flags)\ + .part_name = _part_name,\ + .id = {\ + ((_jedec_id) >> 16) & 0xff,\ + ((_jedec_id) >> 8) & 0xff,\ + (_jedec_id) & 0xff,\ + ((_ext_id) >> 8) & 0xff,\ + (_ext_id) & 0xff,\ + },\ + .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),\ + .sector_size = (_sector_size),\ + .n_sectors = (_n_sectors),\ + .page_size = 256,\ + .flags = (_flags), + +#define INFO6(_part_name, _jedec_id, _ext_id, _sector_size, _n_sectors, _flags)\ + .part_name = _part_name,\ + .id = {\ + ((_jedec_id) >> 16) & 0xff,\ + ((_jedec_id) >> 8) & 0xff,\ + (_jedec_id) & 0xff,\ + ((_ext_id) >> 16) & 0xff,\ + ((_ext_id) >> 8) & 0xff,\ + (_ext_id) & 0xff,\ + },\ + .id_len = 6,\ .sector_size = (_sector_size),\ .n_sectors = (_n_sectors),\ .page_size = 256,\ @@ -102,12 +129,26 @@ typedef struct FlashPartInfo { #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) +/* Spansion configuration registers macros. */ +#define SPANSION_QUAD_CFG_POS 0 +#define SPANSION_QUAD_CFG_LEN 1 +#define SPANSION_DUMMY_CLK_POS 0 +#define SPANSION_DUMMY_CLK_LEN 4 +#define SPANSION_ADDR_LEN_POS 7 +#define SPANSION_ADDR_LEN_LEN 1 + +/* + * Spansion read mode command length in bytes, + * the mode is currently not supported. +*/ + +#define SPANSION_CONTINUOUS_READ_MODE_CMD_LEN 1 + static const FlashPartInfo known_devices[] = { /* Atmel -- some are (confusingly) marketed as "DataFlash" */ { INFO("at25fs010", 0x1f6601, 0, 32 << 10, 4, ER_4K) }, @@ -158,6 +199,8 @@ static const FlashPartInfo known_devices[] = { { INFO("mx25l12855e", 0xc22618, 0, 64 << 10, 256, 0) }, { INFO("mx25l25635e", 0xc22019, 0, 64 << 10, 512, 0) }, { INFO("mx25l25655e", 0xc22619, 0, 64 << 10, 512, 0) }, + { INFO("mx66u51235f", 0xc2253a, 0, 64 << 10, 1024, ER_4K | ER_32K) }, + { INFO("mx66u1g45g", 0xc2253b, 0, 64 << 10, 2048, ER_4K | ER_32K) }, /* Micron */ { INFO("n25q032a11", 0x20bb16, 0, 64 << 10, 64, ER_4K) }, @@ -168,6 +211,11 @@ static const FlashPartInfo known_devices[] = { { 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) }, + { 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) }, + { INFO("mt25ql01g", 0x20ba21, 0, 64 << 10, 2048, ER_4K) }, + { INFO("mt25qu01g", 0x20bb21, 0, 64 << 10, 2048, ER_4K) }, /* Spansion -- single (large) sector size only, at least * for the chips listed here (without boot sectors). @@ -176,8 +224,8 @@ static const FlashPartInfo known_devices[] = { { 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) }, + { INFO6("s25fl512s", 0x010220, 0x4d0080, 256 << 10, 256, 0) }, + { INFO6("s70fl01gs", 0x010221, 0x4d0080, 256 << 10, 512, 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) }, @@ -190,6 +238,10 @@ static const FlashPartInfo known_devices[] = { { INFO("s25fl016k", 0xef4015, 0, 64 << 10, 32, ER_4K | ER_32K) }, { INFO("s25fl064k", 0xef4017, 0, 64 << 10, 128, ER_4K | ER_32K) }, + /* Spansion -- boot sectors support */ + { INFO6("s25fs512s", 0x010220, 0x4d0081, 256 << 10, 256, 0) }, + { INFO6("s70fs01gs", 0x010221, 0x4d0081, 256 << 10, 512, 0) }, + /* 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) }, @@ -240,10 +292,6 @@ static const FlashPartInfo known_devices[] = { { 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 { @@ -255,6 +303,7 @@ typedef enum { JEDEC_READ = 0x9f, BULK_ERASE = 0xc7, READ_FSR = 0x70, + RDCR = 0x15, READ = 0x03, READ4 = 0x13, @@ -271,12 +320,14 @@ typedef enum { PP = 0x02, PP4 = 0x12, + PP4_4 = 0x3e, DPP = 0xa2, QPP = 0x32, ERASE_4K = 0x20, ERASE4_4K = 0x21, ERASE_32K = 0x52, + ERASE4_32K = 0x5c, ERASE_SECTOR = 0xd8, ERASE4_SECTOR = 0xdc, @@ -289,6 +340,13 @@ typedef enum { RESET_ENABLE = 0x66, RESET_MEMORY = 0x99, + /* + * Micron: 0x35 - enable QPI + * Spansion: 0x35 - read control register + */ + RDCR_EQIO = 0x35, + RSTQIO = 0xf5, + RNVCR = 0xB5, WNVCR = 0xB1, @@ -304,9 +362,18 @@ typedef enum { STATE_PAGE_PROGRAM, STATE_READ, STATE_COLLECTING_DATA, + STATE_COLLECTING_VAR_LEN_DATA, STATE_READING_DATA, } CMDState; +typedef enum { + MAN_SPANSION, + MAN_MACRONIX, + MAN_NUMONYX, + MAN_WINBOND, + MAN_GENERIC, +} Manufacturer; + typedef struct Flash { SSISlave parent_obj; @@ -324,11 +391,22 @@ typedef struct Flash { uint8_t cmd_in_progress; uint64_t cur_addr; uint32_t nonvolatile_cfg; + /* Configuration register for Macronix */ uint32_t volatile_cfg; uint32_t enh_volatile_cfg; + /* Spansion cfg registers. */ + uint8_t spansion_cr1nv; + uint8_t spansion_cr2nv; + uint8_t spansion_cr3nv; + uint8_t spansion_cr4nv; + uint8_t spansion_cr1v; + uint8_t spansion_cr2v; + uint8_t spansion_cr3v; + uint8_t spansion_cr4v; bool write_enable; bool four_bytes_address_mode; bool reset_enable; + bool quad_enable; uint8_t ear; int64_t dirty_page; @@ -350,6 +428,22 @@ typedef struct M25P80Class { #define M25P80_GET_CLASS(obj) \ OBJECT_GET_CLASS(M25P80Class, (obj), TYPE_M25P80) +static inline Manufacturer get_man(Flash *s) +{ + switch (s->pi->id[0]) { + case 0x20: + return MAN_NUMONYX; + case 0xEF: + return MAN_WINBOND; + case 0x01: + return MAN_SPANSION; + case 0xC2: + return MAN_MACRONIX; + default: + return MAN_GENERIC; + } +} + static void blk_sync_complete(void *opaque, int ret) { /* do nothing. Masters do not directly interact with the backing store, @@ -398,6 +492,7 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd) capa_to_assert = ER_4K; break; case ERASE_32K: + case ERASE4_32K: len = 32 << 10; capa_to_assert = ER_32K; break; @@ -468,9 +563,11 @@ static inline int get_addr_length(Flash *s) switch (s->cmd_in_progress) { case PP4: + case PP4_4: case READ4: case QIOR4: case ERASE4_4K: + case ERASE4_32K: case ERASE4_SECTOR: case FAST_READ4: case DOR4: @@ -494,7 +591,7 @@ static void complete_collecting_data(Flash *s) } if (get_addr_length(s) == 3) { - s->cur_addr += (s->ear & 0x3) * MAX_3BYTES_SIZE; + s->cur_addr += s->ear * MAX_3BYTES_SIZE; } s->state = STATE_IDLE; @@ -504,6 +601,7 @@ static void complete_collecting_data(Flash *s) case QPP: case PP: case PP4: + case PP4_4: s->state = STATE_PAGE_PROGRAM; break; case READ: @@ -523,11 +621,25 @@ static void complete_collecting_data(Flash *s) case ERASE_4K: case ERASE4_4K: case ERASE_32K: + case ERASE4_32K: case ERASE_SECTOR: case ERASE4_SECTOR: flash_erase(s, s->cur_addr, s->cmd_in_progress); break; case WRSR: + switch (get_man(s)) { + case MAN_SPANSION: + s->quad_enable = !!(s->data[1] & 0x02); + break; + case MAN_MACRONIX: + s->quad_enable = extract32(s->data[0], 6, 1); + if (s->len > 1) { + s->four_bytes_address_mode = extract32(s->data[1], 5, 1); + } + break; + default: + break; + } if (s->write_enable) { s->write_enable = false; } @@ -561,8 +673,10 @@ static void reset_memory(Flash *s) s->state = STATE_IDLE; s->write_enable = false; s->reset_enable = false; + s->quad_enable = false; - if (((s->pi->jedec >> 16) & 0xFF) == JEDEC_NUMONYX) { + switch (get_man(s)) { + case MAN_NUMONYX: s->volatile_cfg = 0; s->volatile_cfg |= VCFG_DUMMY; s->volatile_cfg |= VCFG_WRAP_SEQUENTIAL; @@ -592,16 +706,147 @@ static void reset_memory(Flash *s) s->four_bytes_address_mode = true; } if (!(s->nonvolatile_cfg & NVCFG_LOWER_SEGMENT_MASK)) { - s->ear = CFG_UPPER_128MB_SEG_ENABLED; + s->ear = s->size / MAX_3BYTES_SIZE - 1; } + break; + case MAN_MACRONIX: + s->volatile_cfg = 0x7; + break; + case MAN_SPANSION: + s->spansion_cr1v = s->spansion_cr1nv; + s->spansion_cr2v = s->spansion_cr2nv; + s->spansion_cr3v = s->spansion_cr3nv; + s->spansion_cr4v = s->spansion_cr4nv; + s->quad_enable = extract32(s->spansion_cr1v, + SPANSION_QUAD_CFG_POS, + SPANSION_QUAD_CFG_LEN + ); + s->four_bytes_address_mode = extract32(s->spansion_cr2v, + SPANSION_ADDR_LEN_POS, + SPANSION_ADDR_LEN_LEN + ); + break; + default: + break; } DB_PRINT_L(0, "Reset done.\n"); } +static void decode_fast_read_cmd(Flash *s) +{ + s->needed_bytes = get_addr_length(s); + switch (get_man(s)) { + /* Dummy cycles - modeled with bytes writes instead of bits */ + case MAN_WINBOND: + s->needed_bytes += 8; + break; + case MAN_NUMONYX: + s->needed_bytes += extract32(s->volatile_cfg, 4, 4); + break; + case MAN_MACRONIX: + if (extract32(s->volatile_cfg, 6, 2) == 1) { + s->needed_bytes += 6; + } else { + s->needed_bytes += 8; + } + break; + case MAN_SPANSION: + s->needed_bytes += extract32(s->spansion_cr2v, + SPANSION_DUMMY_CLK_POS, + SPANSION_DUMMY_CLK_LEN + ); + break; + default: + break; + } + s->pos = 0; + s->len = 0; + s->state = STATE_COLLECTING_DATA; +} + +static void decode_dio_read_cmd(Flash *s) +{ + s->needed_bytes = get_addr_length(s); + /* Dummy cycles modeled with bytes writes instead of bits */ + switch (get_man(s)) { + case MAN_WINBOND: + s->needed_bytes += 8; + break; + case MAN_SPANSION: + s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN; + s->needed_bytes += extract32(s->spansion_cr2v, + SPANSION_DUMMY_CLK_POS, + SPANSION_DUMMY_CLK_LEN + ); + break; + case MAN_NUMONYX: + s->needed_bytes += extract32(s->volatile_cfg, 4, 4); + break; + case MAN_MACRONIX: + switch (extract32(s->volatile_cfg, 6, 2)) { + case 1: + s->needed_bytes += 6; + break; + case 2: + s->needed_bytes += 8; + break; + default: + s->needed_bytes += 4; + break; + } + break; + default: + break; + } + s->pos = 0; + s->len = 0; + s->state = STATE_COLLECTING_DATA; +} + +static void decode_qio_read_cmd(Flash *s) +{ + s->needed_bytes = get_addr_length(s); + /* Dummy cycles modeled with bytes writes instead of bits */ + switch (get_man(s)) { + case MAN_WINBOND: + s->needed_bytes += 8; + break; + case MAN_SPANSION: + s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN; + s->needed_bytes += extract32(s->spansion_cr2v, + SPANSION_DUMMY_CLK_POS, + SPANSION_DUMMY_CLK_LEN + ); + break; + case MAN_NUMONYX: + s->needed_bytes += extract32(s->volatile_cfg, 4, 4); + break; + case MAN_MACRONIX: + switch (extract32(s->volatile_cfg, 6, 2)) { + case 1: + s->needed_bytes += 4; + break; + case 2: + s->needed_bytes += 8; + break; + default: + s->needed_bytes += 6; + break; + } + break; + default: + break; + } + s->pos = 0; + s->len = 0; + s->state = STATE_COLLECTING_DATA; +} + static void decode_new_cmd(Flash *s, uint32_t value) { s->cmd_in_progress = value; + int i; DB_PRINT_L(0, "decoded new command:%x\n", value); if (value != RESET_MEMORY) { @@ -613,6 +858,7 @@ static void decode_new_cmd(Flash *s, uint32_t value) case ERASE_4K: case ERASE4_4K: case ERASE_32K: + case ERASE4_32K: case ERASE_SECTOR: case ERASE4_SECTOR: case READ: @@ -621,6 +867,7 @@ static void decode_new_cmd(Flash *s, uint32_t value) case QPP: case PP: case PP4: + case PP4_4: s->needed_bytes = get_addr_length(s); s->pos = 0; s->len = 0; @@ -633,56 +880,35 @@ static void decode_new_cmd(Flash *s, uint32_t value) 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; + decode_fast_read_cmd(s); 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; + decode_dio_read_cmd(s); 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; + decode_qio_read_cmd(s); break; case WRSR: if (s->write_enable) { - s->needed_bytes = 1; + switch (get_man(s)) { + case MAN_SPANSION: + s->needed_bytes = 2; + s->state = STATE_COLLECTING_DATA; + break; + case MAN_MACRONIX: + s->needed_bytes = 2; + s->state = STATE_COLLECTING_VAR_LEN_DATA; + break; + default: + s->needed_bytes = 1; + s->state = STATE_COLLECTING_DATA; + } s->pos = 0; - s->len = 0; - s->state = STATE_COLLECTING_DATA; } break; @@ -695,6 +921,9 @@ static void decode_new_cmd(Flash *s, uint32_t value) case RDSR: s->data[0] = (!!s->write_enable) << 1; + if (get_man(s) == MAN_MACRONIX) { + s->data[0] |= (!!s->quad_enable) << 6; + } s->pos = 0; s->len = 1; s->state = STATE_READING_DATA; @@ -712,17 +941,20 @@ static void decode_new_cmd(Flash *s, uint32_t value) 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; + for (i = 0; i < s->pi->id_len; i++) { + s->data[i] = s->pi->id[i]; } + + s->len = s->pi->id_len; + s->pos = 0; + s->state = STATE_READING_DATA; + break; + + case RDCR: + s->data[0] = s->volatile_cfg & 0xFF; + s->data[0] |= (!!s->four_bytes_address_mode) << 5; s->pos = 0; + s->len = 1; s->state = STATE_READING_DATA; break; @@ -765,7 +997,7 @@ static void decode_new_cmd(Flash *s, uint32_t value) s->state = STATE_READING_DATA; break; case WNVCR: - if (s->write_enable) { + if (s->write_enable && get_man(s) == MAN_NUMONYX) { s->needed_bytes = 2; s->pos = 0; s->len = 0; @@ -808,6 +1040,24 @@ static void decode_new_cmd(Flash *s, uint32_t value) reset_memory(s); } break; + case RDCR_EQIO: + switch (get_man(s)) { + case MAN_SPANSION: + s->data[0] = (!!s->quad_enable) << 1; + s->pos = 0; + s->len = 1; + s->state = STATE_READING_DATA; + break; + case MAN_MACRONIX: + s->quad_enable = true; + break; + default: + break; + } + break; + case RSTQIO: + s->quad_enable = false; + break; default: qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value); break; @@ -819,6 +1069,9 @@ static int m25p80_cs(SSISlave *ss, bool select) Flash *s = M25P80(ss); if (select) { + if (s->state == STATE_COLLECTING_VAR_LEN_DATA) { + complete_collecting_data(s); + } s->len = 0; s->pos = 0; s->state = STATE_IDLE; @@ -852,6 +1105,7 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) break; case STATE_COLLECTING_DATA: + case STATE_COLLECTING_VAR_LEN_DATA: s->data[s->len] = (uint8_t)tx; s->len++; @@ -926,13 +1180,18 @@ static void m25p80_pre_save(void *opaque) } static Property m25p80_properties[] = { + /* This is default value for Micron flash */ DEFINE_PROP_UINT32("nonvolatile-cfg", Flash, nonvolatile_cfg, 0x8FFF), + DEFINE_PROP_UINT8("spansion-cr1nv", Flash, spansion_cr1nv, 0x0), + DEFINE_PROP_UINT8("spansion-cr2nv", Flash, spansion_cr2nv, 0x8), + DEFINE_PROP_UINT8("spansion-cr3nv", Flash, spansion_cr3nv, 0x2), + DEFINE_PROP_UINT8("spansion-cr4nv", Flash, spansion_cr4nv, 0x10), DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_m25p80 = { .name = "xilinx_spi", - .version_id = 2, + .version_id = 3, .minimum_version_id = 1, .pre_save = m25p80_pre_save, .fields = (VMStateField[]) { @@ -950,6 +1209,11 @@ static const VMStateDescription vmstate_m25p80 = { VMSTATE_UINT32_V(nonvolatile_cfg, Flash, 2), VMSTATE_UINT32_V(volatile_cfg, Flash, 2), VMSTATE_UINT32_V(enh_volatile_cfg, Flash, 2), + VMSTATE_BOOL_V(quad_enable, Flash, 3), + VMSTATE_UINT8_V(spansion_cr1nv, Flash, 3), + VMSTATE_UINT8_V(spansion_cr2nv, Flash, 3), + VMSTATE_UINT8_V(spansion_cr3nv, Flash, 3), + VMSTATE_UINT8_V(spansion_cr4nv, Flash, 3), VMSTATE_END_OF_LIST() } }; diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 284e64667c..fb43bbaa46 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -29,9 +29,11 @@ #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" -void virtio_blk_init_request(VirtIOBlock *s, VirtIOBlockReq *req) +void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq, + VirtIOBlockReq *req) { req->dev = s; + req->vq = vq; req->qiov.size = 0; req->in_len = 0; req->next = NULL; @@ -53,11 +55,11 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) trace_virtio_blk_req_complete(req, status); stb_p(&req->in->status, status); - virtqueue_push(s->vq, &req->elem, req->in_len); + virtqueue_push(req->vq, &req->elem, req->in_len); if (s->dataplane_started && !s->dataplane_disabled) { - virtio_blk_data_plane_notify(s->dataplane); + virtio_blk_data_plane_notify(s->dataplane, req->vq); } else { - virtio_notify(vdev, s->vq); + virtio_notify(vdev, req->vq); } } @@ -187,12 +189,12 @@ out: #endif -static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s) +static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s, VirtQueue *vq) { - VirtIOBlockReq *req = virtqueue_pop(s->vq, sizeof(VirtIOBlockReq)); + VirtIOBlockReq *req = virtqueue_pop(vq, sizeof(VirtIOBlockReq)); if (req) { - virtio_blk_init_request(s, req); + virtio_blk_init_request(s, vq, req); } return req; } @@ -583,7 +585,7 @@ void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq) blk_io_plug(s->blk); - while ((req = virtio_blk_get_request(s))) { + while ((req = virtio_blk_get_request(s, vq))) { virtio_blk_handle_request(req, &mrb); } @@ -708,6 +710,7 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) blkcfg.physical_block_exp = get_physical_block_exp(conf); blkcfg.alignment_offset = 0; blkcfg.wce = blk_enable_write_cache(s->blk); + virtio_stw_p(vdev, &blkcfg.num_queues, s->conf.num_queues); memcpy(config, &blkcfg, sizeof(struct virtio_blk_config)); } @@ -751,6 +754,9 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features, if (blk_is_read_only(s->blk)) { virtio_add_feature(&features, VIRTIO_BLK_F_RO); } + if (s->conf.num_queues > 1) { + virtio_add_feature(&features, VIRTIO_BLK_F_MQ); + } return features; } @@ -795,11 +801,6 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) 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); } @@ -811,6 +812,11 @@ static void virtio_blk_save_device(VirtIODevice *vdev, QEMUFile *f) while (req) { qemu_put_sbyte(f, 1); + + if (s->conf.num_queues > 1) { + qemu_put_be32(f, virtio_get_queue_index(req->vq)); + } + qemu_put_virtqueue_element(f, &req->elem); req = req->next; } @@ -834,9 +840,22 @@ static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f, VirtIOBlock *s = VIRTIO_BLK(vdev); while (qemu_get_sbyte(f)) { + unsigned nvqs = s->conf.num_queues; + unsigned vq_idx = 0; VirtIOBlockReq *req; + + if (nvqs > 1) { + vq_idx = qemu_get_be32(f); + + if (vq_idx >= nvqs) { + error_report("Invalid virtqueue index in request list: %#x", + vq_idx); + return -EINVAL; + } + } + req = qemu_get_virtqueue_element(f, sizeof(VirtIOBlockReq)); - virtio_blk_init_request(s, req); + virtio_blk_init_request(s, virtio_get_queue(vdev, vq_idx), req); req->next = s->rq; s->rq = req; } @@ -862,6 +881,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) VirtIOBlkConf *conf = &s->conf; Error *err = NULL; static int virtio_blk_id; + unsigned i; if (!conf->conf.blk) { error_setg(errp, "drive property not set"); @@ -871,6 +891,10 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) error_setg(errp, "Device needs media, but drive is empty"); return; } + if (!conf->num_queues) { + error_setg(errp, "num-queues property must be larger than 0"); + return; + } blkconf_serial(&conf->conf, &conf->serial); s->original_wce = blk_enable_write_cache(conf->conf.blk); @@ -888,7 +912,9 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) 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); + for (i = 0; i < conf->num_queues; i++) { + 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); @@ -941,6 +967,7 @@ static Property virtio_blk_properties[] = { #endif DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, true), + DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, 1), DEFINE_PROP_END_OF_LIST(), }; |
