diff options
105 files changed, 3687 insertions, 1285 deletions
diff --git a/Makefile.objs b/Makefile.objs index fda366d11c..93406ff392 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -19,7 +19,7 @@ block-obj-$(CONFIG_POSIX) += posix-aio-compat.o block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o block-nested-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o -block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o +block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o block-nested-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o block-nested-y += qed-check.o block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o blkverify.o diff --git a/Makefile.target b/Makefile.target index cd2abde394..b0ba95f76e 100644 --- a/Makefile.target +++ b/Makefile.target @@ -286,7 +286,10 @@ obj-sparc-y += cirrus_vga.o else obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o -obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o +obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o + +# GRLIB +obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o endif obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o diff --git a/arch_init.c b/arch_init.c index e32e289c8f..cc56f0f998 100644 --- a/arch_init.c +++ b/arch_init.c @@ -461,7 +461,18 @@ void qemu_service_io(void) } #ifdef HAS_AUDIO -struct soundhw soundhw[] = { +struct soundhw { + const char *name; + const char *descr; + int enabled; + int isa; + union { + int (*init_isa) (qemu_irq *pic); + int (*init_pci) (PCIBus *bus); + } init; +}; + +static struct soundhw soundhw[] = { #ifdef HAS_AUDIO_CHOICE #if defined(TARGET_I386) || defined(TARGET_MIPS) { @@ -610,10 +621,32 @@ void select_soundhw(const char *optarg) } } } + +void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus) +{ + struct soundhw *c; + + for (c = soundhw; c->name; ++c) { + if (c->enabled) { + if (c->isa) { + if (isa_pic) { + c->init.init_isa(isa_pic); + } + } else { + if (pci_bus) { + c->init.init_pci(pci_bus); + } + } + } + } +} #else void select_soundhw(const char *optarg) { } +void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus) +{ +} #endif int qemu_uuid_parse(const char *str, uint8_t *uuid) diff --git a/arch_init.h b/arch_init.h index 682890c0ec..17c9164d39 100644 --- a/arch_init.h +++ b/arch_init.h @@ -27,6 +27,7 @@ void do_acpitable_option(const char *optarg); void do_smbios_option(const char *optarg); void cpudef_init(void); int audio_available(void); +void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus); int kvm_available(void); int xen_available(void); diff --git a/audio/paaudio.c b/audio/paaudio.c index 9cf685d30f..fb4510e426 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -33,13 +33,11 @@ typedef struct { static struct { int samples; - int divisor; char *server; char *sink; char *source; } conf = { - .samples = 1024, - .divisor = 2, + .samples = 4096, }; static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...) @@ -57,9 +55,6 @@ static void *qpa_thread_out (void *arg) { PAVoiceOut *pa = arg; HWVoiceOut *hw = &pa->hw; - int threshold; - - threshold = conf.divisor ? hw->samples / conf.divisor : 0; if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { return NULL; @@ -73,7 +68,7 @@ static void *qpa_thread_out (void *arg) goto exit; } - if (pa->live > threshold) { + if (pa->live > 0) { break; } @@ -82,8 +77,8 @@ static void *qpa_thread_out (void *arg) } } - decr = to_mix = pa->live; - rpos = hw->rpos; + decr = to_mix = audio_MIN (pa->live, conf.samples >> 2); + rpos = pa->rpos; if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { return NULL; @@ -110,8 +105,8 @@ static void *qpa_thread_out (void *arg) return NULL; } - pa->live = 0; pa->rpos = rpos; + pa->live -= decr; pa->decr += decr; } @@ -152,9 +147,6 @@ static void *qpa_thread_in (void *arg) { PAVoiceIn *pa = arg; HWVoiceIn *hw = &pa->hw; - int threshold; - - threshold = conf.divisor ? hw->samples / conf.divisor : 0; if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { return NULL; @@ -168,7 +160,7 @@ static void *qpa_thread_in (void *arg) goto exit; } - if (pa->dead > threshold) { + if (pa->dead > 0) { break; } @@ -177,8 +169,8 @@ static void *qpa_thread_in (void *arg) } } - incr = to_grab = pa->dead; - wpos = hw->wpos; + incr = to_grab = audio_MIN (pa->dead, conf.samples >> 2); + wpos = pa->wpos; if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { return NULL; @@ -295,6 +287,7 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) { int error; static pa_sample_spec ss; + static pa_buffer_attr ba; struct audsettings obt_as = *as; PAVoiceOut *pa = (PAVoiceOut *) hw; @@ -302,6 +295,15 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) ss.channels = as->nchannels; ss.rate = as->freq; + /* + * qemu audio tick runs at 250 Hz (by default), so processing + * data chunks worth 4 ms of sound should be a good fit. + */ + ba.tlength = pa_usec_to_bytes (4 * 1000, &ss); + ba.minreq = pa_usec_to_bytes (2 * 1000, &ss); + ba.maxlength = -1; + ba.prebuf = -1; + obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); pa->s = pa_simple_new ( @@ -312,7 +314,7 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) "pcm.playback", &ss, NULL, /* channel map */ - NULL, /* buffering attributes */ + &ba, /* buffering attributes */ &error ); if (!pa->s) { @@ -323,6 +325,7 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) audio_pcm_init_info (&hw->info, &obt_as); hw->samples = conf.samples; pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); + pa->rpos = hw->rpos; if (!pa->pcm_buf) { dolog ("Could not allocate buffer (%d bytes)\n", hw->samples << hw->info.shift); @@ -377,6 +380,7 @@ static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as) audio_pcm_init_info (&hw->info, &obt_as); hw->samples = conf.samples; pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); + pa->wpos = hw->wpos; if (!pa->pcm_buf) { dolog ("Could not allocate buffer (%d bytes)\n", hw->samples << hw->info.shift); @@ -472,12 +476,6 @@ struct audio_option qpa_options[] = { .descr = "buffer size in samples" }, { - .name = "DIVISOR", - .tag = AUD_OPT_INT, - .valp = &conf.divisor, - .descr = "threshold divisor" - }, - { .name = "SERVER", .tag = AUD_OPT_STR, .valp = &conf.server, diff --git a/block-migration.c b/block-migration.c index 14753254d6..c9d3e81dbf 100644 --- a/block-migration.c +++ b/block-migration.c @@ -350,7 +350,12 @@ static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f) } } - progress = completed_sector_sum * 100 / block_mig_state.total_sector_sum; + if (block_mig_state.total_sector_sum != 0) { + progress = completed_sector_sum * 100 / + block_mig_state.total_sector_sum; + } else { + progress = 100; + } if (progress != block_mig_state.prev_progress) { block_mig_state.prev_progress = progress; qemu_put_be64(f, (progress << BDRV_SECTOR_BITS) @@ -633,8 +638,10 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) int len, flags; char device_name[256]; int64_t addr; - BlockDriverState *bs; + BlockDriverState *bs, *bs_prev = NULL; uint8_t *buf; + int64_t total_sectors = 0; + int nr_sectors; do { addr = qemu_get_be64(f); @@ -656,10 +663,26 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) return -EINVAL; } + if (bs != bs_prev) { + bs_prev = bs; + total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; + if (total_sectors <= 0) { + error_report("Error getting length of block device %s\n", + device_name); + return -EINVAL; + } + } + + if (total_sectors - addr < BDRV_SECTORS_PER_DIRTY_CHUNK) { + nr_sectors = total_sectors - addr; + } else { + nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK; + } + buf = qemu_malloc(BLOCK_SIZE); qemu_get_buffer(f, buf, BLOCK_SIZE); - ret = bdrv_write(bs, addr, buf, BDRV_SECTORS_PER_DIRTY_CHUNK); + ret = bdrv_write(bs, addr, buf, nr_sectors); qemu_free(buf); if (ret < 0) { @@ -645,7 +645,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags, /* call the change callback */ bs->media_changed = 1; if (bs->change_cb) - bs->change_cb(bs->change_opaque); + bs->change_cb(bs->change_opaque, CHANGE_MEDIA); } return 0; @@ -684,7 +684,7 @@ void bdrv_close(BlockDriverState *bs) /* call the change callback */ bs->media_changed = 1; if (bs->change_cb) - bs->change_cb(bs->change_opaque); + bs->change_cb(bs->change_opaque, CHANGE_MEDIA); } } @@ -1135,6 +1135,9 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset) ret = drv->bdrv_truncate(bs, offset); if (ret == 0) { ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); + if (bs->change_cb) { + bs->change_cb(bs->change_opaque, CHANGE_SIZE); + } } return ret; } @@ -1366,7 +1369,8 @@ int bdrv_enable_write_cache(BlockDriverState *bs) /* XXX: no longer used */ void bdrv_set_change_cb(BlockDriverState *bs, - void (*change_cb)(void *opaque), void *opaque) + void (*change_cb)(void *opaque, int reason), + void *opaque) { bs->change_cb = change_cb; bs->change_opaque = opaque; @@ -1411,7 +1415,7 @@ int bdrv_set_key(BlockDriverState *bs, const char *key) /* call the change callback now, we skipped it on open */ bs->media_changed = 1; if (bs->change_cb) - bs->change_cb(bs->change_opaque); + bs->change_cb(bs->change_opaque, CHANGE_MEDIA); } return ret; } @@ -2778,6 +2782,7 @@ int bdrv_img_create(const char *filename, const char *fmt, QEMUOptionParameter *backing_fmt, *backing_file; BlockDriverState *bs = NULL; BlockDriver *drv, *proto_drv; + BlockDriver *backing_drv = NULL; int ret = 0; /* Find driver and parse its options */ @@ -2846,7 +2851,8 @@ int bdrv_img_create(const char *filename, const char *fmt, backing_fmt = get_option_parameter(param, BLOCK_OPT_BACKING_FMT); if (backing_fmt && backing_fmt->value.s) { - if (!bdrv_find_format(backing_fmt->value.s)) { + backing_drv = bdrv_find_format(backing_fmt->value.s); + if (!backing_drv) { error_report("Unknown backing file format '%s'", backing_fmt->value.s); ret = -EINVAL; @@ -2863,9 +2869,9 @@ int bdrv_img_create(const char *filename, const char *fmt, bs = bdrv_new(""); - ret = bdrv_open(bs, backing_file->value.s, flags, drv); + ret = bdrv_open(bs, backing_file->value.s, flags, backing_drv); if (ret < 0) { - error_report("Could not open '%s'", filename); + error_report("Could not open '%s'", backing_file->value.s); goto out; } bdrv_get_geometry(bs, &size); @@ -182,7 +182,8 @@ int bdrv_is_locked(BlockDriverState *bs); void bdrv_set_locked(BlockDriverState *bs, int locked); int bdrv_eject(BlockDriverState *bs, int eject_flag); void bdrv_set_change_cb(BlockDriverState *bs, - void (*change_cb)(void *opaque), void *opaque); + void (*change_cb)(void *opaque, int reason), + void *opaque); void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size); BlockDriverState *bdrv_find(const char *name); BlockDriverState *bdrv_next(BlockDriverState *bs); diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c new file mode 100644 index 0000000000..382473933c --- /dev/null +++ b/block/qcow2-cache.c @@ -0,0 +1,314 @@ +/* + * L2/refcount table cache for the QCOW2 format + * + * Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com> + * + * 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 "block_int.h" +#include "qemu-common.h" +#include "qcow2.h" + +typedef struct Qcow2CachedTable { + void* table; + int64_t offset; + bool dirty; + int cache_hits; + int ref; +} Qcow2CachedTable; + +struct Qcow2Cache { + Qcow2CachedTable* entries; + struct Qcow2Cache* depends; + int size; + bool depends_on_flush; + bool writethrough; +}; + +Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, + bool writethrough) +{ + BDRVQcowState *s = bs->opaque; + Qcow2Cache *c; + int i; + + c = qemu_mallocz(sizeof(*c)); + c->size = num_tables; + c->entries = qemu_mallocz(sizeof(*c->entries) * num_tables); + c->writethrough = writethrough; + + for (i = 0; i < c->size; i++) { + c->entries[i].table = qemu_blockalign(bs, s->cluster_size); + } + + return c; +} + +int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c) +{ + int i; + + for (i = 0; i < c->size; i++) { + assert(c->entries[i].ref == 0); + qemu_vfree(c->entries[i].table); + } + + qemu_free(c->entries); + qemu_free(c); + + return 0; +} + +static int qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c) +{ + int ret; + + ret = qcow2_cache_flush(bs, c->depends); + if (ret < 0) { + return ret; + } + + c->depends = NULL; + c->depends_on_flush = false; + + return 0; +} + +static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) +{ + BDRVQcowState *s = bs->opaque; + int ret = 0; + + if (!c->entries[i].dirty || !c->entries[i].offset) { + return 0; + } + + if (c->depends) { + ret = qcow2_cache_flush_dependency(bs, c); + } else if (c->depends_on_flush) { + ret = bdrv_flush(bs->file); + if (ret >= 0) { + c->depends_on_flush = false; + } + } + + if (ret < 0) { + return ret; + } + + if (c == s->refcount_block_cache) { + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART); + } else if (c == s->l2_table_cache) { + BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); + } + + ret = bdrv_pwrite(bs->file, c->entries[i].offset, c->entries[i].table, + s->cluster_size); + if (ret < 0) { + return ret; + } + + c->entries[i].dirty = false; + + return 0; +} + +int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c) +{ + int result = 0; + int ret; + int i; + + for (i = 0; i < c->size; i++) { + ret = qcow2_cache_entry_flush(bs, c, i); + if (ret < 0 && result != -ENOSPC) { + result = ret; + } + } + + if (result == 0) { + ret = bdrv_flush(bs->file); + if (ret < 0) { + result = ret; + } + } + + return result; +} + +int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, + Qcow2Cache *dependency) +{ + int ret; + + if (dependency->depends) { + ret = qcow2_cache_flush_dependency(bs, dependency); + if (ret < 0) { + return ret; + } + } + + if (c->depends && (c->depends != dependency)) { + ret = qcow2_cache_flush_dependency(bs, c); + if (ret < 0) { + return ret; + } + } + + c->depends = dependency; + return 0; +} + +void qcow2_cache_depends_on_flush(Qcow2Cache *c) +{ + c->depends_on_flush = true; +} + +static int qcow2_cache_find_entry_to_replace(Qcow2Cache *c) +{ + int i; + int min_count = INT_MAX; + int min_index = -1; + + + for (i = 0; i < c->size; i++) { + if (c->entries[i].ref) { + continue; + } + + if (c->entries[i].cache_hits < min_count) { + min_index = i; + min_count = c->entries[i].cache_hits; + } + + /* Give newer hits priority */ + /* TODO Check how to optimize the replacement strategy */ + c->entries[i].cache_hits /= 2; + } + + if (min_index == -1) { + /* This can't happen in current synchronous code, but leave the check + * here as a reminder for whoever starts using AIO with the cache */ + abort(); + } + return min_index; +} + +static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, + uint64_t offset, void **table, bool read_from_disk) +{ + BDRVQcowState *s = bs->opaque; + int i; + int ret; + + /* Check if the table is already cached */ + for (i = 0; i < c->size; i++) { + if (c->entries[i].offset == offset) { + goto found; + } + } + + /* If not, write a table back and replace it */ + i = qcow2_cache_find_entry_to_replace(c); + if (i < 0) { + return i; + } + + ret = qcow2_cache_entry_flush(bs, c, i); + if (ret < 0) { + return ret; + } + + c->entries[i].offset = 0; + if (read_from_disk) { + if (c == s->l2_table_cache) { + BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); + } + + ret = bdrv_pread(bs->file, offset, c->entries[i].table, s->cluster_size); + if (ret < 0) { + return ret; + } + } + + /* Give the table some hits for the start so that it won't be replaced + * immediately. The number 32 is completely arbitrary. */ + c->entries[i].cache_hits = 32; + c->entries[i].offset = offset; + + /* And return the right table */ +found: + c->entries[i].cache_hits++; + c->entries[i].ref++; + *table = c->entries[i].table; + return 0; +} + +int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, + void **table) +{ + return qcow2_cache_do_get(bs, c, offset, table, true); +} + +int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, + void **table) +{ + return qcow2_cache_do_get(bs, c, offset, table, false); +} + +int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table) +{ + int i; + + for (i = 0; i < c->size; i++) { + if (c->entries[i].table == *table) { + goto found; + } + } + return -ENOENT; + +found: + c->entries[i].ref--; + *table = NULL; + + assert(c->entries[i].ref >= 0); + + if (c->writethrough) { + return qcow2_cache_entry_flush(bs, c, i); + } else { + return 0; + } +} + +void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table) +{ + int i; + + for (i = 0; i < c->size; i++) { + if (c->entries[i].table == table) { + goto found; + } + } + abort(); + +found: + c->entries[i].dirty = true; +} + diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 6928c6341d..5fb8c6695a 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -67,7 +67,11 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size) qemu_free(new_l1_table); return new_l1_table_offset; } - bdrv_flush(bs->file); + + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret < 0) { + return ret; + } BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE); for(i = 0; i < s->l1_size; i++) @@ -81,7 +85,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size) /* set new table */ BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ACTIVATE_TABLE); cpu_to_be32w((uint32_t*)data, new_l1_size); - cpu_to_be64w((uint64_t*)(data + 4), new_l1_table_offset); + cpu_to_be64wu((uint64_t*)(data + 4), new_l1_table_offset); ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), data,sizeof(data)); if (ret < 0) { goto fail; @@ -98,63 +102,6 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size) return ret; } -void qcow2_l2_cache_reset(BlockDriverState *bs) -{ - BDRVQcowState *s = bs->opaque; - - memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t)); - memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t)); - memset(s->l2_cache_counts, 0, L2_CACHE_SIZE * sizeof(uint32_t)); -} - -static inline int l2_cache_new_entry(BlockDriverState *bs) -{ - BDRVQcowState *s = bs->opaque; - uint32_t min_count; - int min_index, i; - - /* find a new entry in the least used one */ - min_index = 0; - min_count = 0xffffffff; - for(i = 0; i < L2_CACHE_SIZE; i++) { - if (s->l2_cache_counts[i] < min_count) { - min_count = s->l2_cache_counts[i]; - min_index = i; - } - } - return min_index; -} - -/* - * seek_l2_table - * - * seek l2_offset in the l2_cache table - * if not found, return NULL, - * if found, - * increments the l2 cache hit count of the entry, - * if counter overflow, divide by two all counters - * return the pointer to the l2 cache entry - * - */ - -static uint64_t *seek_l2_table(BDRVQcowState *s, uint64_t l2_offset) -{ - int i, j; - - for(i = 0; i < L2_CACHE_SIZE; i++) { - if (l2_offset == s->l2_cache_offsets[i]) { - /* increment the hit count */ - if (++s->l2_cache_counts[i] == 0xffffffff) { - for(j = 0; j < L2_CACHE_SIZE; j++) { - s->l2_cache_counts[j] >>= 1; - } - } - return s->l2_cache + (i << s->l2_bits); - } - } - return NULL; -} - /* * l2_load * @@ -169,33 +116,11 @@ static int l2_load(BlockDriverState *bs, uint64_t l2_offset, uint64_t **l2_table) { BDRVQcowState *s = bs->opaque; - int min_index; int ret; - /* seek if the table for the given offset is in the cache */ - - *l2_table = seek_l2_table(s, l2_offset); - if (*l2_table != NULL) { - return 0; - } - - /* not found: load a new entry in the least used one */ - - min_index = l2_cache_new_entry(bs); - *l2_table = s->l2_cache + (min_index << s->l2_bits); - - BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); - ret = bdrv_pread(bs->file, l2_offset, *l2_table, - s->l2_size * sizeof(uint64_t)); - if (ret < 0) { - qcow2_l2_cache_reset(bs); - return ret; - } - - s->l2_cache_offsets[min_index] = l2_offset; - s->l2_cache_counts[min_index] = 1; + ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, (void**) l2_table); - return 0; + return ret; } /* @@ -238,7 +163,6 @@ static int write_l1_entry(BlockDriverState *bs, int l1_index) static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) { BDRVQcowState *s = bs->opaque; - int min_index; uint64_t old_l2_offset; uint64_t *l2_table; int64_t l2_offset; @@ -252,29 +176,48 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) if (l2_offset < 0) { return l2_offset; } - bdrv_flush(bs->file); + + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret < 0) { + goto fail; + } /* allocate a new entry in the l2 cache */ - min_index = l2_cache_new_entry(bs); - l2_table = s->l2_cache + (min_index << s->l2_bits); + ret = qcow2_cache_get_empty(bs, s->l2_table_cache, l2_offset, (void**) table); + if (ret < 0) { + return ret; + } + + l2_table = *table; if (old_l2_offset == 0) { /* if there was no old l2 table, clear the new table */ memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); } else { + uint64_t* old_table; + /* if there was an old l2 table, read it from the disk */ BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ); - ret = bdrv_pread(bs->file, old_l2_offset, l2_table, - s->l2_size * sizeof(uint64_t)); + ret = qcow2_cache_get(bs, s->l2_table_cache, old_l2_offset, + (void**) &old_table); + if (ret < 0) { + goto fail; + } + + memcpy(l2_table, old_table, s->cluster_size); + + ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &old_table); if (ret < 0) { goto fail; } } + /* write the l2 table to the file */ BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE); - ret = bdrv_pwrite_sync(bs->file, l2_offset, l2_table, - s->l2_size * sizeof(uint64_t)); + + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); + ret = qcow2_cache_flush(bs, s->l2_table_cache); if (ret < 0) { goto fail; } @@ -286,17 +229,12 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) goto fail; } - /* update the l2 cache entry */ - - s->l2_cache_offsets[min_index] = l2_offset; - s->l2_cache_counts[min_index] = 1; - *table = l2_table; return 0; fail: + qcow2_cache_put(bs, s->l2_table_cache, (void**) table); s->l1_table[l1_index] = old_l2_offset; - qcow2_l2_cache_reset(bs); return ret; } @@ -521,6 +459,8 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, &l2_table[l2_index], 0, QCOW_OFLAG_COPIED); } + qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + nb_available = (c * s->cluster_sectors); out: if (nb_available > nb_needed) @@ -575,6 +515,7 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, return ret; } } else { + /* FIXME Order */ if (l2_offset) qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t)); ret = l2_allocate(bs, l1_index, &l2_table); @@ -632,6 +573,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, cluster_offset = qcow2_alloc_bytes(bs, compressed_size); if (cluster_offset < 0) { + qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); return 0; } @@ -646,38 +588,14 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, /* compressed clusters never have the copied flag */ BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); l2_table[l2_index] = cpu_to_be64(cluster_offset); - if (bdrv_pwrite_sync(bs->file, - l2_offset + l2_index * sizeof(uint64_t), - l2_table + l2_index, - sizeof(uint64_t)) < 0) - return 0; - - return cluster_offset; -} - -/* - * Write L2 table updates to disk, writing whole sectors to avoid a - * read-modify-write in bdrv_pwrite - */ -#define L2_ENTRIES_PER_SECTOR (512 / 8) -static int write_l2_entries(BlockDriverState *bs, uint64_t *l2_table, - uint64_t l2_offset, int l2_index, int num) -{ - int l2_start_index = l2_index & ~(L1_ENTRIES_PER_SECTOR - 1); - int start_offset = (8 * l2_index) & ~511; - int end_offset = (8 * (l2_index + num) + 511) & ~511; - size_t len = end_offset - start_offset; - int ret; - - BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); - ret = bdrv_pwrite(bs->file, l2_offset + start_offset, - &l2_table[l2_start_index], len); + ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); if (ret < 0) { - return ret; + return 0; } - return 0; + return cluster_offset; } int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) @@ -686,6 +604,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) int i, j = 0, l2_index, ret; uint64_t *old_cluster, start_sect, l2_offset, *l2_table; uint64_t cluster_offset = m->cluster_offset; + bool cow = false; if (m->nb_clusters == 0) return 0; @@ -695,6 +614,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) /* copy content of unmodified sectors */ start_sect = (m->offset & ~(s->cluster_size - 1)) >> 9; if (m->n_start) { + cow = true; ret = copy_sectors(bs, start_sect, cluster_offset, 0, m->n_start); if (ret < 0) goto err; @@ -702,17 +622,30 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) if (m->nb_available & (s->cluster_sectors - 1)) { uint64_t end = m->nb_available & ~(uint64_t)(s->cluster_sectors - 1); + cow = true; ret = copy_sectors(bs, start_sect + end, cluster_offset + (end << 9), m->nb_available - end, s->cluster_sectors); if (ret < 0) goto err; } - /* update L2 table */ + /* + * Update L2 table. + * + * Before we update the L2 table to actually point to the new cluster, we + * need to be sure that the refcounts have been increased and COW was + * handled. + */ + if (cow) { + qcow2_cache_depends_on_flush(s->l2_table_cache); + } + + qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache); ret = get_cluster_table(bs, m->offset, &l2_table, &l2_offset, &l2_index); if (ret < 0) { goto err; } + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); for (i = 0; i < m->nb_clusters; i++) { /* if two concurrent writes happen to the same unallocated cluster @@ -728,16 +661,9 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) (i << s->cluster_bits)) | QCOW_OFLAG_COPIED); } - /* - * Before we update the L2 table to actually point to the new cluster, we - * need to be sure that the refcounts have been increased and COW was - * handled. - */ - bdrv_flush(bs->file); - ret = write_l2_entries(bs, l2_table, l2_offset, l2_index, m->nb_clusters); + ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); if (ret < 0) { - qcow2_l2_cache_reset(bs); goto err; } @@ -746,7 +672,6 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) * Also flush bs->file to get the right order for L2 and refcount update. */ if (j != 0) { - bdrv_flush(bs->file); for (i = 0; i < j; i++) { qcow2_free_any_clusters(bs, be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1); @@ -868,7 +793,8 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, m->depends_on = old_alloc; m->nb_clusters = 0; *num = 0; - return 0; + ret = 0; + goto fail; } } } @@ -884,7 +810,8 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, cluster_offset = qcow2_alloc_clusters(bs, nb_clusters * s->cluster_size); if (cluster_offset < 0) { QLIST_REMOVE(m, next_in_flight); - return cluster_offset; + ret = cluster_offset; + goto fail; } /* save info needed for meta data update */ @@ -893,12 +820,21 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, m->nb_clusters = nb_clusters; out: + ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + if (ret < 0) { + return ret; + } + m->nb_available = MIN(nb_clusters << (s->cluster_bits - 9), n_end); m->cluster_offset = cluster_offset; *num = m->nb_available - n_start; return 0; + +fail: + qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + return ret; } static int decompress_buffer(uint8_t *out_buf, int out_buf_size, @@ -952,3 +888,85 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) } return 0; } + +/* + * This discards as many clusters of nb_clusters as possible at once (i.e. + * all clusters in the same L2 table) and returns the number of discarded + * clusters. + */ +static int discard_single_l2(BlockDriverState *bs, uint64_t offset, + unsigned int nb_clusters) +{ + BDRVQcowState *s = bs->opaque; + uint64_t l2_offset, *l2_table; + int l2_index; + int ret; + int i; + + ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index); + if (ret < 0) { + return ret; + } + + /* Limit nb_clusters to one L2 table */ + nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + + for (i = 0; i < nb_clusters; i++) { + uint64_t old_offset; + + old_offset = be64_to_cpu(l2_table[l2_index + i]); + old_offset &= ~QCOW_OFLAG_COPIED; + + if (old_offset == 0) { + continue; + } + + /* First remove L2 entries */ + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); + l2_table[l2_index + i] = cpu_to_be64(0); + + /* Then decrease the refcount */ + qcow2_free_any_clusters(bs, old_offset, 1); + } + + ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + if (ret < 0) { + return ret; + } + + return nb_clusters; +} + +int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, + int nb_sectors) +{ + BDRVQcowState *s = bs->opaque; + uint64_t end_offset; + unsigned int nb_clusters; + int ret; + + end_offset = offset + (nb_sectors << BDRV_SECTOR_BITS); + + /* Round start up and end down */ + offset = align_offset(offset, s->cluster_size); + end_offset &= ~(s->cluster_size - 1); + + if (offset > end_offset) { + return 0; + } + + nb_clusters = size_to_clusters(s, end_offset - offset); + + /* Each L2 table is handled by its own loop iteration */ + while (nb_clusters > 0) { + ret = discard_single_l2(bs, offset, nb_clusters); + if (ret < 0) { + return ret; + } + + nb_clusters -= ret; + offset += (ret * s->cluster_size); + } + + return 0; +} diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index a10453c875..915d85acbf 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -32,27 +32,6 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, int addend); -static int cache_refcount_updates = 0; - -static int write_refcount_block(BlockDriverState *bs) -{ - BDRVQcowState *s = bs->opaque; - size_t size = s->cluster_size; - - if (s->refcount_block_cache_offset == 0) { - return 0; - } - - BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE); - if (bdrv_pwrite_sync(bs->file, s->refcount_block_cache_offset, - s->refcount_block_cache, size) < 0) - { - return -EIO; - } - - return 0; -} - /*********************************************************/ /* refcount handling */ @@ -61,7 +40,6 @@ int qcow2_refcount_init(BlockDriverState *bs) BDRVQcowState *s = bs->opaque; int ret, refcount_table_size2, i; - s->refcount_block_cache = qemu_malloc(s->cluster_size); refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t); s->refcount_table = qemu_malloc(refcount_table_size2); if (s->refcount_table_size > 0) { @@ -81,34 +59,22 @@ int qcow2_refcount_init(BlockDriverState *bs) void qcow2_refcount_close(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; - qemu_free(s->refcount_block_cache); qemu_free(s->refcount_table); } static int load_refcount_block(BlockDriverState *bs, - int64_t refcount_block_offset) + int64_t refcount_block_offset, + void **refcount_block) { BDRVQcowState *s = bs->opaque; int ret; - if (cache_refcount_updates) { - ret = write_refcount_block(bs); - if (ret < 0) { - return ret; - } - } - BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_LOAD); - ret = bdrv_pread(bs->file, refcount_block_offset, s->refcount_block_cache, - s->cluster_size); - if (ret < 0) { - s->refcount_block_cache_offset = 0; - return ret; - } + ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset, + refcount_block); - s->refcount_block_cache_offset = refcount_block_offset; - return 0; + return ret; } /* @@ -122,6 +88,8 @@ static int get_refcount(BlockDriverState *bs, int64_t cluster_index) int refcount_table_index, block_index; int64_t refcount_block_offset; int ret; + uint16_t *refcount_block; + uint16_t refcount; refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT); if (refcount_table_index >= s->refcount_table_size) @@ -129,16 +97,24 @@ static int get_refcount(BlockDriverState *bs, int64_t cluster_index) refcount_block_offset = s->refcount_table[refcount_table_index]; if (!refcount_block_offset) return 0; - if (refcount_block_offset != s->refcount_block_cache_offset) { - /* better than nothing: return allocated if read error */ - ret = load_refcount_block(bs, refcount_block_offset); - if (ret < 0) { - return ret; - } + + ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset, + (void**) &refcount_block); + if (ret < 0) { + return ret; } + block_index = cluster_index & ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1); - return be16_to_cpu(s->refcount_block_cache[block_index]); + refcount = be16_to_cpu(refcount_block[block_index]); + + ret = qcow2_cache_put(bs, s->refcount_block_cache, + (void**) &refcount_block); + if (ret < 0) { + return ret; + } + + return refcount; } /* @@ -174,9 +150,10 @@ static int in_same_refcount_block(BDRVQcowState *s, uint64_t offset_a, * Loads a refcount block. If it doesn't exist yet, it is allocated first * (including growing the refcount table if needed). * - * Returns the offset of the refcount block on success or -errno in error case + * Returns 0 on success or -errno in error case */ -static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index) +static int alloc_refcount_block(BlockDriverState *bs, + int64_t cluster_index, uint16_t **refcount_block) { BDRVQcowState *s = bs->opaque; unsigned int refcount_table_index; @@ -194,13 +171,8 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index) /* If it's already there, we're done */ if (refcount_block_offset) { - if (refcount_block_offset != s->refcount_block_cache_offset) { - ret = load_refcount_block(bs, refcount_block_offset); - if (ret < 0) { - return ret; - } - } - return refcount_block_offset; + return load_refcount_block(bs, refcount_block_offset, + (void**) refcount_block); } } @@ -226,12 +198,10 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index) * refcount block into the cache */ - if (cache_refcount_updates) { - ret = write_refcount_block(bs); - if (ret < 0) { - return ret; - } - } + *refcount_block = NULL; + + /* We write to the refcount table, so we might depend on L2 tables */ + qcow2_cache_flush(bs, s->l2_table_cache); /* Allocate the refcount block itself and mark it as used */ int64_t new_block = alloc_clusters_noref(bs, s->cluster_size); @@ -247,13 +217,18 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index) if (in_same_refcount_block(s, new_block, cluster_index << s->cluster_bits)) { /* Zero the new refcount block before updating it */ - memset(s->refcount_block_cache, 0, s->cluster_size); - s->refcount_block_cache_offset = new_block; + ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block, + (void**) refcount_block); + if (ret < 0) { + goto fail_block; + } + + memset(*refcount_block, 0, s->cluster_size); /* The block describes itself, need to update the cache */ int block_index = (new_block >> s->cluster_bits) & ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1); - s->refcount_block_cache[block_index] = cpu_to_be16(1); + (*refcount_block)[block_index] = cpu_to_be16(1); } else { /* Described somewhere else. This can recurse at most twice before we * arrive at a block that describes itself. */ @@ -266,14 +241,19 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index) /* Initialize the new refcount block only after updating its refcount, * update_refcount uses the refcount cache itself */ - memset(s->refcount_block_cache, 0, s->cluster_size); - s->refcount_block_cache_offset = new_block; + ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block, + (void**) refcount_block); + if (ret < 0) { + goto fail_block; + } + + memset(*refcount_block, 0, s->cluster_size); } /* Now the new refcount block needs to be written to disk */ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE); - ret = bdrv_pwrite_sync(bs->file, new_block, s->refcount_block_cache, - s->cluster_size); + qcow2_cache_entry_mark_dirty(s->refcount_block_cache, *refcount_block); + ret = qcow2_cache_flush(bs, s->refcount_block_cache); if (ret < 0) { goto fail_block; } @@ -290,7 +270,12 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index) } s->refcount_table[refcount_table_index] = new_block; - return new_block; + return 0; + } + + ret = qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block); + if (ret < 0) { + goto fail_block; } /* @@ -410,9 +395,9 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index) qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t)); s->free_cluster_index = old_free_cluster_index; - ret = load_refcount_block(bs, new_block); + ret = load_refcount_block(bs, new_block, (void**) refcount_block); if (ret < 0) { - goto fail_block; + return ret; } return new_block; @@ -420,41 +405,10 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index) fail_table: qemu_free(new_table); fail_block: - s->refcount_block_cache_offset = 0; - return ret; -} - -#define REFCOUNTS_PER_SECTOR (512 >> REFCOUNT_SHIFT) -static int write_refcount_block_entries(BlockDriverState *bs, - int64_t refcount_block_offset, int first_index, int last_index) -{ - BDRVQcowState *s = bs->opaque; - size_t size; - int ret; - - if (cache_refcount_updates) { - return 0; + if (*refcount_block != NULL) { + qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block); } - - if (first_index < 0) { - return 0; - } - - first_index &= ~(REFCOUNTS_PER_SECTOR - 1); - last_index = (last_index + REFCOUNTS_PER_SECTOR) - & ~(REFCOUNTS_PER_SECTOR - 1); - - size = (last_index - first_index) << REFCOUNT_SHIFT; - - BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART); - ret = bdrv_pwrite(bs->file, - refcount_block_offset + (first_index << REFCOUNT_SHIFT), - &s->refcount_block_cache[first_index], size); - if (ret < 0) { - return ret; - } - - return 0; + return ret; } /* XXX: cache several refcount block clusters ? */ @@ -463,9 +417,8 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, { BDRVQcowState *s = bs->opaque; int64_t start, last, cluster_offset; - int64_t refcount_block_offset = 0; - int64_t table_index = -1, old_table_index; - int first_index = -1, last_index = -1; + uint16_t *refcount_block = NULL; + int64_t old_table_index = -1; int ret; #ifdef DEBUG_ALLOC2 @@ -478,6 +431,11 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, return 0; } + if (addend < 0) { + qcow2_cache_set_dependency(bs, s->refcount_block_cache, + s->l2_table_cache); + } + start = offset & ~(s->cluster_size - 1); last = (offset + length - 1) & ~(s->cluster_size - 1); for(cluster_offset = start; cluster_offset <= last; @@ -485,42 +443,33 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, { int block_index, refcount; int64_t cluster_index = cluster_offset >> s->cluster_bits; - int64_t new_block; + int64_t table_index = + cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT); - /* Only write refcount block to disk when we are done with it */ - old_table_index = table_index; - table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT); - if ((old_table_index >= 0) && (table_index != old_table_index)) { + /* Load the refcount block and allocate it if needed */ + if (table_index != old_table_index) { + if (refcount_block) { + ret = qcow2_cache_put(bs, s->refcount_block_cache, + (void**) &refcount_block); + if (ret < 0) { + goto fail; + } + } - ret = write_refcount_block_entries(bs, refcount_block_offset, - first_index, last_index); + ret = alloc_refcount_block(bs, cluster_index, &refcount_block); if (ret < 0) { - return ret; + goto fail; } - - first_index = -1; - last_index = -1; } + old_table_index = table_index; - /* Load the refcount block and allocate it if needed */ - new_block = alloc_refcount_block(bs, cluster_index); - if (new_block < 0) { - ret = new_block; - goto fail; - } - refcount_block_offset = new_block; + qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refcount_block); /* we can update the count and save it */ block_index = cluster_index & ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1); - if (first_index == -1 || block_index < first_index) { - first_index = block_index; - } - if (block_index > last_index) { - last_index = block_index; - } - refcount = be16_to_cpu(s->refcount_block_cache[block_index]); + refcount = be16_to_cpu(refcount_block[block_index]); refcount += addend; if (refcount < 0 || refcount > 0xffff) { ret = -EINVAL; @@ -529,17 +478,16 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, if (refcount == 0 && cluster_index < s->free_cluster_index) { s->free_cluster_index = cluster_index; } - s->refcount_block_cache[block_index] = cpu_to_be16(refcount); + refcount_block[block_index] = cpu_to_be16(refcount); } ret = 0; fail: - /* Write last changed block to disk */ - if (refcount_block_offset != 0) { + if (refcount_block) { int wret; - wret = write_refcount_block_entries(bs, refcount_block_offset, - first_index, last_index); + wret = qcow2_cache_put(bs, s->refcount_block_cache, + (void**) &refcount_block); if (wret < 0) { return ret < 0 ? ret : wret; } @@ -757,10 +705,8 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, BDRVQcowState *s = bs->opaque; uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, l1_allocated; int64_t old_offset, old_l2_offset; - int l2_size, i, j, l1_modified, l2_modified, nb_csectors, refcount; - - qcow2_l2_cache_reset(bs); - cache_refcount_updates = 1; + int i, j, l1_modified, nb_csectors, refcount; + int ret; l2_table = NULL; l1_table = NULL; @@ -783,17 +729,19 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, l1_allocated = 0; } - l2_size = s->l2_size * sizeof(uint64_t); - l2_table = qemu_malloc(l2_size); l1_modified = 0; for(i = 0; i < l1_size; i++) { l2_offset = l1_table[i]; if (l2_offset) { old_l2_offset = l2_offset; l2_offset &= ~QCOW_OFLAG_COPIED; - l2_modified = 0; - if (bdrv_pread(bs->file, l2_offset, l2_table, l2_size) != l2_size) + + ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, + (void**) &l2_table); + if (ret < 0) { goto fail; + } + for(j = 0; j < s->l2_size; j++) { offset = be64_to_cpu(l2_table[j]); if (offset != 0) { @@ -833,17 +781,22 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, offset |= QCOW_OFLAG_COPIED; } if (offset != old_offset) { + if (addend > 0) { + qcow2_cache_set_dependency(bs, s->l2_table_cache, + s->refcount_block_cache); + } l2_table[j] = cpu_to_be64(offset); - l2_modified = 1; + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); } } } - if (l2_modified) { - if (bdrv_pwrite_sync(bs->file, - l2_offset, l2_table, l2_size) < 0) - goto fail; + + ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + if (ret < 0) { + goto fail; } + if (addend != 0) { refcount = update_cluster_refcount(bs, l2_offset >> s->cluster_bits, addend); } else { @@ -871,16 +824,14 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, } if (l1_allocated) qemu_free(l1_table); - qemu_free(l2_table); - cache_refcount_updates = 0; - write_refcount_block(bs); return 0; fail: + if (l2_table) { + qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + } + if (l1_allocated) qemu_free(l1_table); - qemu_free(l2_table); - cache_refcount_updates = 0; - write_refcount_block(bs); return -EIO; } diff --git a/block/qcow2.c b/block/qcow2.c index b6b094c797..dbe4fdd44d 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -143,6 +143,7 @@ static int qcow2_open(BlockDriverState *bs, int flags) int len, i, ret = 0; QCowHeader header; uint64_t ext_end; + bool writethrough; ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); if (ret < 0) { @@ -217,8 +218,13 @@ static int qcow2_open(BlockDriverState *bs, int flags) be64_to_cpus(&s->l1_table[i]); } } - /* alloc L2 cache */ - s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t)); + + /* alloc L2 table/refcount block cache */ + writethrough = ((flags & BDRV_O_CACHE_MASK) == 0); + s->l2_table_cache = qcow2_cache_create(bs, L2_CACHE_SIZE, writethrough); + s->refcount_block_cache = qcow2_cache_create(bs, REFCOUNT_CACHE_SIZE, + writethrough); + s->cluster_cache = qemu_malloc(s->cluster_size); /* one more sector for decompressed data alignment */ s->cluster_data = qemu_malloc(QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size @@ -270,7 +276,9 @@ static int qcow2_open(BlockDriverState *bs, int flags) qcow2_free_snapshots(bs); qcow2_refcount_close(bs); qemu_free(s->l1_table); - qemu_free(s->l2_cache); + if (s->l2_table_cache) { + qcow2_cache_destroy(bs, s->l2_table_cache); + } qemu_free(s->cluster_cache); qemu_free(s->cluster_data); return ret; @@ -719,7 +727,13 @@ static void qcow2_close(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; qemu_free(s->l1_table); - qemu_free(s->l2_cache); + + qcow2_cache_flush(bs, s->l2_table_cache); + qcow2_cache_flush(bs, s->refcount_block_cache); + + qcow2_cache_destroy(bs, s->l2_table_cache); + qcow2_cache_destroy(bs, s->refcount_block_cache); + qemu_free(s->cluster_cache); qemu_free(s->cluster_data); qcow2_refcount_close(bs); @@ -1070,6 +1084,13 @@ static int qcow2_make_empty(BlockDriverState *bs) return 0; } +static int qcow2_discard(BlockDriverState *bs, int64_t sector_num, + int nb_sectors) +{ + return qcow2_discard_clusters(bs, sector_num << BDRV_SECTOR_BITS, + nb_sectors); +} + static int qcow2_truncate(BlockDriverState *bs, int64_t offset) { BDRVQcowState *s = bs->opaque; @@ -1179,6 +1200,19 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num, static int qcow2_flush(BlockDriverState *bs) { + BDRVQcowState *s = bs->opaque; + int ret; + + ret = qcow2_cache_flush(bs, s->l2_table_cache); + if (ret < 0) { + return ret; + } + + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret < 0) { + return ret; + } + return bdrv_flush(bs->file); } @@ -1186,6 +1220,19 @@ static BlockDriverAIOCB *qcow2_aio_flush(BlockDriverState *bs, BlockDriverCompletionFunc *cb, void *opaque) { + BDRVQcowState *s = bs->opaque; + int ret; + + ret = qcow2_cache_flush(bs, s->l2_table_cache); + if (ret < 0) { + return NULL; + } + + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret < 0) { + return NULL; + } + return bdrv_aio_flush(bs->file, cb, opaque); } @@ -1309,6 +1356,7 @@ static BlockDriver bdrv_qcow2 = { .bdrv_aio_writev = qcow2_aio_writev, .bdrv_aio_flush = qcow2_aio_flush, + .bdrv_discard = qcow2_discard, .bdrv_truncate = qcow2_truncate, .bdrv_write_compressed = qcow2_write_compressed, diff --git a/block/qcow2.h b/block/qcow2.h index 5217bea8a2..a019831838 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -51,6 +51,9 @@ #define L2_CACHE_SIZE 16 +/* Must be at least 4 to cover all cases of refcount table growth */ +#define REFCOUNT_CACHE_SIZE 4 + typedef struct QCowHeader { uint32_t magic; uint32_t version; @@ -78,6 +81,9 @@ typedef struct QCowSnapshot { uint64_t vm_clock_nsec; } QCowSnapshot; +struct Qcow2Cache; +typedef struct Qcow2Cache Qcow2Cache; + typedef struct BDRVQcowState { int cluster_bits; int cluster_size; @@ -91,9 +97,10 @@ typedef struct BDRVQcowState { uint64_t cluster_offset_mask; uint64_t l1_table_offset; uint64_t *l1_table; - uint64_t *l2_cache; - uint64_t l2_cache_offsets[L2_CACHE_SIZE]; - uint32_t l2_cache_counts[L2_CACHE_SIZE]; + + Qcow2Cache* l2_table_cache; + Qcow2Cache* refcount_block_cache; + uint8_t *cluster_cache; uint8_t *cluster_data; uint64_t cluster_cache_offset; @@ -102,8 +109,6 @@ typedef struct BDRVQcowState { uint64_t *refcount_table; uint64_t refcount_table_offset; uint32_t refcount_table_size; - uint64_t refcount_block_cache_offset; - uint16_t *refcount_block_cache; int64_t free_cluster_index; int64_t free_byte_offset; @@ -204,6 +209,8 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, int compressed_size); int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); +int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, + int nb_sectors); /* qcow2-snapshot.c functions */ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); @@ -215,4 +222,21 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name); void qcow2_free_snapshots(BlockDriverState *bs); int qcow2_read_snapshots(BlockDriverState *bs); +/* qcow2-cache.c functions */ +Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, + bool writethrough); +int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c); + +void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table); +int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c); +int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, + Qcow2Cache *dependency); +void qcow2_cache_depends_on_flush(Qcow2Cache *c); + +int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, + void **table); +int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, + void **table); +int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table); + #endif diff --git a/block/qed.c b/block/qed.c index 085c4f2210..32734486c5 100644 --- a/block/qed.c +++ b/block/qed.c @@ -469,6 +469,12 @@ static int qed_create(const char *filename, uint32_t cluster_size, return ret; } + /* File must start empty and grow, check truncate is supported */ + ret = bdrv_truncate(bs, 0); + if (ret < 0) { + goto out; + } + if (backing_file) { header.features |= QED_F_BACKING_FILE; header.backing_filename_offset = sizeof(le_header); @@ -971,6 +977,19 @@ static void qed_aio_write_prefill(void *opaque, int ret) } /** + * Check if the QED_F_NEED_CHECK bit should be set during allocating write + */ +static bool qed_should_set_need_check(BDRVQEDState *s) +{ + /* The flush before L2 update path ensures consistency */ + if (s->bs->backing_hd) { + return false; + } + + return !(s->header.features & QED_F_NEED_CHECK); +} + +/** * Write new data cluster * * @acb: Write request @@ -995,15 +1014,12 @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len) acb->cur_cluster = qed_alloc_clusters(s, acb->cur_nclusters); qemu_iovec_copy(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len); - /* Write new cluster if the image is already marked dirty */ - if (s->header.features & QED_F_NEED_CHECK) { + if (qed_should_set_need_check(s)) { + s->header.features |= QED_F_NEED_CHECK; + qed_write_header(s, qed_aio_write_prefill, acb); + } else { qed_aio_write_prefill(acb, 0); - return; } - - /* Mark the image dirty before writing the new cluster */ - s->header.features |= QED_F_NEED_CHECK; - qed_write_header(s, qed_aio_write_prefill, acb); } /** diff --git a/block/raw-win32.c b/block/raw-win32.c index 06c97101bb..c204a80d79 100644 --- a/block/raw-win32.c +++ b/block/raw-win32.c @@ -153,7 +153,7 @@ static int raw_flush(BlockDriverState *bs) int ret; ret = FlushFileBuffers(s->hfile); - if (ret != 0) { + if (ret == 0) { return -EIO; } diff --git a/block/sheepdog.c b/block/sheepdog.c index e62820a804..a54e0dee31 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1294,12 +1294,23 @@ static int do_sd_create(char *filename, int64_t vdi_size, static int sd_create(const char *filename, QEMUOptionParameter *options) { int ret; - uint32_t vid = 0; + uint32_t vid = 0, base_vid = 0; int64_t vdi_size = 0; char *backing_file = NULL; + BDRVSheepdogState s; + char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN]; + uint32_t snapid; strstart(filename, "sheepdog:", (const char **)&filename); + memset(&s, 0, sizeof(s)); + memset(vdi, 0, sizeof(vdi)); + memset(tag, 0, sizeof(tag)); + if (parse_vdiname(&s, filename, vdi, &snapid, tag) < 0) { + error_report("invalid filename\n"); + return -EINVAL; + } + while (options && options->name) { if (!strcmp(options->name, BLOCK_OPT_SIZE)) { vdi_size = options->value.n; @@ -1338,11 +1349,11 @@ static int sd_create(const char *filename, QEMUOptionParameter *options) return -EINVAL; } - vid = s->inode.vdi_id; + base_vid = s->inode.vdi_id; bdrv_delete(bs); } - return do_sd_create((char *)filename, vdi_size, vid, NULL, 0, NULL, NULL); + return do_sd_create((char *)vdi, vdi_size, base_vid, &vid, 0, s.addr, s.port); } static void sd_close(BlockDriverState *bs) diff --git a/block_int.h b/block_int.h index 12663e817d..6ebdc3eea2 100644 --- a/block_int.h +++ b/block_int.h @@ -153,7 +153,7 @@ struct BlockDriverState { int valid_key; /* if true, a valid encryption key has been set */ int sg; /* if true, the device is a /dev/sg* */ /* event callback when inserting/removing */ - void (*change_cb)(void *opaque); + void (*change_cb)(void *opaque, int reason); void *change_opaque; BlockDriver *drv; /* NULL means no media */ @@ -203,6 +203,9 @@ struct BlockDriverState { void *private; }; +#define CHANGE_MEDIA 0x01 +#define CHANGE_SIZE 0x02 + struct BlockDriverAIOCB { AIOPool *pool; BlockDriverState *bs; diff --git a/blockdev.c b/blockdev.c index d7add36929..1c56da0a16 100644 --- a/blockdev.c +++ b/blockdev.c @@ -19,6 +19,37 @@ static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives); +static const char *const if_name[IF_COUNT] = { + [IF_NONE] = "none", + [IF_IDE] = "ide", + [IF_SCSI] = "scsi", + [IF_FLOPPY] = "floppy", + [IF_PFLASH] = "pflash", + [IF_MTD] = "mtd", + [IF_SD] = "sd", + [IF_VIRTIO] = "virtio", + [IF_XEN] = "xen", +}; + +static const int if_max_devs[IF_COUNT] = { + /* + * Do not change these numbers! They govern how drive option + * index maps to unit and bus. That mapping is ABI. + * + * All controllers used to imlement if=T drives need to support + * if_max_devs[T] units, for any T with if_max_devs[T] != 0. + * Otherwise, some index values map to "impossible" bus, unit + * values. + * + * For instance, if you change [IF_SCSI] to 255, -drive + * if=scsi,index=12 no longer means bus=1,unit=5, but + * bus=0,unit=12. With an lsi53c895a controller (7 units max), + * the drive can't be set up. Regression. + */ + [IF_IDE] = 2, + [IF_SCSI] = 7, +}; + /* * We automatically delete the drive when a device using it gets * unplugged. Questionable feature, but we can't just drop it. @@ -44,20 +75,40 @@ void blockdev_auto_del(BlockDriverState *bs) } } -QemuOpts *drive_add(const char *file, const char *fmt, ...) +static int drive_index_to_bus_id(BlockInterfaceType type, int index) { - va_list ap; - char optstr[1024]; - QemuOpts *opts; + int max_devs = if_max_devs[type]; + return max_devs ? index / max_devs : 0; +} + +static int drive_index_to_unit_id(BlockInterfaceType type, int index) +{ + int max_devs = if_max_devs[type]; + return max_devs ? index % max_devs : index; +} - va_start(ap, fmt); - vsnprintf(optstr, sizeof(optstr), fmt, ap); - va_end(ap); +QemuOpts *drive_def(const char *optstr) +{ + return qemu_opts_parse(qemu_find_opts("drive"), optstr, 0); +} - opts = qemu_opts_parse(qemu_find_opts("drive"), optstr, 0); +QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, + const char *optstr) +{ + QemuOpts *opts; + char buf[32]; + + opts = drive_def(optstr); if (!opts) { return NULL; } + if (type != IF_DEFAULT) { + qemu_opt_set(opts, "if", if_name[type]); + } + if (index >= 0) { + snprintf(buf, sizeof(buf), "%d", index); + qemu_opt_set(opts, "index", buf); + } if (file) qemu_opt_set(opts, "file", file); return opts; @@ -79,6 +130,13 @@ DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit) return NULL; } +DriveInfo *drive_get_by_index(BlockInterfaceType type, int index) +{ + return drive_get(type, + drive_index_to_bus_id(type, index), + drive_index_to_unit_id(type, index)); +} + int drive_get_max_bus(BlockInterfaceType type) { int max_bus; @@ -93,6 +151,16 @@ int drive_get_max_bus(BlockInterfaceType type) return max_bus; } +/* Get a block device. This should only be used for single-drive devices + (e.g. SD/Floppy/MTD). Multi-disk devices (scsi/ide) should use the + appropriate bus. */ +DriveInfo *drive_get_next(BlockInterfaceType type) +{ + static int next_block_unit[IF_COUNT]; + + return drive_get(type, 0, next_block_unit[type]++); +} + DriveInfo *drive_get_by_blockdev(BlockDriverState *bs) { DriveInfo *dinfo; @@ -107,7 +175,7 @@ DriveInfo *drive_get_by_blockdev(BlockDriverState *bs) static void bdrv_format_print(void *opaque, const char *name) { - fprintf(stderr, " %s", name); + error_printf(" %s", name); } void drive_uninit(DriveInfo *dinfo) @@ -129,13 +197,13 @@ static int parse_block_error_action(const char *buf, int is_read) } else if (!strcmp(buf, "report")) { return BLOCK_ERR_REPORT; } else { - fprintf(stderr, "qemu: '%s' invalid %s error action\n", - buf, is_read ? "read" : "write"); + error_report("'%s' invalid %s error action", + buf, is_read ? "read" : "write"); return -1; } } -DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error) +DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) { const char *buf; const char *file = NULL; @@ -157,17 +225,13 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error) int snapshot = 0; int ret; - *fatal_error = 1; - translation = BIOS_ATA_TRANSLATION_AUTO; if (default_to_scsi) { type = IF_SCSI; - max_devs = MAX_SCSI_DEVS; pstrcpy(devname, sizeof(devname), "scsi"); } else { type = IF_IDE; - max_devs = MAX_IDE_DEVS; pstrcpy(devname, sizeof(devname), "ide"); } media = MEDIA_DISK; @@ -189,59 +253,34 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error) if ((buf = qemu_opt_get(opts, "if")) != NULL) { pstrcpy(devname, sizeof(devname), buf); - if (!strcmp(buf, "ide")) { - type = IF_IDE; - max_devs = MAX_IDE_DEVS; - } else if (!strcmp(buf, "scsi")) { - type = IF_SCSI; - max_devs = MAX_SCSI_DEVS; - } else if (!strcmp(buf, "floppy")) { - type = IF_FLOPPY; - max_devs = 0; - } else if (!strcmp(buf, "pflash")) { - type = IF_PFLASH; - max_devs = 0; - } else if (!strcmp(buf, "mtd")) { - type = IF_MTD; - max_devs = 0; - } else if (!strcmp(buf, "sd")) { - type = IF_SD; - max_devs = 0; - } else if (!strcmp(buf, "virtio")) { - type = IF_VIRTIO; - max_devs = 0; - } else if (!strcmp(buf, "xen")) { - type = IF_XEN; - max_devs = 0; - } else if (!strcmp(buf, "none")) { - type = IF_NONE; - max_devs = 0; - } else { - fprintf(stderr, "qemu: unsupported bus type '%s'\n", buf); + for (type = 0; type < IF_COUNT && strcmp(buf, if_name[type]); type++) + ; + if (type == IF_COUNT) { + error_report("unsupported bus type '%s'", buf); return NULL; } } + max_devs = if_max_devs[type]; if (cyls || heads || secs) { if (cyls < 1 || (type == IF_IDE && cyls > 16383)) { - fprintf(stderr, "qemu: '%s' invalid physical cyls number\n", buf); + error_report("invalid physical cyls number"); return NULL; } if (heads < 1 || (type == IF_IDE && heads > 16)) { - fprintf(stderr, "qemu: '%s' invalid physical heads number\n", buf); + error_report("invalid physical heads number"); return NULL; } if (secs < 1 || (type == IF_IDE && secs > 63)) { - fprintf(stderr, "qemu: '%s' invalid physical secs number\n", buf); + error_report("invalid physical secs number"); return NULL; } } if ((buf = qemu_opt_get(opts, "trans")) != NULL) { if (!cyls) { - fprintf(stderr, - "qemu: '%s' trans must be used with cyls,heads and secs\n", - buf); + error_report("'%s' trans must be used with cyls,heads and secs", + buf); return NULL; } if (!strcmp(buf, "none")) @@ -251,7 +290,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error) else if (!strcmp(buf, "auto")) translation = BIOS_ATA_TRANSLATION_AUTO; else { - fprintf(stderr, "qemu: '%s' invalid translation type\n", buf); + error_report("'%s' invalid translation type", buf); return NULL; } } @@ -261,13 +300,12 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error) media = MEDIA_DISK; } else if (!strcmp(buf, "cdrom")) { if (cyls || secs || heads) { - fprintf(stderr, - "qemu: '%s' invalid physical CHS format\n", buf); + error_report("'%s' invalid physical CHS format", buf); return NULL; } media = MEDIA_CDROM; } else { - fprintf(stderr, "qemu: '%s' invalid media\n", buf); + error_report("'%s' invalid media", buf); return NULL; } } @@ -283,7 +321,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error) } else if (!strcmp(buf, "writethrough")) { /* this is the default */ } else { - fprintf(stderr, "qemu: invalid cache option\n"); + error_report("invalid cache option"); return NULL; } } @@ -295,7 +333,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error) } else if (!strcmp(buf, "threads")) { /* this is the default */ } else { - fprintf(stderr, "qemu: invalid aio option\n"); + error_report("invalid aio option"); return NULL; } } @@ -303,14 +341,14 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error) if ((buf = qemu_opt_get(opts, "format")) != NULL) { if (strcmp(buf, "?") == 0) { - fprintf(stderr, "qemu: Supported formats:"); - bdrv_iterate_format(bdrv_format_print, NULL); - fprintf(stderr, "\n"); - return NULL; + error_printf("Supported formats:"); + bdrv_iterate_format(bdrv_format_print, NULL); + error_printf("\n"); + return NULL; } drv = bdrv_find_whitelisted_format(buf); if (!drv) { - fprintf(stderr, "qemu: '%s' invalid format\n", buf); + error_report("'%s' invalid format", buf); return NULL; } } @@ -318,7 +356,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error) on_write_error = BLOCK_ERR_STOP_ENOSPC; if ((buf = qemu_opt_get(opts, "werror")) != NULL) { if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) { - fprintf(stderr, "werror is not supported by this format\n"); + error_report("werror is not supported by this bus type"); return NULL; } @@ -331,7 +369,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error) on_read_error = BLOCK_ERR_REPORT; if ((buf = qemu_opt_get(opts, "rerror")) != NULL) { if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) { - fprintf(stderr, "rerror is not supported by this format\n"); + error_report("rerror is not supported by this bus type"); return NULL; } @@ -343,7 +381,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error) if ((devaddr = qemu_opt_get(opts, "addr")) != NULL) { if (type != IF_VIRTIO) { - fprintf(stderr, "addr is not supported\n"); + error_report("addr is not supported by this bus type"); return NULL; } } @@ -352,18 +390,11 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error) if (index != -1) { if (bus_id != 0 || unit_id != -1) { - fprintf(stderr, - "qemu: index cannot be used with bus and unit\n"); + error_report("index cannot be used with bus and unit"); return NULL; } - if (max_devs == 0) - { - unit_id = index; - bus_id = 0; - } else { - unit_id = index % max_devs; - bus_id = index / max_devs; - } + bus_id = drive_index_to_bus_id(type, index); + unit_id = drive_index_to_unit_id(type, index); } /* if user doesn't specify a unit_id, @@ -384,17 +415,18 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error) /* check unit id */ if (max_devs && unit_id >= max_devs) { - fprintf(stderr, "qemu: unit %d too big (max is %d)\n", - unit_id, max_devs - 1); + error_report("unit %d too big (max is %d)", + unit_id, max_devs - 1); return NULL; } /* - * ignore multiple definitions + * catch multiple definitions */ if (drive_get(type, bus_id, unit_id) != NULL) { - *fatal_error = 0; + error_report("drive with bus=%d, unit=%d (index=%d) exists", + bus_id, unit_id, index); return NULL; } @@ -461,12 +493,11 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error) if (devaddr) qemu_opt_set(opts, "addr", devaddr); break; - case IF_COUNT: + default: abort(); } if (!file || !*file) { - *fatal_error = 0; - return NULL; + return dinfo; } if (snapshot) { /* always use cache=unsafe with snapshot */ @@ -479,7 +510,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error) ro = 1; } else if (ro == 1) { if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY && type != IF_NONE) { - fprintf(stderr, "qemu: readonly flag not supported for drive with this interface\n"); + error_report("readonly not supported by this bus type"); return NULL; } } @@ -488,14 +519,13 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error) ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv); if (ret < 0) { - fprintf(stderr, "qemu: could not open disk image %s: %s\n", - file, strerror(-ret)); + error_report("could not open disk image %s: %s", + file, strerror(-ret)); return NULL; } if (bdrv_key_required(dinfo->bdrv)) autostart = 0; - *fatal_error = 0; return dinfo; } @@ -526,6 +556,12 @@ int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data) int ret = 0; int flags; + if (!filename) { + qerror_report(QERR_MISSING_PARAMETER, "snapshot_file"); + ret = -1; + goto out; + } + bs = bdrv_find(device); if (!bs) { qerror_report(QERR_DEVICE_NOT_FOUND, device); @@ -684,13 +720,15 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) /* clean up guest state from pointing to host resource by * finding and removing DeviceState "drive" property */ - for (prop = bs->peer->info->props; prop && prop->name; prop++) { - if (prop->info->type == PROP_TYPE_DRIVE) { - ptr = qdev_get_prop_ptr(bs->peer, prop); - if ((*ptr) == bs) { - bdrv_detach(bs, bs->peer); - *ptr = NULL; - break; + if (bs->peer) { + for (prop = bs->peer->info->props; prop && prop->name; prop++) { + if (prop->info->type == PROP_TYPE_DRIVE) { + ptr = qdev_get_prop_ptr(bs->peer, prop); + if (*ptr == bs) { + bdrv_detach(bs, bs->peer); + *ptr = NULL; + break; + } } } } @@ -700,3 +738,33 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) return 0; } + +/* + * XXX: replace the QERR_UNDEFINED_ERROR errors with real values once the + * existing QERR_ macro mess is cleaned up. A good example for better + * error reports can be found in the qemu-img resize code. + */ +int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + const char *device = qdict_get_str(qdict, "device"); + int64_t size = qdict_get_int(qdict, "size"); + BlockDriverState *bs; + + bs = bdrv_find(device); + if (!bs) { + qerror_report(QERR_DEVICE_NOT_FOUND, device); + return -1; + } + + if (size < 0) { + qerror_report(QERR_UNDEFINED_ERROR); + return -1; + } + + if (bdrv_truncate(bs, size)) { + qerror_report(QERR_UNDEFINED_ERROR); + return -1; + } + + return 0; +} diff --git a/blockdev.h b/blockdev.h index 4536b5cab1..84e462ab3f 100644 --- a/blockdev.h +++ b/blockdev.h @@ -18,6 +18,13 @@ void blockdev_auto_del(BlockDriverState *bs); #define BLOCK_SERIAL_STRLEN 20 +typedef enum { + IF_DEFAULT = -1, /* for use with drive_add() only */ + IF_NONE, + IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN, + IF_COUNT +} BlockInterfaceType; + struct DriveInfo { BlockDriverState *bdrv; char *id; @@ -31,16 +38,17 @@ struct DriveInfo { QTAILQ_ENTRY(DriveInfo) next; }; -#define MAX_IDE_DEVS 2 -#define MAX_SCSI_DEVS 255 - DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); +DriveInfo *drive_get_by_index(BlockInterfaceType type, int index); int drive_get_max_bus(BlockInterfaceType type); +DriveInfo *drive_get_next(BlockInterfaceType type); void drive_uninit(DriveInfo *dinfo); DriveInfo *drive_get_by_blockdev(BlockDriverState *bs); -QemuOpts *drive_add(const char *file, const char *fmt, ...) GCC_FMT_ATTR(2, 3); -DriveInfo *drive_init(QemuOpts *arg, int default_to_scsi, int *fatal_error); +QemuOpts *drive_def(const char *optstr); +QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, + const char *optstr); +DriveInfo *drive_init(QemuOpts *arg, int default_to_scsi); /* device-hotplug */ @@ -53,5 +61,6 @@ int do_change_block(Monitor *mon, const char *device, const char *filename, const char *fmt); int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data); int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data); +int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data); #endif @@ -907,7 +907,8 @@ echo " --enable-docs enable documentation build" echo " --disable-docs disable documentation build" echo " --disable-vhost-net disable vhost-net acceleration support" echo " --enable-vhost-net enable vhost-net acceleration support" -echo " --enable-trace-backend=B Trace backend nop simple ust dtrace" +echo " --enable-trace-backend=B Set trace backend" +echo " Available backends:" $("$source_path"/scripts/tracetool --list-backends) echo " --with-trace-file=NAME Full PATH,NAME of file to store traces" echo " Default:trace-<pid>" echo " --disable-spice disable spice" @@ -369,6 +369,7 @@ void vnc_display_init(DisplayState *ds); void vnc_display_close(DisplayState *ds); int vnc_display_open(DisplayState *ds, const char *display); int vnc_display_password(DisplayState *ds, const char *password); +int vnc_display_disable_login(DisplayState *ds); int vnc_display_pw_expire(DisplayState *ds, time_t expires); void do_info_vnc_print(Monitor *mon, const QObject *data); void do_info_vnc(Monitor *mon, QObject **ret_data); @@ -291,10 +291,11 @@ int fcntl_setfl(int fd, int flag) * value must be terminated by whitespace, ',' or '\0'. Return -1 on * error. */ -ssize_t strtosz_suffix(const char *nptr, char **end, const char default_suffix) +int64_t strtosz_suffix(const char *nptr, char **end, const char default_suffix) { - ssize_t retval = -1; - char *endptr, c, d; + int64_t retval = -1; + char *endptr; + unsigned char c, d; int mul_required = 0; double val, mul, integral, fraction; @@ -303,8 +304,8 @@ ssize_t strtosz_suffix(const char *nptr, char **end, const char default_suffix) if (isnan(val) || endptr == nptr || errno != 0) { goto fail; } - integral = modf(val, &fraction); - if (integral != 0) { + fraction = modf(val, &integral); + if (fraction != 0) { mul_required = 1; } /* @@ -314,7 +315,7 @@ ssize_t strtosz_suffix(const char *nptr, char **end, const char default_suffix) */ c = *endptr; d = c; - if (isspace(c) || c == '\0' || c == ',') { + if (qemu_isspace(c) || c == '\0' || c == ',') { c = 0; if (default_suffix) { d = default_suffix; @@ -322,32 +323,27 @@ ssize_t strtosz_suffix(const char *nptr, char **end, const char default_suffix) d = c; } } - switch (d) { - case 'B': - case 'b': + switch (qemu_toupper(d)) { + case STRTOSZ_DEFSUFFIX_B: mul = 1; if (mul_required) { goto fail; } break; - case 'K': - case 'k': + case STRTOSZ_DEFSUFFIX_KB: mul = 1 << 10; break; case 0: if (mul_required) { goto fail; } - case 'M': - case 'm': + case STRTOSZ_DEFSUFFIX_MB: mul = 1ULL << 20; break; - case 'G': - case 'g': + case STRTOSZ_DEFSUFFIX_GB: mul = 1ULL << 30; break; - case 'T': - case 't': + case STRTOSZ_DEFSUFFIX_TB: mul = 1ULL << 40; break; default: @@ -361,11 +357,11 @@ ssize_t strtosz_suffix(const char *nptr, char **end, const char default_suffix) */ if (c != 0) { endptr++; - if (!isspace(*endptr) && *endptr != ',' && *endptr != 0) { + if (!qemu_isspace(*endptr) && *endptr != ',' && *endptr != 0) { goto fail; } } - if ((val * mul >= ~(size_t)0) || val < 0) { + if ((val * mul >= INT64_MAX) || val < 0) { goto fail; } retval = val * mul; @@ -378,7 +374,7 @@ fail: return retval; } -ssize_t strtosz(const char *nptr, char **end) +int64_t strtosz(const char *nptr, char **end) { return strtosz_suffix(nptr, end, STRTOSZ_DEFSUFFIX_MB); } diff --git a/docs/qdev-device-use.txt b/docs/qdev-device-use.txt index f2f9b757a5..4bb2be8850 100644 --- a/docs/qdev-device-use.txt +++ b/docs/qdev-device-use.txt @@ -80,7 +80,11 @@ The -device argument differs in detail for each kind of drive: This SCSI controller a single SCSI bus, named ID.0. Put a disk on it: - -device scsi-disk,drive=DRIVE-ID,bus=ID.0,scsi-id=SCSI-ID + -device scsi-disk,drive=DRIVE-ID,bus=ID.0,scsi-id=SCSI-ID,removable=RMB + + The (optional) removable parameter lets you override the SCSI INQUIRY + removable (RMB) bit for non CD-ROM devices. It is ignored for CD-ROM devices + which are always removable. RMB is "on" or "off". * if=floppy @@ -116,7 +120,12 @@ For USB devices, the old way is actually different: Provides much less control than -drive's HOST-OPTS... The new way fixes that: - -device usb-storage,drive=DRIVE-ID + -device usb-storage,drive=DRIVE-ID,removable=RMB + +The removable parameter gives control over the SCSI INQUIRY removable (RMB) +bit. USB thumbdrives usually set removable=on, while USB hard disks set +removable=off. See the if=scsi description above for details on the removable +parameter, which applies only to scsi-disk devices and not to scsi-generic. === Character Devices === diff --git a/docs/tracing.txt b/docs/tracing.txt index 963c5047fe..21183f9a68 100644 --- a/docs/tracing.txt +++ b/docs/tracing.txt @@ -133,6 +133,11 @@ source tree. It may not be as powerful as platform-specific or third-party trace backends but it is portable. This is the recommended trace backend unless you have specific needs for more advanced backends. +=== Stderr === + +The "stderr" backend sends trace events directly to standard error output +during emulation. + ==== Monitor commands ==== * info trace diff --git a/hmp-commands.hx b/hmp-commands.hx index 1cea572b15..8df4adf831 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -53,6 +53,25 @@ Quit the emulator. ETEXI { + .name = "block_resize", + .args_type = "device:B,size:o", + .params = "device size", + .help = "resize a block image", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_block_resize, + }, + +STEXI +@item block_resize +@findex block_resize +Resize a block image while a guest is running. Usually requires guest +action to see the updated size. Resize to a lower size is supported, +but should be used with extreme caution. Note that this command only +resizes image files, it can not resize block devices like LVM volumes. +ETEXI + + + { .name = "eject", .args_type = "force:-f,device:B", .params = "[-f] device", diff --git a/hw/device-hotplug.c b/hw/device-hotplug.c index 9704e2feb2..8b2ed7a492 100644 --- a/hw/device-hotplug.c +++ b/hw/device-hotplug.c @@ -29,15 +29,14 @@ DriveInfo *add_init_drive(const char *optstr) { - int fatal_error; DriveInfo *dinfo; QemuOpts *opts; - opts = drive_add(NULL, "%s", optstr); + opts = drive_def(optstr); if (!opts) return NULL; - dinfo = drive_init(opts, current_machine->use_scsi, &fatal_error); + dinfo = drive_init(opts, current_machine->use_scsi); if (!dinfo) { qemu_opts_del(opts); return NULL; @@ -74,18 +74,15 @@ void ecc_reset(ECCState *s) } /* Save/restore */ -void ecc_put(QEMUFile *f, ECCState *s) -{ - qemu_put_8s(f, &s->cp); - qemu_put_be16s(f, &s->lp[0]); - qemu_put_be16s(f, &s->lp[1]); - qemu_put_be16s(f, &s->count); -} - -void ecc_get(QEMUFile *f, ECCState *s) -{ - qemu_get_8s(f, &s->cp); - qemu_get_be16s(f, &s->lp[0]); - qemu_get_be16s(f, &s->lp[1]); - qemu_get_be16s(f, &s->count); -} +VMStateDescription vmstate_ecc_state = { + .name = "ecc-state", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField []) { + VMSTATE_UINT8(cp, ECCState), + VMSTATE_UINT16_ARRAY(lp, ECCState, 2), + VMSTATE_UINT16(count, ECCState), + VMSTATE_END_OF_LIST(), + }, +}; @@ -369,14 +369,18 @@ static inline void set_txint(ChannelState *s) if (!s->rxint_under_svc) { s->txint_under_svc = 1; if (s->chn == chn_a) { - s->rregs[R_INTR] |= INTR_TXINTA; + if (s->wregs[W_INTR] & INTR_TXINT) { + s->rregs[R_INTR] |= INTR_TXINTA; + } if (s->wregs[W_MINTR] & MINTR_STATUSHI) s->otherchn->rregs[R_IVEC] = IVEC_HITXINTA; else s->otherchn->rregs[R_IVEC] = IVEC_LOTXINTA; } else { s->rregs[R_IVEC] = IVEC_TXINTB; - s->otherchn->rregs[R_INTR] |= INTR_TXINTB; + if (s->wregs[W_INTR] & INTR_TXINT) { + s->otherchn->rregs[R_INTR] |= INTR_TXINTB; + } } escc_update_irq(s); } diff --git a/hw/etraxfs_timer.c b/hw/etraxfs_timer.c index ba1adbe3c0..133741b4f5 100644 --- a/hw/etraxfs_timer.c +++ b/hw/etraxfs_timer.c @@ -100,7 +100,6 @@ static uint32_t timer_readl (void *opaque, target_phys_addr_t addr) return r; } -#define TIMER_SLOWDOWN 1 static void update_ctrl(struct etrax_timer *t, int tnum) { unsigned int op; @@ -142,9 +141,6 @@ static void update_ctrl(struct etrax_timer *t, int tnum) } D(printf ("freq_hz=%d div=%d\n", freq_hz, div)); - div = div * TIMER_SLOWDOWN; - div /= 1000; - freq_hz /= 1000; ptimer_set_freq(timer, freq_hz); ptimer_set_limit(timer, div, 0); diff --git a/hw/flash.h b/hw/flash.h index a80205c4e0..d7d103e66f 100644 --- a/hw/flash.h +++ b/hw/flash.h @@ -51,5 +51,4 @@ typedef struct { uint8_t ecc_digest(ECCState *s, uint8_t sample); void ecc_reset(ECCState *s); -void ecc_put(QEMUFile *f, ECCState *s); -void ecc_get(QEMUFile *f, ECCState *s); +extern VMStateDescription vmstate_ecc_state; diff --git a/hw/grlib.h b/hw/grlib.h new file mode 100644 index 0000000000..fdf4b1190a --- /dev/null +++ b/hw/grlib.h @@ -0,0 +1,126 @@ +/* + * QEMU GRLIB Components + * + * Copyright (c) 2010-2011 AdaCore + * + * 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. + */ + +#ifndef _GRLIB_H_ +#define _GRLIB_H_ + +#include "qdev.h" +#include "sysbus.h" + +/* Emulation of GrLib device is base on the GRLIB IP Core User's Manual: + * http://www.gaisler.com/products/grlib/grip.pdf + */ + +/* IRQMP */ + +typedef void (*set_pil_in_fn) (void *opaque, uint32_t pil_in); + +void grlib_irqmp_set_irq(void *opaque, int irq, int level); + +void grlib_irqmp_ack(DeviceState *dev, int intno); + +static inline +DeviceState *grlib_irqmp_create(target_phys_addr_t base, + CPUState *env, + qemu_irq **cpu_irqs, + uint32_t nr_irqs, + set_pil_in_fn set_pil_in) +{ + DeviceState *dev; + + assert(cpu_irqs != NULL); + + dev = qdev_create(NULL, "grlib,irqmp"); + qdev_prop_set_ptr(dev, "set_pil_in", set_pil_in); + qdev_prop_set_ptr(dev, "set_pil_in_opaque", env); + + if (qdev_init(dev)) { + return NULL; + } + + env->irq_manager = dev; + + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + + *cpu_irqs = qemu_allocate_irqs(grlib_irqmp_set_irq, + dev, + nr_irqs); + + return dev; +} + +/* GPTimer */ + +static inline +DeviceState *grlib_gptimer_create(target_phys_addr_t base, + uint32_t nr_timers, + uint32_t freq, + qemu_irq *cpu_irqs, + int base_irq) +{ + DeviceState *dev; + int i; + + dev = qdev_create(NULL, "grlib,gptimer"); + qdev_prop_set_uint32(dev, "nr-timers", nr_timers); + qdev_prop_set_uint32(dev, "frequency", freq); + qdev_prop_set_uint32(dev, "irq-line", base_irq); + + if (qdev_init(dev)) { + return NULL; + } + + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + + for (i = 0; i < nr_timers; i++) { + sysbus_connect_irq(sysbus_from_qdev(dev), i, cpu_irqs[base_irq + i]); + } + + return dev; +} + +/* APB UART */ + +static inline +DeviceState *grlib_apbuart_create(target_phys_addr_t base, + CharDriverState *serial, + qemu_irq irq) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "grlib,apbuart"); + qdev_prop_set_chr(dev, "chrdev", serial); + + if (qdev_init(dev)) { + return NULL; + } + + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + + return dev; +} + +#endif /* ! _GRLIB_H_ */ diff --git a/hw/grlib_apbuart.c b/hw/grlib_apbuart.c new file mode 100644 index 0000000000..101b150aa5 --- /dev/null +++ b/hw/grlib_apbuart.c @@ -0,0 +1,187 @@ +/* + * QEMU GRLIB APB UART Emulator + * + * Copyright (c) 2010-2011 AdaCore + * + * 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 "sysbus.h" +#include "qemu-char.h" + +#include "trace.h" + +#define UART_REG_SIZE 20 /* Size of memory mapped registers */ + +/* UART status register fields */ +#define UART_DATA_READY (1 << 0) +#define UART_TRANSMIT_SHIFT_EMPTY (1 << 1) +#define UART_TRANSMIT_FIFO_EMPTY (1 << 2) +#define UART_BREAK_RECEIVED (1 << 3) +#define UART_OVERRUN (1 << 4) +#define UART_PARITY_ERROR (1 << 5) +#define UART_FRAMING_ERROR (1 << 6) +#define UART_TRANSMIT_FIFO_HALF (1 << 7) +#define UART_RECEIVE_FIFO_HALF (1 << 8) +#define UART_TRANSMIT_FIFO_FULL (1 << 9) +#define UART_RECEIVE_FIFO_FULL (1 << 10) + +/* UART control register fields */ +#define UART_RECEIVE_ENABLE (1 << 0) +#define UART_TRANSMIT_ENABLE (1 << 1) +#define UART_RECEIVE_INTERRUPT (1 << 2) +#define UART_TRANSMIT_INTERRUPT (1 << 3) +#define UART_PARITY_SELECT (1 << 4) +#define UART_PARITY_ENABLE (1 << 5) +#define UART_FLOW_CONTROL (1 << 6) +#define UART_LOOPBACK (1 << 7) +#define UART_EXTERNAL_CLOCK (1 << 8) +#define UART_RECEIVE_FIFO_INTERRUPT (1 << 9) +#define UART_TRANSMIT_FIFO_INTERRUPT (1 << 10) +#define UART_FIFO_DEBUG_MODE (1 << 11) +#define UART_OUTPUT_ENABLE (1 << 12) +#define UART_FIFO_AVAILABLE (1 << 31) + +/* Memory mapped register offsets */ +#define DATA_OFFSET 0x00 +#define STATUS_OFFSET 0x04 +#define CONTROL_OFFSET 0x08 +#define SCALER_OFFSET 0x0C /* not supported */ +#define FIFO_DEBUG_OFFSET 0x10 /* not supported */ + +typedef struct UART { + SysBusDevice busdev; + + qemu_irq irq; + + CharDriverState *chr; + + /* registers */ + uint32_t receive; + uint32_t status; + uint32_t control; +} UART; + +static int grlib_apbuart_can_receive(void *opaque) +{ + UART *uart = opaque; + + return !!(uart->status & UART_DATA_READY); +} + +static void grlib_apbuart_receive(void *opaque, const uint8_t *buf, int size) +{ + UART *uart = opaque; + + uart->receive = *buf; + uart->status |= UART_DATA_READY; + + if (uart->control & UART_RECEIVE_INTERRUPT) { + qemu_irq_pulse(uart->irq); + } +} + +static void grlib_apbuart_event(void *opaque, int event) +{ + trace_grlib_apbuart_event(event); +} + +static void +grlib_apbuart_writel(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + UART *uart = opaque; + unsigned char c = 0; + + addr &= 0xff; + + /* Unit registers */ + switch (addr) { + case DATA_OFFSET: + c = value & 0xFF; + qemu_chr_write(uart->chr, &c, 1); + return; + + case STATUS_OFFSET: + /* Read Only */ + return; + + case CONTROL_OFFSET: + /* Not supported */ + return; + + case SCALER_OFFSET: + /* Not supported */ + return; + + default: + break; + } + + trace_grlib_apbuart_unknown_register("write", addr); +} + +static CPUReadMemoryFunc * const grlib_apbuart_read[] = { + NULL, NULL, NULL, +}; + +static CPUWriteMemoryFunc * const grlib_apbuart_write[] = { + NULL, NULL, grlib_apbuart_writel, +}; + +static int grlib_apbuart_init(SysBusDevice *dev) +{ + UART *uart = FROM_SYSBUS(typeof(*uart), dev); + int uart_regs = 0; + + qemu_chr_add_handlers(uart->chr, + grlib_apbuart_can_receive, + grlib_apbuart_receive, + grlib_apbuart_event, + uart); + + sysbus_init_irq(dev, &uart->irq); + + uart_regs = cpu_register_io_memory(grlib_apbuart_read, + grlib_apbuart_write, + uart, DEVICE_NATIVE_ENDIAN); + if (uart_regs < 0) { + return -1; + } + + sysbus_init_mmio(dev, UART_REG_SIZE, uart_regs); + + return 0; +} + +static SysBusDeviceInfo grlib_gptimer_info = { + .init = grlib_apbuart_init, + .qdev.name = "grlib,apbuart", + .qdev.size = sizeof(UART), + .qdev.props = (Property[]) { + DEFINE_PROP_CHR("chrdev", UART, chr), + DEFINE_PROP_END_OF_LIST() + } +}; + +static void grlib_gptimer_register(void) +{ + sysbus_register_withprop(&grlib_gptimer_info); +} + +device_init(grlib_gptimer_register) diff --git a/hw/grlib_gptimer.c b/hw/grlib_gptimer.c new file mode 100644 index 0000000000..596a9000a1 --- /dev/null +++ b/hw/grlib_gptimer.c @@ -0,0 +1,395 @@ +/* + * QEMU GRLIB GPTimer Emulator + * + * Copyright (c) 2010-2011 AdaCore + * + * 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 "sysbus.h" +#include "qemu-timer.h" + +#include "trace.h" + +#define UNIT_REG_SIZE 16 /* Size of memory mapped regs for the unit */ +#define GPTIMER_REG_SIZE 16 /* Size of memory mapped regs for a GPTimer */ + +#define GPTIMER_MAX_TIMERS 8 + +/* GPTimer Config register fields */ +#define GPTIMER_ENABLE (1 << 0) +#define GPTIMER_RESTART (1 << 1) +#define GPTIMER_LOAD (1 << 2) +#define GPTIMER_INT_ENABLE (1 << 3) +#define GPTIMER_INT_PENDING (1 << 4) +#define GPTIMER_CHAIN (1 << 5) /* Not supported */ +#define GPTIMER_DEBUG_HALT (1 << 6) /* Not supported */ + +/* Memory mapped register offsets */ +#define SCALER_OFFSET 0x00 +#define SCALER_RELOAD_OFFSET 0x04 +#define CONFIG_OFFSET 0x08 +#define COUNTER_OFFSET 0x00 +#define COUNTER_RELOAD_OFFSET 0x04 +#define TIMER_BASE 0x10 + +typedef struct GPTimer GPTimer; +typedef struct GPTimerUnit GPTimerUnit; + +struct GPTimer { + QEMUBH *bh; + struct ptimer_state *ptimer; + + qemu_irq irq; + int id; + GPTimerUnit *unit; + + /* registers */ + uint32_t counter; + uint32_t reload; + uint32_t config; +}; + +struct GPTimerUnit { + SysBusDevice busdev; + + uint32_t nr_timers; /* Number of timers available */ + uint32_t freq_hz; /* System frequency */ + uint32_t irq_line; /* Base irq line */ + + GPTimer *timers; + + /* registers */ + uint32_t scaler; + uint32_t reload; + uint32_t config; +}; + +static void grlib_gptimer_enable(GPTimer *timer) +{ + assert(timer != NULL); + + + ptimer_stop(timer->ptimer); + + if (!(timer->config & GPTIMER_ENABLE)) { + /* Timer disabled */ + trace_grlib_gptimer_disabled(timer->id, timer->config); + return; + } + + /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at + underflow. Set count + 1 to simulate the GPTimer behavior. */ + + trace_grlib_gptimer_enable(timer->id, timer->counter + 1); + + ptimer_set_count(timer->ptimer, timer->counter + 1); + ptimer_run(timer->ptimer, 1); +} + +static void grlib_gptimer_restart(GPTimer *timer) +{ + assert(timer != NULL); + + trace_grlib_gptimer_restart(timer->id, timer->reload); + + timer->counter = timer->reload; + grlib_gptimer_enable(timer); +} + +static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler) +{ + int i = 0; + uint32_t value = 0; + + assert(unit != NULL); + + if (scaler > 0) { + value = unit->freq_hz / (scaler + 1); + } else { + value = unit->freq_hz; + } + + trace_grlib_gptimer_set_scaler(scaler, value); + + for (i = 0; i < unit->nr_timers; i++) { + ptimer_set_freq(unit->timers[i].ptimer, value); + } +} + +static void grlib_gptimer_hit(void *opaque) +{ + GPTimer *timer = opaque; + assert(timer != NULL); + + trace_grlib_gptimer_hit(timer->id); + + /* Timer expired */ + + if (timer->config & GPTIMER_INT_ENABLE) { + /* Set the pending bit (only unset by write in the config register) */ + timer->config |= GPTIMER_INT_PENDING; + qemu_irq_pulse(timer->irq); + } + + if (timer->config & GPTIMER_RESTART) { + grlib_gptimer_restart(timer); + } +} + +static uint32_t grlib_gptimer_readl(void *opaque, target_phys_addr_t addr) +{ + GPTimerUnit *unit = opaque; + target_phys_addr_t timer_addr; + int id; + uint32_t value = 0; + + addr &= 0xff; + + /* Unit registers */ + switch (addr) { + case SCALER_OFFSET: + trace_grlib_gptimer_readl(-1, "scaler:", unit->scaler); + return unit->scaler; + + case SCALER_RELOAD_OFFSET: + trace_grlib_gptimer_readl(-1, "reload:", unit->reload); + return unit->reload; + + case CONFIG_OFFSET: + trace_grlib_gptimer_readl(-1, "config:", unit->config); + return unit->config; + + default: + break; + } + + timer_addr = (addr % TIMER_BASE); + id = (addr - TIMER_BASE) / TIMER_BASE; + + if (id >= 0 && id < unit->nr_timers) { + + /* GPTimer registers */ + switch (timer_addr) { + case COUNTER_OFFSET: + value = ptimer_get_count(unit->timers[id].ptimer); + trace_grlib_gptimer_readl(id, "counter value:", value); + return value; + + case COUNTER_RELOAD_OFFSET: + value = unit->timers[id].reload; + trace_grlib_gptimer_readl(id, "reload value:", value); + return value; + + case CONFIG_OFFSET: + trace_grlib_gptimer_readl(id, "scaler value:", + unit->timers[id].config); + return unit->timers[id].config; + + default: + break; + } + + } + + trace_grlib_gptimer_unknown_register("read", addr); + return 0; +} + +static void +grlib_gptimer_writel(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + GPTimerUnit *unit = opaque; + target_phys_addr_t timer_addr; + int id; + + addr &= 0xff; + + /* Unit registers */ + switch (addr) { + case SCALER_OFFSET: + value &= 0xFFFF; /* clean up the value */ + unit->scaler = value; + trace_grlib_gptimer_writel(-1, "scaler:", unit->scaler); + return; + + case SCALER_RELOAD_OFFSET: + value &= 0xFFFF; /* clean up the value */ + unit->reload = value; + trace_grlib_gptimer_writel(-1, "reload:", unit->reload); + grlib_gptimer_set_scaler(unit, value); + return; + + case CONFIG_OFFSET: + /* Read Only (disable timer freeze not supported) */ + trace_grlib_gptimer_writel(-1, "config (Read Only):", 0); + return; + + default: + break; + } + + timer_addr = (addr % TIMER_BASE); + id = (addr - TIMER_BASE) / TIMER_BASE; + + if (id >= 0 && id < unit->nr_timers) { + + /* GPTimer registers */ + switch (timer_addr) { + case COUNTER_OFFSET: + trace_grlib_gptimer_writel(id, "counter:", value); + unit->timers[id].counter = value; + grlib_gptimer_enable(&unit->timers[id]); + return; + + case COUNTER_RELOAD_OFFSET: + trace_grlib_gptimer_writel(id, "reload:", value); + unit->timers[id].reload = value; + return; + + case CONFIG_OFFSET: + trace_grlib_gptimer_writel(id, "config:", value); + + if (value & GPTIMER_INT_PENDING) { + /* clear pending bit */ + value &= ~GPTIMER_INT_PENDING; + } else { + /* keep pending bit */ + value |= unit->timers[id].config & GPTIMER_INT_PENDING; + } + + unit->timers[id].config = value; + + /* gptimer_restart calls gptimer_enable, so if "enable" and "load" + bits are present, we just have to call restart. */ + + if (value & GPTIMER_LOAD) { + grlib_gptimer_restart(&unit->timers[id]); + } else if (value & GPTIMER_ENABLE) { + grlib_gptimer_enable(&unit->timers[id]); + } + + /* These fields must always be read as 0 */ + value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT); + + unit->timers[id].config = value; + return; + + default: + break; + } + + } + + trace_grlib_gptimer_unknown_register("write", addr); +} + +static CPUReadMemoryFunc * const grlib_gptimer_read[] = { + NULL, NULL, grlib_gptimer_readl, +}; + +static CPUWriteMemoryFunc * const grlib_gptimer_write[] = { + NULL, NULL, grlib_gptimer_writel, +}; + +static void grlib_gptimer_reset(DeviceState *d) +{ + GPTimerUnit *unit = container_of(d, GPTimerUnit, busdev.qdev); + int i = 0; + + assert(unit != NULL); + + unit->scaler = 0; + unit->reload = 0; + unit->config = 0; + + unit->config = unit->nr_timers; + unit->config |= unit->irq_line << 3; + unit->config |= 1 << 8; /* separate interrupt */ + unit->config |= 1 << 9; /* Disable timer freeze */ + + + for (i = 0; i < unit->nr_timers; i++) { + GPTimer *timer = &unit->timers[i]; + + timer->counter = 0; + timer->reload = 0; + timer->config = 0; + ptimer_stop(timer->ptimer); + ptimer_set_count(timer->ptimer, 0); + ptimer_set_freq(timer->ptimer, unit->freq_hz); + } +} + +static int grlib_gptimer_init(SysBusDevice *dev) +{ + GPTimerUnit *unit = FROM_SYSBUS(typeof(*unit), dev); + unsigned int i; + int timer_regs; + + assert(unit->nr_timers > 0); + assert(unit->nr_timers <= GPTIMER_MAX_TIMERS); + + unit->timers = qemu_mallocz(sizeof unit->timers[0] * unit->nr_timers); + + for (i = 0; i < unit->nr_timers; i++) { + GPTimer *timer = &unit->timers[i]; + + timer->unit = unit; + timer->bh = qemu_bh_new(grlib_gptimer_hit, timer); + timer->ptimer = ptimer_init(timer->bh); + timer->id = i; + + /* One IRQ line for each timer */ + sysbus_init_irq(dev, &timer->irq); + + ptimer_set_freq(timer->ptimer, unit->freq_hz); + } + + timer_regs = cpu_register_io_memory(grlib_gptimer_read, + grlib_gptimer_write, + unit, DEVICE_NATIVE_ENDIAN); + if (timer_regs < 0) { + return -1; + } + + sysbus_init_mmio(dev, UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers, + timer_regs); + return 0; +} + +static SysBusDeviceInfo grlib_gptimer_info = { + .init = grlib_gptimer_init, + .qdev.name = "grlib,gptimer", + .qdev.reset = grlib_gptimer_reset, + .qdev.size = sizeof(GPTimerUnit), + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz, 40000000), + DEFINE_PROP_UINT32("irq-line", GPTimerUnit, irq_line, 8), + DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2), + DEFINE_PROP_END_OF_LIST() + } +}; + +static void grlib_gptimer_register(void) +{ + sysbus_register_withprop(&grlib_gptimer_info); +} + +device_init(grlib_gptimer_register) diff --git a/hw/grlib_irqmp.c b/hw/grlib_irqmp.c new file mode 100644 index 0000000000..f47c491a48 --- /dev/null +++ b/hw/grlib_irqmp.c @@ -0,0 +1,376 @@ +/* + * QEMU GRLIB IRQMP Emulator + * + * (Multiprocessor and extended interrupt not supported) + * + * Copyright (c) 2010-2011 AdaCore + * + * 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 "sysbus.h" +#include "cpu.h" + +#include "grlib.h" + +#include "trace.h" + +#define IRQMP_MAX_CPU 16 +#define IRQMP_REG_SIZE 256 /* Size of memory mapped registers */ + +/* Memory mapped register offsets */ +#define LEVEL_OFFSET 0x00 +#define PENDING_OFFSET 0x04 +#define FORCE0_OFFSET 0x08 +#define CLEAR_OFFSET 0x0C +#define MP_STATUS_OFFSET 0x10 +#define BROADCAST_OFFSET 0x14 +#define MASK_OFFSET 0x40 +#define FORCE_OFFSET 0x80 +#define EXTENDED_OFFSET 0xC0 + +typedef struct IRQMPState IRQMPState; + +typedef struct IRQMP { + SysBusDevice busdev; + + void *set_pil_in; + void *set_pil_in_opaque; + + IRQMPState *state; +} IRQMP; + +struct IRQMPState { + uint32_t level; + uint32_t pending; + uint32_t clear; + uint32_t broadcast; + + uint32_t mask[IRQMP_MAX_CPU]; + uint32_t force[IRQMP_MAX_CPU]; + uint32_t extended[IRQMP_MAX_CPU]; + + IRQMP *parent; +}; + +static void grlib_irqmp_check_irqs(IRQMPState *state) +{ + uint32_t pend = 0; + uint32_t level0 = 0; + uint32_t level1 = 0; + set_pil_in_fn set_pil_in; + + assert(state != NULL); + assert(state->parent != NULL); + + /* IRQ for CPU 0 (no SMP support) */ + pend = (state->pending | state->force[0]) + & state->mask[0]; + + level0 = pend & ~state->level; + level1 = pend & state->level; + + trace_grlib_irqmp_check_irqs(state->pending, state->force[0], + state->mask[0], level1, level0); + + set_pil_in = (set_pil_in_fn)state->parent->set_pil_in; + + /* Trigger level1 interrupt first and level0 if there is no level1 */ + if (level1 != 0) { + set_pil_in(state->parent->set_pil_in_opaque, level1); + } else { + set_pil_in(state->parent->set_pil_in_opaque, level0); + } +} + +void grlib_irqmp_ack(DeviceState *dev, int intno) +{ + SysBusDevice *sdev; + IRQMP *irqmp; + IRQMPState *state; + uint32_t mask; + + assert(dev != NULL); + + sdev = sysbus_from_qdev(dev); + assert(sdev != NULL); + + irqmp = FROM_SYSBUS(typeof(*irqmp), sdev); + assert(irqmp != NULL); + + state = irqmp->state; + assert(state != NULL); + + intno &= 15; + mask = 1 << intno; + + trace_grlib_irqmp_ack(intno); + + /* Clear registers */ + state->pending &= ~mask; + state->force[0] &= ~mask; /* Only CPU 0 (No SMP support) */ + + grlib_irqmp_check_irqs(state); +} + +void grlib_irqmp_set_irq(void *opaque, int irq, int level) +{ + IRQMP *irqmp; + IRQMPState *s; + int i = 0; + + assert(opaque != NULL); + + irqmp = FROM_SYSBUS(typeof(*irqmp), sysbus_from_qdev(opaque)); + assert(irqmp != NULL); + + s = irqmp->state; + assert(s != NULL); + assert(s->parent != NULL); + + + if (level) { + trace_grlib_irqmp_set_irq(irq); + + if (s->broadcast & 1 << irq) { + /* Broadcasted IRQ */ + for (i = 0; i < IRQMP_MAX_CPU; i++) { + s->force[i] |= 1 << irq; + } + } else { + s->pending |= 1 << irq; + } + grlib_irqmp_check_irqs(s); + + } +} + +static uint32_t grlib_irqmp_readl(void *opaque, target_phys_addr_t addr) +{ + IRQMP *irqmp = opaque; + IRQMPState *state; + + assert(irqmp != NULL); + state = irqmp->state; + assert(state != NULL); + + addr &= 0xff; + + /* global registers */ + switch (addr) { + case LEVEL_OFFSET: + return state->level; + + case PENDING_OFFSET: + return state->pending; + + case FORCE0_OFFSET: + /* This register is an "alias" for the force register of CPU 0 */ + return state->force[0]; + + case CLEAR_OFFSET: + case MP_STATUS_OFFSET: + /* Always read as 0 */ + return 0; + + case BROADCAST_OFFSET: + return state->broadcast; + + default: + break; + } + + /* mask registers */ + if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) { + int cpu = (addr - MASK_OFFSET) / 4; + assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); + + return state->mask[cpu]; + } + + /* force registers */ + if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) { + int cpu = (addr - FORCE_OFFSET) / 4; + assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); + + return state->force[cpu]; + } + + /* extended (not supported) */ + if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) { + int cpu = (addr - EXTENDED_OFFSET) / 4; + assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); + + return state->extended[cpu]; + } + + trace_grlib_irqmp_unknown_register("read", addr); + return 0; +} + +static void +grlib_irqmp_writel(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + IRQMP *irqmp = opaque; + IRQMPState *state; + + assert(irqmp != NULL); + state = irqmp->state; + assert(state != NULL); + + addr &= 0xff; + + /* global registers */ + switch (addr) { + case LEVEL_OFFSET: + value &= 0xFFFF << 1; /* clean up the value */ + state->level = value; + return; + + case PENDING_OFFSET: + /* Read Only */ + return; + + case FORCE0_OFFSET: + /* This register is an "alias" for the force register of CPU 0 */ + + value &= 0xFFFE; /* clean up the value */ + state->force[0] = value; + grlib_irqmp_check_irqs(irqmp->state); + return; + + case CLEAR_OFFSET: + value &= ~1; /* clean up the value */ + state->pending &= ~value; + return; + + case MP_STATUS_OFFSET: + /* Read Only (no SMP support) */ + return; + + case BROADCAST_OFFSET: + value &= 0xFFFE; /* clean up the value */ + state->broadcast = value; + return; + + default: + break; + } + + /* mask registers */ + if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) { + int cpu = (addr - MASK_OFFSET) / 4; + assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); + + value &= ~1; /* clean up the value */ + state->mask[cpu] = value; + grlib_irqmp_check_irqs(irqmp->state); + return; + } + + /* force registers */ + if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) { + int cpu = (addr - FORCE_OFFSET) / 4; + assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); + + uint32_t force = value & 0xFFFE; + uint32_t clear = (value >> 16) & 0xFFFE; + uint32_t old = state->force[cpu]; + + state->force[cpu] = (old | force) & ~clear; + grlib_irqmp_check_irqs(irqmp->state); + return; + } + + /* extended (not supported) */ + if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) { + int cpu = (addr - EXTENDED_OFFSET) / 4; + assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); + + value &= 0xF; /* clean up the value */ + state->extended[cpu] = value; + return; + } + + trace_grlib_irqmp_unknown_register("write", addr); +} + +static CPUReadMemoryFunc * const grlib_irqmp_read[] = { + NULL, NULL, &grlib_irqmp_readl, +}; + +static CPUWriteMemoryFunc * const grlib_irqmp_write[] = { + NULL, NULL, &grlib_irqmp_writel, +}; + +static void grlib_irqmp_reset(DeviceState *d) +{ + IRQMP *irqmp = container_of(d, IRQMP, busdev.qdev); + assert(irqmp != NULL); + assert(irqmp->state != NULL); + + memset(irqmp->state, 0, sizeof *irqmp->state); + irqmp->state->parent = irqmp; +} + +static int grlib_irqmp_init(SysBusDevice *dev) +{ + IRQMP *irqmp = FROM_SYSBUS(typeof(*irqmp), dev); + int irqmp_regs; + + assert(irqmp != NULL); + + /* Check parameters */ + if (irqmp->set_pil_in == NULL) { + return -1; + } + + irqmp_regs = cpu_register_io_memory(grlib_irqmp_read, + grlib_irqmp_write, + irqmp, DEVICE_NATIVE_ENDIAN); + + irqmp->state = qemu_mallocz(sizeof *irqmp->state); + + if (irqmp_regs < 0) { + return -1; + } + + sysbus_init_mmio(dev, IRQMP_REG_SIZE, irqmp_regs); + + return 0; +} + +static SysBusDeviceInfo grlib_irqmp_info = { + .init = grlib_irqmp_init, + .qdev.name = "grlib,irqmp", + .qdev.reset = grlib_irqmp_reset, + .qdev.size = sizeof(IRQMP), + .qdev.props = (Property[]) { + DEFINE_PROP_PTR("set_pil_in", IRQMP, set_pil_in), + DEFINE_PROP_PTR("set_pil_in_opaque", IRQMP, set_pil_in_opaque), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void grlib_irqmp_register(void) +{ + sysbus_register_withprop(&grlib_irqmp_info); +} + +device_init(grlib_irqmp_register) diff --git a/hw/gumstix.c b/hw/gumstix.c index af8b464b88..ee63f634cc 100644 --- a/hw/gumstix.c +++ b/hw/gumstix.c @@ -78,7 +78,7 @@ static void connex_init(ram_addr_t ram_size, /* Interrupt line of NIC is connected to GPIO line 36 */ smc91c111_init(&nd_table[0], 0x04000300, - pxa2xx_gpio_in_get(cpu->gpio)[36]); + qdev_get_gpio_in(cpu->gpio, 36)); } static void verdex_init(ram_addr_t ram_size, @@ -117,7 +117,7 @@ static void verdex_init(ram_addr_t ram_size, /* Interrupt line of NIC is connected to GPIO line 99 */ smc91c111_init(&nd_table[0], 0x04000300, - pxa2xx_gpio_in_get(cpu->gpio)[99]); + qdev_get_gpio_in(cpu->gpio, 99)); } static QEMUMachine connex_machine = { @@ -4,6 +4,8 @@ #include "isa.h" #include "pci.h" +#define MAX_IDE_DEVS 2 + /* ide-isa.c */ ISADevice *isa_ide_init(int iobase, int iobase2, int isairq, DriveInfo *hd0, DriveInfo *hd1); diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 968fdcecaf..671b4df7f6 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -70,7 +70,6 @@ #include "monitor.h" #include "dma.h" #include "cpu-common.h" -#include "blockdev.h" #include "internal.h" #include <hw/ide/pci.h> @@ -513,12 +512,12 @@ static void map_page(uint8_t **ptr, uint64_t addr, uint32_t wanted) target_phys_addr_t len = wanted; if (*ptr) { - cpu_physical_memory_unmap(*ptr, 1, len, len); + cpu_physical_memory_unmap(*ptr, len, 1, len); } *ptr = cpu_physical_memory_map(addr, &len, 1); if (len < wanted) { - cpu_physical_memory_unmap(*ptr, 1, len, len); + cpu_physical_memory_unmap(*ptr, len, 1, len); *ptr = NULL; } } @@ -956,7 +955,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis) ahci_trigger_irq(ad->hba, ad, PORT_IRQ_D2H_REG_FIS); if (cmd_mapped) { - cpu_physical_memory_unmap(cmd_fis, 0, cmd_len, cmd_len); + cpu_physical_memory_unmap(cmd_fis, cmd_len, 0, cmd_len); } } @@ -1002,7 +1001,7 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist) } out: - cpu_physical_memory_unmap(prdt, 0, prdt_len, prdt_len); + cpu_physical_memory_unmap(prdt, prdt_len, 0, prdt_len); return r; } @@ -1228,7 +1227,7 @@ static int handle_cmd(AHCIState *s, int port, int slot) } out: - cpu_physical_memory_unmap(cmd_fis, 1, cmd_len, cmd_len); + cpu_physical_memory_unmap(cmd_fis, cmd_len, 1, cmd_len); if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) { /* async command, complete later */ diff --git a/hw/ide/core.c b/hw/ide/core.c index 9496e990b9..dd63664c0d 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -321,14 +321,6 @@ static inline void ide_abort_command(IDEState *s) s->error = ABRT_ERR; } -static inline void ide_dma_submit_check(IDEState *s, - BlockDriverCompletionFunc *dma_cb) -{ - if (s->bus->dma->aiocb) - return; - dma_cb(s, -1); -} - /* prepare data transfer and tell what to do after */ static void ide_transfer_start(IDEState *s, uint8_t *buf, int size, EndTransferFunc *end_transfer_func) @@ -487,16 +479,19 @@ static int ide_handle_rw_error(IDEState *s, int error, int op) return 1; } -void ide_read_dma_cb(void *opaque, int ret) +void ide_dma_cb(void *opaque, int ret) { IDEState *s = opaque; int n; int64_t sector_num; +handle_rw_error: if (ret < 0) { - if (ide_handle_rw_error(s, -ret, - BM_STATUS_DMA_RETRY | BM_STATUS_RETRY_READ)) - { + int op = BM_STATUS_DMA_RETRY; + + if (s->is_read) + op |= BM_STATUS_RETRY_READ; + if (ide_handle_rw_error(s, -ret, op)) { return; } } @@ -504,7 +499,7 @@ void ide_read_dma_cb(void *opaque, int ret) n = s->io_buffer_size >> 9; sector_num = ide_get_sector(s); if (n > 0) { - dma_buf_commit(s, 1); + dma_buf_commit(s, s->is_read); sector_num += n; ide_set_sector(s, sector_num); s->nsector -= n; @@ -514,32 +509,47 @@ void ide_read_dma_cb(void *opaque, int ret) if (s->nsector == 0) { s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); - eot: - s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT); - ide_set_inactive(s); - return; + goto eot; } /* launch next transfer */ n = s->nsector; s->io_buffer_index = 0; s->io_buffer_size = n * 512; - if (s->bus->dma->ops->prepare_buf(s->bus->dma, 1) == 0) + if (s->bus->dma->ops->prepare_buf(s->bus->dma, s->is_read) == 0) goto eot; + #ifdef DEBUG_AIO - printf("aio_read: sector_num=%" PRId64 " n=%d\n", sector_num, n); + printf("ide_dma_cb: sector_num=%" PRId64 " n=%d, is_read=%d\n", + sector_num, n, s->is_read); #endif - s->bus->dma->aiocb = dma_bdrv_read(s->bs, &s->sg, sector_num, ide_read_dma_cb, s); - ide_dma_submit_check(s, ide_read_dma_cb); + + if (s->is_read) { + s->bus->dma->aiocb = dma_bdrv_read(s->bs, &s->sg, sector_num, + ide_dma_cb, s); + } else { + s->bus->dma->aiocb = dma_bdrv_write(s->bs, &s->sg, sector_num, + ide_dma_cb, s); + } + + if (!s->bus->dma->aiocb) { + ret = -1; + goto handle_rw_error; + } + return; + +eot: + s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT); + ide_set_inactive(s); } -static void ide_sector_read_dma(IDEState *s) +static void ide_sector_start_dma(IDEState *s, int is_read) { s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT; s->io_buffer_index = 0; s->io_buffer_size = 0; - s->is_read = 1; - s->bus->dma->ops->start_dma(s->bus->dma, s, ide_read_dma_cb); + s->is_read = is_read; + s->bus->dma->ops->start_dma(s->bus->dma, s, ide_dma_cb); } static void ide_sector_write_timer_cb(void *opaque) @@ -594,57 +604,6 @@ void ide_sector_write(IDEState *s) } } -void ide_write_dma_cb(void *opaque, int ret) -{ - IDEState *s = opaque; - int n; - int64_t sector_num; - - if (ret < 0) { - if (ide_handle_rw_error(s, -ret, BM_STATUS_DMA_RETRY)) - return; - } - - n = s->io_buffer_size >> 9; - sector_num = ide_get_sector(s); - if (n > 0) { - dma_buf_commit(s, 0); - sector_num += n; - ide_set_sector(s, sector_num); - s->nsector -= n; - } - - /* end of transfer ? */ - if (s->nsector == 0) { - s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); - eot: - s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT); - ide_set_inactive(s); - return; - } - - n = s->nsector; - s->io_buffer_size = n * 512; - /* launch next transfer */ - if (s->bus->dma->ops->prepare_buf(s->bus->dma, 0) == 0) - goto eot; -#ifdef DEBUG_AIO - printf("aio_write: sector_num=%" PRId64 " n=%d\n", sector_num, n); -#endif - s->bus->dma->aiocb = dma_bdrv_write(s->bs, &s->sg, sector_num, ide_write_dma_cb, s); - ide_dma_submit_check(s, ide_write_dma_cb); -} - -static void ide_sector_write_dma(IDEState *s) -{ - s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT; - s->io_buffer_index = 0; - s->io_buffer_size = 0; - s->is_read = 0; - s->bus->dma->ops->start_dma(s->bus->dma, s, ide_write_dma_cb); -} - void ide_atapi_cmd_ok(IDEState *s) { s->error = 0; @@ -1625,11 +1584,15 @@ static void ide_cfata_metadata_write(IDEState *s) } /* called when the inserted state of the media has changed */ -static void cdrom_change_cb(void *opaque) +static void cdrom_change_cb(void *opaque, int reason) { IDEState *s = opaque; uint64_t nb_sectors; + if (!(reason & CHANGE_MEDIA)) { + return; + } + bdrv_get_geometry(s->bs, &nb_sectors); s->nb_sectors = nb_sectors; @@ -1858,7 +1821,7 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) if (!s->bs) goto abort_cmd; ide_cmd_lba48_transform(s, lba48); - ide_sector_read_dma(s); + ide_sector_start_dma(s, 1); break; case WIN_WRITEDMA_EXT: lba48 = 1; @@ -1867,7 +1830,7 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) if (!s->bs) goto abort_cmd; ide_cmd_lba48_transform(s, lba48); - ide_sector_write_dma(s); + ide_sector_start_dma(s, 0); s->media_changed = 1; break; case WIN_READ_NATIVE_MAX_EXT: diff --git a/hw/ide/internal.h b/hw/ide/internal.h index 697c3b4dc1..d533fb63b3 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -439,7 +439,6 @@ struct IDEState { uint32_t mdata_size; uint8_t *mdata_storage; int media_changed; - /* for pmac */ int is_read; /* SMART */ uint8_t smart_enabled; @@ -560,8 +559,7 @@ void ide_init2_with_non_qdev_drives(IDEBus *bus, DriveInfo *hd0, void ide_init_ioport(IDEBus *bus, int iobase, int iobase2); void ide_exec_cmd(IDEBus *bus, uint32_t val); -void ide_read_dma_cb(void *opaque, int ret); -void ide_write_dma_cb(void *opaque, int ret); +void ide_dma_cb(void *opaque, int ret); void ide_sector_write(IDEState *s); void ide_sector_read(IDEState *s); void ide_flush_cache(IDEState *s); diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 510b2de597..35168cb469 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -178,14 +178,9 @@ static void bmdma_restart_dma(BMDMAState *bm, int is_read) s->io_buffer_index = 0; s->io_buffer_size = 0; s->nsector = bm->nsector; + s->is_read = is_read; bm->cur_addr = bm->addr; - - if (is_read) { - bm->dma_cb = ide_read_dma_cb; - } else { - bm->dma_cb = ide_write_dma_cb; - } - + bm->dma_cb = ide_dma_cb; bmdma_start_dma(&bm->dma, s, bm->dma_cb); } @@ -272,9 +267,7 @@ static void bmdma_irq(void *opaque, int n, int level) return; } - if (bm) { - bm->status |= BM_STATUS_INT; - } + bm->status |= BM_STATUS_INT; /* trigger the real irq */ qemu_set_irq(bm->irq, level); diff --git a/hw/leon3.c b/hw/leon3.c new file mode 100644 index 0000000000..919f49fc1c --- /dev/null +++ b/hw/leon3.c @@ -0,0 +1,217 @@ +/* + * QEMU Leon3 System Emulator + * + * Copyright (c) 2010-2011 AdaCore + * + * 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 "hw.h" +#include "qemu-timer.h" +#include "qemu-char.h" +#include "sysemu.h" +#include "boards.h" +#include "loader.h" +#include "elf.h" +#include "trace.h" + +#include "grlib.h" + +/* Default system clock. */ +#define CPU_CLK (40 * 1000 * 1000) + +#define PROM_FILENAME "u-boot.bin" + +#define MAX_PILS 16 + +typedef struct ResetData { + CPUState *env; + uint32_t entry; /* save kernel entry in case of reset */ +} ResetData; + +static void main_cpu_reset(void *opaque) +{ + ResetData *s = (ResetData *)opaque; + CPUState *env = s->env; + + cpu_reset(env); + + env->halted = 0; + env->pc = s->entry; + env->npc = s->entry + 4; +} + +void leon3_irq_ack(void *irq_manager, int intno) +{ + grlib_irqmp_ack((DeviceState *)irq_manager, intno); +} + +static void leon3_set_pil_in(void *opaque, uint32_t pil_in) +{ + CPUState *env = (CPUState *)opaque; + + assert(env != NULL); + + env->pil_in = pil_in; + + if (env->pil_in && (env->interrupt_index == 0 || + (env->interrupt_index & ~15) == TT_EXTINT)) { + unsigned int i; + + for (i = 15; i > 0; i--) { + if (env->pil_in & (1 << i)) { + int old_interrupt = env->interrupt_index; + + env->interrupt_index = TT_EXTINT | i; + if (old_interrupt != env->interrupt_index) { + trace_leon3_set_irq(i); + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } + break; + } + } + } else if (!env->pil_in && (env->interrupt_index & ~15) == TT_EXTINT) { + trace_leon3_reset_irq(env->interrupt_index & 15); + env->interrupt_index = 0; + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + } +} + +static void leon3_generic_hw_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + CPUState *env; + ram_addr_t ram_offset, prom_offset; + int ret; + char *filename; + qemu_irq *cpu_irqs = NULL; + int bios_size; + int prom_size; + ResetData *reset_info; + + /* Init CPU */ + if (!cpu_model) { + cpu_model = "LEON3"; + } + + env = cpu_init(cpu_model); + if (!env) { + fprintf(stderr, "qemu: Unable to find Sparc CPU definition\n"); + exit(1); + } + + cpu_sparc_set_id(env, 0); + + /* Reset data */ + reset_info = qemu_mallocz(sizeof(ResetData)); + reset_info->env = env; + qemu_register_reset(main_cpu_reset, reset_info); + + /* Allocate IRQ manager */ + grlib_irqmp_create(0x80000200, env, &cpu_irqs, MAX_PILS, &leon3_set_pil_in); + + env->qemu_irq_ack = leon3_irq_manager; + + /* Allocate RAM */ + if ((uint64_t)ram_size > (1UL << 30)) { + fprintf(stderr, + "qemu: Too much memory for this machine: %d, maximum 1G\n", + (unsigned int)(ram_size / (1024 * 1024))); + exit(1); + } + + ram_offset = qemu_ram_alloc(NULL, "leon3.ram", ram_size); + cpu_register_physical_memory(0x40000000, ram_size, ram_offset | IO_MEM_RAM); + + /* Allocate BIOS */ + prom_size = 8 * 1024 * 1024; /* 8Mb */ + prom_offset = qemu_ram_alloc(NULL, "Leon3.bios", prom_size); + cpu_register_physical_memory(0x00000000, prom_size, + prom_offset | IO_MEM_ROM); + + /* Load boot prom */ + if (bios_name == NULL) { + bios_name = PROM_FILENAME; + } + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + + bios_size = get_image_size(filename); + + if (bios_size > prom_size) { + fprintf(stderr, "qemu: could not load prom '%s': file too big\n", + filename); + exit(1); + } + + if (bios_size > 0) { + ret = load_image_targphys(filename, 0x00000000, bios_size); + if (ret < 0 || ret > prom_size) { + fprintf(stderr, "qemu: could not load prom '%s'\n", filename); + exit(1); + } + } else if (kernel_filename == NULL) { + fprintf(stderr, "Can't read bios image %s\n", filename); + exit(1); + } + + /* Can directly load an application. */ + if (kernel_filename != NULL) { + long kernel_size; + uint64_t entry; + + kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, + 1 /* big endian */, ELF_MACHINE, 0); + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + if (bios_size <= 0) { + /* If there is no bios/monitor, start the application. */ + env->pc = entry; + env->npc = entry + 4; + reset_info->entry = entry; + } + } + + /* Allocate timers */ + grlib_gptimer_create(0x80000300, 2, CPU_CLK, cpu_irqs, 6); + + /* Allocate uart */ + if (serial_hds[0]) { + grlib_apbuart_create(0x80000100, serial_hds[0], cpu_irqs[3]); + } +} + +QEMUMachine leon3_generic_machine = { + .name = "leon3_generic", + .desc = "Leon-3 generic", + .init = leon3_generic_hw_init, + .use_scsi = 0, +}; + +static void leon3_machine_init(void) +{ + qemu_register_machine(&leon3_generic_machine); +} + +machine_init(leon3_machine_init); diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index 6466aff316..a1b0e31ee5 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -72,6 +72,7 @@ #define REG_B_UIE 0x10 #define REG_B_SQWE 0x08 #define REG_B_DM 0x04 +#define REG_B_24H 0x02 #define REG_C_UF 0x10 #define REG_C_IRQF 0x80 @@ -246,7 +247,15 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data) rtc_set_time(s); } } - s->cmos_data[RTC_REG_B] = data; + if (((s->cmos_data[RTC_REG_B] ^ data) & (REG_B_DM | REG_B_24H)) && + !(data & REG_B_SET)) { + /* If the time format has changed and not in set mode, + update the registers immediately. */ + s->cmos_data[RTC_REG_B] = data; + rtc_copy_date(s); + } else { + s->cmos_data[RTC_REG_B] = data; + } rtc_timer_update(s, qemu_get_clock(rtc_clock)); break; case RTC_REG_C: @@ -285,7 +294,7 @@ static void rtc_set_time(RTCState *s) tm->tm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]); tm->tm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]); tm->tm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f); - if (!(s->cmos_data[RTC_REG_B] & 0x02) && + if (!(s->cmos_data[RTC_REG_B] & REG_B_24H) && (s->cmos_data[RTC_HOURS] & 0x80)) { tm->tm_hour += 12; } @@ -304,7 +313,7 @@ static void rtc_copy_date(RTCState *s) s->cmos_data[RTC_SECONDS] = rtc_to_bcd(s, tm->tm_sec); s->cmos_data[RTC_MINUTES] = rtc_to_bcd(s, tm->tm_min); - if (s->cmos_data[RTC_REG_B] & 0x02) { + if (s->cmos_data[RTC_REG_B] & REG_B_24H) { /* 24 hour format */ s->cmos_data[RTC_HOURS] = rtc_to_bcd(s, tm->tm_hour); } else { diff --git a/hw/mips_fulong2e.c b/hw/mips_fulong2e.c index 07eb9eeba1..2783ed5837 100644 --- a/hw/mips_fulong2e.c +++ b/hw/mips_fulong2e.c @@ -218,13 +218,11 @@ uint8_t eeprom_spd[0x80] = { }; /* Audio support */ -#ifdef HAS_AUDIO static void audio_init (PCIBus *pci_bus) { vt82c686b_ac97_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 5)); vt82c686b_mc97_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 6)); } -#endif /* Network support */ static void network_init (void) @@ -391,9 +389,7 @@ static void mips_fulong2e_init(ram_addr_t ram_size, const char *boot_device, } /* Sound card */ -#ifdef HAS_AUDIO audio_init(pci_bus); -#endif /* Network card */ network_init(); } diff --git a/hw/mips_jazz.c b/hw/mips_jazz.c index a7caa3f171..85eba5a69a 100644 --- a/hw/mips_jazz.c +++ b/hw/mips_jazz.c @@ -29,7 +29,7 @@ #include "isa.h" #include "fdc.h" #include "sysemu.h" -#include "audio/audio.h" +#include "arch_init.h" #include "boards.h" #include "net.h" #include "esp.h" @@ -90,26 +90,6 @@ static CPUWriteMemoryFunc * const dma_dummy_write[3] = { dma_dummy_writeb, }; -static void audio_init(qemu_irq *pic) -{ - struct soundhw *c; - int audio_enabled = 0; - - for (c = soundhw; !audio_enabled && c->name; ++c) { - audio_enabled = c->enabled; - } - - if (audio_enabled) { - for (c = soundhw; c->name; ++c) { - if (c->enabled) { - if (c->isa) { - c->init.init_isa(pic); - } - } - } - } -} - #define MAGNUM_BIOS_SIZE_MAX 0x7e000 #define MAGNUM_BIOS_SIZE (BIOS_SIZE < MAGNUM_BIOS_SIZE_MAX ? BIOS_SIZE : MAGNUM_BIOS_SIZE_MAX) @@ -284,7 +264,7 @@ void mips_jazz_init (ram_addr_t ram_size, /* Sound card */ /* FIXME: missing Jazz sound at 0x8000c000, rc4030[2] */ - audio_init(i8259); + audio_init(i8259, NULL); /* NVRAM: Unprotected at 0x9000, Protected at 0xa000, Read only at 0xb000 */ ds1225y_init(0x80009000, "nvram"); diff --git a/hw/mips_malta.c b/hw/mips_malta.c index 87554169c5..2d3f242cc8 100644 --- a/hw/mips_malta.c +++ b/hw/mips_malta.c @@ -37,7 +37,7 @@ #include "vmware_vga.h" #include "qemu-char.h" #include "sysemu.h" -#include "audio/audio.h" +#include "arch_init.h" #include "boards.h" #include "qemu-log.h" #include "mips-bios.h" @@ -457,25 +457,6 @@ static MaltaFPGAState *malta_fpga_init(target_phys_addr_t base, qemu_irq uart_ir return s; } -/* Audio support */ -static void audio_init (PCIBus *pci_bus) -{ - struct soundhw *c; - int audio_enabled = 0; - - for (c = soundhw; !audio_enabled && c->name; ++c) { - audio_enabled = c->enabled; - } - - if (audio_enabled) { - for (c = soundhw; c->name; ++c) { - if (c->enabled) { - c->init.init_pci(pci_bus); - } - } - } -} - /* Network support */ static void network_init(void) { @@ -967,7 +948,7 @@ void mips_malta_init (ram_addr_t ram_size, fdctrl_init_isa(fd); /* Sound card */ - audio_init(pci_bus); + audio_init(NULL, pci_bus); /* Network card */ network_init(); diff --git a/hw/onenand.c b/hw/onenand.c index d9cdcf2944..71c1ab40b4 100644 --- a/hw/onenand.c +++ b/hw/onenand.c @@ -19,6 +19,7 @@ */ #include "qemu-common.h" +#include "hw.h" #include "flash.h" #include "irq.h" #include "blockdev.h" @@ -829,23 +829,6 @@ static const int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 }; static const int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc }; static const int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 }; -void pc_audio_init (PCIBus *pci_bus, qemu_irq *pic) -{ - struct soundhw *c; - - for (c = soundhw; c->name; ++c) { - if (c->enabled) { - if (c->isa) { - c->init.init_isa(pic); - } else { - if (pci_bus) { - c->init.init_pci(pci_bus); - } - } - } - } -} - void pc_init_ne2k_isa(NICInfo *nd) { static int nb_ne2k = 0; @@ -100,9 +100,6 @@ void pc_basic_device_init(qemu_irq *isa_irq, FDCtrl **floppy_controller, ISADevice **rtc_state); void pc_init_ne2k_isa(NICInfo *nd); -#ifdef HAS_AUDIO -void pc_audio_init (PCIBus *pci_bus, qemu_irq *pic); -#endif void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, const char *boot_device, BusState *ide0, BusState *ide1, diff --git a/hw/pc_piix.c b/hw/pc_piix.c index f82508d92f..7b744730f7 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -34,6 +34,7 @@ #include "kvm.h" #include "sysemu.h" #include "sysbus.h" +#include "arch_init.h" #include "blockdev.h" #define MAX_IDE_BUS 2 @@ -148,7 +149,7 @@ static void pc_init1(ram_addr_t ram_size, } } - pc_audio_init(pci_enabled ? pci_bus : NULL, isa_irq); + audio_init(isa_irq, pci_enabled ? pci_bus : NULL); pc_cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device, idebus[0], idebus[1], floppy_controller, rtc_state); diff --git a/hw/pci-hotplug.c b/hw/pci-hotplug.c index 716133c376..b6dcbda0ce 100644 --- a/hw/pci-hotplug.c +++ b/hw/pci-hotplug.c @@ -90,7 +90,8 @@ static int scsi_hot_add(Monitor *mon, DeviceState *adapter, * specified). */ dinfo->unit = qemu_opt_get_number(dinfo->opts, "unit", -1); - scsidev = scsi_bus_legacy_add_drive(scsibus, dinfo->bdrv, dinfo->unit); + dinfo->bus = scsibus->busnr; + scsidev = scsi_bus_legacy_add_drive(scsibus, dinfo->bdrv, dinfo->unit, false); if (!scsidev) { return -1; } @@ -641,7 +641,6 @@ static void pci_init_wmask_bridge(PCIDevice *d) PCI_BRIDGE_CTL_FAST_BACK | PCI_BRIDGE_CTL_DISCARD | PCI_BRIDGE_CTL_SEC_DISCARD | - PCI_BRIDGE_CTL_DISCARD_STATUS | PCI_BRIDGE_CTL_DISCARD_SERR); /* Below does not do anything as we never set this bit, put here for * completeness. */ @@ -833,6 +832,7 @@ static int pci_unregister_device(DeviceState *dev) pci_unregister_io_regions(pci_dev); pci_del_option_rom(pci_dev); + qemu_free(pci_dev->romfile); do_pci_unregister_device(pci_dev); return 0; } @@ -2072,7 +2072,7 @@ static char *pcibus_get_dev_path(DeviceState *dev) for (t = d; t; t = t->bus->parent_dev) { p -= slot_len; s = snprintf(slot, sizeof slot, ":%02x.%x", - PCI_SLOT(t->devfn), PCI_FUNC(d->devfn)); + PCI_SLOT(t->devfn), PCI_FUNC(t->devfn)); assert(s == slot_len); memcpy(p, slot, slot_len); } diff --git a/hw/pl181.c b/hw/pl181.c index 3e5f92f0e7..36d9d02d6a 100644 --- a/hw/pl181.c +++ b/hw/pl181.c @@ -7,6 +7,7 @@ * This code is licenced under the GPL. */ +#include "blockdev.h" #include "sysbus.h" #include "sd.h" @@ -449,15 +450,15 @@ static int pl181_init(SysBusDevice *dev) { int iomemtype; pl181_state *s = FROM_SYSBUS(pl181_state, dev); - BlockDriverState *bd; + DriveInfo *dinfo; iomemtype = cpu_register_io_memory(pl181_readfn, pl181_writefn, s, DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); sysbus_init_irq(dev, &s->irq[0]); sysbus_init_irq(dev, &s->irq[1]); - bd = qdev_init_bdrv(&dev->qdev, IF_SD); - s->card = sd_init(bd, 0); + dinfo = drive_get_next(IF_SD); + s->card = sd_init(dinfo ? dinfo->bdrv : NULL, 0); qemu_register_reset(pl181_reset, s); pl181_reset(s); /* ??? Save/restore. */ diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c index 1492266267..6c1499a780 100644 --- a/hw/ppc_prep.c +++ b/hw/ppc_prep.c @@ -600,9 +600,6 @@ static void ppc_prep_init (ram_addr_t ram_size, if (filename) { qemu_free(filename); } - if (env->nip < 0xFFF80000 && bios_size < 0x00100000) { - hw_error("PowerPC 601 / 620 / 970 need a 1MB BIOS\n"); - } if (linux_boot) { kernel_base = KERNEL_LOAD_ADDR; @@ -693,7 +690,7 @@ static void ppc_prep_init (ram_addr_t ram_size, hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); } - for(i = 0; i < MAX_IDE_BUS; i++) { + for(i = 0; i < 1/*MAX_IDE_BUS*/; i++) { isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i], hd[2 * i], hd[2 * i + 1]); @@ -70,13 +70,9 @@ void pxa25x_timer_init(target_phys_addr_t base, qemu_irq *irqs); void pxa27x_timer_init(target_phys_addr_t base, qemu_irq *irqs, qemu_irq irq4); /* pxa2xx_gpio.c */ -typedef struct PXA2xxGPIOInfo PXA2xxGPIOInfo; -PXA2xxGPIOInfo *pxa2xx_gpio_init(target_phys_addr_t base, +DeviceState *pxa2xx_gpio_init(target_phys_addr_t base, CPUState *env, qemu_irq *pic, int lines); -qemu_irq *pxa2xx_gpio_in_get(PXA2xxGPIOInfo *s); -void pxa2xx_gpio_out_set(PXA2xxGPIOInfo *s, - int line, qemu_irq handler); -void pxa2xx_gpio_read_notifier(PXA2xxGPIOInfo *s, qemu_irq handler); +void pxa2xx_gpio_read_notifier(DeviceState *dev, qemu_irq handler); /* pxa2xx_dma.c */ typedef struct PXA2xxDMAState PXA2xxDMAState; @@ -132,7 +128,7 @@ typedef struct { qemu_irq *pic; qemu_irq reset; PXA2xxDMAState *dma; - PXA2xxGPIOInfo *gpio; + DeviceState *gpio; PXA2xxLCDState *lcd; SSIBus **ssp; PXA2xxI2CState *i2c[2]; diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c index 6e72a5c753..d966846f94 100644 --- a/hw/pxa2xx.c +++ b/hw/pxa2xx.c @@ -2158,7 +2158,7 @@ PXA2xxState *pxa270_init(unsigned int sdram_size, const char *revision) /* GPIO1 resets the processor */ /* The handler can be overridden by board-specific code */ - pxa2xx_gpio_out_set(s->gpio, 1, s->reset); + qdev_connect_gpio_out(s->gpio, 1, s->reset); return s; } @@ -2279,7 +2279,7 @@ PXA2xxState *pxa255_init(unsigned int sdram_size) /* GPIO1 resets the processor */ /* The handler can be overridden by board-specific code */ - pxa2xx_gpio_out_set(s->gpio, 1, s->reset); + qdev_connect_gpio_out(s->gpio, 1, s->reset); return s; } diff --git a/hw/pxa2xx_gpio.c b/hw/pxa2xx_gpio.c index 0d034462d2..789965d88b 100644 --- a/hw/pxa2xx_gpio.c +++ b/hw/pxa2xx_gpio.c @@ -8,15 +8,18 @@ */ #include "hw.h" +#include "sysbus.h" #include "pxa.h" #define PXA2XX_GPIO_BANKS 4 +typedef struct PXA2xxGPIOInfo PXA2xxGPIOInfo; struct PXA2xxGPIOInfo { - qemu_irq *pic; + SysBusDevice busdev; + qemu_irq irq0, irq1, irqX; int lines; + int ncpu; CPUState *cpu_env; - qemu_irq *in; /* XXX: GNU C vectors are more suitable */ uint32_t ilevel[PXA2XX_GPIO_BANKS]; @@ -66,19 +69,19 @@ static struct { static void pxa2xx_gpio_irq_update(PXA2xxGPIOInfo *s) { if (s->status[0] & (1 << 0)) - qemu_irq_raise(s->pic[PXA2XX_PIC_GPIO_0]); + qemu_irq_raise(s->irq0); else - qemu_irq_lower(s->pic[PXA2XX_PIC_GPIO_0]); + qemu_irq_lower(s->irq0); if (s->status[0] & (1 << 1)) - qemu_irq_raise(s->pic[PXA2XX_PIC_GPIO_1]); + qemu_irq_raise(s->irq1); else - qemu_irq_lower(s->pic[PXA2XX_PIC_GPIO_1]); + qemu_irq_lower(s->irq1); if ((s->status[0] & ~3) | s->status[1] | s->status[2] | s->status[3]) - qemu_irq_raise(s->pic[PXA2XX_PIC_GPIO_X]); + qemu_irq_raise(s->irqX); else - qemu_irq_lower(s->pic[PXA2XX_PIC_GPIO_X]); + qemu_irq_lower(s->irqX); } /* Bitmap of pins used as standby and sleep wake-up sources. */ @@ -249,96 +252,89 @@ static CPUWriteMemoryFunc * const pxa2xx_gpio_writefn[] = { pxa2xx_gpio_write }; -static void pxa2xx_gpio_save(QEMUFile *f, void *opaque) -{ - PXA2xxGPIOInfo *s = (PXA2xxGPIOInfo *) opaque; - int i; - - qemu_put_be32(f, s->lines); - - for (i = 0; i < PXA2XX_GPIO_BANKS; i ++) { - qemu_put_be32s(f, &s->ilevel[i]); - qemu_put_be32s(f, &s->olevel[i]); - qemu_put_be32s(f, &s->dir[i]); - qemu_put_be32s(f, &s->rising[i]); - qemu_put_be32s(f, &s->falling[i]); - qemu_put_be32s(f, &s->status[i]); - qemu_put_be32s(f, &s->gafr[i * 2 + 0]); - qemu_put_be32s(f, &s->gafr[i * 2 + 1]); - - qemu_put_be32s(f, &s->prev_level[i]); - } -} - -static int pxa2xx_gpio_load(QEMUFile *f, void *opaque, int version_id) +DeviceState *pxa2xx_gpio_init(target_phys_addr_t base, + CPUState *env, qemu_irq *pic, int lines) { - PXA2xxGPIOInfo *s = (PXA2xxGPIOInfo *) opaque; - int i; + DeviceState *dev; - if (qemu_get_be32(f) != s->lines) - return -EINVAL; + dev = qdev_create(NULL, "pxa2xx-gpio"); + qdev_prop_set_int32(dev, "lines", lines); + qdev_prop_set_int32(dev, "ncpu", env->cpu_index); + qdev_init_nofail(dev); - for (i = 0; i < PXA2XX_GPIO_BANKS; i ++) { - qemu_get_be32s(f, &s->ilevel[i]); - qemu_get_be32s(f, &s->olevel[i]); - qemu_get_be32s(f, &s->dir[i]); - qemu_get_be32s(f, &s->rising[i]); - qemu_get_be32s(f, &s->falling[i]); - qemu_get_be32s(f, &s->status[i]); - qemu_get_be32s(f, &s->gafr[i * 2 + 0]); - qemu_get_be32s(f, &s->gafr[i * 2 + 1]); - - qemu_get_be32s(f, &s->prev_level[i]); - } + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, pic[PXA2XX_PIC_GPIO_0]); + sysbus_connect_irq(sysbus_from_qdev(dev), 1, pic[PXA2XX_PIC_GPIO_1]); + sysbus_connect_irq(sysbus_from_qdev(dev), 2, pic[PXA2XX_PIC_GPIO_X]); - return 0; + return dev; } -PXA2xxGPIOInfo *pxa2xx_gpio_init(target_phys_addr_t base, - CPUState *env, qemu_irq *pic, int lines) +static int pxa2xx_gpio_initfn(SysBusDevice *dev) { int iomemtype; PXA2xxGPIOInfo *s; - s = (PXA2xxGPIOInfo *) - qemu_mallocz(sizeof(PXA2xxGPIOInfo)); - memset(s, 0, sizeof(PXA2xxGPIOInfo)); - s->pic = pic; - s->lines = lines; - s->cpu_env = env; - s->in = qemu_allocate_irqs(pxa2xx_gpio_set, s, lines); + s = FROM_SYSBUS(PXA2xxGPIOInfo, dev); - iomemtype = cpu_register_io_memory(pxa2xx_gpio_readfn, - pxa2xx_gpio_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(base, 0x00001000, iomemtype); + s->cpu_env = qemu_get_cpu(s->ncpu); - register_savevm(NULL, "pxa2xx_gpio", 0, 0, - pxa2xx_gpio_save, pxa2xx_gpio_load, s); - - return s; -} + qdev_init_gpio_in(&dev->qdev, pxa2xx_gpio_set, s->lines); + qdev_init_gpio_out(&dev->qdev, s->handler, s->lines); -qemu_irq *pxa2xx_gpio_in_get(PXA2xxGPIOInfo *s) -{ - return s->in; -} + iomemtype = cpu_register_io_memory(pxa2xx_gpio_readfn, + pxa2xx_gpio_writefn, s, DEVICE_NATIVE_ENDIAN); -void pxa2xx_gpio_out_set(PXA2xxGPIOInfo *s, - int line, qemu_irq handler) -{ - if (line >= s->lines) { - printf("%s: No GPIO pin %i\n", __FUNCTION__, line); - return; - } + sysbus_init_mmio(dev, 0x1000, iomemtype); + sysbus_init_irq(dev, &s->irq0); + sysbus_init_irq(dev, &s->irq1); + sysbus_init_irq(dev, &s->irqX); - s->handler[line] = handler; + return 0; } /* * Registers a callback to notify on GPLR reads. This normally * shouldn't be needed but it is used for the hack on Spitz machines. */ -void pxa2xx_gpio_read_notifier(PXA2xxGPIOInfo *s, qemu_irq handler) +void pxa2xx_gpio_read_notifier(DeviceState *dev, qemu_irq handler) { + PXA2xxGPIOInfo *s = FROM_SYSBUS(PXA2xxGPIOInfo, sysbus_from_qdev(dev)); s->read_notify = handler; } + +static const VMStateDescription vmstate_pxa2xx_gpio_regs = { + .name = "pxa2xx-gpio", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_INT32(lines, PXA2xxGPIOInfo), + VMSTATE_UINT32_ARRAY(ilevel, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), + VMSTATE_UINT32_ARRAY(olevel, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), + VMSTATE_UINT32_ARRAY(dir, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), + VMSTATE_UINT32_ARRAY(rising, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), + VMSTATE_UINT32_ARRAY(falling, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), + VMSTATE_UINT32_ARRAY(status, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), + VMSTATE_UINT32_ARRAY(gafr, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS * 2), + VMSTATE_END_OF_LIST(), + }, +}; + +static SysBusDeviceInfo pxa2xx_gpio_info = { + .init = pxa2xx_gpio_initfn, + .qdev.name = "pxa2xx-gpio", + .qdev.desc = "PXA2xx GPIO controller", + .qdev.size = sizeof(PXA2xxGPIOInfo), + .qdev.props = (Property []) { + DEFINE_PROP_INT32("lines", PXA2xxGPIOInfo, lines, 0), + DEFINE_PROP_INT32("ncpu", PXA2xxGPIOInfo, ncpu, 0), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void pxa2xx_gpio_register(void) +{ + sysbus_register_withprop(&pxa2xx_gpio_info); +} +device_init(pxa2xx_gpio_register); @@ -29,7 +29,6 @@ #include "qdev.h" #include "sysemu.h" #include "monitor.h" -#include "blockdev.h" static int qdev_hotplug = 0; static bool qdev_hot_added = false; @@ -458,20 +457,6 @@ void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) } } -static int next_block_unit[IF_COUNT]; - -/* Get a block device. This should only be used for single-drive devices - (e.g. SD/Floppy/MTD). Multi-disk devices (scsi/ide) should use the - appropriate bus. */ -BlockDriverState *qdev_init_bdrv(DeviceState *dev, BlockInterfaceType type) -{ - int unit = next_block_unit[type]++; - DriveInfo *dinfo; - - dinfo = drive_get(type, 0, unit); - return dinfo ? dinfo->bdrv : NULL; -} - BusState *qdev_get_child_bus(DeviceState *dev, const char *name) { BusState *bus; @@ -137,8 +137,6 @@ bool qdev_machine_modified(void); qemu_irq qdev_get_gpio_in(DeviceState *dev, int n); void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin); -BlockDriverState *qdev_init_bdrv(DeviceState *dev, BlockInterfaceType type); - BusState *qdev_get_child_bus(DeviceState *dev, const char *name); /*** Device API. ***/ diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 7febb86e77..ceeb4ecb91 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -87,7 +87,8 @@ void scsi_qdev_register(SCSIDeviceInfo *info) } /* handle legacy '-drive if=scsi,...' cmd line args */ -SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, int unit) +SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, + int unit, bool removable) { const char *driver; DeviceState *dev; @@ -95,6 +96,9 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, int driver = bdrv_is_sg(bdrv) ? "scsi-generic" : "scsi-disk"; dev = qdev_create(&bus->qbus, driver); qdev_prop_set_uint32(dev, "scsi-id", unit); + if (qdev_prop_exists(dev, "removable")) { + qdev_prop_set_bit(dev, "removable", removable); + } if (qdev_prop_set_drive(dev, "drive", bdrv) < 0) { qdev_free(dev); return NULL; @@ -117,7 +121,7 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus) continue; } qemu_opts_loc_restore(dinfo->opts); - if (!scsi_bus_legacy_add_drive(bus, dinfo->bdrv, unit)) { + if (!scsi_bus_legacy_add_drive(bus, dinfo->bdrv, unit, false)) { res = -1; break; } diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 6cb317c8f9..488eedd2cd 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -72,6 +72,7 @@ struct SCSIDiskState /* The qemu block layer uses a fixed 512 byte sector size. This is the number of 512 byte blocks in a single scsi sector. */ int cluster_size; + uint32_t removable; uint64_t max_lba; QEMUBH *bh; char *version; @@ -552,6 +553,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) memcpy(&outbuf[16], "QEMU CD-ROM ", 16); } else { outbuf[0] = 0; + outbuf[1] = s->removable ? 0x80 : 0; memcpy(&outbuf[16], "QEMU HARDDISK ", 16); } memcpy(&outbuf[8], "QEMU ", 8); @@ -1295,6 +1297,7 @@ static SCSIDeviceInfo scsi_disk_info = { DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), DEFINE_PROP_STRING("ver", SCSIDiskState, version), DEFINE_PROP_STRING("serial", SCSIDiskState, serial), + DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false), DEFINE_PROP_END_OF_LIST(), }, }; @@ -3,9 +3,10 @@ #include "qdev.h" #include "block.h" -#include "blockdev.h" #include "block_int.h" +#define MAX_SCSI_DEVS 255 + #define SCSI_CMD_BUF_SIZE 16 /* scsi-disk.c */ @@ -94,7 +95,8 @@ static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d) return DO_UPCAST(SCSIBus, qbus, d->qdev.parent_bus); } -SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, int unit); +SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, + int unit, bool removable); int scsi_bus_legacy_handle_cmdline(SCSIBus *bus); SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun); @@ -422,9 +422,14 @@ static void sd_reset(SDState *sd, BlockDriverState *bdrv) sd->pwd_len = 0; } -static void sd_cardchange(void *opaque) +static void sd_cardchange(void *opaque, int reason) { SDState *sd = opaque; + + if (!(reason & CHANGE_MEDIA)) { + return; + } + qemu_set_irq(sd->inserted_cb, bdrv_is_inserted(sd->bdrv)); if (bdrv_is_inserted(sd->bdrv)) { sd_reset(sd, sd->bdrv); diff --git a/hw/sh7750.c b/hw/sh7750.c index 36b702f628..19d5bf8537 100644 --- a/hw/sh7750.c +++ b/hw/sh7750.c @@ -625,6 +625,7 @@ static uint32_t invalid_read(void *opaque, target_phys_addr_t addr) static uint32_t sh7750_mmct_readl(void *opaque, target_phys_addr_t addr) { + SH7750State *s = opaque; uint32_t ret = 0; switch (MM_REGION_TYPE(addr)) { @@ -633,19 +634,21 @@ static uint32_t sh7750_mmct_readl(void *opaque, target_phys_addr_t addr) /* do nothing */ break; case MM_ITLB_ADDR: + ret = cpu_sh4_read_mmaped_itlb_addr(s->cpu, addr); + break; case MM_ITLB_DATA: - /* XXXXX */ - abort(); - break; + ret = cpu_sh4_read_mmaped_itlb_data(s->cpu, addr); + break; case MM_OCACHE_ADDR: case MM_OCACHE_DATA: /* do nothing */ break; case MM_UTLB_ADDR: + ret = cpu_sh4_read_mmaped_utlb_addr(s->cpu, addr); + break; case MM_UTLB_DATA: - /* XXXXX */ - abort(); - break; + ret = cpu_sh4_read_mmaped_utlb_data(s->cpu, addr); + break; default: abort(); } @@ -673,7 +676,7 @@ static void sh7750_mmct_writel(void *opaque, target_phys_addr_t addr, cpu_sh4_write_mmaped_itlb_addr(s->cpu, addr, mem_value); break; case MM_ITLB_DATA: - /* XXXXX */ + cpu_sh4_write_mmaped_itlb_data(s->cpu, addr, mem_value); abort(); break; case MM_OCACHE_ADDR: @@ -684,8 +687,7 @@ static void sh7750_mmct_writel(void *opaque, target_phys_addr_t addr, cpu_sh4_write_mmaped_utlb_addr(s->cpu, addr, mem_value); break; case MM_UTLB_DATA: - /* XXXXX */ - abort(); + cpu_sh4_write_mmaped_utlb_data(s->cpu, addr, mem_value); break; default: abort(); diff --git a/hw/sharpsl.h b/hw/sharpsl.h index c5ccf791f6..0b3a774f2f 100644 --- a/hw/sharpsl.h +++ b/hw/sharpsl.h @@ -10,13 +10,6 @@ fprintf(stderr, "%s: " format, __FUNCTION__, ##__VA_ARGS__) /* zaurus.c */ -typedef struct ScoopInfo ScoopInfo; -ScoopInfo *scoop_init(PXA2xxState *cpu, - int instance, target_phys_addr_t target_base); -void scoop_gpio_set(void *opaque, int line, int level); -qemu_irq *scoop_gpio_in_get(ScoopInfo *s); -void scoop_gpio_out_set(ScoopInfo *s, int line, - qemu_irq handler); #define SL_PXA_PARAM_BASE 0xa0000a00 void sl_bootparam_write(target_phys_addr_t ptr); diff --git a/hw/slavio_intctl.c b/hw/slavio_intctl.c index fd69354bb3..a83e5b8272 100644 --- a/hw/slavio_intctl.c +++ b/hw/slavio_intctl.c @@ -289,7 +289,12 @@ static void slavio_check_interrupts(SLAVIO_INTCTLState *s, int set_irqs) pil_pending |= (s->slaves[i].intreg_pending & CPU_SOFTIRQ_MASK) >> 16; if (set_irqs) { - for (j = MAX_PILS; j > 0; j--) { + /* Since there is not really an interrupt 0 (and pil_pending + * and irl_out bit zero are thus always zero) there is no need + * to do anything with cpu_irqs[i][0] and it is OK not to do + * the j=0 iteration of this loop. + */ + for (j = MAX_PILS-1; j > 0; j--) { if (pil_pending & (1 << j)) { if (!(s->slaves[i].irl_out & (1 << j))) { qemu_irq_raise(s->cpu_irqs[i][j]); diff --git a/hw/spitz.c b/hw/spitz.c index 092bb64eac..5b1e42dff3 100644 --- a/hw/spitz.c +++ b/hw/spitz.c @@ -23,6 +23,7 @@ #include "audio/audio.h" #include "boards.h" #include "blockdev.h" +#include "sysbus.h" #undef REG_FMT #define REG_FMT "0x%02lx" @@ -46,8 +47,11 @@ #define FLASHCTL_NCE (FLASHCTL_CE0 | FLASHCTL_CE1) typedef struct { + SysBusDevice busdev; NANDFlashState *nand; uint8_t ctl; + uint8_t manf_id; + uint8_t chip_id; ECCState ecc; } SLNANDState; @@ -130,56 +134,53 @@ static void sl_writeb(void *opaque, target_phys_addr_t addr, } } -static void sl_save(QEMUFile *f, void *opaque) -{ - SLNANDState *s = (SLNANDState *) opaque; - - qemu_put_8s(f, &s->ctl); - ecc_put(f, &s->ecc); -} - -static int sl_load(QEMUFile *f, void *opaque, int version_id) -{ - SLNANDState *s = (SLNANDState *) opaque; - - qemu_get_8s(f, &s->ctl); - ecc_get(f, &s->ecc); - - return 0; -} - enum { FLASH_128M, FLASH_1024M, }; +static CPUReadMemoryFunc * const sl_readfn[] = { + sl_readb, + sl_readb, + sl_readl, +}; +static CPUWriteMemoryFunc * const sl_writefn[] = { + sl_writeb, + sl_writeb, + sl_writeb, +}; + static void sl_flash_register(PXA2xxState *cpu, int size) { + DeviceState *dev; + + dev = qdev_create(NULL, "sl-nand"); + + qdev_prop_set_uint8(dev, "manf_id", NAND_MFR_SAMSUNG); + if (size == FLASH_128M) + qdev_prop_set_uint8(dev, "chip_id", 0x73); + else if (size == FLASH_1024M) + qdev_prop_set_uint8(dev, "chip_id", 0xf1); + + qdev_init_nofail(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, FLASH_BASE); +} + +static int sl_nand_init(SysBusDevice *dev) { int iomemtype; SLNANDState *s; - CPUReadMemoryFunc * const sl_readfn[] = { - sl_readb, - sl_readb, - sl_readl, - }; - CPUWriteMemoryFunc * const sl_writefn[] = { - sl_writeb, - sl_writeb, - sl_writeb, - }; - - s = (SLNANDState *) qemu_mallocz(sizeof(SLNANDState)); + + s = FROM_SYSBUS(SLNANDState, dev); + s->ctl = 0; - if (size == FLASH_128M) - s->nand = nand_init(NAND_MFR_SAMSUNG, 0x73); - else if (size == FLASH_1024M) - s->nand = nand_init(NAND_MFR_SAMSUNG, 0xf1); + s->nand = nand_init(s->manf_id, s->chip_id); iomemtype = cpu_register_io_memory(sl_readfn, sl_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(FLASH_BASE, 0x40, iomemtype); - register_savevm(NULL, "sl_flash", 0, 0, sl_save, sl_load, s); + sysbus_init_mmio(dev, 0x40, iomemtype); + + return 0; } /* Spitz Keyboard */ @@ -218,11 +219,10 @@ static const int spitz_gpiomap[5] = { SPITZ_GPIO_AK_INT, SPITZ_GPIO_SYNC, SPITZ_GPIO_ON_KEY, SPITZ_GPIO_SWA, SPITZ_GPIO_SWB, }; -static int spitz_gpio_invert[5] = { 0, 0, 0, 0, 0, }; typedef struct { + SysBusDevice busdev; qemu_irq sense[SPITZ_KEY_SENSE_NUM]; - qemu_irq *strobe; qemu_irq gpiomap[5]; int keymap[0x80]; uint16_t keyrow[SPITZ_KEY_SENSE_NUM]; @@ -273,8 +273,7 @@ static void spitz_keyboard_keydown(SpitzKeyboardState *s, int keycode) /* Handle the additional keys */ if ((spitz_keycode >> 4) == SPITZ_KEY_SENSE_NUM) { - qemu_set_irq(s->gpiomap[spitz_keycode & 0xf], (keycode < 0x80) ^ - spitz_gpio_invert[spitz_keycode & 0xf]); + qemu_set_irq(s->gpiomap[spitz_keycode & 0xf], (keycode < 0x80)); return; } @@ -292,8 +291,9 @@ static void spitz_keyboard_keydown(SpitzKeyboardState *s, int keycode) #define QUEUE_KEY(c) s->fifo[(s->fifopos + s->fifolen ++) & 0xf] = c -static void spitz_keyboard_handler(SpitzKeyboardState *s, int keycode) +static void spitz_keyboard_handler(void *opaque, int keycode) { + SpitzKeyboardState *s = opaque; uint16_t code; int mapcode; switch (keycode) { @@ -439,34 +439,15 @@ static void spitz_keyboard_pre_map(SpitzKeyboardState *s) s->imodifiers = 0; s->fifopos = 0; s->fifolen = 0; - s->kbdtimer = qemu_new_timer(vm_clock, spitz_keyboard_tick, s); - spitz_keyboard_tick(s); } #undef SHIFT #undef CTRL #undef FN -static void spitz_keyboard_save(QEMUFile *f, void *opaque) -{ - SpitzKeyboardState *s = (SpitzKeyboardState *) opaque; - int i; - - qemu_put_be16s(f, &s->sense_state); - qemu_put_be16s(f, &s->strobe_state); - for (i = 0; i < 5; i ++) - qemu_put_byte(f, spitz_gpio_invert[i]); -} - -static int spitz_keyboard_load(QEMUFile *f, void *opaque, int version_id) +static int spitz_keyboard_post_load(void *opaque, int version_id) { SpitzKeyboardState *s = (SpitzKeyboardState *) opaque; - int i; - - qemu_get_be16s(f, &s->sense_state); - qemu_get_be16s(f, &s->strobe_state); - for (i = 0; i < 5; i ++) - spitz_gpio_invert[i] = qemu_get_byte(f); /* Release all pressed keys */ memset(s->keyrow, 0, sizeof(s->keyrow)); @@ -481,12 +462,40 @@ static int spitz_keyboard_load(QEMUFile *f, void *opaque, int version_id) static void spitz_keyboard_register(PXA2xxState *cpu) { - int i, j; + int i; + DeviceState *dev; SpitzKeyboardState *s; - s = (SpitzKeyboardState *) - qemu_mallocz(sizeof(SpitzKeyboardState)); - memset(s, 0, sizeof(SpitzKeyboardState)); + dev = sysbus_create_simple("spitz-keyboard", -1, NULL); + s = FROM_SYSBUS(SpitzKeyboardState, sysbus_from_qdev(dev)); + + for (i = 0; i < SPITZ_KEY_SENSE_NUM; i ++) + qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(cpu->gpio, spitz_gpio_key_sense[i])); + + for (i = 0; i < 5; i ++) + s->gpiomap[i] = qdev_get_gpio_in(cpu->gpio, spitz_gpiomap[i]); + + if (!graphic_rotate) + s->gpiomap[4] = qemu_irq_invert(s->gpiomap[4]); + + for (i = 0; i < 5; i++) + qemu_set_irq(s->gpiomap[i], 0); + + for (i = 0; i < SPITZ_KEY_STROBE_NUM; i ++) + qdev_connect_gpio_out(cpu->gpio, spitz_gpio_key_strobe[i], + qdev_get_gpio_in(dev, i)); + + qemu_mod_timer(s->kbdtimer, qemu_get_clock(vm_clock)); + + qemu_add_kbd_event_handler(spitz_keyboard_handler, s); +} + +static int spitz_keyboard_init(SysBusDevice *dev) +{ + SpitzKeyboardState *s; + int i, j; + + s = FROM_SYSBUS(SpitzKeyboardState, dev); for (i = 0; i < 0x80; i ++) s->keymap[i] = -1; @@ -495,22 +504,13 @@ static void spitz_keyboard_register(PXA2xxState *cpu) if (spitz_keymap[i][j] != -1) s->keymap[spitz_keymap[i][j]] = (i << 4) | j; - for (i = 0; i < SPITZ_KEY_SENSE_NUM; i ++) - s->sense[i] = pxa2xx_gpio_in_get(cpu->gpio)[spitz_gpio_key_sense[i]]; - - for (i = 0; i < 5; i ++) - s->gpiomap[i] = pxa2xx_gpio_in_get(cpu->gpio)[spitz_gpiomap[i]]; - - s->strobe = qemu_allocate_irqs(spitz_keyboard_strobe, s, - SPITZ_KEY_STROBE_NUM); - for (i = 0; i < SPITZ_KEY_STROBE_NUM; i ++) - pxa2xx_gpio_out_set(cpu->gpio, spitz_gpio_key_strobe[i], s->strobe[i]); - spitz_keyboard_pre_map(s); - qemu_add_kbd_event_handler((QEMUPutKBDEvent *) spitz_keyboard_handler, s); - register_savevm(NULL, "spitz_keyboard", 0, 0, - spitz_keyboard_save, spitz_keyboard_load, s); + s->kbdtimer = qemu_new_timer(vm_clock, spitz_keyboard_tick, s); + qdev_init_gpio_in(&dev->qdev, spitz_keyboard_strobe, SPITZ_KEY_STROBE_NUM); + qdev_init_gpio_out(&dev->qdev, s->sense, SPITZ_KEY_SENSE_NUM); + + return 0; } /* LCD backlight controller */ @@ -526,8 +526,8 @@ static void spitz_keyboard_register(PXA2xxState *cpu) typedef struct { SSISlave ssidev; - int bl_intensity; - int bl_power; + uint32_t bl_intensity; + uint32_t bl_power; } SpitzLCDTG; static void spitz_bl_update(SpitzLCDTG *s) @@ -591,21 +591,6 @@ static uint32_t spitz_lcdtg_transfer(SSISlave *dev, uint32_t value) return 0; } -static void spitz_lcdtg_save(QEMUFile *f, void *opaque) -{ - SpitzLCDTG *s = (SpitzLCDTG *)opaque; - qemu_put_be32(f, s->bl_intensity); - qemu_put_be32(f, s->bl_power); -} - -static int spitz_lcdtg_load(QEMUFile *f, void *opaque, int version_id) -{ - SpitzLCDTG *s = (SpitzLCDTG *)opaque; - s->bl_intensity = qemu_get_be32(f); - s->bl_power = qemu_get_be32(f); - return 0; -} - static int spitz_lcdtg_init(SSISlave *dev) { SpitzLCDTG *s = FROM_SSI_SLAVE(SpitzLCDTG, dev); @@ -614,8 +599,6 @@ static int spitz_lcdtg_init(SSISlave *dev) s->bl_power = 0; s->bl_intensity = 0x20; - register_savevm(&dev->qdev, "spitz-lcdtg", -1, 1, - spitz_lcdtg_save, spitz_lcdtg_load, s); return 0; } @@ -634,7 +617,7 @@ static DeviceState *max1111; typedef struct { SSISlave ssidev; SSIBus *bus[3]; - int enable[3]; + uint32_t enable[3]; } CorgiSSPState; static uint32_t corgi_ssp_transfer(SSISlave *dev, uint32_t value) @@ -676,30 +659,6 @@ static void spitz_adc_temp_on(void *opaque, int line, int level) max111x_set_input(max1111, MAX1111_BATT_TEMP, 0); } -static void spitz_ssp_save(QEMUFile *f, void *opaque) -{ - CorgiSSPState *s = (CorgiSSPState *)opaque; - int i; - - for (i = 0; i < 3; i++) { - qemu_put_be32(f, s->enable[i]); - } -} - -static int spitz_ssp_load(QEMUFile *f, void *opaque, int version_id) -{ - CorgiSSPState *s = (CorgiSSPState *)opaque; - int i; - - if (version_id != 1) { - return -EINVAL; - } - for (i = 0; i < 3; i++) { - s->enable[i] = qemu_get_be32(f); - } - return 0; -} - static int corgi_ssp_init(SSISlave *dev) { CorgiSSPState *s = FROM_SSI_SLAVE(CorgiSSPState, dev); @@ -709,8 +668,6 @@ static int corgi_ssp_init(SSISlave *dev) s->bus[1] = ssi_create_bus(&dev->qdev, "ssi1"); s->bus[2] = ssi_create_bus(&dev->qdev, "ssi2"); - register_savevm(&dev->qdev, "spitz_ssp", -1, 1, - spitz_ssp_save, spitz_ssp_load, s); return 0; } @@ -728,7 +685,7 @@ static void spitz_ssp_attach(PXA2xxState *cpu) bus = qdev_get_child_bus(mux, "ssi1"); dev = ssi_create_slave(bus, "ads7846"); qdev_connect_gpio_out(dev, 0, - pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_TP_INT]); + qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_TP_INT)); bus = qdev_get_child_bus(mux, "ssi2"); max1111 = ssi_create_slave(bus, "max1111"); @@ -736,11 +693,11 @@ static void spitz_ssp_attach(PXA2xxState *cpu) max111x_set_input(max1111, MAX1111_BATT_TEMP, 0); max111x_set_input(max1111, MAX1111_ACIN_VOLT, SPITZ_CHARGEON_ACIN); - pxa2xx_gpio_out_set(cpu->gpio, SPITZ_GPIO_LCDCON_CS, + qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_LCDCON_CS, qdev_get_gpio_in(mux, 0)); - pxa2xx_gpio_out_set(cpu->gpio, SPITZ_GPIO_ADS7846_CS, + qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_ADS7846_CS, qdev_get_gpio_in(mux, 1)); - pxa2xx_gpio_out_set(cpu->gpio, SPITZ_GPIO_MAX1111_CS, + qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_MAX1111_CS, qdev_get_gpio_in(mux, 2)); } @@ -790,7 +747,7 @@ static void spitz_i2c_setup(PXA2xxState *cpu) wm = i2c_create_slave(bus, "wm8750", 0); spitz_wm8750_addr(wm, 0, 0); - pxa2xx_gpio_out_set(cpu->gpio, SPITZ_GPIO_WM, + qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_WM, qemu_allocate_irqs(spitz_wm8750_addr, wm, 1)[0]); /* .. and to the sound interface. */ cpu->i2s->opaque = wm; @@ -851,21 +808,21 @@ static void spitz_out_switch(void *opaque, int line, int level) #define SPITZ_SCP2_MIC_BIAS 9 static void spitz_scoop_gpio_setup(PXA2xxState *cpu, - ScoopInfo *scp0, ScoopInfo *scp1) + DeviceState *scp0, DeviceState *scp1) { qemu_irq *outsignals = qemu_allocate_irqs(spitz_out_switch, cpu, 8); - scoop_gpio_out_set(scp0, SPITZ_SCP_CHRG_ON, outsignals[0]); - scoop_gpio_out_set(scp0, SPITZ_SCP_JK_B, outsignals[1]); - scoop_gpio_out_set(scp0, SPITZ_SCP_LED_GREEN, outsignals[2]); - scoop_gpio_out_set(scp0, SPITZ_SCP_LED_ORANGE, outsignals[3]); + qdev_connect_gpio_out(scp0, SPITZ_SCP_CHRG_ON, outsignals[0]); + qdev_connect_gpio_out(scp0, SPITZ_SCP_JK_B, outsignals[1]); + qdev_connect_gpio_out(scp0, SPITZ_SCP_LED_GREEN, outsignals[2]); + qdev_connect_gpio_out(scp0, SPITZ_SCP_LED_ORANGE, outsignals[3]); if (scp1) { - scoop_gpio_out_set(scp1, SPITZ_SCP2_BACKLIGHT_CONT, outsignals[4]); - scoop_gpio_out_set(scp1, SPITZ_SCP2_BACKLIGHT_ON, outsignals[5]); + qdev_connect_gpio_out(scp1, SPITZ_SCP2_BACKLIGHT_CONT, outsignals[4]); + qdev_connect_gpio_out(scp1, SPITZ_SCP2_BACKLIGHT_ON, outsignals[5]); } - scoop_gpio_out_set(scp0, SPITZ_SCP_ADC_TEMP_ON, outsignals[6]); + qdev_connect_gpio_out(scp0, SPITZ_SCP_ADC_TEMP_ON, outsignals[6]); } #define SPITZ_GPIO_HSYNC 22 @@ -883,7 +840,7 @@ static int spitz_hsync; static void spitz_lcd_hsync_handler(void *opaque, int line, int level) { PXA2xxState *cpu = (PXA2xxState *) opaque; - qemu_set_irq(pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_HSYNC], spitz_hsync); + qemu_set_irq(qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_HSYNC), spitz_hsync); spitz_hsync ^= 1; } @@ -903,36 +860,24 @@ static void spitz_gpio_setup(PXA2xxState *cpu, int slots) /* MMC/SD host */ pxa2xx_mmci_handlers(cpu->mmc, - pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_SD_WP], - pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_SD_DETECT]); + qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_SD_WP), + qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_SD_DETECT)); /* Battery lock always closed */ - qemu_irq_raise(pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_BAT_COVER]); + qemu_irq_raise(qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_BAT_COVER)); /* Handle reset */ - pxa2xx_gpio_out_set(cpu->gpio, SPITZ_GPIO_ON_RESET, cpu->reset); + qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_ON_RESET, cpu->reset); /* PCMCIA signals: card's IRQ and Card-Detect */ if (slots >= 1) pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[0], - pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_CF1_IRQ], - pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_CF1_CD]); + qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF1_IRQ), + qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF1_CD)); if (slots >= 2) pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[1], - pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_CF2_IRQ], - pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_CF2_CD]); - - /* Initialise the screen rotation related signals */ - spitz_gpio_invert[3] = 0; /* Always open */ - if (graphic_rotate) { /* Tablet mode */ - spitz_gpio_invert[4] = 0; - } else { /* Portrait mode */ - spitz_gpio_invert[4] = 1; - } - qemu_set_irq(pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_SWA], - spitz_gpio_invert[3]); - qemu_set_irq(pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_SWB], - spitz_gpio_invert[4]); + qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF2_IRQ), + qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF2_CD)); } /* Board init. */ @@ -952,7 +897,7 @@ static void spitz_common_init(ram_addr_t ram_size, const char *cpu_model, enum spitz_model_e model, int arm_id) { PXA2xxState *cpu; - ScoopInfo *scp0, *scp1 = NULL; + DeviceState *scp0, *scp1 = NULL; if (!cpu_model) cpu_model = (model == terrier) ? "pxa270-c5" : "pxa270-c0"; @@ -970,9 +915,9 @@ static void spitz_common_init(ram_addr_t ram_size, spitz_ssp_attach(cpu); - scp0 = scoop_init(cpu, 0, 0x10800000); + scp0 = sysbus_create_simple("scoop", 0x10800000, NULL); if (model != akita) { - scp1 = scoop_init(cpu, 1, 0x08800040); + scp1 = sysbus_create_simple("scoop", 0x08800040, NULL); } spitz_scoop_gpio_setup(cpu, scp0, scp1); @@ -1069,16 +1014,94 @@ static void spitz_machine_init(void) machine_init(spitz_machine_init); +static bool is_version_0(void *opaque, int version_id) +{ + return version_id == 0; +} + +static VMStateDescription vmstate_sl_nand_info = { + .name = "sl-nand", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField []) { + VMSTATE_UINT8(ctl, SLNANDState), + VMSTATE_STRUCT(ecc, SLNANDState, 0, vmstate_ecc_state, ECCState), + VMSTATE_END_OF_LIST(), + }, +}; + +static SysBusDeviceInfo sl_nand_info = { + .init = sl_nand_init, + .qdev.name = "sl-nand", + .qdev.size = sizeof(SLNANDState), + .qdev.vmsd = &vmstate_sl_nand_info, + .qdev.props = (Property []) { + DEFINE_PROP_UINT8("manf_id", SLNANDState, manf_id, NAND_MFR_SAMSUNG), + DEFINE_PROP_UINT8("chip_id", SLNANDState, chip_id, 0xf1), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static VMStateDescription vmstate_spitz_kbd = { + .name = "spitz-keyboard", + .version_id = 1, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .post_load = spitz_keyboard_post_load, + .fields = (VMStateField []) { + VMSTATE_UINT16(sense_state, SpitzKeyboardState), + VMSTATE_UINT16(strobe_state, SpitzKeyboardState), + VMSTATE_UNUSED_TEST(is_version_0, 5), + VMSTATE_END_OF_LIST(), + }, +}; + +static SysBusDeviceInfo spitz_keyboard_info = { + .init = spitz_keyboard_init, + .qdev.name = "spitz-keyboard", + .qdev.size = sizeof(SpitzKeyboardState), + .qdev.vmsd = &vmstate_spitz_kbd, + .qdev.props = (Property []) { + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static const VMStateDescription vmstate_corgi_ssp_regs = { + .name = "corgi-ssp", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT32_ARRAY(enable, CorgiSSPState, 3), + VMSTATE_END_OF_LIST(), + } +}; + static SSISlaveInfo corgi_ssp_info = { .qdev.name = "corgi-ssp", .qdev.size = sizeof(CorgiSSPState), + .qdev.vmsd = &vmstate_corgi_ssp_regs, .init = corgi_ssp_init, .transfer = corgi_ssp_transfer }; +static const VMStateDescription vmstate_spitz_lcdtg_regs = { + .name = "spitz-lcdtg", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT32(bl_intensity, SpitzLCDTG), + VMSTATE_UINT32(bl_power, SpitzLCDTG), + VMSTATE_END_OF_LIST(), + } +}; + static SSISlaveInfo spitz_lcdtg_info = { .qdev.name = "spitz-lcdtg", .qdev.size = sizeof(SpitzLCDTG), + .qdev.vmsd = &vmstate_spitz_lcdtg_regs, .init = spitz_lcdtg_init, .transfer = spitz_lcdtg_transfer }; @@ -1087,6 +1110,8 @@ static void spitz_register_devices(void) { ssi_register_slave(&corgi_ssp_info); ssi_register_slave(&spitz_lcdtg_info); + sysbus_register_withprop(&spitz_keyboard_info); + sysbus_register_withprop(&sl_nand_info); } device_init(spitz_register_devices) diff --git a/hw/ssi-sd.c b/hw/ssi-sd.c index a1a63b23e2..fb4b649279 100644 --- a/hw/ssi-sd.c +++ b/hw/ssi-sd.c @@ -7,6 +7,7 @@ * This code is licenced under the GNU GPL v2. */ +#include "blockdev.h" #include "ssi.h" #include "sd.h" @@ -231,11 +232,11 @@ static int ssi_sd_load(QEMUFile *f, void *opaque, int version_id) static int ssi_sd_init(SSISlave *dev) { ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, dev); - BlockDriverState *bs; + DriveInfo *dinfo; s->mode = SSI_SD_CMD; - bs = qdev_init_bdrv(&dev->qdev, IF_SD); - s->sd = sd_init(bs, 1); + dinfo = drive_get_next(IF_SD); + s->sd = sd_init(dinfo ? dinfo->bdrv : NULL, 1); register_savevm(&dev->qdev, "ssi_sd", -1, 1, ssi_sd_save, ssi_sd_load, s); return 0; } @@ -20,6 +20,7 @@ #include "i2c.h" #include "ssi.h" #include "blockdev.h" +#include "sysbus.h" #define TOSA_RAM 0x04000000 #define TOSA_ROM 0x00800000 @@ -86,34 +87,34 @@ static void tosa_out_switch(void *opaque, int line, int level) static void tosa_gpio_setup(PXA2xxState *cpu, - ScoopInfo *scp0, - ScoopInfo *scp1, + DeviceState *scp0, + DeviceState *scp1, TC6393xbState *tmio) { qemu_irq *outsignals = qemu_allocate_irqs(tosa_out_switch, cpu, 4); /* MMC/SD host */ pxa2xx_mmci_handlers(cpu->mmc, - scoop_gpio_in_get(scp0)[TOSA_GPIO_SD_WP], - qemu_irq_invert(pxa2xx_gpio_in_get(cpu->gpio)[TOSA_GPIO_nSD_DETECT])); + qdev_get_gpio_in(scp0, TOSA_GPIO_SD_WP), + qemu_irq_invert(qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_nSD_DETECT))); /* Handle reset */ - pxa2xx_gpio_out_set(cpu->gpio, TOSA_GPIO_ON_RESET, cpu->reset); + qdev_connect_gpio_out(cpu->gpio, TOSA_GPIO_ON_RESET, cpu->reset); /* PCMCIA signals: card's IRQ and Card-Detect */ pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[0], - pxa2xx_gpio_in_get(cpu->gpio)[TOSA_GPIO_CF_IRQ], - pxa2xx_gpio_in_get(cpu->gpio)[TOSA_GPIO_CF_CD]); + qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_CF_IRQ), + qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_CF_CD)); pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[1], - pxa2xx_gpio_in_get(cpu->gpio)[TOSA_GPIO_JC_CF_IRQ], + qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_JC_CF_IRQ), NULL); - scoop_gpio_out_set(scp1, TOSA_GPIO_BT_LED, outsignals[0]); - scoop_gpio_out_set(scp1, TOSA_GPIO_NOTE_LED, outsignals[1]); - scoop_gpio_out_set(scp1, TOSA_GPIO_CHRG_ERR_LED, outsignals[2]); - scoop_gpio_out_set(scp1, TOSA_GPIO_WLAN_LED, outsignals[3]); + qdev_connect_gpio_out(scp1, TOSA_GPIO_BT_LED, outsignals[0]); + qdev_connect_gpio_out(scp1, TOSA_GPIO_NOTE_LED, outsignals[1]); + qdev_connect_gpio_out(scp1, TOSA_GPIO_CHRG_ERR_LED, outsignals[2]); + qdev_connect_gpio_out(scp1, TOSA_GPIO_WLAN_LED, outsignals[3]); - scoop_gpio_out_set(scp1, TOSA_GPIO_TC6393XB_L3V_ON, tc6393xb_l3v_get(tmio)); + qdev_connect_gpio_out(scp1, TOSA_GPIO_TC6393XB_L3V_ON, tc6393xb_l3v_get(tmio)); } static uint32_t tosa_ssp_tansfer(SSISlave *dev, uint32_t value) @@ -208,7 +209,7 @@ static void tosa_init(ram_addr_t ram_size, { PXA2xxState *cpu; TC6393xbState *tmio; - ScoopInfo *scp0, *scp1; + DeviceState *scp0, *scp1; if (!cpu_model) cpu_model = "pxa255"; @@ -219,10 +220,10 @@ static void tosa_init(ram_addr_t ram_size, qemu_ram_alloc(NULL, "tosa.rom", TOSA_ROM) | IO_MEM_ROM); tmio = tc6393xb_init(0x10000000, - pxa2xx_gpio_in_get(cpu->gpio)[TOSA_GPIO_TC6393XB_INT]); + qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_TC6393XB_INT)); - scp0 = scoop_init(cpu, 0, 0x08800000); - scp1 = scoop_init(cpu, 1, 0x14800040); + scp0 = sysbus_create_simple("scoop", 0x08800000, NULL); + scp1 = sysbus_create_simple("scoop", 0x14800040, NULL); tosa_gpio_setup(cpu, scp0, scp1, tmio); diff --git a/hw/usb-msd.c b/hw/usb-msd.c index 729d96ccc3..97d1e4af13 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -51,6 +51,7 @@ typedef struct { SCSIBus bus; BlockConf conf; SCSIDevice *scsi_dev; + uint32_t removable; int result; /* For async completion. */ USBPacket *packet; @@ -515,7 +516,7 @@ static int usb_msd_initfn(USBDevice *dev) usb_desc_init(dev); scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, usb_msd_command_complete); - s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0); + s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable); if (!s->scsi_dev) { return -1; } @@ -541,7 +542,6 @@ static USBDevice *usb_msd_init(const char *filename) QemuOpts *opts; DriveInfo *dinfo; USBDevice *dev; - int fatal_error; const char *p1; char fmt[32]; @@ -571,7 +571,7 @@ static USBDevice *usb_msd_init(const char *filename) qemu_opt_set(opts, "if", "none"); /* create host drive */ - dinfo = drive_init(opts, 0, &fatal_error); + dinfo = drive_init(opts, 0); if (!dinfo) { qemu_opts_del(opts); return NULL; @@ -607,6 +607,7 @@ static struct USBDeviceInfo msd_info = { .usbdevice_init = usb_msd_init, .qdev.props = (Property[]) { DEFINE_BLOCK_PROPERTIES(MSDState, conf), + DEFINE_PROP_BIT("removable", MSDState, removable, 0, false), DEFINE_PROP_END_OF_LIST(), }, }; diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c index f62ccd1fe8..ffac5a4d8f 100644 --- a/hw/virtio-blk.c +++ b/hw/virtio-blk.c @@ -55,7 +55,7 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, int status) trace_virtio_blk_req_complete(req, status); - req->in->status = status; + stb_p(&req->in->status, status); virtqueue_push(s->vq, &req->elem, req->qiov.size + sizeof(*req->in)); virtio_notify(&s->vdev, s->vq); @@ -94,7 +94,7 @@ static void virtio_blk_rw_complete(void *opaque, int ret) trace_virtio_blk_rw_complete(req, ret); if (ret) { - int is_read = !(req->out->type & VIRTIO_BLK_T_OUT); + int is_read = !(ldl_p(&req->out->type) & VIRTIO_BLK_T_OUT); if (virtio_blk_handle_rw_error(req, -ret, is_read)) return; } @@ -223,10 +223,10 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req) status = VIRTIO_BLK_S_OK; } - req->scsi->errors = hdr.status; - req->scsi->residual = hdr.resid; - req->scsi->sense_len = hdr.sb_len_wr; - req->scsi->data_len = hdr.dxfer_len; + stl_p(&req->scsi->errors, hdr.status); + stl_p(&req->scsi->residual, hdr.resid); + stl_p(&req->scsi->sense_len, hdr.sb_len_wr); + stl_p(&req->scsi->data_len, hdr.dxfer_len); virtio_blk_req_complete(req, status); } @@ -280,10 +280,13 @@ static void virtio_blk_handle_flush(VirtIOBlockReq *req, MultiReqBuffer *mrb) static void virtio_blk_handle_write(VirtIOBlockReq *req, MultiReqBuffer *mrb) { BlockRequest *blkreq; + uint64_t sector; - trace_virtio_blk_handle_write(req, req->out->sector, req->qiov.size / 512); + sector = ldq_p(&req->out->sector); - if (req->out->sector & req->dev->sector_mask) { + trace_virtio_blk_handle_write(req, sector, req->qiov.size / 512); + + if (sector & req->dev->sector_mask) { virtio_blk_rw_complete(req, -EIO); return; } @@ -293,7 +296,7 @@ static void virtio_blk_handle_write(VirtIOBlockReq *req, MultiReqBuffer *mrb) } blkreq = &mrb->blkreq[mrb->num_writes]; - blkreq->sector = req->out->sector; + blkreq->sector = sector; blkreq->nb_sectors = req->qiov.size / BDRV_SECTOR_SIZE; blkreq->qiov = &req->qiov; blkreq->cb = virtio_blk_rw_complete; @@ -306,13 +309,16 @@ static void virtio_blk_handle_write(VirtIOBlockReq *req, MultiReqBuffer *mrb) static void virtio_blk_handle_read(VirtIOBlockReq *req) { BlockDriverAIOCB *acb; + uint64_t sector; + + sector = ldq_p(&req->out->sector); - if (req->out->sector & req->dev->sector_mask) { + if (sector & req->dev->sector_mask) { virtio_blk_rw_complete(req, -EIO); return; } - acb = bdrv_aio_readv(req->dev->bs, req->out->sector, &req->qiov, + acb = bdrv_aio_readv(req->dev->bs, sector, &req->qiov, req->qiov.size / BDRV_SECTOR_SIZE, virtio_blk_rw_complete, req); if (!acb) { @@ -323,6 +329,8 @@ static void virtio_blk_handle_read(VirtIOBlockReq *req) static void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) { + uint32_t type; + if (req->elem.out_num < 1 || req->elem.in_num < 1) { error_report("virtio-blk missing headers"); exit(1); @@ -337,17 +345,19 @@ static void virtio_blk_handle_request(VirtIOBlockReq *req, req->out = (void *)req->elem.out_sg[0].iov_base; req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base; - if (req->out->type & VIRTIO_BLK_T_FLUSH) { + type = ldl_p(&req->out->type); + + if (type & VIRTIO_BLK_T_FLUSH) { virtio_blk_handle_flush(req, mrb); - } else if (req->out->type & VIRTIO_BLK_T_SCSI_CMD) { + } else if (type & VIRTIO_BLK_T_SCSI_CMD) { virtio_blk_handle_scsi(req); - } else if (req->out->type & VIRTIO_BLK_T_GET_ID) { + } else if (type & VIRTIO_BLK_T_GET_ID) { VirtIOBlock *s = req->dev; memcpy(req->elem.in_sg[0].iov_base, s->sn, MIN(req->elem.in_sg[0].iov_len, sizeof(s->sn))); virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); - } else if (req->out->type & VIRTIO_BLK_T_OUT) { + } else if (type & VIRTIO_BLK_T_OUT) { qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1], req->elem.out_num - 1); virtio_blk_handle_write(req, mrb); @@ -504,6 +514,15 @@ static int virtio_blk_load(QEMUFile *f, void *opaque, int version_id) return 0; } +static void virtio_blk_change_cb(void *opaque, int reason) +{ + VirtIOBlock *s = opaque; + + if (reason & CHANGE_SIZE) { + virtio_notify_config(&s->vdev); + } +} + VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf) { VirtIOBlock *s; @@ -546,6 +565,7 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf) register_savevm(dev, "virtio-blk", virtio_blk_id++, 2, virtio_blk_save, virtio_blk_load, s); bdrv_set_removable(s->bs, 0); + bdrv_set_change_cb(s->bs, virtio_blk_change_cb, s); s->bs->buffer_alignment = conf->logical_block_size; add_boot_device_path(conf->bootindex, dev, "/disk@0,0"); diff --git a/hw/virtio-console.c b/hw/virtio-console.c index caea11f3a8..62624ec780 100644 --- a/hw/virtio-console.c +++ b/hw/virtio-console.c @@ -20,11 +20,11 @@ typedef struct VirtConsole { /* Callback function that's called when the guest sends us data */ -static void flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len) +static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len) { VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); - qemu_chr_write(vcon->chr, buf, len); + return qemu_chr_write(vcon->chr, buf, len); } /* Readiness of the guest to accept data on a port */ @@ -48,34 +48,37 @@ static void chr_event(void *opaque, int event) VirtConsole *vcon = opaque; switch (event) { - case CHR_EVENT_OPENED: { + case CHR_EVENT_OPENED: virtio_serial_open(&vcon->port); break; - } case CHR_EVENT_CLOSED: virtio_serial_close(&vcon->port); break; } } -/* Virtio Console Ports */ -static int virtconsole_initfn(VirtIOSerialDevice *dev) +static int generic_port_init(VirtConsole *vcon, VirtIOSerialDevice *dev) { - VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); - VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); - - port->info = dev->info; - - port->is_console = true; + vcon->port.info = dev->info; if (vcon->chr) { qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event, vcon); - port->info->have_data = flush_buf; + vcon->port.info->have_data = flush_buf; } return 0; } +/* Virtio Console Ports */ +static int virtconsole_initfn(VirtIOSerialDevice *dev) +{ + VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); + VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); + + port->is_console = true; + return generic_port_init(vcon, dev); +} + static int virtconsole_exitfn(VirtIOSerialDevice *dev) { VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); @@ -115,14 +118,7 @@ static int virtserialport_initfn(VirtIOSerialDevice *dev) VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); - port->info = dev->info; - - if (vcon->chr) { - qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event, - vcon); - port->info->have_data = flush_buf; - } - return 0; + return generic_port_init(vcon, dev); } static VirtIOSerialPortInfo virtserialport_info = { diff --git a/hw/virtio-net.c b/hw/virtio-net.c index ccb3e632a4..161f11445f 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -79,7 +79,7 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) VirtIONet *n = to_virtio_net(vdev); struct virtio_net_config netcfg; - netcfg.status = n->status; + netcfg.status = lduw_p(&n->status); memcpy(netcfg.mac, n->mac, ETH_ALEN); memcpy(config, &netcfg, sizeof(netcfg)); } @@ -338,7 +338,7 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, n->mac_table.multi_overflow = 0; memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); - mac_data.entries = ldl_le_p(elem->out_sg[1].iov_base); + mac_data.entries = ldl_p(elem->out_sg[1].iov_base); if (sizeof(mac_data.entries) + (mac_data.entries * ETH_ALEN) > elem->out_sg[1].iov_len) @@ -354,7 +354,7 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, n->mac_table.first_multi = n->mac_table.in_use; - mac_data.entries = ldl_le_p(elem->out_sg[2].iov_base); + mac_data.entries = ldl_p(elem->out_sg[2].iov_base); if (sizeof(mac_data.entries) + (mac_data.entries * ETH_ALEN) > elem->out_sg[2].iov_len) @@ -384,7 +384,7 @@ static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd, return VIRTIO_NET_ERR; } - vid = lduw_le_p(elem->out_sg[1].iov_base); + vid = lduw_p(elem->out_sg[1].iov_base); if (vid >= MAX_VLAN) return VIRTIO_NET_ERR; @@ -673,8 +673,9 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_ virtqueue_fill(n->rx_vq, &elem, total, i++); } - if (mhdr) - mhdr->num_buffers = i; + if (mhdr) { + mhdr->num_buffers = lduw_p(&i); + } virtqueue_flush(n->rx_vq, i); virtio_notify(&n->vdev, n->rx_vq); diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c index b728040f3a..09e22aa44a 100644 --- a/hw/virtio-serial-bus.c +++ b/hw/virtio-serial-bus.c @@ -113,39 +113,74 @@ static size_t write_to_port(VirtIOSerialPort *port, return offset; } -static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, - VirtIODevice *vdev, bool discard) +static void discard_vq_data(VirtQueue *vq, VirtIODevice *vdev) { VirtQueueElement elem; - assert(port || discard); + while (virtqueue_pop(vq, &elem)) { + virtqueue_push(vq, &elem, 0); + } + virtio_notify(vdev, vq); +} + +static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, + VirtIODevice *vdev) +{ + assert(port); assert(virtio_queue_ready(vq)); - while ((discard || !port->throttled) && virtqueue_pop(vq, &elem)) { - uint8_t *buf; - size_t ret, buf_size; + while (!port->throttled) { + unsigned int i; - if (!discard) { - buf_size = iov_size(elem.out_sg, elem.out_num); - buf = qemu_malloc(buf_size); - ret = iov_to_buf(elem.out_sg, elem.out_num, buf, 0, buf_size); + /* Pop an elem only if we haven't left off a previous one mid-way */ + if (!port->elem.out_num) { + if (!virtqueue_pop(vq, &port->elem)) { + break; + } + port->iov_idx = 0; + port->iov_offset = 0; + } - port->info->have_data(port, buf, ret); - qemu_free(buf); + for (i = port->iov_idx; i < port->elem.out_num; i++) { + size_t buf_size; + ssize_t ret; + + buf_size = port->elem.out_sg[i].iov_len - port->iov_offset; + ret = port->info->have_data(port, + port->elem.out_sg[i].iov_base + + port->iov_offset, + buf_size); + if (ret < 0 && ret != -EAGAIN) { + /* We don't handle any other type of errors here */ + abort(); + } + if (ret == -EAGAIN || (ret >= 0 && ret < buf_size)) { + virtio_serial_throttle_port(port, true); + port->iov_idx = i; + if (ret > 0) { + port->iov_offset += ret; + } + break; + } + port->iov_offset = 0; } - virtqueue_push(vq, &elem, 0); + if (port->throttled) { + break; + } + virtqueue_push(vq, &port->elem, 0); + port->elem.out_num = 0; } virtio_notify(vdev, vq); } -static void flush_queued_data(VirtIOSerialPort *port, bool discard) +static void flush_queued_data(VirtIOSerialPort *port) { assert(port); if (!virtio_queue_ready(port->ovq)) { return; } - do_flush_queued_data(port, port->ovq, &port->vser->vdev, discard); + do_flush_queued_data(port, port->ovq, &port->vser->vdev); } static size_t send_control_msg(VirtIOSerialPort *port, void *buf, size_t len) @@ -204,7 +239,7 @@ int virtio_serial_close(VirtIOSerialPort *port) * consume, reset the throttling flag and discard the data. */ port->throttled = false; - flush_queued_data(port, true); + discard_vq_data(port->ovq, &port->vser->vdev); send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0); @@ -258,7 +293,7 @@ void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle) return; } - flush_queued_data(port, false); + flush_queued_data(port); } /* Guest wants to notify us of some event */ @@ -414,11 +449,15 @@ static void handle_output(VirtIODevice *vdev, VirtQueue *vq) discard = true; } - if (!discard && port->throttled) { + if (discard) { + discard_vq_data(vq, vdev); + return; + } + if (port->throttled) { return; } - do_flush_queued_data(port, vq, vdev, discard); + do_flush_queued_data(port, vq, vdev); } static void handle_input(VirtIODevice *vdev, VirtQueue *vq) @@ -488,9 +527,24 @@ static void virtio_serial_save(QEMUFile *f, void *opaque) * Items in struct VirtIOSerialPort. */ QTAILQ_FOREACH(port, &s->ports, next) { + uint32_t elem_popped; + qemu_put_be32s(f, &port->id); qemu_put_byte(f, port->guest_connected); qemu_put_byte(f, port->host_connected); + + elem_popped = 0; + if (port->elem.out_num) { + elem_popped = 1; + } + qemu_put_be32s(f, &elem_popped); + if (elem_popped) { + qemu_put_be32s(f, &port->iov_idx); + qemu_put_be64s(f, &port->iov_offset); + + qemu_put_buffer(f, (unsigned char *)&port->elem, + sizeof(port->elem)); + } } } @@ -501,7 +555,7 @@ static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) uint32_t max_nr_ports, nr_active_ports, ports_map; unsigned int i; - if (version_id > 2) { + if (version_id > 3) { return -EINVAL; } @@ -554,6 +608,29 @@ static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, port->host_connected); } + + if (version_id > 2) { + uint32_t elem_popped; + + qemu_get_be32s(f, &elem_popped); + if (elem_popped) { + qemu_get_be32s(f, &port->iov_idx); + qemu_get_be64s(f, &port->iov_offset); + + qemu_get_buffer(f, (unsigned char *)&port->elem, + sizeof(port->elem)); + virtqueue_map_sg(port->elem.in_sg, port->elem.in_addr, + port->elem.in_num, 1); + virtqueue_map_sg(port->elem.out_sg, port->elem.out_addr, + port->elem.out_num, 1); + + /* + * Port was throttled on source machine. Let's + * unthrottle it here so data starts flowing again. + */ + virtio_serial_throttle_port(port, false); + } + } } return 0; } @@ -634,7 +711,7 @@ static void remove_port(VirtIOSerial *vser, uint32_t port_id) port = find_port_by_id(vser, port_id); /* Flush out any unconsumed buffers first */ - flush_queued_data(port, true); + discard_vq_data(port->ovq, &port->vser->vdev); send_control_event(port, VIRTIO_CONSOLE_PORT_REMOVE, 1); } @@ -695,6 +772,8 @@ static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base) port->guest_connected = true; } + port->elem.out_num = 0; + QTAILQ_INSERT_TAIL(&port->vser->ports, port, next); port->ivq = port->vser->ivqs[port->id]; port->ovq = port->vser->ovqs[port->id]; @@ -806,7 +885,7 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports) * Register for the savevm section with the virtio-console name * to preserve backward compat */ - register_savevm(dev, "virtio-console", -1, 2, virtio_serial_save, + register_savevm(dev, "virtio-console", -1, 3, virtio_serial_save, virtio_serial_load, vser); return vdev; diff --git a/hw/virtio-serial.h b/hw/virtio-serial.h index ff08c40681..a308196786 100644 --- a/hw/virtio-serial.h +++ b/hw/virtio-serial.h @@ -102,6 +102,23 @@ struct VirtIOSerialPort { */ uint32_t id; + /* + * This is the elem that we pop from the virtqueue. A slow + * backend that consumes guest data (e.g. the file backend for + * qemu chardevs) can cause the guest to block till all the output + * is flushed. This isn't desired, so we keep a note of the last + * element popped and continue consuming it once the backend + * becomes writable again. + */ + VirtQueueElement elem; + + /* + * The index and the offset into the iov buffer that was popped in + * elem above. + */ + uint32_t iov_idx; + uint64_t iov_offset; + /* Identify if this is a port that binds with hvc in the guest */ uint8_t is_console; @@ -137,10 +154,11 @@ struct VirtIOSerialPortInfo { /* * Guest wrote some data to the port. This data is handed over to - * the app via this callback. The app is supposed to consume all - * the data that is presented to it. + * the app via this callback. The app can return a size less than + * 'len'. In this case, throttling will be enabled for this port. */ - void (*have_data)(VirtIOSerialPort *port, const uint8_t *buf, size_t len); + ssize_t (*have_data)(VirtIOSerialPort *port, const uint8_t *buf, + size_t len); }; /* Interface to the virtio-serial bus */ diff --git a/hw/zaurus.c b/hw/zaurus.c index 36be94a179..fca11a5333 100644 --- a/hw/zaurus.c +++ b/hw/zaurus.c @@ -18,15 +18,17 @@ #include "hw.h" #include "pxa.h" #include "sharpsl.h" +#include "sysbus.h" #undef REG_FMT #define REG_FMT "0x%02lx" /* SCOOP devices */ +typedef struct ScoopInfo ScoopInfo; struct ScoopInfo { + SysBusDevice busdev; qemu_irq handler[16]; - qemu_irq *in; uint16_t status; uint16_t power; uint32_t gpio_level; @@ -153,7 +155,7 @@ static CPUWriteMemoryFunc * const scoop_writefn[] = { scoop_writeb, }; -void scoop_gpio_set(void *opaque, int line, int level) +static void scoop_gpio_set(void *opaque, int line, int level) { ScoopInfo *s = (ScoopInfo *) opaque; @@ -163,77 +165,66 @@ void scoop_gpio_set(void *opaque, int line, int level) s->gpio_level &= ~(1 << line); } -qemu_irq *scoop_gpio_in_get(ScoopInfo *s) +static int scoop_init(SysBusDevice *dev) { - return s->in; -} + ScoopInfo *s = FROM_SYSBUS(ScoopInfo, dev); + int iomemtype; -void scoop_gpio_out_set(ScoopInfo *s, int line, - qemu_irq handler) { - if (line >= 16) { - fprintf(stderr, "No GPIO pin %i\n", line); - exit(-1); - } + s->status = 0x02; + qdev_init_gpio_out(&s->busdev.qdev, s->handler, 16); + qdev_init_gpio_in(&s->busdev.qdev, scoop_gpio_set, 16); + iomemtype = cpu_register_io_memory(scoop_readfn, + scoop_writefn, s, DEVICE_NATIVE_ENDIAN); - s->handler[line] = handler; -} + sysbus_init_mmio(dev, 0x1000, iomemtype); -static void scoop_save(QEMUFile *f, void *opaque) -{ - ScoopInfo *s = (ScoopInfo *) opaque; - qemu_put_be16s(f, &s->status); - qemu_put_be16s(f, &s->power); - qemu_put_be32s(f, &s->gpio_level); - qemu_put_be32s(f, &s->gpio_dir); - qemu_put_be32s(f, &s->prev_level); - qemu_put_be16s(f, &s->mcr); - qemu_put_be16s(f, &s->cdr); - qemu_put_be16s(f, &s->ccr); - qemu_put_be16s(f, &s->irr); - qemu_put_be16s(f, &s->imr); - qemu_put_be16s(f, &s->isr); + return 0; } -static int scoop_load(QEMUFile *f, void *opaque, int version_id) +static bool is_version_0 (void *opaque, int version_id) { - uint16_t dummy; - ScoopInfo *s = (ScoopInfo *) opaque; - qemu_get_be16s(f, &s->status); - qemu_get_be16s(f, &s->power); - qemu_get_be32s(f, &s->gpio_level); - qemu_get_be32s(f, &s->gpio_dir); - qemu_get_be32s(f, &s->prev_level); - qemu_get_be16s(f, &s->mcr); - qemu_get_be16s(f, &s->cdr); - qemu_get_be16s(f, &s->ccr); - qemu_get_be16s(f, &s->irr); - qemu_get_be16s(f, &s->imr); - qemu_get_be16s(f, &s->isr); - if (version_id < 1) - qemu_get_be16s(f, &dummy); - - return 0; + return version_id == 0; } -ScoopInfo *scoop_init(PXA2xxState *cpu, - int instance, - target_phys_addr_t target_base) { - int iomemtype; - ScoopInfo *s; - s = (ScoopInfo *) - qemu_mallocz(sizeof(ScoopInfo)); - memset(s, 0, sizeof(ScoopInfo)); +static const VMStateDescription vmstate_scoop_regs = { + .name = "scoop", + .version_id = 1, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField []) { + VMSTATE_UINT16(status, ScoopInfo), + VMSTATE_UINT16(power, ScoopInfo), + VMSTATE_UINT32(gpio_level, ScoopInfo), + VMSTATE_UINT32(gpio_dir, ScoopInfo), + VMSTATE_UINT32(prev_level, ScoopInfo), + VMSTATE_UINT16(mcr, ScoopInfo), + VMSTATE_UINT16(cdr, ScoopInfo), + VMSTATE_UINT16(ccr, ScoopInfo), + VMSTATE_UINT16(irr, ScoopInfo), + VMSTATE_UINT16(imr, ScoopInfo), + VMSTATE_UINT16(isr, ScoopInfo), + VMSTATE_UNUSED_TEST(is_version_0, 2), + VMSTATE_END_OF_LIST(), + }, +}; - s->status = 0x02; - s->in = qemu_allocate_irqs(scoop_gpio_set, s, 16); - iomemtype = cpu_register_io_memory(scoop_readfn, - scoop_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(target_base, 0x1000, iomemtype); - register_savevm(NULL, "scoop", instance, 1, scoop_save, scoop_load, s); +static SysBusDeviceInfo scoop_sysbus_info = { + .init = scoop_init, + .qdev.name = "scoop", + .qdev.desc = "Scoop2 Sharp custom ASIC", + .qdev.size = sizeof(ScoopInfo), + .qdev.vmsd = &vmstate_scoop_regs, + .qdev.props = (Property[]) { + DEFINE_PROP_END_OF_LIST(), + } +}; - return s; +static void scoop_register(void) +{ + sysbus_register_withprop(&scoop_sysbus_info); } +device_init(scoop_register); /* Write the bootloader parameters memory area. */ @@ -449,10 +449,14 @@ int kvm_check_extension(KVMState *s, unsigned int extension) static int kvm_check_many_ioeventfds(void) { - /* Older kernels have a 6 device limit on the KVM io bus. Find out so we + /* Userspace can use ioeventfd for io notification. This requires a host + * that supports eventfd(2) and an I/O thread; since eventfd does not + * support SIGIO it cannot interrupt the vcpu. + * + * Older kernels have a 6 device limit on the KVM io bus. Find out so we * can avoid creating too many ioeventfds. */ -#ifdef CONFIG_EVENTFD +#if defined(CONFIG_EVENTFD) && defined(CONFIG_IOTHREAD) int ioeventfds[7]; int i, ret = 0; for (i = 0; i < ARRAY_SIZE(ioeventfds); i++) { diff --git a/linux-user/signal.c b/linux-user/signal.c index 0664770c94..b01bd64011 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -1817,9 +1817,10 @@ struct target_sigcontext { /* A Sparc stack frame */ struct sparc_stackf { abi_ulong locals[8]; - abi_ulong ins[6]; - struct sparc_stackf *fp; - abi_ulong callers_pc; + abi_ulong ins[8]; + /* It's simpler to treat fp and callers_pc as elements of ins[] + * since we never need to access them ourselves. + */ char *structptr; abi_ulong xargs[6]; abi_ulong xxargs[1]; @@ -1018,6 +1018,14 @@ static int do_quit(Monitor *mon, const QDict *qdict, QObject **ret_data) static int change_vnc_password(const char *password) { + if (!password || !password[0]) { + if (vnc_display_disable_login(NULL)) { + qerror_report(QERR_SET_PASSWD_FAILED); + return -1; + } + return 0; + } + if (vnc_display_password(NULL, password) < 0) { qerror_report(QERR_SET_PASSWD_FAILED); return -1; @@ -1117,6 +1125,8 @@ static int set_password(Monitor *mon, const QDict *qdict, QObject **ret_data) qerror_report(QERR_INVALID_PARAMETER, "connected"); return -1; } + /* Note that setting an empty password will not disable login through + * this interface. */ rc = vnc_display_password(NULL, password); if (rc != 0) { qerror_report(QERR_SET_PASSWD_FAILED); @@ -2509,8 +2519,9 @@ static void do_wav_capture(Monitor *mon, const QDict *qdict) nchannels = has_channels ? nchannels : 2; if (wav_start_capture (s, path, freq, bits, nchannels)) { - monitor_printf(mon, "Faied to add wave capture\n"); + monitor_printf(mon, "Failed to add wave capture\n"); qemu_free (s); + return; } QLIST_INSERT_HEAD (&capture_head, s, entries); } @@ -4162,7 +4173,7 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon, break; case 'o': { - ssize_t val; + int64_t val; char *end; while (qemu_isspace(*p)) { diff --git a/pc-bios/README b/pc-bios/README index 4b019e08a1..3fc09449fa 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -11,7 +11,7 @@ firmware implementation. The goal is to implement a 100% IEEE 1275-1994 (referred to as Open Firmware) compliant firmware. The included image for PowerPC (for 32 and 64 bit PPC CPUs), Sparc32 - and Sparc64 are built from OpenBIOS SVN revision 859. + and Sparc64 are built from OpenBIOS SVN revision 1018. - The PXE roms come from Rom-o-Matic gPXE 0.9.9 with BANNER_TIMEOUT=0 diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc Binary files differindex cb0af05575..ee6f5ae3b9 100644 --- a/pc-bios/openbios-ppc +++ b/pc-bios/openbios-ppc diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32 Binary files differindex aaff1f00c9..b2dc5c5e7b 100644 --- a/pc-bios/openbios-sparc32 +++ b/pc-bios/openbios-sparc32 diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64 Binary files differindex a1b692e7e5..70a223d573 100644 --- a/pc-bios/openbios-sparc64 +++ b/pc-bios/openbios-sparc64 diff --git a/qemu-common.h b/qemu-common.h index c766b990eb..c7ff280b95 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -153,13 +153,20 @@ int qemu_fls(int i); int qemu_fdatasync(int fd); int fcntl_setfl(int fd, int flag); +/* + * strtosz() suffixes used to specify the default treatment of an + * argument passed to strtosz() without an explicit suffix. + * These should be defined using upper case characters in the range + * A-Z, as strtosz() will use qemu_toupper() on the given argument + * prior to comparison. + */ #define STRTOSZ_DEFSUFFIX_TB 'T' #define STRTOSZ_DEFSUFFIX_GB 'G' #define STRTOSZ_DEFSUFFIX_MB 'M' #define STRTOSZ_DEFSUFFIX_KB 'K' #define STRTOSZ_DEFSUFFIX_B 'B' -ssize_t strtosz(const char *nptr, char **end); -ssize_t strtosz_suffix(const char *nptr, char **end, const char default_suffix); +int64_t strtosz(const char *nptr, char **end); +int64_t strtosz_suffix(const char *nptr, char **end, const char default_suffix); /* path.c */ void init_paths(const char *prefix); @@ -267,12 +274,6 @@ typedef struct VirtIODevice VirtIODevice; typedef uint64_t pcibus_t; -typedef enum { - IF_NONE, - IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN, - IF_COUNT -} BlockInterfaceType; - void cpu_exec_init_all(unsigned long tb_size); /* CPU save/load. */ diff --git a/qemu-img.c b/qemu-img.c index afd9ed2e0e..4a3735811c 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -320,7 +320,7 @@ static int img_create(int argc, char **argv) /* Get image size, if specified */ if (optind < argc) { - ssize_t sval; + int64_t sval; sval = strtosz_suffix(argv[optind++], NULL, STRTOSZ_DEFSUFFIX_B); if (sval < 0) { error_report("Invalid image size specified! You may use k, M, G or " @@ -1068,7 +1068,7 @@ static int img_snapshot(int argc, char **argv) int action = 0; qemu_timeval tv; - bdrv_oflags = BDRV_O_RDWR; + bdrv_oflags = BDRV_O_FLAGS | BDRV_O_RDWR; /* Parse commandline parameters */ for(;;) { c = getopt(argc, argv, "la:c:d:h"); diff --git a/qemu-img.texi b/qemu-img.texi index 1b90ddbcfc..ced64a40ed 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -59,6 +59,13 @@ lists all snapshots in the given image Command description: @table @option +@item check [-f @var{fmt}] @var{filename} + +Perform a consistency check on the disk image @var{filename}. + +Only the formats @code{qcow2}, @code{qed} and @code{vdi} support +consistency checks. + @item create [-f @var{fmt}] [-o @var{options}] @var{filename} [@var{size}] Create the new disk image @var{filename} of size @var{size} and format @@ -107,6 +114,40 @@ they are displayed too. List, apply, create or delete snapshots in image @var{filename}. +@item rebase [-f @var{fmt}] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} + +Changes the backing file of an image. Only the formats @code{qcow2} and +@code{qed} support changing the backing file. + +The backing file is changed to @var{backing_file} and (if the image format of +@var{filename} supports this) the backing file format is changed to +@var{backing_fmt}. + +There are two different modes in which @code{rebase} can operate: +@table @option +@item Safe mode +This is the default mode and performs a real rebase operation. The new backing +file may differ from the old one and qemu-img rebase will take care of keeping +the guest-visible content of @var{filename} unchanged. + +In order to achieve this, any clusters that differ between @var{backing_file} +and the old backing file of @var{filename} are merged into @var{filename} +before actually changing the backing file. + +Note that the safe mode is an expensive operation, comparable to converting +an image. It only works if the old backing file still exists. + +@item Unsafe mode +qemu-img uses the unsafe mode if @code{-u} is specified. In this mode, only the +backing file name and format of @var{filename} is changed without any checks +on the file contents. The user must take care of specifying the correct new +backing file, or the guest-visible content of the image will be corrupted. + +This mode is useful for renaming or moving the backing file to somewhere else. +It can be used without an accessible old backing file, i.e. you can use it to +fix an image whose backing file has already been moved/renamed. +@end table + @item resize @var{filename} [+ | -]@var{size} Change the disk image as if it had been created with @var{size}. @@ -1465,7 +1465,7 @@ discard_f(int argc, char **argv) } gettimeofday(&t1, NULL); - ret = bdrv_discard(bs, offset, count); + ret = bdrv_discard(bs, offset >> BDRV_SECTOR_BITS, count >> BDRV_SECTOR_BITS); gettimeofday(&t2, NULL); if (ret < 0) { diff --git a/qmp-commands.hx b/qmp-commands.hx index 56c4d8bc47..9f79f5fdf2 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -601,6 +601,34 @@ Example: -> { "execute": "netdev_del", "arguments": { "id": "netdev1" } } <- { "return": {} } + +EQMP + + { + .name = "block_resize", + .args_type = "device:B,size:o", + .params = "device size", + .help = "resize a block image", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_block_resize, + }, + +SQMP +block_resize +------------ + +Resize a block image while a guest is running. + +Arguments: + +- "device": the device's ID, must be unique (json-string) +- "size": new size + +Example: + +-> { "execute": "block_resize", "arguments": { "device": "scratch", "size": 1073741824 } } +<- { "return": {} } + EQMP { @@ -78,7 +78,6 @@ #include "sysemu.h" #include "qemu-timer.h" #include "qemu-char.h" -#include "blockdev.h" #include "audio/audio.h" #include "migration.h" #include "qemu_socket.h" diff --git a/scripts/tracetool b/scripts/tracetool index fce491c505..e04668322d 100755 --- a/scripts/tracetool +++ b/scripts/tracetool @@ -13,12 +13,13 @@ set -f usage() { cat >&2 <<EOF -usage: $0 [--nop | --simple | --ust] [-h | -c] +usage: $0 [--nop | --simple | --stderr | --ust | --dtrace] [-h | -c] Generate tracing code for a file on stdin. Backends: --nop Tracing disabled --simple Simple built-in backend + --stderr Stderr built-in backend --ust LTTng User Space Tracing backend --dtrace DTrace/SystemTAP backend @@ -236,6 +237,56 @@ linetoc_end_simple() EOF } +#STDERR +linetoh_begin_stderr() +{ + cat <<EOF +#include <stdio.h> +EOF +} + +linetoh_stderr() +{ + local name args argnames argc fmt + name=$(get_name "$1") + args=$(get_args "$1") + argnames=$(get_argnames "$1" ",") + argc=$(get_argc "$1") + fmt=$(get_fmt "$1") + + if [ "$argc" -gt 0 ]; then + argnames=", $argnames" + fi + + cat <<EOF +static inline void trace_$name($args) +{ + fprintf(stderr, "$name $fmt\n" $argnames); +} +EOF +} + +linetoh_end_stderr() +{ +return +} + +linetoc_begin_stderr() +{ +return +} + +linetoc_stderr() +{ +return +} + +linetoc_end_stderr() +{ +return +} +#END OF STDERR + # Clean up after UST headers which pollute the namespace ust_clean_namespace() { cat <<EOF @@ -546,7 +597,7 @@ targetarch= until [ -z "$1" ] do case "$1" in - "--nop" | "--simple" | "--ust" | "--dtrace") backend="${1#--}" ;; + "--nop" | "--simple" | "--stderr" | "--ust" | "--dtrace") backend="${1#--}" ;; "--binary") shift ; binary="$1" ;; "--target-arch") shift ; targetarch="$1" ;; @@ -557,6 +608,11 @@ do "--check-backend") exit 0 ;; # used by ./configure to test for backend + "--list-backends") # used by ./configure to list available backends + echo "nop simple stderr ust dtrace" + exit 0 + ;; + *) usage;; esac @@ -178,21 +178,6 @@ extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) -#ifdef HAS_AUDIO -struct soundhw { - const char *name; - const char *descr; - int enabled; - int isa; - union { - int (*init_isa) (qemu_irq *pic); - int (*init_pci) (PCIBus *bus); - } init; -}; - -extern struct soundhw soundhw[]; -#endif - void do_usb_add(Monitor *mon, const QDict *qdict); void do_usb_del(Monitor *mon, const QDict *qdict); void usb_info(Monitor *mon); diff --git a/target-arm/neon_helper.c b/target-arm/neon_helper.c index 20f3c162cd..fead1525c4 100644 --- a/target-arm/neon_helper.c +++ b/target-arm/neon_helper.c @@ -880,8 +880,9 @@ uint32_t HELPER(neon_cnt_u8)(uint32_t x) if ((tmp ^ (tmp << 1)) & SIGNBIT) { \ SET_QC(); \ tmp = (tmp >> 31) ^ ~SIGNBIT; \ + } else { \ + tmp <<= 1; \ } \ - tmp <<= 1; \ if (round) { \ int32_t old = tmp; \ tmp += 1 << 15; \ diff --git a/target-arm/translate.c b/target-arm/translate.c index c60cd18e1e..d95133f725 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -3608,14 +3608,14 @@ static inline TCGv neon_get_scalar(int size, int reg) { TCGv tmp; if (size == 1) { - tmp = neon_load_reg(reg >> 1, reg & 1); - } else { - tmp = neon_load_reg(reg >> 2, (reg >> 1) & 1); - if (reg & 1) { - gen_neon_dup_low16(tmp); - } else { + tmp = neon_load_reg(reg & 7, reg >> 4); + if (reg & 8) { gen_neon_dup_high16(tmp); + } else { + gen_neon_dup_low16(tmp); } + } else { + tmp = neon_load_reg(reg & 15, reg >> 4); } return tmp; } @@ -4183,6 +4183,13 @@ static inline void gen_neon_mull(TCGv_i64 dest, TCGv a, TCGv b, int size, int u) break; default: abort(); } + + /* gen_helper_neon_mull_[su]{8|16} do not free their parameters. + Don't forget to clean them now. */ + if (size < 2) { + dead_tmp(a); + dead_tmp(b); + } } /* Translate a NEON data processing instruction. Return nonzero if the @@ -4847,7 +4854,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) if (size == 3) { tcg_temp_free_i64(tmp64); } else { - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } } else if (op == 10) { /* VSHLL */ @@ -5083,8 +5090,6 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) case 8: case 9: case 10: case 11: case 12: case 13: /* VMLAL, VQDMLAL, VMLSL, VQDMLSL, VMULL, VQDMULL */ gen_neon_mull(cpu_V0, tmp, tmp2, size, u); - dead_tmp(tmp2); - dead_tmp(tmp); break; case 14: /* Polynomial VMULL */ cpu_abort(env, "Polynomial VMULL not implemented"); @@ -5235,6 +5240,10 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) return 1; tmp2 = neon_get_scalar(size, rm); + /* We need a copy of tmp2 because gen_neon_mull + * deletes it during pass 0. */ + tmp4 = new_tmp(); + tcg_gen_mov_i32(tmp4, tmp2); tmp3 = neon_load_reg(rn, 1); for (pass = 0; pass < 2; pass++) { @@ -5242,9 +5251,9 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) tmp = neon_load_reg(rn, 0); } else { tmp = tmp3; + tmp2 = tmp4; } gen_neon_mull(cpu_V0, tmp, tmp2, size, u); - dead_tmp(tmp); if (op == 6 || op == 7) { gen_neon_negl(cpu_V0, size); } @@ -5271,7 +5280,6 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) neon_store_reg64(cpu_V0, rd + pass); } - dead_tmp(tmp2); break; default: /* 14 and 15 are RESERVED */ @@ -6888,27 +6896,23 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) tcg_gen_shli_i32(tmp, tmp, shift); } sh = (insn >> 16) & 0x1f; - if (sh != 0) { - tmp2 = tcg_const_i32(sh); - if (insn & (1 << 22)) - gen_helper_usat(tmp, tmp, tmp2); - else - gen_helper_ssat(tmp, tmp, tmp2); - tcg_temp_free_i32(tmp2); - } + tmp2 = tcg_const_i32(sh); + if (insn & (1 << 22)) + gen_helper_usat(tmp, tmp, tmp2); + else + gen_helper_ssat(tmp, tmp, tmp2); + tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); } else if ((insn & 0x00300fe0) == 0x00200f20) { /* [us]sat16 */ tmp = load_reg(s, rm); sh = (insn >> 16) & 0x1f; - if (sh != 0) { - tmp2 = tcg_const_i32(sh); - if (insn & (1 << 22)) - gen_helper_usat16(tmp, tmp, tmp2); - else - gen_helper_ssat16(tmp, tmp, tmp2); - tcg_temp_free_i32(tmp2); - } + tmp2 = tcg_const_i32(sh); + if (insn & (1 << 22)) + gen_helper_usat16(tmp, tmp, tmp2); + else + gen_helper_ssat16(tmp, tmp, tmp2); + tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); } else if ((insn & 0x00700fe0) == 0x00000fa0) { /* Select bytes. */ diff --git a/target-cris/translate.c b/target-cris/translate.c index f4cc1252a5..b4648a01de 100644 --- a/target-cris/translate.c +++ b/target-cris/translate.c @@ -947,15 +947,8 @@ static void gen_tst_cc (DisasContext *dc, TCGv cc, int cond) case CC_EQ: if ((arith_opt || move_opt) && dc->cc_x_uptodate != (2 | X_FLAG)) { - /* If cc_result is zero, T0 should be - non-zero otherwise T0 should be zero. */ - int l1; - l1 = gen_new_label(); - tcg_gen_movi_tl(cc, 0); - tcg_gen_brcondi_tl(TCG_COND_NE, cc_result, - 0, l1); - tcg_gen_movi_tl(cc, 1); - gen_set_label(l1); + tcg_gen_setcond_tl(TCG_COND_EQ, cc, + cc_result, tcg_const_tl(0)); } else { cris_evaluate_flags(dc); diff --git a/target-mips/translate.c b/target-mips/translate.c index 187930e3d2..0f93e2abb1 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -1066,7 +1066,7 @@ static void gen_ld (CPUState *env, DisasContext *ctx, uint32_t opc, opn = "ld"; break; case OPC_LLD: - save_cpu_state(ctx, 0); + save_cpu_state(ctx, 1); op_ld_lld(t0, t0, ctx); gen_store_gpr(t0, rt); opn = "lld"; @@ -1086,7 +1086,7 @@ static void gen_ld (CPUState *env, DisasContext *ctx, uint32_t opc, opn = "ldr"; break; case OPC_LDPC: - save_cpu_state(ctx, 1); + save_cpu_state(ctx, 0); tcg_gen_movi_tl(t1, pc_relative_pc(ctx)); gen_op_addr_add(ctx, t0, t0, t1); op_ld_ld(t0, t0, ctx); @@ -1095,7 +1095,7 @@ static void gen_ld (CPUState *env, DisasContext *ctx, uint32_t opc, break; #endif case OPC_LWPC: - save_cpu_state(ctx, 1); + save_cpu_state(ctx, 0); tcg_gen_movi_tl(t1, pc_relative_pc(ctx)); gen_op_addr_add(ctx, t0, t0, t1); op_ld_lw(t0, t0, ctx); @@ -1238,7 +1238,7 @@ static void gen_st_cond (DisasContext *ctx, uint32_t opc, int rt, switch (opc) { #if defined(TARGET_MIPS64) case OPC_SCD: - save_cpu_state(ctx, 0); + save_cpu_state(ctx, 1); op_st_scd(t1, t0, rt, ctx); opn = "scd"; break; @@ -9971,7 +9971,7 @@ static void gen_ldst_pair (DisasContext *ctx, uint32_t opc, int rd, opn = "lwp"; break; case SWP: - save_cpu_state(ctx, 1); + save_cpu_state(ctx, 0); gen_load_gpr(t1, rd); op_st_sw(t1, t0, ctx); tcg_gen_movi_tl(t1, 4); @@ -9992,7 +9992,7 @@ static void gen_ldst_pair (DisasContext *ctx, uint32_t opc, int rd, opn = "ldp"; break; case SDP: - save_cpu_state(ctx, 1); + save_cpu_state(ctx, 0); gen_load_gpr(t1, rd); op_st_sd(t1, t0, ctx); tcg_gen_movi_tl(t1, 8); diff --git a/target-sh4/cpu.h b/target-sh4/cpu.h index 95df6d2a75..789d1880b7 100644 --- a/target-sh4/cpu.h +++ b/target-sh4/cpu.h @@ -201,10 +201,22 @@ void do_interrupt(CPUSH4State * env); void sh4_cpu_list(FILE *f, fprintf_function cpu_fprintf); #if !defined(CONFIG_USER_ONLY) void cpu_sh4_invalidate_tlb(CPUSH4State *s); +uint32_t cpu_sh4_read_mmaped_itlb_addr(CPUSH4State *s, + target_phys_addr_t addr); void cpu_sh4_write_mmaped_itlb_addr(CPUSH4State *s, target_phys_addr_t addr, - uint32_t mem_value); + uint32_t mem_value); +uint32_t cpu_sh4_read_mmaped_itlb_data(CPUSH4State *s, + target_phys_addr_t addr); +void cpu_sh4_write_mmaped_itlb_data(CPUSH4State *s, target_phys_addr_t addr, + uint32_t mem_value); +uint32_t cpu_sh4_read_mmaped_utlb_addr(CPUSH4State *s, + target_phys_addr_t addr); void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, target_phys_addr_t addr, - uint32_t mem_value); + uint32_t mem_value); +uint32_t cpu_sh4_read_mmaped_utlb_data(CPUSH4State *s, + target_phys_addr_t addr); +void cpu_sh4_write_mmaped_utlb_data(CPUSH4State *s, target_phys_addr_t addr, + uint32_t mem_value); #endif int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr); diff --git a/target-sh4/helper.c b/target-sh4/helper.c index 45449ea64e..d2038bd842 100644 --- a/target-sh4/helper.c +++ b/target-sh4/helper.c @@ -453,6 +453,10 @@ int cpu_sh4_handle_mmu_fault(CPUState * env, target_ulong address, int rw, if (ret != MMU_OK) { env->tea = address; + if (ret != MMU_DTLB_MULTIPLE && ret != MMU_ITLB_MULTIPLE) { + env->pteh = (env->pteh & PTEH_ASID_MASK) | + (address & PTEH_VPN_MASK); + } switch (ret) { case MMU_ITLB_MISS: case MMU_DTLB_MISS_READ: @@ -479,7 +483,7 @@ int cpu_sh4_handle_mmu_fault(CPUState * env, target_ulong address, int rw, break; case MMU_IADDR_ERROR: case MMU_DADDR_ERROR_READ: - env->exception_index = 0x0c0; + env->exception_index = 0x0e0; break; case MMU_DADDR_ERROR_WRITE: env->exception_index = 0x100; @@ -559,14 +563,25 @@ void cpu_load_tlb(CPUSH4State * env) entry->v = 0; } /* ITLB */ - for (i = 0; i < UTLB_SIZE; i++) { - tlb_t * entry = &s->utlb[i]; + for (i = 0; i < ITLB_SIZE; i++) { + tlb_t * entry = &s->itlb[i]; entry->v = 0; } tlb_flush(s, 1); } +uint32_t cpu_sh4_read_mmaped_itlb_addr(CPUSH4State *s, + target_phys_addr_t addr) +{ + int index = (addr & 0x00000300) >> 8; + tlb_t * entry = &s->itlb[index]; + + return (entry->vpn << 10) | + (entry->v << 8) | + (entry->asid); +} + void cpu_sh4_write_mmaped_itlb_addr(CPUSH4State *s, target_phys_addr_t addr, uint32_t mem_value) { @@ -574,7 +589,7 @@ void cpu_sh4_write_mmaped_itlb_addr(CPUSH4State *s, target_phys_addr_t addr, uint8_t v = (uint8_t)((mem_value & 0x00000100) >> 8); uint8_t asid = (uint8_t)(mem_value & 0x000000ff); - int index = (addr & 0x00003f00) >> 8; + int index = (addr & 0x00000300) >> 8; tlb_t * entry = &s->itlb[index]; if (entry->v) { /* Overwriting valid entry in itlb. */ @@ -586,6 +601,70 @@ void cpu_sh4_write_mmaped_itlb_addr(CPUSH4State *s, target_phys_addr_t addr, entry->v = v; } +uint32_t cpu_sh4_read_mmaped_itlb_data(CPUSH4State *s, + target_phys_addr_t addr) +{ + int array = (addr & 0x00800000) >> 23; + int index = (addr & 0x00000300) >> 8; + tlb_t * entry = &s->itlb[index]; + + if (array == 0) { + /* ITLB Data Array 1 */ + return (entry->ppn << 10) | + (entry->v << 8) | + (entry->pr << 5) | + ((entry->sz & 1) << 6) | + ((entry->sz & 2) << 4) | + (entry->c << 3) | + (entry->sh << 1); + } else { + /* ITLB Data Array 2 */ + return (entry->tc << 1) | + (entry->sa); + } +} + +void cpu_sh4_write_mmaped_itlb_data(CPUSH4State *s, target_phys_addr_t addr, + uint32_t mem_value) +{ + int array = (addr & 0x00800000) >> 23; + int index = (addr & 0x00000300) >> 8; + tlb_t * entry = &s->itlb[index]; + + if (array == 0) { + /* ITLB Data Array 1 */ + if (entry->v) { + /* Overwriting valid entry in utlb. */ + target_ulong address = entry->vpn << 10; + tlb_flush_page(s, address); + } + entry->ppn = (mem_value & 0x1ffffc00) >> 10; + entry->v = (mem_value & 0x00000100) >> 8; + entry->sz = (mem_value & 0x00000080) >> 6 | + (mem_value & 0x00000010) >> 4; + entry->pr = (mem_value & 0x00000040) >> 5; + entry->c = (mem_value & 0x00000008) >> 3; + entry->sh = (mem_value & 0x00000002) >> 1; + } else { + /* ITLB Data Array 2 */ + entry->tc = (mem_value & 0x00000008) >> 3; + entry->sa = (mem_value & 0x00000007); + } +} + +uint32_t cpu_sh4_read_mmaped_utlb_addr(CPUSH4State *s, + target_phys_addr_t addr) +{ + int index = (addr & 0x00003f00) >> 8; + tlb_t * entry = &s->utlb[index]; + + increment_urc(s); /* per utlb access */ + + return (entry->vpn << 10) | + (entry->v << 8) | + (entry->asid); +} + void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, target_phys_addr_t addr, uint32_t mem_value) { @@ -658,6 +737,65 @@ void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, target_phys_addr_t addr, } } +uint32_t cpu_sh4_read_mmaped_utlb_data(CPUSH4State *s, + target_phys_addr_t addr) +{ + int array = (addr & 0x00800000) >> 23; + int index = (addr & 0x00003f00) >> 8; + tlb_t * entry = &s->utlb[index]; + + increment_urc(s); /* per utlb access */ + + if (array == 0) { + /* ITLB Data Array 1 */ + return (entry->ppn << 10) | + (entry->v << 8) | + (entry->pr << 5) | + ((entry->sz & 1) << 6) | + ((entry->sz & 2) << 4) | + (entry->c << 3) | + (entry->d << 2) | + (entry->sh << 1) | + (entry->wt); + } else { + /* ITLB Data Array 2 */ + return (entry->tc << 1) | + (entry->sa); + } +} + +void cpu_sh4_write_mmaped_utlb_data(CPUSH4State *s, target_phys_addr_t addr, + uint32_t mem_value) +{ + int array = (addr & 0x00800000) >> 23; + int index = (addr & 0x00003f00) >> 8; + tlb_t * entry = &s->utlb[index]; + + increment_urc(s); /* per utlb access */ + + if (array == 0) { + /* UTLB Data Array 1 */ + if (entry->v) { + /* Overwriting valid entry in utlb. */ + target_ulong address = entry->vpn << 10; + tlb_flush_page(s, address); + } + entry->ppn = (mem_value & 0x1ffffc00) >> 10; + entry->v = (mem_value & 0x00000100) >> 8; + entry->sz = (mem_value & 0x00000080) >> 6 | + (mem_value & 0x00000010) >> 4; + entry->pr = (mem_value & 0x00000060) >> 5; + entry->c = (mem_value & 0x00000008) >> 3; + entry->d = (mem_value & 0x00000004) >> 2; + entry->sh = (mem_value & 0x00000002) >> 1; + entry->wt = (mem_value & 0x00000001); + } else { + /* UTLB Data Array 2 */ + entry->tc = (mem_value & 0x00000008) >> 3; + entry->sa = (mem_value & 0x00000007); + } +} + int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr) { int n; diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h index 7225b2ed3c..320530ec9f 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -252,20 +252,24 @@ typedef struct sparc_def_t { uint32_t maxtl; } sparc_def_t; -#define CPU_FEATURE_FLOAT (1 << 0) -#define CPU_FEATURE_FLOAT128 (1 << 1) -#define CPU_FEATURE_SWAP (1 << 2) -#define CPU_FEATURE_MUL (1 << 3) -#define CPU_FEATURE_DIV (1 << 4) -#define CPU_FEATURE_FLUSH (1 << 5) -#define CPU_FEATURE_FSQRT (1 << 6) -#define CPU_FEATURE_FMUL (1 << 7) -#define CPU_FEATURE_VIS1 (1 << 8) -#define CPU_FEATURE_VIS2 (1 << 9) -#define CPU_FEATURE_FSMULD (1 << 10) -#define CPU_FEATURE_HYPV (1 << 11) -#define CPU_FEATURE_CMT (1 << 12) -#define CPU_FEATURE_GL (1 << 13) +#define CPU_FEATURE_FLOAT (1 << 0) +#define CPU_FEATURE_FLOAT128 (1 << 1) +#define CPU_FEATURE_SWAP (1 << 2) +#define CPU_FEATURE_MUL (1 << 3) +#define CPU_FEATURE_DIV (1 << 4) +#define CPU_FEATURE_FLUSH (1 << 5) +#define CPU_FEATURE_FSQRT (1 << 6) +#define CPU_FEATURE_FMUL (1 << 7) +#define CPU_FEATURE_VIS1 (1 << 8) +#define CPU_FEATURE_VIS2 (1 << 9) +#define CPU_FEATURE_FSMULD (1 << 10) +#define CPU_FEATURE_HYPV (1 << 11) +#define CPU_FEATURE_CMT (1 << 12) +#define CPU_FEATURE_GL (1 << 13) +#define CPU_FEATURE_TA0_SHUTDOWN (1 << 14) /* Shutdown on "ta 0x0" */ +#define CPU_FEATURE_ASR17 (1 << 15) +#define CPU_FEATURE_CACHE_CTRL (1 << 16) + #ifndef TARGET_SPARC64 #define CPU_DEFAULT_FEATURES (CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | \ CPU_FEATURE_MUL | CPU_FEATURE_DIV | \ @@ -437,6 +441,12 @@ typedef struct CPUSPARCState { #define SOFTINT_REG_MASK (SOFTINT_STIMER|SOFTINT_INTRMASK|SOFTINT_TIMER) #endif sparc_def_t *def; + + void *irq_manager; + void (*qemu_irq_ack) (void *irq_manager, int intno); + + /* Leon3 cache control */ + uint32_t cache_control; } CPUSPARCState; #ifndef NO_CPU_IO_DEFS @@ -468,10 +478,14 @@ void cpu_put_cwp64(CPUState *env1, int cwp); int cpu_cwp_inc(CPUState *env1, int cwp); int cpu_cwp_dec(CPUState *env1, int cwp); void cpu_set_cwp(CPUState *env1, int new_cwp); +void leon3_irq_manager(void *irq_manager, int intno); /* sun4m.c, sun4u.c */ void cpu_check_irqs(CPUSPARCState *env); +/* leon3.c */ +void leon3_irq_ack(void *irq_manager, int intno); + #if defined (TARGET_SPARC64) static inline int compare_masked(uint64_t x, uint64_t y, uint64_t mask) diff --git a/target-sparc/helper.c b/target-sparc/helper.c index 6b337ca091..b2d4d70a11 100644 --- a/target-sparc/helper.c +++ b/target-sparc/helper.c @@ -770,6 +770,7 @@ void cpu_reset(CPUSPARCState *env) env->pc = 0; env->npc = env->pc + 4; #endif + env->cache_control = 0; } static int cpu_sparc_register(CPUSPARCState *env, const char *cpu_model) @@ -1274,20 +1275,21 @@ static const sparc_def_t sparc_defs[] = { .mmu_sfsr_mask = 0xffffffff, .mmu_trcr_mask = 0xffffffff, .nwindows = 8, - .features = CPU_DEFAULT_FEATURES, + .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN, }, { .name = "LEON3", .iu_version = 0xf3000000, .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */ .mmu_version = 0xf3000000, - .mmu_bm = 0x00004000, + .mmu_bm = 0x00000000, .mmu_ctpr_mask = 0x007ffff0, .mmu_cxr_mask = 0x0000003f, .mmu_sfsr_mask = 0xffffffff, .mmu_trcr_mask = 0xffffffff, .nwindows = 8, - .features = CPU_DEFAULT_FEATURES, + .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN | + CPU_FEATURE_ASR17 | CPU_FEATURE_CACHE_CTRL, }, #endif }; diff --git a/target-sparc/helper.h b/target-sparc/helper.h index e6d82f9eab..12e8557133 100644 --- a/target-sparc/helper.h +++ b/target-sparc/helper.h @@ -85,6 +85,7 @@ DEF_HELPER_0(fcmpeq_fcc2, void) DEF_HELPER_0(fcmpeq_fcc3, void) #endif DEF_HELPER_1(raise_exception, void, int) +DEF_HELPER_0(shutdown, void) #define F_HELPER_0_0(name) DEF_HELPER_0(f ## name, void) #define F_HELPER_DQ_0_0(name) \ F_HELPER_0_0(name ## d); \ diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index b70970ac2a..854f168c60 100644 --- a/target-sparc/op_helper.c +++ b/target-sparc/op_helper.c @@ -1,6 +1,7 @@ #include "exec.h" #include "host-utils.h" #include "helper.h" +#include "sysemu.h" //#define DEBUG_MMU //#define DEBUG_MXCC @@ -9,6 +10,7 @@ //#define DEBUG_ASI //#define DEBUG_PCALL //#define DEBUG_PSTATE +//#define DEBUG_CACHE_CONTROL #ifdef DEBUG_MMU #define DPRINTF_MMU(fmt, ...) \ @@ -36,6 +38,13 @@ #define DPRINTF_PSTATE(fmt, ...) do {} while (0) #endif +#ifdef DEBUG_CACHE_CONTROL +#define DPRINTF_CACHE_CONTROL(fmt, ...) \ + do { printf("CACHE_CONTROL: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF_CACHE_CONTROL(fmt, ...) do {} while (0) +#endif + #ifdef TARGET_SPARC64 #ifndef TARGET_ABI32 #define AM_CHECK(env1) ((env1)->pstate & PS_AM) @@ -49,6 +58,27 @@ #define QT0 (env->qt0) #define QT1 (env->qt1) +/* Leon3 cache control */ + +/* Cache control: emulate the behavior of cache control registers but without + any effect on the emulated */ + +#define CACHE_STATE_MASK 0x3 +#define CACHE_DISABLED 0x0 +#define CACHE_FROZEN 0x1 +#define CACHE_ENABLED 0x3 + +/* Cache Control register fields */ + +#define CACHE_CTRL_IF (1 << 4) /* Instruction Cache Freeze on Interrupt */ +#define CACHE_CTRL_DF (1 << 5) /* Data Cache Freeze on Interrupt */ +#define CACHE_CTRL_DP (1 << 14) /* Data cache flush pending */ +#define CACHE_CTRL_IP (1 << 15) /* Instruction cache flush pending */ +#define CACHE_CTRL_IB (1 << 16) /* Instruction burst fetch */ +#define CACHE_CTRL_FI (1 << 21) /* Flush Instruction cache (Write only) */ +#define CACHE_CTRL_FD (1 << 22) /* Flush Data cache (Write only) */ +#define CACHE_CTRL_DS (1 << 23) /* Data cache snoop enable */ + #if defined(CONFIG_USER_ONLY) && defined(TARGET_SPARC64) static void do_unassigned_access(target_ulong addr, int is_write, int is_exec, int is_asi, int size); @@ -294,6 +324,13 @@ void HELPER(raise_exception)(int tt) raise_exception(tt); } +void helper_shutdown(void) +{ +#if !defined(CONFIG_USER_ONLY) + qemu_system_shutdown_request(); +#endif +} + void helper_check_align(target_ulong addr, uint32_t align) { if (addr & align) { @@ -1612,6 +1649,109 @@ static void dump_asi(const char *txt, target_ulong addr, int asi, int size, #ifndef TARGET_SPARC64 #ifndef CONFIG_USER_ONLY + + +/* Leon3 cache control */ + +static void leon3_cache_control_int(void) +{ + uint32_t state = 0; + + if (env->cache_control & CACHE_CTRL_IF) { + /* Instruction cache state */ + state = env->cache_control & CACHE_STATE_MASK; + if (state == CACHE_ENABLED) { + state = CACHE_FROZEN; + DPRINTF_CACHE_CONTROL("Instruction cache: freeze\n"); + } + + env->cache_control &= ~CACHE_STATE_MASK; + env->cache_control |= state; + } + + if (env->cache_control & CACHE_CTRL_DF) { + /* Data cache state */ + state = (env->cache_control >> 2) & CACHE_STATE_MASK; + if (state == CACHE_ENABLED) { + state = CACHE_FROZEN; + DPRINTF_CACHE_CONTROL("Data cache: freeze\n"); + } + + env->cache_control &= ~(CACHE_STATE_MASK << 2); + env->cache_control |= (state << 2); + } +} + +static void leon3_cache_control_st(target_ulong addr, uint64_t val, int size) +{ + DPRINTF_CACHE_CONTROL("st addr:%08x, val:%" PRIx64 ", size:%d\n", + addr, val, size); + + if (size != 4) { + DPRINTF_CACHE_CONTROL("32bits only\n"); + return; + } + + switch (addr) { + case 0x00: /* Cache control */ + + /* These values must always be read as zeros */ + val &= ~CACHE_CTRL_FD; + val &= ~CACHE_CTRL_FI; + val &= ~CACHE_CTRL_IB; + val &= ~CACHE_CTRL_IP; + val &= ~CACHE_CTRL_DP; + + env->cache_control = val; + break; + case 0x04: /* Instruction cache configuration */ + case 0x08: /* Data cache configuration */ + /* Read Only */ + break; + default: + DPRINTF_CACHE_CONTROL("write unknown register %08x\n", addr); + break; + }; +} + +static uint64_t leon3_cache_control_ld(target_ulong addr, int size) +{ + uint64_t ret = 0; + + if (size != 4) { + DPRINTF_CACHE_CONTROL("32bits only\n"); + return 0; + } + + switch (addr) { + case 0x00: /* Cache control */ + ret = env->cache_control; + break; + + /* Configuration registers are read and only always keep those + predefined values */ + + case 0x04: /* Instruction cache configuration */ + ret = 0x10220000; + break; + case 0x08: /* Data cache configuration */ + ret = 0x18220000; + break; + default: + DPRINTF_CACHE_CONTROL("read unknown register %08x\n", addr); + break; + }; + DPRINTF_CACHE_CONTROL("ld addr:%08x, ret:0x%" PRIx64 ", size:%d\n", + addr, ret, size); + return ret; +} + +void leon3_irq_manager(void *irq_manager, int intno) +{ + leon3_irq_ack(irq_manager, intno); + leon3_cache_control_int(); +} + uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign) { uint64_t ret = 0; @@ -1621,8 +1761,15 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign) helper_check_align(addr, size - 1); switch (asi) { - case 2: /* SuperSparc MXCC registers */ + case 2: /* SuperSparc MXCC registers and Leon3 cache control */ switch (addr) { + case 0x00: /* Leon3 Cache Control */ + case 0x08: /* Leon3 Instruction Cache config */ + case 0x0C: /* Leon3 Date Cache config */ + if (env->def->features & CPU_FEATURE_CACHE_CTRL) { + ret = leon3_cache_control_ld(addr, size); + } + break; case 0x01c00a00: /* MXCC control register */ if (size == 8) ret = env->mxccregs[3]; @@ -1850,8 +1997,16 @@ void helper_st_asi(target_ulong addr, uint64_t val, int asi, int size) { helper_check_align(addr, size - 1); switch(asi) { - case 2: /* SuperSparc MXCC registers */ + case 2: /* SuperSparc MXCC registers and Leon3 cache control */ switch (addr) { + case 0x00: /* Leon3 Cache Control */ + case 0x08: /* Leon3 Instruction Cache config */ + case 0x0C: /* Leon3 Date Cache config */ + if (env->def->features & CPU_FEATURE_CACHE_CTRL) { + leon3_cache_control_st(addr, val, size); + } + break; + case 0x01c00000: /* MXCC stream data register 0 */ if (size == 8) env->mxccdata[0] = val; @@ -4177,6 +4332,13 @@ void do_interrupt(CPUState *env) env->pc = env->tbr; env->npc = env->pc + 4; env->exception_index = -1; + +#if !defined(CONFIG_USER_ONLY) + /* IRQ acknowledgment */ + if ((intno & ~15) == TT_EXTINT && env->qemu_irq_ack != NULL) { + env->qemu_irq_ack(env->irq_manager, intno); + } +#endif } #endif diff --git a/target-sparc/translate.c b/target-sparc/translate.c index 21c567562e..e26462eef5 100644 --- a/target-sparc/translate.c +++ b/target-sparc/translate.c @@ -1997,8 +1997,9 @@ static void disas_sparc_insn(DisasContext * dc) } else tcg_gen_mov_tl(cpu_dst, cpu_src1); } + cond = GET_FIELD(insn, 3, 6); - if (cond == 0x8) { + if (cond == 0x8) { /* Trap Always */ save_state(dc, cpu_cond); if ((dc->def->features & CPU_FEATURE_HYPV) && supervisor(dc)) @@ -2007,7 +2008,15 @@ static void disas_sparc_insn(DisasContext * dc) tcg_gen_andi_tl(cpu_dst, cpu_dst, V8_TRAP_MASK); tcg_gen_addi_tl(cpu_dst, cpu_dst, TT_TRAP); tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_dst); - gen_helper_raise_exception(cpu_tmp32); + + if (rs2 == 0 && + dc->def->features & CPU_FEATURE_TA0_SHUTDOWN) { + + gen_helper_shutdown(); + + } else { + gen_helper_raise_exception(cpu_tmp32); + } } else if (cond != 0) { TCGv r_cond = tcg_temp_new(); int l1; @@ -2058,6 +2067,17 @@ static void disas_sparc_insn(DisasContext * dc) case 0x10 ... 0x1f: /* implementation-dependent in the SPARCv8 manual, rdy on the microSPARC II */ + /* Read Asr17 */ + if (rs1 == 0x11 && dc->def->features & CPU_FEATURE_ASR17) { + TCGv r_const; + + /* Read Asr17 for a Leon3 monoprocessor */ + r_const = tcg_const_tl((1 << 8) + | (dc->def->nwindows - 1)); + gen_movl_TN_reg(rd, r_const); + tcg_temp_free(r_const); + break; + } #endif gen_movl_TN_reg(rd, cpu_y); break; diff --git a/trace-events b/trace-events index 19cee6a1d8..c75a7a9824 100644 --- a/trace-events +++ b/trace-events @@ -224,3 +224,27 @@ disable qed_aio_write_data(void *s, void *acb, int ret, uint64_t offset, size_t disable qed_aio_write_prefill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64"" disable qed_aio_write_postfill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64"" disable qed_aio_write_main(void *s, void *acb, int ret, uint64_t offset, size_t len) "s %p acb %p ret %d offset %"PRIu64" len %zu" + +# hw/grlib_gptimer.c +disable grlib_gptimer_enable(int id, uint32_t count) "timer:%d set count 0x%x and run" +disable grlib_gptimer_disabled(int id, uint32_t config) "timer:%d Timer disable config 0x%x" +disable grlib_gptimer_restart(int id, uint32_t reload) "timer:%d reload val: 0x%x" +disable grlib_gptimer_set_scaler(uint32_t scaler, uint32_t freq) "scaler:0x%x freq: 0x%x" +disable grlib_gptimer_hit(int id) "timer:%d HIT" +disable grlib_gptimer_readl(int id, const char *s, uint32_t val) "timer:%d %s 0x%x" +disable grlib_gptimer_writel(int id, const char *s, uint32_t val) "timer:%d %s 0x%x" +disable grlib_gptimer_unknown_register(const char *op, uint64_t val) "%s unknown register 0x%"PRIx64"" + +# hw/grlib_irqmp.c +disable grlib_irqmp_check_irqs(uint32_t pend, uint32_t force, uint32_t mask, uint32_t lvl1, uint32_t lvl2) "pend:0x%04x force:0x%04x mask:0x%04x lvl1:0x%04x lvl0:0x%04x\n" +disable grlib_irqmp_ack(int intno) "interrupt:%d" +disable grlib_irqmp_set_irq(int irq) "Raise CPU IRQ %d" +disable grlib_irqmp_unknown_register(const char *op, uint64_t val) "%s unknown register 0x%"PRIx64"" + +# hw/grlib_apbuart.c +disable grlib_apbuart_event(int event) "event:%d" +disable grlib_apbuart_unknown_register(const char *op, uint64_t val) "%s unknown register 0x%"PRIx64"" + +# hw/leon3.c +disable leon3_set_irq(int intno) "Set CPU IRQ %d" +disable leon3_reset_irq(int intno) "Reset CPU IRQ %d" @@ -87,12 +87,6 @@ static void sdl_update(DisplayState *ds, int x, int y, int w, int h) static void sdl_setdata(DisplayState *ds) { - SDL_Rect rec; - rec.x = 0; - rec.y = 0; - rec.w = real_screen->w; - rec.h = real_screen->h; - if (guest_screen != NULL) SDL_FreeSurface(guest_screen); guest_screen = SDL_CreateRGBSurfaceFrom(ds_get_data(ds), ds_get_width(ds), ds_get_height(ds), @@ -2084,7 +2084,7 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len) unsigned char key[8]; time_t now = time(NULL); - if (!vs->vd->password || !vs->vd->password[0]) { + if (!vs->vd->password) { VNC_DEBUG("No password configured on server"); goto reject; } @@ -2484,6 +2484,24 @@ void vnc_display_close(DisplayState *ds) #endif } +int vnc_display_disable_login(DisplayState *ds) +{ + VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + + if (!vs) { + return -1; + } + + if (vs->password) { + qemu_free(vs->password); + } + + vs->password = NULL; + vs->auth = VNC_AUTH_VNC; + + return 0; +} + int vnc_display_password(DisplayState *ds, const char *password) { VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; @@ -2492,19 +2510,18 @@ int vnc_display_password(DisplayState *ds, const char *password) return -1; } + if (!password) { + /* This is not the intention of this interface but err on the side + of being safe */ + return vnc_display_disable_login(ds); + } + if (vs->password) { qemu_free(vs->password); vs->password = NULL; } - if (password && password[0]) { - if (!(vs->password = qemu_strdup(password))) - return -1; - if (vs->auth == VNC_AUTH_NONE) { - vs->auth = VNC_AUTH_VNC; - } - } else { - vs->auth = VNC_AUTH_NONE; - } + vs->password = qemu_strdup(password); + vs->auth = VNC_AUTH_VNC; return 0; } @@ -621,23 +621,18 @@ static int bt_parse(const char *opt) /***********************************************************/ /* QEMU Block devices */ -#define HD_ALIAS "index=%d,media=disk" -#define CDROM_ALIAS "index=2,media=cdrom" -#define FD_ALIAS "index=%d,if=floppy" -#define PFLASH_ALIAS "if=pflash" -#define MTD_ALIAS "if=mtd" -#define SD_ALIAS "index=0,if=sd" +#define HD_OPTS "media=disk" +#define CDROM_OPTS "media=cdrom" +#define FD_OPTS "" +#define PFLASH_OPTS "" +#define MTD_OPTS "" +#define SD_OPTS "" static int drive_init_func(QemuOpts *opts, void *opaque) { int *use_scsi = opaque; - int fatal_error = 0; - if (drive_init(opts, *use_scsi, &fatal_error) == NULL) { - if (fatal_error) - return 1; - } - return 0; + return drive_init(opts, *use_scsi) == NULL; } static int drive_enable_snapshot(QemuOpts *opts, void *opaque) @@ -648,6 +643,29 @@ static int drive_enable_snapshot(QemuOpts *opts, void *opaque) return 0; } +static void default_drive(int enable, int snapshot, int use_scsi, + BlockInterfaceType type, int index, + const char *optstr) +{ + QemuOpts *opts; + + if (type == IF_DEFAULT) { + type = use_scsi ? IF_SCSI : IF_IDE; + } + + if (!enable || drive_get_by_index(type, index)) { + return; + } + + opts = drive_add(type, index, NULL, optstr); + if (snapshot) { + drive_enable_snapshot(opts, NULL); + } + if (!drive_init(opts, use_scsi)) { + exit(1); + } +} + void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque) { boot_set_handler = func; @@ -804,7 +822,7 @@ static void numa_add(const char *optarg) if (get_param_value(option, 128, "mem", optarg) == 0) { node_mem[nodenr] = 0; } else { - ssize_t sval; + int64_t sval; sval = strtosz(option, NULL); if (sval < 0) { fprintf(stderr, "qemu: invalid numa mem size: %s\n", optarg); @@ -1987,7 +2005,7 @@ int main(int argc, char **argv, char **envp) if (optind >= argc) break; if (argv[optind][0] != '-') { - hda_opts = drive_add(argv[optind++], HD_ALIAS, 0); + hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS); } else { const QEMUOption *popt; @@ -2026,24 +2044,29 @@ int main(int argc, char **argv, char **envp) initrd_filename = optarg; break; case QEMU_OPTION_hda: - if (cyls == 0) - hda_opts = drive_add(optarg, HD_ALIAS, 0); - else - hda_opts = drive_add(optarg, HD_ALIAS - ",cyls=%d,heads=%d,secs=%d%s", - 0, cyls, heads, secs, - translation == BIOS_ATA_TRANSLATION_LBA ? + { + char buf[256]; + if (cyls == 0) + snprintf(buf, sizeof(buf), "%s", HD_OPTS); + else + snprintf(buf, sizeof(buf), + "%s,cyls=%d,heads=%d,secs=%d%s", + HD_OPTS , cyls, heads, secs, + translation == BIOS_ATA_TRANSLATION_LBA ? ",trans=lba" : - translation == BIOS_ATA_TRANSLATION_NONE ? + translation == BIOS_ATA_TRANSLATION_NONE ? ",trans=none" : ""); - break; + drive_add(IF_DEFAULT, 0, optarg, buf); + break; + } case QEMU_OPTION_hdb: case QEMU_OPTION_hdc: case QEMU_OPTION_hdd: - drive_add(optarg, HD_ALIAS, popt->index - QEMU_OPTION_hda); + drive_add(IF_DEFAULT, popt->index - QEMU_OPTION_hda, optarg, + HD_OPTS); break; case QEMU_OPTION_drive: - drive_add(NULL, "%s", optarg); + drive_def(optarg); break; case QEMU_OPTION_set: if (qemu_set_option(optarg) != 0) @@ -2054,13 +2077,13 @@ int main(int argc, char **argv, char **envp) exit(1); break; case QEMU_OPTION_mtdblock: - drive_add(optarg, MTD_ALIAS); + drive_add(IF_MTD, -1, optarg, MTD_OPTS); break; case QEMU_OPTION_sd: - drive_add(optarg, SD_ALIAS); + drive_add(IF_SD, 0, optarg, SD_OPTS); break; case QEMU_OPTION_pflash: - drive_add(optarg, PFLASH_ALIAS); + drive_add(IF_PFLASH, -1, optarg, PFLASH_OPTS); break; case QEMU_OPTION_snapshot: snapshot = 1; @@ -2139,7 +2162,7 @@ int main(int argc, char **argv, char **envp) kernel_cmdline = optarg; break; case QEMU_OPTION_cdrom: - drive_add(optarg, CDROM_ALIAS); + drive_add(IF_DEFAULT, 2, optarg, CDROM_OPTS); break; case QEMU_OPTION_boot: { @@ -2192,7 +2215,8 @@ int main(int argc, char **argv, char **envp) break; case QEMU_OPTION_fda: case QEMU_OPTION_fdb: - drive_add(optarg, FD_ALIAS, popt->index - QEMU_OPTION_fda); + drive_add(IF_FLOPPY, popt->index - QEMU_OPTION_fda, + optarg, FD_OPTS); break; case QEMU_OPTION_no_fd_bootchk: fd_bootchk = 0; @@ -2245,7 +2269,7 @@ int main(int argc, char **argv, char **envp) exit(0); break; case QEMU_OPTION_m: { - ssize_t value; + int64_t value; value = strtosz(optarg, NULL); if (value < 0) { @@ -2890,27 +2914,19 @@ int main(int argc, char **argv, char **envp) blk_mig_init(); - if (default_cdrom) { - /* we always create the cdrom drive, even if no disk is there */ - drive_add(NULL, CDROM_ALIAS); - } - - if (default_floppy) { - /* we always create at least one floppy */ - drive_add(NULL, FD_ALIAS, 0); - } - - if (default_sdcard) { - /* we always create one sd slot, even if no card is in it */ - drive_add(NULL, SD_ALIAS); - } - /* open the virtual block devices */ if (snapshot) qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot, NULL, 0); if (qemu_opts_foreach(qemu_find_opts("drive"), drive_init_func, &machine->use_scsi, 1) != 0) exit(1); + default_drive(default_cdrom, snapshot, machine->use_scsi, + IF_DEFAULT, 2, CDROM_OPTS); + default_drive(default_floppy, snapshot, machine->use_scsi, + IF_FLOPPY, 0, FD_OPTS); + default_drive(default_sdcard, snapshot, machine->use_scsi, + IF_SD, 0, SD_OPTS); + register_savevm_live(NULL, "ram", 0, 4, NULL, ram_save_live, NULL, ram_load, NULL); |