Compare commits
5 Commits
v2.12.0-rc
...
rm-protoco
Author | SHA1 | Date | |
---|---|---|---|
|
6321d78dc5 | ||
|
a0974f99aa | ||
|
42883c01bf | ||
|
7966c2b312 | ||
|
5888011244 |
@@ -49,10 +49,9 @@ env:
|
||||
- TEST_CMD="make check"
|
||||
- MAKEFLAGS="-j3"
|
||||
matrix:
|
||||
- CONFIG="--disable-system"
|
||||
- CONFIG="--disable-user"
|
||||
- CONFIG="--enable-debug --enable-debug-tcg"
|
||||
- CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb --disable-user"
|
||||
- CONFIG=""
|
||||
- CONFIG="--enable-debug --enable-debug-tcg --enable-trace-backends=log"
|
||||
- CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb"
|
||||
- CONFIG="--enable-modules --disable-linux-user"
|
||||
- CONFIG="--with-coroutine=ucontext --disable-linux-user"
|
||||
- CONFIG="--with-coroutine=sigaltstack --disable-linux-user"
|
||||
|
22
MAINTAINERS
22
MAINTAINERS
@@ -127,6 +127,7 @@ Alpha
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
S: Maintained
|
||||
F: target/alpha/
|
||||
F: hw/alpha/
|
||||
F: tests/tcg/alpha/
|
||||
F: disas/alpha.c
|
||||
|
||||
@@ -289,7 +290,7 @@ T: git git://github.com/ehabkost/qemu.git x86-next
|
||||
|
||||
Xtensa
|
||||
M: Max Filippov <jcmvbkbc@gmail.com>
|
||||
W: http://wiki.osll.ru/doku.php?id=etc:users:jcmvbkbc:qemu-target-xtensa
|
||||
W: http://wiki.osll.spb.ru/doku.php?id=etc:users:jcmvbkbc:qemu-target-xtensa
|
||||
S: Maintained
|
||||
F: target/xtensa/
|
||||
F: hw/xtensa/
|
||||
@@ -412,12 +413,6 @@ F: include/*/*win32*
|
||||
X: qga/*win32*
|
||||
F: qemu.nsi
|
||||
|
||||
Alpha Machines
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
S: Maintained
|
||||
F: hw/alpha/
|
||||
F: hw/isa/smc37c669-superio.c
|
||||
|
||||
ARM Machines
|
||||
------------
|
||||
Allwinner-a10
|
||||
@@ -705,8 +700,6 @@ Fulong 2E
|
||||
M: Yongbok Kim <yongbok.kim@mips.com>
|
||||
S: Odd Fixes
|
||||
F: hw/mips/mips_fulong2e.c
|
||||
F: hw/isa/vt82c686.c
|
||||
F: include/hw/isa/vt82c686.h
|
||||
|
||||
Boston
|
||||
M: Paul Burton <paul.burton@mips.com>
|
||||
@@ -783,10 +776,9 @@ F: hw/ppc/prep_systemio.c
|
||||
F: hw/ppc/rs6000_mc.c
|
||||
F: hw/pci-host/prep.[hc]
|
||||
F: hw/isa/i82378.c
|
||||
F: hw/isa/pc87312.c
|
||||
F: hw/isa/pc87312.[hc]
|
||||
F: hw/dma/i82374.c
|
||||
F: hw/timer/m48t59-isa.c
|
||||
F: include/hw/isa/pc87312.h
|
||||
F: include/hw/timer/m48t59.h
|
||||
F: pc-bios/ppc_rom.bin
|
||||
|
||||
@@ -932,7 +924,7 @@ M: Michael S. Tsirkin <mst@redhat.com>
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Supported
|
||||
F: hw/char/debugcon.c
|
||||
F: hw/char/parallel*
|
||||
F: hw/char/parallel.c
|
||||
F: hw/char/serial*
|
||||
F: hw/dma/i8257*
|
||||
F: hw/i2c/pm_smbus.c
|
||||
@@ -940,7 +932,6 @@ F: hw/input/pckbd.c
|
||||
F: hw/intc/apic*
|
||||
F: hw/intc/ioapic*
|
||||
F: hw/intc/i8259*
|
||||
F: hw/isa/isa-superio.c
|
||||
F: hw/misc/debugexit.c
|
||||
F: hw/misc/pc-testdev.c
|
||||
F: hw/timer/hpet*
|
||||
@@ -948,11 +939,8 @@ F: hw/timer/i8254*
|
||||
F: hw/timer/mc146818rtc*
|
||||
F: hw/watchdog/wdt_ib700.c
|
||||
F: include/hw/display/vga.h
|
||||
F: include/hw/char/parallel.h
|
||||
F: include/hw/dma/i8257.h
|
||||
F: include/hw/i2c/pm_smbus.h
|
||||
F: include/hw/input/i8042.h
|
||||
F: include/hw/isa/superio.h
|
||||
F: include/hw/isa/i8257.h
|
||||
F: include/hw/timer/hpet.h
|
||||
F: include/hw/timer/i8254*
|
||||
F: include/hw/timer/mc146818rtc*
|
||||
|
27
Makefile
27
Makefile
@@ -438,23 +438,21 @@ all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules
|
||||
qemu-version.h: FORCE
|
||||
$(call quiet-command, \
|
||||
(cd $(SRC_PATH); \
|
||||
printf '#define QEMU_PKGVERSION '; \
|
||||
if test -n "$(PKGVERSION)"; then \
|
||||
pkgvers="$(PKGVERSION)"; \
|
||||
printf '"$(PKGVERSION)"\n'; \
|
||||
else \
|
||||
if test -d .git; then \
|
||||
pkgvers=$$(git describe --match 'v*' 2>/dev/null | tr -d '\n');\
|
||||
printf '" ('; \
|
||||
git describe --match 'v*' 2>/dev/null | tr -d '\n'; \
|
||||
if ! git diff-index --quiet HEAD &>/dev/null; then \
|
||||
pkgvers="$${pkgvers}-dirty"; \
|
||||
printf -- '-dirty'; \
|
||||
fi; \
|
||||
printf ')"\n'; \
|
||||
else \
|
||||
printf '""\n'; \
|
||||
fi; \
|
||||
fi; \
|
||||
printf "#define QEMU_PKGVERSION \"$${pkgvers}\"\n"; \
|
||||
if test -n "$${pkgvers}"; then \
|
||||
printf '#define QEMU_FULL_VERSION QEMU_VERSION " (" QEMU_PKGVERSION ")"\n'; \
|
||||
else \
|
||||
printf '#define QEMU_FULL_VERSION QEMU_VERSION\n'; \
|
||||
fi; \
|
||||
) > $@.tmp)
|
||||
fi) > $@.tmp)
|
||||
$(call quiet-command, if ! cmp -s $@ $@.tmp; then \
|
||||
mv $@.tmp $@; \
|
||||
else \
|
||||
@@ -777,6 +775,7 @@ bepo cz
|
||||
ifdef INSTALL_BLOBS
|
||||
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
||||
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
|
||||
acpi-dsdt.aml \
|
||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
||||
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
||||
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
||||
@@ -1047,16 +1046,10 @@ endif
|
||||
include $(SRC_PATH)/tests/docker/Makefile.include
|
||||
include $(SRC_PATH)/tests/vm/Makefile.include
|
||||
|
||||
printgen:
|
||||
@echo $(GENERATED_FILES)
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo 'Generic targets:'
|
||||
@echo ' all - Build all'
|
||||
ifdef CONFIG_MODULES
|
||||
@echo ' modules - Build all modules'
|
||||
endif
|
||||
@echo ' dir/file.o - Build specified target only'
|
||||
@echo ' install - Install QEMU, documentation and tools'
|
||||
@echo ' ctags/TAGS - Generate tags file for editors'
|
||||
|
@@ -1,4 +1,4 @@
|
||||
obj-$(CONFIG_SOFTMMU) += accel.o
|
||||
obj-$(CONFIG_KVM) += kvm/
|
||||
obj-y += kvm/
|
||||
obj-$(CONFIG_TCG) += tcg/
|
||||
obj-y += stubs/
|
||||
|
@@ -1,2 +1 @@
|
||||
obj-y += kvm-all.o
|
||||
obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o
|
||||
obj-$(CONFIG_KVM) += kvm-all.o
|
||||
|
@@ -38,7 +38,6 @@
|
||||
#include "qemu/event_notifier.h"
|
||||
#include "trace.h"
|
||||
#include "hw/irq.h"
|
||||
#include "sysemu/sev.h"
|
||||
|
||||
#include "hw/boards.h"
|
||||
|
||||
@@ -104,10 +103,6 @@ struct KVMState
|
||||
#endif
|
||||
KVMMemoryListener memory_listener;
|
||||
QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus;
|
||||
|
||||
/* memory encryption */
|
||||
void *memcrypt_handle;
|
||||
int (*memcrypt_encrypt_data)(void *handle, uint8_t *ptr, uint64_t len);
|
||||
};
|
||||
|
||||
KVMState *kvm_state;
|
||||
@@ -143,26 +138,6 @@ int kvm_get_max_memslots(void)
|
||||
return s->nr_slots;
|
||||
}
|
||||
|
||||
bool kvm_memcrypt_enabled(void)
|
||||
{
|
||||
if (kvm_state && kvm_state->memcrypt_handle) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len)
|
||||
{
|
||||
if (kvm_state->memcrypt_handle &&
|
||||
kvm_state->memcrypt_encrypt_data) {
|
||||
return kvm_state->memcrypt_encrypt_data(kvm_state->memcrypt_handle,
|
||||
ptr, len);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
@@ -1661,20 +1636,6 @@ static int kvm_init(MachineState *ms)
|
||||
|
||||
kvm_state = s;
|
||||
|
||||
/*
|
||||
* if memory encryption object is specified then initialize the memory
|
||||
* encryption context.
|
||||
*/
|
||||
if (ms->memory_encryption) {
|
||||
kvm_state->memcrypt_handle = sev_guest_init(ms->memory_encryption);
|
||||
if (!kvm_state->memcrypt_handle) {
|
||||
ret = -1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
kvm_state->memcrypt_encrypt_data = sev_encrypt_data;
|
||||
}
|
||||
|
||||
ret = kvm_arch_init(ms, s);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
|
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* QEMU SEV stub
|
||||
*
|
||||
* Copyright Advanced Micro Devices 2018
|
||||
*
|
||||
* Authors:
|
||||
* Brijesh Singh <brijesh.singh@amd.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/sev.h"
|
||||
|
||||
int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void *sev_guest_init(const char *id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
@@ -105,16 +105,6 @@ int kvm_on_sigbus(int code, void *addr)
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool kvm_memcrypt_enabled(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev)
|
||||
{
|
||||
|
@@ -585,7 +585,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
else {
|
||||
if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
|
||||
replay_interrupt();
|
||||
cpu->exception_index = -1;
|
||||
*last_tb = NULL;
|
||||
}
|
||||
/* The target hook may have updated the 'cpu->interrupt_request';
|
||||
@@ -607,9 +606,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
if (unlikely(atomic_read(&cpu->exit_request)
|
||||
|| (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) {
|
||||
atomic_set(&cpu->exit_request, 0);
|
||||
if (cpu->exception_index == -1) {
|
||||
cpu->exception_index = EXCP_INTERRUPT;
|
||||
}
|
||||
cpu->exception_index = EXCP_INTERRUPT;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -1728,8 +1728,7 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
#endif
|
||||
TranslationBlock *tb;
|
||||
uint32_t n, flags;
|
||||
target_ulong pc, cs_base;
|
||||
uint32_t n;
|
||||
|
||||
tb_lock();
|
||||
tb = tb_find_pc(retaddr);
|
||||
@@ -1767,14 +1766,8 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
|
||||
cpu_abort(cpu, "TB too big during recompile");
|
||||
}
|
||||
|
||||
pc = tb->pc;
|
||||
cs_base = tb->cs_base;
|
||||
flags = tb->flags;
|
||||
tb_phys_invalidate(tb, -1);
|
||||
|
||||
/* Execute one IO instruction without caching
|
||||
instead of creating large TB. */
|
||||
cpu->cflags_next_tb = curr_cflags() | CF_LAST_IO | CF_NOCACHE | 1;
|
||||
/* Adjust the execution state of the next TB. */
|
||||
cpu->cflags_next_tb = curr_cflags() | CF_LAST_IO | n;
|
||||
|
||||
if (tb->cflags & CF_NOCACHE) {
|
||||
if (tb->orig_tb) {
|
||||
@@ -1785,11 +1778,6 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
|
||||
tb_remove(tb);
|
||||
}
|
||||
|
||||
/* Generate new TB instead of the current one. */
|
||||
/* FIXME: In theory this could raise an exception. In practice
|
||||
we have already translated the block once so it's probably ok. */
|
||||
tb_gen_code(cpu, pc, cs_base, flags, curr_cflags() | CF_LAST_IO | n);
|
||||
|
||||
/* TODO: If env->pc != tb->pc (i.e. the faulting instruction was not
|
||||
* the first in the TB) then we end up generating a whole new TB and
|
||||
* repeating the fault, which is horribly inefficient.
|
||||
|
23
block.c
23
block.c
@@ -33,7 +33,6 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
#include "qapi/qmp/qnull.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qapi/qobject-output-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
@@ -1458,7 +1457,7 @@ static QDict *parse_json_filename(const char *filename, Error **errp)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
options = qobject_to(QDict, options_obj);
|
||||
options = qobject_to_qdict(options_obj);
|
||||
if (!options) {
|
||||
qobject_decref(options_obj);
|
||||
error_setg(errp, "Invalid JSON object given");
|
||||
@@ -2434,7 +2433,7 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
|
||||
}
|
||||
visit_complete(v, &obj);
|
||||
|
||||
qdict = qobject_to(QDict, obj);
|
||||
qdict = qobject_to_qdict(obj);
|
||||
qdict_flatten(qdict);
|
||||
|
||||
/* bdrv_open_inherit() defaults to the values in bdrv_flags (for
|
||||
@@ -2646,13 +2645,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
||||
|
||||
/* See cautionary note on accessing @options above */
|
||||
backing = qdict_get_try_str(options, "backing");
|
||||
if (qobject_to(QNull, qdict_get(options, "backing")) != NULL ||
|
||||
(backing && *backing == '\0'))
|
||||
{
|
||||
if (backing) {
|
||||
warn_report("Use of \"backing\": \"\" is deprecated; "
|
||||
"use \"backing\": null instead");
|
||||
}
|
||||
if (backing && *backing == '\0') {
|
||||
flags |= BDRV_O_NO_BACKING;
|
||||
qdict_del(options, "backing");
|
||||
}
|
||||
@@ -2890,16 +2883,8 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
|
||||
|
||||
/* Inherit from parent node */
|
||||
if (parent_options) {
|
||||
QemuOpts *opts;
|
||||
QDict *options_copy;
|
||||
assert(!flags);
|
||||
role->inherit_options(&flags, options, parent_flags, parent_options);
|
||||
options_copy = qdict_clone_shallow(options);
|
||||
opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options_copy, NULL);
|
||||
update_flags_from_options(&flags, opts);
|
||||
qemu_opts_del(opts);
|
||||
QDECREF(options_copy);
|
||||
}
|
||||
|
||||
/* Old values are used for options that aren't set yet */
|
||||
@@ -3686,12 +3671,12 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
|
||||
GSList *ignore_children = g_slist_prepend(NULL, c);
|
||||
bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm,
|
||||
ignore_children, &local_err);
|
||||
g_slist_free(ignore_children);
|
||||
if (local_err) {
|
||||
ret = -EPERM;
|
||||
error_report_err(local_err);
|
||||
goto exit;
|
||||
}
|
||||
g_slist_free(ignore_children);
|
||||
|
||||
/* If so, update the backing file path in the image file */
|
||||
if (c->role->update_filename) {
|
||||
|
@@ -94,94 +94,6 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
cookie->type = type;
|
||||
}
|
||||
|
||||
/* block_latency_histogram_compare_func:
|
||||
* Compare @key with interval [@it[0], @it[1]).
|
||||
* Return: -1 if @key < @it[0]
|
||||
* 0 if @key in [@it[0], @it[1])
|
||||
* +1 if @key >= @it[1]
|
||||
*/
|
||||
static int block_latency_histogram_compare_func(const void *key, const void *it)
|
||||
{
|
||||
uint64_t k = *(uint64_t *)key;
|
||||
uint64_t a = ((uint64_t *)it)[0];
|
||||
uint64_t b = ((uint64_t *)it)[1];
|
||||
|
||||
return k < a ? -1 : (k < b ? 0 : 1);
|
||||
}
|
||||
|
||||
static void block_latency_histogram_account(BlockLatencyHistogram *hist,
|
||||
int64_t latency_ns)
|
||||
{
|
||||
uint64_t *pos;
|
||||
|
||||
if (hist->bins == NULL) {
|
||||
/* histogram disabled */
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (latency_ns < hist->boundaries[0]) {
|
||||
hist->bins[0]++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (latency_ns >= hist->boundaries[hist->nbins - 2]) {
|
||||
hist->bins[hist->nbins - 1]++;
|
||||
return;
|
||||
}
|
||||
|
||||
pos = bsearch(&latency_ns, hist->boundaries, hist->nbins - 2,
|
||||
sizeof(hist->boundaries[0]),
|
||||
block_latency_histogram_compare_func);
|
||||
assert(pos != NULL);
|
||||
|
||||
hist->bins[pos - hist->boundaries + 1]++;
|
||||
}
|
||||
|
||||
int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type,
|
||||
uint64List *boundaries)
|
||||
{
|
||||
BlockLatencyHistogram *hist = &stats->latency_histogram[type];
|
||||
uint64List *entry;
|
||||
uint64_t *ptr;
|
||||
uint64_t prev = 0;
|
||||
int new_nbins = 1;
|
||||
|
||||
for (entry = boundaries; entry; entry = entry->next) {
|
||||
if (entry->value <= prev) {
|
||||
return -EINVAL;
|
||||
}
|
||||
new_nbins++;
|
||||
prev = entry->value;
|
||||
}
|
||||
|
||||
hist->nbins = new_nbins;
|
||||
g_free(hist->boundaries);
|
||||
hist->boundaries = g_new(uint64_t, hist->nbins - 1);
|
||||
for (entry = boundaries, ptr = hist->boundaries; entry;
|
||||
entry = entry->next, ptr++)
|
||||
{
|
||||
*ptr = entry->value;
|
||||
}
|
||||
|
||||
g_free(hist->bins);
|
||||
hist->bins = g_new0(uint64_t, hist->nbins);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void block_latency_histograms_clear(BlockAcctStats *stats)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
|
||||
BlockLatencyHistogram *hist = &stats->latency_histogram[i];
|
||||
g_free(hist->bins);
|
||||
g_free(hist->boundaries);
|
||||
memset(hist, 0, sizeof(*hist));
|
||||
}
|
||||
}
|
||||
|
||||
static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
bool failed)
|
||||
{
|
||||
@@ -204,9 +116,6 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
stats->nr_ops[cookie->type]++;
|
||||
}
|
||||
|
||||
block_latency_histogram_account(&stats->latency_histogram[cookie->type],
|
||||
latency_ns);
|
||||
|
||||
if (!failed || stats->account_failed) {
|
||||
stats->total_time_ns[cookie->type] += latency_ns;
|
||||
stats->last_access_time_ns = time_ns;
|
||||
|
@@ -206,7 +206,7 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
|
||||
BdrvDirtyBitmap *bm;
|
||||
BlockDriverState *bs = blk_bs(job->common.blk);
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret < 0 || block_job_is_cancelled(&job->common)) {
|
||||
/* Merge the successor back into the parent, delete nothing. */
|
||||
bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
|
||||
assert(bm);
|
||||
@@ -621,7 +621,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
}
|
||||
|
||||
/* job->common.len is fixed, so we can't allow resize */
|
||||
job = block_job_create(job_id, &backup_job_driver, txn, bs,
|
||||
job = block_job_create(job_id, &backup_job_driver, bs,
|
||||
BLK_PERM_CONSISTENT_READ,
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
|
||||
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD,
|
||||
@@ -677,6 +677,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
|
||||
&error_abort);
|
||||
job->common.len = len;
|
||||
block_job_txn_add_job(txn, &job->common);
|
||||
|
||||
return &job->common;
|
||||
|
||||
|
@@ -129,10 +129,9 @@ static int coroutine_fn blkreplay_co_flush(BlockDriverState *bs)
|
||||
|
||||
static BlockDriver bdrv_blkreplay = {
|
||||
.format_name = "blkreplay",
|
||||
.protocol_name = "blkreplay",
|
||||
.instance_size = 0,
|
||||
|
||||
.bdrv_file_open = blkreplay_open,
|
||||
.bdrv_open = blkreplay_open,
|
||||
.bdrv_close = blkreplay_close,
|
||||
.bdrv_child_perm = bdrv_filter_default_perms,
|
||||
.bdrv_getlength = blkreplay_getlength,
|
||||
|
@@ -31,13 +31,6 @@
|
||||
|
||||
static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb);
|
||||
|
||||
typedef struct BlockBackendAioNotifier {
|
||||
void (*attached_aio_context)(AioContext *new_context, void *opaque);
|
||||
void (*detach_aio_context)(void *opaque);
|
||||
void *opaque;
|
||||
QLIST_ENTRY(BlockBackendAioNotifier) list;
|
||||
} BlockBackendAioNotifier;
|
||||
|
||||
struct BlockBackend {
|
||||
char *name;
|
||||
int refcnt;
|
||||
@@ -76,7 +69,6 @@ struct BlockBackend {
|
||||
bool allow_write_beyond_eof;
|
||||
|
||||
NotifierList remove_bs_notifiers, insert_bs_notifiers;
|
||||
QLIST_HEAD(, BlockBackendAioNotifier) aio_notifiers;
|
||||
|
||||
int quiesce_counter;
|
||||
VMChangeStateEntry *vmsh;
|
||||
@@ -255,36 +247,6 @@ static int blk_root_inactivate(BdrvChild *child)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void blk_root_attach(BdrvChild *child)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
BlockBackendAioNotifier *notifier;
|
||||
|
||||
trace_blk_root_attach(child, blk, child->bs);
|
||||
|
||||
QLIST_FOREACH(notifier, &blk->aio_notifiers, list) {
|
||||
bdrv_add_aio_context_notifier(child->bs,
|
||||
notifier->attached_aio_context,
|
||||
notifier->detach_aio_context,
|
||||
notifier->opaque);
|
||||
}
|
||||
}
|
||||
|
||||
static void blk_root_detach(BdrvChild *child)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
BlockBackendAioNotifier *notifier;
|
||||
|
||||
trace_blk_root_detach(child, blk, child->bs);
|
||||
|
||||
QLIST_FOREACH(notifier, &blk->aio_notifiers, list) {
|
||||
bdrv_remove_aio_context_notifier(child->bs,
|
||||
notifier->attached_aio_context,
|
||||
notifier->detach_aio_context,
|
||||
notifier->opaque);
|
||||
}
|
||||
}
|
||||
|
||||
static const BdrvChildRole child_root = {
|
||||
.inherit_options = blk_root_inherit_options,
|
||||
|
||||
@@ -298,9 +260,6 @@ static const BdrvChildRole child_root = {
|
||||
|
||||
.activate = blk_root_activate,
|
||||
.inactivate = blk_root_inactivate,
|
||||
|
||||
.attach = blk_root_attach,
|
||||
.detach = blk_root_detach,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -328,7 +287,6 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
|
||||
|
||||
notifier_list_init(&blk->remove_bs_notifiers);
|
||||
notifier_list_init(&blk->insert_bs_notifiers);
|
||||
QLIST_INIT(&blk->aio_notifiers);
|
||||
|
||||
QTAILQ_INSERT_TAIL(&block_backends, blk, link);
|
||||
return blk;
|
||||
@@ -406,7 +364,6 @@ static void blk_delete(BlockBackend *blk)
|
||||
}
|
||||
assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers));
|
||||
assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers));
|
||||
assert(QLIST_EMPTY(&blk->aio_notifiers));
|
||||
QTAILQ_REMOVE(&block_backends, blk, link);
|
||||
drive_info_del(blk->legacy_dinfo);
|
||||
block_acct_cleanup(&blk->stats);
|
||||
@@ -1900,15 +1857,8 @@ void blk_add_aio_context_notifier(BlockBackend *blk,
|
||||
void (*attached_aio_context)(AioContext *new_context, void *opaque),
|
||||
void (*detach_aio_context)(void *opaque), void *opaque)
|
||||
{
|
||||
BlockBackendAioNotifier *notifier;
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
|
||||
notifier = g_new(BlockBackendAioNotifier, 1);
|
||||
notifier->attached_aio_context = attached_aio_context;
|
||||
notifier->detach_aio_context = detach_aio_context;
|
||||
notifier->opaque = opaque;
|
||||
QLIST_INSERT_HEAD(&blk->aio_notifiers, notifier, list);
|
||||
|
||||
if (bs) {
|
||||
bdrv_add_aio_context_notifier(bs, attached_aio_context,
|
||||
detach_aio_context, opaque);
|
||||
@@ -1921,25 +1871,12 @@ void blk_remove_aio_context_notifier(BlockBackend *blk,
|
||||
void (*detach_aio_context)(void *),
|
||||
void *opaque)
|
||||
{
|
||||
BlockBackendAioNotifier *notifier;
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
|
||||
if (bs) {
|
||||
bdrv_remove_aio_context_notifier(bs, attached_aio_context,
|
||||
detach_aio_context, opaque);
|
||||
}
|
||||
|
||||
QLIST_FOREACH(notifier, &blk->aio_notifiers, list) {
|
||||
if (notifier->attached_aio_context == attached_aio_context &&
|
||||
notifier->detach_aio_context == detach_aio_context &&
|
||||
notifier->opaque == opaque) {
|
||||
QLIST_REMOVE(notifier, list);
|
||||
g_free(notifier);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
abort();
|
||||
}
|
||||
|
||||
void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify)
|
||||
|
@@ -289,7 +289,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
return;
|
||||
}
|
||||
|
||||
s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL,
|
||||
s = block_job_create(job_id, &commit_job_driver, bs, 0, BLK_PERM_ALL,
|
||||
speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
|
150
block/crypto.c
150
block/crypto.c
@@ -71,6 +71,8 @@ static ssize_t block_crypto_read_func(QCryptoBlock *block,
|
||||
|
||||
|
||||
struct BlockCryptoCreateData {
|
||||
const char *filename;
|
||||
QemuOpts *opts;
|
||||
BlockBackend *blk;
|
||||
uint64_t size;
|
||||
};
|
||||
@@ -101,18 +103,27 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block,
|
||||
Error **errp)
|
||||
{
|
||||
struct BlockCryptoCreateData *data = opaque;
|
||||
|
||||
if (data->size > INT64_MAX || headerlen > INT64_MAX - data->size) {
|
||||
error_setg(errp, "The requested file size is too large");
|
||||
return -EFBIG;
|
||||
}
|
||||
int ret;
|
||||
|
||||
/* User provided size should reflect amount of space made
|
||||
* available to the guest, so we must take account of that
|
||||
* which will be used by the crypto header
|
||||
*/
|
||||
return blk_truncate(data->blk, data->size + headerlen, PREALLOC_MODE_OFF,
|
||||
errp);
|
||||
data->size += headerlen;
|
||||
|
||||
qemu_opt_set_number(data->opts, BLOCK_OPT_SIZE, data->size, &error_abort);
|
||||
ret = bdrv_create_file(data->filename, data->opts, errp);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
data->blk = blk_new_open(data->filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, errp);
|
||||
if (!data->blk) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -311,29 +322,30 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
|
||||
}
|
||||
|
||||
|
||||
static int block_crypto_co_create_generic(BlockDriverState *bs,
|
||||
int64_t size,
|
||||
QCryptoBlockCreateOptions *opts,
|
||||
Error **errp)
|
||||
static int block_crypto_create_generic(QCryptoBlockFormat format,
|
||||
const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
BlockBackend *blk;
|
||||
int ret = -EINVAL;
|
||||
QCryptoBlockCreateOptions *create_opts = NULL;
|
||||
QCryptoBlock *crypto = NULL;
|
||||
struct BlockCryptoCreateData data;
|
||||
struct BlockCryptoCreateData data = {
|
||||
.size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE),
|
||||
.opts = opts,
|
||||
.filename = filename,
|
||||
};
|
||||
QDict *cryptoopts;
|
||||
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
cryptoopts = qemu_opts_to_qdict(opts, NULL);
|
||||
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
if (ret < 0) {
|
||||
goto cleanup;
|
||||
create_opts = block_crypto_create_opts_init(format, cryptoopts, errp);
|
||||
if (!create_opts) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = (struct BlockCryptoCreateData) {
|
||||
.blk = blk,
|
||||
.size = size,
|
||||
};
|
||||
|
||||
crypto = qcrypto_block_create(opts, NULL,
|
||||
crypto = qcrypto_block_create(create_opts, NULL,
|
||||
block_crypto_init_func,
|
||||
block_crypto_write_func,
|
||||
&data,
|
||||
@@ -346,8 +358,10 @@ static int block_crypto_co_create_generic(BlockDriverState *bs,
|
||||
|
||||
ret = 0;
|
||||
cleanup:
|
||||
QDECREF(cryptoopts);
|
||||
qcrypto_block_free(crypto);
|
||||
blk_unref(blk);
|
||||
blk_unref(data.blk);
|
||||
qapi_free_QCryptoBlockCreateOptions(create_opts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -523,10 +537,7 @@ static int64_t block_crypto_getlength(BlockDriverState *bs)
|
||||
|
||||
uint64_t offset = qcrypto_block_get_payload_offset(crypto->block);
|
||||
assert(offset < INT64_MAX);
|
||||
|
||||
if (offset > len) {
|
||||
return -EIO;
|
||||
}
|
||||
assert(offset < len);
|
||||
|
||||
len -= offset;
|
||||
|
||||
@@ -551,88 +562,12 @@ static int block_crypto_open_luks(BlockDriverState *bs,
|
||||
bs, options, flags, errp);
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
block_crypto_co_create_luks(BlockdevCreateOptions *create_options, Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsLUKS *luks_opts;
|
||||
BlockDriverState *bs = NULL;
|
||||
QCryptoBlockCreateOptions create_opts;
|
||||
int ret;
|
||||
|
||||
assert(create_options->driver == BLOCKDEV_DRIVER_LUKS);
|
||||
luks_opts = &create_options->u.luks;
|
||||
|
||||
bs = bdrv_open_blockdev_ref(luks_opts->file, errp);
|
||||
if (bs == NULL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
create_opts = (QCryptoBlockCreateOptions) {
|
||||
.format = Q_CRYPTO_BLOCK_FORMAT_LUKS,
|
||||
.u.luks = *qapi_BlockdevCreateOptionsLUKS_base(luks_opts),
|
||||
};
|
||||
|
||||
ret = block_crypto_co_create_generic(bs, luks_opts->size, &create_opts,
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
bdrv_unref(bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoBlockCreateOptions *create_opts = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
QDict *cryptoopts;
|
||||
int64_t size;
|
||||
int ret;
|
||||
|
||||
/* Parse options */
|
||||
size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
|
||||
|
||||
cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL,
|
||||
&block_crypto_create_opts_luks,
|
||||
true);
|
||||
|
||||
create_opts = block_crypto_create_opts_init(Q_CRYPTO_BLOCK_FORMAT_LUKS,
|
||||
cryptoopts, errp);
|
||||
if (!create_opts) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Create protocol layer */
|
||||
ret = bdrv_create_file(filename, opts, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
bs = bdrv_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
|
||||
if (!bs) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Create format layer */
|
||||
ret = block_crypto_co_create_generic(bs, size, create_opts, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
bdrv_unref(bs);
|
||||
qapi_free_QCryptoBlockCreateOptions(create_opts);
|
||||
QDECREF(cryptoopts);
|
||||
return ret;
|
||||
return block_crypto_create_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS,
|
||||
filename, opts, errp);
|
||||
}
|
||||
|
||||
static int block_crypto_get_info_luks(BlockDriverState *bs,
|
||||
@@ -688,7 +623,6 @@ BlockDriver bdrv_crypto_luks = {
|
||||
.bdrv_open = block_crypto_open_luks,
|
||||
.bdrv_close = block_crypto_close,
|
||||
.bdrv_child_perm = bdrv_format_default_perms,
|
||||
.bdrv_co_create = block_crypto_co_create_luks,
|
||||
.bdrv_co_create_opts = block_crypto_co_create_opts_luks,
|
||||
.bdrv_truncate = block_crypto_truncate,
|
||||
.create_opts = &block_crypto_create_opts_luks,
|
||||
|
@@ -40,8 +40,6 @@ struct BdrvDirtyBitmap {
|
||||
QemuMutex *mutex;
|
||||
HBitmap *bitmap; /* Dirty bitmap implementation */
|
||||
HBitmap *meta; /* Meta dirty bitmap */
|
||||
bool qmp_locked; /* Bitmap is locked, it can't be modified
|
||||
through QMP */
|
||||
BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */
|
||||
char *name; /* Optional non-empty unique ID */
|
||||
int64_t size; /* Size of the bitmap, in bytes */
|
||||
@@ -185,18 +183,6 @@ bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
|
||||
return bitmap->successor;
|
||||
}
|
||||
|
||||
void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked)
|
||||
{
|
||||
qemu_mutex_lock(bitmap->mutex);
|
||||
bitmap->qmp_locked = qmp_locked;
|
||||
qemu_mutex_unlock(bitmap->mutex);
|
||||
}
|
||||
|
||||
bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return bitmap->qmp_locked;
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
@@ -208,8 +194,6 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
return DIRTY_BITMAP_STATUS_FROZEN;
|
||||
} else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
|
||||
return DIRTY_BITMAP_STATUS_LOCKED;
|
||||
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||
return DIRTY_BITMAP_STATUS_DISABLED;
|
||||
} else {
|
||||
@@ -250,59 +234,6 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
qemu_mutex_lock(bitmap->mutex);
|
||||
bdrv_enable_dirty_bitmap(bitmap->successor);
|
||||
qemu_mutex_unlock(bitmap->mutex);
|
||||
}
|
||||
|
||||
/* Called within bdrv_dirty_bitmap_lock..unlock */
|
||||
static void bdrv_do_release_matching_dirty_bitmap_locked(
|
||||
BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
|
||||
bool (*cond)(BdrvDirtyBitmap *bitmap))
|
||||
{
|
||||
BdrvDirtyBitmap *bm, *next;
|
||||
|
||||
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
|
||||
if ((!bitmap || bm == bitmap) && (!cond || cond(bm))) {
|
||||
assert(!bm->active_iterators);
|
||||
assert(!bdrv_dirty_bitmap_frozen(bm));
|
||||
assert(!bm->meta);
|
||||
QLIST_REMOVE(bm, list);
|
||||
hbitmap_free(bm->bitmap);
|
||||
g_free(bm->name);
|
||||
g_free(bm);
|
||||
|
||||
if (bitmap) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bitmap) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
static void bdrv_do_release_matching_dirty_bitmap(
|
||||
BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
|
||||
bool (*cond)(BdrvDirtyBitmap *bitmap))
|
||||
{
|
||||
bdrv_dirty_bitmaps_lock(bs);
|
||||
bdrv_do_release_matching_dirty_bitmap_locked(bs, bitmap, cond);
|
||||
bdrv_dirty_bitmaps_unlock(bs);
|
||||
}
|
||||
|
||||
/* Called within bdrv_dirty_bitmap_lock..unlock */
|
||||
static void bdrv_release_dirty_bitmap_locked(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
bdrv_do_release_matching_dirty_bitmap_locked(bs, bitmap, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* For a bitmap with a successor, yield our name to the successor,
|
||||
* delete the old bitmap, and return a handle to the new bitmap.
|
||||
@@ -336,11 +267,11 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
||||
* In cases of failure where we can no longer safely delete the parent,
|
||||
* we may wish to re-join the parent and child/successor.
|
||||
* The merged parent will be un-frozen, but not explicitly re-enabled.
|
||||
* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken.
|
||||
* Called with BQL taken.
|
||||
*/
|
||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *parent,
|
||||
Error **errp)
|
||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *parent,
|
||||
Error **errp)
|
||||
{
|
||||
BdrvDirtyBitmap *successor = parent->successor;
|
||||
|
||||
@@ -353,26 +284,12 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
|
||||
error_setg(errp, "Merging of parent and successor bitmap failed");
|
||||
return NULL;
|
||||
}
|
||||
bdrv_release_dirty_bitmap_locked(bs, successor);
|
||||
bdrv_release_dirty_bitmap(bs, successor);
|
||||
parent->successor = NULL;
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *parent,
|
||||
Error **errp)
|
||||
{
|
||||
BdrvDirtyBitmap *ret;
|
||||
|
||||
qemu_mutex_lock(parent->mutex);
|
||||
ret = bdrv_reclaim_dirty_bitmap_locked(bs, parent, errp);
|
||||
qemu_mutex_unlock(parent->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates _all_ bitmaps attached to a BDS.
|
||||
* Called with BQL taken.
|
||||
@@ -396,6 +313,36 @@ static bool bdrv_dirty_bitmap_has_name(BdrvDirtyBitmap *bitmap)
|
||||
return !!bdrv_dirty_bitmap_name(bitmap);
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
static void bdrv_do_release_matching_dirty_bitmap(
|
||||
BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
|
||||
bool (*cond)(BdrvDirtyBitmap *bitmap))
|
||||
{
|
||||
BdrvDirtyBitmap *bm, *next;
|
||||
bdrv_dirty_bitmaps_lock(bs);
|
||||
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
|
||||
if ((!bitmap || bm == bitmap) && (!cond || cond(bm))) {
|
||||
assert(!bm->active_iterators);
|
||||
assert(!bdrv_dirty_bitmap_frozen(bm));
|
||||
assert(!bm->meta);
|
||||
QLIST_REMOVE(bm, list);
|
||||
hbitmap_free(bm->bitmap);
|
||||
g_free(bm->name);
|
||||
g_free(bm);
|
||||
|
||||
if (bitmap) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bitmap) {
|
||||
abort();
|
||||
}
|
||||
|
||||
out:
|
||||
bdrv_dirty_bitmaps_unlock(bs);
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
|
@@ -665,7 +665,7 @@ static int qemu_gluster_parse(BlockdevOptionsGluster *gconf,
|
||||
if (filename) {
|
||||
ret = qemu_gluster_parse_uri(gconf, filename);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "invalid URI %s", filename);
|
||||
error_setg(errp, "invalid URI");
|
||||
error_append_hint(errp, "Usage: file=gluster[+transport]://"
|
||||
"[host[:port]]volume/path[?socket=...]"
|
||||
"[,file.debug=N]"
|
||||
|
@@ -2244,7 +2244,7 @@ static BlockDriver bdrv_iser = {
|
||||
.create_opts = &iscsi_create_opts,
|
||||
.bdrv_reopen_prepare = iscsi_reopen_prepare,
|
||||
.bdrv_reopen_commit = iscsi_reopen_commit,
|
||||
.bdrv_co_invalidate_cache = iscsi_co_invalidate_cache,
|
||||
.bdrv_invalidate_cache = iscsi_invalidate_cache,
|
||||
|
||||
.bdrv_getlength = iscsi_getlength,
|
||||
.bdrv_get_info = iscsi_get_info,
|
||||
|
@@ -869,8 +869,11 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
|
||||
ret = 0;
|
||||
trace_mirror_before_sleep(s, cnt, s->synced, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common) && s->common.force) {
|
||||
break;
|
||||
if (!s->synced) {
|
||||
block_job_sleep_ns(&s->common, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
break;
|
||||
}
|
||||
} else if (!should_complete) {
|
||||
delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0);
|
||||
block_job_sleep_ns(&s->common, delay_ns);
|
||||
@@ -884,8 +887,7 @@ immediate_exit:
|
||||
* or it was cancelled prematurely so that we do not guarantee that
|
||||
* the target is a copy of the source.
|
||||
*/
|
||||
assert(ret < 0 || ((s->common.force || !s->synced) &&
|
||||
block_job_is_cancelled(&s->common)));
|
||||
assert(ret < 0 || (!s->synced && block_job_is_cancelled(&s->common)));
|
||||
assert(need_drain);
|
||||
mirror_wait_for_all_io(s);
|
||||
}
|
||||
@@ -1164,7 +1166,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
|
||||
}
|
||||
|
||||
/* Make sure that the source is not resized while the job is running */
|
||||
s = block_job_create(job_id, driver, NULL, mirror_top_bs,
|
||||
s = block_job_create(job_id, driver, mirror_top_bs,
|
||||
BLK_PERM_CONSISTENT_READ,
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
||||
BLK_PERM_WRITE | BLK_PERM_GRAPH_MOD, speed,
|
||||
|
@@ -228,48 +228,6 @@ static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* nbd_parse_blockstatus_payload
|
||||
* support only one extent in reply and only for
|
||||
* base:allocation context
|
||||
*/
|
||||
static int nbd_parse_blockstatus_payload(NBDClientSession *client,
|
||||
NBDStructuredReplyChunk *chunk,
|
||||
uint8_t *payload, uint64_t orig_length,
|
||||
NBDExtent *extent, Error **errp)
|
||||
{
|
||||
uint32_t context_id;
|
||||
|
||||
if (chunk->length != sizeof(context_id) + sizeof(extent)) {
|
||||
error_setg(errp, "Protocol error: invalid payload for "
|
||||
"NBD_REPLY_TYPE_BLOCK_STATUS");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
context_id = payload_advance32(&payload);
|
||||
if (client->info.meta_base_allocation_id != context_id) {
|
||||
error_setg(errp, "Protocol error: unexpected context id %d for "
|
||||
"NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated context "
|
||||
"id is %d", context_id,
|
||||
client->info.meta_base_allocation_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
extent->length = payload_advance32(&payload);
|
||||
extent->flags = payload_advance32(&payload);
|
||||
|
||||
if (extent->length == 0 ||
|
||||
(client->info.min_block && !QEMU_IS_ALIGNED(extent->length,
|
||||
client->info.min_block)) ||
|
||||
extent->length > orig_length)
|
||||
{
|
||||
error_setg(errp, "Protocol error: server sent status chunk with "
|
||||
"invalid length");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* nbd_parse_error_payload
|
||||
* on success @errp contains message describing nbd error reply
|
||||
*/
|
||||
@@ -523,7 +481,6 @@ static coroutine_fn int nbd_co_receive_one_chunk(
|
||||
|
||||
typedef struct NBDReplyChunkIter {
|
||||
int ret;
|
||||
bool fatal;
|
||||
Error *err;
|
||||
bool done, only_structured;
|
||||
} NBDReplyChunkIter;
|
||||
@@ -533,12 +490,11 @@ static void nbd_iter_error(NBDReplyChunkIter *iter, bool fatal,
|
||||
{
|
||||
assert(ret < 0);
|
||||
|
||||
if ((fatal && !iter->fatal) || iter->ret == 0) {
|
||||
if (fatal || iter->ret == 0) {
|
||||
if (iter->ret != 0) {
|
||||
error_free(iter->err);
|
||||
iter->err = NULL;
|
||||
}
|
||||
iter->fatal = fatal;
|
||||
iter->ret = ret;
|
||||
error_propagate(&iter->err, *local_err);
|
||||
} else {
|
||||
@@ -684,68 +640,6 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle,
|
||||
return iter.ret;
|
||||
}
|
||||
|
||||
static int nbd_co_receive_blockstatus_reply(NBDClientSession *s,
|
||||
uint64_t handle, uint64_t length,
|
||||
NBDExtent *extent, Error **errp)
|
||||
{
|
||||
NBDReplyChunkIter iter;
|
||||
NBDReply reply;
|
||||
void *payload = NULL;
|
||||
Error *local_err = NULL;
|
||||
bool received = false;
|
||||
|
||||
assert(!extent->length);
|
||||
NBD_FOREACH_REPLY_CHUNK(s, iter, handle, s->info.structured_reply,
|
||||
NULL, &reply, &payload)
|
||||
{
|
||||
int ret;
|
||||
NBDStructuredReplyChunk *chunk = &reply.structured;
|
||||
|
||||
assert(nbd_reply_is_structured(&reply));
|
||||
|
||||
switch (chunk->type) {
|
||||
case NBD_REPLY_TYPE_BLOCK_STATUS:
|
||||
if (received) {
|
||||
s->quit = true;
|
||||
error_setg(&local_err, "Several BLOCK_STATUS chunks in reply");
|
||||
nbd_iter_error(&iter, true, -EINVAL, &local_err);
|
||||
}
|
||||
received = true;
|
||||
|
||||
ret = nbd_parse_blockstatus_payload(s, &reply.structured,
|
||||
payload, length, extent,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
s->quit = true;
|
||||
nbd_iter_error(&iter, true, ret, &local_err);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!nbd_reply_type_is_error(chunk->type)) {
|
||||
s->quit = true;
|
||||
error_setg(&local_err,
|
||||
"Unexpected reply type: %d (%s) "
|
||||
"for CMD_BLOCK_STATUS",
|
||||
chunk->type, nbd_reply_type_lookup(chunk->type));
|
||||
nbd_iter_error(&iter, true, -EINVAL, &local_err);
|
||||
}
|
||||
}
|
||||
|
||||
g_free(payload);
|
||||
payload = NULL;
|
||||
}
|
||||
|
||||
if (!extent->length && !iter.err) {
|
||||
error_setg(&iter.err,
|
||||
"Server did not reply with any status extents");
|
||||
if (!iter.ret) {
|
||||
iter.ret = -EIO;
|
||||
}
|
||||
}
|
||||
error_propagate(errp, iter.err);
|
||||
return iter.ret;
|
||||
}
|
||||
|
||||
static int nbd_co_request(BlockDriverState *bs, NBDRequest *request,
|
||||
QEMUIOVector *write_qiov)
|
||||
{
|
||||
@@ -888,51 +782,6 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes)
|
||||
return nbd_co_request(bs, &request, NULL);
|
||||
}
|
||||
|
||||
int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs,
|
||||
bool want_zero,
|
||||
int64_t offset, int64_t bytes,
|
||||
int64_t *pnum, int64_t *map,
|
||||
BlockDriverState **file)
|
||||
{
|
||||
int64_t ret;
|
||||
NBDExtent extent = { 0 };
|
||||
NBDClientSession *client = nbd_get_client_session(bs);
|
||||
Error *local_err = NULL;
|
||||
|
||||
NBDRequest request = {
|
||||
.type = NBD_CMD_BLOCK_STATUS,
|
||||
.from = offset,
|
||||
.len = MIN(MIN_NON_ZERO(QEMU_ALIGN_DOWN(INT_MAX,
|
||||
bs->bl.request_alignment),
|
||||
client->info.max_block), bytes),
|
||||
.flags = NBD_CMD_FLAG_REQ_ONE,
|
||||
};
|
||||
|
||||
if (!client->info.base_allocation) {
|
||||
*pnum = bytes;
|
||||
return BDRV_BLOCK_DATA;
|
||||
}
|
||||
|
||||
ret = nbd_co_send_request(bs, &request, NULL);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = nbd_co_receive_blockstatus_reply(client, request.handle, bytes,
|
||||
&extent, &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
}
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
assert(extent.length);
|
||||
*pnum = extent.length;
|
||||
return (extent.flags & NBD_STATE_HOLE ? 0 : BDRV_BLOCK_DATA) |
|
||||
(extent.flags & NBD_STATE_ZERO ? BDRV_BLOCK_ZERO : 0);
|
||||
}
|
||||
|
||||
void nbd_client_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
NBDClientSession *client = nbd_get_client_session(bs);
|
||||
@@ -977,7 +826,6 @@ int nbd_client_init(BlockDriverState *bs,
|
||||
|
||||
client->info.request_sizes = true;
|
||||
client->info.structured_reply = true;
|
||||
client->info.base_allocation = true;
|
||||
ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
|
||||
tlscreds, hostname,
|
||||
&client->ioc, &client->info, errp);
|
||||
|
@@ -61,10 +61,4 @@ void nbd_client_detach_aio_context(BlockDriverState *bs);
|
||||
void nbd_client_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context);
|
||||
|
||||
int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs,
|
||||
bool want_zero,
|
||||
int64_t offset, int64_t bytes,
|
||||
int64_t *pnum, int64_t *map,
|
||||
BlockDriverState **file);
|
||||
|
||||
#endif /* NBD_CLIENT_H */
|
||||
|
@@ -585,7 +585,6 @@ static BlockDriver bdrv_nbd = {
|
||||
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
||||
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
||||
.bdrv_refresh_filename = nbd_refresh_filename,
|
||||
.bdrv_co_block_status = nbd_client_co_block_status,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_nbd_tcp = {
|
||||
@@ -605,7 +604,6 @@ static BlockDriver bdrv_nbd_tcp = {
|
||||
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
||||
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
||||
.bdrv_refresh_filename = nbd_refresh_filename,
|
||||
.bdrv_co_block_status = nbd_client_co_block_status,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_nbd_unix = {
|
||||
@@ -625,7 +623,6 @@ static BlockDriver bdrv_nbd_unix = {
|
||||
.bdrv_detach_aio_context = nbd_detach_aio_context,
|
||||
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
||||
.bdrv_refresh_filename = nbd_refresh_filename,
|
||||
.bdrv_co_block_status = nbd_client_co_block_status,
|
||||
};
|
||||
|
||||
static void bdrv_nbd_init(void)
|
||||
|
@@ -34,9 +34,6 @@
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/bitmap.h"
|
||||
#include "migration/blocker.h"
|
||||
@@ -82,25 +79,6 @@ static QemuOptsList parallels_runtime_opts = {
|
||||
},
|
||||
};
|
||||
|
||||
static QemuOptsList parallels_create_opts = {
|
||||
.name = "parallels-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(parallels_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size",
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_CLUSTER_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Parallels image cluster size",
|
||||
.def_value_str = stringify(DEFAULT_CLUSTER_SIZE),
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static int64_t bat2sect(BDRVParallelsState *s, uint32_t idx)
|
||||
{
|
||||
@@ -502,62 +480,46 @@ out:
|
||||
}
|
||||
|
||||
|
||||
static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts,
|
||||
Error **errp)
|
||||
static int coroutine_fn parallels_co_create_opts(const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsParallels *parallels_opts;
|
||||
BlockDriverState *bs;
|
||||
BlockBackend *blk;
|
||||
int64_t total_size, cl_size;
|
||||
uint8_t tmp[BDRV_SECTOR_SIZE];
|
||||
Error *local_err = NULL;
|
||||
BlockBackend *file;
|
||||
uint32_t bat_entries, bat_sectors;
|
||||
ParallelsHeader header;
|
||||
uint8_t tmp[BDRV_SECTOR_SIZE];
|
||||
int ret;
|
||||
|
||||
assert(opts->driver == BLOCKDEV_DRIVER_PARALLELS);
|
||||
parallels_opts = &opts->u.parallels;
|
||||
|
||||
/* Sanity checks */
|
||||
total_size = parallels_opts->size;
|
||||
|
||||
if (parallels_opts->has_cluster_size) {
|
||||
cl_size = parallels_opts->cluster_size;
|
||||
} else {
|
||||
cl_size = DEFAULT_CLUSTER_SIZE;
|
||||
}
|
||||
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
cl_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
|
||||
DEFAULT_CLUSTER_SIZE), BDRV_SECTOR_SIZE);
|
||||
if (total_size >= MAX_PARALLELS_IMAGE_FACTOR * cl_size) {
|
||||
error_setg(errp, "Image size is too large for this cluster size");
|
||||
error_propagate(errp, local_err);
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
if (!QEMU_IS_ALIGNED(total_size, BDRV_SECTOR_SIZE)) {
|
||||
error_setg(errp, "Image size must be a multiple of 512 bytes");
|
||||
return -EINVAL;
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!QEMU_IS_ALIGNED(cl_size, BDRV_SECTOR_SIZE)) {
|
||||
error_setg(errp, "Cluster size must be a multiple of 512 bytes");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Create BlockBackend to write to the image */
|
||||
bs = bdrv_open_blockdev_ref(parallels_opts->file, errp);
|
||||
if (bs == NULL) {
|
||||
file = blk_new_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (file == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
blk_set_allow_write_beyond_eof(file, true);
|
||||
|
||||
/* Create image format */
|
||||
ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
|
||||
ret = blk_truncate(file, 0, PREALLOC_MODE_OFF, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
bat_entries = DIV_ROUND_UP(total_size, cl_size);
|
||||
@@ -580,107 +542,24 @@ static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts,
|
||||
memset(tmp, 0, sizeof(tmp));
|
||||
memcpy(tmp, &header, sizeof(header));
|
||||
|
||||
ret = blk_pwrite(blk, 0, tmp, BDRV_SECTOR_SIZE, 0);
|
||||
ret = blk_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE, 0);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
ret = blk_pwrite_zeroes(blk, BDRV_SECTOR_SIZE,
|
||||
ret = blk_pwrite_zeroes(file, BDRV_SECTOR_SIZE,
|
||||
(bat_sectors - 1) << BDRV_SECTOR_BITS, 0);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs);
|
||||
|
||||
done:
|
||||
blk_unref(file);
|
||||
return ret;
|
||||
|
||||
exit:
|
||||
error_setg_errno(errp, -ret, "Failed to create Parallels image");
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int coroutine_fn parallels_co_create_opts(const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
Error *local_err = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
QDict *qdict = NULL;
|
||||
QObject *qobj;
|
||||
Visitor *v;
|
||||
int ret;
|
||||
|
||||
static const QDictRenames opt_renames[] = {
|
||||
{ BLOCK_OPT_CLUSTER_SIZE, "cluster-size" },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
/* Parse options and convert legacy syntax */
|
||||
qdict = qemu_opts_to_qdict_filtered(opts, NULL, ¶llels_create_opts,
|
||||
true);
|
||||
|
||||
if (!qdict_rename_keys(qdict, opt_renames, errp)) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Create and open the file (protocol layer) */
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto done;
|
||||
}
|
||||
|
||||
bs = bdrv_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
|
||||
if (bs == NULL) {
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Now get the QAPI type BlockdevCreateOptions */
|
||||
qdict_put_str(qdict, "driver", "parallels");
|
||||
qdict_put_str(qdict, "file", bs->node_name);
|
||||
|
||||
qobj = qdict_crumple(qdict, errp);
|
||||
QDECREF(qdict);
|
||||
qdict = qobject_to(QDict, qobj);
|
||||
if (qdict == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Silently round up sizes */
|
||||
create_options->u.parallels.size =
|
||||
ROUND_UP(create_options->u.parallels.size, BDRV_SECTOR_SIZE);
|
||||
create_options->u.parallels.cluster_size =
|
||||
ROUND_UP(create_options->u.parallels.cluster_size, BDRV_SECTOR_SIZE);
|
||||
|
||||
/* Create the Parallels image (format layer) */
|
||||
ret = parallels_co_create(create_options, errp);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
QDECREF(qdict);
|
||||
bdrv_unref(bs);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
||||
@@ -892,6 +771,25 @@ static void parallels_close(BlockDriverState *bs)
|
||||
error_free(s->migration_blocker);
|
||||
}
|
||||
|
||||
static QemuOptsList parallels_create_opts = {
|
||||
.name = "parallels-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(parallels_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size",
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_CLUSTER_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Parallels image cluster size",
|
||||
.def_value_str = stringify(DEFAULT_CLUSTER_SIZE),
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_parallels = {
|
||||
.format_name = "parallels",
|
||||
.instance_size = sizeof(BDRVParallelsState),
|
||||
@@ -905,7 +803,6 @@ static BlockDriver bdrv_parallels = {
|
||||
.bdrv_co_readv = parallels_co_readv,
|
||||
.bdrv_co_writev = parallels_co_writev,
|
||||
.supports_backing = true,
|
||||
.bdrv_co_create = parallels_co_create,
|
||||
.bdrv_co_create_opts = parallels_co_create_opts,
|
||||
.bdrv_co_check = parallels_co_check,
|
||||
.create_opts = ¶llels_create_opts,
|
||||
|
53
block/qapi.c
53
block/qapi.c
@@ -394,37 +394,6 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
|
||||
qapi_free_BlockInfo(info);
|
||||
}
|
||||
|
||||
static uint64List *uint64_list(uint64_t *list, int size)
|
||||
{
|
||||
int i;
|
||||
uint64List *out_list = NULL;
|
||||
uint64List **pout_list = &out_list;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
uint64List *entry = g_new(uint64List, 1);
|
||||
entry->value = list[i];
|
||||
*pout_list = entry;
|
||||
pout_list = &entry->next;
|
||||
}
|
||||
|
||||
*pout_list = NULL;
|
||||
|
||||
return out_list;
|
||||
}
|
||||
|
||||
static void bdrv_latency_histogram_stats(BlockLatencyHistogram *hist,
|
||||
bool *not_null,
|
||||
BlockLatencyHistogramInfo **info)
|
||||
{
|
||||
*not_null = hist->bins != NULL;
|
||||
if (*not_null) {
|
||||
*info = g_new0(BlockLatencyHistogramInfo, 1);
|
||||
|
||||
(*info)->boundaries = uint64_list(hist->boundaries, hist->nbins - 1);
|
||||
(*info)->bins = uint64_list(hist->bins, hist->nbins);
|
||||
}
|
||||
}
|
||||
|
||||
static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
|
||||
{
|
||||
BlockAcctStats *stats = blk_get_stats(blk);
|
||||
@@ -490,16 +459,6 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
|
||||
dev_stats->avg_wr_queue_depth =
|
||||
block_acct_queue_depth(ts, BLOCK_ACCT_WRITE);
|
||||
}
|
||||
|
||||
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_READ],
|
||||
&ds->has_x_rd_latency_histogram,
|
||||
&ds->x_rd_latency_histogram);
|
||||
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_WRITE],
|
||||
&ds->has_x_wr_latency_histogram,
|
||||
&ds->x_wr_latency_histogram);
|
||||
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_FLUSH],
|
||||
&ds->has_x_flush_latency_histogram,
|
||||
&ds->x_flush_latency_histogram);
|
||||
}
|
||||
|
||||
static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs,
|
||||
@@ -688,29 +647,29 @@ static void dump_qobject(fprintf_function func_fprintf, void *f,
|
||||
{
|
||||
switch (qobject_type(obj)) {
|
||||
case QTYPE_QNUM: {
|
||||
QNum *value = qobject_to(QNum, obj);
|
||||
QNum *value = qobject_to_qnum(obj);
|
||||
char *tmp = qnum_to_string(value);
|
||||
func_fprintf(f, "%s", tmp);
|
||||
g_free(tmp);
|
||||
break;
|
||||
}
|
||||
case QTYPE_QSTRING: {
|
||||
QString *value = qobject_to(QString, obj);
|
||||
QString *value = qobject_to_qstring(obj);
|
||||
func_fprintf(f, "%s", qstring_get_str(value));
|
||||
break;
|
||||
}
|
||||
case QTYPE_QDICT: {
|
||||
QDict *value = qobject_to(QDict, obj);
|
||||
QDict *value = qobject_to_qdict(obj);
|
||||
dump_qdict(func_fprintf, f, comp_indent, value);
|
||||
break;
|
||||
}
|
||||
case QTYPE_QLIST: {
|
||||
QList *value = qobject_to(QList, obj);
|
||||
QList *value = qobject_to_qlist(obj);
|
||||
dump_qlist(func_fprintf, f, comp_indent, value);
|
||||
break;
|
||||
}
|
||||
case QTYPE_QBOOL: {
|
||||
QBool *value = qobject_to(QBool, obj);
|
||||
QBool *value = qobject_to_qbool(obj);
|
||||
func_fprintf(f, "%s", qbool_get_bool(value) ? "true" : "false");
|
||||
break;
|
||||
}
|
||||
@@ -771,7 +730,7 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
|
||||
|
||||
visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort);
|
||||
visit_complete(v, &obj);
|
||||
data = qdict_get(qobject_to(QDict, obj), "data");
|
||||
data = qdict_get(qobject_to_qdict(obj), "data");
|
||||
dump_qobject(func_fprintf, f, 1, data);
|
||||
qobject_decref(obj);
|
||||
visit_free(v);
|
||||
|
198
block/qcow.c
198
block/qcow.c
@@ -33,8 +33,6 @@
|
||||
#include <zlib.h>
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "crypto/block.h"
|
||||
#include "migration/blocker.h"
|
||||
#include "block/crypto.h"
|
||||
@@ -88,8 +86,6 @@ typedef struct BDRVQcowState {
|
||||
Error *migration_blocker;
|
||||
} BDRVQcowState;
|
||||
|
||||
static QemuOptsList qcow_create_opts;
|
||||
|
||||
static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
|
||||
|
||||
static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
@@ -814,50 +810,62 @@ static void qcow_close(BlockDriverState *bs)
|
||||
error_free(s->migration_blocker);
|
||||
}
|
||||
|
||||
static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
|
||||
Error **errp)
|
||||
static int coroutine_fn qcow_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsQcow *qcow_opts;
|
||||
int header_size, backing_filename_len, l1_size, shift, i;
|
||||
QCowHeader header;
|
||||
uint8_t *tmp;
|
||||
int64_t total_size = 0;
|
||||
char *backing_file = NULL;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
BlockDriverState *bs;
|
||||
BlockBackend *qcow_blk;
|
||||
char *encryptfmt = NULL;
|
||||
QDict *options;
|
||||
QDict *encryptopts = NULL;
|
||||
QCryptoBlockCreateOptions *crypto_opts = NULL;
|
||||
QCryptoBlock *crypto = NULL;
|
||||
|
||||
assert(opts->driver == BLOCKDEV_DRIVER_QCOW);
|
||||
qcow_opts = &opts->u.qcow;
|
||||
|
||||
/* Sanity checks */
|
||||
total_size = qcow_opts->size;
|
||||
/* Read out options */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
if (total_size == 0) {
|
||||
error_setg(errp, "Image size is too small, cannot be zero length");
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (qcow_opts->has_encrypt &&
|
||||
qcow_opts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_QCOW)
|
||||
{
|
||||
error_setg(errp, "Unsupported encryption format");
|
||||
return -EINVAL;
|
||||
backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
|
||||
encryptfmt = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT);
|
||||
if (encryptfmt) {
|
||||
if (qemu_opt_get(opts, BLOCK_OPT_ENCRYPT)) {
|
||||
error_setg(errp, "Options " BLOCK_OPT_ENCRYPT " and "
|
||||
BLOCK_OPT_ENCRYPT_FORMAT " are mutually exclusive");
|
||||
ret = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
} else if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) {
|
||||
encryptfmt = g_strdup("aes");
|
||||
}
|
||||
|
||||
/* Create BlockBackend to write to the image */
|
||||
bs = bdrv_open_blockdev_ref(qcow_opts->file, errp);
|
||||
if (bs == NULL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
qcow_blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(qcow_blk, bs, errp);
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
error_propagate(errp, local_err);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
qcow_blk = blk_new_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (qcow_blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(qcow_blk, true);
|
||||
|
||||
/* Create image format */
|
||||
ret = blk_truncate(qcow_blk, 0, PREALLOC_MODE_OFF, errp);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
@@ -869,15 +877,16 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
|
||||
header.size = cpu_to_be64(total_size);
|
||||
header_size = sizeof(header);
|
||||
backing_filename_len = 0;
|
||||
if (qcow_opts->has_backing_file) {
|
||||
if (strcmp(qcow_opts->backing_file, "fat:")) {
|
||||
if (backing_file) {
|
||||
if (strcmp(backing_file, "fat:")) {
|
||||
header.backing_file_offset = cpu_to_be64(header_size);
|
||||
backing_filename_len = strlen(qcow_opts->backing_file);
|
||||
backing_filename_len = strlen(backing_file);
|
||||
header.backing_file_size = cpu_to_be32(backing_filename_len);
|
||||
header_size += backing_filename_len;
|
||||
} else {
|
||||
/* special backing file for vvfat */
|
||||
qcow_opts->has_backing_file = false;
|
||||
g_free(backing_file);
|
||||
backing_file = NULL;
|
||||
}
|
||||
header.cluster_bits = 9; /* 512 byte cluster to avoid copying
|
||||
unmodified sectors */
|
||||
@@ -892,10 +901,26 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
|
||||
|
||||
header.l1_table_offset = cpu_to_be64(header_size);
|
||||
|
||||
if (qcow_opts->has_encrypt) {
|
||||
options = qemu_opts_to_qdict(opts, NULL);
|
||||
qdict_extract_subqdict(options, &encryptopts, "encrypt.");
|
||||
QDECREF(options);
|
||||
if (encryptfmt) {
|
||||
if (!g_str_equal(encryptfmt, "aes")) {
|
||||
error_setg(errp, "Unknown encryption format '%s', expected 'aes'",
|
||||
encryptfmt);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
|
||||
|
||||
crypto = qcrypto_block_create(qcow_opts->encrypt, "encrypt.",
|
||||
crypto_opts = block_crypto_create_opts_init(
|
||||
Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp);
|
||||
if (!crypto_opts) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
crypto = qcrypto_block_create(crypto_opts, "encrypt.",
|
||||
NULL, NULL, NULL, errp);
|
||||
if (!crypto) {
|
||||
ret = -EINVAL;
|
||||
@@ -911,9 +936,9 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (qcow_opts->has_backing_file) {
|
||||
if (backing_file) {
|
||||
ret = blk_pwrite(qcow_blk, sizeof(header),
|
||||
qcow_opts->backing_file, backing_filename_len, 0);
|
||||
backing_file, backing_filename_len, 0);
|
||||
if (ret != backing_filename_len) {
|
||||
goto exit;
|
||||
}
|
||||
@@ -934,100 +959,12 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
|
||||
ret = 0;
|
||||
exit:
|
||||
blk_unref(qcow_blk);
|
||||
cleanup:
|
||||
QDECREF(encryptopts);
|
||||
g_free(encryptfmt);
|
||||
qcrypto_block_free(crypto);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn qcow_co_create_opts(const char *filename,
|
||||
QemuOpts *opts, Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
QDict *qdict = NULL;
|
||||
QObject *qobj;
|
||||
Visitor *v;
|
||||
const char *val;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
static const QDictRenames opt_renames[] = {
|
||||
{ BLOCK_OPT_BACKING_FILE, "backing-file" },
|
||||
{ BLOCK_OPT_ENCRYPT, BLOCK_OPT_ENCRYPT_FORMAT },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
/* Parse options and convert legacy syntax */
|
||||
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &qcow_create_opts, true);
|
||||
|
||||
val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT);
|
||||
if (val && !strcmp(val, "on")) {
|
||||
qdict_put_str(qdict, BLOCK_OPT_ENCRYPT, "qcow");
|
||||
} else if (val && !strcmp(val, "off")) {
|
||||
qdict_del(qdict, BLOCK_OPT_ENCRYPT);
|
||||
}
|
||||
|
||||
val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT);
|
||||
if (val && !strcmp(val, "aes")) {
|
||||
qdict_put_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT, "qcow");
|
||||
}
|
||||
|
||||
if (!qdict_rename_keys(qdict, opt_renames, errp)) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Create and open the file (protocol layer) */
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs = bdrv_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
|
||||
if (bs == NULL) {
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Now get the QAPI type BlockdevCreateOptions */
|
||||
qdict_put_str(qdict, "driver", "qcow");
|
||||
qdict_put_str(qdict, "file", bs->node_name);
|
||||
|
||||
qobj = qdict_crumple(qdict, errp);
|
||||
QDECREF(qdict);
|
||||
qdict = qobject_to(QDict, qobj);
|
||||
if (qdict == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Silently round up size */
|
||||
assert(create_options->driver == BLOCKDEV_DRIVER_QCOW);
|
||||
create_options->u.qcow.size =
|
||||
ROUND_UP(create_options->u.qcow.size, BDRV_SECTOR_SIZE);
|
||||
|
||||
/* Create the qcow image (format layer) */
|
||||
ret = qcow_co_create(create_options, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
QDECREF(qdict);
|
||||
bdrv_unref(bs);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
qapi_free_QCryptoBlockCreateOptions(crypto_opts);
|
||||
g_free(backing_file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1191,7 +1128,6 @@ static BlockDriver bdrv_qcow = {
|
||||
.bdrv_close = qcow_close,
|
||||
.bdrv_child_perm = bdrv_format_default_perms,
|
||||
.bdrv_reopen_prepare = qcow_reopen_prepare,
|
||||
.bdrv_co_create = qcow_co_create,
|
||||
.bdrv_co_create_opts = qcow_co_create_opts,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.supports_backing = true,
|
||||
|
@@ -3125,7 +3125,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
|
||||
/* Now get the QAPI type BlockdevCreateOptions */
|
||||
qobj = qdict_crumple(qdict, errp);
|
||||
QDECREF(qdict);
|
||||
qdict = qobject_to(QDict, qobj);
|
||||
qdict = qobject_to_qdict(qobj);
|
||||
if (qdict == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto finish;
|
||||
|
210
block/qed.c
210
block/qed.c
@@ -20,11 +20,6 @@
|
||||
#include "trace.h"
|
||||
#include "qed.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
|
||||
static QemuOptsList qed_create_opts;
|
||||
|
||||
static int bdrv_qed_probe(const uint8_t *buf, int buf_size,
|
||||
const char *filename)
|
||||
@@ -599,78 +594,43 @@ static void bdrv_qed_close(BlockDriverState *bs)
|
||||
qemu_vfree(s->l1_table);
|
||||
}
|
||||
|
||||
static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
|
||||
Error **errp)
|
||||
static int qed_create(const char *filename, uint32_t cluster_size,
|
||||
uint64_t image_size, uint32_t table_size,
|
||||
const char *backing_file, const char *backing_fmt,
|
||||
QemuOpts *opts, Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsQed *qed_opts;
|
||||
BlockBackend *blk = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
|
||||
QEDHeader header;
|
||||
QEDHeader le_header;
|
||||
uint8_t *l1_table = NULL;
|
||||
size_t l1_size;
|
||||
int ret = 0;
|
||||
|
||||
assert(opts->driver == BLOCKDEV_DRIVER_QED);
|
||||
qed_opts = &opts->u.qed;
|
||||
|
||||
/* Validate options and set default values */
|
||||
if (!qed_opts->has_cluster_size) {
|
||||
qed_opts->cluster_size = QED_DEFAULT_CLUSTER_SIZE;
|
||||
}
|
||||
if (!qed_opts->has_table_size) {
|
||||
qed_opts->table_size = QED_DEFAULT_TABLE_SIZE;
|
||||
}
|
||||
|
||||
if (!qed_is_cluster_size_valid(qed_opts->cluster_size)) {
|
||||
error_setg(errp, "QED cluster size must be within range [%u, %u] "
|
||||
"and power of 2",
|
||||
QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!qed_is_table_size_valid(qed_opts->table_size)) {
|
||||
error_setg(errp, "QED table size must be within range [%u, %u] "
|
||||
"and power of 2",
|
||||
QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!qed_is_image_size_valid(qed_opts->size, qed_opts->cluster_size,
|
||||
qed_opts->table_size))
|
||||
{
|
||||
error_setg(errp, "QED image size must be a non-zero multiple of "
|
||||
"cluster size and less than %" PRIu64 " bytes",
|
||||
qed_max_image_size(qed_opts->cluster_size,
|
||||
qed_opts->table_size));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Create BlockBackend to write to the image */
|
||||
bs = bdrv_open_blockdev_ref(qed_opts->file, errp);
|
||||
if (bs == NULL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/* Prepare image format */
|
||||
header = (QEDHeader) {
|
||||
QEDHeader header = {
|
||||
.magic = QED_MAGIC,
|
||||
.cluster_size = qed_opts->cluster_size,
|
||||
.table_size = qed_opts->table_size,
|
||||
.cluster_size = cluster_size,
|
||||
.table_size = table_size,
|
||||
.header_size = 1,
|
||||
.features = 0,
|
||||
.compat_features = 0,
|
||||
.l1_table_offset = qed_opts->cluster_size,
|
||||
.image_size = qed_opts->size,
|
||||
.l1_table_offset = cluster_size,
|
||||
.image_size = image_size,
|
||||
};
|
||||
QEDHeader le_header;
|
||||
uint8_t *l1_table = NULL;
|
||||
size_t l1_size = header.cluster_size * header.table_size;
|
||||
Error *local_err = NULL;
|
||||
int ret = 0;
|
||||
BlockBackend *blk;
|
||||
|
||||
l1_size = header.cluster_size * header.table_size;
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
blk = blk_new_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/* File must start empty and grow, check truncate is supported */
|
||||
ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
|
||||
@@ -678,16 +638,13 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (qed_opts->has_backing_file) {
|
||||
if (backing_file) {
|
||||
header.features |= QED_F_BACKING_FILE;
|
||||
header.backing_filename_offset = sizeof(le_header);
|
||||
header.backing_filename_size = strlen(qed_opts->backing_file);
|
||||
header.backing_filename_size = strlen(backing_file);
|
||||
|
||||
if (qed_opts->has_backing_fmt) {
|
||||
const char *backing_fmt = BlockdevDriver_str(qed_opts->backing_fmt);
|
||||
if (qed_fmt_is_raw(backing_fmt)) {
|
||||
header.features |= QED_F_BACKING_FORMAT_NO_PROBE;
|
||||
}
|
||||
if (qed_fmt_is_raw(backing_fmt)) {
|
||||
header.features |= QED_F_BACKING_FORMAT_NO_PROBE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -696,7 +653,7 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
ret = blk_pwrite(blk, sizeof(le_header), qed_opts->backing_file,
|
||||
ret = blk_pwrite(blk, sizeof(le_header), backing_file,
|
||||
header.backing_filename_size, 0);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
@@ -712,7 +669,6 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
|
||||
out:
|
||||
g_free(l1_table);
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -720,78 +676,51 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
QDict *qdict = NULL;
|
||||
QObject *qobj;
|
||||
Visitor *v;
|
||||
BlockDriverState *bs = NULL;
|
||||
Error *local_err = NULL;
|
||||
uint64_t image_size = 0;
|
||||
uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE;
|
||||
uint32_t table_size = QED_DEFAULT_TABLE_SIZE;
|
||||
char *backing_file = NULL;
|
||||
char *backing_fmt = NULL;
|
||||
int ret;
|
||||
|
||||
static const QDictRenames opt_renames[] = {
|
||||
{ BLOCK_OPT_BACKING_FILE, "backing-file" },
|
||||
{ BLOCK_OPT_BACKING_FMT, "backing-fmt" },
|
||||
{ BLOCK_OPT_CLUSTER_SIZE, "cluster-size" },
|
||||
{ BLOCK_OPT_TABLE_SIZE, "table-size" },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
|
||||
backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT);
|
||||
cluster_size = qemu_opt_get_size_del(opts,
|
||||
BLOCK_OPT_CLUSTER_SIZE,
|
||||
QED_DEFAULT_CLUSTER_SIZE);
|
||||
table_size = qemu_opt_get_size_del(opts, BLOCK_OPT_TABLE_SIZE,
|
||||
QED_DEFAULT_TABLE_SIZE);
|
||||
|
||||
/* Parse options and convert legacy syntax */
|
||||
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &qed_create_opts, true);
|
||||
|
||||
if (!qdict_rename_keys(qdict, opt_renames, errp)) {
|
||||
if (!qed_is_cluster_size_valid(cluster_size)) {
|
||||
error_setg(errp, "QED cluster size must be within range [%u, %u] "
|
||||
"and power of 2",
|
||||
QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Create and open the file (protocol layer) */
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs = bdrv_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
|
||||
if (bs == NULL) {
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Now get the QAPI type BlockdevCreateOptions */
|
||||
qdict_put_str(qdict, "driver", "qed");
|
||||
qdict_put_str(qdict, "file", bs->node_name);
|
||||
|
||||
qobj = qdict_crumple(qdict, errp);
|
||||
QDECREF(qdict);
|
||||
qdict = qobject_to(QDict, qobj);
|
||||
if (qdict == NULL) {
|
||||
if (!qed_is_table_size_valid(table_size)) {
|
||||
error_setg(errp, "QED table size must be within range [%u, %u] "
|
||||
"and power of 2",
|
||||
QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
if (!qed_is_image_size_valid(image_size, cluster_size, table_size)) {
|
||||
error_setg(errp, "QED image size must be a non-zero multiple of "
|
||||
"cluster size and less than %" PRIu64 " bytes",
|
||||
qed_max_image_size(cluster_size, table_size));
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Silently round up size */
|
||||
assert(create_options->driver == BLOCKDEV_DRIVER_QED);
|
||||
create_options->u.qed.size =
|
||||
ROUND_UP(create_options->u.qed.size, BDRV_SECTOR_SIZE);
|
||||
ret = qed_create(filename, cluster_size, image_size, table_size,
|
||||
backing_file, backing_fmt, opts, errp);
|
||||
|
||||
/* Create the qed image (format layer) */
|
||||
ret = bdrv_qed_co_create(create_options, errp);
|
||||
|
||||
fail:
|
||||
QDECREF(qdict);
|
||||
bdrv_unref(bs);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
finish:
|
||||
g_free(backing_file);
|
||||
g_free(backing_fmt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1673,7 +1602,6 @@ static BlockDriver bdrv_qed = {
|
||||
.bdrv_close = bdrv_qed_close,
|
||||
.bdrv_reopen_prepare = bdrv_qed_reopen_prepare,
|
||||
.bdrv_child_perm = bdrv_format_default_perms,
|
||||
.bdrv_co_create = bdrv_qed_co_create,
|
||||
.bdrv_co_create_opts = bdrv_qed_co_create_opts,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_co_block_status = bdrv_qed_co_block_status,
|
||||
|
@@ -1098,11 +1098,10 @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
|
||||
static BlockDriver bdrv_quorum = {
|
||||
.format_name = "quorum",
|
||||
.protocol_name = "quorum",
|
||||
|
||||
.instance_size = sizeof(BDRVQuorumState),
|
||||
|
||||
.bdrv_file_open = quorum_open,
|
||||
.bdrv_open = quorum_open,
|
||||
.bdrv_close = quorum_close,
|
||||
.bdrv_refresh_filename = quorum_refresh_filename,
|
||||
|
||||
|
@@ -263,14 +263,14 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json,
|
||||
if (!keypairs_json) {
|
||||
return ret;
|
||||
}
|
||||
keypairs = qobject_to(QList,
|
||||
qobject_from_json(keypairs_json, &error_abort));
|
||||
keypairs = qobject_to_qlist(qobject_from_json(keypairs_json,
|
||||
&error_abort));
|
||||
remaining = qlist_size(keypairs) / 2;
|
||||
assert(remaining);
|
||||
|
||||
while (remaining--) {
|
||||
name = qobject_to(QString, qlist_pop(keypairs));
|
||||
value = qobject_to(QString, qlist_pop(keypairs));
|
||||
name = qobject_to_qstring(qlist_pop(keypairs));
|
||||
value = qobject_to_qstring(qlist_pop(keypairs));
|
||||
assert(name && value);
|
||||
key = qstring_get_str(name);
|
||||
|
||||
|
@@ -703,7 +703,6 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
|
||||
|
||||
BlockDriver bdrv_replication = {
|
||||
.format_name = "replication",
|
||||
.protocol_name = "replication",
|
||||
.instance_size = sizeof(BDRVReplicationState),
|
||||
|
||||
.bdrv_open = replication_open,
|
||||
|
@@ -1036,7 +1036,7 @@ static void sd_parse_uri(SheepdogConfig *cfg, const char *filename,
|
||||
|
||||
cfg->uri = uri = uri_parse(filename);
|
||||
if (!uri) {
|
||||
error_setg(&err, "invalid URI '%s'", filename);
|
||||
error_setg(&err, "invalid URI");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1887,7 +1887,7 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
qdict = qobject_to(QDict, obj);
|
||||
qdict = qobject_to_qdict(obj);
|
||||
qdict_flatten(qdict);
|
||||
|
||||
qdict_put_str(qdict, "driver", "sheepdog");
|
||||
|
@@ -244,7 +244,7 @@ void stream_start(const char *job_id, BlockDriverState *bs,
|
||||
/* Prevent concurrent jobs trying to modify the graph structure here, we
|
||||
* already have our own plans. Also don't allow resize as the image size is
|
||||
* queried only at the job start and then cached. */
|
||||
s = block_job_create(job_id, &stream_job_driver, NULL, bs,
|
||||
s = block_job_create(job_id, &stream_job_driver, bs,
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
||||
BLK_PERM_GRAPH_MOD,
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
||||
|
@@ -215,10 +215,9 @@ static void coroutine_fn throttle_co_drain_end(BlockDriverState *bs)
|
||||
|
||||
static BlockDriver bdrv_throttle = {
|
||||
.format_name = "throttle",
|
||||
.protocol_name = "throttle",
|
||||
.instance_size = sizeof(ThrottleGroupMember),
|
||||
|
||||
.bdrv_file_open = throttle_open,
|
||||
.bdrv_open = throttle_open,
|
||||
.bdrv_close = throttle_close,
|
||||
.bdrv_co_flush = throttle_co_flush,
|
||||
|
||||
|
@@ -4,16 +4,9 @@
|
||||
bdrv_open_common(void *bs, const char *filename, int flags, const char *format_name) "bs %p filename \"%s\" flags 0x%x format_name \"%s\""
|
||||
bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d"
|
||||
|
||||
# blockjob.c
|
||||
block_job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d"
|
||||
block_job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)"
|
||||
block_job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
|
||||
|
||||
# block/block-backend.c
|
||||
blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x"
|
||||
blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x"
|
||||
blk_root_attach(void *child, void *blk, void *bs) "child %p blk %p bs %p"
|
||||
blk_root_detach(void *child, void *blk, void *bs) "child %p blk %p bs %p"
|
||||
|
||||
# block/io.c
|
||||
bdrv_co_preadv(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x"
|
||||
@@ -53,8 +46,6 @@ qmp_block_job_cancel(void *job) "job %p"
|
||||
qmp_block_job_pause(void *job) "job %p"
|
||||
qmp_block_job_resume(void *job) "job %p"
|
||||
qmp_block_job_complete(void *job) "job %p"
|
||||
qmp_block_job_finalize(void *job) "job %p"
|
||||
qmp_block_job_dismiss(void *job) "job %p"
|
||||
qmp_block_stream(void *bs, void *job) "bs %p job %p"
|
||||
|
||||
# block/file-win32.c
|
||||
|
147
block/vdi.c
147
block/vdi.c
@@ -51,9 +51,6 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
@@ -143,8 +140,6 @@
|
||||
#define VDI_DISK_SIZE_MAX ((uint64_t)VDI_BLOCKS_IN_IMAGE_MAX * \
|
||||
(uint64_t)DEFAULT_CLUSTER_SIZE)
|
||||
|
||||
static QemuOptsList vdi_create_opts;
|
||||
|
||||
typedef struct {
|
||||
char text[0x40];
|
||||
uint32_t signature;
|
||||
@@ -721,46 +716,36 @@ nonallocating_write:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
||||
size_t block_size, Error **errp)
|
||||
static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsVdi *vdi_opts;
|
||||
int ret = 0;
|
||||
uint64_t bytes = 0;
|
||||
uint32_t blocks;
|
||||
size_t block_size = DEFAULT_CLUSTER_SIZE;
|
||||
uint32_t image_type = VDI_TYPE_DYNAMIC;
|
||||
VdiHeader header;
|
||||
size_t i;
|
||||
size_t bmap_size;
|
||||
int64_t offset = 0;
|
||||
BlockDriverState *bs_file = NULL;
|
||||
Error *local_err = NULL;
|
||||
BlockBackend *blk = NULL;
|
||||
uint32_t *bmap = NULL;
|
||||
|
||||
assert(create_options->driver == BLOCKDEV_DRIVER_VDI);
|
||||
vdi_opts = &create_options->u.vdi;
|
||||
|
||||
logout("\n");
|
||||
|
||||
/* Validate options and set default values */
|
||||
bytes = vdi_opts->size;
|
||||
if (vdi_opts->q_static) {
|
||||
image_type = VDI_TYPE_STATIC;
|
||||
}
|
||||
#ifndef CONFIG_VDI_STATIC_IMAGE
|
||||
if (image_type == VDI_TYPE_STATIC) {
|
||||
ret = -ENOTSUP;
|
||||
error_setg(errp, "Statically allocated images cannot be created in "
|
||||
"this build");
|
||||
goto exit;
|
||||
}
|
||||
/* Read out options. */
|
||||
bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
#if defined(CONFIG_VDI_BLOCK_SIZE)
|
||||
/* TODO: Additional checks (SECTOR_SIZE * 2^n, ...). */
|
||||
block_size = qemu_opt_get_size_del(opts,
|
||||
BLOCK_OPT_CLUSTER_SIZE,
|
||||
DEFAULT_CLUSTER_SIZE);
|
||||
#endif
|
||||
#ifndef CONFIG_VDI_BLOCK_SIZE
|
||||
if (block_size != DEFAULT_CLUSTER_SIZE) {
|
||||
ret = -ENOTSUP;
|
||||
error_setg(errp,
|
||||
"A non-default cluster size is not supported in this build");
|
||||
goto exit;
|
||||
#if defined(CONFIG_VDI_STATIC_IMAGE)
|
||||
if (qemu_opt_get_bool_del(opts, BLOCK_OPT_STATIC, false)) {
|
||||
image_type = VDI_TYPE_STATIC;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -772,16 +757,18 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Create BlockBackend to write to the image */
|
||||
bs_file = bdrv_open_blockdev_ref(vdi_opts->file, errp);
|
||||
if (!bs_file) {
|
||||
ret = -EIO;
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs_file, errp);
|
||||
if (ret < 0) {
|
||||
blk = blk_new_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
@@ -818,7 +805,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
||||
vdi_header_to_le(&header);
|
||||
ret = blk_pwrite(blk, offset, &header, sizeof(header), 0);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Error writing header");
|
||||
error_setg(errp, "Error writing header to %s", filename);
|
||||
goto exit;
|
||||
}
|
||||
offset += sizeof(header);
|
||||
@@ -839,7 +826,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
||||
}
|
||||
ret = blk_pwrite(blk, offset, bmap, bmap_size, 0);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Error writing bmap");
|
||||
error_setg(errp, "Error writing bmap to %s", filename);
|
||||
goto exit;
|
||||
}
|
||||
offset += bmap_size;
|
||||
@@ -849,96 +836,17 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
||||
ret = blk_truncate(blk, offset + blocks * block_size,
|
||||
PREALLOC_MODE_OFF, errp);
|
||||
if (ret < 0) {
|
||||
error_prepend(errp, "Failed to statically allocate file");
|
||||
error_prepend(errp, "Failed to statically allocate %s", filename);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs_file);
|
||||
g_free(bmap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn vdi_co_create(BlockdevCreateOptions *create_options,
|
||||
Error **errp)
|
||||
{
|
||||
return vdi_co_do_create(create_options, DEFAULT_CLUSTER_SIZE, errp);
|
||||
}
|
||||
|
||||
static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
QDict *qdict = NULL;
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
BlockDriverState *bs_file = NULL;
|
||||
uint64_t block_size = DEFAULT_CLUSTER_SIZE;
|
||||
Visitor *v;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
/* Parse options and convert legacy syntax.
|
||||
*
|
||||
* Since CONFIG_VDI_BLOCK_SIZE is disabled by default,
|
||||
* cluster-size is not part of the QAPI schema; therefore we have
|
||||
* to parse it before creating the QAPI object. */
|
||||
#if defined(CONFIG_VDI_BLOCK_SIZE)
|
||||
block_size = qemu_opt_get_size_del(opts,
|
||||
BLOCK_OPT_CLUSTER_SIZE,
|
||||
DEFAULT_CLUSTER_SIZE);
|
||||
if (block_size < BDRV_SECTOR_SIZE || block_size > UINT32_MAX ||
|
||||
!is_power_of_2(block_size))
|
||||
{
|
||||
error_setg(errp, "Invalid cluster size");
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
|
||||
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vdi_create_opts, true);
|
||||
|
||||
/* Create and open the file (protocol layer) */
|
||||
ret = bdrv_create_file(filename, opts, errp);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
bs_file = bdrv_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
|
||||
if (!bs_file) {
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
qdict_put_str(qdict, "driver", "vdi");
|
||||
qdict_put_str(qdict, "file", bs_file->node_name);
|
||||
|
||||
/* Get the QAPI object */
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Silently round up size */
|
||||
assert(create_options->driver == BLOCKDEV_DRIVER_VDI);
|
||||
create_options->u.vdi.size = ROUND_UP(create_options->u.vdi.size,
|
||||
BDRV_SECTOR_SIZE);
|
||||
|
||||
/* Create the vdi image (format layer) */
|
||||
ret = vdi_co_do_create(create_options, block_size, errp);
|
||||
done:
|
||||
QDECREF(qdict);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
bdrv_unref(bs_file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vdi_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVdiState *s = bs->opaque;
|
||||
@@ -987,7 +895,6 @@ static BlockDriver bdrv_vdi = {
|
||||
.bdrv_close = vdi_close,
|
||||
.bdrv_reopen_prepare = vdi_reopen_prepare,
|
||||
.bdrv_child_perm = bdrv_format_default_perms,
|
||||
.bdrv_co_create = vdi_co_create,
|
||||
.bdrv_co_create_opts = vdi_co_create_opts,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_co_block_status = vdi_co_block_status,
|
||||
|
216
block/vhdx.c
216
block/vhdx.c
@@ -26,9 +26,6 @@
|
||||
#include "block/vhdx.h"
|
||||
#include "migration/blocker.h"
|
||||
#include "qemu/uuid.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
|
||||
/* Options for VHDX creation */
|
||||
|
||||
@@ -42,8 +39,6 @@ typedef enum VHDXImageType {
|
||||
VHDX_TYPE_DIFFERENCING, /* Currently unsupported */
|
||||
} VHDXImageType;
|
||||
|
||||
static QemuOptsList vhdx_create_opts;
|
||||
|
||||
/* Several metadata and region table data entries are identified by
|
||||
* guids in a MS-specific GUID format. */
|
||||
|
||||
@@ -1797,71 +1792,59 @@ exit:
|
||||
* .---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------.
|
||||
* 1MB
|
||||
*/
|
||||
static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
|
||||
Error **errp)
|
||||
static int coroutine_fn vhdx_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsVhdx *vhdx_opts;
|
||||
BlockBackend *blk = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
|
||||
int ret = 0;
|
||||
uint64_t image_size;
|
||||
uint32_t log_size;
|
||||
uint32_t block_size;
|
||||
uint64_t image_size = (uint64_t) 2 * GiB;
|
||||
uint32_t log_size = 1 * MiB;
|
||||
uint32_t block_size = 0;
|
||||
uint64_t signature;
|
||||
uint64_t metadata_offset;
|
||||
bool use_zero_blocks = false;
|
||||
|
||||
gunichar2 *creator = NULL;
|
||||
glong creator_items;
|
||||
BlockBackend *blk;
|
||||
char *type = NULL;
|
||||
VHDXImageType image_type;
|
||||
Error *local_err = NULL;
|
||||
|
||||
assert(opts->driver == BLOCKDEV_DRIVER_VHDX);
|
||||
vhdx_opts = &opts->u.vhdx;
|
||||
image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
log_size = qemu_opt_get_size_del(opts, VHDX_BLOCK_OPT_LOG_SIZE, 0);
|
||||
block_size = qemu_opt_get_size_del(opts, VHDX_BLOCK_OPT_BLOCK_SIZE, 0);
|
||||
type = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
|
||||
use_zero_blocks = qemu_opt_get_bool_del(opts, VHDX_BLOCK_OPT_ZERO, true);
|
||||
|
||||
/* Validate options and set default values */
|
||||
image_size = vhdx_opts->size;
|
||||
if (image_size > VHDX_MAX_IMAGE_SIZE) {
|
||||
error_setg_errno(errp, EINVAL, "Image size too large; max of 64TB");
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!vhdx_opts->has_log_size) {
|
||||
log_size = DEFAULT_LOG_SIZE;
|
||||
} else {
|
||||
log_size = vhdx_opts->log_size;
|
||||
}
|
||||
if (log_size < MiB || (log_size % MiB) != 0) {
|
||||
error_setg_errno(errp, EINVAL, "Log size must be a multiple of 1 MB");
|
||||
return -EINVAL;
|
||||
if (type == NULL) {
|
||||
type = g_strdup("dynamic");
|
||||
}
|
||||
|
||||
if (!vhdx_opts->has_block_state_zero) {
|
||||
use_zero_blocks = true;
|
||||
} else {
|
||||
use_zero_blocks = vhdx_opts->block_state_zero;
|
||||
}
|
||||
|
||||
if (!vhdx_opts->has_subformat) {
|
||||
vhdx_opts->subformat = BLOCKDEV_VHDX_SUBFORMAT_DYNAMIC;
|
||||
}
|
||||
|
||||
switch (vhdx_opts->subformat) {
|
||||
case BLOCKDEV_VHDX_SUBFORMAT_DYNAMIC:
|
||||
if (!strcmp(type, "dynamic")) {
|
||||
image_type = VHDX_TYPE_DYNAMIC;
|
||||
break;
|
||||
case BLOCKDEV_VHDX_SUBFORMAT_FIXED:
|
||||
} else if (!strcmp(type, "fixed")) {
|
||||
image_type = VHDX_TYPE_FIXED;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
} else if (!strcmp(type, "differencing")) {
|
||||
error_setg_errno(errp, ENOTSUP,
|
||||
"Differencing files not yet supported");
|
||||
ret = -ENOTSUP;
|
||||
goto exit;
|
||||
} else {
|
||||
error_setg(errp, "Invalid subformat '%s'", type);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* These are pretty arbitrary, and mainly designed to keep the BAT
|
||||
* size reasonable to load into RAM */
|
||||
if (vhdx_opts->has_block_size) {
|
||||
block_size = vhdx_opts->block_size;
|
||||
} else {
|
||||
if (block_size == 0) {
|
||||
if (image_size > 32 * TiB) {
|
||||
block_size = 64 * MiB;
|
||||
} else if (image_size > (uint64_t) 100 * GiB) {
|
||||
@@ -1873,27 +1856,30 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
|
||||
}
|
||||
}
|
||||
|
||||
if (block_size < MiB || (block_size % MiB) != 0) {
|
||||
error_setg_errno(errp, EINVAL, "Block size must be a multiple of 1 MB");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (block_size > VHDX_BLOCK_SIZE_MAX) {
|
||||
error_setg_errno(errp, EINVAL, "Block size must not exceed %d",
|
||||
VHDX_BLOCK_SIZE_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Create BlockBackend to write to the image */
|
||||
bs = bdrv_open_blockdev_ref(vhdx_opts->file, errp);
|
||||
if (bs == NULL) {
|
||||
return -EIO;
|
||||
}
|
||||
/* make the log size close to what was specified, but must be
|
||||
* min 1MB, and multiple of 1MB */
|
||||
log_size = ROUND_UP(log_size, MiB);
|
||||
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
block_size = ROUND_UP(block_size, MiB);
|
||||
block_size = block_size > VHDX_BLOCK_SIZE_MAX ? VHDX_BLOCK_SIZE_MAX :
|
||||
block_size;
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
goto delete_and_exit;
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk = blk_new_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/* Create (A) */
|
||||
@@ -1945,109 +1931,12 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
|
||||
|
||||
delete_and_exit:
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs);
|
||||
exit:
|
||||
g_free(type);
|
||||
g_free(creator);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn vhdx_co_create_opts(const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
QDict *qdict = NULL;
|
||||
QObject *qobj;
|
||||
Visitor *v;
|
||||
BlockDriverState *bs = NULL;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
static const QDictRenames opt_renames[] = {
|
||||
{ VHDX_BLOCK_OPT_LOG_SIZE, "log-size" },
|
||||
{ VHDX_BLOCK_OPT_BLOCK_SIZE, "block-size" },
|
||||
{ VHDX_BLOCK_OPT_ZERO, "block-state-zero" },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
/* Parse options and convert legacy syntax */
|
||||
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vhdx_create_opts, true);
|
||||
|
||||
if (!qdict_rename_keys(qdict, opt_renames, errp)) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Create and open the file (protocol layer) */
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs = bdrv_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
|
||||
if (bs == NULL) {
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Now get the QAPI type BlockdevCreateOptions */
|
||||
qdict_put_str(qdict, "driver", "vhdx");
|
||||
qdict_put_str(qdict, "file", bs->node_name);
|
||||
|
||||
qobj = qdict_crumple(qdict, errp);
|
||||
QDECREF(qdict);
|
||||
qdict = qobject_to(QDict, qobj);
|
||||
if (qdict == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Silently round up sizes:
|
||||
* The image size is rounded to 512 bytes. Make the block and log size
|
||||
* close to what was specified, but must be at least 1MB, and a multiple of
|
||||
* 1 MB. Also respect VHDX_BLOCK_SIZE_MAX for block sizes. block_size = 0
|
||||
* means auto, which is represented by a missing key in QAPI. */
|
||||
assert(create_options->driver == BLOCKDEV_DRIVER_VHDX);
|
||||
create_options->u.vhdx.size =
|
||||
ROUND_UP(create_options->u.vhdx.size, BDRV_SECTOR_SIZE);
|
||||
|
||||
if (create_options->u.vhdx.has_log_size) {
|
||||
create_options->u.vhdx.log_size =
|
||||
ROUND_UP(create_options->u.vhdx.log_size, MiB);
|
||||
}
|
||||
if (create_options->u.vhdx.has_block_size) {
|
||||
create_options->u.vhdx.block_size =
|
||||
ROUND_UP(create_options->u.vhdx.block_size, MiB);
|
||||
|
||||
if (create_options->u.vhdx.block_size == 0) {
|
||||
create_options->u.vhdx.has_block_size = false;
|
||||
}
|
||||
if (create_options->u.vhdx.block_size > VHDX_BLOCK_SIZE_MAX) {
|
||||
create_options->u.vhdx.block_size = VHDX_BLOCK_SIZE_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create the vhdx image (format layer) */
|
||||
ret = vhdx_co_create(create_options, errp);
|
||||
|
||||
fail:
|
||||
QDECREF(qdict);
|
||||
bdrv_unref(bs);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* If opened r/w, the VHDX driver will automatically replay the log,
|
||||
* if one is present, inside the vhdx_open() call.
|
||||
*
|
||||
@@ -2116,7 +2005,6 @@ static BlockDriver bdrv_vhdx = {
|
||||
.bdrv_child_perm = bdrv_format_default_perms,
|
||||
.bdrv_co_readv = vhdx_co_readv,
|
||||
.bdrv_co_writev = vhdx_co_writev,
|
||||
.bdrv_co_create = vhdx_co_create,
|
||||
.bdrv_co_create_opts = vhdx_co_create_opts,
|
||||
.bdrv_get_info = vhdx_get_info,
|
||||
.bdrv_co_check = vhdx_co_check,
|
||||
|
241
block/vpc.c
241
block/vpc.c
@@ -32,9 +32,6 @@
|
||||
#include "migration/blocker.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/uuid.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
@@ -169,8 +166,6 @@ static QemuOptsList vpc_runtime_opts = {
|
||||
}
|
||||
};
|
||||
|
||||
static QemuOptsList vpc_create_opts;
|
||||
|
||||
static uint32_t vpc_checksum(uint8_t* buf, size_t size)
|
||||
{
|
||||
uint32_t res = 0;
|
||||
@@ -902,19 +897,60 @@ static int create_fixed_disk(BlockBackend *blk, uint8_t *buf,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int calculate_rounded_image_size(BlockdevCreateOptionsVpc *vpc_opts,
|
||||
uint16_t *out_cyls,
|
||||
uint8_t *out_heads,
|
||||
uint8_t *out_secs_per_cyl,
|
||||
int64_t *out_total_sectors,
|
||||
Error **errp)
|
||||
static int coroutine_fn vpc_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
int64_t total_size = vpc_opts->size;
|
||||
uint8_t buf[1024];
|
||||
VHDFooter *footer = (VHDFooter *) buf;
|
||||
char *disk_type_param;
|
||||
int i;
|
||||
uint16_t cyls = 0;
|
||||
uint8_t heads = 0;
|
||||
uint8_t secs_per_cyl = 0;
|
||||
int64_t total_sectors;
|
||||
int i;
|
||||
int64_t total_size;
|
||||
int disk_type;
|
||||
int ret = -EIO;
|
||||
bool force_size;
|
||||
Error *local_err = NULL;
|
||||
BlockBackend *blk = NULL;
|
||||
|
||||
/* Read out options */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
disk_type_param = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
|
||||
if (disk_type_param) {
|
||||
if (!strcmp(disk_type_param, "dynamic")) {
|
||||
disk_type = VHD_DYNAMIC;
|
||||
} else if (!strcmp(disk_type_param, "fixed")) {
|
||||
disk_type = VHD_FIXED;
|
||||
} else {
|
||||
error_setg(errp, "Invalid disk type, %s", disk_type_param);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
disk_type = VHD_DYNAMIC;
|
||||
}
|
||||
|
||||
force_size = qemu_opt_get_bool_del(opts, VPC_OPT_FORCE_SIZE, false);
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
blk = blk_new_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/*
|
||||
* Calculate matching total_size and geometry. Increase the number of
|
||||
@@ -925,7 +961,7 @@ static int calculate_rounded_image_size(BlockdevCreateOptionsVpc *vpc_opts,
|
||||
* we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use
|
||||
* the image size from the VHD footer to calculate total_sectors.
|
||||
*/
|
||||
if (vpc_opts->force_size) {
|
||||
if (force_size) {
|
||||
/* This will force the use of total_size for sector count, below */
|
||||
cyls = VHD_CHS_MAX_C;
|
||||
heads = VHD_CHS_MAX_H;
|
||||
@@ -942,95 +978,19 @@ static int calculate_rounded_image_size(BlockdevCreateOptionsVpc *vpc_opts,
|
||||
/* Allow a maximum disk size of 2040 GiB */
|
||||
if (total_sectors > VHD_MAX_SECTORS) {
|
||||
error_setg(errp, "Disk size is too large, max size is 2040 GiB");
|
||||
return -EFBIG;
|
||||
ret = -EFBIG;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
total_sectors = (int64_t) cyls * heads * secs_per_cyl;
|
||||
}
|
||||
|
||||
*out_total_sectors = total_sectors;
|
||||
if (out_cyls) {
|
||||
*out_cyls = cyls;
|
||||
*out_heads = heads;
|
||||
*out_secs_per_cyl = secs_per_cyl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsVpc *vpc_opts;
|
||||
BlockBackend *blk = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
|
||||
uint8_t buf[1024];
|
||||
VHDFooter *footer = (VHDFooter *) buf;
|
||||
uint16_t cyls = 0;
|
||||
uint8_t heads = 0;
|
||||
uint8_t secs_per_cyl = 0;
|
||||
int64_t total_sectors;
|
||||
int64_t total_size;
|
||||
int disk_type;
|
||||
int ret = -EIO;
|
||||
|
||||
assert(opts->driver == BLOCKDEV_DRIVER_VPC);
|
||||
vpc_opts = &opts->u.vpc;
|
||||
|
||||
/* Validate options and set default values */
|
||||
total_size = vpc_opts->size;
|
||||
|
||||
if (!vpc_opts->has_subformat) {
|
||||
vpc_opts->subformat = BLOCKDEV_VPC_SUBFORMAT_DYNAMIC;
|
||||
}
|
||||
switch (vpc_opts->subformat) {
|
||||
case BLOCKDEV_VPC_SUBFORMAT_DYNAMIC:
|
||||
disk_type = VHD_DYNAMIC;
|
||||
break;
|
||||
case BLOCKDEV_VPC_SUBFORMAT_FIXED:
|
||||
disk_type = VHD_FIXED;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
/* Create BlockBackend to write to the image */
|
||||
bs = bdrv_open_blockdev_ref(vpc_opts->file, errp);
|
||||
if (bs == NULL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/* Get geometry and check that it matches the image size*/
|
||||
ret = calculate_rounded_image_size(vpc_opts, &cyls, &heads, &secs_per_cyl,
|
||||
&total_sectors, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (total_size != total_sectors * BDRV_SECTOR_SIZE) {
|
||||
error_setg(errp, "The requested image size cannot be represented in "
|
||||
"CHS geometry");
|
||||
error_append_hint(errp, "Try size=%llu or force-size=on (the "
|
||||
"latter makes the image incompatible with "
|
||||
"Virtual PC)",
|
||||
total_sectors * BDRV_SECTOR_SIZE);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
total_sectors = (int64_t)cyls * heads * secs_per_cyl;
|
||||
total_size = total_sectors * BDRV_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
/* Prepare the Hard Disk Footer */
|
||||
memset(buf, 0, 1024);
|
||||
|
||||
memcpy(footer->creator, "conectix", 8);
|
||||
if (vpc_opts->force_size) {
|
||||
if (force_size) {
|
||||
memcpy(footer->creator_app, "qem2", 4);
|
||||
} else {
|
||||
memcpy(footer->creator_app, "qemu", 4);
|
||||
@@ -1072,98 +1032,10 @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
|
||||
|
||||
out:
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs);
|
||||
g_free(disk_type_param);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn vpc_co_create_opts(const char *filename,
|
||||
QemuOpts *opts, Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
QDict *qdict = NULL;
|
||||
QObject *qobj;
|
||||
Visitor *v;
|
||||
BlockDriverState *bs = NULL;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
static const QDictRenames opt_renames[] = {
|
||||
{ VPC_OPT_FORCE_SIZE, "force-size" },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
/* Parse options and convert legacy syntax */
|
||||
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vpc_create_opts, true);
|
||||
|
||||
if (!qdict_rename_keys(qdict, opt_renames, errp)) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Create and open the file (protocol layer) */
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs = bdrv_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
|
||||
if (bs == NULL) {
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Now get the QAPI type BlockdevCreateOptions */
|
||||
qdict_put_str(qdict, "driver", "vpc");
|
||||
qdict_put_str(qdict, "file", bs->node_name);
|
||||
|
||||
qobj = qdict_crumple(qdict, errp);
|
||||
QDECREF(qdict);
|
||||
qdict = qobject_to(QDict, qobj);
|
||||
if (qdict == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Silently round up size */
|
||||
assert(create_options->driver == BLOCKDEV_DRIVER_VPC);
|
||||
create_options->u.vpc.size =
|
||||
ROUND_UP(create_options->u.vpc.size, BDRV_SECTOR_SIZE);
|
||||
|
||||
if (!create_options->u.vpc.force_size) {
|
||||
int64_t total_sectors;
|
||||
ret = calculate_rounded_image_size(&create_options->u.vpc, NULL, NULL,
|
||||
NULL, &total_sectors, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
create_options->u.vpc.size = total_sectors * BDRV_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
|
||||
/* Create the vpc image (format layer) */
|
||||
ret = vpc_co_create(create_options, errp);
|
||||
|
||||
fail:
|
||||
QDECREF(qdict);
|
||||
bdrv_unref(bs);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int vpc_has_zero_init(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVPCState *s = bs->opaque;
|
||||
@@ -1224,7 +1096,6 @@ static BlockDriver bdrv_vpc = {
|
||||
.bdrv_close = vpc_close,
|
||||
.bdrv_reopen_prepare = vpc_reopen_prepare,
|
||||
.bdrv_child_perm = bdrv_format_default_perms,
|
||||
.bdrv_co_create = vpc_co_create,
|
||||
.bdrv_co_create_opts = vpc_co_create_opts,
|
||||
|
||||
.bdrv_co_preadv = vpc_co_preadv,
|
||||
|
@@ -3129,7 +3129,7 @@ static void vvfat_qcow_options(int *child_flags, QDict *child_options,
|
||||
int parent_flags, QDict *parent_options)
|
||||
{
|
||||
qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "off");
|
||||
qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on");
|
||||
*child_flags = BDRV_O_NO_FLUSH;
|
||||
}
|
||||
|
||||
static const BdrvChildRole child_vvfat_qcow = {
|
||||
|
154
blockdev.c
154
blockdev.c
@@ -150,7 +150,7 @@ void blockdev_mark_auto_del(BlockBackend *blk)
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
if (bs->job) {
|
||||
block_job_cancel(bs->job, false);
|
||||
block_job_cancel(bs->job);
|
||||
}
|
||||
|
||||
aio_context_release(aio_context);
|
||||
@@ -334,8 +334,7 @@ static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals,
|
||||
|
||||
case QTYPE_QSTRING: {
|
||||
unsigned long long length;
|
||||
const char *str = qstring_get_str(qobject_to(QString,
|
||||
entry->value));
|
||||
const char *str = qstring_get_str(qobject_to_qstring(entry->value));
|
||||
if (parse_uint_full(str, &length, 10) == 0 &&
|
||||
length > 0 && length <= UINT_MAX) {
|
||||
block_acct_add_interval(stats, (unsigned) length);
|
||||
@@ -347,7 +346,7 @@ static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals,
|
||||
}
|
||||
|
||||
case QTYPE_QNUM: {
|
||||
int64_t length = qnum_get_int(qobject_to(QNum, entry->value));
|
||||
int64_t length = qnum_get_int(qobject_to_qnum(entry->value));
|
||||
|
||||
if (length > 0 && length <= UINT_MAX) {
|
||||
block_acct_add_interval(stats, (unsigned) length);
|
||||
@@ -2119,9 +2118,6 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
|
||||
if (bdrv_dirty_bitmap_frozen(state->bitmap)) {
|
||||
error_setg(errp, "Cannot modify a frozen bitmap");
|
||||
return;
|
||||
} else if (bdrv_dirty_bitmap_qmp_locked(state->bitmap)) {
|
||||
error_setg(errp, "Cannot modify a locked bitmap");
|
||||
return;
|
||||
} else if (!bdrv_dirty_bitmap_enabled(state->bitmap)) {
|
||||
error_setg(errp, "Cannot clear a disabled bitmap");
|
||||
return;
|
||||
@@ -2866,11 +2862,6 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
|
||||
"Bitmap '%s' is currently frozen and cannot be removed",
|
||||
name);
|
||||
return;
|
||||
} else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently locked and cannot be removed",
|
||||
name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_get_persistance(bitmap)) {
|
||||
@@ -2905,11 +2896,6 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
|
||||
"Bitmap '%s' is currently frozen and cannot be modified",
|
||||
name);
|
||||
return;
|
||||
} else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently locked and cannot be modified",
|
||||
name);
|
||||
return;
|
||||
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently disabled and cannot be cleared",
|
||||
@@ -3275,7 +3261,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
||||
AioContext *aio_context;
|
||||
QDict *options = NULL;
|
||||
Error *local_err = NULL;
|
||||
int flags, job_flags = BLOCK_JOB_DEFAULT;
|
||||
int flags;
|
||||
int64_t size;
|
||||
bool set_backing_hd = false;
|
||||
|
||||
@@ -3294,12 +3280,6 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
||||
if (!backup->has_job_id) {
|
||||
backup->job_id = NULL;
|
||||
}
|
||||
if (!backup->has_auto_finalize) {
|
||||
backup->auto_finalize = true;
|
||||
}
|
||||
if (!backup->has_auto_dismiss) {
|
||||
backup->auto_dismiss = true;
|
||||
}
|
||||
if (!backup->has_compress) {
|
||||
backup->compress = false;
|
||||
}
|
||||
@@ -3390,24 +3370,12 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
||||
bdrv_unref(target_bs);
|
||||
goto out;
|
||||
}
|
||||
if (bdrv_dirty_bitmap_qmp_locked(bmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently locked and cannot be used for "
|
||||
"backup", backup->bitmap);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (!backup->auto_finalize) {
|
||||
job_flags |= BLOCK_JOB_MANUAL_FINALIZE;
|
||||
}
|
||||
if (!backup->auto_dismiss) {
|
||||
job_flags |= BLOCK_JOB_MANUAL_DISMISS;
|
||||
}
|
||||
|
||||
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
|
||||
backup->sync, bmap, backup->compress,
|
||||
backup->on_source_error, backup->on_target_error,
|
||||
job_flags, NULL, NULL, txn, &local_err);
|
||||
BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err);
|
||||
bdrv_unref(target_bs);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
@@ -3442,7 +3410,6 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
|
||||
Error *local_err = NULL;
|
||||
AioContext *aio_context;
|
||||
BlockJob *job = NULL;
|
||||
int job_flags = BLOCK_JOB_DEFAULT;
|
||||
|
||||
if (!backup->has_speed) {
|
||||
backup->speed = 0;
|
||||
@@ -3456,12 +3423,6 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
|
||||
if (!backup->has_job_id) {
|
||||
backup->job_id = NULL;
|
||||
}
|
||||
if (!backup->has_auto_finalize) {
|
||||
backup->auto_finalize = true;
|
||||
}
|
||||
if (!backup->has_auto_dismiss) {
|
||||
backup->auto_dismiss = true;
|
||||
}
|
||||
if (!backup->has_compress) {
|
||||
backup->compress = false;
|
||||
}
|
||||
@@ -3490,16 +3451,10 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (!backup->auto_finalize) {
|
||||
job_flags |= BLOCK_JOB_MANUAL_FINALIZE;
|
||||
}
|
||||
if (!backup->auto_dismiss) {
|
||||
job_flags |= BLOCK_JOB_MANUAL_DISMISS;
|
||||
}
|
||||
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
|
||||
backup->sync, NULL, backup->compress,
|
||||
backup->on_source_error, backup->on_target_error,
|
||||
job_flags, NULL, NULL, txn, &local_err);
|
||||
BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
@@ -3851,7 +3806,7 @@ void qmp_block_job_cancel(const char *device,
|
||||
}
|
||||
|
||||
trace_qmp_block_job_cancel(job);
|
||||
block_job_user_cancel(job, force, errp);
|
||||
block_job_cancel(job);
|
||||
out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
@@ -3861,12 +3816,12 @@ void qmp_block_job_pause(const char *device, Error **errp)
|
||||
AioContext *aio_context;
|
||||
BlockJob *job = find_block_job(device, &aio_context, errp);
|
||||
|
||||
if (!job) {
|
||||
if (!job || block_job_user_paused(job)) {
|
||||
return;
|
||||
}
|
||||
|
||||
trace_qmp_block_job_pause(job);
|
||||
block_job_user_pause(job, errp);
|
||||
block_job_user_pause(job);
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
@@ -3875,12 +3830,12 @@ void qmp_block_job_resume(const char *device, Error **errp)
|
||||
AioContext *aio_context;
|
||||
BlockJob *job = find_block_job(device, &aio_context, errp);
|
||||
|
||||
if (!job) {
|
||||
if (!job || !block_job_user_paused(job)) {
|
||||
return;
|
||||
}
|
||||
|
||||
trace_qmp_block_job_resume(job);
|
||||
block_job_user_resume(job, errp);
|
||||
block_job_user_resume(job);
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
@@ -3898,34 +3853,6 @@ void qmp_block_job_complete(const char *device, Error **errp)
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void qmp_block_job_finalize(const char *id, Error **errp)
|
||||
{
|
||||
AioContext *aio_context;
|
||||
BlockJob *job = find_block_job(id, &aio_context, errp);
|
||||
|
||||
if (!job) {
|
||||
return;
|
||||
}
|
||||
|
||||
trace_qmp_block_job_finalize(job);
|
||||
block_job_finalize(job, errp);
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void qmp_block_job_dismiss(const char *id, Error **errp)
|
||||
{
|
||||
AioContext *aio_context;
|
||||
BlockJob *job = find_block_job(id, &aio_context, errp);
|
||||
|
||||
if (!job) {
|
||||
return;
|
||||
}
|
||||
|
||||
trace_qmp_block_job_dismiss(job);
|
||||
block_job_dismiss(&job, errp);
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void qmp_change_backing_file(const char *device,
|
||||
const char *image_node_name,
|
||||
const char *backing_file,
|
||||
@@ -4045,6 +3972,7 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
|
||||
QObject *obj;
|
||||
Visitor *v = qobject_output_visitor_new(&obj);
|
||||
QDict *qdict;
|
||||
const QDictEntry *ent;
|
||||
Error *local_err = NULL;
|
||||
|
||||
visit_type_BlockdevOptions(v, NULL, &options, &local_err);
|
||||
@@ -4054,10 +3982,23 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
|
||||
}
|
||||
|
||||
visit_complete(v, &obj);
|
||||
qdict = qobject_to(QDict, obj);
|
||||
qdict = qobject_to_qdict(obj);
|
||||
|
||||
qdict_flatten(qdict);
|
||||
|
||||
/*
|
||||
* Rewrite "backing": null to "backing": ""
|
||||
* TODO Rewrite "" to null instead, and perhaps not even here
|
||||
*/
|
||||
for (ent = qdict_first(qdict); ent; ent = qdict_next(qdict, ent)) {
|
||||
char *dot = strrchr(ent->key, '.');
|
||||
|
||||
if (!strcmp(dot ? dot + 1 : ent->key, "backing")
|
||||
&& qobject_type(ent->value) == QTYPE_QNULL) {
|
||||
qdict_put(qdict, ent->key, qstring_new());
|
||||
}
|
||||
}
|
||||
|
||||
if (!qdict_get_try_str(qdict, "node-name")) {
|
||||
error_setg(errp, "'node-name' must be specified for the root node");
|
||||
goto fail;
|
||||
@@ -4239,49 +4180,6 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
|
||||
aio_context_release(old_context);
|
||||
}
|
||||
|
||||
void qmp_x_block_latency_histogram_set(
|
||||
const char *device,
|
||||
bool has_boundaries, uint64List *boundaries,
|
||||
bool has_boundaries_read, uint64List *boundaries_read,
|
||||
bool has_boundaries_write, uint64List *boundaries_write,
|
||||
bool has_boundaries_flush, uint64List *boundaries_flush,
|
||||
Error **errp)
|
||||
{
|
||||
BlockBackend *blk = blk_by_name(device);
|
||||
BlockAcctStats *stats;
|
||||
|
||||
if (!blk) {
|
||||
error_setg(errp, "Device '%s' not found", device);
|
||||
return;
|
||||
}
|
||||
stats = blk_get_stats(blk);
|
||||
|
||||
if (!has_boundaries && !has_boundaries_read && !has_boundaries_write &&
|
||||
!has_boundaries_flush)
|
||||
{
|
||||
block_latency_histograms_clear(stats);
|
||||
return;
|
||||
}
|
||||
|
||||
if (has_boundaries || has_boundaries_read) {
|
||||
block_latency_histogram_set(
|
||||
stats, BLOCK_ACCT_READ,
|
||||
has_boundaries_read ? boundaries_read : boundaries);
|
||||
}
|
||||
|
||||
if (has_boundaries || has_boundaries_write) {
|
||||
block_latency_histogram_set(
|
||||
stats, BLOCK_ACCT_WRITE,
|
||||
has_boundaries_write ? boundaries_write : boundaries);
|
||||
}
|
||||
|
||||
if (has_boundaries || has_boundaries_flush) {
|
||||
block_latency_histogram_set(
|
||||
stats, BLOCK_ACCT_FLUSH,
|
||||
has_boundaries_flush ? boundaries_flush : boundaries);
|
||||
}
|
||||
}
|
||||
|
||||
QemuOptsList qemu_common_drive_opts = {
|
||||
.name = "drive",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),
|
||||
|
358
blockjob.c
358
blockjob.c
@@ -28,7 +28,6 @@
|
||||
#include "block/block.h"
|
||||
#include "block/blockjob_int.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/trace.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-events-block-core.h"
|
||||
@@ -42,64 +41,6 @@
|
||||
* block_job_enter. */
|
||||
static QemuMutex block_job_mutex;
|
||||
|
||||
/* BlockJob State Transition Table */
|
||||
bool BlockJobSTT[BLOCK_JOB_STATUS__MAX][BLOCK_JOB_STATUS__MAX] = {
|
||||
/* U, C, R, P, Y, S, W, D, X, E, N */
|
||||
/* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
/* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1},
|
||||
/* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0},
|
||||
/* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
/* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0},
|
||||
/* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
|
||||
/* W: */ [BLOCK_JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0},
|
||||
/* D: */ [BLOCK_JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
|
||||
/* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
|
||||
/* E: */ [BLOCK_JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
/* N: */ [BLOCK_JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
};
|
||||
|
||||
bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = {
|
||||
/* U, C, R, P, Y, S, W, D, X, E, N */
|
||||
[BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
|
||||
[BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
|
||||
[BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
|
||||
[BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
|
||||
[BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
|
||||
[BLOCK_JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
|
||||
[BLOCK_JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
|
||||
};
|
||||
|
||||
static void block_job_state_transition(BlockJob *job, BlockJobStatus s1)
|
||||
{
|
||||
BlockJobStatus s0 = job->status;
|
||||
assert(s1 >= 0 && s1 <= BLOCK_JOB_STATUS__MAX);
|
||||
trace_block_job_state_transition(job, job->ret, BlockJobSTT[s0][s1] ?
|
||||
"allowed" : "disallowed",
|
||||
qapi_enum_lookup(&BlockJobStatus_lookup,
|
||||
s0),
|
||||
qapi_enum_lookup(&BlockJobStatus_lookup,
|
||||
s1));
|
||||
assert(BlockJobSTT[s0][s1]);
|
||||
job->status = s1;
|
||||
}
|
||||
|
||||
static int block_job_apply_verb(BlockJob *job, BlockJobVerb bv, Error **errp)
|
||||
{
|
||||
assert(bv >= 0 && bv <= BLOCK_JOB_VERB__MAX);
|
||||
trace_block_job_apply_verb(job, qapi_enum_lookup(&BlockJobStatus_lookup,
|
||||
job->status),
|
||||
qapi_enum_lookup(&BlockJobVerb_lookup, bv),
|
||||
BlockJobVerbTable[bv][job->status] ?
|
||||
"allowed" : "prohibited");
|
||||
if (BlockJobVerbTable[bv][job->status]) {
|
||||
return 0;
|
||||
}
|
||||
error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'",
|
||||
job->id, qapi_enum_lookup(&BlockJobStatus_lookup, job->status),
|
||||
qapi_enum_lookup(&BlockJobVerb_lookup, bv));
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
static void block_job_lock(void)
|
||||
{
|
||||
qemu_mutex_lock(&block_job_mutex);
|
||||
@@ -117,7 +58,6 @@ static void __attribute__((__constructor__)) block_job_init(void)
|
||||
|
||||
static void block_job_event_cancelled(BlockJob *job);
|
||||
static void block_job_event_completed(BlockJob *job, const char *msg);
|
||||
static int block_job_event_pending(BlockJob *job);
|
||||
static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job));
|
||||
|
||||
/* Transactional group of block jobs */
|
||||
@@ -231,7 +171,6 @@ static void block_job_detach_aio_context(void *opaque);
|
||||
void block_job_unref(BlockJob *job)
|
||||
{
|
||||
if (--job->refcnt == 0) {
|
||||
assert(job->status == BLOCK_JOB_STATUS_NULL);
|
||||
BlockDriverState *bs = blk_bs(job->blk);
|
||||
QLIST_REMOVE(job, job_list);
|
||||
bs->job = NULL;
|
||||
@@ -381,88 +320,25 @@ void block_job_start(BlockJob *job)
|
||||
job->pause_count--;
|
||||
job->busy = true;
|
||||
job->paused = false;
|
||||
block_job_state_transition(job, BLOCK_JOB_STATUS_RUNNING);
|
||||
bdrv_coroutine_enter(blk_bs(job->blk), job->co);
|
||||
}
|
||||
|
||||
static void block_job_decommission(BlockJob *job)
|
||||
{
|
||||
assert(job);
|
||||
job->completed = true;
|
||||
job->busy = false;
|
||||
job->paused = false;
|
||||
job->deferred_to_main_loop = true;
|
||||
block_job_state_transition(job, BLOCK_JOB_STATUS_NULL);
|
||||
block_job_unref(job);
|
||||
}
|
||||
|
||||
static void block_job_do_dismiss(BlockJob *job)
|
||||
{
|
||||
block_job_decommission(job);
|
||||
}
|
||||
|
||||
static void block_job_conclude(BlockJob *job)
|
||||
{
|
||||
block_job_state_transition(job, BLOCK_JOB_STATUS_CONCLUDED);
|
||||
if (job->auto_dismiss || !block_job_started(job)) {
|
||||
block_job_do_dismiss(job);
|
||||
}
|
||||
}
|
||||
|
||||
static void block_job_update_rc(BlockJob *job)
|
||||
{
|
||||
if (!job->ret && block_job_is_cancelled(job)) {
|
||||
job->ret = -ECANCELED;
|
||||
}
|
||||
if (job->ret) {
|
||||
block_job_state_transition(job, BLOCK_JOB_STATUS_ABORTING);
|
||||
}
|
||||
}
|
||||
|
||||
static int block_job_prepare(BlockJob *job)
|
||||
{
|
||||
if (job->ret == 0 && job->driver->prepare) {
|
||||
job->ret = job->driver->prepare(job);
|
||||
}
|
||||
return job->ret;
|
||||
}
|
||||
|
||||
static void block_job_commit(BlockJob *job)
|
||||
{
|
||||
assert(!job->ret);
|
||||
if (job->driver->commit) {
|
||||
job->driver->commit(job);
|
||||
}
|
||||
}
|
||||
|
||||
static void block_job_abort(BlockJob *job)
|
||||
{
|
||||
assert(job->ret);
|
||||
if (job->driver->abort) {
|
||||
job->driver->abort(job);
|
||||
}
|
||||
}
|
||||
|
||||
static void block_job_clean(BlockJob *job)
|
||||
{
|
||||
if (job->driver->clean) {
|
||||
job->driver->clean(job);
|
||||
}
|
||||
}
|
||||
|
||||
static int block_job_finalize_single(BlockJob *job)
|
||||
static void block_job_completed_single(BlockJob *job)
|
||||
{
|
||||
assert(job->completed);
|
||||
|
||||
/* Ensure abort is called for late-transactional failures */
|
||||
block_job_update_rc(job);
|
||||
|
||||
if (!job->ret) {
|
||||
block_job_commit(job);
|
||||
if (job->driver->commit) {
|
||||
job->driver->commit(job);
|
||||
}
|
||||
} else {
|
||||
block_job_abort(job);
|
||||
if (job->driver->abort) {
|
||||
job->driver->abort(job);
|
||||
}
|
||||
}
|
||||
if (job->driver->clean) {
|
||||
job->driver->clean(job);
|
||||
}
|
||||
block_job_clean(job);
|
||||
|
||||
if (job->cb) {
|
||||
job->cb(job->opaque, job->ret);
|
||||
@@ -481,13 +357,14 @@ static int block_job_finalize_single(BlockJob *job)
|
||||
}
|
||||
}
|
||||
|
||||
QLIST_REMOVE(job, txn_list);
|
||||
block_job_txn_unref(job->txn);
|
||||
block_job_conclude(job);
|
||||
return 0;
|
||||
if (job->txn) {
|
||||
QLIST_REMOVE(job, txn_list);
|
||||
block_job_txn_unref(job->txn);
|
||||
}
|
||||
block_job_unref(job);
|
||||
}
|
||||
|
||||
static void block_job_cancel_async(BlockJob *job, bool force)
|
||||
static void block_job_cancel_async(BlockJob *job)
|
||||
{
|
||||
if (job->iostatus != BLOCK_DEVICE_IO_STATUS_OK) {
|
||||
block_job_iostatus_reset(job);
|
||||
@@ -498,30 +375,6 @@ static void block_job_cancel_async(BlockJob *job, bool force)
|
||||
job->pause_count--;
|
||||
}
|
||||
job->cancelled = true;
|
||||
/* To prevent 'force == false' overriding a previous 'force == true' */
|
||||
job->force |= force;
|
||||
}
|
||||
|
||||
static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock)
|
||||
{
|
||||
AioContext *ctx;
|
||||
BlockJob *job, *next;
|
||||
int rc = 0;
|
||||
|
||||
QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) {
|
||||
if (lock) {
|
||||
ctx = blk_get_aio_context(job->blk);
|
||||
aio_context_acquire(ctx);
|
||||
}
|
||||
rc = fn(job);
|
||||
if (lock) {
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
if (rc) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int block_job_finish_sync(BlockJob *job,
|
||||
@@ -583,7 +436,7 @@ static void block_job_completed_txn_abort(BlockJob *job)
|
||||
* on the caller, so leave it. */
|
||||
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
|
||||
if (other_job != job) {
|
||||
block_job_cancel_async(other_job, false);
|
||||
block_job_cancel_async(other_job);
|
||||
}
|
||||
}
|
||||
while (!QLIST_EMPTY(&txn->jobs)) {
|
||||
@@ -593,39 +446,18 @@ static void block_job_completed_txn_abort(BlockJob *job)
|
||||
assert(other_job->cancelled);
|
||||
block_job_finish_sync(other_job, NULL, NULL);
|
||||
}
|
||||
block_job_finalize_single(other_job);
|
||||
block_job_completed_single(other_job);
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
|
||||
block_job_txn_unref(txn);
|
||||
}
|
||||
|
||||
static int block_job_needs_finalize(BlockJob *job)
|
||||
{
|
||||
return !job->auto_finalize;
|
||||
}
|
||||
|
||||
static void block_job_do_finalize(BlockJob *job)
|
||||
{
|
||||
int rc;
|
||||
assert(job && job->txn);
|
||||
|
||||
/* prepare the transaction to complete */
|
||||
rc = block_job_txn_apply(job->txn, block_job_prepare, true);
|
||||
if (rc) {
|
||||
block_job_completed_txn_abort(job);
|
||||
} else {
|
||||
block_job_txn_apply(job->txn, block_job_finalize_single, true);
|
||||
}
|
||||
}
|
||||
|
||||
static void block_job_completed_txn_success(BlockJob *job)
|
||||
{
|
||||
AioContext *ctx;
|
||||
BlockJobTxn *txn = job->txn;
|
||||
BlockJob *other_job;
|
||||
|
||||
block_job_state_transition(job, BLOCK_JOB_STATUS_WAITING);
|
||||
|
||||
BlockJob *other_job, *next;
|
||||
/*
|
||||
* Successful completion, see if there are other running jobs in this
|
||||
* txn.
|
||||
@@ -634,14 +466,14 @@ static void block_job_completed_txn_success(BlockJob *job)
|
||||
if (!other_job->completed) {
|
||||
return;
|
||||
}
|
||||
assert(other_job->ret == 0);
|
||||
}
|
||||
|
||||
block_job_txn_apply(txn, block_job_event_pending, false);
|
||||
|
||||
/* If no jobs need manual finalization, automatically do so */
|
||||
if (block_job_txn_apply(txn, block_job_needs_finalize, false) == 0) {
|
||||
block_job_do_finalize(job);
|
||||
/* We are the last completed job, commit the transaction. */
|
||||
QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) {
|
||||
ctx = blk_get_aio_context(other_job->blk);
|
||||
aio_context_acquire(ctx);
|
||||
assert(other_job->ret == 0);
|
||||
block_job_completed_single(other_job);
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -660,9 +492,6 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
error_setg(errp, QERR_UNSUPPORTED);
|
||||
return;
|
||||
}
|
||||
if (block_job_apply_verb(job, BLOCK_JOB_VERB_SET_SPEED, errp)) {
|
||||
return;
|
||||
}
|
||||
job->driver->set_speed(job, speed, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@@ -670,7 +499,7 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
}
|
||||
|
||||
job->speed = speed;
|
||||
if (speed && speed <= old_speed) {
|
||||
if (speed <= old_speed) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -682,10 +511,8 @@ void block_job_complete(BlockJob *job, Error **errp)
|
||||
{
|
||||
/* Should not be reachable via external interface for internal jobs */
|
||||
assert(job->id);
|
||||
if (block_job_apply_verb(job, BLOCK_JOB_VERB_COMPLETE, errp)) {
|
||||
return;
|
||||
}
|
||||
if (job->pause_count || job->cancelled || !job->driver->complete) {
|
||||
if (job->pause_count || job->cancelled ||
|
||||
!block_job_started(job) || !job->driver->complete) {
|
||||
error_setg(errp, "The active block job '%s' cannot be completed",
|
||||
job->id);
|
||||
return;
|
||||
@@ -694,37 +521,8 @@ void block_job_complete(BlockJob *job, Error **errp)
|
||||
job->driver->complete(job, errp);
|
||||
}
|
||||
|
||||
void block_job_finalize(BlockJob *job, Error **errp)
|
||||
void block_job_user_pause(BlockJob *job)
|
||||
{
|
||||
assert(job && job->id && job->txn);
|
||||
if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) {
|
||||
return;
|
||||
}
|
||||
block_job_do_finalize(job);
|
||||
}
|
||||
|
||||
void block_job_dismiss(BlockJob **jobptr, Error **errp)
|
||||
{
|
||||
BlockJob *job = *jobptr;
|
||||
/* similarly to _complete, this is QMP-interface only. */
|
||||
assert(job->id);
|
||||
if (block_job_apply_verb(job, BLOCK_JOB_VERB_DISMISS, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
block_job_do_dismiss(job);
|
||||
*jobptr = NULL;
|
||||
}
|
||||
|
||||
void block_job_user_pause(BlockJob *job, Error **errp)
|
||||
{
|
||||
if (block_job_apply_verb(job, BLOCK_JOB_VERB_PAUSE, errp)) {
|
||||
return;
|
||||
}
|
||||
if (job->user_paused) {
|
||||
error_setg(errp, "Job is already paused");
|
||||
return;
|
||||
}
|
||||
job->user_paused = true;
|
||||
block_job_pause(job);
|
||||
}
|
||||
@@ -734,51 +532,31 @@ bool block_job_user_paused(BlockJob *job)
|
||||
return job->user_paused;
|
||||
}
|
||||
|
||||
void block_job_user_resume(BlockJob *job, Error **errp)
|
||||
void block_job_user_resume(BlockJob *job)
|
||||
{
|
||||
assert(job);
|
||||
if (!job->user_paused || job->pause_count <= 0) {
|
||||
error_setg(errp, "Can't resume a job that was not paused");
|
||||
return;
|
||||
if (job && job->user_paused && job->pause_count > 0) {
|
||||
block_job_iostatus_reset(job);
|
||||
job->user_paused = false;
|
||||
block_job_resume(job);
|
||||
}
|
||||
if (block_job_apply_verb(job, BLOCK_JOB_VERB_RESUME, errp)) {
|
||||
return;
|
||||
}
|
||||
block_job_iostatus_reset(job);
|
||||
job->user_paused = false;
|
||||
block_job_resume(job);
|
||||
}
|
||||
|
||||
void block_job_cancel(BlockJob *job, bool force)
|
||||
void block_job_cancel(BlockJob *job)
|
||||
{
|
||||
if (job->status == BLOCK_JOB_STATUS_CONCLUDED) {
|
||||
block_job_do_dismiss(job);
|
||||
return;
|
||||
}
|
||||
block_job_cancel_async(job, force);
|
||||
if (!block_job_started(job)) {
|
||||
block_job_completed(job, -ECANCELED);
|
||||
} else if (job->deferred_to_main_loop) {
|
||||
block_job_completed_txn_abort(job);
|
||||
} else {
|
||||
if (block_job_started(job)) {
|
||||
block_job_cancel_async(job);
|
||||
block_job_enter(job);
|
||||
} else {
|
||||
block_job_completed(job, -ECANCELED);
|
||||
}
|
||||
}
|
||||
|
||||
void block_job_user_cancel(BlockJob *job, bool force, Error **errp)
|
||||
{
|
||||
if (block_job_apply_verb(job, BLOCK_JOB_VERB_CANCEL, errp)) {
|
||||
return;
|
||||
}
|
||||
block_job_cancel(job, force);
|
||||
}
|
||||
|
||||
/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be
|
||||
* used with block_job_finish_sync() without the need for (rather nasty)
|
||||
* function pointer casts there. */
|
||||
static void block_job_cancel_err(BlockJob *job, Error **errp)
|
||||
{
|
||||
block_job_cancel(job, false);
|
||||
block_job_cancel(job);
|
||||
}
|
||||
|
||||
int block_job_cancel_sync(BlockJob *job)
|
||||
@@ -822,9 +600,6 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
|
||||
info->speed = job->speed;
|
||||
info->io_status = job->iostatus;
|
||||
info->ready = job->ready;
|
||||
info->status = job->status;
|
||||
info->auto_finalize = job->auto_finalize;
|
||||
info->auto_dismiss = job->auto_dismiss;
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -866,24 +641,13 @@ static void block_job_event_completed(BlockJob *job, const char *msg)
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static int block_job_event_pending(BlockJob *job)
|
||||
{
|
||||
block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING);
|
||||
if (!job->auto_finalize && !block_job_is_internal(job)) {
|
||||
qapi_event_send_block_job_pending(job->driver->job_type,
|
||||
job->id,
|
||||
&error_abort);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* API for block job drivers and the block layer. These functions are
|
||||
* declared in blockjob_int.h.
|
||||
*/
|
||||
|
||||
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
|
||||
BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm,
|
||||
BlockDriverState *bs, uint64_t perm,
|
||||
uint64_t shared_perm, int64_t speed, int flags,
|
||||
BlockCompletionFunc *cb, void *opaque, Error **errp)
|
||||
{
|
||||
@@ -938,9 +702,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
|
||||
job->paused = true;
|
||||
job->pause_count = 1;
|
||||
job->refcnt = 1;
|
||||
job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE);
|
||||
job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
|
||||
block_job_state_transition(job, BLOCK_JOB_STATUS_CREATED);
|
||||
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
|
||||
QEMU_CLOCK_REALTIME, SCALE_NS,
|
||||
block_job_sleep_timer_cb, job);
|
||||
@@ -963,22 +724,11 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
|
||||
|
||||
block_job_set_speed(job, speed, &local_err);
|
||||
if (local_err) {
|
||||
block_job_early_fail(job);
|
||||
block_job_unref(job);
|
||||
error_propagate(errp, local_err);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Single jobs are modeled as single-job transactions for sake of
|
||||
* consolidating the job management logic */
|
||||
if (!txn) {
|
||||
txn = block_job_txn_new();
|
||||
block_job_txn_add_job(txn, job);
|
||||
block_job_txn_unref(txn);
|
||||
} else {
|
||||
block_job_txn_add_job(txn, job);
|
||||
}
|
||||
|
||||
return job;
|
||||
}
|
||||
|
||||
@@ -997,19 +747,18 @@ void block_job_pause_all(void)
|
||||
|
||||
void block_job_early_fail(BlockJob *job)
|
||||
{
|
||||
assert(job->status == BLOCK_JOB_STATUS_CREATED);
|
||||
block_job_decommission(job);
|
||||
block_job_unref(job);
|
||||
}
|
||||
|
||||
void block_job_completed(BlockJob *job, int ret)
|
||||
{
|
||||
assert(job && job->txn && !job->completed);
|
||||
assert(blk_bs(job->blk)->job == job);
|
||||
assert(!job->completed);
|
||||
job->completed = true;
|
||||
job->ret = ret;
|
||||
block_job_update_rc(job);
|
||||
trace_block_job_completed(job, ret, job->ret);
|
||||
if (job->ret) {
|
||||
if (!job->txn) {
|
||||
block_job_completed_single(job);
|
||||
} else if (ret < 0 || block_job_is_cancelled(job)) {
|
||||
block_job_completed_txn_abort(job);
|
||||
} else {
|
||||
block_job_completed_txn_success(job);
|
||||
@@ -1057,14 +806,9 @@ void coroutine_fn block_job_pause_point(BlockJob *job)
|
||||
}
|
||||
|
||||
if (block_job_should_pause(job) && !block_job_is_cancelled(job)) {
|
||||
BlockJobStatus status = job->status;
|
||||
block_job_state_transition(job, status == BLOCK_JOB_STATUS_READY ? \
|
||||
BLOCK_JOB_STATUS_STANDBY : \
|
||||
BLOCK_JOB_STATUS_PAUSED);
|
||||
job->paused = true;
|
||||
block_job_do_yield(job, -1);
|
||||
job->paused = false;
|
||||
block_job_state_transition(job, status);
|
||||
}
|
||||
|
||||
if (job->driver->resume) {
|
||||
@@ -1170,7 +914,6 @@ void block_job_iostatus_reset(BlockJob *job)
|
||||
|
||||
void block_job_event_ready(BlockJob *job)
|
||||
{
|
||||
block_job_state_transition(job, BLOCK_JOB_STATUS_READY);
|
||||
job->ready = true;
|
||||
|
||||
if (block_job_is_internal(job)) {
|
||||
@@ -1214,9 +957,8 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
|
||||
action, &error_abort);
|
||||
}
|
||||
if (action == BLOCK_ERROR_ACTION_STOP) {
|
||||
block_job_pause(job);
|
||||
/* make the pause user visible, which will be resumed from QMP. */
|
||||
job->user_paused = true;
|
||||
block_job_user_pause(job);
|
||||
block_job_iostatus_set_err(job, error);
|
||||
}
|
||||
return action;
|
||||
|
@@ -649,7 +649,7 @@ void cpu_loop(CPUSPARCState *env)
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("qemu-" TARGET_NAME " version " QEMU_FULL_VERSION
|
||||
printf("qemu-" TARGET_NAME " version " QEMU_VERSION QEMU_PKGVERSION
|
||||
"\n" QEMU_COPYRIGHT "\n"
|
||||
"usage: qemu-" TARGET_NAME " [options] program [arguments...]\n"
|
||||
"BSD CPU emulator (compiled for %s emulation)\n"
|
||||
@@ -723,7 +723,6 @@ int main(int argc, char **argv)
|
||||
{
|
||||
const char *filename;
|
||||
const char *cpu_model;
|
||||
const char *cpu_type;
|
||||
const char *log_file = NULL;
|
||||
const char *log_mask = NULL;
|
||||
struct target_pt_regs regs1, *regs = ®s1;
|
||||
@@ -901,8 +900,7 @@ int main(int argc, char **argv)
|
||||
tcg_exec_init(0);
|
||||
/* NOTE: we need to init the CPU at this stage to get
|
||||
qemu_host_page_size */
|
||||
cpu_type = parse_cpu_model(cpu_model);
|
||||
cpu = cpu_create(cpu_type);
|
||||
cpu = cpu_init(cpu_model);
|
||||
env = cpu->env_ptr;
|
||||
#if defined(TARGET_SPARC) || defined(TARGET_PPC)
|
||||
cpu_reset(cpu);
|
||||
|
@@ -27,7 +27,6 @@
|
||||
#include "qemu/option.h"
|
||||
#include "chardev/char.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "chardev/char-mux.h"
|
||||
|
||||
/* MUX driver for serial I/O splitting */
|
||||
@@ -231,12 +230,14 @@ static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
|
||||
}
|
||||
}
|
||||
|
||||
bool muxes_realized;
|
||||
|
||||
void mux_chr_send_all_event(Chardev *chr, int event)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(chr);
|
||||
int i;
|
||||
|
||||
if (!machine_init_done) {
|
||||
if (!muxes_realized) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -326,7 +327,7 @@ static void qemu_chr_open_mux(Chardev *chr,
|
||||
/* only default to opened state if we've realized the initial
|
||||
* set of muxes
|
||||
*/
|
||||
*be_opened = machine_init_done;
|
||||
*be_opened = muxes_realized;
|
||||
qemu_chr_fe_init(&d->chr, drv, errp);
|
||||
}
|
||||
|
||||
@@ -346,31 +347,6 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
|
||||
mux->chardev = g_strdup(chardev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after processing of default and command-line-specified
|
||||
* chardevs to deliver CHR_EVENT_OPENED events to any FEs attached
|
||||
* to a mux chardev. This is done here to ensure that
|
||||
* output/prompts/banners are only displayed for the FE that has
|
||||
* focus when initial command-line processing/machine init is
|
||||
* completed.
|
||||
*
|
||||
* After this point, any new FE attached to any new or existing
|
||||
* mux will receive CHR_EVENT_OPENED notifications for the BE
|
||||
* immediately.
|
||||
*/
|
||||
static int open_muxes(Chardev *chr)
|
||||
{
|
||||
/* send OPENED to all already-attached FEs */
|
||||
mux_chr_send_all_event(chr, CHR_EVENT_OPENED);
|
||||
/*
|
||||
* mark mux as OPENED so any new FEs will immediately receive
|
||||
* OPENED event
|
||||
*/
|
||||
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void char_mux_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
@@ -381,7 +357,6 @@ static void char_mux_class_init(ObjectClass *oc, void *data)
|
||||
cc->chr_accept_input = mux_chr_accept_input;
|
||||
cc->chr_add_watch = mux_chr_add_watch;
|
||||
cc->chr_be_event = mux_chr_be_event;
|
||||
cc->chr_machine_done = open_muxes;
|
||||
}
|
||||
|
||||
static const TypeInfo char_mux_type_info = {
|
||||
|
@@ -32,7 +32,6 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/clone-visitor.h"
|
||||
#include "qapi/qapi-visit-sockets.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
#include "chardev/char-io.h"
|
||||
|
||||
@@ -41,11 +40,6 @@
|
||||
|
||||
#define TCP_MAX_FDS 16
|
||||
|
||||
typedef struct {
|
||||
char buf[21];
|
||||
size_t buflen;
|
||||
} TCPChardevTelnetInit;
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
QIOChannel *ioc; /* Client I/O channel */
|
||||
@@ -66,8 +60,6 @@ typedef struct {
|
||||
bool is_listen;
|
||||
bool is_telnet;
|
||||
bool is_tn3270;
|
||||
GSource *telnet_source;
|
||||
TCPChardevTelnetInit *telnet_init;
|
||||
|
||||
GSource *reconnect_timer;
|
||||
int64_t reconnect_time;
|
||||
@@ -78,7 +70,6 @@ typedef struct {
|
||||
OBJECT_CHECK(SocketChardev, (obj), TYPE_CHARDEV_SOCKET)
|
||||
|
||||
static gboolean socket_reconnect_timeout(gpointer opaque);
|
||||
static void tcp_chr_telnet_init(Chardev *chr);
|
||||
|
||||
static void tcp_chr_reconn_timer_cancel(SocketChardev *s)
|
||||
{
|
||||
@@ -432,8 +423,8 @@ static void tcp_chr_disconnect(Chardev *chr)
|
||||
tcp_chr_free_connection(chr);
|
||||
|
||||
if (s->listener) {
|
||||
qio_net_listener_set_client_func_full(s->listener, tcp_chr_accept,
|
||||
chr, NULL, chr->gcontext);
|
||||
qio_net_listener_set_client_func(s->listener, tcp_chr_accept,
|
||||
chr, NULL);
|
||||
}
|
||||
update_disconnected_filename(s);
|
||||
if (emit_close) {
|
||||
@@ -459,7 +450,7 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
|
||||
len = s->max_size;
|
||||
}
|
||||
size = tcp_chr_recv(chr, (void *)buf, len);
|
||||
if (size == 0 || (size == -1 && errno != EAGAIN)) {
|
||||
if (size == 0 || size == -1) {
|
||||
/* connection closed */
|
||||
tcp_chr_disconnect(chr);
|
||||
} else if (size > 0) {
|
||||
@@ -565,33 +556,10 @@ static void tcp_chr_connect(void *opaque)
|
||||
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
|
||||
}
|
||||
|
||||
static void tcp_chr_telnet_destroy(SocketChardev *s)
|
||||
{
|
||||
if (s->telnet_source) {
|
||||
g_source_destroy(s->telnet_source);
|
||||
g_source_unref(s->telnet_source);
|
||||
s->telnet_source = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void tcp_chr_update_read_handler(Chardev *chr)
|
||||
{
|
||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||
|
||||
if (s->listener) {
|
||||
/*
|
||||
* It's possible that chardev context is changed in
|
||||
* qemu_chr_be_update_read_handlers(). Reset it for QIO net
|
||||
* listener if there is.
|
||||
*/
|
||||
qio_net_listener_set_client_func_full(s->listener, tcp_chr_accept,
|
||||
chr, NULL, chr->gcontext);
|
||||
}
|
||||
|
||||
if (s->telnet_source) {
|
||||
tcp_chr_telnet_init(CHARDEV(s));
|
||||
}
|
||||
|
||||
if (!s->connected) {
|
||||
return;
|
||||
}
|
||||
@@ -605,30 +573,32 @@ static void tcp_chr_update_read_handler(Chardev *chr)
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Chardev *chr;
|
||||
char buf[21];
|
||||
size_t buflen;
|
||||
} TCPChardevTelnetInit;
|
||||
|
||||
static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
|
||||
GIOCondition cond G_GNUC_UNUSED,
|
||||
gpointer user_data)
|
||||
{
|
||||
SocketChardev *s = user_data;
|
||||
Chardev *chr = CHARDEV(s);
|
||||
TCPChardevTelnetInit *init = s->telnet_init;
|
||||
TCPChardevTelnetInit *init = user_data;
|
||||
ssize_t ret;
|
||||
|
||||
assert(init);
|
||||
|
||||
ret = qio_channel_write(ioc, init->buf, init->buflen, NULL);
|
||||
if (ret < 0) {
|
||||
if (ret == QIO_CHANNEL_ERR_BLOCK) {
|
||||
ret = 0;
|
||||
} else {
|
||||
tcp_chr_disconnect(chr);
|
||||
tcp_chr_disconnect(init->chr);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
init->buflen -= ret;
|
||||
|
||||
if (init->buflen == 0) {
|
||||
tcp_chr_connect(chr);
|
||||
tcp_chr_connect(init->chr);
|
||||
goto end;
|
||||
}
|
||||
|
||||
@@ -637,30 +607,16 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
|
||||
return G_SOURCE_CONTINUE;
|
||||
|
||||
end:
|
||||
g_free(s->telnet_init);
|
||||
s->telnet_init = NULL;
|
||||
g_source_unref(s->telnet_source);
|
||||
s->telnet_source = NULL;
|
||||
g_free(init);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void tcp_chr_telnet_init(Chardev *chr)
|
||||
{
|
||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||
TCPChardevTelnetInit *init;
|
||||
TCPChardevTelnetInit *init = g_new0(TCPChardevTelnetInit, 1);
|
||||
size_t n = 0;
|
||||
|
||||
/* Destroy existing task */
|
||||
tcp_chr_telnet_destroy(s);
|
||||
|
||||
if (s->telnet_init) {
|
||||
/* We are possibly during a handshake already */
|
||||
goto cont;
|
||||
}
|
||||
|
||||
s->telnet_init = g_new0(TCPChardevTelnetInit, 1);
|
||||
init = s->telnet_init;
|
||||
|
||||
#define IACSET(x, a, b, c) \
|
||||
do { \
|
||||
x[n++] = a; \
|
||||
@@ -668,6 +624,7 @@ static void tcp_chr_telnet_init(Chardev *chr)
|
||||
x[n++] = c; \
|
||||
} while (0)
|
||||
|
||||
init->chr = chr;
|
||||
if (!s->is_tn3270) {
|
||||
init->buflen = 12;
|
||||
/* Prep the telnet negotion to put telnet in binary,
|
||||
@@ -690,11 +647,10 @@ static void tcp_chr_telnet_init(Chardev *chr)
|
||||
|
||||
#undef IACSET
|
||||
|
||||
cont:
|
||||
s->telnet_source = qio_channel_add_watch_source(s->ioc, G_IO_OUT,
|
||||
tcp_chr_telnet_init_io,
|
||||
s, NULL,
|
||||
chr->gcontext);
|
||||
qio_channel_add_watch(
|
||||
s->ioc, G_IO_OUT,
|
||||
tcp_chr_telnet_init_io,
|
||||
init, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -707,7 +663,8 @@ static void tcp_chr_tls_handshake(QIOTask *task,
|
||||
if (qio_task_propagate_error(task, NULL)) {
|
||||
tcp_chr_disconnect(chr);
|
||||
} else {
|
||||
if (s->do_telnetopt) {
|
||||
/* tn3270 does not support TLS yet */
|
||||
if (s->do_telnetopt && !s->is_tn3270) {
|
||||
tcp_chr_telnet_init(chr);
|
||||
} else {
|
||||
tcp_chr_connect(chr);
|
||||
@@ -723,11 +680,6 @@ static void tcp_chr_tls_init(Chardev *chr)
|
||||
Error *err = NULL;
|
||||
gchar *name;
|
||||
|
||||
if (!machine_init_done) {
|
||||
/* This will be postponed to machine_done notifier */
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->is_listen) {
|
||||
tioc = qio_channel_tls_new_server(
|
||||
s->ioc, s->tls_creds,
|
||||
@@ -756,7 +708,7 @@ static void tcp_chr_tls_init(Chardev *chr)
|
||||
tcp_chr_tls_handshake,
|
||||
chr,
|
||||
NULL,
|
||||
chr->gcontext);
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -792,8 +744,7 @@ static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc)
|
||||
qio_channel_set_delay(s->ioc, false);
|
||||
}
|
||||
if (s->listener) {
|
||||
qio_net_listener_set_client_func_full(s->listener, NULL, NULL,
|
||||
NULL, chr->gcontext);
|
||||
qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (s->tls_creds) {
|
||||
@@ -873,11 +824,8 @@ static void char_socket_finalize(Object *obj)
|
||||
tcp_chr_free_connection(chr);
|
||||
tcp_chr_reconn_timer_cancel(s);
|
||||
qapi_free_SocketAddress(s->addr);
|
||||
tcp_chr_telnet_destroy(s);
|
||||
g_free(s->telnet_init);
|
||||
if (s->listener) {
|
||||
qio_net_listener_set_client_func_full(s->listener, NULL, NULL,
|
||||
NULL, chr->gcontext);
|
||||
qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
|
||||
object_unref(OBJECT(s->listener));
|
||||
}
|
||||
if (s->tls_creds) {
|
||||
@@ -907,22 +855,11 @@ cleanup:
|
||||
object_unref(OBJECT(sioc));
|
||||
}
|
||||
|
||||
static void tcp_chr_connect_async(Chardev *chr)
|
||||
{
|
||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||
QIOChannelSocket *sioc;
|
||||
|
||||
sioc = qio_channel_socket_new();
|
||||
tcp_chr_set_client_ioc_name(chr, sioc);
|
||||
qio_channel_socket_connect_async(sioc, s->addr,
|
||||
qemu_chr_socket_connected,
|
||||
chr, NULL, chr->gcontext);
|
||||
}
|
||||
|
||||
static gboolean socket_reconnect_timeout(gpointer opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
SocketChardev *s = SOCKET_CHARDEV(opaque);
|
||||
QIOChannelSocket *sioc;
|
||||
|
||||
g_source_unref(s->reconnect_timer);
|
||||
s->reconnect_timer = NULL;
|
||||
@@ -931,7 +868,11 @@ static gboolean socket_reconnect_timeout(gpointer opaque)
|
||||
return false;
|
||||
}
|
||||
|
||||
tcp_chr_connect_async(chr);
|
||||
sioc = qio_channel_socket_new();
|
||||
tcp_chr_set_client_ioc_name(chr, sioc);
|
||||
qio_channel_socket_connect_async(sioc, s->addr,
|
||||
qemu_chr_socket_connected,
|
||||
chr, NULL, NULL);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1010,8 +951,13 @@ static void qmp_chardev_open_socket(Chardev *chr,
|
||||
s->reconnect_time = reconnect;
|
||||
}
|
||||
|
||||
/* If reconnect_time is set, will do that in chr_machine_done. */
|
||||
if (!s->reconnect_time) {
|
||||
if (s->reconnect_time) {
|
||||
sioc = qio_channel_socket_new();
|
||||
tcp_chr_set_client_ioc_name(chr, sioc);
|
||||
qio_channel_socket_connect_async(sioc, s->addr,
|
||||
qemu_chr_socket_connected,
|
||||
chr, NULL, NULL);
|
||||
} else {
|
||||
if (s->is_listen) {
|
||||
char *name;
|
||||
s->listener = qio_net_listener_new();
|
||||
@@ -1035,10 +981,8 @@ static void qmp_chardev_open_socket(Chardev *chr,
|
||||
return;
|
||||
}
|
||||
if (!s->ioc) {
|
||||
qio_net_listener_set_client_func_full(s->listener,
|
||||
tcp_chr_accept,
|
||||
chr, NULL,
|
||||
chr->gcontext);
|
||||
qio_net_listener_set_client_func(s->listener, tcp_chr_accept,
|
||||
chr, NULL);
|
||||
}
|
||||
} else if (qemu_chr_wait_connected(chr, errp) < 0) {
|
||||
goto error;
|
||||
@@ -1065,36 +1009,25 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
|
||||
const char *path = qemu_opt_get(opts, "path");
|
||||
const char *host = qemu_opt_get(opts, "host");
|
||||
const char *port = qemu_opt_get(opts, "port");
|
||||
const char *fd = qemu_opt_get(opts, "fd");
|
||||
const char *tls_creds = qemu_opt_get(opts, "tls-creds");
|
||||
SocketAddressLegacy *addr;
|
||||
ChardevSocket *sock;
|
||||
|
||||
if ((!!path + !!fd + !!host) != 1) {
|
||||
error_setg(errp,
|
||||
"Exactly one of 'path', 'fd' or 'host' required");
|
||||
return;
|
||||
}
|
||||
|
||||
backend->type = CHARDEV_BACKEND_KIND_SOCKET;
|
||||
if (path) {
|
||||
if (tls_creds) {
|
||||
error_setg(errp, "TLS can only be used over TCP socket");
|
||||
if (!path) {
|
||||
if (!host) {
|
||||
error_setg(errp, "chardev: socket: no host given");
|
||||
return;
|
||||
}
|
||||
} else if (host) {
|
||||
if (!port) {
|
||||
error_setg(errp, "chardev: socket: no port given");
|
||||
return;
|
||||
}
|
||||
} else if (fd) {
|
||||
/* We don't know what host to validate against when in client mode */
|
||||
if (tls_creds && !is_listen) {
|
||||
error_setg(errp, "TLS can not be used with pre-opened client FD");
|
||||
} else {
|
||||
if (tls_creds) {
|
||||
error_setg(errp, "TLS can only be used over TCP socket");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
sock = backend->u.socket.data = g_new0(ChardevSocket, 1);
|
||||
@@ -1120,7 +1053,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
|
||||
addr->type = SOCKET_ADDRESS_LEGACY_KIND_UNIX;
|
||||
q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
|
||||
q_unix->path = g_strdup(path);
|
||||
} else if (host) {
|
||||
} else {
|
||||
addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET;
|
||||
addr->u.inet.data = g_new(InetSocketAddress, 1);
|
||||
*addr->u.inet.data = (InetSocketAddress) {
|
||||
@@ -1133,12 +1066,6 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
|
||||
.has_ipv6 = qemu_opt_get(opts, "ipv6"),
|
||||
.ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
|
||||
};
|
||||
} else if (fd) {
|
||||
addr->type = SOCKET_ADDRESS_LEGACY_KIND_FD;
|
||||
addr->u.fd.data = g_new(String, 1);
|
||||
addr->u.fd.data->str = g_strdup(fd);
|
||||
} else {
|
||||
g_assert_not_reached();
|
||||
}
|
||||
sock->addr = addr;
|
||||
}
|
||||
@@ -1160,21 +1087,6 @@ char_socket_get_connected(Object *obj, Error **errp)
|
||||
return s->connected;
|
||||
}
|
||||
|
||||
static int tcp_chr_machine_done_hook(Chardev *chr)
|
||||
{
|
||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||
|
||||
if (s->reconnect_time) {
|
||||
tcp_chr_connect_async(chr);
|
||||
}
|
||||
|
||||
if (s->ioc && s->tls_creds) {
|
||||
tcp_chr_tls_init(chr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void char_socket_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
@@ -1190,7 +1102,6 @@ static void char_socket_class_init(ObjectClass *oc, void *data)
|
||||
cc->chr_add_client = tcp_chr_add_client;
|
||||
cc->chr_add_watch = tcp_chr_add_watch;
|
||||
cc->chr_update_read_handler = tcp_chr_update_read_handler;
|
||||
cc->chr_machine_done = tcp_chr_machine_done_hook;
|
||||
|
||||
object_class_property_add(oc, "addr", "SocketAddress",
|
||||
char_socket_get_addr, NULL,
|
||||
|
@@ -281,31 +281,40 @@ static const TypeInfo char_type_info = {
|
||||
.class_init = char_class_init,
|
||||
};
|
||||
|
||||
static int chardev_machine_done_notify_one(Object *child, void *opaque)
|
||||
/**
|
||||
* Called after processing of default and command-line-specified
|
||||
* chardevs to deliver CHR_EVENT_OPENED events to any FEs attached
|
||||
* to a mux chardev. This is done here to ensure that
|
||||
* output/prompts/banners are only displayed for the FE that has
|
||||
* focus when initial command-line processing/machine init is
|
||||
* completed.
|
||||
*
|
||||
* After this point, any new FE attached to any new or existing
|
||||
* mux will receive CHR_EVENT_OPENED notifications for the BE
|
||||
* immediately.
|
||||
*/
|
||||
static int open_muxes(Object *child, void *opaque)
|
||||
{
|
||||
Chardev *chr = (Chardev *)child;
|
||||
ChardevClass *class = CHARDEV_GET_CLASS(chr);
|
||||
|
||||
if (class->chr_machine_done) {
|
||||
return class->chr_machine_done(chr);
|
||||
if (CHARDEV_IS_MUX(child)) {
|
||||
/* send OPENED to all already-attached FEs */
|
||||
mux_chr_send_all_event(CHARDEV(child), CHR_EVENT_OPENED);
|
||||
/* mark mux as OPENED so any new FEs will immediately receive
|
||||
* OPENED event
|
||||
*/
|
||||
qemu_chr_be_event(CHARDEV(child), CHR_EVENT_OPENED);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void chardev_machine_done_hook(Notifier *notifier, void *unused)
|
||||
static void muxes_realize_done(Notifier *notifier, void *unused)
|
||||
{
|
||||
int ret = object_child_foreach(get_chardevs_root(),
|
||||
chardev_machine_done_notify_one, NULL);
|
||||
|
||||
if (ret) {
|
||||
error_report("Failed to call chardev machine_done hooks");
|
||||
exit(1);
|
||||
}
|
||||
muxes_realized = true;
|
||||
object_child_foreach(get_chardevs_root(), open_muxes, NULL);
|
||||
}
|
||||
|
||||
static Notifier chardev_machine_done_notify = {
|
||||
.notify = chardev_machine_done_hook,
|
||||
static Notifier muxes_realize_notify = {
|
||||
.notify = muxes_realize_done,
|
||||
};
|
||||
|
||||
static bool qemu_chr_is_busy(Chardev *s)
|
||||
@@ -798,9 +807,6 @@ QemuOptsList qemu_chardev_opts = {
|
||||
},{
|
||||
.name = "port",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},{
|
||||
.name = "fd",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},{
|
||||
.name = "localaddr",
|
||||
.type = QEMU_OPT_STRING,
|
||||
@@ -1112,7 +1118,7 @@ static void register_types(void)
|
||||
* as part of realize functions like serial_isa_realizefn when -nographic
|
||||
* is specified
|
||||
*/
|
||||
qemu_add_machine_init_done_notifier(&chardev_machine_done_notify);
|
||||
qemu_add_machine_init_done_notifier(&muxes_realize_notify);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
45
configure
vendored
45
configure
vendored
@@ -342,7 +342,7 @@ attr=""
|
||||
libattr=""
|
||||
xfs=""
|
||||
tcg="yes"
|
||||
membarrier=""
|
||||
|
||||
vhost_net="no"
|
||||
vhost_crypto="no"
|
||||
vhost_scsi="no"
|
||||
@@ -1161,13 +1161,9 @@ for opt do
|
||||
;;
|
||||
--enable-attr) attr="yes"
|
||||
;;
|
||||
--disable-membarrier) membarrier="no"
|
||||
;;
|
||||
--enable-membarrier) membarrier="yes"
|
||||
;;
|
||||
--disable-blobs) blobs="no"
|
||||
;;
|
||||
--with-pkgversion=*) pkgversion="$optarg"
|
||||
--with-pkgversion=*) pkgversion=" ($optarg)"
|
||||
;;
|
||||
--with-coroutine=*) coroutine="$optarg"
|
||||
;;
|
||||
@@ -1581,7 +1577,6 @@ disabled with --disable-FEATURE, default is enabled if available:
|
||||
xen-pci-passthrough
|
||||
brlapi BrlAPI (Braile)
|
||||
curl curl connectivity
|
||||
membarrier membarrier system call (for Linux 4.14+ or Windows)
|
||||
fdt fdt device tree
|
||||
bluez bluez stack connectivity
|
||||
kvm KVM acceleration support
|
||||
@@ -5143,37 +5138,6 @@ if compile_prog "" "" ; then
|
||||
have_fsxattr=yes
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# check for usable membarrier system call
|
||||
if test "$membarrier" = "yes"; then
|
||||
have_membarrier=no
|
||||
if test "$mingw32" = "yes" ; then
|
||||
have_membarrier=yes
|
||||
elif test "$linux" = "yes" ; then
|
||||
cat > $TMPC << EOF
|
||||
#include <linux/membarrier.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
int main(void) {
|
||||
syscall(__NR_membarrier, MEMBARRIER_CMD_QUERY, 0);
|
||||
syscall(__NR_membarrier, MEMBARRIER_CMD_SHARED, 0);
|
||||
exit(0);
|
||||
}
|
||||
EOF
|
||||
if compile_prog "" "" ; then
|
||||
have_membarrier=yes
|
||||
fi
|
||||
fi
|
||||
if test "$have_membarrier" = "no"; then
|
||||
feature_not_found "membarrier" "membarrier system call not available"
|
||||
fi
|
||||
else
|
||||
# Do not enable it by default even for Mingw32, because it doesn't
|
||||
# work on Wine.
|
||||
membarrier=no
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# check if rtnetlink.h exists and is useful
|
||||
have_rtnetlink=no
|
||||
@@ -5800,7 +5764,6 @@ fi
|
||||
echo "malloc trim support $malloc_trim"
|
||||
echo "RDMA support $rdma"
|
||||
echo "fdt support $fdt"
|
||||
echo "membarrier $membarrier"
|
||||
echo "preadv support $preadv"
|
||||
echo "fdatasync $fdatasync"
|
||||
echo "madvise $madvise"
|
||||
@@ -6288,9 +6251,6 @@ fi
|
||||
if test "$fdt" = "yes" ; then
|
||||
echo "CONFIG_FDT=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$membarrier" = "yes" ; then
|
||||
echo "CONFIG_MEMBARRIER=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$signalfd" = "yes" ; then
|
||||
echo "CONFIG_SIGNALFD=y" >> $config_host_mak
|
||||
fi
|
||||
@@ -6880,7 +6840,6 @@ case "$target_name" in
|
||||
;;
|
||||
xtensa|xtensaeb)
|
||||
TARGET_ARCH=xtensa
|
||||
mttcg="yes"
|
||||
;;
|
||||
*)
|
||||
error_exit "Unsupported target CPU"
|
||||
|
@@ -26,20 +26,9 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/mman.h>
|
||||
#include "qemu/compiler.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/syscall.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/vhost.h>
|
||||
|
||||
#ifdef __NR_userfaultfd
|
||||
#include <linux/userfaultfd.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#include "qemu/compiler.h"
|
||||
#include "qemu/atomic.h"
|
||||
|
||||
#include "libvhost-user.h"
|
||||
@@ -97,9 +86,6 @@ vu_request_to_string(unsigned int req)
|
||||
REQ(VHOST_USER_SET_VRING_ENDIAN),
|
||||
REQ(VHOST_USER_GET_CONFIG),
|
||||
REQ(VHOST_USER_SET_CONFIG),
|
||||
REQ(VHOST_USER_POSTCOPY_ADVISE),
|
||||
REQ(VHOST_USER_POSTCOPY_LISTEN),
|
||||
REQ(VHOST_USER_POSTCOPY_END),
|
||||
REQ(VHOST_USER_MAX),
|
||||
};
|
||||
#undef REQ
|
||||
@@ -185,35 +171,6 @@ vmsg_close_fds(VhostUserMsg *vmsg)
|
||||
}
|
||||
}
|
||||
|
||||
/* A test to see if we have userfault available */
|
||||
static bool
|
||||
have_userfault(void)
|
||||
{
|
||||
#if defined(__linux__) && defined(__NR_userfaultfd) &&\
|
||||
defined(UFFD_FEATURE_MISSING_SHMEM) &&\
|
||||
defined(UFFD_FEATURE_MISSING_HUGETLBFS)
|
||||
/* Now test the kernel we're running on really has the features */
|
||||
int ufd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
|
||||
struct uffdio_api api_struct;
|
||||
if (ufd < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
api_struct.api = UFFD_API;
|
||||
api_struct.features = UFFD_FEATURE_MISSING_SHMEM |
|
||||
UFFD_FEATURE_MISSING_HUGETLBFS;
|
||||
if (ioctl(ufd, UFFDIO_API, &api_struct)) {
|
||||
close(ufd);
|
||||
return false;
|
||||
}
|
||||
close(ufd);
|
||||
return true;
|
||||
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_message_read(VuDev *dev, int conn_fd, VhostUserMsg *vmsg)
|
||||
{
|
||||
@@ -288,31 +245,6 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg)
|
||||
{
|
||||
int rc;
|
||||
uint8_t *p = (uint8_t *)vmsg;
|
||||
char control[CMSG_SPACE(VHOST_MEMORY_MAX_NREGIONS * sizeof(int))] = { };
|
||||
struct iovec iov = {
|
||||
.iov_base = (char *)vmsg,
|
||||
.iov_len = VHOST_USER_HDR_SIZE,
|
||||
};
|
||||
struct msghdr msg = {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = control,
|
||||
};
|
||||
struct cmsghdr *cmsg;
|
||||
|
||||
memset(control, 0, sizeof(control));
|
||||
assert(vmsg->fd_num <= VHOST_MEMORY_MAX_NREGIONS);
|
||||
if (vmsg->fd_num > 0) {
|
||||
size_t fdsize = vmsg->fd_num * sizeof(int);
|
||||
msg.msg_controllen = CMSG_SPACE(fdsize);
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_len = CMSG_LEN(fdsize);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
memcpy(CMSG_DATA(cmsg), vmsg->fds, fdsize);
|
||||
} else {
|
||||
msg.msg_controllen = 0;
|
||||
}
|
||||
|
||||
/* Set the version in the flags when sending the reply */
|
||||
vmsg->flags &= ~VHOST_USER_VERSION_MASK;
|
||||
@@ -320,7 +252,7 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg)
|
||||
vmsg->flags |= VHOST_USER_REPLY_MASK;
|
||||
|
||||
do {
|
||||
rc = sendmsg(conn_fd, &msg, 0);
|
||||
rc = write(conn_fd, p, VHOST_USER_HDR_SIZE);
|
||||
} while (rc < 0 && (errno == EINTR || errno == EAGAIN));
|
||||
|
||||
do {
|
||||
@@ -413,7 +345,6 @@ vu_get_features_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
}
|
||||
|
||||
vmsg->size = sizeof(vmsg->payload.u64);
|
||||
vmsg->fd_num = 0;
|
||||
|
||||
DPRINT("Sending back to guest u64: 0x%016"PRIx64"\n", vmsg->payload.u64);
|
||||
|
||||
@@ -478,148 +409,6 @@ vu_reset_device_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_set_mem_table_exec_postcopy(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
int i;
|
||||
VhostUserMemory *memory = &vmsg->payload.memory;
|
||||
dev->nregions = memory->nregions;
|
||||
|
||||
DPRINT("Nregions: %d\n", memory->nregions);
|
||||
for (i = 0; i < dev->nregions; i++) {
|
||||
void *mmap_addr;
|
||||
VhostUserMemoryRegion *msg_region = &memory->regions[i];
|
||||
VuDevRegion *dev_region = &dev->regions[i];
|
||||
|
||||
DPRINT("Region %d\n", i);
|
||||
DPRINT(" guest_phys_addr: 0x%016"PRIx64"\n",
|
||||
msg_region->guest_phys_addr);
|
||||
DPRINT(" memory_size: 0x%016"PRIx64"\n",
|
||||
msg_region->memory_size);
|
||||
DPRINT(" userspace_addr 0x%016"PRIx64"\n",
|
||||
msg_region->userspace_addr);
|
||||
DPRINT(" mmap_offset 0x%016"PRIx64"\n",
|
||||
msg_region->mmap_offset);
|
||||
|
||||
dev_region->gpa = msg_region->guest_phys_addr;
|
||||
dev_region->size = msg_region->memory_size;
|
||||
dev_region->qva = msg_region->userspace_addr;
|
||||
dev_region->mmap_offset = msg_region->mmap_offset;
|
||||
|
||||
/* We don't use offset argument of mmap() since the
|
||||
* mapped address has to be page aligned, and we use huge
|
||||
* pages.
|
||||
* In postcopy we're using PROT_NONE here to catch anyone
|
||||
* accessing it before we userfault
|
||||
*/
|
||||
mmap_addr = mmap(0, dev_region->size + dev_region->mmap_offset,
|
||||
PROT_NONE, MAP_SHARED,
|
||||
vmsg->fds[i], 0);
|
||||
|
||||
if (mmap_addr == MAP_FAILED) {
|
||||
vu_panic(dev, "region mmap error: %s", strerror(errno));
|
||||
} else {
|
||||
dev_region->mmap_addr = (uint64_t)(uintptr_t)mmap_addr;
|
||||
DPRINT(" mmap_addr: 0x%016"PRIx64"\n",
|
||||
dev_region->mmap_addr);
|
||||
}
|
||||
|
||||
/* Return the address to QEMU so that it can translate the ufd
|
||||
* fault addresses back.
|
||||
*/
|
||||
msg_region->userspace_addr = (uintptr_t)(mmap_addr +
|
||||
dev_region->mmap_offset);
|
||||
close(vmsg->fds[i]);
|
||||
}
|
||||
|
||||
/* Send the message back to qemu with the addresses filled in */
|
||||
vmsg->fd_num = 0;
|
||||
if (!vu_message_write(dev, dev->sock, vmsg)) {
|
||||
vu_panic(dev, "failed to respond to set-mem-table for postcopy");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Wait for QEMU to confirm that it's registered the handler for the
|
||||
* faults.
|
||||
*/
|
||||
if (!vu_message_read(dev, dev->sock, vmsg) ||
|
||||
vmsg->size != sizeof(vmsg->payload.u64) ||
|
||||
vmsg->payload.u64 != 0) {
|
||||
vu_panic(dev, "failed to receive valid ack for postcopy set-mem-table");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* OK, now we can go and register the memory and generate faults */
|
||||
for (i = 0; i < dev->nregions; i++) {
|
||||
VuDevRegion *dev_region = &dev->regions[i];
|
||||
int ret;
|
||||
#ifdef UFFDIO_REGISTER
|
||||
/* We should already have an open ufd. Mark each memory
|
||||
* range as ufd.
|
||||
* Discard any mapping we have here; note I can't use MADV_REMOVE
|
||||
* or fallocate to make the hole since I don't want to lose
|
||||
* data that's already arrived in the shared process.
|
||||
* TODO: How to do hugepage
|
||||
*/
|
||||
ret = madvise((void *)dev_region->mmap_addr,
|
||||
dev_region->size + dev_region->mmap_offset,
|
||||
MADV_DONTNEED);
|
||||
if (ret) {
|
||||
fprintf(stderr,
|
||||
"%s: Failed to madvise(DONTNEED) region %d: %s\n",
|
||||
__func__, i, strerror(errno));
|
||||
}
|
||||
/* Turn off transparent hugepages so we dont get lose wakeups
|
||||
* in neighbouring pages.
|
||||
* TODO: Turn this backon later.
|
||||
*/
|
||||
ret = madvise((void *)dev_region->mmap_addr,
|
||||
dev_region->size + dev_region->mmap_offset,
|
||||
MADV_NOHUGEPAGE);
|
||||
if (ret) {
|
||||
/* Note: This can happen legally on kernels that are configured
|
||||
* without madvise'able hugepages
|
||||
*/
|
||||
fprintf(stderr,
|
||||
"%s: Failed to madvise(NOHUGEPAGE) region %d: %s\n",
|
||||
__func__, i, strerror(errno));
|
||||
}
|
||||
struct uffdio_register reg_struct;
|
||||
reg_struct.range.start = (uintptr_t)dev_region->mmap_addr;
|
||||
reg_struct.range.len = dev_region->size + dev_region->mmap_offset;
|
||||
reg_struct.mode = UFFDIO_REGISTER_MODE_MISSING;
|
||||
|
||||
if (ioctl(dev->postcopy_ufd, UFFDIO_REGISTER, ®_struct)) {
|
||||
vu_panic(dev, "%s: Failed to userfault region %d "
|
||||
"@%p + size:%zx offset: %zx: (ufd=%d)%s\n",
|
||||
__func__, i,
|
||||
dev_region->mmap_addr,
|
||||
dev_region->size, dev_region->mmap_offset,
|
||||
dev->postcopy_ufd, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if (!(reg_struct.ioctls & ((__u64)1 << _UFFDIO_COPY))) {
|
||||
vu_panic(dev, "%s Region (%d) doesn't support COPY",
|
||||
__func__, i);
|
||||
return false;
|
||||
}
|
||||
DPRINT("%s: region %d: Registered userfault for %llx + %llx\n",
|
||||
__func__, i, reg_struct.range.start, reg_struct.range.len);
|
||||
/* Now it's registered we can let the client at it */
|
||||
if (mprotect((void *)dev_region->mmap_addr,
|
||||
dev_region->size + dev_region->mmap_offset,
|
||||
PROT_READ | PROT_WRITE)) {
|
||||
vu_panic(dev, "failed to mprotect region %d for postcopy (%s)",
|
||||
i, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
/* TODO: Stash 'zero' support flags somewhere */
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_set_mem_table_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
@@ -636,10 +425,6 @@ vu_set_mem_table_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
}
|
||||
dev->nregions = memory->nregions;
|
||||
|
||||
if (dev->postcopy_listening) {
|
||||
return vu_set_mem_table_exec_postcopy(dev, vmsg);
|
||||
}
|
||||
|
||||
DPRINT("Nregions: %d\n", memory->nregions);
|
||||
for (i = 0; i < dev->nregions; i++) {
|
||||
void *mmap_addr;
|
||||
@@ -715,7 +500,6 @@ vu_set_log_base_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
dev->log_size = log_mmap_size;
|
||||
|
||||
vmsg->size = sizeof(vmsg->payload.u64);
|
||||
vmsg->fd_num = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -968,17 +752,12 @@ vu_get_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
uint64_t features = 1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD |
|
||||
1ULL << VHOST_USER_PROTOCOL_F_SLAVE_REQ;
|
||||
|
||||
if (have_userfault()) {
|
||||
features |= 1ULL << VHOST_USER_PROTOCOL_F_PAGEFAULT;
|
||||
}
|
||||
|
||||
if (dev->iface->get_protocol_features) {
|
||||
features |= dev->iface->get_protocol_features(dev);
|
||||
}
|
||||
|
||||
vmsg->payload.u64 = features;
|
||||
vmsg->size = sizeof(vmsg->payload.u64);
|
||||
vmsg->fd_num = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1077,77 +856,6 @@ vu_set_config(VuDev *dev, VhostUserMsg *vmsg)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_set_postcopy_advise(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
dev->postcopy_ufd = -1;
|
||||
#ifdef UFFDIO_API
|
||||
struct uffdio_api api_struct;
|
||||
|
||||
dev->postcopy_ufd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
|
||||
vmsg->size = 0;
|
||||
#endif
|
||||
|
||||
if (dev->postcopy_ufd == -1) {
|
||||
vu_panic(dev, "Userfaultfd not available: %s", strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef UFFDIO_API
|
||||
api_struct.api = UFFD_API;
|
||||
api_struct.features = 0;
|
||||
if (ioctl(dev->postcopy_ufd, UFFDIO_API, &api_struct)) {
|
||||
vu_panic(dev, "Failed UFFDIO_API: %s", strerror(errno));
|
||||
close(dev->postcopy_ufd);
|
||||
dev->postcopy_ufd = -1;
|
||||
goto out;
|
||||
}
|
||||
/* TODO: Stash feature flags somewhere */
|
||||
#endif
|
||||
|
||||
out:
|
||||
/* Return a ufd to the QEMU */
|
||||
vmsg->fd_num = 1;
|
||||
vmsg->fds[0] = dev->postcopy_ufd;
|
||||
return true; /* = send a reply */
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_set_postcopy_listen(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
vmsg->payload.u64 = -1;
|
||||
vmsg->size = sizeof(vmsg->payload.u64);
|
||||
|
||||
if (dev->nregions) {
|
||||
vu_panic(dev, "Regions already registered at postcopy-listen");
|
||||
return true;
|
||||
}
|
||||
dev->postcopy_listening = true;
|
||||
|
||||
vmsg->flags = VHOST_USER_VERSION | VHOST_USER_REPLY_MASK;
|
||||
vmsg->payload.u64 = 0; /* Success */
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_set_postcopy_end(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
DPRINT("%s: Entry\n", __func__);
|
||||
dev->postcopy_listening = false;
|
||||
if (dev->postcopy_ufd > 0) {
|
||||
close(dev->postcopy_ufd);
|
||||
dev->postcopy_ufd = -1;
|
||||
DPRINT("%s: Done close\n", __func__);
|
||||
}
|
||||
|
||||
vmsg->fd_num = 0;
|
||||
vmsg->payload.u64 = 0;
|
||||
vmsg->size = sizeof(vmsg->payload.u64);
|
||||
vmsg->flags = VHOST_USER_VERSION | VHOST_USER_REPLY_MASK;
|
||||
DPRINT("%s: exit\n", __func__);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
@@ -1219,12 +927,6 @@ vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
|
||||
return vu_set_config(dev, vmsg);
|
||||
case VHOST_USER_NONE:
|
||||
break;
|
||||
case VHOST_USER_POSTCOPY_ADVISE:
|
||||
return vu_set_postcopy_advise(dev, vmsg);
|
||||
case VHOST_USER_POSTCOPY_LISTEN:
|
||||
return vu_set_postcopy_listen(dev, vmsg);
|
||||
case VHOST_USER_POSTCOPY_END:
|
||||
return vu_set_postcopy_end(dev, vmsg);
|
||||
default:
|
||||
vmsg_close_fds(vmsg);
|
||||
vu_panic(dev, "Unhandled request: %d", vmsg->request);
|
||||
|
@@ -48,8 +48,6 @@ enum VhostUserProtocolFeature {
|
||||
VHOST_USER_PROTOCOL_F_NET_MTU = 4,
|
||||
VHOST_USER_PROTOCOL_F_SLAVE_REQ = 5,
|
||||
VHOST_USER_PROTOCOL_F_CROSS_ENDIAN = 6,
|
||||
VHOST_USER_PROTOCOL_F_CRYPTO_SESSION = 7,
|
||||
VHOST_USER_PROTOCOL_F_PAGEFAULT = 8,
|
||||
|
||||
VHOST_USER_PROTOCOL_F_MAX
|
||||
};
|
||||
@@ -83,11 +81,6 @@ typedef enum VhostUserRequest {
|
||||
VHOST_USER_SET_VRING_ENDIAN = 23,
|
||||
VHOST_USER_GET_CONFIG = 24,
|
||||
VHOST_USER_SET_CONFIG = 25,
|
||||
VHOST_USER_CREATE_CRYPTO_SESSION = 26,
|
||||
VHOST_USER_CLOSE_CRYPTO_SESSION = 27,
|
||||
VHOST_USER_POSTCOPY_ADVISE = 28,
|
||||
VHOST_USER_POSTCOPY_LISTEN = 29,
|
||||
VHOST_USER_POSTCOPY_END = 30,
|
||||
VHOST_USER_MAX
|
||||
} VhostUserRequest;
|
||||
|
||||
@@ -284,10 +277,6 @@ struct VuDev {
|
||||
* re-initialize */
|
||||
vu_panic_cb panic;
|
||||
const VuDevIface *iface;
|
||||
|
||||
/* Postcopy data */
|
||||
int postcopy_ufd;
|
||||
bool postcopy_listening;
|
||||
};
|
||||
|
||||
typedef struct VuVirtqElement {
|
||||
|
24
cpus.c
24
cpus.c
@@ -1317,8 +1317,6 @@ static void prepare_icount_for_run(CPUState *cpu)
|
||||
insns_left = MIN(0xffff, cpu->icount_budget);
|
||||
cpu->icount_decr.u16.low = insns_left;
|
||||
cpu->icount_extra = cpu->icount_budget - insns_left;
|
||||
|
||||
replay_mutex_lock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1334,8 +1332,6 @@ static void process_icount_data(CPUState *cpu)
|
||||
cpu->icount_budget = 0;
|
||||
|
||||
replay_account_executed_instructions();
|
||||
|
||||
replay_mutex_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1350,9 +1346,11 @@ static int tcg_cpu_exec(CPUState *cpu)
|
||||
#ifdef CONFIG_PROFILER
|
||||
ti = profile_getclock();
|
||||
#endif
|
||||
qemu_mutex_unlock_iothread();
|
||||
cpu_exec_start(cpu);
|
||||
ret = cpu_exec(cpu);
|
||||
cpu_exec_end(cpu);
|
||||
qemu_mutex_lock_iothread();
|
||||
#ifdef CONFIG_PROFILER
|
||||
tcg_time += profile_getclock() - ti;
|
||||
#endif
|
||||
@@ -1419,9 +1417,6 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
|
||||
cpu->exit_request = 1;
|
||||
|
||||
while (1) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
replay_mutex_lock();
|
||||
qemu_mutex_lock_iothread();
|
||||
/* Account partial waits to QEMU_CLOCK_VIRTUAL. */
|
||||
qemu_account_warp_timer();
|
||||
|
||||
@@ -1430,8 +1425,6 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
|
||||
*/
|
||||
handle_icount_deadline();
|
||||
|
||||
replay_mutex_unlock();
|
||||
|
||||
if (!cpu) {
|
||||
cpu = first_cpu;
|
||||
}
|
||||
@@ -1447,13 +1440,11 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
|
||||
if (cpu_can_run(cpu)) {
|
||||
int r;
|
||||
|
||||
qemu_mutex_unlock_iothread();
|
||||
prepare_icount_for_run(cpu);
|
||||
|
||||
r = tcg_cpu_exec(cpu);
|
||||
|
||||
process_icount_data(cpu);
|
||||
qemu_mutex_lock_iothread();
|
||||
|
||||
if (r == EXCP_DEBUG) {
|
||||
cpu_handle_guest_debug(cpu);
|
||||
@@ -1643,9 +1634,7 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
|
||||
while (1) {
|
||||
if (cpu_can_run(cpu)) {
|
||||
int r;
|
||||
qemu_mutex_unlock_iothread();
|
||||
r = tcg_cpu_exec(cpu);
|
||||
qemu_mutex_lock_iothread();
|
||||
switch (r) {
|
||||
case EXCP_DEBUG:
|
||||
cpu_handle_guest_debug(cpu);
|
||||
@@ -1792,21 +1781,12 @@ void pause_all_vcpus(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* We need to drop the replay_lock so any vCPU threads woken up
|
||||
* can finish their replay tasks
|
||||
*/
|
||||
replay_mutex_unlock();
|
||||
|
||||
while (!all_vcpus_paused()) {
|
||||
qemu_cond_wait(&qemu_pause_cond, &qemu_global_mutex);
|
||||
CPU_FOREACH(cpu) {
|
||||
qemu_cpu_kick(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
qemu_mutex_unlock_iothread();
|
||||
replay_mutex_lock();
|
||||
qemu_mutex_lock_iothread();
|
||||
}
|
||||
|
||||
void cpu_resume(CPUState *cpu)
|
||||
|
@@ -4,12 +4,7 @@ include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_SERIAL_ISA=y
|
||||
CONFIG_I82374=y
|
||||
CONFIG_I8254=y
|
||||
CONFIG_I8257=y
|
||||
CONFIG_PARALLEL=y
|
||||
CONFIG_PARALLEL_ISA=y
|
||||
CONFIG_FDC=y
|
||||
CONFIG_PCKBD=y
|
||||
CONFIG_VGA_CIRRUS=y
|
||||
CONFIG_IDE_CORE=y
|
||||
|
@@ -63,6 +63,3 @@ CONFIG_PXB=y
|
||||
CONFIG_ACPI_VMGENID=y
|
||||
CONFIG_FW_CFG_DMA=y
|
||||
CONFIG_I2C=y
|
||||
CONFIG_SEV=$(CONFIG_KVM)
|
||||
CONFIG_VTD=y
|
||||
CONFIG_AMD_IOMMU=y
|
||||
|
@@ -63,6 +63,3 @@ CONFIG_PXB=y
|
||||
CONFIG_ACPI_VMGENID=y
|
||||
CONFIG_FW_CFG_DMA=y
|
||||
CONFIG_I2C=y
|
||||
CONFIG_SEV=$(CONFIG_KVM)
|
||||
CONFIG_VTD=y
|
||||
CONFIG_AMD_IOMMU=y
|
||||
|
@@ -1 +0,0 @@
|
||||
# Default configuration for xtensa-linux-user
|
@@ -1 +0,0 @@
|
||||
# Default configuration for xtensa-linux-user
|
@@ -1,109 +0,0 @@
|
||||
Secure Encrypted Virtualization (SEV) is a feature found on AMD processors.
|
||||
|
||||
SEV is an extension to the AMD-V architecture which supports running encrypted
|
||||
virtual machine (VMs) under the control of KVM. Encrypted VMs have their pages
|
||||
(code and data) secured such that only the guest itself has access to the
|
||||
unencrypted version. Each encrypted VM is associated with a unique encryption
|
||||
key; if its data is accessed to a different entity using a different key the
|
||||
encrypted guests data will be incorrectly decrypted, leading to unintelligible
|
||||
data.
|
||||
|
||||
The key management of this feature is handled by separate processor known as
|
||||
AMD secure processor (AMD-SP) which is present in AMD SOCs. Firmware running
|
||||
inside the AMD-SP provide commands to support common VM lifecycle. This
|
||||
includes commands for launching, snapshotting, migrating and debugging the
|
||||
encrypted guest. Those SEV command can be issued via KVM_MEMORY_ENCRYPT_OP
|
||||
ioctls.
|
||||
|
||||
Launching
|
||||
---------
|
||||
Boot images (such as bios) must be encrypted before guest can be booted.
|
||||
MEMORY_ENCRYPT_OP ioctl provides commands to encrypt the images :LAUNCH_START,
|
||||
LAUNCH_UPDATE_DATA, LAUNCH_MEASURE and LAUNCH_FINISH. These four commands
|
||||
together generate a fresh memory encryption key for the VM, encrypt the boot
|
||||
images and provide a measurement than can be used as an attestation of the
|
||||
successful launch.
|
||||
|
||||
LAUNCH_START is called first to create a cryptographic launch context within
|
||||
the firmware. To create this context, guest owner must provides guest policy,
|
||||
its public Diffie-Hellman key (PDH) and session parameters. These inputs
|
||||
should be treated as binary blob and must be passed as-is to the SEV firmware.
|
||||
|
||||
The guest policy is passed as plaintext and hypervisor may able to read it
|
||||
but should not modify it (any modification of the policy bits will result
|
||||
in bad measurement). The guest policy is a 4-byte data structure containing
|
||||
several flags that restricts what can be done on running SEV guest.
|
||||
See KM Spec section 3 and 6.2 for more details.
|
||||
|
||||
The guest policy can be provided via the 'policy' property (see below)
|
||||
|
||||
# ${QEMU} \
|
||||
sev-guest,id=sev0,policy=0x1...\
|
||||
|
||||
Guest owners provided DH certificate and session parameters will be used to
|
||||
establish a cryptographic session with the guest owner to negotiate keys used
|
||||
for the attestation.
|
||||
|
||||
The DH certificate and session blob can be provided via 'dh-cert-file' and
|
||||
'session-file' property (see below
|
||||
|
||||
# ${QEMU} \
|
||||
sev-guest,id=sev0,dh-cert-file=<file1>,session-file=<file2>
|
||||
|
||||
LAUNCH_UPDATE_DATA encrypts the memory region using the cryptographic context
|
||||
created via LAUNCH_START command. If required, this command can be called
|
||||
multiple times to encrypt different memory regions. The command also calculates
|
||||
the measurement of the memory contents as it encrypts.
|
||||
|
||||
LAUNCH_MEASURE command can be used to retrieve the measurement of encrypted
|
||||
memory. This measurement is a signature of the memory contents that can be
|
||||
sent to the guest owner as an attestation that the memory was encrypted
|
||||
correctly by the firmware. The guest owner may wait to provide the guest
|
||||
confidential information until it can verify the attestation measurement.
|
||||
Since the guest owner knows the initial contents of the guest at boot, the
|
||||
attestation measurement can be verified by comparing it to what the guest owner
|
||||
expects.
|
||||
|
||||
LAUNCH_FINISH command finalizes the guest launch and destroy's the cryptographic
|
||||
context.
|
||||
|
||||
See SEV KM API Spec [1] 'Launching a guest' usage flow (Appendix A) for the
|
||||
complete flow chart.
|
||||
|
||||
To launch a SEV guest
|
||||
|
||||
# ${QEMU} \
|
||||
-machine ...,memory-encryption=sev0 \
|
||||
-object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1
|
||||
|
||||
Debugging
|
||||
-----------
|
||||
Since memory contents of SEV guest is encrypted hence hypervisor access to the
|
||||
guest memory will get a cipher text. If guest policy allows debugging, then
|
||||
hypervisor can use DEBUG_DECRYPT and DEBUG_ENCRYPT commands access the guest
|
||||
memory region for debug purposes. This is not supported in QEMU yet.
|
||||
|
||||
Snapshot/Restore
|
||||
-----------------
|
||||
TODO
|
||||
|
||||
Live Migration
|
||||
----------------
|
||||
TODO
|
||||
|
||||
References
|
||||
-----------------
|
||||
|
||||
AMD Memory Encryption whitepaper:
|
||||
http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/12/AMD_Memory_Encryption_Whitepaper_v7-Public.pdf
|
||||
|
||||
Secure Encrypted Virutualization Key Management:
|
||||
[1] http://support.amd.com/TechDocs/55766_SEV-KM API_Specification.pdf
|
||||
|
||||
KVM Forum slides:
|
||||
http://www.linux-kvm.org/images/7/74/02x08A-Thomas_Lendacky-AMDs_Virtualizatoin_Memory_Encryption_Technology.pdf
|
||||
|
||||
AMD64 Architecture Programmer's Manual:
|
||||
http://support.amd.com/TechDocs/24593.pdf
|
||||
SME is section 7.10
|
||||
SEV is section 15.34
|
@@ -122,30 +122,20 @@ In general, if the algorithm you are writing includes both writes
|
||||
and reads on the same side, it is generally simpler to use sequentially
|
||||
consistent primitives.
|
||||
|
||||
When using this model, variables are accessed with:
|
||||
|
||||
- atomic_read() and atomic_set(); these prevent the compiler from
|
||||
optimizing accesses out of existence and creating unsolicited
|
||||
accesses, but do not otherwise impose any ordering on loads and
|
||||
stores: both the compiler and the processor are free to reorder
|
||||
them.
|
||||
|
||||
- atomic_load_acquire(), which guarantees the LOAD to appear to
|
||||
happen, with respect to the other components of the system,
|
||||
before all the LOAD or STORE operations specified afterwards.
|
||||
Operations coming before atomic_load_acquire() can still be
|
||||
reordered after it.
|
||||
|
||||
- atomic_store_release(), which guarantees the STORE to appear to
|
||||
happen, with respect to the other components of the system,
|
||||
after all the LOAD or STORE operations specified afterwards.
|
||||
Operations coming after atomic_store_release() can still be
|
||||
reordered after it.
|
||||
|
||||
Restrictions to the ordering of accesses can also be specified
|
||||
When using this model, variables are accessed with atomic_read() and
|
||||
atomic_set(), and restrictions to the ordering of accesses is enforced
|
||||
using the memory barrier macros: smp_rmb(), smp_wmb(), smp_mb(),
|
||||
smp_mb_acquire(), smp_mb_release(), smp_read_barrier_depends().
|
||||
|
||||
atomic_read() and atomic_set() prevents the compiler from using
|
||||
optimizations that might otherwise optimize accesses out of existence
|
||||
on the one hand, or that might create unsolicited accesses on the other.
|
||||
In general this should not have any effect, because the same compiler
|
||||
barriers are already implied by memory barriers. However, it is useful
|
||||
to do so, because it tells readers which variables are shared with
|
||||
other threads, and which are local to the current thread or protected
|
||||
by other, more mundane means.
|
||||
|
||||
Memory barriers control the order of references to shared memory.
|
||||
They come in six kinds:
|
||||
|
||||
@@ -242,7 +232,7 @@ make atomic_mb_set() the more expensive operation.
|
||||
|
||||
There are two common cases in which atomic_mb_read and atomic_mb_set
|
||||
generate too many memory barriers, and thus it can be useful to manually
|
||||
place barriers, or use atomic_load_acquire/atomic_store_release instead:
|
||||
place barriers instead:
|
||||
|
||||
- when a data structure has one thread that is always a writer
|
||||
and one thread that is always a reader, manual placement of
|
||||
@@ -253,15 +243,18 @@ place barriers, or use atomic_load_acquire/atomic_store_release instead:
|
||||
thread 1 thread 1
|
||||
------------------------- ------------------------
|
||||
(other writes)
|
||||
atomic_mb_set(&a, x) atomic_store_release(&a, x)
|
||||
atomic_mb_set(&b, y) atomic_store_release(&b, y)
|
||||
smp_mb_release()
|
||||
atomic_mb_set(&a, x) atomic_set(&a, x)
|
||||
smp_wmb()
|
||||
atomic_mb_set(&b, y) atomic_set(&b, y)
|
||||
|
||||
=>
|
||||
thread 2 thread 2
|
||||
------------------------- ------------------------
|
||||
y = atomic_mb_read(&b) y = atomic_load_acquire(&b)
|
||||
x = atomic_mb_read(&a) x = atomic_load_acquire(&a)
|
||||
(other reads)
|
||||
y = atomic_mb_read(&b) y = atomic_read(&b)
|
||||
smp_rmb()
|
||||
x = atomic_mb_read(&a) x = atomic_read(&a)
|
||||
smp_mb_acquire()
|
||||
|
||||
Note that the barrier between the stores in thread 1, and between
|
||||
the loads in thread 2, has been optimized here to a write or a
|
||||
@@ -283,6 +276,7 @@ place barriers, or use atomic_load_acquire/atomic_store_release instead:
|
||||
smp_mb_acquire();
|
||||
|
||||
Similarly, atomic_mb_set() can be transformed as follows:
|
||||
smp_mb():
|
||||
|
||||
smp_mb_release();
|
||||
for (i = 0; i < 10; i++) => for (i = 0; i < 10; i++)
|
||||
@@ -290,8 +284,6 @@ place barriers, or use atomic_load_acquire/atomic_store_release instead:
|
||||
smp_mb();
|
||||
|
||||
|
||||
The other thread can still use atomic_mb_read()/atomic_mb_set().
|
||||
|
||||
The two tricks can be combined. In this case, splitting a loop in
|
||||
two lets you hoist the barriers out of the loops _and_ eliminate the
|
||||
expensive smp_mb():
|
||||
@@ -304,6 +296,8 @@ expensive smp_mb():
|
||||
atomic_set(&a[i], false);
|
||||
smp_mb();
|
||||
|
||||
The other thread can still use atomic_mb_read()/atomic_mb_set()
|
||||
|
||||
|
||||
Memory barrier pairing
|
||||
----------------------
|
||||
@@ -392,7 +386,10 @@ and memory barriers, and the equivalents in QEMU:
|
||||
note that smp_store_mb() is a little weaker than atomic_mb_set().
|
||||
atomic_mb_read() compiles to the same instructions as Linux's
|
||||
smp_load_acquire(), but this should be treated as an implementation
|
||||
detail.
|
||||
detail. QEMU does have atomic_load_acquire() and atomic_store_release()
|
||||
macros, but for now they are only used within atomic.h. This may
|
||||
change in the future.
|
||||
|
||||
|
||||
SOURCES
|
||||
=======
|
||||
|
@@ -577,44 +577,3 @@ Postcopy now works with hugetlbfs backed memory:
|
||||
hugepages works well, however 1GB hugepages are likely to be problematic
|
||||
since it takes ~1 second to transfer a 1GB hugepage across a 10Gbps link,
|
||||
and until the full page is transferred the destination thread is blocked.
|
||||
|
||||
Postcopy with shared memory
|
||||
---------------------------
|
||||
|
||||
Postcopy migration with shared memory needs explicit support from the other
|
||||
processes that share memory and from QEMU. There are restrictions on the type of
|
||||
memory that userfault can support shared.
|
||||
|
||||
The Linux kernel userfault support works on `/dev/shm` memory and on `hugetlbfs`
|
||||
(although the kernel doesn't provide an equivalent to `madvise(MADV_DONTNEED)`
|
||||
for hugetlbfs which may be a problem in some configurations).
|
||||
|
||||
The vhost-user code in QEMU supports clients that have Postcopy support,
|
||||
and the `vhost-user-bridge` (in `tests/`) and the DPDK package have changes
|
||||
to support postcopy.
|
||||
|
||||
The client needs to open a userfaultfd and register the areas
|
||||
of memory that it maps with userfault. The client must then pass the
|
||||
userfaultfd back to QEMU together with a mapping table that allows
|
||||
fault addresses in the clients address space to be converted back to
|
||||
RAMBlock/offsets. The client's userfaultfd is added to the postcopy
|
||||
fault-thread and page requests are made on behalf of the client by QEMU.
|
||||
QEMU performs 'wake' operations on the client's userfaultfd to allow it
|
||||
to continue after a page has arrived.
|
||||
|
||||
.. note::
|
||||
There are two future improvements that would be nice:
|
||||
a) Some way to make QEMU ignorant of the addresses in the clients
|
||||
address space
|
||||
b) Avoiding the need for QEMU to perform ufd-wake calls after the
|
||||
pages have arrived
|
||||
|
||||
Retro-fitting postcopy to existing clients is possible:
|
||||
a) A mechanism is needed for the registration with userfault as above,
|
||||
and the registration needs to be coordinated with the phases of
|
||||
postcopy. In vhost-user extra messages are added to the existing
|
||||
control channel.
|
||||
b) Any thread that can block due to guest memory accesses must be
|
||||
identified and the implication understood; for example if the
|
||||
guest memory access is made while holding a lock then all other
|
||||
threads waiting for that lock will also be blocked.
|
||||
|
@@ -554,12 +554,9 @@ following example objects:
|
||||
|
||||
=== Commands ===
|
||||
|
||||
--- General Command Layout ---
|
||||
|
||||
Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT,
|
||||
'*returns': TYPE-NAME, '*boxed': true,
|
||||
'*gen': false, '*success-response': false,
|
||||
'*allow-oob': true }
|
||||
'*gen': false, '*success-response': false }
|
||||
|
||||
Commands are defined by using a dictionary containing several members,
|
||||
where three members are most common. The 'command' member is a
|
||||
@@ -639,49 +636,6 @@ possible, the command expression should include the optional key
|
||||
'success-response' with boolean value false. So far, only QGA makes
|
||||
use of this member.
|
||||
|
||||
A command can be declared to support Out-Of-Band (OOB) execution. By
|
||||
default, commands do not support OOB. To declare a command that
|
||||
supports it, the schema includes an extra 'allow-oob' field. For
|
||||
example:
|
||||
|
||||
{ 'command': 'migrate_recover',
|
||||
'data': { 'uri': 'str' }, 'allow-oob': true }
|
||||
|
||||
To execute a command with out-of-band priority, the client specifies
|
||||
the "control" field in the request, with "run-oob" set to
|
||||
true. Example:
|
||||
|
||||
=> { "execute": "command-support-oob",
|
||||
"arguments": { ... },
|
||||
"control": { "run-oob": true } }
|
||||
<= { "return": { } }
|
||||
|
||||
Without it, even the commands that support out-of-band execution will
|
||||
still be run in-band.
|
||||
|
||||
Under normal QMP command execution, the following apply to each
|
||||
command:
|
||||
|
||||
- They are executed in order,
|
||||
- They run only in main thread of QEMU,
|
||||
- They have the BQL taken during execution.
|
||||
|
||||
When a command is executed with OOB, the following changes occur:
|
||||
|
||||
- They can be completed before a pending in-band command,
|
||||
- They run in a dedicated monitor thread,
|
||||
- They do not take the BQL during execution.
|
||||
|
||||
OOB command handlers must satisfy the following conditions:
|
||||
|
||||
- It executes extremely fast,
|
||||
- It does not take any lock, or, it can take very small locks if all
|
||||
critical regions also follow the rules for OOB command handler code,
|
||||
- It does not invoke system calls that may block,
|
||||
- It does not access guest RAM that may block when userfaultfd is
|
||||
enabled for postcopy live migration.
|
||||
|
||||
If in doubt, do not implement OOB execution support.
|
||||
|
||||
=== Events ===
|
||||
|
||||
@@ -785,12 +739,10 @@ references by name.
|
||||
QAPI schema definitions not reachable that way are omitted.
|
||||
|
||||
The SchemaInfo for a command has meta-type "command", and variant
|
||||
members "arg-type", "ret-type" and "allow-oob". On the wire, the
|
||||
"arguments" member of a client's "execute" command must conform to the
|
||||
object type named by "arg-type". The "return" member that the server
|
||||
passes in a success response conforms to the type named by
|
||||
"ret-type". When "allow-oob" is set, it means the command supports
|
||||
out-of-band execution.
|
||||
members "arg-type" and "ret-type". On the wire, the "arguments"
|
||||
member of a client's "execute" command must conform to the object type
|
||||
named by "arg-type". The "return" member that the server passes in a
|
||||
success response conforms to the type named by "ret-type".
|
||||
|
||||
If the command takes no arguments, "arg-type" names an object type
|
||||
without members. Likewise, if the command returns nothing, "ret-type"
|
||||
@@ -1367,27 +1319,18 @@ Example:
|
||||
#ifndef EXAMPLE_QMP_INTROSPECT_H
|
||||
#define EXAMPLE_QMP_INTROSPECT_H
|
||||
|
||||
extern const QLitObject qmp_schema_qlit;
|
||||
extern const char example_qmp_schema_json[];
|
||||
|
||||
#endif
|
||||
$ cat qapi-generated/example-qapi-introspect.c
|
||||
[Uninteresting stuff omitted...]
|
||||
|
||||
const QLitObject example_qmp_schema_qlit = QLIT_QLIST(((QLitObject[]) {
|
||||
QLIT_QDICT(((QLitDictEntry[]) {
|
||||
{ "arg-type", QLIT_QSTR("0") },
|
||||
{ "meta-type", QLIT_QSTR("event") },
|
||||
{ "name", QLIT_QSTR("Event") },
|
||||
{ }
|
||||
})),
|
||||
QLIT_QDICT(((QLitDictEntry[]) {
|
||||
{ "members", QLIT_QLIST(((QLitObject[]) {
|
||||
{ }
|
||||
})) },
|
||||
{ "meta-type", QLIT_QSTR("object") },
|
||||
{ "name", QLIT_QSTR("0") },
|
||||
{ }
|
||||
})),
|
||||
...
|
||||
{ }
|
||||
}));
|
||||
const char example_qmp_schema_json[] = "["
|
||||
"{\"arg-type\": \"0\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
|
||||
"{\"arg-type\": \"1\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"2\"}, "
|
||||
"{\"members\": [], \"meta-type\": \"object\", \"name\": \"0\"}, "
|
||||
"{\"members\": [{\"name\": \"arg1\", \"type\": \"[2]\"}], \"meta-type\": \"object\", \"name\": \"1\"}, "
|
||||
"{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"default\": null, \"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, "
|
||||
"{\"element-type\": \"2\", \"meta-type\": \"array\", \"name\": \"[2]\"}, "
|
||||
"{\"json-type\": \"int\", \"meta-type\": \"builtin\", \"name\": \"int\"}, "
|
||||
"{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}]";
|
||||
|
@@ -83,27 +83,16 @@ The greeting message format is:
|
||||
2.2.1 Capabilities
|
||||
------------------
|
||||
|
||||
Currently supported capabilities are:
|
||||
As of the date this document was last revised, no server or client
|
||||
capability strings have been defined.
|
||||
|
||||
- "oob": the QMP server supports "Out-Of-Band" (OOB) command
|
||||
execution. For more details, please see the "run-oob" parameter in
|
||||
the "Issuing Commands" section below. Not all commands allow this
|
||||
"oob" execution. The "query-qmp-schema" command can be used to
|
||||
inspect which commands support "oob" execution.
|
||||
|
||||
QMP clients can get a list of supported QMP capabilities of the QMP
|
||||
server in the greeting message mentioned above. By default, all the
|
||||
capabilities are off. To enable any QMP capabilities, the QMP client
|
||||
needs to send the "qmp_capabilities" command with an extra parameter
|
||||
for the requested capabilities.
|
||||
|
||||
2.3 Issuing Commands
|
||||
--------------------
|
||||
|
||||
The format for command execution is:
|
||||
|
||||
{ "execute": json-string, "arguments": json-object, "id": json-value,
|
||||
"control": json-object }
|
||||
{ "execute": json-string, "arguments": json-object, "id": json-value }
|
||||
|
||||
Where,
|
||||
|
||||
@@ -113,16 +102,10 @@ The format for command execution is:
|
||||
required. Each command documents what contents will be considered
|
||||
valid when handling the json-argument
|
||||
- The "id" member is a transaction identification associated with the
|
||||
command execution. It is required for all commands if the OOB -
|
||||
capability was enabled at startup, and optional otherwise. The same
|
||||
"id" field will be part of the response if provided. The "id" member
|
||||
can be any json-value, although most clients merely use a
|
||||
json-number incremented for each successive command
|
||||
- The "control" member is optional, and currently only used for
|
||||
out-of-band execution. The handling or response of an "oob" command
|
||||
can overtake prior in-band commands. To enable "oob" handling of a
|
||||
particular command, just provide a control field with: { "control":
|
||||
{ "run-oob": true } }
|
||||
command execution, it is optional and will be part of the response if
|
||||
provided. The "id" member can be any json-value, although most
|
||||
clients merely use a json-number incremented for each successive
|
||||
command
|
||||
|
||||
2.4 Commands Responses
|
||||
----------------------
|
||||
@@ -130,11 +113,6 @@ The format for command execution is:
|
||||
There are two possible responses which the Server will issue as the result
|
||||
of a command execution: success or error.
|
||||
|
||||
As long as the commands were issued with a proper "id" field, then the
|
||||
same "id" field will be attached in the corresponding response message
|
||||
so that requests and responses can match. Clients should drop all the
|
||||
responses that have an unknown "id" field.
|
||||
|
||||
2.4.1 success
|
||||
-------------
|
||||
|
||||
|
@@ -290,15 +290,6 @@ Once the source has finished migration, rings will be stopped by
|
||||
the source. No further update must be done before rings are
|
||||
restarted.
|
||||
|
||||
In postcopy migration the slave is started before all the memory has been
|
||||
received from the source host, and care must be taken to avoid accessing pages
|
||||
that have yet to be received. The slave opens a 'userfault'-fd and registers
|
||||
the memory with it; this fd is then passed back over to the master.
|
||||
The master services requests on the userfaultfd for pages that are accessed
|
||||
and when the page is available it performs WAKE ioctl's on the userfaultfd
|
||||
to wake the stalled slave. The client indicates support for this via the
|
||||
VHOST_USER_PROTOCOL_F_PAGEFAULT feature.
|
||||
|
||||
Memory access
|
||||
-------------
|
||||
|
||||
@@ -378,7 +369,6 @@ Protocol features
|
||||
#define VHOST_USER_PROTOCOL_F_SLAVE_REQ 5
|
||||
#define VHOST_USER_PROTOCOL_F_CROSS_ENDIAN 6
|
||||
#define VHOST_USER_PROTOCOL_F_CRYPTO_SESSION 7
|
||||
#define VHOST_USER_PROTOCOL_F_PAGEFAULT 8
|
||||
|
||||
Master message types
|
||||
--------------------
|
||||
@@ -455,21 +445,12 @@ Master message types
|
||||
Id: 5
|
||||
Equivalent ioctl: VHOST_SET_MEM_TABLE
|
||||
Master payload: memory regions description
|
||||
Slave payload: (postcopy only) memory regions description
|
||||
|
||||
Sets the memory map regions on the slave so it can translate the vring
|
||||
addresses. In the ancillary data there is an array of file descriptors
|
||||
for each memory mapped region. The size and ordering of the fds matches
|
||||
the number and ordering of memory regions.
|
||||
|
||||
When VHOST_USER_POSTCOPY_LISTEN has been received, SET_MEM_TABLE replies with
|
||||
the bases of the memory mapped regions to the master. The slave must
|
||||
have mmap'd the regions but not yet accessed them and should not yet generate
|
||||
a userfault event. Note NEED_REPLY_MASK is not set in this case.
|
||||
QEMU will then reply back to the list of mappings with an empty
|
||||
VHOST_USER_SET_MEM_TABLE as an acknowledgment; only upon reception of this
|
||||
message may the guest start accessing the memory and generating faults.
|
||||
|
||||
* VHOST_USER_SET_LOG_BASE
|
||||
|
||||
Id: 6
|
||||
@@ -708,39 +689,6 @@ Master message types
|
||||
feature has been successfully negotiated.
|
||||
It's a required feature for crypto devices.
|
||||
|
||||
* VHOST_USER_POSTCOPY_ADVISE
|
||||
Id: 28
|
||||
Master payload: N/A
|
||||
Slave payload: userfault fd
|
||||
|
||||
When VHOST_USER_PROTOCOL_F_PAGEFAULT is supported, the
|
||||
master advises slave that a migration with postcopy enabled is underway,
|
||||
the slave must open a userfaultfd for later use.
|
||||
Note that at this stage the migration is still in precopy mode.
|
||||
|
||||
* VHOST_USER_POSTCOPY_LISTEN
|
||||
Id: 29
|
||||
Master payload: N/A
|
||||
|
||||
Master advises slave that a transition to postcopy mode has happened.
|
||||
The slave must ensure that shared memory is registered with userfaultfd
|
||||
to cause faulting of non-present pages.
|
||||
|
||||
This is always sent sometime after a VHOST_USER_POSTCOPY_ADVISE, and
|
||||
thus only when VHOST_USER_PROTOCOL_F_PAGEFAULT is supported.
|
||||
|
||||
* VHOST_USER_POSTCOPY_END
|
||||
Id: 30
|
||||
Slave payload: u64
|
||||
|
||||
Master advises that postcopy migration has now completed. The
|
||||
slave must disable the userfaultfd. The response is an acknowledgement
|
||||
only.
|
||||
When VHOST_USER_PROTOCOL_F_PAGEFAULT is supported, this message
|
||||
is sent at the end of the migration, after VHOST_USER_POSTCOPY_LISTEN
|
||||
was previously sent.
|
||||
The value returned is an error indication; 0 is success.
|
||||
|
||||
Slave message types
|
||||
-------------------
|
||||
|
||||
|
161
docs/replay.txt
161
docs/replay.txt
@@ -7,10 +7,14 @@ See the COPYING file in the top-level directory.
|
||||
Record/replay
|
||||
-------------
|
||||
|
||||
Record/replay functions are used for the deterministic replay of qemu execution.
|
||||
Record/replay functions are used for the reverse execution and deterministic
|
||||
replay of qemu execution. This implementation of deterministic replay can
|
||||
be used for deterministic debugging of guest code through a gdb remote
|
||||
interface.
|
||||
|
||||
Execution recording writes a non-deterministic events log, which can be later
|
||||
used for replaying the execution anywhere and for unlimited number of times.
|
||||
It also supports checkpointing for faster rewind to the specific replay moment.
|
||||
It also supports checkpointing for faster rewinding during reverse debugging.
|
||||
Execution replaying reads the log and replays all non-deterministic events
|
||||
including external input, hardware clocks, and interrupts.
|
||||
|
||||
@@ -24,36 +28,16 @@ Deterministic replay has the following features:
|
||||
input devices.
|
||||
|
||||
Usage of the record/replay:
|
||||
* First, record the execution with the following command line:
|
||||
qemu-system-i386 \
|
||||
-icount shift=7,rr=record,rrfile=replay.bin \
|
||||
-drive file=disk.qcow2,if=none,id=img-direct \
|
||||
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \
|
||||
-device ide-hd,drive=img-blkreplay \
|
||||
-netdev user,id=net1 -device rtl8139,netdev=net1 \
|
||||
-object filter-replay,id=replay,netdev=net1
|
||||
* After recording, you can replay it by using another command line:
|
||||
qemu-system-i386 \
|
||||
-icount shift=7,rr=replay,rrfile=replay.bin \
|
||||
-drive file=disk.qcow2,if=none,id=img-direct \
|
||||
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \
|
||||
-device ide-hd,drive=img-blkreplay \
|
||||
-netdev user,id=net1 -device rtl8139,netdev=net1 \
|
||||
-object filter-replay,id=replay,netdev=net1
|
||||
The only difference with recording is changing the rr option
|
||||
from record to replay.
|
||||
* Block device images are not actually changed in the recording mode,
|
||||
* First, record the execution, by adding the following arguments to the command line:
|
||||
'-icount shift=7,rr=record,rrfile=replay.bin -net none'.
|
||||
Block devices' images are not actually changed in the recording mode,
|
||||
because all of the changes are written to the temporary overlay file.
|
||||
This behavior is enabled by using blkreplay driver. It should be used
|
||||
for every enabled block device, as described in 'Block devices' section.
|
||||
* '-net none' option should be specified when network is not used,
|
||||
because QEMU adds network card by default. When network is needed,
|
||||
it should be configured explicitly with replay filter, as described
|
||||
in 'Network devices' section.
|
||||
* Interaction with audio devices and serial ports are recorded and replayed
|
||||
automatically when such devices are enabled.
|
||||
* Then you can replay it by using another command
|
||||
line option: '-icount shift=7,rr=replay,rrfile=replay.bin -net none'
|
||||
* '-net none' option should also be specified if network replay patches
|
||||
are not applied.
|
||||
|
||||
Academic papers with description of deterministic replay implementation:
|
||||
Papers with description of deterministic replay implementation:
|
||||
http://www.computer.org/csdl/proceedings/csmr/2012/4666/00/4666a553-abs.html
|
||||
http://dl.acm.org/citation.cfm?id=2786805.2803179
|
||||
|
||||
@@ -62,33 +46,8 @@ Modifications of qemu include:
|
||||
* saving different asynchronous events (e.g. system shutdown) into the log
|
||||
* synchronization of the bottom halves execution
|
||||
* synchronization of the threads from thread pool
|
||||
* recording/replaying user input (mouse, keyboard, and microphone)
|
||||
* recording/replaying user input (mouse and keyboard)
|
||||
* adding internal checkpoints for cpu and io synchronization
|
||||
* network filter for recording and replaying the packets
|
||||
* block driver for making block layer deterministic
|
||||
* serial port input record and replay
|
||||
|
||||
Locking and thread synchronisation
|
||||
----------------------------------
|
||||
|
||||
Previously the synchronisation of the main thread and the vCPU thread
|
||||
was ensured by the holding of the BQL. However the trend has been to
|
||||
reduce the time the BQL was held across the system including under TCG
|
||||
system emulation. As it is important that batches of events are kept
|
||||
in sequence (e.g. expiring timers and checkpoints in the main thread
|
||||
while instruction checkpoints are written by the vCPU thread) we need
|
||||
another lock to keep things in lock-step. This role is now handled by
|
||||
the replay_mutex_lock. It used to be held only for each event being
|
||||
written but now it is held for a whole execution period. This results
|
||||
in a deterministic ping-pong between the two main threads.
|
||||
|
||||
As the BQL is now a finer grained lock than the replay_lock it is almost
|
||||
certainly a bug, and a source of deadlocks, to take the
|
||||
replay_mutex_lock while the BQL is held. This is enforced by an assert.
|
||||
While the unlocks are usually in the reverse order, this is not
|
||||
necessary; you can drop the replay_lock while holding the BQL, without
|
||||
doing a more complicated unlock_iothread/replay_unlock/lock_iothread
|
||||
sequence.
|
||||
|
||||
Non-deterministic events
|
||||
------------------------
|
||||
@@ -96,11 +55,12 @@ Non-deterministic events
|
||||
Our record/replay system is based on saving and replaying non-deterministic
|
||||
events (e.g. keyboard input) and simulating deterministic ones (e.g. reading
|
||||
from HDD or memory of the VM). Saving only non-deterministic events makes
|
||||
log file smaller and simulation faster.
|
||||
log file smaller, simulation faster, and allows using reverse debugging even
|
||||
for realtime applications.
|
||||
|
||||
The following non-deterministic data from peripheral devices is saved into
|
||||
the log: mouse and keyboard input, network packets, audio controller input,
|
||||
serial port input, and hardware clocks (they are non-deterministic
|
||||
USB packets, serial port input, and hardware clocks (they are non-deterministic
|
||||
too, because their values are taken from the host machine). Inputs from
|
||||
simulated hardware, memory of VM, software interrupts, and execution of
|
||||
instructions are not saved into the log, because they are deterministic and
|
||||
@@ -223,7 +183,7 @@ Block devices record/replay module intercepts calls of
|
||||
bdrv coroutine functions at the top of block drivers stack.
|
||||
To record and replay block operations the drive must be configured
|
||||
as following:
|
||||
-drive file=disk.qcow2,if=none,id=img-direct
|
||||
-drive file=disk.qcow,if=none,id=img-direct
|
||||
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay
|
||||
-device ide-hd,drive=img-blkreplay
|
||||
|
||||
@@ -252,12 +212,6 @@ This snapshot is created at start of recording and restored at start
|
||||
of replaying. It also can be loaded while replaying to roll back
|
||||
the execution.
|
||||
|
||||
Use QEMU monitor to create additional snapshots. 'savevm <name>' command
|
||||
created the snapshot and 'loadvm <name>' restores it. To prevent corruption
|
||||
of the original disk image, use overlay files linked to the original images.
|
||||
Therefore all new snapshots (including the starting one) will be saved in
|
||||
overlays and the original image remains unchanged.
|
||||
|
||||
Network devices
|
||||
---------------
|
||||
|
||||
@@ -278,80 +232,3 @@ Audio devices
|
||||
Audio data is recorded and replay automatically. The command line for recording
|
||||
and replaying must contain identical specifications of audio hardware, e.g.:
|
||||
-soundhw ac97
|
||||
|
||||
Serial ports
|
||||
------------
|
||||
|
||||
Serial ports input is recorded and replay automatically. The command lines
|
||||
for recording and replaying must contain identical number of ports in record
|
||||
and replay modes, but their backends may differ.
|
||||
E.g., '-serial stdio' in record mode, and '-serial null' in replay mode.
|
||||
|
||||
Replay log format
|
||||
-----------------
|
||||
|
||||
Record/replay log consits of the header and the sequence of execution
|
||||
events. The header includes 4-byte replay version id and 8-byte reserved
|
||||
field. Version is updated every time replay log format changes to prevent
|
||||
using replay log created by another build of qemu.
|
||||
|
||||
The sequence of the events describes virtual machine state changes.
|
||||
It includes all non-deterministic inputs of VM, synchronization marks and
|
||||
instruction counts used to correctly inject inputs at replay.
|
||||
|
||||
Synchronization marks (checkpoints) are used for synchronizing qemu threads
|
||||
that perform operations with virtual hardware. These operations may change
|
||||
system's state (e.g., change some register or generate interrupt) and
|
||||
therefore should execute synchronously with CPU thread.
|
||||
|
||||
Every event in the log includes 1-byte event id and optional arguments.
|
||||
When argument is an array, it is stored as 4-byte array length
|
||||
and corresponding number of bytes with data.
|
||||
Here is the list of events that are written into the log:
|
||||
|
||||
- EVENT_INSTRUCTION. Instructions executed since last event.
|
||||
Argument: 4-byte number of executed instructions.
|
||||
- EVENT_INTERRUPT. Used to synchronize interrupt processing.
|
||||
- EVENT_EXCEPTION. Used to synchronize exception handling.
|
||||
- EVENT_ASYNC. This is a group of events. They are always processed
|
||||
together with checkpoints. When such an event is generated, it is
|
||||
stored in the queue and processed only when checkpoint occurs.
|
||||
Every such event is followed by 1-byte checkpoint id and 1-byte
|
||||
async event id from the following list:
|
||||
- REPLAY_ASYNC_EVENT_BH. Bottom-half callback. This event synchronizes
|
||||
callbacks that affect virtual machine state, but normally called
|
||||
asyncronously.
|
||||
Argument: 8-byte operation id.
|
||||
- REPLAY_ASYNC_EVENT_INPUT. Input device event. Contains
|
||||
parameters of keyboard and mouse input operations
|
||||
(key press/release, mouse pointer movement).
|
||||
Arguments: 9-16 bytes depending of input event.
|
||||
- REPLAY_ASYNC_EVENT_INPUT_SYNC. Internal input synchronization event.
|
||||
- REPLAY_ASYNC_EVENT_CHAR_READ. Character (e.g., serial port) device input
|
||||
initiated by the sender.
|
||||
Arguments: 1-byte character device id.
|
||||
Array with bytes were read.
|
||||
- REPLAY_ASYNC_EVENT_BLOCK. Block device operation. Used to synchronize
|
||||
operations with disk and flash drives with CPU.
|
||||
Argument: 8-byte operation id.
|
||||
- REPLAY_ASYNC_EVENT_NET. Incoming network packet.
|
||||
Arguments: 1-byte network adapter id.
|
||||
4-byte packet flags.
|
||||
Array with packet bytes.
|
||||
- EVENT_SHUTDOWN. Occurs when user sends shutdown event to qemu,
|
||||
e.g., by closing the window.
|
||||
- EVENT_CHAR_WRITE. Used to synchronize character output operations.
|
||||
Arguments: 4-byte output function return value.
|
||||
4-byte offset in the output array.
|
||||
- EVENT_CHAR_READ_ALL. Used to synchronize character input operations,
|
||||
initiated by qemu.
|
||||
Argument: Array with bytes that were read.
|
||||
- EVENT_CHAR_READ_ALL_ERROR. Unsuccessful character input operation,
|
||||
initiated by qemu.
|
||||
Argument: 4-byte error code.
|
||||
- EVENT_CLOCK + clock_id. Group of events for host clock read operations.
|
||||
Argument: 8-byte clock value.
|
||||
- EVENT_CHECKPOINT + checkpoint_id. Checkpoint for synchronization of
|
||||
CPU, internal threads, and asynchronous input events. May be followed
|
||||
by one or more EVENT_ASYNC events.
|
||||
- EVENT_END. Last event in the log.
|
||||
|
14
dump.c
14
dump.c
@@ -1609,18 +1609,10 @@ static void vmcoreinfo_update_phys_base(DumpState *s)
|
||||
|
||||
lines = g_strsplit((char *)vmci, "\n", -1);
|
||||
for (i = 0; lines[i]; i++) {
|
||||
const char *prefix = NULL;
|
||||
|
||||
if (s->dump_info.d_machine == EM_X86_64) {
|
||||
prefix = "NUMBER(phys_base)=";
|
||||
} else if (s->dump_info.d_machine == EM_AARCH64) {
|
||||
prefix = "NUMBER(PHYS_OFFSET)=";
|
||||
}
|
||||
|
||||
if (prefix && g_str_has_prefix(lines[i], prefix)) {
|
||||
if (qemu_strtou64(lines[i] + strlen(prefix), NULL, 16,
|
||||
if (g_str_has_prefix(lines[i], "NUMBER(phys_base)=")) {
|
||||
if (qemu_strtou64(lines[i] + 18, NULL, 16,
|
||||
&phys_base) < 0) {
|
||||
warn_report("Failed to read %s", prefix);
|
||||
warn_report("Failed to read NUMBER(phys_base)=");
|
||||
} else {
|
||||
s->dump_info.phys_base = phys_base;
|
||||
}
|
||||
|
109
exec.c
109
exec.c
@@ -99,11 +99,6 @@ static MemoryRegion io_mem_unassigned;
|
||||
*/
|
||||
#define RAM_RESIZEABLE (1 << 2)
|
||||
|
||||
/* UFFDIO_ZEROPAGE is available on this RAMBlock to atomically
|
||||
* zero the page and wake waiting processes.
|
||||
* (Set during postcopy)
|
||||
*/
|
||||
#define RAM_UF_ZEROPAGE (1 << 3)
|
||||
#endif
|
||||
|
||||
#ifdef TARGET_PAGE_BITS_VARY
|
||||
@@ -822,29 +817,6 @@ void cpu_exec_realizefn(CPUState *cpu, Error **errp)
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *parse_cpu_model(const char *cpu_model)
|
||||
{
|
||||
ObjectClass *oc;
|
||||
CPUClass *cc;
|
||||
gchar **model_pieces;
|
||||
const char *cpu_type;
|
||||
|
||||
model_pieces = g_strsplit(cpu_model, ",", 2);
|
||||
|
||||
oc = cpu_class_by_name(CPU_RESOLVING_TYPE, model_pieces[0]);
|
||||
if (oc == NULL) {
|
||||
error_report("unable to find CPU model '%s'", model_pieces[0]);
|
||||
g_strfreev(model_pieces);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
cpu_type = object_class_get_name(oc);
|
||||
cc = CPU_CLASS(oc);
|
||||
cc->parse_features(cpu_type, model_pieces[1], &error_fatal);
|
||||
g_strfreev(model_pieces);
|
||||
return cpu_type;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
static void breakpoint_invalidate(CPUState *cpu, target_ulong pc)
|
||||
{
|
||||
@@ -1795,17 +1767,6 @@ bool qemu_ram_is_shared(RAMBlock *rb)
|
||||
return rb->flags & RAM_SHARED;
|
||||
}
|
||||
|
||||
/* Note: Only set at the start of postcopy */
|
||||
bool qemu_ram_is_uf_zeroable(RAMBlock *rb)
|
||||
{
|
||||
return rb->flags & RAM_UF_ZEROPAGE;
|
||||
}
|
||||
|
||||
void qemu_ram_set_uf_zeroable(RAMBlock *rb)
|
||||
{
|
||||
rb->flags |= RAM_UF_ZEROPAGE;
|
||||
}
|
||||
|
||||
/* Called with iothread lock held. */
|
||||
void qemu_ram_set_idstr(RAMBlock *new_block, const char *name, DeviceState *dev)
|
||||
{
|
||||
@@ -2336,16 +2297,6 @@ static void *qemu_ram_ptr_length(RAMBlock *ram_block, ram_addr_t addr,
|
||||
return ramblock_ptr(block, addr);
|
||||
}
|
||||
|
||||
/* Return the offset of a hostpointer within a ramblock */
|
||||
ram_addr_t qemu_ram_block_host_offset(RAMBlock *rb, void *host)
|
||||
{
|
||||
ram_addr_t res = (uint8_t *)host - (uint8_t *)rb->host;
|
||||
assert((uintptr_t)host >= (uintptr_t)rb->host);
|
||||
assert(res < rb->max_length);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Translates a host ptr back to a RAMBlock, a ram_addr and an offset
|
||||
* in that RAMBlock.
|
||||
@@ -3770,7 +3721,6 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length)
|
||||
}
|
||||
|
||||
if ((start + length) <= rb->used_length) {
|
||||
bool need_madvise, need_fallocate;
|
||||
uint8_t *host_endaddr = host_startaddr + length;
|
||||
if ((uintptr_t)host_endaddr & (rb->page_size - 1)) {
|
||||
error_report("ram_block_discard_range: Unaligned end address: %p",
|
||||
@@ -3780,60 +3730,29 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length)
|
||||
|
||||
errno = ENOTSUP; /* If we are missing MADVISE etc */
|
||||
|
||||
/* The logic here is messy;
|
||||
* madvise DONTNEED fails for hugepages
|
||||
* fallocate works on hugepages and shmem
|
||||
*/
|
||||
need_madvise = (rb->page_size == qemu_host_page_size);
|
||||
need_fallocate = rb->fd != -1;
|
||||
if (need_fallocate) {
|
||||
/* For a file, this causes the area of the file to be zero'd
|
||||
* if read, and for hugetlbfs also causes it to be unmapped
|
||||
* so a userfault will trigger.
|
||||
if (rb->page_size == qemu_host_page_size) {
|
||||
#if defined(CONFIG_MADVISE)
|
||||
/* Note: We need the madvise MADV_DONTNEED behaviour of definitely
|
||||
* freeing the page.
|
||||
*/
|
||||
ret = madvise(host_startaddr, length, MADV_DONTNEED);
|
||||
#endif
|
||||
} else {
|
||||
/* Huge page case - unfortunately it can't do DONTNEED, but
|
||||
* it can do the equivalent by FALLOC_FL_PUNCH_HOLE in the
|
||||
* huge page file.
|
||||
*/
|
||||
#ifdef CONFIG_FALLOCATE_PUNCH_HOLE
|
||||
ret = fallocate(rb->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
|
||||
start, length);
|
||||
if (ret) {
|
||||
ret = -errno;
|
||||
error_report("ram_block_discard_range: Failed to fallocate "
|
||||
"%s:%" PRIx64 " +%zx (%d)",
|
||||
rb->idstr, start, length, ret);
|
||||
goto err;
|
||||
}
|
||||
#else
|
||||
ret = -ENOSYS;
|
||||
error_report("ram_block_discard_range: fallocate not available/file"
|
||||
"%s:%" PRIx64 " +%zx (%d)",
|
||||
rb->idstr, start, length, ret);
|
||||
goto err;
|
||||
#endif
|
||||
}
|
||||
if (need_madvise) {
|
||||
/* For normal RAM this causes it to be unmapped,
|
||||
* for shared memory it causes the local mapping to disappear
|
||||
* and to fall back on the file contents (which we just
|
||||
* fallocate'd away).
|
||||
*/
|
||||
#if defined(CONFIG_MADVISE)
|
||||
ret = madvise(host_startaddr, length, MADV_DONTNEED);
|
||||
if (ret) {
|
||||
ret = -errno;
|
||||
error_report("ram_block_discard_range: Failed to discard range "
|
||||
"%s:%" PRIx64 " +%zx (%d)",
|
||||
rb->idstr, start, length, ret);
|
||||
goto err;
|
||||
}
|
||||
#else
|
||||
ret = -ENOSYS;
|
||||
error_report("ram_block_discard_range: MADVISE not available"
|
||||
if (ret) {
|
||||
ret = -errno;
|
||||
error_report("ram_block_discard_range: Failed to discard range "
|
||||
"%s:%" PRIx64 " +%zx (%d)",
|
||||
rb->idstr, start, length, ret);
|
||||
goto err;
|
||||
#endif
|
||||
}
|
||||
trace_ram_block_discard_range(rb->idstr, host_startaddr, length,
|
||||
need_madvise, need_fallocate, ret);
|
||||
} else {
|
||||
error_report("ram_block_discard_range: Overrun block '%s' (%" PRIu64
|
||||
"/%zx/" RAM_ADDR_FMT")",
|
||||
|
@@ -867,22 +867,6 @@ Display the amount of initially allocated and present hotpluggable (if
|
||||
enabled) memory in bytes.
|
||||
ETEXI
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
{
|
||||
.name = "sev",
|
||||
.args_type = "",
|
||||
.params = "",
|
||||
.help = "show SEV information",
|
||||
.cmd = hmp_info_sev,
|
||||
},
|
||||
#endif
|
||||
|
||||
STEXI
|
||||
@item info sev
|
||||
@findex info sev
|
||||
Show SEV information.
|
||||
ETEXI
|
||||
|
||||
STEXI
|
||||
@end table
|
||||
ETEXI
|
||||
|
@@ -106,8 +106,7 @@ ETEXI
|
||||
.args_type = "force:-f,device:B",
|
||||
.params = "[-f] device",
|
||||
.help = "stop an active background block operation (use -f"
|
||||
"\n\t\t\t if you want to abort the operation immediately"
|
||||
"\n\t\t\t instead of keep running until data is in sync)",
|
||||
"\n\t\t\t if the operation is currently paused)",
|
||||
.cmd = hmp_block_job_cancel,
|
||||
},
|
||||
|
||||
|
16
hmp.c
16
hmp.c
@@ -1321,7 +1321,7 @@ void hmp_savevm(Monitor *mon, const QDict *qdict)
|
||||
void hmp_delvm(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
Error *err = NULL;
|
||||
Error *err;
|
||||
const char *name = qdict_get_str(qdict, "name");
|
||||
|
||||
if (bdrv_all_delete_snapshot(name, &bs, &err) < 0) {
|
||||
@@ -2423,18 +2423,7 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
|
||||
switch (value->type) {
|
||||
case MEMORY_DEVICE_INFO_KIND_DIMM:
|
||||
di = value->u.dimm.data;
|
||||
break;
|
||||
|
||||
case MEMORY_DEVICE_INFO_KIND_NVDIMM:
|
||||
di = value->u.nvdimm.data;
|
||||
break;
|
||||
|
||||
default:
|
||||
di = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (di) {
|
||||
monitor_printf(mon, "Memory device [%s]: \"%s\"\n",
|
||||
MemoryDeviceInfoKind_str(value->type),
|
||||
di->id ? di->id : "");
|
||||
@@ -2447,6 +2436,9 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
|
||||
di->hotplugged ? "true" : "false");
|
||||
monitor_printf(mon, " hotpluggable: %s\n",
|
||||
di->hotpluggable ? "true" : "false");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
1
hmp.h
1
hmp.h
@@ -143,6 +143,5 @@ void hmp_info_ramblock(Monitor *mon, const QDict *qdict);
|
||||
void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_sev(Monitor *mon, const QDict *qdict);
|
||||
|
||||
#endif
|
||||
|
@@ -258,22 +258,6 @@ static void build_append_int(GArray *table, uint64_t value)
|
||||
}
|
||||
}
|
||||
|
||||
/* Generic Address Structure (GAS)
|
||||
* ACPI 2.0/3.0: 5.2.3.1 Generic Address Structure
|
||||
* 2.0 compat note:
|
||||
* @access_width must be 0, see ACPI 2.0:Table 5-1
|
||||
*/
|
||||
void build_append_gas(GArray *table, AmlAddressSpace as,
|
||||
uint8_t bit_width, uint8_t bit_offset,
|
||||
uint8_t access_width, uint64_t address)
|
||||
{
|
||||
build_append_int_noprefix(table, as, 1);
|
||||
build_append_int_noprefix(table, bit_width, 1);
|
||||
build_append_int_noprefix(table, bit_offset, 1);
|
||||
build_append_int_noprefix(table, access_width, 1);
|
||||
build_append_int_noprefix(table, address, 8);
|
||||
}
|
||||
|
||||
/*
|
||||
* Build NAME(XXXX, 0x00000000) where 0x00000000 is encoded as a dword,
|
||||
* and return the offset to 0x00000000 for runtime patching.
|
||||
@@ -1678,127 +1662,3 @@ void build_slit(GArray *table_data, BIOSLinker *linker)
|
||||
"SLIT",
|
||||
table_data->len - slit_start, 1, NULL, NULL);
|
||||
}
|
||||
|
||||
/* build rev1/rev3/rev5.1 FADT */
|
||||
void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f,
|
||||
const char *oem_id, const char *oem_table_id)
|
||||
{
|
||||
int off;
|
||||
int fadt_start = tbl->len;
|
||||
|
||||
acpi_data_push(tbl, sizeof(AcpiTableHeader));
|
||||
|
||||
/* FACS address to be filled by Guest linker at runtime */
|
||||
off = tbl->len;
|
||||
build_append_int_noprefix(tbl, 0, 4); /* FIRMWARE_CTRL */
|
||||
if (f->facs_tbl_offset) { /* don't patch if not supported by platform */
|
||||
bios_linker_loader_add_pointer(linker,
|
||||
ACPI_BUILD_TABLE_FILE, off, 4,
|
||||
ACPI_BUILD_TABLE_FILE, *f->facs_tbl_offset);
|
||||
}
|
||||
|
||||
/* DSDT address to be filled by Guest linker at runtime */
|
||||
off = tbl->len;
|
||||
build_append_int_noprefix(tbl, 0, 4); /* DSDT */
|
||||
if (f->dsdt_tbl_offset) { /* don't patch if not supported by platform */
|
||||
bios_linker_loader_add_pointer(linker,
|
||||
ACPI_BUILD_TABLE_FILE, off, 4,
|
||||
ACPI_BUILD_TABLE_FILE, *f->dsdt_tbl_offset);
|
||||
}
|
||||
|
||||
/* ACPI1.0: INT_MODEL, ACPI2.0+: Reserved */
|
||||
build_append_int_noprefix(tbl, f->int_model /* Multiple APIC */, 1);
|
||||
/* Preferred_PM_Profile */
|
||||
build_append_int_noprefix(tbl, 0 /* Unspecified */, 1);
|
||||
build_append_int_noprefix(tbl, f->sci_int, 2); /* SCI_INT */
|
||||
build_append_int_noprefix(tbl, f->smi_cmd, 4); /* SMI_CMD */
|
||||
build_append_int_noprefix(tbl, f->acpi_enable_cmd, 1); /* ACPI_ENABLE */
|
||||
build_append_int_noprefix(tbl, f->acpi_disable_cmd, 1); /* ACPI_DISABLE */
|
||||
build_append_int_noprefix(tbl, 0 /* not supported */, 1); /* S4BIOS_REQ */
|
||||
/* ACPI1.0: Reserved, ACPI2.0+: PSTATE_CNT */
|
||||
build_append_int_noprefix(tbl, 0, 1);
|
||||
build_append_int_noprefix(tbl, f->pm1a_evt.address, 4); /* PM1a_EVT_BLK */
|
||||
build_append_int_noprefix(tbl, 0, 4); /* PM1b_EVT_BLK */
|
||||
build_append_int_noprefix(tbl, f->pm1a_cnt.address, 4); /* PM1a_CNT_BLK */
|
||||
build_append_int_noprefix(tbl, 0, 4); /* PM1b_CNT_BLK */
|
||||
build_append_int_noprefix(tbl, 0, 4); /* PM2_CNT_BLK */
|
||||
build_append_int_noprefix(tbl, f->pm_tmr.address, 4); /* PM_TMR_BLK */
|
||||
build_append_int_noprefix(tbl, f->gpe0_blk.address, 4); /* GPE0_BLK */
|
||||
build_append_int_noprefix(tbl, 0, 4); /* GPE1_BLK */
|
||||
/* PM1_EVT_LEN */
|
||||
build_append_int_noprefix(tbl, f->pm1a_evt.bit_width / 8, 1);
|
||||
/* PM1_CNT_LEN */
|
||||
build_append_int_noprefix(tbl, f->pm1a_cnt.bit_width / 8, 1);
|
||||
build_append_int_noprefix(tbl, 0, 1); /* PM2_CNT_LEN */
|
||||
build_append_int_noprefix(tbl, f->pm_tmr.bit_width / 8, 1); /* PM_TMR_LEN */
|
||||
/* GPE0_BLK_LEN */
|
||||
build_append_int_noprefix(tbl, f->gpe0_blk.bit_width / 8, 1);
|
||||
build_append_int_noprefix(tbl, 0, 1); /* GPE1_BLK_LEN */
|
||||
build_append_int_noprefix(tbl, 0, 1); /* GPE1_BASE */
|
||||
build_append_int_noprefix(tbl, 0, 1); /* CST_CNT */
|
||||
build_append_int_noprefix(tbl, f->plvl2_lat, 2); /* P_LVL2_LAT */
|
||||
build_append_int_noprefix(tbl, f->plvl3_lat, 2); /* P_LVL3_LAT */
|
||||
build_append_int_noprefix(tbl, 0, 2); /* FLUSH_SIZE */
|
||||
build_append_int_noprefix(tbl, 0, 2); /* FLUSH_STRIDE */
|
||||
build_append_int_noprefix(tbl, 0, 1); /* DUTY_OFFSET */
|
||||
build_append_int_noprefix(tbl, 0, 1); /* DUTY_WIDTH */
|
||||
build_append_int_noprefix(tbl, 0, 1); /* DAY_ALRM */
|
||||
build_append_int_noprefix(tbl, 0, 1); /* MON_ALRM */
|
||||
build_append_int_noprefix(tbl, f->rtc_century, 1); /* CENTURY */
|
||||
build_append_int_noprefix(tbl, 0, 2); /* IAPC_BOOT_ARCH */
|
||||
build_append_int_noprefix(tbl, 0, 1); /* Reserved */
|
||||
build_append_int_noprefix(tbl, f->flags, 4); /* Flags */
|
||||
|
||||
if (f->rev == 1) {
|
||||
goto build_hdr;
|
||||
}
|
||||
|
||||
build_append_gas_from_struct(tbl, &f->reset_reg); /* RESET_REG */
|
||||
build_append_int_noprefix(tbl, f->reset_val, 1); /* RESET_VALUE */
|
||||
/* Since ACPI 5.1 */
|
||||
if ((f->rev >= 6) || ((f->rev == 5) && f->minor_ver > 0)) {
|
||||
build_append_int_noprefix(tbl, f->arm_boot_arch, 2); /* ARM_BOOT_ARCH */
|
||||
/* FADT Minor Version */
|
||||
build_append_int_noprefix(tbl, f->minor_ver, 1);
|
||||
} else {
|
||||
build_append_int_noprefix(tbl, 0, 3); /* Reserved upto ACPI 5.0 */
|
||||
}
|
||||
build_append_int_noprefix(tbl, 0, 8); /* X_FIRMWARE_CTRL */
|
||||
|
||||
/* XDSDT address to be filled by Guest linker at runtime */
|
||||
off = tbl->len;
|
||||
build_append_int_noprefix(tbl, 0, 8); /* X_DSDT */
|
||||
if (f->xdsdt_tbl_offset) {
|
||||
bios_linker_loader_add_pointer(linker,
|
||||
ACPI_BUILD_TABLE_FILE, off, 8,
|
||||
ACPI_BUILD_TABLE_FILE, *f->xdsdt_tbl_offset);
|
||||
}
|
||||
|
||||
build_append_gas_from_struct(tbl, &f->pm1a_evt); /* X_PM1a_EVT_BLK */
|
||||
/* X_PM1b_EVT_BLK */
|
||||
build_append_gas(tbl, AML_AS_SYSTEM_MEMORY, 0 , 0, 0, 0);
|
||||
build_append_gas_from_struct(tbl, &f->pm1a_cnt); /* X_PM1a_CNT_BLK */
|
||||
/* X_PM1b_CNT_BLK */
|
||||
build_append_gas(tbl, AML_AS_SYSTEM_MEMORY, 0 , 0, 0, 0);
|
||||
/* X_PM2_CNT_BLK */
|
||||
build_append_gas(tbl, AML_AS_SYSTEM_MEMORY, 0 , 0, 0, 0);
|
||||
build_append_gas_from_struct(tbl, &f->pm_tmr); /* X_PM_TMR_BLK */
|
||||
build_append_gas_from_struct(tbl, &f->gpe0_blk); /* X_GPE0_BLK */
|
||||
build_append_gas(tbl, AML_AS_SYSTEM_MEMORY, 0 , 0, 0, 0); /* X_GPE1_BLK */
|
||||
|
||||
if (f->rev <= 4) {
|
||||
goto build_hdr;
|
||||
}
|
||||
|
||||
/* SLEEP_CONTROL_REG */
|
||||
build_append_gas(tbl, AML_AS_SYSTEM_MEMORY, 0 , 0, 0, 0);
|
||||
/* SLEEP_STATUS_REG */
|
||||
build_append_gas(tbl, AML_AS_SYSTEM_MEMORY, 0 , 0, 0, 0);
|
||||
|
||||
/* TODO: extra fields need to be added to support revisions above rev5 */
|
||||
assert(f->rev == 5);
|
||||
|
||||
build_hdr:
|
||||
build_header(linker, tbl, (void *)(tbl->data + fadt_start),
|
||||
"FACP", tbl->len - fadt_start, f->rev, oem_id, oem_table_id);
|
||||
}
|
||||
|
@@ -19,8 +19,7 @@
|
||||
#include "hw/timer/mc146818rtc.h"
|
||||
#include "hw/ide.h"
|
||||
#include "hw/timer/i8254.h"
|
||||
#include "hw/isa/superio.h"
|
||||
#include "hw/dma/i8257.h"
|
||||
#include "hw/char/serial.h"
|
||||
#include "qemu/cutils.h"
|
||||
|
||||
#define MAX_IDE_BUS 2
|
||||
@@ -82,21 +81,19 @@ static void clipper_init(MachineState *machine)
|
||||
mc146818_rtc_init(isa_bus, 1900, rtc_irq);
|
||||
|
||||
i8254_pit_init(isa_bus, 0x40, 0, NULL);
|
||||
isa_create_simple(isa_bus, "i8042");
|
||||
|
||||
/* VGA setup. Don't bother loading the bios. */
|
||||
pci_vga_init(pci_bus);
|
||||
|
||||
/* Serial code setup. */
|
||||
serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS);
|
||||
|
||||
/* Network setup. e1000 is good enough, failing Tulip support. */
|
||||
for (i = 0; i < nb_nics; i++) {
|
||||
pci_nic_init_nofail(&nd_table[i], pci_bus, "e1000", NULL);
|
||||
}
|
||||
|
||||
/* 2 82C37 (dma) */
|
||||
isa_create_simple(isa_bus, "i82374");
|
||||
|
||||
/* Super I/O */
|
||||
isa_create_simple(isa_bus, TYPE_SMC37C669_SUPERIO);
|
||||
|
||||
/* IDE disk setup. */
|
||||
{
|
||||
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
|
||||
|
@@ -23,40 +23,9 @@
|
||||
/* "QA7" (Pi2) interrupt controller and mailboxes etc. */
|
||||
#define BCM2836_CONTROL_BASE 0x40000000
|
||||
|
||||
struct BCM283XInfo {
|
||||
const char *name;
|
||||
const char *cpu_type;
|
||||
int clusterid;
|
||||
};
|
||||
|
||||
static const BCM283XInfo bcm283x_socs[] = {
|
||||
{
|
||||
.name = TYPE_BCM2836,
|
||||
.cpu_type = ARM_CPU_TYPE_NAME("cortex-a15"),
|
||||
.clusterid = 0xf,
|
||||
},
|
||||
#ifdef TARGET_AARCH64
|
||||
{
|
||||
.name = TYPE_BCM2837,
|
||||
.cpu_type = ARM_CPU_TYPE_NAME("cortex-a53"),
|
||||
.clusterid = 0x0,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
static void bcm2836_init(Object *obj)
|
||||
{
|
||||
BCM283XState *s = BCM283X(obj);
|
||||
BCM283XClass *bc = BCM283X_GET_CLASS(obj);
|
||||
const BCM283XInfo *info = bc->info;
|
||||
int n;
|
||||
|
||||
for (n = 0; n < BCM283X_NCPUS; n++) {
|
||||
object_initialize(&s->cpus[n], sizeof(s->cpus[n]),
|
||||
info->cpu_type);
|
||||
object_property_add_child(obj, "cpu[*]", OBJECT(&s->cpus[n]),
|
||||
&error_abort);
|
||||
}
|
||||
BCM2836State *s = BCM2836(obj);
|
||||
|
||||
object_initialize(&s->control, sizeof(s->control), TYPE_BCM2836_CONTROL);
|
||||
object_property_add_child(obj, "control", OBJECT(&s->control), NULL);
|
||||
@@ -75,15 +44,21 @@ static void bcm2836_init(Object *obj)
|
||||
|
||||
static void bcm2836_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
BCM283XState *s = BCM283X(dev);
|
||||
BCM283XClass *bc = BCM283X_GET_CLASS(dev);
|
||||
const BCM283XInfo *info = bc->info;
|
||||
BCM2836State *s = BCM2836(dev);
|
||||
Object *obj;
|
||||
Error *err = NULL;
|
||||
int n;
|
||||
|
||||
/* common peripherals from bcm2835 */
|
||||
|
||||
obj = OBJECT(dev);
|
||||
for (n = 0; n < BCM2836_NCPUS; n++) {
|
||||
object_initialize(&s->cpus[n], sizeof(s->cpus[n]),
|
||||
s->cpu_type);
|
||||
object_property_add_child(obj, "cpu[*]", OBJECT(&s->cpus[n]),
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), "ram", &err);
|
||||
if (obj == NULL) {
|
||||
error_setg(errp, "%s: required ram link not found: %s",
|
||||
@@ -127,9 +102,11 @@ static void bcm2836_realize(DeviceState *dev, Error **errp)
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 1,
|
||||
qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-fiq", 0));
|
||||
|
||||
for (n = 0; n < BCM283X_NCPUS; n++) {
|
||||
/* TODO: this should be converted to a property of ARM_CPU */
|
||||
s->cpus[n].mp_affinity = (info->clusterid << 8) | n;
|
||||
for (n = 0; n < BCM2836_NCPUS; n++) {
|
||||
/* Mirror bcm2836, which has clusterid set to 0xf
|
||||
* TODO: this should be converted to a property of ARM_CPU
|
||||
*/
|
||||
s->cpus[n].mp_affinity = 0xF00 | n;
|
||||
|
||||
/* set periphbase/CBAR value for CPU-local registers */
|
||||
object_property_set_int(OBJECT(&s->cpus[n]),
|
||||
@@ -173,44 +150,30 @@ static void bcm2836_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
static Property bcm2836_props[] = {
|
||||
DEFINE_PROP_UINT32("enabled-cpus", BCM283XState, enabled_cpus,
|
||||
BCM283X_NCPUS),
|
||||
DEFINE_PROP_STRING("cpu-type", BCM2836State, cpu_type),
|
||||
DEFINE_PROP_UINT32("enabled-cpus", BCM2836State, enabled_cpus, BCM2836_NCPUS),
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
};
|
||||
|
||||
static void bcm283x_class_init(ObjectClass *oc, void *data)
|
||||
static void bcm2836_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
BCM283XClass *bc = BCM283X_CLASS(oc);
|
||||
|
||||
bc->info = data;
|
||||
dc->realize = bcm2836_realize;
|
||||
dc->props = bcm2836_props;
|
||||
dc->realize = bcm2836_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo bcm283x_type_info = {
|
||||
.name = TYPE_BCM283X,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(BCM283XState),
|
||||
static const TypeInfo bcm2836_type_info = {
|
||||
.name = TYPE_BCM2836,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(BCM2836State),
|
||||
.instance_init = bcm2836_init,
|
||||
.class_size = sizeof(BCM283XClass),
|
||||
.abstract = true,
|
||||
.class_init = bcm2836_class_init,
|
||||
};
|
||||
|
||||
static void bcm2836_register_types(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
type_register_static(&bcm283x_type_info);
|
||||
for (i = 0; i < ARRAY_SIZE(bcm283x_socs); i++) {
|
||||
TypeInfo ti = {
|
||||
.name = bcm283x_socs[i].name,
|
||||
.parent = TYPE_BCM283X,
|
||||
.class_init = bcm283x_class_init,
|
||||
.class_data = (void *) &bcm283x_socs[i],
|
||||
};
|
||||
type_register(&ti);
|
||||
}
|
||||
type_register_static(&bcm2836_type_info);
|
||||
}
|
||||
|
||||
type_init(bcm2836_register_types)
|
||||
|
@@ -720,18 +720,6 @@ static void do_cpu_reset(void *opaque)
|
||||
} else {
|
||||
env->pstate = PSTATE_MODE_EL1h;
|
||||
}
|
||||
/* AArch64 kernels never boot in secure mode */
|
||||
assert(!info->secure_boot);
|
||||
/* This hook is only supported for AArch32 currently:
|
||||
* bootloader_aarch64[] will not call the hook, and
|
||||
* the code above has already dropped us into EL2 or EL1.
|
||||
*/
|
||||
assert(!info->secure_board_setup);
|
||||
}
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_EL2)) {
|
||||
/* If we have EL2 then Linux expects the HVC insn to work */
|
||||
env->cp15.scr_el3 |= SCR_HCE;
|
||||
}
|
||||
|
||||
/* Set to non-secure if not a secure boot */
|
||||
|
@@ -27,6 +27,7 @@
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/boards.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/char/pl011.h"
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/char/serial.h"
|
||||
#include "hw/boards.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "hw/arm/msf2-soc.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
|
@@ -27,13 +27,12 @@
|
||||
#define BOARDSETUP_ADDR (MVBAR_ADDR + 0x20) /* board setup code */
|
||||
#define FIRMWARE_ADDR_2 0x8000 /* Pi 2 loads kernel.img here by default */
|
||||
#define FIRMWARE_ADDR_3 0x80000 /* Pi 3 loads kernel.img here by default */
|
||||
#define SPINTABLE_ADDR 0xd8 /* Pi 3 bootloader spintable */
|
||||
|
||||
/* Table of Linux board IDs for different Pi versions */
|
||||
static const int raspi_boardid[] = {[1] = 0xc42, [2] = 0xc43, [3] = 0xc44};
|
||||
|
||||
typedef struct RasPiState {
|
||||
BCM283XState soc;
|
||||
BCM2836State soc;
|
||||
MemoryRegion ram;
|
||||
} RasPiState;
|
||||
|
||||
@@ -64,40 +63,6 @@ static void write_smpboot(ARMCPU *cpu, const struct arm_boot_info *info)
|
||||
info->smp_loader_start);
|
||||
}
|
||||
|
||||
static void write_smpboot64(ARMCPU *cpu, const struct arm_boot_info *info)
|
||||
{
|
||||
/* Unlike the AArch32 version we don't need to call the board setup hook.
|
||||
* The mechanism for doing the spin-table is also entirely different.
|
||||
* We must have four 64-bit fields at absolute addresses
|
||||
* 0xd8, 0xe0, 0xe8, 0xf0 in RAM, which are the flag variables for
|
||||
* our CPUs, and which we must ensure are zero initialized before
|
||||
* the primary CPU goes into the kernel. We put these variables inside
|
||||
* a rom blob, so that the reset for ROM contents zeroes them for us.
|
||||
*/
|
||||
static const uint32_t smpboot[] = {
|
||||
0xd2801b05, /* mov x5, 0xd8 */
|
||||
0xd53800a6, /* mrs x6, mpidr_el1 */
|
||||
0x924004c6, /* and x6, x6, #0x3 */
|
||||
0xd503205f, /* spin: wfe */
|
||||
0xf86678a4, /* ldr x4, [x5,x6,lsl #3] */
|
||||
0xb4ffffc4, /* cbz x4, spin */
|
||||
0xd2800000, /* mov x0, #0x0 */
|
||||
0xd2800001, /* mov x1, #0x0 */
|
||||
0xd2800002, /* mov x2, #0x0 */
|
||||
0xd2800003, /* mov x3, #0x0 */
|
||||
0xd61f0080, /* br x4 */
|
||||
};
|
||||
|
||||
static const uint64_t spintables[] = {
|
||||
0, 0, 0, 0
|
||||
};
|
||||
|
||||
rom_add_blob_fixed("raspi_smpboot", smpboot, sizeof(smpboot),
|
||||
info->smp_loader_start);
|
||||
rom_add_blob_fixed("raspi_spintables", spintables, sizeof(spintables),
|
||||
SPINTABLE_ADDR);
|
||||
}
|
||||
|
||||
static void write_board_setup(ARMCPU *cpu, const struct arm_boot_info *info)
|
||||
{
|
||||
arm_write_secure_board_setup_dummy_smc(cpu, info, MVBAR_ADDR);
|
||||
@@ -117,28 +82,15 @@ static void setup_boot(MachineState *machine, int version, size_t ram_size)
|
||||
binfo.board_id = raspi_boardid[version];
|
||||
binfo.ram_size = ram_size;
|
||||
binfo.nb_cpus = smp_cpus;
|
||||
|
||||
if (version <= 2) {
|
||||
/* The rpi1 and 2 require some custom setup code to run in Secure
|
||||
* mode before booting a kernel (to set up the SMC vectors so
|
||||
* that we get a no-op SMC; this is used by Linux to call the
|
||||
* firmware for some cache maintenance operations.
|
||||
* The rpi3 doesn't need this.
|
||||
*/
|
||||
binfo.board_setup_addr = BOARDSETUP_ADDR;
|
||||
binfo.write_board_setup = write_board_setup;
|
||||
binfo.secure_board_setup = true;
|
||||
binfo.secure_boot = true;
|
||||
}
|
||||
binfo.board_setup_addr = BOARDSETUP_ADDR;
|
||||
binfo.write_board_setup = write_board_setup;
|
||||
binfo.secure_board_setup = true;
|
||||
binfo.secure_boot = true;
|
||||
|
||||
/* Pi2 and Pi3 requires SMP setup */
|
||||
if (version >= 2) {
|
||||
binfo.smp_loader_start = SMPBOOT_ADDR;
|
||||
if (version == 2) {
|
||||
binfo.write_secondary_boot = write_smpboot;
|
||||
} else {
|
||||
binfo.write_secondary_boot = write_smpboot64;
|
||||
}
|
||||
binfo.write_secondary_boot = write_smpboot;
|
||||
binfo.secondary_cpu_reset_hook = reset_secondary;
|
||||
}
|
||||
|
||||
@@ -175,8 +127,7 @@ static void raspi_init(MachineState *machine, int version)
|
||||
BusState *bus;
|
||||
DeviceState *carddev;
|
||||
|
||||
object_initialize(&s->soc, sizeof(s->soc),
|
||||
version == 3 ? TYPE_BCM2837 : TYPE_BCM2836);
|
||||
object_initialize(&s->soc, sizeof(s->soc), TYPE_BCM2836);
|
||||
object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
|
||||
&error_abort);
|
||||
|
||||
@@ -189,6 +140,8 @@ static void raspi_init(MachineState *machine, int version)
|
||||
/* Setup the SOC */
|
||||
object_property_add_const_link(OBJECT(&s->soc), "ram", OBJECT(&s->ram),
|
||||
&error_abort);
|
||||
object_property_set_str(OBJECT(&s->soc), machine->cpu_type, "cpu-type",
|
||||
&error_abort);
|
||||
object_property_set_int(OBJECT(&s->soc), smp_cpus, "enabled-cpus",
|
||||
&error_abort);
|
||||
int board_rev = version == 3 ? 0xa02082 : 0xa21041;
|
||||
@@ -227,9 +180,9 @@ static void raspi2_machine_init(MachineClass *mc)
|
||||
mc->no_floppy = 1;
|
||||
mc->no_cdrom = 1;
|
||||
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a15");
|
||||
mc->max_cpus = BCM283X_NCPUS;
|
||||
mc->min_cpus = BCM283X_NCPUS;
|
||||
mc->default_cpus = BCM283X_NCPUS;
|
||||
mc->max_cpus = BCM2836_NCPUS;
|
||||
mc->min_cpus = BCM2836_NCPUS;
|
||||
mc->default_cpus = BCM2836_NCPUS;
|
||||
mc->default_ram_size = 1024 * 1024 * 1024;
|
||||
mc->ignore_memory_transaction_failures = true;
|
||||
};
|
||||
@@ -250,9 +203,9 @@ static void raspi3_machine_init(MachineClass *mc)
|
||||
mc->no_floppy = 1;
|
||||
mc->no_cdrom = 1;
|
||||
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a53");
|
||||
mc->max_cpus = BCM283X_NCPUS;
|
||||
mc->min_cpus = BCM283X_NCPUS;
|
||||
mc->default_cpus = BCM283X_NCPUS;
|
||||
mc->max_cpus = BCM2836_NCPUS;
|
||||
mc->min_cpus = BCM2836_NCPUS;
|
||||
mc->default_cpus = BCM2836_NCPUS;
|
||||
mc->default_ram_size = 1024 * 1024 * 1024;
|
||||
}
|
||||
DEFINE_MACHINE("raspi3", raspi3_machine_init)
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/i2c/i2c.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/char/pl011.h"
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#include "hw/boards.h"
|
||||
#include "hw/i2c/i2c.h"
|
||||
#include "hw/ssi/ssi.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
@@ -651,33 +651,42 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
|
||||
}
|
||||
|
||||
/* FADT */
|
||||
static void build_fadt_rev5(GArray *table_data, BIOSLinker *linker,
|
||||
VirtMachineState *vms, unsigned dsdt_tbl_offset)
|
||||
static void build_fadt(GArray *table_data, BIOSLinker *linker,
|
||||
VirtMachineState *vms, unsigned dsdt_tbl_offset)
|
||||
{
|
||||
/* ACPI v5.1 */
|
||||
AcpiFadtData fadt = {
|
||||
.rev = 5,
|
||||
.minor_ver = 1,
|
||||
.flags = 1 << ACPI_FADT_F_HW_REDUCED_ACPI,
|
||||
.xdsdt_tbl_offset = &dsdt_tbl_offset,
|
||||
};
|
||||
int fadt_start = table_data->len;
|
||||
AcpiFadtDescriptorRev5_1 *fadt = acpi_data_push(table_data, sizeof(*fadt));
|
||||
unsigned xdsdt_entry_offset = (char *)&fadt->x_dsdt - table_data->data;
|
||||
uint16_t bootflags;
|
||||
|
||||
switch (vms->psci_conduit) {
|
||||
case QEMU_PSCI_CONDUIT_DISABLED:
|
||||
fadt.arm_boot_arch = 0;
|
||||
bootflags = 0;
|
||||
break;
|
||||
case QEMU_PSCI_CONDUIT_HVC:
|
||||
fadt.arm_boot_arch = ACPI_FADT_ARM_PSCI_COMPLIANT |
|
||||
ACPI_FADT_ARM_PSCI_USE_HVC;
|
||||
bootflags = ACPI_FADT_ARM_PSCI_COMPLIANT | ACPI_FADT_ARM_PSCI_USE_HVC;
|
||||
break;
|
||||
case QEMU_PSCI_CONDUIT_SMC:
|
||||
fadt.arm_boot_arch = ACPI_FADT_ARM_PSCI_COMPLIANT;
|
||||
bootflags = ACPI_FADT_ARM_PSCI_COMPLIANT;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
build_fadt(table_data, linker, &fadt, NULL, NULL);
|
||||
/* Hardware Reduced = 1 and use PSCI 0.2+ */
|
||||
fadt->flags = cpu_to_le32(1 << ACPI_FADT_F_HW_REDUCED_ACPI);
|
||||
fadt->arm_boot_flags = cpu_to_le16(bootflags);
|
||||
|
||||
/* ACPI v5.1 (fadt->revision.fadt->minor_revision) */
|
||||
fadt->minor_revision = 0x1;
|
||||
|
||||
/* DSDT address to be filled by Guest linker */
|
||||
bios_linker_loader_add_pointer(linker,
|
||||
ACPI_BUILD_TABLE_FILE, xdsdt_entry_offset, sizeof(fadt->x_dsdt),
|
||||
ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset);
|
||||
|
||||
build_header(linker, table_data, (void *)(table_data->data + fadt_start),
|
||||
"FACP", table_data->len - fadt_start, 5, NULL, NULL);
|
||||
}
|
||||
|
||||
/* DSDT */
|
||||
@@ -752,7 +761,7 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
|
||||
|
||||
/* FADT MADT GTDT MCFG SPCR pointed to by RSDT */
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
build_fadt_rev5(tables_blob, tables->linker, vms, dsdt);
|
||||
build_fadt(tables_blob, tables->linker, vms, dsdt);
|
||||
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
build_madt(tables_blob, tables->linker, vms);
|
||||
|
@@ -1,7 +1,6 @@
|
||||
common-obj-$(CONFIG_IPACK) += ipoctal232.o
|
||||
common-obj-$(CONFIG_ESCC) += escc.o
|
||||
common-obj-$(CONFIG_PARALLEL) += parallel.o
|
||||
common-obj-$(CONFIG_PARALLEL) += parallel-isa.o
|
||||
common-obj-$(CONFIG_PL011) += pl011.o
|
||||
common-obj-$(CONFIG_SERIAL) += serial.o
|
||||
common-obj-$(CONFIG_SERIAL_ISA) += serial-isa.o
|
||||
|
@@ -37,8 +37,8 @@
|
||||
|
||||
static const VMStateDescription vmstate_imx_serial = {
|
||||
.name = TYPE_IMX_SERIAL,
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 2,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT32(readbuff, IMXSerialState),
|
||||
VMSTATE_UINT32(usr1, IMXSerialState),
|
||||
@@ -50,36 +50,22 @@ static const VMStateDescription vmstate_imx_serial = {
|
||||
VMSTATE_UINT32(ubmr, IMXSerialState),
|
||||
VMSTATE_UINT32(ubrc, IMXSerialState),
|
||||
VMSTATE_UINT32(ucr3, IMXSerialState),
|
||||
VMSTATE_UINT32(ucr4, IMXSerialState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
};
|
||||
|
||||
static void imx_update(IMXSerialState *s)
|
||||
{
|
||||
uint32_t usr1;
|
||||
uint32_t usr2;
|
||||
uint32_t mask;
|
||||
uint32_t flags;
|
||||
|
||||
/*
|
||||
* Lucky for us TRDY and RRDY has the same offset in both USR1 and
|
||||
* UCR1, so we can get away with something as simple as the
|
||||
* following:
|
||||
*/
|
||||
usr1 = s->usr1 & s->ucr1 & (USR1_TRDY | USR1_RRDY);
|
||||
/*
|
||||
* Bits that we want in USR2 are not as conveniently laid out,
|
||||
* unfortunately.
|
||||
*/
|
||||
mask = (s->ucr1 & UCR1_TXMPTYEN) ? USR2_TXFE : 0;
|
||||
/*
|
||||
* TCEN and TXDC are both bit 3
|
||||
*/
|
||||
mask |= s->ucr4 & UCR4_TCEN;
|
||||
flags = (s->usr1 & s->ucr1) & (USR1_TRDY|USR1_RRDY);
|
||||
if (s->ucr1 & UCR1_TXMPTYEN) {
|
||||
flags |= (s->uts1 & UTS1_TXEMPTY);
|
||||
} else {
|
||||
flags &= ~USR1_TRDY;
|
||||
}
|
||||
|
||||
usr2 = s->usr2 & mask;
|
||||
|
||||
qemu_set_irq(s->irq, usr1 || usr2);
|
||||
qemu_set_irq(s->irq, !!flags);
|
||||
}
|
||||
|
||||
static void imx_serial_reset(IMXSerialState *s)
|
||||
@@ -169,8 +155,6 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset,
|
||||
return s->ucr3;
|
||||
|
||||
case 0x23: /* UCR4 */
|
||||
return s->ucr4;
|
||||
|
||||
case 0x29: /* BRM Incremental */
|
||||
return 0x0; /* TODO */
|
||||
|
||||
@@ -199,10 +183,8 @@ static void imx_serial_write(void *opaque, hwaddr offset,
|
||||
* qemu_chr_fe_write and background I/O callbacks */
|
||||
qemu_chr_fe_write_all(&s->chr, &ch, 1);
|
||||
s->usr1 &= ~USR1_TRDY;
|
||||
s->usr2 &= ~USR2_TXDC;
|
||||
imx_update(s);
|
||||
s->usr1 |= USR1_TRDY;
|
||||
s->usr2 |= USR2_TXDC;
|
||||
imx_update(s);
|
||||
}
|
||||
break;
|
||||
@@ -275,12 +257,8 @@ static void imx_serial_write(void *opaque, hwaddr offset,
|
||||
s->ucr3 = value & 0xffff;
|
||||
break;
|
||||
|
||||
case 0x23: /* UCR4 */
|
||||
s->ucr4 = value & 0xffff;
|
||||
imx_update(s);
|
||||
break;
|
||||
|
||||
case 0x2d: /* UTS1 */
|
||||
case 0x23: /* UCR4 */
|
||||
qemu_log_mask(LOG_UNIMP, "[%s]%s: Unimplemented reg 0x%"
|
||||
HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset);
|
||||
/* TODO */
|
||||
|
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* QEMU Parallel PORT (ISA bus helpers)
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/char/parallel.h"
|
||||
|
||||
static void parallel_init(ISABus *bus, int index, Chardev *chr)
|
||||
{
|
||||
DeviceState *dev;
|
||||
ISADevice *isadev;
|
||||
|
||||
isadev = isa_create(bus, "isa-parallel");
|
||||
dev = DEVICE(isadev);
|
||||
qdev_prop_set_uint32(dev, "index", index);
|
||||
qdev_prop_set_chr(dev, "chardev", chr);
|
||||
qdev_init_nofail(dev);
|
||||
}
|
||||
|
||||
void parallel_hds_isa_init(ISABus *bus, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
assert(n <= MAX_PARALLEL_PORTS);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (parallel_hds[i]) {
|
||||
parallel_init(bus, i, parallel_hds[i]);
|
||||
}
|
||||
}
|
||||
}
|
@@ -28,7 +28,7 @@
|
||||
#include "chardev/char-parallel.h"
|
||||
#include "chardev/char-fe.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/char/parallel.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
//#define DEBUG_PARALLEL
|
||||
|
@@ -334,22 +334,6 @@ static bool machine_get_enforce_config_section(Object *obj, Error **errp)
|
||||
return ms->enforce_config_section;
|
||||
}
|
||||
|
||||
static char *machine_get_memory_encryption(Object *obj, Error **errp)
|
||||
{
|
||||
MachineState *ms = MACHINE(obj);
|
||||
|
||||
return g_strdup(ms->memory_encryption);
|
||||
}
|
||||
|
||||
static void machine_set_memory_encryption(Object *obj, const char *value,
|
||||
Error **errp)
|
||||
{
|
||||
MachineState *ms = MACHINE(obj);
|
||||
|
||||
g_free(ms->memory_encryption);
|
||||
ms->memory_encryption = g_strdup(value);
|
||||
}
|
||||
|
||||
void machine_class_allow_dynamic_sysbus_dev(MachineClass *mc, const char *type)
|
||||
{
|
||||
strList *item = g_new0(strList, 1);
|
||||
@@ -628,12 +612,6 @@ static void machine_class_init(ObjectClass *oc, void *data)
|
||||
&error_abort);
|
||||
object_class_property_set_description(oc, "enforce-config-section",
|
||||
"Set on to enforce configuration section migration", &error_abort);
|
||||
|
||||
object_class_property_add_str(oc, "memory-encryption",
|
||||
machine_get_memory_encryption, machine_set_memory_encryption,
|
||||
&error_abort);
|
||||
object_class_property_set_description(oc, "memory-encryption",
|
||||
"Set memory encyption object to use", &error_abort);
|
||||
}
|
||||
|
||||
static void machine_class_base_init(ObjectClass *oc, void *data)
|
||||
|
@@ -24,9 +24,9 @@ static void machine_none_init(MachineState *mch)
|
||||
{
|
||||
CPUState *cpu = NULL;
|
||||
|
||||
/* Initialize CPU (if user asked for it) */
|
||||
if (mch->cpu_type) {
|
||||
cpu = cpu_create(mch->cpu_type);
|
||||
/* Initialize CPU (if a model has been specified) */
|
||||
if (mch->cpu_model) {
|
||||
cpu = cpu_init(mch->cpu_model);
|
||||
if (!cpu) {
|
||||
error_report("Unable to initialize CPU");
|
||||
exit(1);
|
||||
|
@@ -292,14 +292,6 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp)
|
||||
pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
|
||||
}
|
||||
|
||||
static void pci_secondary_vga_exit(PCIDevice *dev)
|
||||
{
|
||||
PCIVGAState *d = PCI_VGA(dev);
|
||||
VGACommonState *s = &d->vga;
|
||||
|
||||
graphic_console_close(s->con);
|
||||
}
|
||||
|
||||
static void pci_secondary_vga_init(Object *obj)
|
||||
{
|
||||
/* Expose framebuffer byteorder via QOM */
|
||||
@@ -369,7 +361,6 @@ static void secondary_class_init(ObjectClass *klass, void *data)
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = pci_secondary_vga_realize;
|
||||
k->exit = pci_secondary_vga_exit;
|
||||
k->class_id = PCI_CLASS_DISPLAY_OTHER;
|
||||
dc->props = secondary_pci_properties;
|
||||
dc->reset = pci_secondary_vga_reset;
|
||||
|
@@ -24,7 +24,6 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/dma/i8257.h"
|
||||
|
||||
#define TYPE_I82374 "i82374"
|
||||
#define I82374(obj) OBJECT_CHECK(I82374State, (obj), TYPE_I82374)
|
||||
@@ -124,7 +123,7 @@ static void i82374_realize(DeviceState *dev, Error **errp)
|
||||
portio_list_add(&s->port_list, isa_address_space_io(&s->parent_obj),
|
||||
s->iobase);
|
||||
|
||||
i8257_dma_init(isa_bus_from_device(ISA_DEVICE(dev)), true);
|
||||
DMA_init(isa_bus_from_device(ISA_DEVICE(dev)), 1);
|
||||
memset(s->commands, 0, sizeof(s->commands));
|
||||
}
|
||||
|
||||
|
@@ -24,7 +24,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/dma/i8257.h"
|
||||
#include "hw/isa/i8257.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "trace.h"
|
||||
|
||||
@@ -622,7 +622,7 @@ static void i8257_register_types(void)
|
||||
|
||||
type_init(i8257_register_types)
|
||||
|
||||
void i8257_dma_init(ISABus *bus, bool high_page_enable)
|
||||
void DMA_init(ISABus *bus, int high_page_enable)
|
||||
{
|
||||
ISADevice *isa1, *isa2;
|
||||
DeviceState *d;
|
||||
|
@@ -2,8 +2,8 @@ obj-$(CONFIG_KVM) += kvm/
|
||||
obj-y += multiboot.o
|
||||
obj-y += pc.o pc_piix.o pc_q35.o
|
||||
obj-y += pc_sysfw.o
|
||||
obj-$(CONFIG_VTD) += x86-iommu.o intel_iommu.o
|
||||
obj-$(CONFIG_AMD_IOMMU) += x86-iommu.o amd_iommu.o
|
||||
obj-y += x86-iommu.o intel_iommu.o
|
||||
obj-y += amd_iommu.o
|
||||
obj-$(CONFIG_XEN) += ../xenpv/ xen/
|
||||
obj-$(CONFIG_VMPORT) += vmport.o
|
||||
obj-$(CONFIG_VMMOUSE) += vmmouse.o
|
||||
|
@@ -91,11 +91,17 @@ typedef struct AcpiMcfgInfo {
|
||||
} AcpiMcfgInfo;
|
||||
|
||||
typedef struct AcpiPmInfo {
|
||||
bool force_rev1_fadt;
|
||||
bool s3_disabled;
|
||||
bool s4_disabled;
|
||||
bool pcihp_bridge_en;
|
||||
uint8_t s4_val;
|
||||
AcpiFadtData fadt;
|
||||
uint16_t sci_int;
|
||||
uint8_t acpi_enable_cmd;
|
||||
uint8_t acpi_disable_cmd;
|
||||
uint32_t gpe0_blk;
|
||||
uint32_t gpe0_blk_len;
|
||||
uint32_t io_base;
|
||||
uint16_t cpu_hp_io_base;
|
||||
uint16_t pcihp_io_base;
|
||||
uint16_t pcihp_io_len;
|
||||
@@ -118,59 +124,21 @@ typedef struct AcpiBuildPciBusHotplugState {
|
||||
bool pcihp_bridge_en;
|
||||
} AcpiBuildPciBusHotplugState;
|
||||
|
||||
static void init_common_fadt_data(Object *o, AcpiFadtData *data)
|
||||
{
|
||||
uint32_t io = object_property_get_uint(o, ACPI_PM_PROP_PM_IO_BASE, NULL);
|
||||
AmlAddressSpace as = AML_AS_SYSTEM_IO;
|
||||
AcpiFadtData fadt = {
|
||||
.rev = 3,
|
||||
.flags =
|
||||
(1 << ACPI_FADT_F_WBINVD) |
|
||||
(1 << ACPI_FADT_F_PROC_C1) |
|
||||
(1 << ACPI_FADT_F_SLP_BUTTON) |
|
||||
(1 << ACPI_FADT_F_RTC_S4) |
|
||||
(1 << ACPI_FADT_F_USE_PLATFORM_CLOCK) |
|
||||
/* APIC destination mode ("Flat Logical") has an upper limit of 8
|
||||
* CPUs for more than 8 CPUs, "Clustered Logical" mode has to be
|
||||
* used
|
||||
*/
|
||||
((max_cpus > 8) ? (1 << ACPI_FADT_F_FORCE_APIC_CLUSTER_MODEL) : 0),
|
||||
.int_model = 1 /* Multiple APIC */,
|
||||
.rtc_century = RTC_CENTURY,
|
||||
.plvl2_lat = 0xfff /* C2 state not supported */,
|
||||
.plvl3_lat = 0xfff /* C3 state not supported */,
|
||||
.smi_cmd = ACPI_PORT_SMI_CMD,
|
||||
.sci_int = object_property_get_uint(o, ACPI_PM_PROP_SCI_INT, NULL),
|
||||
.acpi_enable_cmd =
|
||||
object_property_get_uint(o, ACPI_PM_PROP_ACPI_ENABLE_CMD, NULL),
|
||||
.acpi_disable_cmd =
|
||||
object_property_get_uint(o, ACPI_PM_PROP_ACPI_DISABLE_CMD, NULL),
|
||||
.pm1a_evt = { .space_id = as, .bit_width = 4 * 8, .address = io },
|
||||
.pm1a_cnt = { .space_id = as, .bit_width = 2 * 8,
|
||||
.address = io + 0x04 },
|
||||
.pm_tmr = { .space_id = as, .bit_width = 4 * 8, .address = io + 0x08 },
|
||||
.gpe0_blk = { .space_id = as, .bit_width =
|
||||
object_property_get_uint(o, ACPI_PM_PROP_GPE0_BLK_LEN, NULL) * 8,
|
||||
.address = object_property_get_uint(o, ACPI_PM_PROP_GPE0_BLK, NULL)
|
||||
},
|
||||
};
|
||||
*data = fadt;
|
||||
}
|
||||
|
||||
static void acpi_get_pm_info(AcpiPmInfo *pm)
|
||||
{
|
||||
Object *piix = piix4_pm_find();
|
||||
Object *lpc = ich9_lpc_find();
|
||||
Object *obj = piix ? piix : lpc;
|
||||
Object *obj = NULL;
|
||||
QObject *o;
|
||||
|
||||
pm->force_rev1_fadt = false;
|
||||
pm->cpu_hp_io_base = 0;
|
||||
pm->pcihp_io_base = 0;
|
||||
pm->pcihp_io_len = 0;
|
||||
|
||||
init_common_fadt_data(obj, &pm->fadt);
|
||||
if (piix) {
|
||||
/* w2k requires FADT(rev1) or it won't boot, keep PC compatible */
|
||||
pm->fadt.rev = 1;
|
||||
pm->force_rev1_fadt = true;
|
||||
obj = piix;
|
||||
pm->cpu_hp_io_base = PIIX4_CPU_HOTPLUG_IO_BASE;
|
||||
pm->pcihp_io_base =
|
||||
object_property_get_uint(obj, ACPI_PCIHP_IO_BASE_PROP, NULL);
|
||||
@@ -178,42 +146,50 @@ static void acpi_get_pm_info(AcpiPmInfo *pm)
|
||||
object_property_get_uint(obj, ACPI_PCIHP_IO_LEN_PROP, NULL);
|
||||
}
|
||||
if (lpc) {
|
||||
struct AcpiGenericAddress r = { .space_id = AML_AS_SYSTEM_IO,
|
||||
.bit_width = 8, .address = ICH9_RST_CNT_IOPORT };
|
||||
pm->fadt.reset_reg = r;
|
||||
pm->fadt.reset_val = 0xf;
|
||||
pm->fadt.flags |= 1 << ACPI_FADT_F_RESET_REG_SUP;
|
||||
obj = lpc;
|
||||
pm->cpu_hp_io_base = ICH9_CPU_HOTPLUG_IO_BASE;
|
||||
}
|
||||
assert(obj);
|
||||
|
||||
/* The above need not be conditional on machine type because the reset port
|
||||
* happens to be the same on PIIX (pc) and ICH9 (q35). */
|
||||
QEMU_BUILD_BUG_ON(ICH9_RST_CNT_IOPORT != RCR_IOPORT);
|
||||
|
||||
/* Fill in optional s3/s4 related properties */
|
||||
o = object_property_get_qobject(obj, ACPI_PM_PROP_S3_DISABLED, NULL);
|
||||
if (o) {
|
||||
pm->s3_disabled = qnum_get_uint(qobject_to(QNum, o));
|
||||
pm->s3_disabled = qnum_get_uint(qobject_to_qnum(o));
|
||||
} else {
|
||||
pm->s3_disabled = false;
|
||||
}
|
||||
qobject_decref(o);
|
||||
o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_DISABLED, NULL);
|
||||
if (o) {
|
||||
pm->s4_disabled = qnum_get_uint(qobject_to(QNum, o));
|
||||
pm->s4_disabled = qnum_get_uint(qobject_to_qnum(o));
|
||||
} else {
|
||||
pm->s4_disabled = false;
|
||||
}
|
||||
qobject_decref(o);
|
||||
o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_VAL, NULL);
|
||||
if (o) {
|
||||
pm->s4_val = qnum_get_uint(qobject_to(QNum, o));
|
||||
pm->s4_val = qnum_get_uint(qobject_to_qnum(o));
|
||||
} else {
|
||||
pm->s4_val = false;
|
||||
}
|
||||
qobject_decref(o);
|
||||
|
||||
/* Fill in mandatory properties */
|
||||
pm->sci_int = object_property_get_uint(obj, ACPI_PM_PROP_SCI_INT, NULL);
|
||||
|
||||
pm->acpi_enable_cmd = object_property_get_uint(obj,
|
||||
ACPI_PM_PROP_ACPI_ENABLE_CMD,
|
||||
NULL);
|
||||
pm->acpi_disable_cmd =
|
||||
object_property_get_uint(obj,
|
||||
ACPI_PM_PROP_ACPI_DISABLE_CMD,
|
||||
NULL);
|
||||
pm->io_base = object_property_get_uint(obj, ACPI_PM_PROP_PM_IO_BASE,
|
||||
NULL);
|
||||
pm->gpe0_blk = object_property_get_uint(obj, ACPI_PM_PROP_GPE0_BLK,
|
||||
NULL);
|
||||
pm->gpe0_blk_len = object_property_get_uint(obj, ACPI_PM_PROP_GPE0_BLK_LEN,
|
||||
NULL);
|
||||
pm->pcihp_bridge_en =
|
||||
object_property_get_bool(obj, "acpi-pci-hotplug-with-bridge-support",
|
||||
NULL);
|
||||
@@ -281,6 +257,8 @@ static void acpi_get_pci_holes(Range *hole, Range *hole64)
|
||||
NULL));
|
||||
}
|
||||
|
||||
#define ACPI_PORT_SMI_CMD 0x00b2 /* TODO: this is APM_CNT_IOPORT */
|
||||
|
||||
static void acpi_align_size(GArray *blob, unsigned align)
|
||||
{
|
||||
/* Align size to multiple of given size. This reduces the chance
|
||||
@@ -298,6 +276,106 @@ build_facs(GArray *table_data, BIOSLinker *linker)
|
||||
facs->length = cpu_to_le32(sizeof(*facs));
|
||||
}
|
||||
|
||||
/* Load chipset information in FADT */
|
||||
static void fadt_setup(AcpiFadtDescriptorRev3 *fadt, AcpiPmInfo *pm)
|
||||
{
|
||||
fadt->model = 1;
|
||||
fadt->reserved1 = 0;
|
||||
fadt->sci_int = cpu_to_le16(pm->sci_int);
|
||||
fadt->smi_cmd = cpu_to_le32(ACPI_PORT_SMI_CMD);
|
||||
fadt->acpi_enable = pm->acpi_enable_cmd;
|
||||
fadt->acpi_disable = pm->acpi_disable_cmd;
|
||||
/* EVT, CNT, TMR offset matches hw/acpi/core.c */
|
||||
fadt->pm1a_evt_blk = cpu_to_le32(pm->io_base);
|
||||
fadt->pm1a_cnt_blk = cpu_to_le32(pm->io_base + 0x04);
|
||||
fadt->pm_tmr_blk = cpu_to_le32(pm->io_base + 0x08);
|
||||
fadt->gpe0_blk = cpu_to_le32(pm->gpe0_blk);
|
||||
/* EVT, CNT, TMR length matches hw/acpi/core.c */
|
||||
fadt->pm1_evt_len = 4;
|
||||
fadt->pm1_cnt_len = 2;
|
||||
fadt->pm_tmr_len = 4;
|
||||
fadt->gpe0_blk_len = pm->gpe0_blk_len;
|
||||
fadt->plvl2_lat = cpu_to_le16(0xfff); /* C2 state not supported */
|
||||
fadt->plvl3_lat = cpu_to_le16(0xfff); /* C3 state not supported */
|
||||
fadt->flags = cpu_to_le32((1 << ACPI_FADT_F_WBINVD) |
|
||||
(1 << ACPI_FADT_F_PROC_C1) |
|
||||
(1 << ACPI_FADT_F_SLP_BUTTON) |
|
||||
(1 << ACPI_FADT_F_RTC_S4));
|
||||
fadt->flags |= cpu_to_le32(1 << ACPI_FADT_F_USE_PLATFORM_CLOCK);
|
||||
/* APIC destination mode ("Flat Logical") has an upper limit of 8 CPUs
|
||||
* For more than 8 CPUs, "Clustered Logical" mode has to be used
|
||||
*/
|
||||
if (max_cpus > 8) {
|
||||
fadt->flags |= cpu_to_le32(1 << ACPI_FADT_F_FORCE_APIC_CLUSTER_MODEL);
|
||||
}
|
||||
fadt->century = RTC_CENTURY;
|
||||
if (pm->force_rev1_fadt) {
|
||||
return;
|
||||
}
|
||||
|
||||
fadt->flags |= cpu_to_le32(1 << ACPI_FADT_F_RESET_REG_SUP);
|
||||
fadt->reset_value = 0xf;
|
||||
fadt->reset_register.space_id = AML_SYSTEM_IO;
|
||||
fadt->reset_register.bit_width = 8;
|
||||
fadt->reset_register.address = cpu_to_le64(ICH9_RST_CNT_IOPORT);
|
||||
/* The above need not be conditional on machine type because the reset port
|
||||
* happens to be the same on PIIX (pc) and ICH9 (q35). */
|
||||
QEMU_BUILD_BUG_ON(ICH9_RST_CNT_IOPORT != RCR_IOPORT);
|
||||
|
||||
fadt->xpm1a_event_block.space_id = AML_SYSTEM_IO;
|
||||
fadt->xpm1a_event_block.bit_width = fadt->pm1_evt_len * 8;
|
||||
fadt->xpm1a_event_block.address = cpu_to_le64(pm->io_base);
|
||||
|
||||
fadt->xpm1a_control_block.space_id = AML_SYSTEM_IO;
|
||||
fadt->xpm1a_control_block.bit_width = fadt->pm1_cnt_len * 8;
|
||||
fadt->xpm1a_control_block.address = cpu_to_le64(pm->io_base + 0x4);
|
||||
|
||||
fadt->xpm_timer_block.space_id = AML_SYSTEM_IO;
|
||||
fadt->xpm_timer_block.bit_width = fadt->pm_tmr_len * 8;
|
||||
fadt->xpm_timer_block.address = cpu_to_le64(pm->io_base + 0x8);
|
||||
|
||||
fadt->xgpe0_block.space_id = AML_SYSTEM_IO;
|
||||
fadt->xgpe0_block.bit_width = pm->gpe0_blk_len * 8;
|
||||
fadt->xgpe0_block.address = cpu_to_le64(pm->gpe0_blk);
|
||||
}
|
||||
|
||||
|
||||
/* FADT */
|
||||
static void
|
||||
build_fadt(GArray *table_data, BIOSLinker *linker, AcpiPmInfo *pm,
|
||||
unsigned facs_tbl_offset, unsigned dsdt_tbl_offset,
|
||||
const char *oem_id, const char *oem_table_id)
|
||||
{
|
||||
AcpiFadtDescriptorRev3 *fadt = acpi_data_push(table_data, sizeof(*fadt));
|
||||
unsigned fw_ctrl_offset = (char *)&fadt->firmware_ctrl - table_data->data;
|
||||
unsigned dsdt_entry_offset = (char *)&fadt->dsdt - table_data->data;
|
||||
unsigned xdsdt_entry_offset = (char *)&fadt->x_dsdt - table_data->data;
|
||||
int fadt_size = sizeof(*fadt);
|
||||
int rev = 3;
|
||||
|
||||
/* FACS address to be filled by Guest linker */
|
||||
bios_linker_loader_add_pointer(linker,
|
||||
ACPI_BUILD_TABLE_FILE, fw_ctrl_offset, sizeof(fadt->firmware_ctrl),
|
||||
ACPI_BUILD_TABLE_FILE, facs_tbl_offset);
|
||||
|
||||
/* DSDT address to be filled by Guest linker */
|
||||
fadt_setup(fadt, pm);
|
||||
bios_linker_loader_add_pointer(linker,
|
||||
ACPI_BUILD_TABLE_FILE, dsdt_entry_offset, sizeof(fadt->dsdt),
|
||||
ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset);
|
||||
if (pm->force_rev1_fadt) {
|
||||
rev = 1;
|
||||
fadt_size = offsetof(typeof(*fadt), reset_register);
|
||||
} else {
|
||||
bios_linker_loader_add_pointer(linker,
|
||||
ACPI_BUILD_TABLE_FILE, xdsdt_entry_offset, sizeof(fadt->x_dsdt),
|
||||
ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset);
|
||||
}
|
||||
|
||||
build_header(linker, table_data,
|
||||
(void *)fadt, "FACP", fadt_size, rev, oem_id, oem_table_id);
|
||||
}
|
||||
|
||||
void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid,
|
||||
const CPUArchIdList *apic_ids, GArray *entry)
|
||||
{
|
||||
@@ -429,14 +507,14 @@ static void build_append_pcihp_notify_entry(Aml *method, int slot)
|
||||
static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
|
||||
bool pcihp_bridge_en)
|
||||
{
|
||||
Aml *dev, *notify_method = NULL, *method;
|
||||
Aml *dev, *notify_method, *method;
|
||||
QObject *bsel;
|
||||
PCIBus *sec;
|
||||
int i;
|
||||
|
||||
bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL);
|
||||
if (bsel) {
|
||||
uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel));
|
||||
uint64_t bsel_val = qnum_get_uint(qobject_to_qnum(bsel));
|
||||
|
||||
aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val)));
|
||||
notify_method = aml_method("DVNT", 2, AML_NOTSERIALIZED);
|
||||
@@ -546,7 +624,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
|
||||
|
||||
/* If bus supports hotplug select it and notify about local events */
|
||||
if (bsel) {
|
||||
uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel));
|
||||
uint64_t bsel_val = qnum_get_uint(qobject_to_qnum(bsel));
|
||||
|
||||
aml_append(method, aml_store(aml_int(bsel_val), aml_name("BNUM")));
|
||||
aml_append(method,
|
||||
@@ -1975,12 +2053,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
|
||||
aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
|
||||
crs = aml_resource_template();
|
||||
aml_append(crs,
|
||||
aml_io(
|
||||
AML_DECODE16,
|
||||
pm->fadt.gpe0_blk.address,
|
||||
pm->fadt.gpe0_blk.address,
|
||||
1,
|
||||
pm->fadt.gpe0_blk.bit_width / 8)
|
||||
aml_io(AML_DECODE16, pm->gpe0_blk, pm->gpe0_blk, 1, pm->gpe0_blk_len)
|
||||
);
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
aml_append(scope, dev);
|
||||
@@ -2250,55 +2323,6 @@ build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog)
|
||||
#define HOLE_640K_START (640 * 1024)
|
||||
#define HOLE_640K_END (1024 * 1024)
|
||||
|
||||
static void build_srat_hotpluggable_memory(GArray *table_data, uint64_t base,
|
||||
uint64_t len, int default_node)
|
||||
{
|
||||
MemoryDeviceInfoList *info_list = qmp_pc_dimm_device_list();
|
||||
MemoryDeviceInfoList *info;
|
||||
MemoryDeviceInfo *mi;
|
||||
PCDIMMDeviceInfo *di;
|
||||
uint64_t end = base + len, cur, size;
|
||||
bool is_nvdimm;
|
||||
AcpiSratMemoryAffinity *numamem;
|
||||
MemoryAffinityFlags flags;
|
||||
|
||||
for (cur = base, info = info_list;
|
||||
cur < end;
|
||||
cur += size, info = info->next) {
|
||||
numamem = acpi_data_push(table_data, sizeof *numamem);
|
||||
|
||||
if (!info) {
|
||||
build_srat_memory(numamem, cur, end - cur, default_node,
|
||||
MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
|
||||
break;
|
||||
}
|
||||
|
||||
mi = info->value;
|
||||
is_nvdimm = (mi->type == MEMORY_DEVICE_INFO_KIND_NVDIMM);
|
||||
di = !is_nvdimm ? mi->u.dimm.data : mi->u.nvdimm.data;
|
||||
|
||||
if (cur < di->addr) {
|
||||
build_srat_memory(numamem, cur, di->addr - cur, default_node,
|
||||
MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
|
||||
numamem = acpi_data_push(table_data, sizeof *numamem);
|
||||
}
|
||||
|
||||
size = di->size;
|
||||
|
||||
flags = MEM_AFFINITY_ENABLED;
|
||||
if (di->hotpluggable) {
|
||||
flags |= MEM_AFFINITY_HOTPLUGGABLE;
|
||||
}
|
||||
if (is_nvdimm) {
|
||||
flags |= MEM_AFFINITY_NON_VOLATILE;
|
||||
}
|
||||
|
||||
build_srat_memory(numamem, di->addr, size, di->node, flags);
|
||||
}
|
||||
|
||||
qapi_free_MemoryDeviceInfoList(info_list);
|
||||
}
|
||||
|
||||
static void
|
||||
build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
|
||||
{
|
||||
@@ -2410,9 +2434,10 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
|
||||
* providing _PXM method if necessary.
|
||||
*/
|
||||
if (hotplugabble_address_space_size) {
|
||||
build_srat_hotpluggable_memory(table_data, pcms->hotplug_memory.base,
|
||||
hotplugabble_address_space_size,
|
||||
pcms->numa_nodes - 1);
|
||||
numamem = acpi_data_push(table_data, sizeof *numamem);
|
||||
build_srat_memory(numamem, pcms->hotplug_memory.base,
|
||||
hotplugabble_address_space_size, pcms->numa_nodes - 1,
|
||||
MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
|
||||
}
|
||||
|
||||
build_header(linker, table_data,
|
||||
@@ -2613,12 +2638,12 @@ static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg)
|
||||
if (!o) {
|
||||
return false;
|
||||
}
|
||||
mcfg->mcfg_base = qnum_get_uint(qobject_to(QNum, o));
|
||||
mcfg->mcfg_base = qnum_get_uint(qobject_to_qnum(o));
|
||||
qobject_decref(o);
|
||||
|
||||
o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_SIZE, NULL);
|
||||
assert(o);
|
||||
mcfg->mcfg_size = qnum_get_uint(qobject_to(QNum, o));
|
||||
mcfg->mcfg_size = qnum_get_uint(qobject_to_qnum(o));
|
||||
qobject_decref(o);
|
||||
return true;
|
||||
}
|
||||
@@ -2675,10 +2700,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
|
||||
/* ACPI tables pointed to by RSDT */
|
||||
fadt = tables_blob->len;
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
pm.fadt.facs_tbl_offset = &facs;
|
||||
pm.fadt.dsdt_tbl_offset = &dsdt;
|
||||
pm.fadt.xdsdt_tbl_offset = &dsdt;
|
||||
build_fadt(tables_blob, tables->linker, &pm.fadt,
|
||||
build_fadt(tables_blob, tables->linker, &pm, facs, dsdt,
|
||||
slic_oem.id, slic_oem.table_id);
|
||||
aml_len += tables_blob->len - fadt;
|
||||
|
||||
|
86
hw/i386/pc.c
86
hw/i386/pc.c
@@ -26,7 +26,6 @@
|
||||
#include "hw/hw.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/char/serial.h"
|
||||
#include "hw/char/parallel.h"
|
||||
#include "hw/i386/apic.h"
|
||||
#include "hw/i386/topology.h"
|
||||
#include "sysemu/cpus.h"
|
||||
@@ -41,9 +40,7 @@
|
||||
#include "elf.h"
|
||||
#include "multiboot.h"
|
||||
#include "hw/timer/mc146818rtc.h"
|
||||
#include "hw/dma/i8257.h"
|
||||
#include "hw/timer/i8254.h"
|
||||
#include "hw/input/i8042.h"
|
||||
#include "hw/audio/pcspk.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/sysbus.h"
|
||||
@@ -53,6 +50,8 @@
|
||||
#include "sysemu/qtest.h"
|
||||
#include "kvm_i386.h"
|
||||
#include "hw/xen/xen.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "hw/block/block.h"
|
||||
#include "ui/qemu-spice.h"
|
||||
#include "exec/memory.h"
|
||||
#include "exec/address-spaces.h"
|
||||
@@ -1517,44 +1516,6 @@ static const MemoryRegionOps ioportF0_io_ops = {
|
||||
},
|
||||
};
|
||||
|
||||
static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, bool no_vmport)
|
||||
{
|
||||
int i;
|
||||
DriveInfo *fd[MAX_FD];
|
||||
qemu_irq *a20_line;
|
||||
ISADevice *i8042, *port92, *vmmouse;
|
||||
|
||||
serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS);
|
||||
parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS);
|
||||
|
||||
for (i = 0; i < MAX_FD; i++) {
|
||||
fd[i] = drive_get(IF_FLOPPY, 0, i);
|
||||
create_fdctrl |= !!fd[i];
|
||||
}
|
||||
if (create_fdctrl) {
|
||||
fdctrl_init_isa(isa_bus, fd);
|
||||
}
|
||||
|
||||
i8042 = isa_create_simple(isa_bus, "i8042");
|
||||
if (!no_vmport) {
|
||||
vmport_init(isa_bus);
|
||||
vmmouse = isa_try_create(isa_bus, "vmmouse");
|
||||
} else {
|
||||
vmmouse = NULL;
|
||||
}
|
||||
if (vmmouse) {
|
||||
DeviceState *dev = DEVICE(vmmouse);
|
||||
qdev_prop_set_ptr(dev, "ps2_mouse", i8042);
|
||||
qdev_init_nofail(dev);
|
||||
}
|
||||
port92 = isa_create_simple(isa_bus, "port92");
|
||||
|
||||
a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2);
|
||||
i8042_setup_a20_line(i8042, a20_line[0]);
|
||||
port92_init(port92, a20_line[1]);
|
||||
g_free(a20_line);
|
||||
}
|
||||
|
||||
void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
|
||||
ISADevice **rtc_state,
|
||||
bool create_fdctrl,
|
||||
@@ -1563,11 +1524,13 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
|
||||
uint32_t hpet_irqs)
|
||||
{
|
||||
int i;
|
||||
DriveInfo *fd[MAX_FD];
|
||||
DeviceState *hpet = NULL;
|
||||
int pit_isa_irq = 0;
|
||||
qemu_irq pit_alt_irq = NULL;
|
||||
qemu_irq rtc_irq = NULL;
|
||||
ISADevice *pit = NULL;
|
||||
qemu_irq *a20_line;
|
||||
ISADevice *i8042, *port92, *vmmouse, *pit = NULL;
|
||||
MemoryRegion *ioport80_io = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *ioportF0_io = g_new(MemoryRegion, 1);
|
||||
|
||||
@@ -1624,25 +1587,50 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
|
||||
pcspk_init(isa_bus, pit);
|
||||
}
|
||||
|
||||
i8257_dma_init(isa_bus, 0);
|
||||
serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS);
|
||||
parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS);
|
||||
|
||||
/* Super I/O */
|
||||
pc_superio_init(isa_bus, create_fdctrl, no_vmport);
|
||||
a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2);
|
||||
i8042 = isa_create_simple(isa_bus, "i8042");
|
||||
i8042_setup_a20_line(i8042, a20_line[0]);
|
||||
if (!no_vmport) {
|
||||
vmport_init(isa_bus);
|
||||
vmmouse = isa_try_create(isa_bus, "vmmouse");
|
||||
} else {
|
||||
vmmouse = NULL;
|
||||
}
|
||||
if (vmmouse) {
|
||||
DeviceState *dev = DEVICE(vmmouse);
|
||||
qdev_prop_set_ptr(dev, "ps2_mouse", i8042);
|
||||
qdev_init_nofail(dev);
|
||||
}
|
||||
port92 = isa_create_simple(isa_bus, "port92");
|
||||
port92_init(port92, a20_line[1]);
|
||||
g_free(a20_line);
|
||||
|
||||
DMA_init(isa_bus, 0);
|
||||
|
||||
for(i = 0; i < MAX_FD; i++) {
|
||||
fd[i] = drive_get(IF_FLOPPY, 0, i);
|
||||
create_fdctrl |= !!fd[i];
|
||||
}
|
||||
if (create_fdctrl) {
|
||||
fdctrl_init_isa(isa_bus, fd);
|
||||
}
|
||||
}
|
||||
|
||||
void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus)
|
||||
void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
|
||||
{
|
||||
int i;
|
||||
|
||||
rom_set_order_override(FW_CFG_ORDER_OVERRIDE_NIC);
|
||||
for (i = 0; i < nb_nics; i++) {
|
||||
NICInfo *nd = &nd_table[i];
|
||||
const char *model = nd->model ? nd->model : pcmc->default_nic_model;
|
||||
|
||||
if (g_str_equal(model, "ne2k_isa")) {
|
||||
if (!pci_bus || (nd->model && strcmp(nd->model, "ne2k_isa") == 0)) {
|
||||
pc_init_ne2k_isa(isa_bus, nd);
|
||||
} else {
|
||||
pci_nic_init_nofail(nd, pci_bus, model, NULL);
|
||||
pci_nic_init_nofail(nd, pci_bus, "e1000", NULL);
|
||||
}
|
||||
}
|
||||
rom_reset_order_override();
|
||||
|
@@ -40,6 +40,7 @@
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "hw/i2c/smbus.h"
|
||||
#include "hw/xen/xen.h"
|
||||
#include "exec/memory.h"
|
||||
@@ -239,7 +240,7 @@ static void pc_init1(MachineState *machine,
|
||||
pc_basic_device_init(isa_bus, pcms->gsi, &rtc_state, true,
|
||||
(pcms->vmport != ON_OFF_AUTO_ON), pcms->pit, 0x4);
|
||||
|
||||
pc_nic_init(pcmc, isa_bus, pci_bus);
|
||||
pc_nic_init(isa_bus, pci_bus);
|
||||
|
||||
ide_drive_get(hd, ARRAY_SIZE(hd));
|
||||
if (pcmc->pci_enabled) {
|
||||
@@ -416,9 +417,6 @@ static void pc_xen_hvm_init(MachineState *machine)
|
||||
|
||||
static void pc_i440fx_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pcmc->default_nic_model = "e1000";
|
||||
|
||||
m->family = "pc_piix";
|
||||
m->desc = "Standard PC (i440FX + PIIX, 1996)";
|
||||
m->default_machine_opts = "firmware=bios-256k.bin";
|
||||
@@ -1116,7 +1114,6 @@ static void isapc_machine_options(MachineClass *m)
|
||||
pcmc->gigabyte_align = false;
|
||||
pcmc->smbios_legacy_mode = true;
|
||||
pcmc->has_reserved_memory = false;
|
||||
pcmc->default_nic_model = "ne2k_isa";
|
||||
m->default_cpu_type = X86_CPU_TYPE_NAME("486");
|
||||
}
|
||||
|
||||
|
@@ -272,7 +272,7 @@ static void pc_q35_init(MachineState *machine)
|
||||
|
||||
/* the rest devices to which pci devfn is automatically assigned */
|
||||
pc_vga_init(isa_bus, host_bus);
|
||||
pc_nic_init(pcmc, isa_bus, host_bus);
|
||||
pc_nic_init(isa_bus, host_bus);
|
||||
|
||||
if (pcms->acpi_nvdimm_state.is_enabled) {
|
||||
nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io,
|
||||
@@ -294,9 +294,6 @@ static void pc_q35_init(MachineState *machine)
|
||||
|
||||
static void pc_q35_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pcmc->default_nic_model = "e1000e";
|
||||
|
||||
m->family = "pc_q35";
|
||||
m->desc = "Standard PC (Q35 + ICH9, 2009)";
|
||||
m->units_per_default_bus = 1;
|
||||
@@ -319,10 +316,7 @@ DEFINE_Q35_MACHINE(v2_12, "pc-q35-2.12", NULL,
|
||||
|
||||
static void pc_q35_2_11_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
|
||||
pc_q35_2_12_machine_options(m);
|
||||
pcmc->default_nic_model = "e1000";
|
||||
m->alias = NULL;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_11);
|
||||
}
|
||||
|
@@ -113,8 +113,6 @@ static void pc_system_flash_init(MemoryRegion *rom_memory)
|
||||
pflash_t *system_flash;
|
||||
MemoryRegion *flash_mem;
|
||||
char name[64];
|
||||
void *flash_ptr;
|
||||
int ret, flash_size;
|
||||
|
||||
sector_bits = 12;
|
||||
sector_size = 1 << sector_bits;
|
||||
@@ -171,17 +169,6 @@ static void pc_system_flash_init(MemoryRegion *rom_memory)
|
||||
if (unit == 0) {
|
||||
flash_mem = pflash_cfi01_get_memory(system_flash);
|
||||
pc_isa_bios_init(rom_memory, flash_mem, size);
|
||||
|
||||
/* Encrypt the pflash boot ROM */
|
||||
if (kvm_memcrypt_enabled()) {
|
||||
flash_ptr = memory_region_get_ram_ptr(flash_mem);
|
||||
flash_size = memory_region_size(flash_mem);
|
||||
ret = kvm_memcrypt_encrypt_data(flash_ptr, flash_size);
|
||||
if (ret) {
|
||||
error_report("failed to encrypt pflash rom");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -25,7 +25,6 @@
|
||||
#include "hw/hw.h"
|
||||
#include "ui/console.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/input/i8042.h"
|
||||
#include "hw/qdev.h"
|
||||
|
||||
/* debug only vmmouse */
|
||||
|
@@ -25,7 +25,6 @@
|
||||
#include "hw/hw.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/input/i8042.h"
|
||||
#include "sysemu/hw_accel.h"
|
||||
#include "hw/qdev.h"
|
||||
#include "qemu/log.h"
|
||||
|
@@ -18,6 +18,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "sysemu/dma.h"
|
||||
#include "hw/ide/internal.h"
|
||||
#include "hw/ide/ahci_internal.h"
|
||||
|
@@ -26,6 +26,7 @@
|
||||
#include "hw/hw.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/dma.h"
|
||||
|
||||
|
@@ -65,6 +65,7 @@
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "sysemu/dma.h"
|
||||
#include "hw/ide/pci.h"
|
||||
#include "hw/ide/ahci_internal.h"
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "sysemu/dma.h"
|
||||
|
||||
#include "hw/ide/internal.h"
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user