* configure fix for environment variables (Daniel)
* fix memory leaks (Alex) * x86_64 MTTCG fixes (Emilio) * introduce atomic64 (Emilio) * Fix for virtio hang (Fam, myself) * SH serial port fix (Geert) * Deprecate rotation_rate for scsi-block (Fam) * Extend memory-backend-file availability to all POSIX hosts (Hikaru) * Memory API cleanups and fixes (Igor, Li Qiang, Peter, Philippe) * MSI/IOMMU fix (Jan) * Socket reconnection fixes (Marc-André) * icount fixes (Emilio, myself) * QSP fixes for Coverity (myself) * Some record/replay improovements (Pavel) * Packed struct fixes (Peter) * Windows dump fixes and elf2dmp (Viktor) * kbmclock fix (Yongji) -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQEcBAABAgAGBQJbs6coAAoJEL/70l94x66DaL0IAISiRZcm7SMFTUafivyzQ9Ao vk2SZ64/BUmDI5q5t30NGiVkMzAc0qDunRSqD4FnIvhGl8phFSSYqaN28JFLe4l1 JhX7FdLQgeevYY35hEPjpCEOAR7WD116/NaZ/UZ+7zZ4Z+CtcCEXZefb4dD9vijj M/rH7vXJsulSb7q2Np3hhbai/GL7ZvNURaHOXZpuPE2aJGAcSXhYtAbGHPJ4NKgn qjP3AGTose8cRD0u5smY0JnyL5vcF606+dupIUsnciDC3wF1SPusTMwLRLoEGNA4 lmxTdGbxvvM1TDu/mY70WYXJ2ujC9Suhj1jkftgDTWRZqwQ9N/B4eB2JcQ9WbhQ= =SzeA -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging * configure fix for environment variables (Daniel) * fix memory leaks (Alex) * x86_64 MTTCG fixes (Emilio) * introduce atomic64 (Emilio) * Fix for virtio hang (Fam, myself) * SH serial port fix (Geert) * Deprecate rotation_rate for scsi-block (Fam) * Extend memory-backend-file availability to all POSIX hosts (Hikaru) * Memory API cleanups and fixes (Igor, Li Qiang, Peter, Philippe) * MSI/IOMMU fix (Jan) * Socket reconnection fixes (Marc-André) * icount fixes (Emilio, myself) * QSP fixes for Coverity (myself) * Some record/replay improovements (Pavel) * Packed struct fixes (Peter) * Windows dump fixes and elf2dmp (Viktor) * kbmclock fix (Yongji) # gpg: Signature made Tue 02 Oct 2018 18:13:12 BST # gpg: using RSA key BFFBD25F78C7AE83 # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * remotes/bonzini/tags/for-upstream: (80 commits) hw/scsi/mptendian: Avoid taking address of fields in packed structs cpus: fix TCG kick timer leak docs/devel/memory.txt: Document _with_attrs accessors hw/nvram/fw_cfg: Use memberwise copy of MemoryRegionOps struct memory: Remove old_mmio accessors memory: Fix access_with_adjusted_size(small size) on big-endian memory regions memory: Refactor common shifting code from accessors memory: Use MAKE_64BIT_MASK() virtio: do not take address of packed members replay: replay BH for IDE trim operation hostmem-file: make available memory-backend-file on POSIX-based hosts target/i386: fix translation for icount mode hvf: drop unused variable qom/object: add some interface asserts accel/tcg: Remove dead code lsi53c895a: convert to trace-events scsi-block: Deprecate rotation_rate kvmclock: run KVM_KVMCLOCK_CTRL ioctl in vcpu thread MAINTAINERS: add myself as elf2dmp maintainer contrib: add elf2dmp tool ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
dafd950536
@ -1903,6 +1903,11 @@ S: Maintained
|
||||
F: include/qemu/iova-tree.h
|
||||
F: util/iova-tree.c
|
||||
|
||||
elf2dmp
|
||||
M: Viktor Prutyanov <viktor.prutyanov@phystech.edu>
|
||||
S: Maintained
|
||||
F: contrib/elf2dmp/
|
||||
|
||||
Usermode Emulation
|
||||
------------------
|
||||
Overall
|
||||
|
5
Makefile
5
Makefile
@ -415,6 +415,7 @@ dummy := $(call unnest-vars,, \
|
||||
chardev-obj-y \
|
||||
util-obj-y \
|
||||
qga-obj-y \
|
||||
elf2dmp-obj-y \
|
||||
ivshmem-client-obj-y \
|
||||
ivshmem-server-obj-y \
|
||||
libvhost-user-obj-y \
|
||||
@ -710,6 +711,10 @@ ifneq ($(EXESUF),)
|
||||
qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI)
|
||||
endif
|
||||
|
||||
elf2dmp: LIBS = $(CURL_LIBS)
|
||||
elf2dmp: $(elf2dmp-obj-y)
|
||||
$(call LINK, $^)
|
||||
|
||||
ifdef CONFIG_IVSHMEM
|
||||
ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS)
|
||||
$(call LINK, $^)
|
||||
|
@ -186,6 +186,7 @@ qga-vss-dll-obj-y = qga/
|
||||
|
||||
######################################################################
|
||||
# contrib
|
||||
elf2dmp-obj-y = contrib/elf2dmp/
|
||||
ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/
|
||||
ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/
|
||||
libvhost-user-obj-y = contrib/libvhost-user/
|
||||
|
@ -2009,15 +2009,6 @@ void tb_invalidate_phys_page_fast(struct page_collection *pages,
|
||||
{
|
||||
PageDesc *p;
|
||||
|
||||
#if 0
|
||||
if (1) {
|
||||
qemu_log("modifying code at 0x%x size=%d EIP=%x PC=%08x\n",
|
||||
cpu_single_env->mem_io_vaddr, len,
|
||||
cpu_single_env->eip,
|
||||
cpu_single_env->eip +
|
||||
(intptr_t)cpu_single_env->segs[R_CS].base);
|
||||
}
|
||||
#endif
|
||||
assert_memory_lock();
|
||||
|
||||
p = page_find(start >> TARGET_PAGE_BITS);
|
||||
|
@ -34,6 +34,8 @@ void translator_loop_temp_check(DisasContextBase *db)
|
||||
void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
CPUState *cpu, TranslationBlock *tb)
|
||||
{
|
||||
int bp_insn = 0;
|
||||
|
||||
/* Initialize DisasContext */
|
||||
db->tb = tb;
|
||||
db->pc_first = tb->pc;
|
||||
@ -71,11 +73,13 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||
|
||||
/* Pass breakpoint hits to target for further processing */
|
||||
if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
|
||||
if (!db->singlestep_enabled
|
||||
&& unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
|
||||
CPUBreakpoint *bp;
|
||||
QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
|
||||
if (bp->pc == db->pc_next) {
|
||||
if (ops->breakpoint_check(db, cpu, bp)) {
|
||||
bp_insn = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -118,7 +122,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
|
||||
/* Emit code to exit the TB, as indicated by db->is_jmp. */
|
||||
ops->tb_stop(db, cpu);
|
||||
gen_tb_end(db->tb, db->num_insns);
|
||||
gen_tb_end(db->tb, db->num_insns - bp_insn);
|
||||
|
||||
/* The disas_log hook may use these values rather than recompute. */
|
||||
db->tb->size = db->pc_next - db->pc_first;
|
||||
|
@ -4,7 +4,7 @@ common-obj-$(CONFIG_POSIX) += rng-random.o
|
||||
common-obj-$(CONFIG_TPM) += tpm.o
|
||||
|
||||
common-obj-y += hostmem.o hostmem-ram.o
|
||||
common-obj-$(CONFIG_LINUX) += hostmem-file.o
|
||||
common-obj-$(CONFIG_POSIX) += hostmem-file.o
|
||||
|
||||
common-obj-y += cryptodev.o
|
||||
common-obj-y += cryptodev-builtin.o
|
||||
|
@ -51,7 +51,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
error_setg(errp, "mem-path property not set");
|
||||
return;
|
||||
}
|
||||
#ifndef CONFIG_LINUX
|
||||
#ifndef CONFIG_POSIX
|
||||
error_setg(errp, "-mem-path not supported on this host");
|
||||
#else
|
||||
if (!host_memory_backend_mr_inited(backend)) {
|
||||
|
@ -140,18 +140,22 @@ memfd_backend_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
bc->alloc = memfd_backend_memory_alloc;
|
||||
|
||||
object_class_property_add_bool(oc, "hugetlb",
|
||||
memfd_backend_get_hugetlb,
|
||||
memfd_backend_set_hugetlb,
|
||||
&error_abort);
|
||||
object_class_property_add(oc, "hugetlbsize", "int",
|
||||
memfd_backend_get_hugetlbsize,
|
||||
memfd_backend_set_hugetlbsize,
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_add_bool(oc, "seal",
|
||||
memfd_backend_get_seal,
|
||||
memfd_backend_set_seal,
|
||||
&error_abort);
|
||||
if (qemu_memfd_check(MFD_HUGETLB)) {
|
||||
object_class_property_add_bool(oc, "hugetlb",
|
||||
memfd_backend_get_hugetlb,
|
||||
memfd_backend_set_hugetlb,
|
||||
&error_abort);
|
||||
object_class_property_add(oc, "hugetlbsize", "int",
|
||||
memfd_backend_get_hugetlbsize,
|
||||
memfd_backend_set_hugetlbsize,
|
||||
NULL, NULL, &error_abort);
|
||||
}
|
||||
if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
|
||||
object_class_property_add_bool(oc, "seal",
|
||||
memfd_backend_get_seal,
|
||||
memfd_backend_set_seal,
|
||||
&error_abort);
|
||||
}
|
||||
}
|
||||
|
||||
static const TypeInfo memfd_backend_info = {
|
||||
@ -164,7 +168,9 @@ static const TypeInfo memfd_backend_info = {
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&memfd_backend_info);
|
||||
if (qemu_memfd_check(0)) {
|
||||
type_register_static(&memfd_backend_info);
|
||||
}
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
@ -31,10 +31,6 @@
|
||||
|
||||
#include "chardev/char-io.h"
|
||||
|
||||
#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
|
||||
|| defined(__GLIBC__)
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
QIOChannel *ioc;
|
||||
@ -299,5 +295,3 @@ static void register_types(void)
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
||||
#endif
|
||||
|
@ -32,7 +32,6 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/clone-visitor.h"
|
||||
#include "qapi/qapi-visit-sockets.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
#include "chardev/char-io.h"
|
||||
|
||||
@ -354,6 +353,15 @@ static GSource *tcp_chr_add_watch(Chardev *chr, GIOCondition cond)
|
||||
return qio_channel_create_watch(s->ioc, cond);
|
||||
}
|
||||
|
||||
static void remove_hup_source(SocketChardev *s)
|
||||
{
|
||||
if (s->hup_source != NULL) {
|
||||
g_source_destroy(s->hup_source);
|
||||
g_source_unref(s->hup_source);
|
||||
s->hup_source = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void tcp_chr_free_connection(Chardev *chr)
|
||||
{
|
||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||
@ -368,11 +376,7 @@ static void tcp_chr_free_connection(Chardev *chr)
|
||||
s->read_msgfds_num = 0;
|
||||
}
|
||||
|
||||
if (s->hup_source != NULL) {
|
||||
g_source_destroy(s->hup_source);
|
||||
g_source_unref(s->hup_source);
|
||||
s->hup_source = NULL;
|
||||
}
|
||||
remove_hup_source(s);
|
||||
|
||||
tcp_set_msgfds(chr, NULL, 0);
|
||||
remove_fd_in_watch(chr);
|
||||
@ -541,6 +545,27 @@ static char *sockaddr_to_str(struct sockaddr_storage *ss, socklen_t ss_len,
|
||||
}
|
||||
}
|
||||
|
||||
static void update_ioc_handlers(SocketChardev *s)
|
||||
{
|
||||
Chardev *chr = CHARDEV(s);
|
||||
|
||||
if (!s->connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
remove_fd_in_watch(chr);
|
||||
chr->gsource = io_add_watch_poll(chr, s->ioc,
|
||||
tcp_chr_read_poll,
|
||||
tcp_chr_read, chr,
|
||||
chr->gcontext);
|
||||
|
||||
remove_hup_source(s);
|
||||
s->hup_source = qio_channel_create_watch(s->ioc, G_IO_HUP);
|
||||
g_source_set_callback(s->hup_source, (GSourceFunc)tcp_chr_hup,
|
||||
chr, NULL);
|
||||
g_source_attach(s->hup_source, chr->gcontext);
|
||||
}
|
||||
|
||||
static void tcp_chr_connect(void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
@ -553,16 +578,7 @@ static void tcp_chr_connect(void *opaque)
|
||||
s->is_listen, s->is_telnet);
|
||||
|
||||
s->connected = 1;
|
||||
chr->gsource = io_add_watch_poll(chr, s->ioc,
|
||||
tcp_chr_read_poll,
|
||||
tcp_chr_read,
|
||||
chr, chr->gcontext);
|
||||
|
||||
s->hup_source = qio_channel_create_watch(s->ioc, G_IO_HUP);
|
||||
g_source_set_callback(s->hup_source, (GSourceFunc)tcp_chr_hup,
|
||||
chr, NULL);
|
||||
g_source_attach(s->hup_source, chr->gcontext);
|
||||
|
||||
update_ioc_handlers(s);
|
||||
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
|
||||
}
|
||||
|
||||
@ -593,17 +609,7 @@ static void tcp_chr_update_read_handler(Chardev *chr)
|
||||
tcp_chr_telnet_init(CHARDEV(s));
|
||||
}
|
||||
|
||||
if (!s->connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
remove_fd_in_watch(chr);
|
||||
if (s->ioc) {
|
||||
chr->gsource = io_add_watch_poll(chr, s->ioc,
|
||||
tcp_chr_read_poll,
|
||||
tcp_chr_read, chr,
|
||||
chr->gcontext);
|
||||
}
|
||||
update_ioc_handlers(s);
|
||||
}
|
||||
|
||||
static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
|
||||
@ -724,11 +730,6 @@ static void tcp_chr_tls_init(Chardev *chr)
|
||||
Error *err = NULL;
|
||||
gchar *name;
|
||||
|
||||
if (!machine_init_done) {
|
||||
/* This will be postponed to machine_done notifier */
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->is_listen) {
|
||||
tioc = qio_channel_tls_new_server(
|
||||
s->ioc, s->tls_creds,
|
||||
@ -1011,8 +1012,9 @@ static void qmp_chardev_open_socket(Chardev *chr,
|
||||
s->reconnect_time = reconnect;
|
||||
}
|
||||
|
||||
/* If reconnect_time is set, will do that in chr_machine_done. */
|
||||
if (!s->reconnect_time) {
|
||||
if (s->reconnect_time) {
|
||||
tcp_chr_connect_async(chr);
|
||||
} else {
|
||||
if (s->is_listen) {
|
||||
char *name;
|
||||
s->listener = qio_net_listener_new();
|
||||
@ -1161,21 +1163,6 @@ char_socket_get_connected(Object *obj, Error **errp)
|
||||
return s->connected;
|
||||
}
|
||||
|
||||
static int tcp_chr_machine_done_hook(Chardev *chr)
|
||||
{
|
||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||
|
||||
if (s->reconnect_time) {
|
||||
tcp_chr_connect_async(chr);
|
||||
}
|
||||
|
||||
if (s->ioc && s->tls_creds) {
|
||||
tcp_chr_tls_init(chr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void char_socket_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
@ -1191,7 +1178,6 @@ static void char_socket_class_init(ObjectClass *oc, void *data)
|
||||
cc->chr_add_client = tcp_chr_add_client;
|
||||
cc->chr_add_watch = tcp_chr_add_watch;
|
||||
cc->chr_update_read_handler = tcp_chr_update_read_handler;
|
||||
cc->chr_machine_done = tcp_chr_machine_done_hook;
|
||||
|
||||
object_class_property_add(oc, "addr", "SocketAddress",
|
||||
char_socket_get_addr, NULL,
|
||||
|
45
configure
vendored
45
configure
vendored
@ -5721,6 +5721,9 @@ if test "$want_tools" = "yes" ; then
|
||||
if [ "$ivshmem" = "yes" ]; then
|
||||
tools="ivshmem-client\$(EXESUF) ivshmem-server\$(EXESUF) $tools"
|
||||
fi
|
||||
if [ "$posix" = "yes" ] && [ "$curl" = "yes" ]; then
|
||||
tools="elf2dmp $tools"
|
||||
fi
|
||||
fi
|
||||
if test "$softmmu" = yes ; then
|
||||
if test "$linux" = yes; then
|
||||
@ -7024,12 +7027,14 @@ TARGET_ABI_DIR=""
|
||||
|
||||
case "$target_name" in
|
||||
i386)
|
||||
mttcg="yes"
|
||||
gdb_xml_files="i386-32bit.xml i386-32bit-core.xml i386-32bit-sse.xml"
|
||||
target_compiler=$cross_cc_i386
|
||||
target_compiler_cflags=$cross_cc_ccflags_i386
|
||||
;;
|
||||
x86_64)
|
||||
TARGET_BASE_ARCH=i386
|
||||
mttcg="yes"
|
||||
gdb_xml_files="i386-64bit.xml i386-64bit-core.xml i386-64bit-sse.xml"
|
||||
target_compiler=$cross_cc_x86_64
|
||||
;;
|
||||
@ -7527,6 +7532,46 @@ cat <<EOD >config.status
|
||||
# Compiler output produced by configure, useful for debugging
|
||||
# configure, is in config.log if it exists.
|
||||
EOD
|
||||
|
||||
preserve_env() {
|
||||
envname=$1
|
||||
|
||||
eval envval=\$$envname
|
||||
|
||||
if test -n "$envval"
|
||||
then
|
||||
echo "$envname='$envval'" >> config.status
|
||||
echo "export $envname" >> config.status
|
||||
else
|
||||
echo "unset $envname" >> config.status
|
||||
fi
|
||||
}
|
||||
|
||||
# Preserve various env variables that influence what
|
||||
# features/build target configure will detect
|
||||
preserve_env AR
|
||||
preserve_env AS
|
||||
preserve_env CC
|
||||
preserve_env CPP
|
||||
preserve_env CXX
|
||||
preserve_env INSTALL
|
||||
preserve_env LD
|
||||
preserve_env LD_LIBRARY_PATH
|
||||
preserve_env LIBTOOL
|
||||
preserve_env MAKE
|
||||
preserve_env NM
|
||||
preserve_env OBJCOPY
|
||||
preserve_env PATH
|
||||
preserve_env PKG_CONFIG
|
||||
preserve_env PKG_CONFIG_LIBDIR
|
||||
preserve_env PKG_CONFIG_PATH
|
||||
preserve_env PYTHON
|
||||
preserve_env SDL_CONFIG
|
||||
preserve_env SDL2_CONFIG
|
||||
preserve_env SMBD
|
||||
preserve_env STRIP
|
||||
preserve_env WINDRES
|
||||
|
||||
printf "exec" >>config.status
|
||||
printf " '%s'" "$0" "$@" >>config.status
|
||||
echo ' "$@"' >>config.status
|
||||
|
1
contrib/elf2dmp/Makefile.objs
Normal file
1
contrib/elf2dmp/Makefile.objs
Normal file
@ -0,0 +1 @@
|
||||
elf2dmp-obj-y = main.o addrspace.o download.o pdb.o qemu_elf.o
|
233
contrib/elf2dmp/addrspace.c
Normal file
233
contrib/elf2dmp/addrspace.c
Normal file
@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "addrspace.h"
|
||||
|
||||
static struct pa_block *pa_space_find_block(struct pa_space *ps, uint64_t pa)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < ps->block_nr; i++) {
|
||||
if (ps->block[i].paddr <= pa &&
|
||||
pa <= ps->block[i].paddr + ps->block[i].size) {
|
||||
return ps->block + i;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static uint8_t *pa_space_resolve(struct pa_space *ps, uint64_t pa)
|
||||
{
|
||||
struct pa_block *block = pa_space_find_block(ps, pa);
|
||||
|
||||
if (!block) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return block->addr + (pa - block->paddr);
|
||||
}
|
||||
|
||||
int pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf)
|
||||
{
|
||||
Elf64_Half phdr_nr = elf_getphdrnum(qemu_elf->map);
|
||||
Elf64_Phdr *phdr = elf64_getphdr(qemu_elf->map);
|
||||
size_t block_i = 0;
|
||||
size_t i;
|
||||
|
||||
ps->block_nr = 0;
|
||||
|
||||
for (i = 0; i < phdr_nr; i++) {
|
||||
if (phdr[i].p_type == PT_LOAD) {
|
||||
ps->block_nr++;
|
||||
}
|
||||
}
|
||||
|
||||
ps->block = malloc(sizeof(*ps->block) * ps->block_nr);
|
||||
if (!ps->block) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < phdr_nr; i++) {
|
||||
if (phdr[i].p_type == PT_LOAD) {
|
||||
ps->block[block_i] = (struct pa_block) {
|
||||
.addr = (uint8_t *)qemu_elf->map + phdr[i].p_offset,
|
||||
.paddr = phdr[i].p_paddr,
|
||||
.size = phdr[i].p_filesz,
|
||||
};
|
||||
block_i++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pa_space_destroy(struct pa_space *ps)
|
||||
{
|
||||
ps->block_nr = 0;
|
||||
free(ps->block);
|
||||
}
|
||||
|
||||
void va_space_set_dtb(struct va_space *vs, uint64_t dtb)
|
||||
{
|
||||
vs->dtb = dtb & 0x00ffffffffff000;
|
||||
}
|
||||
|
||||
void va_space_create(struct va_space *vs, struct pa_space *ps, uint64_t dtb)
|
||||
{
|
||||
vs->ps = ps;
|
||||
va_space_set_dtb(vs, dtb);
|
||||
}
|
||||
|
||||
static uint64_t get_pml4e(struct va_space *vs, uint64_t va)
|
||||
{
|
||||
uint64_t pa = (vs->dtb & 0xffffffffff000) | ((va & 0xff8000000000) >> 36);
|
||||
|
||||
return *(uint64_t *)pa_space_resolve(vs->ps, pa);
|
||||
}
|
||||
|
||||
static uint64_t get_pdpi(struct va_space *vs, uint64_t va, uint64_t pml4e)
|
||||
{
|
||||
uint64_t pdpte_paddr = (pml4e & 0xffffffffff000) |
|
||||
((va & 0x7FC0000000) >> 27);
|
||||
|
||||
return *(uint64_t *)pa_space_resolve(vs->ps, pdpte_paddr);
|
||||
}
|
||||
|
||||
static uint64_t pde_index(uint64_t va)
|
||||
{
|
||||
return (va >> 21) & 0x1FF;
|
||||
}
|
||||
|
||||
static uint64_t pdba_base(uint64_t pdpe)
|
||||
{
|
||||
return pdpe & 0xFFFFFFFFFF000;
|
||||
}
|
||||
|
||||
static uint64_t get_pgd(struct va_space *vs, uint64_t va, uint64_t pdpe)
|
||||
{
|
||||
uint64_t pgd_entry = pdba_base(pdpe) + pde_index(va) * 8;
|
||||
|
||||
return *(uint64_t *)pa_space_resolve(vs->ps, pgd_entry);
|
||||
}
|
||||
|
||||
static uint64_t pte_index(uint64_t va)
|
||||
{
|
||||
return (va >> 12) & 0x1FF;
|
||||
}
|
||||
|
||||
static uint64_t ptba_base(uint64_t pde)
|
||||
{
|
||||
return pde & 0xFFFFFFFFFF000;
|
||||
}
|
||||
|
||||
static uint64_t get_pte(struct va_space *vs, uint64_t va, uint64_t pgd)
|
||||
{
|
||||
uint64_t pgd_val = ptba_base(pgd) + pte_index(va) * 8;
|
||||
|
||||
return *(uint64_t *)pa_space_resolve(vs->ps, pgd_val);
|
||||
}
|
||||
|
||||
static uint64_t get_paddr(uint64_t va, uint64_t pte)
|
||||
{
|
||||
return (pte & 0xFFFFFFFFFF000) | (va & 0xFFF);
|
||||
}
|
||||
|
||||
static bool is_present(uint64_t entry)
|
||||
{
|
||||
return entry & 0x1;
|
||||
}
|
||||
|
||||
static bool page_size_flag(uint64_t entry)
|
||||
{
|
||||
return entry & (1 << 7);
|
||||
}
|
||||
|
||||
static uint64_t get_1GB_paddr(uint64_t va, uint64_t pdpte)
|
||||
{
|
||||
return (pdpte & 0xfffffc0000000) | (va & 0x3fffffff);
|
||||
}
|
||||
|
||||
static uint64_t get_2MB_paddr(uint64_t va, uint64_t pgd_entry)
|
||||
{
|
||||
return (pgd_entry & 0xfffffffe00000) | (va & 0x00000001fffff);
|
||||
}
|
||||
|
||||
static uint64_t va_space_va2pa(struct va_space *vs, uint64_t va)
|
||||
{
|
||||
uint64_t pml4e, pdpe, pgd, pte;
|
||||
|
||||
pml4e = get_pml4e(vs, va);
|
||||
if (!is_present(pml4e)) {
|
||||
return INVALID_PA;
|
||||
}
|
||||
|
||||
pdpe = get_pdpi(vs, va, pml4e);
|
||||
if (!is_present(pdpe)) {
|
||||
return INVALID_PA;
|
||||
}
|
||||
|
||||
if (page_size_flag(pdpe)) {
|
||||
return get_1GB_paddr(va, pdpe);
|
||||
}
|
||||
|
||||
pgd = get_pgd(vs, va, pdpe);
|
||||
if (!is_present(pgd)) {
|
||||
return INVALID_PA;
|
||||
}
|
||||
|
||||
if (page_size_flag(pgd)) {
|
||||
return get_2MB_paddr(va, pgd);
|
||||
}
|
||||
|
||||
pte = get_pte(vs, va, pgd);
|
||||
if (!is_present(pte)) {
|
||||
return INVALID_PA;
|
||||
}
|
||||
|
||||
return get_paddr(va, pte);
|
||||
}
|
||||
|
||||
void *va_space_resolve(struct va_space *vs, uint64_t va)
|
||||
{
|
||||
uint64_t pa = va_space_va2pa(vs, va);
|
||||
|
||||
if (pa == INVALID_PA) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pa_space_resolve(vs->ps, pa);
|
||||
}
|
||||
|
||||
int va_space_rw(struct va_space *vs, uint64_t addr,
|
||||
void *buf, size_t size, int is_write)
|
||||
{
|
||||
while (size) {
|
||||
uint64_t page = addr & PFN_MASK;
|
||||
size_t s = (page + PAGE_SIZE) - addr;
|
||||
void *ptr;
|
||||
|
||||
s = (s > size) ? size : s;
|
||||
|
||||
ptr = va_space_resolve(vs, addr);
|
||||
if (!ptr) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (is_write) {
|
||||
memcpy(ptr, buf, s);
|
||||
} else {
|
||||
memcpy(buf, ptr, s);
|
||||
}
|
||||
|
||||
size -= s;
|
||||
buf = (uint8_t *)buf + s;
|
||||
addr += s;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
44
contrib/elf2dmp/addrspace.h
Normal file
44
contrib/elf2dmp/addrspace.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ADDRSPACE_H
|
||||
#define ADDRSPACE_H
|
||||
|
||||
#include "qemu_elf.h"
|
||||
|
||||
#define PAGE_BITS 12
|
||||
#define PAGE_SIZE (1ULL << PAGE_BITS)
|
||||
#define PFN_MASK (~(PAGE_SIZE - 1))
|
||||
|
||||
#define INVALID_PA UINT64_MAX
|
||||
|
||||
struct pa_block {
|
||||
uint8_t *addr;
|
||||
uint64_t paddr;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
struct pa_space {
|
||||
size_t block_nr;
|
||||
struct pa_block *block;
|
||||
};
|
||||
|
||||
struct va_space {
|
||||
uint64_t dtb;
|
||||
struct pa_space *ps;
|
||||
};
|
||||
|
||||
int pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf);
|
||||
void pa_space_destroy(struct pa_space *ps);
|
||||
|
||||
void va_space_create(struct va_space *vs, struct pa_space *ps, uint64_t dtb);
|
||||
void va_space_set_dtb(struct va_space *vs, uint64_t dtb);
|
||||
void *va_space_resolve(struct va_space *vs, uint64_t va);
|
||||
int va_space_rw(struct va_space *vs, uint64_t addr,
|
||||
void *buf, size_t size, int is_write);
|
||||
|
||||
#endif /* ADDRSPACE_H */
|
47
contrib/elf2dmp/download.c
Normal file
47
contrib/elf2dmp/download.c
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <curl/curl.h>
|
||||
#include "download.h"
|
||||
|
||||
int download_url(const char *name, const char *url)
|
||||
{
|
||||
int err = 0;
|
||||
FILE *file;
|
||||
CURL *curl = curl_easy_init();
|
||||
|
||||
if (!curl) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
file = fopen(name, "wb");
|
||||
if (!file) {
|
||||
err = 1;
|
||||
goto out_curl;
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
|
||||
|
||||
if (curl_easy_perform(curl) != CURLE_OK) {
|
||||
err = 1;
|
||||
fclose(file);
|
||||
unlink(name);
|
||||
goto out_curl;
|
||||
}
|
||||
|
||||
err = fclose(file);
|
||||
|
||||
out_curl:
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
return err;
|
||||
}
|
13
contrib/elf2dmp/download.h
Normal file
13
contrib/elf2dmp/download.h
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DOWNLOAD_H
|
||||
#define DOWNLOAD_H
|
||||
|
||||
int download_url(const char *name, const char *url);
|
||||
|
||||
#endif /* DOWNLOAD_H */
|
13
contrib/elf2dmp/err.h
Normal file
13
contrib/elf2dmp/err.h
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ERR_H
|
||||
#define ERR_H
|
||||
|
||||
#define eprintf(...) fprintf(stderr, __VA_ARGS__)
|
||||
|
||||
#endif /* ERR_H */
|
194
contrib/elf2dmp/kdbg.h
Normal file
194
contrib/elf2dmp/kdbg.h
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef KDBG_H
|
||||
#define KDBG_H
|
||||
|
||||
typedef struct DBGKD_GET_VERSION64 {
|
||||
uint16_t MajorVersion;
|
||||
uint16_t MinorVersion;
|
||||
uint8_t ProtocolVersion;
|
||||
uint8_t KdSecondaryVersion;
|
||||
uint16_t Flags;
|
||||
uint16_t MachineType;
|
||||
uint8_t MaxPacketType;
|
||||
uint8_t MaxStateChange;
|
||||
uint8_t MaxManipulate;
|
||||
uint8_t Simulation;
|
||||
uint16_t Unused[1];
|
||||
uint64_t KernBase;
|
||||
uint64_t PsLoadedModuleList;
|
||||
uint64_t DebuggerDataList;
|
||||
} DBGKD_GET_VERSION64;
|
||||
|
||||
typedef struct DBGKD_DEBUG_DATA_HEADER64 {
|
||||
struct LIST_ENTRY64 {
|
||||
struct LIST_ENTRY64 *Flink;
|
||||
struct LIST_ENTRY64 *Blink;
|
||||
} List;
|
||||
uint32_t OwnerTag;
|
||||
uint32_t Size;
|
||||
} DBGKD_DEBUG_DATA_HEADER64;
|
||||
|
||||
typedef struct KDDEBUGGER_DATA64 {
|
||||
DBGKD_DEBUG_DATA_HEADER64 Header;
|
||||
|
||||
uint64_t KernBase;
|
||||
uint64_t BreakpointWithStatus;
|
||||
uint64_t SavedContext;
|
||||
uint16_t ThCallbackStack;
|
||||
uint16_t NextCallback;
|
||||
uint16_t FramePointer;
|
||||
uint16_t PaeEnabled:1;
|
||||
uint64_t KiCallUserMode;
|
||||
uint64_t KeUserCallbackDispatcher;
|
||||
uint64_t PsLoadedModuleList;
|
||||
uint64_t PsActiveProcessHead;
|
||||
uint64_t PspCidTable;
|
||||
uint64_t ExpSystemResourcesList;
|
||||
uint64_t ExpPagedPoolDescriptor;
|
||||
uint64_t ExpNumberOfPagedPools;
|
||||
uint64_t KeTimeIncrement;
|
||||
uint64_t KeBugCheckCallbackListHead;
|
||||
uint64_t KiBugcheckData;
|
||||
uint64_t IopErrorLogListHead;
|
||||
uint64_t ObpRootDirectoryObject;
|
||||
uint64_t ObpTypeObjectType;
|
||||
uint64_t MmSystemCacheStart;
|
||||
uint64_t MmSystemCacheEnd;
|
||||
uint64_t MmSystemCacheWs;
|
||||
uint64_t MmPfnDatabase;
|
||||
uint64_t MmSystemPtesStart;
|
||||
uint64_t MmSystemPtesEnd;
|
||||
uint64_t MmSubsectionBase;
|
||||
uint64_t MmNumberOfPagingFiles;
|
||||
uint64_t MmLowestPhysicalPage;
|
||||
uint64_t MmHighestPhysicalPage;
|
||||
uint64_t MmNumberOfPhysicalPages;
|
||||
uint64_t MmMaximumNonPagedPoolInBytes;
|
||||
uint64_t MmNonPagedSystemStart;
|
||||
uint64_t MmNonPagedPoolStart;
|
||||
uint64_t MmNonPagedPoolEnd;
|
||||
uint64_t MmPagedPoolStart;
|
||||
uint64_t MmPagedPoolEnd;
|
||||
uint64_t MmPagedPoolInformation;
|
||||
uint64_t MmPageSize;
|
||||
uint64_t MmSizeOfPagedPoolInBytes;
|
||||
uint64_t MmTotalCommitLimit;
|
||||
uint64_t MmTotalCommittedPages;
|
||||
uint64_t MmSharedCommit;
|
||||
uint64_t MmDriverCommit;
|
||||
uint64_t MmProcessCommit;
|
||||
uint64_t MmPagedPoolCommit;
|
||||
uint64_t MmExtendedCommit;
|
||||
uint64_t MmZeroedPageListHead;
|
||||
uint64_t MmFreePageListHead;
|
||||
uint64_t MmStandbyPageListHead;
|
||||
uint64_t MmModifiedPageListHead;
|
||||
uint64_t MmModifiedNoWritePageListHead;
|
||||
uint64_t MmAvailablePages;
|
||||
uint64_t MmResidentAvailablePages;
|
||||
uint64_t PoolTrackTable;
|
||||
uint64_t NonPagedPoolDescriptor;
|
||||
uint64_t MmHighestUserAddress;
|
||||
uint64_t MmSystemRangeStart;
|
||||
uint64_t MmUserProbeAddress;
|
||||
uint64_t KdPrintCircularBuffer;
|
||||
uint64_t KdPrintCircularBufferEnd;
|
||||
uint64_t KdPrintWritePointer;
|
||||
uint64_t KdPrintRolloverCount;
|
||||
uint64_t MmLoadedUserImageList;
|
||||
|
||||
/* NT 5.1 Addition */
|
||||
|
||||
uint64_t NtBuildLab;
|
||||
uint64_t KiNormalSystemCall;
|
||||
|
||||
/* NT 5.0 hotfix addition */
|
||||
|
||||
uint64_t KiProcessorBlock;
|
||||
uint64_t MmUnloadedDrivers;
|
||||
uint64_t MmLastUnloadedDriver;
|
||||
uint64_t MmTriageActionTaken;
|
||||
uint64_t MmSpecialPoolTag;
|
||||
uint64_t KernelVerifier;
|
||||
uint64_t MmVerifierData;
|
||||
uint64_t MmAllocatedNonPagedPool;
|
||||
uint64_t MmPeakCommitment;
|
||||
uint64_t MmTotalCommitLimitMaximum;
|
||||
uint64_t CmNtCSDVersion;
|
||||
|
||||
/* NT 5.1 Addition */
|
||||
|
||||
uint64_t MmPhysicalMemoryBlock;
|
||||
uint64_t MmSessionBase;
|
||||
uint64_t MmSessionSize;
|
||||
uint64_t MmSystemParentTablePage;
|
||||
|
||||
/* Server 2003 addition */
|
||||
|
||||
uint64_t MmVirtualTranslationBase;
|
||||
uint16_t OffsetKThreadNextProcessor;
|
||||
uint16_t OffsetKThreadTeb;
|
||||
uint16_t OffsetKThreadKernelStack;
|
||||
uint16_t OffsetKThreadInitialStack;
|
||||
uint16_t OffsetKThreadApcProcess;
|
||||
uint16_t OffsetKThreadState;
|
||||
uint16_t OffsetKThreadBStore;
|
||||
uint16_t OffsetKThreadBStoreLimit;
|
||||
uint16_t SizeEProcess;
|
||||
uint16_t OffsetEprocessPeb;
|
||||
uint16_t OffsetEprocessParentCID;
|
||||
uint16_t OffsetEprocessDirectoryTableBase;
|
||||
uint16_t SizePrcb;
|
||||
uint16_t OffsetPrcbDpcRoutine;
|
||||
uint16_t OffsetPrcbCurrentThread;
|
||||
uint16_t OffsetPrcbMhz;
|
||||
uint16_t OffsetPrcbCpuType;
|
||||
uint16_t OffsetPrcbVendorString;
|
||||
uint16_t OffsetPrcbProcStateContext;
|
||||
uint16_t OffsetPrcbNumber;
|
||||
uint16_t SizeEThread;
|
||||
uint64_t KdPrintCircularBufferPtr;
|
||||
uint64_t KdPrintBufferSize;
|
||||
uint64_t KeLoaderBlock;
|
||||
uint16_t SizePcr;
|
||||
uint16_t OffsetPcrSelfPcr;
|
||||
uint16_t OffsetPcrCurrentPrcb;
|
||||
uint16_t OffsetPcrContainedPrcb;
|
||||
uint16_t OffsetPcrInitialBStore;
|
||||
uint16_t OffsetPcrBStoreLimit;
|
||||
uint16_t OffsetPcrInitialStack;
|
||||
uint16_t OffsetPcrStackLimit;
|
||||
uint16_t OffsetPrcbPcrPage;
|
||||
uint16_t OffsetPrcbProcStateSpecialReg;
|
||||
uint16_t GdtR0Code;
|
||||
uint16_t GdtR0Data;
|
||||
uint16_t GdtR0Pcr;
|
||||
uint16_t GdtR3Code;
|
||||
uint16_t GdtR3Data;
|
||||
uint16_t GdtR3Teb;
|
||||
uint16_t GdtLdt;
|
||||
uint16_t GdtTss;
|
||||
uint16_t Gdt64R3CmCode;
|
||||
uint16_t Gdt64R3CmTeb;
|
||||
uint64_t IopNumTriageDumpDataBlocks;
|
||||
uint64_t IopTriageDumpDataBlocks;
|
||||
|
||||
/* Longhorn addition */
|
||||
|
||||
uint64_t VfCrashDataBlock;
|
||||
uint64_t MmBadPagesDetected;
|
||||
uint64_t MmZeroedPageSingleBitErrorsDetected;
|
||||
|
||||
/* Windows 7 addition */
|
||||
|
||||
uint64_t EtwpDebuggerData;
|
||||
uint16_t OffsetPrcbContext;
|
||||
} KDDEBUGGER_DATA64;
|
||||
|
||||
#endif /* KDBG_H */
|
589
contrib/elf2dmp/main.c
Normal file
589
contrib/elf2dmp/main.c
Normal file
@ -0,0 +1,589 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "err.h"
|
||||
#include "addrspace.h"
|
||||
#include "pe.h"
|
||||
#include "pdb.h"
|
||||
#include "kdbg.h"
|
||||
#include "download.h"
|
||||
#include "qemu/win_dump_defs.h"
|
||||
|
||||
#define SYM_URL_BASE "https://msdl.microsoft.com/download/symbols/"
|
||||
#define PDB_NAME "ntkrnlmp.pdb"
|
||||
|
||||
#define INITIAL_MXCSR 0x1f80
|
||||
|
||||
typedef struct idt_desc {
|
||||
uint16_t offset1; /* offset bits 0..15 */
|
||||
uint16_t selector;
|
||||
uint8_t ist;
|
||||
uint8_t type_attr;
|
||||
uint16_t offset2; /* offset bits 16..31 */
|
||||
uint32_t offset3; /* offset bits 32..63 */
|
||||
uint32_t rsrvd;
|
||||
} __attribute__ ((packed)) idt_desc_t;
|
||||
|
||||
static uint64_t idt_desc_addr(idt_desc_t desc)
|
||||
{
|
||||
return (uint64_t)desc.offset1 | ((uint64_t)desc.offset2 << 16) |
|
||||
((uint64_t)desc.offset3 << 32);
|
||||
}
|
||||
|
||||
static const uint64_t SharedUserData = 0xfffff78000000000;
|
||||
|
||||
#define KUSD_OFFSET_SUITE_MASK 0x2d0
|
||||
#define KUSD_OFFSET_PRODUCT_TYPE 0x264
|
||||
|
||||
#define SYM_RESOLVE(base, r, s) ((s = pdb_resolve(base, r, #s)),\
|
||||
s ? printf(#s" = 0x%016lx\n", s) : eprintf("Failed to resolve "#s"\n"), s)
|
||||
|
||||
static uint64_t rol(uint64_t x, uint64_t y)
|
||||
{
|
||||
return (x << y) | (x >> (64 - y));
|
||||
}
|
||||
|
||||
/*
|
||||
* Decoding algorithm can be found in Volatility project
|
||||
*/
|
||||
static void kdbg_decode(uint64_t *dst, uint64_t *src, size_t size,
|
||||
uint64_t kwn, uint64_t kwa, uint64_t kdbe)
|
||||
{
|
||||
size_t i;
|
||||
assert(size % sizeof(uint64_t) == 0);
|
||||
for (i = 0; i < size / sizeof(uint64_t); i++) {
|
||||
uint64_t block;
|
||||
|
||||
block = src[i];
|
||||
block = rol(block ^ kwn, (uint8_t)kwn);
|
||||
block = __builtin_bswap64(block ^ kdbe) ^ kwa;
|
||||
dst[i] = block;
|
||||
}
|
||||
}
|
||||
|
||||
static KDDEBUGGER_DATA64 *get_kdbg(uint64_t KernBase, struct pdb_reader *pdb,
|
||||
struct va_space *vs, uint64_t KdDebuggerDataBlock)
|
||||
{
|
||||
const char OwnerTag[4] = "KDBG";
|
||||
KDDEBUGGER_DATA64 *kdbg = NULL;
|
||||
DBGKD_DEBUG_DATA_HEADER64 kdbg_hdr;
|
||||
bool decode = false;
|
||||
uint64_t kwn, kwa, KdpDataBlockEncoded;
|
||||
|
||||
if (va_space_rw(vs,
|
||||
KdDebuggerDataBlock + offsetof(KDDEBUGGER_DATA64, Header),
|
||||
&kdbg_hdr, sizeof(kdbg_hdr), 0)) {
|
||||
eprintf("Failed to extract KDBG header\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (memcmp(&kdbg_hdr.OwnerTag, OwnerTag, sizeof(OwnerTag))) {
|
||||
uint64_t KiWaitNever, KiWaitAlways;
|
||||
|
||||
decode = true;
|
||||
|
||||
if (!SYM_RESOLVE(KernBase, pdb, KiWaitNever) ||
|
||||
!SYM_RESOLVE(KernBase, pdb, KiWaitAlways) ||
|
||||
!SYM_RESOLVE(KernBase, pdb, KdpDataBlockEncoded)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (va_space_rw(vs, KiWaitNever, &kwn, sizeof(kwn), 0) ||
|
||||
va_space_rw(vs, KiWaitAlways, &kwa, sizeof(kwa), 0)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
printf("[KiWaitNever] = 0x%016lx\n", kwn);
|
||||
printf("[KiWaitAlways] = 0x%016lx\n", kwa);
|
||||
|
||||
/*
|
||||
* If KDBG header can be decoded, KDBG size is available
|
||||
* and entire KDBG can be decoded.
|
||||
*/
|
||||
printf("Decoding KDBG header...\n");
|
||||
kdbg_decode((uint64_t *)&kdbg_hdr, (uint64_t *)&kdbg_hdr,
|
||||
sizeof(kdbg_hdr), kwn, kwa, KdpDataBlockEncoded);
|
||||
|
||||
printf("Owner tag is \'%.4s\'\n", (char *)&kdbg_hdr.OwnerTag);
|
||||
if (memcmp(&kdbg_hdr.OwnerTag, OwnerTag, sizeof(OwnerTag))) {
|
||||
eprintf("Failed to decode KDBG header\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
kdbg = malloc(kdbg_hdr.Size);
|
||||
if (!kdbg) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (va_space_rw(vs, KdDebuggerDataBlock, kdbg, kdbg_hdr.Size, 0)) {
|
||||
eprintf("Failed to extract entire KDBG\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!decode) {
|
||||
return kdbg;
|
||||
}
|
||||
|
||||
printf("Decoding KdDebuggerDataBlock...\n");
|
||||
kdbg_decode((uint64_t *)kdbg, (uint64_t *)kdbg, kdbg_hdr.Size,
|
||||
kwn, kwa, KdpDataBlockEncoded);
|
||||
|
||||
va_space_rw(vs, KdDebuggerDataBlock, kdbg, kdbg_hdr.Size, 1);
|
||||
|
||||
return kdbg;
|
||||
}
|
||||
|
||||
static void win_context_init_from_qemu_cpu_state(WinContext *ctx,
|
||||
QEMUCPUState *s)
|
||||
{
|
||||
WinContext win_ctx = (WinContext){
|
||||
.ContextFlags = WIN_CTX_X64 | WIN_CTX_INT | WIN_CTX_SEG | WIN_CTX_CTL,
|
||||
.MxCsr = INITIAL_MXCSR,
|
||||
|
||||
.SegCs = s->cs.selector,
|
||||
.SegSs = s->ss.selector,
|
||||
.SegDs = s->ds.selector,
|
||||
.SegEs = s->es.selector,
|
||||
.SegFs = s->fs.selector,
|
||||
.SegGs = s->gs.selector,
|
||||
.EFlags = (uint32_t)s->rflags,
|
||||
|
||||
.Rax = s->rax,
|
||||
.Rbx = s->rbx,
|
||||
.Rcx = s->rcx,
|
||||
.Rdx = s->rdx,
|
||||
.Rsp = s->rsp,
|
||||
.Rbp = s->rbp,
|
||||
.Rsi = s->rsi,
|
||||
.Rdi = s->rdi,
|
||||
.R8 = s->r8,
|
||||
.R9 = s->r9,
|
||||
.R10 = s->r10,
|
||||
.R11 = s->r11,
|
||||
.R12 = s->r12,
|
||||
.R13 = s->r13,
|
||||
.R14 = s->r14,
|
||||
.R15 = s->r15,
|
||||
|
||||
.Rip = s->rip,
|
||||
.FltSave = {
|
||||
.MxCsr = INITIAL_MXCSR,
|
||||
},
|
||||
};
|
||||
|
||||
*ctx = win_ctx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds paging-structure hierarchy base,
|
||||
* if previously set doesn't give access to kernel structures
|
||||
*/
|
||||
static int fix_dtb(struct va_space *vs, QEMU_Elf *qe)
|
||||
{
|
||||
/*
|
||||
* Firstly, test previously set DTB.
|
||||
*/
|
||||
if (va_space_resolve(vs, SharedUserData)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Secondly, find CPU which run system task.
|
||||
*/
|
||||
size_t i;
|
||||
for (i = 0; i < qe->state_nr; i++) {
|
||||
QEMUCPUState *s = qe->state[i];
|
||||
|
||||
if (is_system(s)) {
|
||||
va_space_set_dtb(vs, s->cr[3]);
|
||||
printf("DTB 0x%016lx has been found from CPU #%zu"
|
||||
" as system task CR3\n", vs->dtb, i);
|
||||
return !(va_space_resolve(vs, SharedUserData));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Thirdly, use KERNEL_GS_BASE from CPU #0 as PRCB address and
|
||||
* CR3 as [Prcb+0x7000]
|
||||
*/
|
||||
if (qe->has_kernel_gs_base) {
|
||||
QEMUCPUState *s = qe->state[0];
|
||||
uint64_t Prcb = s->kernel_gs_base;
|
||||
uint64_t *cr3 = va_space_resolve(vs, Prcb + 0x7000);
|
||||
|
||||
if (!cr3) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
va_space_set_dtb(vs, *cr3);
|
||||
printf("DirectoryTableBase = 0x%016lx has been found from CPU #0"
|
||||
" as interrupt handling CR3\n", vs->dtb);
|
||||
return !(va_space_resolve(vs, SharedUserData));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int fill_header(WinDumpHeader64 *hdr, struct pa_space *ps,
|
||||
struct va_space *vs, uint64_t KdDebuggerDataBlock,
|
||||
KDDEBUGGER_DATA64 *kdbg, uint64_t KdVersionBlock, int nr_cpus)
|
||||
{
|
||||
uint32_t *suite_mask = va_space_resolve(vs, SharedUserData +
|
||||
KUSD_OFFSET_SUITE_MASK);
|
||||
int32_t *product_type = va_space_resolve(vs, SharedUserData +
|
||||
KUSD_OFFSET_PRODUCT_TYPE);
|
||||
DBGKD_GET_VERSION64 kvb;
|
||||
WinDumpHeader64 h;
|
||||
size_t i;
|
||||
|
||||
QEMU_BUILD_BUG_ON(KUSD_OFFSET_SUITE_MASK >= PAGE_SIZE);
|
||||
QEMU_BUILD_BUG_ON(KUSD_OFFSET_PRODUCT_TYPE >= PAGE_SIZE);
|
||||
|
||||
if (!suite_mask || !product_type) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (va_space_rw(vs, KdVersionBlock, &kvb, sizeof(kvb), 0)) {
|
||||
eprintf("Failed to extract KdVersionBlock\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
h = (WinDumpHeader64) {
|
||||
.Signature = "PAGE",
|
||||
.ValidDump = "DU64",
|
||||
.MajorVersion = kvb.MajorVersion,
|
||||
.MinorVersion = kvb.MinorVersion,
|
||||
.DirectoryTableBase = vs->dtb,
|
||||
.PfnDatabase = kdbg->MmPfnDatabase,
|
||||
.PsLoadedModuleList = kdbg->PsLoadedModuleList,
|
||||
.PsActiveProcessHead = kdbg->PsActiveProcessHead,
|
||||
.MachineImageType = kvb.MachineType,
|
||||
.NumberProcessors = nr_cpus,
|
||||
.BugcheckCode = LIVE_SYSTEM_DUMP,
|
||||
.KdDebuggerDataBlock = KdDebuggerDataBlock,
|
||||
.DumpType = 1,
|
||||
.Comment = "Hello from elf2dmp!",
|
||||
.SuiteMask = *suite_mask,
|
||||
.ProductType = *product_type,
|
||||
.SecondaryDataState = kvb.KdSecondaryVersion,
|
||||
.PhysicalMemoryBlock = (WinDumpPhyMemDesc64) {
|
||||
.NumberOfRuns = ps->block_nr,
|
||||
},
|
||||
.RequiredDumpSpace = sizeof(h),
|
||||
};
|
||||
|
||||
for (i = 0; i < ps->block_nr; i++) {
|
||||
h.PhysicalMemoryBlock.NumberOfPages += ps->block[i].size / PAGE_SIZE;
|
||||
h.PhysicalMemoryBlock.Run[i] = (WinDumpPhyMemRun64) {
|
||||
.BasePage = ps->block[i].paddr / PAGE_SIZE,
|
||||
.PageCount = ps->block[i].size / PAGE_SIZE,
|
||||
};
|
||||
}
|
||||
|
||||
h.RequiredDumpSpace += h.PhysicalMemoryBlock.NumberOfPages << PAGE_BITS;
|
||||
|
||||
*hdr = h;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fill_context(KDDEBUGGER_DATA64 *kdbg,
|
||||
struct va_space *vs, QEMU_Elf *qe)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < qe->state_nr; i++) {
|
||||
uint64_t Prcb;
|
||||
uint64_t Context;
|
||||
WinContext ctx;
|
||||
QEMUCPUState *s = qe->state[i];
|
||||
|
||||
if (va_space_rw(vs, kdbg->KiProcessorBlock + sizeof(Prcb) * i,
|
||||
&Prcb, sizeof(Prcb), 0)) {
|
||||
eprintf("Failed to read CPU #%d PRCB location\n", i);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (va_space_rw(vs, Prcb + kdbg->OffsetPrcbContext,
|
||||
&Context, sizeof(Context), 0)) {
|
||||
eprintf("Failed to read CPU #%d ContextFrame location\n", i);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Filling context for CPU #%d...\n", i);
|
||||
win_context_init_from_qemu_cpu_state(&ctx, s);
|
||||
|
||||
if (va_space_rw(vs, Context, &ctx, sizeof(ctx), 1)) {
|
||||
eprintf("Failed to fill CPU #%d context\n", i);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_dump(struct pa_space *ps,
|
||||
WinDumpHeader64 *hdr, const char *name)
|
||||
{
|
||||
FILE *dmp_file = fopen(name, "wb");
|
||||
size_t i;
|
||||
|
||||
if (!dmp_file) {
|
||||
eprintf("Failed to open output file \'%s\'\n", name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Writing header to file...\n");
|
||||
|
||||
if (fwrite(hdr, sizeof(*hdr), 1, dmp_file) != 1) {
|
||||
eprintf("Failed to write dump header\n");
|
||||
fclose(dmp_file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < ps->block_nr; i++) {
|
||||
struct pa_block *b = &ps->block[i];
|
||||
|
||||
printf("Writing block #%zu/%zu to file...\n", i, ps->block_nr);
|
||||
if (fwrite(b->addr, b->size, 1, dmp_file) != 1) {
|
||||
eprintf("Failed to write dump header\n");
|
||||
fclose(dmp_file);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return fclose(dmp_file);
|
||||
}
|
||||
|
||||
static int pe_get_pdb_symstore_hash(uint64_t base, void *start_addr,
|
||||
char *hash, struct va_space *vs)
|
||||
{
|
||||
const char e_magic[2] = "MZ";
|
||||
const char Signature[4] = "PE\0\0";
|
||||
const char sign_rsds[4] = "RSDS";
|
||||
IMAGE_DOS_HEADER *dos_hdr = start_addr;
|
||||
IMAGE_NT_HEADERS64 nt_hdrs;
|
||||
IMAGE_FILE_HEADER *file_hdr = &nt_hdrs.FileHeader;
|
||||
IMAGE_OPTIONAL_HEADER64 *opt_hdr = &nt_hdrs.OptionalHeader;
|
||||
IMAGE_DATA_DIRECTORY *data_dir = nt_hdrs.OptionalHeader.DataDirectory;
|
||||
IMAGE_DEBUG_DIRECTORY debug_dir;
|
||||
OMFSignatureRSDS rsds;
|
||||
char *pdb_name;
|
||||
size_t pdb_name_sz;
|
||||
size_t i;
|
||||
|
||||
QEMU_BUILD_BUG_ON(sizeof(*dos_hdr) >= PAGE_SIZE);
|
||||
|
||||
if (memcmp(&dos_hdr->e_magic, e_magic, sizeof(e_magic))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (va_space_rw(vs, base + dos_hdr->e_lfanew,
|
||||
&nt_hdrs, sizeof(nt_hdrs), 0)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (memcmp(&nt_hdrs.Signature, Signature, sizeof(Signature)) ||
|
||||
file_hdr->Machine != 0x8664 || opt_hdr->Magic != 0x020b) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Debug Directory RVA = 0x%016x\n",
|
||||
data_dir[IMAGE_FILE_DEBUG_DIRECTORY].VirtualAddress);
|
||||
|
||||
if (va_space_rw(vs,
|
||||
base + data_dir[IMAGE_FILE_DEBUG_DIRECTORY].VirtualAddress,
|
||||
&debug_dir, sizeof(debug_dir), 0)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (debug_dir.Type != IMAGE_DEBUG_TYPE_CODEVIEW) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (va_space_rw(vs,
|
||||
base + debug_dir.AddressOfRawData,
|
||||
&rsds, sizeof(rsds), 0)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("CodeView signature is \'%.4s\'\n", rsds.Signature);
|
||||
|
||||
if (memcmp(&rsds.Signature, sign_rsds, sizeof(sign_rsds))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
pdb_name_sz = debug_dir.SizeOfData - sizeof(rsds);
|
||||
pdb_name = malloc(pdb_name_sz);
|
||||
if (!pdb_name) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (va_space_rw(vs, base + debug_dir.AddressOfRawData +
|
||||
offsetof(OMFSignatureRSDS, name), pdb_name, pdb_name_sz, 0)) {
|
||||
free(pdb_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("PDB name is \'%s\', \'%s\' expected\n", pdb_name, PDB_NAME);
|
||||
|
||||
if (strcmp(pdb_name, PDB_NAME)) {
|
||||
eprintf("Unexpected PDB name, it seems the kernel isn't found\n");
|
||||
free(pdb_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
free(pdb_name);
|
||||
|
||||
sprintf(hash, "%.08x%.04x%.04x%.02x%.02x", rsds.guid.a, rsds.guid.b,
|
||||
rsds.guid.c, rsds.guid.d[0], rsds.guid.d[1]);
|
||||
hash += 20;
|
||||
for (i = 0; i < 6; i++, hash += 2) {
|
||||
sprintf(hash, "%.02x", rsds.guid.e[i]);
|
||||
}
|
||||
|
||||
sprintf(hash, "%.01x", rsds.age);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int err = 0;
|
||||
QEMU_Elf qemu_elf;
|
||||
struct pa_space ps;
|
||||
struct va_space vs;
|
||||
QEMUCPUState *state;
|
||||
idt_desc_t first_idt_desc;
|
||||
uint64_t KernBase;
|
||||
void *nt_start_addr = NULL;
|
||||
WinDumpHeader64 header;
|
||||
char pdb_hash[34];
|
||||
char pdb_url[] = SYM_URL_BASE PDB_NAME
|
||||
"/0123456789ABCDEF0123456789ABCDEFx/" PDB_NAME;
|
||||
struct pdb_reader pdb;
|
||||
uint64_t KdDebuggerDataBlock;
|
||||
KDDEBUGGER_DATA64 *kdbg;
|
||||
uint64_t KdVersionBlock;
|
||||
|
||||
if (argc != 3) {
|
||||
eprintf("usage:\n\t%s elf_file dmp_file\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (QEMU_Elf_init(&qemu_elf, argv[1])) {
|
||||
eprintf("Failed to initialize QEMU ELF dump\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pa_space_create(&ps, &qemu_elf)) {
|
||||
eprintf("Failed to initialize physical address space\n");
|
||||
err = 1;
|
||||
goto out_elf;
|
||||
}
|
||||
|
||||
state = qemu_elf.state[0];
|
||||
printf("CPU #0 CR3 is 0x%016lx\n", state->cr[3]);
|
||||
|
||||
va_space_create(&vs, &ps, state->cr[3]);
|
||||
if (fix_dtb(&vs, &qemu_elf)) {
|
||||
eprintf("Failed to find paging base\n");
|
||||
err = 1;
|
||||
goto out_elf;
|
||||
}
|
||||
|
||||
printf("CPU #0 IDT is at 0x%016lx\n", state->idt.base);
|
||||
|
||||
if (va_space_rw(&vs, state->idt.base,
|
||||
&first_idt_desc, sizeof(first_idt_desc), 0)) {
|
||||
eprintf("Failed to get CPU #0 IDT[0]\n");
|
||||
err = 1;
|
||||
goto out_ps;
|
||||
}
|
||||
printf("CPU #0 IDT[0] -> 0x%016lx\n", idt_desc_addr(first_idt_desc));
|
||||
|
||||
KernBase = idt_desc_addr(first_idt_desc) & ~(PAGE_SIZE - 1);
|
||||
printf("Searching kernel downwards from 0x%16lx...\n", KernBase);
|
||||
|
||||
for (; KernBase >= 0xfffff78000000000; KernBase -= PAGE_SIZE) {
|
||||
nt_start_addr = va_space_resolve(&vs, KernBase);
|
||||
if (!nt_start_addr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*(uint16_t *)nt_start_addr == 0x5a4d) { /* MZ */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("KernBase = 0x%16lx, signature is \'%.2s\'\n", KernBase,
|
||||
(char *)nt_start_addr);
|
||||
|
||||
if (pe_get_pdb_symstore_hash(KernBase, nt_start_addr, pdb_hash, &vs)) {
|
||||
eprintf("Failed to get PDB symbol store hash\n");
|
||||
err = 1;
|
||||
goto out_ps;
|
||||
}
|
||||
|
||||
sprintf(pdb_url, "%s%s/%s/%s", SYM_URL_BASE, PDB_NAME, pdb_hash, PDB_NAME);
|
||||
printf("PDB URL is %s\n", pdb_url);
|
||||
|
||||
if (download_url(PDB_NAME, pdb_url)) {
|
||||
eprintf("Failed to download PDB file\n");
|
||||
err = 1;
|
||||
goto out_ps;
|
||||
}
|
||||
|
||||
if (pdb_init_from_file(PDB_NAME, &pdb)) {
|
||||
eprintf("Failed to initialize PDB reader\n");
|
||||
err = 1;
|
||||
goto out_pdb_file;
|
||||
}
|
||||
|
||||
if (!SYM_RESOLVE(KernBase, &pdb, KdDebuggerDataBlock) ||
|
||||
!SYM_RESOLVE(KernBase, &pdb, KdVersionBlock)) {
|
||||
err = 1;
|
||||
goto out_pdb;
|
||||
}
|
||||
|
||||
kdbg = get_kdbg(KernBase, &pdb, &vs, KdDebuggerDataBlock);
|
||||
if (!kdbg) {
|
||||
err = 1;
|
||||
goto out_pdb;
|
||||
}
|
||||
|
||||
if (fill_header(&header, &ps, &vs, KdDebuggerDataBlock, kdbg,
|
||||
KdVersionBlock, qemu_elf.state_nr)) {
|
||||
err = 1;
|
||||
goto out_pdb;
|
||||
}
|
||||
|
||||
if (fill_context(kdbg, &vs, &qemu_elf)) {
|
||||
err = 1;
|
||||
goto out_pdb;
|
||||
}
|
||||
|
||||
if (write_dump(&ps, &header, argv[2])) {
|
||||
eprintf("Failed to save dump\n");
|
||||
err = 1;
|
||||
goto out_kdbg;
|
||||
}
|
||||
|
||||
out_kdbg:
|
||||
free(kdbg);
|
||||
out_pdb:
|
||||
pdb_exit(&pdb);
|
||||
out_pdb_file:
|
||||
unlink(PDB_NAME);
|
||||
out_ps:
|
||||
pa_space_destroy(&ps);
|
||||
out_elf:
|
||||
QEMU_Elf_exit(&qemu_elf);
|
||||
|
||||
return err;
|
||||
}
|
322
contrib/elf2dmp/pdb.c
Normal file
322
contrib/elf2dmp/pdb.c
Normal file
@ -0,0 +1,322 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* Based on source of Wine project
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "pdb.h"
|
||||
#include "err.h"
|
||||
|
||||
static uint32_t pdb_get_file_size(const struct pdb_reader *r, unsigned idx)
|
||||
{
|
||||
return r->ds.toc->file_size[idx];
|
||||
}
|
||||
|
||||
static pdb_seg *get_seg_by_num(struct pdb_reader *r, size_t n)
|
||||
{
|
||||
size_t i = 0;
|
||||
char *ptr;
|
||||
|
||||
for (ptr = r->segs; (ptr < r->segs + r->segs_size); ) {
|
||||
i++;
|
||||
ptr += 8;
|
||||
if (i == n) {
|
||||
break;
|
||||
}
|
||||
ptr += sizeof(pdb_seg);
|
||||
}
|
||||
|
||||
return (pdb_seg *)ptr;
|
||||
}
|
||||
|
||||
uint64_t pdb_find_public_v3_symbol(struct pdb_reader *r, const char *name)
|
||||
{
|
||||
size_t size = pdb_get_file_size(r, r->symbols->gsym_file);
|
||||
int length;
|
||||
const union codeview_symbol *sym;
|
||||
const uint8_t *root = r->modimage;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < size; i += length) {
|
||||
sym = (const void *)(root + i);
|
||||
length = sym->generic.len + 2;
|
||||
|
||||
if (!sym->generic.id || length < 4) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (sym->generic.id == S_PUB_V3 &&
|
||||
!strcmp(name, sym->public_v3.name)) {
|
||||
pdb_seg *segment = get_seg_by_num(r, sym->public_v3.segment);
|
||||
uint32_t sect_rva = segment->dword[1];
|
||||
uint64_t rva = sect_rva + sym->public_v3.offset;
|
||||
|
||||
printf("%s: 0x%016x(%d:\'%.8s\') + 0x%08x = 0x%09lx\n", name,
|
||||
sect_rva, sym->public_v3.segment,
|
||||
((char *)segment - 8), sym->public_v3.offset, rva);
|
||||
return rva;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name)
|
||||
{
|
||||
uint64_t rva = pdb_find_public_v3_symbol(r, name);
|
||||
|
||||
if (!rva) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return img_base + rva;
|
||||
}
|
||||
|
||||
static void pdb_reader_ds_exit(struct pdb_reader *r)
|
||||
{
|
||||
free(r->ds.toc);
|
||||
}
|
||||
|
||||
static void pdb_exit_symbols(struct pdb_reader *r)
|
||||
{
|
||||
free(r->modimage);
|
||||
free(r->symbols);
|
||||
}
|
||||
|
||||
static void pdb_exit_segments(struct pdb_reader *r)
|
||||
{
|
||||
free(r->segs);
|
||||
}
|
||||
|
||||
static void *pdb_ds_read(const PDB_DS_HEADER *header,
|
||||
const uint32_t *block_list, int size)
|
||||
{
|
||||
int i, nBlocks;
|
||||
uint8_t *buffer;
|
||||
|
||||
if (!size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nBlocks = (size + header->block_size - 1) / header->block_size;
|
||||
|
||||
buffer = malloc(nBlocks * header->block_size);
|
||||
if (!buffer) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < nBlocks; i++) {
|
||||
memcpy(buffer + i * header->block_size, (const char *)header +
|
||||
block_list[i] * header->block_size, header->block_size);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void *pdb_ds_read_file(struct pdb_reader* r, uint32_t file_number)
|
||||
{
|
||||
const uint32_t *block_list;
|
||||
uint32_t block_size;
|
||||
const uint32_t *file_size;
|
||||
size_t i;
|
||||
|
||||
if (!r->ds.toc || file_number >= r->ds.toc->num_files) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
file_size = r->ds.toc->file_size;
|
||||
r->file_used[file_number / 32] |= 1 << (file_number % 32);
|
||||
|
||||
if (file_size[file_number] == 0 || file_size[file_number] == 0xFFFFFFFF) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
block_list = file_size + r->ds.toc->num_files;
|
||||
block_size = r->ds.header->block_size;
|
||||
|
||||
for (i = 0; i < file_number; i++) {
|
||||
block_list += (file_size[i] + block_size - 1) / block_size;
|
||||
}
|
||||
|
||||
return pdb_ds_read(r->ds.header, block_list, file_size[file_number]);
|
||||
}
|
||||
|
||||
static int pdb_init_segments(struct pdb_reader *r)
|
||||
{
|
||||
char *segs;
|
||||
unsigned stream_idx = r->sidx.segments;
|
||||
|
||||
segs = pdb_ds_read_file(r, stream_idx);
|
||||
if (!segs) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
r->segs = segs;
|
||||
r->segs_size = pdb_get_file_size(r, stream_idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pdb_init_symbols(struct pdb_reader *r)
|
||||
{
|
||||
int err = 0;
|
||||
PDB_SYMBOLS *symbols;
|
||||
PDB_STREAM_INDEXES *sidx = &r->sidx;
|
||||
|
||||
memset(sidx, -1, sizeof(*sidx));
|
||||
|
||||
symbols = pdb_ds_read_file(r, 3);
|
||||
if (!symbols) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
r->symbols = symbols;
|
||||
|
||||
if (symbols->stream_index_size != sizeof(PDB_STREAM_INDEXES)) {
|
||||
err = 1;
|
||||
goto out_symbols;
|
||||
}
|
||||
|
||||
memcpy(sidx, (const char *)symbols + sizeof(PDB_SYMBOLS) +
|
||||
symbols->module_size + symbols->offset_size +
|
||||
symbols->hash_size + symbols->srcmodule_size +
|
||||
symbols->pdbimport_size + symbols->unknown2_size, sizeof(*sidx));
|
||||
|
||||
/* Read global symbol table */
|
||||
r->modimage = pdb_ds_read_file(r, symbols->gsym_file);
|
||||
if (!r->modimage) {
|
||||
err = 1;
|
||||
goto out_symbols;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_symbols:
|
||||
free(symbols);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
|
||||
{
|
||||
memset(r->file_used, 0, sizeof(r->file_used));
|
||||
r->ds.header = hdr;
|
||||
r->ds.toc = pdb_ds_read(hdr, (uint32_t *)((uint8_t *)hdr +
|
||||
hdr->toc_page * hdr->block_size), hdr->toc_size);
|
||||
|
||||
if (!r->ds.toc) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pdb_reader_init(struct pdb_reader *r, void *data)
|
||||
{
|
||||
int err = 0;
|
||||
const char pdb7[] = "Microsoft C/C++ MSF 7.00";
|
||||
|
||||
if (memcmp(data, pdb7, sizeof(pdb7) - 1)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pdb_reader_ds_init(r, data)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
r->ds.root = pdb_ds_read_file(r, 1);
|
||||
if (!r->ds.root) {
|
||||
err = 1;
|
||||
goto out_ds;
|
||||
}
|
||||
|
||||
if (pdb_init_symbols(r)) {
|
||||
err = 1;
|
||||
goto out_root;
|
||||
}
|
||||
|
||||
if (pdb_init_segments(r)) {
|
||||
err = 1;
|
||||
goto out_sym;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_sym:
|
||||
pdb_exit_symbols(r);
|
||||
out_root:
|
||||
free(r->ds.root);
|
||||
out_ds:
|
||||
pdb_reader_ds_exit(r);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void pdb_reader_exit(struct pdb_reader *r)
|
||||
{
|
||||
pdb_exit_segments(r);
|
||||
pdb_exit_symbols(r);
|
||||
free(r->ds.root);
|
||||
pdb_reader_ds_exit(r);
|
||||
}
|
||||
|
||||
int pdb_init_from_file(const char *name, struct pdb_reader *reader)
|
||||
{
|
||||
int err = 0;
|
||||
int fd;
|
||||
void *map;
|
||||
struct stat st;
|
||||
|
||||
fd = open(name, O_RDONLY, 0);
|
||||
if (fd == -1) {
|
||||
eprintf("Failed to open PDB file \'%s\'\n", name);
|
||||
return 1;
|
||||
}
|
||||
reader->fd = fd;
|
||||
|
||||
fstat(fd, &st);
|
||||
reader->file_size = st.st_size;
|
||||
|
||||
map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (map == MAP_FAILED) {
|
||||
eprintf("Failed to map PDB file\n");
|
||||
err = 1;
|
||||
goto out_fd;
|
||||
}
|
||||
|
||||
if (pdb_reader_init(reader, map)) {
|
||||
err = 1;
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_unmap:
|
||||
munmap(map, st.st_size);
|
||||
out_fd:
|
||||
close(fd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void pdb_exit(struct pdb_reader *reader)
|
||||
{
|
||||
munmap(reader->ds.header, reader->file_size);
|
||||
close(reader->fd);
|
||||
pdb_reader_exit(reader);
|
||||
}
|
241
contrib/elf2dmp/pdb.h
Normal file
241
contrib/elf2dmp/pdb.h
Normal file
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PDB_H
|
||||
#define PDB_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct GUID {
|
||||
unsigned int Data1;
|
||||
unsigned short Data2;
|
||||
unsigned short Data3;
|
||||
unsigned char Data4[8];
|
||||
} GUID;
|
||||
|
||||
struct PDB_FILE {
|
||||
uint32_t size;
|
||||
uint32_t unknown;
|
||||
};
|
||||
|
||||
typedef struct PDB_DS_HEADER {
|
||||
char signature[32];
|
||||
uint32_t block_size;
|
||||
uint32_t unknown1;
|
||||
uint32_t num_pages;
|
||||
uint32_t toc_size;
|
||||
uint32_t unknown2;
|
||||
uint32_t toc_page;
|
||||
} PDB_DS_HEADER;
|
||||
|
||||
typedef struct PDB_DS_TOC {
|
||||
uint32_t num_files;
|
||||
uint32_t file_size[1];
|
||||
} PDB_DS_TOC;
|
||||
|
||||
typedef struct PDB_DS_ROOT {
|
||||
uint32_t Version;
|
||||
uint32_t TimeDateStamp;
|
||||
uint32_t Age;
|
||||
GUID guid;
|
||||
uint32_t cbNames;
|
||||
char names[1];
|
||||
} PDB_DS_ROOT;
|
||||
|
||||
typedef struct PDB_TYPES_OLD {
|
||||
uint32_t version;
|
||||
uint16_t first_index;
|
||||
uint16_t last_index;
|
||||
uint32_t type_size;
|
||||
uint16_t file;
|
||||
uint16_t pad;
|
||||
} PDB_TYPES_OLD;
|
||||
|
||||
typedef struct PDB_TYPES {
|
||||
uint32_t version;
|
||||
uint32_t type_offset;
|
||||
uint32_t first_index;
|
||||
uint32_t last_index;
|
||||
uint32_t type_size;
|
||||
uint16_t file;
|
||||
uint16_t pad;
|
||||
uint32_t hash_size;
|
||||
uint32_t hash_base;
|
||||
uint32_t hash_offset;
|
||||
uint32_t hash_len;
|
||||
uint32_t search_offset;
|
||||
uint32_t search_len;
|
||||
uint32_t unknown_offset;
|
||||
uint32_t unknown_len;
|
||||
} PDB_TYPES;
|
||||
|
||||
typedef struct PDB_SYMBOL_RANGE {
|
||||
uint16_t segment;
|
||||
uint16_t pad1;
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
uint32_t characteristics;
|
||||
uint16_t index;
|
||||
uint16_t pad2;
|
||||
} PDB_SYMBOL_RANGE;
|
||||
|
||||
typedef struct PDB_SYMBOL_RANGE_EX {
|
||||
uint16_t segment;
|
||||
uint16_t pad1;
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
uint32_t characteristics;
|
||||
uint16_t index;
|
||||
uint16_t pad2;
|
||||
uint32_t timestamp;
|
||||
uint32_t unknown;
|
||||
} PDB_SYMBOL_RANGE_EX;
|
||||
|
||||
typedef struct PDB_SYMBOL_FILE {
|
||||
uint32_t unknown1;
|
||||
PDB_SYMBOL_RANGE range;
|
||||
uint16_t flag;
|
||||
uint16_t file;
|
||||
uint32_t symbol_size;
|
||||
uint32_t lineno_size;
|
||||
uint32_t unknown2;
|
||||
uint32_t nSrcFiles;
|
||||
uint32_t attribute;
|
||||
char filename[1];
|
||||
} PDB_SYMBOL_FILE;
|
||||
|
||||
typedef struct PDB_SYMBOL_FILE_EX {
|
||||
uint32_t unknown1;
|
||||
PDB_SYMBOL_RANGE_EX range;
|
||||
uint16_t flag;
|
||||
uint16_t file;
|
||||
uint32_t symbol_size;
|
||||
uint32_t lineno_size;
|
||||
uint32_t unknown2;
|
||||
uint32_t nSrcFiles;
|
||||
uint32_t attribute;
|
||||
uint32_t reserved[2];
|
||||
char filename[1];
|
||||
} PDB_SYMBOL_FILE_EX;
|
||||
|
||||
typedef struct PDB_SYMBOL_SOURCE {
|
||||
uint16_t nModules;
|
||||
uint16_t nSrcFiles;
|
||||
uint16_t table[1];
|
||||
} PDB_SYMBOL_SOURCE;
|
||||
|
||||
typedef struct PDB_SYMBOL_IMPORT {
|
||||
uint32_t unknown1;
|
||||
uint32_t unknown2;
|
||||
uint32_t TimeDateStamp;
|
||||
uint32_t Age;
|
||||
char filename[1];
|
||||
} PDB_SYMBOL_IMPORT;
|
||||
|
||||
typedef struct PDB_SYMBOLS_OLD {
|
||||
uint16_t hash1_file;
|
||||
uint16_t hash2_file;
|
||||
uint16_t gsym_file;
|
||||
uint16_t pad;
|
||||
uint32_t module_size;
|
||||
uint32_t offset_size;
|
||||
uint32_t hash_size;
|
||||
uint32_t srcmodule_size;
|
||||
} PDB_SYMBOLS_OLD;
|
||||
|
||||
typedef struct PDB_SYMBOLS {
|
||||
uint32_t signature;
|
||||
uint32_t version;
|
||||
uint32_t unknown;
|
||||
uint32_t hash1_file;
|
||||
uint32_t hash2_file;
|
||||
uint16_t gsym_file;
|
||||
uint16_t unknown1;
|
||||
uint32_t module_size;
|
||||
uint32_t offset_size;
|
||||
uint32_t hash_size;
|
||||
uint32_t srcmodule_size;
|
||||
uint32_t pdbimport_size;
|
||||
uint32_t resvd0;
|
||||
uint32_t stream_index_size;
|
||||
uint32_t unknown2_size;
|
||||
uint16_t resvd3;
|
||||
uint16_t machine;
|
||||
uint32_t resvd4;
|
||||
} PDB_SYMBOLS;
|
||||
|
||||
typedef struct {
|
||||
uint16_t FPO;
|
||||
uint16_t unk0;
|
||||
uint16_t unk1;
|
||||
uint16_t unk2;
|
||||
uint16_t unk3;
|
||||
uint16_t segments;
|
||||
} PDB_STREAM_INDEXES_OLD;
|
||||
|
||||
typedef struct {
|
||||
uint16_t FPO;
|
||||
uint16_t unk0;
|
||||
uint16_t unk1;
|
||||
uint16_t unk2;
|
||||
uint16_t unk3;
|
||||
uint16_t segments;
|
||||
uint16_t unk4;
|
||||
uint16_t unk5;
|
||||
uint16_t unk6;
|
||||
uint16_t FPO_EXT;
|
||||
uint16_t unk7;
|
||||
} PDB_STREAM_INDEXES;
|
||||
|
||||
union codeview_symbol {
|
||||
struct {
|
||||
int16_t len;
|
||||
int16_t id;
|
||||
} generic;
|
||||
|
||||
struct {
|
||||
int16_t len;
|
||||
int16_t id;
|
||||
uint32_t symtype;
|
||||
uint32_t offset;
|
||||
uint16_t segment;
|
||||
char name[1];
|
||||
} public_v3;
|
||||
};
|
||||
|
||||
#define S_PUB_V3 0x110E
|
||||
|
||||
typedef struct pdb_seg {
|
||||
uint32_t dword[8];
|
||||
} __attribute__ ((packed)) pdb_seg;
|
||||
|
||||
#define IMAGE_FILE_MACHINE_I386 0x014c
|
||||
#define IMAGE_FILE_MACHINE_AMD64 0x8664
|
||||
|
||||
struct pdb_reader {
|
||||
int fd;
|
||||
size_t file_size;
|
||||
struct {
|
||||
PDB_DS_HEADER *header;
|
||||
PDB_DS_TOC *toc;
|
||||
PDB_DS_ROOT *root;
|
||||
} ds;
|
||||
uint32_t file_used[1024];
|
||||
PDB_SYMBOLS *symbols;
|
||||
PDB_STREAM_INDEXES sidx;
|
||||
uint8_t *modimage;
|
||||
char *segs;
|
||||
size_t segs_size;
|
||||
};
|
||||
|
||||
int pdb_init_from_file(const char *name, struct pdb_reader *reader);
|
||||
void pdb_exit(struct pdb_reader *reader);
|
||||
uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name);
|
||||
uint64_t pdb_find_public_v3_symbol(struct pdb_reader *reader, const char *name);
|
||||
|
||||
#endif /* PDB_H */
|
121
contrib/elf2dmp/pe.h
Normal file
121
contrib/elf2dmp/pe.h
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PE_H
|
||||
#define PE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct IMAGE_DOS_HEADER {
|
||||
uint16_t e_magic; /* 0x00: MZ Header signature */
|
||||
uint16_t e_cblp; /* 0x02: Bytes on last page of file */
|
||||
uint16_t e_cp; /* 0x04: Pages in file */
|
||||
uint16_t e_crlc; /* 0x06: Relocations */
|
||||
uint16_t e_cparhdr; /* 0x08: Size of header in paragraphs */
|
||||
uint16_t e_minalloc; /* 0x0a: Minimum extra paragraphs needed */
|
||||
uint16_t e_maxalloc; /* 0x0c: Maximum extra paragraphs needed */
|
||||
uint16_t e_ss; /* 0x0e: Initial (relative) SS value */
|
||||
uint16_t e_sp; /* 0x10: Initial SP value */
|
||||
uint16_t e_csum; /* 0x12: Checksum */
|
||||
uint16_t e_ip; /* 0x14: Initial IP value */
|
||||
uint16_t e_cs; /* 0x16: Initial (relative) CS value */
|
||||
uint16_t e_lfarlc; /* 0x18: File address of relocation table */
|
||||
uint16_t e_ovno; /* 0x1a: Overlay number */
|
||||
uint16_t e_res[4]; /* 0x1c: Reserved words */
|
||||
uint16_t e_oemid; /* 0x24: OEM identifier (for e_oeminfo) */
|
||||
uint16_t e_oeminfo; /* 0x26: OEM information; e_oemid specific */
|
||||
uint16_t e_res2[10]; /* 0x28: Reserved words */
|
||||
uint32_t e_lfanew; /* 0x3c: Offset to extended header */
|
||||
} __attribute__ ((packed)) IMAGE_DOS_HEADER;
|
||||
|
||||
typedef struct IMAGE_FILE_HEADER {
|
||||
uint16_t Machine;
|
||||
uint16_t NumberOfSections;
|
||||
uint32_t TimeDateStamp;
|
||||
uint32_t PointerToSymbolTable;
|
||||
uint32_t NumberOfSymbols;
|
||||
uint16_t SizeOfOptionalHeader;
|
||||
uint16_t Characteristics;
|
||||
} __attribute__ ((packed)) IMAGE_FILE_HEADER;
|
||||
|
||||
typedef struct IMAGE_DATA_DIRECTORY {
|
||||
uint32_t VirtualAddress;
|
||||
uint32_t Size;
|
||||
} __attribute__ ((packed)) IMAGE_DATA_DIRECTORY;
|
||||
|
||||
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
|
||||
|
||||
typedef struct IMAGE_OPTIONAL_HEADER64 {
|
||||
uint16_t Magic; /* 0x20b */
|
||||
uint8_t MajorLinkerVersion;
|
||||
uint8_t MinorLinkerVersion;
|
||||
uint32_t SizeOfCode;
|
||||
uint32_t SizeOfInitializedData;
|
||||
uint32_t SizeOfUninitializedData;
|
||||
uint32_t AddressOfEntryPoint;
|
||||
uint32_t BaseOfCode;
|
||||
uint64_t ImageBase;
|
||||
uint32_t SectionAlignment;
|
||||
uint32_t FileAlignment;
|
||||
uint16_t MajorOperatingSystemVersion;
|
||||
uint16_t MinorOperatingSystemVersion;
|
||||
uint16_t MajorImageVersion;
|
||||
uint16_t MinorImageVersion;
|
||||
uint16_t MajorSubsystemVersion;
|
||||
uint16_t MinorSubsystemVersion;
|
||||
uint32_t Win32VersionValue;
|
||||
uint32_t SizeOfImage;
|
||||
uint32_t SizeOfHeaders;
|
||||
uint32_t CheckSum;
|
||||
uint16_t Subsystem;
|
||||
uint16_t DllCharacteristics;
|
||||
uint64_t SizeOfStackReserve;
|
||||
uint64_t SizeOfStackCommit;
|
||||
uint64_t SizeOfHeapReserve;
|
||||
uint64_t SizeOfHeapCommit;
|
||||
uint32_t LoaderFlags;
|
||||
uint32_t NumberOfRvaAndSizes;
|
||||
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
|
||||
} __attribute__ ((packed)) IMAGE_OPTIONAL_HEADER64;
|
||||
|
||||
typedef struct IMAGE_NT_HEADERS64 {
|
||||
uint32_t Signature;
|
||||
IMAGE_FILE_HEADER FileHeader;
|
||||
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
|
||||
} __attribute__ ((packed)) IMAGE_NT_HEADERS64;
|
||||
|
||||
#define IMAGE_FILE_DEBUG_DIRECTORY 6
|
||||
|
||||
typedef struct IMAGE_DEBUG_DIRECTORY {
|
||||
uint32_t Characteristics;
|
||||
uint32_t TimeDateStamp;
|
||||
uint16_t MajorVersion;
|
||||
uint16_t MinorVersion;
|
||||
uint32_t Type;
|
||||
uint32_t SizeOfData;
|
||||
uint32_t AddressOfRawData;
|
||||
uint32_t PointerToRawData;
|
||||
} __attribute__ ((packed)) IMAGE_DEBUG_DIRECTORY;
|
||||
|
||||
#define IMAGE_DEBUG_TYPE_CODEVIEW 2
|
||||
|
||||
typedef struct guid_t {
|
||||
uint32_t a;
|
||||
uint16_t b;
|
||||
uint16_t c;
|
||||
uint8_t d[2];
|
||||
uint8_t e[6];
|
||||
} __attribute__ ((packed)) guid_t;
|
||||
|
||||
typedef struct OMFSignatureRSDS {
|
||||
char Signature[4];
|
||||
guid_t guid;
|
||||
uint32_t age;
|
||||
char name[];
|
||||
} __attribute__ ((packed)) OMFSignatureRSDS;
|
||||
|
||||
#endif /* PE_H */
|
164
contrib/elf2dmp/qemu_elf.c
Normal file
164
contrib/elf2dmp/qemu_elf.c
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "err.h"
|
||||
#include "qemu_elf.h"
|
||||
|
||||
#define QEMU_NOTE_NAME "QEMU"
|
||||
|
||||
#ifndef ROUND_UP
|
||||
#define ROUND_UP(n, d) (((n) + (d) - 1) & -(0 ? (n) : (d)))
|
||||
#endif
|
||||
|
||||
#ifndef DIV_ROUND_UP
|
||||
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
|
||||
#endif
|
||||
|
||||
#define ELF_NOTE_SIZE(hdr_size, name_size, desc_size) \
|
||||
((DIV_ROUND_UP((hdr_size), 4) + \
|
||||
DIV_ROUND_UP((name_size), 4) + \
|
||||
DIV_ROUND_UP((desc_size), 4)) * 4)
|
||||
|
||||
int is_system(QEMUCPUState *s)
|
||||
{
|
||||
return s->gs.base >> 63;
|
||||
}
|
||||
|
||||
static char *nhdr_get_name(Elf64_Nhdr *nhdr)
|
||||
{
|
||||
return (char *)nhdr + ROUND_UP(sizeof(*nhdr), 4);
|
||||
}
|
||||
|
||||
static void *nhdr_get_desc(Elf64_Nhdr *nhdr)
|
||||
{
|
||||
return nhdr_get_name(nhdr) + ROUND_UP(nhdr->n_namesz, 4);
|
||||
}
|
||||
|
||||
static Elf64_Nhdr *nhdr_get_next(Elf64_Nhdr *nhdr)
|
||||
{
|
||||
return (void *)((uint8_t *)nhdr + ELF_NOTE_SIZE(sizeof(*nhdr),
|
||||
nhdr->n_namesz, nhdr->n_descsz));
|
||||
}
|
||||
|
||||
Elf64_Phdr *elf64_getphdr(void *map)
|
||||
{
|
||||
Elf64_Ehdr *ehdr = map;
|
||||
Elf64_Phdr *phdr = (void *)((uint8_t *)map + ehdr->e_phoff);
|
||||
|
||||
return phdr;
|
||||
}
|
||||
|
||||
Elf64_Half elf_getphdrnum(void *map)
|
||||
{
|
||||
Elf64_Ehdr *ehdr = map;
|
||||
|
||||
return ehdr->e_phnum;
|
||||
}
|
||||
|
||||
static int init_states(QEMU_Elf *qe)
|
||||
{
|
||||
Elf64_Phdr *phdr = elf64_getphdr(qe->map);
|
||||
Elf64_Nhdr *start = (void *)((uint8_t *)qe->map + phdr[0].p_offset);
|
||||
Elf64_Nhdr *end = (void *)((uint8_t *)start + phdr[0].p_memsz);
|
||||
Elf64_Nhdr *nhdr;
|
||||
size_t cpu_nr = 0;
|
||||
|
||||
if (phdr[0].p_type != PT_NOTE) {
|
||||
eprintf("Failed to find PT_NOTE\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
qe->has_kernel_gs_base = 1;
|
||||
|
||||
for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) {
|
||||
if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) {
|
||||
QEMUCPUState *state = nhdr_get_desc(nhdr);
|
||||
|
||||
if (state->size < sizeof(*state)) {
|
||||
eprintf("CPU #%zu: QEMU CPU state size %u doesn't match\n",
|
||||
cpu_nr, state->size);
|
||||
/*
|
||||
* We assume either every QEMU CPU state has KERNEL_GS_BASE or
|
||||
* no one has.
|
||||
*/
|
||||
qe->has_kernel_gs_base = 0;
|
||||
}
|
||||
cpu_nr++;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%zu CPU states has been found\n", cpu_nr);
|
||||
|
||||
qe->state = malloc(sizeof(*qe->state) * cpu_nr);
|
||||
if (!qe->state) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
cpu_nr = 0;
|
||||
|
||||
for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) {
|
||||
if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) {
|
||||
qe->state[cpu_nr] = nhdr_get_desc(nhdr);
|
||||
cpu_nr++;
|
||||
}
|
||||
}
|
||||
|
||||
qe->state_nr = cpu_nr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exit_states(QEMU_Elf *qe)
|
||||
{
|
||||
free(qe->state);
|
||||
}
|
||||
|
||||
int QEMU_Elf_init(QEMU_Elf *qe, const char *filename)
|
||||
{
|
||||
int err = 0;
|
||||
struct stat st;
|
||||
|
||||
qe->fd = open(filename, O_RDONLY, 0);
|
||||
if (qe->fd == -1) {
|
||||
eprintf("Failed to open ELF dump file \'%s\'\n", filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fstat(qe->fd, &st);
|
||||
qe->size = st.st_size;
|
||||
|
||||
qe->map = mmap(NULL, qe->size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE, qe->fd, 0);
|
||||
if (qe->map == MAP_FAILED) {
|
||||
eprintf("Failed to map ELF file\n");
|
||||
err = 1;
|
||||
goto out_fd;
|
||||
}
|
||||
|
||||
if (init_states(qe)) {
|
||||
eprintf("Failed to extract QEMU CPU states\n");
|
||||
err = 1;
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_unmap:
|
||||
munmap(qe->map, qe->size);
|
||||
out_fd:
|
||||
close(qe->fd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void QEMU_Elf_exit(QEMU_Elf *qe)
|
||||
{
|
||||
exit_states(qe);
|
||||
munmap(qe->map, qe->size);
|
||||
close(qe->fd);
|
||||
}
|
51
contrib/elf2dmp/qemu_elf.h
Normal file
51
contrib/elf2dmp/qemu_elf.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QEMU_ELF_H
|
||||
#define QEMU_ELF_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <elf.h>
|
||||
|
||||
typedef struct QEMUCPUSegment {
|
||||
uint32_t selector;
|
||||
uint32_t limit;
|
||||
uint32_t flags;
|
||||
uint32_t pad;
|
||||
uint64_t base;
|
||||
} QEMUCPUSegment;
|
||||
|
||||
typedef struct QEMUCPUState {
|
||||
uint32_t version;
|
||||
uint32_t size;
|
||||
uint64_t rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp;
|
||||
uint64_t r8, r9, r10, r11, r12, r13, r14, r15;
|
||||
uint64_t rip, rflags;
|
||||
QEMUCPUSegment cs, ds, es, fs, gs, ss;
|
||||
QEMUCPUSegment ldt, tr, gdt, idt;
|
||||
uint64_t cr[5];
|
||||
uint64_t kernel_gs_base;
|
||||
} QEMUCPUState;
|
||||
|
||||
int is_system(QEMUCPUState *s);
|
||||
|
||||
typedef struct QEMU_Elf {
|
||||
int fd;
|
||||
size_t size;
|
||||
void *map;
|
||||
QEMUCPUState **state;
|
||||
size_t state_nr;
|
||||
int has_kernel_gs_base;
|
||||
} QEMU_Elf;
|
||||
|
||||
int QEMU_Elf_init(QEMU_Elf *qe, const char *filename);
|
||||
void QEMU_Elf_exit(QEMU_Elf *qe);
|
||||
|
||||
Elf64_Phdr *elf64_getphdr(void *map);
|
||||
Elf64_Half elf_getphdrnum(void *map);
|
||||
|
||||
#endif /* QEMU_ELF_H */
|
84
cpus.c
84
cpus.c
@ -245,21 +245,27 @@ static int64_t cpu_get_icount_executed(CPUState *cpu)
|
||||
* account executed instructions. This is done by the TCG vCPU
|
||||
* thread so the main-loop can see time has moved forward.
|
||||
*/
|
||||
void cpu_update_icount(CPUState *cpu)
|
||||
static void cpu_update_icount_locked(CPUState *cpu)
|
||||
{
|
||||
int64_t executed = cpu_get_icount_executed(cpu);
|
||||
cpu->icount_budget -= executed;
|
||||
|
||||
#ifndef CONFIG_ATOMIC64
|
||||
atomic_set_i64(&timers_state.qemu_icount,
|
||||
timers_state.qemu_icount + executed);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the global shared timer_state.qemu_icount to take into
|
||||
* account executed instructions. This is done by the TCG vCPU
|
||||
* thread so the main-loop can see time has moved forward.
|
||||
*/
|
||||
void cpu_update_icount(CPUState *cpu)
|
||||
{
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
#endif
|
||||
atomic_set__nocheck(&timers_state.qemu_icount,
|
||||
timers_state.qemu_icount + executed);
|
||||
#ifndef CONFIG_ATOMIC64
|
||||
cpu_update_icount_locked(cpu);
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int64_t cpu_get_icount_raw_locked(void)
|
||||
@ -272,16 +278,17 @@ static int64_t cpu_get_icount_raw_locked(void)
|
||||
exit(1);
|
||||
}
|
||||
/* Take into account what has run */
|
||||
cpu_update_icount(cpu);
|
||||
cpu_update_icount_locked(cpu);
|
||||
}
|
||||
/* The read is protected by the seqlock, so __nocheck is okay. */
|
||||
return atomic_read__nocheck(&timers_state.qemu_icount);
|
||||
/* The read is protected by the seqlock, but needs atomic64 to avoid UB */
|
||||
return atomic_read_i64(&timers_state.qemu_icount);
|
||||
}
|
||||
|
||||
static int64_t cpu_get_icount_locked(void)
|
||||
{
|
||||
int64_t icount = cpu_get_icount_raw_locked();
|
||||
return atomic_read__nocheck(&timers_state.qemu_icount_bias) + cpu_icount_to_ns(icount);
|
||||
return atomic_read_i64(&timers_state.qemu_icount_bias) +
|
||||
cpu_icount_to_ns(icount);
|
||||
}
|
||||
|
||||
int64_t cpu_get_icount_raw(void)
|
||||
@ -454,9 +461,9 @@ static void icount_adjust(void)
|
||||
timers_state.icount_time_shift + 1);
|
||||
}
|
||||
last_delta = delta;
|
||||
atomic_set__nocheck(&timers_state.qemu_icount_bias,
|
||||
cur_icount - (timers_state.qemu_icount
|
||||
<< timers_state.icount_time_shift));
|
||||
atomic_set_i64(&timers_state.qemu_icount_bias,
|
||||
cur_icount - (timers_state.qemu_icount
|
||||
<< timers_state.icount_time_shift));
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
}
|
||||
@ -516,8 +523,8 @@ static void icount_warp_rt(void)
|
||||
int64_t delta = clock - cur_icount;
|
||||
warp_delta = MIN(warp_delta, delta);
|
||||
}
|
||||
atomic_set__nocheck(&timers_state.qemu_icount_bias,
|
||||
timers_state.qemu_icount_bias + warp_delta);
|
||||
atomic_set_i64(&timers_state.qemu_icount_bias,
|
||||
timers_state.qemu_icount_bias + warp_delta);
|
||||
}
|
||||
timers_state.vm_clock_warp_start = -1;
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
@ -548,8 +555,8 @@ void qtest_clock_warp(int64_t dest)
|
||||
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
atomic_set__nocheck(&timers_state.qemu_icount_bias,
|
||||
timers_state.qemu_icount_bias + warp);
|
||||
atomic_set_i64(&timers_state.qemu_icount_bias,
|
||||
timers_state.qemu_icount_bias + warp);
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
|
||||
@ -576,18 +583,29 @@ void qemu_start_warp_timer(void)
|
||||
return;
|
||||
}
|
||||
|
||||
/* warp clock deterministically in record/replay mode */
|
||||
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_START)) {
|
||||
return;
|
||||
}
|
||||
if (replay_mode != REPLAY_MODE_PLAY) {
|
||||
if (!all_cpu_threads_idle()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!all_cpu_threads_idle()) {
|
||||
return;
|
||||
}
|
||||
if (qtest_enabled()) {
|
||||
/* When testing, qtest commands advance icount. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (qtest_enabled()) {
|
||||
/* When testing, qtest commands advance icount. */
|
||||
return;
|
||||
replay_checkpoint(CHECKPOINT_CLOCK_WARP_START);
|
||||
} else {
|
||||
/* warp clock deterministically in record/replay mode */
|
||||
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_START)) {
|
||||
/* vCPU is sleeping and warp can't be started.
|
||||
It is probably a race condition: notification sent
|
||||
to vCPU was processed in advance and vCPU went to sleep.
|
||||
Therefore we have to wake it up for doing someting. */
|
||||
if (replay_has_checkpoint()) {
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* We want to use the earliest deadline from ALL vm_clocks */
|
||||
@ -620,8 +638,8 @@ void qemu_start_warp_timer(void)
|
||||
*/
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
atomic_set__nocheck(&timers_state.qemu_icount_bias,
|
||||
timers_state.qemu_icount_bias + deadline);
|
||||
atomic_set_i64(&timers_state.qemu_icount_bias,
|
||||
timers_state.qemu_icount_bias + deadline);
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
@ -823,6 +841,7 @@ int cpu_throttle_get_percentage(void)
|
||||
void cpu_ticks_init(void)
|
||||
{
|
||||
seqlock_init(&timers_state.vm_clock_seqlock);
|
||||
qemu_spin_init(&timers_state.vm_clock_lock);
|
||||
vmstate_register(NULL, 0, &vmstate_timers, &timers_state);
|
||||
throttle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
|
||||
cpu_throttle_timer_tick, NULL);
|
||||
@ -964,6 +983,8 @@ static void start_tcg_kick_timer(void)
|
||||
if (!tcg_kick_vcpu_timer && CPU_NEXT(first_cpu)) {
|
||||
tcg_kick_vcpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||
kick_tcg_thread, NULL);
|
||||
}
|
||||
if (tcg_kick_vcpu_timer && !timer_pending(tcg_kick_vcpu_timer)) {
|
||||
timer_mod(tcg_kick_vcpu_timer, qemu_tcg_next_kick());
|
||||
}
|
||||
}
|
||||
@ -971,9 +992,8 @@ static void start_tcg_kick_timer(void)
|
||||
static void stop_tcg_kick_timer(void)
|
||||
{
|
||||
assert(!mttcg_enabled);
|
||||
if (tcg_kick_vcpu_timer) {
|
||||
if (tcg_kick_vcpu_timer && timer_pending(tcg_kick_vcpu_timer)) {
|
||||
timer_del(tcg_kick_vcpu_timer);
|
||||
tcg_kick_vcpu_timer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,8 +326,15 @@ visible as the pci-hole alias clips it to a 0.5GB range.
|
||||
MMIO Operations
|
||||
---------------
|
||||
|
||||
MMIO regions are provided with ->read() and ->write() callbacks; in addition
|
||||
various constraints can be supplied to control how these callbacks are called:
|
||||
MMIO regions are provided with ->read() and ->write() callbacks,
|
||||
which are sufficient for most devices. Some devices change behaviour
|
||||
based on the attributes used for the memory transaction, or need
|
||||
to be able to respond that the access should provoke a bus error
|
||||
rather than completing successfully; those devices can use the
|
||||
->read_with_attrs() and ->write_with_attrs() callbacks instead.
|
||||
|
||||
In addition various constraints can be supplied to control how these
|
||||
callbacks are called:
|
||||
|
||||
- .valid.min_access_size, .valid.max_access_size define the access sizes
|
||||
(in bytes) which the device accepts; accesses outside this range will
|
||||
@ -342,5 +349,3 @@ various constraints can be supplied to control how these callbacks are called:
|
||||
- .impl.unaligned specifies that the *implementation* supports unaligned
|
||||
accesses; if false, unaligned accesses will be emulated by two aligned
|
||||
accesses.
|
||||
- .old_mmio eases the porting of code that was formerly using
|
||||
cpu_register_io_memory(). It should not be used in new code.
|
||||
|
4
exec.c
4
exec.c
@ -1734,7 +1734,7 @@ long qemu_getrampagesize(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#ifdef CONFIG_POSIX
|
||||
static int64_t get_file_size(int fd)
|
||||
{
|
||||
int64_t size = lseek(fd, 0, SEEK_END);
|
||||
@ -2230,7 +2230,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp, bool shared)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
#ifdef CONFIG_POSIX
|
||||
RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr,
|
||||
uint32_t ram_flags, int fd,
|
||||
Error **errp)
|
||||
|
@ -150,7 +150,8 @@ static void clipper_init(MachineState *machine)
|
||||
}
|
||||
|
||||
if (initrd_filename) {
|
||||
long initrd_base, initrd_size;
|
||||
long initrd_base;
|
||||
int64_t initrd_size;
|
||||
|
||||
initrd_size = get_image_size(initrd_filename);
|
||||
if (initrd_size < 0) {
|
||||
|
@ -506,10 +506,13 @@ static void es1370_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
d - &s->chan[0], val >> 16, (val & 0xffff));
|
||||
break;
|
||||
|
||||
case ES1370_REG_ADC_FRAMEADR:
|
||||
d += 2;
|
||||
goto frameadr;
|
||||
case ES1370_REG_DAC1_FRAMEADR:
|
||||
case ES1370_REG_DAC2_FRAMEADR:
|
||||
case ES1370_REG_ADC_FRAMEADR:
|
||||
d += (addr - ES1370_REG_DAC1_FRAMEADR) >> 3;
|
||||
frameadr:
|
||||
d->frame_addr = val;
|
||||
ldebug ("chan %td frame address %#x\n", d - &s->chan[0], val);
|
||||
break;
|
||||
@ -521,10 +524,13 @@ static void es1370_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
lwarn ("writing to phantom frame address %#x\n", val);
|
||||
break;
|
||||
|
||||
case ES1370_REG_ADC_FRAMECNT:
|
||||
d += 2;
|
||||
goto framecnt;
|
||||
case ES1370_REG_DAC1_FRAMECNT:
|
||||
case ES1370_REG_DAC2_FRAMECNT:
|
||||
case ES1370_REG_ADC_FRAMECNT:
|
||||
d += (addr - ES1370_REG_DAC1_FRAMECNT) >> 3;
|
||||
framecnt:
|
||||
d->frame_cnt = val;
|
||||
d->leftover = 0;
|
||||
ldebug ("chan %td frame count %d, buffer size %d\n",
|
||||
|
@ -345,9 +345,9 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
default:
|
||||
case 0:
|
||||
if (s->lcr & UART_LCR_DLAB) {
|
||||
if (size == 2) {
|
||||
if (size == 1) {
|
||||
s->divider = (s->divider & 0xff00) | val;
|
||||
} else if (size == 4) {
|
||||
} else {
|
||||
s->divider = val;
|
||||
}
|
||||
serial_update_parameters(s);
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "hw/sh4/sh.h"
|
||||
#include "chardev/char-fe.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/timer.h"
|
||||
|
||||
//#define DEBUG_SERIAL
|
||||
|
||||
@ -63,6 +64,8 @@ typedef struct {
|
||||
int rtrg;
|
||||
|
||||
CharBackend chr;
|
||||
QEMUTimer *fifo_timeout_timer;
|
||||
uint64_t etu; /* Elementary Time Unit (ns) */
|
||||
|
||||
qemu_irq eri;
|
||||
qemu_irq rxi;
|
||||
@ -314,6 +317,16 @@ static int sh_serial_can_receive1(void *opaque)
|
||||
return sh_serial_can_receive(s);
|
||||
}
|
||||
|
||||
static void sh_serial_timeout_int(void *opaque)
|
||||
{
|
||||
sh_serial_state *s = opaque;
|
||||
|
||||
s->flags |= SH_SERIAL_FLAG_RDF;
|
||||
if (s->scr & (1 << 6) && s->rxi) {
|
||||
qemu_set_irq(s->rxi, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
sh_serial_state *s = opaque;
|
||||
@ -330,8 +343,12 @@ static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size)
|
||||
if (s->rx_cnt >= s->rtrg) {
|
||||
s->flags |= SH_SERIAL_FLAG_RDF;
|
||||
if (s->scr & (1 << 6) && s->rxi) {
|
||||
timer_del(s->fifo_timeout_timer);
|
||||
qemu_set_irq(s->rxi, 1);
|
||||
}
|
||||
} else {
|
||||
timer_mod(s->fifo_timeout_timer,
|
||||
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 15 * s->etu);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -402,6 +419,9 @@ void sh_serial_init(MemoryRegion *sysmem,
|
||||
sh_serial_event, NULL, s, NULL, true);
|
||||
}
|
||||
|
||||
s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||
sh_serial_timeout_int, s);
|
||||
s->etu = NANOSECONDS_PER_SECOND / 9600;
|
||||
s->eri = eri_source;
|
||||
s->rxi = rxi_source;
|
||||
s->txi = txi_source;
|
||||
|
@ -667,9 +667,9 @@ static void virtio_serial_save_device(VirtIODevice *vdev, QEMUFile *f)
|
||||
|
||||
/* The config space (ignored on the far end in current versions) */
|
||||
get_config(vdev, (uint8_t *)&config);
|
||||
qemu_put_be16s(f, &config.cols);
|
||||
qemu_put_be16s(f, &config.rows);
|
||||
qemu_put_be32s(f, &config.max_nr_ports);
|
||||
qemu_put_be16(f, config.cols);
|
||||
qemu_put_be16(f, config.rows);
|
||||
qemu_put_be32(f, config.max_nr_ports);
|
||||
|
||||
/* The ports map */
|
||||
max_nr_ports = s->serial.max_virtserial_ports;
|
||||
|
@ -61,9 +61,10 @@
|
||||
static int roms_loaded;
|
||||
|
||||
/* return the size or -1 if error */
|
||||
int get_image_size(const char *filename)
|
||||
int64_t get_image_size(const char *filename)
|
||||
{
|
||||
int fd, size;
|
||||
int fd;
|
||||
int64_t size;
|
||||
fd = open(filename, O_RDONLY | O_BINARY);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
@ -191,7 +191,7 @@ static void machine_hppa_init(MachineState *machine)
|
||||
|
||||
if (initrd_filename) {
|
||||
ram_addr_t initrd_base;
|
||||
long initrd_size;
|
||||
int64_t initrd_size;
|
||||
|
||||
initrd_size = get_image_size(initrd_filename);
|
||||
if (initrd_size < 0) {
|
||||
|
@ -147,6 +147,15 @@ static void kvm_update_clock(KVMClockState *s)
|
||||
s->clock_is_reliable = kvm_has_adjust_clock_stable();
|
||||
}
|
||||
|
||||
static void do_kvmclock_ctrl(CPUState *cpu, run_on_cpu_data data)
|
||||
{
|
||||
int ret = kvm_vcpu_ioctl(cpu, KVM_KVMCLOCK_CTRL, 0);
|
||||
|
||||
if (ret && ret != -EINVAL) {
|
||||
fprintf(stderr, "%s: %s\n", __func__, strerror(-ret));
|
||||
}
|
||||
}
|
||||
|
||||
static void kvmclock_vm_state_change(void *opaque, int running,
|
||||
RunState state)
|
||||
{
|
||||
@ -183,13 +192,7 @@ static void kvmclock_vm_state_change(void *opaque, int running,
|
||||
return;
|
||||
}
|
||||
CPU_FOREACH(cpu) {
|
||||
ret = kvm_vcpu_ioctl(cpu, KVM_KVMCLOCK_CTRL, 0);
|
||||
if (ret) {
|
||||
if (ret != -EINVAL) {
|
||||
fprintf(stderr, "%s: %s\n", __func__, strerror(-ret));
|
||||
}
|
||||
return;
|
||||
}
|
||||
run_on_cpu(cpu, do_kvmclock_ctrl, RUN_ON_CPU_NULL);
|
||||
}
|
||||
} else {
|
||||
|
||||
|
@ -838,7 +838,8 @@ static void load_linux(PCMachineState *pcms,
|
||||
FWCfgState *fw_cfg)
|
||||
{
|
||||
uint16_t protocol;
|
||||
int setup_size, kernel_size, initrd_size = 0, cmdline_size;
|
||||
int setup_size, kernel_size, cmdline_size;
|
||||
int64_t initrd_size = 0;
|
||||
int dtb_size, setup_data_offset;
|
||||
uint32_t initrd_max;
|
||||
uint8_t header[8192], *setup, *kernel, *initrd_data;
|
||||
@ -974,6 +975,10 @@ static void load_linux(PCMachineState *pcms,
|
||||
fprintf(stderr, "qemu: error reading initrd %s: %s\n",
|
||||
initrd_filename, strerror(errno));
|
||||
exit(1);
|
||||
} else if (initrd_size >= initrd_max) {
|
||||
fprintf(stderr, "qemu: initrd is too large, cannot support."
|
||||
"(max: %"PRIu32", need %"PRId64")\n", initrd_max, initrd_size);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
initrd_addr = (initrd_max-initrd_size) & ~4095;
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "sysemu/replay.h"
|
||||
|
||||
#include "hw/ide/internal.h"
|
||||
#include "trace.h"
|
||||
@ -479,7 +480,7 @@ static void ide_issue_trim_cb(void *opaque, int ret)
|
||||
done:
|
||||
iocb->aiocb = NULL;
|
||||
if (iocb->bh) {
|
||||
qemu_bh_schedule(iocb->bh);
|
||||
replay_bh_schedule_event(iocb->bh);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -914,7 +914,12 @@ static void ps2_common_post_load(PS2State *s)
|
||||
uint8_t tmp_data[PS2_QUEUE_SIZE];
|
||||
|
||||
/* set the useful data buffer queue size, < PS2_QUEUE_SIZE */
|
||||
size = (q->count < 0 || q->count > PS2_QUEUE_SIZE) ? 0 : q->count;
|
||||
size = q->count;
|
||||
if (q->count < 0) {
|
||||
size = 0;
|
||||
} else if (q->count > PS2_QUEUE_SIZE) {
|
||||
size = PS2_QUEUE_SIZE;
|
||||
}
|
||||
|
||||
/* move the queue elements to the start of data array */
|
||||
for (i = 0; i < size; i++) {
|
||||
@ -929,7 +934,6 @@ static void ps2_common_post_load(PS2State *s)
|
||||
q->rptr = 0;
|
||||
q->wptr = (size == PS2_QUEUE_SIZE) ? 0 : size;
|
||||
q->count = size;
|
||||
s->update_irq(s->update_arg, q->count != 0);
|
||||
}
|
||||
|
||||
static void ps2_kbd_reset(void *opaque)
|
||||
|
@ -104,9 +104,9 @@ static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t* prom_buf, int index,
|
||||
|
||||
static int64_t load_kernel (CPUMIPSState *env)
|
||||
{
|
||||
int64_t kernel_entry, kernel_low, kernel_high;
|
||||
int64_t kernel_entry, kernel_low, kernel_high, initrd_size;
|
||||
int index = 0;
|
||||
long kernel_size, initrd_size;
|
||||
long kernel_size;
|
||||
ram_addr_t initrd_offset;
|
||||
uint32_t *prom_buf;
|
||||
long prom_size;
|
||||
@ -150,7 +150,7 @@ static int64_t load_kernel (CPUMIPSState *env)
|
||||
|
||||
prom_set(prom_buf, index++, "%s", loaderparams.kernel_filename);
|
||||
if (initrd_size > 0) {
|
||||
prom_set(prom_buf, index++, "rd_start=0x%" PRIx64 " rd_size=%li %s",
|
||||
prom_set(prom_buf, index++, "rd_start=0x%" PRIx64 " rd_size=%" PRId64 " %s",
|
||||
cpu_mips_phys_to_kseg0(NULL, initrd_offset), initrd_size,
|
||||
loaderparams.kernel_cmdline);
|
||||
} else {
|
||||
|
@ -995,8 +995,8 @@ static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t* prom_buf, int index,
|
||||
/* Kernel */
|
||||
static int64_t load_kernel (void)
|
||||
{
|
||||
int64_t kernel_entry, kernel_high;
|
||||
long kernel_size, initrd_size;
|
||||
int64_t kernel_entry, kernel_high, initrd_size;
|
||||
long kernel_size;
|
||||
ram_addr_t initrd_offset;
|
||||
int big_endian;
|
||||
uint32_t *prom_buf;
|
||||
@ -1070,7 +1070,7 @@ static int64_t load_kernel (void)
|
||||
|
||||
prom_set(prom_buf, prom_index++, "%s", loaderparams.kernel_filename);
|
||||
if (initrd_size > 0) {
|
||||
prom_set(prom_buf, prom_index++, "rd_start=0x%" PRIx64 " rd_size=%li %s",
|
||||
prom_set(prom_buf, prom_index++, "rd_start=0x%" PRIx64 " rd_size=%" PRId64 " %s",
|
||||
xlate_to_kseg0(NULL, initrd_offset), initrd_size,
|
||||
loaderparams.kernel_cmdline);
|
||||
} else {
|
||||
|
@ -58,9 +58,8 @@ typedef struct ResetData {
|
||||
|
||||
static int64_t load_kernel(void)
|
||||
{
|
||||
int64_t entry, kernel_high;
|
||||
int64_t entry, kernel_high, initrd_size;
|
||||
long kernel_size;
|
||||
long initrd_size;
|
||||
ram_addr_t initrd_offset;
|
||||
int big_endian;
|
||||
|
||||
|
@ -81,8 +81,8 @@ typedef struct ResetData {
|
||||
static int64_t load_kernel(void)
|
||||
{
|
||||
const size_t params_size = 264;
|
||||
int64_t entry, kernel_high;
|
||||
long kernel_size, initrd_size;
|
||||
int64_t entry, kernel_high, initrd_size;
|
||||
long kernel_size;
|
||||
ram_addr_t initrd_offset;
|
||||
uint32_t *params_buf;
|
||||
int big_endian;
|
||||
@ -136,7 +136,7 @@ static int64_t load_kernel(void)
|
||||
params_buf[1] = tswap32(0x12345678);
|
||||
|
||||
if (initrd_size > 0) {
|
||||
snprintf((char *)params_buf + 8, 256, "rd_start=0x%" PRIx64 " rd_size=%li %s",
|
||||
snprintf((char *)params_buf + 8, 256, "rd_start=0x%" PRIx64 " rd_size=%" PRId64 " %s",
|
||||
cpu_mips_phys_to_kseg0(NULL, initrd_offset),
|
||||
initrd_size, loaderparams.kernel_cmdline);
|
||||
} else {
|
||||
|
@ -23,6 +23,11 @@ typedef struct ISADebugExitState {
|
||||
MemoryRegion io;
|
||||
} ISADebugExitState;
|
||||
|
||||
static uint64_t debug_exit_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void debug_exit_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
unsigned width)
|
||||
{
|
||||
@ -30,6 +35,7 @@ static void debug_exit_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
}
|
||||
|
||||
static const MemoryRegionOps debug_exit_ops = {
|
||||
.read = debug_exit_read,
|
||||
.write = debug_exit_write,
|
||||
.valid.min_access_size = 1,
|
||||
.valid.max_access_size = 4,
|
||||
|
@ -30,7 +30,8 @@
|
||||
#include "qemu/main-loop.h" /* iothread mutex */
|
||||
#include "qapi/visitor.h"
|
||||
|
||||
#define EDU(obj) OBJECT_CHECK(EduState, obj, "edu")
|
||||
#define TYPE_PCI_EDU_DEVICE "edu"
|
||||
#define EDU(obj) OBJECT_CHECK(EduState, obj, TYPE_PCI_EDU_DEVICE)
|
||||
|
||||
#define FACT_IRQ 0x00000001
|
||||
#define DMA_IRQ 0x00000100
|
||||
@ -414,7 +415,7 @@ static void pci_edu_register_types(void)
|
||||
{ },
|
||||
};
|
||||
static const TypeInfo edu_info = {
|
||||
.name = "edu",
|
||||
.name = TYPE_PCI_EDU_DEVICE,
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(EduState),
|
||||
.instance_init = edu_instance_init,
|
||||
|
@ -105,7 +105,12 @@ static void hv_synic_test_dev_control(HypervTestDev *dev, uint32_t ctl,
|
||||
}
|
||||
}
|
||||
|
||||
static void hv_test_dev_control(void *opaque, hwaddr addr, uint64_t data,
|
||||
static uint64_t hv_test_dev_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hv_test_dev_write(void *opaque, hwaddr addr, uint64_t data,
|
||||
uint32_t len)
|
||||
{
|
||||
HypervTestDev *dev = HYPERV_TEST_DEV(opaque);
|
||||
@ -127,7 +132,8 @@ static void hv_test_dev_control(void *opaque, hwaddr addr, uint64_t data,
|
||||
}
|
||||
|
||||
static const MemoryRegionOps synic_test_sint_ops = {
|
||||
.write = hv_test_dev_control,
|
||||
.read = hv_test_dev_read,
|
||||
.write = hv_test_dev_write,
|
||||
.valid.min_access_size = 4,
|
||||
.valid.max_access_size = 4,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
|
@ -58,7 +58,12 @@ typedef struct PCTestdev {
|
||||
#define TESTDEV(obj) \
|
||||
OBJECT_CHECK(PCTestdev, (obj), TYPE_TESTDEV)
|
||||
|
||||
static void test_irq_line(void *opaque, hwaddr addr, uint64_t data,
|
||||
static uint64_t test_irq_line_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_irq_line_write(void *opaque, hwaddr addr, uint64_t data,
|
||||
unsigned len)
|
||||
{
|
||||
PCTestdev *dev = opaque;
|
||||
@ -68,7 +73,8 @@ static void test_irq_line(void *opaque, hwaddr addr, uint64_t data,
|
||||
}
|
||||
|
||||
static const MemoryRegionOps test_irq_ops = {
|
||||
.write = test_irq_line,
|
||||
.read = test_irq_line_read,
|
||||
.write = test_irq_line_write,
|
||||
.valid.min_access_size = 1,
|
||||
.valid.max_access_size = 1,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
@ -110,7 +116,12 @@ static const MemoryRegionOps test_ioport_byte_ops = {
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static void test_flush_page(void *opaque, hwaddr addr, uint64_t data,
|
||||
static uint64_t test_flush_page_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_flush_page_write(void *opaque, hwaddr addr, uint64_t data,
|
||||
unsigned len)
|
||||
{
|
||||
hwaddr page = 4096;
|
||||
@ -126,7 +137,8 @@ static void test_flush_page(void *opaque, hwaddr addr, uint64_t data,
|
||||
}
|
||||
|
||||
static const MemoryRegionOps test_flush_ops = {
|
||||
.write = test_flush_page,
|
||||
.read = test_flush_page_read,
|
||||
.write = test_flush_page_write,
|
||||
.valid.min_access_size = 4,
|
||||
.valid.max_access_size = 4,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
|
@ -54,8 +54,8 @@ typedef struct {
|
||||
static void load_kernel(MoxieCPU *cpu, LoaderParams *loader_params)
|
||||
{
|
||||
uint64_t entry, kernel_low, kernel_high;
|
||||
int64_t initrd_size;
|
||||
long kernel_size;
|
||||
long initrd_size;
|
||||
ram_addr_t initrd_offset;
|
||||
|
||||
kernel_size = load_elf(loader_params->kernel_filename, NULL, NULL,
|
||||
|
@ -434,6 +434,11 @@ static bool fw_cfg_data_mem_valid(void *opaque, hwaddr addr,
|
||||
return addr == 0;
|
||||
}
|
||||
|
||||
static uint64_t fw_cfg_ctl_mem_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fw_cfg_ctl_mem_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
@ -468,6 +473,7 @@ static bool fw_cfg_comb_valid(void *opaque, hwaddr addr,
|
||||
}
|
||||
|
||||
static const MemoryRegionOps fw_cfg_ctl_mem_ops = {
|
||||
.read = fw_cfg_ctl_mem_read,
|
||||
.write = fw_cfg_ctl_mem_write,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
.valid.accepts = fw_cfg_ctl_mem_valid,
|
||||
@ -1109,12 +1115,7 @@ static void fw_cfg_mem_realize(DeviceState *dev, Error **errp)
|
||||
sysbus_init_mmio(sbd, &s->ctl_iomem);
|
||||
|
||||
if (s->data_width > data_ops->valid.max_access_size) {
|
||||
/* memberwise copy because the "old_mmio" member is const */
|
||||
s->wide_data_ops.read = data_ops->read;
|
||||
s->wide_data_ops.write = data_ops->write;
|
||||
s->wide_data_ops.endianness = data_ops->endianness;
|
||||
s->wide_data_ops.valid = data_ops->valid;
|
||||
s->wide_data_ops.impl = data_ops->impl;
|
||||
s->wide_data_ops = *data_ops;
|
||||
|
||||
s->wide_data_ops.valid.max_access_size = s->data_width;
|
||||
s->wide_data_ops.impl.max_access_size = s->data_width;
|
||||
|
@ -20,20 +20,7 @@
|
||||
#include "hw/scsi/scsi.h"
|
||||
#include "sysemu/dma.h"
|
||||
#include "qemu/log.h"
|
||||
|
||||
//#define DEBUG_LSI
|
||||
//#define DEBUG_LSI_REG
|
||||
|
||||
#ifdef DEBUG_LSI
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { printf("lsi_scsi: " fmt , ## __VA_ARGS__); } while (0)
|
||||
#define BADF(fmt, ...) \
|
||||
do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, ...) do {} while(0)
|
||||
#define BADF(fmt, ...) \
|
||||
do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__);} while (0)
|
||||
#endif
|
||||
#include "trace.h"
|
||||
|
||||
static const char *names[] = {
|
||||
"SCNTL0", "SCNTL1", "SCNTL2", "SCNTL3", "SCID", "SXFER", "SDID", "GPREG",
|
||||
@ -313,7 +300,7 @@ static inline int lsi_irq_on_rsl(LSIState *s)
|
||||
|
||||
static void lsi_soft_reset(LSIState *s)
|
||||
{
|
||||
DPRINTF("Reset\n");
|
||||
trace_lsi_reset();
|
||||
s->carry = 0;
|
||||
|
||||
s->msg_action = 0;
|
||||
@ -484,15 +471,13 @@ static void lsi_update_irq(LSIState *s)
|
||||
level = 1;
|
||||
|
||||
if (level != last_level) {
|
||||
DPRINTF("Update IRQ level %d dstat %02x sist %02x%02x\n",
|
||||
level, s->dstat, s->sist1, s->sist0);
|
||||
trace_lsi_update_irq(level, s->dstat, s->sist1, s->sist0);
|
||||
last_level = level;
|
||||
}
|
||||
lsi_set_irq(s, level);
|
||||
|
||||
if (!level && lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON)) {
|
||||
DPRINTF("Handled IRQs & disconnected, looking for pending "
|
||||
"processes\n");
|
||||
trace_lsi_update_irq_disconnected();
|
||||
QTAILQ_FOREACH(p, &s->queue, next) {
|
||||
if (p->pending) {
|
||||
lsi_reselect(s, p);
|
||||
@ -508,8 +493,7 @@ static void lsi_script_scsi_interrupt(LSIState *s, int stat0, int stat1)
|
||||
uint32_t mask0;
|
||||
uint32_t mask1;
|
||||
|
||||
DPRINTF("SCSI Interrupt 0x%02x%02x prev 0x%02x%02x\n",
|
||||
stat1, stat0, s->sist1, s->sist0);
|
||||
trace_lsi_script_scsi_interrupt(stat1, stat0, s->sist1, s->sist0);
|
||||
s->sist0 |= stat0;
|
||||
s->sist1 |= stat1;
|
||||
/* Stop processor on fatal or unmasked interrupt. As a special hack
|
||||
@ -527,7 +511,7 @@ static void lsi_script_scsi_interrupt(LSIState *s, int stat0, int stat1)
|
||||
/* Stop SCRIPTS execution and raise a DMA interrupt. */
|
||||
static void lsi_script_dma_interrupt(LSIState *s, int stat)
|
||||
{
|
||||
DPRINTF("DMA Interrupt 0x%x prev 0x%x\n", stat, s->dstat);
|
||||
trace_lsi_script_dma_interrupt(stat, s->dstat);
|
||||
s->dstat |= stat;
|
||||
lsi_update_irq(s);
|
||||
lsi_stop_script(s);
|
||||
@ -547,9 +531,9 @@ static void lsi_bad_phase(LSIState *s, int out, int new_phase)
|
||||
} else {
|
||||
s->dsp = (s->scntl2 & LSI_SCNTL2_WSR ? s->pmjad2 : s->pmjad1);
|
||||
}
|
||||
DPRINTF("Data phase mismatch jump to %08x\n", s->dsp);
|
||||
trace_lsi_bad_phase_jump(s->dsp);
|
||||
} else {
|
||||
DPRINTF("Phase mismatch interrupt\n");
|
||||
trace_lsi_bad_phase_interrupt();
|
||||
lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0);
|
||||
lsi_stop_script(s);
|
||||
}
|
||||
@ -576,7 +560,7 @@ static void lsi_disconnect(LSIState *s)
|
||||
|
||||
static void lsi_bad_selection(LSIState *s, uint32_t id)
|
||||
{
|
||||
DPRINTF("Selected absent target %d\n", id);
|
||||
trace_lsi_bad_selection(id);
|
||||
lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO);
|
||||
lsi_disconnect(s);
|
||||
}
|
||||
@ -591,7 +575,7 @@ static void lsi_do_dma(LSIState *s, int out)
|
||||
assert(s->current);
|
||||
if (!s->current->dma_len) {
|
||||
/* Wait until data is available. */
|
||||
DPRINTF("DMA no data available\n");
|
||||
trace_lsi_do_dma_unavailable();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -611,7 +595,7 @@ static void lsi_do_dma(LSIState *s, int out)
|
||||
else if (s->sbms)
|
||||
addr |= ((uint64_t)s->sbms << 32);
|
||||
|
||||
DPRINTF("DMA addr=0x" DMA_ADDR_FMT " len=%d\n", addr, count);
|
||||
trace_lsi_do_dma(addr, count);
|
||||
s->csbc += count;
|
||||
s->dnad += count;
|
||||
s->dbc -= count;
|
||||
@ -640,7 +624,7 @@ static void lsi_queue_command(LSIState *s)
|
||||
{
|
||||
lsi_request *p = s->current;
|
||||
|
||||
DPRINTF("Queueing tag=0x%x\n", p->tag);
|
||||
trace_lsi_queue_command(p->tag);
|
||||
assert(s->current != NULL);
|
||||
assert(s->current->dma_len == 0);
|
||||
QTAILQ_INSERT_TAIL(&s->queue, s->current, next);
|
||||
@ -654,9 +638,9 @@ static void lsi_queue_command(LSIState *s)
|
||||
static void lsi_add_msg_byte(LSIState *s, uint8_t data)
|
||||
{
|
||||
if (s->msg_len >= LSI_MAX_MSGIN_LEN) {
|
||||
BADF("MSG IN data too long\n");
|
||||
trace_lsi_add_msg_byte_error();
|
||||
} else {
|
||||
DPRINTF("MSG IN 0x%02x\n", data);
|
||||
trace_lsi_add_msg_byte(data);
|
||||
s->msg[s->msg_len++] = data;
|
||||
}
|
||||
}
|
||||
@ -676,7 +660,7 @@ static void lsi_reselect(LSIState *s, lsi_request *p)
|
||||
if (!(s->dcntl & LSI_DCNTL_COM)) {
|
||||
s->sfbr = 1 << (id & 0x7);
|
||||
}
|
||||
DPRINTF("Reselected target %d\n", id);
|
||||
trace_lsi_reselect(id);
|
||||
s->scntl1 |= LSI_SCNTL1_CON;
|
||||
lsi_set_phase(s, PHASE_MI);
|
||||
s->msg_action = p->out ? 2 : 3;
|
||||
@ -732,7 +716,7 @@ static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len)
|
||||
lsi_request *p = req->hba_private;
|
||||
|
||||
if (p->pending) {
|
||||
BADF("Multiple IO pending for request %p\n", p);
|
||||
trace_lsi_queue_req_error(p);
|
||||
}
|
||||
p->pending = len;
|
||||
/* Reselect if waiting for it, or if reselection triggers an IRQ
|
||||
@ -747,7 +731,7 @@ static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len)
|
||||
lsi_reselect(s, p);
|
||||
return 0;
|
||||
} else {
|
||||
DPRINTF("Queueing IO tag=0x%x\n", p->tag);
|
||||
trace_lsi_queue_req(p->tag);
|
||||
p->pending = len;
|
||||
return 1;
|
||||
}
|
||||
@ -760,7 +744,7 @@ static void lsi_command_complete(SCSIRequest *req, uint32_t status, size_t resid
|
||||
int out;
|
||||
|
||||
out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
|
||||
DPRINTF("Command complete status=%d\n", (int)status);
|
||||
trace_lsi_command_complete(status);
|
||||
s->status = status;
|
||||
s->command_complete = 2;
|
||||
if (s->waiting && s->dbc != 0) {
|
||||
@ -795,7 +779,7 @@ static void lsi_transfer_data(SCSIRequest *req, uint32_t len)
|
||||
out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
|
||||
|
||||
/* host adapter (re)connected */
|
||||
DPRINTF("Data ready tag=0x%x len=%d\n", req->tag, len);
|
||||
trace_lsi_transfer_data(req->tag, len);
|
||||
s->current->dma_len = len;
|
||||
s->command_complete = 1;
|
||||
if (s->waiting) {
|
||||
@ -814,7 +798,7 @@ static void lsi_do_command(LSIState *s)
|
||||
uint32_t id;
|
||||
int n;
|
||||
|
||||
DPRINTF("Send command len=%d\n", s->dbc);
|
||||
trace_lsi_do_command(s->dbc);
|
||||
if (s->dbc > 16)
|
||||
s->dbc = 16;
|
||||
pci_dma_read(PCI_DEVICE(s), s->dnad, buf, s->dbc);
|
||||
@ -862,9 +846,10 @@ static void lsi_do_command(LSIState *s)
|
||||
static void lsi_do_status(LSIState *s)
|
||||
{
|
||||
uint8_t status;
|
||||
DPRINTF("Get status len=%d status=%d\n", s->dbc, s->status);
|
||||
if (s->dbc != 1)
|
||||
BADF("Bad Status move\n");
|
||||
trace_lsi_do_status(s->dbc, s->status);
|
||||
if (s->dbc != 1) {
|
||||
trace_lsi_do_status_error();
|
||||
}
|
||||
s->dbc = 1;
|
||||
status = s->status;
|
||||
s->sfbr = status;
|
||||
@ -877,7 +862,7 @@ static void lsi_do_status(LSIState *s)
|
||||
static void lsi_do_msgin(LSIState *s)
|
||||
{
|
||||
int len;
|
||||
DPRINTF("Message in len=%d/%d\n", s->dbc, s->msg_len);
|
||||
trace_lsi_do_msgin(s->dbc, s->msg_len);
|
||||
s->sfbr = s->msg[0];
|
||||
len = s->msg_len;
|
||||
if (len > s->dbc)
|
||||
@ -942,36 +927,36 @@ static void lsi_do_msgout(LSIState *s)
|
||||
current_req = lsi_find_by_tag(s, current_tag);
|
||||
}
|
||||
|
||||
DPRINTF("MSG out len=%d\n", s->dbc);
|
||||
trace_lsi_do_msgout(s->dbc);
|
||||
while (s->dbc) {
|
||||
msg = lsi_get_msgbyte(s);
|
||||
s->sfbr = msg;
|
||||
|
||||
switch (msg) {
|
||||
case 0x04:
|
||||
DPRINTF("MSG: Disconnect\n");
|
||||
trace_lsi_do_msgout_disconnect();
|
||||
lsi_disconnect(s);
|
||||
break;
|
||||
case 0x08:
|
||||
DPRINTF("MSG: No Operation\n");
|
||||
trace_lsi_do_msgout_noop();
|
||||
lsi_set_phase(s, PHASE_CMD);
|
||||
break;
|
||||
case 0x01:
|
||||
len = lsi_get_msgbyte(s);
|
||||
msg = lsi_get_msgbyte(s);
|
||||
(void)len; /* avoid a warning about unused variable*/
|
||||
DPRINTF("Extended message 0x%x (len %d)\n", msg, len);
|
||||
trace_lsi_do_msgout_extended(msg, len);
|
||||
switch (msg) {
|
||||
case 1:
|
||||
DPRINTF("SDTR (ignored)\n");
|
||||
trace_lsi_do_msgout_ignored("SDTR");
|
||||
lsi_skip_msgbytes(s, 2);
|
||||
break;
|
||||
case 3:
|
||||
DPRINTF("WDTR (ignored)\n");
|
||||
trace_lsi_do_msgout_ignored("WDTR");
|
||||
lsi_skip_msgbytes(s, 1);
|
||||
break;
|
||||
case 4:
|
||||
DPRINTF("PPR (ignored)\n");
|
||||
trace_lsi_do_msgout_ignored("PPR");
|
||||
lsi_skip_msgbytes(s, 5);
|
||||
break;
|
||||
default:
|
||||
@ -980,19 +965,20 @@ static void lsi_do_msgout(LSIState *s)
|
||||
break;
|
||||
case 0x20: /* SIMPLE queue */
|
||||
s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID;
|
||||
DPRINTF("SIMPLE queue tag=0x%x\n", s->select_tag & 0xff);
|
||||
trace_lsi_do_msgout_simplequeue(s->select_tag & 0xff);
|
||||
break;
|
||||
case 0x21: /* HEAD of queue */
|
||||
BADF("HEAD queue not implemented\n");
|
||||
qemu_log_mask(LOG_UNIMP, "lsi_scsi: HEAD queue not implemented\n");
|
||||
s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID;
|
||||
break;
|
||||
case 0x22: /* ORDERED queue */
|
||||
BADF("ORDERED queue not implemented\n");
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"lsi_scsi: ORDERED queue not implemented\n");
|
||||
s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID;
|
||||
break;
|
||||
case 0x0d:
|
||||
/* The ABORT TAG message clears the current I/O process only. */
|
||||
DPRINTF("MSG: ABORT TAG tag=0x%x\n", current_tag);
|
||||
trace_lsi_do_msgout_abort(current_tag);
|
||||
if (current_req) {
|
||||
scsi_req_cancel(current_req->req);
|
||||
}
|
||||
@ -1004,17 +990,17 @@ static void lsi_do_msgout(LSIState *s)
|
||||
/* The ABORT message clears all I/O processes for the selecting
|
||||
initiator on the specified logical unit of the target. */
|
||||
if (msg == 0x06) {
|
||||
DPRINTF("MSG: ABORT tag=0x%x\n", current_tag);
|
||||
trace_lsi_do_msgout_abort(current_tag);
|
||||
}
|
||||
/* The CLEAR QUEUE message clears all I/O processes for all
|
||||
initiators on the specified logical unit of the target. */
|
||||
if (msg == 0x0e) {
|
||||
DPRINTF("MSG: CLEAR QUEUE tag=0x%x\n", current_tag);
|
||||
trace_lsi_do_msgout_clearqueue(current_tag);
|
||||
}
|
||||
/* The BUS DEVICE RESET message clears all I/O processes for all
|
||||
initiators on all logical units of the target. */
|
||||
if (msg == 0x0c) {
|
||||
DPRINTF("MSG: BUS DEVICE RESET tag=0x%x\n", current_tag);
|
||||
trace_lsi_do_msgout_busdevicereset(current_tag);
|
||||
}
|
||||
|
||||
/* clear the current I/O process */
|
||||
@ -1042,14 +1028,14 @@ static void lsi_do_msgout(LSIState *s)
|
||||
goto bad;
|
||||
}
|
||||
s->current_lun = msg & 7;
|
||||
DPRINTF("Select LUN %d\n", s->current_lun);
|
||||
trace_lsi_do_msgout_select(s->current_lun);
|
||||
lsi_set_phase(s, PHASE_CMD);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
bad:
|
||||
BADF("Unimplemented message 0x%02x\n", msg);
|
||||
qemu_log_mask(LOG_UNIMP, "Unimplemented message 0x%02x\n", msg);
|
||||
lsi_set_phase(s, PHASE_MI);
|
||||
lsi_add_msg_byte(s, 7); /* MESSAGE REJECT */
|
||||
s->msg_action = 0;
|
||||
@ -1061,7 +1047,7 @@ static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count)
|
||||
int n;
|
||||
uint8_t buf[LSI_BUF_SIZE];
|
||||
|
||||
DPRINTF("memcpy dest 0x%08x src 0x%08x count %d\n", dest, src, count);
|
||||
trace_lsi_memcpy(dest, src, count);
|
||||
while (count) {
|
||||
n = (count > LSI_BUF_SIZE) ? LSI_BUF_SIZE : count;
|
||||
lsi_mem_read(s, src, buf, n);
|
||||
@ -1076,7 +1062,7 @@ static void lsi_wait_reselect(LSIState *s)
|
||||
{
|
||||
lsi_request *p;
|
||||
|
||||
DPRINTF("Wait Reselect\n");
|
||||
trace_lsi_wait_reselect();
|
||||
|
||||
QTAILQ_FOREACH(p, &s->queue, next) {
|
||||
if (p->pending) {
|
||||
@ -1109,14 +1095,14 @@ again:
|
||||
}
|
||||
addr = read_dword(s, s->dsp + 4);
|
||||
addr_high = 0;
|
||||
DPRINTF("SCRIPTS dsp=%08x opcode %08x arg %08x\n", s->dsp, insn, addr);
|
||||
trace_lsi_execute_script(s->dsp, insn, addr);
|
||||
s->dsps = addr;
|
||||
s->dcmd = insn >> 24;
|
||||
s->dsp += 8;
|
||||
switch (insn >> 30) {
|
||||
case 0: /* Block move. */
|
||||
if (s->sist1 & LSI_SIST1_STO) {
|
||||
DPRINTF("Delayed select timeout\n");
|
||||
trace_lsi_execute_script_blockmove_delayed();
|
||||
lsi_stop_script(s);
|
||||
break;
|
||||
}
|
||||
@ -1171,8 +1157,9 @@ again:
|
||||
addr_high = s->dbms;
|
||||
break;
|
||||
default:
|
||||
BADF("Illegal selector specified (0x%x > 0x15)"
|
||||
" for 64-bit DMA block move", selector);
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"lsi_scsi: Illegal selector specified (0x%x > 0x15) "
|
||||
"for 64-bit DMA block move", selector);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1184,8 +1171,8 @@ again:
|
||||
s->ia = s->dsp - 12;
|
||||
}
|
||||
if ((s->sstat1 & PHASE_MASK) != ((insn >> 24) & 7)) {
|
||||
DPRINTF("Wrong phase got %d expected %d\n",
|
||||
s->sstat1 & PHASE_MASK, (insn >> 24) & 7);
|
||||
trace_lsi_execute_script_blockmove_badphase(s->sstat1 & PHASE_MASK,
|
||||
(insn >> 24) & 7);
|
||||
lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0);
|
||||
break;
|
||||
}
|
||||
@ -1217,8 +1204,8 @@ again:
|
||||
lsi_do_msgin(s);
|
||||
break;
|
||||
default:
|
||||
BADF("Unimplemented phase %d\n", s->sstat1 & PHASE_MASK);
|
||||
exit(1);
|
||||
qemu_log_mask(LOG_UNIMP, "lsi_scsi: Unimplemented phase %d\n",
|
||||
s->sstat1 & PHASE_MASK);
|
||||
}
|
||||
s->dfifo = s->dbc & 0xff;
|
||||
s->ctest5 = (s->ctest5 & 0xfc) | ((s->dbc >> 8) & 3);
|
||||
@ -1246,7 +1233,7 @@ again:
|
||||
case 0: /* Select */
|
||||
s->sdid = id;
|
||||
if (s->scntl1 & LSI_SCNTL1_CON) {
|
||||
DPRINTF("Already reselected, jumping to alternative address\n");
|
||||
trace_lsi_execute_script_io_alreadyreselected();
|
||||
s->dsp = s->dnad;
|
||||
break;
|
||||
}
|
||||
@ -1256,8 +1243,8 @@ again:
|
||||
lsi_bad_selection(s, id);
|
||||
break;
|
||||
}
|
||||
DPRINTF("Selected target %d%s\n",
|
||||
id, insn & (1 << 3) ? " ATN" : "");
|
||||
trace_lsi_execute_script_io_selected(id,
|
||||
insn & (1 << 3) ? " ATN" : "");
|
||||
/* ??? Linux drivers compain when this is set. Maybe
|
||||
it only applies in low-level mode (unimplemented).
|
||||
lsi_script_scsi_interrupt(s, LSI_SIST0_CMP, 0); */
|
||||
@ -1269,7 +1256,7 @@ again:
|
||||
lsi_set_phase(s, PHASE_MO);
|
||||
break;
|
||||
case 1: /* Disconnect */
|
||||
DPRINTF("Wait Disconnect\n");
|
||||
trace_lsi_execute_script_io_disconnect();
|
||||
s->scntl1 &= ~LSI_SCNTL1_CON;
|
||||
break;
|
||||
case 2: /* Wait Reselect */
|
||||
@ -1278,7 +1265,7 @@ again:
|
||||
}
|
||||
break;
|
||||
case 3: /* Set */
|
||||
DPRINTF("Set%s%s%s%s\n",
|
||||
trace_lsi_execute_script_io_set(
|
||||
insn & (1 << 3) ? " ATN" : "",
|
||||
insn & (1 << 6) ? " ACK" : "",
|
||||
insn & (1 << 9) ? " TM" : "",
|
||||
@ -1288,14 +1275,14 @@ again:
|
||||
lsi_set_phase(s, PHASE_MO);
|
||||
}
|
||||
if (insn & (1 << 9)) {
|
||||
BADF("Target mode not implemented\n");
|
||||
exit(1);
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"lsi_scsi: Target mode not implemented\n");
|
||||
}
|
||||
if (insn & (1 << 10))
|
||||
s->carry = 1;
|
||||
break;
|
||||
case 4: /* Clear */
|
||||
DPRINTF("Clear%s%s%s%s\n",
|
||||
trace_lsi_execute_script_io_clear(
|
||||
insn & (1 << 3) ? " ATN" : "",
|
||||
insn & (1 << 6) ? " ACK" : "",
|
||||
insn & (1 << 9) ? " TM" : "",
|
||||
@ -1313,18 +1300,17 @@ again:
|
||||
uint8_t data8;
|
||||
int reg;
|
||||
int operator;
|
||||
#ifdef DEBUG_LSI
|
||||
|
||||
static const char *opcode_names[3] =
|
||||
{"Write", "Read", "Read-Modify-Write"};
|
||||
static const char *operator_names[8] =
|
||||
{"MOV", "SHL", "OR", "XOR", "AND", "SHR", "ADD", "ADC"};
|
||||
#endif
|
||||
|
||||
reg = ((insn >> 16) & 0x7f) | (insn & 0x80);
|
||||
data8 = (insn >> 8) & 0xff;
|
||||
opcode = (insn >> 27) & 7;
|
||||
operator = (insn >> 24) & 7;
|
||||
DPRINTF("%s reg 0x%x %s data8=0x%02x sfbr=0x%02x%s\n",
|
||||
trace_lsi_execute_script_io_opcode(
|
||||
opcode_names[opcode - 5], reg,
|
||||
operator_names[operator], data8, s->sfbr,
|
||||
(insn & (1 << 23)) ? " SFBR" : "");
|
||||
@ -1404,21 +1390,21 @@ again:
|
||||
int jmp;
|
||||
|
||||
if ((insn & 0x002e0000) == 0) {
|
||||
DPRINTF("NOP\n");
|
||||
trace_lsi_execute_script_tc_nop();
|
||||
break;
|
||||
}
|
||||
if (s->sist1 & LSI_SIST1_STO) {
|
||||
DPRINTF("Delayed select timeout\n");
|
||||
trace_lsi_execute_script_tc_delayedselect_timeout();
|
||||
lsi_stop_script(s);
|
||||
break;
|
||||
}
|
||||
cond = jmp = (insn & (1 << 19)) != 0;
|
||||
if (cond == jmp && (insn & (1 << 21))) {
|
||||
DPRINTF("Compare carry %d\n", s->carry == jmp);
|
||||
trace_lsi_execute_script_tc_compc(s->carry == jmp);
|
||||
cond = s->carry != 0;
|
||||
}
|
||||
if (cond == jmp && (insn & (1 << 17))) {
|
||||
DPRINTF("Compare phase %d %c= %d\n",
|
||||
trace_lsi_execute_script_tc_compp(
|
||||
(s->sstat1 & PHASE_MASK),
|
||||
jmp ? '=' : '!',
|
||||
((insn >> 24) & 7));
|
||||
@ -1428,7 +1414,7 @@ again:
|
||||
uint8_t mask;
|
||||
|
||||
mask = (~insn >> 8) & 0xff;
|
||||
DPRINTF("Compare data 0x%x & 0x%x %c= 0x%x\n",
|
||||
trace_lsi_execute_script_tc_compd(
|
||||
s->sfbr, mask, jmp ? '=' : '!', insn & mask);
|
||||
cond = (s->sfbr & mask) == (insn & mask);
|
||||
}
|
||||
@ -1439,21 +1425,21 @@ again:
|
||||
}
|
||||
switch ((insn >> 27) & 7) {
|
||||
case 0: /* Jump */
|
||||
DPRINTF("Jump to 0x%08x\n", addr);
|
||||
trace_lsi_execute_script_tc_jump(addr);
|
||||
s->adder = addr;
|
||||
s->dsp = addr;
|
||||
break;
|
||||
case 1: /* Call */
|
||||
DPRINTF("Call 0x%08x\n", addr);
|
||||
trace_lsi_execute_script_tc_call(addr);
|
||||
s->temp = s->dsp;
|
||||
s->dsp = addr;
|
||||
break;
|
||||
case 2: /* Return */
|
||||
DPRINTF("Return to 0x%08x\n", s->temp);
|
||||
trace_lsi_execute_script_tc_return(s->temp);
|
||||
s->dsp = s->temp;
|
||||
break;
|
||||
case 3: /* Interrupt */
|
||||
DPRINTF("Interrupt 0x%08x\n", s->dsps);
|
||||
trace_lsi_execute_script_tc_interrupt(s->dsps);
|
||||
if ((insn & (1 << 20)) != 0) {
|
||||
s->istat0 |= LSI_ISTAT0_INTF;
|
||||
lsi_update_irq(s);
|
||||
@ -1462,12 +1448,12 @@ again:
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DPRINTF("Illegal transfer control\n");
|
||||
trace_lsi_execute_script_tc_illegal();
|
||||
lsi_script_dma_interrupt(s, LSI_DSTAT_IID);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
DPRINTF("Control condition failed\n");
|
||||
trace_lsi_execute_script_tc_cc_failed();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1495,13 +1481,12 @@ again:
|
||||
reg = (insn >> 16) & 0xff;
|
||||
if (insn & (1 << 24)) {
|
||||
pci_dma_read(pci_dev, addr, data, n);
|
||||
DPRINTF("Load reg 0x%x size %d addr 0x%08x = %08x\n", reg, n,
|
||||
addr, *(int *)data);
|
||||
trace_lsi_execute_script_mm_load(reg, n, addr, *(int *)data);
|
||||
for (i = 0; i < n; i++) {
|
||||
lsi_reg_writeb(s, reg + i, data[i]);
|
||||
}
|
||||
} else {
|
||||
DPRINTF("Store reg 0x%x size %d addr 0x%08x\n", reg, n, addr);
|
||||
trace_lsi_execute_script_mm_store(reg, n, addr);
|
||||
for (i = 0; i < n; i++) {
|
||||
data[i] = lsi_reg_readb(s, reg + i);
|
||||
}
|
||||
@ -1515,8 +1500,10 @@ again:
|
||||
assume this is the case and force an unexpected device disconnect.
|
||||
This is apparently sufficient to beat the drivers into submission.
|
||||
*/
|
||||
if (!(s->sien0 & LSI_SIST0_UDC))
|
||||
fprintf(stderr, "inf. loop with UDC masked\n");
|
||||
if (!(s->sien0 & LSI_SIST0_UDC)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"lsi_scsi: inf. loop with UDC masked");
|
||||
}
|
||||
lsi_script_scsi_interrupt(s, LSI_SIST0_UDC, 0);
|
||||
lsi_disconnect(s);
|
||||
} else if (s->istat1 & LSI_ISTAT1_SRUN && !s->waiting) {
|
||||
@ -1526,7 +1513,7 @@ again:
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
DPRINTF("SCRIPTS execution stopped\n");
|
||||
trace_lsi_execute_script_stop();
|
||||
}
|
||||
|
||||
static uint8_t lsi_reg_readb(LSIState *s, int offset)
|
||||
@ -1761,10 +1748,8 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset)
|
||||
#undef CASE_GET_REG24
|
||||
#undef CASE_GET_REG32
|
||||
|
||||
#ifdef DEBUG_LSI_REG
|
||||
DPRINTF("Read reg %s %x = %02x\n",
|
||||
offset < ARRAY_SIZE(names) ? names[offset] : "???", offset, ret);
|
||||
#endif
|
||||
trace_lsi_reg_read(offset < ARRAY_SIZE(names) ? names[offset] : "???",
|
||||
offset, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1782,21 +1767,22 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
|
||||
case addr + 2: s->name &= 0xff00ffff; s->name |= val << 16; break; \
|
||||
case addr + 3: s->name &= 0x00ffffff; s->name |= val << 24; break;
|
||||
|
||||
#ifdef DEBUG_LSI_REG
|
||||
DPRINTF("Write reg %s %x = %02x\n",
|
||||
offset < ARRAY_SIZE(names) ? names[offset] : "???", offset, val);
|
||||
#endif
|
||||
trace_lsi_reg_write(offset < ARRAY_SIZE(names) ? names[offset] : "???",
|
||||
offset, val);
|
||||
|
||||
switch (offset) {
|
||||
case 0x00: /* SCNTL0 */
|
||||
s->scntl0 = val;
|
||||
if (val & LSI_SCNTL0_START) {
|
||||
BADF("Start sequence not implemented\n");
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"lsi_scsi: Start sequence not implemented\n");
|
||||
}
|
||||
break;
|
||||
case 0x01: /* SCNTL1 */
|
||||
s->scntl1 = val & ~LSI_SCNTL1_SST;
|
||||
if (val & LSI_SCNTL1_IARB) {
|
||||
BADF("Immediate Arbritration not implemented\n");
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"lsi_scsi: Immediate Arbritration not implemented\n");
|
||||
}
|
||||
if (val & LSI_SCNTL1_RST) {
|
||||
if (!(s->sstat0 & LSI_SSTAT0_RST)) {
|
||||
@ -1823,7 +1809,8 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
|
||||
break;
|
||||
case 0x06: /* SDID */
|
||||
if ((s->ssid & 0x80) && (val & 0xf) != (s->ssid & 0xf)) {
|
||||
BADF("Destination ID does not match SSID\n");
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"lsi_scsi: Destination ID does not match SSID\n");
|
||||
}
|
||||
s->sdid = val & 0xf;
|
||||
break;
|
||||
@ -1851,7 +1838,7 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
|
||||
lsi_update_irq(s);
|
||||
}
|
||||
if (s->waiting == 1 && val & LSI_ISTAT0_SIGP) {
|
||||
DPRINTF("Woken by SIGP\n");
|
||||
trace_lsi_awoken();
|
||||
s->waiting = 0;
|
||||
s->dsp = s->dnad;
|
||||
lsi_execute_script(s);
|
||||
@ -1878,13 +1865,15 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
|
||||
CASE_SET_REG32(temp, 0x1c)
|
||||
case 0x21: /* CTEST4 */
|
||||
if (val & 7) {
|
||||
BADF("Unimplemented CTEST4-FBL 0x%x\n", val);
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"lsi_scsi: Unimplemented CTEST4-FBL 0x%x\n", val);
|
||||
}
|
||||
s->ctest4 = val;
|
||||
break;
|
||||
case 0x22: /* CTEST5 */
|
||||
if (val & (LSI_CTEST5_ADCK | LSI_CTEST5_BBCK)) {
|
||||
BADF("CTEST5 DMA increment not implemented\n");
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"lsi_scsi: CTEST5 DMA increment not implemented\n");
|
||||
}
|
||||
s->ctest5 = val;
|
||||
break;
|
||||
@ -1941,7 +1930,8 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
|
||||
break;
|
||||
case 0x49: /* STIME1 */
|
||||
if (val & 0xf) {
|
||||
DPRINTF("General purpose timer not implemented\n");
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"lsi_scsi: General purpose timer not implemented\n");
|
||||
/* ??? Raising the interrupt immediately seems to be sufficient
|
||||
to keep the FreeBSD driver happy. */
|
||||
lsi_script_scsi_interrupt(s, 0, LSI_SIST1_GEN);
|
||||
@ -1958,13 +1948,15 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
|
||||
break;
|
||||
case 0x4e: /* STEST2 */
|
||||
if (val & 1) {
|
||||
BADF("Low level mode not implemented\n");
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"lsi_scsi: Low level mode not implemented\n");
|
||||
}
|
||||
s->stest2 = val;
|
||||
break;
|
||||
case 0x4f: /* STEST3 */
|
||||
if (val & 0x41) {
|
||||
BADF("SCSI FIFO test mode not implemented\n");
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"lsi_scsi: SCSI FIFO test mode not implemented\n");
|
||||
}
|
||||
s->stest3 = val;
|
||||
break;
|
||||
|
@ -35,152 +35,155 @@
|
||||
|
||||
static void mptsas_fix_sgentry_endianness(MPISGEntry *sge)
|
||||
{
|
||||
le32_to_cpus(&sge->FlagsLength);
|
||||
sge->FlagsLength = le32_to_cpu(sge->FlagsLength);
|
||||
if (sge->FlagsLength & MPI_SGE_FLAGS_64_BIT_ADDRESSING) {
|
||||
le64_to_cpus(&sge->u.Address64);
|
||||
sge->u.Address64 = le64_to_cpu(sge->u.Address64);
|
||||
} else {
|
||||
le32_to_cpus(&sge->u.Address32);
|
||||
sge->u.Address32 = le32_to_cpu(sge->u.Address32);
|
||||
}
|
||||
}
|
||||
|
||||
static void mptsas_fix_sgentry_endianness_reply(MPISGEntry *sge)
|
||||
{
|
||||
if (sge->FlagsLength & MPI_SGE_FLAGS_64_BIT_ADDRESSING) {
|
||||
cpu_to_le64s(&sge->u.Address64);
|
||||
sge->u.Address64 = cpu_to_le64(sge->u.Address64);
|
||||
} else {
|
||||
cpu_to_le32s(&sge->u.Address32);
|
||||
sge->u.Address32 = cpu_to_le32(sge->u.Address32);
|
||||
}
|
||||
cpu_to_le32s(&sge->FlagsLength);
|
||||
sge->FlagsLength = cpu_to_le32(sge->FlagsLength);
|
||||
}
|
||||
|
||||
void mptsas_fix_scsi_io_endianness(MPIMsgSCSIIORequest *req)
|
||||
{
|
||||
le32_to_cpus(&req->MsgContext);
|
||||
le32_to_cpus(&req->Control);
|
||||
le32_to_cpus(&req->DataLength);
|
||||
le32_to_cpus(&req->SenseBufferLowAddr);
|
||||
req->MsgContext = le32_to_cpu(req->MsgContext);
|
||||
req->Control = le32_to_cpu(req->Control);
|
||||
req->DataLength = le32_to_cpu(req->DataLength);
|
||||
req->SenseBufferLowAddr = le32_to_cpu(req->SenseBufferLowAddr);
|
||||
}
|
||||
|
||||
void mptsas_fix_scsi_io_reply_endianness(MPIMsgSCSIIOReply *reply)
|
||||
{
|
||||
cpu_to_le32s(&reply->MsgContext);
|
||||
cpu_to_le16s(&reply->IOCStatus);
|
||||
cpu_to_le32s(&reply->IOCLogInfo);
|
||||
cpu_to_le32s(&reply->TransferCount);
|
||||
cpu_to_le32s(&reply->SenseCount);
|
||||
cpu_to_le32s(&reply->ResponseInfo);
|
||||
cpu_to_le16s(&reply->TaskTag);
|
||||
reply->MsgContext = cpu_to_le32(reply->MsgContext);
|
||||
reply->IOCStatus = cpu_to_le16(reply->IOCStatus);
|
||||
reply->IOCLogInfo = cpu_to_le32(reply->IOCLogInfo);
|
||||
reply->TransferCount = cpu_to_le32(reply->TransferCount);
|
||||
reply->SenseCount = cpu_to_le32(reply->SenseCount);
|
||||
reply->ResponseInfo = cpu_to_le32(reply->ResponseInfo);
|
||||
reply->TaskTag = cpu_to_le16(reply->TaskTag);
|
||||
}
|
||||
|
||||
void mptsas_fix_scsi_task_mgmt_endianness(MPIMsgSCSITaskMgmt *req)
|
||||
{
|
||||
le32_to_cpus(&req->MsgContext);
|
||||
le32_to_cpus(&req->TaskMsgContext);
|
||||
req->MsgContext = le32_to_cpu(req->MsgContext);
|
||||
req->TaskMsgContext = le32_to_cpu(req->TaskMsgContext);
|
||||
}
|
||||
|
||||
void mptsas_fix_scsi_task_mgmt_reply_endianness(MPIMsgSCSITaskMgmtReply *reply)
|
||||
{
|
||||
cpu_to_le32s(&reply->MsgContext);
|
||||
cpu_to_le16s(&reply->IOCStatus);
|
||||
cpu_to_le32s(&reply->IOCLogInfo);
|
||||
cpu_to_le32s(&reply->TerminationCount);
|
||||
reply->MsgContext = cpu_to_le32(reply->MsgContext);
|
||||
reply->IOCStatus = cpu_to_le16(reply->IOCStatus);
|
||||
reply->IOCLogInfo = cpu_to_le32(reply->IOCLogInfo);
|
||||
reply->TerminationCount = cpu_to_le32(reply->TerminationCount);
|
||||
}
|
||||
|
||||
void mptsas_fix_ioc_init_endianness(MPIMsgIOCInit *req)
|
||||
{
|
||||
le32_to_cpus(&req->MsgContext);
|
||||
le16_to_cpus(&req->ReplyFrameSize);
|
||||
le32_to_cpus(&req->HostMfaHighAddr);
|
||||
le32_to_cpus(&req->SenseBufferHighAddr);
|
||||
le32_to_cpus(&req->ReplyFifoHostSignalingAddr);
|
||||
req->MsgContext = le32_to_cpu(req->MsgContext);
|
||||
req->ReplyFrameSize = le16_to_cpu(req->ReplyFrameSize);
|
||||
req->HostMfaHighAddr = le32_to_cpu(req->HostMfaHighAddr);
|
||||
req->SenseBufferHighAddr = le32_to_cpu(req->SenseBufferHighAddr);
|
||||
req->ReplyFifoHostSignalingAddr =
|
||||
le32_to_cpu(req->ReplyFifoHostSignalingAddr);
|
||||
mptsas_fix_sgentry_endianness(&req->HostPageBufferSGE);
|
||||
le16_to_cpus(&req->MsgVersion);
|
||||
le16_to_cpus(&req->HeaderVersion);
|
||||
req->MsgVersion = le16_to_cpu(req->MsgVersion);
|
||||
req->HeaderVersion = le16_to_cpu(req->HeaderVersion);
|
||||
}
|
||||
|
||||
void mptsas_fix_ioc_init_reply_endianness(MPIMsgIOCInitReply *reply)
|
||||
{
|
||||
cpu_to_le32s(&reply->MsgContext);
|
||||
cpu_to_le16s(&reply->IOCStatus);
|
||||
cpu_to_le32s(&reply->IOCLogInfo);
|
||||
reply->MsgContext = cpu_to_le32(reply->MsgContext);
|
||||
reply->IOCStatus = cpu_to_le16(reply->IOCStatus);
|
||||
reply->IOCLogInfo = cpu_to_le32(reply->IOCLogInfo);
|
||||
}
|
||||
|
||||
void mptsas_fix_ioc_facts_endianness(MPIMsgIOCFacts *req)
|
||||
{
|
||||
le32_to_cpus(&req->MsgContext);
|
||||
req->MsgContext = le32_to_cpu(req->MsgContext);
|
||||
}
|
||||
|
||||
void mptsas_fix_ioc_facts_reply_endianness(MPIMsgIOCFactsReply *reply)
|
||||
{
|
||||
cpu_to_le16s(&reply->MsgVersion);
|
||||
cpu_to_le16s(&reply->HeaderVersion);
|
||||
cpu_to_le32s(&reply->MsgContext);
|
||||
cpu_to_le16s(&reply->IOCExceptions);
|
||||
cpu_to_le16s(&reply->IOCStatus);
|
||||
cpu_to_le32s(&reply->IOCLogInfo);
|
||||
cpu_to_le16s(&reply->ReplyQueueDepth);
|
||||
cpu_to_le16s(&reply->RequestFrameSize);
|
||||
cpu_to_le16s(&reply->ProductID);
|
||||
cpu_to_le32s(&reply->CurrentHostMfaHighAddr);
|
||||
cpu_to_le16s(&reply->GlobalCredits);
|
||||
cpu_to_le32s(&reply->CurrentSenseBufferHighAddr);
|
||||
cpu_to_le16s(&reply->CurReplyFrameSize);
|
||||
cpu_to_le32s(&reply->FWImageSize);
|
||||
cpu_to_le32s(&reply->IOCCapabilities);
|
||||
cpu_to_le16s(&reply->HighPriorityQueueDepth);
|
||||
reply->MsgVersion = cpu_to_le16(reply->MsgVersion);
|
||||
reply->HeaderVersion = cpu_to_le16(reply->HeaderVersion);
|
||||
reply->MsgContext = cpu_to_le32(reply->MsgContext);
|
||||
reply->IOCExceptions = cpu_to_le16(reply->IOCExceptions);
|
||||
reply->IOCStatus = cpu_to_le16(reply->IOCStatus);
|
||||
reply->IOCLogInfo = cpu_to_le32(reply->IOCLogInfo);
|
||||
reply->ReplyQueueDepth = cpu_to_le16(reply->ReplyQueueDepth);
|
||||
reply->RequestFrameSize = cpu_to_le16(reply->RequestFrameSize);
|
||||
reply->ProductID = cpu_to_le16(reply->ProductID);
|
||||
reply->CurrentHostMfaHighAddr = cpu_to_le32(reply->CurrentHostMfaHighAddr);
|
||||
reply->GlobalCredits = cpu_to_le16(reply->GlobalCredits);
|
||||
reply->CurrentSenseBufferHighAddr =
|
||||
cpu_to_le32(reply->CurrentSenseBufferHighAddr);
|
||||
reply->CurReplyFrameSize = cpu_to_le16(reply->CurReplyFrameSize);
|
||||
reply->FWImageSize = cpu_to_le32(reply->FWImageSize);
|
||||
reply->IOCCapabilities = cpu_to_le32(reply->IOCCapabilities);
|
||||
reply->HighPriorityQueueDepth = cpu_to_le16(reply->HighPriorityQueueDepth);
|
||||
mptsas_fix_sgentry_endianness_reply(&reply->HostPageBufferSGE);
|
||||
cpu_to_le32s(&reply->ReplyFifoHostSignalingAddr);
|
||||
reply->ReplyFifoHostSignalingAddr =
|
||||
cpu_to_le32(reply->ReplyFifoHostSignalingAddr);
|
||||
}
|
||||
|
||||
void mptsas_fix_config_endianness(MPIMsgConfig *req)
|
||||
{
|
||||
le16_to_cpus(&req->ExtPageLength);
|
||||
le32_to_cpus(&req->MsgContext);
|
||||
le32_to_cpus(&req->PageAddress);
|
||||
req->ExtPageLength = le16_to_cpu(req->ExtPageLength);
|
||||
req->MsgContext = le32_to_cpu(req->MsgContext);
|
||||
req->PageAddress = le32_to_cpu(req->PageAddress);
|
||||
mptsas_fix_sgentry_endianness(&req->PageBufferSGE);
|
||||
}
|
||||
|
||||
void mptsas_fix_config_reply_endianness(MPIMsgConfigReply *reply)
|
||||
{
|
||||
cpu_to_le16s(&reply->ExtPageLength);
|
||||
cpu_to_le32s(&reply->MsgContext);
|
||||
cpu_to_le16s(&reply->IOCStatus);
|
||||
cpu_to_le32s(&reply->IOCLogInfo);
|
||||
reply->ExtPageLength = cpu_to_le16(reply->ExtPageLength);
|
||||
reply->MsgContext = cpu_to_le32(reply->MsgContext);
|
||||
reply->IOCStatus = cpu_to_le16(reply->IOCStatus);
|
||||
reply->IOCLogInfo = cpu_to_le32(reply->IOCLogInfo);
|
||||
}
|
||||
|
||||
void mptsas_fix_port_facts_endianness(MPIMsgPortFacts *req)
|
||||
{
|
||||
le32_to_cpus(&req->MsgContext);
|
||||
req->MsgContext = le32_to_cpu(req->MsgContext);
|
||||
}
|
||||
|
||||
void mptsas_fix_port_facts_reply_endianness(MPIMsgPortFactsReply *reply)
|
||||
{
|
||||
cpu_to_le32s(&reply->MsgContext);
|
||||
cpu_to_le16s(&reply->IOCStatus);
|
||||
cpu_to_le32s(&reply->IOCLogInfo);
|
||||
cpu_to_le16s(&reply->MaxDevices);
|
||||
cpu_to_le16s(&reply->PortSCSIID);
|
||||
cpu_to_le16s(&reply->ProtocolFlags);
|
||||
cpu_to_le16s(&reply->MaxPostedCmdBuffers);
|
||||
cpu_to_le16s(&reply->MaxPersistentIDs);
|
||||
cpu_to_le16s(&reply->MaxLanBuckets);
|
||||
reply->MsgContext = cpu_to_le32(reply->MsgContext);
|
||||
reply->IOCStatus = cpu_to_le16(reply->IOCStatus);
|
||||
reply->IOCLogInfo = cpu_to_le32(reply->IOCLogInfo);
|
||||
reply->MaxDevices = cpu_to_le16(reply->MaxDevices);
|
||||
reply->PortSCSIID = cpu_to_le16(reply->PortSCSIID);
|
||||
reply->ProtocolFlags = cpu_to_le16(reply->ProtocolFlags);
|
||||
reply->MaxPostedCmdBuffers = cpu_to_le16(reply->MaxPostedCmdBuffers);
|
||||
reply->MaxPersistentIDs = cpu_to_le16(reply->MaxPersistentIDs);
|
||||
reply->MaxLanBuckets = cpu_to_le16(reply->MaxLanBuckets);
|
||||
}
|
||||
|
||||
void mptsas_fix_port_enable_endianness(MPIMsgPortEnable *req)
|
||||
{
|
||||
le32_to_cpus(&req->MsgContext);
|
||||
req->MsgContext = le32_to_cpu(req->MsgContext);
|
||||
}
|
||||
|
||||
void mptsas_fix_port_enable_reply_endianness(MPIMsgPortEnableReply *reply)
|
||||
{
|
||||
cpu_to_le32s(&reply->MsgContext);
|
||||
cpu_to_le16s(&reply->IOCStatus);
|
||||
cpu_to_le32s(&reply->IOCLogInfo);
|
||||
reply->MsgContext = cpu_to_le32(reply->MsgContext);
|
||||
reply->IOCStatus = cpu_to_le16(reply->IOCStatus);
|
||||
reply->IOCLogInfo = cpu_to_le32(reply->IOCLogInfo);
|
||||
}
|
||||
|
||||
void mptsas_fix_event_notification_endianness(MPIMsgEventNotify *req)
|
||||
{
|
||||
le32_to_cpus(&req->MsgContext);
|
||||
req->MsgContext = le32_to_cpu(req->MsgContext);
|
||||
}
|
||||
|
||||
void mptsas_fix_event_notification_reply_endianness(MPIMsgEventNotifyReply *reply)
|
||||
@ -188,16 +191,16 @@ void mptsas_fix_event_notification_reply_endianness(MPIMsgEventNotifyReply *repl
|
||||
int length = reply->EventDataLength;
|
||||
int i;
|
||||
|
||||
cpu_to_le16s(&reply->EventDataLength);
|
||||
cpu_to_le32s(&reply->MsgContext);
|
||||
cpu_to_le16s(&reply->IOCStatus);
|
||||
cpu_to_le32s(&reply->IOCLogInfo);
|
||||
cpu_to_le32s(&reply->Event);
|
||||
cpu_to_le32s(&reply->EventContext);
|
||||
reply->EventDataLength = cpu_to_le16(reply->EventDataLength);
|
||||
reply->MsgContext = cpu_to_le32(reply->MsgContext);
|
||||
reply->IOCStatus = cpu_to_le16(reply->IOCStatus);
|
||||
reply->IOCLogInfo = cpu_to_le32(reply->IOCLogInfo);
|
||||
reply->Event = cpu_to_le32(reply->Event);
|
||||
reply->EventContext = cpu_to_le32(reply->EventContext);
|
||||
|
||||
/* Really depends on the event kind. This will do for now. */
|
||||
for (i = 0; i < length; i++) {
|
||||
cpu_to_le32s(&reply->Data[i]);
|
||||
reply->Data[i] = cpu_to_le32(reply->Data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2610,6 +2610,12 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->rotation_rate) {
|
||||
error_report_once("rotation_rate is specified for scsi-block but is "
|
||||
"not implemented. This option is deprecated and will "
|
||||
"be removed in a future version");
|
||||
}
|
||||
|
||||
/* check we are using a driver managing SG_IO (version 3 and after) */
|
||||
rc = blk_ioctl(s->qdev.conf.blk, SG_GET_VERSION_NUM, &sg_version);
|
||||
if (rc < 0) {
|
||||
|
@ -229,3 +229,65 @@ spapr_vscsi_process_login(void) "Got login, sending response !"
|
||||
spapr_vscsi_queue_cmd_no_drive(uint64_t lun) "Command for lun 0x%08" PRIx64 " with no drive"
|
||||
spapr_vscsi_queue_cmd(uint32_t qtag, unsigned cdb, const char *cmd, int lun, int ret) "Queued command tag 0x%"PRIx32" CMD 0x%x=%s LUN %d ret: %d"
|
||||
spapr_vscsi_do_crq(unsigned c0, unsigned c1) "crq: %02x %02x ..."
|
||||
|
||||
# hw/scsi/lsi53c895a.c
|
||||
lsi_reset(void) "Reset"
|
||||
lsi_update_irq(int level, uint8_t dstat, uint8_t sist1, uint8_t sist0) "Update IRQ level %d dstat 0x%02x sist 0x%02x0x%02x"
|
||||
lsi_update_irq_disconnected(void) "Handled IRQs & disconnected, looking for pending processes"
|
||||
lsi_script_scsi_interrupt(uint8_t stat1, uint8_t stat0, uint8_t sist1, uint8_t sist0) "SCSI Interrupt 0x%02x0x%02x prev 0x%02x0x%02x"
|
||||
lsi_script_dma_interrupt(uint8_t stat, uint8_t dstat) "DMA Interrupt 0x%x prev 0x%x"
|
||||
lsi_bad_phase_jump(uint32_t dsp) "Data phase mismatch jump to 0x%"PRIX32
|
||||
lsi_bad_phase_interrupt(void) "Phase mismatch interrupt"
|
||||
lsi_bad_selection(uint32_t id) "Selected absent target %"PRIu32
|
||||
lsi_do_dma_unavailable(void) "DMA no data available"
|
||||
lsi_do_dma(uint64_t addr, int len) "DMA addr=0x%"PRIx64" len=%d"
|
||||
lsi_queue_command(uint32_t tag) "Queueing tag=0x%"PRId32
|
||||
lsi_add_msg_byte_error(void) "MSG IN data too long"
|
||||
lsi_add_msg_byte(uint8_t data) "MSG IN 0x%02x"
|
||||
lsi_reselect(int id) "Reselected target %d"
|
||||
lsi_queue_req_error(void *p) "Multiple IO pending for request %p"
|
||||
lsi_queue_req(uint32_t tag) "Queueing IO tag=0x%"PRIx32
|
||||
lsi_command_complete(uint32_t status) "Command complete status=%"PRId32
|
||||
lsi_transfer_data(uint32_t tag, uint32_t len) "Data ready tag=0x%"PRIx32" len=%"PRId32
|
||||
lsi_do_command(uint32_t dbc) "Send command len=%"PRId32
|
||||
lsi_do_status(uint32_t dbc, uint8_t status) "Get status len=%"PRId32" status=%d"
|
||||
lsi_do_status_error(void) "Bad Status move"
|
||||
lsi_do_msgin(uint32_t dbc, int len) "Message in len=%"PRId32" %d"
|
||||
lsi_do_msgout(uint32_t dbc) "MSG out len=%"PRId32
|
||||
lsi_do_msgout_disconnect(void) "MSG: Disconnect"
|
||||
lsi_do_msgout_noop(void) "MSG: No Operation"
|
||||
lsi_do_msgout_extended(uint8_t msg, uint8_t len) "Extended message 0x%x (len %d)"
|
||||
lsi_do_msgout_ignored(const char *msg) "%s (ignored)"
|
||||
lsi_do_msgout_simplequeue(uint8_t select_tag) "SIMPLE queue tag=0x%x"
|
||||
lsi_do_msgout_abort(uint32_t tag) "MSG: ABORT TAG tag=0x%"PRId32
|
||||
lsi_do_msgout_clearqueue(uint32_t tag) "MSG: CLEAR QUEUE tag=0x%"PRIx32
|
||||
lsi_do_msgout_busdevicereset(uint32_t tag) "MSG: BUS DEVICE RESET tag=0x%"PRIx32
|
||||
lsi_do_msgout_select(int id) "Select LUN %d"
|
||||
lsi_memcpy(uint32_t dest, uint32_t src, int count) "memcpy dest 0x%"PRIx32" src 0x%"PRIx32" count %d"
|
||||
lsi_wait_reselect(void) "Wait Reselect"
|
||||
lsi_execute_script(uint32_t dsp, uint32_t insn, uint32_t addr) "SCRIPTS dsp=0x%"PRIx32" opcode 0x%"PRIx32" arg 0x%"PRIx32
|
||||
lsi_execute_script_blockmove_delayed(void) "Delayed select timeout"
|
||||
lsi_execute_script_blockmove_badphase(uint8_t phase, uint8_t expected) "Wrong phase got %d expected %d"
|
||||
lsi_execute_script_io_alreadyreselected(void) "Already reselected, jumping to alternative address"
|
||||
lsi_execute_script_io_selected(uint8_t id, const char *atn) "Selected target %d%s"
|
||||
lsi_execute_script_io_disconnect(void) "Wait Disconnect"
|
||||
lsi_execute_script_io_set(const char *atn, const char *ack, const char *tm, const char *cc) "Set%s%s%s%s"
|
||||
lsi_execute_script_io_clear(const char *atn, const char *ack, const char *tm, const char *cc) "Clear%s%s%s%s"
|
||||
lsi_execute_script_io_opcode(const char *opcode, int reg, const char *opname, uint8_t data8, uint32_t sfbr, const char *ssfbr) "%s reg 0x%x %s data8=0x%02x sfbr=0x%02x%s"
|
||||
lsi_execute_script_tc_nop(void) "NOP"
|
||||
lsi_execute_script_tc_delayedselect_timeout(void) "Delayed select timeout"
|
||||
lsi_execute_script_tc_compc(int result) "Compare carry %d"
|
||||
lsi_execute_script_tc_compp(uint8_t phase, int op, uint8_t insn_phase) "Compare phase %d %c= %d"
|
||||
lsi_execute_script_tc_compd(uint32_t sfbr, uint8_t mask, int op, int result) "Compare data 0x%"PRIx32" & 0x%x %c= 0x%x"
|
||||
lsi_execute_script_tc_jump(uint32_t addr) "Jump to 0x%"PRIx32
|
||||
lsi_execute_script_tc_call(uint32_t addr) "Call 0x%"PRIx32
|
||||
lsi_execute_script_tc_return(uint32_t addr) "Return to 0x%"PRIx32
|
||||
lsi_execute_script_tc_interrupt(uint32_t addr) "Interrupt 0x%"PRIx32
|
||||
lsi_execute_script_tc_illegal(void) "Illegal transfer control"
|
||||
lsi_execute_script_tc_cc_failed(void) "Control condition failed"
|
||||
lsi_execute_script_mm_load(int reg, int n, uint32_t addr, int data) "Load reg 0x%x size %d addr 0x%"PRIx32" = 0x%08x"
|
||||
lsi_execute_script_mm_store(int reg, int n, uint32_t addr) "Store reg 0x%x size %d addr 0x%"PRIx32
|
||||
lsi_execute_script_stop(void) "SCRIPTS execution stopped"
|
||||
lsi_awoken(void) "Woken by SIGP"
|
||||
lsi_reg_read(const char *name, int offset, uint8_t ret) "Read reg %s 0x%x = 0x%02x"
|
||||
lsi_reg_write(const char *name, int offset, uint8_t val) "Write reg %s 0x%x = 0x%02x"
|
||||
|
@ -358,6 +358,10 @@ int virtio_queue_ready(VirtQueue *vq)
|
||||
* Called within rcu_read_lock(). */
|
||||
static int virtio_queue_empty_rcu(VirtQueue *vq)
|
||||
{
|
||||
if (unlikely(vq->vdev->broken)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unlikely(!vq->vring.avail)) {
|
||||
return 1;
|
||||
}
|
||||
@ -373,6 +377,10 @@ int virtio_queue_empty(VirtQueue *vq)
|
||||
{
|
||||
bool empty;
|
||||
|
||||
if (unlikely(vq->vdev->broken)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unlikely(!vq->vring.avail)) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -201,11 +201,6 @@ struct MemoryRegionOps {
|
||||
*/
|
||||
bool unaligned;
|
||||
} impl;
|
||||
|
||||
/* If .read and .write are not present, old_mmio may be used for
|
||||
* backwards compatibility with old mmio registration
|
||||
*/
|
||||
const MemoryRegionMmio old_mmio;
|
||||
};
|
||||
|
||||
enum IOMMUMemoryRegionAttr {
|
||||
@ -633,7 +628,7 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr,
|
||||
uint64_t length,
|
||||
void *host),
|
||||
Error **errp);
|
||||
#ifdef __linux__
|
||||
#ifdef CONFIG_POSIX
|
||||
|
||||
/**
|
||||
* memory_region_init_ram_from_file: Initialize RAM memory region with a
|
||||
|
@ -10,7 +10,7 @@
|
||||
* Returns the size of the image file on success, -1 otherwise.
|
||||
* On error, errno is also set as appropriate.
|
||||
*/
|
||||
int get_image_size(const char *filename);
|
||||
int64_t get_image_size(const char *filename);
|
||||
int load_image(const char *filename, uint8_t *addr); /* deprecated */
|
||||
ssize_t load_image_size(const char *filename, void *addr, size_t size);
|
||||
|
||||
|
@ -98,7 +98,7 @@
|
||||
* We'd prefer not want to pull in everything else TCG related, so handle
|
||||
* those few cases by hand.
|
||||
*
|
||||
* Note that x32 is fully detected with __x64_64__ + _ILP32, and that for
|
||||
* Note that x32 is fully detected with __x86_64__ + _ILP32, and that for
|
||||
* Sparc we always force the use of sparcv9 in configure.
|
||||
*/
|
||||
#if defined(__x86_64__) || defined(__sparc__)
|
||||
@ -450,4 +450,38 @@
|
||||
_oldn; \
|
||||
})
|
||||
|
||||
/* Abstractions to access atomically (i.e. "once") i64/u64 variables */
|
||||
#ifdef CONFIG_ATOMIC64
|
||||
static inline int64_t atomic_read_i64(const int64_t *ptr)
|
||||
{
|
||||
/* use __nocheck because sizeof(void *) might be < sizeof(u64) */
|
||||
return atomic_read__nocheck(ptr);
|
||||
}
|
||||
|
||||
static inline uint64_t atomic_read_u64(const uint64_t *ptr)
|
||||
{
|
||||
return atomic_read__nocheck(ptr);
|
||||
}
|
||||
|
||||
static inline void atomic_set_i64(int64_t *ptr, int64_t val)
|
||||
{
|
||||
atomic_set__nocheck(ptr, val);
|
||||
}
|
||||
|
||||
static inline void atomic_set_u64(uint64_t *ptr, uint64_t val)
|
||||
{
|
||||
atomic_set__nocheck(ptr, val);
|
||||
}
|
||||
|
||||
static inline void atomic64_init(void)
|
||||
{
|
||||
}
|
||||
#else /* !CONFIG_ATOMIC64 */
|
||||
int64_t atomic_read_i64(const int64_t *ptr);
|
||||
uint64_t atomic_read_u64(const uint64_t *ptr);
|
||||
void atomic_set_i64(int64_t *ptr, int64_t val);
|
||||
void atomic_set_u64(uint64_t *ptr, uint64_t val);
|
||||
void atomic64_init(void);
|
||||
#endif /* !CONFIG_ATOMIC64 */
|
||||
|
||||
#endif /* QEMU_ATOMIC_H */
|
||||
|
@ -16,12 +16,28 @@
|
||||
#define F_SEAL_WRITE 0x0008 /* prevent writes */
|
||||
#endif
|
||||
|
||||
#ifndef MFD_CLOEXEC
|
||||
#define MFD_CLOEXEC 0x0001U
|
||||
#endif
|
||||
|
||||
#ifndef MFD_ALLOW_SEALING
|
||||
#define MFD_ALLOW_SEALING 0x0002U
|
||||
#endif
|
||||
|
||||
#ifndef MFD_HUGETLB
|
||||
#define MFD_HUGETLB 0x0004U
|
||||
#endif
|
||||
|
||||
#ifndef MFD_HUGE_SHIFT
|
||||
#define MFD_HUGE_SHIFT 26
|
||||
#endif
|
||||
|
||||
int qemu_memfd_create(const char *name, size_t size, bool hugetlb,
|
||||
uint64_t hugetlbsize, unsigned int seals, Error **errp);
|
||||
bool qemu_memfd_alloc_check(void);
|
||||
void *qemu_memfd_alloc(const char *name, size_t size, unsigned int seals,
|
||||
int *fd, Error **errp);
|
||||
void qemu_memfd_free(void *ptr, size_t size, int fd);
|
||||
bool qemu_memfd_check(void);
|
||||
bool qemu_memfd_check(unsigned int flags);
|
||||
|
||||
#endif /* QEMU_MEMFD_H */
|
||||
|
@ -448,7 +448,8 @@ bool qemu_has_ofd_lock(void);
|
||||
#define FMT_pid "%d"
|
||||
#endif
|
||||
|
||||
int qemu_create_pidfile(const char *filename);
|
||||
bool qemu_write_pidfile(const char *pidfile, Error **errp);
|
||||
|
||||
int qemu_get_thread_id(void);
|
||||
|
||||
#ifndef CONFIG_IOVEC
|
||||
@ -570,6 +571,8 @@ extern uintptr_t qemu_real_host_page_size;
|
||||
extern intptr_t qemu_real_host_page_mask;
|
||||
|
||||
extern int qemu_icache_linesize;
|
||||
extern int qemu_icache_linesize_log;
|
||||
extern int qemu_dcache_linesize;
|
||||
extern int qemu_dcache_linesize_log;
|
||||
|
||||
#endif
|
||||
|
@ -48,6 +48,22 @@ extern QemuCondWaitFunc qemu_cond_wait_func;
|
||||
#define qemu_mutex_trylock__raw(m) \
|
||||
qemu_mutex_trylock_impl(m, __FILE__, __LINE__)
|
||||
|
||||
#ifdef __COVERITY__
|
||||
/*
|
||||
* Coverity is severely confused by the indirect function calls,
|
||||
* hide them.
|
||||
*/
|
||||
#define qemu_mutex_lock(m) \
|
||||
qemu_mutex_lock_impl(m, __FILE__, __LINE__);
|
||||
#define qemu_mutex_trylock(m) \
|
||||
qemu_mutex_trylock_impl(m, __FILE__, __LINE__);
|
||||
#define qemu_rec_mutex_lock(m) \
|
||||
qemu_rec_mutex_lock_impl(m, __FILE__, __LINE__);
|
||||
#define qemu_rec_mutex_trylock(m) \
|
||||
qemu_rec_mutex_trylock_impl(m, __FILE__, __LINE__);
|
||||
#define qemu_cond_wait(c, m) \
|
||||
qemu_cond_wait_impl(c, m, __FILE__, __LINE__);
|
||||
#else
|
||||
#define qemu_mutex_lock(m) ({ \
|
||||
QemuMutexLockFunc _f = atomic_read(&qemu_mutex_lock_func); \
|
||||
_f(m, __FILE__, __LINE__); \
|
||||
@ -73,6 +89,7 @@ extern QemuCondWaitFunc qemu_cond_wait_func;
|
||||
QemuCondWaitFunc _f = atomic_read(&qemu_cond_wait_func); \
|
||||
_f(c, m, __FILE__, __LINE__); \
|
||||
})
|
||||
#endif
|
||||
|
||||
#define qemu_mutex_unlock(mutex) \
|
||||
qemu_mutex_unlock_impl(mutex, __FILE__, __LINE__)
|
||||
|
@ -42,6 +42,14 @@
|
||||
* In icount mode, this clock counts nanoseconds while the virtual
|
||||
* machine is running. It is used to increase @QEMU_CLOCK_VIRTUAL
|
||||
* while the CPUs are sleeping and thus not executing instructions.
|
||||
*
|
||||
* @QEMU_CLOCK_VIRTUAL_EXT: virtual clock for external subsystems
|
||||
*
|
||||
* The virtual clock only runs during the emulation. It stops
|
||||
* when the virtual machine is stopped. The timers for this clock
|
||||
* do not recorded in rr mode, therefore this clock could be used
|
||||
* for the subsystems that operate outside the guest core.
|
||||
*
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
@ -49,6 +57,7 @@ typedef enum {
|
||||
QEMU_CLOCK_VIRTUAL = 1,
|
||||
QEMU_CLOCK_HOST = 2,
|
||||
QEMU_CLOCK_VIRTUAL_RT = 3,
|
||||
QEMU_CLOCK_VIRTUAL_EXT = 4,
|
||||
QEMU_CLOCK_MAX
|
||||
} QEMUClockType;
|
||||
|
||||
|
179
include/qemu/win_dump_defs.h
Normal file
179
include/qemu/win_dump_defs.h
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Windows crashdump definitions
|
||||
*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QEMU_WIN_DUMP_DEFS_H
|
||||
#define QEMU_WIN_DUMP_DEFS_H
|
||||
|
||||
typedef struct WinDumpPhyMemRun64 {
|
||||
uint64_t BasePage;
|
||||
uint64_t PageCount;
|
||||
} QEMU_PACKED WinDumpPhyMemRun64;
|
||||
|
||||
typedef struct WinDumpPhyMemDesc64 {
|
||||
uint32_t NumberOfRuns;
|
||||
uint32_t unused;
|
||||
uint64_t NumberOfPages;
|
||||
WinDumpPhyMemRun64 Run[43];
|
||||
} QEMU_PACKED WinDumpPhyMemDesc64;
|
||||
|
||||
typedef struct WinDumpExceptionRecord {
|
||||
uint32_t ExceptionCode;
|
||||
uint32_t ExceptionFlags;
|
||||
uint64_t ExceptionRecord;
|
||||
uint64_t ExceptionAddress;
|
||||
uint32_t NumberParameters;
|
||||
uint32_t unused;
|
||||
uint64_t ExceptionInformation[15];
|
||||
} QEMU_PACKED WinDumpExceptionRecord;
|
||||
|
||||
typedef struct WinDumpHeader64 {
|
||||
char Signature[4];
|
||||
char ValidDump[4];
|
||||
uint32_t MajorVersion;
|
||||
uint32_t MinorVersion;
|
||||
uint64_t DirectoryTableBase;
|
||||
uint64_t PfnDatabase;
|
||||
uint64_t PsLoadedModuleList;
|
||||
uint64_t PsActiveProcessHead;
|
||||
uint32_t MachineImageType;
|
||||
uint32_t NumberProcessors;
|
||||
union {
|
||||
struct {
|
||||
uint32_t BugcheckCode;
|
||||
uint32_t unused0;
|
||||
uint64_t BugcheckParameter1;
|
||||
uint64_t BugcheckParameter2;
|
||||
uint64_t BugcheckParameter3;
|
||||
uint64_t BugcheckParameter4;
|
||||
};
|
||||
uint8_t BugcheckData[40];
|
||||
};
|
||||
uint8_t VersionUser[32];
|
||||
uint64_t KdDebuggerDataBlock;
|
||||
union {
|
||||
WinDumpPhyMemDesc64 PhysicalMemoryBlock;
|
||||
uint8_t PhysicalMemoryBlockBuffer[704];
|
||||
};
|
||||
union {
|
||||
uint8_t ContextBuffer[3000];
|
||||
};
|
||||
WinDumpExceptionRecord Exception;
|
||||
uint32_t DumpType;
|
||||
uint32_t unused1;
|
||||
uint64_t RequiredDumpSpace;
|
||||
uint64_t SystemTime;
|
||||
char Comment[128];
|
||||
uint64_t SystemUpTime;
|
||||
uint32_t MiniDumpFields;
|
||||
uint32_t SecondaryDataState;
|
||||
uint32_t ProductType;
|
||||
uint32_t SuiteMask;
|
||||
uint32_t WriterStatus;
|
||||
uint8_t unused2;
|
||||
uint8_t KdSecondaryVersion;
|
||||
uint8_t reserved[4018];
|
||||
} QEMU_PACKED WinDumpHeader64;
|
||||
|
||||
#define KDBG_OWNER_TAG_OFFSET64 0x10
|
||||
#define KDBG_MM_PFN_DATABASE_OFFSET64 0xC0
|
||||
#define KDBG_KI_BUGCHECK_DATA_OFFSET64 0x88
|
||||
#define KDBG_KI_PROCESSOR_BLOCK_OFFSET64 0x218
|
||||
#define KDBG_OFFSET_PRCB_CONTEXT_OFFSET64 0x338
|
||||
|
||||
#define VMCOREINFO_ELF_NOTE_HDR_SIZE 24
|
||||
|
||||
#define WIN_CTX_X64 0x00100000L
|
||||
|
||||
#define WIN_CTX_CTL 0x00000001L
|
||||
#define WIN_CTX_INT 0x00000002L
|
||||
#define WIN_CTX_SEG 0x00000004L
|
||||
#define WIN_CTX_FP 0x00000008L
|
||||
#define WIN_CTX_DBG 0x00000010L
|
||||
|
||||
#define WIN_CTX_FULL (WIN_CTX_X64 | WIN_CTX_CTL | WIN_CTX_INT | WIN_CTX_FP)
|
||||
#define WIN_CTX_ALL (WIN_CTX_FULL | WIN_CTX_SEG | WIN_CTX_DBG)
|
||||
|
||||
#define LIVE_SYSTEM_DUMP 0x00000161
|
||||
|
||||
typedef struct WinM128A {
|
||||
uint64_t low;
|
||||
int64_t high;
|
||||
} QEMU_ALIGNED(16) WinM128A;
|
||||
|
||||
typedef struct WinContext {
|
||||
uint64_t PHome[6];
|
||||
|
||||
uint32_t ContextFlags;
|
||||
uint32_t MxCsr;
|
||||
|
||||
uint16_t SegCs;
|
||||
uint16_t SegDs;
|
||||
uint16_t SegEs;
|
||||
uint16_t SegFs;
|
||||
uint16_t SegGs;
|
||||
uint16_t SegSs;
|
||||
uint32_t EFlags;
|
||||
|
||||
uint64_t Dr0;
|
||||
uint64_t Dr1;
|
||||
uint64_t Dr2;
|
||||
uint64_t Dr3;
|
||||
uint64_t Dr6;
|
||||
uint64_t Dr7;
|
||||
|
||||
uint64_t Rax;
|
||||
uint64_t Rcx;
|
||||
uint64_t Rdx;
|
||||
uint64_t Rbx;
|
||||
uint64_t Rsp;
|
||||
uint64_t Rbp;
|
||||
uint64_t Rsi;
|
||||
uint64_t Rdi;
|
||||
uint64_t R8;
|
||||
uint64_t R9;
|
||||
uint64_t R10;
|
||||
uint64_t R11;
|
||||
uint64_t R12;
|
||||
uint64_t R13;
|
||||
uint64_t R14;
|
||||
uint64_t R15;
|
||||
|
||||
uint64_t Rip;
|
||||
|
||||
struct {
|
||||
uint16_t ControlWord;
|
||||
uint16_t StatusWord;
|
||||
uint8_t TagWord;
|
||||
uint8_t Reserved1;
|
||||
uint16_t ErrorOpcode;
|
||||
uint32_t ErrorOffset;
|
||||
uint16_t ErrorSelector;
|
||||
uint16_t Reserved2;
|
||||
uint32_t DataOffset;
|
||||
uint16_t DataSelector;
|
||||
uint16_t Reserved3;
|
||||
uint32_t MxCsr;
|
||||
uint32_t MxCsr_Mask;
|
||||
WinM128A FloatRegisters[8];
|
||||
WinM128A XmmRegisters[16];
|
||||
uint8_t Reserved4[96];
|
||||
} FltSave;
|
||||
|
||||
WinM128A VectorRegister[26];
|
||||
uint64_t VectorControl;
|
||||
|
||||
uint64_t DebugControl;
|
||||
uint64_t LastBranchToRip;
|
||||
uint64_t LastBranchFromRip;
|
||||
uint64_t LastExceptionToRip;
|
||||
uint64_t LastExceptionFromRip;
|
||||
} QEMU_ALIGNED(16) WinContext;
|
||||
|
||||
#endif /* QEMU_WIN_DUMP_DEFS_H */
|
@ -120,6 +120,9 @@ void replay_shutdown_request(ShutdownCause cause);
|
||||
Returns 0 in PLAY mode if checkpoint was not found.
|
||||
Returns 1 in all other cases. */
|
||||
bool replay_checkpoint(ReplayCheckpoint checkpoint);
|
||||
/*! Used to determine that checkpoint is pending.
|
||||
Does not proceed to the next event in the log. */
|
||||
bool replay_has_checkpoint(void);
|
||||
|
||||
/* Asynchronous events queue */
|
||||
|
||||
|
163
memory.c
163
memory.c
@ -374,6 +374,33 @@ static void adjust_endianness(MemoryRegion *mr, uint64_t *data, unsigned size)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void memory_region_shift_read_access(uint64_t *value,
|
||||
signed shift,
|
||||
uint64_t mask,
|
||||
uint64_t tmp)
|
||||
{
|
||||
if (shift >= 0) {
|
||||
*value |= (tmp & mask) << shift;
|
||||
} else {
|
||||
*value |= (tmp & mask) >> -shift;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint64_t memory_region_shift_write_access(uint64_t *value,
|
||||
signed shift,
|
||||
uint64_t mask)
|
||||
{
|
||||
uint64_t tmp;
|
||||
|
||||
if (shift >= 0) {
|
||||
tmp = (*value >> shift) & mask;
|
||||
} else {
|
||||
tmp = (*value << -shift) & mask;
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static hwaddr memory_region_to_absolute_addr(MemoryRegion *mr, hwaddr offset)
|
||||
{
|
||||
MemoryRegion *root;
|
||||
@ -396,37 +423,11 @@ static int get_cpu_index(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static MemTxResult memory_region_oldmmio_read_accessor(MemoryRegion *mr,
|
||||
hwaddr addr,
|
||||
uint64_t *value,
|
||||
unsigned size,
|
||||
unsigned shift,
|
||||
uint64_t mask,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
uint64_t tmp;
|
||||
|
||||
tmp = mr->ops->old_mmio.read[ctz32(size)](mr->opaque, addr);
|
||||
if (mr->subpage) {
|
||||
trace_memory_region_subpage_read(get_cpu_index(), mr, addr, tmp, size);
|
||||
} else if (mr == &io_mem_notdirty) {
|
||||
/* Accesses to code which has previously been translated into a TB show
|
||||
* up in the MMIO path, as accesses to the io_mem_notdirty
|
||||
* MemoryRegion. */
|
||||
trace_memory_region_tb_read(get_cpu_index(), addr, tmp, size);
|
||||
} else if (TRACE_MEMORY_REGION_OPS_READ_ENABLED) {
|
||||
hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr);
|
||||
trace_memory_region_ops_read(get_cpu_index(), mr, abs_addr, tmp, size);
|
||||
}
|
||||
*value |= (tmp & mask) << shift;
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
static MemTxResult memory_region_read_accessor(MemoryRegion *mr,
|
||||
hwaddr addr,
|
||||
uint64_t *value,
|
||||
unsigned size,
|
||||
unsigned shift,
|
||||
signed shift,
|
||||
uint64_t mask,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
@ -444,7 +445,7 @@ static MemTxResult memory_region_read_accessor(MemoryRegion *mr,
|
||||
hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr);
|
||||
trace_memory_region_ops_read(get_cpu_index(), mr, abs_addr, tmp, size);
|
||||
}
|
||||
*value |= (tmp & mask) << shift;
|
||||
memory_region_shift_read_access(value, shift, mask, tmp);
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
@ -452,7 +453,7 @@ static MemTxResult memory_region_read_with_attrs_accessor(MemoryRegion *mr,
|
||||
hwaddr addr,
|
||||
uint64_t *value,
|
||||
unsigned size,
|
||||
unsigned shift,
|
||||
signed shift,
|
||||
uint64_t mask,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
@ -471,47 +472,20 @@ static MemTxResult memory_region_read_with_attrs_accessor(MemoryRegion *mr,
|
||||
hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr);
|
||||
trace_memory_region_ops_read(get_cpu_index(), mr, abs_addr, tmp, size);
|
||||
}
|
||||
*value |= (tmp & mask) << shift;
|
||||
memory_region_shift_read_access(value, shift, mask, tmp);
|
||||
return r;
|
||||
}
|
||||
|
||||
static MemTxResult memory_region_oldmmio_write_accessor(MemoryRegion *mr,
|
||||
hwaddr addr,
|
||||
uint64_t *value,
|
||||
unsigned size,
|
||||
unsigned shift,
|
||||
uint64_t mask,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
uint64_t tmp;
|
||||
|
||||
tmp = (*value >> shift) & mask;
|
||||
if (mr->subpage) {
|
||||
trace_memory_region_subpage_write(get_cpu_index(), mr, addr, tmp, size);
|
||||
} else if (mr == &io_mem_notdirty) {
|
||||
/* Accesses to code which has previously been translated into a TB show
|
||||
* up in the MMIO path, as accesses to the io_mem_notdirty
|
||||
* MemoryRegion. */
|
||||
trace_memory_region_tb_write(get_cpu_index(), addr, tmp, size);
|
||||
} else if (TRACE_MEMORY_REGION_OPS_WRITE_ENABLED) {
|
||||
hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr);
|
||||
trace_memory_region_ops_write(get_cpu_index(), mr, abs_addr, tmp, size);
|
||||
}
|
||||
mr->ops->old_mmio.write[ctz32(size)](mr->opaque, addr, tmp);
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
static MemTxResult memory_region_write_accessor(MemoryRegion *mr,
|
||||
hwaddr addr,
|
||||
uint64_t *value,
|
||||
unsigned size,
|
||||
unsigned shift,
|
||||
signed shift,
|
||||
uint64_t mask,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
uint64_t tmp;
|
||||
uint64_t tmp = memory_region_shift_write_access(value, shift, mask);
|
||||
|
||||
tmp = (*value >> shift) & mask;
|
||||
if (mr->subpage) {
|
||||
trace_memory_region_subpage_write(get_cpu_index(), mr, addr, tmp, size);
|
||||
} else if (mr == &io_mem_notdirty) {
|
||||
@ -531,13 +505,12 @@ static MemTxResult memory_region_write_with_attrs_accessor(MemoryRegion *mr,
|
||||
hwaddr addr,
|
||||
uint64_t *value,
|
||||
unsigned size,
|
||||
unsigned shift,
|
||||
signed shift,
|
||||
uint64_t mask,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
uint64_t tmp;
|
||||
uint64_t tmp = memory_region_shift_write_access(value, shift, mask);
|
||||
|
||||
tmp = (*value >> shift) & mask;
|
||||
if (mr->subpage) {
|
||||
trace_memory_region_subpage_write(get_cpu_index(), mr, addr, tmp, size);
|
||||
} else if (mr == &io_mem_notdirty) {
|
||||
@ -562,7 +535,7 @@ static MemTxResult access_with_adjusted_size(hwaddr addr,
|
||||
hwaddr addr,
|
||||
uint64_t *value,
|
||||
unsigned size,
|
||||
unsigned shift,
|
||||
signed shift,
|
||||
uint64_t mask,
|
||||
MemTxAttrs attrs),
|
||||
MemoryRegion *mr,
|
||||
@ -582,7 +555,7 @@ static MemTxResult access_with_adjusted_size(hwaddr addr,
|
||||
|
||||
/* FIXME: support unaligned access? */
|
||||
access_size = MAX(MIN(size, access_size_max), access_size_min);
|
||||
access_mask = -1ULL >> (64 - access_size * 8);
|
||||
access_mask = MAKE_64BIT_MASK(0, access_size * 8);
|
||||
if (memory_region_big_endian(mr)) {
|
||||
for (i = 0; i < size; i += access_size) {
|
||||
r |= access_fn(mr, addr + i, value, access_size,
|
||||
@ -1394,16 +1367,12 @@ static MemTxResult memory_region_dispatch_read1(MemoryRegion *mr,
|
||||
mr->ops->impl.max_access_size,
|
||||
memory_region_read_accessor,
|
||||
mr, attrs);
|
||||
} else if (mr->ops->read_with_attrs) {
|
||||
} else {
|
||||
return access_with_adjusted_size(addr, pval, size,
|
||||
mr->ops->impl.min_access_size,
|
||||
mr->ops->impl.max_access_size,
|
||||
memory_region_read_with_attrs_accessor,
|
||||
mr, attrs);
|
||||
} else {
|
||||
return access_with_adjusted_size(addr, pval, size, 1, 4,
|
||||
memory_region_oldmmio_read_accessor,
|
||||
mr, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1475,17 +1444,13 @@ MemTxResult memory_region_dispatch_write(MemoryRegion *mr,
|
||||
mr->ops->impl.max_access_size,
|
||||
memory_region_write_accessor, mr,
|
||||
attrs);
|
||||
} else if (mr->ops->write_with_attrs) {
|
||||
} else {
|
||||
return
|
||||
access_with_adjusted_size(addr, &data, size,
|
||||
mr->ops->impl.min_access_size,
|
||||
mr->ops->impl.max_access_size,
|
||||
memory_region_write_with_attrs_accessor,
|
||||
mr, attrs);
|
||||
} else {
|
||||
return access_with_adjusted_size(addr, &data, size, 1, 4,
|
||||
memory_region_oldmmio_write_accessor,
|
||||
mr, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1518,12 +1483,18 @@ void memory_region_init_ram_shared_nomigrate(MemoryRegion *mr,
|
||||
bool share,
|
||||
Error **errp)
|
||||
{
|
||||
Error *err = NULL;
|
||||
memory_region_init(mr, owner, name, size);
|
||||
mr->ram = true;
|
||||
mr->terminates = true;
|
||||
mr->destructor = memory_region_destructor_ram;
|
||||
mr->ram_block = qemu_ram_alloc(size, share, mr, errp);
|
||||
mr->ram_block = qemu_ram_alloc(size, share, mr, &err);
|
||||
mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
|
||||
if (err) {
|
||||
mr->size = int128_zero();
|
||||
object_unparent(OBJECT(mr));
|
||||
error_propagate(errp, err);
|
||||
}
|
||||
}
|
||||
|
||||
void memory_region_init_resizeable_ram(MemoryRegion *mr,
|
||||
@ -1536,16 +1507,22 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr,
|
||||
void *host),
|
||||
Error **errp)
|
||||
{
|
||||
Error *err = NULL;
|
||||
memory_region_init(mr, owner, name, size);
|
||||
mr->ram = true;
|
||||
mr->terminates = true;
|
||||
mr->destructor = memory_region_destructor_ram;
|
||||
mr->ram_block = qemu_ram_alloc_resizeable(size, max_size, resized,
|
||||
mr, errp);
|
||||
mr, &err);
|
||||
mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
|
||||
if (err) {
|
||||
mr->size = int128_zero();
|
||||
object_unparent(OBJECT(mr));
|
||||
error_propagate(errp, err);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
#ifdef CONFIG_POSIX
|
||||
void memory_region_init_ram_from_file(MemoryRegion *mr,
|
||||
struct Object *owner,
|
||||
const char *name,
|
||||
@ -1555,13 +1532,19 @@ void memory_region_init_ram_from_file(MemoryRegion *mr,
|
||||
const char *path,
|
||||
Error **errp)
|
||||
{
|
||||
Error *err = NULL;
|
||||
memory_region_init(mr, owner, name, size);
|
||||
mr->ram = true;
|
||||
mr->terminates = true;
|
||||
mr->destructor = memory_region_destructor_ram;
|
||||
mr->align = align;
|
||||
mr->ram_block = qemu_ram_alloc_from_file(size, mr, ram_flags, path, errp);
|
||||
mr->ram_block = qemu_ram_alloc_from_file(size, mr, ram_flags, path, &err);
|
||||
mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
|
||||
if (err) {
|
||||
mr->size = int128_zero();
|
||||
object_unparent(OBJECT(mr));
|
||||
error_propagate(errp, err);
|
||||
}
|
||||
}
|
||||
|
||||
void memory_region_init_ram_from_fd(MemoryRegion *mr,
|
||||
@ -1572,14 +1555,20 @@ void memory_region_init_ram_from_fd(MemoryRegion *mr,
|
||||
int fd,
|
||||
Error **errp)
|
||||
{
|
||||
Error *err = NULL;
|
||||
memory_region_init(mr, owner, name, size);
|
||||
mr->ram = true;
|
||||
mr->terminates = true;
|
||||
mr->destructor = memory_region_destructor_ram;
|
||||
mr->ram_block = qemu_ram_alloc_from_fd(size, mr,
|
||||
share ? RAM_SHARED : 0,
|
||||
fd, errp);
|
||||
fd, &err);
|
||||
mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
|
||||
if (err) {
|
||||
mr->size = int128_zero();
|
||||
object_unparent(OBJECT(mr));
|
||||
error_propagate(errp, err);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1630,13 +1619,19 @@ void memory_region_init_rom_nomigrate(MemoryRegion *mr,
|
||||
uint64_t size,
|
||||
Error **errp)
|
||||
{
|
||||
Error *err = NULL;
|
||||
memory_region_init(mr, owner, name, size);
|
||||
mr->ram = true;
|
||||
mr->readonly = true;
|
||||
mr->terminates = true;
|
||||
mr->destructor = memory_region_destructor_ram;
|
||||
mr->ram_block = qemu_ram_alloc(size, false, mr, errp);
|
||||
mr->ram_block = qemu_ram_alloc(size, false, mr, &err);
|
||||
mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
|
||||
if (err) {
|
||||
mr->size = int128_zero();
|
||||
object_unparent(OBJECT(mr));
|
||||
error_propagate(errp, err);
|
||||
}
|
||||
}
|
||||
|
||||
void memory_region_init_rom_device_nomigrate(MemoryRegion *mr,
|
||||
@ -1647,6 +1642,7 @@ void memory_region_init_rom_device_nomigrate(MemoryRegion *mr,
|
||||
uint64_t size,
|
||||
Error **errp)
|
||||
{
|
||||
Error *err = NULL;
|
||||
assert(ops);
|
||||
memory_region_init(mr, owner, name, size);
|
||||
mr->ops = ops;
|
||||
@ -1654,7 +1650,12 @@ void memory_region_init_rom_device_nomigrate(MemoryRegion *mr,
|
||||
mr->terminates = true;
|
||||
mr->rom_device = true;
|
||||
mr->destructor = memory_region_destructor_ram;
|
||||
mr->ram_block = qemu_ram_alloc(size, false, mr, errp);
|
||||
mr->ram_block = qemu_ram_alloc(size, false, mr, &err);
|
||||
if (err) {
|
||||
mr->size = int128_zero();
|
||||
object_unparent(OBJECT(mr));
|
||||
error_propagate(errp, err);
|
||||
}
|
||||
}
|
||||
|
||||
void memory_region_init_iommu(void *_iommu_mr,
|
||||
|
24
os-posix.c
24
os-posix.c
@ -344,30 +344,6 @@ void os_set_line_buffering(void)
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
}
|
||||
|
||||
int qemu_create_pidfile(const char *filename)
|
||||
{
|
||||
char buffer[128];
|
||||
int len;
|
||||
int fd;
|
||||
|
||||
fd = qemu_open(filename, O_RDWR | O_CREAT, 0600);
|
||||
if (fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (lockf(fd, F_TLOCK, 0) == -1) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
len = snprintf(buffer, sizeof(buffer), FMT_pid "\n", getpid());
|
||||
if (write(fd, buffer, len) != len) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* keep pidfile open & locked forever */
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_daemonized(void)
|
||||
{
|
||||
return daemonize;
|
||||
|
25
os-win32.c
25
os-win32.c
@ -97,28 +97,3 @@ int os_parse_cmd_args(int index, const char *optarg)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int qemu_create_pidfile(const char *filename)
|
||||
{
|
||||
char buffer[128];
|
||||
int len;
|
||||
HANDLE file;
|
||||
OVERLAPPED overlap;
|
||||
BOOL ret;
|
||||
memset(&overlap, 0, sizeof(overlap));
|
||||
|
||||
file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL,
|
||||
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
return -1;
|
||||
}
|
||||
len = snprintf(buffer, sizeof(buffer), "%d\n", getpid());
|
||||
ret = WriteFile(file, (LPCVOID)buffer, (DWORD)len,
|
||||
NULL, &overlap);
|
||||
CloseHandle(file);
|
||||
if (ret == 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
54
qga/main.c
54
qga/main.c
@ -340,46 +340,6 @@ static FILE *ga_open_logfile(const char *logfile)
|
||||
return f;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
static bool ga_open_pidfile(const char *pidfile)
|
||||
{
|
||||
int pidfd;
|
||||
char pidstr[32];
|
||||
|
||||
pidfd = qemu_open(pidfile, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
|
||||
if (pidfd == -1 || lockf(pidfd, F_TLOCK, 0)) {
|
||||
g_critical("Cannot lock pid file, %s", strerror(errno));
|
||||
if (pidfd != -1) {
|
||||
close(pidfd);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ftruncate(pidfd, 0)) {
|
||||
g_critical("Failed to truncate pid file");
|
||||
goto fail;
|
||||
}
|
||||
snprintf(pidstr, sizeof(pidstr), "%d\n", getpid());
|
||||
if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
|
||||
g_critical("Failed to write pid file");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* keep pidfile open & locked forever */
|
||||
return true;
|
||||
|
||||
fail:
|
||||
unlink(pidfile);
|
||||
close(pidfd);
|
||||
return false;
|
||||
}
|
||||
#else /* _WIN32 */
|
||||
static bool ga_open_pidfile(const char *pidfile)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static gint ga_strcmp(gconstpointer str1, gconstpointer str2)
|
||||
{
|
||||
return strcmp(str1, str2);
|
||||
@ -479,8 +439,11 @@ void ga_unset_frozen(GAState *s)
|
||||
ga_enable_logging(s);
|
||||
g_warning("logging re-enabled due to filesystem unfreeze");
|
||||
if (s->deferred_options.pid_filepath) {
|
||||
if (!ga_open_pidfile(s->deferred_options.pid_filepath)) {
|
||||
g_warning("failed to create/open pid file");
|
||||
Error *err = NULL;
|
||||
|
||||
if (!qemu_write_pidfile(s->deferred_options.pid_filepath, &err)) {
|
||||
g_warning("%s", error_get_pretty(err));
|
||||
error_free(err);
|
||||
}
|
||||
s->deferred_options.pid_filepath = NULL;
|
||||
}
|
||||
@ -515,8 +478,11 @@ static void become_daemon(const char *pidfile)
|
||||
}
|
||||
|
||||
if (pidfile) {
|
||||
if (!ga_open_pidfile(pidfile)) {
|
||||
g_critical("failed to create pidfile");
|
||||
Error *err = NULL;
|
||||
|
||||
if (!qemu_write_pidfile(pidfile, &err)) {
|
||||
g_critical("%s", error_get_pretty(err));
|
||||
error_free(err);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
@ -286,7 +286,14 @@ static void type_initialize(TypeImpl *ti)
|
||||
if (ti->instance_size == 0) {
|
||||
ti->abstract = true;
|
||||
}
|
||||
|
||||
if (type_is_ancestor(ti, type_interface)) {
|
||||
assert(ti->instance_size == 0);
|
||||
assert(ti->abstract);
|
||||
assert(!ti->instance_init);
|
||||
assert(!ti->instance_post_init);
|
||||
assert(!ti->instance_finalize);
|
||||
assert(!ti->num_interfaces);
|
||||
}
|
||||
ti->class = g_malloc0(ti->class_size);
|
||||
|
||||
parent = type_get_parent(ti);
|
||||
|
@ -94,18 +94,6 @@ void replay_disable_events(void)
|
||||
}
|
||||
}
|
||||
|
||||
void replay_clear_events(void)
|
||||
{
|
||||
g_assert(replay_mutex_locked());
|
||||
|
||||
while (!QTAILQ_EMPTY(&events_list)) {
|
||||
Event *event = QTAILQ_FIRST(&events_list);
|
||||
QTAILQ_REMOVE(&events_list, event, events);
|
||||
|
||||
g_free(event);
|
||||
}
|
||||
}
|
||||
|
||||
/*! Adds specified async event to the queue */
|
||||
void replay_add_event(ReplayAsyncEventKind event_kind,
|
||||
void *opaque,
|
||||
@ -308,7 +296,7 @@ void replay_init_events(void)
|
||||
void replay_finish_events(void)
|
||||
{
|
||||
events_enabled = false;
|
||||
replay_clear_events();
|
||||
replay_flush_events();
|
||||
}
|
||||
|
||||
bool replay_events_enabled(void)
|
||||
|
@ -142,8 +142,6 @@ void replay_init_events(void);
|
||||
void replay_finish_events(void);
|
||||
/*! Flushes events queue */
|
||||
void replay_flush_events(void);
|
||||
/*! Clears events list before loading new VM state */
|
||||
void replay_clear_events(void);
|
||||
/*! Returns true if there are any unsaved events in the queue */
|
||||
bool replay_has_events(void);
|
||||
/*! Saves events from queue into the file */
|
||||
|
@ -33,11 +33,18 @@ static int replay_pre_save(void *opaque)
|
||||
static int replay_post_load(void *opaque, int version_id)
|
||||
{
|
||||
ReplayState *state = opaque;
|
||||
fseek(replay_file, state->file_offset, SEEK_SET);
|
||||
qemu_clock_set_last(QEMU_CLOCK_HOST, state->host_clock_last);
|
||||
/* If this was a vmstate, saved in recording mode,
|
||||
we need to initialize replay data fields. */
|
||||
replay_fetch_data_kind();
|
||||
if (replay_mode == REPLAY_MODE_PLAY) {
|
||||
fseek(replay_file, state->file_offset, SEEK_SET);
|
||||
qemu_clock_set_last(QEMU_CLOCK_HOST, state->host_clock_last);
|
||||
/* If this was a vmstate, saved in recording mode,
|
||||
we need to initialize replay data fields. */
|
||||
replay_fetch_data_kind();
|
||||
} else if (replay_mode == REPLAY_MODE_RECORD) {
|
||||
/* This is only useful for loading the initial state.
|
||||
Therefore reset all the counters. */
|
||||
state->instructions_count = 0;
|
||||
state->block_request_id = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -224,6 +224,18 @@ out:
|
||||
return res;
|
||||
}
|
||||
|
||||
bool replay_has_checkpoint(void)
|
||||
{
|
||||
bool res = false;
|
||||
if (replay_mode == REPLAY_MODE_PLAY) {
|
||||
g_assert(replay_mutex_locked());
|
||||
replay_account_executed_instructions();
|
||||
res = EVENT_CHECKPOINT <= replay_state.data_kind
|
||||
&& replay_state.data_kind <= EVENT_CHECKPOINT_LAST;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void replay_enable(const char *fname, int mode)
|
||||
{
|
||||
const char *fmode = NULL;
|
||||
|
@ -117,39 +117,6 @@ QEMU_COPYRIGHT "\n"
|
||||
, name);
|
||||
}
|
||||
|
||||
static void write_pidfile(void)
|
||||
{
|
||||
int pidfd;
|
||||
char pidstr[32];
|
||||
|
||||
pidfd = qemu_open(pidfile, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
|
||||
if (pidfd == -1) {
|
||||
error_report("Cannot open pid file, %s", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (lockf(pidfd, F_TLOCK, 0)) {
|
||||
error_report("Cannot lock pid file, %s", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
if (ftruncate(pidfd, 0)) {
|
||||
error_report("Failed to truncate pid file");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
snprintf(pidstr, sizeof(pidstr), "%d\n", getpid());
|
||||
if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
|
||||
error_report("Failed to write pid file");
|
||||
goto fail;
|
||||
}
|
||||
return;
|
||||
|
||||
fail:
|
||||
unlink(pidfile);
|
||||
close(pidfd);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* SG_IO support */
|
||||
|
||||
typedef struct PRHelperSGIOData {
|
||||
@ -1080,8 +1047,11 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (daemonize || pidfile_specified)
|
||||
write_pidfile();
|
||||
if ((daemonize || pidfile_specified) &&
|
||||
!qemu_write_pidfile(pidfile, &local_err)) {
|
||||
error_report_err(local_err);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LIBCAP
|
||||
if (drop_privileges() < 0) {
|
||||
|
@ -17,7 +17,7 @@ static void ra_timer_handler(void *opaque)
|
||||
{
|
||||
Slirp *slirp = opaque;
|
||||
timer_mod(slirp->ra_timer,
|
||||
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
|
||||
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_EXT) + NDP_Interval);
|
||||
ndp_send_ra(slirp);
|
||||
}
|
||||
|
||||
@ -27,9 +27,10 @@ void icmp6_init(Slirp *slirp)
|
||||
return;
|
||||
}
|
||||
|
||||
slirp->ra_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, ra_timer_handler, slirp);
|
||||
slirp->ra_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL_EXT,
|
||||
ra_timer_handler, slirp);
|
||||
timer_mod(slirp->ra_timer,
|
||||
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
|
||||
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_EXT) + NDP_Interval);
|
||||
}
|
||||
|
||||
void icmp6_cleanup(Slirp *slirp)
|
||||
|
@ -5429,20 +5429,51 @@ static void x86_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb)
|
||||
cpu->env.eip = tb->pc - tb->cs_base;
|
||||
}
|
||||
|
||||
static bool x86_cpu_has_work(CPUState *cs)
|
||||
int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request)
|
||||
{
|
||||
X86CPU *cpu = X86_CPU(cs);
|
||||
CPUX86State *env = &cpu->env;
|
||||
|
||||
return ((cs->interrupt_request & (CPU_INTERRUPT_HARD |
|
||||
CPU_INTERRUPT_POLL)) &&
|
||||
(env->eflags & IF_MASK)) ||
|
||||
(cs->interrupt_request & (CPU_INTERRUPT_NMI |
|
||||
CPU_INTERRUPT_INIT |
|
||||
CPU_INTERRUPT_SIPI |
|
||||
CPU_INTERRUPT_MCE)) ||
|
||||
((cs->interrupt_request & CPU_INTERRUPT_SMI) &&
|
||||
!(env->hflags & HF_SMM_MASK));
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
if (interrupt_request & CPU_INTERRUPT_POLL) {
|
||||
return CPU_INTERRUPT_POLL;
|
||||
}
|
||||
#endif
|
||||
if (interrupt_request & CPU_INTERRUPT_SIPI) {
|
||||
return CPU_INTERRUPT_SIPI;
|
||||
}
|
||||
|
||||
if (env->hflags2 & HF2_GIF_MASK) {
|
||||
if ((interrupt_request & CPU_INTERRUPT_SMI) &&
|
||||
!(env->hflags & HF_SMM_MASK)) {
|
||||
return CPU_INTERRUPT_SMI;
|
||||
} else if ((interrupt_request & CPU_INTERRUPT_NMI) &&
|
||||
!(env->hflags2 & HF2_NMI_MASK)) {
|
||||
return CPU_INTERRUPT_NMI;
|
||||
} else if (interrupt_request & CPU_INTERRUPT_MCE) {
|
||||
return CPU_INTERRUPT_MCE;
|
||||
} else if ((interrupt_request & CPU_INTERRUPT_HARD) &&
|
||||
(((env->hflags2 & HF2_VINTR_MASK) &&
|
||||
(env->hflags2 & HF2_HIF_MASK)) ||
|
||||
(!(env->hflags2 & HF2_VINTR_MASK) &&
|
||||
(env->eflags & IF_MASK &&
|
||||
!(env->hflags & HF_INHIBIT_IRQ_MASK))))) {
|
||||
return CPU_INTERRUPT_HARD;
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
} else if ((interrupt_request & CPU_INTERRUPT_VIRQ) &&
|
||||
(env->eflags & IF_MASK) &&
|
||||
!(env->hflags & HF_INHIBIT_IRQ_MASK)) {
|
||||
return CPU_INTERRUPT_VIRQ;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool x86_cpu_has_work(CPUState *cs)
|
||||
{
|
||||
return x86_cpu_pending_interrupt(cs, cs->interrupt_request) != 0;
|
||||
}
|
||||
|
||||
static void x86_disas_set_info(CPUState *cs, disassemble_info *info)
|
||||
|
@ -171,7 +171,7 @@ typedef enum X86Seg {
|
||||
#define HF_AC_SHIFT 18 /* must be same as eflags */
|
||||
#define HF_SMM_SHIFT 19 /* CPU in SMM mode */
|
||||
#define HF_SVME_SHIFT 20 /* SVME enabled (copy of EFER.SVME) */
|
||||
#define HF_SVMI_SHIFT 21 /* SVM intercepts are active */
|
||||
#define HF_GUEST_SHIFT 21 /* SVM intercepts are active */
|
||||
#define HF_OSFXSR_SHIFT 22 /* CR4.OSFXSR */
|
||||
#define HF_SMAP_SHIFT 23 /* CR4.SMAP */
|
||||
#define HF_IOBPT_SHIFT 24 /* an io breakpoint enabled */
|
||||
@ -196,7 +196,7 @@ typedef enum X86Seg {
|
||||
#define HF_AC_MASK (1 << HF_AC_SHIFT)
|
||||
#define HF_SMM_MASK (1 << HF_SMM_SHIFT)
|
||||
#define HF_SVME_MASK (1 << HF_SVME_SHIFT)
|
||||
#define HF_SVMI_MASK (1 << HF_SVMI_SHIFT)
|
||||
#define HF_GUEST_MASK (1 << HF_GUEST_SHIFT)
|
||||
#define HF_OSFXSR_MASK (1 << HF_OSFXSR_SHIFT)
|
||||
#define HF_SMAP_MASK (1 << HF_SMAP_SHIFT)
|
||||
#define HF_IOBPT_MASK (1 << HF_IOBPT_SHIFT)
|
||||
@ -1327,7 +1327,9 @@ typedef struct CPUX86State {
|
||||
bool tsc_valid;
|
||||
int64_t tsc_khz;
|
||||
int64_t user_tsc_khz; /* for sanity check only */
|
||||
void *kvm_xsave_buf;
|
||||
#if defined(CONFIG_KVM) || defined(CONFIG_HVF)
|
||||
void *xsave_buf;
|
||||
#endif
|
||||
#if defined(CONFIG_HVF)
|
||||
HVFX86EmulatorState *hvf_emul;
|
||||
#endif
|
||||
@ -1485,6 +1487,7 @@ extern struct VMStateDescription vmstate_x86_cpu;
|
||||
*/
|
||||
void x86_cpu_do_interrupt(CPUState *cpu);
|
||||
bool x86_cpu_exec_interrupt(CPUState *cpu, int int_req);
|
||||
int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request);
|
||||
|
||||
int x86_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu,
|
||||
int cpuid, void *opaque);
|
||||
|
@ -53,7 +53,7 @@ static int check_exception(CPUX86State *env, int intno, int *error_code,
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
if (env->old_exception == EXCP08_DBLE) {
|
||||
if (env->hflags & HF_SVMI_MASK) {
|
||||
if (env->hflags & HF_GUEST_MASK) {
|
||||
cpu_vmexit(env, SVM_EXIT_SHUTDOWN, 0, retaddr); /* does not return */
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,6 @@
|
||||
|
||||
These sources (and ../hvf-all.c) are adapted from Veertu Inc's vdhh (Veertu Desktop Hosted Hypervisor) (last known location: https://github.com/veertuinc/vdhh) with some minor changes, the most significant of which were:
|
||||
|
||||
1. Adapt to our current QEMU's `CPUState` structure and `address_space_rw` API; many struct members have been moved around (emulated x86 state, kvm_xsave_buf) due to historical differences + QEMU needing to handle more emulation targets.
|
||||
1. Adapt to our current QEMU's `CPUState` structure and `address_space_rw` API; many struct members have been moved around (emulated x86 state, xsave_buf) due to historical differences + QEMU needing to handle more emulation targets.
|
||||
2. Removal of `apic_page` and hyperv-related functionality.
|
||||
3. More relaxed use of `qemu_mutex_lock_iothread`.
|
||||
|
@ -72,7 +72,6 @@
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "target/i386/cpu.h"
|
||||
|
||||
pthread_rwlock_t mem_lock = PTHREAD_RWLOCK_INITIALIZER;
|
||||
HVFState *hvf_state;
|
||||
int hvf_disabled = 1;
|
||||
|
||||
@ -587,7 +586,7 @@ int hvf_init_vcpu(CPUState *cpu)
|
||||
hvf_reset_vcpu(cpu);
|
||||
|
||||
x86cpu = X86_CPU(cpu);
|
||||
x86cpu->env.kvm_xsave_buf = qemu_memalign(4096, 4096);
|
||||
x86cpu->env.xsave_buf = qemu_memalign(4096, 4096);
|
||||
|
||||
hv_vcpu_enable_native_msr(cpu->hvf_fd, MSR_STAR, 1);
|
||||
hv_vcpu_enable_native_msr(cpu->hvf_fd, MSR_LSTAR, 1);
|
||||
|
@ -75,7 +75,7 @@ void hvf_put_xsave(CPUState *cpu_state)
|
||||
|
||||
struct X86XSaveArea *xsave;
|
||||
|
||||
xsave = X86_CPU(cpu_state)->env.kvm_xsave_buf;
|
||||
xsave = X86_CPU(cpu_state)->env.xsave_buf;
|
||||
|
||||
x86_cpu_xsave_all_areas(X86_CPU(cpu_state), xsave);
|
||||
|
||||
@ -163,7 +163,7 @@ void hvf_get_xsave(CPUState *cpu_state)
|
||||
{
|
||||
struct X86XSaveArea *xsave;
|
||||
|
||||
xsave = X86_CPU(cpu_state)->env.kvm_xsave_buf;
|
||||
xsave = X86_CPU(cpu_state)->env.xsave_buf;
|
||||
|
||||
if (hv_vcpu_read_fpstate(cpu_state->hvf_fd, (void*)xsave, 4096)) {
|
||||
abort();
|
||||
|
@ -1189,7 +1189,7 @@ int kvm_arch_init_vcpu(CPUState *cs)
|
||||
}
|
||||
|
||||
if (has_xsave) {
|
||||
env->kvm_xsave_buf = qemu_memalign(4096, sizeof(struct kvm_xsave));
|
||||
env->xsave_buf = qemu_memalign(4096, sizeof(struct kvm_xsave));
|
||||
}
|
||||
cpu->kvm_msr_buf = g_malloc0(MSR_BUF_SIZE);
|
||||
|
||||
@ -1639,7 +1639,7 @@ ASSERT_OFFSET(XSAVE_PKRU, pkru_state);
|
||||
static int kvm_put_xsave(X86CPU *cpu)
|
||||
{
|
||||
CPUX86State *env = &cpu->env;
|
||||
X86XSaveArea *xsave = env->kvm_xsave_buf;
|
||||
X86XSaveArea *xsave = env->xsave_buf;
|
||||
|
||||
if (!has_xsave) {
|
||||
return kvm_put_fpu(cpu);
|
||||
@ -2081,7 +2081,7 @@ static int kvm_get_fpu(X86CPU *cpu)
|
||||
static int kvm_get_xsave(X86CPU *cpu)
|
||||
{
|
||||
CPUX86State *env = &cpu->env;
|
||||
X86XSaveArea *xsave = env->kvm_xsave_buf;
|
||||
X86XSaveArea *xsave = env->xsave_buf;
|
||||
int ret;
|
||||
|
||||
if (!has_xsave) {
|
||||
@ -3669,6 +3669,10 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
|
||||
MSIMessage src, dst;
|
||||
X86IOMMUClass *class = X86_IOMMU_GET_CLASS(iommu);
|
||||
|
||||
if (!class->int_remap) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
src.address = route->u.msi.address_hi;
|
||||
src.address <<= VTD_MSI_ADDR_HI_SHIFT;
|
||||
src.address |= route->u.msi.address_lo;
|
||||
|
@ -1244,7 +1244,7 @@ static void do_interrupt_all(X86CPU *cpu, int intno, int is_int,
|
||||
}
|
||||
if (env->cr[0] & CR0_PE_MASK) {
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
if (env->hflags & HF_SVMI_MASK) {
|
||||
if (env->hflags & HF_GUEST_MASK) {
|
||||
handle_even_inj(env, intno, is_int, error_code, is_hw, 0);
|
||||
}
|
||||
#endif
|
||||
@ -1259,7 +1259,7 @@ static void do_interrupt_all(X86CPU *cpu, int intno, int is_int,
|
||||
}
|
||||
} else {
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
if (env->hflags & HF_SVMI_MASK) {
|
||||
if (env->hflags & HF_GUEST_MASK) {
|
||||
handle_even_inj(env, intno, is_int, error_code, is_hw, 1);
|
||||
}
|
||||
#endif
|
||||
@ -1267,7 +1267,7 @@ static void do_interrupt_all(X86CPU *cpu, int intno, int is_int,
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
if (env->hflags & HF_SVMI_MASK) {
|
||||
if (env->hflags & HF_GUEST_MASK) {
|
||||
CPUState *cs = CPU(cpu);
|
||||
uint32_t event_inj = x86_ldl_phys(cs, env->vm_vmcb +
|
||||
offsetof(struct vmcb,
|
||||
@ -1319,74 +1319,66 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
||||
{
|
||||
X86CPU *cpu = X86_CPU(cs);
|
||||
CPUX86State *env = &cpu->env;
|
||||
bool ret = false;
|
||||
int intno;
|
||||
|
||||
interrupt_request = x86_cpu_pending_interrupt(cs, interrupt_request);
|
||||
if (!interrupt_request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Don't process multiple interrupt requests in a single call.
|
||||
* This is required to make icount-driven execution deterministic.
|
||||
*/
|
||||
switch (interrupt_request) {
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
if (interrupt_request & CPU_INTERRUPT_POLL) {
|
||||
case CPU_INTERRUPT_POLL:
|
||||
cs->interrupt_request &= ~CPU_INTERRUPT_POLL;
|
||||
apic_poll_irq(cpu->apic_state);
|
||||
/* Don't process multiple interrupt requests in a single call.
|
||||
This is required to make icount-driven execution deterministic. */
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
if (interrupt_request & CPU_INTERRUPT_SIPI) {
|
||||
case CPU_INTERRUPT_SIPI:
|
||||
do_cpu_sipi(cpu);
|
||||
ret = true;
|
||||
} else if (env->hflags2 & HF2_GIF_MASK) {
|
||||
if ((interrupt_request & CPU_INTERRUPT_SMI) &&
|
||||
!(env->hflags & HF_SMM_MASK)) {
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_SMI, 0, 0);
|
||||
cs->interrupt_request &= ~CPU_INTERRUPT_SMI;
|
||||
do_smm_enter(cpu);
|
||||
ret = true;
|
||||
} else if ((interrupt_request & CPU_INTERRUPT_NMI) &&
|
||||
!(env->hflags2 & HF2_NMI_MASK)) {
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_NMI, 0, 0);
|
||||
cs->interrupt_request &= ~CPU_INTERRUPT_NMI;
|
||||
env->hflags2 |= HF2_NMI_MASK;
|
||||
do_interrupt_x86_hardirq(env, EXCP02_NMI, 1);
|
||||
ret = true;
|
||||
} else if (interrupt_request & CPU_INTERRUPT_MCE) {
|
||||
cs->interrupt_request &= ~CPU_INTERRUPT_MCE;
|
||||
do_interrupt_x86_hardirq(env, EXCP12_MCHK, 0);
|
||||
ret = true;
|
||||
} else if ((interrupt_request & CPU_INTERRUPT_HARD) &&
|
||||
(((env->hflags2 & HF2_VINTR_MASK) &&
|
||||
(env->hflags2 & HF2_HIF_MASK)) ||
|
||||
(!(env->hflags2 & HF2_VINTR_MASK) &&
|
||||
(env->eflags & IF_MASK &&
|
||||
!(env->hflags & HF_INHIBIT_IRQ_MASK))))) {
|
||||
int intno;
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_INTR, 0, 0);
|
||||
cs->interrupt_request &= ~(CPU_INTERRUPT_HARD |
|
||||
CPU_INTERRUPT_VIRQ);
|
||||
intno = cpu_get_pic_interrupt(env);
|
||||
qemu_log_mask(CPU_LOG_TB_IN_ASM,
|
||||
"Servicing hardware INT=0x%02x\n", intno);
|
||||
do_interrupt_x86_hardirq(env, intno, 1);
|
||||
/* ensure that no TB jump will be modified as
|
||||
the program flow was changed */
|
||||
ret = true;
|
||||
break;
|
||||
case CPU_INTERRUPT_SMI:
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_SMI, 0, 0);
|
||||
cs->interrupt_request &= ~CPU_INTERRUPT_SMI;
|
||||
do_smm_enter(cpu);
|
||||
break;
|
||||
case CPU_INTERRUPT_NMI:
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_NMI, 0, 0);
|
||||
cs->interrupt_request &= ~CPU_INTERRUPT_NMI;
|
||||
env->hflags2 |= HF2_NMI_MASK;
|
||||
do_interrupt_x86_hardirq(env, EXCP02_NMI, 1);
|
||||
break;
|
||||
case CPU_INTERRUPT_MCE:
|
||||
cs->interrupt_request &= ~CPU_INTERRUPT_MCE;
|
||||
do_interrupt_x86_hardirq(env, EXCP12_MCHK, 0);
|
||||
break;
|
||||
case CPU_INTERRUPT_HARD:
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_INTR, 0, 0);
|
||||
cs->interrupt_request &= ~(CPU_INTERRUPT_HARD |
|
||||
CPU_INTERRUPT_VIRQ);
|
||||
intno = cpu_get_pic_interrupt(env);
|
||||
qemu_log_mask(CPU_LOG_TB_IN_ASM,
|
||||
"Servicing hardware INT=0x%02x\n", intno);
|
||||
do_interrupt_x86_hardirq(env, intno, 1);
|
||||
break;
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
} else if ((interrupt_request & CPU_INTERRUPT_VIRQ) &&
|
||||
(env->eflags & IF_MASK) &&
|
||||
!(env->hflags & HF_INHIBIT_IRQ_MASK)) {
|
||||
int intno;
|
||||
/* FIXME: this should respect TPR */
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR, 0, 0);
|
||||
intno = x86_ldl_phys(cs, env->vm_vmcb
|
||||
case CPU_INTERRUPT_VIRQ:
|
||||
/* FIXME: this should respect TPR */
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR, 0, 0);
|
||||
intno = x86_ldl_phys(cs, env->vm_vmcb
|
||||
+ offsetof(struct vmcb, control.int_vector));
|
||||
qemu_log_mask(CPU_LOG_TB_IN_ASM,
|
||||
"Servicing virtual hardware INT=0x%02x\n", intno);
|
||||
do_interrupt_x86_hardirq(env, intno, 1);
|
||||
cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
|
||||
ret = true;
|
||||
qemu_log_mask(CPU_LOG_TB_IN_ASM,
|
||||
"Servicing virtual hardware INT=0x%02x\n", intno);
|
||||
do_interrupt_x86_hardirq(env, intno, 1);
|
||||
cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
/* Ensure that no TB jump will be modified as the program flow was changed. */
|
||||
return true;
|
||||
}
|
||||
|
||||
void helper_lldt(CPUX86State *env, int selector)
|
||||
|
@ -228,7 +228,7 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend)
|
||||
}
|
||||
|
||||
/* enable intercepts */
|
||||
env->hflags |= HF_SVMI_MASK;
|
||||
env->hflags |= HF_GUEST_MASK;
|
||||
|
||||
env->tsc_offset = x86_ldq_phys(cs, env->vm_vmcb +
|
||||
offsetof(struct vmcb, control.tsc_offset));
|
||||
@ -503,7 +503,7 @@ void cpu_svm_check_intercept_param(CPUX86State *env, uint32_t type,
|
||||
{
|
||||
CPUState *cs = CPU(x86_env_get_cpu(env));
|
||||
|
||||
if (likely(!(env->hflags & HF_SVMI_MASK))) {
|
||||
if (likely(!(env->hflags & HF_GUEST_MASK))) {
|
||||
return;
|
||||
}
|
||||
switch (type) {
|
||||
@ -697,7 +697,7 @@ void do_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1)
|
||||
|
||||
/* Reload the host state from vm_hsave */
|
||||
env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK);
|
||||
env->hflags &= ~HF_SVMI_MASK;
|
||||
env->hflags &= ~HF_GUEST_MASK;
|
||||
env->intercept = 0;
|
||||
env->intercept_exceptions = 0;
|
||||
cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -613,7 +613,7 @@ test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \
|
||||
tests/test-rcu-tailq.o \
|
||||
tests/test-qdist.o tests/test-shift128.o \
|
||||
tests/test-qht.o tests/qht-bench.o tests/test-qht-par.o \
|
||||
tests/atomic_add-bench.o
|
||||
tests/atomic_add-bench.o tests/atomic64-bench.o
|
||||
|
||||
$(test-obj-y): QEMU_INCLUDES += -Itests
|
||||
QEMU_CFLAGS += -I$(SRC_PATH)/tests
|
||||
@ -668,6 +668,7 @@ tests/test-qht-par$(EXESUF): tests/test-qht-par.o tests/qht-bench$(EXESUF) $(tes
|
||||
tests/qht-bench$(EXESUF): tests/qht-bench.o $(test-util-obj-y)
|
||||
tests/test-bufferiszero$(EXESUF): tests/test-bufferiszero.o $(test-util-obj-y)
|
||||
tests/atomic_add-bench$(EXESUF): tests/atomic_add-bench.o $(test-util-obj-y)
|
||||
tests/atomic64-bench$(EXESUF): tests/atomic64-bench.o $(test-util-obj-y)
|
||||
|
||||
tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \
|
||||
hw/core/qdev.o hw/core/qdev-properties.o hw/core/hotplug.o\
|
||||
|
171
tests/atomic64-bench.c
Normal file
171
tests/atomic64-bench.c
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
|
||||
*
|
||||
* License: GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "qemu/processor.h"
|
||||
|
||||
struct thread_info {
|
||||
uint64_t r;
|
||||
uint64_t accesses;
|
||||
} QEMU_ALIGNED(64);
|
||||
|
||||
struct count {
|
||||
int64_t i64;
|
||||
} QEMU_ALIGNED(64);
|
||||
|
||||
static QemuThread *threads;
|
||||
static struct thread_info *th_info;
|
||||
static unsigned int n_threads = 1;
|
||||
static unsigned int n_ready_threads;
|
||||
static struct count *counts;
|
||||
static unsigned int duration = 1;
|
||||
static unsigned int range = 1024;
|
||||
static bool test_start;
|
||||
static bool test_stop;
|
||||
|
||||
static const char commands_string[] =
|
||||
" -d = duration in seconds\n"
|
||||
" -n = number of threads\n"
|
||||
" -r = range (will be rounded up to pow2)";
|
||||
|
||||
static void usage_complete(char *argv[])
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [options]\n", argv[0]);
|
||||
fprintf(stderr, "options:\n%s\n", commands_string);
|
||||
}
|
||||
|
||||
/*
|
||||
* From: https://en.wikipedia.org/wiki/Xorshift
|
||||
* This is faster than rand_r(), and gives us a wider range (RAND_MAX is only
|
||||
* guaranteed to be >= INT_MAX).
|
||||
*/
|
||||
static uint64_t xorshift64star(uint64_t x)
|
||||
{
|
||||
x ^= x >> 12; /* a */
|
||||
x ^= x << 25; /* b */
|
||||
x ^= x >> 27; /* c */
|
||||
return x * UINT64_C(2685821657736338717);
|
||||
}
|
||||
|
||||
static void *thread_func(void *arg)
|
||||
{
|
||||
struct thread_info *info = arg;
|
||||
|
||||
atomic_inc(&n_ready_threads);
|
||||
while (!atomic_read(&test_start)) {
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
while (!atomic_read(&test_stop)) {
|
||||
unsigned int index;
|
||||
|
||||
info->r = xorshift64star(info->r);
|
||||
index = info->r & (range - 1);
|
||||
atomic_read_i64(&counts[index].i64);
|
||||
info->accesses++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void run_test(void)
|
||||
{
|
||||
unsigned int remaining;
|
||||
unsigned int i;
|
||||
|
||||
while (atomic_read(&n_ready_threads) != n_threads) {
|
||||
cpu_relax();
|
||||
}
|
||||
atomic_set(&test_start, true);
|
||||
do {
|
||||
remaining = sleep(duration);
|
||||
} while (remaining);
|
||||
atomic_set(&test_stop, true);
|
||||
|
||||
for (i = 0; i < n_threads; i++) {
|
||||
qemu_thread_join(&threads[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void create_threads(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
threads = g_new(QemuThread, n_threads);
|
||||
th_info = g_new(struct thread_info, n_threads);
|
||||
counts = g_malloc0_n(range, sizeof(*counts));
|
||||
|
||||
for (i = 0; i < n_threads; i++) {
|
||||
struct thread_info *info = &th_info[i];
|
||||
|
||||
info->r = (i + 1) ^ time(NULL);
|
||||
info->accesses = 0;
|
||||
qemu_thread_create(&threads[i], NULL, thread_func, info,
|
||||
QEMU_THREAD_JOINABLE);
|
||||
}
|
||||
}
|
||||
|
||||
static void pr_params(void)
|
||||
{
|
||||
printf("Parameters:\n");
|
||||
printf(" # of threads: %u\n", n_threads);
|
||||
printf(" duration: %u\n", duration);
|
||||
printf(" ops' range: %u\n", range);
|
||||
}
|
||||
|
||||
static void pr_stats(void)
|
||||
{
|
||||
unsigned long long val = 0;
|
||||
double tx;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n_threads; i++) {
|
||||
val += th_info[i].accesses;
|
||||
}
|
||||
tx = val / duration / 1e6;
|
||||
|
||||
printf("Results:\n");
|
||||
printf("Duration: %u s\n", duration);
|
||||
printf(" Throughput: %.2f Mops/s\n", tx);
|
||||
printf(" Throughput/thread: %.2f Mops/s/thread\n", tx / n_threads);
|
||||
}
|
||||
|
||||
static void parse_args(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
|
||||
for (;;) {
|
||||
c = getopt(argc, argv, "hd:n:r:");
|
||||
if (c < 0) {
|
||||
break;
|
||||
}
|
||||
switch (c) {
|
||||
case 'h':
|
||||
usage_complete(argv);
|
||||
exit(0);
|
||||
case 'd':
|
||||
duration = atoi(optarg);
|
||||
break;
|
||||
case 'n':
|
||||
n_threads = atoi(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
range = pow2ceil(atoi(optarg));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
parse_args(argc, argv);
|
||||
pr_params();
|
||||
create_threads();
|
||||
run_test();
|
||||
pr_stats();
|
||||
return 0;
|
||||
}
|
@ -307,7 +307,7 @@ static int socket_can_read_hello(void *opaque)
|
||||
return 10;
|
||||
}
|
||||
|
||||
static void char_socket_test_common(Chardev *chr)
|
||||
static void char_socket_test_common(Chardev *chr, bool reconnect)
|
||||
{
|
||||
Chardev *chr_client;
|
||||
QObject *addr;
|
||||
@ -327,7 +327,8 @@ static void char_socket_test_common(Chardev *chr)
|
||||
addr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort);
|
||||
qdict = qobject_to(QDict, addr);
|
||||
port = qdict_get_str(qdict, "port");
|
||||
tmp = g_strdup_printf("tcp:127.0.0.1:%s", port);
|
||||
tmp = g_strdup_printf("tcp:127.0.0.1:%s%s", port,
|
||||
reconnect ? ",reconnect=1" : "");
|
||||
qobject_unref(qdict);
|
||||
|
||||
qemu_chr_fe_init(&be, chr, &error_abort);
|
||||
@ -347,6 +348,12 @@ static void char_socket_test_common(Chardev *chr)
|
||||
g_assert_cmpint(id, >, 0);
|
||||
main_loop();
|
||||
|
||||
d.chr = chr_client;
|
||||
id = g_idle_add(char_socket_test_idle, &d);
|
||||
g_source_set_name_by_id(id, "test-idle");
|
||||
g_assert_cmpint(id, >, 0);
|
||||
main_loop();
|
||||
|
||||
g_assert(object_property_get_bool(OBJECT(chr), "connected", &error_abort));
|
||||
g_assert(object_property_get_bool(OBJECT(chr_client),
|
||||
"connected", &error_abort));
|
||||
@ -356,6 +363,7 @@ static void char_socket_test_common(Chardev *chr)
|
||||
|
||||
object_unparent(OBJECT(chr_client));
|
||||
|
||||
d.chr = chr;
|
||||
d.conn_expected = false;
|
||||
g_idle_add(char_socket_test_idle, &d);
|
||||
main_loop();
|
||||
@ -368,7 +376,15 @@ static void char_socket_basic_test(void)
|
||||
{
|
||||
Chardev *chr = qemu_chr_new("server", "tcp:127.0.0.1:0,server,nowait");
|
||||
|
||||
char_socket_test_common(chr);
|
||||
char_socket_test_common(chr, false);
|
||||
}
|
||||
|
||||
|
||||
static void char_socket_reconnect_test(void)
|
||||
{
|
||||
Chardev *chr = qemu_chr_new("server", "tcp:127.0.0.1:0,server,nowait");
|
||||
|
||||
char_socket_test_common(chr, true);
|
||||
}
|
||||
|
||||
|
||||
@ -400,7 +416,7 @@ static void char_socket_fdpass_test(void)
|
||||
|
||||
qemu_opts_del(opts);
|
||||
|
||||
char_socket_test_common(chr);
|
||||
char_socket_test_common(chr, false);
|
||||
}
|
||||
|
||||
|
||||
@ -819,6 +835,7 @@ int main(int argc, char **argv)
|
||||
g_test_add_func("/char/file-fifo", char_file_fifo_test);
|
||||
#endif
|
||||
g_test_add_func("/char/socket/basic", char_socket_basic_test);
|
||||
g_test_add_func("/char/socket/reconnect", char_socket_reconnect_test);
|
||||
g_test_add_func("/char/socket/fdpass", char_socket_fdpass_test);
|
||||
g_test_add_func("/char/udp", char_udp_test);
|
||||
#ifdef HAVE_CHARDEV_SERIAL
|
||||
|
@ -33,8 +33,8 @@
|
||||
static QemuMutex counts_mutex;
|
||||
static long long n_reads = 0LL;
|
||||
static long long n_updates = 0LL;
|
||||
static long long n_reclaims = 0LL;
|
||||
static long long n_nodes_removed = 0LL;
|
||||
static int64_t n_reclaims;
|
||||
static int64_t n_nodes_removed;
|
||||
static long long n_nodes = 0LL;
|
||||
static int g_test_in_charge = 0;
|
||||
|
||||
@ -104,7 +104,7 @@ static void reclaim_list_el(struct rcu_head *prcu)
|
||||
struct list_element *el = container_of(prcu, struct list_element, rcu);
|
||||
g_free(el);
|
||||
/* Accessed only from call_rcu thread. */
|
||||
n_reclaims++;
|
||||
atomic_set_i64(&n_reclaims, n_reclaims + 1);
|
||||
}
|
||||
|
||||
#if TEST_LIST_TYPE == 1
|
||||
@ -232,7 +232,7 @@ static void *rcu_q_updater(void *arg)
|
||||
qemu_mutex_lock(&counts_mutex);
|
||||
n_nodes += n_nodes_local;
|
||||
n_updates += n_updates_local;
|
||||
n_nodes_removed += n_removed_local;
|
||||
atomic_set_i64(&n_nodes_removed, n_nodes_removed + n_removed_local);
|
||||
qemu_mutex_unlock(&counts_mutex);
|
||||
return NULL;
|
||||
}
|
||||
@ -286,19 +286,21 @@ static void rcu_qtest(const char *test, int duration, int nreaders)
|
||||
n_removed_local++;
|
||||
}
|
||||
qemu_mutex_lock(&counts_mutex);
|
||||
n_nodes_removed += n_removed_local;
|
||||
atomic_set_i64(&n_nodes_removed, n_nodes_removed + n_removed_local);
|
||||
qemu_mutex_unlock(&counts_mutex);
|
||||
synchronize_rcu();
|
||||
while (n_nodes_removed > n_reclaims) {
|
||||
while (atomic_read_i64(&n_nodes_removed) > atomic_read_i64(&n_reclaims)) {
|
||||
g_usleep(100);
|
||||
synchronize_rcu();
|
||||
}
|
||||
if (g_test_in_charge) {
|
||||
g_assert_cmpint(n_nodes_removed, ==, n_reclaims);
|
||||
g_assert_cmpint(atomic_read_i64(&n_nodes_removed), ==,
|
||||
atomic_read_i64(&n_reclaims));
|
||||
} else {
|
||||
printf("%s: %d readers; 1 updater; nodes read: " \
|
||||
"%lld, nodes removed: %lld; nodes reclaimed: %lld\n",
|
||||
test, nthreadsrunning - 1, n_reads, n_nodes_removed, n_reclaims);
|
||||
"%lld, nodes removed: %"PRIi64"; nodes reclaimed: %"PRIi64"\n",
|
||||
test, nthreadsrunning - 1, n_reads,
|
||||
atomic_read_i64(&n_nodes_removed), atomic_read_i64(&n_reclaims));
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ static char *get_qemu_cmd(TestServer *s,
|
||||
int mem, enum test_memfd memfd, const char *mem_path,
|
||||
const char *chr_opts, const char *extra)
|
||||
{
|
||||
if (memfd == TEST_MEMFD_AUTO && qemu_memfd_check()) {
|
||||
if (memfd == TEST_MEMFD_AUTO && qemu_memfd_check(0)) {
|
||||
memfd = TEST_MEMFD_YES;
|
||||
}
|
||||
|
||||
@ -903,7 +903,7 @@ static void test_multiqueue(void)
|
||||
s->queues = 2;
|
||||
test_server_listen(s);
|
||||
|
||||
if (qemu_memfd_check()) {
|
||||
if (qemu_memfd_check(0)) {
|
||||
cmd = g_strdup_printf(
|
||||
QEMU_CMD_MEMFD QEMU_CMD_CHR QEMU_CMD_NETDEV ",queues=%d "
|
||||
"-device virtio-net-pci,netdev=net0,mq=on,vectors=%d",
|
||||
@ -963,7 +963,7 @@ int main(int argc, char **argv)
|
||||
/* run the main loop thread so the chardev may operate */
|
||||
thread = g_thread_new(NULL, thread_function, loop);
|
||||
|
||||
if (qemu_memfd_check()) {
|
||||
if (qemu_memfd_check(0)) {
|
||||
qtest_add_data_func("/vhost-user/read-guest-mem/memfd",
|
||||
GINT_TO_POINTER(TEST_MEMFD_YES),
|
||||
test_read_guest_mem);
|
||||
|
@ -271,7 +271,7 @@ static void qemu_input_queue_process(void *opaque)
|
||||
item = QTAILQ_FIRST(queue);
|
||||
switch (item->type) {
|
||||
case QEMU_INPUT_QUEUE_DELAY:
|
||||
timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)
|
||||
timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_EXT)
|
||||
+ item->delay_ms);
|
||||
return;
|
||||
case QEMU_INPUT_QUEUE_EVENT:
|
||||
@ -301,7 +301,7 @@ static void qemu_input_queue_delay(struct QemuInputEventQueueHead *queue,
|
||||
queue_count++;
|
||||
|
||||
if (start_timer) {
|
||||
timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)
|
||||
timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_EXT)
|
||||
+ item->delay_ms);
|
||||
}
|
||||
}
|
||||
@ -448,8 +448,8 @@ void qemu_input_event_send_key_delay(uint32_t delay_ms)
|
||||
}
|
||||
|
||||
if (!kbd_timer) {
|
||||
kbd_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, qemu_input_queue_process,
|
||||
&kbd_queue);
|
||||
kbd_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL_EXT,
|
||||
qemu_input_queue_process, &kbd_queue);
|
||||
}
|
||||
if (queue_count < queue_limit) {
|
||||
qemu_input_queue_delay(&kbd_queue, kbd_timer,
|
||||
|
@ -3,6 +3,7 @@ util-obj-y += bufferiszero.o
|
||||
util-obj-y += lockcnt.o
|
||||
util-obj-y += aiocb.o async.o aio-wait.o thread-pool.o qemu-timer.o
|
||||
util-obj-y += main-loop.o iohandler.o
|
||||
util-obj-$(call lnot,$(CONFIG_ATOMIC64)) += atomic64.o
|
||||
util-obj-$(CONFIG_POSIX) += aio-posix.o
|
||||
util-obj-$(CONFIG_POSIX) += compatfd.o
|
||||
util-obj-$(CONFIG_POSIX) += event_notifier-posix.o
|
||||
|
83
util/atomic64.c
Normal file
83
util/atomic64.c
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
|
||||
*
|
||||
* License: GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/atomic.h"
|
||||
#include "qemu/thread.h"
|
||||
|
||||
#ifdef CONFIG_ATOMIC64
|
||||
#error This file must only be compiled if !CONFIG_ATOMIC64
|
||||
#endif
|
||||
|
||||
/*
|
||||
* When !CONFIG_ATOMIC64, we serialize both reads and writes with spinlocks.
|
||||
* We use an array of spinlocks, with padding computed at run-time based on
|
||||
* the host's dcache line size.
|
||||
* We point to the array with a void * to simplify the padding's computation.
|
||||
* Each spinlock is located every lock_size bytes.
|
||||
*/
|
||||
static void *lock_array;
|
||||
static size_t lock_size;
|
||||
|
||||
/*
|
||||
* Systems without CONFIG_ATOMIC64 are unlikely to have many cores, so we use a
|
||||
* small array of locks.
|
||||
*/
|
||||
#define NR_LOCKS 16
|
||||
|
||||
static QemuSpin *addr_to_lock(const void *addr)
|
||||
{
|
||||
uintptr_t a = (uintptr_t)addr;
|
||||
uintptr_t idx;
|
||||
|
||||
idx = a >> qemu_dcache_linesize_log;
|
||||
idx ^= (idx >> 8) ^ (idx >> 16);
|
||||
idx &= NR_LOCKS - 1;
|
||||
return lock_array + idx * lock_size;
|
||||
}
|
||||
|
||||
#define GEN_READ(name, type) \
|
||||
type name(const type *ptr) \
|
||||
{ \
|
||||
QemuSpin *lock = addr_to_lock(ptr); \
|
||||
type ret; \
|
||||
\
|
||||
qemu_spin_lock(lock); \
|
||||
ret = *ptr; \
|
||||
qemu_spin_unlock(lock); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
GEN_READ(atomic_read_i64, int64_t)
|
||||
GEN_READ(atomic_read_u64, uint64_t)
|
||||
#undef GEN_READ
|
||||
|
||||
#define GEN_SET(name, type) \
|
||||
void name(type *ptr, type val) \
|
||||
{ \
|
||||
QemuSpin *lock = addr_to_lock(ptr); \
|
||||
\
|
||||
qemu_spin_lock(lock); \
|
||||
*ptr = val; \
|
||||
qemu_spin_unlock(lock); \
|
||||
}
|
||||
|
||||
GEN_SET(atomic_set_i64, int64_t)
|
||||
GEN_SET(atomic_set_u64, uint64_t)
|
||||
#undef GEN_SET
|
||||
|
||||
void atomic64_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
lock_size = ROUND_UP(sizeof(QemuSpin), qemu_dcache_linesize);
|
||||
lock_array = qemu_memalign(qemu_dcache_linesize, lock_size * NR_LOCKS);
|
||||
for (i = 0; i < NR_LOCKS; i++) {
|
||||
QemuSpin *lock = lock_array + i * lock_size;
|
||||
|
||||
qemu_spin_init(lock);
|
||||
}
|
||||
}
|
@ -7,9 +7,13 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "qemu/atomic.h"
|
||||
|
||||
int qemu_icache_linesize = 0;
|
||||
int qemu_icache_linesize_log;
|
||||
int qemu_dcache_linesize = 0;
|
||||
int qemu_dcache_linesize_log;
|
||||
|
||||
/*
|
||||
* Operating system specific detection mechanisms.
|
||||
@ -172,6 +176,13 @@ static void __attribute__((constructor)) init_cache_info(void)
|
||||
arch_cache_info(&isize, &dsize);
|
||||
fallback_cache_info(&isize, &dsize);
|
||||
|
||||
assert((isize & (isize - 1)) == 0);
|
||||
assert((dsize & (dsize - 1)) == 0);
|
||||
|
||||
qemu_icache_linesize = isize;
|
||||
qemu_icache_linesize_log = ctz32(isize);
|
||||
qemu_dcache_linesize = dsize;
|
||||
qemu_dcache_linesize_log = ctz32(dsize);
|
||||
|
||||
atomic64_init();
|
||||
}
|
||||
|
37
util/memfd.c
37
util/memfd.c
@ -45,22 +45,6 @@ static int memfd_create(const char *name, unsigned int flags)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef MFD_CLOEXEC
|
||||
#define MFD_CLOEXEC 0x0001U
|
||||
#endif
|
||||
|
||||
#ifndef MFD_ALLOW_SEALING
|
||||
#define MFD_ALLOW_SEALING 0x0002U
|
||||
#endif
|
||||
|
||||
#ifndef MFD_HUGETLB
|
||||
#define MFD_HUGETLB 0x0004U
|
||||
#endif
|
||||
|
||||
#ifndef MFD_HUGE_SHIFT
|
||||
#define MFD_HUGE_SHIFT 26
|
||||
#endif
|
||||
|
||||
int qemu_memfd_create(const char *name, size_t size, bool hugetlb,
|
||||
uint64_t hugetlbsize, unsigned int seals, Error **errp)
|
||||
{
|
||||
@ -201,23 +185,16 @@ bool qemu_memfd_alloc_check(void)
|
||||
*
|
||||
* Check if host supports memfd.
|
||||
*/
|
||||
bool qemu_memfd_check(void)
|
||||
bool qemu_memfd_check(unsigned int flags)
|
||||
{
|
||||
#ifdef CONFIG_LINUX
|
||||
static int memfd_check = MEMFD_TODO;
|
||||
int mfd = memfd_create("test", flags);
|
||||
|
||||
if (memfd_check == MEMFD_TODO) {
|
||||
int mfd = memfd_create("test", 0);
|
||||
if (mfd >= 0) {
|
||||
memfd_check = MEMFD_OK;
|
||||
close(mfd);
|
||||
} else {
|
||||
memfd_check = MEMFD_KO;
|
||||
}
|
||||
if (mfd >= 0) {
|
||||
close(mfd);
|
||||
return true;
|
||||
}
|
||||
|
||||
return memfd_check == MEMFD_OK;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -88,6 +88,79 @@ int qemu_daemon(int nochdir, int noclose)
|
||||
return daemon(nochdir, noclose);
|
||||
}
|
||||
|
||||
bool qemu_write_pidfile(const char *path, Error **errp)
|
||||
{
|
||||
int fd;
|
||||
char pidstr[32];
|
||||
|
||||
while (1) {
|
||||
struct stat a, b;
|
||||
struct flock lock = {
|
||||
.l_type = F_WRLCK,
|
||||
.l_whence = SEEK_SET,
|
||||
.l_len = 0,
|
||||
};
|
||||
|
||||
fd = qemu_open(path, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
|
||||
if (fd == -1) {
|
||||
error_setg_errno(errp, errno, "Cannot open pid file");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fstat(fd, &b) < 0) {
|
||||
error_setg_errno(errp, errno, "Cannot stat file");
|
||||
goto fail_close;
|
||||
}
|
||||
|
||||
if (fcntl(fd, F_SETLK, &lock)) {
|
||||
error_setg_errno(errp, errno, "Cannot lock pid file");
|
||||
goto fail_close;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now make sure the path we locked is the same one that now
|
||||
* exists on the filesystem.
|
||||
*/
|
||||
if (stat(path, &a) < 0) {
|
||||
/*
|
||||
* PID file disappeared, someone else must be racing with
|
||||
* us, so try again.
|
||||
*/
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (a.st_ino == b.st_ino) {
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* PID file was recreated, someone else must be racing with
|
||||
* us, so try again.
|
||||
*/
|
||||
close(fd);
|
||||
}
|
||||
|
||||
if (ftruncate(fd, 0) < 0) {
|
||||
error_setg_errno(errp, errno, "Failed to truncate pid file");
|
||||
goto fail_unlink;
|
||||
}
|
||||
|
||||
snprintf(pidstr, sizeof(pidstr), FMT_pid "\n", getpid());
|
||||
if (write(fd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
|
||||
error_setg(errp, "Failed to write pid file");
|
||||
goto fail_unlink;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
fail_unlink:
|
||||
unlink(path);
|
||||
fail_close:
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
void *qemu_oom_check(void *ptr)
|
||||
{
|
||||
if (ptr == NULL) {
|
||||
|
@ -776,3 +776,30 @@ ssize_t qemu_recvfrom_wrap(int sockfd, void *buf, size_t len, int flags,
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool qemu_write_pidfile(const char *filename, Error **errp)
|
||||
{
|
||||
char buffer[128];
|
||||
int len;
|
||||
HANDLE file;
|
||||
OVERLAPPED overlap;
|
||||
BOOL ret;
|
||||
memset(&overlap, 0, sizeof(overlap));
|
||||
|
||||
file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL,
|
||||
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
error_setg(errp, "Failed to create PID file");
|
||||
return false;
|
||||
}
|
||||
len = snprintf(buffer, sizeof(buffer), FMT_pid "\n", (pid_t)getpid());
|
||||
ret = WriteFile(file, (LPCVOID)buffer, (DWORD)len,
|
||||
NULL, &overlap);
|
||||
CloseHandle(file);
|
||||
if (ret == 0) {
|
||||
error_setg(errp, "Failed to write PID file");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -496,6 +496,7 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
|
||||
|
||||
switch (timer_list->clock->type) {
|
||||
case QEMU_CLOCK_REALTIME:
|
||||
case QEMU_CLOCK_VIRTUAL_EXT:
|
||||
break;
|
||||
default:
|
||||
case QEMU_CLOCK_VIRTUAL:
|
||||
@ -597,6 +598,7 @@ int64_t qemu_clock_get_ns(QEMUClockType type)
|
||||
return get_clock();
|
||||
default:
|
||||
case QEMU_CLOCK_VIRTUAL:
|
||||
case QEMU_CLOCK_VIRTUAL_EXT:
|
||||
if (use_icount) {
|
||||
return cpu_get_icount();
|
||||
} else {
|
||||
|
49
util/qsp.c
49
util/qsp.c
@ -84,13 +84,6 @@ struct QSPEntry {
|
||||
uint64_t n_acqs;
|
||||
uint64_t ns;
|
||||
unsigned int n_objs; /* count of coalesced objs; only used for reporting */
|
||||
#ifndef CONFIG_ATOMIC64
|
||||
/*
|
||||
* If we cannot update the counts atomically, then use a seqlock.
|
||||
* We don't need an associated lock because the updates are thread-local.
|
||||
*/
|
||||
QemuSeqLock sequence;
|
||||
#endif
|
||||
};
|
||||
typedef struct QSPEntry QSPEntry;
|
||||
|
||||
@ -344,47 +337,16 @@ static QSPEntry *qsp_entry_get(const void *obj, const char *file, int line,
|
||||
return qsp_entry_find(&qsp_ht, &orig, hash);
|
||||
}
|
||||
|
||||
/*
|
||||
* @from is in the global hash table; read it atomically if the host
|
||||
* supports it, otherwise use the seqlock.
|
||||
*/
|
||||
static void qsp_entry_aggregate(QSPEntry *to, const QSPEntry *from)
|
||||
{
|
||||
#ifdef CONFIG_ATOMIC64
|
||||
to->ns += atomic_read__nocheck(&from->ns);
|
||||
to->n_acqs += atomic_read__nocheck(&from->n_acqs);
|
||||
#else
|
||||
unsigned int version;
|
||||
uint64_t ns, n_acqs;
|
||||
|
||||
do {
|
||||
version = seqlock_read_begin(&from->sequence);
|
||||
ns = atomic_read__nocheck(&from->ns);
|
||||
n_acqs = atomic_read__nocheck(&from->n_acqs);
|
||||
} while (seqlock_read_retry(&from->sequence, version));
|
||||
|
||||
to->ns += ns;
|
||||
to->n_acqs += n_acqs;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* @e is in the global hash table; it is only written to by the current thread,
|
||||
* so we write to it atomically (as in "write once") to prevent torn reads.
|
||||
* If the host doesn't support u64 atomics, use the seqlock.
|
||||
*/
|
||||
static inline void do_qsp_entry_record(QSPEntry *e, int64_t delta, bool acq)
|
||||
{
|
||||
#ifndef CONFIG_ATOMIC64
|
||||
seqlock_write_begin(&e->sequence);
|
||||
#endif
|
||||
atomic_set__nocheck(&e->ns, e->ns + delta);
|
||||
atomic_set_u64(&e->ns, e->ns + delta);
|
||||
if (acq) {
|
||||
atomic_set__nocheck(&e->n_acqs, e->n_acqs + 1);
|
||||
atomic_set_u64(&e->n_acqs, e->n_acqs + 1);
|
||||
}
|
||||
#ifndef CONFIG_ATOMIC64
|
||||
seqlock_write_end(&e->sequence);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void qsp_entry_record(QSPEntry *e, int64_t delta)
|
||||
@ -550,7 +512,12 @@ static void qsp_aggregate(void *p, uint32_t h, void *up)
|
||||
|
||||
hash = qsp_entry_no_thread_hash(e);
|
||||
agg = qsp_entry_find(ht, e, hash);
|
||||
qsp_entry_aggregate(agg, e);
|
||||
/*
|
||||
* The entry is in the global hash table; read from it atomically (as in
|
||||
* "read once").
|
||||
*/
|
||||
agg->ns += atomic_read_u64(&e->ns);
|
||||
agg->n_acqs += atomic_read_u64(&e->n_acqs);
|
||||
}
|
||||
|
||||
static void qsp_iter_diff(void *p, uint32_t hash, void *htp)
|
||||
|
25
vl.c
25
vl.c
@ -2560,6 +2560,16 @@ static void qemu_run_exit_notifiers(void)
|
||||
notifier_list_notify(&exit_notifiers, NULL);
|
||||
}
|
||||
|
||||
static const char *pid_file;
|
||||
static Notifier qemu_unlink_pidfile_notifier;
|
||||
|
||||
static void qemu_unlink_pidfile(Notifier *n, void *data)
|
||||
{
|
||||
if (pid_file) {
|
||||
unlink(pid_file);
|
||||
}
|
||||
}
|
||||
|
||||
bool machine_init_done;
|
||||
|
||||
void qemu_add_machine_init_done_notifier(Notifier *notify)
|
||||
@ -2884,7 +2894,6 @@ int main(int argc, char **argv, char **envp)
|
||||
const char *vga_model = NULL;
|
||||
const char *qtest_chrdev = NULL;
|
||||
const char *qtest_log = NULL;
|
||||
const char *pid_file = NULL;
|
||||
const char *incoming = NULL;
|
||||
bool userconfig = true;
|
||||
bool nographic = false;
|
||||
@ -3906,11 +3915,14 @@ int main(int argc, char **argv, char **envp)
|
||||
os_daemonize();
|
||||
rcu_disable_atfork();
|
||||
|
||||
if (pid_file && qemu_create_pidfile(pid_file) != 0) {
|
||||
error_report("could not acquire pid file: %s", strerror(errno));
|
||||
if (pid_file && !qemu_write_pidfile(pid_file, &err)) {
|
||||
error_reportf_err(err, "cannot create PID file: ");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
qemu_unlink_pidfile_notifier.notify = qemu_unlink_pidfile;
|
||||
qemu_add_exit_notifier(&qemu_unlink_pidfile_notifier);
|
||||
|
||||
if (qemu_init_main_loop(&main_loop_err)) {
|
||||
error_report_err(main_loop_err);
|
||||
exit(1);
|
||||
@ -4523,9 +4535,7 @@ int main(int argc, char **argv, char **envp)
|
||||
replay_checkpoint(CHECKPOINT_RESET);
|
||||
qemu_system_reset(SHUTDOWN_CAUSE_NONE);
|
||||
register_global_state();
|
||||
if (replay_mode != REPLAY_MODE_NONE) {
|
||||
replay_vmstate_init();
|
||||
} else if (loadvm) {
|
||||
if (loadvm) {
|
||||
Error *local_err = NULL;
|
||||
if (load_snapshot(loadvm, &local_err) < 0) {
|
||||
error_report_err(local_err);
|
||||
@ -4533,6 +4543,9 @@ int main(int argc, char **argv, char **envp)
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (replay_mode != REPLAY_MODE_NONE) {
|
||||
replay_vmstate_init();
|
||||
}
|
||||
|
||||
qdev_prop_check_globals();
|
||||
if (vmstate_dump_file) {
|
||||
|
42
win_dump.c
42
win_dump.c
@ -30,28 +30,32 @@ static size_t write_run(WinDumpPhyMemRun64 *run, int fd, Error **errp)
|
||||
void *buf;
|
||||
uint64_t addr = run->BasePage << TARGET_PAGE_BITS;
|
||||
uint64_t size = run->PageCount << TARGET_PAGE_BITS;
|
||||
uint64_t len = size;
|
||||
uint64_t len, l;
|
||||
size_t total = 0;
|
||||
|
||||
buf = cpu_physical_memory_map(addr, &len, false);
|
||||
if (!buf) {
|
||||
error_setg(errp, "win-dump: failed to map run");
|
||||
return 0;
|
||||
}
|
||||
if (len != size) {
|
||||
error_setg(errp, "win-dump: failed to map entire run");
|
||||
len = 0;
|
||||
goto out_unmap;
|
||||
while (size) {
|
||||
len = size;
|
||||
|
||||
buf = cpu_physical_memory_map(addr, &len, false);
|
||||
if (!buf) {
|
||||
error_setg(errp, "win-dump: failed to map physical range"
|
||||
" 0x%016" PRIx64 "-0x%016" PRIx64, addr, addr + size - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
l = qemu_write_full(fd, buf, len);
|
||||
cpu_physical_memory_unmap(buf, addr, false, len);
|
||||
if (l != len) {
|
||||
error_setg(errp, QERR_IO_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
addr += l;
|
||||
size -= l;
|
||||
total += l;
|
||||
}
|
||||
|
||||
len = qemu_write_full(fd, buf, len);
|
||||
if (len != size) {
|
||||
error_setg(errp, QERR_IO_ERROR);
|
||||
}
|
||||
|
||||
out_unmap:
|
||||
cpu_physical_memory_unmap(buf, addr, false, len);
|
||||
|
||||
return len;
|
||||
return total;
|
||||
}
|
||||
|
||||
static void write_runs(DumpState *s, WinDumpHeader64 *h, Error **errp)
|
||||
|
166
win_dump.h
166
win_dump.h
@ -8,169 +8,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
typedef struct WinDumpPhyMemRun64 {
|
||||
uint64_t BasePage;
|
||||
uint64_t PageCount;
|
||||
} QEMU_PACKED WinDumpPhyMemRun64;
|
||||
#ifndef WIN_DUMP_H
|
||||
#define WIN_DUMP_H
|
||||
|
||||
typedef struct WinDumpPhyMemDesc64 {
|
||||
uint32_t NumberOfRuns;
|
||||
uint32_t unused;
|
||||
uint64_t NumberOfPages;
|
||||
WinDumpPhyMemRun64 Run[43];
|
||||
} QEMU_PACKED WinDumpPhyMemDesc64;
|
||||
|
||||
typedef struct WinDumpExceptionRecord {
|
||||
uint32_t ExceptionCode;
|
||||
uint32_t ExceptionFlags;
|
||||
uint64_t ExceptionRecord;
|
||||
uint64_t ExceptionAddress;
|
||||
uint32_t NumberParameters;
|
||||
uint32_t unused;
|
||||
uint64_t ExceptionInformation[15];
|
||||
} QEMU_PACKED WinDumpExceptionRecord;
|
||||
|
||||
typedef struct WinDumpHeader64 {
|
||||
char Signature[4];
|
||||
char ValidDump[4];
|
||||
uint32_t MajorVersion;
|
||||
uint32_t MinorVersion;
|
||||
uint64_t DirectoryTableBase;
|
||||
uint64_t PfnDatabase;
|
||||
uint64_t PsLoadedModuleList;
|
||||
uint64_t PsActiveProcessHead;
|
||||
uint32_t MachineImageType;
|
||||
uint32_t NumberProcessors;
|
||||
union {
|
||||
struct {
|
||||
uint32_t BugcheckCode;
|
||||
uint32_t unused0;
|
||||
uint64_t BugcheckParameter1;
|
||||
uint64_t BugcheckParameter2;
|
||||
uint64_t BugcheckParameter3;
|
||||
uint64_t BugcheckParameter4;
|
||||
};
|
||||
uint8_t BugcheckData[40];
|
||||
};
|
||||
uint8_t VersionUser[32];
|
||||
uint64_t KdDebuggerDataBlock;
|
||||
union {
|
||||
WinDumpPhyMemDesc64 PhysicalMemoryBlock;
|
||||
uint8_t PhysicalMemoryBlockBuffer[704];
|
||||
};
|
||||
union {
|
||||
uint8_t ContextBuffer[3000];
|
||||
};
|
||||
WinDumpExceptionRecord Exception;
|
||||
uint32_t DumpType;
|
||||
uint32_t unused1;
|
||||
uint64_t RequiredDumpSpace;
|
||||
uint64_t SystemTime;
|
||||
char Comment[128];
|
||||
uint64_t SystemUpTime;
|
||||
uint32_t MiniDumpFields;
|
||||
uint32_t SecondaryDataState;
|
||||
uint32_t ProductType;
|
||||
uint32_t SuiteMask;
|
||||
uint32_t WriterStatus;
|
||||
uint8_t unused2;
|
||||
uint8_t KdSecondaryVersion;
|
||||
uint8_t reserved[4018];
|
||||
} QEMU_PACKED WinDumpHeader64;
|
||||
#include "qemu/win_dump_defs.h"
|
||||
|
||||
void create_win_dump(DumpState *s, Error **errp);
|
||||
|
||||
#define KDBG_OWNER_TAG_OFFSET64 0x10
|
||||
#define KDBG_MM_PFN_DATABASE_OFFSET64 0xC0
|
||||
#define KDBG_KI_BUGCHECK_DATA_OFFSET64 0x88
|
||||
#define KDBG_KI_PROCESSOR_BLOCK_OFFSET64 0x218
|
||||
#define KDBG_OFFSET_PRCB_CONTEXT_OFFSET64 0x338
|
||||
|
||||
#define VMCOREINFO_ELF_NOTE_HDR_SIZE 24
|
||||
|
||||
#define WIN_CTX_X64 0x00100000L
|
||||
|
||||
#define WIN_CTX_CTL 0x00000001L
|
||||
#define WIN_CTX_INT 0x00000002L
|
||||
#define WIN_CTX_SEG 0x00000004L
|
||||
#define WIN_CTX_FP 0x00000008L
|
||||
#define WIN_CTX_DBG 0x00000010L
|
||||
|
||||
#define WIN_CTX_FULL (WIN_CTX_X64 | WIN_CTX_CTL | WIN_CTX_INT | WIN_CTX_FP)
|
||||
#define WIN_CTX_ALL (WIN_CTX_FULL | WIN_CTX_SEG | WIN_CTX_DBG)
|
||||
|
||||
#define LIVE_SYSTEM_DUMP 0x00000161
|
||||
|
||||
typedef struct WinM128A {
|
||||
uint64_t low;
|
||||
int64_t high;
|
||||
} QEMU_ALIGNED(16) WinM128A;
|
||||
|
||||
typedef struct WinContext {
|
||||
uint64_t PHome[6];
|
||||
|
||||
uint32_t ContextFlags;
|
||||
uint32_t MxCsr;
|
||||
|
||||
uint16_t SegCs;
|
||||
uint16_t SegDs;
|
||||
uint16_t SegEs;
|
||||
uint16_t SegFs;
|
||||
uint16_t SegGs;
|
||||
uint16_t SegSs;
|
||||
uint32_t EFlags;
|
||||
|
||||
uint64_t Dr0;
|
||||
uint64_t Dr1;
|
||||
uint64_t Dr2;
|
||||
uint64_t Dr3;
|
||||
uint64_t Dr6;
|
||||
uint64_t Dr7;
|
||||
|
||||
uint64_t Rax;
|
||||
uint64_t Rcx;
|
||||
uint64_t Rdx;
|
||||
uint64_t Rbx;
|
||||
uint64_t Rsp;
|
||||
uint64_t Rbp;
|
||||
uint64_t Rsi;
|
||||
uint64_t Rdi;
|
||||
uint64_t R8;
|
||||
uint64_t R9;
|
||||
uint64_t R10;
|
||||
uint64_t R11;
|
||||
uint64_t R12;
|
||||
uint64_t R13;
|
||||
uint64_t R14;
|
||||
uint64_t R15;
|
||||
|
||||
uint64_t Rip;
|
||||
|
||||
struct {
|
||||
uint16_t ControlWord;
|
||||
uint16_t StatusWord;
|
||||
uint8_t TagWord;
|
||||
uint8_t Reserved1;
|
||||
uint16_t ErrorOpcode;
|
||||
uint32_t ErrorOffset;
|
||||
uint16_t ErrorSelector;
|
||||
uint16_t Reserved2;
|
||||
uint32_t DataOffset;
|
||||
uint16_t DataSelector;
|
||||
uint16_t Reserved3;
|
||||
uint32_t MxCsr;
|
||||
uint32_t MxCsr_Mask;
|
||||
WinM128A FloatRegisters[8];
|
||||
WinM128A XmmRegisters[16];
|
||||
uint8_t Reserved4[96];
|
||||
} FltSave;
|
||||
|
||||
WinM128A VectorRegister[26];
|
||||
uint64_t VectorControl;
|
||||
|
||||
uint64_t DebugControl;
|
||||
uint64_t LastBranchToRip;
|
||||
uint64_t LastBranchFromRip;
|
||||
uint64_t LastExceptionToRip;
|
||||
uint64_t LastExceptionFromRip;
|
||||
} QEMU_ALIGNED(16) WinContext;
|
||||
#endif /* WIN_DUMP_H */
|
||||
|
Loading…
Reference in New Issue
Block a user