mirror of https://gitee.com/openkylin/qemu.git
* 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:
commit
586fc27e6a
|
@ -220,6 +220,24 @@ Data:
|
|||
},
|
||||
"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
|
||||
--------------
|
||||
|
||||
|
|
215
dump.c
215
dump.c
|
@ -25,6 +25,7 @@
|
|||
#include "sysemu/cpus.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "qapi-event.h"
|
||||
|
||||
#include <zlib.h>
|
||||
#ifdef CONFIG_LZO
|
||||
|
@ -82,12 +83,6 @@ static int dump_cleanup(DumpState *s)
|
|||
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)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
ret = cpu_write_elf64_note(f, cpu, id, s);
|
||||
if (ret < 0) {
|
||||
dump_error(s, "dump: failed to write elf notes", errp);
|
||||
error_setg(errp, "dump: failed to write elf notes");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -253,7 +248,7 @@ static void write_elf64_notes(WriteCoreDumpFunction f, DumpState *s,
|
|||
CPU_FOREACH(cpu) {
|
||||
ret = cpu_write_elf64_qemunote(f, cpu, s);
|
||||
if (ret < 0) {
|
||||
dump_error(s, "dump: failed to write CPU status", errp);
|
||||
error_setg(errp, "dump: failed to write CPU status");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -275,7 +270,7 @@ static void write_elf32_note(DumpState *s, Error **errp)
|
|||
|
||||
ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s);
|
||||
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);
|
||||
ret = cpu_write_elf32_note(f, cpu, id, s);
|
||||
if (ret < 0) {
|
||||
dump_error(s, "dump: failed to write elf notes", errp);
|
||||
error_setg(errp, "dump: failed to write elf notes");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -298,7 +293,7 @@ static void write_elf32_notes(WriteCoreDumpFunction f, DumpState *s,
|
|||
CPU_FOREACH(cpu) {
|
||||
ret = cpu_write_elf32_qemunote(f, cpu, s);
|
||||
if (ret < 0) {
|
||||
dump_error(s, "dump: failed to write CPU status", errp);
|
||||
error_setg(errp, "dump: failed to write CPU status");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -326,7 +321,7 @@ static void write_elf_section(DumpState *s, int type, Error **errp)
|
|||
|
||||
ret = fd_write_vmcore(&shdr, shdr_size, s);
|
||||
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);
|
||||
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)
|
||||
{
|
||||
while (1) {
|
||||
|
@ -624,8 +616,6 @@ static void dump_iterate(DumpState *s, Error **errp)
|
|||
}
|
||||
|
||||
} while (!get_next_block(s, block));
|
||||
|
||||
dump_completed(s);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -784,7 +774,7 @@ static void create_header32(DumpState *s, Error **errp)
|
|||
|
||||
if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS *
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -800,7 +790,7 @@ static void create_header32(DumpState *s, Error **errp)
|
|||
}
|
||||
if (write_buffer(s->fd, offset_note, s->note_buf,
|
||||
s->note_size) < 0) {
|
||||
dump_error(s, "dump: failed to write notes", errp);
|
||||
error_setg(errp, "dump: failed to write notes");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -865,7 +855,7 @@ static void create_header64(DumpState *s, Error **errp)
|
|||
dh->status = cpu_to_dump32(s, status);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -884,7 +874,7 @@ static void create_header64(DumpState *s, Error **errp)
|
|||
|
||||
if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS *
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -901,7 +891,7 @@ static void create_header64(DumpState *s, Error **errp)
|
|||
|
||||
if (write_buffer(s->fd, offset_note, s->note_buf,
|
||||
s->note_size) < 0) {
|
||||
dump_error(s, "dump: failed to write notes", errp);
|
||||
error_setg(errp, "dump: failed to write notes");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -1087,7 +1077,7 @@ static void write_dump_bitmap(DumpState *s, Error **errp)
|
|||
while (get_next_page(&block_iter, &pfn, NULL, s)) {
|
||||
ret = set_dump_bitmap(last_pfn, pfn, true, dump_bitmap_buf, s);
|
||||
if (ret < 0) {
|
||||
dump_error(s, "dump: failed to set dump_bitmap", errp);
|
||||
error_setg(errp, "dump: failed to set dump_bitmap");
|
||||
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,
|
||||
dump_bitmap_buf, s);
|
||||
if (ret < 0) {
|
||||
dump_error(s, "dump: failed to sync dump_bitmap", errp);
|
||||
error_setg(errp, "dump: failed to sync dump_bitmap");
|
||||
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);
|
||||
g_free(buf);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1253,7 +1243,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
|||
ret = write_cache(&page_desc, &pd_zero, sizeof(PageDescriptor),
|
||||
false);
|
||||
if (ret < 0) {
|
||||
dump_error(s, "dump: failed to write page desc", errp);
|
||||
error_setg(errp, "dump: failed to write page desc");
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
|
@ -1278,7 +1268,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
|||
|
||||
ret = write_cache(&page_data, buf_out, size_out, false);
|
||||
if (ret < 0) {
|
||||
dump_error(s, "dump: failed to write page data", errp);
|
||||
error_setg(errp, "dump: failed to write page data");
|
||||
goto out;
|
||||
}
|
||||
#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);
|
||||
if (ret < 0) {
|
||||
dump_error(s, "dump: failed to write page data", errp);
|
||||
error_setg(errp, "dump: failed to write page data");
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
@ -1305,7 +1295,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
|||
|
||||
ret = write_cache(&page_data, buf_out, size_out, false);
|
||||
if (ret < 0) {
|
||||
dump_error(s, "dump: failed to write page data", errp);
|
||||
error_setg(errp, "dump: failed to write page data");
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
@ -1321,7 +1311,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
|||
ret = write_cache(&page_data, buf,
|
||||
s->dump_info.page_size, false);
|
||||
if (ret < 0) {
|
||||
dump_error(s, "dump: failed to write page data", errp);
|
||||
error_setg(errp, "dump: failed to write page data");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
@ -1333,20 +1323,21 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
|||
|
||||
ret = write_cache(&page_desc, &pd, sizeof(PageDescriptor), false);
|
||||
if (ret < 0) {
|
||||
dump_error(s, "dump: failed to write page desc", errp);
|
||||
error_setg(errp, "dump: failed to write page desc");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
s->written_size += s->dump_info.page_size;
|
||||
}
|
||||
|
||||
ret = write_cache(&page_desc, NULL, 0, true);
|
||||
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;
|
||||
}
|
||||
ret = write_cache(&page_data, NULL, 0, true);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1390,7 +1381,7 @@ static void create_kdump_vmcore(DumpState *s, Error **errp)
|
|||
|
||||
ret = write_start_flat_header(s->fd);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1414,11 +1405,9 @@ static void create_kdump_vmcore(DumpState *s, Error **errp)
|
|||
|
||||
ret = write_end_flat_header(s->fd);
|
||||
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;
|
||||
}
|
||||
|
||||
dump_completed(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);
|
||||
}
|
||||
|
||||
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,
|
||||
DumpGuestMemoryFormat format, bool paging, bool has_filter,
|
||||
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;
|
||||
int ret;
|
||||
|
||||
s->has_format = has_format;
|
||||
s->format = format;
|
||||
s->written_size = 0;
|
||||
|
||||
/* kdump-compressed is conflict with paging and filter */
|
||||
if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
|
||||
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_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);
|
||||
if (s->start == -1) {
|
||||
|
@ -1624,8 +1659,60 @@ cleanup:
|
|||
dump_cleanup(s);
|
||||
}
|
||||
|
||||
void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
|
||||
int64_t begin, bool has_length,
|
||||
/* this operation might be time consuming. */
|
||||
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,
|
||||
DumpGuestMemoryFormat format, Error **errp)
|
||||
{
|
||||
|
@ -1633,6 +1720,19 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
|
|||
int fd = -1;
|
||||
DumpState *s;
|
||||
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
|
||||
|
@ -1652,6 +1752,9 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
|
|||
error_setg(errp, QERR_MISSING_PARAMETER, "begin");
|
||||
return;
|
||||
}
|
||||
if (has_detach) {
|
||||
detach_p = detach;
|
||||
}
|
||||
|
||||
/* check whether lzo/snappy is supported */
|
||||
#ifndef CONFIG_LZO
|
||||
|
@ -1690,23 +1793,25 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
|
|||
return;
|
||||
}
|
||||
|
||||
s = g_malloc0(sizeof(DumpState));
|
||||
s = &dump_state_global;
|
||||
dump_state_prepare(s);
|
||||
|
||||
dump_init(s, fd, has_format, format, paging, has_begin,
|
||||
begin, length, &local_err);
|
||||
if (local_err) {
|
||||
g_free(s);
|
||||
error_propagate(errp, local_err);
|
||||
atomic_set(&s->status, DUMP_STATUS_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
|
||||
create_kdump_vmcore(s, errp);
|
||||
if (detach_p) {
|
||||
/* detached dump */
|
||||
qemu_thread_create(&s->dump_thread, "dump_thread", dump_thread,
|
||||
s, QEMU_THREAD_DETACHED);
|
||||
} else {
|
||||
create_vmcore(s, errp);
|
||||
/* sync dump */
|
||||
dump_process(s, errp);
|
||||
}
|
||||
|
||||
g_free(s);
|
||||
}
|
||||
|
||||
DumpGuestMemoryCapability *qmp_query_dump_guest_memory_capability(Error **errp)
|
||||
|
|
48
exec.c
48
exec.c
|
@ -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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mr->ram_block = new_block;
|
||||
return addr;
|
||||
}
|
||||
|
||||
|
@ -1866,9 +1868,13 @@ void *qemu_get_ram_block_host_ptr(ram_addr_t addr)
|
|||
*
|
||||
* 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) {
|
||||
/* 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.
|
||||
*/
|
||||
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;
|
||||
if (*size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
block = qemu_get_ram_block(addr);
|
||||
if (block == NULL) {
|
||||
block = qemu_get_ram_block(addr);
|
||||
}
|
||||
offset_inside_block = addr - block->offset;
|
||||
*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) {
|
||||
case 1:
|
||||
stb_p(qemu_get_ram_ptr(ram_addr), val);
|
||||
stb_p(qemu_get_ram_ptr(NULL, ram_addr), val);
|
||||
break;
|
||||
case 2:
|
||||
stw_p(qemu_get_ram_ptr(ram_addr), val);
|
||||
stw_p(qemu_get_ram_ptr(NULL, ram_addr), val);
|
||||
break;
|
||||
case 4:
|
||||
stl_p(qemu_get_ram_ptr(ram_addr), val);
|
||||
stl_p(qemu_get_ram_ptr(NULL, ram_addr), val);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
|
@ -2607,7 +2616,7 @@ static MemTxResult address_space_write_continue(AddressSpace *as, hwaddr addr,
|
|||
} else {
|
||||
addr1 += memory_region_get_ram_addr(mr);
|
||||
/* RAM case */
|
||||
ptr = qemu_get_ram_ptr(addr1);
|
||||
ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
|
||||
memcpy(ptr, buf, l);
|
||||
invalidate_and_set_dirty(mr, addr1, l);
|
||||
}
|
||||
|
@ -2698,7 +2707,7 @@ MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr,
|
|||
}
|
||||
} else {
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
@ -2783,7 +2792,7 @@ static inline void cpu_physical_memory_write_rom_internal(AddressSpace *as,
|
|||
} else {
|
||||
addr1 += memory_region_get_ram_addr(mr);
|
||||
/* ROM/RAM case */
|
||||
ptr = qemu_get_ram_ptr(addr1);
|
||||
ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
|
||||
switch (type) {
|
||||
case WRITE_DATA:
|
||||
memcpy(ptr, buf, l);
|
||||
|
@ -2995,7 +3004,7 @@ void *address_space_map(AddressSpace *as,
|
|||
|
||||
memory_region_ref(mr);
|
||||
*plen = done;
|
||||
ptr = qemu_ram_ptr_length(raddr + base, plen);
|
||||
ptr = qemu_ram_ptr_length(mr->ram_block, raddr + base, plen);
|
||||
rcu_read_unlock();
|
||||
|
||||
return ptr;
|
||||
|
@ -3079,7 +3088,8 @@ static inline uint32_t address_space_ldl_internal(AddressSpace *as, hwaddr addr,
|
|||
#endif
|
||||
} else {
|
||||
/* 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)
|
||||
+ addr1);
|
||||
switch (endian) {
|
||||
|
@ -3174,7 +3184,8 @@ static inline uint64_t address_space_ldq_internal(AddressSpace *as, hwaddr addr,
|
|||
#endif
|
||||
} else {
|
||||
/* 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)
|
||||
+ addr1);
|
||||
switch (endian) {
|
||||
|
@ -3289,7 +3300,8 @@ static inline uint32_t address_space_lduw_internal(AddressSpace *as,
|
|||
#endif
|
||||
} else {
|
||||
/* 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)
|
||||
+ addr1);
|
||||
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);
|
||||
} else {
|
||||
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);
|
||||
|
||||
dirty_log_mask = memory_region_get_dirty_log_mask(mr);
|
||||
|
@ -3429,7 +3441,7 @@ static inline void address_space_stl_internal(AddressSpace *as,
|
|||
} else {
|
||||
/* RAM case */
|
||||
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) {
|
||||
case DEVICE_LITTLE_ENDIAN:
|
||||
stl_le_p(ptr, val);
|
||||
|
@ -3539,7 +3551,7 @@ static inline void address_space_stw_internal(AddressSpace *as,
|
|||
} else {
|
||||
/* RAM case */
|
||||
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) {
|
||||
case DEVICE_LITTLE_ENDIAN:
|
||||
stw_le_p(ptr, val);
|
||||
|
|
|
@ -784,6 +784,20 @@ STEXI
|
|||
@item info skeys @var{address}
|
||||
@findex skeys
|
||||
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
|
||||
|
||||
STEXI
|
||||
|
|
|
@ -1056,10 +1056,11 @@ ETEXI
|
|||
|
||||
{
|
||||
.name = "dump-guest-memory",
|
||||
.args_type = "paging:-p,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:i?,length:i?",
|
||||
.params = "[-p] [-z|-l|-s] filename [begin length]",
|
||||
.args_type = "paging:-p,detach:-d,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:i?,length:i?",
|
||||
.params = "[-p] [-d] [-z|-l|-s] filename [begin length]",
|
||||
.help = "dump guest memory into file 'filename'.\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"
|
||||
"-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"
|
||||
|
|
26
hmp.c
26
hmp.c
|
@ -1599,8 +1599,10 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
|
|||
const char *file = qdict_get_str(qdict, "filename");
|
||||
bool has_begin = qdict_haskey(qdict, "begin");
|
||||
bool has_length = qdict_haskey(qdict, "length");
|
||||
bool has_detach = qdict_haskey(qdict, "detach");
|
||||
int64_t begin = 0;
|
||||
int64_t length = 0;
|
||||
bool detach = false;
|
||||
enum DumpGuestMemoryFormat dump_format = DUMP_GUEST_MEMORY_FORMAT_ELF;
|
||||
char *prot;
|
||||
|
||||
|
@ -1628,11 +1630,14 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
|
|||
if (has_length) {
|
||||
length = qdict_get_int(qdict, "length");
|
||||
}
|
||||
if (has_detach) {
|
||||
detach = qdict_get_bool(qdict, "detach");
|
||||
}
|
||||
|
||||
prot = g_strconcat("file:", file, NULL);
|
||||
|
||||
qmp_dump_guest_memory(paging, prot, has_begin, begin, has_length, length,
|
||||
true, dump_format, &err);
|
||||
qmp_dump_guest_memory(paging, prot, true, detach, has_begin, begin,
|
||||
has_length, length, true, dump_format, &err);
|
||||
hmp_handle_error(mon, &err);
|
||||
g_free(prot);
|
||||
}
|
||||
|
@ -2358,3 +2363,20 @@ void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict)
|
|||
|
||||
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
1
hmp.h
|
@ -131,5 +131,6 @@ void hmp_rocker(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_groups(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_dump(Monitor *mon, const QDict *qdict);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "qemu/notify.h"
|
||||
#include "qom/object.h"
|
||||
#include "qemu/rcu.h"
|
||||
#include "qemu/typedefs.h"
|
||||
|
||||
#define MAX_PHYS_ADDR_SPACE_BITS 62
|
||||
#define MAX_PHYS_ADDR (((hwaddr)1 << MAX_PHYS_ADDR_SPACE_BITS) - 1)
|
||||
|
@ -169,6 +170,7 @@ struct MemoryRegion {
|
|||
bool global_locking;
|
||||
uint8_t dirty_log_mask;
|
||||
ram_addr_t ram_addr;
|
||||
RAMBlock *ram_block;
|
||||
Object *owner;
|
||||
const MemoryRegionIOMMUOps *iommu_ops;
|
||||
|
||||
|
@ -1386,7 +1388,7 @@ MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr,
|
|||
MemoryRegion *mr);
|
||||
MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,
|
||||
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)
|
||||
{
|
||||
|
@ -1395,8 +1397,6 @@ static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write)
|
|||
} else {
|
||||
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);
|
||||
if (len == l && memory_access_is_direct(mr, false)) {
|
||||
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);
|
||||
} else {
|
||||
result = address_space_read_continue(as, addr, attrs, buf, len,
|
||||
|
|
|
@ -493,4 +493,8 @@ int parse_debug_env(const char *name, int max, int initial);
|
|||
const char *qemu_ether_ntoa(const MACAddr *mac);
|
||||
void page_size_init(void);
|
||||
|
||||
/* returns non-zero if dump is in progress, otherwise zero is
|
||||
* returned. */
|
||||
bool dump_in_progress(void);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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 */
|
||||
typedef struct QEMULogItem {
|
||||
int mask;
|
||||
|
|
|
@ -114,6 +114,16 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
|
|||
void (*init)(struct CharDriverState *s),
|
||||
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:
|
||||
*
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
|
||||
#include "sysemu/dump-arch.h"
|
||||
#include "sysemu/memory_mapping.h"
|
||||
#include "qapi-types.h"
|
||||
|
||||
typedef struct QEMU_PACKED MakedumpfileHeader {
|
||||
char signature[16]; /* = "makedumpfile" */
|
||||
|
@ -176,6 +177,20 @@ typedef struct DumpState {
|
|||
off_t offset_page; /* offset of page part in vmcore */
|
||||
size_t num_dumpable; /* number of page that can be dumped */
|
||||
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;
|
||||
|
||||
uint16_t cpu_to_dump16(DumpState *s, uint16_t val);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/typedefs.h"
|
||||
#include "exec/memory.h"
|
||||
|
||||
typedef struct GuestPhysBlock {
|
||||
/* visible to guest, reflects PCI hole, etc */
|
||||
|
@ -27,6 +28,9 @@ typedef struct GuestPhysBlock {
|
|||
/* points into host memory */
|
||||
uint8_t *host_addr;
|
||||
|
||||
/* points to the MemoryRegion that this block belongs to */
|
||||
MemoryRegion *mr;
|
||||
|
||||
QTAILQ_ENTRY(GuestPhysBlock) next;
|
||||
} GuestPhysBlock;
|
||||
|
||||
|
|
3
memory.c
3
memory.c
|
@ -912,6 +912,7 @@ void memory_region_init(MemoryRegion *mr,
|
|||
}
|
||||
mr->name = g_strdup(name);
|
||||
mr->owner = owner;
|
||||
mr->ram_block = NULL;
|
||||
|
||||
if (name) {
|
||||
char *escaped_name = memory_region_escape_name(name);
|
||||
|
@ -1569,7 +1570,7 @@ void *memory_region_get_ram_ptr(MemoryRegion *mr)
|
|||
mr = mr->alias;
|
||||
}
|
||||
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();
|
||||
|
||||
return ptr + offset;
|
||||
|
|
|
@ -178,6 +178,7 @@ void guest_phys_blocks_free(GuestPhysBlockList *list)
|
|||
|
||||
QTAILQ_FOREACH_SAFE(p, &list->head, next, q) {
|
||||
QTAILQ_REMOVE(&list->head, p, next);
|
||||
memory_region_unref(p->mr);
|
||||
g_free(p);
|
||||
}
|
||||
list->num = 0;
|
||||
|
@ -241,6 +242,8 @@ static void guest_phys_blocks_region_add(MemoryListener *listener,
|
|||
block->target_start = target_start;
|
||||
block->target_end = target_end;
|
||||
block->host_addr = host_addr;
|
||||
block->mr = section->mr;
|
||||
memory_region_ref(section->mr);
|
||||
|
||||
QTAILQ_INSERT_TAIL(&g->list->head, block, next);
|
||||
++g->list->num;
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "qemu-options.h"
|
||||
#include "qemu/rcu.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/log.h"
|
||||
|
||||
#ifdef CONFIG_LINUX
|
||||
#include <sys/prctl.h>
|
||||
|
@ -275,7 +276,10 @@ void os_setup_post(void)
|
|||
|
||||
dup2(fd, 0);
|
||||
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);
|
||||
|
||||
|
|
|
@ -2195,6 +2195,10 @@
|
|||
# 2. fd: the protocol starts with "fd:", and the following string
|
||||
# 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.
|
||||
#
|
||||
# @length: #optional if specified, the memory size, in bytes. If you don't
|
||||
|
@ -2211,8 +2215,56 @@
|
|||
# Since: 1.2
|
||||
##
|
||||
{ 'command': 'dump-guest-memory',
|
||||
'data': { 'paging': 'bool', 'protocol': 'str', '*begin': 'int',
|
||||
'*length': 'int', '*format': 'DumpGuestMemoryFormat' } }
|
||||
'data': { 'paging': 'bool', 'protocol': 'str', '*detach': 'bool',
|
||||
'*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:
|
||||
|
|
|
@ -369,3 +369,19 @@
|
|||
##
|
||||
{ 'event': 'MEM_UNPLUG_ERROR',
|
||||
'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' } }
|
||||
|
|
|
@ -3490,7 +3490,7 @@ fail:
|
|||
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");
|
||||
|
||||
|
|
|
@ -158,7 +158,8 @@ TODO (no longer available)
|
|||
* pcsys_introduction:: Introduction
|
||||
* pcsys_quickstart:: Quick Start
|
||||
* 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
|
||||
* disk_images:: Disk Images
|
||||
* pcsys_network:: Network emulation
|
||||
|
@ -272,7 +273,7 @@ targets do not need a disk image.
|
|||
@c man end
|
||||
|
||||
@node pcsys_keys
|
||||
@section Keys
|
||||
@section Keys in the graphical frontends
|
||||
|
||||
@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},
|
||||
@key{Ctrl-PageUp} and @key{Ctrl-PageDown} to move in the back log.
|
||||
|
||||
@kindex Ctrl-a h
|
||||
During emulation, if you are using the @option{-nographic} option, use
|
||||
@key{Ctrl-a h} to get terminal commands:
|
||||
@c man end
|
||||
|
||||
@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
|
||||
@item Ctrl-a h
|
||||
@kindex Ctrl-a h
|
||||
@item Ctrl-a ?
|
||||
@kindex Ctrl-a ?
|
||||
Print this help
|
||||
@item Ctrl-a x
|
||||
@kindex Ctrl-a x
|
||||
|
@ -346,10 +355,11 @@ Toggle console timestamps
|
|||
Send break (magic sysrq in Linux)
|
||||
@item 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
|
||||
@kindex Ctrl-a a
|
||||
Send Ctrl-a
|
||||
@kindex Ctrl-a Ctrl-a
|
||||
Send the escape character to the frontend
|
||||
@end table
|
||||
@c man end
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
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
|
||||
between attached front-ends. Specify @option{mux=on} to enable this mode.
|
||||
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
|
||||
to a file to record all data transmitted via the backend. The @option{logappend}
|
||||
|
|
|
@ -838,8 +838,8 @@ EQMP
|
|||
|
||||
{
|
||||
.name = "dump-guest-memory",
|
||||
.args_type = "paging:b,protocol:s,begin:i?,end:i?,format:s?",
|
||||
.params = "-p protocol [begin] [length] [format]",
|
||||
.args_type = "paging:b,protocol:s,detach:b?,begin:i?,end:i?,format:s?",
|
||||
.params = "-p protocol [-d] [begin] [length] [format]",
|
||||
.help = "dump guest memory to file",
|
||||
.mhandler.cmd_new = qmp_marshal_dump_guest_memory,
|
||||
},
|
||||
|
@ -855,6 +855,9 @@ Arguments:
|
|||
- "paging": do paging to get guest's memory mapping (json-bool)
|
||||
- "protocol": destination file(started with "file:") or destination file
|
||||
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
|
||||
with length together (json-int)
|
||||
- "length": the memory size, in bytes. It's optional, and should be specified
|
||||
|
@ -892,6 +895,30 @@ Example:
|
|||
<- { "return": { "formats":
|
||||
["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
|
||||
|
||||
#if defined TARGET_S390X
|
||||
|
|
14
qmp.c
14
qmp.c
|
@ -103,6 +103,13 @@ void qmp_quit(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)) {
|
||||
autostart = 0;
|
||||
} else {
|
||||
|
@ -175,6 +182,13 @@ void qmp_cont(Error **errp)
|
|||
BlockBackend *blk;
|
||||
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()) {
|
||||
error_setg(errp, "Resetting the Virtual Machine is required");
|
||||
return;
|
||||
|
|
|
@ -796,11 +796,12 @@ def check_access(options):
|
|||
sys.stderr.write("Please enable CONFIG_TRACING in your kernel "
|
||||
"when using the option -t (default).\n"
|
||||
"If it is enabled, make {0} readable by the "
|
||||
"current user.\n")
|
||||
"current user.\n"
|
||||
.format(PATH_DEBUGFS_TRACING))
|
||||
if options.tracepoints:
|
||||
sys.exit(1)
|
||||
|
||||
sys.stderr.write("Falling back to debugfs statistics!\n"
|
||||
sys.stderr.write("Falling back to debugfs statistics!\n")
|
||||
options.debugfs = True
|
||||
sleep(5)
|
||||
|
||||
|
|
|
@ -366,26 +366,30 @@ static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend,
|
|||
Error **errp)
|
||||
{
|
||||
const char *name = qemu_opt_get(opts, "name");
|
||||
ChardevSpiceChannel *spicevmc;
|
||||
|
||||
if (name == NULL) {
|
||||
error_setg(errp, "chardev: spice channel: no name given");
|
||||
return;
|
||||
}
|
||||
backend->u.spicevmc = g_new0(ChardevSpiceChannel, 1);
|
||||
backend->u.spicevmc->type = g_strdup(name);
|
||||
spicevmc = backend->u.spicevmc = g_new0(ChardevSpiceChannel, 1);
|
||||
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,
|
||||
Error **errp)
|
||||
{
|
||||
const char *name = qemu_opt_get(opts, "name");
|
||||
ChardevSpicePort *spiceport;
|
||||
|
||||
if (name == NULL) {
|
||||
error_setg(errp, "chardev: spice port: no name given");
|
||||
return;
|
||||
}
|
||||
backend->u.spiceport = g_new0(ChardevSpicePort, 1);
|
||||
backend->u.spiceport->fqdn = g_strdup(name);
|
||||
spiceport = backend->u.spiceport = g_new0(ChardevSpicePort, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevSpicePort_base(spiceport));
|
||||
spiceport->fqdn = g_strdup(name);
|
||||
}
|
||||
|
||||
static void register_types(void)
|
||||
|
|
|
@ -470,19 +470,26 @@ static const X86RegisterInfo32 x86_reg_info_32[CPU_NB_REGS32] = {
|
|||
#undef REGISTER
|
||||
|
||||
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 },
|
||||
[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 },
|
||||
[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 },
|
||||
[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 },
|
||||
[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 },
|
||||
[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 },
|
||||
[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 },
|
||||
};
|
||||
|
||||
|
@ -2480,7 +2487,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
|
|||
*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;
|
||||
} else if (count == 1) {
|
||||
*eax = env->features[FEAT_XSAVE];
|
||||
|
@ -2714,15 +2721,15 @@ static void x86_cpu_reset(CPUState *s)
|
|||
cpu_watchpoint_remove_all(s, BP_CPU);
|
||||
|
||||
cr4 = 0;
|
||||
xcr0 = XSTATE_FP;
|
||||
xcr0 = XSTATE_FP_MASK;
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* Enable all the features for user-mode. */
|
||||
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) {
|
||||
xcr0 |= XSTATE_BNDREGS | XSTATE_BNDCSR;
|
||||
xcr0 |= XSTATE_BNDREGS_MASK | XSTATE_BNDCSR_MASK;
|
||||
}
|
||||
if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) {
|
||||
cr4 |= CR4_OSFXSR_MASK | CR4_OSXSAVE_MASK;
|
||||
|
|
|
@ -404,16 +404,25 @@
|
|||
#define MSR_IA32_BNDCFGS 0x00000d90
|
||||
#define MSR_IA32_XSS 0x00000da0
|
||||
|
||||
#define XSTATE_FP (1ULL << 0)
|
||||
#define XSTATE_SSE (1ULL << 1)
|
||||
#define XSTATE_YMM (1ULL << 2)
|
||||
#define XSTATE_BNDREGS (1ULL << 3)
|
||||
#define XSTATE_BNDCSR (1ULL << 4)
|
||||
#define XSTATE_OPMASK (1ULL << 5)
|
||||
#define XSTATE_ZMM_Hi256 (1ULL << 6)
|
||||
#define XSTATE_Hi16_ZMM (1ULL << 7)
|
||||
#define XSTATE_PKRU (1ULL << 9)
|
||||
#define XSTATE_FP_BIT 0
|
||||
#define XSTATE_SSE_BIT 1
|
||||
#define XSTATE_YMM_BIT 2
|
||||
#define XSTATE_BNDREGS_BIT 3
|
||||
#define XSTATE_BNDCSR_BIT 4
|
||||
#define XSTATE_OPMASK_BIT 5
|
||||
#define XSTATE_ZMM_Hi256_BIT 6
|
||||
#define XSTATE_Hi16_ZMM_BIT 7
|
||||
#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 */
|
||||
typedef enum FeatureWord {
|
||||
|
|
|
@ -1215,7 +1215,7 @@ static uint64_t get_xinuse(CPUX86State *env)
|
|||
indicate in use. That said, the state of BNDREGS is important
|
||||
enough to track in HFLAGS, so we might as well use that here. */
|
||||
if ((env->hflags & HF_MPX_IU_MASK) == 0) {
|
||||
inuse &= ~XSTATE_BNDREGS;
|
||||
inuse &= ~XSTATE_BNDREGS_MASK;
|
||||
}
|
||||
return inuse;
|
||||
}
|
||||
|
@ -1239,22 +1239,22 @@ static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm,
|
|||
rfbm &= env->xcr0;
|
||||
opt &= rfbm;
|
||||
|
||||
if (opt & XSTATE_FP) {
|
||||
if (opt & XSTATE_FP_MASK) {
|
||||
do_xsave_fpu(env, ptr, ra);
|
||||
}
|
||||
if (rfbm & XSTATE_SSE) {
|
||||
if (rfbm & XSTATE_SSE_MASK) {
|
||||
/* Note that saving MXCSR is not suppressed by XSAVEOPT. */
|
||||
do_xsave_mxcsr(env, ptr, ra);
|
||||
}
|
||||
if (opt & XSTATE_SSE) {
|
||||
if (opt & XSTATE_SSE_MASK) {
|
||||
do_xsave_sse(env, ptr, ra);
|
||||
}
|
||||
if (opt & XSTATE_BNDREGS) {
|
||||
target_ulong off = x86_ext_save_areas[XSTATE_BNDREGS].offset;
|
||||
if (opt & XSTATE_BNDREGS_MASK) {
|
||||
target_ulong off = x86_ext_save_areas[XSTATE_BNDREGS_BIT].offset;
|
||||
do_xsave_bndregs(env, ptr + off, ra);
|
||||
}
|
||||
if (opt & XSTATE_BNDCSR) {
|
||||
target_ulong off = x86_ext_save_areas[XSTATE_BNDCSR].offset;
|
||||
if (opt & XSTATE_BNDCSR_MASK) {
|
||||
target_ulong off = x86_ext_save_areas[XSTATE_BNDCSR_BIT].offset;
|
||||
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);
|
||||
}
|
||||
|
||||
if (rfbm & XSTATE_FP) {
|
||||
if (xstate_bv & XSTATE_FP) {
|
||||
if (rfbm & XSTATE_FP_MASK) {
|
||||
if (xstate_bv & XSTATE_FP_MASK) {
|
||||
do_xrstor_fpu(env, ptr, ra);
|
||||
} else {
|
||||
helper_fninit(env);
|
||||
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
|
||||
whether or not the XSTATE_BV bit is set. */
|
||||
do_xrstor_mxcsr(env, ptr, ra);
|
||||
if (xstate_bv & XSTATE_SSE) {
|
||||
if (xstate_bv & XSTATE_SSE_MASK) {
|
||||
do_xrstor_sse(env, ptr, ra);
|
||||
} else {
|
||||
/* ??? 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));
|
||||
}
|
||||
}
|
||||
if (rfbm & XSTATE_BNDREGS) {
|
||||
if (xstate_bv & XSTATE_BNDREGS) {
|
||||
target_ulong off = x86_ext_save_areas[XSTATE_BNDREGS].offset;
|
||||
if (rfbm & XSTATE_BNDREGS_MASK) {
|
||||
if (xstate_bv & XSTATE_BNDREGS_MASK) {
|
||||
target_ulong off = x86_ext_save_areas[XSTATE_BNDREGS_BIT].offset;
|
||||
do_xrstor_bndregs(env, ptr + off, ra);
|
||||
env->hflags |= HF_MPX_IU_MASK;
|
||||
} else {
|
||||
|
@ -1429,9 +1429,9 @@ void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm)
|
|||
env->hflags &= ~HF_MPX_IU_MASK;
|
||||
}
|
||||
}
|
||||
if (rfbm & XSTATE_BNDCSR) {
|
||||
if (xstate_bv & XSTATE_BNDCSR) {
|
||||
target_ulong off = x86_ext_save_areas[XSTATE_BNDCSR].offset;
|
||||
if (rfbm & XSTATE_BNDCSR_MASK) {
|
||||
if (xstate_bv & XSTATE_BNDCSR_MASK) {
|
||||
target_ulong off = x86_ext_save_areas[XSTATE_BNDCSR_BIT].offset;
|
||||
do_xrstor_bndcsr(env, ptr + off, ra);
|
||||
} else {
|
||||
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. */
|
||||
if (ecx != 0 || (mask & XSTATE_FP) == 0) {
|
||||
if (ecx != 0 || (mask & XSTATE_FP_MASK) == 0) {
|
||||
goto do_gpf;
|
||||
}
|
||||
|
||||
|
@ -1482,7 +1482,8 @@ void helper_xsetbv(CPUX86State *env, uint32_t ecx, uint64_t mask)
|
|||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ void cpu_sync_bndcs_hflags(CPUX86State *env)
|
|||
}
|
||||
|
||||
if ((env->cr[4] & CR4_OSXSAVE_MASK)
|
||||
&& (env->xcr0 & XSTATE_BNDCSR)
|
||||
&& (env->xcr0 & XSTATE_BNDCSR_MASK)
|
||||
&& (bndcsr & BNDCFG_ENABLE)) {
|
||||
hflags |= HF_MPX_EN_MASK;
|
||||
} else {
|
||||
|
|
20
ui/console.c
20
ui/console.c
|
@ -2060,31 +2060,33 @@ static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend,
|
|||
Error **errp)
|
||||
{
|
||||
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);
|
||||
if (val != 0) {
|
||||
backend->u.vc->has_width = true;
|
||||
backend->u.vc->width = val;
|
||||
vc->has_width = true;
|
||||
vc->width = val;
|
||||
}
|
||||
|
||||
val = qemu_opt_get_number(opts, "height", 0);
|
||||
if (val != 0) {
|
||||
backend->u.vc->has_height = true;
|
||||
backend->u.vc->height = val;
|
||||
vc->has_height = true;
|
||||
vc->height = val;
|
||||
}
|
||||
|
||||
val = qemu_opt_get_number(opts, "cols", 0);
|
||||
if (val != 0) {
|
||||
backend->u.vc->has_cols = true;
|
||||
backend->u.vc->cols = val;
|
||||
vc->has_cols = true;
|
||||
vc->cols = val;
|
||||
}
|
||||
|
||||
val = qemu_opt_get_number(opts, "rows", 0);
|
||||
if (val != 0) {
|
||||
backend->u.vc->has_rows = true;
|
||||
backend->u.vc->rows = val;
|
||||
vc->has_rows = true;
|
||||
vc->rows = val;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
11
util/log.c
11
util/log.c
|
@ -56,13 +56,20 @@ void do_qemu_set_log(int log_flags, bool use_own_buffers)
|
|||
#ifdef CONFIG_TRACE_LOG
|
||||
qemu_loglevel |= LOG_TRACE;
|
||||
#endif
|
||||
if (qemu_loglevel && !qemu_logfile) {
|
||||
if ((qemu_loglevel || is_daemonized()) && !qemu_logfile) {
|
||||
if (logfilename) {
|
||||
qemu_logfile = fopen(logfilename, log_append ? "a" : "w");
|
||||
if (!qemu_logfile) {
|
||||
perror(logfilename);
|
||||
_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 {
|
||||
/* Default to stderr if no log file specified */
|
||||
qemu_logfile = stderr;
|
||||
|
@ -82,7 +89,7 @@ void do_qemu_set_log(int log_flags, bool use_own_buffers)
|
|||
log_append = 1;
|
||||
}
|
||||
}
|
||||
if (!qemu_loglevel && qemu_logfile) {
|
||||
if (!qemu_loglevel && !is_daemonized() && qemu_logfile) {
|
||||
qemu_log_close();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue