* Asynchronous dump-guest-memory from Peter

* improved logging with -D -daemonize from Dimitris
 * more address_space_* optimization from Gonglei
 * TCG xsave/xrstor thinko fix
 * chardev bugfix and documentation patch
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQEcBAABCAAGBQJWzxnbAAoJEL/70l94x66DlfkIAJyo9kOareVLOnAE8ccayghk
 0SbU1ZR9etgdeH4vZIUKzFzSg86pnqqub/w9yxgNG35PsiXVOnzgYSv6W1qsXdVE
 v32u6c+vWHvHc3cCOFI5+6nURftf2+p/vB2VFXiI23VUbhs22UAjXsUdfbp321X5
 Krme2fxk0kmwPHoKiyek0qiXa8nt0fiuFzU7DN7gTQMoDFaDEqvcULlNJHUFnep+
 M1yQfhSzrD97bafPwmIDU0LejJxrzR6phuzWedugU1tay6y3pD85wQqHdFI7fn/o
 dC4F+vg41Lc/5jKUgRMpe5FmX4VM+DvRdYgZZsp9/SkBM7DuL7crhVWVwvQj82Y=
 =8BvG
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging

* Asynchronous dump-guest-memory from Peter
* improved logging with -D -daemonize from Dimitris
* more address_space_* optimization from Gonglei
* TCG xsave/xrstor thinko fix
* chardev bugfix and documentation patch

# gpg: Signature made Thu 25 Feb 2016 15:12:27 GMT using RSA key ID 78C7AE83
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>"
# gpg:                 aka "Paolo Bonzini <pbonzini@redhat.com>"

* remotes/bonzini/tags/for-upstream:
  target-i386: fix confusion in xcr0 bit position vs. mask
  chardev: Properly initialize ChardevCommon components
  memory: Remove unreachable return statement
  memory: optimize qemu_get_ram_ptr and qemu_ram_ptr_length
  exec: store RAMBlock pointer into memory region
  log: Redirect stderr to logfile if deamonized
  dump-guest-memory: add qmp event DUMP_COMPLETED
  Dump: add hmp command "info dump"
  Dump: add qmp command "query-dump"
  DumpState: adding total_size and written_size fields
  dump-guest-memory: add "detach" support
  dump-guest-memory: disable dump when in INMIGRATE state
  dump-guest-memory: introduce dump_process() helper function.
  dump-guest-memory: add dump_in_progress() helper function
  dump-guest-memory: using static DumpState, add DumpStatus
  dump-guest-memory: add "detach" flag for QMP/HMP interfaces.
  dump-guest-memory: cleanup: removing dump_{error|cleanup}().
  scripts/kvm/kvm_stat: Fix missing right parantheses and ".format(...)"
  qemu-options.hx: Improve documentation of chardev multiplexing mode

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2016-02-25 15:30:57 +00:00
commit 586fc27e6a
31 changed files with 563 additions and 164 deletions

View file

@ -220,6 +220,24 @@ Data:
}, },
"timestamp": { "seconds": 1265044230, "microseconds": 450486 } } "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
DUMP_COMPLETED
--------------
Emitted when the guest has finished one memory dump.
Data:
- "result": DumpQueryResult type described in qapi-schema.json
- "error": Error message when dump failed. This is only a
human-readable string provided when dump failed. It should not be
parsed in any way (json-string, optional)
Example:
{ "event": "DUMP_COMPLETED",
"data": {"result": {"total": 1090650112, "status": "completed",
"completed": 1090650112} } }
GUEST_PANICKED GUEST_PANICKED
-------------- --------------

215
dump.c
View file

@ -25,6 +25,7 @@
#include "sysemu/cpus.h" #include "sysemu/cpus.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "qmp-commands.h" #include "qmp-commands.h"
#include "qapi-event.h"
#include <zlib.h> #include <zlib.h>
#ifdef CONFIG_LZO #ifdef CONFIG_LZO
@ -82,12 +83,6 @@ static int dump_cleanup(DumpState *s)
return 0; return 0;
} }
static void dump_error(DumpState *s, const char *reason, Error **errp)
{
dump_cleanup(s);
error_setg(errp, "%s", reason);
}
static int fd_write_vmcore(const void *buf, size_t size, void *opaque) static int fd_write_vmcore(const void *buf, size_t size, void *opaque)
{ {
DumpState *s = opaque; DumpState *s = opaque;
@ -128,7 +123,7 @@ static void write_elf64_header(DumpState *s, Error **errp)
ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s); ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write elf header", errp); error_setg(errp, "dump: failed to write elf header");
} }
} }
@ -159,7 +154,7 @@ static void write_elf32_header(DumpState *s, Error **errp)
ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s); ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write elf header", errp); error_setg(errp, "dump: failed to write elf header");
} }
} }
@ -182,7 +177,7 @@ static void write_elf64_load(DumpState *s, MemoryMapping *memory_mapping,
ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s); ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write program header table", errp); error_setg(errp, "dump: failed to write program header table");
} }
} }
@ -205,7 +200,7 @@ static void write_elf32_load(DumpState *s, MemoryMapping *memory_mapping,
ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s); ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write program header table", errp); error_setg(errp, "dump: failed to write program header table");
} }
} }
@ -225,7 +220,7 @@ static void write_elf64_note(DumpState *s, Error **errp)
ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s); ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write program header table", errp); error_setg(errp, "dump: failed to write program header table");
} }
} }
@ -245,7 +240,7 @@ static void write_elf64_notes(WriteCoreDumpFunction f, DumpState *s,
id = cpu_index(cpu); id = cpu_index(cpu);
ret = cpu_write_elf64_note(f, cpu, id, s); ret = cpu_write_elf64_note(f, cpu, id, s);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write elf notes", errp); error_setg(errp, "dump: failed to write elf notes");
return; return;
} }
} }
@ -253,7 +248,7 @@ static void write_elf64_notes(WriteCoreDumpFunction f, DumpState *s,
CPU_FOREACH(cpu) { CPU_FOREACH(cpu) {
ret = cpu_write_elf64_qemunote(f, cpu, s); ret = cpu_write_elf64_qemunote(f, cpu, s);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write CPU status", errp); error_setg(errp, "dump: failed to write CPU status");
return; return;
} }
} }
@ -275,7 +270,7 @@ static void write_elf32_note(DumpState *s, Error **errp)
ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s); ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write program header table", errp); error_setg(errp, "dump: failed to write program header table");
} }
} }
@ -290,7 +285,7 @@ static void write_elf32_notes(WriteCoreDumpFunction f, DumpState *s,
id = cpu_index(cpu); id = cpu_index(cpu);
ret = cpu_write_elf32_note(f, cpu, id, s); ret = cpu_write_elf32_note(f, cpu, id, s);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write elf notes", errp); error_setg(errp, "dump: failed to write elf notes");
return; return;
} }
} }
@ -298,7 +293,7 @@ static void write_elf32_notes(WriteCoreDumpFunction f, DumpState *s,
CPU_FOREACH(cpu) { CPU_FOREACH(cpu) {
ret = cpu_write_elf32_qemunote(f, cpu, s); ret = cpu_write_elf32_qemunote(f, cpu, s);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write CPU status", errp); error_setg(errp, "dump: failed to write CPU status");
return; return;
} }
} }
@ -326,7 +321,7 @@ static void write_elf_section(DumpState *s, int type, Error **errp)
ret = fd_write_vmcore(&shdr, shdr_size, s); ret = fd_write_vmcore(&shdr, shdr_size, s);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write section header table", errp); error_setg(errp, "dump: failed to write section header table");
} }
} }
@ -336,7 +331,9 @@ static void write_data(DumpState *s, void *buf, int length, Error **errp)
ret = fd_write_vmcore(buf, length, s); ret = fd_write_vmcore(buf, length, s);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to save memory", errp); error_setg(errp, "dump: failed to save memory");
} else {
s->written_size += length;
} }
} }
@ -568,11 +565,6 @@ static void dump_begin(DumpState *s, Error **errp)
} }
} }
static void dump_completed(DumpState *s)
{
dump_cleanup(s);
}
static int get_next_block(DumpState *s, GuestPhysBlock *block) static int get_next_block(DumpState *s, GuestPhysBlock *block)
{ {
while (1) { while (1) {
@ -624,8 +616,6 @@ static void dump_iterate(DumpState *s, Error **errp)
} }
} while (!get_next_block(s, block)); } while (!get_next_block(s, block));
dump_completed(s);
} }
static void create_vmcore(DumpState *s, Error **errp) static void create_vmcore(DumpState *s, Error **errp)
@ -765,7 +755,7 @@ static void create_header32(DumpState *s, Error **errp)
dh->status = cpu_to_dump32(s, status); dh->status = cpu_to_dump32(s, status);
if (write_buffer(s->fd, 0, dh, size) < 0) { if (write_buffer(s->fd, 0, dh, size) < 0) {
dump_error(s, "dump: failed to write disk dump header", errp); error_setg(errp, "dump: failed to write disk dump header");
goto out; goto out;
} }
@ -784,7 +774,7 @@ static void create_header32(DumpState *s, Error **errp)
if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS * if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS *
block_size, kh, size) < 0) { block_size, kh, size) < 0) {
dump_error(s, "dump: failed to write kdump sub header", errp); error_setg(errp, "dump: failed to write kdump sub header");
goto out; goto out;
} }
@ -800,7 +790,7 @@ static void create_header32(DumpState *s, Error **errp)
} }
if (write_buffer(s->fd, offset_note, s->note_buf, if (write_buffer(s->fd, offset_note, s->note_buf,
s->note_size) < 0) { s->note_size) < 0) {
dump_error(s, "dump: failed to write notes", errp); error_setg(errp, "dump: failed to write notes");
goto out; goto out;
} }
@ -865,7 +855,7 @@ static void create_header64(DumpState *s, Error **errp)
dh->status = cpu_to_dump32(s, status); dh->status = cpu_to_dump32(s, status);
if (write_buffer(s->fd, 0, dh, size) < 0) { if (write_buffer(s->fd, 0, dh, size) < 0) {
dump_error(s, "dump: failed to write disk dump header", errp); error_setg(errp, "dump: failed to write disk dump header");
goto out; goto out;
} }
@ -884,7 +874,7 @@ static void create_header64(DumpState *s, Error **errp)
if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS * if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS *
block_size, kh, size) < 0) { block_size, kh, size) < 0) {
dump_error(s, "dump: failed to write kdump sub header", errp); error_setg(errp, "dump: failed to write kdump sub header");
goto out; goto out;
} }
@ -901,7 +891,7 @@ static void create_header64(DumpState *s, Error **errp)
if (write_buffer(s->fd, offset_note, s->note_buf, if (write_buffer(s->fd, offset_note, s->note_buf,
s->note_size) < 0) { s->note_size) < 0) {
dump_error(s, "dump: failed to write notes", errp); error_setg(errp, "dump: failed to write notes");
goto out; goto out;
} }
@ -1087,7 +1077,7 @@ static void write_dump_bitmap(DumpState *s, Error **errp)
while (get_next_page(&block_iter, &pfn, NULL, s)) { while (get_next_page(&block_iter, &pfn, NULL, s)) {
ret = set_dump_bitmap(last_pfn, pfn, true, dump_bitmap_buf, s); ret = set_dump_bitmap(last_pfn, pfn, true, dump_bitmap_buf, s);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to set dump_bitmap", errp); error_setg(errp, "dump: failed to set dump_bitmap");
goto out; goto out;
} }
@ -1104,7 +1094,7 @@ static void write_dump_bitmap(DumpState *s, Error **errp)
ret = set_dump_bitmap(last_pfn, last_pfn + bits_per_buf, false, ret = set_dump_bitmap(last_pfn, last_pfn + bits_per_buf, false,
dump_bitmap_buf, s); dump_bitmap_buf, s);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to sync dump_bitmap", errp); error_setg(errp, "dump: failed to sync dump_bitmap");
goto out; goto out;
} }
} }
@ -1237,7 +1227,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
ret = write_cache(&page_data, buf, s->dump_info.page_size, false); ret = write_cache(&page_data, buf, s->dump_info.page_size, false);
g_free(buf); g_free(buf);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write page data (zero page)", errp); error_setg(errp, "dump: failed to write page data (zero page)");
goto out; goto out;
} }
@ -1253,7 +1243,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
ret = write_cache(&page_desc, &pd_zero, sizeof(PageDescriptor), ret = write_cache(&page_desc, &pd_zero, sizeof(PageDescriptor),
false); false);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write page desc", errp); error_setg(errp, "dump: failed to write page desc");
goto out; goto out;
} }
} else { } else {
@ -1278,7 +1268,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
ret = write_cache(&page_data, buf_out, size_out, false); ret = write_cache(&page_data, buf_out, size_out, false);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write page data", errp); error_setg(errp, "dump: failed to write page data");
goto out; goto out;
} }
#ifdef CONFIG_LZO #ifdef CONFIG_LZO
@ -1291,7 +1281,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
ret = write_cache(&page_data, buf_out, size_out, false); ret = write_cache(&page_data, buf_out, size_out, false);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write page data", errp); error_setg(errp, "dump: failed to write page data");
goto out; goto out;
} }
#endif #endif
@ -1305,7 +1295,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
ret = write_cache(&page_data, buf_out, size_out, false); ret = write_cache(&page_data, buf_out, size_out, false);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write page data", errp); error_setg(errp, "dump: failed to write page data");
goto out; goto out;
} }
#endif #endif
@ -1321,7 +1311,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
ret = write_cache(&page_data, buf, ret = write_cache(&page_data, buf,
s->dump_info.page_size, false); s->dump_info.page_size, false);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write page data", errp); error_setg(errp, "dump: failed to write page data");
goto out; goto out;
} }
} }
@ -1333,20 +1323,21 @@ static void write_dump_pages(DumpState *s, Error **errp)
ret = write_cache(&page_desc, &pd, sizeof(PageDescriptor), false); ret = write_cache(&page_desc, &pd, sizeof(PageDescriptor), false);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write page desc", errp); error_setg(errp, "dump: failed to write page desc");
goto out; goto out;
} }
} }
s->written_size += s->dump_info.page_size;
} }
ret = write_cache(&page_desc, NULL, 0, true); ret = write_cache(&page_desc, NULL, 0, true);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to sync cache for page_desc", errp); error_setg(errp, "dump: failed to sync cache for page_desc");
goto out; goto out;
} }
ret = write_cache(&page_data, NULL, 0, true); ret = write_cache(&page_data, NULL, 0, true);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to sync cache for page_data", errp); error_setg(errp, "dump: failed to sync cache for page_data");
goto out; goto out;
} }
@ -1390,7 +1381,7 @@ static void create_kdump_vmcore(DumpState *s, Error **errp)
ret = write_start_flat_header(s->fd); ret = write_start_flat_header(s->fd);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write start flat header", errp); error_setg(errp, "dump: failed to write start flat header");
return; return;
} }
@ -1414,11 +1405,9 @@ static void create_kdump_vmcore(DumpState *s, Error **errp)
ret = write_end_flat_header(s->fd); ret = write_end_flat_header(s->fd);
if (ret < 0) { if (ret < 0) {
dump_error(s, "dump: failed to write end flat header", errp); error_setg(errp, "dump: failed to write end flat header");
return; return;
} }
dump_completed(s);
} }
static ram_addr_t get_start_block(DumpState *s) static ram_addr_t get_start_block(DumpState *s)
@ -1457,6 +1446,44 @@ static void get_max_mapnr(DumpState *s)
s->max_mapnr = dump_paddr_to_pfn(s, last_block->target_end); s->max_mapnr = dump_paddr_to_pfn(s, last_block->target_end);
} }
static DumpState dump_state_global = { .status = DUMP_STATUS_NONE };
static void dump_state_prepare(DumpState *s)
{
/* zero the struct, setting status to active */
*s = (DumpState) { .status = DUMP_STATUS_ACTIVE };
}
bool dump_in_progress(void)
{
DumpState *state = &dump_state_global;
return (atomic_read(&state->status) == DUMP_STATUS_ACTIVE);
}
/* calculate total size of memory to be dumped (taking filter into
* acoount.) */
static int64_t dump_calculate_size(DumpState *s)
{
GuestPhysBlock *block;
int64_t size = 0, total = 0, left = 0, right = 0;
QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) {
if (s->has_filter) {
/* calculate the overlapped region. */
left = MAX(s->begin, block->target_start);
right = MIN(s->begin + s->length, block->target_end);
size = right - left;
size = size > 0 ? size : 0;
} else {
/* count the whole region in */
size = (block->target_end - block->target_start);
}
total += size;
}
return total;
}
static void dump_init(DumpState *s, int fd, bool has_format, static void dump_init(DumpState *s, int fd, bool has_format,
DumpGuestMemoryFormat format, bool paging, bool has_filter, DumpGuestMemoryFormat format, bool paging, bool has_filter,
int64_t begin, int64_t length, Error **errp) int64_t begin, int64_t length, Error **errp)
@ -1466,6 +1493,10 @@ static void dump_init(DumpState *s, int fd, bool has_format,
Error *err = NULL; Error *err = NULL;
int ret; int ret;
s->has_format = has_format;
s->format = format;
s->written_size = 0;
/* kdump-compressed is conflict with paging and filter */ /* kdump-compressed is conflict with paging and filter */
if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) { if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
assert(!paging && !has_filter); assert(!paging && !has_filter);
@ -1496,6 +1527,10 @@ static void dump_init(DumpState *s, int fd, bool has_format,
guest_phys_blocks_init(&s->guest_phys_blocks); guest_phys_blocks_init(&s->guest_phys_blocks);
guest_phys_blocks_append(&s->guest_phys_blocks); guest_phys_blocks_append(&s->guest_phys_blocks);
s->total_size = dump_calculate_size(s);
#ifdef DEBUG_DUMP_GUEST_MEMORY
fprintf(stderr, "DUMP: total memory to dump: %lu\n", s->total_size);
#endif
s->start = get_start_block(s); s->start = get_start_block(s);
if (s->start == -1) { if (s->start == -1) {
@ -1624,8 +1659,60 @@ cleanup:
dump_cleanup(s); dump_cleanup(s);
} }
void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin, /* this operation might be time consuming. */
int64_t begin, bool has_length, static void dump_process(DumpState *s, Error **errp)
{
Error *local_err = NULL;
DumpQueryResult *result = NULL;
if (s->has_format && s->format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
create_kdump_vmcore(s, &local_err);
} else {
create_vmcore(s, &local_err);
}
/* make sure status is written after written_size updates */
smp_wmb();
atomic_set(&s->status,
(local_err ? DUMP_STATUS_FAILED : DUMP_STATUS_COMPLETED));
/* send DUMP_COMPLETED message (unconditionally) */
result = qmp_query_dump(NULL);
/* should never fail */
assert(result);
qapi_event_send_dump_completed(result, !!local_err, (local_err ? \
error_get_pretty(local_err) : NULL),
&error_abort);
qapi_free_DumpQueryResult(result);
error_propagate(errp, local_err);
dump_cleanup(s);
}
static void *dump_thread(void *data)
{
Error *err = NULL;
DumpState *s = (DumpState *)data;
dump_process(s, &err);
error_free(err);
return NULL;
}
DumpQueryResult *qmp_query_dump(Error **errp)
{
DumpQueryResult *result = g_new(DumpQueryResult, 1);
DumpState *state = &dump_state_global;
result->status = atomic_read(&state->status);
/* make sure we are reading status and written_size in order */
smp_rmb();
result->completed = state->written_size;
result->total = state->total_size;
return result;
}
void qmp_dump_guest_memory(bool paging, const char *file,
bool has_detach, bool detach,
bool has_begin, int64_t begin, bool has_length,
int64_t length, bool has_format, int64_t length, bool has_format,
DumpGuestMemoryFormat format, Error **errp) DumpGuestMemoryFormat format, Error **errp)
{ {
@ -1633,6 +1720,19 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
int fd = -1; int fd = -1;
DumpState *s; DumpState *s;
Error *local_err = NULL; Error *local_err = NULL;
bool detach_p = false;
if (runstate_check(RUN_STATE_INMIGRATE)) {
error_setg(errp, "Dump not allowed during incoming migration.");
return;
}
/* if there is a dump in background, we should wait until the dump
* finished */
if (dump_in_progress()) {
error_setg(errp, "There is a dump in process, please wait.");
return;
}
/* /*
* kdump-compressed format need the whole memory dumped, so paging or * kdump-compressed format need the whole memory dumped, so paging or
@ -1652,6 +1752,9 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
error_setg(errp, QERR_MISSING_PARAMETER, "begin"); error_setg(errp, QERR_MISSING_PARAMETER, "begin");
return; return;
} }
if (has_detach) {
detach_p = detach;
}
/* check whether lzo/snappy is supported */ /* check whether lzo/snappy is supported */
#ifndef CONFIG_LZO #ifndef CONFIG_LZO
@ -1690,23 +1793,25 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
return; return;
} }
s = g_malloc0(sizeof(DumpState)); s = &dump_state_global;
dump_state_prepare(s);
dump_init(s, fd, has_format, format, paging, has_begin, dump_init(s, fd, has_format, format, paging, has_begin,
begin, length, &local_err); begin, length, &local_err);
if (local_err) { if (local_err) {
g_free(s);
error_propagate(errp, local_err); error_propagate(errp, local_err);
atomic_set(&s->status, DUMP_STATUS_FAILED);
return; return;
} }
if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) { if (detach_p) {
create_kdump_vmcore(s, errp); /* detached dump */
qemu_thread_create(&s->dump_thread, "dump_thread", dump_thread,
s, QEMU_THREAD_DETACHED);
} else { } else {
create_vmcore(s, errp); /* sync dump */
dump_process(s, errp);
} }
g_free(s);
} }
DumpGuestMemoryCapability *qmp_query_dump_guest_memory_capability(Error **errp) DumpGuestMemoryCapability *qmp_query_dump_guest_memory_capability(Error **errp)

48
exec.c
View file

@ -1717,6 +1717,8 @@ ram_addr_t qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size,
error_propagate(errp, local_err); error_propagate(errp, local_err);
return -1; return -1;
} }
mr->ram_block = new_block;
return addr; return addr;
} }
@ -1866,9 +1868,13 @@ void *qemu_get_ram_block_host_ptr(ram_addr_t addr)
* *
* Called within RCU critical section. * Called within RCU critical section.
*/ */
void *qemu_get_ram_ptr(ram_addr_t addr) void *qemu_get_ram_ptr(RAMBlock *ram_block, ram_addr_t addr)
{ {
RAMBlock *block = qemu_get_ram_block(addr); RAMBlock *block = ram_block;
if (block == NULL) {
block = qemu_get_ram_block(addr);
}
if (xen_enabled() && block->host == NULL) { if (xen_enabled() && block->host == NULL) {
/* We need to check if the requested address is in the RAM /* We need to check if the requested address is in the RAM
@ -1889,15 +1895,18 @@ void *qemu_get_ram_ptr(ram_addr_t addr)
* *
* Called within RCU critical section. * Called within RCU critical section.
*/ */
static void *qemu_ram_ptr_length(ram_addr_t addr, hwaddr *size) static void *qemu_ram_ptr_length(RAMBlock *ram_block, ram_addr_t addr,
hwaddr *size)
{ {
RAMBlock *block; RAMBlock *block = ram_block;
ram_addr_t offset_inside_block; ram_addr_t offset_inside_block;
if (*size == 0) { if (*size == 0) {
return NULL; return NULL;
} }
block = qemu_get_ram_block(addr); if (block == NULL) {
block = qemu_get_ram_block(addr);
}
offset_inside_block = addr - block->offset; offset_inside_block = addr - block->offset;
*size = MIN(*size, block->max_length - offset_inside_block); *size = MIN(*size, block->max_length - offset_inside_block);
@ -2025,13 +2034,13 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr,
} }
switch (size) { switch (size) {
case 1: case 1:
stb_p(qemu_get_ram_ptr(ram_addr), val); stb_p(qemu_get_ram_ptr(NULL, ram_addr), val);
break; break;
case 2: case 2:
stw_p(qemu_get_ram_ptr(ram_addr), val); stw_p(qemu_get_ram_ptr(NULL, ram_addr), val);
break; break;
case 4: case 4:
stl_p(qemu_get_ram_ptr(ram_addr), val); stl_p(qemu_get_ram_ptr(NULL, ram_addr), val);
break; break;
default: default:
abort(); abort();
@ -2607,7 +2616,7 @@ static MemTxResult address_space_write_continue(AddressSpace *as, hwaddr addr,
} else { } else {
addr1 += memory_region_get_ram_addr(mr); addr1 += memory_region_get_ram_addr(mr);
/* RAM case */ /* RAM case */
ptr = qemu_get_ram_ptr(addr1); ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
memcpy(ptr, buf, l); memcpy(ptr, buf, l);
invalidate_and_set_dirty(mr, addr1, l); invalidate_and_set_dirty(mr, addr1, l);
} }
@ -2698,7 +2707,7 @@ MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr,
} }
} else { } else {
/* RAM case */ /* RAM case */
ptr = qemu_get_ram_ptr(mr->ram_addr + addr1); ptr = qemu_get_ram_ptr(mr->ram_block, mr->ram_addr + addr1);
memcpy(buf, ptr, l); memcpy(buf, ptr, l);
} }
@ -2783,7 +2792,7 @@ static inline void cpu_physical_memory_write_rom_internal(AddressSpace *as,
} else { } else {
addr1 += memory_region_get_ram_addr(mr); addr1 += memory_region_get_ram_addr(mr);
/* ROM/RAM case */ /* ROM/RAM case */
ptr = qemu_get_ram_ptr(addr1); ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
switch (type) { switch (type) {
case WRITE_DATA: case WRITE_DATA:
memcpy(ptr, buf, l); memcpy(ptr, buf, l);
@ -2995,7 +3004,7 @@ void *address_space_map(AddressSpace *as,
memory_region_ref(mr); memory_region_ref(mr);
*plen = done; *plen = done;
ptr = qemu_ram_ptr_length(raddr + base, plen); ptr = qemu_ram_ptr_length(mr->ram_block, raddr + base, plen);
rcu_read_unlock(); rcu_read_unlock();
return ptr; return ptr;
@ -3079,7 +3088,8 @@ static inline uint32_t address_space_ldl_internal(AddressSpace *as, hwaddr addr,
#endif #endif
} else { } else {
/* RAM case */ /* RAM case */
ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(mr) ptr = qemu_get_ram_ptr(mr->ram_block,
(memory_region_get_ram_addr(mr)
& TARGET_PAGE_MASK) & TARGET_PAGE_MASK)
+ addr1); + addr1);
switch (endian) { switch (endian) {
@ -3174,7 +3184,8 @@ static inline uint64_t address_space_ldq_internal(AddressSpace *as, hwaddr addr,
#endif #endif
} else { } else {
/* RAM case */ /* RAM case */
ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(mr) ptr = qemu_get_ram_ptr(mr->ram_block,
(memory_region_get_ram_addr(mr)
& TARGET_PAGE_MASK) & TARGET_PAGE_MASK)
+ addr1); + addr1);
switch (endian) { switch (endian) {
@ -3289,7 +3300,8 @@ static inline uint32_t address_space_lduw_internal(AddressSpace *as,
#endif #endif
} else { } else {
/* RAM case */ /* RAM case */
ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(mr) ptr = qemu_get_ram_ptr(mr->ram_block,
(memory_region_get_ram_addr(mr)
& TARGET_PAGE_MASK) & TARGET_PAGE_MASK)
+ addr1); + addr1);
switch (endian) { switch (endian) {
@ -3374,7 +3386,7 @@ void address_space_stl_notdirty(AddressSpace *as, hwaddr addr, uint32_t val,
r = memory_region_dispatch_write(mr, addr1, val, 4, attrs); r = memory_region_dispatch_write(mr, addr1, val, 4, attrs);
} else { } else {
addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK; addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK;
ptr = qemu_get_ram_ptr(addr1); ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
stl_p(ptr, val); stl_p(ptr, val);
dirty_log_mask = memory_region_get_dirty_log_mask(mr); dirty_log_mask = memory_region_get_dirty_log_mask(mr);
@ -3429,7 +3441,7 @@ static inline void address_space_stl_internal(AddressSpace *as,
} else { } else {
/* RAM case */ /* RAM case */
addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK; addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK;
ptr = qemu_get_ram_ptr(addr1); ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
switch (endian) { switch (endian) {
case DEVICE_LITTLE_ENDIAN: case DEVICE_LITTLE_ENDIAN:
stl_le_p(ptr, val); stl_le_p(ptr, val);
@ -3539,7 +3551,7 @@ static inline void address_space_stw_internal(AddressSpace *as,
} else { } else {
/* RAM case */ /* RAM case */
addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK; addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK;
ptr = qemu_get_ram_ptr(addr1); ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
switch (endian) { switch (endian) {
case DEVICE_LITTLE_ENDIAN: case DEVICE_LITTLE_ENDIAN:
stw_le_p(ptr, val); stw_le_p(ptr, val);

View file

@ -784,6 +784,20 @@ STEXI
@item info skeys @var{address} @item info skeys @var{address}
@findex skeys @findex skeys
Display the value of a storage key (s390 only) Display the value of a storage key (s390 only)
ETEXI
{
.name = "dump",
.args_type = "",
.params = "",
.help = "Display the latest dump status",
.mhandler.cmd = hmp_info_dump,
},
STEXI
@item info dump
@findex dump
Display the latest dump status.
ETEXI ETEXI
STEXI STEXI

View file

@ -1056,10 +1056,11 @@ ETEXI
{ {
.name = "dump-guest-memory", .name = "dump-guest-memory",
.args_type = "paging:-p,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:i?,length:i?", .args_type = "paging:-p,detach:-d,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:i?,length:i?",
.params = "[-p] [-z|-l|-s] filename [begin length]", .params = "[-p] [-d] [-z|-l|-s] filename [begin length]",
.help = "dump guest memory into file 'filename'.\n\t\t\t" .help = "dump guest memory into file 'filename'.\n\t\t\t"
"-p: do paging to get guest's memory mapping.\n\t\t\t" "-p: do paging to get guest's memory mapping.\n\t\t\t"
"-d: return immediately (do not wait for completion).\n\t\t\t"
"-z: dump in kdump-compressed format, with zlib compression.\n\t\t\t" "-z: dump in kdump-compressed format, with zlib compression.\n\t\t\t"
"-l: dump in kdump-compressed format, with lzo compression.\n\t\t\t" "-l: dump in kdump-compressed format, with lzo compression.\n\t\t\t"
"-s: dump in kdump-compressed format, with snappy compression.\n\t\t\t" "-s: dump in kdump-compressed format, with snappy compression.\n\t\t\t"

26
hmp.c
View file

@ -1599,8 +1599,10 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
const char *file = qdict_get_str(qdict, "filename"); const char *file = qdict_get_str(qdict, "filename");
bool has_begin = qdict_haskey(qdict, "begin"); bool has_begin = qdict_haskey(qdict, "begin");
bool has_length = qdict_haskey(qdict, "length"); bool has_length = qdict_haskey(qdict, "length");
bool has_detach = qdict_haskey(qdict, "detach");
int64_t begin = 0; int64_t begin = 0;
int64_t length = 0; int64_t length = 0;
bool detach = false;
enum DumpGuestMemoryFormat dump_format = DUMP_GUEST_MEMORY_FORMAT_ELF; enum DumpGuestMemoryFormat dump_format = DUMP_GUEST_MEMORY_FORMAT_ELF;
char *prot; char *prot;
@ -1628,11 +1630,14 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
if (has_length) { if (has_length) {
length = qdict_get_int(qdict, "length"); length = qdict_get_int(qdict, "length");
} }
if (has_detach) {
detach = qdict_get_bool(qdict, "detach");
}
prot = g_strconcat("file:", file, NULL); prot = g_strconcat("file:", file, NULL);
qmp_dump_guest_memory(paging, prot, has_begin, begin, has_length, length, qmp_dump_guest_memory(paging, prot, true, detach, has_begin, begin,
true, dump_format, &err); has_length, length, true, dump_format, &err);
hmp_handle_error(mon, &err); hmp_handle_error(mon, &err);
g_free(prot); g_free(prot);
} }
@ -2358,3 +2363,20 @@ void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict)
qapi_free_RockerOfDpaGroupList(list); qapi_free_RockerOfDpaGroupList(list);
} }
void hmp_info_dump(Monitor *mon, const QDict *qdict)
{
DumpQueryResult *result = qmp_query_dump(NULL);
assert(result && result->status < DUMP_STATUS__MAX);
monitor_printf(mon, "Status: %s\n", DumpStatus_lookup[result->status]);
if (result->status == DUMP_STATUS_ACTIVE) {
float percent = 0;
assert(result->total != 0);
percent = 100.0 * result->completed / result->total;
monitor_printf(mon, "Finished: %.2f %%\n", percent);
}
qapi_free_DumpQueryResult(result);
}

1
hmp.h
View file

@ -131,5 +131,6 @@ void hmp_rocker(Monitor *mon, const QDict *qdict);
void hmp_rocker_ports(Monitor *mon, const QDict *qdict); void hmp_rocker_ports(Monitor *mon, const QDict *qdict);
void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict); void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict);
void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict); void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict);
void hmp_info_dump(Monitor *mon, const QDict *qdict);
#endif #endif

View file

@ -31,6 +31,7 @@
#include "qemu/notify.h" #include "qemu/notify.h"
#include "qom/object.h" #include "qom/object.h"
#include "qemu/rcu.h" #include "qemu/rcu.h"
#include "qemu/typedefs.h"
#define MAX_PHYS_ADDR_SPACE_BITS 62 #define MAX_PHYS_ADDR_SPACE_BITS 62
#define MAX_PHYS_ADDR (((hwaddr)1 << MAX_PHYS_ADDR_SPACE_BITS) - 1) #define MAX_PHYS_ADDR (((hwaddr)1 << MAX_PHYS_ADDR_SPACE_BITS) - 1)
@ -169,6 +170,7 @@ struct MemoryRegion {
bool global_locking; bool global_locking;
uint8_t dirty_log_mask; uint8_t dirty_log_mask;
ram_addr_t ram_addr; ram_addr_t ram_addr;
RAMBlock *ram_block;
Object *owner; Object *owner;
const MemoryRegionIOMMUOps *iommu_ops; const MemoryRegionIOMMUOps *iommu_ops;
@ -1386,7 +1388,7 @@ MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr,
MemoryRegion *mr); MemoryRegion *mr);
MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr, MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,
MemTxAttrs attrs, uint8_t *buf, int len); MemTxAttrs attrs, uint8_t *buf, int len);
void *qemu_get_ram_ptr(ram_addr_t addr); void *qemu_get_ram_ptr(RAMBlock *ram_block, ram_addr_t addr);
static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write)
{ {
@ -1395,8 +1397,6 @@ static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write)
} else { } else {
return memory_region_is_ram(mr) || memory_region_is_romd(mr); return memory_region_is_ram(mr) || memory_region_is_romd(mr);
} }
return false;
} }
/** /**
@ -1427,7 +1427,7 @@ MemTxResult address_space_read(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
mr = address_space_translate(as, addr, &addr1, &l, false); mr = address_space_translate(as, addr, &addr1, &l, false);
if (len == l && memory_access_is_direct(mr, false)) { if (len == l && memory_access_is_direct(mr, false)) {
addr1 += memory_region_get_ram_addr(mr); addr1 += memory_region_get_ram_addr(mr);
ptr = qemu_get_ram_ptr(addr1); ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
memcpy(buf, ptr, len); memcpy(buf, ptr, len);
} else { } else {
result = address_space_read_continue(as, addr, attrs, buf, len, result = address_space_read_continue(as, addr, attrs, buf, len,

View file

@ -493,4 +493,8 @@ int parse_debug_env(const char *name, int max, int initial);
const char *qemu_ether_ntoa(const MACAddr *mac); const char *qemu_ether_ntoa(const MACAddr *mac);
void page_size_init(void); void page_size_init(void);
/* returns non-zero if dump is in progress, otherwise zero is
* returned. */
bool dump_in_progress(void);
#endif #endif

View file

@ -90,12 +90,6 @@ static inline void qemu_log_close(void)
} }
} }
/* Set up a new log file */
static inline void qemu_log_set_file(FILE *f)
{
qemu_logfile = f;
}
/* define log items */ /* define log items */
typedef struct QEMULogItem { typedef struct QEMULogItem {
int mask; int mask;

View file

@ -114,6 +114,16 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
void (*init)(struct CharDriverState *s), void (*init)(struct CharDriverState *s),
Error **errp); Error **errp);
/**
* @qemu_chr_parse_common:
*
* Parse the common options available to all character backends.
*
* @opts the options that still need parsing
* @backend a new backend
*/
void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend);
/** /**
* @qemu_chr_new: * @qemu_chr_new:
* *

View file

@ -38,6 +38,7 @@
#include "sysemu/dump-arch.h" #include "sysemu/dump-arch.h"
#include "sysemu/memory_mapping.h" #include "sysemu/memory_mapping.h"
#include "qapi-types.h"
typedef struct QEMU_PACKED MakedumpfileHeader { typedef struct QEMU_PACKED MakedumpfileHeader {
char signature[16]; /* = "makedumpfile" */ char signature[16]; /* = "makedumpfile" */
@ -176,6 +177,20 @@ typedef struct DumpState {
off_t offset_page; /* offset of page part in vmcore */ off_t offset_page; /* offset of page part in vmcore */
size_t num_dumpable; /* number of page that can be dumped */ size_t num_dumpable; /* number of page that can be dumped */
uint32_t flag_compress; /* indicate the compression format */ uint32_t flag_compress; /* indicate the compression format */
DumpStatus status; /* current dump status */
bool has_format; /* whether format is provided */
DumpGuestMemoryFormat format; /* valid only if has_format == true */
QemuThread dump_thread; /* thread for detached dump */
int64_t total_size; /* total memory size (in bytes) to
* be dumped. When filter is
* enabled, this will only count
* those to be written. */
int64_t written_size; /* written memory size (in bytes),
* this could be used to calculate
* how much work we have
* finished. */
} DumpState; } DumpState;
uint16_t cpu_to_dump16(DumpState *s, uint16_t val); uint16_t cpu_to_dump16(DumpState *s, uint16_t val);

View file

@ -16,6 +16,7 @@
#include "qemu/queue.h" #include "qemu/queue.h"
#include "qemu/typedefs.h" #include "qemu/typedefs.h"
#include "exec/memory.h"
typedef struct GuestPhysBlock { typedef struct GuestPhysBlock {
/* visible to guest, reflects PCI hole, etc */ /* visible to guest, reflects PCI hole, etc */
@ -27,6 +28,9 @@ typedef struct GuestPhysBlock {
/* points into host memory */ /* points into host memory */
uint8_t *host_addr; uint8_t *host_addr;
/* points to the MemoryRegion that this block belongs to */
MemoryRegion *mr;
QTAILQ_ENTRY(GuestPhysBlock) next; QTAILQ_ENTRY(GuestPhysBlock) next;
} GuestPhysBlock; } GuestPhysBlock;

View file

@ -912,6 +912,7 @@ void memory_region_init(MemoryRegion *mr,
} }
mr->name = g_strdup(name); mr->name = g_strdup(name);
mr->owner = owner; mr->owner = owner;
mr->ram_block = NULL;
if (name) { if (name) {
char *escaped_name = memory_region_escape_name(name); char *escaped_name = memory_region_escape_name(name);
@ -1569,7 +1570,7 @@ void *memory_region_get_ram_ptr(MemoryRegion *mr)
mr = mr->alias; mr = mr->alias;
} }
assert(mr->ram_addr != RAM_ADDR_INVALID); assert(mr->ram_addr != RAM_ADDR_INVALID);
ptr = qemu_get_ram_ptr(mr->ram_addr & TARGET_PAGE_MASK); ptr = qemu_get_ram_ptr(mr->ram_block, mr->ram_addr & TARGET_PAGE_MASK);
rcu_read_unlock(); rcu_read_unlock();
return ptr + offset; return ptr + offset;

View file

@ -178,6 +178,7 @@ void guest_phys_blocks_free(GuestPhysBlockList *list)
QTAILQ_FOREACH_SAFE(p, &list->head, next, q) { QTAILQ_FOREACH_SAFE(p, &list->head, next, q) {
QTAILQ_REMOVE(&list->head, p, next); QTAILQ_REMOVE(&list->head, p, next);
memory_region_unref(p->mr);
g_free(p); g_free(p);
} }
list->num = 0; list->num = 0;
@ -241,6 +242,8 @@ static void guest_phys_blocks_region_add(MemoryListener *listener,
block->target_start = target_start; block->target_start = target_start;
block->target_end = target_end; block->target_end = target_end;
block->host_addr = host_addr; block->host_addr = host_addr;
block->mr = section->mr;
memory_region_ref(section->mr);
QTAILQ_INSERT_TAIL(&g->list->head, block, next); QTAILQ_INSERT_TAIL(&g->list->head, block, next);
++g->list->num; ++g->list->num;

View file

@ -37,6 +37,7 @@
#include "qemu-options.h" #include "qemu-options.h"
#include "qemu/rcu.h" #include "qemu/rcu.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/log.h"
#ifdef CONFIG_LINUX #ifdef CONFIG_LINUX
#include <sys/prctl.h> #include <sys/prctl.h>
@ -275,7 +276,10 @@ void os_setup_post(void)
dup2(fd, 0); dup2(fd, 0);
dup2(fd, 1); dup2(fd, 1);
dup2(fd, 2); /* In case -D is given do not redirect stderr to /dev/null */
if (!qemu_logfile) {
dup2(fd, 2);
}
close(fd); close(fd);

View file

@ -2195,6 +2195,10 @@
# 2. fd: the protocol starts with "fd:", and the following string # 2. fd: the protocol starts with "fd:", and the following string
# is the fd's name. # is the fd's name.
# #
# @detach: #optional if true, QMP will return immediately rather than
# waiting for the dump to finish. The user can track progress
# using "query-dump". (since 2.6).
#
# @begin: #optional if specified, the starting physical address. # @begin: #optional if specified, the starting physical address.
# #
# @length: #optional if specified, the memory size, in bytes. If you don't # @length: #optional if specified, the memory size, in bytes. If you don't
@ -2211,8 +2215,56 @@
# Since: 1.2 # Since: 1.2
## ##
{ 'command': 'dump-guest-memory', { 'command': 'dump-guest-memory',
'data': { 'paging': 'bool', 'protocol': 'str', '*begin': 'int', 'data': { 'paging': 'bool', 'protocol': 'str', '*detach': 'bool',
'*length': 'int', '*format': 'DumpGuestMemoryFormat' } } '*begin': 'int', '*length': 'int',
'*format': 'DumpGuestMemoryFormat'} }
##
# @DumpStatus
#
# Describe the status of a long-running background guest memory dump.
#
# @none: no dump-guest-memory has started yet.
#
# @active: there is one dump running in background.
#
# @completed: the last dump has finished successfully.
#
# @failed: the last dump has failed.
#
# Since 2.6
##
{ 'enum': 'DumpStatus',
'data': [ 'none', 'active', 'completed', 'failed' ] }
##
# @DumpQueryResult
#
# The result format for 'query-dump'.
#
# @status: enum of @DumpStatus, which shows current dump status
#
# @completed: bytes written in latest dump (uncompressed)
#
# @total: total bytes to be written in latest dump (uncompressed)
#
# Since 2.6
##
{ 'struct': 'DumpQueryResult',
'data': { 'status': 'DumpStatus',
'completed': 'int',
'total': 'int' } }
##
# @query-dump
#
# Query latest dump status.
#
# Returns: A @DumpStatus object showing the dump status.
#
# Since: 2.6
##
{ 'command': 'query-dump', 'returns': 'DumpQueryResult' }
## ##
# @DumpGuestMemoryCapability: # @DumpGuestMemoryCapability:

View file

@ -369,3 +369,19 @@
## ##
{ 'event': 'MEM_UNPLUG_ERROR', { 'event': 'MEM_UNPLUG_ERROR',
'data': { 'device': 'str', 'msg': 'str' } } 'data': { 'device': 'str', 'msg': 'str' } }
##
# @DUMP_COMPLETED
#
# Emitted when background dump has completed
#
# @result: DumpQueryResult type described in qapi-schema.json.
#
# @error: #optional human-readable error string that provides
# hint on why dump failed. Only presents on failure. The
# user should not try to interpret the error string.
#
# Since: 2.6
##
{ 'event': 'DUMP_COMPLETED' ,
'data': { 'result': 'DumpQueryResult', '*error': 'str' } }

View file

@ -3490,7 +3490,7 @@ fail:
return NULL; return NULL;
} }
static void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend) void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend)
{ {
const char *logfile = qemu_opt_get(opts, "logfile"); const char *logfile = qemu_opt_get(opts, "logfile");

View file

@ -158,7 +158,8 @@ TODO (no longer available)
* pcsys_introduction:: Introduction * pcsys_introduction:: Introduction
* pcsys_quickstart:: Quick Start * pcsys_quickstart:: Quick Start
* sec_invocation:: Invocation * sec_invocation:: Invocation
* pcsys_keys:: Keys * pcsys_keys:: Keys in the graphical frontends
* mux_keys:: Keys in the character backend multiplexer
* pcsys_monitor:: QEMU Monitor * pcsys_monitor:: QEMU Monitor
* disk_images:: Disk Images * disk_images:: Disk Images
* pcsys_network:: Network emulation * pcsys_network:: Network emulation
@ -272,7 +273,7 @@ targets do not need a disk image.
@c man end @c man end
@node pcsys_keys @node pcsys_keys
@section Keys @section Keys in the graphical frontends
@c man begin OPTIONS @c man begin OPTIONS
@ -322,15 +323,23 @@ Toggle mouse and keyboard grab.
In the virtual consoles, you can use @key{Ctrl-Up}, @key{Ctrl-Down}, In the virtual consoles, you can use @key{Ctrl-Up}, @key{Ctrl-Down},
@key{Ctrl-PageUp} and @key{Ctrl-PageDown} to move in the back log. @key{Ctrl-PageUp} and @key{Ctrl-PageDown} to move in the back log.
@kindex Ctrl-a h @c man end
During emulation, if you are using the @option{-nographic} option, use
@key{Ctrl-a h} to get terminal commands: @node mux_keys
@section Keys in the character backend multiplexer
@c man begin OPTIONS
During emulation, if you are using a character backend multiplexer
(which is the default if you are using @option{-nographic}) then
several commands are available via an escape sequence. These
key sequences all start with an escape character, which is @key{Ctrl-a}
by default, but can be changed with @option{-echr}. The list below assumes
you're using the default.
@table @key @table @key
@item Ctrl-a h @item Ctrl-a h
@kindex Ctrl-a h @kindex Ctrl-a h
@item Ctrl-a ?
@kindex Ctrl-a ?
Print this help Print this help
@item Ctrl-a x @item Ctrl-a x
@kindex Ctrl-a x @kindex Ctrl-a x
@ -346,10 +355,11 @@ Toggle console timestamps
Send break (magic sysrq in Linux) Send break (magic sysrq in Linux)
@item Ctrl-a c @item Ctrl-a c
@kindex Ctrl-a c @kindex Ctrl-a c
Switch between console and monitor Rotate between the frontends connected to the multiplexer (usually
this switches between the monitor and the console)
@item Ctrl-a Ctrl-a @item Ctrl-a Ctrl-a
@kindex Ctrl-a a @kindex Ctrl-a Ctrl-a
Send Ctrl-a Send the escape character to the frontend
@end table @end table
@c man end @c man end

View file

@ -2166,8 +2166,49 @@ All devices must have an id, which can be any string up to 127 characters long.
It is used to uniquely identify this device in other command line directives. It is used to uniquely identify this device in other command line directives.
A character device may be used in multiplexing mode by multiple front-ends. A character device may be used in multiplexing mode by multiple front-ends.
The key sequence of @key{Control-a} and @key{c} will rotate the input focus Specify @option{mux=on} to enable this mode.
between attached front-ends. Specify @option{mux=on} to enable this mode. A multiplexer is a "1:N" device, and here the "1" end is your specified chardev
backend, and the "N" end is the various parts of QEMU that can talk to a chardev.
If you create a chardev with @option{id=myid} and @option{mux=on}, QEMU will
create a multiplexer with your specified ID, and you can then configure multiple
front ends to use that chardev ID for their input/output. Up to four different
front ends can be connected to a single multiplexed chardev. (Without
multiplexing enabled, a chardev can only be used by a single front end.)
For instance you could use this to allow a single stdio chardev to be used by
two serial ports and the QEMU monitor:
@example
-chardev stdio,mux=on,id=char0 \
-mon chardev=char0,mode=readline,default \
-serial chardev:char0 \
-serial chardev:char0
@end example
You can have more than one multiplexer in a system configuration; for instance
you could have a TCP port multiplexed between UART 0 and UART 1, and stdio
multiplexed between the QEMU monitor and a parallel port:
@example
-chardev stdio,mux=on,id=char0 \
-mon chardev=char0,mode=readline,default \
-parallel chardev:char0 \
-chardev tcp,...,mux=on,id=char1 \
-serial chardev:char1 \
-serial chardev:char1
@end example
When you're using a multiplexed character device, some escape sequences are
interpreted in the input. @xref{mux_keys, Keys in the character backend
multiplexer}.
Note that some other command line options may implicitly create multiplexed
character backends; for instance @option{-serial mon:stdio} creates a
multiplexed stdio backend connected to the serial port and the QEMU monitor,
and @option{-nographic} also multiplexes the console and the monitor to
stdio.
There is currently no support for multiplexing in the other direction
(where a single QEMU front end takes input and output from multiple chardevs).
Every backend supports the @option{logfile} option, which supplies the path Every backend supports the @option{logfile} option, which supplies the path
to a file to record all data transmitted via the backend. The @option{logappend} to a file to record all data transmitted via the backend. The @option{logappend}

View file

@ -838,8 +838,8 @@ EQMP
{ {
.name = "dump-guest-memory", .name = "dump-guest-memory",
.args_type = "paging:b,protocol:s,begin:i?,end:i?,format:s?", .args_type = "paging:b,protocol:s,detach:b?,begin:i?,end:i?,format:s?",
.params = "-p protocol [begin] [length] [format]", .params = "-p protocol [-d] [begin] [length] [format]",
.help = "dump guest memory to file", .help = "dump guest memory to file",
.mhandler.cmd_new = qmp_marshal_dump_guest_memory, .mhandler.cmd_new = qmp_marshal_dump_guest_memory,
}, },
@ -855,6 +855,9 @@ Arguments:
- "paging": do paging to get guest's memory mapping (json-bool) - "paging": do paging to get guest's memory mapping (json-bool)
- "protocol": destination file(started with "file:") or destination file - "protocol": destination file(started with "file:") or destination file
descriptor (started with "fd:") (json-string) descriptor (started with "fd:") (json-string)
- "detach": if specified, command will return immediately, without waiting
for the dump to finish. The user can track progress using
"query-dump". (json-bool)
- "begin": the starting physical address. It's optional, and should be specified - "begin": the starting physical address. It's optional, and should be specified
with length together (json-int) with length together (json-int)
- "length": the memory size, in bytes. It's optional, and should be specified - "length": the memory size, in bytes. It's optional, and should be specified
@ -892,6 +895,30 @@ Example:
<- { "return": { "formats": <- { "return": { "formats":
["elf", "kdump-zlib", "kdump-lzo", "kdump-snappy"] } ["elf", "kdump-zlib", "kdump-lzo", "kdump-snappy"] }
EQMP
{
.name = "query-dump",
.args_type = "",
.params = "",
.help = "query background dump status",
.mhandler.cmd_new = qmp_marshal_query_dump,
},
SQMP
query-dump
----------
Query background dump status.
Arguments: None.
Example:
-> { "execute": "query-dump" }
<- { "return": { "status": "active", "completed": 1024000,
"total": 2048000 } }
EQMP EQMP
#if defined TARGET_S390X #if defined TARGET_S390X

14
qmp.c
View file

@ -103,6 +103,13 @@ void qmp_quit(Error **errp)
void qmp_stop(Error **errp) void qmp_stop(Error **errp)
{ {
/* if there is a dump in background, we should wait until the dump
* finished */
if (dump_in_progress()) {
error_setg(errp, "There is a dump in process, please wait.");
return;
}
if (runstate_check(RUN_STATE_INMIGRATE)) { if (runstate_check(RUN_STATE_INMIGRATE)) {
autostart = 0; autostart = 0;
} else { } else {
@ -175,6 +182,13 @@ void qmp_cont(Error **errp)
BlockBackend *blk; BlockBackend *blk;
BlockDriverState *bs; BlockDriverState *bs;
/* if there is a dump in background, we should wait until the dump
* finished */
if (dump_in_progress()) {
error_setg(errp, "There is a dump in process, please wait.");
return;
}
if (runstate_needs_reset()) { if (runstate_needs_reset()) {
error_setg(errp, "Resetting the Virtual Machine is required"); error_setg(errp, "Resetting the Virtual Machine is required");
return; return;

View file

@ -796,11 +796,12 @@ def check_access(options):
sys.stderr.write("Please enable CONFIG_TRACING in your kernel " sys.stderr.write("Please enable CONFIG_TRACING in your kernel "
"when using the option -t (default).\n" "when using the option -t (default).\n"
"If it is enabled, make {0} readable by the " "If it is enabled, make {0} readable by the "
"current user.\n") "current user.\n"
.format(PATH_DEBUGFS_TRACING))
if options.tracepoints: if options.tracepoints:
sys.exit(1) sys.exit(1)
sys.stderr.write("Falling back to debugfs statistics!\n" sys.stderr.write("Falling back to debugfs statistics!\n")
options.debugfs = True options.debugfs = True
sleep(5) sleep(5)

View file

@ -366,26 +366,30 @@ static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend,
Error **errp) Error **errp)
{ {
const char *name = qemu_opt_get(opts, "name"); const char *name = qemu_opt_get(opts, "name");
ChardevSpiceChannel *spicevmc;
if (name == NULL) { if (name == NULL) {
error_setg(errp, "chardev: spice channel: no name given"); error_setg(errp, "chardev: spice channel: no name given");
return; return;
} }
backend->u.spicevmc = g_new0(ChardevSpiceChannel, 1); spicevmc = backend->u.spicevmc = g_new0(ChardevSpiceChannel, 1);
backend->u.spicevmc->type = g_strdup(name); qemu_chr_parse_common(opts, qapi_ChardevSpiceChannel_base(spicevmc));
spicevmc->type = g_strdup(name);
} }
static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend, static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
Error **errp) Error **errp)
{ {
const char *name = qemu_opt_get(opts, "name"); const char *name = qemu_opt_get(opts, "name");
ChardevSpicePort *spiceport;
if (name == NULL) { if (name == NULL) {
error_setg(errp, "chardev: spice port: no name given"); error_setg(errp, "chardev: spice port: no name given");
return; return;
} }
backend->u.spiceport = g_new0(ChardevSpicePort, 1); spiceport = backend->u.spiceport = g_new0(ChardevSpicePort, 1);
backend->u.spiceport->fqdn = g_strdup(name); qemu_chr_parse_common(opts, qapi_ChardevSpicePort_base(spiceport));
spiceport->fqdn = g_strdup(name);
} }
static void register_types(void) static void register_types(void)

View file

@ -470,19 +470,26 @@ static const X86RegisterInfo32 x86_reg_info_32[CPU_NB_REGS32] = {
#undef REGISTER #undef REGISTER
const ExtSaveArea x86_ext_save_areas[] = { const ExtSaveArea x86_ext_save_areas[] = {
[2] = { .feature = FEAT_1_ECX, .bits = CPUID_EXT_AVX, [XSTATE_YMM_BIT] =
{ .feature = FEAT_1_ECX, .bits = CPUID_EXT_AVX,
.offset = 0x240, .size = 0x100 }, .offset = 0x240, .size = 0x100 },
[3] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX, [XSTATE_BNDREGS_BIT] =
{ .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX,
.offset = 0x3c0, .size = 0x40 }, .offset = 0x3c0, .size = 0x40 },
[4] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX, [XSTATE_BNDCSR_BIT] =
{ .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX,
.offset = 0x400, .size = 0x40 }, .offset = 0x400, .size = 0x40 },
[5] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F, [XSTATE_OPMASK_BIT] =
{ .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F,
.offset = 0x440, .size = 0x40 }, .offset = 0x440, .size = 0x40 },
[6] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F, [XSTATE_ZMM_Hi256_BIT] =
{ .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F,
.offset = 0x480, .size = 0x200 }, .offset = 0x480, .size = 0x200 },
[7] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F, [XSTATE_Hi16_ZMM_BIT] =
{ .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F,
.offset = 0x680, .size = 0x400 }, .offset = 0x680, .size = 0x400 },
[9] = { .feature = FEAT_7_0_ECX, .bits = CPUID_7_0_ECX_PKU, [XSTATE_PKRU_BIT] =
{ .feature = FEAT_7_0_ECX, .bits = CPUID_7_0_ECX_PKU,
.offset = 0xA80, .size = 0x8 }, .offset = 0xA80, .size = 0x8 },
}; };
@ -2480,7 +2487,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
*ecx = MAX(*ecx, esa->offset + esa->size); *ecx = MAX(*ecx, esa->offset + esa->size);
} }
} }
*eax |= ena_mask & (XSTATE_FP | XSTATE_SSE); *eax |= ena_mask & (XSTATE_FP_MASK | XSTATE_SSE_MASK);
*ebx = *ecx; *ebx = *ecx;
} else if (count == 1) { } else if (count == 1) {
*eax = env->features[FEAT_XSAVE]; *eax = env->features[FEAT_XSAVE];
@ -2714,15 +2721,15 @@ static void x86_cpu_reset(CPUState *s)
cpu_watchpoint_remove_all(s, BP_CPU); cpu_watchpoint_remove_all(s, BP_CPU);
cr4 = 0; cr4 = 0;
xcr0 = XSTATE_FP; xcr0 = XSTATE_FP_MASK;
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
/* Enable all the features for user-mode. */ /* Enable all the features for user-mode. */
if (env->features[FEAT_1_EDX] & CPUID_SSE) { if (env->features[FEAT_1_EDX] & CPUID_SSE) {
xcr0 |= XSTATE_SSE; xcr0 |= XSTATE_SSE_MASK;
} }
if (env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_MPX) { if (env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_MPX) {
xcr0 |= XSTATE_BNDREGS | XSTATE_BNDCSR; xcr0 |= XSTATE_BNDREGS_MASK | XSTATE_BNDCSR_MASK;
} }
if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) { if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) {
cr4 |= CR4_OSFXSR_MASK | CR4_OSXSAVE_MASK; cr4 |= CR4_OSFXSR_MASK | CR4_OSXSAVE_MASK;

View file

@ -404,16 +404,25 @@
#define MSR_IA32_BNDCFGS 0x00000d90 #define MSR_IA32_BNDCFGS 0x00000d90
#define MSR_IA32_XSS 0x00000da0 #define MSR_IA32_XSS 0x00000da0
#define XSTATE_FP (1ULL << 0) #define XSTATE_FP_BIT 0
#define XSTATE_SSE (1ULL << 1) #define XSTATE_SSE_BIT 1
#define XSTATE_YMM (1ULL << 2) #define XSTATE_YMM_BIT 2
#define XSTATE_BNDREGS (1ULL << 3) #define XSTATE_BNDREGS_BIT 3
#define XSTATE_BNDCSR (1ULL << 4) #define XSTATE_BNDCSR_BIT 4
#define XSTATE_OPMASK (1ULL << 5) #define XSTATE_OPMASK_BIT 5
#define XSTATE_ZMM_Hi256 (1ULL << 6) #define XSTATE_ZMM_Hi256_BIT 6
#define XSTATE_Hi16_ZMM (1ULL << 7) #define XSTATE_Hi16_ZMM_BIT 7
#define XSTATE_PKRU (1ULL << 9) #define XSTATE_PKRU_BIT 9
#define XSTATE_FP_MASK (1ULL << XSTATE_FP_BIT)
#define XSTATE_SSE_MASK (1ULL << XSTATE_SSE_BIT)
#define XSTATE_YMM_MASK (1ULL << XSTATE_YMM_BIT)
#define XSTATE_BNDREGS_MASK (1ULL << XSTATE_BNDREGS_BIT)
#define XSTATE_BNDCSR_MASK (1ULL << XSTATE_BNDCSR_BIT)
#define XSTATE_OPMASK_MASK (1ULL << XSTATE_OPMASK_BIT)
#define XSTATE_ZMM_Hi256_MASK (1ULL << XSTATE_ZMM_Hi256_BIT)
#define XSTATE_Hi16_ZMM_MASK (1ULL << XSTATE_Hi16_ZMM_BIT)
#define XSTATE_PKRU_MASK (1ULL << XSTATE_PKRU_BIT)
/* CPUID feature words */ /* CPUID feature words */
typedef enum FeatureWord { typedef enum FeatureWord {

View file

@ -1215,7 +1215,7 @@ static uint64_t get_xinuse(CPUX86State *env)
indicate in use. That said, the state of BNDREGS is important indicate in use. That said, the state of BNDREGS is important
enough to track in HFLAGS, so we might as well use that here. */ enough to track in HFLAGS, so we might as well use that here. */
if ((env->hflags & HF_MPX_IU_MASK) == 0) { if ((env->hflags & HF_MPX_IU_MASK) == 0) {
inuse &= ~XSTATE_BNDREGS; inuse &= ~XSTATE_BNDREGS_MASK;
} }
return inuse; return inuse;
} }
@ -1239,22 +1239,22 @@ static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm,
rfbm &= env->xcr0; rfbm &= env->xcr0;
opt &= rfbm; opt &= rfbm;
if (opt & XSTATE_FP) { if (opt & XSTATE_FP_MASK) {
do_xsave_fpu(env, ptr, ra); do_xsave_fpu(env, ptr, ra);
} }
if (rfbm & XSTATE_SSE) { if (rfbm & XSTATE_SSE_MASK) {
/* Note that saving MXCSR is not suppressed by XSAVEOPT. */ /* Note that saving MXCSR is not suppressed by XSAVEOPT. */
do_xsave_mxcsr(env, ptr, ra); do_xsave_mxcsr(env, ptr, ra);
} }
if (opt & XSTATE_SSE) { if (opt & XSTATE_SSE_MASK) {
do_xsave_sse(env, ptr, ra); do_xsave_sse(env, ptr, ra);
} }
if (opt & XSTATE_BNDREGS) { if (opt & XSTATE_BNDREGS_MASK) {
target_ulong off = x86_ext_save_areas[XSTATE_BNDREGS].offset; target_ulong off = x86_ext_save_areas[XSTATE_BNDREGS_BIT].offset;
do_xsave_bndregs(env, ptr + off, ra); do_xsave_bndregs(env, ptr + off, ra);
} }
if (opt & XSTATE_BNDCSR) { if (opt & XSTATE_BNDCSR_MASK) {
target_ulong off = x86_ext_save_areas[XSTATE_BNDCSR].offset; target_ulong off = x86_ext_save_areas[XSTATE_BNDCSR_BIT].offset;
do_xsave_bndcsr(env, ptr + off, ra); do_xsave_bndcsr(env, ptr + off, ra);
} }
@ -1399,19 +1399,19 @@ void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm)
raise_exception_ra(env, EXCP0D_GPF, ra); raise_exception_ra(env, EXCP0D_GPF, ra);
} }
if (rfbm & XSTATE_FP) { if (rfbm & XSTATE_FP_MASK) {
if (xstate_bv & XSTATE_FP) { if (xstate_bv & XSTATE_FP_MASK) {
do_xrstor_fpu(env, ptr, ra); do_xrstor_fpu(env, ptr, ra);
} else { } else {
helper_fninit(env); helper_fninit(env);
memset(env->fpregs, 0, sizeof(env->fpregs)); memset(env->fpregs, 0, sizeof(env->fpregs));
} }
} }
if (rfbm & XSTATE_SSE) { if (rfbm & XSTATE_SSE_MASK) {
/* Note that the standard form of XRSTOR loads MXCSR from memory /* Note that the standard form of XRSTOR loads MXCSR from memory
whether or not the XSTATE_BV bit is set. */ whether or not the XSTATE_BV bit is set. */
do_xrstor_mxcsr(env, ptr, ra); do_xrstor_mxcsr(env, ptr, ra);
if (xstate_bv & XSTATE_SSE) { if (xstate_bv & XSTATE_SSE_MASK) {
do_xrstor_sse(env, ptr, ra); do_xrstor_sse(env, ptr, ra);
} else { } else {
/* ??? When AVX is implemented, we may have to be more /* ??? When AVX is implemented, we may have to be more
@ -1419,9 +1419,9 @@ void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm)
memset(env->xmm_regs, 0, sizeof(env->xmm_regs)); memset(env->xmm_regs, 0, sizeof(env->xmm_regs));
} }
} }
if (rfbm & XSTATE_BNDREGS) { if (rfbm & XSTATE_BNDREGS_MASK) {
if (xstate_bv & XSTATE_BNDREGS) { if (xstate_bv & XSTATE_BNDREGS_MASK) {
target_ulong off = x86_ext_save_areas[XSTATE_BNDREGS].offset; target_ulong off = x86_ext_save_areas[XSTATE_BNDREGS_BIT].offset;
do_xrstor_bndregs(env, ptr + off, ra); do_xrstor_bndregs(env, ptr + off, ra);
env->hflags |= HF_MPX_IU_MASK; env->hflags |= HF_MPX_IU_MASK;
} else { } else {
@ -1429,9 +1429,9 @@ void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm)
env->hflags &= ~HF_MPX_IU_MASK; env->hflags &= ~HF_MPX_IU_MASK;
} }
} }
if (rfbm & XSTATE_BNDCSR) { if (rfbm & XSTATE_BNDCSR_MASK) {
if (xstate_bv & XSTATE_BNDCSR) { if (xstate_bv & XSTATE_BNDCSR_MASK) {
target_ulong off = x86_ext_save_areas[XSTATE_BNDCSR].offset; target_ulong off = x86_ext_save_areas[XSTATE_BNDCSR_BIT].offset;
do_xrstor_bndcsr(env, ptr + off, ra); do_xrstor_bndcsr(env, ptr + off, ra);
} else { } else {
memset(&env->bndcs_regs, 0, sizeof(env->bndcs_regs)); memset(&env->bndcs_regs, 0, sizeof(env->bndcs_regs));
@ -1470,7 +1470,7 @@ void helper_xsetbv(CPUX86State *env, uint32_t ecx, uint64_t mask)
} }
/* Only XCR0 is defined at present; the FPU may not be disabled. */ /* Only XCR0 is defined at present; the FPU may not be disabled. */
if (ecx != 0 || (mask & XSTATE_FP) == 0) { if (ecx != 0 || (mask & XSTATE_FP_MASK) == 0) {
goto do_gpf; goto do_gpf;
} }
@ -1482,7 +1482,8 @@ void helper_xsetbv(CPUX86State *env, uint32_t ecx, uint64_t mask)
} }
/* Disallow enabling only half of MPX. */ /* Disallow enabling only half of MPX. */
if ((mask ^ (mask * (XSTATE_BNDCSR / XSTATE_BNDREGS))) & XSTATE_BNDCSR) { if ((mask ^ (mask * (XSTATE_BNDCSR_MASK / XSTATE_BNDREGS_MASK)))
& XSTATE_BNDCSR_MASK) {
goto do_gpf; goto do_gpf;
} }

View file

@ -36,7 +36,7 @@ void cpu_sync_bndcs_hflags(CPUX86State *env)
} }
if ((env->cr[4] & CR4_OSXSAVE_MASK) if ((env->cr[4] & CR4_OSXSAVE_MASK)
&& (env->xcr0 & XSTATE_BNDCSR) && (env->xcr0 & XSTATE_BNDCSR_MASK)
&& (bndcsr & BNDCFG_ENABLE)) { && (bndcsr & BNDCFG_ENABLE)) {
hflags |= HF_MPX_EN_MASK; hflags |= HF_MPX_EN_MASK;
} else { } else {

View file

@ -2060,31 +2060,33 @@ static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend,
Error **errp) Error **errp)
{ {
int val; int val;
ChardevVC *vc;
backend->u.vc = g_new0(ChardevVC, 1); vc = backend->u.vc = g_new0(ChardevVC, 1);
qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc));
val = qemu_opt_get_number(opts, "width", 0); val = qemu_opt_get_number(opts, "width", 0);
if (val != 0) { if (val != 0) {
backend->u.vc->has_width = true; vc->has_width = true;
backend->u.vc->width = val; vc->width = val;
} }
val = qemu_opt_get_number(opts, "height", 0); val = qemu_opt_get_number(opts, "height", 0);
if (val != 0) { if (val != 0) {
backend->u.vc->has_height = true; vc->has_height = true;
backend->u.vc->height = val; vc->height = val;
} }
val = qemu_opt_get_number(opts, "cols", 0); val = qemu_opt_get_number(opts, "cols", 0);
if (val != 0) { if (val != 0) {
backend->u.vc->has_cols = true; vc->has_cols = true;
backend->u.vc->cols = val; vc->cols = val;
} }
val = qemu_opt_get_number(opts, "rows", 0); val = qemu_opt_get_number(opts, "rows", 0);
if (val != 0) { if (val != 0) {
backend->u.vc->has_rows = true; vc->has_rows = true;
backend->u.vc->rows = val; vc->rows = val;
} }
} }

View file

@ -56,13 +56,20 @@ void do_qemu_set_log(int log_flags, bool use_own_buffers)
#ifdef CONFIG_TRACE_LOG #ifdef CONFIG_TRACE_LOG
qemu_loglevel |= LOG_TRACE; qemu_loglevel |= LOG_TRACE;
#endif #endif
if (qemu_loglevel && !qemu_logfile) { if ((qemu_loglevel || is_daemonized()) && !qemu_logfile) {
if (logfilename) { if (logfilename) {
qemu_logfile = fopen(logfilename, log_append ? "a" : "w"); qemu_logfile = fopen(logfilename, log_append ? "a" : "w");
if (!qemu_logfile) { if (!qemu_logfile) {
perror(logfilename); perror(logfilename);
_exit(1); _exit(1);
} }
/* In case we are a daemon redirect stderr to logfile */
if (is_daemonized()) {
dup2(fileno(qemu_logfile), STDERR_FILENO);
fclose(qemu_logfile);
/* This will skip closing logfile in qemu_log_close() */
qemu_logfile = stderr;
}
} else { } else {
/* Default to stderr if no log file specified */ /* Default to stderr if no log file specified */
qemu_logfile = stderr; qemu_logfile = stderr;
@ -82,7 +89,7 @@ void do_qemu_set_log(int log_flags, bool use_own_buffers)
log_append = 1; log_append = 1;
} }
} }
if (!qemu_loglevel && qemu_logfile) { if (!qemu_loglevel && !is_daemonized() && qemu_logfile) {
qemu_log_close(); qemu_log_close();
} }
} }