From 31ffa2aa3c130e987aa659a8a48a0355734299da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 24 Nov 2022 16:34:11 +0100 Subject: [PATCH 001/185] cpu: Remove capstone meson dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only disas.c requires capstone CFLAGS, not cpu.c. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20221130135241.85060-2-philmd@linaro.org> --- meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 6cb2b1a42f..77d2ae87e4 100644 --- a/meson.build +++ b/meson.build @@ -3140,11 +3140,12 @@ if have_block endif common_ss.add(files('cpus-common.c')) +specific_ss.add(files('cpu.c')) subdir('softmmu') common_ss.add(capstone) -specific_ss.add(files('cpu.c', 'disas.c'), capstone) +specific_ss.add(files('disas.c'), capstone) # Work around a gcc bug/misfeature wherein constant propagation looks # through an alias: From 8a8dc2679065fb82d0e03453caee9aba38e92af9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 24 Nov 2022 16:36:49 +0100 Subject: [PATCH 002/185] cpu: Move breakpoint helpers to common code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This code is not target-specific. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20221130135241.85060-4-philmd@linaro.org> --- cpu.c | 71 -------------------------------------------------- cpus-common.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 71 deletions(-) diff --git a/cpu.c b/cpu.c index 21cf809614..44df16231f 100644 --- a/cpu.c +++ b/cpu.c @@ -319,77 +319,6 @@ void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs) } #endif -/* Add a breakpoint. */ -int cpu_breakpoint_insert(CPUState *cpu, vaddr pc, int flags, - CPUBreakpoint **breakpoint) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - CPUBreakpoint *bp; - - if (cc->gdb_adjust_breakpoint) { - pc = cc->gdb_adjust_breakpoint(cpu, pc); - } - - bp = g_malloc(sizeof(*bp)); - - bp->pc = pc; - bp->flags = flags; - - /* keep all GDB-injected breakpoints in front */ - if (flags & BP_GDB) { - QTAILQ_INSERT_HEAD(&cpu->breakpoints, bp, entry); - } else { - QTAILQ_INSERT_TAIL(&cpu->breakpoints, bp, entry); - } - - if (breakpoint) { - *breakpoint = bp; - } - - trace_breakpoint_insert(cpu->cpu_index, pc, flags); - return 0; -} - -/* Remove a specific breakpoint. */ -int cpu_breakpoint_remove(CPUState *cpu, vaddr pc, int flags) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - CPUBreakpoint *bp; - - if (cc->gdb_adjust_breakpoint) { - pc = cc->gdb_adjust_breakpoint(cpu, pc); - } - - QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { - if (bp->pc == pc && bp->flags == flags) { - cpu_breakpoint_remove_by_ref(cpu, bp); - return 0; - } - } - return -ENOENT; -} - -/* Remove a specific breakpoint by reference. */ -void cpu_breakpoint_remove_by_ref(CPUState *cpu, CPUBreakpoint *bp) -{ - QTAILQ_REMOVE(&cpu->breakpoints, bp, entry); - - trace_breakpoint_remove(cpu->cpu_index, bp->pc, bp->flags); - g_free(bp); -} - -/* Remove all matching breakpoints. */ -void cpu_breakpoint_remove_all(CPUState *cpu, int mask) -{ - CPUBreakpoint *bp, *next; - - QTAILQ_FOREACH_SAFE(bp, &cpu->breakpoints, entry, next) { - if (bp->flags & mask) { - cpu_breakpoint_remove_by_ref(cpu, bp); - } - } -} - /* enable or disable single step mode. EXCP_DEBUG is returned by the CPU loop after each instruction */ void cpu_single_step(CPUState *cpu, int enabled) diff --git a/cpus-common.c b/cpus-common.c index 39f355de98..b0047e456f 100644 --- a/cpus-common.c +++ b/cpus-common.c @@ -23,6 +23,7 @@ #include "hw/core/cpu.h" #include "sysemu/cpus.h" #include "qemu/lockable.h" +#include "trace/trace-root.h" static QemuMutex qemu_cpu_list_lock; static QemuCond exclusive_cond; @@ -368,3 +369,74 @@ void process_queued_cpu_work(CPUState *cpu) qemu_mutex_unlock(&cpu->work_mutex); qemu_cond_broadcast(&qemu_work_cond); } + +/* Add a breakpoint. */ +int cpu_breakpoint_insert(CPUState *cpu, vaddr pc, int flags, + CPUBreakpoint **breakpoint) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + CPUBreakpoint *bp; + + if (cc->gdb_adjust_breakpoint) { + pc = cc->gdb_adjust_breakpoint(cpu, pc); + } + + bp = g_malloc(sizeof(*bp)); + + bp->pc = pc; + bp->flags = flags; + + /* keep all GDB-injected breakpoints in front */ + if (flags & BP_GDB) { + QTAILQ_INSERT_HEAD(&cpu->breakpoints, bp, entry); + } else { + QTAILQ_INSERT_TAIL(&cpu->breakpoints, bp, entry); + } + + if (breakpoint) { + *breakpoint = bp; + } + + trace_breakpoint_insert(cpu->cpu_index, pc, flags); + return 0; +} + +/* Remove a specific breakpoint. */ +int cpu_breakpoint_remove(CPUState *cpu, vaddr pc, int flags) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + CPUBreakpoint *bp; + + if (cc->gdb_adjust_breakpoint) { + pc = cc->gdb_adjust_breakpoint(cpu, pc); + } + + QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { + if (bp->pc == pc && bp->flags == flags) { + cpu_breakpoint_remove_by_ref(cpu, bp); + return 0; + } + } + return -ENOENT; +} + +/* Remove a specific breakpoint by reference. */ +void cpu_breakpoint_remove_by_ref(CPUState *cpu, CPUBreakpoint *bp) +{ + QTAILQ_REMOVE(&cpu->breakpoints, bp, entry); + + trace_breakpoint_remove(cpu->cpu_index, bp->pc, bp->flags); + g_free(bp); +} + +/* Remove all matching breakpoints. */ +void cpu_breakpoint_remove_all(CPUState *cpu, int mask) +{ + CPUBreakpoint *bp, *next; + + QTAILQ_FOREACH_SAFE(bp, &cpu->breakpoints, entry, next) { + if (bp->flags & mask) { + cpu_breakpoint_remove_by_ref(cpu, bp); + } + } +} From 55b5b8e9284147529fa92804127f6d99ce4f89d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 6 Dec 2022 16:20:27 +0100 Subject: [PATCH 003/185] gdbstub: Use vaddr type for generic insert/remove_breakpoint() API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both insert/remove_breakpoint() handlers are used in system and user emulation. We can not use the 'hwaddr' type on user emulation, we have to use 'vaddr' which is defined as "wide enough to contain any #target_ulong virtual address". gdbstub.c doesn't require to include "exec/hwaddr.h" anymore. Reviewed-by: Richard Henderson Reviewed-by: Fabiano Rosas Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20221216215519.5522-4-philmd@linaro.org> --- accel/kvm/kvm-all.c | 4 ++-- accel/kvm/kvm-cpus.h | 4 ++-- accel/tcg/tcg-accel-ops.c | 4 ++-- gdbstub/gdbstub.c | 1 - gdbstub/internals.h | 6 ++++-- gdbstub/softmmu.c | 5 ++--- gdbstub/user.c | 5 ++--- include/sysemu/accel-ops.h | 6 +++--- 8 files changed, 17 insertions(+), 18 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 9b26582655..79b3d58a9c 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -3305,7 +3305,7 @@ bool kvm_supports_guest_debug(void) return kvm_has_guest_debug; } -int kvm_insert_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len) +int kvm_insert_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len) { struct kvm_sw_breakpoint *bp; int err; @@ -3343,7 +3343,7 @@ int kvm_insert_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len) return 0; } -int kvm_remove_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len) +int kvm_remove_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len) { struct kvm_sw_breakpoint *bp; int err; diff --git a/accel/kvm/kvm-cpus.h b/accel/kvm/kvm-cpus.h index fd63fe6a59..ca40add32c 100644 --- a/accel/kvm/kvm-cpus.h +++ b/accel/kvm/kvm-cpus.h @@ -19,8 +19,8 @@ void kvm_cpu_synchronize_post_reset(CPUState *cpu); void kvm_cpu_synchronize_post_init(CPUState *cpu); void kvm_cpu_synchronize_pre_loadvm(CPUState *cpu); bool kvm_supports_guest_debug(void); -int kvm_insert_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len); -int kvm_remove_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len); +int kvm_insert_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len); +int kvm_remove_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len); void kvm_remove_all_breakpoints(CPUState *cpu); #endif /* KVM_CPUS_H */ diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 19cbf1db3a..d9228fd403 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -116,7 +116,7 @@ static inline int xlat_gdb_type(CPUState *cpu, int gdbtype) return cputype; } -static int tcg_insert_breakpoint(CPUState *cs, int type, hwaddr addr, hwaddr len) +static int tcg_insert_breakpoint(CPUState *cs, int type, vaddr addr, vaddr len) { CPUState *cpu; int err = 0; @@ -147,7 +147,7 @@ static int tcg_insert_breakpoint(CPUState *cs, int type, hwaddr addr, hwaddr len } } -static int tcg_remove_breakpoint(CPUState *cs, int type, hwaddr addr, hwaddr len) +static int tcg_remove_breakpoint(CPUState *cs, int type, vaddr addr, vaddr len) { CPUState *cpu; int err = 0; diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index be88ca0d71..c3fbc31123 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -48,7 +48,6 @@ #include "sysemu/runstate.h" #include "semihosting/semihost.h" #include "exec/exec-all.h" -#include "exec/hwaddr.h" #include "sysemu/replay.h" #include "internals.h" diff --git a/gdbstub/internals.h b/gdbstub/internals.h index eabb0341d1..b23999f951 100644 --- a/gdbstub/internals.h +++ b/gdbstub/internals.h @@ -9,9 +9,11 @@ #ifndef _INTERNALS_H_ #define _INTERNALS_H_ +#include "exec/cpu-common.h" + bool gdb_supports_guest_debug(void); -int gdb_breakpoint_insert(CPUState *cs, int type, hwaddr addr, hwaddr len); -int gdb_breakpoint_remove(CPUState *cs, int type, hwaddr addr, hwaddr len); +int gdb_breakpoint_insert(CPUState *cs, int type, vaddr addr, vaddr len); +int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len); void gdb_breakpoint_remove_all(CPUState *cs); #endif /* _INTERNALS_H_ */ diff --git a/gdbstub/softmmu.c b/gdbstub/softmmu.c index f208c6cf15..129575e510 100644 --- a/gdbstub/softmmu.c +++ b/gdbstub/softmmu.c @@ -11,7 +11,6 @@ #include "qemu/osdep.h" #include "exec/gdbstub.h" -#include "exec/hwaddr.h" #include "sysemu/cpus.h" #include "internals.h" @@ -24,7 +23,7 @@ bool gdb_supports_guest_debug(void) return false; } -int gdb_breakpoint_insert(CPUState *cs, int type, hwaddr addr, hwaddr len) +int gdb_breakpoint_insert(CPUState *cs, int type, vaddr addr, vaddr len) { const AccelOpsClass *ops = cpus_get_accel(); if (ops->insert_breakpoint) { @@ -33,7 +32,7 @@ int gdb_breakpoint_insert(CPUState *cs, int type, hwaddr addr, hwaddr len) return -ENOSYS; } -int gdb_breakpoint_remove(CPUState *cs, int type, hwaddr addr, hwaddr len) +int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len) { const AccelOpsClass *ops = cpus_get_accel(); if (ops->remove_breakpoint) { diff --git a/gdbstub/user.c b/gdbstub/user.c index 033e5fdd71..484bd8f461 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -9,7 +9,6 @@ */ #include "qemu/osdep.h" -#include "exec/hwaddr.h" #include "exec/gdbstub.h" #include "hw/core/cpu.h" #include "internals.h" @@ -20,7 +19,7 @@ bool gdb_supports_guest_debug(void) return true; } -int gdb_breakpoint_insert(CPUState *cs, int type, hwaddr addr, hwaddr len) +int gdb_breakpoint_insert(CPUState *cs, int type, vaddr addr, vaddr len) { CPUState *cpu; int err = 0; @@ -41,7 +40,7 @@ int gdb_breakpoint_insert(CPUState *cs, int type, hwaddr addr, hwaddr len) } } -int gdb_breakpoint_remove(CPUState *cs, int type, hwaddr addr, hwaddr len) +int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len) { CPUState *cpu; int err = 0; diff --git a/include/sysemu/accel-ops.h b/include/sysemu/accel-ops.h index 8cc7996def..30690c71bd 100644 --- a/include/sysemu/accel-ops.h +++ b/include/sysemu/accel-ops.h @@ -10,7 +10,7 @@ #ifndef ACCEL_OPS_H #define ACCEL_OPS_H -#include "exec/hwaddr.h" +#include "exec/cpu-common.h" #include "qom/object.h" #define ACCEL_OPS_SUFFIX "-ops" @@ -48,8 +48,8 @@ struct AccelOpsClass { /* gdbstub hooks */ bool (*supports_guest_debug)(void); - int (*insert_breakpoint)(CPUState *cpu, int type, hwaddr addr, hwaddr len); - int (*remove_breakpoint)(CPUState *cpu, int type, hwaddr addr, hwaddr len); + int (*insert_breakpoint)(CPUState *cpu, int type, vaddr addr, vaddr len); + int (*remove_breakpoint)(CPUState *cpu, int type, vaddr addr, vaddr len); void (*remove_all_breakpoints)(CPUState *cpu); }; From 6d2d454a884eeae588a9a08fcbdc6c9b6d079720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 6 Dec 2022 16:20:51 +0100 Subject: [PATCH 004/185] target/cpu: Restrict cpu_get_phys_page_debug() handlers to sysemu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'hwaddr' type is only available / meaningful on system emulation. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20221216215519.5522-5-philmd@linaro.org> --- target/alpha/cpu.h | 2 +- target/arm/cpu.h | 2 +- target/cris/cpu.h | 3 +-- target/hppa/cpu.h | 2 +- target/i386/cpu.h | 5 ++--- target/m68k/cpu.h | 2 +- target/microblaze/cpu.h | 4 ++-- target/nios2/cpu.h | 2 +- target/openrisc/cpu.h | 3 ++- target/ppc/cpu.h | 2 +- target/riscv/cpu.h | 2 +- target/rx/cpu.h | 2 +- target/rx/helper.c | 4 ++-- target/sh4/cpu.h | 2 +- target/sparc/cpu.h | 3 ++- target/xtensa/cpu.h | 2 +- 16 files changed, 21 insertions(+), 21 deletions(-) diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h index d0abc949a8..5e67304d81 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -276,9 +276,9 @@ extern const VMStateDescription vmstate_alpha_cpu; void alpha_cpu_do_interrupt(CPUState *cpu); bool alpha_cpu_exec_interrupt(CPUState *cpu, int int_req); +hwaddr alpha_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); #endif /* !CONFIG_USER_ONLY */ void alpha_cpu_dump_state(CPUState *cs, FILE *f, int flags); -hwaddr alpha_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int alpha_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int alpha_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 12b1082537..787121694c 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1104,10 +1104,10 @@ extern const VMStateDescription vmstate_arm_cpu; void arm_cpu_do_interrupt(CPUState *cpu); void arm_v7m_cpu_do_interrupt(CPUState *cpu); -#endif /* !CONFIG_USER_ONLY */ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, MemTxAttrs *attrs); +#endif /* !CONFIG_USER_ONLY */ int arm_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int arm_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); diff --git a/target/cris/cpu.h b/target/cris/cpu.h index e6776f25b1..71fa1f96e0 100644 --- a/target/cris/cpu.h +++ b/target/cris/cpu.h @@ -193,12 +193,11 @@ bool cris_cpu_exec_interrupt(CPUState *cpu, int int_req); bool cris_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +hwaddr cris_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); #endif void cris_cpu_dump_state(CPUState *cs, FILE *f, int flags); -hwaddr cris_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); - int crisv10_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int cris_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int cris_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 6f3b6beecf..b595ef25a9 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -322,11 +322,11 @@ static inline void cpu_hppa_change_prot_id(CPUHPPAState *env) { } void cpu_hppa_change_prot_id(CPUHPPAState *env); #endif -hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr); int hppa_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int hppa_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void hppa_cpu_dump_state(CPUState *cs, FILE *f, int); #ifndef CONFIG_USER_ONLY +hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr); bool hppa_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); diff --git a/target/i386/cpu.h b/target/i386/cpu.h index d4bc19577a..f729e0f09c 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1987,9 +1987,6 @@ void x86_cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list, void x86_cpu_dump_state(CPUState *cs, FILE *f, int flags); -hwaddr x86_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, - MemTxAttrs *attrs); - int x86_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int x86_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); @@ -1997,6 +1994,8 @@ void x86_cpu_list(void); int cpu_x86_support_mca_broadcast(CPUX86State *env); #ifndef CONFIG_USER_ONLY +hwaddr x86_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, + MemTxAttrs *attrs); int cpu_get_pic_interrupt(CPUX86State *s); /* MSDOS compatibility mode FPU exception support */ diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index 3a9cfe2f33..68ed531fc3 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -176,9 +176,9 @@ struct ArchCPU { #ifndef CONFIG_USER_ONLY void m68k_cpu_do_interrupt(CPUState *cpu); bool m68k_cpu_exec_interrupt(CPUState *cpu, int int_req); +hwaddr m68k_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); #endif /* !CONFIG_USER_ONLY */ void m68k_cpu_dump_state(CPUState *cpu, FILE *f, int flags); -hwaddr m68k_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int m68k_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int m68k_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index e541fbb0b3..f66df02226 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -358,13 +358,13 @@ struct ArchCPU { #ifndef CONFIG_USER_ONLY void mb_cpu_do_interrupt(CPUState *cs); bool mb_cpu_exec_interrupt(CPUState *cs, int int_req); +hwaddr mb_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, + MemTxAttrs *attrs); #endif /* !CONFIG_USER_ONLY */ G_NORETURN void mb_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); void mb_cpu_dump_state(CPUState *cpu, FILE *f, int flags); -hwaddr mb_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, - MemTxAttrs *attrs); int mb_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int mb_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); int mb_cpu_gdb_read_stack_protect(CPUArchState *cpu, GByteArray *buf, int reg); diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h index b1a5549074..20042c4332 100644 --- a/target/nios2/cpu.h +++ b/target/nios2/cpu.h @@ -262,7 +262,6 @@ void nios2_tcg_init(void); void nios2_cpu_do_interrupt(CPUState *cs); void dump_mmu(CPUNios2State *env); void nios2_cpu_dump_state(CPUState *cpu, FILE *f, int flags); -hwaddr nios2_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); G_NORETURN void nios2_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); @@ -288,6 +287,7 @@ static inline int cpu_mmu_index(CPUNios2State *env, bool ifetch) } #ifndef CONFIG_USER_ONLY +hwaddr nios2_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index 5f60749705..f16e8b3274 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -312,7 +312,6 @@ struct ArchCPU { void cpu_openrisc_list(void); void openrisc_cpu_dump_state(CPUState *cpu, FILE *f, int flags); -hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int openrisc_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int openrisc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void openrisc_translate_init(void); @@ -321,6 +320,8 @@ int print_insn_or1k(bfd_vma addr, disassemble_info *info); #define cpu_list cpu_openrisc_list #ifndef CONFIG_USER_ONLY +hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); + bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 3923f174f8..557d736dab 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1361,12 +1361,12 @@ static inline bool vhyp_cpu_in_nested(PowerPCCPU *cpu) #endif /* CONFIG_USER_ONLY */ void ppc_cpu_dump_state(CPUState *cpu, FILE *f, int flags); -hwaddr ppc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int ppc_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int ppc_cpu_gdb_read_register_apple(CPUState *cpu, GByteArray *buf, int reg); int ppc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); int ppc_cpu_gdb_write_register_apple(CPUState *cpu, uint8_t *buf, int reg); #ifndef CONFIG_USER_ONLY +hwaddr ppc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); void ppc_gdb_gen_spr_xml(PowerPCCPU *cpu); const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name); #endif diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 7128438d8e..df9cbc0d3f 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -573,7 +573,6 @@ bool riscv_cpu_virt_enabled(CPURISCVState *env); void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable); bool riscv_cpu_two_stage_lookup(int mmu_idx); int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch); -hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); G_NORETURN void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); @@ -592,6 +591,7 @@ void riscv_cpu_list(void); #define cpu_mmu_index riscv_cpu_mmu_index #ifndef CONFIG_USER_ONLY +hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request); void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env); int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts); diff --git a/target/rx/cpu.h b/target/rx/cpu.h index 5655dffeff..555d230f24 100644 --- a/target/rx/cpu.h +++ b/target/rx/cpu.h @@ -123,11 +123,11 @@ const char *rx_crname(uint8_t cr); #ifndef CONFIG_USER_ONLY void rx_cpu_do_interrupt(CPUState *cpu); bool rx_cpu_exec_interrupt(CPUState *cpu, int int_req); +hwaddr rx_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); #endif /* !CONFIG_USER_ONLY */ void rx_cpu_dump_state(CPUState *cpu, FILE *f, int flags); int rx_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int rx_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); -hwaddr rx_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); void rx_translate_init(void); void rx_cpu_list(void); diff --git a/target/rx/helper.c b/target/rx/helper.c index f34945e7e2..dad5fb4976 100644 --- a/target/rx/helper.c +++ b/target/rx/helper.c @@ -144,9 +144,9 @@ bool rx_cpu_exec_interrupt(CPUState *cs, int interrupt_request) return false; } -#endif /* !CONFIG_USER_ONLY */ - hwaddr rx_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { return addr; } + +#endif /* !CONFIG_USER_ONLY */ diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index 727b829598..02bfd612ea 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -214,7 +214,6 @@ struct ArchCPU { void superh_cpu_dump_state(CPUState *cpu, FILE *f, int flags); -hwaddr superh_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int superh_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int superh_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); G_NORETURN void superh_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, @@ -225,6 +224,7 @@ void sh4_translate_init(void); void sh4_cpu_list(void); #if !defined(CONFIG_USER_ONLY) +hwaddr superh_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); bool superh_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index e478c5eb16..ed0069d0b1 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -569,10 +569,11 @@ struct ArchCPU { #ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_sparc_cpu; + +hwaddr sparc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); #endif void sparc_cpu_do_interrupt(CPUState *cpu); -hwaddr sparc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int sparc_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int sparc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); G_NORETURN void sparc_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index 579adcb769..b7a54711a6 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -576,9 +576,9 @@ void xtensa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, unsigned size, MMUAccessType access_type, int mmu_idx, MemTxAttrs attrs, MemTxResult response, uintptr_t retaddr); +hwaddr xtensa_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); #endif void xtensa_cpu_dump_state(CPUState *cpu, FILE *f, int flags); -hwaddr xtensa_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); void xtensa_count_regs(const XtensaConfig *config, unsigned *n_regs, unsigned *n_core_regs); int xtensa_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); From d90ebc4731d3951f751840844effa468be3081a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 16 Dec 2022 12:08:50 +0100 Subject: [PATCH 005/185] target/cpu: Restrict do_transaction_failed() handlers to sysemu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'hwaddr' type is only available / meaningful on system emulation. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20221216215519.5522-6-philmd@linaro.org> --- target/arm/internals.h | 2 ++ target/m68k/cpu.h | 2 ++ target/riscv/cpu.h | 10 +++++----- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index 680c574717..3c7341e774 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -611,6 +611,7 @@ G_NORETURN void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); +#ifndef CONFIG_USER_ONLY /* arm_cpu_do_transaction_failed: handle a memory system error response * (eg "no device/memory present at address") by raising an external abort * exception @@ -620,6 +621,7 @@ void arm_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, MMUAccessType access_type, int mmu_idx, MemTxAttrs attrs, MemTxResult response, uintptr_t retaddr); +#endif /* Call any registered EL change hooks */ static inline void arm_call_pre_el_change_hook(ARMCPU *cpu) diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index 68ed531fc3..048d5aae2b 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -581,10 +581,12 @@ static inline int cpu_mmu_index (CPUM68KState *env, bool ifetch) bool m68k_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +#ifndef CONFIG_USER_ONLY void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, unsigned size, MMUAccessType access_type, int mmu_idx, MemTxAttrs attrs, MemTxResult response, uintptr_t retaddr); +#endif #include "exec/cpu-all.h" diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index df9cbc0d3f..d8e72c3e7c 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -579,11 +579,6 @@ G_NORETURN void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); -void riscv_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, - vaddr addr, unsigned size, - MMUAccessType access_type, - int mmu_idx, MemTxAttrs attrs, - MemTxResult response, uintptr_t retaddr); char *riscv_isa_string(RISCVCPU *cpu); void riscv_cpu_list(void); @@ -591,6 +586,11 @@ void riscv_cpu_list(void); #define cpu_mmu_index riscv_cpu_mmu_index #ifndef CONFIG_USER_ONLY +void riscv_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, + vaddr addr, unsigned size, + MMUAccessType access_type, + int mmu_idx, MemTxAttrs attrs, + MemTxResult response, uintptr_t retaddr); hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request); void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env); From aa8a7d20187f51e7916edf563b8248c31a9070e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 16 Dec 2022 11:28:06 +0100 Subject: [PATCH 006/185] target/i386: Remove NEED_CPU_H guard from target-specific headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NEED_CPU_H is always defined for these target-specific headers. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20221216220158.6317-2-philmd@linaro.org> --- target/i386/hax/hax-i386.h | 2 -- target/i386/hvf/hvf-i386.h | 4 ---- 2 files changed, 6 deletions(-) diff --git a/target/i386/hax/hax-i386.h b/target/i386/hax/hax-i386.h index efbb346238..409ebdb4af 100644 --- a/target/i386/hax/hax-i386.h +++ b/target/i386/hax/hax-i386.h @@ -49,7 +49,6 @@ struct hax_vm { struct hax_vcpu_state **vcpus; }; -#ifdef NEED_CPU_H /* Functions exported to host specific mode */ hax_fd hax_vcpu_get_fd(CPUArchState *env); int valid_hax_tunnel_size(uint16_t size); @@ -66,7 +65,6 @@ int hax_sync_vcpu_state(CPUArchState *env, struct vcpu_state_t *state, int set); int hax_sync_msr(CPUArchState *env, struct hax_msr_data *msrs, int set); int hax_sync_fpu(CPUArchState *env, struct fx_layout *fl, int set); -#endif int hax_vm_destroy(struct hax_vm *vm); int hax_capability(struct hax_state *hax, struct hax_capabilityinfo *cap); diff --git a/target/i386/hvf/hvf-i386.h b/target/i386/hvf/hvf-i386.h index 76e9235524..95b47c1c2e 100644 --- a/target/i386/hvf/hvf-i386.h +++ b/target/i386/hvf/hvf-i386.h @@ -24,11 +24,7 @@ void hvf_handle_io(CPUArchState *, uint16_t, void *, int, int, int); -#ifdef NEED_CPU_H -/* Functions exported to host specific mode */ - /* Host specific functions */ int hvf_inject_interrupt(CPUArchState *env, int vector); -#endif #endif From dd20414f9acd26b563efbf38c228ff9efced2ac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 16 Dec 2022 10:48:06 +0100 Subject: [PATCH 007/185] target/i386/cpu: Remove dead helper_lock() declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Missed in commit 37b995f6e7 ("target-i386: remove helper_lock()"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20221216220158.6317-3-philmd@linaro.org> --- target/i386/cpu.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index f729e0f09c..9824b7f8f2 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2313,9 +2313,6 @@ static inline void cpu_set_fpuc(CPUX86State *env, uint16_t fpuc) } } -/* mem_helper.c */ -void helper_lock_init(void); - /* svm_helper.c */ #ifdef CONFIG_USER_ONLY static inline void From b5c6a3c1df17c444b6ab587d363a758a54212be0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 16 Dec 2022 13:39:32 +0100 Subject: [PATCH 008/185] target/i386: Remove x86_cpu_dump_local_apic_state() dead stub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit x86_cpu_dump_local_apic_state() is called from monitor.c which is only compiled for system emulation since commit bf95728400 ("monitor: remove target-specific code from monitor.c"). Interestingly this stub was added few weeks later in commit 1f871d49e3 ("hmp: added local apic dump state") and was not necessary by that time. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20221216220158.6317-5-philmd@linaro.org> --- target/i386/cpu-dump.c | 5 +---- target/i386/cpu.h | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/target/i386/cpu-dump.c b/target/i386/cpu-dump.c index 08ac957e99..40697064d9 100644 --- a/target/i386/cpu-dump.c +++ b/target/i386/cpu-dump.c @@ -335,10 +335,7 @@ void x86_cpu_dump_local_apic_state(CPUState *cs, int flags) } qemu_printf(" PPR 0x%02x\n", apic_get_ppr(s)); } -#else -void x86_cpu_dump_local_apic_state(CPUState *cs, int flags) -{ -} + #endif /* !CONFIG_USER_ONLY */ #define DUMP_CODE_BYTES_TOTAL 50 diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 9824b7f8f2..32d048f326 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2355,12 +2355,16 @@ typedef int X86CPUVersion; */ void x86_cpu_set_default_version(X86CPUVersion version); +#ifndef CONFIG_USER_ONLY + #define APIC_DEFAULT_ADDRESS 0xfee00000 #define APIC_SPACE_SIZE 0x100000 /* cpu-dump.c */ void x86_cpu_dump_local_apic_state(CPUState *cs, int flags); +#endif + /* cpu.c */ bool cpu_is_bsp(X86CPU *cpu); From 5c39f954f5b8e32d22fa9fe5af7fb953a4258fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 17 Dec 2022 16:10:06 +0100 Subject: [PATCH 009/185] target/hppa: Extract FPU helpers to fpu_helper.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20221217173219.8715-2-philmd@linaro.org> --- target/hppa/fpu_helper.c | 450 +++++++++++++++++++++++++++++++++++++++ target/hppa/meson.build | 1 + target/hppa/op_helper.c | 427 ------------------------------------- 3 files changed, 451 insertions(+), 427 deletions(-) create mode 100644 target/hppa/fpu_helper.c diff --git a/target/hppa/fpu_helper.c b/target/hppa/fpu_helper.c new file mode 100644 index 0000000000..576f283b04 --- /dev/null +++ b/target/hppa/fpu_helper.c @@ -0,0 +1,450 @@ +/* + * Helpers for HPPA FPU instructions. + * + * Copyright (c) 2016 Richard Henderson + * + * 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, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" +#include "fpu/softfloat.h" + +void HELPER(loaded_fr0)(CPUHPPAState *env) +{ + uint32_t shadow = env->fr[0] >> 32; + int rm, d; + + env->fr0_shadow = shadow; + + switch (extract32(shadow, 9, 2)) { + default: + rm = float_round_nearest_even; + break; + case 1: + rm = float_round_to_zero; + break; + case 2: + rm = float_round_up; + break; + case 3: + rm = float_round_down; + break; + } + set_float_rounding_mode(rm, &env->fp_status); + + d = extract32(shadow, 5, 1); + set_flush_to_zero(d, &env->fp_status); + set_flush_inputs_to_zero(d, &env->fp_status); +} + +void cpu_hppa_loaded_fr0(CPUHPPAState *env) +{ + helper_loaded_fr0(env); +} + +#define CONVERT_BIT(X, SRC, DST) \ + ((SRC) > (DST) \ + ? (X) / ((SRC) / (DST)) & (DST) \ + : ((X) & (SRC)) * ((DST) / (SRC))) + +static void update_fr0_op(CPUHPPAState *env, uintptr_t ra) +{ + uint32_t soft_exp = get_float_exception_flags(&env->fp_status); + uint32_t hard_exp = 0; + uint32_t shadow = env->fr0_shadow; + + if (likely(soft_exp == 0)) { + env->fr[0] = (uint64_t)shadow << 32; + return; + } + set_float_exception_flags(0, &env->fp_status); + + hard_exp |= CONVERT_BIT(soft_exp, float_flag_inexact, 1u << 0); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, 1u << 1); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, 1u << 2); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, 1u << 3); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, 1u << 4); + shadow |= hard_exp << (32 - 5); + env->fr0_shadow = shadow; + env->fr[0] = (uint64_t)shadow << 32; + + if (hard_exp & shadow) { + hppa_dynamic_excp(env, EXCP_ASSIST, ra); + } +} + +float32 HELPER(fsqrt_s)(CPUHPPAState *env, float32 arg) +{ + float32 ret = float32_sqrt(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(frnd_s)(CPUHPPAState *env, float32 arg) +{ + float32 ret = float32_round_to_int(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fadd_s)(CPUHPPAState *env, float32 a, float32 b) +{ + float32 ret = float32_add(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fsub_s)(CPUHPPAState *env, float32 a, float32 b) +{ + float32 ret = float32_sub(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fmpy_s)(CPUHPPAState *env, float32 a, float32 b) +{ + float32 ret = float32_mul(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fdiv_s)(CPUHPPAState *env, float32 a, float32 b) +{ + float32 ret = float32_div(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fsqrt_d)(CPUHPPAState *env, float64 arg) +{ + float64 ret = float64_sqrt(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(frnd_d)(CPUHPPAState *env, float64 arg) +{ + float64 ret = float64_round_to_int(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fadd_d)(CPUHPPAState *env, float64 a, float64 b) +{ + float64 ret = float64_add(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fsub_d)(CPUHPPAState *env, float64 a, float64 b) +{ + float64 ret = float64_sub(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fmpy_d)(CPUHPPAState *env, float64 a, float64 b) +{ + float64 ret = float64_mul(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fdiv_d)(CPUHPPAState *env, float64 a, float64 b) +{ + float64 ret = float64_div(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fcnv_s_d)(CPUHPPAState *env, float32 arg) +{ + float64 ret = float32_to_float64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fcnv_d_s)(CPUHPPAState *env, float64 arg) +{ + float32 ret = float64_to_float32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fcnv_w_s)(CPUHPPAState *env, int32_t arg) +{ + float32 ret = int32_to_float32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fcnv_dw_s)(CPUHPPAState *env, int64_t arg) +{ + float32 ret = int64_to_float32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fcnv_w_d)(CPUHPPAState *env, int32_t arg) +{ + float64 ret = int32_to_float64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fcnv_dw_d)(CPUHPPAState *env, int64_t arg) +{ + float64 ret = int64_to_float64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int32_t HELPER(fcnv_s_w)(CPUHPPAState *env, float32 arg) +{ + int32_t ret = float32_to_int32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int32_t HELPER(fcnv_d_w)(CPUHPPAState *env, float64 arg) +{ + int32_t ret = float64_to_int32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int64_t HELPER(fcnv_s_dw)(CPUHPPAState *env, float32 arg) +{ + int64_t ret = float32_to_int64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int64_t HELPER(fcnv_d_dw)(CPUHPPAState *env, float64 arg) +{ + int64_t ret = float64_to_int64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int32_t HELPER(fcnv_t_s_w)(CPUHPPAState *env, float32 arg) +{ + int32_t ret = float32_to_int32_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int32_t HELPER(fcnv_t_d_w)(CPUHPPAState *env, float64 arg) +{ + int32_t ret = float64_to_int32_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int64_t HELPER(fcnv_t_s_dw)(CPUHPPAState *env, float32 arg) +{ + int64_t ret = float32_to_int64_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int64_t HELPER(fcnv_t_d_dw)(CPUHPPAState *env, float64 arg) +{ + int64_t ret = float64_to_int64_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fcnv_uw_s)(CPUHPPAState *env, uint32_t arg) +{ + float32 ret = uint32_to_float32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fcnv_udw_s)(CPUHPPAState *env, uint64_t arg) +{ + float32 ret = uint64_to_float32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fcnv_uw_d)(CPUHPPAState *env, uint32_t arg) +{ + float64 ret = uint32_to_float64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fcnv_udw_d)(CPUHPPAState *env, uint64_t arg) +{ + float64 ret = uint64_to_float64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint32_t HELPER(fcnv_s_uw)(CPUHPPAState *env, float32 arg) +{ + uint32_t ret = float32_to_uint32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint32_t HELPER(fcnv_d_uw)(CPUHPPAState *env, float64 arg) +{ + uint32_t ret = float64_to_uint32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint64_t HELPER(fcnv_s_udw)(CPUHPPAState *env, float32 arg) +{ + uint64_t ret = float32_to_uint64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint64_t HELPER(fcnv_d_udw)(CPUHPPAState *env, float64 arg) +{ + uint64_t ret = float64_to_uint64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint32_t HELPER(fcnv_t_s_uw)(CPUHPPAState *env, float32 arg) +{ + uint32_t ret = float32_to_uint32_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint32_t HELPER(fcnv_t_d_uw)(CPUHPPAState *env, float64 arg) +{ + uint32_t ret = float64_to_uint32_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint64_t HELPER(fcnv_t_s_udw)(CPUHPPAState *env, float32 arg) +{ + uint64_t ret = float32_to_uint64_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint64_t HELPER(fcnv_t_d_udw)(CPUHPPAState *env, float64 arg) +{ + uint64_t ret = float64_to_uint64_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +static void update_fr0_cmp(CPUHPPAState *env, uint32_t y, + uint32_t c, FloatRelation r) +{ + uint32_t shadow = env->fr0_shadow; + + switch (r) { + case float_relation_greater: + c = extract32(c, 4, 1); + break; + case float_relation_less: + c = extract32(c, 3, 1); + break; + case float_relation_equal: + c = extract32(c, 2, 1); + break; + case float_relation_unordered: + c = extract32(c, 1, 1); + break; + default: + g_assert_not_reached(); + } + + if (y) { + /* targeted comparison */ + /* set fpsr[ca[y - 1]] to current compare */ + shadow = deposit32(shadow, 21 - (y - 1), 1, c); + } else { + /* queued comparison */ + /* shift cq right by one place */ + shadow = deposit32(shadow, 11, 10, extract32(shadow, 12, 10)); + /* move fpsr[c] to fpsr[cq[0]] */ + shadow = deposit32(shadow, 21, 1, extract32(shadow, 26, 1)); + /* set fpsr[c] to current compare */ + shadow = deposit32(shadow, 26, 1, c); + } + + env->fr0_shadow = shadow; + env->fr[0] = (uint64_t)shadow << 32; +} + +void HELPER(fcmp_s)(CPUHPPAState *env, float32 a, float32 b, + uint32_t y, uint32_t c) +{ + FloatRelation r; + if (c & 1) { + r = float32_compare(a, b, &env->fp_status); + } else { + r = float32_compare_quiet(a, b, &env->fp_status); + } + update_fr0_op(env, GETPC()); + update_fr0_cmp(env, y, c, r); +} + +void HELPER(fcmp_d)(CPUHPPAState *env, float64 a, float64 b, + uint32_t y, uint32_t c) +{ + FloatRelation r; + if (c & 1) { + r = float64_compare(a, b, &env->fp_status); + } else { + r = float64_compare_quiet(a, b, &env->fp_status); + } + update_fr0_op(env, GETPC()); + update_fr0_cmp(env, y, c, r); +} + +float32 HELPER(fmpyfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c) +{ + float32 ret = float32_muladd(a, b, c, 0, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fmpynfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c) +{ + float32 ret = float32_muladd(a, b, c, float_muladd_negate_product, + &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fmpyfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c) +{ + float64 ret = float64_muladd(a, b, c, 0, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fmpynfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c) +{ + float64 ret = float64_muladd(a, b, c, float_muladd_negate_product, + &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} diff --git a/target/hppa/meson.build b/target/hppa/meson.build index 021e42a2d0..fb90aed5de 100644 --- a/target/hppa/meson.build +++ b/target/hppa/meson.build @@ -4,6 +4,7 @@ hppa_ss = ss.source_set() hppa_ss.add(gen) hppa_ss.add(files( 'cpu.c', + 'fpu_helper.c', 'gdbstub.c', 'helper.c', 'int_helper.c', diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index fbd80e4248..f5905c9fc2 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -25,7 +25,6 @@ #include "exec/cpu_ldst.h" #include "qemu/timer.h" #include "sysemu/runstate.h" -#include "fpu/softfloat.h" #include "trace.h" G_NORETURN void HELPER(excp)(CPUHPPAState *env, int excp) @@ -197,432 +196,6 @@ target_ureg HELPER(probe)(CPUHPPAState *env, target_ulong addr, #endif } -void HELPER(loaded_fr0)(CPUHPPAState *env) -{ - uint32_t shadow = env->fr[0] >> 32; - int rm, d; - - env->fr0_shadow = shadow; - - switch (extract32(shadow, 9, 2)) { - default: - rm = float_round_nearest_even; - break; - case 1: - rm = float_round_to_zero; - break; - case 2: - rm = float_round_up; - break; - case 3: - rm = float_round_down; - break; - } - set_float_rounding_mode(rm, &env->fp_status); - - d = extract32(shadow, 5, 1); - set_flush_to_zero(d, &env->fp_status); - set_flush_inputs_to_zero(d, &env->fp_status); -} - -void cpu_hppa_loaded_fr0(CPUHPPAState *env) -{ - helper_loaded_fr0(env); -} - -#define CONVERT_BIT(X, SRC, DST) \ - ((SRC) > (DST) \ - ? (X) / ((SRC) / (DST)) & (DST) \ - : ((X) & (SRC)) * ((DST) / (SRC))) - -static void update_fr0_op(CPUHPPAState *env, uintptr_t ra) -{ - uint32_t soft_exp = get_float_exception_flags(&env->fp_status); - uint32_t hard_exp = 0; - uint32_t shadow = env->fr0_shadow; - - if (likely(soft_exp == 0)) { - env->fr[0] = (uint64_t)shadow << 32; - return; - } - set_float_exception_flags(0, &env->fp_status); - - hard_exp |= CONVERT_BIT(soft_exp, float_flag_inexact, 1u << 0); - hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, 1u << 1); - hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, 1u << 2); - hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, 1u << 3); - hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, 1u << 4); - shadow |= hard_exp << (32 - 5); - env->fr0_shadow = shadow; - env->fr[0] = (uint64_t)shadow << 32; - - if (hard_exp & shadow) { - hppa_dynamic_excp(env, EXCP_ASSIST, ra); - } -} - -float32 HELPER(fsqrt_s)(CPUHPPAState *env, float32 arg) -{ - float32 ret = float32_sqrt(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(frnd_s)(CPUHPPAState *env, float32 arg) -{ - float32 ret = float32_round_to_int(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fadd_s)(CPUHPPAState *env, float32 a, float32 b) -{ - float32 ret = float32_add(a, b, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fsub_s)(CPUHPPAState *env, float32 a, float32 b) -{ - float32 ret = float32_sub(a, b, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fmpy_s)(CPUHPPAState *env, float32 a, float32 b) -{ - float32 ret = float32_mul(a, b, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fdiv_s)(CPUHPPAState *env, float32 a, float32 b) -{ - float32 ret = float32_div(a, b, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fsqrt_d)(CPUHPPAState *env, float64 arg) -{ - float64 ret = float64_sqrt(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(frnd_d)(CPUHPPAState *env, float64 arg) -{ - float64 ret = float64_round_to_int(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fadd_d)(CPUHPPAState *env, float64 a, float64 b) -{ - float64 ret = float64_add(a, b, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fsub_d)(CPUHPPAState *env, float64 a, float64 b) -{ - float64 ret = float64_sub(a, b, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fmpy_d)(CPUHPPAState *env, float64 a, float64 b) -{ - float64 ret = float64_mul(a, b, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fdiv_d)(CPUHPPAState *env, float64 a, float64 b) -{ - float64 ret = float64_div(a, b, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fcnv_s_d)(CPUHPPAState *env, float32 arg) -{ - float64 ret = float32_to_float64(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fcnv_d_s)(CPUHPPAState *env, float64 arg) -{ - float32 ret = float64_to_float32(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fcnv_w_s)(CPUHPPAState *env, int32_t arg) -{ - float32 ret = int32_to_float32(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fcnv_dw_s)(CPUHPPAState *env, int64_t arg) -{ - float32 ret = int64_to_float32(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fcnv_w_d)(CPUHPPAState *env, int32_t arg) -{ - float64 ret = int32_to_float64(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fcnv_dw_d)(CPUHPPAState *env, int64_t arg) -{ - float64 ret = int64_to_float64(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -int32_t HELPER(fcnv_s_w)(CPUHPPAState *env, float32 arg) -{ - int32_t ret = float32_to_int32(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -int32_t HELPER(fcnv_d_w)(CPUHPPAState *env, float64 arg) -{ - int32_t ret = float64_to_int32(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -int64_t HELPER(fcnv_s_dw)(CPUHPPAState *env, float32 arg) -{ - int64_t ret = float32_to_int64(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -int64_t HELPER(fcnv_d_dw)(CPUHPPAState *env, float64 arg) -{ - int64_t ret = float64_to_int64(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -int32_t HELPER(fcnv_t_s_w)(CPUHPPAState *env, float32 arg) -{ - int32_t ret = float32_to_int32_round_to_zero(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -int32_t HELPER(fcnv_t_d_w)(CPUHPPAState *env, float64 arg) -{ - int32_t ret = float64_to_int32_round_to_zero(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -int64_t HELPER(fcnv_t_s_dw)(CPUHPPAState *env, float32 arg) -{ - int64_t ret = float32_to_int64_round_to_zero(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -int64_t HELPER(fcnv_t_d_dw)(CPUHPPAState *env, float64 arg) -{ - int64_t ret = float64_to_int64_round_to_zero(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fcnv_uw_s)(CPUHPPAState *env, uint32_t arg) -{ - float32 ret = uint32_to_float32(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fcnv_udw_s)(CPUHPPAState *env, uint64_t arg) -{ - float32 ret = uint64_to_float32(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fcnv_uw_d)(CPUHPPAState *env, uint32_t arg) -{ - float64 ret = uint32_to_float64(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fcnv_udw_d)(CPUHPPAState *env, uint64_t arg) -{ - float64 ret = uint64_to_float64(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -uint32_t HELPER(fcnv_s_uw)(CPUHPPAState *env, float32 arg) -{ - uint32_t ret = float32_to_uint32(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -uint32_t HELPER(fcnv_d_uw)(CPUHPPAState *env, float64 arg) -{ - uint32_t ret = float64_to_uint32(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -uint64_t HELPER(fcnv_s_udw)(CPUHPPAState *env, float32 arg) -{ - uint64_t ret = float32_to_uint64(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -uint64_t HELPER(fcnv_d_udw)(CPUHPPAState *env, float64 arg) -{ - uint64_t ret = float64_to_uint64(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -uint32_t HELPER(fcnv_t_s_uw)(CPUHPPAState *env, float32 arg) -{ - uint32_t ret = float32_to_uint32_round_to_zero(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -uint32_t HELPER(fcnv_t_d_uw)(CPUHPPAState *env, float64 arg) -{ - uint32_t ret = float64_to_uint32_round_to_zero(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -uint64_t HELPER(fcnv_t_s_udw)(CPUHPPAState *env, float32 arg) -{ - uint64_t ret = float32_to_uint64_round_to_zero(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -uint64_t HELPER(fcnv_t_d_udw)(CPUHPPAState *env, float64 arg) -{ - uint64_t ret = float64_to_uint64_round_to_zero(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -static void update_fr0_cmp(CPUHPPAState *env, uint32_t y, - uint32_t c, FloatRelation r) -{ - uint32_t shadow = env->fr0_shadow; - - switch (r) { - case float_relation_greater: - c = extract32(c, 4, 1); - break; - case float_relation_less: - c = extract32(c, 3, 1); - break; - case float_relation_equal: - c = extract32(c, 2, 1); - break; - case float_relation_unordered: - c = extract32(c, 1, 1); - break; - default: - g_assert_not_reached(); - } - - if (y) { - /* targeted comparison */ - /* set fpsr[ca[y - 1]] to current compare */ - shadow = deposit32(shadow, 21 - (y - 1), 1, c); - } else { - /* queued comparison */ - /* shift cq right by one place */ - shadow = deposit32(shadow, 11, 10, extract32(shadow, 12, 10)); - /* move fpsr[c] to fpsr[cq[0]] */ - shadow = deposit32(shadow, 21, 1, extract32(shadow, 26, 1)); - /* set fpsr[c] to current compare */ - shadow = deposit32(shadow, 26, 1, c); - } - - env->fr0_shadow = shadow; - env->fr[0] = (uint64_t)shadow << 32; -} - -void HELPER(fcmp_s)(CPUHPPAState *env, float32 a, float32 b, - uint32_t y, uint32_t c) -{ - FloatRelation r; - if (c & 1) { - r = float32_compare(a, b, &env->fp_status); - } else { - r = float32_compare_quiet(a, b, &env->fp_status); - } - update_fr0_op(env, GETPC()); - update_fr0_cmp(env, y, c, r); -} - -void HELPER(fcmp_d)(CPUHPPAState *env, float64 a, float64 b, - uint32_t y, uint32_t c) -{ - FloatRelation r; - if (c & 1) { - r = float64_compare(a, b, &env->fp_status); - } else { - r = float64_compare_quiet(a, b, &env->fp_status); - } - update_fr0_op(env, GETPC()); - update_fr0_cmp(env, y, c, r); -} - -float32 HELPER(fmpyfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c) -{ - float32 ret = float32_muladd(a, b, c, 0, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fmpynfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c) -{ - float32 ret = float32_muladd(a, b, c, float_muladd_negate_product, - &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fmpyfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c) -{ - float64 ret = float64_muladd(a, b, c, 0, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fmpynfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c) -{ - float64 ret = float64_muladd(a, b, c, float_muladd_negate_product, - &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - target_ureg HELPER(read_interval_timer)(void) { #ifdef CONFIG_USER_ONLY From c70647eeb8d324f882094bdee9e5f5c16a1de5ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 17 Dec 2022 16:18:29 +0100 Subject: [PATCH 010/185] target/hppa: Extract system helpers to sys_helper.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Message-Id: <20221217173219.8715-3-philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- target/hppa/meson.build | 1 + target/hppa/op_helper.c | 77 ----------------------------- target/hppa/sys_helper.c | 101 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 77 deletions(-) create mode 100644 target/hppa/sys_helper.c diff --git a/target/hppa/meson.build b/target/hppa/meson.build index fb90aed5de..81b4b4e617 100644 --- a/target/hppa/meson.build +++ b/target/hppa/meson.build @@ -16,6 +16,7 @@ hppa_softmmu_ss = ss.source_set() hppa_softmmu_ss.add(files( 'machine.c', 'mem_helper.c', + 'sys_helper.c', )) target_arch += {'hppa': hppa_ss} diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index f5905c9fc2..32c27c66b2 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -24,7 +24,6 @@ #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" #include "trace.h" G_NORETURN void HELPER(excp)(CPUHPPAState *env, int excp) @@ -209,79 +208,3 @@ target_ureg HELPER(read_interval_timer)(void) return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) >> 2; #endif } - -#ifndef CONFIG_USER_ONLY -void HELPER(write_interval_timer)(CPUHPPAState *env, target_ureg val) -{ - HPPACPU *cpu = env_archcpu(env); - uint64_t current = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - uint64_t timeout; - - /* Even in 64-bit mode, the comparator is always 32-bit. But the - value we expose to the guest is 1/4 of the speed of the clock, - so moosh in 34 bits. */ - timeout = deposit64(current, 0, 34, (uint64_t)val << 2); - - /* If the mooshing puts the clock in the past, advance to next round. */ - if (timeout < current + 1000) { - timeout += 1ULL << 34; - } - - cpu->env.cr[CR_IT] = timeout; - timer_mod(cpu->alarm_timer, timeout); -} - -void HELPER(halt)(CPUHPPAState *env) -{ - qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); - helper_excp(env, EXCP_HLT); -} - -void HELPER(reset)(CPUHPPAState *env) -{ - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - helper_excp(env, EXCP_HLT); -} - -target_ureg HELPER(swap_system_mask)(CPUHPPAState *env, target_ureg nsm) -{ - target_ulong psw = env->psw; - /* - * Setting the PSW Q bit to 1, if it was not already 1, is an - * undefined operation. - * - * However, HP-UX 10.20 does this with the SSM instruction. - * Tested this on HP9000/712 and HP9000/785/C3750 and both - * machines set the Q bit from 0 to 1 without an exception, - * so let this go without comment. - */ - env->psw = (psw & ~PSW_SM) | (nsm & PSW_SM); - return psw & PSW_SM; -} - -void HELPER(rfi)(CPUHPPAState *env) -{ - env->iasq_f = (uint64_t)env->cr[CR_IIASQ] << 32; - env->iasq_b = (uint64_t)env->cr_back[0] << 32; - env->iaoq_f = env->cr[CR_IIAOQ]; - env->iaoq_b = env->cr_back[1]; - cpu_hppa_put_psw(env, env->cr[CR_IPSW]); -} - -void HELPER(getshadowregs)(CPUHPPAState *env) -{ - env->gr[1] = env->shadow[0]; - env->gr[8] = env->shadow[1]; - env->gr[9] = env->shadow[2]; - env->gr[16] = env->shadow[3]; - env->gr[17] = env->shadow[4]; - env->gr[24] = env->shadow[5]; - env->gr[25] = env->shadow[6]; -} - -void HELPER(rfi_r)(CPUHPPAState *env) -{ - helper_getshadowregs(env); - helper_rfi(env); -} -#endif diff --git a/target/hppa/sys_helper.c b/target/hppa/sys_helper.c new file mode 100644 index 0000000000..4bb4cf611c --- /dev/null +++ b/target/hppa/sys_helper.c @@ -0,0 +1,101 @@ +/* + * Helpers for HPPA system instructions. + * + * Copyright (c) 2016 Richard Henderson + * + * 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, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" +#include "qemu/timer.h" +#include "sysemu/runstate.h" + +void HELPER(write_interval_timer)(CPUHPPAState *env, target_ureg val) +{ + HPPACPU *cpu = env_archcpu(env); + uint64_t current = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + uint64_t timeout; + + /* + * Even in 64-bit mode, the comparator is always 32-bit. But the + * value we expose to the guest is 1/4 of the speed of the clock, + * so moosh in 34 bits. + */ + timeout = deposit64(current, 0, 34, (uint64_t)val << 2); + + /* If the mooshing puts the clock in the past, advance to next round. */ + if (timeout < current + 1000) { + timeout += 1ULL << 34; + } + + cpu->env.cr[CR_IT] = timeout; + timer_mod(cpu->alarm_timer, timeout); +} + +void HELPER(halt)(CPUHPPAState *env) +{ + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + helper_excp(env, EXCP_HLT); +} + +void HELPER(reset)(CPUHPPAState *env) +{ + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + helper_excp(env, EXCP_HLT); +} + +target_ureg HELPER(swap_system_mask)(CPUHPPAState *env, target_ureg nsm) +{ + target_ulong psw = env->psw; + /* + * Setting the PSW Q bit to 1, if it was not already 1, is an + * undefined operation. + * + * However, HP-UX 10.20 does this with the SSM instruction. + * Tested this on HP9000/712 and HP9000/785/C3750 and both + * machines set the Q bit from 0 to 1 without an exception, + * so let this go without comment. + */ + env->psw = (psw & ~PSW_SM) | (nsm & PSW_SM); + return psw & PSW_SM; +} + +void HELPER(rfi)(CPUHPPAState *env) +{ + env->iasq_f = (uint64_t)env->cr[CR_IIASQ] << 32; + env->iasq_b = (uint64_t)env->cr_back[0] << 32; + env->iaoq_f = env->cr[CR_IIAOQ]; + env->iaoq_b = env->cr_back[1]; + cpu_hppa_put_psw(env, env->cr[CR_IPSW]); +} + +void HELPER(getshadowregs)(CPUHPPAState *env) +{ + env->gr[1] = env->shadow[0]; + env->gr[8] = env->shadow[1]; + env->gr[9] = env->shadow[2]; + env->gr[16] = env->shadow[3]; + env->gr[17] = env->shadow[4]; + env->gr[24] = env->shadow[5]; + env->gr[25] = env->shadow[6]; +} + +void HELPER(rfi_r)(CPUHPPAState *env) +{ + helper_getshadowregs(env); + helper_rfi(env); +} From 99b4d7431b54e275ef1981fa2f778004587edc1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 17 Dec 2022 18:17:32 +0100 Subject: [PATCH 011/185] target/alpha: Remove obsolete STATUS document MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Likely out of sync: last update is from 2008 (commit d1412eb240), 12 years ago. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20221217172907.8364-2-philmd@linaro.org> --- target/alpha/STATUS | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 target/alpha/STATUS diff --git a/target/alpha/STATUS b/target/alpha/STATUS deleted file mode 100644 index 6c9744569e..0000000000 --- a/target/alpha/STATUS +++ /dev/null @@ -1,28 +0,0 @@ -(to be completed) - -Alpha emulation structure: -cpu.h : CPU definitions globally exported -exec.h : CPU definitions used only for translated code execution -helper.c : helpers that can be called either by the translated code - or the QEMU core, including the exception handler. -op_helper.c : helpers that can be called only from TCG -helper.h : TCG helpers prototypes -translate.c : Alpha instructions to micro-operations translator - -Code translator status: -The Alpha CPU instruction emulation should be quite complete with the -limitation that the VAX floating-point load and stores are not tested. -The 4 MMU modes are implemented. - -Linux user mode emulation status: -a few programs start to run. Most crash at a certain point, dereferencing a -NULL pointer. It seems that the UNIQUE register is not initialized properly. -It may appear that old executables, not relying on TLS support, run but -this is to be proved... - -Full system emulation status: -* Alpha PALCode emulation is in a very early stage and is not sufficient - to run any real OS. The alpha-softmmu target is not enabled for now. -* no hardware platform description is implemented -* there might be problems in the Alpha PALCode dedicated instructions - that would prevent to use a native PALCode image. From 4e92e31294572663f214ceeeaf8bc76e463a7050 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Fri, 16 Dec 2022 15:50:26 +0100 Subject: [PATCH 012/185] target/loongarch/cpu: Remove unused "sysbus.h" header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cpu is used in both user and system emulation context while sysbus.h is system-only. Remove it since it's not needed anyway. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20221217172907.8364-3-philmd@linaro.org> --- target/loongarch/cpu.h | 1 - 1 file changed, 1 deletion(-) diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index e35cf65597..3882e970b4 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -13,7 +13,6 @@ #include "hw/registerfields.h" #include "qemu/timer.h" #include "exec/memory.h" -#include "hw/sysbus.h" #include "cpu-csr.h" #define IOCSRF_TEMP 0 From 8f15d6179aa0beff4ee67a48c39ec9a0ec1e719b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 16 Dec 2022 15:52:50 +0100 Subject: [PATCH 013/185] target/loongarch/cpu: Restrict "memory.h" header to sysemu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Missed in 0093b9a5ee ("target/loongarch: Adjust functions and structure to support user-mode") while cleaning commit f84a2aacf5 ("target/loongarch: Add LoongArch IOCSR instruction"). Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20221217172907.8364-4-philmd@linaro.org> --- target/loongarch/cpu.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 3882e970b4..d60693fafe 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -12,7 +12,9 @@ #include "fpu/softfloat-types.h" #include "hw/registerfields.h" #include "qemu/timer.h" +#ifndef CONFIG_USER_ONLY #include "exec/memory.h" +#endif #include "cpu-csr.h" #define IOCSRF_TEMP 0 From 414fa2aaa2ba5e4ce9b4382be1ed60b3d0dac06a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 16 Dec 2022 16:15:44 +0100 Subject: [PATCH 014/185] target/ppc/internal: Restrict MMU declarations to sysemu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'hwaddr' type is only available / meaningful on system emulation. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20221217172907.8364-5-philmd@linaro.org> --- target/ppc/internal.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 337a362205..901bae6d39 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -242,9 +242,12 @@ static inline int prot_for_access_type(MMUAccessType access_type) g_assert_not_reached(); } +#ifndef CONFIG_USER_ONLY + /* PowerPC MMU emulation */ typedef struct mmu_ctx_t mmu_ctx_t; + bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, bool guest_visible); @@ -266,6 +269,8 @@ struct mmu_ctx_t { int nx; /* Non-execute area */ }; +#endif /* !CONFIG_USER_ONLY */ + /* Common routines used by software and hardware TLBs emulation */ static inline int pte_is_valid(target_ulong pte0) { From 60f5fadd13740c61fdee0012a0dd21867493c909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 17 Dec 2022 14:57:07 +0100 Subject: [PATCH 015/185] target/ppc/kvm: Remove unused "sysbus.h" header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nothing requires SysBus declarations here. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20221217172907.8364-6-philmd@linaro.org> --- target/ppc/kvm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 7c25348b7b..78f6fc50cd 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -32,7 +32,6 @@ #include "sysemu/device_tree.h" #include "mmu-hash64.h" -#include "hw/sysbus.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_cpu_core.h" #include "hw/hw.h" From a1a65aade61fae15fdb6560e1a618865f6f21012 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 21 Feb 2023 16:30:06 +0100 Subject: [PATCH 016/185] target/ppc: Fix warning with clang-15 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When compiling for windows-arm64 using clang-15, it reports a sometimes uninitialized variable. This seems to be a false positive, as a default case guards switch expressions, preventing to return an uninitialized value, but clang seems unhappy with assert(0) definition. Change code to g_assert_not_reached() fix the warning. Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230221153006.20300-5-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- target/ppc/dfp_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/ppc/dfp_helper.c b/target/ppc/dfp_helper.c index cc024316d5..5967ea07a9 100644 --- a/target/ppc/dfp_helper.c +++ b/target/ppc/dfp_helper.c @@ -121,7 +121,7 @@ static void dfp_set_round_mode_from_immediate(uint8_t r, uint8_t rmc, case 3: /* use FPSCR rounding mode */ return; default: - assert(0); /* cannot get here */ + g_assert_not_reached(); } } else { /* r == 1 */ switch (rmc & 3) { @@ -138,7 +138,7 @@ static void dfp_set_round_mode_from_immediate(uint8_t r, uint8_t rmc, rnd = DEC_ROUND_HALF_DOWN; break; default: - assert(0); /* cannot get here */ + g_assert_not_reached(); } } decContextSetRounding(&dfp->context, rnd); From 04bc30275876a4194233a763e1b8031f7a980813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 17 Dec 2022 18:06:21 +0100 Subject: [PATCH 017/185] target/riscv/cpu: Move Floating-Point fields closer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20221217172907.8364-7-philmd@linaro.org> --- target/riscv/cpu.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index d8e72c3e7c..31537fc05f 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -148,7 +148,6 @@ typedef struct PMUCTRState { struct CPUArchState { target_ulong gpr[32]; target_ulong gprh[32]; /* 64 top bits of the 128-bit registers */ - uint64_t fpr[32]; /* assume both F and D extensions */ /* vector coprocessor state. */ uint64_t vreg[32 * RV_VLEN_MAX / 64] QEMU_ALIGNED(16); @@ -163,7 +162,10 @@ struct CPUArchState { target_ulong load_res; target_ulong load_val; + /* Floating-Point state */ + uint64_t fpr[32]; /* assume both F and D extensions */ target_ulong frm; + float_status fp_status; target_ulong badaddr; target_ulong bins; @@ -379,8 +381,6 @@ struct CPUArchState { target_ulong cur_pmmask; target_ulong cur_pmbase; - float_status fp_status; - /* Fields from here on are preserved across CPU reset. */ QEMUTimer *stimer; /* Internal timer for S-mode interrupt */ QEMUTimer *vstimer; /* Internal timer for VS-mode interrupt */ From c3ae4b0a526813148d59e30c1de7c842c73fb2dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 7 Dec 2022 18:18:53 +0100 Subject: [PATCH 018/185] target/sparc/sysemu: Remove pointless CONFIG_USER_ONLY guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit caac44a52a ("target/sparc: Make sparc_cpu_tlb_fill sysemu only") restricted mmu_helper.c to system emulation. Checking whether CONFIG_USER_ONLY is defined is now pointless. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20221217172907.8364-9-philmd@linaro.org> --- target/sparc/mmu_helper.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c index 158ec2ae8f..6e7f46f847 100644 --- a/target/sparc/mmu_helper.c +++ b/target/sparc/mmu_helper.c @@ -924,7 +924,6 @@ hwaddr sparc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) return phys_addr; } -#ifndef CONFIG_USER_ONLY G_NORETURN void sparc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, MMUAccessType access_type, int mmu_idx, @@ -942,4 +941,3 @@ G_NORETURN void sparc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, cpu_raise_exception_ra(env, TT_UNALIGNED, retaddr); } -#endif /* !CONFIG_USER_ONLY */ From 9585201a8bfaac69365cfc3ab40fea3428443102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 16 Dec 2022 13:47:05 +0100 Subject: [PATCH 019/185] target/xtensa/cpu: Include missing "memory.h" header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Under system emulation, xtensa_cpu_initfn() calls memory_region_init_io(), itself declared in "exec/memory.h". Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20221217172907.8364-10-philmd@linaro.org> --- target/xtensa/cpu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 2dc8f2d232..acaf8c905f 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -35,6 +35,9 @@ #include "qemu/module.h" #include "migration/vmstate.h" #include "hw/qdev-clock.h" +#ifndef CONFIG_USER_ONLY +#include "exec/memory.h" +#endif static void xtensa_cpu_set_pc(CPUState *cs, vaddr value) From 21917c16613b4f18f57d77b6638e795c25438f5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Jan 2023 16:04:07 +0100 Subject: [PATCH 020/185] target/tricore: Remove unused fields from CPUTriCoreState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove dead code: - unused fields in CPUTriCoreState - (unexisting) tricore_def_t structure - forward declaration of tricore_boot_info structure (declared in "hw/tricore/tricore.h", used once in hw/tricore/tricore_testboard.c). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Bastian Koppelmann Message-Id: <20230117184217.83305-1-philmd@linaro.org> --- target/tricore/cpu.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/target/tricore/cpu.h b/target/tricore/cpu.h index 3b9c533a7c..47d0ffb745 100644 --- a/target/tricore/cpu.h +++ b/target/tricore/cpu.h @@ -25,10 +25,6 @@ #include "qemu/cpu-float.h" #include "tricore-defs.h" -struct tricore_boot_info; - -typedef struct tricore_def_t tricore_def_t; - typedef struct CPUArchState { /* GPR Register */ uint32_t gpr_a[16]; @@ -179,16 +175,9 @@ typedef struct CPUArchState { uint32_t M3CNT; /* Floating Point Registers */ float_status fp_status; - /* QEMU */ - int error_code; - uint32_t hflags; /* CPU State */ /* Internal CPU feature flags. */ uint64_t features; - - const tricore_def_t *cpu_model; - void *irq[8]; - struct QEMUTimer *timer; /* Internal timer */ } CPUTriCoreState; /** From 919a0423d8cbdb99e31a9a9d5c49e6e364ddf160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 19 Dec 2022 14:31:00 +0100 Subject: [PATCH 021/185] qom/object_interfaces: Fix QAPI headers included MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit a0e61807a3 ("qapi: Remove QMP events and commands from user-mode builds") we don't generate the "qapi-commands-qom.h" header in a user-emulation-only build. Commit f375026606 ("qom: Factor out user_creatable_process_cmdline") incorrectly added a dependency on this "qapi/qapi-commands-qom.h" header (the QMP handlers are still defined in qom/qom-qmp-cmds.c). Remove it, and add "qapi/qmp/qobject.h" which declares qobject_unref. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20221220115709.18508-1-philmd@linaro.org> --- qom/object_interfaces.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index f94b6c3193..7d31589b04 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -2,8 +2,8 @@ #include "qemu/cutils.h" #include "qapi/error.h" -#include "qapi/qapi-commands-qom.h" #include "qapi/qapi-visit-qom.h" +#include "qapi/qmp/qobject.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" #include "qapi/qmp/qjson.h" From 5ac034b1c2b2c4ec5508564fe1db37feac38be29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 20 Dec 2022 16:00:41 +0100 Subject: [PATCH 022/185] trace: Do not try to include QMP commands in user emulation binaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QMP is not available on user emulation; there is not monitor. Besides, since commit a0e61807a3 ("qapi: Remove QMP events and commands from user-mode builds") we don't generate the qapi-commands-trace.h header in a user-emulation-only build. Remove the QMP trace commands from qemu-user binaries. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20221220150417.26751-1-philmd@linaro.org> --- trace/meson.build | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/trace/meson.build b/trace/meson.build index d565948b09..8e80be895c 100644 --- a/trace/meson.build +++ b/trace/meson.build @@ -89,4 +89,6 @@ if 'ftrace' in get_option('trace_backends') trace_ss.add(files('ftrace.c')) endif trace_ss.add(files('control.c')) -trace_ss.add(files('qmp.c')) +if have_system or have_tools or have_ga + trace_ss.add(files('qmp.c')) +endif From c32c487a03b94d373ce84691dc5615dfa695f7a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 19 Dec 2022 18:09:29 +0100 Subject: [PATCH 023/185] exec: Remove unused 'qemu/timer.h' timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20221219170806.60580-2-philmd@linaro.org> --- accel/tcg/cpu-exec.c | 1 - accel/tcg/translate-all.c | 1 - include/exec/gen-icount.h | 1 - include/sysemu/cpus.h | 1 - tcg/tcg.c | 1 - 5 files changed, 5 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 5357608b14..29b9bdac38 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -28,7 +28,6 @@ #include "exec/exec-all.h" #include "tcg/tcg.h" #include "qemu/atomic.h" -#include "qemu/timer.h" #include "qemu/rcu.h" #include "exec/log.h" #include "qemu/main-loop.h" diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 9e925c10f3..84f129337b 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -49,7 +49,6 @@ #include "exec/translator.h" #include "qemu/bitmap.h" #include "qemu/qemu-print.h" -#include "qemu/timer.h" #include "qemu/main-loop.h" #include "qemu/cacheinfo.h" #include "exec/log.h" diff --git a/include/exec/gen-icount.h b/include/exec/gen-icount.h index c57204ddad..166170b08e 100644 --- a/include/exec/gen-icount.h +++ b/include/exec/gen-icount.h @@ -2,7 +2,6 @@ #define GEN_ICOUNT_H #include "exec/exec-all.h" -#include "qemu/timer.h" /* Helpers for instruction counting code generation. */ diff --git a/include/sysemu/cpus.h b/include/sysemu/cpus.h index 1bace3379b..0535a4c68a 100644 --- a/include/sysemu/cpus.h +++ b/include/sysemu/cpus.h @@ -1,7 +1,6 @@ #ifndef QEMU_CPUS_H #define QEMU_CPUS_H -#include "qemu/timer.h" #include "sysemu/accel-ops.h" /* register accel-specific operations */ diff --git a/tcg/tcg.c b/tcg/tcg.c index a4a3da6804..9822c65ea8 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -34,7 +34,6 @@ #include "qemu/cutils.h" #include "qemu/host-utils.h" #include "qemu/qemu-print.h" -#include "qemu/timer.h" #include "qemu/cacheflush.h" #include "qemu/cacheinfo.h" From 13f73ca3721df1b0775f41fa2f4ce8e9963cd4d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 20 Dec 2022 10:59:27 +0100 Subject: [PATCH 024/185] tcg: Silent -Wmissing-field-initializers warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Silent when compiling with -Wextra: tcg/i386/tcg-target.opc.h:34:1: warning: missing field 'args_ct' initializer [-Wmissing-field-initializers] DEF(x86_punpckl_vec, 1, 2, 0, IMPLVEC) ^ ../tcg/tcg-common.c:30:66: note: expanded from macro 'DEF' { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags }, ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-Id: <20221220143532.24958-2-philmd@linaro.org> --- tcg/tcg-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/tcg-common.c b/tcg/tcg-common.c index aa0c4f60c9..35e7616ae9 100644 --- a/tcg/tcg-common.c +++ b/tcg/tcg-common.c @@ -27,7 +27,7 @@ TCGOpDef tcg_op_defs[] = { #define DEF(s, oargs, iargs, cargs, flags) \ - { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags }, + { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags, NULL }, #include "tcg/tcg-opc.h" #undef DEF }; From 1398e1b80e4473e880627a878bca524db7420cc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 19 Dec 2022 18:09:33 +0100 Subject: [PATCH 025/185] tcg/tcg-op-gvec: Remove unused "qemu/main-loop.h" header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20221219170806.60580-3-philmd@linaro.org> --- tcg/tcg-op-gvec.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index 079a761b04..aacedd3e15 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -21,7 +21,6 @@ #include "tcg/tcg.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" -#include "qemu/main-loop.h" #include "tcg/tcg-gvec-desc.h" #define MAX_UNROLL 4 From 00c9a5c2c3155e82b2dbf874d84cd2a2061b804c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 19 Dec 2022 18:09:40 +0100 Subject: [PATCH 026/185] accel/tcg: Restrict 'qapi-commands-machine.h' to system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit a0e61807a3 ("qapi: Remove QMP events and commands from user-mode builds") we don't generate the "qapi-commands-machine.h" header in a user-emulation-only build. Rename 'hmp.c' as 'monitor.c' and move the QMP functions from cpu-exec.c (which is always compiled) to monitor.c (which is only compiled when system-emulation is selected). Reviewed-by: Richard Henderson Message-Id: <20221219170806.60580-4-philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- accel/tcg/cpu-exec.c | 88 +---------------------------------- accel/tcg/hmp.c | 14 ------ accel/tcg/internal.h | 3 ++ accel/tcg/meson.build | 2 +- accel/tcg/monitor.c | 106 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 112 insertions(+), 101 deletions(-) delete mode 100644 accel/tcg/hmp.c create mode 100644 accel/tcg/monitor.c diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 29b9bdac38..5c9e30a803 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "qemu/qemu-print.h" #include "qapi/error.h" -#include "qapi/qapi-commands-machine.h" #include "qapi/type-helpers.h" #include "hw/core/tcg-cpu-ops.h" #include "trace.h" @@ -63,8 +62,8 @@ typedef struct SyncClocks { #define MAX_DELAY_PRINT_RATE 2000000000LL #define MAX_NB_PRINTS 100 -static int64_t max_delay; -static int64_t max_advance; +int64_t max_delay; +int64_t max_advance; static void align_clocks(SyncClocks *sc, CPUState *cpu) { @@ -1071,86 +1070,3 @@ void tcg_exec_unrealizefn(CPUState *cpu) tlb_destroy(cpu); g_free_rcu(cpu->tb_jmp_cache, rcu); } - -#ifndef CONFIG_USER_ONLY - -static void dump_drift_info(GString *buf) -{ - if (!icount_enabled()) { - return; - } - - g_string_append_printf(buf, "Host - Guest clock %"PRIi64" ms\n", - (cpu_get_clock() - icount_get()) / SCALE_MS); - if (icount_align_option) { - g_string_append_printf(buf, "Max guest delay %"PRIi64" ms\n", - -max_delay / SCALE_MS); - g_string_append_printf(buf, "Max guest advance %"PRIi64" ms\n", - max_advance / SCALE_MS); - } else { - g_string_append_printf(buf, "Max guest delay NA\n"); - g_string_append_printf(buf, "Max guest advance NA\n"); - } -} - -HumanReadableText *qmp_x_query_jit(Error **errp) -{ - g_autoptr(GString) buf = g_string_new(""); - - if (!tcg_enabled()) { - error_setg(errp, "JIT information is only available with accel=tcg"); - return NULL; - } - - dump_exec_info(buf); - dump_drift_info(buf); - - return human_readable_text_from_str(buf); -} - -HumanReadableText *qmp_x_query_opcount(Error **errp) -{ - g_autoptr(GString) buf = g_string_new(""); - - if (!tcg_enabled()) { - error_setg(errp, "Opcode count information is only available with accel=tcg"); - return NULL; - } - - tcg_dump_op_count(buf); - - return human_readable_text_from_str(buf); -} - -#ifdef CONFIG_PROFILER - -int64_t dev_time; - -HumanReadableText *qmp_x_query_profile(Error **errp) -{ - g_autoptr(GString) buf = g_string_new(""); - static int64_t last_cpu_exec_time; - int64_t cpu_exec_time; - int64_t delta; - - cpu_exec_time = tcg_cpu_exec_time(); - delta = cpu_exec_time - last_cpu_exec_time; - - g_string_append_printf(buf, "async time %" PRId64 " (%0.3f)\n", - dev_time, dev_time / (double)NANOSECONDS_PER_SECOND); - g_string_append_printf(buf, "qemu time %" PRId64 " (%0.3f)\n", - delta, delta / (double)NANOSECONDS_PER_SECOND); - last_cpu_exec_time = cpu_exec_time; - dev_time = 0; - - return human_readable_text_from_str(buf); -} -#else -HumanReadableText *qmp_x_query_profile(Error **errp) -{ - error_setg(errp, "Internal profiler not compiled"); - return NULL; -} -#endif - -#endif /* !CONFIG_USER_ONLY */ diff --git a/accel/tcg/hmp.c b/accel/tcg/hmp.c deleted file mode 100644 index bb67941420..0000000000 --- a/accel/tcg/hmp.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qapi/error.h" -#include "qapi/qapi-commands-machine.h" -#include "exec/exec-all.h" -#include "monitor/monitor.h" - -static void hmp_tcg_register(void) -{ - monitor_register_hmp_info_hrt("jit", qmp_x_query_jit); - monitor_register_hmp_info_hrt("opcount", qmp_x_query_opcount); -} - -type_init(hmp_tcg_register); diff --git a/accel/tcg/internal.h b/accel/tcg/internal.h index 6edff16fb0..130d7fd564 100644 --- a/accel/tcg/internal.h +++ b/accel/tcg/internal.h @@ -64,4 +64,7 @@ static inline target_ulong log_pc(CPUState *cpu, const TranslationBlock *tb) #endif } +extern int64_t max_delay; +extern int64_t max_advance; + #endif /* ACCEL_TCG_INTERNAL_H */ diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 77740b1a0d..aeb20a6ef0 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -18,7 +18,7 @@ specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss) specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files( 'cputlb.c', - 'hmp.c', + 'monitor.c', )) tcg_module_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files( diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c new file mode 100644 index 0000000000..1450e160e9 --- /dev/null +++ b/accel/tcg/monitor.c @@ -0,0 +1,106 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * QEMU TCG monitor + * + * Copyright (c) 2003-2005 Fabrice Bellard + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/type-helpers.h" +#include "qapi/qapi-commands-machine.h" +#include "monitor/monitor.h" +#include "sysemu/cpus.h" +#include "sysemu/cpu-timers.h" +#include "sysemu/tcg.h" +#include "internal.h" + + +static void dump_drift_info(GString *buf) +{ + if (!icount_enabled()) { + return; + } + + g_string_append_printf(buf, "Host - Guest clock %"PRIi64" ms\n", + (cpu_get_clock() - icount_get()) / SCALE_MS); + if (icount_align_option) { + g_string_append_printf(buf, "Max guest delay %"PRIi64" ms\n", + -max_delay / SCALE_MS); + g_string_append_printf(buf, "Max guest advance %"PRIi64" ms\n", + max_advance / SCALE_MS); + } else { + g_string_append_printf(buf, "Max guest delay NA\n"); + g_string_append_printf(buf, "Max guest advance NA\n"); + } +} + +HumanReadableText *qmp_x_query_jit(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + + if (!tcg_enabled()) { + error_setg(errp, "JIT information is only available with accel=tcg"); + return NULL; + } + + dump_exec_info(buf); + dump_drift_info(buf); + + return human_readable_text_from_str(buf); +} + +HumanReadableText *qmp_x_query_opcount(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + + if (!tcg_enabled()) { + error_setg(errp, + "Opcode count information is only available with accel=tcg"); + return NULL; + } + + tcg_dump_op_count(buf); + + return human_readable_text_from_str(buf); +} + +#ifdef CONFIG_PROFILER + +int64_t dev_time; + +HumanReadableText *qmp_x_query_profile(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + static int64_t last_cpu_exec_time; + int64_t cpu_exec_time; + int64_t delta; + + cpu_exec_time = tcg_cpu_exec_time(); + delta = cpu_exec_time - last_cpu_exec_time; + + g_string_append_printf(buf, "async time %" PRId64 " (%0.3f)\n", + dev_time, dev_time / (double)NANOSECONDS_PER_SECOND); + g_string_append_printf(buf, "qemu time %" PRId64 " (%0.3f)\n", + delta, delta / (double)NANOSECONDS_PER_SECOND); + last_cpu_exec_time = cpu_exec_time; + dev_time = 0; + + return human_readable_text_from_str(buf); +} +#else +HumanReadableText *qmp_x_query_profile(Error **errp) +{ + error_setg(errp, "Internal profiler not compiled"); + return NULL; +} +#endif + +static void hmp_tcg_register(void) +{ + monitor_register_hmp_info_hrt("jit", qmp_x_query_jit); + monitor_register_hmp_info_hrt("opcount", qmp_x_query_opcount); +} + +type_init(hmp_tcg_register); From f227c07bbb9569ed12e1559083fe27a797e40c66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 15 Feb 2023 16:13:41 +0100 Subject: [PATCH 027/185] accel/xen: Remove dead code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unused since introduction in commit 04b0de0ee8 ("xen: factor out common functions"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Acked-by: Paul Durrant Message-Id: <20230215153451.30626-1-philmd@linaro.org> --- accel/xen/xen-all.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index 69aa7d018b..c1b697a8bd 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -23,16 +23,6 @@ #include "migration/global_state.h" #include "hw/boards.h" -//#define DEBUG_XEN - -#ifdef DEBUG_XEN -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, "xen: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - bool xen_allowed; xc_interface *xen_xc; From 2459d4209f11bf21ec0c2fe7811c9635337d13f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 20 Dec 2022 13:05:58 +0100 Subject: [PATCH 028/185] accel/kvm: Silent -Wmissing-field-initializers warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Silent when compiling with -Wextra: ../accel/kvm/kvm-all.c:2291:17: warning: missing field 'num' initializer [-Wmissing-field-initializers] { NULL, } ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-Id: <20221220143532.24958-3-philmd@linaro.org> --- accel/kvm/kvm-all.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 79b3d58a9c..86f7523833 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2361,13 +2361,13 @@ static int kvm_init(MachineState *ms) static const char upgrade_note[] = "Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n" "(see http://sourceforge.net/projects/kvm).\n"; - struct { + const struct { const char *name; int num; } num_cpus[] = { { "SMP", ms->smp.cpus }, { "hotpluggable", ms->smp.max_cpus }, - { NULL, } + { /* end of list */ } }, *nc = num_cpus; int soft_vcpus_limit, hard_vcpus_limit; KVMState *s; From 5db2dac46220df5143dfe17dfa3d97957f710360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 16 Dec 2022 13:59:07 +0100 Subject: [PATCH 029/185] sysemu/kvm: Remove CONFIG_USER_ONLY guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User emulation shouldn't really include this header; if included these declarations are guarded by CONFIG_KVM_IS_POSSIBLE. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20221216220738.7355-2-philmd@linaro.org> --- include/sysemu/kvm.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index e9a97eda8c..c8281c07a7 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -471,10 +471,8 @@ uint64_t kvm_arch_get_supported_msr_feature(KVMState *s, uint32_t index); void kvm_set_sigmask_len(KVMState *s, unsigned int sigmask_len); -#if !defined(CONFIG_USER_ONLY) int kvm_physical_memory_addr_from_host(KVMState *s, void *ram_addr, hwaddr *phys_addr); -#endif #endif /* NEED_CPU_H */ From 5b5968c47774c3eca51c6d3db80b9479b015f9bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 19 Dec 2022 18:09:43 +0100 Subject: [PATCH 030/185] replay: Extract core API to 'exec/replay-core.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit replay API is used deeply within TCG common code (common to user and system emulation). Unfortunately "sysemu/replay.h" requires some QAPI headers for few system-specific declarations, example: void replay_input_event(QemuConsole *src, InputEvent *evt); Since commit c2651c0eaa ("qapi/meson: Restrict UI module to system emulation and tools") the QAPI header defining the InputEvent is not generated anymore. To keep it simple, extract the 'core' replay prototypes to a new "exec/replay-core.h" header which we include in the TCG code that doesn't need the rest of the replay API. Reviewed-by: Pavel Dovgalyuk Message-Id: <20221219170806.60580-5-philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + accel/tcg/cpu-exec.c | 2 +- accel/tcg/tcg-all.c | 2 +- accel/tcg/translator.c | 2 +- accel/tcg/user-exec-stub.c | 2 +- cpu.c | 2 +- gdbstub/gdbstub.c | 2 +- hw/core/ptimer.c | 2 +- include/exec/replay-core.h | 80 ++++++++++++++++++++++++++++++++++ include/sysemu/replay.h | 67 ++-------------------------- stubs/replay.c | 2 +- tests/unit/ptimer-test-stubs.c | 2 +- util/guest-random.c | 2 +- 13 files changed, 95 insertions(+), 73 deletions(-) create mode 100644 include/exec/replay-core.h diff --git a/MAINTAINERS b/MAINTAINERS index c6e6549f06..9c01f728e0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3237,6 +3237,7 @@ S: Supported F: replay/* F: block/blkreplay.c F: net/filter-replay.c +F: include/exec/replay-core.h F: include/sysemu/replay.h F: docs/devel/replay.rst F: docs/system/replay.rst diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 5c9e30a803..ef557e57aa 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -36,7 +36,7 @@ #include "sysemu/cpus.h" #include "exec/cpu-all.h" #include "sysemu/cpu-timers.h" -#include "sysemu/replay.h" +#include "exec/replay-core.h" #include "sysemu/tcg.h" #include "exec/helper-proto.h" #include "tb-jmp-cache.h" diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index 30b503fb22..5dab1ae9dd 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "sysemu/tcg.h" -#include "sysemu/replay.h" +#include "exec/replay-core.h" #include "sysemu/cpu-timers.h" #include "tcg/tcg.h" #include "qapi/error.h" diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 1cf404ced0..15d11fabc5 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -16,7 +16,7 @@ #include "exec/log.h" #include "exec/translator.h" #include "exec/plugin-gen.h" -#include "sysemu/replay.h" +#include "exec/replay-core.h" /* Pairs with tcg_clear_temp_count. To be called by #TranslatorOps.{translate_insn,tb_stop} if diff --git a/accel/tcg/user-exec-stub.c b/accel/tcg/user-exec-stub.c index 968cd3ca60..874e1f1a20 100644 --- a/accel/tcg/user-exec-stub.c +++ b/accel/tcg/user-exec-stub.c @@ -1,6 +1,6 @@ #include "qemu/osdep.h" #include "hw/core/cpu.h" -#include "sysemu/replay.h" +#include "exec/replay-core.h" bool enable_cpu_pm = false; diff --git a/cpu.c b/cpu.c index 44df16231f..2e9f931249 100644 --- a/cpu.c +++ b/cpu.c @@ -33,7 +33,7 @@ #endif #include "sysemu/tcg.h" #include "sysemu/kvm.h" -#include "sysemu/replay.h" +#include "exec/replay-core.h" #include "exec/cpu-common.h" #include "exec/exec-all.h" #include "exec/translate-all.h" diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index c3fbc31123..fb9c49e0fd 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -48,7 +48,7 @@ #include "sysemu/runstate.h" #include "semihosting/semihost.h" #include "exec/exec-all.h" -#include "sysemu/replay.h" +#include "exec/replay-core.h" #include "internals.h" diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c index eb5ba1aff7..e03165febf 100644 --- a/hw/core/ptimer.c +++ b/hw/core/ptimer.c @@ -10,7 +10,7 @@ #include "hw/ptimer.h" #include "migration/vmstate.h" #include "qemu/host-utils.h" -#include "sysemu/replay.h" +#include "exec/replay-core.h" #include "sysemu/cpu-timers.h" #include "sysemu/qtest.h" #include "block/aio.h" diff --git a/include/exec/replay-core.h b/include/exec/replay-core.h new file mode 100644 index 0000000000..244c77acce --- /dev/null +++ b/include/exec/replay-core.h @@ -0,0 +1,80 @@ +/* + * QEMU replay core API + * + * Copyright (c) 2010-2015 Institute for System Programming + * of the Russian Academy of Sciences. + * + * 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 EXEC_REPLAY_H +#define EXEC_REPLAY_H + +#include "qapi/qapi-types-replay.h" + +extern ReplayMode replay_mode; + +/* Replay process control functions */ + +/* Enables recording or saving event log with specified parameters */ +void replay_configure(struct QemuOpts *opts); +/* Initializes timers used for snapshotting and enables events recording */ +void replay_start(void); +/* Closes replay log file and frees other resources. */ +void replay_finish(void); +/* Adds replay blocker with the specified error description */ +void replay_add_blocker(const char *feature); +/* Returns name of the replay log file */ +const char *replay_get_filename(void); + +/* + * Start making one step in backward direction. + * Used by gdbstub for backwards debugging. + * Returns true on success. + */ +bool replay_reverse_step(void); +/* + * Start searching the last breakpoint/watchpoint. + * Used by gdbstub for backwards debugging. + * Returns true if the process successfully started. + */ +bool replay_reverse_continue(void); +/* + * Returns true if replay module is processing + * reverse_continue or reverse_step request + */ +bool replay_running_debug(void); +/* Called in reverse debugging mode to collect breakpoint information */ +void replay_breakpoint(void); +/* Called when gdb is attached to gdbstub */ +void replay_gdb_attached(void); + +/* Interrupts and exceptions */ + +/* Called by exception handler to write or read exception processing events */ +bool replay_exception(void); +/* + * Used to determine that exception is pending. + * Does not proceed to the next event in the log. + */ +bool replay_has_exception(void); +/* + * Called by interrupt handlers to write or read interrupt processing events. + * Returns true if interrupt should be processed. + */ +bool replay_interrupt(void); +/* + * Tries to read interrupt event from the file. + * Returns true, when interrupt request is pending. + */ +bool replay_has_interrupt(void); + +/* Processing data from random generators */ + +/* Saves the values from the random number generator */ +void replay_save_random(int ret, void *buf, size_t len); +/* Loads the saved values for the random number generator */ +int replay_read_random(void *buf, size_t len); + +#endif diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h index 6e5ab09f71..08aae5869f 100644 --- a/include/sysemu/replay.h +++ b/include/sysemu/replay.h @@ -1,8 +1,8 @@ -#ifndef REPLAY_H -#define REPLAY_H +#ifndef SYSEMU_REPLAY_H +#define SYSEMU_REPLAY_H /* - * replay.h + * QEMU replay (system interface) * * Copyright (c) 2010-2015 Institute for System Programming * of the Russian Academy of Sciences. @@ -12,9 +12,9 @@ * */ +#include "exec/replay-core.h" #include "qapi/qapi-types-misc.h" #include "qapi/qapi-types-run-state.h" -#include "qapi/qapi-types-replay.h" #include "qapi/qapi-types-ui.h" #include "block/aio.h" @@ -45,8 +45,6 @@ typedef enum ReplayCheckpoint ReplayCheckpoint; typedef struct ReplayNetState ReplayNetState; -extern ReplayMode replay_mode; - /* Name of the initial VM snapshot */ extern char *replay_snapshot; @@ -63,40 +61,6 @@ extern char *replay_snapshot; void replay_mutex_lock(void); void replay_mutex_unlock(void); -/* Replay process control functions */ - -/*! Enables recording or saving event log with specified parameters */ -void replay_configure(struct QemuOpts *opts); -/*! Initializes timers used for snapshotting and enables events recording */ -void replay_start(void); -/*! Closes replay log file and frees other resources. */ -void replay_finish(void); -/*! Adds replay blocker with the specified error description */ -void replay_add_blocker(const char *feature); -/* Returns name of the replay log file */ -const char *replay_get_filename(void); -/* - * Start making one step in backward direction. - * Used by gdbstub for backwards debugging. - * Returns true on success. - */ -bool replay_reverse_step(void); -/* - * Start searching the last breakpoint/watchpoint. - * Used by gdbstub for backwards debugging. - * Returns true if the process successfully started. - */ -bool replay_reverse_continue(void); -/* - * Returns true if replay module is processing - * reverse_continue or reverse_step request - */ -bool replay_running_debug(void); -/* Called in reverse debugging mode to collect breakpoint information */ -void replay_breakpoint(void); -/* Called when gdb is attached to gdbstub */ -void replay_gdb_attached(void); - /* Processing the instructions */ /*! Returns number of executed instructions. */ @@ -106,22 +70,6 @@ int replay_get_instructions(void); /*! Updates instructions counter in replay mode. */ void replay_account_executed_instructions(void); -/* Interrupts and exceptions */ - -/*! Called by exception handler to write or read - exception processing events. */ -bool replay_exception(void); -/*! Used to determine that exception is pending. - Does not proceed to the next event in the log. */ -bool replay_has_exception(void); -/*! Called by interrupt handlers to write or read - interrupt processing events. - \return true if interrupt should be processed */ -bool replay_interrupt(void); -/*! Tries to read interrupt event from the file. - Returns true, when interrupt request is pending */ -bool replay_has_interrupt(void); - /* Processing clocks and other time sources */ /*! Save the specified clock */ @@ -143,13 +91,6 @@ int64_t replay_read_clock(ReplayClockKind kind, int64_t raw_icount); ? replay_save_clock((clock), (value), icount_get_raw_locked()) \ : (value)) -/* Processing data from random generators */ - -/* Saves the values from the random number generator */ -void replay_save_random(int ret, void *buf, size_t len); -/* Loads the saved values for the random number generator */ -int replay_read_random(void *buf, size_t len); - /* Events */ /*! Called when qemu shutdown is requested. */ diff --git a/stubs/replay.c b/stubs/replay.c index 9d5b4be339..42c92e4acb 100644 --- a/stubs/replay.c +++ b/stubs/replay.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/replay.h" +#include "exec/replay-core.h" ReplayMode replay_mode; diff --git a/tests/unit/ptimer-test-stubs.c b/tests/unit/ptimer-test-stubs.c index f5e75a96b6..f2bfcede93 100644 --- a/tests/unit/ptimer-test-stubs.c +++ b/tests/unit/ptimer-test-stubs.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" -#include "sysemu/replay.h" +#include "exec/replay-core.h" #include "migration/vmstate.h" #include "ptimer-test.h" diff --git a/util/guest-random.c b/util/guest-random.c index 23643f86cc..a24d27624c 100644 --- a/util/guest-random.c +++ b/util/guest-random.c @@ -14,7 +14,7 @@ #include "qapi/error.h" #include "qemu/guest-random.h" #include "crypto/random.h" -#include "sysemu/replay.h" +#include "exec/replay-core.h" static __thread GRand *thread_rand; From 62b6323d1cfca6fbe9f875cdb8b96f5cac4658c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 19 Dec 2022 18:09:46 +0100 Subject: [PATCH 031/185] tests/unit: Restrict machine-smp.c test to system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20221219170806.60580-6-philmd@linaro.org> --- tests/unit/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/meson.build b/tests/unit/meson.build index ffa444f432..51f453e6c4 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -46,7 +46,6 @@ tests = { 'test-uuid': [], 'ptimer-test': ['ptimer-test-stubs.c', meson.project_source_root() / 'hw/core/ptimer.c'], 'test-qapi-util': [], - 'test-smp-parse': [qom, meson.project_source_root() / 'hw/core/machine-smp.c'], 'test-interval-tree': [], } @@ -134,6 +133,7 @@ if have_system 'test-util-sockets': ['socket-helpers.c'], 'test-base64': [], 'test-bufferiszero': [], + 'test-smp-parse': [qom, meson.project_source_root() / 'hw/core/machine-smp.c'], 'test-vmstate': [migration, io], 'test-yank': ['socket-helpers.c', qom, io, chardev] } From feea81d8de5c2c5374ab77ab963fa8c045b9fa4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 20 Dec 2022 11:02:54 +0100 Subject: [PATCH 032/185] softmmu: Silent -Wmissing-field-initializers warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Silent when compiling with -Wextra: ../softmmu/vl.c:886:12: warning: missing field 'flags' initializer [-Wmissing-field-initializers] { NULL }, ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-Id: <20221220143532.24958-4-philmd@linaro.org> --- softmmu/vl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/softmmu/vl.c b/softmmu/vl.c index f29e4c4dc3..cebe8d9452 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -883,7 +883,7 @@ static const QEMUOption qemu_options[] = { #define ARCHHEADING(text, arch_mask) #include "qemu-options.def" - { NULL }, + { /* end of list */ } }; typedef struct VGAInterfaceInfo { From 2609ec2868e6c286e755a73b4504714a0296aba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 9 Dec 2022 14:47:52 +0100 Subject: [PATCH 033/185] softmmu: Extract watchpoint API from physmem.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The watchpoint API is specific to TCG system emulation. Move it to a new compile unit. The inlined stubs are used for user-mode and non-TCG accelerators. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20221209141254.68662-1-philmd@linaro.org> --- MAINTAINERS | 1 + include/hw/core/cpu.h | 2 +- softmmu/meson.build | 3 +- softmmu/physmem.c | 191 ------------------------------------ softmmu/watchpoint.c | 220 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 224 insertions(+), 193 deletions(-) create mode 100644 softmmu/watchpoint.c diff --git a/MAINTAINERS b/MAINTAINERS index 9c01f728e0..c923cf624a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -123,6 +123,7 @@ M: Richard Henderson R: Paolo Bonzini S: Maintained F: softmmu/cpus.c +F: softmmu/watchpoint.c F: cpus-common.c F: page-vary.c F: page-vary-common.c diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 671f041bec..5b2792d937 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -946,7 +946,7 @@ static inline bool cpu_breakpoint_test(CPUState *cpu, vaddr pc, int mask) return false; } -#ifdef CONFIG_USER_ONLY +#if !defined(CONFIG_TCG) || defined(CONFIG_USER_ONLY) static inline int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, int flags, CPUWatchpoint **watchpoint) { diff --git a/softmmu/meson.build b/softmmu/meson.build index 1828db149c..0180577517 100644 --- a/softmmu/meson.build +++ b/softmmu/meson.build @@ -8,7 +8,8 @@ specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files( )]) specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: [files( - 'icount.c' + 'icount.c', + 'watchpoint.c', )]) softmmu_ss.add(files( diff --git a/softmmu/physmem.c b/softmmu/physmem.c index cb998cdf23..df54b917a9 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -781,197 +781,6 @@ AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx) return cpu->cpu_ases[asidx].as; } -/* Add a watchpoint. */ -int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, - int flags, CPUWatchpoint **watchpoint) -{ - CPUWatchpoint *wp; - vaddr in_page; - - /* forbid ranges which are empty or run off the end of the address space */ - if (len == 0 || (addr + len - 1) < addr) { - error_report("tried to set invalid watchpoint at %" - VADDR_PRIx ", len=%" VADDR_PRIu, addr, len); - return -EINVAL; - } - wp = g_malloc(sizeof(*wp)); - - wp->vaddr = addr; - wp->len = len; - wp->flags = flags; - - /* keep all GDB-injected watchpoints in front */ - if (flags & BP_GDB) { - QTAILQ_INSERT_HEAD(&cpu->watchpoints, wp, entry); - } else { - QTAILQ_INSERT_TAIL(&cpu->watchpoints, wp, entry); - } - - in_page = -(addr | TARGET_PAGE_MASK); - if (len <= in_page) { - tlb_flush_page(cpu, addr); - } else { - tlb_flush(cpu); - } - - if (watchpoint) - *watchpoint = wp; - return 0; -} - -/* Remove a specific watchpoint. */ -int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, vaddr len, - int flags) -{ - CPUWatchpoint *wp; - - QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { - if (addr == wp->vaddr && len == wp->len - && flags == (wp->flags & ~BP_WATCHPOINT_HIT)) { - cpu_watchpoint_remove_by_ref(cpu, wp); - return 0; - } - } - return -ENOENT; -} - -/* Remove a specific watchpoint by reference. */ -void cpu_watchpoint_remove_by_ref(CPUState *cpu, CPUWatchpoint *watchpoint) -{ - QTAILQ_REMOVE(&cpu->watchpoints, watchpoint, entry); - - tlb_flush_page(cpu, watchpoint->vaddr); - - g_free(watchpoint); -} - -/* Remove all matching watchpoints. */ -void cpu_watchpoint_remove_all(CPUState *cpu, int mask) -{ - CPUWatchpoint *wp, *next; - - QTAILQ_FOREACH_SAFE(wp, &cpu->watchpoints, entry, next) { - if (wp->flags & mask) { - cpu_watchpoint_remove_by_ref(cpu, wp); - } - } -} - -#ifdef CONFIG_TCG -/* Return true if this watchpoint address matches the specified - * access (ie the address range covered by the watchpoint overlaps - * partially or completely with the address range covered by the - * access). - */ -static inline bool watchpoint_address_matches(CPUWatchpoint *wp, - vaddr addr, vaddr len) -{ - /* We know the lengths are non-zero, but a little caution is - * required to avoid errors in the case where the range ends - * exactly at the top of the address space and so addr + len - * wraps round to zero. - */ - vaddr wpend = wp->vaddr + wp->len - 1; - vaddr addrend = addr + len - 1; - - return !(addr > wpend || wp->vaddr > addrend); -} - -/* Return flags for watchpoints that match addr + prot. */ -int cpu_watchpoint_address_matches(CPUState *cpu, vaddr addr, vaddr len) -{ - CPUWatchpoint *wp; - int ret = 0; - - QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { - if (watchpoint_address_matches(wp, addr, len)) { - ret |= wp->flags; - } - } - return ret; -} - -/* Generate a debug exception if a watchpoint has been hit. */ -void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, - MemTxAttrs attrs, int flags, uintptr_t ra) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - CPUWatchpoint *wp; - - assert(tcg_enabled()); - if (cpu->watchpoint_hit) { - /* - * We re-entered the check after replacing the TB. - * Now raise the debug interrupt so that it will - * trigger after the current instruction. - */ - qemu_mutex_lock_iothread(); - cpu_interrupt(cpu, CPU_INTERRUPT_DEBUG); - qemu_mutex_unlock_iothread(); - return; - } - - if (cc->tcg_ops->adjust_watchpoint_address) { - /* this is currently used only by ARM BE32 */ - addr = cc->tcg_ops->adjust_watchpoint_address(cpu, addr, len); - } - QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { - if (watchpoint_address_matches(wp, addr, len) - && (wp->flags & flags)) { - if (replay_running_debug()) { - /* - * replay_breakpoint reads icount. - * Force recompile to succeed, because icount may - * be read only at the end of the block. - */ - if (!cpu->can_do_io) { - /* Force execution of one insn next time. */ - cpu->cflags_next_tb = 1 | CF_LAST_IO | CF_NOIRQ | curr_cflags(cpu); - cpu_loop_exit_restore(cpu, ra); - } - /* - * Don't process the watchpoints when we are - * in a reverse debugging operation. - */ - replay_breakpoint(); - return; - } - if (flags == BP_MEM_READ) { - wp->flags |= BP_WATCHPOINT_HIT_READ; - } else { - wp->flags |= BP_WATCHPOINT_HIT_WRITE; - } - wp->hitaddr = MAX(addr, wp->vaddr); - wp->hitattrs = attrs; - - if (wp->flags & BP_CPU && cc->tcg_ops->debug_check_watchpoint && - !cc->tcg_ops->debug_check_watchpoint(cpu, wp)) { - wp->flags &= ~BP_WATCHPOINT_HIT; - continue; - } - cpu->watchpoint_hit = wp; - - mmap_lock(); - /* This call also restores vCPU state */ - tb_check_watchpoint(cpu, ra); - if (wp->flags & BP_STOP_BEFORE_ACCESS) { - cpu->exception_index = EXCP_DEBUG; - mmap_unlock(); - cpu_loop_exit(cpu); - } else { - /* Force execution of one insn next time. */ - cpu->cflags_next_tb = 1 | CF_LAST_IO | CF_NOIRQ | curr_cflags(cpu); - mmap_unlock(); - cpu_loop_exit_noexc(cpu); - } - } else { - wp->flags &= ~BP_WATCHPOINT_HIT; - } - } -} - -#endif /* CONFIG_TCG */ - /* Called from RCU critical section */ static RAMBlock *qemu_get_ram_block(ram_addr_t addr) { diff --git a/softmmu/watchpoint.c b/softmmu/watchpoint.c new file mode 100644 index 0000000000..279129dd1c --- /dev/null +++ b/softmmu/watchpoint.c @@ -0,0 +1,220 @@ +/* + * CPU watchpoints + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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, see . + */ + +#include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "exec/exec-all.h" +#include "exec/translate-all.h" +#include "sysemu/tcg.h" +#include "sysemu/replay.h" +#include "hw/core/tcg-cpu-ops.h" +#include "hw/core/cpu.h" + +/* Add a watchpoint. */ +int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, + int flags, CPUWatchpoint **watchpoint) +{ + CPUWatchpoint *wp; + vaddr in_page; + + /* forbid ranges which are empty or run off the end of the address space */ + if (len == 0 || (addr + len - 1) < addr) { + error_report("tried to set invalid watchpoint at %" + VADDR_PRIx ", len=%" VADDR_PRIu, addr, len); + return -EINVAL; + } + wp = g_malloc(sizeof(*wp)); + + wp->vaddr = addr; + wp->len = len; + wp->flags = flags; + + /* keep all GDB-injected watchpoints in front */ + if (flags & BP_GDB) { + QTAILQ_INSERT_HEAD(&cpu->watchpoints, wp, entry); + } else { + QTAILQ_INSERT_TAIL(&cpu->watchpoints, wp, entry); + } + + in_page = -(addr | TARGET_PAGE_MASK); + if (len <= in_page) { + tlb_flush_page(cpu, addr); + } else { + tlb_flush(cpu); + } + + if (watchpoint) { + *watchpoint = wp; + } + return 0; +} + +/* Remove a specific watchpoint. */ +int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, vaddr len, + int flags) +{ + CPUWatchpoint *wp; + + QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { + if (addr == wp->vaddr && len == wp->len + && flags == (wp->flags & ~BP_WATCHPOINT_HIT)) { + cpu_watchpoint_remove_by_ref(cpu, wp); + return 0; + } + } + return -ENOENT; +} + +/* Remove a specific watchpoint by reference. */ +void cpu_watchpoint_remove_by_ref(CPUState *cpu, CPUWatchpoint *watchpoint) +{ + QTAILQ_REMOVE(&cpu->watchpoints, watchpoint, entry); + + tlb_flush_page(cpu, watchpoint->vaddr); + + g_free(watchpoint); +} + +/* Remove all matching watchpoints. */ +void cpu_watchpoint_remove_all(CPUState *cpu, int mask) +{ + CPUWatchpoint *wp, *next; + + QTAILQ_FOREACH_SAFE(wp, &cpu->watchpoints, entry, next) { + if (wp->flags & mask) { + cpu_watchpoint_remove_by_ref(cpu, wp); + } + } +} + +/* + * Return true if this watchpoint address matches the specified + * access (ie the address range covered by the watchpoint overlaps + * partially or completely with the address range covered by the + * access). + */ +static inline bool watchpoint_address_matches(CPUWatchpoint *wp, + vaddr addr, vaddr len) +{ + /* + * We know the lengths are non-zero, but a little caution is + * required to avoid errors in the case where the range ends + * exactly at the top of the address space and so addr + len + * wraps round to zero. + */ + vaddr wpend = wp->vaddr + wp->len - 1; + vaddr addrend = addr + len - 1; + + return !(addr > wpend || wp->vaddr > addrend); +} + +/* Return flags for watchpoints that match addr + prot. */ +int cpu_watchpoint_address_matches(CPUState *cpu, vaddr addr, vaddr len) +{ + CPUWatchpoint *wp; + int ret = 0; + + QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { + if (watchpoint_address_matches(wp, addr, len)) { + ret |= wp->flags; + } + } + return ret; +} + +/* Generate a debug exception if a watchpoint has been hit. */ +void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, + MemTxAttrs attrs, int flags, uintptr_t ra) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + CPUWatchpoint *wp; + + assert(tcg_enabled()); + if (cpu->watchpoint_hit) { + /* + * We re-entered the check after replacing the TB. + * Now raise the debug interrupt so that it will + * trigger after the current instruction. + */ + qemu_mutex_lock_iothread(); + cpu_interrupt(cpu, CPU_INTERRUPT_DEBUG); + qemu_mutex_unlock_iothread(); + return; + } + + if (cc->tcg_ops->adjust_watchpoint_address) { + /* this is currently used only by ARM BE32 */ + addr = cc->tcg_ops->adjust_watchpoint_address(cpu, addr, len); + } + QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { + if (watchpoint_address_matches(wp, addr, len) + && (wp->flags & flags)) { + if (replay_running_debug()) { + /* + * replay_breakpoint reads icount. + * Force recompile to succeed, because icount may + * be read only at the end of the block. + */ + if (!cpu->can_do_io) { + /* Force execution of one insn next time. */ + cpu->cflags_next_tb = 1 | CF_LAST_IO | CF_NOIRQ + | curr_cflags(cpu); + cpu_loop_exit_restore(cpu, ra); + } + /* + * Don't process the watchpoints when we are + * in a reverse debugging operation. + */ + replay_breakpoint(); + return; + } + if (flags == BP_MEM_READ) { + wp->flags |= BP_WATCHPOINT_HIT_READ; + } else { + wp->flags |= BP_WATCHPOINT_HIT_WRITE; + } + wp->hitaddr = MAX(addr, wp->vaddr); + wp->hitattrs = attrs; + + if (wp->flags & BP_CPU && cc->tcg_ops->debug_check_watchpoint && + !cc->tcg_ops->debug_check_watchpoint(cpu, wp)) { + wp->flags &= ~BP_WATCHPOINT_HIT; + continue; + } + cpu->watchpoint_hit = wp; + + mmap_lock(); + /* This call also restores vCPU state */ + tb_check_watchpoint(cpu, ra); + if (wp->flags & BP_STOP_BEFORE_ACCESS) { + cpu->exception_index = EXCP_DEBUG; + mmap_unlock(); + cpu_loop_exit(cpu); + } else { + /* Force execution of one insn next time. */ + cpu->cflags_next_tb = 1 | CF_LAST_IO | CF_NOIRQ + | curr_cflags(cpu); + mmap_unlock(); + cpu_loop_exit_noexc(cpu); + } + } else { + wp->flags &= ~BP_WATCHPOINT_HIT; + } + } +} From 38ce1ebf578f3c8cdfb150f397a43d4a65ae74d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 17 Feb 2023 09:01:36 +0100 Subject: [PATCH 034/185] qemu/typedefs: Sort in case-insensitive alphabetical order (again) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following the recommendation added in commit a98c370c46 ("typedefs: (Re-)sort entries alphabetically"), and similarly to commit 64baadc272 ("Sort include/qemu/typedefs.h"), sort again the type definitions (in case-insensitive alphabetical order, using 'sort --ignore-case'). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230217141832.24777-2-philmd@linaro.org> --- include/qemu/typedefs.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index c7c8a85315..df4b55ac65 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -49,6 +49,7 @@ typedef struct DeviceState DeviceState; typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot; typedef struct DisplayChangeListener DisplayChangeListener; typedef struct DriveInfo DriveInfo; +typedef struct DumpState DumpState; typedef struct Error Error; typedef struct EventNotifier EventNotifier; typedef struct FlatView FlatView; @@ -56,6 +57,7 @@ typedef struct FWCfgEntry FWCfgEntry; typedef struct FWCfgIoState FWCfgIoState; typedef struct FWCfgMemState FWCfgMemState; typedef struct FWCfgState FWCfgState; +typedef struct GraphicHwOps GraphicHwOps; typedef struct HostMemoryBackend HostMemoryBackend; typedef struct I2CBus I2CBus; typedef struct I2SCodec I2SCodec; @@ -90,10 +92,10 @@ typedef struct PCIDevice PCIDevice; typedef struct PCIEAERErr PCIEAERErr; typedef struct PCIEAERLog PCIEAERLog; typedef struct PCIEAERMsg PCIEAERMsg; -typedef struct PCIESriovPF PCIESriovPF; -typedef struct PCIESriovVF PCIESriovVF; typedef struct PCIEPort PCIEPort; typedef struct PCIESlot PCIESlot; +typedef struct PCIESriovPF PCIESriovPF; +typedef struct PCIESriovVF PCIESriovVF; typedef struct PCIExpressDevice PCIExpressDevice; typedef struct PCIExpressHost PCIExpressHost; typedef struct PCIHostDeviceAddress PCIHostDeviceAddress; @@ -106,6 +108,7 @@ typedef struct QBool QBool; typedef struct QDict QDict; typedef struct QEMUBH QEMUBH; typedef struct QemuConsole QemuConsole; +typedef struct QEMUCursor QEMUCursor; typedef struct QEMUFile QEMUFile; typedef struct QemuLockable QemuLockable; typedef struct QemuMutex QemuMutex; @@ -132,9 +135,6 @@ typedef struct VirtIODevice VirtIODevice; typedef struct Visitor Visitor; typedef struct VMChangeStateEntry VMChangeStateEntry; typedef struct VMStateDescription VMStateDescription; -typedef struct DumpState DumpState; -typedef struct GraphicHwOps GraphicHwOps; -typedef struct QEMUCursor QEMUCursor; /* * Pointer types From 12a51d983de118cf2258fe238b22fabedb4a5ffc Mon Sep 17 00:00:00 2001 From: Mauro Matteo Cascella Date: Thu, 22 Dec 2022 18:29:15 +0100 Subject: [PATCH 035/185] hw/nubus/nubus-device: Fix memory leak in nubus_device_realize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Local variable "name" is allocated through strdup_printf and should be freed with g_free() to avoid memory leak. Fixes: 3616f424 ("nubus-device: add romfile property for loading declaration ROMs") Signed-off-by: Mauro Matteo Cascella Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20221222172915.671597-1-mcascell@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/nubus/nubus-device.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/nubus/nubus-device.c b/hw/nubus/nubus-device.c index 0f1852f671..49008e4938 100644 --- a/hw/nubus/nubus-device.c +++ b/hw/nubus/nubus-device.c @@ -80,6 +80,7 @@ static void nubus_device_realize(DeviceState *dev, Error **errp) &error_abort); ret = load_image_mr(path, &nd->decl_rom); g_free(path); + g_free(name); if (ret < 0) { error_setg(errp, "could not load romfile \"%s\"", nd->romfile); return; From 2d2f2507c65bbb79bb2f0157db3f73ec114d9b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 12 Feb 2023 23:26:59 +0100 Subject: [PATCH 036/185] hw/qdev: Constify DeviceState* argument of qdev_get_parent_bus() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The structure is accessed read-only by qdev_get_parent_bus(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230212224730.51438-2-philmd@linaro.org> --- hw/core/qdev.c | 2 +- include/hw/qdev-core.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index d759c4602c..43d863b0c5 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -330,7 +330,7 @@ bool qdev_machine_modified(void) return qdev_hot_added || qdev_hot_removed; } -BusState *qdev_get_parent_bus(DeviceState *dev) +BusState *qdev_get_parent_bus(const DeviceState *dev) { return dev->parent_bus; } diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 35fddb19a6..f5b3b2f89a 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -715,7 +715,7 @@ static inline void qdev_init_gpio_in_named(DeviceState *dev, void qdev_pass_gpios(DeviceState *dev, DeviceState *container, const char *name); -BusState *qdev_get_parent_bus(DeviceState *dev); +BusState *qdev_get_parent_bus(const DeviceState *dev); /*** BUS API. ***/ From a371975ef6f634a2322b3dd19682dc0d12d4c77a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 16 Feb 2023 10:09:48 +0100 Subject: [PATCH 037/185] hw/cpu: Extend CPUState::cluster_index documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copy part of the description of commit f7b78602fd ("accel/tcg: Add cluster number to TCG TB hash") in tcg_cpu_init_cflags(), improving a bit CPUState::cluster_index documentation. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-Id: <20230216142338.82982-2-philmd@linaro.org> --- accel/tcg/tcg-accel-ops.c | 13 ++++++++++++- include/hw/core/cpu.h | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index d9228fd403..aeb1cbaf65 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -44,7 +44,18 @@ void tcg_cpu_init_cflags(CPUState *cpu, bool parallel) { - uint32_t cflags = cpu->cluster_index << CF_CLUSTER_SHIFT; + uint32_t cflags; + + /* + * Include the cluster number in the hash we use to look up TBs. + * This is important because a TB that is valid for one cluster at + * a given physical address and set of CPU flags is not necessarily + * valid for another: + * the two clusters may have different views of physical memory, or + * may have different CPU features (eg FPU present or absent). + */ + cflags = cpu->cluster_index << CF_CLUSTER_SHIFT; + cflags |= parallel ? CF_PARALLEL : 0; cflags |= icount_enabled() ? CF_USE_ICOUNT : 0; cpu->tcg_cflags = cflags; diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 5b2792d937..fb5d9667ca 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -272,6 +272,8 @@ struct qemu_work_item; * to a cluster this will be UNASSIGNED_CLUSTER_INDEX; otherwise it will * be the same as the cluster-id property of the CPU object's TYPE_CPU_CLUSTER * QOM parent. + * Under TCG this value is propagated to @tcg_cflags. + * See TranslationBlock::TCG CF_CLUSTER_MASK. * @tcg_cflags: Pre-computed cflags for this cpu. * @nr_cores: Number of cores within this CPU package. * @nr_threads: Number of threads within this CPU. From 892afa04e6d5fdeedaef060f77e05e897964b143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 16 Dec 2022 22:25:23 +0100 Subject: [PATCH 038/185] hw/i386/x86: Reduce init_topo_info() scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is not used anywhere outside this file, so we can delete the prototype from include/hw/i386/x86.h and make the function "static void". This fixes when building with -Wall and using Clang ("Apple clang version 14.0.0 (clang-1400.0.29.202)"): ../hw/i386/x86.c:70:24: error: static function 'MACHINE' is used in an inline function with external linkage [-Werror,-Wstatic-in-inline] MachineState *ms = MACHINE(x86ms); ^ include/hw/i386/x86.h:101:1: note: use 'static' to give inline function 'init_topo_info' internal linkage void init_topo_info(X86CPUTopoInfo *topo_info, const X86MachineState *x86ms); ^ static include/hw/boards.h:24:49: note: 'MACHINE' declared here OBJECT_DECLARE_TYPE(MachineState, MachineClass, MACHINE) ^ Reported-by: Stefan Weil Suggested-by: Peter Maydell Reviewed-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20221216220158.6317-6-philmd@linaro.org> --- hw/i386/x86.c | 2 +- include/hw/i386/x86.h | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 48be7a1c23..5f28dc8390 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -64,7 +64,7 @@ /* Physical Address of PVH entry point read from kernel ELF NOTE */ static size_t pvh_start_addr; -inline void init_topo_info(X86CPUTopoInfo *topo_info, +static void init_topo_info(X86CPUTopoInfo *topo_info, const X86MachineState *x86ms) { MachineState *ms = MACHINE(x86ms); diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index 62fa5774f8..5d3047a1d1 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -20,7 +20,6 @@ #include "exec/hwaddr.h" #include "qemu/notify.h" -#include "hw/i386/topology.h" #include "hw/boards.h" #include "hw/nmi.h" #include "hw/isa/isa.h" @@ -98,8 +97,6 @@ struct X86MachineState { #define TYPE_X86_MACHINE MACHINE_TYPE_NAME("x86") OBJECT_DECLARE_TYPE(X86MachineState, X86MachineClass, X86_MACHINE) -void init_topo_info(X86CPUTopoInfo *topo_info, const X86MachineState *x86ms); - uint32_t x86_cpu_apic_id_from_index(X86MachineState *pcms, unsigned int cpu_index); From c288b6869a033986894607c8412bf23ec93f4d87 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 13 Feb 2023 18:30:22 +0100 Subject: [PATCH 039/185] hw/i386/ich9: Rename Q35_MASK to ICH9_MASK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Q35_MASK macro is already defined by TYPE_Q35_HOST_DEVICE, so let TYPE_ICH9_LPC_DEVICE have its own one to prevent potential name clash. Signed-off-by: Bernhard Beschow Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230213173033.98762-2-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/i386/ich9.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h index 222781e8b9..36e0ccd16a 100644 --- a/include/hw/i386/ich9.h +++ b/include/hw/i386/ich9.h @@ -75,7 +75,7 @@ struct ICH9LPCState { qemu_irq gsi[GSI_NUM_PINS]; }; -#define Q35_MASK(bit, ms_bit, ls_bit) \ +#define ICH9_MASK(bit, ms_bit, ls_bit) \ ((uint##bit##_t)(((1ULL << ((ms_bit) + 1)) - 1) & ~((1ULL << ls_bit) - 1))) /* ICH9: Chipset Configuration Registers */ @@ -137,13 +137,13 @@ struct ICH9LPCState { #define ICH9_LPC_NB_PIRQS 8 /* PCI A-H */ #define ICH9_LPC_PMBASE 0x40 -#define ICH9_LPC_PMBASE_BASE_ADDRESS_MASK Q35_MASK(32, 15, 7) +#define ICH9_LPC_PMBASE_BASE_ADDRESS_MASK ICH9_MASK(32, 15, 7) #define ICH9_LPC_PMBASE_RTE 0x1 #define ICH9_LPC_PMBASE_DEFAULT 0x1 #define ICH9_LPC_ACPI_CTRL 0x44 #define ICH9_LPC_ACPI_CTRL_ACPI_EN 0x80 -#define ICH9_LPC_ACPI_CTRL_SCI_IRQ_SEL_MASK Q35_MASK(8, 2, 0) +#define ICH9_LPC_ACPI_CTRL_SCI_IRQ_SEL_MASK ICH9_MASK(8, 2, 0) #define ICH9_LPC_ACPI_CTRL_9 0x0 #define ICH9_LPC_ACPI_CTRL_10 0x1 #define ICH9_LPC_ACPI_CTRL_11 0x2 @@ -162,7 +162,7 @@ struct ICH9LPCState { #define ICH9_LPC_PIRQH_ROUT 0x6b #define ICH9_LPC_PIRQ_ROUT_IRQEN 0x80 -#define ICH9_LPC_PIRQ_ROUT_MASK Q35_MASK(8, 3, 0) +#define ICH9_LPC_PIRQ_ROUT_MASK ICH9_MASK(8, 3, 0) #define ICH9_LPC_PIRQ_ROUT_DEFAULT 0x80 #define ICH9_LPC_GEN_PMCON_1 0xa0 @@ -172,7 +172,7 @@ struct ICH9LPCState { #define ICH9_LPC_GEN_PMCON_LOCK 0xa6 #define ICH9_LPC_RCBA 0xf0 -#define ICH9_LPC_RCBA_BA_MASK Q35_MASK(32, 31, 14) +#define ICH9_LPC_RCBA_BA_MASK ICH9_MASK(32, 31, 14) #define ICH9_LPC_RCBA_EN 0x1 #define ICH9_LPC_RCBA_DEFAULT 0x0 From 29a457cbbc90a9938f8ca3d6bf397e99b9559e58 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 13 Feb 2023 18:30:23 +0100 Subject: [PATCH 040/185] hw/isa/lpc_ich9: Unexport PIRQ functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to rely on the board to wire up the ICH9 PCI IRQs. All functions access private state of the LPC device which suggests that it should wire up the IRQs. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230213173033.98762-3-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/pc_q35.c | 3 --- hw/isa/lpc_ich9.c | 11 ++++++++--- include/hw/i386/ich9.h | 3 --- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 66cd718b70..5dfaeeed5f 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -270,9 +270,6 @@ static void pc_q35_init(MachineState *machine) for (i = 0; i < GSI_NUM_PINS; i++) { qdev_connect_gpio_out_named(lpc_dev, ICH9_GPIO_GSI, i, x86ms->gsi[i]); } - pci_bus_irqs(host_bus, ich9_lpc_set_irq, ich9_lpc, ICH9_LPC_NB_PIRQS); - pci_bus_map_irqs(host_bus, ich9_lpc_map_irq); - pci_bus_set_route_irq_fn(host_bus, ich9_route_intx_pin_to_irq); isa_bus = ich9_lpc->isa_bus; if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) { diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 1fba3c210c..54a8839cd2 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -259,7 +259,7 @@ static void ich9_lpc_update_apic(ICH9LPCState *lpc, int gsi) qemu_set_irq(lpc->gsi[gsi], level); } -void ich9_lpc_set_irq(void *opaque, int pirq, int level) +static void ich9_lpc_set_irq(void *opaque, int pirq, int level) { ICH9LPCState *lpc = opaque; int pic_irq, pic_dis; @@ -275,7 +275,7 @@ void ich9_lpc_set_irq(void *opaque, int pirq, int level) /* return the pirq number (PIRQ[A-H]:0-7) corresponding to * a given device irq pin. */ -int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx) +static int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx) { BusState *bus = qdev_get_parent_bus(&pci_dev->qdev); PCIBus *pci_bus = PCI_BUS(bus); @@ -286,7 +286,7 @@ int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx) return lpc->irr[PCI_SLOT(pci_dev->devfn)][intx]; } -PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin) +static PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin) { ICH9LPCState *lpc = opaque; PCIINTxRoute route; @@ -680,6 +680,7 @@ static void ich9_lpc_realize(PCIDevice *d, Error **errp) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(d); DeviceState *dev = DEVICE(d); + PCIBus *pci_bus = pci_get_bus(d); ISABus *isa_bus; if ((lpc->smi_host_features & BIT_ULL(ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT)) && @@ -728,6 +729,10 @@ static void ich9_lpc_realize(PCIDevice *d, Error **errp) isa_bus_irqs(isa_bus, lpc->gsi); i8257_dma_init(isa_bus, 0); + + pci_bus_irqs(pci_bus, ich9_lpc_set_irq, d, ICH9_LPC_NB_PIRQS); + pci_bus_map_irqs(pci_bus, ich9_lpc_map_irq); + pci_bus_set_route_irq_fn(pci_bus, ich9_route_intx_pin_to_irq); } static bool ich9_rst_cnt_needed(void *opaque) diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h index 36e0ccd16a..921e4c7ef6 100644 --- a/include/hw/i386/ich9.h +++ b/include/hw/i386/ich9.h @@ -9,9 +9,6 @@ #include "hw/acpi/ich9.h" #include "qom/object.h" -void ich9_lpc_set_irq(void *opaque, int irq_num, int level); -int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx); -PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin); void ich9_lpc_pm_init(PCIDevice *pci_lpc, bool smm_enabled); I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base); From 958f818230e07ab016b873d3006234f584572607 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 13 Feb 2023 18:30:24 +0100 Subject: [PATCH 041/185] hw/isa/lpc_ich9: Eliminate ICH9LPCState::isa_bus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By using qdev_get_child_bus() we can eliminate ICH9LPCState::isa_bus and spare the ich9_lpc variable in pc_q35, too. Signed-off-by: Bernhard Beschow Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230213173033.98762-4-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/pc_q35.c | 4 +--- hw/isa/lpc_ich9.c | 5 +---- include/hw/i386/ich9.h | 3 --- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 5dfaeeed5f..5e3f7c7e86 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -132,7 +132,6 @@ static void pc_q35_init(MachineState *machine) GSIState *gsi_state; ISABus *isa_bus; int i; - ICH9LPCState *ich9_lpc; PCIDevice *ahci; ram_addr_t lowmem; DriveInfo *hd[MAX_SATA_PORTS]; @@ -265,12 +264,11 @@ static void pc_q35_init(MachineState *machine) /* irq lines */ gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled); - ich9_lpc = ICH9_LPC_DEVICE(lpc); lpc_dev = DEVICE(lpc); for (i = 0; i < GSI_NUM_PINS; i++) { qdev_connect_gpio_out_named(lpc_dev, ICH9_GPIO_GSI, i, x86ms->gsi[i]); } - isa_bus = ich9_lpc->isa_bus; + isa_bus = ISA_BUS(qdev_get_child_bus(lpc_dev, "isa.0")); if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) { pc_i8259_create(isa_bus, gsi_state->i8259_irq); diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 54a8839cd2..71f7c18a2e 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -710,8 +710,6 @@ static void ich9_lpc_realize(PCIDevice *d, Error **errp) memory_region_init_io(&lpc->rcrb_mem, OBJECT(d), &rcrb_mmio_ops, lpc, "lpc-rcrb-mmio", ICH9_CC_SIZE); - lpc->isa_bus = isa_bus; - ich9_cc_init(lpc); apm_init(d, &lpc->apm, ich9_apm_ctrl_changed, lpc); @@ -818,8 +816,7 @@ static void ich9_send_gpe(AcpiDeviceIf *adev, AcpiEventStatusBits ev) static void build_ich9_isa_aml(AcpiDevAmlIf *adev, Aml *scope) { Aml *field; - ICH9LPCState *s = ICH9_LPC_DEVICE(adev); - BusState *bus = BUS(s->isa_bus); + BusState *bus = qdev_get_child_bus(DEVICE(adev), "isa.0"); Aml *sb_scope = aml_scope("\\_SB"); /* ICH9 PCI to ISA irq remapping */ diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h index 921e4c7ef6..05464f6965 100644 --- a/include/hw/i386/ich9.h +++ b/include/hw/i386/ich9.h @@ -1,7 +1,6 @@ #ifndef HW_ICH9_H #define HW_ICH9_H -#include "hw/isa/isa.h" #include "hw/sysbus.h" #include "hw/i386/pc.h" #include "hw/isa/apm.h" @@ -64,8 +63,6 @@ struct ICH9LPCState { * triggers feature lockdown */ uint64_t smi_negotiated_features; /* guest-invisible, host endian */ - /* isa bus */ - ISABus *isa_bus; MemoryRegion rcrb_mem; /* root complex register block */ Notifier machine_ready; From d73c2b1c04b980e7586ba6fdd1e0f6203f45ed6d Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 13 Feb 2023 18:30:25 +0100 Subject: [PATCH 042/185] hw/i2c/smbus_ich9: Move ich9_smb_set_irq() in front of ich9_smbus_realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a preparation for the next commit to make it cleaner. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230213173033.98762-5-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i2c/smbus_ich9.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index 52ba77f3fc..d29c0f6ffa 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -80,6 +80,18 @@ static void ich9_smbus_write_config(PCIDevice *d, uint32_t address, } } +static void ich9_smb_set_irq(PMSMBus *pmsmb, bool enabled) +{ + ICH9SMBState *s = pmsmb->opaque; + + if (enabled == s->irq_enabled) { + return; + } + + s->irq_enabled = enabled; + pci_set_irq(&s->dev, enabled); +} + static void ich9_smbus_realize(PCIDevice *d, Error **errp) { ICH9SMBState *s = ICH9_SMB_DEVICE(d); @@ -125,18 +137,6 @@ static void ich9_smb_class_init(ObjectClass *klass, void *data) adevc->build_dev_aml = build_ich9_smb_aml; } -static void ich9_smb_set_irq(PMSMBus *pmsmb, bool enabled) -{ - ICH9SMBState *s = pmsmb->opaque; - - if (enabled == s->irq_enabled) { - return; - } - - s->irq_enabled = enabled; - pci_set_irq(&s->dev, enabled); -} - I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base) { PCIDevice *d = From 07981e8fa022ce2ffe6dc63e23ee9afb7867b445 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 13 Feb 2023 18:30:26 +0100 Subject: [PATCH 043/185] hw/i2c/smbus_ich9: Inline ich9_smb_init() and remove it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ich9_smb_init() is a legacy init function, so modernize the code. Note that the smb_io_base parameter was unused. Signed-off-by: Bernhard Beschow Acked-by: Corey Minyard Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230213173033.98762-6-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i2c/smbus_ich9.c | 13 +++---------- hw/i386/pc_q35.c | 11 ++++++++--- include/hw/i386/ich9.h | 1 - 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index d29c0f6ffa..f0dd3cb147 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -105,6 +105,9 @@ static void ich9_smbus_realize(PCIDevice *d, Error **errp) pm_smbus_init(&d->qdev, &s->smb, false); pci_register_bar(d, ICH9_SMB_SMB_BASE_BAR, PCI_BASE_ADDRESS_SPACE_IO, &s->smb.io); + + s->smb.set_irq = ich9_smb_set_irq; + s->smb.opaque = s; } static void build_ich9_smb_aml(AcpiDevAmlIf *adev, Aml *scope) @@ -137,16 +140,6 @@ static void ich9_smb_class_init(ObjectClass *klass, void *data) adevc->build_dev_aml = build_ich9_smb_aml; } -I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base) -{ - PCIDevice *d = - pci_create_simple_multifunction(bus, devfn, true, TYPE_ICH9_SMB_DEVICE); - ICH9SMBState *s = ICH9_SMB_DEVICE(d); - s->smb.set_irq = ich9_smb_set_irq; - s->smb.opaque = s; - return s->smb.smbus; -} - static const TypeInfo ich9_smb_info = { .name = TYPE_ICH9_SMB_DEVICE, .parent = TYPE_PCI_DEVICE, diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 5e3f7c7e86..7137c38846 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -315,10 +315,15 @@ static void pc_q35_init(MachineState *machine) } if (pcms->smbus_enabled) { + PCIDevice *smb; + /* TODO: Populate SPD eeprom data. */ - pcms->smbus = ich9_smb_init(host_bus, - PCI_DEVFN(ICH9_SMB_DEV, ICH9_SMB_FUNC), - 0xb100); + smb = pci_create_simple_multifunction(host_bus, + PCI_DEVFN(ICH9_SMB_DEV, + ICH9_SMB_FUNC), + true, TYPE_ICH9_SMB_DEVICE); + pcms->smbus = I2C_BUS(qdev_get_child_bus(DEVICE(smb), "i2c")); + smbus_eeprom_init(pcms->smbus, 8, NULL, 0); } diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h index 05464f6965..52ea116f44 100644 --- a/include/hw/i386/ich9.h +++ b/include/hw/i386/ich9.h @@ -9,7 +9,6 @@ #include "qom/object.h" void ich9_lpc_pm_init(PCIDevice *pci_lpc, bool smm_enabled); -I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base); void ich9_generate_smi(void); From ecf403cbb87020c6215856cb4843ca49c0202ba2 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 13 Feb 2023 18:30:27 +0100 Subject: [PATCH 044/185] hw/i386/pc_q35: Allow for setting properties before realizing TYPE_ICH9_LPC_DEVICE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a preparation to make the next patch cleaner. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230213173033.98762-7-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/pc_q35.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 7137c38846..d949f2efc1 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -235,9 +235,9 @@ static void pc_q35_init(MachineState *machine) phb = PCI_HOST_BRIDGE(q35_host); host_bus = phb->bus; /* create ISA bus */ - lpc = pci_create_simple_multifunction(host_bus, PCI_DEVFN(ICH9_LPC_DEV, - ICH9_LPC_FUNC), true, - TYPE_ICH9_LPC_DEVICE); + lpc = pci_new_multifunction(PCI_DEVFN(ICH9_LPC_DEV, ICH9_LPC_FUNC), true, + TYPE_ICH9_LPC_DEVICE); + pci_realize_and_unref(lpc, host_bus, &error_fatal); object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, TYPE_HOTPLUG_HANDLER, From 20fe3af24f1367906ab1eb6aa56e2cef73f116a8 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 13 Feb 2023 18:30:28 +0100 Subject: [PATCH 045/185] hw/isa/lpc_ich9: Connect PM stuff to LPC internally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make TYPE_ICH9_LPC_DEVICE more self-contained by moving the call to ich9_lpc_pm_init() from board code to its realize function. In order to propagate x86_machine_is_smm_enabled(), introduce an "smm-enabled" property like we have in piix4. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230213173033.98762-8-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/acpi/ich9.c | 8 ++------ hw/i386/pc_q35.c | 5 ++--- hw/isa/lpc_ich9.c | 8 +++++--- include/hw/acpi/ich9.h | 6 ++---- include/hw/i386/ich9.h | 2 -- 5 files changed, 11 insertions(+), 18 deletions(-) diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index a93c470e9d..54bb3d83b3 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -291,9 +291,7 @@ static void pm_powerdown_req(Notifier *n, void *opaque) acpi_pm1_evt_power_down(&pm->acpi_regs); } -void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, - bool smm_enabled, - qemu_irq sci_irq) +void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, qemu_irq sci_irq) { memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE); memory_region_set_enabled(&pm->io, false); @@ -303,7 +301,7 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, acpi_pm_tmr_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io); acpi_pm1_evt_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io); acpi_pm1_cnt_init(&pm->acpi_regs, &pm->io, pm->disable_s3, pm->disable_s4, - pm->s4_val, !pm->smm_compat && !smm_enabled); + pm->s4_val, !pm->smm_compat && !pm->smm_enabled); acpi_gpe_init(&pm->acpi_regs, ICH9_PMIO_GPE0_LEN); memory_region_init_io(&pm->io_gpe, OBJECT(lpc_pci), &ich9_gpe_ops, pm, @@ -314,8 +312,6 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, "acpi-smi", 8); memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi); - pm->smm_enabled = smm_enabled; - if (pm->enable_tco) { acpi_pm_tco_init(&pm->tco_regs, &pm->io); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index d949f2efc1..4508e8ac10 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -237,6 +237,8 @@ static void pc_q35_init(MachineState *machine) /* create ISA bus */ lpc = pci_new_multifunction(PCI_DEVFN(ICH9_LPC_DEV, ICH9_LPC_FUNC), true, TYPE_ICH9_LPC_DEVICE); + qdev_prop_set_bit(DEVICE(lpc), "smm-enabled", + x86_machine_is_smm_enabled(x86ms)); pci_realize_and_unref(lpc, host_bus, &error_fatal); object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, @@ -291,9 +293,6 @@ static void pc_q35_init(MachineState *machine) pc_basic_device_init(pcms, isa_bus, x86ms->gsi, &rtc_state, !mc->no_floppy, 0xff0104); - /* connect pm stuff to lpc */ - ich9_lpc_pm_init(lpc, x86_machine_is_smm_enabled(x86ms)); - if (pcms->sata_enabled) { /* ahci and SATA device, for q35 1 ahci controller is built-in */ ahci = pci_create_simple_multifunction(host_bus, diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 71f7c18a2e..c5060d145f 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -407,14 +407,13 @@ static void smi_features_ok_callback(void *opaque) lpc->smi_features_ok = 1; } -void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool smm_enabled) +static void ich9_lpc_pm_init(ICH9LPCState *lpc) { - ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci); qemu_irq sci_irq; FWCfgState *fw_cfg = fw_cfg_find(); sci_irq = qemu_allocate_irq(ich9_set_sci, lpc, 0); - ich9_pm_init(lpc_pci, &lpc->pm, smm_enabled, sci_irq); + ich9_pm_init(PCI_DEVICE(lpc), &lpc->pm, sci_irq); if (lpc->smi_host_features && fw_cfg) { uint64_t host_features_le; @@ -731,6 +730,8 @@ static void ich9_lpc_realize(PCIDevice *d, Error **errp) pci_bus_irqs(pci_bus, ich9_lpc_set_irq, d, ICH9_LPC_NB_PIRQS); pci_bus_map_irqs(pci_bus, ich9_lpc_map_irq); pci_bus_set_route_irq_fn(pci_bus, ich9_route_intx_pin_to_irq); + + ich9_lpc_pm_init(lpc); } static bool ich9_rst_cnt_needed(void *opaque) @@ -797,6 +798,7 @@ static const VMStateDescription vmstate_ich9_lpc = { static Property ich9_lpc_properties[] = { DEFINE_PROP_BOOL("noreboot", ICH9LPCState, pin_strap.spkr_hi, false), DEFINE_PROP_BOOL("smm-compat", ICH9LPCState, pm.smm_compat, false), + DEFINE_PROP_BOOL("smm-enabled", ICH9LPCState, pm.smm_enabled, false), DEFINE_PROP_BIT64("x-smi-broadcast", ICH9LPCState, smi_host_features, ICH9_LPC_SMI_F_BROADCAST_BIT, true), DEFINE_PROP_BIT64("x-smi-cpu-hotplug", ICH9LPCState, smi_host_features, diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h index d41866a229..57a542c4b8 100644 --- a/include/hw/acpi/ich9.h +++ b/include/hw/acpi/ich9.h @@ -64,7 +64,7 @@ typedef struct ICH9LPCPMRegs { uint8_t disable_s3; uint8_t disable_s4; uint8_t s4_val; - uint8_t smm_enabled; + bool smm_enabled; bool smm_compat; bool enable_tco; TCOIORegs tco_regs; @@ -72,9 +72,7 @@ typedef struct ICH9LPCPMRegs { #define ACPI_PM_PROP_TCO_ENABLED "enable_tco" -void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, - bool smm_enabled, - qemu_irq sci_irq); +void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, qemu_irq sci_irq); void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base); extern const VMStateDescription vmstate_ich9_pm; diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h index 52ea116f44..433c8942c9 100644 --- a/include/hw/i386/ich9.h +++ b/include/hw/i386/ich9.h @@ -8,8 +8,6 @@ #include "hw/acpi/ich9.h" #include "qom/object.h" -void ich9_lpc_pm_init(PCIDevice *pci_lpc, bool smm_enabled); - void ich9_generate_smi(void); #define ICH9_CC_SIZE (16 * 1024) /* 16KB. Chipset configuration registers */ From fb1856cb9b7cd81f45e1ee6ce079a4eea59314aa Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 13 Feb 2023 18:30:29 +0100 Subject: [PATCH 046/185] hw/isa/lpc_ich9: Remove redundant ich9_lpc_reset() invocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ich9_lpc_reset() is the dc->reset callback which is called automatically. No need to call it explicitly during k->realize. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230213173033.98762-9-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/isa/lpc_ich9.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index c5060d145f..2a4baac129 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -57,8 +57,6 @@ /*****************************************************************************/ /* ICH9 LPC PCI to ISA bridge */ -static void ich9_lpc_reset(DeviceState *qdev); - /* chipset configuration register * to access chipset configuration registers, pci_[sg]et_{byte, word, long} * are used. @@ -439,8 +437,6 @@ static void ich9_lpc_pm_init(ICH9LPCState *lpc) sizeof lpc->smi_features_ok, true); } - - ich9_lpc_reset(DEVICE(lpc)); } /* APM */ From e3e3a8ad1216faccd737f6bc06919deb366b0be3 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 13 Feb 2023 18:30:30 +0100 Subject: [PATCH 047/185] hw/i386/ich9: Remove redundant GSI_NUM_PINS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most code uses IOAPIC_NUM_PINS. The only place where GSI_NUM_PINS defines the size of an array is ICH9LPCState::gsi which needs to match IOAPIC_NUM_PINS. Remove GSI_NUM_PINS for consistency. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230213173033.98762-10-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/pc.c | 6 +++--- hw/i386/pc_q35.c | 3 ++- hw/isa/lpc_ich9.c | 2 +- include/hw/i386/ich9.h | 2 +- include/hw/i386/x86.h | 1 - 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index a7a2ededf9..d257545018 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -28,7 +28,7 @@ #include "hw/i386/pc.h" #include "hw/char/serial.h" #include "hw/char/parallel.h" -#include "hw/i386/apic.h" +#include "hw/i386/ioapic.h" #include "hw/i386/topology.h" #include "hw/i386/fw_cfg.h" #include "hw/i386/vmport.h" @@ -405,7 +405,7 @@ GSIState *pc_gsi_create(qemu_irq **irqs, bool pci_enabled) if (kvm_ioapic_in_kernel()) { kvm_pc_setup_irq_routing(pci_enabled); } - *irqs = qemu_allocate_irqs(gsi_handler, s, GSI_NUM_PINS); + *irqs = qemu_allocate_irqs(gsi_handler, s, IOAPIC_NUM_PINS); return s; } @@ -1296,7 +1296,7 @@ void pc_basic_device_init(struct PCMachineState *pcms, sysbus_realize_and_unref(SYS_BUS_DEVICE(hpet), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(hpet), 0, HPET_BASE); - for (i = 0; i < GSI_NUM_PINS; i++) { + for (i = 0; i < IOAPIC_NUM_PINS; i++) { sysbus_connect_irq(SYS_BUS_DEVICE(hpet), i, gsi[i]); } pit_isa_irq = -1; diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 4508e8ac10..d35316878d 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -43,6 +43,7 @@ #include "hw/i386/ich9.h" #include "hw/i386/amd_iommu.h" #include "hw/i386/intel_iommu.h" +#include "hw/i386/ioapic.h" #include "hw/display/ramfb.h" #include "hw/firmware/smbios.h" #include "hw/ide/pci.h" @@ -267,7 +268,7 @@ static void pc_q35_init(MachineState *machine) gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled); lpc_dev = DEVICE(lpc); - for (i = 0; i < GSI_NUM_PINS; i++) { + for (i = 0; i < IOAPIC_NUM_PINS; i++) { qdev_connect_gpio_out_named(lpc_dev, ICH9_GPIO_GSI, i, x86ms->gsi[i]); } isa_bus = ISA_BUS(qdev_get_child_bus(lpc_dev, "isa.0")); diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 2a4baac129..e3385ca7be 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -717,7 +717,7 @@ static void ich9_lpc_realize(PCIDevice *d, Error **errp) ICH9_RST_CNT_IOPORT, &lpc->rst_cnt_mem, 1); - qdev_init_gpio_out_named(dev, lpc->gsi, ICH9_GPIO_GSI, GSI_NUM_PINS); + qdev_init_gpio_out_named(dev, lpc->gsi, ICH9_GPIO_GSI, IOAPIC_NUM_PINS); isa_bus_irqs(isa_bus, lpc->gsi); diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h index 433c8942c9..d29090a9b7 100644 --- a/include/hw/i386/ich9.h +++ b/include/hw/i386/ich9.h @@ -63,7 +63,7 @@ struct ICH9LPCState { MemoryRegion rcrb_mem; /* root complex register block */ Notifier machine_ready; - qemu_irq gsi[GSI_NUM_PINS]; + qemu_irq gsi[IOAPIC_NUM_PINS]; }; #define ICH9_MASK(bit, ms_bit, ls_bit) \ diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index 5d3047a1d1..a09388b657 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -131,7 +131,6 @@ bool x86_machine_is_acpi_enabled(const X86MachineState *x86ms); /* Global System Interrupts */ -#define GSI_NUM_PINS IOAPIC_NUM_PINS #define ACPI_BUILD_PCI_IRQS ((1<<5) | (1<<9) | (1<<10) | (1<<11)) typedef struct GSIState { From 7f54640b4bb6f1bdc5d89e66bbd0b5c3bfc28ba9 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 13 Feb 2023 18:30:31 +0100 Subject: [PATCH 048/185] hw: Move ioapic*.h to intc/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ioapic sources reside in hw/intc already. Move the headers there as well. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230213173033.98762-11-shentey@gmail.com> [PMD: Keep ioapic_internal.h in hw/intc/, not under include/] Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + hw/i386/kvm/ioapic.c | 3 +-- hw/i386/pc.c | 2 +- hw/i386/pc_q35.c | 2 +- hw/intc/apic.c | 2 +- hw/intc/ioapic.c | 4 ++-- hw/intc/ioapic_common.c | 4 ++-- {include/hw/i386 => hw/intc}/ioapic_internal.h | 8 ++++---- include/hw/i386/x86.h | 2 +- include/hw/{i386 => intc}/ioapic.h | 6 +++--- target/i386/whpx/whpx-all.c | 2 +- 11 files changed, 18 insertions(+), 18 deletions(-) rename {include/hw/i386 => hw/intc}/ioapic_internal.h (96%) rename include/hw/{i386 => intc}/ioapic.h (93%) diff --git a/MAINTAINERS b/MAINTAINERS index c923cf624a..882bc50dee 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1712,6 +1712,7 @@ F: include/hw/char/parallel.h F: include/hw/dma/i8257.h F: include/hw/i2c/pm_smbus.h F: include/hw/input/i8042.h +F: include/hw/intc/ioapic* F: include/hw/isa/i8259_internal.h F: include/hw/isa/superio.h F: include/hw/timer/hpet.h diff --git a/hw/i386/kvm/ioapic.c b/hw/i386/kvm/ioapic.c index 272e26b4a2..cd5ea5d60b 100644 --- a/hw/i386/kvm/ioapic.c +++ b/hw/i386/kvm/ioapic.c @@ -12,9 +12,8 @@ #include "qemu/osdep.h" #include "monitor/monitor.h" -#include "hw/i386/x86.h" #include "hw/qdev-properties.h" -#include "hw/i386/ioapic_internal.h" +#include "hw/intc/ioapic_internal.h" #include "hw/intc/kvm_irqcount.h" #include "sysemu/kvm.h" diff --git a/hw/i386/pc.c b/hw/i386/pc.c index d257545018..7ad71b19aa 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -28,7 +28,6 @@ #include "hw/i386/pc.h" #include "hw/char/serial.h" #include "hw/char/parallel.h" -#include "hw/i386/ioapic.h" #include "hw/i386/topology.h" #include "hw/i386/fw_cfg.h" #include "hw/i386/vmport.h" @@ -47,6 +46,7 @@ #include "multiboot.h" #include "hw/rtc/mc146818rtc.h" #include "hw/intc/i8259.h" +#include "hw/intc/ioapic.h" #include "hw/timer/i8254.h" #include "hw/input/i8042.h" #include "hw/irq.h" diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index d35316878d..0afea6705c 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -43,11 +43,11 @@ #include "hw/i386/ich9.h" #include "hw/i386/amd_iommu.h" #include "hw/i386/intel_iommu.h" -#include "hw/i386/ioapic.h" #include "hw/display/ramfb.h" #include "hw/firmware/smbios.h" #include "hw/ide/pci.h" #include "hw/ide/ahci.h" +#include "hw/intc/ioapic.h" #include "hw/usb.h" #include "hw/usb/hcd-uhci.h" #include "qapi/error.h" diff --git a/hw/intc/apic.c b/hw/intc/apic.c index 2d3e55f4e2..0ff060f721 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -20,7 +20,7 @@ #include "qemu/thread.h" #include "hw/i386/apic_internal.h" #include "hw/i386/apic.h" -#include "hw/i386/ioapic.h" +#include "hw/intc/ioapic.h" #include "hw/intc/i8259.h" #include "hw/intc/kvm_irqcount.h" #include "hw/pci/msi.h" diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index 264262959d..6364ecab1b 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -24,10 +24,10 @@ #include "qapi/error.h" #include "monitor/monitor.h" #include "hw/i386/apic.h" -#include "hw/i386/ioapic.h" -#include "hw/i386/ioapic_internal.h" #include "hw/i386/x86.h" #include "hw/intc/i8259.h" +#include "hw/intc/ioapic.h" +#include "hw/intc/ioapic_internal.h" #include "hw/pci/msi.h" #include "hw/qdev-properties.h" #include "sysemu/kvm.h" diff --git a/hw/intc/ioapic_common.c b/hw/intc/ioapic_common.c index aa5f760871..b05f436dac 100644 --- a/hw/intc/ioapic_common.c +++ b/hw/intc/ioapic_common.c @@ -24,9 +24,9 @@ #include "qemu/module.h" #include "migration/vmstate.h" #include "monitor/monitor.h" -#include "hw/i386/ioapic.h" -#include "hw/i386/ioapic_internal.h" #include "hw/intc/intc.h" +#include "hw/intc/ioapic.h" +#include "hw/intc/ioapic_internal.h" #include "hw/sysbus.h" /* ioapic_no count start from 0 to MAX_IOAPICS, diff --git a/include/hw/i386/ioapic_internal.h b/hw/intc/ioapic_internal.h similarity index 96% rename from include/hw/i386/ioapic_internal.h rename to hw/intc/ioapic_internal.h index e8ff338d7f..37b8565539 100644 --- a/include/hw/i386/ioapic_internal.h +++ b/hw/intc/ioapic_internal.h @@ -19,11 +19,11 @@ * License along with this library; if not, see . */ -#ifndef QEMU_IOAPIC_INTERNAL_H -#define QEMU_IOAPIC_INTERNAL_H +#ifndef HW_INTC_IOAPIC_INTERNAL_H +#define HW_INTC_IOAPIC_INTERNAL_H #include "exec/memory.h" -#include "hw/i386/ioapic.h" +#include "hw/intc/ioapic.h" #include "hw/sysbus.h" #include "qemu/notify.h" #include "qom/object.h" @@ -115,4 +115,4 @@ void ioapic_reset_common(DeviceState *dev); void ioapic_stat_update_irq(IOAPICCommonState *s, int irq, int level); -#endif /* QEMU_IOAPIC_INTERNAL_H */ +#endif /* HW_INTC_IOAPIC_INTERNAL_H */ diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index a09388b657..890dfad23e 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -22,8 +22,8 @@ #include "hw/boards.h" #include "hw/nmi.h" +#include "hw/intc/ioapic.h" #include "hw/isa/isa.h" -#include "hw/i386/ioapic.h" #include "qom/object.h" struct X86MachineClass { diff --git a/include/hw/i386/ioapic.h b/include/hw/intc/ioapic.h similarity index 93% rename from include/hw/i386/ioapic.h rename to include/hw/intc/ioapic.h index ef37b8a9fd..aa122e25e3 100644 --- a/include/hw/i386/ioapic.h +++ b/include/hw/intc/ioapic.h @@ -17,8 +17,8 @@ * License along with this library; if not, see . */ -#ifndef HW_IOAPIC_H -#define HW_IOAPIC_H +#ifndef HW_INTC_IOAPIC_H +#define HW_INTC_IOAPIC_H #define IOAPIC_NUM_PINS 24 #define IO_APIC_DEFAULT_ADDRESS 0xfec00000 @@ -30,4 +30,4 @@ void ioapic_eoi_broadcast(int vector); -#endif /* HW_IOAPIC_H */ +#endif /* HW_INTC_IOAPIC_H */ diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index e738d83e81..3d0c0b375f 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -19,7 +19,7 @@ #include "sysemu/runstate.h" #include "qemu/main-loop.h" #include "hw/boards.h" -#include "hw/i386/ioapic.h" +#include "hw/intc/ioapic.h" #include "hw/i386/apic_internal.h" #include "qemu/error-report.h" #include "qapi/error.h" From 71671814a8086b8179df03128589a09703f2553e Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 13 Feb 2023 18:30:32 +0100 Subject: [PATCH 049/185] hw/i386/ich9: Clean up includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230213173033.98762-12-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/acpi-build.c | 1 + hw/isa/lpc_ich9.c | 2 +- include/hw/i386/ich9.h | 8 +++++--- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index b67dcbbb37..1bb73c3e9a 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -59,6 +59,7 @@ #include "hw/acpi/pcihp.h" #include "hw/i386/fw_cfg.h" #include "hw/i386/ich9.h" +#include "hw/i386/pc.h" #include "hw/pci/pci_bus.h" #include "hw/pci-host/i440fx.h" #include "hw/pci-host/q35.h" diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index e3385ca7be..ce946760bb 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -40,8 +40,8 @@ #include "hw/irq.h" #include "hw/isa/apm.h" #include "hw/pci/pci.h" -#include "hw/pci/pci_bridge.h" #include "hw/i386/ich9.h" +#include "hw/i386/pc.h" #include "hw/acpi/acpi.h" #include "hw/acpi/ich9.h" #include "hw/pci/pci_bus.h" diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h index d29090a9b7..3125863049 100644 --- a/include/hw/i386/ich9.h +++ b/include/hw/i386/ich9.h @@ -1,11 +1,13 @@ #ifndef HW_ICH9_H #define HW_ICH9_H -#include "hw/sysbus.h" -#include "hw/i386/pc.h" #include "hw/isa/apm.h" -#include "hw/acpi/acpi.h" #include "hw/acpi/ich9.h" +#include "hw/intc/ioapic.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" +#include "exec/memory.h" +#include "qemu/notify.h" #include "qom/object.h" void ich9_generate_smi(void); From 1a6981bbdc124950c9055974dadf1b36c61b9e1f Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 13 Feb 2023 18:30:33 +0100 Subject: [PATCH 050/185] hw: Move ich9.h to southbridge/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ICH9 is a south bridge which doesn't necessarily depend on x86, so move it into the southbridge folder, analoguous to PIIX. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230213173033.98762-13-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + hw/acpi/ich9.c | 2 +- hw/acpi/ich9_tco.c | 2 +- hw/i2c/smbus_ich9.c | 2 +- hw/i386/acpi-build.c | 2 +- hw/i386/pc_q35.c | 2 +- hw/isa/lpc_ich9.c | 2 +- hw/pci-bridge/i82801b11.c | 2 +- include/hw/{i386 => southbridge}/ich9.h | 6 +++--- tests/qtest/tco-test.c | 2 +- 10 files changed, 12 insertions(+), 11 deletions(-) rename include/hw/{i386 => southbridge}/ich9.h (99%) diff --git a/MAINTAINERS b/MAINTAINERS index 882bc50dee..da930c34a9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1680,6 +1680,7 @@ F: hw/i2c/smbus_ich9.c F: hw/acpi/piix4.c F: hw/acpi/ich9*.c F: include/hw/acpi/ich9*.h +F: include/hw/southbridge/ich9.h F: include/hw/southbridge/piix.h F: hw/isa/apm.c F: include/hw/isa/apm.h diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 54bb3d83b3..d23bfcaa6b 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -36,7 +36,7 @@ #include "hw/acpi/acpi.h" #include "hw/acpi/ich9_tco.h" -#include "hw/i386/ich9.h" +#include "hw/southbridge/ich9.h" #include "hw/mem/pc-dimm.h" #include "hw/mem/nvdimm.h" diff --git a/hw/acpi/ich9_tco.c b/hw/acpi/ich9_tco.c index fbf97f81f4..1540f4fd46 100644 --- a/hw/acpi/ich9_tco.c +++ b/hw/acpi/ich9_tco.c @@ -9,7 +9,7 @@ #include "qemu/osdep.h" #include "sysemu/watchdog.h" -#include "hw/i386/ich9.h" +#include "hw/southbridge/ich9.h" #include "migration/vmstate.h" #include "hw/acpi/ich9_tco.h" diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index f0dd3cb147..18d40e93c1 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -27,7 +27,7 @@ #include "migration/vmstate.h" #include "qemu/module.h" -#include "hw/i386/ich9.h" +#include "hw/southbridge/ich9.h" #include "qom/object.h" #include "hw/acpi/acpi_aml_interface.h" diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 1bb73c3e9a..d27921fd8f 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -55,10 +55,10 @@ #include "hw/hyperv/vmbus-bridge.h" /* Supported chipsets: */ +#include "hw/southbridge/ich9.h" #include "hw/southbridge/piix.h" #include "hw/acpi/pcihp.h" #include "hw/i386/fw_cfg.h" -#include "hw/i386/ich9.h" #include "hw/i386/pc.h" #include "hw/pci/pci_bus.h" #include "hw/pci-host/i440fx.h" diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 0afea6705c..09004f3f1f 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -40,7 +40,6 @@ #include "hw/qdev-properties.h" #include "hw/i386/x86.h" #include "hw/i386/pc.h" -#include "hw/i386/ich9.h" #include "hw/i386/amd_iommu.h" #include "hw/i386/intel_iommu.h" #include "hw/display/ramfb.h" @@ -48,6 +47,7 @@ #include "hw/ide/pci.h" #include "hw/ide/ahci.h" #include "hw/intc/ioapic.h" +#include "hw/southbridge/ich9.h" #include "hw/usb.h" #include "hw/usb/hcd-uhci.h" #include "qapi/error.h" diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index ce946760bb..96fd500502 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -40,7 +40,7 @@ #include "hw/irq.h" #include "hw/isa/apm.h" #include "hw/pci/pci.h" -#include "hw/i386/ich9.h" +#include "hw/southbridge/ich9.h" #include "hw/i386/pc.h" #include "hw/acpi/acpi.h" #include "hw/acpi/ich9.h" diff --git a/hw/pci-bridge/i82801b11.c b/hw/pci-bridge/i82801b11.c index f3b4a14611..0e83cd11b2 100644 --- a/hw/pci-bridge/i82801b11.c +++ b/hw/pci-bridge/i82801b11.c @@ -45,7 +45,7 @@ #include "hw/pci/pci_bridge.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "hw/i386/ich9.h" +#include "hw/southbridge/ich9.h" /*****************************************************************************/ /* ICH9 DMI-to-PCI bridge */ diff --git a/include/hw/i386/ich9.h b/include/hw/southbridge/ich9.h similarity index 99% rename from include/hw/i386/ich9.h rename to include/hw/southbridge/ich9.h index 3125863049..7004eecbf9 100644 --- a/include/hw/i386/ich9.h +++ b/include/hw/southbridge/ich9.h @@ -1,5 +1,5 @@ -#ifndef HW_ICH9_H -#define HW_ICH9_H +#ifndef HW_SOUTHBRIDGE_ICH9_H +#define HW_SOUTHBRIDGE_ICH9_H #include "hw/isa/apm.h" #include "hw/acpi/ich9.h" @@ -242,4 +242,4 @@ struct ICH9LPCState { #define ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT 1 #define ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT 2 -#endif /* HW_ICH9_H */ +#endif /* HW_SOUTHBRIDGE_ICH9_H */ diff --git a/tests/qtest/tco-test.c b/tests/qtest/tco-test.c index d865e95dfc..0547d41173 100644 --- a/tests/qtest/tco-test.c +++ b/tests/qtest/tco-test.c @@ -14,7 +14,7 @@ #include "libqos/pci-pc.h" #include "qapi/qmp/qdict.h" #include "hw/pci/pci_regs.h" -#include "hw/i386/ich9.h" +#include "hw/southbridge/ich9.h" #include "hw/acpi/ich9.h" #include "hw/acpi/ich9_tco.h" From 9d724e0ba8986a54fab14e5f2de0f9f03f461eac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Feb 2023 18:37:52 +0100 Subject: [PATCH 051/185] hw/pci: Fix a typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix 'interrutp' typo. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230211152239.88106-2-philmd@linaro.org> --- hw/pci/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index cc51f98593..bad8e63db3 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1648,7 +1648,7 @@ void pci_device_set_intx_routing_notifier(PCIDevice *dev, * 9.1: Interrupt routing. Table 9-1 * * the PCI Express Base Specification, Revision 2.1 - * 2.2.8.1: INTx interrutp signaling - Rules + * 2.2.8.1: INTx interrupt signaling - Rules * the Implementation Note * Table 2-20 */ From 78827d5f7b63111c67c38c009c0708f7f956369d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 13:19:46 +0100 Subject: [PATCH 052/185] hw/intc/i8259: Document i8259_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit i8259_init() helper creates a i8259 device on an ISA bus, connects its IRQ output to the parent's input IRQ, and returns an array of 16 ISA input IRQs. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230210163744.32182-2-philmd@linaro.org> --- hw/intc/i8259.c | 4 ++-- include/hw/intc/i8259.h | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/hw/intc/i8259.c b/hw/intc/i8259.c index 0261f087b2..17910f3bcb 100644 --- a/hw/intc/i8259.c +++ b/hw/intc/i8259.c @@ -406,7 +406,7 @@ static void pic_realize(DeviceState *dev, Error **errp) pc->parent_realize(dev, errp); } -qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq) +qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq_in) { qemu_irq *irq_set; DeviceState *dev; @@ -418,7 +418,7 @@ qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq) isadev = i8259_init_chip(TYPE_I8259, bus, true); dev = DEVICE(isadev); - qdev_connect_gpio_out(dev, 0, parent_irq); + qdev_connect_gpio_out(dev, 0, parent_irq_in); for (i = 0 ; i < 8; i++) { irq_set[i] = qdev_get_gpio_in(dev, i); } diff --git a/include/hw/intc/i8259.h b/include/hw/intc/i8259.h index a0e34dd990..c412575775 100644 --- a/include/hw/intc/i8259.h +++ b/include/hw/intc/i8259.h @@ -4,7 +4,15 @@ /* i8259.c */ extern PICCommonState *isa_pic; -qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq); + +/* + * i8259_init() + * + * Create a i8259 device on an ISA @bus, + * connect its output to @parent_irq_in, + * return an (allocated) array of 16 input IRQs. + */ +qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq_in); qemu_irq *kvm_i8259_init(ISABus *bus); int pic_get_output(PICCommonState *s); int pic_read_irq(PICCommonState *s); From 08d8bf4fe34b2731948ced89bf0aa80fe31f85fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 13:15:16 +0100 Subject: [PATCH 053/185] hw/isa/i82378: Rename output IRQ as 'cpu_intr' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit a04ff94097 ("prep: Add i82378 PCI-to-ISA bridge emulation") aimed to model the 2 output IRQs: CPU intr and NMI. Commit 5039d6e235 ("i8257: remove cpu_request_exit irq") removed the NMI IRQ. Since this model only use the CPU interrupt, replace the 'out[2]' array by a single 'cpu_intr'. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230210163744.32182-3-philmd@linaro.org> --- hw/isa/i82378.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c index e3322e03bf..84ce761f5f 100644 --- a/hw/isa/i82378.c +++ b/hw/isa/i82378.c @@ -32,7 +32,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(I82378State, I82378) struct I82378State { PCIDevice parent_obj; - qemu_irq out[2]; + qemu_irq cpu_intr; qemu_irq *i8259; MemoryRegion io; }; @@ -50,7 +50,7 @@ static const VMStateDescription vmstate_i82378 = { static void i82378_request_out0_irq(void *opaque, int irq, int level) { I82378State *s = opaque; - qemu_set_irq(s->out[0], level); + qemu_set_irq(s->cpu_intr, level); } static void i82378_request_pic_irq(void *opaque, int irq, int level) @@ -113,7 +113,7 @@ static void i82378_init(Object *obj) DeviceState *dev = DEVICE(obj); I82378State *s = I82378(obj); - qdev_init_gpio_out(dev, s->out, 1); + qdev_init_gpio_out(dev, &s->cpu_intr, 1); qdev_init_gpio_in(dev, i82378_request_pic_irq, 16); } From cef2e7148e32d61338de0220619d308bf42af770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Feb 2023 16:05:34 +0100 Subject: [PATCH 054/185] hw/isa/i82378: Remove intermediate IRQ forwarder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the i82378 model was added in commit a04ff940974 ("prep: Add i82378 PCI-to-ISA bridge emulation") the i8259 model was not yet QOM'ified. This happened later in commit 747c70af78f ("i8259: Convert to qdev"). Directly dispatch ISA IRQs to 'cpu_intr' output IRQ by removing the intermediate i82378_request_out0_irq() handler. Rename ISA IRQs array as 'isa_irqs_in' to emphasize these are input IRQs. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230210163744.32182-4-philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/isa/i82378.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c index 84ce761f5f..d32653369d 100644 --- a/hw/isa/i82378.c +++ b/hw/isa/i82378.c @@ -33,7 +33,7 @@ struct I82378State { PCIDevice parent_obj; qemu_irq cpu_intr; - qemu_irq *i8259; + qemu_irq *isa_irqs_in; MemoryRegion io; }; @@ -47,18 +47,12 @@ static const VMStateDescription vmstate_i82378 = { }, }; -static void i82378_request_out0_irq(void *opaque, int irq, int level) -{ - I82378State *s = opaque; - qemu_set_irq(s->cpu_intr, level); -} - static void i82378_request_pic_irq(void *opaque, int irq, int level) { DeviceState *dev = opaque; I82378State *s = I82378(dev); - qemu_set_irq(s->i8259[irq], level); + qemu_set_irq(s->isa_irqs_in[irq], level); } static void i82378_realize(PCIDevice *pci, Error **errp) @@ -94,9 +88,8 @@ static void i82378_realize(PCIDevice *pci, Error **errp) */ /* 2 82C59 (irq) */ - s->i8259 = i8259_init(isabus, - qemu_allocate_irq(i82378_request_out0_irq, s, 0)); - isa_bus_irqs(isabus, s->i8259); + s->isa_irqs_in = i8259_init(isabus, s->cpu_intr); + isa_bus_irqs(isabus, s->isa_irqs_in); /* 1 82C54 (pit) */ pit = i8254_pit_init(isabus, 0x40, 0, NULL); From bb98e0f59cde846666d9fddc60ae74ef7ddfca17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 10:38:42 +0100 Subject: [PATCH 055/185] hw/isa/vt82c686: Remove intermediate IRQ forwarder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Directly dispatch ISA IRQs to 'cpu_intr' output IRQ by removing the intermediate via_isa_request_i8259_irq() handler. Rename ISA IRQs array as 'isa_irqs_in' to emphasize these are input IRQs. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230210163744.32182-5-philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/isa/vt82c686.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index 3f9bd0c04d..a913a509f7 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -548,7 +548,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(ViaISAState, VIA_ISA) struct ViaISAState { PCIDevice dev; qemu_irq cpu_intr; - qemu_irq *isa_irqs; + qemu_irq *isa_irqs_in; ViaSuperIOState via_sio; RTCState rtc; PCIIDEState ide; @@ -595,13 +595,7 @@ static const TypeInfo via_isa_info = { void via_isa_set_irq(PCIDevice *d, int n, int level) { ViaISAState *s = VIA_ISA(d); - qemu_set_irq(s->isa_irqs[n], level); -} - -static void via_isa_request_i8259_irq(void *opaque, int irq, int level) -{ - ViaISAState *s = opaque; - qemu_set_irq(s->cpu_intr, level); + qemu_set_irq(s->isa_irqs_in[n], level); } static void via_isa_realize(PCIDevice *d, Error **errp) @@ -609,12 +603,10 @@ static void via_isa_realize(PCIDevice *d, Error **errp) ViaISAState *s = VIA_ISA(d); DeviceState *dev = DEVICE(d); PCIBus *pci_bus = pci_get_bus(d); - qemu_irq *isa_irq; ISABus *isa_bus; int i; qdev_init_gpio_out(dev, &s->cpu_intr, 1); - isa_irq = qemu_allocate_irqs(via_isa_request_i8259_irq, s, 1); isa_bus = isa_bus_new(dev, pci_address_space(d), pci_address_space_io(d), errp); @@ -622,8 +614,8 @@ static void via_isa_realize(PCIDevice *d, Error **errp) return; } - s->isa_irqs = i8259_init(isa_bus, *isa_irq); - isa_bus_irqs(isa_bus, s->isa_irqs); + s->isa_irqs_in = i8259_init(isa_bus, s->cpu_intr); + isa_bus_irqs(isa_bus, s->isa_irqs_in); i8254_pit_init(isa_bus, 0x40, 0, NULL); i8257_dma_init(isa_bus, 0); From eba245659bfef2f325538c94f6c682f69d582fcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 13:04:29 +0100 Subject: [PATCH 056/185] hw/sparc64/sun4u: Keep reference to ISA input IRQs in EbusState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep reference to ISA input IRQs in EbusState. To emphasize input/output distinction, rename arrays as isa_irqs_in / isa_irqs_out. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230210163744.32182-7-philmd@linaro.org> --- hw/sparc64/sun4u.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 387181ff77..8fe47e2c22 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -84,7 +84,8 @@ struct EbusState { PCIDevice parent_obj; ISABus *isa_bus; - qemu_irq isa_bus_irqs[ISA_NUM_IRQS]; + qemu_irq *isa_irqs_in; + qemu_irq isa_irqs_out[ISA_NUM_IRQS]; uint64_t console_serial_base; MemoryRegion bar0; MemoryRegion bar1; @@ -287,7 +288,7 @@ static const TypeInfo power_info = { static void ebus_isa_irq_handler(void *opaque, int n, int level) { EbusState *s = EBUS(opaque); - qemu_irq irq = s->isa_bus_irqs[n]; + qemu_irq irq = s->isa_irqs_out[n]; /* Pass ISA bus IRQs onto their gpio equivalent */ trace_ebus_isa_irq_handler(n, level); @@ -303,7 +304,6 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) ISADevice *isa_dev; SysBusDevice *sbd; DeviceState *dev; - qemu_irq *isa_irq; DriveInfo *fd[MAX_FD]; int i; @@ -315,9 +315,9 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) } /* ISA bus */ - isa_irq = qemu_allocate_irqs(ebus_isa_irq_handler, s, ISA_NUM_IRQS); - isa_bus_irqs(s->isa_bus, isa_irq); - qdev_init_gpio_out_named(DEVICE(s), s->isa_bus_irqs, "isa-irq", + s->isa_irqs_in = qemu_allocate_irqs(ebus_isa_irq_handler, s, ISA_NUM_IRQS); + isa_bus_irqs(s->isa_bus, s->isa_irqs_in); + qdev_init_gpio_out_named(DEVICE(s), s->isa_irqs_out, "isa-irq", ISA_NUM_IRQS); /* Serial ports */ From 97cfb5e430f95b58c09a9da21b60196f32475beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 14 Feb 2023 12:48:15 +0100 Subject: [PATCH 057/185] hw/isa: Remove empty ISADeviceClass structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ISADeviceClass is an empty class and just increase code complexity. Remove it, directly embedding DeviceClass in classes expanding TYPE_ISA_DEVICE. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Bernhard Beschow Message-Id: <20230215161641.32663-19-philmd@linaro.org> --- hw/isa/isa-bus.c | 1 - hw/rtc/m48t59-isa.c | 2 +- include/hw/isa/i8259_internal.h | 2 +- include/hw/isa/isa.h | 6 +----- include/hw/isa/superio.h | 2 +- include/hw/timer/i8254_internal.h | 2 +- 6 files changed, 5 insertions(+), 10 deletions(-) diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index f155b80010..845686cb12 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -213,7 +213,6 @@ static const TypeInfo isa_device_type_info = { .parent = TYPE_DEVICE, .instance_size = sizeof(ISADevice), .abstract = true, - .class_size = sizeof(ISADeviceClass), .class_init = isa_device_class_init, }; diff --git a/hw/rtc/m48t59-isa.c b/hw/rtc/m48t59-isa.c index e61f7ec370..5bb46f2383 100644 --- a/hw/rtc/m48t59-isa.c +++ b/hw/rtc/m48t59-isa.c @@ -47,7 +47,7 @@ struct M48txxISAState { }; struct M48txxISADeviceClass { - ISADeviceClass parent_class; + DeviceClass parent_class; M48txxInfo info; }; diff --git a/include/hw/isa/i8259_internal.h b/include/hw/isa/i8259_internal.h index d272d879fb..155b098452 100644 --- a/include/hw/isa/i8259_internal.h +++ b/include/hw/isa/i8259_internal.h @@ -35,7 +35,7 @@ OBJECT_DECLARE_TYPE(PICCommonState, PICCommonClass, PIC_COMMON) struct PICCommonClass { - ISADeviceClass parent_class; + DeviceClass parent_class; void (*pre_save)(PICCommonState *s); void (*post_load)(PICCommonState *s); diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index 25acd5c34c..7195bfb96c 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -11,7 +11,7 @@ #define ISA_NUM_IRQS 16 #define TYPE_ISA_DEVICE "isa-device" -OBJECT_DECLARE_TYPE(ISADevice, ISADeviceClass, ISA_DEVICE) +OBJECT_DECLARE_SIMPLE_TYPE(ISADevice, ISA_DEVICE) #define TYPE_ISA_BUS "ISA" OBJECT_DECLARE_SIMPLE_TYPE(ISABus, ISA_BUS) @@ -48,10 +48,6 @@ struct IsaDmaClass { void *opaque); }; -struct ISADeviceClass { - DeviceClass parent_class; -}; - struct ISABus { /*< private >*/ BusState parent_obj; diff --git a/include/hw/isa/superio.h b/include/hw/isa/superio.h index b9f5c19155..0dc45104d4 100644 --- a/include/hw/isa/superio.h +++ b/include/hw/isa/superio.h @@ -44,7 +44,7 @@ typedef struct ISASuperIOFuncs { struct ISASuperIOClass { /*< private >*/ - ISADeviceClass parent_class; + DeviceClass parent_class; /*< public >*/ DeviceRealize parent_realize; diff --git a/include/hw/timer/i8254_internal.h b/include/hw/timer/i8254_internal.h index a9a600d941..1761deb4cf 100644 --- a/include/hw/timer/i8254_internal.h +++ b/include/hw/timer/i8254_internal.h @@ -58,7 +58,7 @@ struct PITCommonState { }; struct PITCommonClass { - ISADeviceClass parent_class; + DeviceClass parent_class; void (*set_channel_gate)(PITCommonState *s, PITChannelState *sc, int val); void (*get_channel_info)(PITCommonState *s, PITChannelState *sc, From 88b5877734886e9aa9331447888ec54e75f8f6aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 11:59:43 +0100 Subject: [PATCH 058/185] hw/isa: Reorder to separate ISABus* vs ISADevice* functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Separate functions taking an ISABus* argument versus functions taking a ISADevice* one. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230210163744.32182-8-philmd@linaro.org> --- include/hw/isa/isa.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index 7195bfb96c..34fa8977e9 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -70,12 +70,8 @@ struct ISADevice { ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space, MemoryRegion *address_space_io, Error **errp); void isa_bus_irqs(ISABus *bus, qemu_irq *irqs); -qemu_irq isa_get_irq(ISADevice *dev, unsigned isairq); -void isa_connect_gpio_out(ISADevice *isadev, int gpioirq, unsigned isairq); void isa_bus_dma(ISABus *bus, IsaDma *dma8, IsaDma *dma16); IsaDma *isa_get_dma(ISABus *bus, int nchan); -MemoryRegion *isa_address_space(ISADevice *dev); -MemoryRegion *isa_address_space_io(ISADevice *dev); ISADevice *isa_new(const char *name); ISADevice *isa_try_new(const char *name); bool isa_realize_and_unref(ISADevice *dev, ISABus *bus, Error **errp); @@ -83,6 +79,11 @@ ISADevice *isa_create_simple(ISABus *bus, const char *name); ISADevice *isa_vga_init(ISABus *bus); +qemu_irq isa_get_irq(ISADevice *dev, unsigned isairq); +void isa_connect_gpio_out(ISADevice *isadev, int gpioirq, unsigned isairq); +MemoryRegion *isa_address_space(ISADevice *dev); +MemoryRegion *isa_address_space_io(ISADevice *dev); + /** * isa_register_ioport: Install an I/O port region on the ISA bus. * From 23c69bb822910ccb5c78fa3bf2b6bb96a47188d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 7 Feb 2023 22:47:20 +0100 Subject: [PATCH 059/185] hw/isa: Un-inline isa_bus_from_device() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No point in inlining isa_bus_from_device() which is only used at device realization time. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230210163744.32182-9-philmd@linaro.org> Reviewed-by: Bernhard Beschow --- hw/isa/isa-bus.c | 5 +++++ include/hw/isa/isa.h | 6 +----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index 845686cb12..2464357971 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -164,6 +164,11 @@ bool isa_realize_and_unref(ISADevice *dev, ISABus *bus, Error **errp) return qdev_realize_and_unref(&dev->parent_obj, &bus->parent_obj, errp); } +ISABus *isa_bus_from_device(ISADevice *dev) +{ + return ISA_BUS(qdev_get_parent_bus(DEVICE(dev))); +} + ISADevice *isa_vga_init(ISABus *bus) { vga_interface_created = true; diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index 34fa8977e9..1d3ffec4de 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -83,6 +83,7 @@ qemu_irq isa_get_irq(ISADevice *dev, unsigned isairq); void isa_connect_gpio_out(ISADevice *isadev, int gpioirq, unsigned isairq); MemoryRegion *isa_address_space(ISADevice *dev); MemoryRegion *isa_address_space_io(ISADevice *dev); +ISABus *isa_bus_from_device(ISADevice *dev); /** * isa_register_ioport: Install an I/O port region on the ISA bus. @@ -120,9 +121,4 @@ int isa_register_portio_list(ISADevice *dev, const MemoryRegionPortio *portio, void *opaque, const char *name); -static inline ISABus *isa_bus_from_device(ISADevice *d) -{ - return ISA_BUS(qdev_get_parent_bus(DEVICE(d))); -} - #endif From 7067887ea18622e381f09b998b67095f51e4aa49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 13:32:18 +0100 Subject: [PATCH 060/185] hw/isa: Rename isa_bus_irqs() -> isa_bus_register_input_irqs() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit isa_bus_irqs() register an array of input IRQs on the ISA bus. Rename it as isa_bus_register_input_irqs(). Mechanical change using: $ sed -i -e 's/isa_bus_irqs/isa_bus_register_input_irqs/g' \ $(git grep -wl isa_bus_irqs) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230210163744.32182-10-philmd@linaro.org> --- hw/hppa/machine.c | 2 +- hw/i386/microvm.c | 2 +- hw/i386/pc_piix.c | 2 +- hw/isa/i82378.c | 2 +- hw/isa/isa-bus.c | 8 ++++---- hw/isa/lpc_ich9.c | 2 +- hw/isa/piix4.c | 2 +- hw/isa/vt82c686.c | 2 +- hw/mips/jazz.c | 2 +- hw/ppc/pnv_lpc.c | 2 +- hw/sparc64/sun4u.c | 2 +- include/hw/isa/isa.h | 4 ++-- 12 files changed, 16 insertions(+), 16 deletions(-) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 7ac68c943f..8fea5fa6b8 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -98,7 +98,7 @@ static ISABus *hppa_isa_bus(void) isa_irqs = i8259_init(isa_bus, /* qemu_allocate_irq(dino_set_isa_irq, s, 0)); */ NULL); - isa_bus_irqs(isa_bus, isa_irqs); + isa_bus_register_input_irqs(isa_bus, isa_irqs); return isa_bus; } diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index 29f30dd6d3..fed468a34d 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -174,7 +174,7 @@ static void microvm_devices_init(MicrovmMachineState *mms) isa_bus = isa_bus_new(NULL, get_system_memory(), get_system_io(), &error_abort); - isa_bus_irqs(isa_bus, x86ms->gsi); + isa_bus_register_input_irqs(isa_bus, x86ms->gsi); ioapic_init_gsi(gsi_state, "machine"); if (ioapics > 1) { diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index df64dd8dcc..7c48ba30e0 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -246,7 +246,7 @@ static void pc_init1(MachineState *machine, i8257_dma_init(isa_bus, 0); pcms->hpet_enabled = false; } - isa_bus_irqs(isa_bus, x86ms->gsi); + isa_bus_register_input_irqs(isa_bus, x86ms->gsi); if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) { pc_i8259_create(isa_bus, gsi_state->i8259_irq); diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c index d32653369d..233059c6dc 100644 --- a/hw/isa/i82378.c +++ b/hw/isa/i82378.c @@ -89,7 +89,7 @@ static void i82378_realize(PCIDevice *pci, Error **errp) /* 2 82C59 (irq) */ s->isa_irqs_in = i8259_init(isabus, s->cpu_intr); - isa_bus_irqs(isabus, s->isa_irqs_in); + isa_bus_register_input_irqs(isabus, s->isa_irqs_in); /* 1 82C54 (pit) */ pit = i8254_pit_init(isabus, 0x40, 0, NULL); diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index 2464357971..ce9999f7f7 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -67,13 +67,13 @@ ISABus *isa_bus_new(DeviceState *dev, MemoryRegion* address_space, return isabus; } -void isa_bus_irqs(ISABus *bus, qemu_irq *irqs) +void isa_bus_register_input_irqs(ISABus *bus, qemu_irq *irqs_in) { - bus->irqs = irqs; + bus->irqs_in = irqs_in; } /* - * isa_get_irq() returns the corresponding qemu_irq entry for the i8259. + * isa_get_irq() returns the corresponding input qemu_irq entry for the i8259. * * This function is only for special cases such as the 'ferr', and * temporary use for normal devices until they are converted to qdev. @@ -82,7 +82,7 @@ qemu_irq isa_get_irq(ISADevice *dev, unsigned isairq) { assert(!dev || ISA_BUS(qdev_get_parent_bus(DEVICE(dev))) == isabus); assert(isairq < ISA_NUM_IRQS); - return isabus->irqs[isairq]; + return isabus->irqs_in[isairq]; } void isa_connect_gpio_out(ISADevice *isadev, int gpioirq, unsigned isairq) diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 96fd500502..d8303d0322 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -719,7 +719,7 @@ static void ich9_lpc_realize(PCIDevice *d, Error **errp) qdev_init_gpio_out_named(dev, lpc->gsi, ICH9_GPIO_GSI, IOAPIC_NUM_PINS); - isa_bus_irqs(isa_bus, lpc->gsi); + isa_bus_register_input_irqs(isa_bus, lpc->gsi); i8257_dma_init(isa_bus, 0); diff --git a/hw/isa/piix4.c b/hw/isa/piix4.c index de60ceef73..ef24826993 100644 --- a/hw/isa/piix4.c +++ b/hw/isa/piix4.c @@ -212,7 +212,7 @@ static void piix4_realize(PCIDevice *dev, Error **errp) s->isa = i8259_init(isa_bus, *i8259_out_irq); /* initialize ISA irqs */ - isa_bus_irqs(isa_bus, s->isa); + isa_bus_register_input_irqs(isa_bus, s->isa); /* initialize pit */ i8254_pit_init(isa_bus, 0x40, 0, NULL); diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index a913a509f7..52814cc751 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -615,7 +615,7 @@ static void via_isa_realize(PCIDevice *d, Error **errp) } s->isa_irqs_in = i8259_init(isa_bus, s->cpu_intr); - isa_bus_irqs(isa_bus, s->isa_irqs_in); + isa_bus_register_input_irqs(isa_bus, s->isa_irqs_in); i8254_pit_init(isa_bus, 0x40, 0, NULL); i8257_dma_init(isa_bus, 0); diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c index 6aefe9a61b..ca4426a92c 100644 --- a/hw/mips/jazz.c +++ b/hw/mips/jazz.c @@ -249,7 +249,7 @@ static void mips_jazz_init(MachineState *machine, /* ISA devices */ i8259 = i8259_init(isa_bus, env->irq[4]); - isa_bus_irqs(isa_bus, i8259); + isa_bus_register_input_irqs(isa_bus, i8259); i8257_dma_init(isa_bus, 0); pit = i8254_pit_init(isa_bus, 0x40, 0, NULL); pcspk_init(isa_new(TYPE_PC_SPEAKER), isa_bus, pit); diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index 71143b7692..01f44c19eb 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -837,7 +837,7 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp) irqs = qemu_allocate_irqs(handler, lpc, ISA_NUM_IRQS); - isa_bus_irqs(isa_bus, irqs); + isa_bus_register_input_irqs(isa_bus, irqs); return isa_bus; } diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 8fe47e2c22..8a56ba9f98 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -316,7 +316,7 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) /* ISA bus */ s->isa_irqs_in = qemu_allocate_irqs(ebus_isa_irq_handler, s, ISA_NUM_IRQS); - isa_bus_irqs(s->isa_bus, s->isa_irqs_in); + isa_bus_register_input_irqs(s->isa_bus, s->isa_irqs_in); qdev_init_gpio_out_named(DEVICE(s), s->isa_irqs_out, "isa-irq", ISA_NUM_IRQS); diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index 1d3ffec4de..eb920aa710 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -55,7 +55,7 @@ struct ISABus { MemoryRegion *address_space; MemoryRegion *address_space_io; - qemu_irq *irqs; + qemu_irq *irqs_in; IsaDma *dma[2]; }; @@ -69,7 +69,7 @@ struct ISADevice { ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space, MemoryRegion *address_space_io, Error **errp); -void isa_bus_irqs(ISABus *bus, qemu_irq *irqs); +void isa_bus_register_input_irqs(ISABus *bus, qemu_irq *irqs_in); void isa_bus_dma(ISABus *bus, IsaDma *dma8, IsaDma *dma16); IsaDma *isa_get_dma(ISABus *bus, int nchan); ISADevice *isa_new(const char *name); From e5bf2779a1feafa85adae9a8ba22cbb54af00c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Jan 2023 12:45:54 +0100 Subject: [PATCH 061/185] hw/isa: Use isa_address_space_io() to reduce access on global 'isabus' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230210163744.32182-11-philmd@linaro.org> --- hw/isa/isa-bus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index ce9999f7f7..b3d2e5ec52 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -114,7 +114,7 @@ static inline void isa_init_ioport(ISADevice *dev, uint16_t ioport) void isa_register_ioport(ISADevice *dev, MemoryRegion *io, uint16_t start) { - memory_region_add_subregion(isabus->address_space_io, start, io); + memory_region_add_subregion(isa_address_space_io(dev), start, io); isa_init_ioport(dev, start); } @@ -135,7 +135,7 @@ int isa_register_portio_list(ISADevice *dev, isa_init_ioport(dev, start); portio_list_init(piolist, OBJECT(dev), pio_start, opaque, name); - portio_list_add(piolist, isabus->address_space_io, start); + portio_list_add(piolist, isa_address_space_io(dev), start); return 0; } From dc8d6cf2033c813ade9863a926f2d71a22edd249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 15 Feb 2023 15:38:19 +0100 Subject: [PATCH 062/185] hw/isa: Rename isa_get_dma() -> isa_bus_get_dma() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit isa_get_dma() returns a DMA channel handler from an ISABus. To emphasize this, rename it as isa_bus_get_dma(). Mechanical change using: $ sed -i -e 's/isa_get_dma/isa_bus_get_dma/g' \ $(git grep -l isa_get_dma) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20230215161641.32663-2-philmd@linaro.org> --- hw/audio/cs4231a.c | 2 +- hw/audio/gus.c | 2 +- hw/audio/sb16.c | 4 ++-- hw/block/fdc-isa.c | 2 +- hw/dma/i82374.c | 2 +- hw/isa/isa-bus.c | 2 +- include/hw/isa/isa.h | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c index 7f17a72a9c..ec066fcd89 100644 --- a/hw/audio/cs4231a.c +++ b/hw/audio/cs4231a.c @@ -671,7 +671,7 @@ static void cs4231a_realizefn (DeviceState *dev, Error **errp) CSState *s = CS4231A (dev); IsaDmaClass *k; - s->isa_dma = isa_get_dma(isa_bus_from_device(d), s->dma); + s->isa_dma = isa_bus_get_dma(isa_bus_from_device(d), s->dma); if (!s->isa_dma) { error_setg(errp, "ISA controller does not support DMA"); return; diff --git a/hw/audio/gus.c b/hw/audio/gus.c index 42f010b671..2a08a0f7d7 100644 --- a/hw/audio/gus.c +++ b/hw/audio/gus.c @@ -240,7 +240,7 @@ static void gus_realizefn (DeviceState *dev, Error **errp) IsaDmaClass *k; struct audsettings as; - s->isa_dma = isa_get_dma(isa_bus_from_device(d), s->emu.gusdma); + s->isa_dma = isa_bus_get_dma(isa_bus_from_device(d), s->emu.gusdma); if (!s->isa_dma) { error_setg(errp, "ISA controller does not support DMA"); return; diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c index 2215386ddb..ae745c7283 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -1401,8 +1401,8 @@ static void sb16_realizefn (DeviceState *dev, Error **errp) SB16State *s = SB16 (dev); IsaDmaClass *k; - s->isa_hdma = isa_get_dma(isa_bus_from_device(isadev), s->hdma); - s->isa_dma = isa_get_dma(isa_bus_from_device(isadev), s->dma); + s->isa_hdma = isa_bus_get_dma(isa_bus_from_device(isadev), s->hdma); + s->isa_dma = isa_bus_get_dma(isa_bus_from_device(isadev), s->dma); if (!s->isa_dma || !s->isa_hdma) { error_setg(errp, "ISA controller does not support DMA"); return; diff --git a/hw/block/fdc-isa.c b/hw/block/fdc-isa.c index fee1ca68a8..a5f07b668d 100644 --- a/hw/block/fdc-isa.c +++ b/hw/block/fdc-isa.c @@ -98,7 +98,7 @@ static void isabus_fdc_realize(DeviceState *dev, Error **errp) fdctrl->dma_chann = isa->dma; if (fdctrl->dma_chann != -1) { IsaDmaClass *k; - fdctrl->dma = isa_get_dma(isa_bus_from_device(isadev), isa->dma); + fdctrl->dma = isa_bus_get_dma(isa_bus_from_device(isadev), isa->dma); if (!fdctrl->dma) { error_setg(errp, "ISA controller does not support DMA"); return; diff --git a/hw/dma/i82374.c b/hw/dma/i82374.c index 34c3aaf7d3..63734c22c9 100644 --- a/hw/dma/i82374.c +++ b/hw/dma/i82374.c @@ -125,7 +125,7 @@ static void i82374_realize(DeviceState *dev, Error **errp) I82374State *s = I82374(dev); ISABus *isa_bus = isa_bus_from_device(ISA_DEVICE(dev)); - if (isa_get_dma(isa_bus, 0)) { + if (isa_bus_get_dma(isa_bus, 0)) { error_setg(errp, "DMA already initialized on ISA bus"); return; } diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index b3d2e5ec52..80d8fb4c5b 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -99,7 +99,7 @@ void isa_bus_dma(ISABus *bus, IsaDma *dma8, IsaDma *dma16) bus->dma[1] = dma16; } -IsaDma *isa_get_dma(ISABus *bus, int nchan) +IsaDma *isa_bus_get_dma(ISABus *bus, int nchan) { assert(bus); return bus->dma[nchan > 3 ? 1 : 0]; diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index eb920aa710..3a60f6afa6 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -71,7 +71,7 @@ ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space, MemoryRegion *address_space_io, Error **errp); void isa_bus_register_input_irqs(ISABus *bus, qemu_irq *irqs_in); void isa_bus_dma(ISABus *bus, IsaDma *dma8, IsaDma *dma16); -IsaDma *isa_get_dma(ISABus *bus, int nchan); +IsaDma *isa_bus_get_dma(ISABus *bus, int nchan); ISADevice *isa_new(const char *name); ISADevice *isa_try_new(const char *name); bool isa_realize_and_unref(ISADevice *dev, ISABus *bus, Error **errp); From d2fbec575f652dc35239c5b026f7ebabc548beb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 11:55:03 +0100 Subject: [PATCH 063/185] hw/isa: Factor isa_bus_get_irq() out of isa_get_irq() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit isa_get_irq() was added in commit 3a38d437ca ("Add isa_reserve_irq()" Fri Aug 14 11:36:15 2009) as: a temporary interface to be used to allocate ISA IRQs for devices which have not yet been converted to qdev, and for special cases which are not suited for qdev conversions, such as the 'ferr'. We still use it 14 years later, using the global 'isabus' singleton. In order to get rid of such *temporary* interface, extract isa_bus_get_irq() which can take any ISABus* object. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20230215161641.32663-3-philmd@linaro.org> --- hw/isa/isa-bus.c | 14 ++++++++++---- include/hw/isa/isa.h | 8 ++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index 80d8fb4c5b..a289eccfb1 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -72,6 +72,13 @@ void isa_bus_register_input_irqs(ISABus *bus, qemu_irq *irqs_in) bus->irqs_in = irqs_in; } +qemu_irq isa_bus_get_irq(ISABus *bus, unsigned irqnum) +{ + assert(irqnum < ISA_NUM_IRQS); + assert(bus->irqs_in); + return bus->irqs_in[irqnum]; +} + /* * isa_get_irq() returns the corresponding input qemu_irq entry for the i8259. * @@ -81,14 +88,13 @@ void isa_bus_register_input_irqs(ISABus *bus, qemu_irq *irqs_in) qemu_irq isa_get_irq(ISADevice *dev, unsigned isairq) { assert(!dev || ISA_BUS(qdev_get_parent_bus(DEVICE(dev))) == isabus); - assert(isairq < ISA_NUM_IRQS); - return isabus->irqs_in[isairq]; + return isa_bus_get_irq(isabus, isairq); } void isa_connect_gpio_out(ISADevice *isadev, int gpioirq, unsigned isairq) { - qemu_irq irq = isa_get_irq(isadev, isairq); - qdev_connect_gpio_out(DEVICE(isadev), gpioirq, irq); + qemu_irq input_irq = isa_get_irq(isadev, isairq); + qdev_connect_gpio_out(DEVICE(isadev), gpioirq, input_irq); } void isa_bus_dma(ISABus *bus, IsaDma *dma8, IsaDma *dma16) diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index 3a60f6afa6..40d6224a4e 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -72,6 +72,14 @@ ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space, void isa_bus_register_input_irqs(ISABus *bus, qemu_irq *irqs_in); void isa_bus_dma(ISABus *bus, IsaDma *dma8, IsaDma *dma16); IsaDma *isa_bus_get_dma(ISABus *bus, int nchan); +/** + * isa_bus_get_irq: Return input IRQ on ISA bus. + * @bus: the #ISABus to plug ISA devices on. + * @irqnum: the ISA IRQ number. + * + * Return IRQ @irqnum from the PIC associated on ISA @bus. + */ +qemu_irq isa_bus_get_irq(ISABus *bus, unsigned irqnum); ISADevice *isa_new(const char *name); ISADevice *isa_try_new(const char *name); bool isa_realize_and_unref(ISADevice *dev, ISABus *bus, Error **errp); From 8e7db8ab5179474b6277d549d62c1184681dc915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 15 Feb 2023 15:36:42 +0100 Subject: [PATCH 064/185] hw: Replace isa_get_irq() by isa_bus_get_irq() when ISABus is available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20230215161641.32663-4-philmd@linaro.org> --- hw/audio/cs4231a.c | 5 +++-- hw/audio/gus.c | 5 +++-- hw/audio/sb16.c | 7 ++++--- hw/block/fdc-isa.c | 5 +++-- include/hw/timer/i8254.h | 3 ++- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c index ec066fcd89..5c6d643732 100644 --- a/hw/audio/cs4231a.c +++ b/hw/audio/cs4231a.c @@ -668,16 +668,17 @@ static void cs4231a_initfn (Object *obj) static void cs4231a_realizefn (DeviceState *dev, Error **errp) { ISADevice *d = ISA_DEVICE (dev); + ISABus *bus = isa_bus_from_device(d); CSState *s = CS4231A (dev); IsaDmaClass *k; - s->isa_dma = isa_bus_get_dma(isa_bus_from_device(d), s->dma); + s->isa_dma = isa_bus_get_dma(bus, s->dma); if (!s->isa_dma) { error_setg(errp, "ISA controller does not support DMA"); return; } - s->pic = isa_get_irq(d, s->irq); + s->pic = isa_bus_get_irq(bus, s->irq); k = ISADMA_GET_CLASS(s->isa_dma); k->register_channel(s->isa_dma, s->dma, cs_dma_read, s); diff --git a/hw/audio/gus.c b/hw/audio/gus.c index 2a08a0f7d7..787345ce54 100644 --- a/hw/audio/gus.c +++ b/hw/audio/gus.c @@ -236,11 +236,12 @@ static const MemoryRegionPortio gus_portio_list2[] = { static void gus_realizefn (DeviceState *dev, Error **errp) { ISADevice *d = ISA_DEVICE(dev); + ISABus *bus = isa_bus_from_device(d); GUSState *s = GUS (dev); IsaDmaClass *k; struct audsettings as; - s->isa_dma = isa_bus_get_dma(isa_bus_from_device(d), s->emu.gusdma); + s->isa_dma = isa_bus_get_dma(bus, s->emu.gusdma); if (!s->isa_dma) { error_setg(errp, "ISA controller does not support DMA"); return; @@ -282,7 +283,7 @@ static void gus_realizefn (DeviceState *dev, Error **errp) s->emu.himemaddr = s->himem; s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32; s->emu.opaque = s; - s->pic = isa_get_irq(d, s->emu.gusirq); + s->pic = isa_bus_get_irq(bus, s->emu.gusirq); AUD_set_active_out (s->voice, 1); } diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c index ae745c7283..535ccccdc9 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -1398,17 +1398,18 @@ static void sb16_initfn (Object *obj) static void sb16_realizefn (DeviceState *dev, Error **errp) { ISADevice *isadev = ISA_DEVICE (dev); + ISABus *bus = isa_bus_from_device(isadev); SB16State *s = SB16 (dev); IsaDmaClass *k; - s->isa_hdma = isa_bus_get_dma(isa_bus_from_device(isadev), s->hdma); - s->isa_dma = isa_bus_get_dma(isa_bus_from_device(isadev), s->dma); + s->isa_hdma = isa_bus_get_dma(bus, s->hdma); + s->isa_dma = isa_bus_get_dma(bus, s->dma); if (!s->isa_dma || !s->isa_hdma) { error_setg(errp, "ISA controller does not support DMA"); return; } - s->pic = isa_get_irq(isadev, s->irq); + s->pic = isa_bus_get_irq(bus, s->irq); s->mixer_regs[0x80] = magic_of_irq (s->irq); s->mixer_regs[0x81] = (1 << s->dma) | (1 << s->hdma); diff --git a/hw/block/fdc-isa.c b/hw/block/fdc-isa.c index a5f07b668d..7ec075e470 100644 --- a/hw/block/fdc-isa.c +++ b/hw/block/fdc-isa.c @@ -86,6 +86,7 @@ static const MemoryRegionPortio fdc_portio_list[] = { static void isabus_fdc_realize(DeviceState *dev, Error **errp) { ISADevice *isadev = ISA_DEVICE(dev); + ISABus *bus = isa_bus_from_device(isadev); FDCtrlISABus *isa = ISA_FDC(dev); FDCtrl *fdctrl = &isa->state; Error *err = NULL; @@ -94,11 +95,11 @@ static void isabus_fdc_realize(DeviceState *dev, Error **errp) isa->iobase, fdc_portio_list, fdctrl, "fdc"); - fdctrl->irq = isa_get_irq(isadev, isa->irq); + fdctrl->irq = isa_bus_get_irq(bus, isa->irq); fdctrl->dma_chann = isa->dma; if (fdctrl->dma_chann != -1) { IsaDmaClass *k; - fdctrl->dma = isa_bus_get_dma(isa_bus_from_device(isadev), isa->dma); + fdctrl->dma = isa_bus_get_dma(bus, isa->dma); if (!fdctrl->dma) { error_setg(errp, "ISA controller does not support DMA"); return; diff --git a/include/hw/timer/i8254.h b/include/hw/timer/i8254.h index 3e569f42b6..8402caad30 100644 --- a/include/hw/timer/i8254.h +++ b/include/hw/timer/i8254.h @@ -56,7 +56,8 @@ static inline ISADevice *i8254_pit_init(ISABus *bus, int base, int isa_irq, qdev_prop_set_uint32(dev, "iobase", base); isa_realize_and_unref(d, bus, &error_fatal); qdev_connect_gpio_out(dev, 0, - isa_irq >= 0 ? isa_get_irq(d, isa_irq) : alt_irq); + isa_irq >= 0 ? isa_bus_get_irq(bus, isa_irq) + : alt_irq); return d; } From 8df7129790896ffde2f9cb1005a0ac8cf1005280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 11 Feb 2023 00:17:03 +0100 Subject: [PATCH 065/185] hw/rtc/mc146818rtc: Rename RTCState -> MC146818RtcState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RTCState only represents a Motorola MC146818 model, not any RTC chipset. Rename the structure as MC146818RtcState using: $ sed -i -e s/RTCState/MC146818RtcState/g $(git grep -wl RTCState) Reviewed-by: Richard Henderson Message-Id: <20230210233116.80311-2-philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/isa/piix4.c | 2 +- hw/isa/vt82c686.c | 2 +- hw/rtc/mc146818rtc.c | 119 ++++++++++++++++++----------------- include/hw/rtc/mc146818rtc.h | 6 +- 4 files changed, 65 insertions(+), 64 deletions(-) diff --git a/hw/isa/piix4.c b/hw/isa/piix4.c index ef24826993..e0b149f8eb 100644 --- a/hw/isa/piix4.c +++ b/hw/isa/piix4.c @@ -47,7 +47,7 @@ struct PIIX4State { qemu_irq cpu_intr; qemu_irq *isa; - RTCState rtc; + MC146818RtcState rtc; PCIIDEState ide; UHCIState uhci; PIIX4PMState pm; diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index 52814cc751..f4c40965cd 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -550,7 +550,7 @@ struct ViaISAState { qemu_irq cpu_intr; qemu_irq *isa_irqs_in; ViaSuperIOState via_sio; - RTCState rtc; + MC146818RtcState rtc; PCIIDEState ide; UHCIState uhci[2]; ViaPMState pm; diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index ba612a151d..c285a53286 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -71,19 +71,19 @@ #define RTC_ISA_BASE 0x70 -static void rtc_set_time(RTCState *s); -static void rtc_update_time(RTCState *s); -static void rtc_set_cmos(RTCState *s, const struct tm *tm); -static inline int rtc_from_bcd(RTCState *s, int a); -static uint64_t get_next_alarm(RTCState *s); +static void rtc_set_time(MC146818RtcState *s); +static void rtc_update_time(MC146818RtcState *s); +static void rtc_set_cmos(MC146818RtcState *s, const struct tm *tm); +static inline int rtc_from_bcd(MC146818RtcState *s, int a); +static uint64_t get_next_alarm(MC146818RtcState *s); -static inline bool rtc_running(RTCState *s) +static inline bool rtc_running(MC146818RtcState *s) { return (!(s->cmos_data[RTC_REG_B] & REG_B_SET) && (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20); } -static uint64_t get_guest_rtc_ns(RTCState *s) +static uint64_t get_guest_rtc_ns(MC146818RtcState *s) { uint64_t guest_clock = qemu_clock_get_ns(rtc_clock); @@ -91,7 +91,7 @@ static uint64_t get_guest_rtc_ns(RTCState *s) guest_clock - s->last_update + s->offset; } -static void rtc_coalesced_timer_update(RTCState *s) +static void rtc_coalesced_timer_update(MC146818RtcState *s) { if (s->irq_coalesced == 0) { timer_del(s->coalesced_timer); @@ -104,19 +104,19 @@ static void rtc_coalesced_timer_update(RTCState *s) } } -static QLIST_HEAD(, RTCState) rtc_devices = +static QLIST_HEAD(, MC146818RtcState) rtc_devices = QLIST_HEAD_INITIALIZER(rtc_devices); void qmp_rtc_reset_reinjection(Error **errp) { - RTCState *s; + MC146818RtcState *s; QLIST_FOREACH(s, &rtc_devices, link) { s->irq_coalesced = 0; } } -static bool rtc_policy_slew_deliver_irq(RTCState *s) +static bool rtc_policy_slew_deliver_irq(MC146818RtcState *s) { kvm_reset_irq_delivered(); qemu_irq_raise(s->irq); @@ -125,7 +125,7 @@ static bool rtc_policy_slew_deliver_irq(RTCState *s) static void rtc_coalesced_timer(void *opaque) { - RTCState *s = opaque; + MC146818RtcState *s = opaque; if (s->irq_coalesced != 0) { s->cmos_data[RTC_REG_C] |= 0xc0; @@ -140,7 +140,7 @@ static void rtc_coalesced_timer(void *opaque) rtc_coalesced_timer_update(s); } -static uint32_t rtc_periodic_clock_ticks(RTCState *s) +static uint32_t rtc_periodic_clock_ticks(MC146818RtcState *s) { int period_code; @@ -157,8 +157,8 @@ static uint32_t rtc_periodic_clock_ticks(RTCState *s) * handle periodic timer. @old_period indicates the periodic timer update * is just due to period adjustment. */ -static void -periodic_timer_update(RTCState *s, int64_t current_time, uint32_t old_period, bool period_change) +static void periodic_timer_update(MC146818RtcState *s, int64_t current_time, + uint32_t old_period, bool period_change) { uint32_t period; int64_t cur_clock, next_irq_clock, lost_clock = 0; @@ -234,7 +234,7 @@ periodic_timer_update(RTCState *s, int64_t current_time, uint32_t old_period, bo static void rtc_periodic_timer(void *opaque) { - RTCState *s = opaque; + MC146818RtcState *s = opaque; periodic_timer_update(s, s->next_periodic_time, s->period, false); s->cmos_data[RTC_REG_C] |= REG_C_PF; @@ -255,7 +255,7 @@ static void rtc_periodic_timer(void *opaque) } /* handle update-ended timer */ -static void check_update_timer(RTCState *s) +static void check_update_timer(MC146818RtcState *s) { uint64_t next_update_time; uint64_t guest_nsec; @@ -306,7 +306,7 @@ static void check_update_timer(RTCState *s) } } -static inline uint8_t convert_hour(RTCState *s, uint8_t hour) +static inline uint8_t convert_hour(MC146818RtcState *s, uint8_t hour) { if (!(s->cmos_data[RTC_REG_B] & REG_B_24H)) { hour %= 12; @@ -317,7 +317,7 @@ static inline uint8_t convert_hour(RTCState *s, uint8_t hour) return hour; } -static uint64_t get_next_alarm(RTCState *s) +static uint64_t get_next_alarm(MC146818RtcState *s) { int32_t alarm_sec, alarm_min, alarm_hour, cur_hour, cur_min, cur_sec; int32_t hour, min, sec; @@ -410,7 +410,7 @@ static uint64_t get_next_alarm(RTCState *s) static void rtc_update_timer(void *opaque) { - RTCState *s = opaque; + MC146818RtcState *s = opaque; int32_t irqs = REG_C_UF; int32_t new_irqs; @@ -439,7 +439,7 @@ static void rtc_update_timer(void *opaque) static void cmos_ioport_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { - RTCState *s = opaque; + MC146818RtcState *s = opaque; uint32_t old_period; bool update_periodic_timer; @@ -557,7 +557,7 @@ static void cmos_ioport_write(void *opaque, hwaddr addr, } } -static inline int rtc_to_bcd(RTCState *s, int a) +static inline int rtc_to_bcd(MC146818RtcState *s, int a) { if (s->cmos_data[RTC_REG_B] & REG_B_DM) { return a; @@ -566,7 +566,7 @@ static inline int rtc_to_bcd(RTCState *s, int a) } } -static inline int rtc_from_bcd(RTCState *s, int a) +static inline int rtc_from_bcd(MC146818RtcState *s, int a) { if ((a & 0xc0) == 0xc0) { return -1; @@ -578,7 +578,7 @@ static inline int rtc_from_bcd(RTCState *s, int a) } } -static void rtc_get_time(RTCState *s, struct tm *tm) +static void rtc_get_time(MC146818RtcState *s, struct tm *tm) { tm->tm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]); tm->tm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]); @@ -597,7 +597,7 @@ static void rtc_get_time(RTCState *s, struct tm *tm) rtc_from_bcd(s, s->cmos_data[RTC_CENTURY]) * 100 - 1900; } -static void rtc_set_time(RTCState *s) +static void rtc_set_time(MC146818RtcState *s) { struct tm tm; g_autofree const char *qom_path = object_get_canonical_path(OBJECT(s)); @@ -609,7 +609,7 @@ static void rtc_set_time(RTCState *s) qapi_event_send_rtc_change(qemu_timedate_diff(&tm), qom_path); } -static void rtc_set_cmos(RTCState *s, const struct tm *tm) +static void rtc_set_cmos(MC146818RtcState *s, const struct tm *tm) { int year; @@ -633,7 +633,7 @@ static void rtc_set_cmos(RTCState *s, const struct tm *tm) s->cmos_data[RTC_CENTURY] = rtc_to_bcd(s, year / 100); } -static void rtc_update_time(RTCState *s) +static void rtc_update_time(MC146818RtcState *s) { struct tm ret; time_t guest_sec; @@ -649,7 +649,7 @@ static void rtc_update_time(RTCState *s) } } -static int update_in_progress(RTCState *s) +static int update_in_progress(MC146818RtcState *s) { int64_t guest_nsec; @@ -678,7 +678,7 @@ static int update_in_progress(RTCState *s) static uint64_t cmos_ioport_read(void *opaque, hwaddr addr, unsigned size) { - RTCState *s = opaque; + MC146818RtcState *s = opaque; int ret; if ((addr & 1) == 0) { return 0xff; @@ -741,21 +741,21 @@ static uint64_t cmos_ioport_read(void *opaque, hwaddr addr, void rtc_set_memory(ISADevice *dev, int addr, int val) { - RTCState *s = MC146818_RTC(dev); + MC146818RtcState *s = MC146818_RTC(dev); if (addr >= 0 && addr <= 127) s->cmos_data[addr] = val; } int rtc_get_memory(ISADevice *dev, int addr) { - RTCState *s = MC146818_RTC(dev); + MC146818RtcState *s = MC146818_RTC(dev); assert(addr >= 0 && addr <= 127); return s->cmos_data[addr]; } static void rtc_set_date_from_host(ISADevice *dev) { - RTCState *s = MC146818_RTC(dev); + MC146818RtcState *s = MC146818_RTC(dev); struct tm tm; qemu_get_timedate(&tm, 0); @@ -770,7 +770,7 @@ static void rtc_set_date_from_host(ISADevice *dev) static int rtc_pre_save(void *opaque) { - RTCState *s = opaque; + MC146818RtcState *s = opaque; rtc_update_time(s); @@ -779,7 +779,7 @@ static int rtc_pre_save(void *opaque) static int rtc_post_load(void *opaque, int version_id) { - RTCState *s = opaque; + MC146818RtcState *s = opaque; if (version_id <= 2 || rtc_clock == QEMU_CLOCK_REALTIME) { rtc_set_time(s); @@ -810,7 +810,7 @@ static int rtc_post_load(void *opaque, int version_id) static bool rtc_irq_reinject_on_ack_count_needed(void *opaque) { - RTCState *s = (RTCState *)opaque; + MC146818RtcState *s = (MC146818RtcState *)opaque; return s->irq_reinject_on_ack_count != 0; } @@ -820,7 +820,7 @@ static const VMStateDescription vmstate_rtc_irq_reinject_on_ack_count = { .minimum_version_id = 1, .needed = rtc_irq_reinject_on_ack_count_needed, .fields = (VMStateField[]) { - VMSTATE_UINT16(irq_reinject_on_ack_count, RTCState), + VMSTATE_UINT16(irq_reinject_on_ack_count, MC146818RtcState), VMSTATE_END_OF_LIST() } }; @@ -832,19 +832,19 @@ static const VMStateDescription vmstate_rtc = { .pre_save = rtc_pre_save, .post_load = rtc_post_load, .fields = (VMStateField[]) { - VMSTATE_BUFFER(cmos_data, RTCState), - VMSTATE_UINT8(cmos_index, RTCState), + VMSTATE_BUFFER(cmos_data, MC146818RtcState), + VMSTATE_UINT8(cmos_index, MC146818RtcState), VMSTATE_UNUSED(7*4), - VMSTATE_TIMER_PTR(periodic_timer, RTCState), - VMSTATE_INT64(next_periodic_time, RTCState), + VMSTATE_TIMER_PTR(periodic_timer, MC146818RtcState), + VMSTATE_INT64(next_periodic_time, MC146818RtcState), VMSTATE_UNUSED(3*8), - VMSTATE_UINT32_V(irq_coalesced, RTCState, 2), - VMSTATE_UINT32_V(period, RTCState, 2), - VMSTATE_UINT64_V(base_rtc, RTCState, 3), - VMSTATE_UINT64_V(last_update, RTCState, 3), - VMSTATE_INT64_V(offset, RTCState, 3), - VMSTATE_TIMER_PTR_V(update_timer, RTCState, 3), - VMSTATE_UINT64_V(next_alarm_time, RTCState, 3), + VMSTATE_UINT32_V(irq_coalesced, MC146818RtcState, 2), + VMSTATE_UINT32_V(period, MC146818RtcState, 2), + VMSTATE_UINT64_V(base_rtc, MC146818RtcState, 3), + VMSTATE_UINT64_V(last_update, MC146818RtcState, 3), + VMSTATE_INT64_V(offset, MC146818RtcState, 3), + VMSTATE_TIMER_PTR_V(update_timer, MC146818RtcState, 3), + VMSTATE_UINT64_V(next_alarm_time, MC146818RtcState, 3), VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription*[]) { @@ -857,7 +857,8 @@ static const VMStateDescription vmstate_rtc = { BIOS will read it and start S3 resume at POST Entry */ static void rtc_notify_suspend(Notifier *notifier, void *data) { - RTCState *s = container_of(notifier, RTCState, suspend_notifier); + MC146818RtcState *s = container_of(notifier, MC146818RtcState, + suspend_notifier); rtc_set_memory(ISA_DEVICE(s), 0xF, 0xFE); } @@ -873,7 +874,7 @@ static const MemoryRegionOps cmos_ops = { static void rtc_get_date(Object *obj, struct tm *current_tm, Error **errp) { - RTCState *s = MC146818_RTC(obj); + MC146818RtcState *s = MC146818_RTC(obj); rtc_update_time(s); rtc_get_time(s, current_tm); @@ -882,7 +883,7 @@ static void rtc_get_date(Object *obj, struct tm *current_tm, Error **errp) static void rtc_realizefn(DeviceState *dev, Error **errp) { ISADevice *isadev = ISA_DEVICE(dev); - RTCState *s = MC146818_RTC(dev); + MC146818RtcState *s = MC146818_RTC(dev); s->cmos_data[RTC_REG_A] = 0x26; s->cmos_data[RTC_REG_B] = 0x02; @@ -949,7 +950,7 @@ ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq) { DeviceState *dev; ISADevice *isadev; - RTCState *s; + MC146818RtcState *s; isadev = isa_new(TYPE_MC146818_RTC); dev = DEVICE(isadev); @@ -969,17 +970,17 @@ ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq) } static Property mc146818rtc_properties[] = { - DEFINE_PROP_INT32("base_year", RTCState, base_year, 1980), - DEFINE_PROP_UINT16("iobase", RTCState, io_base, RTC_ISA_BASE), - DEFINE_PROP_UINT8("irq", RTCState, isairq, RTC_ISA_IRQ), - DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", RTCState, + DEFINE_PROP_INT32("base_year", MC146818RtcState, base_year, 1980), + DEFINE_PROP_UINT16("iobase", MC146818RtcState, io_base, RTC_ISA_BASE), + DEFINE_PROP_UINT8("irq", MC146818RtcState, isairq, RTC_ISA_IRQ), + DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", MC146818RtcState, lost_tick_policy, LOST_TICK_POLICY_DISCARD), DEFINE_PROP_END_OF_LIST(), }; static void rtc_reset_enter(Object *obj, ResetType type) { - RTCState *s = MC146818_RTC(obj); + MC146818RtcState *s = MC146818_RTC(obj); /* Reason: VM do suspend self will set 0xfe * Reset any values other than 0xfe(Guest suspend case) */ @@ -1000,14 +1001,14 @@ static void rtc_reset_enter(Object *obj, ResetType type) static void rtc_reset_hold(Object *obj) { - RTCState *s = MC146818_RTC(obj); + MC146818RtcState *s = MC146818_RTC(obj); qemu_irq_lower(s->irq); } static void rtc_build_aml(AcpiDevAmlIf *adev, Aml *scope) { - RTCState *s = MC146818_RTC(adev); + MC146818RtcState *s = MC146818_RTC(adev); Aml *dev; Aml *crs; @@ -1045,7 +1046,7 @@ static void rtc_class_initfn(ObjectClass *klass, void *data) static const TypeInfo mc146818rtc_info = { .name = TYPE_MC146818_RTC, .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(RTCState), + .instance_size = sizeof(MC146818RtcState), .class_init = rtc_class_initfn, .interfaces = (InterfaceInfo[]) { { TYPE_ACPI_DEV_AML_IF }, diff --git a/include/hw/rtc/mc146818rtc.h b/include/hw/rtc/mc146818rtc.h index 45bcd6f040..11631af7e3 100644 --- a/include/hw/rtc/mc146818rtc.h +++ b/include/hw/rtc/mc146818rtc.h @@ -16,9 +16,9 @@ #include "qom/object.h" #define TYPE_MC146818_RTC "mc146818rtc" -OBJECT_DECLARE_SIMPLE_TYPE(RTCState, MC146818_RTC) +OBJECT_DECLARE_SIMPLE_TYPE(MC146818RtcState, MC146818_RTC) -struct RTCState { +struct MC146818RtcState { ISADevice parent_obj; MemoryRegion io; @@ -46,7 +46,7 @@ struct RTCState { Notifier clock_reset_notifier; LostTickPolicy lost_tick_policy; Notifier suspend_notifier; - QLIST_ENTRY(RTCState) link; + QLIST_ENTRY(MC146818RtcState) link; }; #define RTC_ISA_IRQ 8 From 55c86cb8038d8a2db792712fd27b4f486ee09c7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 11 Feb 2023 00:17:51 +0100 Subject: [PATCH 066/185] hw/rtc/mc146818rtc: Pass MC146818RtcState instead of ISADevice argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rtc_get_memory() and rtc_set_memory() methods can not take any TYPE_ISA_DEVICE object. They expect a TYPE_MC146818_RTC one. Simplify the API by passing a MC146818RtcState. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230210233116.80311-3-philmd@linaro.org> --- hw/i386/microvm.c | 6 ++---- hw/i386/pc.c | 16 +++++++++------- hw/i386/x86.c | 4 +++- hw/ppc/prep.c | 3 +-- hw/rtc/mc146818rtc.c | 13 ++++++------- include/hw/rtc/mc146818rtc.h | 8 ++++---- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index fed468a34d..2349c4a0ab 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -57,7 +57,7 @@ #define MICROVM_QBOOT_FILENAME "qboot.rom" #define MICROVM_BIOS_FILENAME "bios-microvm.bin" -static void microvm_set_rtc(MicrovmMachineState *mms, ISADevice *s) +static void microvm_set_rtc(MicrovmMachineState *mms, MC146818RtcState *s) { X86MachineState *x86ms = X86_MACHINE(mms); int val; @@ -161,7 +161,6 @@ static void microvm_devices_init(MicrovmMachineState *mms) const char *default_firmware; X86MachineState *x86ms = X86_MACHINE(mms); ISABus *isa_bus; - ISADevice *rtc_state; GSIState *gsi_state; int ioapics; int i; @@ -267,8 +266,7 @@ static void microvm_devices_init(MicrovmMachineState *mms) if (mms->rtc == ON_OFF_AUTO_ON || (mms->rtc == ON_OFF_AUTO_AUTO && !kvm_enabled())) { - rtc_state = mc146818_rtc_init(isa_bus, 2000, NULL); - microvm_set_rtc(mms, rtc_state); + microvm_set_rtc(mms, mc146818_rtc_init(isa_bus, 2000, NULL)); } if (mms->isa_serial) { diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 7ad71b19aa..91f195032c 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -438,7 +438,7 @@ static uint64_t ioportF0_read(void *opaque, hwaddr addr, unsigned size) #define REG_EQUIPMENT_BYTE 0x14 -static void cmos_init_hd(ISADevice *s, int type_ofs, int info_ofs, +static void cmos_init_hd(MC146818RtcState *s, int type_ofs, int info_ofs, int16_t cylinders, int8_t heads, int8_t sectors) { rtc_set_memory(s, type_ofs, 47); @@ -470,7 +470,8 @@ static int boot_device2nibble(char boot_device) return 0; } -static void set_boot_dev(ISADevice *s, const char *boot_device, Error **errp) +static void set_boot_dev(MC146818RtcState *s, const char *boot_device, + Error **errp) { #define PC_MAX_BOOT_DEVICES 3 int nbds, bds[3] = { 0, }; @@ -498,7 +499,7 @@ static void pc_boot_set(void *opaque, const char *boot_device, Error **errp) set_boot_dev(opaque, boot_device, errp); } -static void pc_cmos_init_floppy(ISADevice *rtc_state, ISADevice *floppy) +static void pc_cmos_init_floppy(MC146818RtcState *rtc_state, ISADevice *floppy) { int val, nb, i; FloppyDriveType fd_type[2] = { FLOPPY_DRIVE_TYPE_NONE, @@ -536,7 +537,7 @@ static void pc_cmos_init_floppy(ISADevice *rtc_state, ISADevice *floppy) } typedef struct pc_cmos_init_late_arg { - ISADevice *rtc_state; + MC146818RtcState *rtc_state; BusState *idebus[2]; } pc_cmos_init_late_arg; @@ -603,7 +604,7 @@ static ISADevice *pc_find_fdc0(void) static void pc_cmos_init_late(void *opaque) { pc_cmos_init_late_arg *arg = opaque; - ISADevice *s = arg->rtc_state; + MC146818RtcState *s = arg->rtc_state; int16_t cylinders; int8_t heads, sectors; int val; @@ -645,11 +646,12 @@ static void pc_cmos_init_late(void *opaque) void pc_cmos_init(PCMachineState *pcms, BusState *idebus0, BusState *idebus1, - ISADevice *s) + ISADevice *rtc) { int val; static pc_cmos_init_late_arg arg; X86MachineState *x86ms = X86_MACHINE(pcms); + MC146818RtcState *s = MC146818_RTC(rtc); /* various important CMOS locations needed by PC/Bochs bios */ @@ -1303,7 +1305,7 @@ void pc_basic_device_init(struct PCMachineState *pcms, pit_alt_irq = qdev_get_gpio_in(hpet, HPET_LEGACY_PIT_INT); rtc_irq = qdev_get_gpio_in(hpet, HPET_LEGACY_RTC_INT); } - *rtc_state = mc146818_rtc_init(isa_bus, 2000, rtc_irq); + *rtc_state = ISA_DEVICE(mc146818_rtc_init(isa_bus, 2000, rtc_irq)); qemu_register_boot_set(pc_boot_set, *rtc_state); diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 5f28dc8390..38d2ec0581 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -150,8 +150,10 @@ void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version) } } -void x86_rtc_set_cpus_count(ISADevice *rtc, uint16_t cpus_count) +void x86_rtc_set_cpus_count(ISADevice *s, uint16_t cpus_count) { + MC146818RtcState *rtc = MC146818_RTC(s); + if (cpus_count > 0xff) { /* * If the number of CPUs can't be represented in 8 bits, the diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index fcbe4c5837..076e2d0d22 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -212,10 +212,9 @@ static int PPC_NVRAM_set_params (Nvram *nvram, uint16_t NVRAM_size, static int prep_set_cmos_checksum(DeviceState *dev, void *opaque) { uint16_t checksum = *(uint16_t *)opaque; - ISADevice *rtc; if (object_dynamic_cast(OBJECT(dev), TYPE_MC146818_RTC)) { - rtc = ISA_DEVICE(dev); + MC146818RtcState *rtc = MC146818_RTC(dev); rtc_set_memory(rtc, 0x2e, checksum & 0xff); rtc_set_memory(rtc, 0x3e, checksum & 0xff); rtc_set_memory(rtc, 0x2f, checksum >> 8); diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index c285a53286..b4d7777634 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -739,16 +739,14 @@ static uint64_t cmos_ioport_read(void *opaque, hwaddr addr, } } -void rtc_set_memory(ISADevice *dev, int addr, int val) +void rtc_set_memory(MC146818RtcState *s, int addr, int val) { - MC146818RtcState *s = MC146818_RTC(dev); if (addr >= 0 && addr <= 127) s->cmos_data[addr] = val; } -int rtc_get_memory(ISADevice *dev, int addr) +int rtc_get_memory(MC146818RtcState *s, int addr) { - MC146818RtcState *s = MC146818_RTC(dev); assert(addr >= 0 && addr <= 127); return s->cmos_data[addr]; } @@ -859,7 +857,7 @@ static void rtc_notify_suspend(Notifier *notifier, void *data) { MC146818RtcState *s = container_of(notifier, MC146818RtcState, suspend_notifier); - rtc_set_memory(ISA_DEVICE(s), 0xF, 0xFE); + rtc_set_memory(s, 0xF, 0xFE); } static const MemoryRegionOps cmos_ops = { @@ -946,7 +944,8 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) QLIST_INSERT_HEAD(&rtc_devices, s, link); } -ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq) +MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year, + qemu_irq intercept_irq) { DeviceState *dev; ISADevice *isadev; @@ -966,7 +965,7 @@ ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq) object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(isadev), "date"); - return isadev; + return s; } static Property mc146818rtc_properties[] = { diff --git a/include/hw/rtc/mc146818rtc.h b/include/hw/rtc/mc146818rtc.h index 11631af7e3..a6b0c135c0 100644 --- a/include/hw/rtc/mc146818rtc.h +++ b/include/hw/rtc/mc146818rtc.h @@ -51,10 +51,10 @@ struct MC146818RtcState { #define RTC_ISA_IRQ 8 -ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, - qemu_irq intercept_irq); -void rtc_set_memory(ISADevice *dev, int addr, int val); -int rtc_get_memory(ISADevice *dev, int addr); +MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year, + qemu_irq intercept_irq); +void rtc_set_memory(MC146818RtcState *s, int addr, int val); +int rtc_get_memory(MC146818RtcState *s, int addr); void qmp_rtc_reset_reinjection(Error **errp); #endif /* HW_RTC_MC146818RTC_H */ From 2d4bd81e396fb7d5f9af0c55be78a6af2f1fd19c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 11 Feb 2023 00:18:53 +0100 Subject: [PATCH 067/185] hw/rtc: Rename rtc_[get|set]_memory -> mc146818rtc_[get|set]_cmos_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rtc_get_memory() and rtc_set_memory() helpers only work with TYPE_MC146818_RTC devices. 'memory' in their name refer to the CMOS region. Rename them as mc146818rtc_get_cmos_data() and mc146818rtc_set_cmos_data() to be explicit about what they are doing. Mechanical change doing: $ sed -i -e 's/rtc_set_memory/mc146818rtc_set_cmos_data/g' \ $(git grep -wl rtc_set_memory) $ sed -i -e 's/rtc_get_memory/mc146818rtc_get_cmos_data/g' \ $(git grep -wl rtc_get_memory) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230210233116.80311-4-philmd@linaro.org> --- hw/i386/microvm.c | 22 +++++++------- hw/i386/pc.c | 58 ++++++++++++++++++------------------ hw/i386/x86.c | 4 +-- hw/ppc/prep.c | 8 ++--- hw/rtc/mc146818rtc.c | 6 ++-- include/hw/rtc/mc146818rtc.h | 4 +-- 6 files changed, 51 insertions(+), 51 deletions(-) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index 2349c4a0ab..68c22016d2 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -63,8 +63,8 @@ static void microvm_set_rtc(MicrovmMachineState *mms, MC146818RtcState *s) int val; val = MIN(x86ms->below_4g_mem_size / KiB, 640); - rtc_set_memory(s, 0x15, val); - rtc_set_memory(s, 0x16, val >> 8); + mc146818rtc_set_cmos_data(s, 0x15, val); + mc146818rtc_set_cmos_data(s, 0x16, val >> 8); /* extended memory (next 64MiB) */ if (x86ms->below_4g_mem_size > 1 * MiB) { val = (x86ms->below_4g_mem_size - 1 * MiB) / KiB; @@ -74,10 +74,10 @@ static void microvm_set_rtc(MicrovmMachineState *mms, MC146818RtcState *s) if (val > 65535) { val = 65535; } - rtc_set_memory(s, 0x17, val); - rtc_set_memory(s, 0x18, val >> 8); - rtc_set_memory(s, 0x30, val); - rtc_set_memory(s, 0x31, val >> 8); + mc146818rtc_set_cmos_data(s, 0x17, val); + mc146818rtc_set_cmos_data(s, 0x18, val >> 8); + mc146818rtc_set_cmos_data(s, 0x30, val); + mc146818rtc_set_cmos_data(s, 0x31, val >> 8); /* memory between 16MiB and 4GiB */ if (x86ms->below_4g_mem_size > 16 * MiB) { val = (x86ms->below_4g_mem_size - 16 * MiB) / (64 * KiB); @@ -87,13 +87,13 @@ static void microvm_set_rtc(MicrovmMachineState *mms, MC146818RtcState *s) if (val > 65535) { val = 65535; } - rtc_set_memory(s, 0x34, val); - rtc_set_memory(s, 0x35, val >> 8); + mc146818rtc_set_cmos_data(s, 0x34, val); + mc146818rtc_set_cmos_data(s, 0x35, val >> 8); /* memory above 4GiB */ val = x86ms->above_4g_mem_size / 65536; - rtc_set_memory(s, 0x5b, val); - rtc_set_memory(s, 0x5c, val >> 8); - rtc_set_memory(s, 0x5d, val >> 16); + mc146818rtc_set_cmos_data(s, 0x5b, val); + mc146818rtc_set_cmos_data(s, 0x5c, val >> 8); + mc146818rtc_set_cmos_data(s, 0x5d, val >> 16); } static void create_gpex(MicrovmMachineState *mms) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 91f195032c..98c2becc11 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -441,16 +441,16 @@ static uint64_t ioportF0_read(void *opaque, hwaddr addr, unsigned size) static void cmos_init_hd(MC146818RtcState *s, int type_ofs, int info_ofs, int16_t cylinders, int8_t heads, int8_t sectors) { - rtc_set_memory(s, type_ofs, 47); - rtc_set_memory(s, info_ofs, cylinders); - rtc_set_memory(s, info_ofs + 1, cylinders >> 8); - rtc_set_memory(s, info_ofs + 2, heads); - rtc_set_memory(s, info_ofs + 3, 0xff); - rtc_set_memory(s, info_ofs + 4, 0xff); - rtc_set_memory(s, info_ofs + 5, 0xc0 | ((heads > 8) << 3)); - rtc_set_memory(s, info_ofs + 6, cylinders); - rtc_set_memory(s, info_ofs + 7, cylinders >> 8); - rtc_set_memory(s, info_ofs + 8, sectors); + mc146818rtc_set_cmos_data(s, type_ofs, 47); + mc146818rtc_set_cmos_data(s, info_ofs, cylinders); + mc146818rtc_set_cmos_data(s, info_ofs + 1, cylinders >> 8); + mc146818rtc_set_cmos_data(s, info_ofs + 2, heads); + mc146818rtc_set_cmos_data(s, info_ofs + 3, 0xff); + mc146818rtc_set_cmos_data(s, info_ofs + 4, 0xff); + mc146818rtc_set_cmos_data(s, info_ofs + 5, 0xc0 | ((heads > 8) << 3)); + mc146818rtc_set_cmos_data(s, info_ofs + 6, cylinders); + mc146818rtc_set_cmos_data(s, info_ofs + 7, cylinders >> 8); + mc146818rtc_set_cmos_data(s, info_ofs + 8, sectors); } /* convert boot_device letter to something recognizable by the bios */ @@ -490,8 +490,8 @@ static void set_boot_dev(MC146818RtcState *s, const char *boot_device, return; } } - rtc_set_memory(s, 0x3d, (bds[1] << 4) | bds[0]); - rtc_set_memory(s, 0x38, (bds[2] << 4) | (fd_bootchk ? 0x0 : 0x1)); + mc146818rtc_set_cmos_data(s, 0x3d, (bds[1] << 4) | bds[0]); + mc146818rtc_set_cmos_data(s, 0x38, (bds[2] << 4) | (fd_bootchk ? 0x0 : 0x1)); } static void pc_boot_set(void *opaque, const char *boot_device, Error **errp) @@ -513,9 +513,9 @@ static void pc_cmos_init_floppy(MC146818RtcState *rtc_state, ISADevice *floppy) } val = (cmos_get_fd_drive_type(fd_type[0]) << 4) | cmos_get_fd_drive_type(fd_type[1]); - rtc_set_memory(rtc_state, 0x10, val); + mc146818rtc_set_cmos_data(rtc_state, 0x10, val); - val = rtc_get_memory(rtc_state, REG_EQUIPMENT_BYTE); + val = mc146818rtc_get_cmos_data(rtc_state, REG_EQUIPMENT_BYTE); nb = 0; if (fd_type[0] != FLOPPY_DRIVE_TYPE_NONE) { nb++; @@ -533,7 +533,7 @@ static void pc_cmos_init_floppy(MC146818RtcState *rtc_state, ISADevice *floppy) val |= 0x41; /* 2 drives, ready for boot */ break; } - rtc_set_memory(rtc_state, REG_EQUIPMENT_BYTE, val); + mc146818rtc_set_cmos_data(rtc_state, REG_EQUIPMENT_BYTE, val); } typedef struct pc_cmos_init_late_arg { @@ -621,7 +621,7 @@ static void pc_cmos_init_late(void *opaque) cmos_init_hd(s, 0x1a, 0x24, cylinders, heads, sectors); val |= 0x0f; } - rtc_set_memory(s, 0x12, val); + mc146818rtc_set_cmos_data(s, 0x12, val); val = 0; for (i = 0; i < 4; i++) { @@ -637,7 +637,7 @@ static void pc_cmos_init_late(void *opaque) val |= trans << (i * 2); } } - rtc_set_memory(s, 0x39, val); + mc146818rtc_set_cmos_data(s, 0x39, val); pc_cmos_init_floppy(s, pc_find_fdc0()); @@ -658,8 +658,8 @@ void pc_cmos_init(PCMachineState *pcms, /* memory size */ /* base memory (first MiB) */ val = MIN(x86ms->below_4g_mem_size / KiB, 640); - rtc_set_memory(s, 0x15, val); - rtc_set_memory(s, 0x16, val >> 8); + mc146818rtc_set_cmos_data(s, 0x15, val); + mc146818rtc_set_cmos_data(s, 0x16, val >> 8); /* extended memory (next 64MiB) */ if (x86ms->below_4g_mem_size > 1 * MiB) { val = (x86ms->below_4g_mem_size - 1 * MiB) / KiB; @@ -668,10 +668,10 @@ void pc_cmos_init(PCMachineState *pcms, } if (val > 65535) val = 65535; - rtc_set_memory(s, 0x17, val); - rtc_set_memory(s, 0x18, val >> 8); - rtc_set_memory(s, 0x30, val); - rtc_set_memory(s, 0x31, val >> 8); + mc146818rtc_set_cmos_data(s, 0x17, val); + mc146818rtc_set_cmos_data(s, 0x18, val >> 8); + mc146818rtc_set_cmos_data(s, 0x30, val); + mc146818rtc_set_cmos_data(s, 0x31, val >> 8); /* memory between 16MiB and 4GiB */ if (x86ms->below_4g_mem_size > 16 * MiB) { val = (x86ms->below_4g_mem_size - 16 * MiB) / (64 * KiB); @@ -680,13 +680,13 @@ void pc_cmos_init(PCMachineState *pcms, } if (val > 65535) val = 65535; - rtc_set_memory(s, 0x34, val); - rtc_set_memory(s, 0x35, val >> 8); + mc146818rtc_set_cmos_data(s, 0x34, val); + mc146818rtc_set_cmos_data(s, 0x35, val >> 8); /* memory above 4GiB */ val = x86ms->above_4g_mem_size / 65536; - rtc_set_memory(s, 0x5b, val); - rtc_set_memory(s, 0x5c, val >> 8); - rtc_set_memory(s, 0x5d, val >> 16); + mc146818rtc_set_cmos_data(s, 0x5b, val); + mc146818rtc_set_cmos_data(s, 0x5c, val >> 8); + mc146818rtc_set_cmos_data(s, 0x5d, val >> 16); object_property_add_link(OBJECT(pcms), "rtc_state", TYPE_ISA_DEVICE, @@ -701,7 +701,7 @@ void pc_cmos_init(PCMachineState *pcms, val = 0; val |= 0x02; /* FPU is there */ val |= 0x04; /* PS/2 mouse installed */ - rtc_set_memory(s, REG_EQUIPMENT_BYTE, val); + mc146818rtc_set_cmos_data(s, REG_EQUIPMENT_BYTE, val); /* hard drives and FDC */ arg.rtc_state = s; diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 38d2ec0581..c44846f47b 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -160,9 +160,9 @@ void x86_rtc_set_cpus_count(ISADevice *s, uint16_t cpus_count) * BIOS must use "FW_CFG_NB_CPUS". Set RTC field to 0 just * to make old BIOSes fail more predictably. */ - rtc_set_memory(rtc, 0x5f, 0); + mc146818rtc_set_cmos_data(rtc, 0x5f, 0); } else { - rtc_set_memory(rtc, 0x5f, cpus_count - 1); + mc146818rtc_set_cmos_data(rtc, 0x5f, cpus_count - 1); } } diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 076e2d0d22..d00280c0f8 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -215,10 +215,10 @@ static int prep_set_cmos_checksum(DeviceState *dev, void *opaque) if (object_dynamic_cast(OBJECT(dev), TYPE_MC146818_RTC)) { MC146818RtcState *rtc = MC146818_RTC(dev); - rtc_set_memory(rtc, 0x2e, checksum & 0xff); - rtc_set_memory(rtc, 0x3e, checksum & 0xff); - rtc_set_memory(rtc, 0x2f, checksum >> 8); - rtc_set_memory(rtc, 0x3f, checksum >> 8); + mc146818rtc_set_cmos_data(rtc, 0x2e, checksum & 0xff); + mc146818rtc_set_cmos_data(rtc, 0x3e, checksum & 0xff); + mc146818rtc_set_cmos_data(rtc, 0x2f, checksum >> 8); + mc146818rtc_set_cmos_data(rtc, 0x3f, checksum >> 8); object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(rtc), "date"); diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index b4d7777634..c27c362db9 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -739,13 +739,13 @@ static uint64_t cmos_ioport_read(void *opaque, hwaddr addr, } } -void rtc_set_memory(MC146818RtcState *s, int addr, int val) +void mc146818rtc_set_cmos_data(MC146818RtcState *s, int addr, int val) { if (addr >= 0 && addr <= 127) s->cmos_data[addr] = val; } -int rtc_get_memory(MC146818RtcState *s, int addr) +int mc146818rtc_get_cmos_data(MC146818RtcState *s, int addr) { assert(addr >= 0 && addr <= 127); return s->cmos_data[addr]; @@ -857,7 +857,7 @@ static void rtc_notify_suspend(Notifier *notifier, void *data) { MC146818RtcState *s = container_of(notifier, MC146818RtcState, suspend_notifier); - rtc_set_memory(s, 0xF, 0xFE); + mc146818rtc_set_cmos_data(s, 0xF, 0xFE); } static const MemoryRegionOps cmos_ops = { diff --git a/include/hw/rtc/mc146818rtc.h b/include/hw/rtc/mc146818rtc.h index a6b0c135c0..97cec0b3e8 100644 --- a/include/hw/rtc/mc146818rtc.h +++ b/include/hw/rtc/mc146818rtc.h @@ -53,8 +53,8 @@ struct MC146818RtcState { MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq); -void rtc_set_memory(MC146818RtcState *s, int addr, int val); -int rtc_get_memory(MC146818RtcState *s, int addr); +void mc146818rtc_set_cmos_data(MC146818RtcState *s, int addr, int val); +int mc146818rtc_get_cmos_data(MC146818RtcState *s, int addr); void qmp_rtc_reset_reinjection(Error **errp); #endif /* HW_RTC_MC146818RTC_H */ From 2dbf9dd80bd14f7935fd9f722ed3cb87c5b9b0f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 15 Feb 2023 17:23:49 +0100 Subject: [PATCH 068/185] hw/timer/hpet: Include missing 'hw/qdev-properties.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid when refactoring unrelated headers: hw/timer/hpet.c:776:39: error: array has incomplete element type 'Property' (aka 'struct Property') static Property hpet_device_properties[] = { ^ hw/timer/hpet.c:777:5: error: implicit declaration of function 'DEFINE_PROP_UINT8' is invalid in C99 [-Werror,-Wimplicit-function-declaration] DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS), ^ Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20230215174353.37097-2-philmd@linaro.org> --- hw/timer/hpet.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 9520471be2..214d6a0501 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -30,6 +30,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/timer.h" +#include "hw/qdev-properties.h" #include "hw/timer/hpet.h" #include "hw/sysbus.h" #include "hw/rtc/mc146818rtc.h" From acab7d6022943a710472028e4e7f0010ee4f9376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Jan 2023 22:06:36 +0100 Subject: [PATCH 069/185] hw/audio/hda-codec: Avoid forward-declaring HDAAudioState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To avoid forward-declaring HDAAudioState, declare HDA_AUDIO QOM definitions before its use in the HDAAudioStream structure. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230220131837.26292-2-philmd@linaro.org> --- hw/audio/hda-codec.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index feb8f9e2bb..c51d8ba617 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -145,7 +145,9 @@ static const char *fmt2name[] = { [ AUDIO_FORMAT_S32 ] = "PCM-S32", }; -typedef struct HDAAudioState HDAAudioState; +#define TYPE_HDA_AUDIO "hda-audio" +OBJECT_DECLARE_SIMPLE_TYPE(HDAAudioState, HDA_AUDIO) + typedef struct HDAAudioStream HDAAudioStream; struct HDAAudioStream { @@ -171,9 +173,6 @@ struct HDAAudioStream { int64_t buft_start; }; -#define TYPE_HDA_AUDIO "hda-audio" -OBJECT_DECLARE_SIMPLE_TYPE(HDAAudioState, HDA_AUDIO) - struct HDAAudioState { HDACodecDevice hda; const char *name; From d9c214d745f47791710a3d2cc94a439bf9f83776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 9 Jan 2023 15:19:33 +0100 Subject: [PATCH 070/185] hw/audio/es1370: Avoid forward-declaring ES1370State MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To avoid forward-declaring ES1370State, declare ES1370 QOM definitions before its use in the chan_bits structure. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230220131837.26292-3-philmd@linaro.org> --- hw/audio/es1370.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index 54cc19a637..ea76c9a734 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -256,6 +256,9 @@ static void print_sctl (uint32_t val) #define lwarn(...) #endif +#define TYPE_ES1370 "ES1370" +OBJECT_DECLARE_SIMPLE_TYPE(ES1370State, ES1370) + struct chan { uint32_t shift; uint32_t leftover; @@ -278,7 +281,6 @@ struct ES1370State { uint32_t codec; uint32_t sctl; }; -typedef struct ES1370State ES1370State; struct chan_bits { uint32_t ctl_en; @@ -292,9 +294,6 @@ struct chan_bits { uint32_t *old_freq, uint32_t *new_freq); }; -#define TYPE_ES1370 "ES1370" -OBJECT_DECLARE_SIMPLE_TYPE(ES1370State, ES1370) - static void es1370_dac1_calc_freq (ES1370State *s, uint32_t ctl, uint32_t *old_freq, uint32_t *new_freq); static void es1370_dac2_and_adc_calc_freq (ES1370State *s, uint32_t ctl, From 721d8f254090c8f2b716bf988fcacac16bd9ecf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 20 Feb 2023 14:15:15 +0100 Subject: [PATCH 071/185] hw/audio/es1370: Replace container_of() by ES1370() QOM cast macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230220131837.26292-4-philmd@linaro.org> --- hw/audio/es1370.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index ea76c9a734..4f738a0ad8 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -843,7 +843,8 @@ static const VMStateDescription vmstate_es1370 = { static void es1370_on_reset(DeviceState *dev) { - ES1370State *s = container_of(dev, ES1370State, dev.qdev); + ES1370State *s = ES1370(dev); + es1370_reset (s); } From 911a6afbc56c2179445ff04ad428500d85e5c0f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 20 Feb 2023 11:47:14 +0100 Subject: [PATCH 072/185] hw/audio/ac97: Replace container_of() by AC97() QOM cast macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230220131837.26292-5-philmd@linaro.org> --- hw/audio/ac97.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index 364cdfa733..b3a45bb49f 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -1295,7 +1295,7 @@ static const MemoryRegionOps ac97_io_nabm_ops = { static void ac97_on_reset(DeviceState *dev) { - AC97LinkState *s = container_of(dev, AC97LinkState, dev.qdev); + AC97LinkState *s = AC97(dev); reset_bm_regs(s, &s->bm_regs[0]); reset_bm_regs(s, &s->bm_regs[1]); From c272a72467feaa0b783c0c4eb8ffaa048888a1d7 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Tue, 25 Jan 2022 20:48:36 +0100 Subject: [PATCH 073/185] hw/audio/ac97: Split off some definitions to a header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These can be shared with other AC97 implementations. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-Id: <6f0980fdc3753624be6f3935a6ab0a2dc1df4b30.1677445307.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/audio/ac97.c | 43 +------------------------------- hw/audio/ac97.h | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 42 deletions(-) create mode 100644 hw/audio/ac97.h diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index b3a45bb49f..c2a5ce062a 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -26,43 +26,7 @@ #include "qemu/module.h" #include "sysemu/dma.h" #include "qom/object.h" - -enum { - AC97_Reset = 0x00, - AC97_Master_Volume_Mute = 0x02, - AC97_Headphone_Volume_Mute = 0x04, - AC97_Master_Volume_Mono_Mute = 0x06, - AC97_Master_Tone_RL = 0x08, - AC97_PC_BEEP_Volume_Mute = 0x0A, - AC97_Phone_Volume_Mute = 0x0C, - AC97_Mic_Volume_Mute = 0x0E, - AC97_Line_In_Volume_Mute = 0x10, - AC97_CD_Volume_Mute = 0x12, - AC97_Video_Volume_Mute = 0x14, - AC97_Aux_Volume_Mute = 0x16, - AC97_PCM_Out_Volume_Mute = 0x18, - AC97_Record_Select = 0x1A, - AC97_Record_Gain_Mute = 0x1C, - AC97_Record_Gain_Mic_Mute = 0x1E, - AC97_General_Purpose = 0x20, - AC97_3D_Control = 0x22, - AC97_AC_97_RESERVED = 0x24, - AC97_Powerdown_Ctrl_Stat = 0x26, - AC97_Extended_Audio_ID = 0x28, - AC97_Extended_Audio_Ctrl_Stat = 0x2A, - AC97_PCM_Front_DAC_Rate = 0x2C, - AC97_PCM_Surround_DAC_Rate = 0x2E, - AC97_PCM_LFE_DAC_Rate = 0x30, - AC97_PCM_LR_ADC_Rate = 0x32, - AC97_MIC_ADC_Rate = 0x34, - AC97_6Ch_Vol_C_LFE_Mute = 0x36, - AC97_6Ch_Vol_L_R_Surround_Mute = 0x38, - AC97_Vendor_Reserved = 0x58, - AC97_Sigmatel_Analog = 0x6c, /* We emulate a Sigmatel codec */ - AC97_Sigmatel_Dac2Invert = 0x6e, /* We emulate a Sigmatel codec */ - AC97_Vendor_ID1 = 0x7c, - AC97_Vendor_ID2 = 0x7e -}; +#include "ac97.h" #define SOFT_VOLUME #define SR_FIFOE 16 /* rwc */ @@ -121,11 +85,6 @@ enum { #define BD_IOC (1 << 31) #define BD_BUP (1 << 30) -#define EACS_VRA 1 -#define EACS_VRM 8 - -#define MUTE_SHIFT 15 - #define TYPE_AC97 "AC97" OBJECT_DECLARE_SIMPLE_TYPE(AC97LinkState, AC97) diff --git a/hw/audio/ac97.h b/hw/audio/ac97.h new file mode 100644 index 0000000000..0358b56ff4 --- /dev/null +++ b/hw/audio/ac97.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2006 InnoTek Systemberatung GmbH + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License as published by the Free Software Foundation, + * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE + * distribution. VirtualBox OSE is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY of any kind. + * + * If you received this file as part of a commercial VirtualBox + * distribution, then only the terms of your commercial VirtualBox + * license agreement apply instead of the previous paragraph. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#ifndef AC97_H +#define AC97_H + +enum { + AC97_Reset = 0x00, + AC97_Master_Volume_Mute = 0x02, + AC97_Headphone_Volume_Mute = 0x04, + AC97_Master_Volume_Mono_Mute = 0x06, + AC97_Master_Tone_RL = 0x08, + AC97_PC_BEEP_Volume_Mute = 0x0A, + AC97_Phone_Volume_Mute = 0x0C, + AC97_Mic_Volume_Mute = 0x0E, + AC97_Line_In_Volume_Mute = 0x10, + AC97_CD_Volume_Mute = 0x12, + AC97_Video_Volume_Mute = 0x14, + AC97_Aux_Volume_Mute = 0x16, + AC97_PCM_Out_Volume_Mute = 0x18, + AC97_Record_Select = 0x1A, + AC97_Record_Gain_Mute = 0x1C, + AC97_Record_Gain_Mic_Mute = 0x1E, + AC97_General_Purpose = 0x20, + AC97_3D_Control = 0x22, + AC97_AC_97_RESERVED = 0x24, + AC97_Powerdown_Ctrl_Stat = 0x26, + AC97_Extended_Audio_ID = 0x28, + AC97_Extended_Audio_Ctrl_Stat = 0x2A, + AC97_PCM_Front_DAC_Rate = 0x2C, + AC97_PCM_Surround_DAC_Rate = 0x2E, + AC97_PCM_LFE_DAC_Rate = 0x30, + AC97_PCM_LR_ADC_Rate = 0x32, + AC97_MIC_ADC_Rate = 0x34, + AC97_6Ch_Vol_C_LFE_Mute = 0x36, + AC97_6Ch_Vol_L_R_Surround_Mute = 0x38, + AC97_Vendor_Reserved = 0x58, + AC97_Sigmatel_Analog = 0x6c, /* We emulate a Sigmatel codec */ + AC97_Sigmatel_Dac2Invert = 0x6e, /* We emulate a Sigmatel codec */ + AC97_Vendor_ID1 = 0x7c, + AC97_Vendor_ID2 = 0x7e +}; + +#define EACS_VRA 1 +#define EACS_VRM 8 + +#define MUTE_SHIFT 15 + +#endif /* AC97_H */ From 1fc3efc6dd1ce8ac7cd267df04b0cb0caf1b7cbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Jan 2023 22:05:19 +0100 Subject: [PATCH 074/185] hw/usb/dev-smartcard-reader: Avoid forward-declaring CCIDBus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To avoid forward-declaring CCIDBus, declare CCID_BUS QOM definitions before its use in the USBCCIDState structure. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230220150515.32549-2-philmd@linaro.org> --- hw/usb/dev-smartcard-reader.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index 28164d89be..be0a4fc3bc 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -278,7 +278,9 @@ typedef struct BulkIn { struct CCIDBus { BusState qbus; }; -typedef struct CCIDBus CCIDBus; + +#define TYPE_CCID_BUS "ccid-bus" +OBJECT_DECLARE_SIMPLE_TYPE(CCIDBus, CCID_BUS) /* * powered - defaults to true, changed by PowerOn/PowerOff messages @@ -1174,9 +1176,6 @@ static Property ccid_props[] = { DEFINE_PROP_END_OF_LIST(), }; -#define TYPE_CCID_BUS "ccid-bus" -OBJECT_DECLARE_SIMPLE_TYPE(CCIDBus, CCID_BUS) - static const TypeInfo ccid_bus_info = { .name = TYPE_CCID_BUS, .parent = TYPE_BUS, From c3e9090c5ed2d01063542cd2b4f2c0912100dae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 17 Feb 2023 14:11:23 +0100 Subject: [PATCH 075/185] hw/usb/u2f: Declare QOM macros using OBJECT_DECLARE_TYPE() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hw/usb/u2f.h was added by commit 80e267f1d1 ("hw/usb: Add U2F key base class"), almost the same time of the automatic conversion done by commit c821774a3b ("Use OBJECT_DECLARE_TYPE where posible"). Manually convert to OBJECT_DECLARE_TYPE(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230220150515.32549-9-philmd@linaro.org> --- hw/usb/u2f.h | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/hw/usb/u2f.h b/hw/usb/u2f.h index a408a82927..8bff13141a 100644 --- a/hw/usb/u2f.h +++ b/hw/usb/u2f.h @@ -31,22 +31,16 @@ #define U2FHID_PACKET_SIZE 64 #define U2FHID_PENDING_IN_NUM 32 -typedef struct U2FKeyState U2FKeyState; typedef struct U2FKeyInfo U2FKeyInfo; #define TYPE_U2F_KEY "u2f-key" -#define U2F_KEY(obj) \ - OBJECT_CHECK(U2FKeyState, (obj), TYPE_U2F_KEY) -#define U2F_KEY_CLASS(klass) \ - OBJECT_CLASS_CHECK(U2FKeyClass, (klass), TYPE_U2F_KEY) -#define U2F_KEY_GET_CLASS(obj) \ - OBJECT_GET_CLASS(U2FKeyClass, (obj), TYPE_U2F_KEY) +OBJECT_DECLARE_TYPE(U2FKeyState, U2FKeyClass, U2F_KEY) /* * Callbacks to be used by the U2F key base device (i.e. hw/u2f.c) * to interact with its variants (i.e. hw/u2f-*.c) */ -typedef struct U2FKeyClass { +struct U2FKeyClass { /*< private >*/ USBDeviceClass parent_class; @@ -55,12 +49,12 @@ typedef struct U2FKeyClass { const uint8_t packet[U2FHID_PACKET_SIZE]); void (*realize)(U2FKeyState *key, Error **errp); void (*unrealize)(U2FKeyState *key); -} U2FKeyClass; +}; /* * State of the U2F key base device (i.e. hw/u2f.c) */ -typedef struct U2FKeyState { +struct U2FKeyState { USBDevice dev; USBEndpoint *ep; uint8_t idle; @@ -70,7 +64,7 @@ typedef struct U2FKeyState { uint8_t pending_in_start; uint8_t pending_in_end; uint8_t pending_in_num; -} U2FKeyState; +}; /* * API to be used by the U2F key device variants (i.e. hw/u2f-*.c) From d9b934f21e55fb62abb0f962aae731f36f4e0a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 3 Feb 2023 10:52:26 +0100 Subject: [PATCH 076/185] hw/usb/ohci: Include missing 'sysbus.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid when including "hw/usb/hcd-ohci.h": hw/usb/hcd-ohci.h:100:5: error: unknown type name 'SysBusDevice' SysBusDevice parent_obj; ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Markus Armbruster Message-Id: <20230203113650.78146-6-philmd@linaro.org> --- hw/usb/hcd-ohci.h | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/usb/hcd-ohci.h b/hw/usb/hcd-ohci.h index 11ac57058d..e5e6b434fd 100644 --- a/hw/usb/hcd-ohci.h +++ b/hw/usb/hcd-ohci.h @@ -21,6 +21,7 @@ #ifndef HCD_OHCI_H #define HCD_OHCI_H +#include "hw/sysbus.h" #include "sysemu/dma.h" #include "hw/usb.h" #include "qom/object.h" From 4713720a78c56ec2e00bcb4abcac71e99315e4ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 20 Feb 2023 11:44:15 +0100 Subject: [PATCH 077/185] hw/usb/ohci: Use OHCIState type definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Forward-define the type first, then use it for the ohci_die() handler. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230220150515.32549-3-philmd@linaro.org> --- hw/usb/hcd-ohci.c | 2 +- hw/usb/hcd-ohci.h | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 9d68036d23..7ecf9b15b7 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -1827,7 +1827,7 @@ static USBBusOps ohci_bus_ops = { void usb_ohci_init(OHCIState *ohci, DeviceState *dev, uint32_t num_ports, dma_addr_t localmem_base, char *masterbus, uint32_t firstport, AddressSpace *as, - void (*ohci_die_fn)(struct OHCIState *), Error **errp) + void (*ohci_die_fn)(OHCIState *), Error **errp) { Error *err = NULL; int i; diff --git a/hw/usb/hcd-ohci.h b/hw/usb/hcd-ohci.h index e5e6b434fd..e1827227ac 100644 --- a/hw/usb/hcd-ohci.h +++ b/hw/usb/hcd-ohci.h @@ -34,7 +34,9 @@ typedef struct OHCIPort { uint32_t ctrl; } OHCIPort; -typedef struct OHCIState { +typedef struct OHCIState OHCIState; + +struct OHCIState { USBBus bus; qemu_irq irq; MemoryRegion mem; @@ -90,8 +92,8 @@ typedef struct OHCIState { uint32_t async_td; bool async_complete; - void (*ohci_die)(struct OHCIState *ohci); -} OHCIState; + void (*ohci_die)(OHCIState *ohci); +}; #define TYPE_SYSBUS_OHCI "sysbus-ohci" OBJECT_DECLARE_SIMPLE_TYPE(OHCISysBusState, SYSBUS_OHCI) @@ -113,7 +115,7 @@ extern const VMStateDescription vmstate_ohci_state; void usb_ohci_init(OHCIState *ohci, DeviceState *dev, uint32_t num_ports, dma_addr_t localmem_base, char *masterbus, uint32_t firstport, AddressSpace *as, - void (*ohci_die_fn)(struct OHCIState *), Error **errp); + void (*ohci_die_fn)(OHCIState *), Error **errp); void ohci_bus_stop(OHCIState *ohci); void ohci_stop_endpoints(OHCIState *ohci); void ohci_hard_reset(OHCIState *ohci); From 572a673214ff92189675f03f1b8d5449bba6ec97 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 20 Feb 2023 19:15:04 +0100 Subject: [PATCH 078/185] hw/usb/ohci: Code style fix comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: BALATON Zoltan Reviewed-by: Peter Maydell Message-Id: <9b0aadedc7c4780fefdc27f14f72ac9003032fbf.1676916639.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-ohci.c | 99 +++++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 50 deletions(-) diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 7ecf9b15b7..93fc592161 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -23,7 +23,7 @@ * o Disable timers when nothing needs to be done, or remove timer usage * all together. * o BIOS work to boot from USB storage -*/ + */ #include "qemu/osdep.h" #include "hw/irq.h" @@ -39,7 +39,7 @@ #include "hcd-ohci.h" /* This causes frames to occur 1000x slower */ -//#define OHCI_TIME_WARP 1 +/*#define OHCI_TIME_WARP 1*/ #define ED_LINK_LIMIT 32 @@ -58,7 +58,7 @@ struct ohci_hcca { #define ED_WBACK_OFFSET offsetof(struct ohci_ed, head) #define ED_WBACK_SIZE 4 -/* Bitfields for the first word of an Endpoint Desciptor. */ +/* Bitfields for the first word of an Endpoint Desciptor. */ #define OHCI_ED_FA_SHIFT 0 #define OHCI_ED_FA_MASK (0x7f<per_cur = 0; ohci->done = 0; ohci->done_count = 7; - - /* FSMPS is marked TBD in OCHI 1.0, what gives ffs? + /* + * FSMPS is marked TBD in OCHI 1.0, what gives ffs? * I took the value linux sets ... */ ohci->fsmps = 0x2778; @@ -460,10 +460,10 @@ static inline int ohci_read_hcca(OHCIState *ohci, static inline int ohci_put_ed(OHCIState *ohci, dma_addr_t addr, struct ohci_ed *ed) { - /* ed->tail is under control of the HCD. + /* + * ed->tail is under control of the HCD. * Since just ed->head is changed by HC, just write back this */ - return put_dwords(ohci, addr + ED_WBACK_OFFSET, (uint32_t *)((char *)ed + ED_WBACK_OFFSET), ED_WBACK_SIZE >> 2); @@ -601,8 +601,10 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed) trace_usb_ohci_iso_td_relative_frame_number_neg(relative_frame_number); return 1; } else if (relative_frame_number > frame_count) { - /* ISO TD expired - retire the TD to the Done Queue and continue with - the next ISO TD of the same ED */ + /* + * ISO TD expired - retire the TD to the Done Queue and continue with + * the next ISO TD of the same ED + */ trace_usb_ohci_iso_td_relative_frame_number_big(relative_frame_number, frame_count); if (OHCI_CC_DATAOVERRUN == OHCI_BM(iso_td.flags, TD_CC)) { @@ -845,9 +847,10 @@ static void ohci_td_pkt(const char *msg, const uint8_t *buf, size_t len) } } -/* Service a transport descriptor. - Returns nonzero to terminate processing of this endpoint. */ - +/* + * Service a transport descriptor. + * Returns nonzero to terminate processing of this endpoint. + */ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) { int dir; @@ -869,7 +872,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) return 1; } - /* See if this TD has already been submitted to the device. */ + /* See if this TD has already been submitted to the device. */ completion = (addr == ohci->async_td); if (completion && !ohci->async_complete) { trace_usb_ohci_td_skip_async(); @@ -885,7 +888,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) switch (dir) { case OHCI_TD_DIR_OUT: case OHCI_TD_DIR_IN: - /* Same value. */ + /* Same value. */ break; default: dir = OHCI_BM(td.flags, TD_DP); @@ -956,11 +959,12 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) } ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); if (ohci->async_td) { - /* ??? The hardware should allow one active packet per - endpoint. We only allow one active packet per controller. - This should be sufficient as long as devices respond in a - timely manner. - */ + /* + * ??? The hardware should allow one active packet per + * endpoint. We only allow one active packet per controller. + * This should be sufficient as long as devices respond in a + * timely manner. + */ trace_usb_ohci_td_too_many_pending(ep->nr); return 1; } @@ -996,7 +1000,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) /* Writeback */ if (ret == pktlen || (dir == OHCI_TD_DIR_IN && ret >= 0 && flag_r)) { - /* Transmission succeeded. */ + /* Transmission succeeded. */ if (ret == len) { td.cbp = 0; } else { @@ -1048,8 +1052,10 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) OHCI_SET_BM(td.flags, TD_EC, 3); break; } - /* An error occurred so we have to clear the interrupt counter. See - * spec at 6.4.4 on page 104 */ + /* + * An error occurred so we have to clear the interrupt counter. + * See spec at 6.4.4 on page 104 + */ ohci->done_count = 0; } ed->head |= OHCI_ED_H; @@ -1071,7 +1077,7 @@ exit_no_retire: return OHCI_BM(td.flags, TD_CC) != OHCI_CC_NOERROR; } -/* Service an endpoint list. Returns nonzero if active TD were found. */ +/* Service an endpoint list. Returns nonzero if active TD were found. */ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) { struct ohci_ed ed; @@ -1095,7 +1101,7 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) { uint32_t addr; - /* Cancel pending packets for ED that have been paused. */ + /* Cancel pending packets for ED that have been paused. */ addr = ed.head & OHCI_DPTR_MASK; if (ohci->async_td && addr == ohci->async_td) { usb_cancel_packet(&ohci->usb_packet); @@ -1151,7 +1157,7 @@ static void ohci_sof(OHCIState *ohci) ohci_set_interrupt(ohci, OHCI_INTR_SF); } -/* Process Control and Bulk lists. */ +/* Process Control and Bulk lists. */ static void ohci_process_lists(OHCIState *ohci) { if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) { @@ -1192,7 +1198,7 @@ static void ohci_frame_boundary(void *opaque) ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n])); } - /* Cancel all pending packets if either of the lists has been disabled. */ + /* Cancel all pending packets if either of the lists has been disabled. */ if (ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) { ohci_stop_endpoints(ohci); } @@ -1234,18 +1240,17 @@ static void ohci_frame_boundary(void *opaque) } } -/* Start sending SOF tokens across the USB bus, lists are processed in +/* + * Start sending SOF tokens across the USB bus, lists are processed in * next frame */ static int ohci_bus_start(OHCIState *ohci) { trace_usb_ohci_start(ohci->name); - - /* Delay the first SOF event by one frame time as - * linux driver is not ready to receive it and - * can meet some race conditions + /* + * Delay the first SOF event by one frame time as linux driver is + * not ready to receive it and can meet some race conditions */ - ohci->sof_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ohci_eof_timer(ohci); @@ -1259,9 +1264,9 @@ void ohci_bus_stop(OHCIState *ohci) timer_del(ohci->eof_timer); } -/* Sets a flag in a port status register but only set it if the port is - * connected, if not set ConnectStatusChange flag. If flag is enabled - * return 1. +/* + * Sets a flag in a port status reg but only set it if the port is connected. + * If not set ConnectStatusChange flag. If flag is enabled return 1. */ static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val) { @@ -1271,9 +1276,7 @@ static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val) if (val == 0) return 0; - /* If CurrentConnectStatus is cleared we set - * ConnectStatusChange - */ + /* If CurrentConnectStatus is cleared we set ConnectStatusChange */ if (!(ohci->rhport[i].ctrl & OHCI_PORT_CCS)) { ohci->rhport[i].ctrl |= OHCI_PORT_CSC; if (ohci->rhstatus & OHCI_RHS_DRWE) { @@ -1291,7 +1294,7 @@ static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val) return ret; } -/* Set the frame interval - frame interval toggle is manipulated by the hcd only */ +/* Frame interval toggle is manipulated by the hcd only */ static void ohci_set_frame_interval(OHCIState *ohci, uint16_t val) { val &= OHCI_FMI_FI; @@ -1357,9 +1360,7 @@ static uint32_t ohci_get_frame_remaining(OHCIState *ohci) if ((ohci->ctl & OHCI_CTL_HCFS) != OHCI_USB_OPERATIONAL) return (ohci->frt << 31); - /* Being in USB operational state guarnatees sof_time was - * set already. - */ + /* Being in USB operational state guarnatees sof_time was set already. */ tks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - ohci->sof_time; if (tks < 0) { tks = 0; @@ -1439,13 +1440,11 @@ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val) trace_usb_ohci_port_reset(portnum); usb_device_reset(port->port.dev); port->ctrl &= ~OHCI_PORT_PRS; - /* ??? Should this also set OHCI_PORT_PESC. */ + /* ??? Should this also set OHCI_PORT_PESC. */ port->ctrl |= OHCI_PORT_PES | OHCI_PORT_PRSC; } - /* Invert order here to ensure in ambiguous case, device is - * powered up... - */ + /* Invert order here to ensure in ambiguous case, device is powered up. */ if (val & OHCI_PORT_LSDA) ohci_port_power(ohci, portnum, 0); if (val & OHCI_PORT_PPS) @@ -1892,7 +1891,7 @@ void usb_ohci_init(OHCIState *ohci, DeviceState *dev, uint32_t num_ports, ohci_frame_boundary, ohci); } -/** +/* * A typical OHCI will stop operating and set itself into error state * (which can be queried by MMIO) to signal that it got an error. */ From 978c93be7a301cec2f551a6434ed79620807932c Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 20 Feb 2023 19:15:05 +0100 Subject: [PATCH 079/185] hw/usb/ohci: Code style fix white space errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: BALATON Zoltan Reviewed-by: Peter Maydell Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-ohci.c | 154 +++++++++++++++++++++++----------------------- 1 file changed, 76 insertions(+), 78 deletions(-) diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 93fc592161..9d6bb8312e 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -60,46 +60,46 @@ struct ohci_hcca { /* Bitfields for the first word of an Endpoint Desciptor. */ #define OHCI_ED_FA_SHIFT 0 -#define OHCI_ED_FA_MASK (0x7f<frame_number, starting_frame); + relative_frame_number = USUB(ohci->frame_number, starting_frame); trace_usb_ohci_iso_td_head( ed->head & OHCI_DPTR_MASK, ed->tail & OHCI_DPTR_MASK, @@ -657,8 +657,8 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed) next_offset = iso_td.be; } - if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) || - ((relative_frame_number < frame_count) && + if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) || + ((relative_frame_number < frame_count) && !(OHCI_BM(next_offset, TD_PSW_CC) & 0xe))) { trace_usb_ohci_iso_td_bad_cc_not_accessed(start_offset, next_offset); return 1; @@ -1118,7 +1118,7 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) ed.tail & OHCI_DPTR_MASK, ed.next & OHCI_DPTR_MASK); trace_usb_ohci_ed_pkt_flags( OHCI_BM(ed.flags, ED_FA), OHCI_BM(ed.flags, ED_EN), - OHCI_BM(ed.flags, ED_D), (ed.flags & OHCI_ED_S)!= 0, + OHCI_BM(ed.flags, ED_D), (ed.flags & OHCI_ED_S) != 0, (ed.flags & OHCI_ED_K) != 0, (ed.flags & OHCI_ED_F) != 0, OHCI_BM(ed.flags, ED_MPS)); @@ -1311,10 +1311,8 @@ static void ohci_port_power(OHCIState *ohci, int i, int p) if (p) { ohci->rhport[i].ctrl |= OHCI_PORT_PPS; } else { - ohci->rhport[i].ctrl &= ~(OHCI_PORT_PPS| - OHCI_PORT_CCS| - OHCI_PORT_PSS| - OHCI_PORT_PRS); + ohci->rhport[i].ctrl &= ~(OHCI_PORT_PPS | OHCI_PORT_CCS | + OHCI_PORT_PSS | OHCI_PORT_PRS); } } @@ -1858,7 +1856,7 @@ void usb_ohci_init(OHCIState *ohci, DeviceState *dev, uint32_t num_ports, ohci->num_ports = num_ports; if (masterbus) { USBPort *ports[OHCI_MAX_PORTS]; - for(i = 0; i < num_ports; i++) { + for (i = 0; i < num_ports; i++) { ports[i] = &ohci->rhport[i].port; } usb_register_companion(masterbus, ports, num_ports, From 1e58e7a0d0ede9d7ca2c001f18bdf2cd0059f244 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 20 Feb 2023 19:15:06 +0100 Subject: [PATCH 080/185] hw/usb/ohci: Code style fix missing braces and extra parenthesis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: BALATON Zoltan Reviewed-by: Peter Maydell Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-ohci.c | 106 ++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 46 deletions(-) diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 9d6bb8312e..b7877866f6 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -499,9 +499,9 @@ static int ohci_copy_td(OHCIState *ohci, struct ohci_td *td, ptr = td->cbp; n = 0x1000 - (ptr & 0xfff); - if (n > len) + if (n > len) { n = len; - + } if (dma_memory_rw(ohci->as, ptr + ohci->localmem_base, buf, n, dir, MEMTXATTRS_UNSPECIFIED)) { return -1; @@ -527,9 +527,9 @@ static int ohci_copy_iso_td(OHCIState *ohci, ptr = start_addr; n = 0x1000 - (ptr & 0xfff); - if (n > len) + if (n > len) { n = len; - + } if (dma_memory_rw(ohci->as, ptr + ohci->localmem_base, buf, n, dir, MEMTXATTRS_UNSPECIFIED)) { return -1; @@ -617,8 +617,9 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed) iso_td.next = ohci->done; ohci->done = addr; i = OHCI_BM(iso_td.flags, TD_DI); - if (i < ohci->done_count) + if (i < ohci->done_count) { ohci->done_count = i; + } if (ohci_put_iso_td(ohci, addr, &iso_td)) { ohci_die(ohci); return 1; @@ -803,8 +804,9 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed) iso_td.next = ohci->done; ohci->done = addr; i = OHCI_BM(iso_td.flags, TD_DI); - if (i < ohci->done_count) + if (i < ohci->done_count) { ohci->done_count = i; + } } if (ohci_put_iso_td(ohci, addr, &iso_td)) { ohci_die(ohci); @@ -1022,8 +1024,9 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) /* Setting ED_C is part of the TD retirement process */ ed->head &= ~OHCI_ED_C; - if (td.flags & OHCI_TD_T0) + if (td.flags & OHCI_TD_T0) { ed->head |= OHCI_ED_C; + } } else { if (ret >= 0) { trace_usb_ohci_td_underrun(); @@ -1067,8 +1070,9 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) td.next = ohci->done; ohci->done = addr; i = OHCI_BM(td.flags, TD_DI); - if (i < ohci->done_count) + if (i < ohci->done_count) { ohci->done_count = i; + } exit_no_retire: if (ohci_put_td(ohci, addr, &td)) { ohci_die(ohci); @@ -1087,9 +1091,9 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) uint32_t link_cnt = 0; active = 0; - if (head == 0) + if (head == 0) { return 0; - + } for (cur = head; cur && link_cnt++ < ED_LINK_LIMIT; cur = next_ed) { if (ohci_read_ed(ohci, cur, &ed)) { trace_usb_ohci_ed_read_error(cur); @@ -1125,8 +1129,9 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) active = 1; if ((ed.flags & OHCI_ED_F) == 0) { - if (ohci_service_td(ohci, &ed)) + if (ohci_service_td(ohci, &ed)) { break; + } } else { /* Handle isochronous endpoints */ if (ohci_service_iso_td(ohci, &ed)) { @@ -1218,19 +1223,21 @@ static void ohci_frame_boundary(void *opaque) hcca.frame = cpu_to_le16(ohci->frame_number); if (ohci->done_count == 0 && !(ohci->intr_status & OHCI_INTR_WD)) { - if (!ohci->done) + if (!ohci->done) { abort(); - if (ohci->intr & ohci->intr_status) + } + if (ohci->intr & ohci->intr_status) { ohci->done |= 1; + } hcca.done = cpu_to_le32(ohci->done); ohci->done = 0; ohci->done_count = 7; ohci_set_interrupt(ohci, OHCI_INTR_WD); } - if (ohci->done_count != 7 && ohci->done_count != 0) + if (ohci->done_count != 7 && ohci->done_count != 0) { ohci->done_count--; - + } /* Do SOF stuff here */ ohci_sof(ohci); @@ -1273,9 +1280,9 @@ static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val) int ret = 1; /* writing a 0 has no effect */ - if (val == 0) + if (val == 0) { return 0; - + } /* If CurrentConnectStatus is cleared we set ConnectStatusChange */ if (!(ohci->rhport[i].ctrl & OHCI_PORT_CCS)) { ohci->rhport[i].ctrl |= OHCI_PORT_CSC; @@ -1285,9 +1292,9 @@ static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val) return 0; } - if (ohci->rhport[i].ctrl & val) + if (ohci->rhport[i].ctrl & val) { ret = 0; - + } /* set the bit */ ohci->rhport[i].ctrl |= val; @@ -1327,9 +1334,9 @@ static void ohci_set_ctl(OHCIState *ohci, uint32_t val) new_state = ohci->ctl & OHCI_CTL_HCFS; /* no state change */ - if (old_state == new_state) + if (old_state == new_state) { return; - + } trace_usb_ohci_set_ctl(ohci->name, new_state); switch (new_state) { case OHCI_USB_OPERATIONAL: @@ -1355,9 +1362,9 @@ static uint32_t ohci_get_frame_remaining(OHCIState *ohci) uint16_t fr; int64_t tks; - if ((ohci->ctl & OHCI_CTL_HCFS) != OHCI_USB_OPERATIONAL) - return (ohci->frt << 31); - + if ((ohci->ctl & OHCI_CTL_HCFS) != OHCI_USB_OPERATIONAL) { + return ohci->frt << 31; + } /* Being in USB operational state guarnatees sof_time was set already. */ tks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - ohci->sof_time; if (tks < 0) { @@ -1365,9 +1372,9 @@ static uint32_t ohci_get_frame_remaining(OHCIState *ohci) } /* avoid muldiv if possible */ - if (tks >= usb_frame_time) - return (ohci->frt << 31); - + if (tks >= usb_frame_time) { + return ohci->frt << 31; + } tks = tks / usb_bit_time; fr = (uint16_t)(ohci->fi - tks); @@ -1383,33 +1390,36 @@ static void ohci_set_hub_status(OHCIState *ohci, uint32_t val) old_state = ohci->rhstatus; /* write 1 to clear OCIC */ - if (val & OHCI_RHS_OCIC) + if (val & OHCI_RHS_OCIC) { ohci->rhstatus &= ~OHCI_RHS_OCIC; - + } if (val & OHCI_RHS_LPS) { int i; - for (i = 0; i < ohci->num_ports; i++) + for (i = 0; i < ohci->num_ports; i++) { ohci_port_power(ohci, i, 0); + } trace_usb_ohci_hub_power_down(); } if (val & OHCI_RHS_LPSC) { int i; - for (i = 0; i < ohci->num_ports; i++) + for (i = 0; i < ohci->num_ports; i++) { ohci_port_power(ohci, i, 1); + } trace_usb_ohci_hub_power_up(); } - if (val & OHCI_RHS_DRWE) + if (val & OHCI_RHS_DRWE) { ohci->rhstatus |= OHCI_RHS_DRWE; - - if (val & OHCI_RHS_CRWE) + } + if (val & OHCI_RHS_CRWE) { ohci->rhstatus &= ~OHCI_RHS_DRWE; - - if (old_state != ohci->rhstatus) + } + if (old_state != ohci->rhstatus) { ohci_set_interrupt(ohci, OHCI_INTR_RHSC); + } } /* Set root hub port status */ @@ -1422,12 +1432,12 @@ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val) old_state = port->ctrl; /* Write to clear CSC, PESC, PSSC, OCIC, PRSC */ - if (val & OHCI_PORT_WTC) + if (val & OHCI_PORT_WTC) { port->ctrl &= ~(val & OHCI_PORT_WTC); - - if (val & OHCI_PORT_CCS) + } + if (val & OHCI_PORT_CCS) { port->ctrl &= ~OHCI_PORT_PES; - + } ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PES); if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PSS)) { @@ -1443,13 +1453,15 @@ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val) } /* Invert order here to ensure in ambiguous case, device is powered up. */ - if (val & OHCI_PORT_LSDA) + if (val & OHCI_PORT_LSDA) { ohci_port_power(ohci, portnum, 0); - if (val & OHCI_PORT_PPS) + } + if (val & OHCI_PORT_PPS) { ohci_port_power(ohci, portnum, 1); - - if (old_state != port->ctrl) + } + if (old_state != port->ctrl) { ohci_set_interrupt(ohci, OHCI_INTR_RHSC); + } } static uint64_t ohci_mem_read(void *opaque, @@ -1606,8 +1618,9 @@ static void ohci_mem_write(void *opaque, /* Bits written as '0' remain unchanged in the register */ ohci->status |= val; - if (ohci->status & OHCI_STATUS_HCR) + if (ohci->status & OHCI_STATUS_HCR) { ohci_soft_reset(ohci); + } break; case 3: /* HcInterruptStatus */ @@ -1685,8 +1698,9 @@ static void ohci_mem_write(void *opaque, case 25: /* HcHReset */ ohci->hreset = val & ~OHCI_HRESET_FSBIR; - if (val & OHCI_HRESET_FSBIR) + if (val & OHCI_HRESET_FSBIR) { ohci_hard_reset(ohci); + } break; case 26: /* HcHInterruptEnable */ From ef680088f7727dfdc2778ddfab77d60bc5f36c46 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 20 Feb 2023 19:15:07 +0100 Subject: [PATCH 081/185] hw/usb/ohci: Move a function next to where it is used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ohci_port_set_if_connected() function is only used by ohci_port_set_status(), move next to it to have them at the same place. Signed-off-by: BALATON Zoltan Reviewed-by: Peter Maydell Message-Id: <46411d4980ab0fba61ab0d2209a939fdc41eb573.1676916640.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-ohci.c | 60 +++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index b7877866f6..2a52aace80 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -1271,36 +1271,6 @@ void ohci_bus_stop(OHCIState *ohci) timer_del(ohci->eof_timer); } -/* - * Sets a flag in a port status reg but only set it if the port is connected. - * If not set ConnectStatusChange flag. If flag is enabled return 1. - */ -static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val) -{ - int ret = 1; - - /* writing a 0 has no effect */ - if (val == 0) { - return 0; - } - /* If CurrentConnectStatus is cleared we set ConnectStatusChange */ - if (!(ohci->rhport[i].ctrl & OHCI_PORT_CCS)) { - ohci->rhport[i].ctrl |= OHCI_PORT_CSC; - if (ohci->rhstatus & OHCI_RHS_DRWE) { - /* TODO: CSC is a wakeup event */ - } - return 0; - } - - if (ohci->rhport[i].ctrl & val) { - ret = 0; - } - /* set the bit */ - ohci->rhport[i].ctrl |= val; - - return ret; -} - /* Frame interval toggle is manipulated by the hcd only */ static void ohci_set_frame_interval(OHCIState *ohci, uint16_t val) { @@ -1422,6 +1392,36 @@ static void ohci_set_hub_status(OHCIState *ohci, uint32_t val) } } +/* + * Sets a flag in a port status reg but only set it if the port is connected. + * If not set ConnectStatusChange flag. If flag is enabled return 1. + */ +static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val) +{ + int ret = 1; + + /* writing a 0 has no effect */ + if (val == 0) { + return 0; + } + /* If CurrentConnectStatus is cleared we set ConnectStatusChange */ + if (!(ohci->rhport[i].ctrl & OHCI_PORT_CCS)) { + ohci->rhport[i].ctrl |= OHCI_PORT_CSC; + if (ohci->rhstatus & OHCI_RHS_DRWE) { + /* TODO: CSC is a wakeup event */ + } + return 0; + } + + if (ohci->rhport[i].ctrl & val) { + ret = 0; + } + /* set the bit */ + ohci->rhport[i].ctrl |= val; + + return ret; +} + /* Set root hub port status */ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val) { From 9ae8d31d6a8405dec26282ad5a974cbab5485bc7 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 20 Feb 2023 19:19:09 +0100 Subject: [PATCH 082/185] hw/usb/ohci: Add trace points for register access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To help debugging add trace points that print values read from or written to the device's registers. Signed-off-by: BALATON Zoltan Message-Id: <1bb4985e5dfc1df5a290e77f76fd827ae3592ab7.1676916640.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-ohci.c | 27 +++++++++++++++++++++++++++ hw/usb/trace-events | 4 ++++ 2 files changed, 31 insertions(+) diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 2a52aace80..35a1a55145 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -235,6 +235,24 @@ struct ohci_iso_td { #define OHCI_HRESET_FSBIR (1 << 0) +static const char *ohci_reg_names[] = { + "HcRevision", "HcControl", "HcCommandStatus", "HcInterruptStatus", + "HcInterruptEnable", "HcInterruptDisable", "HcHCCA", "HcPeriodCurrentED", + "HcControlHeadED", "HcControlCurrentED", "HcBulkHeadED", "HcBulkCurrentED", + "HcDoneHead", "HcFmInterval", "HcFmRemaining", "HcFmNumber", + "HcPeriodicStart", "HcLSThreshold", "HcRhDescriptorA", "HcRhDescriptorB", + "HcRhStatus" +}; + +static const char *ohci_reg_name(hwaddr addr) +{ + if (addr >> 2 < ARRAY_SIZE(ohci_reg_names)) { + return ohci_reg_names[addr >> 2]; + } else { + return ""; + } +} + static void ohci_die(OHCIState *ohci) { ohci->ohci_die(ohci); @@ -1478,6 +1496,8 @@ static uint64_t ohci_mem_read(void *opaque, } else if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) { /* HcRhPortStatus */ retval = ohci->rhport[(addr - 0x54) >> 2].ctrl | OHCI_PORT_PPS; + trace_usb_ohci_mem_port_read(size, "HcRhPortStatus", (addr - 0x50) >> 2, + addr, addr >> 2, retval); } else { switch (addr >> 2) { case 0: /* HcRevision */ @@ -1582,6 +1602,10 @@ static uint64_t ohci_mem_read(void *opaque, trace_usb_ohci_mem_read_bad_offset(addr); retval = 0xffffffff; } + if (addr != 0xc || retval) { + trace_usb_ohci_mem_read(size, ohci_reg_name(addr), addr, addr >> 2, + retval); + } } return retval; @@ -1602,10 +1626,13 @@ static void ohci_mem_write(void *opaque, if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) { /* HcRhPortStatus */ + trace_usb_ohci_mem_port_write(size, "HcRhPortStatus", + (addr - 0x50) >> 2, addr, addr >> 2, val); ohci_port_set_status(ohci, (addr - 0x54) >> 2, val); return; } + trace_usb_ohci_mem_write(size, ohci_reg_name(addr), addr, addr >> 2, val); switch (addr >> 2) { case 1: /* HcControl */ ohci_set_ctl(ohci, val); diff --git a/hw/usb/trace-events b/hw/usb/trace-events index b65269892c..6bb9655c8d 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -57,8 +57,12 @@ usb_ohci_ed_read_error(uint32_t addr) "ED read error at 0x%x" usb_ohci_ed_pkt(uint32_t cur, int h, int c, uint32_t head, uint32_t tail, uint32_t next) "ED @ 0x%.8x h=%u c=%u\n head=0x%.8x tailp=0x%.8x next=0x%.8x" usb_ohci_ed_pkt_flags(uint32_t fa, uint32_t en, uint32_t d, int s, int k, int f, uint32_t mps) "fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u" usb_ohci_hcca_read_error(uint32_t addr) "HCCA read error at 0x%x" +usb_ohci_mem_read(uint32_t size, const char *name, uint32_t addr, uint32_t offs, uint32_t val) "%d %s 0x%x %d -> 0x%x" +usb_ohci_mem_port_read(uint32_t size, const char *name, uint32_t port, uint32_t addr, uint32_t offs, uint32_t val) "%d %s[%d] 0x%x %d -> 0x%x" usb_ohci_mem_read_unaligned(uint32_t addr) "at 0x%x" usb_ohci_mem_read_bad_offset(uint32_t addr) "0x%x" +usb_ohci_mem_write(uint32_t size, const char *name, uint32_t addr, uint32_t offs, uint32_t val) "%d %s 0x%x %d <- 0x%x" +usb_ohci_mem_port_write(uint32_t size, const char *name, uint32_t port, uint32_t addr, uint32_t offs, uint32_t val) "%d %s[%d] 0x%x %d <- 0x%x" usb_ohci_mem_write_unaligned(uint32_t addr) "at 0x%x" usb_ohci_mem_write_bad_offset(uint32_t addr) "0x%x" usb_ohci_process_lists(uint32_t head, uint32_t cur) "head 0x%x, cur 0x%x" From 380194624af8a740d3afa6fca8bae8f91d88ace0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 20 Feb 2023 19:15:10 +0100 Subject: [PATCH 083/185] hw/usb/ohci: Fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: BALATON Zoltan Signed-off-by: Philippe Mathieu-Daudé Message-Id: <03599fd4db313ac4f651cceb43340109ad6a14b8.1676916640.git.balaton@eik.bme.hu> --- hw/usb/hcd-ohci.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 35a1a55145..6f8b543243 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -58,7 +58,7 @@ struct ohci_hcca { #define ED_WBACK_OFFSET offsetof(struct ohci_ed, head) #define ED_WBACK_SIZE 4 -/* Bitfields for the first word of an Endpoint Desciptor. */ +/* Bitfields for the first word of an Endpoint Descriptor. */ #define OHCI_ED_FA_SHIFT 0 #define OHCI_ED_FA_MASK (0x7f << OHCI_ED_FA_SHIFT) #define OHCI_ED_EN_SHIFT 7 @@ -71,11 +71,11 @@ struct ohci_hcca { #define OHCI_ED_MPS_SHIFT 16 #define OHCI_ED_MPS_MASK (0x7ff << OHCI_ED_MPS_SHIFT) -/* Flags in the head field of an Endpoint Desciptor. */ +/* Flags in the head field of an Endpoint Descriptor. */ #define OHCI_ED_H 1 #define OHCI_ED_C 2 -/* Bitfields for the first word of a Transfer Desciptor. */ +/* Bitfields for the first word of a Transfer Descriptor. */ #define OHCI_TD_R (1 << 18) #define OHCI_TD_DP_SHIFT 19 #define OHCI_TD_DP_MASK (3 << OHCI_TD_DP_SHIFT) @@ -88,14 +88,14 @@ struct ohci_hcca { #define OHCI_TD_CC_SHIFT 28 #define OHCI_TD_CC_MASK (0xf << OHCI_TD_CC_SHIFT) -/* Bitfields for the first word of an Isochronous Transfer Desciptor. */ -/* CC & DI - same as in the General Transfer Desciptor */ +/* Bitfields for the first word of an Isochronous Transfer Descriptor. */ +/* CC & DI - same as in the General Transfer Descriptor */ #define OHCI_TD_SF_SHIFT 0 #define OHCI_TD_SF_MASK (0xffff << OHCI_TD_SF_SHIFT) #define OHCI_TD_FC_SHIFT 24 #define OHCI_TD_FC_MASK (7 << OHCI_TD_FC_SHIFT) -/* Isochronous Transfer Desciptor - Offset / PacketStatusWord */ +/* Isochronous Transfer Descriptor - Offset / PacketStatusWord */ #define OHCI_TD_PSW_CC_SHIFT 12 #define OHCI_TD_PSW_CC_MASK (0xf << OHCI_TD_PSW_CC_SHIFT) #define OHCI_TD_PSW_SIZE_SHIFT 0 From ef177ee12281b4bdfc5950bbaa8b3a6ff71331cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Jan 2023 13:58:32 +0100 Subject: [PATCH 084/185] hw/usb/uhci: Declare QOM macros using OBJECT_DECLARE_TYPE() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The automatic conversion done during commit a489d1951c ("Use OBJECT_DECLARE_TYPE when possible") missed this model because the typedefs are in a different file unit (hcd-uhci.c) than where the DECLARE_INSTANCE_CHECKER() is (hcd-uhci.h). Manually convert to OBJECT_DECLARE_TYPE(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230220150515.32549-5-philmd@linaro.org> --- hw/usb/hcd-uhci.c | 2 -- hw/usb/hcd-uhci.h | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 30ae0104bb..f77ffda69a 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -60,9 +60,7 @@ enum { TD_RESULT_ASYNC_CONT, }; -typedef struct UHCIState UHCIState; typedef struct UHCIAsync UHCIAsync; -typedef struct UHCIPCIDeviceClass UHCIPCIDeviceClass; struct UHCIPCIDeviceClass { PCIDeviceClass parent_class; diff --git a/hw/usb/hcd-uhci.h b/hw/usb/hcd-uhci.h index e0fdb98ef1..69f8b40c49 100644 --- a/hw/usb/hcd-uhci.h +++ b/hw/usb/hcd-uhci.h @@ -75,7 +75,7 @@ typedef struct UHCIState { } UHCIState; #define TYPE_UHCI "pci-uhci-usb" -DECLARE_INSTANCE_CHECKER(UHCIState, UHCI, TYPE_UHCI) +OBJECT_DECLARE_TYPE(UHCIState, UHCIPCIDeviceClass, UHCI) typedef struct UHCIInfo { const char *name; From ce6ffeaed0872fb7a5e0f0ce164f5493956fb0f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 20 Feb 2023 11:42:46 +0100 Subject: [PATCH 085/185] hw/usb/uhci: Replace container_of() by UHCI_GET_CLASS() QOM macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By using the QOM UHCI_GET_CLASS() cast macro we don't to use the intermediate PCIDeviceClass variable. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230220150515.32549-6-philmd@linaro.org> --- hw/usb/hcd-uhci.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index f77ffda69a..8ac1175ad2 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -1159,8 +1159,7 @@ static USBBusOps uhci_bus_ops = { void usb_uhci_common_realize(PCIDevice *dev, Error **errp) { Error *err = NULL; - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); - UHCIPCIDeviceClass *u = container_of(pc, UHCIPCIDeviceClass, parent_class); + UHCIPCIDeviceClass *u = UHCI_GET_CLASS(dev); UHCIState *s = UHCI(dev); uint8_t *pci_conf = s->dev.config; int i; @@ -1267,7 +1266,7 @@ void uhci_data_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - UHCIPCIDeviceClass *u = container_of(k, UHCIPCIDeviceClass, parent_class); + UHCIPCIDeviceClass *u = UHCI_CLASS(klass); UHCIInfo *info = data; k->realize = info->realize ? info->realize : usb_uhci_common_realize; From 67d58d1949ed1128e1f0311cc0b31649697635ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 17 Feb 2023 14:12:13 +0100 Subject: [PATCH 086/185] hw/usb/xhci-nec: Declare QOM macros for NEC_XHCI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NEC_XHCI is a QOM object type. Declare its macros / typedefs using OBJECT_DECLARE_SIMPLE_TYPE(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230220150515.32549-7-philmd@linaro.org> --- hw/usb/hcd-xhci-nec.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/usb/hcd-xhci-nec.c b/hw/usb/hcd-xhci-nec.c index 13c9ac5dbd..d732be0ef9 100644 --- a/hw/usb/hcd-xhci-nec.c +++ b/hw/usb/hcd-xhci-nec.c @@ -27,14 +27,16 @@ #include "hcd-xhci-pci.h" -typedef struct XHCINecState { +OBJECT_DECLARE_SIMPLE_TYPE(XHCINecState, NEC_XHCI) + +struct XHCINecState { /*< private >*/ XHCIPciState parent_obj; /*< public >*/ uint32_t flags; uint32_t intrs; uint32_t slots; -} XHCINecState; +}; static Property nec_xhci_properties[] = { DEFINE_PROP_ON_OFF_AUTO("msi", XHCIPciState, msi, ON_OFF_AUTO_AUTO), From b08bb02bcc3ba7ba82db766bd9cd2772a52eb903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 17 Feb 2023 14:12:13 +0100 Subject: [PATCH 087/185] hw/usb/xhci-nec: Replace container_of() by NEC_XHCI() QOM cast macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230220150515.32549-8-philmd@linaro.org> --- hw/usb/hcd-xhci-nec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/hcd-xhci-nec.c b/hw/usb/hcd-xhci-nec.c index d732be0ef9..328e5bfe7c 100644 --- a/hw/usb/hcd-xhci-nec.c +++ b/hw/usb/hcd-xhci-nec.c @@ -53,7 +53,7 @@ static Property nec_xhci_properties[] = { static void nec_xhci_instance_init(Object *obj) { XHCIPciState *pci = XHCI_PCI(obj); - XHCINecState *nec = container_of(pci, XHCINecState, parent_obj); + XHCINecState *nec = NEC_XHCI(obj); pci->xhci.flags = nec->flags; pci->xhci.numintrs = nec->intrs; From 01c400ae435f911eaf575b016090922c074dc1e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 3 Feb 2023 15:29:52 +0100 Subject: [PATCH 088/185] hw/display/sm501: Embed OHCI QOM child in chipset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Note this device doesn't implement unrealize(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: BALATON Zoltan Message-Id: <20230203145536.17585-11-philmd@linaro.org> --- hw/display/sm501.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index e1d0591d36..38b8d0e16b 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -28,6 +28,7 @@ #include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" +#include "hw/usb/hcd-ohci.h" #include "hw/char/serial.h" #include "ui/console.h" #include "hw/sysbus.h" @@ -1945,13 +1946,13 @@ struct SM501SysBusState { uint32_t vram_size; uint32_t base; SerialMM serial; + OHCISysBusState ohci; }; static void sm501_realize_sysbus(DeviceState *dev, Error **errp) { SM501SysBusState *s = SYSBUS_SM501(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - DeviceState *usb_dev; MemoryRegion *mr; sm501_init(&s->state, dev, s->vram_size); @@ -1964,13 +1965,11 @@ static void sm501_realize_sysbus(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->state.mmio_region); /* bridge to usb host emulation module */ - usb_dev = qdev_new("sysbus-ohci"); - qdev_prop_set_uint32(usb_dev, "num-ports", 2); - qdev_prop_set_uint64(usb_dev, "dma-offset", s->base); - sysbus_realize_and_unref(SYS_BUS_DEVICE(usb_dev), &error_fatal); + qdev_prop_set_uint64(DEVICE(&s->ohci), "dma-offset", s->base); + sysbus_realize_and_unref(SYS_BUS_DEVICE(&s->ohci), &error_fatal); memory_region_add_subregion(&s->state.mmio_region, SM501_USB_HOST, - sysbus_mmio_get_region(SYS_BUS_DEVICE(usb_dev), 0)); - sysbus_pass_irq(sbd, SYS_BUS_DEVICE(usb_dev)); + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->ohci), 0)); + sysbus_pass_irq(sbd, SYS_BUS_DEVICE(&s->ohci)); /* bridge to serial emulation module */ sysbus_realize(SYS_BUS_DEVICE(&s->serial), &error_fatal); @@ -2017,8 +2016,12 @@ static void sm501_sysbus_class_init(ObjectClass *klass, void *data) static void sm501_sysbus_init(Object *o) { SM501SysBusState *sm501 = SYSBUS_SM501(o); + OHCISysBusState *ohci = &sm501->ohci; SerialMM *smm = &sm501->serial; + object_initialize_child(o, "ohci", ohci, TYPE_SYSBUS_OHCI); + qdev_prop_set_uint32(DEVICE(ohci), "num-ports", 2); + object_initialize_child(o, "serial", smm, TYPE_SERIAL_MM); qdev_set_legacy_instance_id(DEVICE(smm), SM501_UART0, 2); qdev_prop_set_uint8(DEVICE(smm), "regshift", 2); From 6a015046606ebf260950605ec48fc6420422f43c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 3 Feb 2023 15:31:52 +0100 Subject: [PATCH 089/185] hw/display/sm501: Alias 'dma-offset' QOM property in chipset object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to use an intermediate 'dma-offset' property in the chipset object. Alias the property, so when the machine (here r2d-plus) sets the value on the chipset, it is propagated to the OHCI object. Note we can rename the chipset 'base' property as 'dma-offset' since the object is a non-user-creatable sysbus type. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: BALATON Zoltan Message-Id: <20230203145536.17585-12-philmd@linaro.org> --- hw/display/sm501.c | 7 ++----- hw/sh4/r2d.c | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 38b8d0e16b..bb53499dff 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -1944,7 +1944,6 @@ struct SM501SysBusState { /*< public >*/ SM501State state; uint32_t vram_size; - uint32_t base; SerialMM serial; OHCISysBusState ohci; }; @@ -1965,7 +1964,6 @@ static void sm501_realize_sysbus(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->state.mmio_region); /* bridge to usb host emulation module */ - qdev_prop_set_uint64(DEVICE(&s->ohci), "dma-offset", s->base); sysbus_realize_and_unref(SYS_BUS_DEVICE(&s->ohci), &error_fatal); memory_region_add_subregion(&s->state.mmio_region, SM501_USB_HOST, sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->ohci), 0)); @@ -1980,7 +1978,6 @@ static void sm501_realize_sysbus(DeviceState *dev, Error **errp) static Property sm501_sysbus_properties[] = { DEFINE_PROP_UINT32("vram-size", SM501SysBusState, vram_size, 0), - DEFINE_PROP_UINT32("base", SM501SysBusState, base, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -2020,6 +2017,7 @@ static void sm501_sysbus_init(Object *o) SerialMM *smm = &sm501->serial; object_initialize_child(o, "ohci", ohci, TYPE_SYSBUS_OHCI); + object_property_add_alias(o, "dma-offset", OBJECT(ohci), "dma-offset"); qdev_prop_set_uint32(DEVICE(ohci), "num-ports", 2); object_initialize_child(o, "serial", smm, TYPE_SERIAL_MM); @@ -2027,8 +2025,7 @@ static void sm501_sysbus_init(Object *o) qdev_prop_set_uint8(DEVICE(smm), "regshift", 2); qdev_prop_set_uint8(DEVICE(smm), "endianness", DEVICE_LITTLE_ENDIAN); - object_property_add_alias(o, "chardev", - OBJECT(smm), "chardev"); + object_property_add_alias(o, "chardev", OBJECT(smm), "chardev"); } static const TypeInfo sm501_sysbus_info = { diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 39fc4f19d9..d7020d6d2b 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -274,7 +274,7 @@ static void r2d_init(MachineState *machine) dev = qdev_new("sysbus-sm501"); busdev = SYS_BUS_DEVICE(dev); qdev_prop_set_uint32(dev, "vram-size", SM501_VRAM_SIZE); - qdev_prop_set_uint32(dev, "base", 0x10000000); + qdev_prop_set_uint64(dev, "dma-offset", 0x10000000); qdev_prop_set_chr(dev, "chardev", serial_hd(2)); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, 0x10000000); From 8b0ce7f7c8c71883d2bbcb906469eb60497dfa07 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Wed, 15 Feb 2023 16:35:42 +0100 Subject: [PATCH 090/185] hw/display/sm501: Implement more 2D raster operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add simple implementation for two raster operations that are used by AmigaOS which fixes graphics problems in some programs using these. Signed-off-by: BALATON Zoltan Reported-by: Rene Engel Tested-by: Rene Engel Reviewed-by: Daniel Henrique Barboza Message-Id: <17ef3c59dc7868f75034e9ebe21e2999c8f718d4.1677445307.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/sm501.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index bb53499dff..c4c567d977 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -754,7 +754,7 @@ static void sm501_2d_operation(SM501State *s) } if ((rop_mode && rop == 0x5) || (!rop_mode && rop == 0x55)) { - /* Invert dest, is there a way to do this with pixman? */ + /* DSTINVERT, is there a way to do this with pixman? */ unsigned int x, y, i; uint8_t *d = s->local_mem + dst_base; @@ -764,6 +764,34 @@ static void sm501_2d_operation(SM501State *s) stn_he_p(&d[i], bypp, ~ldn_he_p(&d[i], bypp)); } } + } else if (!rop_mode && rop == 0x99) { + /* DSxn, is there a way to do this with pixman? */ + unsigned int x, y, i, j; + uint8_t *sp = s->local_mem + src_base; + uint8_t *d = s->local_mem + dst_base; + + for (y = 0; y < height; y++) { + i = (dst_x + (dst_y + y) * dst_pitch) * bypp; + j = (src_x + (src_y + y) * src_pitch) * bypp; + for (x = 0; x < width; x++, i += bypp, j += bypp) { + stn_he_p(&d[i], bypp, + ~(ldn_he_p(&sp[j], bypp) ^ ldn_he_p(&d[i], bypp))); + } + } + } else if (!rop_mode && rop == 0xee) { + /* SRCPAINT, is there a way to do this with pixman? */ + unsigned int x, y, i, j; + uint8_t *sp = s->local_mem + src_base; + uint8_t *d = s->local_mem + dst_base; + + for (y = 0; y < height; y++) { + i = (dst_x + (dst_y + y) * dst_pitch) * bypp; + j = (src_x + (src_y + y) * src_pitch) * bypp; + for (x = 0; x < width; x++, i += bypp, j += bypp) { + stn_he_p(&d[i], bypp, + ldn_he_p(&sp[j], bypp) | ldn_he_p(&d[i], bypp)); + } + } } else { /* Do copy src for unimplemented ops, better than unpainted area */ if ((rop_mode && (rop != 0xc || rop2_source_is_pattern)) || From c09b5158e17f7112678a9d80e13419195e45dba8 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sat, 25 Feb 2023 22:35:28 +0100 Subject: [PATCH 091/185] hw/display/sm501: Add fallbacks to pixman routines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pixman may return false if it does not have a suitable implementation. Add fallbacks to handle such cases. Signed-off-by: BALATON Zoltan Reported-by: Rene Engel Tested-by: Rene Engel Message-Id: <20ed9442a0146238254ccc340c0d1efa226c6356.1677445307.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/sm501.c | 75 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index c4c567d977..17835159fc 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -692,7 +692,7 @@ static void sm501_2d_operation(SM501State *s) unsigned int dst_pitch = (s->twoD_pitch >> 16) & 0x1FFF; int crt = (s->dc_crt_control & SM501_DC_CRT_CONTROL_SEL) ? 1 : 0; int fb_len = get_width(s, crt) * get_height(s, crt) * get_bpp(s, crt); - bool overlap = false; + bool overlap = false, fallback = false; if ((s->twoD_stretch >> 16) & 0xF) { qemu_log_mask(LOG_UNIMP, "sm501: only XY addressing is supported.\n"); @@ -835,25 +835,48 @@ static void sm501_2d_operation(SM501State *s) if (tmp_stride * sizeof(uint32_t) * height > sizeof(tmp_buf)) { tmp = g_malloc(tmp_stride * sizeof(uint32_t) * height); } - pixman_blt((uint32_t *)&s->local_mem[src_base], tmp, - src_pitch * bypp / sizeof(uint32_t), - tmp_stride, 8 * bypp, 8 * bypp, - src_x, src_y, 0, 0, width, height); - pixman_blt(tmp, (uint32_t *)&s->local_mem[dst_base], - tmp_stride, - dst_pitch * bypp / sizeof(uint32_t), - 8 * bypp, 8 * bypp, - 0, 0, dst_x, dst_y, width, height); + fallback = !pixman_blt((uint32_t *)&s->local_mem[src_base], + tmp, + src_pitch * bypp / sizeof(uint32_t), + tmp_stride, + 8 * bypp, 8 * bypp, + src_x, src_y, 0, 0, width, height); + if (!fallback) { + fallback = !pixman_blt(tmp, + (uint32_t *)&s->local_mem[dst_base], + tmp_stride, + dst_pitch * bypp / sizeof(uint32_t), + 8 * bypp, 8 * bypp, + 0, 0, dst_x, dst_y, width, height); + } if (tmp != tmp_buf) { g_free(tmp); } } else { - pixman_blt((uint32_t *)&s->local_mem[src_base], - (uint32_t *)&s->local_mem[dst_base], - src_pitch * bypp / sizeof(uint32_t), - dst_pitch * bypp / sizeof(uint32_t), - 8 * bypp, 8 * bypp, - src_x, src_y, dst_x, dst_y, width, height); + fallback = !pixman_blt((uint32_t *)&s->local_mem[src_base], + (uint32_t *)&s->local_mem[dst_base], + src_pitch * bypp / sizeof(uint32_t), + dst_pitch * bypp / sizeof(uint32_t), + 8 * bypp, 8 * bypp, src_x, src_y, + dst_x, dst_y, width, height); + } + if (fallback) { + uint8_t *sp = s->local_mem + src_base; + uint8_t *d = s->local_mem + dst_base; + unsigned int y, i, j; + for (y = 0; y < height; y++) { + if (overlap) { /* overlap also means rtl */ + i = (dst_y + height - 1 - y) * dst_pitch; + i = (dst_x + i) * bypp; + j = (src_y + height - 1 - y) * src_pitch; + j = (src_x + j) * bypp; + memmove(&d[i], &sp[j], width * bypp); + } else { + i = (dst_x + (dst_y + y) * dst_pitch) * bypp; + j = (src_x + (src_y + y) * src_pitch) * bypp; + memcpy(&d[i], &sp[j], width * bypp); + } + } } } break; @@ -868,13 +891,19 @@ static void sm501_2d_operation(SM501State *s) color = cpu_to_le16(color); } - if (width == 1 && height == 1) { - unsigned int i = (dst_x + dst_y * dst_pitch) * bypp; - stn_he_p(&s->local_mem[dst_base + i], bypp, color); - } else { - pixman_fill((uint32_t *)&s->local_mem[dst_base], - dst_pitch * bypp / sizeof(uint32_t), - 8 * bypp, dst_x, dst_y, width, height, color); + if ((width == 1 && height == 1) || + !pixman_fill((uint32_t *)&s->local_mem[dst_base], + dst_pitch * bypp / sizeof(uint32_t), 8 * bypp, + dst_x, dst_y, width, height, color)) { + /* fallback when pixman failed or we don't want to call it */ + uint8_t *d = s->local_mem + dst_base; + unsigned int x, y, i; + for (y = 0; y < height; y++, i += dst_pitch * bypp) { + i = (dst_x + (dst_y + y) * dst_pitch) * bypp; + for (x = 0; x < width; x++, i += bypp) { + stn_he_p(&d[i], bypp, color); + } + } } break; } From 07b29eb378732ac48632c503df85b1837d87e4d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 2 Feb 2023 14:06:42 +0100 Subject: [PATCH 092/185] hw/ppc/sam460ex: Correctly set MAL properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MAL properties are declared as uint8_t: static Property ppc4xx_mal_properties[] = { DEFINE_PROP_UINT8("txc-num", Ppc4xxMalState, txcnum, 0), DEFINE_PROP_UINT8("rxc-num", Ppc4xxMalState, rxcnum, 0), DEFINE_PROP_END_OF_LIST(), }; Correct the API use by setting the property using qdev_prop_set_uint8(). No behavioral change. Fixes: da116a8aab ("ppc/ppc405: QOM'ify MAL") Reviewed-by: BALATON Zoltan Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230203145536.17585-7-philmd@linaro.org> --- hw/ppc/sam460ex.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 4a22ce3761..cf065aae0e 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -389,8 +389,8 @@ static void sam460ex_init(MachineState *machine) /* MAL */ dev = qdev_new(TYPE_PPC4xx_MAL); - qdev_prop_set_uint32(dev, "txc-num", 4); - qdev_prop_set_uint32(dev, "rxc-num", 16); + qdev_prop_set_uint8(dev, "txc-num", 4); + qdev_prop_set_uint8(dev, "rxc-num", 16); ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(dev), cpu, &error_fatal); object_unref(OBJECT(dev)); sbdev = SYS_BUS_DEVICE(dev); From 954ea53c6260126817410e184776606943d20ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 22 Feb 2023 00:15:54 +0100 Subject: [PATCH 093/185] block/vvfat: Remove pointless check of NDEBUG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 262a69f428 ("osdep.h: Prohibit disabling assert() in supported builds") 'NDEBUG' can not be defined, so '#ifndef NDEBUG' is dead code. Remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230221232520.14480-5-philmd@linaro.org> --- block/vvfat.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/block/vvfat.c b/block/vvfat.c index d7d775bd2c..fd45e86416 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -2784,13 +2784,10 @@ static int handle_commits(BDRVVVFATState* s) fail = -2; break; case ACTION_WRITEOUT: { -#ifndef NDEBUG - /* these variables are only used by assert() below */ direntry_t* entry = array_get(&(s->directory), commit->param.writeout.dir_index); uint32_t begin = begin_of_direntry(entry); mapping_t* mapping = find_mapping_for_cluster(s, begin); -#endif assert(mapping); assert(mapping->begin == begin); From 837cd584f404bbe613a58743e65bb10a2df7506d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 22 Feb 2023 00:00:42 +0100 Subject: [PATCH 094/185] scripts/checkpatch.pl: Do not allow assert(0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 262a69f428 ("osdep.h: Prohibit disabling assert() in supported builds") we can not build QEMU with NDEBUG (or G_DISABLE_ASSERT) defined, thus 'assert(0)' always aborts QEMU. However some static analyzers / compilers doesn't notice NDEBUG can't be defined and emit warnings if code is used after an 'assert(0)' call. Apparently such compiler isn't as clever with G_DISABLE_ASSERT, so we can silent these warnings by using g_assert_not_reached() which is easier to read anyway. In order to avoid these annoying warnings, add a checkpatch rule to prohibit 'assert(0)'. Suggest using g_assert_not_reached() instead. For example when reverting the previous patch we get: ERROR: use g_assert_not_reached() instead of assert(0) #21: FILE: target/ppc/dfp_helper.c:124: + assert(0); /* cannot get here */ ERROR: use g_assert_not_reached() instead of assert(0) #30: FILE: target/ppc/dfp_helper.c:141: + assert(0); /* cannot get here */ total: 2 errors, 0 warnings, 16 lines checked Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20230221232520.14480-3-philmd@linaro.org> --- scripts/checkpatch.pl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 6ecabfb2b5..d768171dcf 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2982,6 +2982,9 @@ sub process { if ($line =~ /\bsysconf\(_SC_PAGESIZE\)/) { ERROR("use qemu_real_host_page_size() instead of sysconf(_SC_PAGESIZE)\n" . $herecurr); } + if ($line =~ /\b(g_)?assert\(0\)/) { + ERROR("use g_assert_not_reached() instead of assert(0)\n" . $herecurr); + } my $non_exit_glib_asserts = qr{g_assert_cmpstr| g_assert_cmpint| g_assert_cmpuint| From 1b0477a6d9eecd8c12c072510a170bcd0b1f6bbb Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 6 Feb 2023 13:25:44 -0500 Subject: [PATCH 095/185] MAINTAINERS: Mark IDE and Floppy as "Odd Fixes" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I have not been able to give these devices the love they need for a while now. Update the maintainers file to reflect the truth of the matter. Signed-off-by: John Snow Message-Id: <20230206182544.711117-1-jsnow@redhat.com> Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index da930c34a9..92bee632a7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1798,7 +1798,7 @@ F: hw/misc/edu.c IDE M: John Snow L: qemu-block@nongnu.org -S: Supported +S: Odd Fixes F: include/hw/ide.h F: include/hw/ide/ F: hw/ide/ @@ -1823,7 +1823,7 @@ T: git https://github.com/cminyard/qemu.git master-ipmi-rebase Floppy M: John Snow L: qemu-block@nongnu.org -S: Supported +S: Odd Fixes F: hw/block/fdc.c F: hw/block/fdc-internal.h F: hw/block/fdc-isa.c From 60462816b5effb6aafe81b78aca51f5c6efa3816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 20 Feb 2023 10:25:22 +0100 Subject: [PATCH 096/185] hw/i386/xen: Remove unused 'hw/ide.h' include from header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Acked-by: Paul Durrant Message-Id: <20230220092707.22584-1-philmd@linaro.org> --- hw/i386/xen/xen_platform.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index 66e6de31a6..3795a203d4 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -25,7 +25,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "hw/ide.h" #include "hw/ide/pci.h" #include "hw/pci/pci.h" #include "hw/xen/xen_common.h" From eb8fde18abc317d612c5d12bc273c5a42a912c5e Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 17 Feb 2023 11:31:30 +0100 Subject: [PATCH 097/185] hw/ide/ahci: Trace ncq write command as write instead of read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: e4baa9f00b ("AHCI: Replace DPRINTF with trace-events") Signed-off-by: Fiona Ebner Reviewed-by: Alex Bennée Reviewed-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kevin Wolf Message-Id: <20230217103130.42077-1-f.ebner@proxmox.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/ide/ahci.c | 4 ++-- hw/ide/trace-events | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 7ce001cacd..595a96203f 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1085,8 +1085,8 @@ static void execute_ncq_command(NCQTransferState *ncq_tfs) ncq_cb, ncq_tfs); break; case WRITE_FPDMA_QUEUED: - trace_execute_ncq_command_read(ad->hba, port, ncq_tfs->tag, - ncq_tfs->sector_count, ncq_tfs->lba); + trace_execute_ncq_command_write(ad->hba, port, ncq_tfs->tag, + ncq_tfs->sector_count, ncq_tfs->lba); dma_acct_start(ide_state->blk, &ncq_tfs->acct, &ncq_tfs->sglist, BLOCK_ACCT_WRITE); ncq_tfs->aiocb = dma_blk_write(ide_state->blk, &ncq_tfs->sglist, diff --git a/hw/ide/trace-events b/hw/ide/trace-events index 15d7921f15..5ef344ae73 100644 --- a/hw/ide/trace-events +++ b/hw/ide/trace-events @@ -91,6 +91,7 @@ ahci_populate_sglist_short_map(void *s, int port) "ahci(%p)[%d]: mapped less tha ahci_populate_sglist_bad_offset(void *s, int port, int off_idx, int64_t off_pos) "ahci(%p)[%d]: Incorrect offset! off_idx: %d, off_pos: %"PRId64 ncq_finish(void *s, int port, uint8_t tag) "ahci(%p)[%d][tag:%d]: NCQ transfer finished" execute_ncq_command_read(void *s, int port, uint8_t tag, int count, int64_t lba) "ahci(%p)[%d][tag:%d]: NCQ reading %d sectors from LBA %"PRId64 +execute_ncq_command_write(void *s, int port, uint8_t tag, int count, int64_t lba) "ahci(%p)[%d][tag:%d]: NCQ writing %d sectors to LBA %"PRId64 execute_ncq_command_unsup(void *s, int port, uint8_t tag, uint8_t cmd) "ahci(%p)[%d][tag:%d]: error: unsupported NCQ command (0x%02x) received" process_ncq_command_mismatch(void *s, int port, uint8_t tag, uint8_t slot) "ahci(%p)[%d][tag:%d]: Warning: NCQ slot (%d) did not match the given tag" process_ncq_command_aux(void *s, int port, uint8_t tag) "ahci(%p)[%d][tag:%d]: Warn: Attempt to use NCQ auxiliary fields" From c79f63ff39d8b48a88104f08dccad76eccb5f27a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Jan 2023 11:37:55 +0100 Subject: [PATCH 098/185] hw/ide/mmio: Use CamelCase for MMIO_IDE state name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following docs/devel/style.rst guidelines, rename MMIOIDEState as IdeMmioState. Having the structure name and its typedef named equally, we can manually convert from the old DECLARE_INSTANCE_CHECKER() macro to the more recent OBJECT_DECLARE_SIMPLE_TYPE(). Note, due to that name mismatch, this macro wasn't automatically converted during commit 8063396bf3 ("Use OBJECT_DECLARE_SIMPLE_TYPE when possible"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Message-Id: <20230220091358.17038-3-philmd@linaro.org> --- hw/ide/mmio.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index fb2ebd4847..08493930b7 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -40,9 +40,7 @@ */ #define TYPE_MMIO_IDE "mmio-ide" -typedef struct MMIOIDEState MMIOState; -DECLARE_INSTANCE_CHECKER(MMIOState, MMIO_IDE, - TYPE_MMIO_IDE) +OBJECT_DECLARE_SIMPLE_TYPE(MMIOIDEState, MMIO_IDE) struct MMIOIDEState { /*< private >*/ @@ -58,7 +56,7 @@ struct MMIOIDEState { static void mmio_ide_reset(DeviceState *dev) { - MMIOState *s = MMIO_IDE(dev); + MMIOIDEState *s = MMIO_IDE(dev); ide_bus_reset(&s->bus); } @@ -66,7 +64,7 @@ static void mmio_ide_reset(DeviceState *dev) static uint64_t mmio_ide_read(void *opaque, hwaddr addr, unsigned size) { - MMIOState *s = opaque; + MMIOIDEState *s = opaque; addr >>= s->shift; if (addr & 7) return ide_ioport_read(&s->bus, addr); @@ -77,7 +75,7 @@ static uint64_t mmio_ide_read(void *opaque, hwaddr addr, static void mmio_ide_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - MMIOState *s = opaque; + MMIOIDEState *s = opaque; addr >>= s->shift; if (addr & 7) ide_ioport_write(&s->bus, addr, val); @@ -94,14 +92,14 @@ static const MemoryRegionOps mmio_ide_ops = { static uint64_t mmio_ide_status_read(void *opaque, hwaddr addr, unsigned size) { - MMIOState *s= opaque; + MMIOIDEState *s = opaque; return ide_status_read(&s->bus, 0); } static void mmio_ide_ctrl_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - MMIOState *s = opaque; + MMIOIDEState *s = opaque; ide_ctrl_write(&s->bus, 0, val); } @@ -116,8 +114,8 @@ static const VMStateDescription vmstate_ide_mmio = { .version_id = 3, .minimum_version_id = 0, .fields = (VMStateField[]) { - VMSTATE_IDE_BUS(bus, MMIOState), - VMSTATE_IDE_DRIVES(bus.ifs, MMIOState), + VMSTATE_IDE_BUS(bus, MMIOIDEState), + VMSTATE_IDE_DRIVES(bus.ifs, MMIOIDEState), VMSTATE_END_OF_LIST() } }; @@ -125,7 +123,7 @@ static const VMStateDescription vmstate_ide_mmio = { static void mmio_ide_realizefn(DeviceState *dev, Error **errp) { SysBusDevice *d = SYS_BUS_DEVICE(dev); - MMIOState *s = MMIO_IDE(dev); + MMIOIDEState *s = MMIO_IDE(dev); ide_init2(&s->bus, s->irq); @@ -140,14 +138,14 @@ static void mmio_ide_realizefn(DeviceState *dev, Error **errp) static void mmio_ide_initfn(Object *obj) { SysBusDevice *d = SYS_BUS_DEVICE(obj); - MMIOState *s = MMIO_IDE(obj); + MMIOIDEState *s = MMIO_IDE(obj); ide_bus_init(&s->bus, sizeof(s->bus), DEVICE(obj), 0, 2); sysbus_init_irq(d, &s->irq); } static Property mmio_ide_properties[] = { - DEFINE_PROP_UINT32("shift", MMIOState, shift, 0), + DEFINE_PROP_UINT32("shift", MMIOIDEState, shift, 0), DEFINE_PROP_END_OF_LIST() }; @@ -164,7 +162,7 @@ static void mmio_ide_class_init(ObjectClass *oc, void *data) static const TypeInfo mmio_ide_type_info = { .name = TYPE_MMIO_IDE, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MMIOState), + .instance_size = sizeof(MMIOIDEState), .instance_init = mmio_ide_initfn, .class_init = mmio_ide_class_init, }; @@ -176,7 +174,7 @@ static void mmio_ide_register_types(void) void mmio_ide_init_drives(DeviceState *dev, DriveInfo *hd0, DriveInfo *hd1) { - MMIOState *s = MMIO_IDE(dev); + MMIOIDEState *s = MMIO_IDE(dev); if (hd0 != NULL) { ide_create_drive(&s->bus, 0, hd0); From 01c43405d63210fcb7da6a3cab862f0651942596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 23:24:42 +0100 Subject: [PATCH 099/185] hw/ide/mmio: Extract TYPE_MMIO_IDE declarations to 'hw/ide/mmio.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "hw/ide.h" is a mixed bag of lost IDE declarations. Extract mmio_ide_init_drives() and the TYPE_MMIO_IDE QOM declarations to a new "hw/ide/mmio.h" header. Document the SysBus interface. Message-Id: <20230215112712.23110-4-philmd@linaro.org> Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé --- hw/ide/mmio.c | 5 +---- hw/sh4/r2d.c | 2 +- include/hw/ide.h | 3 --- include/hw/ide/mmio.h | 26 ++++++++++++++++++++++++++ 4 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 include/hw/ide/mmio.h diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index 08493930b7..6bf9048b1e 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -29,9 +29,9 @@ #include "qemu/module.h" #include "sysemu/dma.h" +#include "hw/ide/mmio.h" #include "hw/ide/internal.h" #include "hw/qdev-properties.h" -#include "qom/object.h" /***********************************************************/ /* MMIO based ide port @@ -39,9 +39,6 @@ * dedicated ide controller, which is often seen on embedded boards. */ -#define TYPE_MMIO_IDE "mmio-ide" -OBJECT_DECLARE_SIMPLE_TYPE(MMIOIDEState, MMIO_IDE) - struct MMIOIDEState { /*< private >*/ SysBusDevice parent_obj; diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index d7020d6d2b..826a0a31b5 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -38,7 +38,7 @@ #include "hw/qdev-properties.h" #include "net/net.h" #include "sh7750_regs.h" -#include "hw/ide.h" +#include "hw/ide/mmio.h" #include "hw/irq.h" #include "hw/loader.h" #include "hw/usb.h" diff --git a/include/hw/ide.h b/include/hw/ide.h index 60f1f4f714..5f8c36b2aa 100644 --- a/include/hw/ide.h +++ b/include/hw/ide.h @@ -8,9 +8,6 @@ ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq, DriveInfo *hd0, DriveInfo *hd1); -/* ide-mmio.c */ -void mmio_ide_init_drives(DeviceState *dev, DriveInfo *hd0, DriveInfo *hd1); - int ide_get_geometry(BusState *bus, int unit, int16_t *cyls, int8_t *heads, int8_t *secs); int ide_get_bios_chs_trans(BusState *bus, int unit); diff --git a/include/hw/ide/mmio.h b/include/hw/ide/mmio.h new file mode 100644 index 0000000000..d726a49848 --- /dev/null +++ b/include/hw/ide/mmio.h @@ -0,0 +1,26 @@ +/* + * QEMU IDE Emulation: mmio support (for embedded). + * + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2006 Openedhand Ltd. + * + * SPDX-License-Identifier: MIT + */ + +#ifndef HW_IDE_MMIO_H +#define HW_IDE_MMIO_H + +#include "qom/object.h" + +/* + * QEMU interface: + * + sysbus IRQ 0: asserted by the IDE channel + * + sysbus MMIO region 0: data registers + * + sysbus MMIO region 1: status & control registers + */ +#define TYPE_MMIO_IDE "mmio-ide" +OBJECT_DECLARE_SIMPLE_TYPE(MMIOIDEState, MMIO_IDE) + +void mmio_ide_init_drives(DeviceState *dev, DriveInfo *hd0, DriveInfo *hd1); + +#endif From 794093e80a2fb0e13f921b52ebe1a02ad30ab735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 23:26:36 +0100 Subject: [PATCH 100/185] hw/ide/isa: Extract TYPE_ISA_IDE declarations to 'hw/ide/isa.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "hw/ide.h" is a mixed bag of lost IDE declarations. Extract isa_ide_init() and the TYPE_ISA_IDE QOM declarations to a new "hw/ide/isa.h" header. Rename ISAIDEState::isairq as 'irqnum' to emphasize this is not a qemu_irq object but the number (index) of an ISA IRQ. Message-Id: <20230215112712.23110-5-philmd@linaro.org> Reviewed-by: Richard Henderson Reviewed-by: Bernhard Beschow Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/pc_piix.c | 1 + hw/ide/isa.c | 14 ++++++-------- include/hw/ide.h | 5 ----- include/hw/ide/isa.h | 20 ++++++++++++++++++++ 4 files changed, 27 insertions(+), 13 deletions(-) create mode 100644 include/hw/ide/isa.h diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 7c48ba30e0..126b6c11df 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -39,6 +39,7 @@ #include "hw/pci/pci_ids.h" #include "hw/usb.h" #include "net/net.h" +#include "hw/ide/isa.h" #include "hw/ide/pci.h" #include "hw/ide/piix.h" #include "hw/irq.h" diff --git a/hw/ide/isa.c b/hw/ide/isa.c index 8bedbd13f1..5c3e83a0fc 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -31,22 +31,20 @@ #include "qemu/module.h" #include "sysemu/dma.h" +#include "hw/ide/isa.h" #include "hw/ide/internal.h" #include "qom/object.h" /***********************************************************/ /* ISA IDE definitions */ -#define TYPE_ISA_IDE "isa-ide" -OBJECT_DECLARE_SIMPLE_TYPE(ISAIDEState, ISA_IDE) - struct ISAIDEState { ISADevice parent_obj; IDEBus bus; uint32_t iobase; uint32_t iobase2; - uint32_t isairq; + uint32_t irqnum; qemu_irq irq; }; @@ -75,13 +73,13 @@ static void isa_ide_realizefn(DeviceState *dev, Error **errp) ide_bus_init(&s->bus, sizeof(s->bus), dev, 0, 2); ide_init_ioport(&s->bus, isadev, s->iobase, s->iobase2); - s->irq = isa_get_irq(isadev, s->isairq); + s->irq = isa_get_irq(isadev, s->irqnum); ide_init2(&s->bus, s->irq); vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_isa, s); ide_register_restart_cb(&s->bus); } -ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq, +ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int irqnum, DriveInfo *hd0, DriveInfo *hd1) { DeviceState *dev; @@ -92,7 +90,7 @@ ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq, dev = DEVICE(isadev); qdev_prop_set_uint32(dev, "iobase", iobase); qdev_prop_set_uint32(dev, "iobase2", iobase2); - qdev_prop_set_uint32(dev, "irq", isairq); + qdev_prop_set_uint32(dev, "irq", irqnum); isa_realize_and_unref(isadev, bus, &error_fatal); s = ISA_IDE(dev); @@ -108,7 +106,7 @@ ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq, static Property isa_ide_properties[] = { DEFINE_PROP_UINT32("iobase", ISAIDEState, iobase, 0x1f0), DEFINE_PROP_UINT32("iobase2", ISAIDEState, iobase2, 0x3f6), - DEFINE_PROP_UINT32("irq", ISAIDEState, isairq, 14), + DEFINE_PROP_UINT32("irq", ISAIDEState, irqnum, 14), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/hw/ide.h b/include/hw/ide.h index 5f8c36b2aa..24a7aa2925 100644 --- a/include/hw/ide.h +++ b/include/hw/ide.h @@ -1,13 +1,8 @@ #ifndef HW_IDE_H #define HW_IDE_H -#include "hw/isa/isa.h" #include "exec/memory.h" -/* ide-isa.c */ -ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq, - DriveInfo *hd0, DriveInfo *hd1); - int ide_get_geometry(BusState *bus, int unit, int16_t *cyls, int8_t *heads, int8_t *secs); int ide_get_bios_chs_trans(BusState *bus, int unit); diff --git a/include/hw/ide/isa.h b/include/hw/ide/isa.h new file mode 100644 index 0000000000..1cd0ff1fa6 --- /dev/null +++ b/include/hw/ide/isa.h @@ -0,0 +1,20 @@ +/* + * QEMU IDE Emulation: ISA Bus support. + * + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2006 Openedhand Ltd. + * + * SPDX-License-Identifier: MIT + */ +#ifndef HW_IDE_ISA_H +#define HW_IDE_ISA_H + +#include "qom/object.h" + +#define TYPE_ISA_IDE "isa-ide" +OBJECT_DECLARE_SIMPLE_TYPE(ISAIDEState, ISA_IDE) + +ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int irqnum, + DriveInfo *hd0, DriveInfo *hd1); + +#endif From 3b759fbf390dbfd86fe397a4f7dc5d4920d37091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Feb 2023 23:16:51 +0100 Subject: [PATCH 101/185] hw/ide/isa: Remove intermediate ISAIDEState::irq variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The intermediate ISAIDEState::irq variable just add noise, remove it. Message-Id: <20230215112712.23110-6-philmd@linaro.org> Reviewed-by: Bernhard Beschow Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé --- hw/ide/isa.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/ide/isa.c b/hw/ide/isa.c index 5c3e83a0fc..ad47e0899e 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -45,7 +45,6 @@ struct ISAIDEState { uint32_t iobase; uint32_t iobase2; uint32_t irqnum; - qemu_irq irq; }; static void isa_ide_reset(DeviceState *d) @@ -73,8 +72,7 @@ static void isa_ide_realizefn(DeviceState *dev, Error **errp) ide_bus_init(&s->bus, sizeof(s->bus), dev, 0, 2); ide_init_ioport(&s->bus, isadev, s->iobase, s->iobase2); - s->irq = isa_get_irq(isadev, s->irqnum); - ide_init2(&s->bus, s->irq); + ide_init2(&s->bus, isa_get_irq(isadev, s->irqnum)); vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_isa, s); ide_register_restart_cb(&s->bus); } From cc135b10f4385d374fdbb4032618da8f5fd965a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 23:35:21 +0100 Subject: [PATCH 102/185] hw/ide/atapi: Restrict 'scsi/constants.h' inclusion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only atapi.c requires the SCSI constants. No need to include it in all files including "hw/ide/internal.h". Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230215112712.23110-7-philmd@linaro.org> Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée --- hw/ide/atapi.c | 1 + include/hw/ide/internal.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index 0a9aa6f009..0c36bd0afd 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -27,6 +27,7 @@ #include "hw/ide/internal.h" #include "hw/scsi/scsi.h" #include "sysemu/block-backend.h" +#include "scsi/constants.h" #include "trace.h" #define ATAPI_SECTOR_BITS (2 + BDRV_SECTOR_BITS) diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index fc0aa81a88..29a8e79817 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -13,7 +13,6 @@ #include "hw/isa/isa.h" #include "sysemu/dma.h" #include "hw/block/block.h" -#include "scsi/constants.h" /* debug IDE devices */ #define USE_DMA_CDROM From 989bc90b2c5fe6f3144de0c54a1678bbc3ef505c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 23:36:25 +0100 Subject: [PATCH 103/185] hw/ide: Remove unused 'qapi/qapi-types-run-state.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Missed in commit d7458e7754 ("hw/ide/internal: Remove unused DMARestartFunc typedef") which removed the single use of RunState. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230215112712.23110-8-philmd@linaro.org> Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée --- include/hw/ide/internal.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index 29a8e79817..e6deb1c5dc 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -7,7 +7,6 @@ * non-internal declarations are in hw/ide.h */ -#include "qapi/qapi-types-run-state.h" #include "hw/ide.h" #include "hw/irq.h" #include "hw/isa/isa.h" From 1f7a0d0339c1b273865f849eb3b83c411e344865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 23:37:23 +0100 Subject: [PATCH 104/185] hw/ide: Include 'exec/ioport.h' instead of 'hw/isa/isa.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The IDEBus structure has PortioList fields, so we need its declarations from "exec/ioport.h". "hw/isa/isa.h" is not required. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230215112712.23110-9-philmd@linaro.org> Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée --- include/hw/ide/internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index e6deb1c5dc..84d3722d67 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -9,9 +9,9 @@ #include "hw/ide.h" #include "hw/irq.h" -#include "hw/isa/isa.h" #include "sysemu/dma.h" #include "hw/block/block.h" +#include "exec/ioport.h" /* debug IDE devices */ #define USE_DMA_CDROM From da9f1172c0125672314dd07e237db43d4bbc3805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 23:01:55 +0100 Subject: [PATCH 105/185] hw/ide: Un-inline ide_set_irq() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only include "hw/irq.h" where appropriate. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230215112712.23110-10-philmd@linaro.org> Reviewed-by: Richard Henderson --- hw/ide/ahci.c | 1 + hw/ide/core.c | 8 ++++++++ hw/ide/ich.c | 1 + hw/ide/macio.c | 1 + hw/ide/microdrive.c | 1 + hw/ide/pci.c | 1 + hw/misc/macio/gpio.c | 1 + hw/sparc64/sun4u.c | 1 + include/hw/ide/internal.h | 9 +-------- 9 files changed, 16 insertions(+), 8 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 595a96203f..08c7ae6e3c 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -22,6 +22,7 @@ */ #include "qemu/osdep.h" +#include "hw/irq.h" #include "hw/pci/msi.h" #include "hw/pci/pci.h" #include "hw/qdev-properties.h" diff --git a/hw/ide/core.c b/hw/ide/core.c index 5d1039378f..1473b6057f 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "hw/irq.h" #include "hw/isa/isa.h" #include "migration/vmstate.h" #include "qemu/error-report.h" @@ -2782,6 +2783,13 @@ void ide_init2(IDEBus *bus, qemu_irq irq) bus->dma = &ide_dma_nop; } +void ide_set_irq(IDEBus *bus) +{ + if (!(bus->cmd & IDE_CTRL_DISABLE_IRQ)) { + qemu_irq_raise(bus->irq); + } +} + void ide_exit(IDEState *s) { timer_free(s->sector_write_timer); diff --git a/hw/ide/ich.c b/hw/ide/ich.c index 1007a51fcb..d61faab532 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -61,6 +61,7 @@ */ #include "qemu/osdep.h" +#include "hw/irq.h" #include "hw/pci/msi.h" #include "hw/pci/pci.h" #include "migration/vmstate.h" diff --git a/hw/ide/macio.c b/hw/ide/macio.c index e604466acb..15fd934831 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "hw/irq.h" #include "hw/ppc/mac_dbdma.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" diff --git a/hw/ide/microdrive.c b/hw/ide/microdrive.c index 56c5be3655..b9822b939b 100644 --- a/hw/ide/microdrive.c +++ b/hw/ide/microdrive.c @@ -29,6 +29,7 @@ #include "qapi/error.h" #include "qemu/module.h" #include "sysemu/dma.h" +#include "hw/irq.h" #include "hw/ide/internal.h" #include "qom/object.h" diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 84ba733548..ae638dee0d 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "hw/irq.h" #include "hw/pci/pci.h" #include "migration/vmstate.h" #include "sysemu/dma.h" diff --git a/hw/misc/macio/gpio.c b/hw/misc/macio/gpio.c index c8ac5633b2..4deb330471 100644 --- a/hw/misc/macio/gpio.c +++ b/hw/misc/macio/gpio.c @@ -28,6 +28,7 @@ #include "migration/vmstate.h" #include "hw/misc/macio/macio.h" #include "hw/misc/macio/gpio.h" +#include "hw/irq.h" #include "hw/nmi.h" #include "qemu/log.h" #include "qemu/module.h" diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 8a56ba9f98..a25e951f9d 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -28,6 +28,7 @@ #include "qapi/error.h" #include "qemu/datadir.h" #include "cpu.h" +#include "hw/irq.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bridge.h" #include "hw/pci/pci_bus.h" diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index 84d3722d67..57a6278327 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -8,7 +8,6 @@ */ #include "hw/ide.h" -#include "hw/irq.h" #include "sysemu/dma.h" #include "hw/block/block.h" #include "exec/ioport.h" @@ -572,13 +571,6 @@ static inline IDEState *idebus_active_if(IDEBus *bus) return bus->ifs + bus->unit; } -static inline void ide_set_irq(IDEBus *bus) -{ - if (!(bus->cmd & IDE_CTRL_DISABLE_IRQ)) { - qemu_irq_raise(bus->irq); - } -} - /* hw/ide/core.c */ extern const VMStateDescription vmstate_ide_bus; @@ -627,6 +619,7 @@ int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind, void ide_init2(IDEBus *bus, qemu_irq irq); void ide_exit(IDEState *s); int ide_init_ioport(IDEBus *bus, ISADevice *isa, int iobase, int iobase2); +void ide_set_irq(IDEBus *bus); void ide_register_restart_cb(IDEBus *bus); void ide_exec_cmd(IDEBus *bus, uint32_t val); From 0cfe719d1fa8c34c813c8b51074523893fd15203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 11:33:47 +0100 Subject: [PATCH 106/185] hw/ide: Rename ide_set_irq() -> ide_bus_set_irq() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ide_set_irq() operates on a IDEBus; rename it as ide_bus_set_irq() to emphasize its first argument is a IDEBus. Mechanical change using: $ sed -i -e 's/ide_set_irq/ide_bus_set_irq/g' \ $(git grep -l ide_set_irq) Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230215112712.23110-11-philmd@linaro.org> Reviewed-by: Richard Henderson --- hw/ide/atapi.c | 12 +++++------ hw/ide/core.c | 44 +++++++++++++++++++-------------------- hw/ide/macio.c | 2 +- include/hw/ide/internal.h | 2 +- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index 0c36bd0afd..dcc39df9a4 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -179,7 +179,7 @@ void ide_atapi_cmd_ok(IDEState *s) s->status = READY_STAT | SEEK_STAT; s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; ide_transfer_stop(s); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc) @@ -191,7 +191,7 @@ void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc) s->sense_key = sense_key; s->asc = asc; ide_transfer_stop(s); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } void ide_atapi_io_error(IDEState *s, int ret) @@ -254,7 +254,7 @@ void ide_atapi_cmd_reply_end(IDEState *s) } else { /* a new transfer is needed */ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); byte_count_limit = atapi_byte_count_limit(s); trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit); size = s->packet_transfer_size; @@ -294,7 +294,7 @@ void ide_atapi_cmd_reply_end(IDEState *s) /* end of transfer */ trace_ide_atapi_cmd_reply_end_eot(s, s->status); ide_atapi_cmd_ok(s); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } /* send a reply of 'size' bytes in s->io_buffer to an ATAPI command */ @@ -340,7 +340,7 @@ static void ide_atapi_cmd_check_status(IDEState *s) s->error = MC_ERR | (UNIT_ATTENTION << 4); s->status = ERR_STAT; s->nsector = 0; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } /* ATAPI DMA support */ @@ -384,7 +384,7 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret) if (s->packet_transfer_size <= 0) { s->status = READY_STAT | SEEK_STAT; s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); goto eot; } diff --git a/hw/ide/core.c b/hw/ide/core.c index 1473b6057f..117e26cef1 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -654,7 +654,7 @@ void ide_set_sector(IDEState *s, int64_t sector_num) static void ide_rw_error(IDEState *s) { ide_abort_command(s); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } static void ide_buffered_readv_cb(void *opaque, int ret) @@ -773,7 +773,7 @@ static void ide_sector_read_cb(void *opaque, int ret) s->nsector -= n; /* Allow the guest to read the io_buffer */ ide_transfer_start(s, s->io_buffer, n * BDRV_SECTOR_SIZE, ide_sector_read); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } static void ide_sector_read(IDEState *s) @@ -837,7 +837,7 @@ void ide_dma_error(IDEState *s) dma_buf_commit(s, 0); ide_abort_command(s); ide_set_inactive(s, false); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } int ide_handle_rw_error(IDEState *s, int error, int op) @@ -907,7 +907,7 @@ static void ide_dma_cb(void *opaque, int ret) /* end of transfer ? */ if (s->nsector == 0) { s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); goto eot; } @@ -1007,7 +1007,7 @@ static void ide_sector_write(IDEState *s); static void ide_sector_write_timer_cb(void *opaque) { IDEState *s = opaque; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } static void ide_sector_write_cb(void *opaque, int ret) @@ -1055,7 +1055,7 @@ static void ide_sector_write_cb(void *opaque, int ret) timer_mod(s->sector_write_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (NANOSECONDS_PER_SECOND / 1000)); } else { - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } } @@ -1106,7 +1106,7 @@ static void ide_flush_cb(void *opaque, int ret) } s->status = READY_STAT | SEEK_STAT; ide_cmd_done(s); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } static void ide_flush_cache(IDEState *s) @@ -1195,7 +1195,7 @@ static void ide_cd_change_cb(void *opaque, bool load, Error **errp) s->cdrom_changed = 1; s->events.new_media = true; s->events.eject_request = false; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } static void ide_cd_eject_request_cb(void *opaque, bool force) @@ -1206,7 +1206,7 @@ static void ide_cd_eject_request_cb(void *opaque, bool force) if (force) { s->tray_locked = false; } - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } static void ide_cmd_lba48_transform(IDEState *s, int lba48) @@ -1440,7 +1440,7 @@ static bool cmd_identify(IDEState *s, uint8_t cmd) } s->status = READY_STAT | SEEK_STAT; ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); return false; } else { if (s->drive_kind == IDE_CD) { @@ -1630,7 +1630,7 @@ static bool cmd_specify(IDEState *s, uint8_t cmd) if (s->blk && s->drive_kind != IDE_CD) { s->heads = (s->select & (ATA_DEV_HS)) + 1; s->sectors = s->nsector; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } else { ide_abort_command(s); } @@ -1731,7 +1731,7 @@ static bool cmd_identify_packet(IDEState *s, uint8_t cmd) ide_atapi_identify(s); s->status = READY_STAT | SEEK_STAT; ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); return false; } @@ -1756,7 +1756,7 @@ static bool cmd_exec_dev_diagnostic(IDEState *s, uint8_t cmd) * They are part of the regular output (this is why ERR_STAT isn't set) * Device 0 passed, Device 1 passed or not present. */ s->error = 0x01; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } return false; @@ -1788,7 +1788,7 @@ static bool cmd_cfa_req_ext_error_code(IDEState *s, uint8_t cmd) { s->error = 0x09; /* miscellaneous error */ s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); return false; } @@ -1827,7 +1827,7 @@ static bool cmd_cfa_translate_sector(IDEState *s, uint8_t cmd) s->io_buffer[0x1a] = 0x01; /* Hot count */ ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); return false; } @@ -1851,7 +1851,7 @@ static bool cmd_cfa_access_metadata_storage(IDEState *s, uint8_t cmd) ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); s->status = 0x00; /* NOTE: READY is _not_ set */ - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); return false; } @@ -1934,7 +1934,7 @@ static bool cmd_smart(IDEState *s, uint8_t cmd) s->status = READY_STAT | SEEK_STAT; ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); return false; case SMART_READ_DATA: @@ -1975,7 +1975,7 @@ static bool cmd_smart(IDEState *s, uint8_t cmd) s->status = READY_STAT | SEEK_STAT; ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); return false; case SMART_READ_LOG: @@ -2014,7 +2014,7 @@ static bool cmd_smart(IDEState *s, uint8_t cmd) } s->status = READY_STAT | SEEK_STAT; ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); return false; case SMART_EXECUTE_OFFLINE: @@ -2146,7 +2146,7 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) if (!ide_cmd_permitted(s, val)) { ide_abort_command(s); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); return; } @@ -2164,7 +2164,7 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) } ide_cmd_done(s); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } } @@ -2783,7 +2783,7 @@ void ide_init2(IDEBus *bus, qemu_irq irq) bus->dma = &ide_dma_nop; } -void ide_set_irq(IDEBus *bus) +void ide_bus_set_irq(IDEBus *bus) { if (!(bus->cmd & IDE_CTRL_DISABLE_IRQ)) { qemu_irq_raise(bus->irq); diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 15fd934831..24fb7a3f9d 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -160,7 +160,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) MACIO_DPRINTF("End of IDE transfer\n"); qemu_sglist_destroy(&s->sg); s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); m->dma_active = false; goto done; } diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index 57a6278327..7b4b71d0b0 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -619,7 +619,7 @@ int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind, void ide_init2(IDEBus *bus, qemu_irq irq); void ide_exit(IDEState *s); int ide_init_ioport(IDEBus *bus, ISADevice *isa, int iobase, int iobase2); -void ide_set_irq(IDEBus *bus); +void ide_bus_set_irq(IDEBus *bus); void ide_register_restart_cb(IDEBus *bus); void ide_exec_cmd(IDEBus *bus, uint32_t val); From b6a5ab27fbc42731877b0297062dccd4239874ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 11:31:51 +0100 Subject: [PATCH 107/185] hw/ide: Rename ide_create_drive() -> ide_bus_create_drive() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ide_create_drive() operates on a IDEBus; rename it as ide_bus_create_drive() to emphasize its first argument is a IDEBus. Mechanical change using: $ sed -i -e 's/ide_create_drive/ide_bus_create_drive/g' \ $(git grep -wl ide_create_drive) Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230215112712.23110-12-philmd@linaro.org> Reviewed-by: Richard Henderson --- hw/arm/sbsa-ref.c | 2 +- hw/ide/ahci.c | 2 +- hw/ide/isa.c | 4 ++-- hw/ide/macio.c | 2 +- hw/ide/microdrive.c | 2 +- hw/ide/mmio.c | 4 ++-- hw/ide/pci.c | 2 +- hw/ide/qdev.c | 2 +- include/hw/ide/internal.h | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index f778cb6d09..0b93558dde 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -554,7 +554,7 @@ static void create_ahci(const SBSAMachineState *sms) if (hd[i] == NULL) { continue; } - ide_create_drive(&ahci->dev[i].port, 0, hd[i]); + ide_bus_create_drive(&ahci->dev[i].port, 0, hd[i]); } } diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 08c7ae6e3c..f338a55c4e 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1842,7 +1842,7 @@ void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **hd) if (hd[i] == NULL) { continue; } - ide_create_drive(&ahci->dev[i].port, 0, hd[i]); + ide_bus_create_drive(&ahci->dev[i].port, 0, hd[i]); } } diff --git a/hw/ide/isa.c b/hw/ide/isa.c index ad47e0899e..74f7b43137 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -93,10 +93,10 @@ ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int irqnum, s = ISA_IDE(dev); if (hd0) { - ide_create_drive(&s->bus, 0, hd0); + ide_bus_create_drive(&s->bus, 0, hd0); } if (hd1) { - ide_create_drive(&s->bus, 1, hd1); + ide_bus_create_drive(&s->bus, 1, hd1); } return isadev; } diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 24fb7a3f9d..7efbbc720a 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -501,7 +501,7 @@ void macio_ide_init_drives(MACIOIDEState *s, DriveInfo **hd_table) for (i = 0; i < 2; i++) { if (hd_table[i]) { - ide_create_drive(&s->bus, i, hd_table[i]); + ide_bus_create_drive(&s->bus, i, hd_table[i]); } } } diff --git a/hw/ide/microdrive.c b/hw/ide/microdrive.c index b9822b939b..08504b499f 100644 --- a/hw/ide/microdrive.c +++ b/hw/ide/microdrive.c @@ -566,7 +566,7 @@ PCMCIACardState *dscm1xxxx_init(DriveInfo *dinfo) qdev_realize(DEVICE(md), NULL, &error_fatal); if (dinfo != NULL) { - ide_create_drive(&md->bus, 0, dinfo); + ide_bus_create_drive(&md->bus, 0, dinfo); } md->bus.ifs[0].drive_kind = IDE_CFATA; md->bus.ifs[0].mdata_size = METADATA_SIZE; diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index 6bf9048b1e..4e2c1a4413 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -174,10 +174,10 @@ void mmio_ide_init_drives(DeviceState *dev, DriveInfo *hd0, DriveInfo *hd1) MMIOIDEState *s = MMIO_IDE(dev); if (hd0 != NULL) { - ide_create_drive(&s->bus, 0, hd0); + ide_bus_create_drive(&s->bus, 0, hd0); } if (hd1 != NULL) { - ide_create_drive(&s->bus, 1, hd1); + ide_bus_create_drive(&s->bus, 1, hd1); } } diff --git a/hw/ide/pci.c b/hw/ide/pci.c index ae638dee0d..4223f5e64d 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -489,7 +489,7 @@ void pci_ide_create_devs(PCIDevice *dev) ide_drive_get(hd_table, ARRAY_SIZE(hd_table)); for (i = 0; i < 4; i++) { if (hd_table[i]) { - ide_create_drive(d->bus + bus[i], unit[i], hd_table[i]); + ide_bus_create_drive(d->bus + bus[i], unit[i], hd_table[i]); } } } diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index 6f6c7462f3..1b3b4da01d 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -124,7 +124,7 @@ static void ide_qdev_realize(DeviceState *qdev, Error **errp) dc->realize(dev, errp); } -IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive) +IDEDevice *ide_bus_create_drive(IDEBus *bus, int unit, DriveInfo *drive) { DeviceState *dev; diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index 7b4b71d0b0..ccfe064643 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -645,7 +645,7 @@ void ide_atapi_cmd_reply_end(IDEState *s); /* hw/ide/qdev.c */ void ide_bus_init(IDEBus *idebus, size_t idebus_size, DeviceState *dev, int bus_id, int max_units); -IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive); +IDEDevice *ide_bus_create_drive(IDEBus *bus, int unit, DriveInfo *drive); int ide_handle_rw_error(IDEState *s, int error, int op); From e29b124610f9b92c315e4655f52dec36d23de247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 14 Feb 2023 16:33:38 +0100 Subject: [PATCH 108/185] hw/ide: Rename ide_register_restart_cb -> ide_bus_register_restart_cb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ide_register_restart_cb() operates on a IDEBus; rename it as ide_bus_register_restart_cb() to emphasize its first argument is a IDEBus. Mechanical change using: $ sed -i -e 's/ide_register_restart_cb/ide_bus_register_restart_cb/g' \ $(git grep -l ide_register_restart_cb) Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230215112712.23110-13-philmd@linaro.org> Reviewed-by: Richard Henderson --- hw/ide/ahci.c | 2 +- hw/ide/cmd646.c | 2 +- hw/ide/core.c | 2 +- hw/ide/isa.c | 2 +- hw/ide/piix.c | 2 +- hw/ide/sii3112.c | 2 +- hw/ide/via.c | 2 +- include/hw/ide/internal.h | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index f338a55c4e..316dbb97d3 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1560,7 +1560,7 @@ void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports) ad->port_no = i; ad->port.dma = &ad->dma; ad->port.dma->ops = &ahci_dma_ops; - ide_register_restart_cb(&ad->port); + ide_bus_register_restart_cb(&ad->port); } g_free(irqs); } diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index 94c576262c..2865bc25fc 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -298,7 +298,7 @@ static void pci_cmd646_ide_realize(PCIDevice *dev, Error **errp) bmdma_init(&d->bus[i], &d->bmdma[i], d); d->bmdma[i].bus = &d->bus[i]; - ide_register_restart_cb(&d->bus[i]); + ide_bus_register_restart_cb(&d->bus[i]); } } diff --git a/hw/ide/core.c b/hw/ide/core.c index 117e26cef1..5897411b95 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2759,7 +2759,7 @@ static void ide_restart_cb(void *opaque, bool running, RunState state) } } -void ide_register_restart_cb(IDEBus *bus) +void ide_bus_register_restart_cb(IDEBus *bus) { if (bus->dma->ops->restart_dma) { bus->vmstate = qemu_add_vm_change_state_handler(ide_restart_cb, bus); diff --git a/hw/ide/isa.c b/hw/ide/isa.c index 74f7b43137..f8ed26b587 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -74,7 +74,7 @@ static void isa_ide_realizefn(DeviceState *dev, Error **errp) ide_init_ioport(&s->bus, isadev, s->iobase, s->iobase2); ide_init2(&s->bus, isa_get_irq(isadev, s->irqnum)); vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_isa, s); - ide_register_restart_cb(&s->bus); + ide_bus_register_restart_cb(&s->bus); } ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int irqnum, diff --git a/hw/ide/piix.c b/hw/ide/piix.c index 267dbf37db..daeb9b605d 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -149,7 +149,7 @@ static int pci_piix_init_ports(PCIIDEState *d) bmdma_init(&d->bus[i], &d->bmdma[i], d); d->bmdma[i].bus = &d->bus[i]; - ide_register_restart_cb(&d->bus[i]); + ide_bus_register_restart_cb(&d->bus[i]); } return 0; diff --git a/hw/ide/sii3112.c b/hw/ide/sii3112.c index 46204f10d7..c918370220 100644 --- a/hw/ide/sii3112.c +++ b/hw/ide/sii3112.c @@ -288,7 +288,7 @@ static void sii3112_pci_realize(PCIDevice *dev, Error **errp) bmdma_init(&s->bus[i], &s->bmdma[i], s); s->bmdma[i].bus = &s->bus[i]; - ide_register_restart_cb(&s->bus[i]); + ide_bus_register_restart_cb(&s->bus[i]); } } diff --git a/hw/ide/via.c b/hw/ide/via.c index e1a429405d..fd398226d4 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -195,7 +195,7 @@ static void via_ide_realize(PCIDevice *dev, Error **errp) bmdma_init(&d->bus[i], &d->bmdma[i], d); d->bmdma[i].bus = &d->bus[i]; - ide_register_restart_cb(&d->bus[i]); + ide_bus_register_restart_cb(&d->bus[i]); } } diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index ccfe064643..cc95cd47a0 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -620,7 +620,7 @@ void ide_init2(IDEBus *bus, qemu_irq irq); void ide_exit(IDEState *s); int ide_init_ioport(IDEBus *bus, ISADevice *isa, int iobase, int iobase2); void ide_bus_set_irq(IDEBus *bus); -void ide_register_restart_cb(IDEBus *bus); +void ide_bus_register_restart_cb(IDEBus *bus); void ide_exec_cmd(IDEBus *bus, uint32_t val); From 783f4474412ca2ccb4345f87cfa497404a1e6be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 11:26:20 +0100 Subject: [PATCH 109/185] hw/ide: Rename ide_exec_cmd() -> ide_bus_exec_cmd() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ide_exec_cmd() operates on a IDEBus; rename it as ide_bus_exec_cmd() to emphasize its first argument is a IDEBus. Mechanical change using: $ sed -i -e 's/ide_exec_cmd/ide_bus_exec_cmd/g' \ $(git grep -wl ide_exec_cmd) Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230215112712.23110-14-philmd@linaro.org> Reviewed-by: Richard Henderson --- hw/ide/ahci.c | 2 +- hw/ide/core.c | 6 +++--- hw/ide/trace-events | 2 +- include/hw/ide/internal.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 316dbb97d3..e6498964af 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1269,7 +1269,7 @@ static void handle_reg_h2d_fis(AHCIState *s, int port, cmd->status = 0; /* We're ready to process the command in FIS byte 2. */ - ide_exec_cmd(&s->dev[port].port, cmd_fis[2]); + ide_bus_exec_cmd(&s->dev[port].port, cmd_fis[2]); } static int handle_cmd(AHCIState *s, int port, uint8_t slot) diff --git a/hw/ide/core.c b/hw/ide/core.c index 5897411b95..1be0731d1a 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -1327,7 +1327,7 @@ void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val) case ATA_IOPORT_WR_COMMAND: ide_clear_hob(bus); qemu_irq_lower(bus->irq); - ide_exec_cmd(bus, val); + ide_bus_exec_cmd(bus, val); break; } } @@ -2123,13 +2123,13 @@ static bool ide_cmd_permitted(IDEState *s, uint32_t cmd) && (ide_cmd_table[cmd].flags & (1u << s->drive_kind)); } -void ide_exec_cmd(IDEBus *bus, uint32_t val) +void ide_bus_exec_cmd(IDEBus *bus, uint32_t val) { IDEState *s; bool complete; s = idebus_active_if(bus); - trace_ide_exec_cmd(bus, s, val); + trace_ide_bus_exec_cmd(bus, s, val); /* ignore commands to non existent slave */ if (s != bus->ifs && !s->blk) { diff --git a/hw/ide/trace-events b/hw/ide/trace-events index 5ef344ae73..57042cafdd 100644 --- a/hw/ide/trace-events +++ b/hw/ide/trace-events @@ -12,7 +12,7 @@ ide_data_writew(uint32_t addr, uint32_t val, void *bus, void *s) ide_data_readl(uint32_t addr, uint32_t val, void *bus, void *s) "IDE PIO rd @ 0x%"PRIx32" (Data: Long); val 0x%08"PRIx32"; bus %p; IDEState %p" ide_data_writel(uint32_t addr, uint32_t val, void *bus, void *s) "IDE PIO wr @ 0x%"PRIx32" (Data: Long); val 0x%08"PRIx32"; bus %p; IDEState %p" # misc -ide_exec_cmd(void *bus, void *state, uint32_t cmd) "IDE exec cmd: bus %p; state %p; cmd 0x%02x" +ide_bus_exec_cmd(void *bus, void *state, uint32_t cmd) "IDE exec cmd: bus %p; state %p; cmd 0x%02x" ide_cancel_dma_sync_buffered(void *fn, void *req) "invoking cb %p of buffered request %p with -ECANCELED" ide_cancel_dma_sync_remaining(void) "draining all remaining requests" ide_sector_read(int64_t sector_num, int nsectors) "sector=%"PRId64" nsectors=%d" diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index cc95cd47a0..11a4931ef9 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -622,7 +622,7 @@ int ide_init_ioport(IDEBus *bus, ISADevice *isa, int iobase, int iobase2); void ide_bus_set_irq(IDEBus *bus); void ide_bus_register_restart_cb(IDEBus *bus); -void ide_exec_cmd(IDEBus *bus, uint32_t val); +void ide_bus_exec_cmd(IDEBus *bus, uint32_t val); void ide_transfer_start(IDEState *s, uint8_t *buf, int size, EndTransferFunc *end_transfer_func); From c9519630435fe5c6ec5dacdca1b0fa0000c3a608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 11:27:23 +0100 Subject: [PATCH 110/185] hw/ide: Rename ide_init2() -> ide_bus_init_output_irq() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ide_init2() initializes a IDEBus, and set its output IRQ. To emphasize this, rename it as ide_bus_init_output_irq(). Mechanical change using: $ sed -i -e 's/ide_init2/ide_bus_init_output_irq/g' \ $(git grep -l ide_init2) Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230215112712.23110-15-philmd@linaro.org> Reviewed-by: Richard Henderson --- hw/ide/ahci.c | 2 +- hw/ide/cmd646.c | 2 +- hw/ide/core.c | 4 ++-- hw/ide/isa.c | 2 +- hw/ide/macio.c | 2 +- hw/ide/microdrive.c | 2 +- hw/ide/mmio.c | 2 +- hw/ide/piix.c | 3 ++- hw/ide/sii3112.c | 2 +- hw/ide/via.c | 2 +- include/hw/ide/internal.h | 4 ++-- 11 files changed, 14 insertions(+), 13 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index e6498964af..55902e1df7 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1554,7 +1554,7 @@ void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports) AHCIDevice *ad = &s->dev[i]; ide_bus_init(&ad->port, sizeof(ad->port), qdev, i, 1); - ide_init2(&ad->port, irqs[i]); + ide_bus_init_output_irq(&ad->port, irqs[i]); ad->hba = s; ad->port_no = i; diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index 2865bc25fc..26a90ed45f 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -294,7 +294,7 @@ static void pci_cmd646_ide_realize(PCIDevice *dev, Error **errp) qdev_init_gpio_in(ds, cmd646_set_irq, 2); for (i = 0; i < 2; i++) { ide_bus_init(&d->bus[i], sizeof(d->bus[i]), ds, i, 2); - ide_init2(&d->bus[i], qdev_get_gpio_in(ds, i)); + ide_bus_init_output_irq(&d->bus[i], qdev_get_gpio_in(ds, i)); bmdma_init(&d->bus[i], &d->bmdma[i], d); d->bmdma[i].bus = &d->bus[i]; diff --git a/hw/ide/core.c b/hw/ide/core.c index 1be0731d1a..fd2215c506 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2771,7 +2771,7 @@ static IDEDMA ide_dma_nop = { .aiocb = NULL, }; -void ide_init2(IDEBus *bus, qemu_irq irq) +void ide_bus_init_output_irq(IDEBus *bus, qemu_irq irq_out) { int i; @@ -2779,7 +2779,7 @@ void ide_init2(IDEBus *bus, qemu_irq irq) ide_init1(bus, i); ide_reset(&bus->ifs[i]); } - bus->irq = irq; + bus->irq = irq_out; bus->dma = &ide_dma_nop; } diff --git a/hw/ide/isa.c b/hw/ide/isa.c index f8ed26b587..95053e026f 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -72,7 +72,7 @@ static void isa_ide_realizefn(DeviceState *dev, Error **errp) ide_bus_init(&s->bus, sizeof(s->bus), dev, 0, 2); ide_init_ioport(&s->bus, isadev, s->iobase, s->iobase2); - ide_init2(&s->bus, isa_get_irq(isadev, s->irqnum)); + ide_bus_init_output_irq(&s->bus, isa_get_irq(isadev, s->irqnum)); vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_isa, s); ide_bus_register_restart_cb(&s->bus); } diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 7efbbc720a..6be29e44bc 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -420,7 +420,7 @@ static void macio_ide_realizefn(DeviceState *dev, Error **errp) { MACIOIDEState *s = MACIO_IDE(dev); - ide_init2(&s->bus, s->ide_irq); + ide_bus_init_output_irq(&s->bus, s->ide_irq); /* Register DMA callbacks */ s->dma.ops = &dbdma_ops; diff --git a/hw/ide/microdrive.c b/hw/ide/microdrive.c index 08504b499f..84452ae4ef 100644 --- a/hw/ide/microdrive.c +++ b/hw/ide/microdrive.c @@ -599,7 +599,7 @@ static void microdrive_realize(DeviceState *dev, Error **errp) { MicroDriveState *md = MICRODRIVE(dev); - ide_init2(&md->bus, qemu_allocate_irq(md_set_irq, md, 0)); + ide_bus_init_output_irq(&md->bus, qemu_allocate_irq(md_set_irq, md, 0)); } static void microdrive_init(Object *obj) diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index 4e2c1a4413..3aeacab3bb 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -122,7 +122,7 @@ static void mmio_ide_realizefn(DeviceState *dev, Error **errp) SysBusDevice *d = SYS_BUS_DEVICE(dev); MMIOIDEState *s = MMIO_IDE(dev); - ide_init2(&s->bus, s->irq); + ide_bus_init_output_irq(&s->bus, s->irq); memory_region_init_io(&s->iomem1, OBJECT(s), &mmio_ide_ops, s, "ide-mmio.1", 16 << s->shift); diff --git a/hw/ide/piix.c b/hw/ide/piix.c index daeb9b605d..2f71376b93 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -145,7 +145,8 @@ static int pci_piix_init_ports(PCIIDEState *d) if (ret) { return ret; } - ide_init2(&d->bus[i], isa_get_irq(NULL, port_info[i].isairq)); + ide_bus_init_output_irq(&d->bus[i], + isa_get_irq(NULL, port_info[i].isairq)); bmdma_init(&d->bus[i], &d->bmdma[i], d); d->bmdma[i].bus = &d->bus[i]; diff --git a/hw/ide/sii3112.c b/hw/ide/sii3112.c index c918370220..f9becdff8e 100644 --- a/hw/ide/sii3112.c +++ b/hw/ide/sii3112.c @@ -284,7 +284,7 @@ static void sii3112_pci_realize(PCIDevice *dev, Error **errp) qdev_init_gpio_in(ds, sii3112_set_irq, 2); for (i = 0; i < 2; i++) { ide_bus_init(&s->bus[i], sizeof(s->bus[i]), ds, i, 1); - ide_init2(&s->bus[i], qdev_get_gpio_in(ds, i)); + ide_bus_init_output_irq(&s->bus[i], qdev_get_gpio_in(ds, i)); bmdma_init(&s->bus[i], &s->bmdma[i], s); s->bmdma[i].bus = &s->bus[i]; diff --git a/hw/ide/via.c b/hw/ide/via.c index fd398226d4..ab9e43e244 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -191,7 +191,7 @@ static void via_ide_realize(PCIDevice *dev, Error **errp) qdev_init_gpio_in(ds, via_ide_set_irq, 2); for (i = 0; i < 2; i++) { ide_bus_init(&d->bus[i], sizeof(d->bus[i]), ds, i, 2); - ide_init2(&d->bus[i], qdev_get_gpio_in(ds, i)); + ide_bus_init_output_irq(&d->bus[i], qdev_get_gpio_in(ds, i)); bmdma_init(&d->bus[i], &d->bmdma[i], d); d->bmdma[i].bus = &d->bus[i]; diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index 11a4931ef9..c687282a78 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -488,7 +488,7 @@ struct IDEBus { IDEDMA *dma; uint8_t unit; uint8_t cmd; - qemu_irq irq; + qemu_irq irq; /* bus output */ int error_status; uint8_t retry_unit; @@ -616,8 +616,8 @@ int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind, uint64_t wwn, uint32_t cylinders, uint32_t heads, uint32_t secs, int chs_trans, Error **errp); -void ide_init2(IDEBus *bus, qemu_irq irq); void ide_exit(IDEState *s); +void ide_bus_init_output_irq(IDEBus *bus, qemu_irq irq_out); int ide_init_ioport(IDEBus *bus, ISADevice *isa, int iobase, int iobase2); void ide_bus_set_irq(IDEBus *bus); void ide_bus_register_restart_cb(IDEBus *bus); From 2c50207f0d74132f033d967db6436c89e63ade02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 11:33:08 +0100 Subject: [PATCH 111/185] hw/ide: Rename idebus_active_if() -> ide_bus_active_if() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit idebus_active_if() operates on a IDEBus; rename it as ide_bus_active_if() to emphasize its first argument is a IDEBus. Mechanical change using: $ sed -i -e 's/idebus_active_if/ide_bus_active_if/g' \ $(git grep -l idebus_active_if) Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230215112712.23110-16-philmd@linaro.org> Reviewed-by: Richard Henderson --- hw/ide/core.c | 18 +++++++++--------- hw/ide/macio.c | 8 ++++---- hw/ide/microdrive.c | 4 ++-- hw/ide/pci.c | 2 +- include/hw/ide/internal.h | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index fd2215c506..2d034731cf 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -1265,7 +1265,7 @@ const char *ATA_IOPORT_WR_lookup[ATA_IOPORT_WR_NUM_REGISTERS] = { void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val) { IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); + IDEState *s = ide_bus_active_if(bus); int reg_num = addr & 7; trace_ide_ioport_write(addr, ATA_IOPORT_WR_lookup[reg_num], val, bus, s); @@ -2128,7 +2128,7 @@ void ide_bus_exec_cmd(IDEBus *bus, uint32_t val) IDEState *s; bool complete; - s = idebus_active_if(bus); + s = ide_bus_active_if(bus); trace_ide_bus_exec_cmd(bus, s, val); /* ignore commands to non existent slave */ @@ -2195,7 +2195,7 @@ const char *ATA_IOPORT_RR_lookup[ATA_IOPORT_RR_NUM_REGISTERS] = { uint32_t ide_ioport_read(void *opaque, uint32_t addr) { IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); + IDEState *s = ide_bus_active_if(bus); uint32_t reg_num; int ret, hob; @@ -2281,7 +2281,7 @@ uint32_t ide_ioport_read(void *opaque, uint32_t addr) uint32_t ide_status_read(void *opaque, uint32_t addr) { IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); + IDEState *s = ide_bus_active_if(bus); int ret; if ((!bus->ifs[0].blk && !bus->ifs[1].blk) || @@ -2370,7 +2370,7 @@ static bool ide_is_pio_out(IDEState *s) void ide_data_writew(void *opaque, uint32_t addr, uint32_t val) { IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); + IDEState *s = ide_bus_active_if(bus); uint8_t *p; trace_ide_data_writew(addr, val, bus, s); @@ -2406,7 +2406,7 @@ void ide_data_writew(void *opaque, uint32_t addr, uint32_t val) uint32_t ide_data_readw(void *opaque, uint32_t addr) { IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); + IDEState *s = ide_bus_active_if(bus); uint8_t *p; int ret; @@ -2444,7 +2444,7 @@ uint32_t ide_data_readw(void *opaque, uint32_t addr) void ide_data_writel(void *opaque, uint32_t addr, uint32_t val) { IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); + IDEState *s = ide_bus_active_if(bus); uint8_t *p; trace_ide_data_writel(addr, val, bus, s); @@ -2472,7 +2472,7 @@ void ide_data_writel(void *opaque, uint32_t addr, uint32_t val) uint32_t ide_data_readl(void *opaque, uint32_t addr) { IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); + IDEState *s = ide_bus_active_if(bus); uint8_t *p; int ret; @@ -2711,7 +2711,7 @@ static void ide_restart_bh(void *opaque) return; } - s = idebus_active_if(bus); + s = ide_bus_active_if(bus); is_read = (bus->error_status & IDE_RETRY_READ) != 0; /* The error status must be cleared before resubmitting the request: The diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 6be29e44bc..dca1cc9efc 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -60,7 +60,7 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) { DBDMA_io *io = opaque; MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); + IDEState *s = ide_bus_active_if(&m->bus); int64_t offset; MACIO_DPRINTF("pmac_ide_atapi_transfer_cb\n"); @@ -136,7 +136,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) { DBDMA_io *io = opaque; MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); + IDEState *s = ide_bus_active_if(&m->bus); int64_t offset; MACIO_DPRINTF("pmac_ide_transfer_cb\n"); @@ -220,7 +220,7 @@ done: static void pmac_ide_transfer(DBDMA_io *io) { MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); + IDEState *s = ide_bus_active_if(&m->bus); MACIO_DPRINTF("\n"); @@ -251,7 +251,7 @@ static void pmac_ide_transfer(DBDMA_io *io) static void pmac_ide_flush(DBDMA_io *io) { MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); + IDEState *s = ide_bus_active_if(&m->bus); if (s->bus->dma->aiocb) { blk_drain(s->blk); diff --git a/hw/ide/microdrive.c b/hw/ide/microdrive.c index 84452ae4ef..f1017f7333 100644 --- a/hw/ide/microdrive.c +++ b/hw/ide/microdrive.c @@ -250,14 +250,14 @@ static uint16_t md_common_read(PCMCIACardState *card, uint32_t at) case 0xd: /* Error */ return ide_ioport_read(&s->bus, 0x1); case 0xe: /* Alternate Status */ - ifs = idebus_active_if(&s->bus); + ifs = ide_bus_active_if(&s->bus); if (ifs->blk) { return ifs->status; } else { return 0; } case 0xf: /* Device Address */ - ifs = idebus_active_if(&s->bus); + ifs = ide_bus_active_if(&s->bus); return 0xc2 | ((~ifs->select << 2) & 0x3c); default: return ide_ioport_read(&s->bus, at); diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 4223f5e64d..2ddcb49b27 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -296,7 +296,7 @@ void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val) /* Ignore writes to SSBM if it keeps the old value */ if ((val & BM_CMD_START) != (bm->cmd & BM_CMD_START)) { if (!(val & BM_CMD_START)) { - ide_cancel_dma_sync(idebus_active_if(bm->bus)); + ide_cancel_dma_sync(ide_bus_active_if(bm->bus)); bm->status &= ~BM_STATUS_DMAING; } else { bm->cur_addr = bm->addr; diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index c687282a78..c2b794150f 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -566,7 +566,7 @@ static inline uint8_t ide_dma_cmd_to_retry(uint8_t dma_cmd) return 0; } -static inline IDEState *idebus_active_if(IDEBus *bus) +static inline IDEState *ide_bus_active_if(IDEBus *bus) { return bus->ifs + bus->unit; } From 066282672ba3d26aa7603e43559c658bf19b1043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 23:33:35 +0100 Subject: [PATCH 112/185] hw/ide: Declare ide_get_[geometry/bios_chs_trans] in 'hw/ide/internal.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ide_get_geometry() and ide_get_bios_chs_trans() are only used by the TYPE_PC_MACHINE. "hw/ide.h" is a mixed bag of lost IDE declarations. In order to remove this (almost) pointless header soon, move these declarations to "hw/ide/internal.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Message-Id: <20230220091358.17038-18-philmd@linaro.org> --- hw/i386/pc.c | 3 ++- include/hw/ide.h | 4 ---- include/hw/ide/internal.h | 4 ++++ 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 98c2becc11..992951c107 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -33,7 +33,8 @@ #include "hw/i386/vmport.h" #include "sysemu/cpus.h" #include "hw/block/fdc.h" -#include "hw/ide.h" +#include "hw/ide/internal.h" +#include "hw/ide/isa.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bus.h" #include "hw/pci-bridge/pci_expander_bridge.h" diff --git a/include/hw/ide.h b/include/hw/ide.h index 24a7aa2925..db963bdb77 100644 --- a/include/hw/ide.h +++ b/include/hw/ide.h @@ -3,10 +3,6 @@ #include "exec/memory.h" -int ide_get_geometry(BusState *bus, int unit, - int16_t *cyls, int8_t *heads, int8_t *secs); -int ide_get_bios_chs_trans(BusState *bus, int unit); - /* ide/core.c */ void ide_drive_get(DriveInfo **hd, int max_bus); diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index c2b794150f..d9f1f77dd5 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -647,6 +647,10 @@ void ide_bus_init(IDEBus *idebus, size_t idebus_size, DeviceState *dev, int bus_id, int max_units); IDEDevice *ide_bus_create_drive(IDEBus *bus, int unit, DriveInfo *drive); +int ide_get_geometry(BusState *bus, int unit, + int16_t *cyls, int8_t *heads, int8_t *secs); +int ide_get_bios_chs_trans(BusState *bus, int unit); + int ide_handle_rw_error(IDEState *s, int error, int op); #endif /* HW_IDE_INTERNAL_H */ From 2435503ab7cc5f02a8b7c74f9b15ba26878e3e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 10 Feb 2023 22:58:05 +0100 Subject: [PATCH 113/185] hw/ide/ioport: Remove unnecessary includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230215112712.23110-17-philmd@linaro.org> Reviewed-by: Richard Henderson --- hw/ide/ioport.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/hw/ide/ioport.c b/hw/ide/ioport.c index e6caa537fa..e2ecc6230c 100644 --- a/hw/ide/ioport.c +++ b/hw/ide/ioport.c @@ -25,16 +25,6 @@ #include "qemu/osdep.h" #include "hw/isa/isa.h" -#include "qemu/error-report.h" -#include "qemu/timer.h" -#include "sysemu/blockdev.h" -#include "sysemu/dma.h" -#include "hw/block/block.h" -#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" From 3e5f247e36631bef9cc2c35381a01f7b6e6449be Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 22 Aug 2022 19:02:12 +0200 Subject: [PATCH 114/185] hw/ide/pci: Unexport bmdma_active_if() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function is only used inside ide/pci.c, so doesn't need to be exported. Signed-off-by: Bernhard Beschow Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230215112712.23110-18-philmd@linaro.org> --- hw/ide/pci.c | 6 ++++++ include/hw/ide/pci.h | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 2ddcb49b27..fc9224bbc9 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -104,6 +104,12 @@ const MemoryRegionOps pci_ide_data_le_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static IDEState *bmdma_active_if(BMDMAState *bmdma) +{ + assert(bmdma->bus->retry_unit != (uint8_t)-1); + return bmdma->bus->ifs + bmdma->bus->retry_unit; +} + static void bmdma_start_dma(const IDEDMA *dma, IDEState *s, BlockCompletionFunc *dma_cb) { diff --git a/include/hw/ide/pci.h b/include/hw/ide/pci.h index 2a6284acac..7b5e3f6e1c 100644 --- a/include/hw/ide/pci.h +++ b/include/hw/ide/pci.h @@ -55,12 +55,6 @@ struct PCIIDEState { MemoryRegion data_bar[2]; }; -static inline IDEState *bmdma_active_if(BMDMAState *bmdma) -{ - assert(bmdma->bus->retry_unit != (uint8_t)-1); - return bmdma->bus->ifs + bmdma->bus->retry_unit; -} - void bmdma_init(IDEBus *bus, BMDMAState *bm, PCIIDEState *d); void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val); extern MemoryRegionOps bmdma_addr_ioport_ops; From caa91462812f798982c57e8c0d53e50ef10379fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Feb 2023 11:57:15 +0100 Subject: [PATCH 115/185] hw/ide/piix: Remove unused includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230215112712.23110-19-philmd@linaro.org> Reviewed-by: Richard Henderson --- hw/ide/piix.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/hw/ide/piix.c b/hw/ide/piix.c index 2f71376b93..6354ae740b 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -28,14 +28,9 @@ */ #include "qemu/osdep.h" -#include "hw/pci/pci.h" #include "migration/vmstate.h" #include "qapi/error.h" -#include "qemu/module.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "sysemu/dma.h" - +#include "hw/pci/pci.h" #include "hw/ide/piix.h" #include "hw/ide/pci.h" #include "trace.h" From 511aa9f9e79d694ad7a396acf1d38aa2212c01e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 14 Feb 2023 16:47:39 +0100 Subject: [PATCH 116/185] hw/ide/piix: Pass Error* to pci_piix_init_ports() for better error msg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230215112712.23110-20-philmd@linaro.org> Reviewed-by: Richard Henderson --- hw/ide/piix.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/hw/ide/piix.c b/hw/ide/piix.c index 6354ae740b..f10bdf39ff 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -121,7 +121,7 @@ static void piix_ide_reset(DeviceState *dev) pci_set_byte(pci_conf + 0x20, 0x01); /* BMIBA: 20-23h */ } -static int pci_piix_init_ports(PCIIDEState *d) +static bool pci_piix_init_ports(PCIIDEState *d, Error **errp) { static const struct { int iobase; @@ -138,7 +138,9 @@ static int pci_piix_init_ports(PCIIDEState *d) ret = ide_init_ioport(&d->bus[i], NULL, port_info[i].iobase, port_info[i].iobase2); if (ret) { - return ret; + error_setg_errno(errp, -ret, "Failed to realize %s port %u", + object_get_typename(OBJECT(d)), i); + return false; } ide_bus_init_output_irq(&d->bus[i], isa_get_irq(NULL, port_info[i].isairq)); @@ -148,14 +150,13 @@ static int pci_piix_init_ports(PCIIDEState *d) ide_bus_register_restart_cb(&d->bus[i]); } - return 0; + return true; } static void pci_piix_ide_realize(PCIDevice *dev, Error **errp) { PCIIDEState *d = PCI_IDE(dev); uint8_t *pci_conf = dev->config; - int rc; pci_conf[PCI_CLASS_PROG] = 0x80; // legacy ATA mode @@ -164,10 +165,8 @@ static void pci_piix_ide_realize(PCIDevice *dev, Error **errp) vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_pci, d); - rc = pci_piix_init_ports(d); - if (rc) { - error_setg_errno(errp, -rc, "Failed to realize %s", - object_get_typename(OBJECT(dev))); + if (!pci_piix_init_ports(d, errp)) { + return; } } From 533580d738849947a699a825f9d7eb85e42e8be1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 14 Feb 2023 16:28:19 +0100 Subject: [PATCH 117/185] hw/ide/piix: Refactor pci_piix_init_ports as pci_piix_init_bus per bus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20230215112712.23110-21-philmd@linaro.org> Reviewed-by: Richard Henderson --- hw/ide/piix.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/hw/ide/piix.c b/hw/ide/piix.c index f10bdf39ff..41d60921e3 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -121,7 +121,7 @@ static void piix_ide_reset(DeviceState *dev) pci_set_byte(pci_conf + 0x20, 0x01); /* BMIBA: 20-23h */ } -static bool pci_piix_init_ports(PCIIDEState *d, Error **errp) +static bool pci_piix_init_bus(PCIIDEState *d, unsigned i, Error **errp) { static const struct { int iobase; @@ -131,24 +131,21 @@ static bool pci_piix_init_ports(PCIIDEState *d, Error **errp) {0x1f0, 0x3f6, 14}, {0x170, 0x376, 15}, }; - int i, ret; + int ret; - for (i = 0; i < 2; i++) { - ide_bus_init(&d->bus[i], sizeof(d->bus[i]), DEVICE(d), i, 2); - ret = ide_init_ioport(&d->bus[i], NULL, port_info[i].iobase, - port_info[i].iobase2); - if (ret) { - error_setg_errno(errp, -ret, "Failed to realize %s port %u", - object_get_typename(OBJECT(d)), i); - return false; - } - ide_bus_init_output_irq(&d->bus[i], - isa_get_irq(NULL, port_info[i].isairq)); - - bmdma_init(&d->bus[i], &d->bmdma[i], d); - d->bmdma[i].bus = &d->bus[i]; - ide_bus_register_restart_cb(&d->bus[i]); + ide_bus_init(&d->bus[i], sizeof(d->bus[i]), DEVICE(d), i, 2); + ret = ide_init_ioport(&d->bus[i], NULL, port_info[i].iobase, + port_info[i].iobase2); + if (ret) { + error_setg_errno(errp, -ret, "Failed to realize %s port %u", + object_get_typename(OBJECT(d)), i); + return false; } + ide_bus_init_output_irq(&d->bus[i], isa_get_irq(NULL, port_info[i].isairq)); + + bmdma_init(&d->bus[i], &d->bmdma[i], d); + d->bmdma[i].bus = &d->bus[i]; + ide_bus_register_restart_cb(&d->bus[i]); return true; } @@ -165,8 +162,10 @@ static void pci_piix_ide_realize(PCIDevice *dev, Error **errp) vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_pci, d); - if (!pci_piix_init_ports(d, errp)) { - return; + for (unsigned i = 0; i < 2; i++) { + if (!pci_piix_init_bus(d, i, errp)) { + return; + } } } From d39d792e2da28ca8e7fa6d6475bddc8667fbf611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 24 Mar 2021 18:47:59 +0100 Subject: [PATCH 118/185] hw/ide/via: Replace magic 2 value by ARRAY_SIZE / MAX_IDE_DEVS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Acked-by: John Snow Message-Id: <20210511041848.2743312-5-f4bug@amsat.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/ide/via.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/ide/via.c b/hw/ide/via.c index ab9e43e244..177baea9a7 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -90,7 +90,7 @@ static void bmdma_setup_bar(PCIIDEState *d) int i; memory_region_init(&d->bmdma_bar, OBJECT(d), "via-bmdma-container", 16); - for(i = 0;i < 2; i++) { + for (i = 0; i < ARRAY_SIZE(d->bmdma); i++) { BMDMAState *bm = &d->bmdma[i]; memory_region_init_io(&bm->extra_io, OBJECT(d), &via_bmdma_ops, bm, @@ -122,7 +122,7 @@ static void via_ide_reset(DeviceState *dev) uint8_t *pci_conf = pd->config; int i; - for (i = 0; i < 2; i++) { + for (i = 0; i < ARRAY_SIZE(d->bus); i++) { ide_bus_reset(&d->bus[i]); } @@ -188,9 +188,9 @@ static void via_ide_realize(PCIDevice *dev, Error **errp) bmdma_setup_bar(d); pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar); - qdev_init_gpio_in(ds, via_ide_set_irq, 2); - for (i = 0; i < 2; i++) { - ide_bus_init(&d->bus[i], sizeof(d->bus[i]), ds, i, 2); + qdev_init_gpio_in(ds, via_ide_set_irq, ARRAY_SIZE(d->bus)); + for (i = 0; i < ARRAY_SIZE(d->bus); i++) { + ide_bus_init(&d->bus[i], sizeof(d->bus[i]), ds, i, MAX_IDE_DEVS); ide_bus_init_output_irq(&d->bus[i], qdev_get_gpio_in(ds, i)); bmdma_init(&d->bus[i], &d->bmdma[i], d); @@ -204,7 +204,7 @@ static void via_ide_exitfn(PCIDevice *dev) PCIIDEState *d = PCI_IDE(dev); unsigned i; - for (i = 0; i < 2; ++i) { + for (i = 0; i < ARRAY_SIZE(d->bmdma); ++i) { memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].extra_io); memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].addr_ioport); } From 7ae8e6c9a21bff7db376a8b1b135dffa0bb920d6 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Thu, 26 Jan 2023 22:17:36 +0100 Subject: [PATCH 119/185] hw/ide/pci: Add PCIIDEState::isa_irq[] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These legacy ISA IRQs allow the PIIX IDE functions to be wired up in their south bridges and the VIA IDE functions to disuse PCI_INTERRUPT_LINE as outlined in https://lists.nongnu.org/archive/html/qemu-devel/2020-03/msg01707.html Suggested-by: Mark Cave-Ayland Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230126211740.66874-7-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/ide/pci.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/hw/ide/pci.h b/include/hw/ide/pci.h index 7b5e3f6e1c..74c127e32f 100644 --- a/include/hw/ide/pci.h +++ b/include/hw/ide/pci.h @@ -49,6 +49,7 @@ struct PCIIDEState { IDEBus bus[2]; BMDMAState bmdma[2]; + qemu_irq isa_irq[2]; uint32_t secondary; /* used only for cmd646 */ MemoryRegion bmdma_bar; MemoryRegion cmd_bar[2]; From bb509d944129e9987d10d97a1dd4a3e099cd505d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 16 Dec 2022 08:28:32 +0100 Subject: [PATCH 120/185] dump: Replace tswapN() -> cpu_to_dumpN() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All uses of tswap in that file are wrong, and should be using cpu_to_dumpN, which correctly tests the endianness of the output. Reported-by: Richard Henderson Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Reviewed-by: Marc-André Lureau Message-Id: <20230225094903.53167-2-philmd@linaro.org> --- dump/dump.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dump/dump.c b/dump/dump.c index 1362810991..0ab229e5e9 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -907,13 +907,13 @@ static void get_note_sizes(DumpState *s, const void *note, if (dump_is_64bit(s)) { const Elf64_Nhdr *hdr = note; note_head_sz = sizeof(Elf64_Nhdr); - name_sz = tswap64(hdr->n_namesz); - desc_sz = tswap64(hdr->n_descsz); + name_sz = cpu_to_dump64(s, hdr->n_namesz); + desc_sz = cpu_to_dump64(s, hdr->n_descsz); } else { const Elf32_Nhdr *hdr = note; note_head_sz = sizeof(Elf32_Nhdr); - name_sz = tswap32(hdr->n_namesz); - desc_sz = tswap32(hdr->n_descsz); + name_sz = cpu_to_dump32(s, hdr->n_namesz); + desc_sz = cpu_to_dump32(s, hdr->n_descsz); } if (note_head_size) { From c5d40b22e91cfddc7ba9e46c406bec3503cb3c71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Feb 2023 23:38:59 +0100 Subject: [PATCH 121/185] dump: Replace TARGET_PAGE_SIZE -> qemu_target_page_size() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TARGET_PAGE_SIZE is target specific. In preparation of making dump.c target-agnostic, replace the compile-time TARGET_PAGE_SIZE definition by runtime qemu_target_page_size(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Reviewed-by: Marc-André Lureau Message-Id: <20230225094903.53167-3-philmd@linaro.org> --- dump/dump.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dump/dump.c b/dump/dump.c index 0ab229e5e9..4d68a74ffa 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -15,6 +15,7 @@ #include "qemu/cutils.h" #include "elf.h" #include "exec/hwaddr.h" +#include "exec/target_page.h" #include "monitor/monitor.h" #include "sysemu/kvm.h" #include "sysemu/dump.h" @@ -1860,7 +1861,7 @@ static void dump_init(DumpState *s, int fd, bool has_format, } if (!s->dump_info.page_size) { - s->dump_info.page_size = TARGET_PAGE_SIZE; + s->dump_info.page_size = qemu_target_page_size(); } s->note_size = cpu_get_note_size(s->dump_info.d_class, From ac978771e44787b4f76d09d992b342b0c10bb3c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Feb 2023 23:56:46 +0100 Subject: [PATCH 122/185] dump: Clean included headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "qemu/win_dump_defs.h" is only required by win_dump.c, but win_dump.h requires "sysemu/dump.h" which declares the DumpState type. Remove various unused headers. Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20230225094903.53167-4-philmd@linaro.org> --- dump/dump-hmp-cmds.c | 2 +- dump/dump.c | 6 ++---- dump/win_dump.c | 15 +++++---------- dump/win_dump.h | 2 +- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/dump/dump-hmp-cmds.c b/dump/dump-hmp-cmds.c index e5053b04cd..b038785fee 100644 --- a/dump/dump-hmp-cmds.c +++ b/dump/dump-hmp-cmds.c @@ -1,5 +1,5 @@ /* - * Human Monitor Interface commands + * Windows crashdump (Human Monitor Interface commands) * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. diff --git a/dump/dump.c b/dump/dump.c index 4d68a74ffa..da63129825 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -14,22 +14,20 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "elf.h" -#include "exec/hwaddr.h" +#include "qemu/bswap.h" #include "exec/target_page.h" #include "monitor/monitor.h" -#include "sysemu/kvm.h" #include "sysemu/dump.h" -#include "sysemu/memory_mapping.h" #include "sysemu/runstate.h" #include "sysemu/cpus.h" #include "qapi/error.h" #include "qapi/qapi-commands-dump.h" #include "qapi/qapi-events-dump.h" #include "qapi/qmp/qerror.h" -#include "qemu/error-report.h" #include "qemu/main-loop.h" #include "hw/misc/vmcoreinfo.h" #include "migration/blocker.h" +#include "hw/core/cpu.h" #ifdef TARGET_X86_64 #include "win_dump.h" diff --git a/dump/win_dump.c b/dump/win_dump.c index f20b6051b6..ba7fa404fe 100644 --- a/dump/win_dump.c +++ b/dump/win_dump.c @@ -1,5 +1,5 @@ /* - * Windows crashdump + * Windows crashdump (target specific implementations) * * Copyright (c) 2018 Virtuozzo International GmbH * @@ -9,19 +9,14 @@ */ #include "qemu/osdep.h" -#include "qemu/cutils.h" -#include "elf.h" -#include "exec/hwaddr.h" -#include "monitor/monitor.h" -#include "sysemu/kvm.h" #include "sysemu/dump.h" -#include "sysemu/memory_mapping.h" -#include "sysemu/cpus.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" -#include "qemu/error-report.h" -#include "hw/misc/vmcoreinfo.h" +#include "exec/cpu-defs.h" +#include "hw/core/cpu.h" +#include "qemu/win_dump_defs.h" #include "win_dump.h" +#include "cpu.h" static size_t win_dump_ptr_size(bool x64) { diff --git a/dump/win_dump.h b/dump/win_dump.h index b8c25348f4..56f63683c3 100644 --- a/dump/win_dump.h +++ b/dump/win_dump.h @@ -11,7 +11,7 @@ #ifndef WIN_DUMP_H #define WIN_DUMP_H -#include "qemu/win_dump_defs.h" +#include "sysemu/dump.h" void create_win_dump(DumpState *s, Error **errp); From efc3146a6e32399c2935b1806449ed5e5d0a1719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Feb 2023 23:58:16 +0100 Subject: [PATCH 123/185] dump: Simplify compiling win_dump.o by introducing win_dump_available() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To make dump.c less target dependent, move the TARGET_X86_64 #ifdef'ry from dump.c to win_dump.c (introducing a win_dump_available() method there). By doing so we can build win_dump.c on any target, and simplify the meson rule. Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20230225094903.53167-5-philmd@linaro.org> --- dump/dump.c | 16 +++++----------- dump/meson.build | 2 +- dump/win_dump.c | 18 ++++++++++++++++++ dump/win_dump.h | 3 +++ 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/dump/dump.c b/dump/dump.c index da63129825..fa650980d8 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -28,10 +28,7 @@ #include "hw/misc/vmcoreinfo.h" #include "migration/blocker.h" #include "hw/core/cpu.h" - -#ifdef TARGET_X86_64 #include "win_dump.h" -#endif #include #ifdef CONFIG_LZO @@ -2126,12 +2123,10 @@ void qmp_dump_guest_memory(bool paging, const char *file, } #endif -#ifndef TARGET_X86_64 - if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_WIN_DMP) { - error_setg(errp, "Windows dump is only available for x86-64"); + if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_WIN_DMP + && !win_dump_available(errp)) { return; } -#endif #if !defined(WIN32) if (strstart(file, "fd:", &p)) { @@ -2213,10 +2208,9 @@ DumpGuestMemoryCapability *qmp_query_dump_guest_memory_capability(Error **errp) QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY); #endif - /* Windows dump is available only if target is x86_64 */ -#ifdef TARGET_X86_64 - QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_WIN_DMP); -#endif + if (win_dump_available(NULL)) { + QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_WIN_DMP); + } return cap; } diff --git a/dump/meson.build b/dump/meson.build index 2eff29c3ea..f13b29a849 100644 --- a/dump/meson.build +++ b/dump/meson.build @@ -1,4 +1,4 @@ softmmu_ss.add(files('dump-hmp-cmds.c')) specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files('dump.c'), snappy, lzo]) -specific_ss.add(when: ['CONFIG_SOFTMMU', 'TARGET_X86_64'], if_true: files('win_dump.c')) +specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files('win_dump.c')) diff --git a/dump/win_dump.c b/dump/win_dump.c index ba7fa404fe..ff9c5bd339 100644 --- a/dump/win_dump.c +++ b/dump/win_dump.c @@ -18,6 +18,13 @@ #include "win_dump.h" #include "cpu.h" +#if defined(TARGET_X86_64) + +bool win_dump_available(Error **errp) +{ + return true; +} + static size_t win_dump_ptr_size(bool x64) { return x64 ? sizeof(uint64_t) : sizeof(uint32_t); @@ -470,3 +477,14 @@ out_cr3: return; } + +#else /* !TARGET_X86_64 */ + +bool win_dump_available(Error **errp) +{ + error_setg(errp, "Windows dump is only available for x86-64"); + + return false; +} + +#endif diff --git a/dump/win_dump.h b/dump/win_dump.h index 56f63683c3..c9b49f87dc 100644 --- a/dump/win_dump.h +++ b/dump/win_dump.h @@ -13,6 +13,9 @@ #include "sysemu/dump.h" +/* Check Windows dump availability for the current target */ +bool win_dump_available(Error **errp); + void create_win_dump(DumpState *s, Error **errp); #endif /* WIN_DUMP_H */ From 972448628a9fe967ac1ba0d56a60247bbd4f7501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 23 Feb 2023 23:59:19 +0100 Subject: [PATCH 124/185] dump: Add create_win_dump() stub for non-x86 targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the non-x86 create_win_dump(). We can remove the last TARGET_X86_64 #ifdef'ry in dump.c, which thus becomes target-independent. Update meson accordingly. Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20230225094903.53167-6-philmd@linaro.org> --- dump/dump.c | 2 -- dump/meson.build | 4 +--- dump/win_dump.c | 5 +++++ 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/dump/dump.c b/dump/dump.c index fa650980d8..544d5bce3a 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -2018,9 +2018,7 @@ static void dump_process(DumpState *s, Error **errp) DumpQueryResult *result = NULL; if (s->has_format && s->format == DUMP_GUEST_MEMORY_FORMAT_WIN_DMP) { -#ifdef TARGET_X86_64 create_win_dump(s, errp); -#endif } else if (s->has_format && s->format != DUMP_GUEST_MEMORY_FORMAT_ELF) { create_kdump_vmcore(s, errp); } else { diff --git a/dump/meson.build b/dump/meson.build index f13b29a849..df52ee4268 100644 --- a/dump/meson.build +++ b/dump/meson.build @@ -1,4 +1,2 @@ -softmmu_ss.add(files('dump-hmp-cmds.c')) - -specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files('dump.c'), snappy, lzo]) +softmmu_ss.add([files('dump.c', 'dump-hmp-cmds.c'), snappy, lzo]) specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files('win_dump.c')) diff --git a/dump/win_dump.c b/dump/win_dump.c index ff9c5bd339..0152f7330a 100644 --- a/dump/win_dump.c +++ b/dump/win_dump.c @@ -487,4 +487,9 @@ bool win_dump_available(Error **errp) return false; } +void create_win_dump(DumpState *s, Error **errp) +{ + win_dump_available(errp); +} + #endif From 23bdd0de97a18e34fe05126fbaf4de540e9eb7b2 Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Tue, 27 Dec 2022 17:15:31 +0100 Subject: [PATCH 125/185] ui/cocoa: user friendly characters for release mouse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While mouse is grabbed, window title contains a hint for the user what keyboard keys to press to release the mouse. Make that hint text a bit more user friendly for a Mac user: - Replace "Ctrl" and "Alt" by appropriate symbols for those keyboard keys typically displayed for them on a Mac (encode those symbols by using UTF-8 characters). - Drop " + " in between the keys, as that's not common on macOS for documenting keyboard shortcuts. - Convert lower case "g" to upper case "G", as that's common on macOS. - Add one additional space at start and end of key stroke set, to visually separate the key strokes from the rest of the text. Signed-off-by: Christian Schoenebeck Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- ui/cocoa.m | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ui/cocoa.m b/ui/cocoa.m index e915c344a8..289a2b193e 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -72,6 +72,9 @@ #define cgrect(nsrect) (*(CGRect *)&(nsrect)) +#define UC_CTRL_KEY "\xe2\x8c\x83" +#define UC_ALT_KEY "\xe2\x8c\xa5" + typedef struct { int width; int height; @@ -1135,9 +1138,9 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven if (!isFullscreen) { if (qemu_name) - [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s - (Press ctrl + alt + g to release Mouse)", qemu_name]]; + [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s - (Press " UC_CTRL_KEY " " UC_ALT_KEY " G to release Mouse)", qemu_name]]; else - [normalWindow setTitle:@"QEMU - (Press ctrl + alt + g to release Mouse)"]; + [normalWindow setTitle:@"QEMU - (Press " UC_CTRL_KEY " " UC_ALT_KEY " G to release Mouse)"]; } [self hideCursor]; CGAssociateMouseAndMouseCursorPosition(isAbsoluteEnabled); From 50c88402ca599e577f025e78a4380431be2b3f6d Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Wed, 13 Feb 2019 12:29:47 -0500 Subject: [PATCH 126/185] include: import Xen public headers to hw/xen/interface There's already a partial set here; update them and pull in a more complete set. To start with, define __XEN_TOOLS__ in hw/xen/xen.h to ensure that any internal definitions needed by Xen toolstack libraries are present regardless of the order in which the headers are included. A reckoning will come later, once we make the PV backends work in emulation and untangle the headers for Xen-native vs. generic parts. Signed-off-by: Joao Martins [dwmw2: Update to Xen public headers from 4.16.2 release, add some in io/, define __XEN_TOOLS__ in hw/xen/xen.h, move to hw/xen/interface/] Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- include/hw/xen/interface/arch-arm.h | 510 ++++++++ include/hw/xen/interface/arch-x86/cpuid.h | 118 ++ .../hw/xen/interface/arch-x86/xen-x86_32.h | 194 +++ .../hw/xen/interface/arch-x86/xen-x86_64.h | 241 ++++ include/hw/xen/interface/arch-x86/xen.h | 398 +++++++ include/hw/xen/interface/event_channel.h | 388 ++++++ include/hw/xen/interface/features.h | 143 +++ include/hw/xen/interface/grant_table.h | 650 ++++++++++ include/hw/xen/interface/hvm/hvm_op.h | 395 +++++++ include/hw/xen/interface/hvm/params.h | 318 +++++ include/hw/xen/interface/io/blkif.h | 14 +- include/hw/xen/interface/io/console.h | 10 + include/hw/xen/interface/io/fbif.h | 20 + include/hw/xen/interface/io/kbdif.h | 10 + include/hw/xen/interface/io/netif.h | 94 +- include/hw/xen/interface/io/ring.h | 99 +- include/hw/xen/interface/io/usbif.h | 353 ++++-- include/hw/xen/interface/io/xenbus.h | 10 + include/hw/xen/interface/io/xs_wire.h | 153 +++ include/hw/xen/interface/memory.h | 754 ++++++++++++ include/hw/xen/interface/physdev.h | 383 ++++++ include/hw/xen/interface/sched.h | 202 ++++ include/hw/xen/interface/trace.h | 341 ++++++ include/hw/xen/interface/vcpu.h | 248 ++++ include/hw/xen/interface/version.h | 113 ++ include/hw/xen/interface/xen-compat.h | 46 + include/hw/xen/interface/xen.h | 1049 +++++++++++++++++ include/hw/xen/xen.h | 16 +- 28 files changed, 7134 insertions(+), 136 deletions(-) create mode 100644 include/hw/xen/interface/arch-arm.h create mode 100644 include/hw/xen/interface/arch-x86/cpuid.h create mode 100644 include/hw/xen/interface/arch-x86/xen-x86_32.h create mode 100644 include/hw/xen/interface/arch-x86/xen-x86_64.h create mode 100644 include/hw/xen/interface/arch-x86/xen.h create mode 100644 include/hw/xen/interface/event_channel.h create mode 100644 include/hw/xen/interface/features.h create mode 100644 include/hw/xen/interface/hvm/hvm_op.h create mode 100644 include/hw/xen/interface/hvm/params.h create mode 100644 include/hw/xen/interface/io/xs_wire.h create mode 100644 include/hw/xen/interface/memory.h create mode 100644 include/hw/xen/interface/physdev.h create mode 100644 include/hw/xen/interface/sched.h create mode 100644 include/hw/xen/interface/trace.h create mode 100644 include/hw/xen/interface/vcpu.h create mode 100644 include/hw/xen/interface/version.h create mode 100644 include/hw/xen/interface/xen-compat.h create mode 100644 include/hw/xen/interface/xen.h diff --git a/include/hw/xen/interface/arch-arm.h b/include/hw/xen/interface/arch-arm.h new file mode 100644 index 0000000000..94b31511dd --- /dev/null +++ b/include/hw/xen/interface/arch-arm.h @@ -0,0 +1,510 @@ +/****************************************************************************** + * arch-arm.h + * + * Guest OS interface to ARM Xen. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright 2011 (C) Citrix Systems + */ + +#ifndef __XEN_PUBLIC_ARCH_ARM_H__ +#define __XEN_PUBLIC_ARCH_ARM_H__ + +/* + * `incontents 50 arm_abi Hypercall Calling Convention + * + * A hypercall is issued using the ARM HVC instruction. + * + * A hypercall can take up to 5 arguments. These are passed in + * registers, the first argument in x0/r0 (for arm64/arm32 guests + * respectively irrespective of whether the underlying hypervisor is + * 32- or 64-bit), the second argument in x1/r1, the third in x2/r2, + * the forth in x3/r3 and the fifth in x4/r4. + * + * The hypercall number is passed in r12 (arm) or x16 (arm64). In both + * cases the relevant ARM procedure calling convention specifies this + * is an inter-procedure-call scratch register (e.g. for use in linker + * stubs). This use does not conflict with use during a hypercall. + * + * The HVC ISS must contain a Xen specific TAG: XEN_HYPERCALL_TAG. + * + * The return value is in x0/r0. + * + * The hypercall will clobber x16/r12 and the argument registers used + * by that hypercall (except r0 which is the return value) i.e. in + * addition to x16/r12 a 2 argument hypercall will clobber x1/r1 and a + * 4 argument hypercall will clobber x1/r1, x2/r2 and x3/r3. + * + * Parameter structs passed to hypercalls are laid out according to + * the Procedure Call Standard for the ARM Architecture (AAPCS, AKA + * EABI) and Procedure Call Standard for the ARM 64-bit Architecture + * (AAPCS64). Where there is a conflict the 64-bit standard should be + * used regardless of guest type. Structures which are passed as + * hypercall arguments are always little endian. + * + * All memory which is shared with other entities in the system + * (including the hypervisor and other guests) must reside in memory + * which is mapped as Normal Inner Write-Back Outer Write-Back Inner-Shareable. + * This applies to: + * - hypercall arguments passed via a pointer to guest memory. + * - memory shared via the grant table mechanism (including PV I/O + * rings etc). + * - memory shared with the hypervisor (struct shared_info, struct + * vcpu_info, the grant table, etc). + * + * Any cache allocation hints are acceptable. + */ + +/* + * `incontents 55 arm_hcall Supported Hypercalls + * + * Xen on ARM makes extensive use of hardware facilities and therefore + * only a subset of the potential hypercalls are required. + * + * Since ARM uses second stage paging any machine/physical addresses + * passed to hypercalls are Guest Physical Addresses (Intermediate + * Physical Addresses) unless otherwise noted. + * + * The following hypercalls (and sub operations) are supported on the + * ARM platform. Other hypercalls should be considered + * unavailable/unsupported. + * + * HYPERVISOR_memory_op + * All generic sub-operations + * + * HYPERVISOR_domctl + * All generic sub-operations, with the exception of: + * * XEN_DOMCTL_irq_permission (not yet implemented) + * + * HYPERVISOR_sched_op + * All generic sub-operations, with the exception of: + * * SCHEDOP_block -- prefer wfi hardware instruction + * + * HYPERVISOR_console_io + * All generic sub-operations + * + * HYPERVISOR_xen_version + * All generic sub-operations + * + * HYPERVISOR_event_channel_op + * All generic sub-operations + * + * HYPERVISOR_physdev_op + * Exactly these sub-operations are supported: + * PHYSDEVOP_pci_device_add + * PHYSDEVOP_pci_device_remove + * + * HYPERVISOR_sysctl + * All generic sub-operations, with the exception of: + * * XEN_SYSCTL_page_offline_op + * * XEN_SYSCTL_get_pmstat + * * XEN_SYSCTL_pm_op + * + * HYPERVISOR_hvm_op + * Exactly these sub-operations are supported: + * * HVMOP_set_param + * * HVMOP_get_param + * + * HYPERVISOR_grant_table_op + * All generic sub-operations + * + * HYPERVISOR_vcpu_op + * Exactly these sub-operations are supported: + * * VCPUOP_register_vcpu_info + * * VCPUOP_register_runstate_memory_area + * + * HYPERVISOR_argo_op + * All generic sub-operations + * + * Other notes on the ARM ABI: + * + * - struct start_info is not exported to ARM guests. + * + * - struct shared_info is mapped by ARM guests using the + * HYPERVISOR_memory_op sub-op XENMEM_add_to_physmap, passing + * XENMAPSPACE_shared_info as space parameter. + * + * - All the per-cpu struct vcpu_info are mapped by ARM guests using the + * HYPERVISOR_vcpu_op sub-op VCPUOP_register_vcpu_info, including cpu0 + * struct vcpu_info. + * + * - The grant table is mapped using the HYPERVISOR_memory_op sub-op + * XENMEM_add_to_physmap, passing XENMAPSPACE_grant_table as space + * parameter. The memory range specified under the Xen compatible + * hypervisor node on device tree can be used as target gpfn for the + * mapping. + * + * - Xenstore is initialized by using the two hvm_params + * HVM_PARAM_STORE_PFN and HVM_PARAM_STORE_EVTCHN. They can be read + * with the HYPERVISOR_hvm_op sub-op HVMOP_get_param. + * + * - The paravirtualized console is initialized by using the two + * hvm_params HVM_PARAM_CONSOLE_PFN and HVM_PARAM_CONSOLE_EVTCHN. They + * can be read with the HYPERVISOR_hvm_op sub-op HVMOP_get_param. + * + * - Event channel notifications are delivered using the percpu GIC + * interrupt specified under the Xen compatible hypervisor node on + * device tree. + * + * - The device tree Xen compatible node is fully described under Linux + * at Documentation/devicetree/bindings/arm/xen.txt. + */ + +#define XEN_HYPERCALL_TAG 0XEA1 + +#define int64_aligned_t int64_t __attribute__((aligned(8))) +#define uint64_aligned_t uint64_t __attribute__((aligned(8))) + +#ifndef __ASSEMBLY__ +#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \ + typedef union { type *p; unsigned long q; } \ + __guest_handle_ ## name; \ + typedef union { type *p; uint64_aligned_t q; } \ + __guest_handle_64_ ## name + +/* + * XEN_GUEST_HANDLE represents a guest pointer, when passed as a field + * in a struct in memory. On ARM is always 8 bytes sizes and 8 bytes + * aligned. + * XEN_GUEST_HANDLE_PARAM represents a guest pointer, when passed as an + * hypercall argument. It is 4 bytes on aarch32 and 8 bytes on aarch64. + */ +#define __DEFINE_XEN_GUEST_HANDLE(name, type) \ + ___DEFINE_XEN_GUEST_HANDLE(name, type); \ + ___DEFINE_XEN_GUEST_HANDLE(const_##name, const type) +#define DEFINE_XEN_GUEST_HANDLE(name) __DEFINE_XEN_GUEST_HANDLE(name, name) +#define __XEN_GUEST_HANDLE(name) __guest_handle_64_ ## name +#define XEN_GUEST_HANDLE(name) __XEN_GUEST_HANDLE(name) +#define XEN_GUEST_HANDLE_PARAM(name) __guest_handle_ ## name +#define set_xen_guest_handle_raw(hnd, val) \ + do { \ + __typeof__(&(hnd)) _sxghr_tmp = &(hnd); \ + _sxghr_tmp->q = 0; \ + _sxghr_tmp->p = val; \ + } while ( 0 ) +#define set_xen_guest_handle(hnd, val) set_xen_guest_handle_raw(hnd, val) + +typedef uint64_t xen_pfn_t; +#define PRI_xen_pfn PRIx64 +#define PRIu_xen_pfn PRIu64 + +/* + * Maximum number of virtual CPUs in legacy multi-processor guests. + * Only one. All other VCPUS must use VCPUOP_register_vcpu_info. + */ +#define XEN_LEGACY_MAX_VCPUS 1 + +typedef uint64_t xen_ulong_t; +#define PRI_xen_ulong PRIx64 + +#if defined(__XEN__) || defined(__XEN_TOOLS__) +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +/* Anonymous union includes both 32- and 64-bit names (e.g., r0/x0). */ +# define __DECL_REG(n64, n32) union { \ + uint64_t n64; \ + uint32_t n32; \ + } +#else +/* Non-gcc sources must always use the proper 64-bit name (e.g., x0). */ +#define __DECL_REG(n64, n32) uint64_t n64 +#endif + +struct vcpu_guest_core_regs +{ + /* Aarch64 Aarch32 */ + __DECL_REG(x0, r0_usr); + __DECL_REG(x1, r1_usr); + __DECL_REG(x2, r2_usr); + __DECL_REG(x3, r3_usr); + __DECL_REG(x4, r4_usr); + __DECL_REG(x5, r5_usr); + __DECL_REG(x6, r6_usr); + __DECL_REG(x7, r7_usr); + __DECL_REG(x8, r8_usr); + __DECL_REG(x9, r9_usr); + __DECL_REG(x10, r10_usr); + __DECL_REG(x11, r11_usr); + __DECL_REG(x12, r12_usr); + + __DECL_REG(x13, sp_usr); + __DECL_REG(x14, lr_usr); + + __DECL_REG(x15, __unused_sp_hyp); + + __DECL_REG(x16, lr_irq); + __DECL_REG(x17, sp_irq); + + __DECL_REG(x18, lr_svc); + __DECL_REG(x19, sp_svc); + + __DECL_REG(x20, lr_abt); + __DECL_REG(x21, sp_abt); + + __DECL_REG(x22, lr_und); + __DECL_REG(x23, sp_und); + + __DECL_REG(x24, r8_fiq); + __DECL_REG(x25, r9_fiq); + __DECL_REG(x26, r10_fiq); + __DECL_REG(x27, r11_fiq); + __DECL_REG(x28, r12_fiq); + + __DECL_REG(x29, sp_fiq); + __DECL_REG(x30, lr_fiq); + + /* Return address and mode */ + __DECL_REG(pc64, pc32); /* ELR_EL2 */ + uint64_t cpsr; /* SPSR_EL2 */ + + union { + uint64_t spsr_el1; /* AArch64 */ + uint32_t spsr_svc; /* AArch32 */ + }; + + /* AArch32 guests only */ + uint32_t spsr_fiq, spsr_irq, spsr_und, spsr_abt; + + /* AArch64 guests only */ + uint64_t sp_el0; + uint64_t sp_el1, elr_el1; +}; +typedef struct vcpu_guest_core_regs vcpu_guest_core_regs_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_guest_core_regs_t); + +#undef __DECL_REG + +struct vcpu_guest_context { +#define _VGCF_online 0 +#define VGCF_online (1<<_VGCF_online) + uint32_t flags; /* VGCF_* */ + + struct vcpu_guest_core_regs user_regs; /* Core CPU registers */ + + uint64_t sctlr; + uint64_t ttbcr, ttbr0, ttbr1; +}; +typedef struct vcpu_guest_context vcpu_guest_context_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t); + +/* + * struct xen_arch_domainconfig's ABI is covered by + * XEN_DOMCTL_INTERFACE_VERSION. + */ +#define XEN_DOMCTL_CONFIG_GIC_NATIVE 0 +#define XEN_DOMCTL_CONFIG_GIC_V2 1 +#define XEN_DOMCTL_CONFIG_GIC_V3 2 + +#define XEN_DOMCTL_CONFIG_TEE_NONE 0 +#define XEN_DOMCTL_CONFIG_TEE_OPTEE 1 + +struct xen_arch_domainconfig { + /* IN/OUT */ + uint8_t gic_version; + /* IN */ + uint16_t tee_type; + /* IN */ + uint32_t nr_spis; + /* + * OUT + * Based on the property clock-frequency in the DT timer node. + * The property may be present when the bootloader/firmware doesn't + * set correctly CNTFRQ which hold the timer frequency. + * + * As it's not possible to trap this register, we have to replicate + * the value in the guest DT. + * + * = 0 => property not present + * > 0 => Value of the property + * + */ + uint32_t clock_frequency; +}; +#endif /* __XEN__ || __XEN_TOOLS__ */ + +struct arch_vcpu_info { +}; +typedef struct arch_vcpu_info arch_vcpu_info_t; + +struct arch_shared_info { +}; +typedef struct arch_shared_info arch_shared_info_t; +typedef uint64_t xen_callback_t; + +#endif + +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +/* PSR bits (CPSR, SPSR) */ + +#define PSR_THUMB (1<<5) /* Thumb Mode enable */ +#define PSR_FIQ_MASK (1<<6) /* Fast Interrupt mask */ +#define PSR_IRQ_MASK (1<<7) /* Interrupt mask */ +#define PSR_ABT_MASK (1<<8) /* Asynchronous Abort mask */ +#define PSR_BIG_ENDIAN (1<<9) /* arm32: Big Endian Mode */ +#define PSR_DBG_MASK (1<<9) /* arm64: Debug Exception mask */ +#define PSR_IT_MASK (0x0600fc00) /* Thumb If-Then Mask */ +#define PSR_JAZELLE (1<<24) /* Jazelle Mode */ + +/* 32 bit modes */ +#define PSR_MODE_USR 0x10 +#define PSR_MODE_FIQ 0x11 +#define PSR_MODE_IRQ 0x12 +#define PSR_MODE_SVC 0x13 +#define PSR_MODE_MON 0x16 +#define PSR_MODE_ABT 0x17 +#define PSR_MODE_HYP 0x1a +#define PSR_MODE_UND 0x1b +#define PSR_MODE_SYS 0x1f + +/* 64 bit modes */ +#define PSR_MODE_BIT 0x10 /* Set iff AArch32 */ +#define PSR_MODE_EL3h 0x0d +#define PSR_MODE_EL3t 0x0c +#define PSR_MODE_EL2h 0x09 +#define PSR_MODE_EL2t 0x08 +#define PSR_MODE_EL1h 0x05 +#define PSR_MODE_EL1t 0x04 +#define PSR_MODE_EL0t 0x00 + +#define PSR_GUEST32_INIT (PSR_ABT_MASK|PSR_FIQ_MASK|PSR_IRQ_MASK|PSR_MODE_SVC) +#define PSR_GUEST64_INIT (PSR_ABT_MASK|PSR_FIQ_MASK|PSR_IRQ_MASK|PSR_MODE_EL1h) + +#define SCTLR_GUEST_INIT xen_mk_ullong(0x00c50078) + +/* + * Virtual machine platform (memory layout, interrupts) + * + * These are defined for consistency between the tools and the + * hypervisor. Guests must not rely on these hardcoded values but + * should instead use the FDT. + */ + +/* Physical Address Space */ + +/* + * vGIC mappings: Only one set of mapping is used by the guest. + * Therefore they can overlap. + */ + +/* vGIC v2 mappings */ +#define GUEST_GICD_BASE xen_mk_ullong(0x03001000) +#define GUEST_GICD_SIZE xen_mk_ullong(0x00001000) +#define GUEST_GICC_BASE xen_mk_ullong(0x03002000) +#define GUEST_GICC_SIZE xen_mk_ullong(0x00002000) + +/* vGIC v3 mappings */ +#define GUEST_GICV3_GICD_BASE xen_mk_ullong(0x03001000) +#define GUEST_GICV3_GICD_SIZE xen_mk_ullong(0x00010000) + +#define GUEST_GICV3_RDIST_REGIONS 1 + +#define GUEST_GICV3_GICR0_BASE xen_mk_ullong(0x03020000) /* vCPU0..127 */ +#define GUEST_GICV3_GICR0_SIZE xen_mk_ullong(0x01000000) + +/* + * 256 MB is reserved for VPCI configuration space based on calculation + * 256 buses x 32 devices x 8 functions x 4 KB = 256 MB + */ +#define GUEST_VPCI_ECAM_BASE xen_mk_ullong(0x10000000) +#define GUEST_VPCI_ECAM_SIZE xen_mk_ullong(0x10000000) + +/* ACPI tables physical address */ +#define GUEST_ACPI_BASE xen_mk_ullong(0x20000000) +#define GUEST_ACPI_SIZE xen_mk_ullong(0x02000000) + +/* PL011 mappings */ +#define GUEST_PL011_BASE xen_mk_ullong(0x22000000) +#define GUEST_PL011_SIZE xen_mk_ullong(0x00001000) + +/* Guest PCI-PCIe memory space where config space and BAR will be available.*/ +#define GUEST_VPCI_ADDR_TYPE_MEM xen_mk_ullong(0x02000000) +#define GUEST_VPCI_MEM_ADDR xen_mk_ullong(0x23000000) +#define GUEST_VPCI_MEM_SIZE xen_mk_ullong(0x10000000) + +/* + * 16MB == 4096 pages reserved for guest to use as a region to map its + * grant table in. + */ +#define GUEST_GNTTAB_BASE xen_mk_ullong(0x38000000) +#define GUEST_GNTTAB_SIZE xen_mk_ullong(0x01000000) + +#define GUEST_MAGIC_BASE xen_mk_ullong(0x39000000) +#define GUEST_MAGIC_SIZE xen_mk_ullong(0x01000000) + +#define GUEST_RAM_BANKS 2 + +/* + * The way to find the extended regions (to be exposed to the guest as unused + * address space) relies on the fact that the regions reserved for the RAM + * below are big enough to also accommodate such regions. + */ +#define GUEST_RAM0_BASE xen_mk_ullong(0x40000000) /* 3GB of low RAM @ 1GB */ +#define GUEST_RAM0_SIZE xen_mk_ullong(0xc0000000) + +/* 4GB @ 4GB Prefetch Memory for VPCI */ +#define GUEST_VPCI_ADDR_TYPE_PREFETCH_MEM xen_mk_ullong(0x42000000) +#define GUEST_VPCI_PREFETCH_MEM_ADDR xen_mk_ullong(0x100000000) +#define GUEST_VPCI_PREFETCH_MEM_SIZE xen_mk_ullong(0x100000000) + +#define GUEST_RAM1_BASE xen_mk_ullong(0x0200000000) /* 1016GB of RAM @ 8GB */ +#define GUEST_RAM1_SIZE xen_mk_ullong(0xfe00000000) + +#define GUEST_RAM_BASE GUEST_RAM0_BASE /* Lowest RAM address */ +/* Largest amount of actual RAM, not including holes */ +#define GUEST_RAM_MAX (GUEST_RAM0_SIZE + GUEST_RAM1_SIZE) +/* Suitable for e.g. const uint64_t ramfoo[] = GUEST_RAM_BANK_FOOS; */ +#define GUEST_RAM_BANK_BASES { GUEST_RAM0_BASE, GUEST_RAM1_BASE } +#define GUEST_RAM_BANK_SIZES { GUEST_RAM0_SIZE, GUEST_RAM1_SIZE } + +/* Current supported guest VCPUs */ +#define GUEST_MAX_VCPUS 128 + +/* Interrupts */ +#define GUEST_TIMER_VIRT_PPI 27 +#define GUEST_TIMER_PHYS_S_PPI 29 +#define GUEST_TIMER_PHYS_NS_PPI 30 +#define GUEST_EVTCHN_PPI 31 + +#define GUEST_VPL011_SPI 32 + +/* PSCI functions */ +#define PSCI_cpu_suspend 0 +#define PSCI_cpu_off 1 +#define PSCI_cpu_on 2 +#define PSCI_migrate 3 + +#endif + +#ifndef __ASSEMBLY__ +/* Stub definition of PMU structure */ +typedef struct xen_pmu_arch { uint8_t dummy; } xen_pmu_arch_t; +#endif + +#endif /* __XEN_PUBLIC_ARCH_ARM_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/arch-x86/cpuid.h b/include/hw/xen/interface/arch-x86/cpuid.h new file mode 100644 index 0000000000..ce46305bee --- /dev/null +++ b/include/hw/xen/interface/arch-x86/cpuid.h @@ -0,0 +1,118 @@ +/****************************************************************************** + * arch-x86/cpuid.h + * + * CPUID interface to Xen. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2007 Citrix Systems, Inc. + * + * Authors: + * Keir Fraser + */ + +#ifndef __XEN_PUBLIC_ARCH_X86_CPUID_H__ +#define __XEN_PUBLIC_ARCH_X86_CPUID_H__ + +/* + * For compatibility with other hypervisor interfaces, the Xen cpuid leaves + * can be found at the first otherwise unused 0x100 aligned boundary starting + * from 0x40000000. + * + * e.g If viridian extensions are enabled for an HVM domain, the Xen cpuid + * leaves will start at 0x40000100 + */ + +#define XEN_CPUID_FIRST_LEAF 0x40000000 +#define XEN_CPUID_LEAF(i) (XEN_CPUID_FIRST_LEAF + (i)) + +/* + * Leaf 1 (0x40000x00) + * EAX: Largest Xen-information leaf. All leaves up to an including @EAX + * are supported by the Xen host. + * EBX-EDX: "XenVMMXenVMM" signature, allowing positive identification + * of a Xen host. + */ +#define XEN_CPUID_SIGNATURE_EBX 0x566e6558 /* "XenV" */ +#define XEN_CPUID_SIGNATURE_ECX 0x65584d4d /* "MMXe" */ +#define XEN_CPUID_SIGNATURE_EDX 0x4d4d566e /* "nVMM" */ + +/* + * Leaf 2 (0x40000x01) + * EAX[31:16]: Xen major version. + * EAX[15: 0]: Xen minor version. + * EBX-EDX: Reserved (currently all zeroes). + */ + +/* + * Leaf 3 (0x40000x02) + * EAX: Number of hypercall transfer pages. This register is always guaranteed + * to specify one hypercall page. + * EBX: Base address of Xen-specific MSRs. + * ECX: Features 1. Unused bits are set to zero. + * EDX: Features 2. Unused bits are set to zero. + */ + +/* Does the host support MMU_PT_UPDATE_PRESERVE_AD for this guest? */ +#define _XEN_CPUID_FEAT1_MMU_PT_UPDATE_PRESERVE_AD 0 +#define XEN_CPUID_FEAT1_MMU_PT_UPDATE_PRESERVE_AD (1u<<0) + +/* + * Leaf 4 (0x40000x03) + * Sub-leaf 0: EAX: bit 0: emulated tsc + * bit 1: host tsc is known to be reliable + * bit 2: RDTSCP instruction available + * EBX: tsc_mode: 0=default (emulate if necessary), 1=emulate, + * 2=no emulation, 3=no emulation + TSC_AUX support + * ECX: guest tsc frequency in kHz + * EDX: guest tsc incarnation (migration count) + * Sub-leaf 1: EAX: tsc offset low part + * EBX: tsc offset high part + * ECX: multiplicator for tsc->ns conversion + * EDX: shift amount for tsc->ns conversion + * Sub-leaf 2: EAX: host tsc frequency in kHz + */ + +/* + * Leaf 5 (0x40000x04) + * HVM-specific features + * Sub-leaf 0: EAX: Features + * Sub-leaf 0: EBX: vcpu id (iff EAX has XEN_HVM_CPUID_VCPU_ID_PRESENT flag) + * Sub-leaf 0: ECX: domain id (iff EAX has XEN_HVM_CPUID_DOMID_PRESENT flag) + */ +#define XEN_HVM_CPUID_APIC_ACCESS_VIRT (1u << 0) /* Virtualized APIC registers */ +#define XEN_HVM_CPUID_X2APIC_VIRT (1u << 1) /* Virtualized x2APIC accesses */ +/* Memory mapped from other domains has valid IOMMU entries */ +#define XEN_HVM_CPUID_IOMMU_MAPPINGS (1u << 2) +#define XEN_HVM_CPUID_VCPU_ID_PRESENT (1u << 3) /* vcpu id is present in EBX */ +#define XEN_HVM_CPUID_DOMID_PRESENT (1u << 4) /* domid is present in ECX */ + +/* + * Leaf 6 (0x40000x05) + * PV-specific parameters + * Sub-leaf 0: EAX: max available sub-leaf + * Sub-leaf 0: EBX: bits 0-7: max machine address width + */ + +/* Max. address width in bits taking memory hotplug into account. */ +#define XEN_CPUID_MACHINE_ADDRESS_WIDTH_MASK (0xffu << 0) + +#define XEN_CPUID_MAX_NUM_LEAVES 5 + +#endif /* __XEN_PUBLIC_ARCH_X86_CPUID_H__ */ diff --git a/include/hw/xen/interface/arch-x86/xen-x86_32.h b/include/hw/xen/interface/arch-x86/xen-x86_32.h new file mode 100644 index 0000000000..19d7388633 --- /dev/null +++ b/include/hw/xen/interface/arch-x86/xen-x86_32.h @@ -0,0 +1,194 @@ +/****************************************************************************** + * xen-x86_32.h + * + * Guest OS interface to x86 32-bit Xen. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2004-2007, K A Fraser + */ + +#ifndef __XEN_PUBLIC_ARCH_X86_XEN_X86_32_H__ +#define __XEN_PUBLIC_ARCH_X86_XEN_X86_32_H__ + +/* + * Hypercall interface: + * Input: %ebx, %ecx, %edx, %esi, %edi, %ebp (arguments 1-6) + * Output: %eax + * Access is via hypercall page (set up by guest loader or via a Xen MSR): + * call hypercall_page + hypercall-number * 32 + * Clobbered: Argument registers (e.g., 2-arg hypercall clobbers %ebx,%ecx) + */ + +/* + * These flat segments are in the Xen-private section of every GDT. Since these + * are also present in the initial GDT, many OSes will be able to avoid + * installing their own GDT. + */ +#define FLAT_RING1_CS 0xe019 /* GDT index 259 */ +#define FLAT_RING1_DS 0xe021 /* GDT index 260 */ +#define FLAT_RING1_SS 0xe021 /* GDT index 260 */ +#define FLAT_RING3_CS 0xe02b /* GDT index 261 */ +#define FLAT_RING3_DS 0xe033 /* GDT index 262 */ +#define FLAT_RING3_SS 0xe033 /* GDT index 262 */ + +#define FLAT_KERNEL_CS FLAT_RING1_CS +#define FLAT_KERNEL_DS FLAT_RING1_DS +#define FLAT_KERNEL_SS FLAT_RING1_SS +#define FLAT_USER_CS FLAT_RING3_CS +#define FLAT_USER_DS FLAT_RING3_DS +#define FLAT_USER_SS FLAT_RING3_SS + +#define __HYPERVISOR_VIRT_START_PAE 0xF5800000 +#define __MACH2PHYS_VIRT_START_PAE 0xF5800000 +#define __MACH2PHYS_VIRT_END_PAE 0xF6800000 +#define HYPERVISOR_VIRT_START_PAE xen_mk_ulong(__HYPERVISOR_VIRT_START_PAE) +#define MACH2PHYS_VIRT_START_PAE xen_mk_ulong(__MACH2PHYS_VIRT_START_PAE) +#define MACH2PHYS_VIRT_END_PAE xen_mk_ulong(__MACH2PHYS_VIRT_END_PAE) + +/* Non-PAE bounds are obsolete. */ +#define __HYPERVISOR_VIRT_START_NONPAE 0xFC000000 +#define __MACH2PHYS_VIRT_START_NONPAE 0xFC000000 +#define __MACH2PHYS_VIRT_END_NONPAE 0xFC400000 +#define HYPERVISOR_VIRT_START_NONPAE \ + xen_mk_ulong(__HYPERVISOR_VIRT_START_NONPAE) +#define MACH2PHYS_VIRT_START_NONPAE \ + xen_mk_ulong(__MACH2PHYS_VIRT_START_NONPAE) +#define MACH2PHYS_VIRT_END_NONPAE \ + xen_mk_ulong(__MACH2PHYS_VIRT_END_NONPAE) + +#define __HYPERVISOR_VIRT_START __HYPERVISOR_VIRT_START_PAE +#define __MACH2PHYS_VIRT_START __MACH2PHYS_VIRT_START_PAE +#define __MACH2PHYS_VIRT_END __MACH2PHYS_VIRT_END_PAE + +#ifndef HYPERVISOR_VIRT_START +#define HYPERVISOR_VIRT_START xen_mk_ulong(__HYPERVISOR_VIRT_START) +#endif + +#define MACH2PHYS_VIRT_START xen_mk_ulong(__MACH2PHYS_VIRT_START) +#define MACH2PHYS_VIRT_END xen_mk_ulong(__MACH2PHYS_VIRT_END) +#define MACH2PHYS_NR_ENTRIES ((MACH2PHYS_VIRT_END-MACH2PHYS_VIRT_START)>>2) +#ifndef machine_to_phys_mapping +#define machine_to_phys_mapping ((unsigned long *)MACH2PHYS_VIRT_START) +#endif + +/* 32-/64-bit invariability for control interfaces (domctl/sysctl). */ +#if defined(__XEN__) || defined(__XEN_TOOLS__) +#undef ___DEFINE_XEN_GUEST_HANDLE +#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \ + typedef struct { type *p; } \ + __guest_handle_ ## name; \ + typedef struct { union { type *p; uint64_aligned_t q; }; } \ + __guest_handle_64_ ## name +#undef set_xen_guest_handle_raw +#define set_xen_guest_handle_raw(hnd, val) \ + do { if ( sizeof(hnd) == 8 ) *(uint64_t *)&(hnd) = 0; \ + (hnd).p = val; \ + } while ( 0 ) +#define int64_aligned_t int64_t __attribute__((aligned(8))) +#define uint64_aligned_t uint64_t __attribute__((aligned(8))) +#define __XEN_GUEST_HANDLE_64(name) __guest_handle_64_ ## name +#define XEN_GUEST_HANDLE_64(name) __XEN_GUEST_HANDLE_64(name) +#endif + +#ifndef __ASSEMBLY__ + +#if defined(XEN_GENERATING_COMPAT_HEADERS) +/* nothing */ +#elif defined(__XEN__) || defined(__XEN_TOOLS__) +/* Anonymous unions include all permissible names (e.g., al/ah/ax/eax). */ +#define __DECL_REG_LO8(which) union { \ + uint32_t e ## which ## x; \ + uint16_t which ## x; \ + struct { \ + uint8_t which ## l; \ + uint8_t which ## h; \ + }; \ +} +#define __DECL_REG_LO16(name) union { \ + uint32_t e ## name, _e ## name; \ + uint16_t name; \ +} +#else +/* Other sources must always use the proper 32-bit name (e.g., eax). */ +#define __DECL_REG_LO8(which) uint32_t e ## which ## x +#define __DECL_REG_LO16(name) uint32_t e ## name +#endif + +struct cpu_user_regs { + __DECL_REG_LO8(b); + __DECL_REG_LO8(c); + __DECL_REG_LO8(d); + __DECL_REG_LO16(si); + __DECL_REG_LO16(di); + __DECL_REG_LO16(bp); + __DECL_REG_LO8(a); + uint16_t error_code; /* private */ + uint16_t entry_vector; /* private */ + __DECL_REG_LO16(ip); + uint16_t cs; + uint8_t saved_upcall_mask; + uint8_t _pad0; + __DECL_REG_LO16(flags); /* eflags.IF == !saved_upcall_mask */ + __DECL_REG_LO16(sp); + uint16_t ss, _pad1; + uint16_t es, _pad2; + uint16_t ds, _pad3; + uint16_t fs, _pad4; + uint16_t gs, _pad5; +}; +typedef struct cpu_user_regs cpu_user_regs_t; +DEFINE_XEN_GUEST_HANDLE(cpu_user_regs_t); + +#undef __DECL_REG_LO8 +#undef __DECL_REG_LO16 + +/* + * Page-directory addresses above 4GB do not fit into architectural %cr3. + * When accessing %cr3, or equivalent field in vcpu_guest_context, guests + * must use the following accessor macros to pack/unpack valid MFNs. + */ +#define xen_pfn_to_cr3(pfn) (((unsigned)(pfn) << 12) | ((unsigned)(pfn) >> 20)) +#define xen_cr3_to_pfn(cr3) (((unsigned)(cr3) >> 12) | ((unsigned)(cr3) << 20)) + +struct arch_vcpu_info { + unsigned long cr2; + unsigned long pad[5]; /* sizeof(vcpu_info_t) == 64 */ +}; +typedef struct arch_vcpu_info arch_vcpu_info_t; + +struct xen_callback { + unsigned long cs; + unsigned long eip; +}; +typedef struct xen_callback xen_callback_t; + +#endif /* !__ASSEMBLY__ */ + +#endif /* __XEN_PUBLIC_ARCH_X86_XEN_X86_32_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/arch-x86/xen-x86_64.h b/include/hw/xen/interface/arch-x86/xen-x86_64.h new file mode 100644 index 0000000000..40aed14366 --- /dev/null +++ b/include/hw/xen/interface/arch-x86/xen-x86_64.h @@ -0,0 +1,241 @@ +/****************************************************************************** + * xen-x86_64.h + * + * Guest OS interface to x86 64-bit Xen. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2004-2006, K A Fraser + */ + +#ifndef __XEN_PUBLIC_ARCH_X86_XEN_X86_64_H__ +#define __XEN_PUBLIC_ARCH_X86_XEN_X86_64_H__ + +/* + * Hypercall interface: + * Input: %rdi, %rsi, %rdx, %r10, %r8, %r9 (arguments 1-6) + * Output: %rax + * Access is via hypercall page (set up by guest loader or via a Xen MSR): + * call hypercall_page + hypercall-number * 32 + * Clobbered: argument registers (e.g., 2-arg hypercall clobbers %rdi,%rsi) + */ + +/* + * 64-bit segment selectors + * These flat segments are in the Xen-private section of every GDT. Since these + * are also present in the initial GDT, many OSes will be able to avoid + * installing their own GDT. + */ + +#define FLAT_RING3_CS32 0xe023 /* GDT index 260 */ +#define FLAT_RING3_CS64 0xe033 /* GDT index 262 */ +#define FLAT_RING3_DS32 0xe02b /* GDT index 261 */ +#define FLAT_RING3_DS64 0x0000 /* NULL selector */ +#define FLAT_RING3_SS32 0xe02b /* GDT index 261 */ +#define FLAT_RING3_SS64 0xe02b /* GDT index 261 */ + +#define FLAT_KERNEL_DS64 FLAT_RING3_DS64 +#define FLAT_KERNEL_DS32 FLAT_RING3_DS32 +#define FLAT_KERNEL_DS FLAT_KERNEL_DS64 +#define FLAT_KERNEL_CS64 FLAT_RING3_CS64 +#define FLAT_KERNEL_CS32 FLAT_RING3_CS32 +#define FLAT_KERNEL_CS FLAT_KERNEL_CS64 +#define FLAT_KERNEL_SS64 FLAT_RING3_SS64 +#define FLAT_KERNEL_SS32 FLAT_RING3_SS32 +#define FLAT_KERNEL_SS FLAT_KERNEL_SS64 + +#define FLAT_USER_DS64 FLAT_RING3_DS64 +#define FLAT_USER_DS32 FLAT_RING3_DS32 +#define FLAT_USER_DS FLAT_USER_DS64 +#define FLAT_USER_CS64 FLAT_RING3_CS64 +#define FLAT_USER_CS32 FLAT_RING3_CS32 +#define FLAT_USER_CS FLAT_USER_CS64 +#define FLAT_USER_SS64 FLAT_RING3_SS64 +#define FLAT_USER_SS32 FLAT_RING3_SS32 +#define FLAT_USER_SS FLAT_USER_SS64 + +#define __HYPERVISOR_VIRT_START 0xFFFF800000000000 +#define __HYPERVISOR_VIRT_END 0xFFFF880000000000 +#define __MACH2PHYS_VIRT_START 0xFFFF800000000000 +#define __MACH2PHYS_VIRT_END 0xFFFF804000000000 + +#ifndef HYPERVISOR_VIRT_START +#define HYPERVISOR_VIRT_START xen_mk_ulong(__HYPERVISOR_VIRT_START) +#define HYPERVISOR_VIRT_END xen_mk_ulong(__HYPERVISOR_VIRT_END) +#endif + +#define MACH2PHYS_VIRT_START xen_mk_ulong(__MACH2PHYS_VIRT_START) +#define MACH2PHYS_VIRT_END xen_mk_ulong(__MACH2PHYS_VIRT_END) +#define MACH2PHYS_NR_ENTRIES ((MACH2PHYS_VIRT_END-MACH2PHYS_VIRT_START)>>3) +#ifndef machine_to_phys_mapping +#define machine_to_phys_mapping ((unsigned long *)HYPERVISOR_VIRT_START) +#endif + +/* + * int HYPERVISOR_set_segment_base(unsigned int which, unsigned long base) + * @which == SEGBASE_* ; @base == 64-bit base address + * Returns 0 on success. + */ +#define SEGBASE_FS 0 +#define SEGBASE_GS_USER 1 +#define SEGBASE_GS_KERNEL 2 +#define SEGBASE_GS_USER_SEL 3 /* Set user %gs specified in base[15:0] */ + +/* + * int HYPERVISOR_iret(void) + * All arguments are on the kernel stack, in the following format. + * Never returns if successful. Current kernel context is lost. + * The saved CS is mapped as follows: + * RING0 -> RING3 kernel mode. + * RING1 -> RING3 kernel mode. + * RING2 -> RING3 kernel mode. + * RING3 -> RING3 user mode. + * However RING0 indicates that the guest kernel should return to iteself + * directly with + * orb $3,1*8(%rsp) + * iretq + * If flags contains VGCF_in_syscall: + * Restore RAX, RIP, RFLAGS, RSP. + * Discard R11, RCX, CS, SS. + * Otherwise: + * Restore RAX, R11, RCX, CS:RIP, RFLAGS, SS:RSP. + * All other registers are saved on hypercall entry and restored to user. + */ +/* Guest exited in SYSCALL context? Return to guest with SYSRET? */ +#define _VGCF_in_syscall 8 +#define VGCF_in_syscall (1<<_VGCF_in_syscall) +#define VGCF_IN_SYSCALL VGCF_in_syscall + +#ifndef __ASSEMBLY__ + +struct iret_context { + /* Top of stack (%rsp at point of hypercall). */ + uint64_t rax, r11, rcx, flags, rip, cs, rflags, rsp, ss; + /* Bottom of iret stack frame. */ +}; + +#if defined(__XEN__) || defined(__XEN_TOOLS__) +/* Anonymous unions include all permissible names (e.g., al/ah/ax/eax/rax). */ +#define __DECL_REG_LOHI(which) union { \ + uint64_t r ## which ## x; \ + uint32_t e ## which ## x; \ + uint16_t which ## x; \ + struct { \ + uint8_t which ## l; \ + uint8_t which ## h; \ + }; \ +} +#define __DECL_REG_LO8(name) union { \ + uint64_t r ## name; \ + uint32_t e ## name; \ + uint16_t name; \ + uint8_t name ## l; \ +} +#define __DECL_REG_LO16(name) union { \ + uint64_t r ## name; \ + uint32_t e ## name; \ + uint16_t name; \ +} +#define __DECL_REG_HI(num) union { \ + uint64_t r ## num; \ + uint32_t r ## num ## d; \ + uint16_t r ## num ## w; \ + uint8_t r ## num ## b; \ +} +#elif defined(__GNUC__) && !defined(__STRICT_ANSI__) +/* Anonymous union includes both 32- and 64-bit names (e.g., eax/rax). */ +#define __DECL_REG(name) union { \ + uint64_t r ## name, e ## name; \ + uint32_t _e ## name; \ +} +#else +/* Non-gcc sources must always use the proper 64-bit name (e.g., rax). */ +#define __DECL_REG(name) uint64_t r ## name +#endif + +#ifndef __DECL_REG_LOHI +#define __DECL_REG_LOHI(name) __DECL_REG(name ## x) +#define __DECL_REG_LO8 __DECL_REG +#define __DECL_REG_LO16 __DECL_REG +#define __DECL_REG_HI(num) uint64_t r ## num +#endif + +struct cpu_user_regs { + __DECL_REG_HI(15); + __DECL_REG_HI(14); + __DECL_REG_HI(13); + __DECL_REG_HI(12); + __DECL_REG_LO8(bp); + __DECL_REG_LOHI(b); + __DECL_REG_HI(11); + __DECL_REG_HI(10); + __DECL_REG_HI(9); + __DECL_REG_HI(8); + __DECL_REG_LOHI(a); + __DECL_REG_LOHI(c); + __DECL_REG_LOHI(d); + __DECL_REG_LO8(si); + __DECL_REG_LO8(di); + uint32_t error_code; /* private */ + uint32_t entry_vector; /* private */ + __DECL_REG_LO16(ip); + uint16_t cs, _pad0[1]; + uint8_t saved_upcall_mask; + uint8_t _pad1[3]; + __DECL_REG_LO16(flags); /* rflags.IF == !saved_upcall_mask */ + __DECL_REG_LO8(sp); + uint16_t ss, _pad2[3]; + uint16_t es, _pad3[3]; + uint16_t ds, _pad4[3]; + uint16_t fs, _pad5[3]; + uint16_t gs, _pad6[3]; +}; +typedef struct cpu_user_regs cpu_user_regs_t; +DEFINE_XEN_GUEST_HANDLE(cpu_user_regs_t); + +#undef __DECL_REG +#undef __DECL_REG_LOHI +#undef __DECL_REG_LO8 +#undef __DECL_REG_LO16 +#undef __DECL_REG_HI + +#define xen_pfn_to_cr3(pfn) ((unsigned long)(pfn) << 12) +#define xen_cr3_to_pfn(cr3) ((unsigned long)(cr3) >> 12) + +struct arch_vcpu_info { + unsigned long cr2; + unsigned long pad; /* sizeof(vcpu_info_t) == 64 */ +}; +typedef struct arch_vcpu_info arch_vcpu_info_t; + +typedef unsigned long xen_callback_t; + +#endif /* !__ASSEMBLY__ */ + +#endif /* __XEN_PUBLIC_ARCH_X86_XEN_X86_64_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/arch-x86/xen.h b/include/hw/xen/interface/arch-x86/xen.h new file mode 100644 index 0000000000..7acd94c8eb --- /dev/null +++ b/include/hw/xen/interface/arch-x86/xen.h @@ -0,0 +1,398 @@ +/****************************************************************************** + * arch-x86/xen.h + * + * Guest OS interface to x86 Xen. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2004-2006, K A Fraser + */ + +#include "../xen.h" + +#ifndef __XEN_PUBLIC_ARCH_X86_XEN_H__ +#define __XEN_PUBLIC_ARCH_X86_XEN_H__ + +/* Structural guest handles introduced in 0x00030201. */ +#if __XEN_INTERFACE_VERSION__ >= 0x00030201 +#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \ + typedef struct { type *p; } __guest_handle_ ## name +#else +#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \ + typedef type * __guest_handle_ ## name +#endif + +/* + * XEN_GUEST_HANDLE represents a guest pointer, when passed as a field + * in a struct in memory. + * XEN_GUEST_HANDLE_PARAM represent a guest pointer, when passed as an + * hypercall argument. + * XEN_GUEST_HANDLE_PARAM and XEN_GUEST_HANDLE are the same on X86 but + * they might not be on other architectures. + */ +#define __DEFINE_XEN_GUEST_HANDLE(name, type) \ + ___DEFINE_XEN_GUEST_HANDLE(name, type); \ + ___DEFINE_XEN_GUEST_HANDLE(const_##name, const type) +#define DEFINE_XEN_GUEST_HANDLE(name) __DEFINE_XEN_GUEST_HANDLE(name, name) +#define __XEN_GUEST_HANDLE(name) __guest_handle_ ## name +#define XEN_GUEST_HANDLE(name) __XEN_GUEST_HANDLE(name) +#define XEN_GUEST_HANDLE_PARAM(name) XEN_GUEST_HANDLE(name) +#define set_xen_guest_handle_raw(hnd, val) do { (hnd).p = val; } while (0) +#define set_xen_guest_handle(hnd, val) set_xen_guest_handle_raw(hnd, val) + +#if defined(__i386__) +# ifdef __XEN__ +__DeFiNe__ __DECL_REG_LO8(which) uint32_t e ## which ## x +__DeFiNe__ __DECL_REG_LO16(name) union { uint32_t e ## name; } +# endif +#include "xen-x86_32.h" +# ifdef __XEN__ +__UnDeF__ __DECL_REG_LO8 +__UnDeF__ __DECL_REG_LO16 +__DeFiNe__ __DECL_REG_LO8(which) e ## which ## x +__DeFiNe__ __DECL_REG_LO16(name) e ## name +# endif +#elif defined(__x86_64__) +#include "xen-x86_64.h" +#endif + +#ifndef __ASSEMBLY__ +typedef unsigned long xen_pfn_t; +#define PRI_xen_pfn "lx" +#define PRIu_xen_pfn "lu" +#endif + +#define XEN_HAVE_PV_GUEST_ENTRY 1 + +#define XEN_HAVE_PV_UPCALL_MASK 1 + +/* + * `incontents 200 segdesc Segment Descriptor Tables + */ +/* + * ` enum neg_errnoval + * ` HYPERVISOR_set_gdt(const xen_pfn_t frames[], unsigned int entries); + * ` + */ +/* + * A number of GDT entries are reserved by Xen. These are not situated at the + * start of the GDT because some stupid OSes export hard-coded selector values + * in their ABI. These hard-coded values are always near the start of the GDT, + * so Xen places itself out of the way, at the far end of the GDT. + * + * NB The LDT is set using the MMUEXT_SET_LDT op of HYPERVISOR_mmuext_op + */ +#define FIRST_RESERVED_GDT_PAGE 14 +#define FIRST_RESERVED_GDT_BYTE (FIRST_RESERVED_GDT_PAGE * 4096) +#define FIRST_RESERVED_GDT_ENTRY (FIRST_RESERVED_GDT_BYTE / 8) + + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_update_descriptor(u64 pa, u64 desc); + * ` + * ` @pa The machine physical address of the descriptor to + * ` update. Must be either a descriptor page or writable. + * ` @desc The descriptor value to update, in the same format as a + * ` native descriptor table entry. + */ + +/* Maximum number of virtual CPUs in legacy multi-processor guests. */ +#define XEN_LEGACY_MAX_VCPUS 32 + +#ifndef __ASSEMBLY__ + +typedef unsigned long xen_ulong_t; +#define PRI_xen_ulong "lx" + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_stack_switch(unsigned long ss, unsigned long esp); + * ` + * Sets the stack segment and pointer for the current vcpu. + */ + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_set_trap_table(const struct trap_info traps[]); + * ` + */ +/* + * Send an array of these to HYPERVISOR_set_trap_table(). + * Terminate the array with a sentinel entry, with traps[].address==0. + * The privilege level specifies which modes may enter a trap via a software + * interrupt. On x86/64, since rings 1 and 2 are unavailable, we allocate + * privilege levels as follows: + * Level == 0: Noone may enter + * Level == 1: Kernel may enter + * Level == 2: Kernel may enter + * Level == 3: Everyone may enter + * + * Note: For compatibility with kernels not setting up exception handlers + * early enough, Xen will avoid trying to inject #GP (and hence crash + * the domain) when an RDMSR would require this, but no handler was + * set yet. The precise conditions are implementation specific, and + * new code may not rely on such behavior anyway. + */ +#define TI_GET_DPL(_ti) ((_ti)->flags & 3) +#define TI_GET_IF(_ti) ((_ti)->flags & 4) +#define TI_SET_DPL(_ti,_dpl) ((_ti)->flags |= (_dpl)) +#define TI_SET_IF(_ti,_if) ((_ti)->flags |= ((!!(_if))<<2)) +struct trap_info { + uint8_t vector; /* exception vector */ + uint8_t flags; /* 0-3: privilege level; 4: clear event enable? */ + uint16_t cs; /* code selector */ + unsigned long address; /* code offset */ +}; +typedef struct trap_info trap_info_t; +DEFINE_XEN_GUEST_HANDLE(trap_info_t); + +typedef uint64_t tsc_timestamp_t; /* RDTSC timestamp */ + +/* + * The following is all CPU context. Note that the fpu_ctxt block is filled + * in by FXSAVE if the CPU has feature FXSR; otherwise FSAVE is used. + * + * Also note that when calling DOMCTL_setvcpucontext for HVM guests, not all + * information in this structure is updated, the fields read include: fpu_ctxt + * (if VGCT_I387_VALID is set), flags, user_regs and debugreg[*]. + * + * Note: VCPUOP_initialise for HVM guests is non-symetric with + * DOMCTL_setvcpucontext, and uses struct vcpu_hvm_context from hvm/hvm_vcpu.h + */ +struct vcpu_guest_context { + /* FPU registers come first so they can be aligned for FXSAVE/FXRSTOR. */ + struct { char x[512]; } fpu_ctxt; /* User-level FPU registers */ +#define VGCF_I387_VALID (1<<0) +#define VGCF_IN_KERNEL (1<<2) +#define _VGCF_i387_valid 0 +#define VGCF_i387_valid (1<<_VGCF_i387_valid) +#define _VGCF_in_kernel 2 +#define VGCF_in_kernel (1<<_VGCF_in_kernel) +#define _VGCF_failsafe_disables_events 3 +#define VGCF_failsafe_disables_events (1<<_VGCF_failsafe_disables_events) +#define _VGCF_syscall_disables_events 4 +#define VGCF_syscall_disables_events (1<<_VGCF_syscall_disables_events) +#define _VGCF_online 5 +#define VGCF_online (1<<_VGCF_online) + unsigned long flags; /* VGCF_* flags */ + struct cpu_user_regs user_regs; /* User-level CPU registers */ + struct trap_info trap_ctxt[256]; /* Virtual IDT */ + unsigned long ldt_base, ldt_ents; /* LDT (linear address, # ents) */ + unsigned long gdt_frames[16], gdt_ents; /* GDT (machine frames, # ents) */ + unsigned long kernel_ss, kernel_sp; /* Virtual TSS (only SS1/SP1) */ + /* NB. User pagetable on x86/64 is placed in ctrlreg[1]. */ + unsigned long ctrlreg[8]; /* CR0-CR7 (control registers) */ + unsigned long debugreg[8]; /* DB0-DB7 (debug registers) */ +#ifdef __i386__ + unsigned long event_callback_cs; /* CS:EIP of event callback */ + unsigned long event_callback_eip; + unsigned long failsafe_callback_cs; /* CS:EIP of failsafe callback */ + unsigned long failsafe_callback_eip; +#else + unsigned long event_callback_eip; + unsigned long failsafe_callback_eip; +#ifdef __XEN__ + union { + unsigned long syscall_callback_eip; + struct { + unsigned int event_callback_cs; /* compat CS of event cb */ + unsigned int failsafe_callback_cs; /* compat CS of failsafe cb */ + }; + }; +#else + unsigned long syscall_callback_eip; +#endif +#endif + unsigned long vm_assist; /* VMASST_TYPE_* bitmap */ +#ifdef __x86_64__ + /* Segment base addresses. */ + uint64_t fs_base; + uint64_t gs_base_kernel; + uint64_t gs_base_user; +#endif +}; +typedef struct vcpu_guest_context vcpu_guest_context_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t); + +struct arch_shared_info { + /* + * Number of valid entries in the p2m table(s) anchored at + * pfn_to_mfn_frame_list_list and/or p2m_vaddr. + */ + unsigned long max_pfn; + /* + * Frame containing list of mfns containing list of mfns containing p2m. + * A value of 0 indicates it has not yet been set up, ~0 indicates it has + * been set to invalid e.g. due to the p2m being too large for the 3-level + * p2m tree. In this case the linear mapper p2m list anchored at p2m_vaddr + * is to be used. + */ + xen_pfn_t pfn_to_mfn_frame_list_list; + unsigned long nmi_reason; + /* + * Following three fields are valid if p2m_cr3 contains a value different + * from 0. + * p2m_cr3 is the root of the address space where p2m_vaddr is valid. + * p2m_cr3 is in the same format as a cr3 value in the vcpu register state + * and holds the folded machine frame number (via xen_pfn_to_cr3) of a + * L3 or L4 page table. + * p2m_vaddr holds the virtual address of the linear p2m list. All entries + * in the range [0...max_pfn[ are accessible via this pointer. + * p2m_generation will be incremented by the guest before and after each + * change of the mappings of the p2m list. p2m_generation starts at 0 and + * a value with the least significant bit set indicates that a mapping + * update is in progress. This allows guest external software (e.g. in Dom0) + * to verify that read mappings are consistent and whether they have changed + * since the last check. + * Modifying a p2m element in the linear p2m list is allowed via an atomic + * write only. + */ + unsigned long p2m_cr3; /* cr3 value of the p2m address space */ + unsigned long p2m_vaddr; /* virtual address of the p2m list */ + unsigned long p2m_generation; /* generation count of p2m mapping */ +#ifdef __i386__ + /* There's no room for this field in the generic structure. */ + uint32_t wc_sec_hi; +#endif +}; +typedef struct arch_shared_info arch_shared_info_t; + +#if defined(__XEN__) || defined(__XEN_TOOLS__) +/* + * struct xen_arch_domainconfig's ABI is covered by + * XEN_DOMCTL_INTERFACE_VERSION. + */ +struct xen_arch_domainconfig { +#define _XEN_X86_EMU_LAPIC 0 +#define XEN_X86_EMU_LAPIC (1U<<_XEN_X86_EMU_LAPIC) +#define _XEN_X86_EMU_HPET 1 +#define XEN_X86_EMU_HPET (1U<<_XEN_X86_EMU_HPET) +#define _XEN_X86_EMU_PM 2 +#define XEN_X86_EMU_PM (1U<<_XEN_X86_EMU_PM) +#define _XEN_X86_EMU_RTC 3 +#define XEN_X86_EMU_RTC (1U<<_XEN_X86_EMU_RTC) +#define _XEN_X86_EMU_IOAPIC 4 +#define XEN_X86_EMU_IOAPIC (1U<<_XEN_X86_EMU_IOAPIC) +#define _XEN_X86_EMU_PIC 5 +#define XEN_X86_EMU_PIC (1U<<_XEN_X86_EMU_PIC) +#define _XEN_X86_EMU_VGA 6 +#define XEN_X86_EMU_VGA (1U<<_XEN_X86_EMU_VGA) +#define _XEN_X86_EMU_IOMMU 7 +#define XEN_X86_EMU_IOMMU (1U<<_XEN_X86_EMU_IOMMU) +#define _XEN_X86_EMU_PIT 8 +#define XEN_X86_EMU_PIT (1U<<_XEN_X86_EMU_PIT) +#define _XEN_X86_EMU_USE_PIRQ 9 +#define XEN_X86_EMU_USE_PIRQ (1U<<_XEN_X86_EMU_USE_PIRQ) +#define _XEN_X86_EMU_VPCI 10 +#define XEN_X86_EMU_VPCI (1U<<_XEN_X86_EMU_VPCI) + +#define XEN_X86_EMU_ALL (XEN_X86_EMU_LAPIC | XEN_X86_EMU_HPET | \ + XEN_X86_EMU_PM | XEN_X86_EMU_RTC | \ + XEN_X86_EMU_IOAPIC | XEN_X86_EMU_PIC | \ + XEN_X86_EMU_VGA | XEN_X86_EMU_IOMMU | \ + XEN_X86_EMU_PIT | XEN_X86_EMU_USE_PIRQ |\ + XEN_X86_EMU_VPCI) + uint32_t emulation_flags; + +/* + * Select whether to use a relaxed behavior for accesses to MSRs not explicitly + * handled by Xen instead of injecting a #GP to the guest. Note this option + * doesn't allow the guest to read or write to the underlying MSR. + */ +#define XEN_X86_MSR_RELAXED (1u << 0) + uint32_t misc_flags; +}; + +/* Location of online VCPU bitmap. */ +#define XEN_ACPI_CPU_MAP 0xaf00 +#define XEN_ACPI_CPU_MAP_LEN ((HVM_MAX_VCPUS + 7) / 8) + +/* GPE0 bit set during CPU hotplug */ +#define XEN_ACPI_GPE0_CPUHP_BIT 2 +#endif + +/* + * Representations of architectural CPUID and MSR information. Used as the + * serialised version of Xen's internal representation. + */ +typedef struct xen_cpuid_leaf { +#define XEN_CPUID_NO_SUBLEAF 0xffffffffu + uint32_t leaf, subleaf; + uint32_t a, b, c, d; +} xen_cpuid_leaf_t; +DEFINE_XEN_GUEST_HANDLE(xen_cpuid_leaf_t); + +typedef struct xen_msr_entry { + uint32_t idx; + uint32_t flags; /* Reserved MBZ. */ + uint64_t val; +} xen_msr_entry_t; +DEFINE_XEN_GUEST_HANDLE(xen_msr_entry_t); + +#endif /* !__ASSEMBLY__ */ + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_fpu_taskswitch(int set); + * ` + * Sets (if set!=0) or clears (if set==0) CR0.TS. + */ + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_set_debugreg(int regno, unsigned long value); + * + * ` unsigned long + * ` HYPERVISOR_get_debugreg(int regno); + * For 0<=reg<=7, returns the debug register value. + * For other values of reg, returns ((unsigned long)-EINVAL). + * (Unfortunately, this interface is defective.) + */ + +/* + * Prefix forces emulation of some non-trapping instructions. + * Currently only CPUID. + */ +#ifdef __ASSEMBLY__ +#define XEN_EMULATE_PREFIX .byte 0x0f,0x0b,0x78,0x65,0x6e ; +#define XEN_CPUID XEN_EMULATE_PREFIX cpuid +#else +#define XEN_EMULATE_PREFIX ".byte 0x0f,0x0b,0x78,0x65,0x6e ; " +#define XEN_CPUID XEN_EMULATE_PREFIX "cpuid" +#endif + +/* + * Debug console IO port, also called "port E9 hack". Each character written + * to this IO port will be printed on the hypervisor console, subject to log + * level restrictions. + */ +#define XEN_HVM_DEBUGCONS_IOPORT 0xe9 + +#endif /* __XEN_PUBLIC_ARCH_X86_XEN_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/event_channel.h b/include/hw/xen/interface/event_channel.h new file mode 100644 index 0000000000..73c9f38ce1 --- /dev/null +++ b/include/hw/xen/interface/event_channel.h @@ -0,0 +1,388 @@ +/****************************************************************************** + * event_channel.h + * + * Event channels between domains. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2003-2004, K A Fraser. + */ + +#ifndef __XEN_PUBLIC_EVENT_CHANNEL_H__ +#define __XEN_PUBLIC_EVENT_CHANNEL_H__ + +#include "xen.h" + +/* + * `incontents 150 evtchn Event Channels + * + * Event channels are the basic primitive provided by Xen for event + * notifications. An event is the Xen equivalent of a hardware + * interrupt. They essentially store one bit of information, the event + * of interest is signalled by transitioning this bit from 0 to 1. + * + * Notifications are received by a guest via an upcall from Xen, + * indicating when an event arrives (setting the bit). Further + * notifications are masked until the bit is cleared again (therefore, + * guests must check the value of the bit after re-enabling event + * delivery to ensure no missed notifications). + * + * Event notifications can be masked by setting a flag; this is + * equivalent to disabling interrupts and can be used to ensure + * atomicity of certain operations in the guest kernel. + * + * Event channels are represented by the evtchn_* fields in + * struct shared_info and struct vcpu_info. + */ + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_event_channel_op(enum event_channel_op cmd, void *args) + * ` + * @cmd == EVTCHNOP_* (event-channel operation). + * @args == struct evtchn_* Operation-specific extra arguments (NULL if none). + */ + +/* ` enum event_channel_op { // EVTCHNOP_* => struct evtchn_* */ +#define EVTCHNOP_bind_interdomain 0 +#define EVTCHNOP_bind_virq 1 +#define EVTCHNOP_bind_pirq 2 +#define EVTCHNOP_close 3 +#define EVTCHNOP_send 4 +#define EVTCHNOP_status 5 +#define EVTCHNOP_alloc_unbound 6 +#define EVTCHNOP_bind_ipi 7 +#define EVTCHNOP_bind_vcpu 8 +#define EVTCHNOP_unmask 9 +#define EVTCHNOP_reset 10 +#define EVTCHNOP_init_control 11 +#define EVTCHNOP_expand_array 12 +#define EVTCHNOP_set_priority 13 +#ifdef __XEN__ +#define EVTCHNOP_reset_cont 14 +#endif +/* ` } */ + +typedef uint32_t evtchn_port_t; +DEFINE_XEN_GUEST_HANDLE(evtchn_port_t); + +/* + * EVTCHNOP_alloc_unbound: Allocate a port in domain and mark as + * accepting interdomain bindings from domain . A fresh port + * is allocated in and returned as . + * NOTES: + * 1. If the caller is unprivileged then must be DOMID_SELF. + * 2. may be DOMID_SELF, allowing loopback connections. + */ +struct evtchn_alloc_unbound { + /* IN parameters */ + domid_t dom, remote_dom; + /* OUT parameters */ + evtchn_port_t port; +}; +typedef struct evtchn_alloc_unbound evtchn_alloc_unbound_t; + +/* + * EVTCHNOP_bind_interdomain: Construct an interdomain event channel between + * the calling domain and . must identify + * a port that is unbound and marked as accepting bindings from the calling + * domain. A fresh port is allocated in the calling domain and returned as + * . + * + * In case the peer domain has already tried to set our event channel + * pending, before it was bound, EVTCHNOP_bind_interdomain always sets + * the local event channel pending. + * + * The usual pattern of use, in the guest's upcall (or subsequent + * handler) is as follows: (Re-enable the event channel for subsequent + * signalling and then) check for the existence of whatever condition + * is being waited for by other means, and take whatever action is + * needed (if any). + * + * NOTES: + * 1. may be DOMID_SELF, allowing loopback connections. + */ +struct evtchn_bind_interdomain { + /* IN parameters. */ + domid_t remote_dom; + evtchn_port_t remote_port; + /* OUT parameters. */ + evtchn_port_t local_port; +}; +typedef struct evtchn_bind_interdomain evtchn_bind_interdomain_t; + +/* + * EVTCHNOP_bind_virq: Bind a local event channel to VIRQ on specified + * vcpu. + * NOTES: + * 1. Virtual IRQs are classified as per-vcpu or global. See the VIRQ list + * in xen.h for the classification of each VIRQ. + * 2. Global VIRQs must be allocated on VCPU0 but can subsequently be + * re-bound via EVTCHNOP_bind_vcpu. + * 3. Per-vcpu VIRQs may be bound to at most one event channel per vcpu. + * The allocated event channel is bound to the specified vcpu and the + * binding cannot be changed. + */ +struct evtchn_bind_virq { + /* IN parameters. */ + uint32_t virq; /* enum virq */ + uint32_t vcpu; + /* OUT parameters. */ + evtchn_port_t port; +}; +typedef struct evtchn_bind_virq evtchn_bind_virq_t; + +/* + * EVTCHNOP_bind_pirq: Bind a local event channel to a real IRQ (PIRQ ). + * NOTES: + * 1. A physical IRQ may be bound to at most one event channel per domain. + * 2. Only a sufficiently-privileged domain may bind to a physical IRQ. + */ +struct evtchn_bind_pirq { + /* IN parameters. */ + uint32_t pirq; +#define BIND_PIRQ__WILL_SHARE 1 + uint32_t flags; /* BIND_PIRQ__* */ + /* OUT parameters. */ + evtchn_port_t port; +}; +typedef struct evtchn_bind_pirq evtchn_bind_pirq_t; + +/* + * EVTCHNOP_bind_ipi: Bind a local event channel to receive events. + * NOTES: + * 1. The allocated event channel is bound to the specified vcpu. The binding + * may not be changed. + */ +struct evtchn_bind_ipi { + uint32_t vcpu; + /* OUT parameters. */ + evtchn_port_t port; +}; +typedef struct evtchn_bind_ipi evtchn_bind_ipi_t; + +/* + * EVTCHNOP_close: Close a local event channel . If the channel is + * interdomain then the remote end is placed in the unbound state + * (EVTCHNSTAT_unbound), awaiting a new connection. + */ +struct evtchn_close { + /* IN parameters. */ + evtchn_port_t port; +}; +typedef struct evtchn_close evtchn_close_t; + +/* + * EVTCHNOP_send: Send an event to the remote end of the channel whose local + * endpoint is . + */ +struct evtchn_send { + /* IN parameters. */ + evtchn_port_t port; +}; +typedef struct evtchn_send evtchn_send_t; + +/* + * EVTCHNOP_status: Get the current status of the communication channel which + * has an endpoint at . + * NOTES: + * 1. may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may obtain the status of an event + * channel for which is not DOMID_SELF. + */ +struct evtchn_status { + /* IN parameters */ + domid_t dom; + evtchn_port_t port; + /* OUT parameters */ +#define EVTCHNSTAT_closed 0 /* Channel is not in use. */ +#define EVTCHNSTAT_unbound 1 /* Channel is waiting interdom connection.*/ +#define EVTCHNSTAT_interdomain 2 /* Channel is connected to remote domain. */ +#define EVTCHNSTAT_pirq 3 /* Channel is bound to a phys IRQ line. */ +#define EVTCHNSTAT_virq 4 /* Channel is bound to a virtual IRQ line */ +#define EVTCHNSTAT_ipi 5 /* Channel is bound to a virtual IPI line */ + uint32_t status; + uint32_t vcpu; /* VCPU to which this channel is bound. */ + union { + struct { + domid_t dom; + } unbound; /* EVTCHNSTAT_unbound */ + struct { + domid_t dom; + evtchn_port_t port; + } interdomain; /* EVTCHNSTAT_interdomain */ + uint32_t pirq; /* EVTCHNSTAT_pirq */ + uint32_t virq; /* EVTCHNSTAT_virq */ + } u; +}; +typedef struct evtchn_status evtchn_status_t; + +/* + * EVTCHNOP_bind_vcpu: Specify which vcpu a channel should notify when an + * event is pending. + * NOTES: + * 1. IPI-bound channels always notify the vcpu specified at bind time. + * This binding cannot be changed. + * 2. Per-VCPU VIRQ channels always notify the vcpu specified at bind time. + * This binding cannot be changed. + * 3. All other channels notify vcpu0 by default. This default is set when + * the channel is allocated (a port that is freed and subsequently reused + * has its binding reset to vcpu0). + */ +struct evtchn_bind_vcpu { + /* IN parameters. */ + evtchn_port_t port; + uint32_t vcpu; +}; +typedef struct evtchn_bind_vcpu evtchn_bind_vcpu_t; + +/* + * EVTCHNOP_unmask: Unmask the specified local event-channel port and deliver + * a notification to the appropriate VCPU if an event is pending. + */ +struct evtchn_unmask { + /* IN parameters. */ + evtchn_port_t port; +}; +typedef struct evtchn_unmask evtchn_unmask_t; + +/* + * EVTCHNOP_reset: Close all event channels associated with specified domain. + * NOTES: + * 1. may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may specify other than DOMID_SELF. + * 3. Destroys all control blocks and event array, resets event channel + * operations to 2-level ABI if called with == DOMID_SELF and FIFO + * ABI was used. Guests should not bind events during EVTCHNOP_reset call + * as these events are likely to be lost. + */ +struct evtchn_reset { + /* IN parameters. */ + domid_t dom; +}; +typedef struct evtchn_reset evtchn_reset_t; + +/* + * EVTCHNOP_init_control: initialize the control block for the FIFO ABI. + * + * Note: any events that are currently pending will not be resent and + * will be lost. Guests should call this before binding any event to + * avoid losing any events. + */ +struct evtchn_init_control { + /* IN parameters. */ + uint64_t control_gfn; + uint32_t offset; + uint32_t vcpu; + /* OUT parameters. */ + uint8_t link_bits; + uint8_t _pad[7]; +}; +typedef struct evtchn_init_control evtchn_init_control_t; + +/* + * EVTCHNOP_expand_array: add an additional page to the event array. + */ +struct evtchn_expand_array { + /* IN parameters. */ + uint64_t array_gfn; +}; +typedef struct evtchn_expand_array evtchn_expand_array_t; + +/* + * EVTCHNOP_set_priority: set the priority for an event channel. + */ +struct evtchn_set_priority { + /* IN parameters. */ + evtchn_port_t port; + uint32_t priority; +}; +typedef struct evtchn_set_priority evtchn_set_priority_t; + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_event_channel_op_compat(struct evtchn_op *op) + * ` + * Superceded by new event_channel_op() hypercall since 0x00030202. + */ +struct evtchn_op { + uint32_t cmd; /* enum event_channel_op */ + union { + evtchn_alloc_unbound_t alloc_unbound; + evtchn_bind_interdomain_t bind_interdomain; + evtchn_bind_virq_t bind_virq; + evtchn_bind_pirq_t bind_pirq; + evtchn_bind_ipi_t bind_ipi; + evtchn_close_t close; + evtchn_send_t send; + evtchn_status_t status; + evtchn_bind_vcpu_t bind_vcpu; + evtchn_unmask_t unmask; + } u; +}; +typedef struct evtchn_op evtchn_op_t; +DEFINE_XEN_GUEST_HANDLE(evtchn_op_t); + +/* + * 2-level ABI + */ + +#define EVTCHN_2L_NR_CHANNELS (sizeof(xen_ulong_t) * sizeof(xen_ulong_t) * 64) + +/* + * FIFO ABI + */ + +/* Events may have priorities from 0 (highest) to 15 (lowest). */ +#define EVTCHN_FIFO_PRIORITY_MAX 0 +#define EVTCHN_FIFO_PRIORITY_DEFAULT 7 +#define EVTCHN_FIFO_PRIORITY_MIN 15 + +#define EVTCHN_FIFO_MAX_QUEUES (EVTCHN_FIFO_PRIORITY_MIN + 1) + +typedef uint32_t event_word_t; + +#define EVTCHN_FIFO_PENDING 31 +#define EVTCHN_FIFO_MASKED 30 +#define EVTCHN_FIFO_LINKED 29 +#define EVTCHN_FIFO_BUSY 28 + +#define EVTCHN_FIFO_LINK_BITS 17 +#define EVTCHN_FIFO_LINK_MASK ((1 << EVTCHN_FIFO_LINK_BITS) - 1) + +#define EVTCHN_FIFO_NR_CHANNELS (1 << EVTCHN_FIFO_LINK_BITS) + +struct evtchn_fifo_control_block { + uint32_t ready; + uint32_t _rsvd; + uint32_t head[EVTCHN_FIFO_MAX_QUEUES]; +}; +typedef struct evtchn_fifo_control_block evtchn_fifo_control_block_t; + +#endif /* __XEN_PUBLIC_EVENT_CHANNEL_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/features.h b/include/hw/xen/interface/features.h new file mode 100644 index 0000000000..9ee2f760ef --- /dev/null +++ b/include/hw/xen/interface/features.h @@ -0,0 +1,143 @@ +/****************************************************************************** + * features.h + * + * Feature flags, reported by XENVER_get_features. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2006, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_FEATURES_H__ +#define __XEN_PUBLIC_FEATURES_H__ + +/* + * `incontents 200 elfnotes_features XEN_ELFNOTE_FEATURES + * + * The list of all the features the guest supports. They are set by + * parsing the XEN_ELFNOTE_FEATURES and XEN_ELFNOTE_SUPPORTED_FEATURES + * string. The format is the feature names (as given here without the + * "XENFEAT_" prefix) separated by '|' characters. + * If a feature is required for the kernel to function then the feature name + * must be preceded by a '!' character. + * + * Note that if XEN_ELFNOTE_SUPPORTED_FEATURES is used, then in the + * XENFEAT_dom0 MUST be set if the guest is to be booted as dom0, + */ + +/* + * If set, the guest does not need to write-protect its pagetables, and can + * update them via direct writes. + */ +#define XENFEAT_writable_page_tables 0 + +/* + * If set, the guest does not need to write-protect its segment descriptor + * tables, and can update them via direct writes. + */ +#define XENFEAT_writable_descriptor_tables 1 + +/* + * If set, translation between the guest's 'pseudo-physical' address space + * and the host's machine address space are handled by the hypervisor. In this + * mode the guest does not need to perform phys-to/from-machine translations + * when performing page table operations. + */ +#define XENFEAT_auto_translated_physmap 2 + +/* If set, the guest is running in supervisor mode (e.g., x86 ring 0). */ +#define XENFEAT_supervisor_mode_kernel 3 + +/* + * If set, the guest does not need to allocate x86 PAE page directories + * below 4GB. This flag is usually implied by auto_translated_physmap. + */ +#define XENFEAT_pae_pgdir_above_4gb 4 + +/* x86: Does this Xen host support the MMU_PT_UPDATE_PRESERVE_AD hypercall? */ +#define XENFEAT_mmu_pt_update_preserve_ad 5 + +/* x86: Does this Xen host support the MMU_{CLEAR,COPY}_PAGE hypercall? */ +#define XENFEAT_highmem_assist 6 + +/* + * If set, GNTTABOP_map_grant_ref honors flags to be placed into guest kernel + * available pte bits. + */ +#define XENFEAT_gnttab_map_avail_bits 7 + +/* x86: Does this Xen host support the HVM callback vector type? */ +#define XENFEAT_hvm_callback_vector 8 + +/* x86: pvclock algorithm is safe to use on HVM */ +#define XENFEAT_hvm_safe_pvclock 9 + +/* x86: pirq can be used by HVM guests */ +#define XENFEAT_hvm_pirqs 10 + +/* operation as Dom0 is supported */ +#define XENFEAT_dom0 11 + +/* Xen also maps grant references at pfn = mfn. + * This feature flag is deprecated and should not be used. +#define XENFEAT_grant_map_identity 12 + */ + +/* Guest can use XENMEMF_vnode to specify virtual node for memory op. */ +#define XENFEAT_memory_op_vnode_supported 13 + +/* arm: Hypervisor supports ARM SMC calling convention. */ +#define XENFEAT_ARM_SMCCC_supported 14 + +/* + * x86/PVH: If set, ACPI RSDP can be placed at any address. Otherwise RSDP + * must be located in lower 1MB, as required by ACPI Specification for IA-PC + * systems. + * This feature flag is only consulted if XEN_ELFNOTE_GUEST_OS contains + * the "linux" string. + */ +#define XENFEAT_linux_rsdp_unrestricted 15 + +/* + * A direct-mapped (or 1:1 mapped) domain is a domain for which its + * local pages have gfn == mfn. If a domain is direct-mapped, + * XENFEAT_direct_mapped is set; otherwise XENFEAT_not_direct_mapped + * is set. + * + * If neither flag is set (e.g. older Xen releases) the assumptions are: + * - not auto_translated domains (x86 only) are always direct-mapped + * - on x86, auto_translated domains are not direct-mapped + * - on ARM, Dom0 is direct-mapped, DomUs are not + */ +#define XENFEAT_not_direct_mapped 16 +#define XENFEAT_direct_mapped 17 + +#define XENFEAT_NR_SUBMAPS 1 + +#endif /* __XEN_PUBLIC_FEATURES_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/grant_table.h b/include/hw/xen/interface/grant_table.h index 2af0cbdde3..7934d7b718 100644 --- a/include/hw/xen/interface/grant_table.h +++ b/include/hw/xen/interface/grant_table.h @@ -28,9 +28,659 @@ #ifndef __XEN_PUBLIC_GRANT_TABLE_H__ #define __XEN_PUBLIC_GRANT_TABLE_H__ +#include "xen.h" + +/* + * `incontents 150 gnttab Grant Tables + * + * Xen's grant tables provide a generic mechanism to memory sharing + * between domains. This shared memory interface underpins the split + * device drivers for block and network IO. + * + * Each domain has its own grant table. This is a data structure that + * is shared with Xen; it allows the domain to tell Xen what kind of + * permissions other domains have on its pages. Entries in the grant + * table are identified by grant references. A grant reference is an + * integer, which indexes into the grant table. It acts as a + * capability which the grantee can use to perform operations on the + * granter's memory. + * + * This capability-based system allows shared-memory communications + * between unprivileged domains. A grant reference also encapsulates + * the details of a shared page, removing the need for a domain to + * know the real machine address of a page it is sharing. This makes + * it possible to share memory correctly with domains running in + * fully virtualised memory. + */ + +/*********************************** + * GRANT TABLE REPRESENTATION + */ + +/* Some rough guidelines on accessing and updating grant-table entries + * in a concurrency-safe manner. For more information, Linux contains a + * reference implementation for guest OSes (drivers/xen/grant_table.c, see + * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=blob;f=drivers/xen/grant-table.c;hb=HEAD + * + * NB. WMB is a no-op on current-generation x86 processors. However, a + * compiler barrier will still be required. + * + * Introducing a valid entry into the grant table: + * 1. Write ent->domid. + * 2. Write ent->frame: + * GTF_permit_access: Frame to which access is permitted. + * GTF_accept_transfer: Pseudo-phys frame slot being filled by new + * frame, or zero if none. + * 3. Write memory barrier (WMB). + * 4. Write ent->flags, inc. valid type. + * + * Invalidating an unused GTF_permit_access entry: + * 1. flags = ent->flags. + * 2. Observe that !(flags & (GTF_reading|GTF_writing)). + * 3. Check result of SMP-safe CMPXCHG(&ent->flags, flags, 0). + * NB. No need for WMB as reuse of entry is control-dependent on success of + * step 3, and all architectures guarantee ordering of ctrl-dep writes. + * + * Invalidating an in-use GTF_permit_access entry: + * This cannot be done directly. Request assistance from the domain controller + * which can set a timeout on the use of a grant entry and take necessary + * action. (NB. This is not yet implemented!). + * + * Invalidating an unused GTF_accept_transfer entry: + * 1. flags = ent->flags. + * 2. Observe that !(flags & GTF_transfer_committed). [*] + * 3. Check result of SMP-safe CMPXCHG(&ent->flags, flags, 0). + * NB. No need for WMB as reuse of entry is control-dependent on success of + * step 3, and all architectures guarantee ordering of ctrl-dep writes. + * [*] If GTF_transfer_committed is set then the grant entry is 'committed'. + * The guest must /not/ modify the grant entry until the address of the + * transferred frame is written. It is safe for the guest to spin waiting + * for this to occur (detect by observing GTF_transfer_completed in + * ent->flags). + * + * Invalidating a committed GTF_accept_transfer entry: + * 1. Wait for (ent->flags & GTF_transfer_completed). + * + * Changing a GTF_permit_access from writable to read-only: + * Use SMP-safe CMPXCHG to set GTF_readonly, while checking !GTF_writing. + * + * Changing a GTF_permit_access from read-only to writable: + * Use SMP-safe bit-setting instruction. + */ + /* * Reference to a grant entry in a specified domain's grant table. */ typedef uint32_t grant_ref_t; +/* + * A grant table comprises a packed array of grant entries in one or more + * page frames shared between Xen and a guest. + * [XEN]: This field is written by Xen and read by the sharing guest. + * [GST]: This field is written by the guest and read by Xen. + */ + +/* + * Version 1 of the grant table entry structure is maintained largely for + * backwards compatibility. New guests are recommended to support using + * version 2 to overcome version 1 limitations, but to default to version 1. + */ +#if __XEN_INTERFACE_VERSION__ < 0x0003020a +#define grant_entry_v1 grant_entry +#define grant_entry_v1_t grant_entry_t +#endif +struct grant_entry_v1 { + /* GTF_xxx: various type and flag information. [XEN,GST] */ + uint16_t flags; + /* The domain being granted foreign privileges. [GST] */ + domid_t domid; + /* + * GTF_permit_access: GFN that @domid is allowed to map and access. [GST] + * GTF_accept_transfer: GFN that @domid is allowed to transfer into. [GST] + * GTF_transfer_completed: MFN whose ownership transferred by @domid + * (non-translated guests only). [XEN] + */ + uint32_t frame; +}; +typedef struct grant_entry_v1 grant_entry_v1_t; + +/* The first few grant table entries will be preserved across grant table + * version changes and may be pre-populated at domain creation by tools. + */ +#define GNTTAB_NR_RESERVED_ENTRIES 8 +#define GNTTAB_RESERVED_CONSOLE 0 +#define GNTTAB_RESERVED_XENSTORE 1 + +/* + * Type of grant entry. + * GTF_invalid: This grant entry grants no privileges. + * GTF_permit_access: Allow @domid to map/access @frame. + * GTF_accept_transfer: Allow @domid to transfer ownership of one page frame + * to this guest. Xen writes the page number to @frame. + * GTF_transitive: Allow @domid to transitively access a subrange of + * @trans_grant in @trans_domid. No mappings are allowed. + */ +#define GTF_invalid (0U<<0) +#define GTF_permit_access (1U<<0) +#define GTF_accept_transfer (2U<<0) +#define GTF_transitive (3U<<0) +#define GTF_type_mask (3U<<0) + +/* + * Subflags for GTF_permit_access and GTF_transitive. + * GTF_readonly: Restrict @domid to read-only mappings and accesses. [GST] + * GTF_reading: Grant entry is currently mapped for reading by @domid. [XEN] + * GTF_writing: Grant entry is currently mapped for writing by @domid. [XEN] + * Further subflags for GTF_permit_access only. + * GTF_PAT, GTF_PWT, GTF_PCD: (x86) cache attribute flags to be used for + * mappings of the grant [GST] + * GTF_sub_page: Grant access to only a subrange of the page. @domid + * will only be allowed to copy from the grant, and not + * map it. [GST] + */ +#define _GTF_readonly (2) +#define GTF_readonly (1U<<_GTF_readonly) +#define _GTF_reading (3) +#define GTF_reading (1U<<_GTF_reading) +#define _GTF_writing (4) +#define GTF_writing (1U<<_GTF_writing) +#define _GTF_PWT (5) +#define GTF_PWT (1U<<_GTF_PWT) +#define _GTF_PCD (6) +#define GTF_PCD (1U<<_GTF_PCD) +#define _GTF_PAT (7) +#define GTF_PAT (1U<<_GTF_PAT) +#define _GTF_sub_page (8) +#define GTF_sub_page (1U<<_GTF_sub_page) + +/* + * Subflags for GTF_accept_transfer: + * GTF_transfer_committed: Xen sets this flag to indicate that it is committed + * to transferring ownership of a page frame. When a guest sees this flag + * it must /not/ modify the grant entry until GTF_transfer_completed is + * set by Xen. + * GTF_transfer_completed: It is safe for the guest to spin-wait on this flag + * after reading GTF_transfer_committed. Xen will always write the frame + * address, followed by ORing this flag, in a timely manner. + */ +#define _GTF_transfer_committed (2) +#define GTF_transfer_committed (1U<<_GTF_transfer_committed) +#define _GTF_transfer_completed (3) +#define GTF_transfer_completed (1U<<_GTF_transfer_completed) + +/* + * Version 2 grant table entries. These fulfil the same role as + * version 1 entries, but can represent more complicated operations. + * Any given domain will have either a version 1 or a version 2 table, + * and every entry in the table will be the same version. + * + * The interface by which domains use grant references does not depend + * on the grant table version in use by the other domain. + */ +#if __XEN_INTERFACE_VERSION__ >= 0x0003020a +/* + * Version 1 and version 2 grant entries share a common prefix. The + * fields of the prefix are documented as part of struct + * grant_entry_v1. + */ +struct grant_entry_header { + uint16_t flags; + domid_t domid; +}; +typedef struct grant_entry_header grant_entry_header_t; + +/* + * Version 2 of the grant entry structure. + */ +union grant_entry_v2 { + grant_entry_header_t hdr; + + /* + * This member is used for V1-style full page grants, where either: + * + * -- hdr.type is GTF_accept_transfer, or + * -- hdr.type is GTF_permit_access and GTF_sub_page is not set. + * + * In that case, the frame field has the same semantics as the + * field of the same name in the V1 entry structure. + */ + struct { + grant_entry_header_t hdr; + uint32_t pad0; + uint64_t frame; + } full_page; + + /* + * If the grant type is GTF_grant_access and GTF_sub_page is set, + * @domid is allowed to access bytes [@page_off,@page_off+@length) + * in frame @frame. + */ + struct { + grant_entry_header_t hdr; + uint16_t page_off; + uint16_t length; + uint64_t frame; + } sub_page; + + /* + * If the grant is GTF_transitive, @domid is allowed to use the + * grant @gref in domain @trans_domid, as if it was the local + * domain. Obviously, the transitive access must be compatible + * with the original grant. + * + * The current version of Xen does not allow transitive grants + * to be mapped. + */ + struct { + grant_entry_header_t hdr; + domid_t trans_domid; + uint16_t pad0; + grant_ref_t gref; + } transitive; + + uint32_t __spacer[4]; /* Pad to a power of two */ +}; +typedef union grant_entry_v2 grant_entry_v2_t; + +typedef uint16_t grant_status_t; + +#endif /* __XEN_INTERFACE_VERSION__ */ + +/*********************************** + * GRANT TABLE QUERIES AND USES + */ + +/* ` enum neg_errnoval + * ` HYPERVISOR_grant_table_op(enum grant_table_op cmd, + * ` void *args, + * ` unsigned int count) + * ` + * + * @args points to an array of a per-command data structure. The array + * has @count members + */ + +/* ` enum grant_table_op { // GNTTABOP_* => struct gnttab_* */ +#define GNTTABOP_map_grant_ref 0 +#define GNTTABOP_unmap_grant_ref 1 +#define GNTTABOP_setup_table 2 +#define GNTTABOP_dump_table 3 +#define GNTTABOP_transfer 4 +#define GNTTABOP_copy 5 +#define GNTTABOP_query_size 6 +#define GNTTABOP_unmap_and_replace 7 +#if __XEN_INTERFACE_VERSION__ >= 0x0003020a +#define GNTTABOP_set_version 8 +#define GNTTABOP_get_status_frames 9 +#define GNTTABOP_get_version 10 +#define GNTTABOP_swap_grant_ref 11 +#define GNTTABOP_cache_flush 12 +#endif /* __XEN_INTERFACE_VERSION__ */ +/* ` } */ + +/* + * Handle to track a mapping created via a grant reference. + */ +typedef uint32_t grant_handle_t; + +/* + * GNTTABOP_map_grant_ref: Map the grant entry (,) for access + * by devices and/or host CPUs. If successful, is a tracking number + * that must be presented later to destroy the mapping(s). On error, + * is a negative status code. + * NOTES: + * 1. If GNTMAP_device_map is specified then is the address + * via which I/O devices may access the granted frame. + * 2. If GNTMAP_host_map is specified then a mapping will be added at + * either a host virtual address in the current address space, or at + * a PTE at the specified machine address. The type of mapping to + * perform is selected through the GNTMAP_contains_pte flag, and the + * address is specified in . + * 3. Mappings should only be destroyed via GNTTABOP_unmap_grant_ref. If a + * host mapping is destroyed by other means then it is *NOT* guaranteed + * to be accounted to the correct grant reference! + */ +struct gnttab_map_grant_ref { + /* IN parameters. */ + uint64_t host_addr; + uint32_t flags; /* GNTMAP_* */ + grant_ref_t ref; + domid_t dom; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ + grant_handle_t handle; + uint64_t dev_bus_addr; +}; +typedef struct gnttab_map_grant_ref gnttab_map_grant_ref_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_map_grant_ref_t); + +/* + * GNTTABOP_unmap_grant_ref: Destroy one or more grant-reference mappings + * tracked by . If or is zero, that + * field is ignored. If non-zero, they must refer to a device/host mapping + * that is tracked by + * NOTES: + * 1. The call may fail in an undefined manner if either mapping is not + * tracked by . + * 3. After executing a batch of unmaps, it is guaranteed that no stale + * mappings will remain in the device or host TLBs. + */ +struct gnttab_unmap_grant_ref { + /* IN parameters. */ + uint64_t host_addr; + uint64_t dev_bus_addr; + grant_handle_t handle; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ +}; +typedef struct gnttab_unmap_grant_ref gnttab_unmap_grant_ref_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_unmap_grant_ref_t); + +/* + * GNTTABOP_setup_table: Set up a grant table for comprising at least + * pages. The frame addresses are written to the . + * Only addresses are written, even if the table is larger. + * NOTES: + * 1. may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may specify != DOMID_SELF. + * 3. Xen may not support more than a single grant-table page per domain. + */ +struct gnttab_setup_table { + /* IN parameters. */ + domid_t dom; + uint32_t nr_frames; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ +#if __XEN_INTERFACE_VERSION__ < 0x00040300 + XEN_GUEST_HANDLE(ulong) frame_list; +#else + XEN_GUEST_HANDLE(xen_pfn_t) frame_list; +#endif +}; +typedef struct gnttab_setup_table gnttab_setup_table_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_setup_table_t); + +/* + * GNTTABOP_dump_table: Dump the contents of the grant table to the + * xen console. Debugging use only. + */ +struct gnttab_dump_table { + /* IN parameters. */ + domid_t dom; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ +}; +typedef struct gnttab_dump_table gnttab_dump_table_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_dump_table_t); + +/* + * GNTTABOP_transfer: Transfer to a foreign domain. The foreign domain + * has previously registered its interest in the transfer via . + * + * Note that, even if the transfer fails, the specified page no longer belongs + * to the calling domain *unless* the error is GNTST_bad_page. + * + * Note further that only PV guests can use this operation. + */ +struct gnttab_transfer { + /* IN parameters. */ + xen_pfn_t mfn; + domid_t domid; + grant_ref_t ref; + /* OUT parameters. */ + int16_t status; +}; +typedef struct gnttab_transfer gnttab_transfer_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_transfer_t); + + +/* + * GNTTABOP_copy: Hypervisor based copy + * source and destinations can be eithers MFNs or, for foreign domains, + * grant references. the foreign domain has to grant read/write access + * in its grant table. + * + * The flags specify what type source and destinations are (either MFN + * or grant reference). + * + * Note that this can also be used to copy data between two domains + * via a third party if the source and destination domains had previously + * grant appropriate access to their pages to the third party. + * + * source_offset specifies an offset in the source frame, dest_offset + * the offset in the target frame and len specifies the number of + * bytes to be copied. + */ + +#define _GNTCOPY_source_gref (0) +#define GNTCOPY_source_gref (1<<_GNTCOPY_source_gref) +#define _GNTCOPY_dest_gref (1) +#define GNTCOPY_dest_gref (1<<_GNTCOPY_dest_gref) + +struct gnttab_copy { + /* IN parameters. */ + struct gnttab_copy_ptr { + union { + grant_ref_t ref; + xen_pfn_t gmfn; + } u; + domid_t domid; + uint16_t offset; + } source, dest; + uint16_t len; + uint16_t flags; /* GNTCOPY_* */ + /* OUT parameters. */ + int16_t status; +}; +typedef struct gnttab_copy gnttab_copy_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_copy_t); + +/* + * GNTTABOP_query_size: Query the current and maximum sizes of the shared + * grant table. + * NOTES: + * 1. may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may specify != DOMID_SELF. + */ +struct gnttab_query_size { + /* IN parameters. */ + domid_t dom; + /* OUT parameters. */ + uint32_t nr_frames; + uint32_t max_nr_frames; + int16_t status; /* => enum grant_status */ +}; +typedef struct gnttab_query_size gnttab_query_size_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_query_size_t); + +/* + * GNTTABOP_unmap_and_replace: Destroy one or more grant-reference mappings + * tracked by but atomically replace the page table entry with one + * pointing to the machine address under . will be + * redirected to the null entry. + * NOTES: + * 1. The call may fail in an undefined manner if either mapping is not + * tracked by . + * 2. After executing a batch of unmaps, it is guaranteed that no stale + * mappings will remain in the device or host TLBs. + */ +struct gnttab_unmap_and_replace { + /* IN parameters. */ + uint64_t host_addr; + uint64_t new_addr; + grant_handle_t handle; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ +}; +typedef struct gnttab_unmap_and_replace gnttab_unmap_and_replace_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_unmap_and_replace_t); + +#if __XEN_INTERFACE_VERSION__ >= 0x0003020a +/* + * GNTTABOP_set_version: Request a particular version of the grant + * table shared table structure. This operation may be used to toggle + * between different versions, but must be performed while no grants + * are active. The only defined versions are 1 and 2. + */ +struct gnttab_set_version { + /* IN/OUT parameters */ + uint32_t version; +}; +typedef struct gnttab_set_version gnttab_set_version_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_set_version_t); + + +/* + * GNTTABOP_get_status_frames: Get the list of frames used to store grant + * status for . In grant format version 2, the status is separated + * from the other shared grant fields to allow more efficient synchronization + * using barriers instead of atomic cmpexch operations. + * specify the size of vector . + * The frame addresses are returned in the . + * Only addresses are returned, even if the table is larger. + * NOTES: + * 1. may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may specify != DOMID_SELF. + */ +struct gnttab_get_status_frames { + /* IN parameters. */ + uint32_t nr_frames; + domid_t dom; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ + XEN_GUEST_HANDLE(uint64_t) frame_list; +}; +typedef struct gnttab_get_status_frames gnttab_get_status_frames_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_get_status_frames_t); + +/* + * GNTTABOP_get_version: Get the grant table version which is in + * effect for domain . + */ +struct gnttab_get_version { + /* IN parameters */ + domid_t dom; + uint16_t pad; + /* OUT parameters */ + uint32_t version; +}; +typedef struct gnttab_get_version gnttab_get_version_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_get_version_t); + +/* + * GNTTABOP_swap_grant_ref: Swap the contents of two grant entries. + */ +struct gnttab_swap_grant_ref { + /* IN parameters */ + grant_ref_t ref_a; + grant_ref_t ref_b; + /* OUT parameters */ + int16_t status; /* => enum grant_status */ +}; +typedef struct gnttab_swap_grant_ref gnttab_swap_grant_ref_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_swap_grant_ref_t); + +/* + * Issue one or more cache maintenance operations on a portion of a + * page granted to the calling domain by a foreign domain. + */ +struct gnttab_cache_flush { + union { + uint64_t dev_bus_addr; + grant_ref_t ref; + } a; + uint16_t offset; /* offset from start of grant */ + uint16_t length; /* size within the grant */ +#define GNTTAB_CACHE_CLEAN (1u<<0) +#define GNTTAB_CACHE_INVAL (1u<<1) +#define GNTTAB_CACHE_SOURCE_GREF (1u<<31) + uint32_t op; +}; +typedef struct gnttab_cache_flush gnttab_cache_flush_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_cache_flush_t); + +#endif /* __XEN_INTERFACE_VERSION__ */ + +/* + * Bitfield values for gnttab_map_grant_ref.flags. + */ + /* Map the grant entry for access by I/O devices. */ +#define _GNTMAP_device_map (0) +#define GNTMAP_device_map (1<<_GNTMAP_device_map) + /* Map the grant entry for access by host CPUs. */ +#define _GNTMAP_host_map (1) +#define GNTMAP_host_map (1<<_GNTMAP_host_map) + /* Accesses to the granted frame will be restricted to read-only access. */ +#define _GNTMAP_readonly (2) +#define GNTMAP_readonly (1<<_GNTMAP_readonly) + /* + * GNTMAP_host_map subflag: + * 0 => The host mapping is usable only by the guest OS. + * 1 => The host mapping is usable by guest OS + current application. + */ +#define _GNTMAP_application_map (3) +#define GNTMAP_application_map (1<<_GNTMAP_application_map) + + /* + * GNTMAP_contains_pte subflag: + * 0 => This map request contains a host virtual address. + * 1 => This map request contains the machine addess of the PTE to update. + */ +#define _GNTMAP_contains_pte (4) +#define GNTMAP_contains_pte (1<<_GNTMAP_contains_pte) + +/* + * Bits to be placed in guest kernel available PTE bits (architecture + * dependent; only supported when XENFEAT_gnttab_map_avail_bits is set). + */ +#define _GNTMAP_guest_avail0 (16) +#define GNTMAP_guest_avail_mask ((uint32_t)~0 << _GNTMAP_guest_avail0) + +/* + * Values for error status returns. All errors are -ve. + */ +/* ` enum grant_status { */ +#define GNTST_okay (0) /* Normal return. */ +#define GNTST_general_error (-1) /* General undefined error. */ +#define GNTST_bad_domain (-2) /* Unrecognsed domain id. */ +#define GNTST_bad_gntref (-3) /* Unrecognised or inappropriate gntref. */ +#define GNTST_bad_handle (-4) /* Unrecognised or inappropriate handle. */ +#define GNTST_bad_virt_addr (-5) /* Inappropriate virtual address to map. */ +#define GNTST_bad_dev_addr (-6) /* Inappropriate device address to unmap.*/ +#define GNTST_no_device_space (-7) /* Out of space in I/O MMU. */ +#define GNTST_permission_denied (-8) /* Not enough privilege for operation. */ +#define GNTST_bad_page (-9) /* Specified page was invalid for op. */ +#define GNTST_bad_copy_arg (-10) /* copy arguments cross page boundary. */ +#define GNTST_address_too_big (-11) /* transfer page address too large. */ +#define GNTST_eagain (-12) /* Operation not done; try again. */ +#define GNTST_no_space (-13) /* Out of space (handles etc). */ +/* ` } */ + +#define GNTTABOP_error_msgs { \ + "okay", \ + "undefined error", \ + "unrecognised domain id", \ + "invalid grant reference", \ + "invalid mapping handle", \ + "invalid virtual address", \ + "invalid device address", \ + "no spare translation slot in the I/O MMU", \ + "permission denied", \ + "bad page", \ + "copy arguments cross page boundary", \ + "page address size too large", \ + "operation not done; try again", \ + "out of space", \ +} + #endif /* __XEN_PUBLIC_GRANT_TABLE_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/hvm/hvm_op.h b/include/hw/xen/interface/hvm/hvm_op.h new file mode 100644 index 0000000000..870ec52060 --- /dev/null +++ b/include/hw/xen/interface/hvm/hvm_op.h @@ -0,0 +1,395 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2007, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_HVM_HVM_OP_H__ +#define __XEN_PUBLIC_HVM_HVM_OP_H__ + +#include "../xen.h" +#include "../trace.h" +#include "../event_channel.h" + +/* Get/set subcommands: extra argument == pointer to xen_hvm_param struct. */ +#define HVMOP_set_param 0 +#define HVMOP_get_param 1 +struct xen_hvm_param { + domid_t domid; /* IN */ + uint16_t pad; + uint32_t index; /* IN */ + uint64_t value; /* IN/OUT */ +}; +typedef struct xen_hvm_param xen_hvm_param_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_param_t); + +struct xen_hvm_altp2m_suppress_ve { + uint16_t view; + uint8_t suppress_ve; /* Boolean type. */ + uint8_t pad1; + uint32_t pad2; + uint64_t gfn; +}; + +struct xen_hvm_altp2m_suppress_ve_multi { + uint16_t view; + uint8_t suppress_ve; /* Boolean type. */ + uint8_t pad1; + int32_t first_error; /* Should be set to 0. */ + uint64_t first_gfn; /* Value may be updated. */ + uint64_t last_gfn; + uint64_t first_error_gfn; /* Gfn of the first error. */ +}; + +#if __XEN_INTERFACE_VERSION__ < 0x00040900 + +/* Set the logical level of one of a domain's PCI INTx wires. */ +#define HVMOP_set_pci_intx_level 2 +struct xen_hvm_set_pci_intx_level { + /* Domain to be updated. */ + domid_t domid; + /* PCI INTx identification in PCI topology (domain:bus:device:intx). */ + uint8_t domain, bus, device, intx; + /* Assertion level (0 = unasserted, 1 = asserted). */ + uint8_t level; +}; +typedef struct xen_hvm_set_pci_intx_level xen_hvm_set_pci_intx_level_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_pci_intx_level_t); + +/* Set the logical level of one of a domain's ISA IRQ wires. */ +#define HVMOP_set_isa_irq_level 3 +struct xen_hvm_set_isa_irq_level { + /* Domain to be updated. */ + domid_t domid; + /* ISA device identification, by ISA IRQ (0-15). */ + uint8_t isa_irq; + /* Assertion level (0 = unasserted, 1 = asserted). */ + uint8_t level; +}; +typedef struct xen_hvm_set_isa_irq_level xen_hvm_set_isa_irq_level_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_isa_irq_level_t); + +#define HVMOP_set_pci_link_route 4 +struct xen_hvm_set_pci_link_route { + /* Domain to be updated. */ + domid_t domid; + /* PCI link identifier (0-3). */ + uint8_t link; + /* ISA IRQ (1-15), or 0 (disable link). */ + uint8_t isa_irq; +}; +typedef struct xen_hvm_set_pci_link_route xen_hvm_set_pci_link_route_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_pci_link_route_t); + +#endif /* __XEN_INTERFACE_VERSION__ < 0x00040900 */ + +/* Flushes all VCPU TLBs: @arg must be NULL. */ +#define HVMOP_flush_tlbs 5 + +/* + * hvmmem_type_t should not be defined when generating the corresponding + * compat header. This will ensure that the improperly named HVMMEM_(*) + * values are defined only once. + */ +#ifndef XEN_GENERATING_COMPAT_HEADERS + +typedef enum { + HVMMEM_ram_rw, /* Normal read/write guest RAM */ + HVMMEM_ram_ro, /* Read-only; writes are discarded */ + HVMMEM_mmio_dm, /* Reads and write go to the device model */ +#if __XEN_INTERFACE_VERSION__ < 0x00040700 + HVMMEM_mmio_write_dm, /* Read-only; writes go to the device model */ +#else + HVMMEM_unused, /* Placeholder; setting memory to this type + will fail for code after 4.7.0 */ +#endif + HVMMEM_ioreq_server /* Memory type claimed by an ioreq server; type + changes to this value are only allowed after + an ioreq server has claimed its ownership. + Only pages with HVMMEM_ram_rw are allowed to + change to this type; conversely, pages with + this type are only allowed to be changed back + to HVMMEM_ram_rw. */ +} hvmmem_type_t; + +#endif /* XEN_GENERATING_COMPAT_HEADERS */ + +/* Hint from PV drivers for pagetable destruction. */ +#define HVMOP_pagetable_dying 9 +struct xen_hvm_pagetable_dying { + /* Domain with a pagetable about to be destroyed. */ + domid_t domid; + uint16_t pad[3]; /* align next field on 8-byte boundary */ + /* guest physical address of the toplevel pagetable dying */ + uint64_t gpa; +}; +typedef struct xen_hvm_pagetable_dying xen_hvm_pagetable_dying_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_pagetable_dying_t); + +/* Get the current Xen time, in nanoseconds since system boot. */ +#define HVMOP_get_time 10 +struct xen_hvm_get_time { + uint64_t now; /* OUT */ +}; +typedef struct xen_hvm_get_time xen_hvm_get_time_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_get_time_t); + +#define HVMOP_xentrace 11 +struct xen_hvm_xentrace { + uint16_t event, extra_bytes; + uint8_t extra[TRACE_EXTRA_MAX * sizeof(uint32_t)]; +}; +typedef struct xen_hvm_xentrace xen_hvm_xentrace_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_xentrace_t); + +/* Following tools-only interfaces may change in future. */ +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +/* Deprecated by XENMEM_access_op_set_access */ +#define HVMOP_set_mem_access 12 + +/* Deprecated by XENMEM_access_op_get_access */ +#define HVMOP_get_mem_access 13 + +#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ + +#define HVMOP_get_mem_type 15 +/* Return hvmmem_type_t for the specified pfn. */ +struct xen_hvm_get_mem_type { + /* Domain to be queried. */ + domid_t domid; + /* OUT variable. */ + uint16_t mem_type; + uint16_t pad[2]; /* align next field on 8-byte boundary */ + /* IN variable. */ + uint64_t pfn; +}; +typedef struct xen_hvm_get_mem_type xen_hvm_get_mem_type_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_get_mem_type_t); + +/* Following tools-only interfaces may change in future. */ +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +/* + * Definitions relating to DMOP_create_ioreq_server. (Defined here for + * backwards compatibility). + */ + +#define HVM_IOREQSRV_BUFIOREQ_OFF 0 +#define HVM_IOREQSRV_BUFIOREQ_LEGACY 1 +/* + * Use this when read_pointer gets updated atomically and + * the pointer pair gets read atomically: + */ +#define HVM_IOREQSRV_BUFIOREQ_ATOMIC 2 + +#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ + +#if defined(__i386__) || defined(__x86_64__) + +/* + * HVMOP_set_evtchn_upcall_vector: Set a that should be used for event + * channel upcalls on the specified . If set, + * this vector will be used in preference to the + * domain global callback via (see + * HVM_PARAM_CALLBACK_IRQ). + */ +#define HVMOP_set_evtchn_upcall_vector 23 +struct xen_hvm_evtchn_upcall_vector { + uint32_t vcpu; + uint8_t vector; +}; +typedef struct xen_hvm_evtchn_upcall_vector xen_hvm_evtchn_upcall_vector_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_evtchn_upcall_vector_t); + +#endif /* defined(__i386__) || defined(__x86_64__) */ + +#define HVMOP_guest_request_vm_event 24 + +/* HVMOP_altp2m: perform altp2m state operations */ +#define HVMOP_altp2m 25 + +#define HVMOP_ALTP2M_INTERFACE_VERSION 0x00000001 + +struct xen_hvm_altp2m_domain_state { + /* IN or OUT variable on/off */ + uint8_t state; +}; +typedef struct xen_hvm_altp2m_domain_state xen_hvm_altp2m_domain_state_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_altp2m_domain_state_t); + +struct xen_hvm_altp2m_vcpu_enable_notify { + uint32_t vcpu_id; + uint32_t pad; + /* #VE info area gfn */ + uint64_t gfn; +}; +typedef struct xen_hvm_altp2m_vcpu_enable_notify xen_hvm_altp2m_vcpu_enable_notify_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_altp2m_vcpu_enable_notify_t); + +struct xen_hvm_altp2m_vcpu_disable_notify { + uint32_t vcpu_id; +}; +typedef struct xen_hvm_altp2m_vcpu_disable_notify xen_hvm_altp2m_vcpu_disable_notify_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_altp2m_vcpu_disable_notify_t); + +struct xen_hvm_altp2m_view { + /* IN/OUT variable */ + uint16_t view; + uint16_t hvmmem_default_access; /* xenmem_access_t */ +}; +typedef struct xen_hvm_altp2m_view xen_hvm_altp2m_view_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_altp2m_view_t); + +#if __XEN_INTERFACE_VERSION__ < 0x00040a00 +struct xen_hvm_altp2m_set_mem_access { + /* view */ + uint16_t view; + /* Memory type */ + uint16_t access; /* xenmem_access_t */ + uint32_t pad; + /* gfn */ + uint64_t gfn; +}; +typedef struct xen_hvm_altp2m_set_mem_access xen_hvm_altp2m_set_mem_access_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_altp2m_set_mem_access_t); +#endif /* __XEN_INTERFACE_VERSION__ < 0x00040a00 */ + +struct xen_hvm_altp2m_mem_access { + /* view */ + uint16_t view; + /* Memory type */ + uint16_t access; /* xenmem_access_t */ + uint32_t pad; + /* gfn */ + uint64_t gfn; +}; +typedef struct xen_hvm_altp2m_mem_access xen_hvm_altp2m_mem_access_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_altp2m_mem_access_t); + +struct xen_hvm_altp2m_set_mem_access_multi { + /* view */ + uint16_t view; + uint16_t pad; + /* Number of pages */ + uint32_t nr; + /* + * Used for continuation purposes. + * Must be set to zero upon initial invocation. + */ + uint64_t opaque; + /* List of pfns to set access for */ + XEN_GUEST_HANDLE(const_uint64) pfn_list; + /* Corresponding list of access settings for pfn_list */ + XEN_GUEST_HANDLE(const_uint8) access_list; +}; + +struct xen_hvm_altp2m_change_gfn { + /* view */ + uint16_t view; + uint16_t pad1; + uint32_t pad2; + /* old gfn */ + uint64_t old_gfn; + /* new gfn, INVALID_GFN (~0UL) means revert */ + uint64_t new_gfn; +}; +typedef struct xen_hvm_altp2m_change_gfn xen_hvm_altp2m_change_gfn_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_altp2m_change_gfn_t); + +struct xen_hvm_altp2m_get_vcpu_p2m_idx { + uint32_t vcpu_id; + uint16_t altp2m_idx; +}; + +struct xen_hvm_altp2m_set_visibility { + uint16_t altp2m_idx; + uint8_t visible; + uint8_t pad; +}; + +struct xen_hvm_altp2m_op { + uint32_t version; /* HVMOP_ALTP2M_INTERFACE_VERSION */ + uint32_t cmd; +/* Get/set the altp2m state for a domain */ +#define HVMOP_altp2m_get_domain_state 1 +#define HVMOP_altp2m_set_domain_state 2 +/* Set a given VCPU to receive altp2m event notifications */ +#define HVMOP_altp2m_vcpu_enable_notify 3 +/* Create a new view */ +#define HVMOP_altp2m_create_p2m 4 +/* Destroy a view */ +#define HVMOP_altp2m_destroy_p2m 5 +/* Switch view for an entire domain */ +#define HVMOP_altp2m_switch_p2m 6 +/* Notify that a page of memory is to have specific access types */ +#define HVMOP_altp2m_set_mem_access 7 +/* Change a p2m entry to have a different gfn->mfn mapping */ +#define HVMOP_altp2m_change_gfn 8 +/* Set access for an array of pages */ +#define HVMOP_altp2m_set_mem_access_multi 9 +/* Set the "Suppress #VE" bit on a page */ +#define HVMOP_altp2m_set_suppress_ve 10 +/* Get the "Suppress #VE" bit of a page */ +#define HVMOP_altp2m_get_suppress_ve 11 +/* Get the access of a page of memory from a certain view */ +#define HVMOP_altp2m_get_mem_access 12 +/* Disable altp2m event notifications for a given VCPU */ +#define HVMOP_altp2m_vcpu_disable_notify 13 +/* Get the active vcpu p2m index */ +#define HVMOP_altp2m_get_p2m_idx 14 +/* Set the "Supress #VE" bit for a range of pages */ +#define HVMOP_altp2m_set_suppress_ve_multi 15 +/* Set visibility for a given altp2m view */ +#define HVMOP_altp2m_set_visibility 16 + domid_t domain; + uint16_t pad1; + uint32_t pad2; + union { + struct xen_hvm_altp2m_domain_state domain_state; + struct xen_hvm_altp2m_vcpu_enable_notify enable_notify; + struct xen_hvm_altp2m_view view; +#if __XEN_INTERFACE_VERSION__ < 0x00040a00 + struct xen_hvm_altp2m_set_mem_access set_mem_access; +#endif /* __XEN_INTERFACE_VERSION__ < 0x00040a00 */ + struct xen_hvm_altp2m_mem_access mem_access; + struct xen_hvm_altp2m_change_gfn change_gfn; + struct xen_hvm_altp2m_set_mem_access_multi set_mem_access_multi; + struct xen_hvm_altp2m_suppress_ve suppress_ve; + struct xen_hvm_altp2m_suppress_ve_multi suppress_ve_multi; + struct xen_hvm_altp2m_vcpu_disable_notify disable_notify; + struct xen_hvm_altp2m_get_vcpu_p2m_idx get_vcpu_p2m_idx; + struct xen_hvm_altp2m_set_visibility set_visibility; + uint8_t pad[64]; + } u; +}; +typedef struct xen_hvm_altp2m_op xen_hvm_altp2m_op_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_altp2m_op_t); + +#endif /* __XEN_PUBLIC_HVM_HVM_OP_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/hvm/params.h b/include/hw/xen/interface/hvm/params.h new file mode 100644 index 0000000000..c9d6e70d7b --- /dev/null +++ b/include/hw/xen/interface/hvm/params.h @@ -0,0 +1,318 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2007, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_HVM_PARAMS_H__ +#define __XEN_PUBLIC_HVM_PARAMS_H__ + +#include "hvm_op.h" + +/* These parameters are deprecated and their meaning is undefined. */ +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +#define HVM_PARAM_PAE_ENABLED 4 +#define HVM_PARAM_DM_DOMAIN 13 +#define HVM_PARAM_MEMORY_EVENT_CR0 20 +#define HVM_PARAM_MEMORY_EVENT_CR3 21 +#define HVM_PARAM_MEMORY_EVENT_CR4 22 +#define HVM_PARAM_MEMORY_EVENT_INT3 23 +#define HVM_PARAM_NESTEDHVM 24 +#define HVM_PARAM_MEMORY_EVENT_SINGLE_STEP 25 +#define HVM_PARAM_BUFIOREQ_EVTCHN 26 +#define HVM_PARAM_MEMORY_EVENT_MSR 30 + +#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ + +/* + * Parameter space for HVMOP_{set,get}_param. + */ + +#define HVM_PARAM_CALLBACK_IRQ 0 +#define HVM_PARAM_CALLBACK_IRQ_TYPE_MASK xen_mk_ullong(0xFF00000000000000) +/* + * How should CPU0 event-channel notifications be delivered? + * + * If val == 0 then CPU0 event-channel notifications are not delivered. + * If val != 0, val[63:56] encodes the type, as follows: + */ + +#define HVM_PARAM_CALLBACK_TYPE_GSI 0 +/* + * val[55:0] is a delivery GSI. GSI 0 cannot be used, as it aliases val == 0, + * and disables all notifications. + */ + +#define HVM_PARAM_CALLBACK_TYPE_PCI_INTX 1 +/* + * val[55:0] is a delivery PCI INTx line: + * Domain = val[47:32], Bus = val[31:16] DevFn = val[15:8], IntX = val[1:0] + */ + +#if defined(__i386__) || defined(__x86_64__) +#define HVM_PARAM_CALLBACK_TYPE_VECTOR 2 +/* + * val[7:0] is a vector number. Check for XENFEAT_hvm_callback_vector to know + * if this delivery method is available. + */ +#elif defined(__arm__) || defined(__aarch64__) +#define HVM_PARAM_CALLBACK_TYPE_PPI 2 +/* + * val[55:16] needs to be zero. + * val[15:8] is interrupt flag of the PPI used by event-channel: + * bit 8: the PPI is edge(1) or level(0) triggered + * bit 9: the PPI is active low(1) or high(0) + * val[7:0] is a PPI number used by event-channel. + * This is only used by ARM/ARM64 and masking/eoi the interrupt associated to + * the notification is handled by the interrupt controller. + */ +#define HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_MASK 0xFF00 +#define HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_LOW_LEVEL 2 +#endif + +/* + * These are not used by Xen. They are here for convenience of HVM-guest + * xenbus implementations. + */ +#define HVM_PARAM_STORE_PFN 1 +#define HVM_PARAM_STORE_EVTCHN 2 + +#define HVM_PARAM_IOREQ_PFN 5 + +#define HVM_PARAM_BUFIOREQ_PFN 6 + +#if defined(__i386__) || defined(__x86_64__) + +/* + * Viridian enlightenments + * + * (See http://download.microsoft.com/download/A/B/4/AB43A34E-BDD0-4FA6-BDEF-79EEF16E880B/Hypervisor%20Top%20Level%20Functional%20Specification%20v4.0.docx) + * + * To expose viridian enlightenments to the guest set this parameter + * to the desired feature mask. The base feature set must be present + * in any valid feature mask. + */ +#define HVM_PARAM_VIRIDIAN 9 + +/* Base+Freq viridian feature sets: + * + * - Hypercall MSRs (HV_X64_MSR_GUEST_OS_ID and HV_X64_MSR_HYPERCALL) + * - APIC access MSRs (HV_X64_MSR_EOI, HV_X64_MSR_ICR and HV_X64_MSR_TPR) + * - Virtual Processor index MSR (HV_X64_MSR_VP_INDEX) + * - Timer frequency MSRs (HV_X64_MSR_TSC_FREQUENCY and + * HV_X64_MSR_APIC_FREQUENCY) + */ +#define _HVMPV_base_freq 0 +#define HVMPV_base_freq (1 << _HVMPV_base_freq) + +/* Feature set modifications */ + +/* Disable timer frequency MSRs (HV_X64_MSR_TSC_FREQUENCY and + * HV_X64_MSR_APIC_FREQUENCY). + * This modification restores the viridian feature set to the + * original 'base' set exposed in releases prior to Xen 4.4. + */ +#define _HVMPV_no_freq 1 +#define HVMPV_no_freq (1 << _HVMPV_no_freq) + +/* Enable Partition Time Reference Counter (HV_X64_MSR_TIME_REF_COUNT) */ +#define _HVMPV_time_ref_count 2 +#define HVMPV_time_ref_count (1 << _HVMPV_time_ref_count) + +/* Enable Reference TSC Page (HV_X64_MSR_REFERENCE_TSC) */ +#define _HVMPV_reference_tsc 3 +#define HVMPV_reference_tsc (1 << _HVMPV_reference_tsc) + +/* Use Hypercall for remote TLB flush */ +#define _HVMPV_hcall_remote_tlb_flush 4 +#define HVMPV_hcall_remote_tlb_flush (1 << _HVMPV_hcall_remote_tlb_flush) + +/* Use APIC assist */ +#define _HVMPV_apic_assist 5 +#define HVMPV_apic_assist (1 << _HVMPV_apic_assist) + +/* Enable crash MSRs */ +#define _HVMPV_crash_ctl 6 +#define HVMPV_crash_ctl (1 << _HVMPV_crash_ctl) + +/* Enable SYNIC MSRs */ +#define _HVMPV_synic 7 +#define HVMPV_synic (1 << _HVMPV_synic) + +/* Enable STIMER MSRs */ +#define _HVMPV_stimer 8 +#define HVMPV_stimer (1 << _HVMPV_stimer) + +/* Use Synthetic Cluster IPI Hypercall */ +#define _HVMPV_hcall_ipi 9 +#define HVMPV_hcall_ipi (1 << _HVMPV_hcall_ipi) + +/* Enable ExProcessorMasks */ +#define _HVMPV_ex_processor_masks 10 +#define HVMPV_ex_processor_masks (1 << _HVMPV_ex_processor_masks) + +/* Allow more than 64 VPs */ +#define _HVMPV_no_vp_limit 11 +#define HVMPV_no_vp_limit (1 << _HVMPV_no_vp_limit) + +/* Enable vCPU hotplug */ +#define _HVMPV_cpu_hotplug 12 +#define HVMPV_cpu_hotplug (1 << _HVMPV_cpu_hotplug) + +#define HVMPV_feature_mask \ + (HVMPV_base_freq | \ + HVMPV_no_freq | \ + HVMPV_time_ref_count | \ + HVMPV_reference_tsc | \ + HVMPV_hcall_remote_tlb_flush | \ + HVMPV_apic_assist | \ + HVMPV_crash_ctl | \ + HVMPV_synic | \ + HVMPV_stimer | \ + HVMPV_hcall_ipi | \ + HVMPV_ex_processor_masks | \ + HVMPV_no_vp_limit | \ + HVMPV_cpu_hotplug) + +#endif + +/* + * Set mode for virtual timers (currently x86 only): + * delay_for_missed_ticks (default): + * Do not advance a vcpu's time beyond the correct delivery time for + * interrupts that have been missed due to preemption. Deliver missed + * interrupts when the vcpu is rescheduled and advance the vcpu's virtual + * time stepwise for each one. + * no_delay_for_missed_ticks: + * As above, missed interrupts are delivered, but guest time always tracks + * wallclock (i.e., real) time while doing so. + * no_missed_ticks_pending: + * No missed interrupts are held pending. Instead, to ensure ticks are + * delivered at some non-zero rate, if we detect missed ticks then the + * internal tick alarm is not disabled if the VCPU is preempted during the + * next tick period. + * one_missed_tick_pending: + * Missed interrupts are collapsed together and delivered as one 'late tick'. + * Guest time always tracks wallclock (i.e., real) time. + */ +#define HVM_PARAM_TIMER_MODE 10 +#define HVMPTM_delay_for_missed_ticks 0 +#define HVMPTM_no_delay_for_missed_ticks 1 +#define HVMPTM_no_missed_ticks_pending 2 +#define HVMPTM_one_missed_tick_pending 3 + +/* Boolean: Enable virtual HPET (high-precision event timer)? (x86-only) */ +#define HVM_PARAM_HPET_ENABLED 11 + +/* Identity-map page directory used by Intel EPT when CR0.PG=0. */ +#define HVM_PARAM_IDENT_PT 12 + +/* ACPI S state: currently support S0 and S3 on x86. */ +#define HVM_PARAM_ACPI_S_STATE 14 + +/* TSS used on Intel when CR0.PE=0. */ +#define HVM_PARAM_VM86_TSS 15 + +/* Boolean: Enable aligning all periodic vpts to reduce interrupts */ +#define HVM_PARAM_VPT_ALIGN 16 + +/* Console debug shared memory ring and event channel */ +#define HVM_PARAM_CONSOLE_PFN 17 +#define HVM_PARAM_CONSOLE_EVTCHN 18 + +/* + * Select location of ACPI PM1a and TMR control blocks. Currently two locations + * are supported, specified by version 0 or 1 in this parameter: + * - 0: default, use the old addresses + * PM1A_EVT == 0x1f40; PM1A_CNT == 0x1f44; PM_TMR == 0x1f48 + * - 1: use the new default qemu addresses + * PM1A_EVT == 0xb000; PM1A_CNT == 0xb004; PM_TMR == 0xb008 + * You can find these address definitions in + */ +#define HVM_PARAM_ACPI_IOPORTS_LOCATION 19 + +/* Params for the mem event rings */ +#define HVM_PARAM_PAGING_RING_PFN 27 +#define HVM_PARAM_MONITOR_RING_PFN 28 +#define HVM_PARAM_SHARING_RING_PFN 29 + +/* SHUTDOWN_* action in case of a triple fault */ +#define HVM_PARAM_TRIPLE_FAULT_REASON 31 + +#define HVM_PARAM_IOREQ_SERVER_PFN 32 +#define HVM_PARAM_NR_IOREQ_SERVER_PAGES 33 + +/* Location of the VM Generation ID in guest physical address space. */ +#define HVM_PARAM_VM_GENERATION_ID_ADDR 34 + +/* + * Set mode for altp2m: + * disabled: don't activate altp2m (default) + * mixed: allow access to all altp2m ops for both in-guest and external tools + * external: allow access to external privileged tools only + * limited: guest only has limited access (ie. control VMFUNC and #VE) + * + * Note that 'mixed' mode has not been evaluated for safety from a + * security perspective. Before using this mode in a + * security-critical environment, each subop should be evaluated for + * safety, with unsafe subops blacklisted in XSM. + */ +#define HVM_PARAM_ALTP2M 35 +#define XEN_ALTP2M_disabled 0 +#define XEN_ALTP2M_mixed 1 +#define XEN_ALTP2M_external 2 +#define XEN_ALTP2M_limited 3 + +/* + * Size of the x87 FPU FIP/FDP registers that the hypervisor needs to + * save/restore. This is a workaround for a hardware limitation that + * does not allow the full FIP/FDP and FCS/FDS to be restored. + * + * Valid values are: + * + * 8: save/restore 64-bit FIP/FDP and clear FCS/FDS (default if CPU + * has FPCSDS feature). + * + * 4: save/restore 32-bit FIP/FDP, FCS/FDS, and clear upper 32-bits of + * FIP/FDP. + * + * 0: allow hypervisor to choose based on the value of FIP/FDP + * (default if CPU does not have FPCSDS). + * + * If FPCSDS (bit 13 in CPUID leaf 0x7, subleaf 0x0) is set, the CPU + * never saves FCS/FDS and this parameter should be left at the + * default of 8. + */ +#define HVM_PARAM_X87_FIP_WIDTH 36 + +/* + * TSS (and its size) used on Intel when CR0.PE=0. The address occupies + * the low 32 bits, while the size is in the high 32 ones. + */ +#define HVM_PARAM_VM86_TSS_SIZED 37 + +/* Enable MCA capabilities. */ +#define HVM_PARAM_MCA_CAP 38 +#define XEN_HVM_MCA_CAP_LMCE (xen_mk_ullong(1) << 0) +#define XEN_HVM_MCA_CAP_MASK XEN_HVM_MCA_CAP_LMCE + +#define HVM_NR_PARAMS 39 + +#endif /* __XEN_PUBLIC_HVM_PARAMS_H__ */ diff --git a/include/hw/xen/interface/io/blkif.h b/include/hw/xen/interface/io/blkif.h index d07fa1e078..4cdba79aba 100644 --- a/include/hw/xen/interface/io/blkif.h +++ b/include/hw/xen/interface/io/blkif.h @@ -118,7 +118,7 @@ * * The underlying storage is not affected by the direct IO memory * lifetime bug. See: - * http://lists.xen.org/archives/html/xen-devel/2012-12/msg01154.html + * https://lists.xen.org/archives/html/xen-devel/2012-12/msg01154.html * * Therefore this option gives the backend permission to use * O_DIRECT, notwithstanding that bug. @@ -341,7 +341,7 @@ * access (even when it should be read-only). If the frontend hits the * maximum number of allowed persistently mapped grants, it can fallback * to non persistent mode. This will cause a performance degradation, - * since the backend driver will still try to map those grants + * since the the backend driver will still try to map those grants * persistently. Since the persistent grants protocol is compatible with * the previous protocol, a frontend driver can choose to work in * persistent mode even when the backend doesn't support it. @@ -710,3 +710,13 @@ DEFINE_RING_TYPES(blkif, struct blkif_request, struct blkif_response); #define VDISK_READONLY 0x4 #endif /* __XEN_PUBLIC_IO_BLKIF_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/io/console.h b/include/hw/xen/interface/io/console.h index e2155d1cf5..4811f47220 100644 --- a/include/hw/xen/interface/io/console.h +++ b/include/hw/xen/interface/io/console.h @@ -44,3 +44,13 @@ DEFINE_XEN_FLEX_RING(xencons); #endif #endif /* __XEN_PUBLIC_IO_CONSOLE_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/io/fbif.h b/include/hw/xen/interface/io/fbif.h index ea87ebec0a..cc25aab32e 100644 --- a/include/hw/xen/interface/io/fbif.h +++ b/include/hw/xen/interface/io/fbif.h @@ -153,4 +153,24 @@ struct xenfb_page unsigned long pd[256]; }; +/* + * Wart: xenkbd needs to know default resolution. Put it here until a + * better solution is found, but don't leak it to the backend. + */ +#ifdef __KERNEL__ +#define XENFB_WIDTH 800 +#define XENFB_HEIGHT 600 +#define XENFB_DEPTH 32 #endif + +#endif + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/io/kbdif.h b/include/hw/xen/interface/io/kbdif.h index 1d68cd458e..a6b01c52c7 100644 --- a/include/hw/xen/interface/io/kbdif.h +++ b/include/hw/xen/interface/io/kbdif.h @@ -564,3 +564,13 @@ struct xenkbd_page }; #endif /* __XEN_PUBLIC_IO_KBDIF_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/io/netif.h b/include/hw/xen/interface/io/netif.h index 48fa530950..00dd258712 100644 --- a/include/hw/xen/interface/io/netif.h +++ b/include/hw/xen/interface/io/netif.h @@ -171,7 +171,7 @@ * The ability of the backend to use a control ring is advertised by * setting: * - * /local/domain/X/backend///feature-ctrl-ring = "1" + * /local/domain/X/backend/vif///feature-ctrl-ring = "1" * * The frontend provides a control ring to the backend by setting: * @@ -190,6 +190,32 @@ * order as requests. */ +/* + * Link state + * ========== + * + * The backend can advertise its current link (carrier) state to the + * frontend using the /local/domain/X/backend/vif///carrier + * node. If this node is not present, then the frontend should assume that + * the link is up (for compatibility with backends that do not implement + * this feature). If this node is present, then a value of "0" should be + * interpreted by the frontend as the link being down (no carrier) and a + * value of "1" should be interpreted as the link being up (carrier + * present). + */ + +/* + * MTU + * === + * + * The toolstack may set a value of MTU for the frontend by setting the + * /local/domain//device/vif//mtu node with the MTU value in + * octets. If this node is absent the frontend should assume an MTU value + * of 1500 octets. A frontend is also at liberty to ignore this value so + * it is only suitable for informing the frontend that a packet payload + * >1500 octets is permitted. + */ + /* * Hash types * ========== @@ -267,6 +293,62 @@ #define XEN_NETIF_CTRL_HASH_ALGORITHM_TOEPLITZ 1 +/* + * This algorithm uses a 'key' as well as the data buffer itself. + * (Buffer[] and Key[] are treated as shift-registers where the MSB of + * Buffer/Key[0] is considered 'left-most' and the LSB of Buffer/Key[N-1] + * is the 'right-most'). + * + * Value = 0 + * For number of bits in Buffer[] + * If (left-most bit of Buffer[] is 1) + * Value ^= left-most 32 bits of Key[] + * Key[] << 1 + * Buffer[] << 1 + * + * The code below is provided for convenience where an operating system + * does not already provide an implementation. + */ +#ifdef XEN_NETIF_DEFINE_TOEPLITZ +static uint32_t xen_netif_toeplitz_hash(const uint8_t *key, + unsigned int keylen, + const uint8_t *buf, + unsigned int buflen) +{ + unsigned int keyi, bufi; + uint64_t prefix = 0; + uint64_t hash = 0; + + /* Pre-load prefix with the first 8 bytes of the key */ + for (keyi = 0; keyi < 8; keyi++) { + prefix <<= 8; + prefix |= (keyi < keylen) ? key[keyi] : 0; + } + + for (bufi = 0; bufi < buflen; bufi++) { + uint8_t byte = buf[bufi]; + unsigned int bit; + + for (bit = 0; bit < 8; bit++) { + if (byte & 0x80) + hash ^= prefix; + prefix <<= 1; + byte <<=1; + } + + /* + * 'prefix' has now been left-shifted by 8, so + * OR in the next byte. + */ + prefix |= (keyi < keylen) ? key[keyi] : 0; + keyi++; + } + + /* The valid part of the hash is in the upper 32 bits. */ + return hash >> 32; +} +#endif /* XEN_NETIF_DEFINE_TOEPLITZ */ + /* * Control requests (struct xen_netif_ctrl_request) * ================================================ @@ -1008,3 +1090,13 @@ DEFINE_RING_TYPES(netif_rx, struct netif_rx_request, struct netif_rx_response); #define NETIF_RSP_NULL 1 #endif + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/io/ring.h b/include/hw/xen/interface/io/ring.h index 115705f3f4..c486c457e0 100644 --- a/include/hw/xen/interface/io/ring.h +++ b/include/hw/xen/interface/io/ring.h @@ -1,6 +1,6 @@ /****************************************************************************** * ring.h - * + * * Shared producer-consumer ring macros. * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -33,13 +33,6 @@ * - standard integers types (uint8_t, uint16_t, etc) * They are provided by stdint.h of the standard headers. * - * Before using the different macros, you need to provide the following - * macros: - * - xen_mb() a memory barrier - * - xen_rmb() a read memory barrier - * - xen_wmb() a write memory barrier - * Example of those can be found in xenctrl.h. - * * In addition, if you intend to use the FLEX macros, you also need to * provide the following, before invoking the FLEX macros: * - size_t @@ -49,6 +42,14 @@ * and grant_table.h from the Xen public headers. */ +#include "../xen-compat.h" + +#if __XEN_INTERFACE_VERSION__ < 0x00030208 +#define xen_mb() mb() +#define xen_rmb() rmb() +#define xen_wmb() wmb() +#endif + typedef unsigned int RING_IDX; /* Round a 32-bit unsigned constant down to the nearest power of two. */ @@ -61,12 +62,12 @@ typedef unsigned int RING_IDX; /* * Calculate size of a shared ring, given the total available space for the * ring and indexes (_sz), and the name tag of the request/response structure. - * A ring contains as many entries as will fit, rounded down to the nearest + * A ring contains as many entries as will fit, rounded down to the nearest * power of two (so we can mask with (size-1) to loop around). */ #define __CONST_RING_SIZE(_s, _sz) \ (__RD32(((_sz) - offsetof(struct _s##_sring, ring)) / \ - sizeof_field(struct _s##_sring, ring[0]))) + sizeof(((struct _s##_sring *)0)->ring[0]))) /* * The same for passing in an actual pointer instead of a name tag. */ @@ -75,7 +76,7 @@ typedef unsigned int RING_IDX; /* * Macros to make the correct C datatypes for a new kind of ring. - * + * * To make a new ring datatype, you need to have two message structures, * let's say request_t, and response_t already defined. * @@ -85,7 +86,7 @@ typedef unsigned int RING_IDX; * * These expand out to give you a set of types, as you can see below. * The most important of these are: - * + * * mytag_sring_t - The shared ring. * mytag_front_ring_t - The 'front' half of the ring. * mytag_back_ring_t - The 'back' half of the ring. @@ -153,15 +154,15 @@ typedef struct __name##_back_ring __name##_back_ring_t /* * Macros for manipulating rings. - * - * FRONT_RING_whatever works on the "front end" of a ring: here + * + * FRONT_RING_whatever works on the "front end" of a ring: here * requests are pushed on to the ring and responses taken off it. - * - * BACK_RING_whatever works on the "back end" of a ring: here + * + * BACK_RING_whatever works on the "back end" of a ring: here * requests are taken off the ring and responses put on. - * - * N.B. these macros do NO INTERLOCKS OR FLOW CONTROL. - * This is OK in 1-for-1 request-response situations where the + * + * N.B. these macros do NO INTERLOCKS OR FLOW CONTROL. + * This is OK in 1-for-1 request-response situations where the * requestor (front end) never has more than RING_SIZE()-1 * outstanding requests. */ @@ -174,20 +175,24 @@ typedef struct __name##_back_ring __name##_back_ring_t (void)memset((_s)->__pad, 0, sizeof((_s)->__pad)); \ } while(0) -#define FRONT_RING_INIT(_r, _s, __size) do { \ - (_r)->req_prod_pvt = 0; \ - (_r)->rsp_cons = 0; \ +#define FRONT_RING_ATTACH(_r, _s, _i, __size) do { \ + (_r)->req_prod_pvt = (_i); \ + (_r)->rsp_cons = (_i); \ (_r)->nr_ents = __RING_SIZE(_s, __size); \ (_r)->sring = (_s); \ } while (0) -#define BACK_RING_INIT(_r, _s, __size) do { \ - (_r)->rsp_prod_pvt = 0; \ - (_r)->req_cons = 0; \ +#define FRONT_RING_INIT(_r, _s, __size) FRONT_RING_ATTACH(_r, _s, 0, __size) + +#define BACK_RING_ATTACH(_r, _s, _i, __size) do { \ + (_r)->rsp_prod_pvt = (_i); \ + (_r)->req_cons = (_i); \ (_r)->nr_ents = __RING_SIZE(_s, __size); \ (_r)->sring = (_s); \ } while (0) +#define BACK_RING_INIT(_r, _s, __size) BACK_RING_ATTACH(_r, _s, 0, __size) + /* How big is this ring? */ #define RING_SIZE(_r) \ ((_r)->nr_ents) @@ -206,33 +211,45 @@ typedef struct __name##_back_ring __name##_back_ring_t #define RING_HAS_UNCONSUMED_RESPONSES(_r) \ ((_r)->sring->rsp_prod - (_r)->rsp_cons) +#ifdef __GNUC__ #define RING_HAS_UNCONSUMED_REQUESTS(_r) ({ \ unsigned int req = (_r)->sring->req_prod - (_r)->req_cons; \ unsigned int rsp = RING_SIZE(_r) - \ ((_r)->req_cons - (_r)->rsp_prod_pvt); \ req < rsp ? req : rsp; \ }) +#else +/* Same as above, but without the nice GCC ({ ... }) syntax. */ +#define RING_HAS_UNCONSUMED_REQUESTS(_r) \ + ((((_r)->sring->req_prod - (_r)->req_cons) < \ + (RING_SIZE(_r) - ((_r)->req_cons - (_r)->rsp_prod_pvt))) ? \ + ((_r)->sring->req_prod - (_r)->req_cons) : \ + (RING_SIZE(_r) - ((_r)->req_cons - (_r)->rsp_prod_pvt))) +#endif /* Direct access to individual ring elements, by index. */ #define RING_GET_REQUEST(_r, _idx) \ (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req)) +#define RING_GET_RESPONSE(_r, _idx) \ + (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].rsp)) + /* - * Get a local copy of a request. + * Get a local copy of a request/response. * - * Use this in preference to RING_GET_REQUEST() so all processing is + * Use this in preference to RING_GET_{REQUEST,RESPONSE}() so all processing is * done on a local copy that cannot be modified by the other end. * * Note that https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145 may cause this - * to be ineffective where _req is a struct which consists of only bitfields. + * to be ineffective where dest is a struct which consists of only bitfields. */ -#define RING_COPY_REQUEST(_r, _idx, _req) do { \ - /* Use volatile to force the copy into _req. */ \ - *(_req) = *(volatile typeof(_req))RING_GET_REQUEST(_r, _idx); \ +#define RING_COPY_(type, r, idx, dest) do { \ + /* Use volatile to force the copy into dest. */ \ + *(dest) = *(volatile __typeof__(dest))RING_GET_##type(r, idx); \ } while (0) -#define RING_GET_RESPONSE(_r, _idx) \ - (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].rsp)) +#define RING_COPY_REQUEST(r, idx, req) RING_COPY_(REQUEST, r, idx, req) +#define RING_COPY_RESPONSE(r, idx, rsp) RING_COPY_(RESPONSE, r, idx, rsp) /* Loop termination condition: Would the specified index overflow the ring? */ #define RING_REQUEST_CONS_OVERFLOW(_r, _cons) \ @@ -242,6 +259,10 @@ typedef struct __name##_back_ring __name##_back_ring_t #define RING_REQUEST_PROD_OVERFLOW(_r, _prod) \ (((_prod) - (_r)->rsp_prod_pvt) > RING_SIZE(_r)) +/* Ill-behaved backend determination: Can there be this many responses? */ +#define RING_RESPONSE_PROD_OVERFLOW(_r, _prod) \ + (((_prod) - (_r)->rsp_cons) > RING_SIZE(_r)) + #define RING_PUSH_REQUESTS(_r) do { \ xen_wmb(); /* back sees requests /before/ updated producer index */ \ (_r)->sring->req_prod = (_r)->req_prod_pvt; \ @@ -254,26 +275,26 @@ typedef struct __name##_back_ring __name##_back_ring_t /* * Notification hold-off (req_event and rsp_event): - * + * * When queueing requests or responses on a shared ring, it may not always be * necessary to notify the remote end. For example, if requests are in flight * in a backend, the front may be able to queue further requests without * notifying the back (if the back checks for new requests when it queues * responses). - * + * * When enqueuing requests or responses: - * + * * Use RING_PUSH_{REQUESTS,RESPONSES}_AND_CHECK_NOTIFY(). The second argument * is a boolean return value. True indicates that the receiver requires an * asynchronous notification. - * + * * After dequeuing requests or responses (before sleeping the connection): - * + * * Use RING_FINAL_CHECK_FOR_REQUESTS() or RING_FINAL_CHECK_FOR_RESPONSES(). * The second argument is a boolean return value. True indicates that there * are pending messages on the ring (i.e., the connection should not be put * to sleep). - * + * * These macros will set the req_event/rsp_event field to trigger a * notification on the very next message that is enqueued. If you want to * create batches of work (i.e., only receive a notification after several diff --git a/include/hw/xen/interface/io/usbif.h b/include/hw/xen/interface/io/usbif.h index c6a58639d6..c0a552e195 100644 --- a/include/hw/xen/interface/io/usbif.h +++ b/include/hw/xen/interface/io/usbif.h @@ -32,6 +32,34 @@ #include "../grant_table.h" /* + * Detailed Interface Description + * ============================== + * The pvUSB interface is using a split driver design: a frontend driver in + * the guest and a backend driver in a driver domain (normally dom0) having + * access to the physical USB device(s) being passed to the guest. + * + * The frontend and backend drivers use XenStore to initiate the connection + * between them, the I/O activity is handled via two shared ring pages and an + * event channel. As the interface between frontend and backend is at the USB + * host connector level, multiple (up to 31) physical USB devices can be + * handled by a single connection. + * + * The Xen pvUSB device name is "qusb", so the frontend's XenStore entries are + * to be found under "device/qusb", while the backend's XenStore entries are + * under "backend//qusb". + * + * When a new pvUSB connection is established, the frontend needs to setup the + * two shared ring pages for communication and the event channel. The ring + * pages need to be made available to the backend via the grant table + * interface. + * + * One of the shared ring pages is used by the backend to inform the frontend + * about USB device plug events (device to be added or removed). This is the + * "conn-ring". + * + * The other ring page is used for USB I/O communication (requests and + * responses). This is the "urb-ring". + * * Feature and Parameter Negotiation * ================================= * The two halves of a Xen pvUSB driver utilize nodes within the XenStore to @@ -99,130 +127,273 @@ * The machine ABI rules governing the format of all ring request and * response structures. * + * Protocol Description + * ==================== + * + *-------------------------- USB device plug events -------------------------- + * + * USB device plug events are send via the "conn-ring" shared page. As only + * events are being sent, the respective requests from the frontend to the + * backend are just dummy ones. + * The events sent to the frontend have the following layout: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | id | portnum | speed | 4 + * +----------------+----------------+----------------+----------------+ + * id - uint16_t, event id (taken from the actual frontend dummy request) + * portnum - uint8_t, port number (1 ... 31) + * speed - uint8_t, device USBIF_SPEED_*, USBIF_SPEED_NONE == unplug + * + * The dummy request: + * 0 1 octet + * +----------------+----------------+ + * | id | 2 + * +----------------+----------------+ + * id - uint16_t, guest supplied value (no need for being unique) + * + *-------------------------- USB I/O request --------------------------------- + * + * A single USB I/O request on the "urb-ring" has the following layout: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | id | nr_buffer_segs | 4 + * +----------------+----------------+----------------+----------------+ + * | pipe | 8 + * +----------------+----------------+----------------+----------------+ + * | transfer_flags | buffer_length | 12 + * +----------------+----------------+----------------+----------------+ + * | request type specific | 16 + * | data | 20 + * +----------------+----------------+----------------+----------------+ + * | seg[0] | 24 + * | data | 28 + * +----------------+----------------+----------------+----------------+ + * |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/| + * +----------------+----------------+----------------+----------------+ + * | seg[USBIF_MAX_SEGMENTS_PER_REQUEST - 1] | 144 + * | data | 148 + * +----------------+----------------+----------------+----------------+ + * Bit field bit number 0 is always least significant bit, undefined bits must + * be zero. + * id - uint16_t, guest supplied value + * nr_buffer_segs - uint16_t, number of segment entries in seg[] array + * pipe - uint32_t, bit field with multiple information: + * bits 0-4: port request to send to + * bit 5: unlink request with specified id (cancel I/O) if set (see below) + * bit 7: direction (1 = read from device) + * bits 8-14: device number on port + * bits 15-18: endpoint of device + * bits 30-31: request type: 00 = isochronous, 01 = interrupt, + * 10 = control, 11 = bulk + * transfer_flags - uint16_t, bit field with processing flags: + * bit 0: less data than specified allowed + * buffer_length - uint16_t, total length of data + * request type specific data - 8 bytes, see below + * seg[] - array with 8 byte elements, see below + * + * Request type specific data for isochronous request: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | interval | start_frame | 4 + * +----------------+----------------+----------------+----------------+ + * | number_of_packets | nr_frame_desc_segs | 8 + * +----------------+----------------+----------------+----------------+ + * interval - uint16_t, time interval in msecs between frames + * start_frame - uint16_t, start frame number + * number_of_packets - uint16_t, number of packets to transfer + * nr_frame_desc_segs - uint16_t number of seg[] frame descriptors elements + * + * Request type specific data for interrupt request: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | interval | 0 | 4 + * +----------------+----------------+----------------+----------------+ + * | 0 | 8 + * +----------------+----------------+----------------+----------------+ + * interval - uint16_t, time in msecs until interruption + * + * Request type specific data for control request: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | data of setup packet | 4 + * | | 8 + * +----------------+----------------+----------------+----------------+ + * + * Request type specific data for bulk request: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | 0 | 4 + * | 0 | 8 + * +----------------+----------------+----------------+----------------+ + * + * Request type specific data for unlink request: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | unlink_id | 0 | 4 + * +----------------+----------------+----------------+----------------+ + * | 0 | 8 + * +----------------+----------------+----------------+----------------+ + * unlink_id - uint16_t, request id of request to terminate + * + * seg[] array element layout: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | gref | 4 + * +----------------+----------------+----------------+----------------+ + * | offset | length | 8 + * +----------------+----------------+----------------+----------------+ + * gref - uint32_t, grant reference of buffer page + * offset - uint16_t, offset of buffer start in page + * length - uint16_t, length of buffer in page + * + *-------------------------- USB I/O response -------------------------------- + * + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | id | start_frame | 4 + * +----------------+----------------+----------------+----------------+ + * | status | 8 + * +----------------+----------------+----------------+----------------+ + * | actual_length | 12 + * +----------------+----------------+----------------+----------------+ + * | error_count | 16 + * +----------------+----------------+----------------+----------------+ + * id - uint16_t, id of the request this response belongs to + * start_frame - uint16_t, start_frame this response (iso requests only) + * status - int32_t, USBIF_STATUS_* (non-iso requests) + * actual_length - uint32_t, actual size of data transferred + * error_count - uint32_t, number of errors (iso requests) */ enum usb_spec_version { - USB_VER_UNKNOWN = 0, - USB_VER_USB11, - USB_VER_USB20, - USB_VER_USB30, /* not supported yet */ + USB_VER_UNKNOWN = 0, + USB_VER_USB11, + USB_VER_USB20, + USB_VER_USB30, /* not supported yet */ }; /* * USB pipe in usbif_request * - * - port number: bits 0-4 - * (USB_MAXCHILDREN is 31) + * - port number: bits 0-4 + * (USB_MAXCHILDREN is 31) * - * - operation flag: bit 5 - * (0 = submit urb, - * 1 = unlink urb) + * - operation flag: bit 5 + * (0 = submit urb, + * 1 = unlink urb) * - * - direction: bit 7 - * (0 = Host-to-Device [Out] - * 1 = Device-to-Host [In]) + * - direction: bit 7 + * (0 = Host-to-Device [Out] + * 1 = Device-to-Host [In]) * - * - device address: bits 8-14 + * - device address: bits 8-14 * - * - endpoint: bits 15-18 + * - endpoint: bits 15-18 * - * - pipe type: bits 30-31 - * (00 = isochronous, 01 = interrupt, - * 10 = control, 11 = bulk) + * - pipe type: bits 30-31 + * (00 = isochronous, 01 = interrupt, + * 10 = control, 11 = bulk) */ -#define USBIF_PIPE_PORT_MASK 0x0000001f -#define USBIF_PIPE_UNLINK 0x00000020 -#define USBIF_PIPE_DIR 0x00000080 -#define USBIF_PIPE_DEV_MASK 0x0000007f -#define USBIF_PIPE_DEV_SHIFT 8 -#define USBIF_PIPE_EP_MASK 0x0000000f -#define USBIF_PIPE_EP_SHIFT 15 -#define USBIF_PIPE_TYPE_MASK 0x00000003 -#define USBIF_PIPE_TYPE_SHIFT 30 -#define USBIF_PIPE_TYPE_ISOC 0 -#define USBIF_PIPE_TYPE_INT 1 -#define USBIF_PIPE_TYPE_CTRL 2 -#define USBIF_PIPE_TYPE_BULK 3 +#define USBIF_PIPE_PORT_MASK 0x0000001f +#define USBIF_PIPE_UNLINK 0x00000020 +#define USBIF_PIPE_DIR 0x00000080 +#define USBIF_PIPE_DEV_MASK 0x0000007f +#define USBIF_PIPE_DEV_SHIFT 8 +#define USBIF_PIPE_EP_MASK 0x0000000f +#define USBIF_PIPE_EP_SHIFT 15 +#define USBIF_PIPE_TYPE_MASK 0x00000003 +#define USBIF_PIPE_TYPE_SHIFT 30 +#define USBIF_PIPE_TYPE_ISOC 0 +#define USBIF_PIPE_TYPE_INT 1 +#define USBIF_PIPE_TYPE_CTRL 2 +#define USBIF_PIPE_TYPE_BULK 3 -#define usbif_pipeportnum(pipe) ((pipe) & USBIF_PIPE_PORT_MASK) -#define usbif_setportnum_pipe(pipe, portnum) ((pipe) | (portnum)) +#define usbif_pipeportnum(pipe) ((pipe) & USBIF_PIPE_PORT_MASK) +#define usbif_setportnum_pipe(pipe, portnum) ((pipe) | (portnum)) -#define usbif_pipeunlink(pipe) ((pipe) & USBIF_PIPE_UNLINK) -#define usbif_pipesubmit(pipe) (!usbif_pipeunlink(pipe)) -#define usbif_setunlink_pipe(pipe) ((pipe) | USBIF_PIPE_UNLINK) +#define usbif_pipeunlink(pipe) ((pipe) & USBIF_PIPE_UNLINK) +#define usbif_pipesubmit(pipe) (!usbif_pipeunlink(pipe)) +#define usbif_setunlink_pipe(pipe) ((pipe) | USBIF_PIPE_UNLINK) -#define usbif_pipein(pipe) ((pipe) & USBIF_PIPE_DIR) -#define usbif_pipeout(pipe) (!usbif_pipein(pipe)) +#define usbif_pipein(pipe) ((pipe) & USBIF_PIPE_DIR) +#define usbif_pipeout(pipe) (!usbif_pipein(pipe)) -#define usbif_pipedevice(pipe) \ - (((pipe) >> USBIF_PIPE_DEV_SHIFT) & USBIF_PIPE_DEV_MASK) +#define usbif_pipedevice(pipe) \ + (((pipe) >> USBIF_PIPE_DEV_SHIFT) & USBIF_PIPE_DEV_MASK) -#define usbif_pipeendpoint(pipe) \ - (((pipe) >> USBIF_PIPE_EP_SHIFT) & USBIF_PIPE_EP_MASK) +#define usbif_pipeendpoint(pipe) \ + (((pipe) >> USBIF_PIPE_EP_SHIFT) & USBIF_PIPE_EP_MASK) -#define usbif_pipetype(pipe) \ - (((pipe) >> USBIF_PIPE_TYPE_SHIFT) & USBIF_PIPE_TYPE_MASK) -#define usbif_pipeisoc(pipe) (usbif_pipetype(pipe) == USBIF_PIPE_TYPE_ISOC) -#define usbif_pipeint(pipe) (usbif_pipetype(pipe) == USBIF_PIPE_TYPE_INT) -#define usbif_pipectrl(pipe) (usbif_pipetype(pipe) == USBIF_PIPE_TYPE_CTRL) -#define usbif_pipebulk(pipe) (usbif_pipetype(pipe) == USBIF_PIPE_TYPE_BULK) +#define usbif_pipetype(pipe) \ + (((pipe) >> USBIF_PIPE_TYPE_SHIFT) & USBIF_PIPE_TYPE_MASK) +#define usbif_pipeisoc(pipe) (usbif_pipetype(pipe) == USBIF_PIPE_TYPE_ISOC) +#define usbif_pipeint(pipe) (usbif_pipetype(pipe) == USBIF_PIPE_TYPE_INT) +#define usbif_pipectrl(pipe) (usbif_pipetype(pipe) == USBIF_PIPE_TYPE_CTRL) +#define usbif_pipebulk(pipe) (usbif_pipetype(pipe) == USBIF_PIPE_TYPE_BULK) #define USBIF_MAX_SEGMENTS_PER_REQUEST (16) -#define USBIF_MAX_PORTNR 31 -#define USBIF_RING_SIZE 4096 +#define USBIF_MAX_PORTNR 31 +#define USBIF_RING_SIZE 4096 /* * RING for transferring urbs. */ struct usbif_request_segment { - grant_ref_t gref; - uint16_t offset; - uint16_t length; + grant_ref_t gref; + uint16_t offset; + uint16_t length; }; struct usbif_urb_request { - uint16_t id; /* request id */ - uint16_t nr_buffer_segs; /* number of urb->transfer_buffer segments */ + uint16_t id; /* request id */ + uint16_t nr_buffer_segs; /* number of urb->transfer_buffer segments */ - /* basic urb parameter */ - uint32_t pipe; - uint16_t transfer_flags; -#define USBIF_SHORT_NOT_OK 0x0001 - uint16_t buffer_length; - union { - uint8_t ctrl[8]; /* setup_packet (Ctrl) */ + /* basic urb parameter */ + uint32_t pipe; + uint16_t transfer_flags; +#define USBIF_SHORT_NOT_OK 0x0001 + uint16_t buffer_length; + union { + uint8_t ctrl[8]; /* setup_packet (Ctrl) */ - struct { - uint16_t interval; /* maximum (1024*8) in usb core */ - uint16_t start_frame; /* start frame */ - uint16_t number_of_packets; /* number of ISO packet */ - uint16_t nr_frame_desc_segs; /* number of iso_frame_desc segments */ - } isoc; + struct { + uint16_t interval; /* maximum (1024*8) in usb core */ + uint16_t start_frame; /* start frame */ + uint16_t number_of_packets; /* number of ISO packet */ + uint16_t nr_frame_desc_segs; /* number of iso_frame_desc segments */ + } isoc; - struct { - uint16_t interval; /* maximum (1024*8) in usb core */ - uint16_t pad[3]; - } intr; + struct { + uint16_t interval; /* maximum (1024*8) in usb core */ + uint16_t pad[3]; + } intr; - struct { - uint16_t unlink_id; /* unlink request id */ - uint16_t pad[3]; - } unlink; + struct { + uint16_t unlink_id; /* unlink request id */ + uint16_t pad[3]; + } unlink; - } u; + } u; - /* urb data segments */ - struct usbif_request_segment seg[USBIF_MAX_SEGMENTS_PER_REQUEST]; + /* urb data segments */ + struct usbif_request_segment seg[USBIF_MAX_SEGMENTS_PER_REQUEST]; }; typedef struct usbif_urb_request usbif_urb_request_t; struct usbif_urb_response { - uint16_t id; /* request id */ - uint16_t start_frame; /* start frame (ISO) */ - int32_t status; /* status (non-ISO) */ - int32_t actual_length; /* actual transfer length */ - int32_t error_count; /* number of ISO errors */ + uint16_t id; /* request id */ + uint16_t start_frame; /* start frame (ISO) */ + int32_t status; /* status (non-ISO) */ +#define USBIF_STATUS_OK 0 +#define USBIF_STATUS_NODEV (-19) +#define USBIF_STATUS_INVAL (-22) +#define USBIF_STATUS_STALL (-32) +#define USBIF_STATUS_IOERROR (-71) +#define USBIF_STATUS_BABBLE (-75) +#define USBIF_STATUS_SHUTDOWN (-108) + int32_t actual_length; /* actual transfer length */ + int32_t error_count; /* number of ISO errors */ }; typedef struct usbif_urb_response usbif_urb_response_t; @@ -233,18 +404,18 @@ DEFINE_RING_TYPES(usbif_urb, struct usbif_urb_request, struct usbif_urb_response * RING for notifying connect/disconnect events to frontend */ struct usbif_conn_request { - uint16_t id; + uint16_t id; }; typedef struct usbif_conn_request usbif_conn_request_t; struct usbif_conn_response { - uint16_t id; /* request id */ - uint8_t portnum; /* port number */ - uint8_t speed; /* usb_device_speed */ -#define USBIF_SPEED_NONE 0 -#define USBIF_SPEED_LOW 1 -#define USBIF_SPEED_FULL 2 -#define USBIF_SPEED_HIGH 3 + uint16_t id; /* request id */ + uint8_t portnum; /* port number */ + uint8_t speed; /* usb_device_speed */ +#define USBIF_SPEED_NONE 0 +#define USBIF_SPEED_LOW 1 +#define USBIF_SPEED_FULL 2 +#define USBIF_SPEED_HIGH 3 }; typedef struct usbif_conn_response usbif_conn_response_t; diff --git a/include/hw/xen/interface/io/xenbus.h b/include/hw/xen/interface/io/xenbus.h index 2fbf2a7fdc..927f9db552 100644 --- a/include/hw/xen/interface/io/xenbus.h +++ b/include/hw/xen/interface/io/xenbus.h @@ -68,3 +68,13 @@ enum xenbus_state { typedef enum xenbus_state XenbusState; #endif /* _XEN_PUBLIC_IO_XENBUS_H */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/io/xs_wire.h b/include/hw/xen/interface/io/xs_wire.h new file mode 100644 index 0000000000..4dd6632669 --- /dev/null +++ b/include/hw/xen/interface/io/xs_wire.h @@ -0,0 +1,153 @@ +/* + * Details of the "wire" protocol between Xen Store Daemon and client + * library or guest kernel. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (C) 2005 Rusty Russell IBM Corporation + */ + +#ifndef _XS_WIRE_H +#define _XS_WIRE_H + +enum xsd_sockmsg_type +{ + XS_CONTROL, +#define XS_DEBUG XS_CONTROL + XS_DIRECTORY, + XS_READ, + XS_GET_PERMS, + XS_WATCH, + XS_UNWATCH, + XS_TRANSACTION_START, + XS_TRANSACTION_END, + XS_INTRODUCE, + XS_RELEASE, + XS_GET_DOMAIN_PATH, + XS_WRITE, + XS_MKDIR, + XS_RM, + XS_SET_PERMS, + XS_WATCH_EVENT, + XS_ERROR, + XS_IS_DOMAIN_INTRODUCED, + XS_RESUME, + XS_SET_TARGET, + /* XS_RESTRICT has been removed */ + XS_RESET_WATCHES = XS_SET_TARGET + 2, + XS_DIRECTORY_PART, + + XS_TYPE_COUNT, /* Number of valid types. */ + + XS_INVALID = 0xffff /* Guaranteed to remain an invalid type */ +}; + +#define XS_WRITE_NONE "NONE" +#define XS_WRITE_CREATE "CREATE" +#define XS_WRITE_CREATE_EXCL "CREATE|EXCL" + +/* We hand errors as strings, for portability. */ +struct xsd_errors +{ + int errnum; + const char *errstring; +}; +#ifdef EINVAL +#define XSD_ERROR(x) { x, #x } +/* LINTED: static unused */ +static struct xsd_errors xsd_errors[] +#if defined(__GNUC__) +__attribute__((unused)) +#endif + = { + XSD_ERROR(EINVAL), + XSD_ERROR(EACCES), + XSD_ERROR(EEXIST), + XSD_ERROR(EISDIR), + XSD_ERROR(ENOENT), + XSD_ERROR(ENOMEM), + XSD_ERROR(ENOSPC), + XSD_ERROR(EIO), + XSD_ERROR(ENOTEMPTY), + XSD_ERROR(ENOSYS), + XSD_ERROR(EROFS), + XSD_ERROR(EBUSY), + XSD_ERROR(EAGAIN), + XSD_ERROR(EISCONN), + XSD_ERROR(E2BIG) +}; +#endif + +struct xsd_sockmsg +{ + uint32_t type; /* XS_??? */ + uint32_t req_id;/* Request identifier, echoed in daemon's response. */ + uint32_t tx_id; /* Transaction id (0 if not related to a transaction). */ + uint32_t len; /* Length of data following this. */ + + /* Generally followed by nul-terminated string(s). */ +}; + +enum xs_watch_type +{ + XS_WATCH_PATH = 0, + XS_WATCH_TOKEN +}; + +/* + * `incontents 150 xenstore_struct XenStore wire protocol. + * + * Inter-domain shared memory communications. */ +#define XENSTORE_RING_SIZE 1024 +typedef uint32_t XENSTORE_RING_IDX; +#define MASK_XENSTORE_IDX(idx) ((idx) & (XENSTORE_RING_SIZE-1)) +struct xenstore_domain_interface { + char req[XENSTORE_RING_SIZE]; /* Requests to xenstore daemon. */ + char rsp[XENSTORE_RING_SIZE]; /* Replies and async watch events. */ + XENSTORE_RING_IDX req_cons, req_prod; + XENSTORE_RING_IDX rsp_cons, rsp_prod; + uint32_t server_features; /* Bitmap of features supported by the server */ + uint32_t connection; +}; + +/* Violating this is very bad. See docs/misc/xenstore.txt. */ +#define XENSTORE_PAYLOAD_MAX 4096 + +/* Violating these just gets you an error back */ +#define XENSTORE_ABS_PATH_MAX 3072 +#define XENSTORE_REL_PATH_MAX 2048 + +/* The ability to reconnect a ring */ +#define XENSTORE_SERVER_FEATURE_RECONNECTION 1 + +/* Valid values for the connection field */ +#define XENSTORE_CONNECTED 0 /* the steady-state */ +#define XENSTORE_RECONNECT 1 /* guest has initiated a reconnect */ + +#endif /* _XS_WIRE_H */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/memory.h b/include/hw/xen/interface/memory.h new file mode 100644 index 0000000000..383a9468c3 --- /dev/null +++ b/include/hw/xen/interface/memory.h @@ -0,0 +1,754 @@ +/****************************************************************************** + * memory.h + * + * Memory reservation and information. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2005, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_MEMORY_H__ +#define __XEN_PUBLIC_MEMORY_H__ + +#include "xen.h" +#include "physdev.h" + +/* + * Increase or decrease the specified domain's memory reservation. Returns the + * number of extents successfully allocated or freed. + * arg == addr of struct xen_memory_reservation. + */ +#define XENMEM_increase_reservation 0 +#define XENMEM_decrease_reservation 1 +#define XENMEM_populate_physmap 6 + +#if __XEN_INTERFACE_VERSION__ >= 0x00030209 +/* + * Maximum # bits addressable by the user of the allocated region (e.g., I/O + * devices often have a 32-bit limitation even in 64-bit systems). If zero + * then the user has no addressing restriction. This field is not used by + * XENMEM_decrease_reservation. + */ +#define XENMEMF_address_bits(x) (x) +#define XENMEMF_get_address_bits(x) ((x) & 0xffu) +/* NUMA node to allocate from. */ +#define XENMEMF_node(x) (((x) + 1) << 8) +#define XENMEMF_get_node(x) ((((x) >> 8) - 1) & 0xffu) +/* Flag to populate physmap with populate-on-demand entries */ +#define XENMEMF_populate_on_demand (1<<16) +/* Flag to request allocation only from the node specified */ +#define XENMEMF_exact_node_request (1<<17) +#define XENMEMF_exact_node(n) (XENMEMF_node(n) | XENMEMF_exact_node_request) +/* Flag to indicate the node specified is virtual node */ +#define XENMEMF_vnode (1<<18) +#endif + +struct xen_memory_reservation { + + /* + * XENMEM_increase_reservation: + * OUT: MFN (*not* GMFN) bases of extents that were allocated + * XENMEM_decrease_reservation: + * IN: GMFN bases of extents to free + * XENMEM_populate_physmap: + * IN: GPFN bases of extents to populate with memory + * OUT: GMFN bases of extents that were allocated + * (NB. This command also updates the mach_to_phys translation table) + * XENMEM_claim_pages: + * IN: must be zero + */ + XEN_GUEST_HANDLE(xen_pfn_t) extent_start; + + /* Number of extents, and size/alignment of each (2^extent_order pages). */ + xen_ulong_t nr_extents; + unsigned int extent_order; + +#if __XEN_INTERFACE_VERSION__ >= 0x00030209 + /* XENMEMF flags. */ + unsigned int mem_flags; +#else + unsigned int address_bits; +#endif + + /* + * Domain whose reservation is being changed. + * Unprivileged domains can specify only DOMID_SELF. + */ + domid_t domid; +}; +typedef struct xen_memory_reservation xen_memory_reservation_t; +DEFINE_XEN_GUEST_HANDLE(xen_memory_reservation_t); + +/* + * An atomic exchange of memory pages. If return code is zero then + * @out.extent_list provides GMFNs of the newly-allocated memory. + * Returns zero on complete success, otherwise a negative error code. + * On complete success then always @nr_exchanged == @in.nr_extents. + * On partial success @nr_exchanged indicates how much work was done. + * + * Note that only PV guests can use this operation. + */ +#define XENMEM_exchange 11 +struct xen_memory_exchange { + /* + * [IN] Details of memory extents to be exchanged (GMFN bases). + * Note that @in.address_bits is ignored and unused. + */ + struct xen_memory_reservation in; + + /* + * [IN/OUT] Details of new memory extents. + * We require that: + * 1. @in.domid == @out.domid + * 2. @in.nr_extents << @in.extent_order == + * @out.nr_extents << @out.extent_order + * 3. @in.extent_start and @out.extent_start lists must not overlap + * 4. @out.extent_start lists GPFN bases to be populated + * 5. @out.extent_start is overwritten with allocated GMFN bases + */ + struct xen_memory_reservation out; + + /* + * [OUT] Number of input extents that were successfully exchanged: + * 1. The first @nr_exchanged input extents were successfully + * deallocated. + * 2. The corresponding first entries in the output extent list correctly + * indicate the GMFNs that were successfully exchanged. + * 3. All other input and output extents are untouched. + * 4. If not all input exents are exchanged then the return code of this + * command will be non-zero. + * 5. THIS FIELD MUST BE INITIALISED TO ZERO BY THE CALLER! + */ + xen_ulong_t nr_exchanged; +}; +typedef struct xen_memory_exchange xen_memory_exchange_t; +DEFINE_XEN_GUEST_HANDLE(xen_memory_exchange_t); + +/* + * Returns the maximum machine frame number of mapped RAM in this system. + * This command always succeeds (it never returns an error code). + * arg == NULL. + */ +#define XENMEM_maximum_ram_page 2 + +struct xen_memory_domain { + /* [IN] Domain information is being queried for. */ + domid_t domid; +}; + +/* + * Returns the current or maximum memory reservation, in pages, of the + * specified domain (may be DOMID_SELF). Returns -ve errcode on failure. + * arg == addr of struct xen_memory_domain. + */ +#define XENMEM_current_reservation 3 +#define XENMEM_maximum_reservation 4 + +/* + * Returns the maximum GFN in use by the specified domain (may be DOMID_SELF). + * Returns -ve errcode on failure. + * arg == addr of struct xen_memory_domain. + */ +#define XENMEM_maximum_gpfn 14 + +/* + * Returns a list of MFN bases of 2MB extents comprising the machine_to_phys + * mapping table. Architectures which do not have a m2p table do not implement + * this command. + * arg == addr of xen_machphys_mfn_list_t. + */ +#define XENMEM_machphys_mfn_list 5 +struct xen_machphys_mfn_list { + /* + * Size of the 'extent_start' array. Fewer entries will be filled if the + * machphys table is smaller than max_extents * 2MB. + */ + unsigned int max_extents; + + /* + * Pointer to buffer to fill with list of extent starts. If there are + * any large discontiguities in the machine address space, 2MB gaps in + * the machphys table will be represented by an MFN base of zero. + */ + XEN_GUEST_HANDLE(xen_pfn_t) extent_start; + + /* + * Number of extents written to the above array. This will be smaller + * than 'max_extents' if the machphys table is smaller than max_e * 2MB. + */ + unsigned int nr_extents; +}; +typedef struct xen_machphys_mfn_list xen_machphys_mfn_list_t; +DEFINE_XEN_GUEST_HANDLE(xen_machphys_mfn_list_t); + +/* + * For a compat caller, this is identical to XENMEM_machphys_mfn_list. + * + * For a non compat caller, this functions similarly to + * XENMEM_machphys_mfn_list, but returns the mfns making up the compatibility + * m2p table. + */ +#define XENMEM_machphys_compat_mfn_list 25 + +/* + * Returns the location in virtual address space of the machine_to_phys + * mapping table. Architectures which do not have a m2p table, or which do not + * map it by default into guest address space, do not implement this command. + * arg == addr of xen_machphys_mapping_t. + */ +#define XENMEM_machphys_mapping 12 +struct xen_machphys_mapping { + xen_ulong_t v_start, v_end; /* Start and end virtual addresses. */ + xen_ulong_t max_mfn; /* Maximum MFN that can be looked up. */ +}; +typedef struct xen_machphys_mapping xen_machphys_mapping_t; +DEFINE_XEN_GUEST_HANDLE(xen_machphys_mapping_t); + +/* Source mapping space. */ +/* ` enum phys_map_space { */ +#define XENMAPSPACE_shared_info 0 /* shared info page */ +#define XENMAPSPACE_grant_table 1 /* grant table page */ +#define XENMAPSPACE_gmfn 2 /* GMFN */ +#define XENMAPSPACE_gmfn_range 3 /* GMFN range, XENMEM_add_to_physmap only. */ +#define XENMAPSPACE_gmfn_foreign 4 /* GMFN from another dom, + * XENMEM_add_to_physmap_batch only. */ +#define XENMAPSPACE_dev_mmio 5 /* device mmio region + ARM only; the region is mapped in + Stage-2 using the Normal Memory + Inner/Outer Write-Back Cacheable + memory attribute. */ +/* ` } */ + +/* + * Sets the GPFN at which a particular page appears in the specified guest's + * physical address space (translated guests only). + * arg == addr of xen_add_to_physmap_t. + */ +#define XENMEM_add_to_physmap 7 +struct xen_add_to_physmap { + /* Which domain to change the mapping for. */ + domid_t domid; + + /* Number of pages to go through for gmfn_range */ + uint16_t size; + + unsigned int space; /* => enum phys_map_space */ + +#define XENMAPIDX_grant_table_status 0x80000000 + + /* Index into space being mapped. */ + xen_ulong_t idx; + + /* GPFN in domid where the source mapping page should appear. */ + xen_pfn_t gpfn; +}; +typedef struct xen_add_to_physmap xen_add_to_physmap_t; +DEFINE_XEN_GUEST_HANDLE(xen_add_to_physmap_t); + +/* A batched version of add_to_physmap. */ +#define XENMEM_add_to_physmap_batch 23 +struct xen_add_to_physmap_batch { + /* IN */ + /* Which domain to change the mapping for. */ + domid_t domid; + uint16_t space; /* => enum phys_map_space */ + + /* Number of pages to go through */ + uint16_t size; + +#if __XEN_INTERFACE_VERSION__ < 0x00040700 + domid_t foreign_domid; /* IFF gmfn_foreign. Should be 0 for other spaces. */ +#else + union xen_add_to_physmap_batch_extra { + domid_t foreign_domid; /* gmfn_foreign */ + uint16_t res0; /* All the other spaces. Should be 0 */ + } u; +#endif + + /* Indexes into space being mapped. */ + XEN_GUEST_HANDLE(xen_ulong_t) idxs; + + /* GPFN in domid where the source mapping page should appear. */ + XEN_GUEST_HANDLE(xen_pfn_t) gpfns; + + /* OUT */ + + /* Per index error code. */ + XEN_GUEST_HANDLE(int) errs; +}; +typedef struct xen_add_to_physmap_batch xen_add_to_physmap_batch_t; +DEFINE_XEN_GUEST_HANDLE(xen_add_to_physmap_batch_t); + +#if __XEN_INTERFACE_VERSION__ < 0x00040400 +#define XENMEM_add_to_physmap_range XENMEM_add_to_physmap_batch +#define xen_add_to_physmap_range xen_add_to_physmap_batch +typedef struct xen_add_to_physmap_batch xen_add_to_physmap_range_t; +DEFINE_XEN_GUEST_HANDLE(xen_add_to_physmap_range_t); +#endif + +/* + * Unmaps the page appearing at a particular GPFN from the specified guest's + * physical address space (translated guests only). + * arg == addr of xen_remove_from_physmap_t. + */ +#define XENMEM_remove_from_physmap 15 +struct xen_remove_from_physmap { + /* Which domain to change the mapping for. */ + domid_t domid; + + /* GPFN of the current mapping of the page. */ + xen_pfn_t gpfn; +}; +typedef struct xen_remove_from_physmap xen_remove_from_physmap_t; +DEFINE_XEN_GUEST_HANDLE(xen_remove_from_physmap_t); + +/*** REMOVED ***/ +/*#define XENMEM_translate_gpfn_list 8*/ + +/* + * Returns the pseudo-physical memory map as it was when the domain + * was started (specified by XENMEM_set_memory_map). + * arg == addr of xen_memory_map_t. + */ +#define XENMEM_memory_map 9 +struct xen_memory_map { + /* + * On call the number of entries which can be stored in buffer. On + * return the number of entries which have been stored in + * buffer. + */ + unsigned int nr_entries; + + /* + * Entries in the buffer are in the same format as returned by the + * BIOS INT 0x15 EAX=0xE820 call. + */ + XEN_GUEST_HANDLE(void) buffer; +}; +typedef struct xen_memory_map xen_memory_map_t; +DEFINE_XEN_GUEST_HANDLE(xen_memory_map_t); + +/* + * Returns the real physical memory map. Passes the same structure as + * XENMEM_memory_map. + * Specifying buffer as NULL will return the number of entries required + * to store the complete memory map. + * arg == addr of xen_memory_map_t. + */ +#define XENMEM_machine_memory_map 10 + +/* + * Set the pseudo-physical memory map of a domain, as returned by + * XENMEM_memory_map. + * arg == addr of xen_foreign_memory_map_t. + */ +#define XENMEM_set_memory_map 13 +struct xen_foreign_memory_map { + domid_t domid; + struct xen_memory_map map; +}; +typedef struct xen_foreign_memory_map xen_foreign_memory_map_t; +DEFINE_XEN_GUEST_HANDLE(xen_foreign_memory_map_t); + +#define XENMEM_set_pod_target 16 +#define XENMEM_get_pod_target 17 +struct xen_pod_target { + /* IN */ + uint64_t target_pages; + /* OUT */ + uint64_t tot_pages; + uint64_t pod_cache_pages; + uint64_t pod_entries; + /* IN */ + domid_t domid; +}; +typedef struct xen_pod_target xen_pod_target_t; + +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +#ifndef uint64_aligned_t +#define uint64_aligned_t uint64_t +#endif + +/* + * Get the number of MFNs saved through memory sharing. + * The call never fails. + */ +#define XENMEM_get_sharing_freed_pages 18 +#define XENMEM_get_sharing_shared_pages 19 + +#define XENMEM_paging_op 20 +#define XENMEM_paging_op_nominate 0 +#define XENMEM_paging_op_evict 1 +#define XENMEM_paging_op_prep 2 + +struct xen_mem_paging_op { + uint8_t op; /* XENMEM_paging_op_* */ + domid_t domain; + + /* IN: (XENMEM_paging_op_prep) buffer to immediately fill page from */ + XEN_GUEST_HANDLE_64(const_uint8) buffer; + /* IN: gfn of page being operated on */ + uint64_aligned_t gfn; +}; +typedef struct xen_mem_paging_op xen_mem_paging_op_t; +DEFINE_XEN_GUEST_HANDLE(xen_mem_paging_op_t); + +#define XENMEM_access_op 21 +#define XENMEM_access_op_set_access 0 +#define XENMEM_access_op_get_access 1 +/* + * XENMEM_access_op_enable_emulate and XENMEM_access_op_disable_emulate are + * currently unused, but since they have been in use please do not reuse them. + * + * #define XENMEM_access_op_enable_emulate 2 + * #define XENMEM_access_op_disable_emulate 3 + */ +#define XENMEM_access_op_set_access_multi 4 + +typedef enum { + XENMEM_access_n, + XENMEM_access_r, + XENMEM_access_w, + XENMEM_access_rw, + XENMEM_access_x, + XENMEM_access_rx, + XENMEM_access_wx, + XENMEM_access_rwx, + /* + * Page starts off as r-x, but automatically + * change to r-w on a write + */ + XENMEM_access_rx2rw, + /* + * Log access: starts off as n, automatically + * goes to rwx, generating an event without + * pausing the vcpu + */ + XENMEM_access_n2rwx, + /* Take the domain default */ + XENMEM_access_default +} xenmem_access_t; + +struct xen_mem_access_op { + /* XENMEM_access_op_* */ + uint8_t op; + /* xenmem_access_t */ + uint8_t access; + domid_t domid; + /* + * Number of pages for set op (or size of pfn_list for + * XENMEM_access_op_set_access_multi) + * Ignored on setting default access and other ops + */ + uint32_t nr; + /* + * First pfn for set op + * pfn for get op + * ~0ull is used to set and get the default access for pages + */ + uint64_aligned_t pfn; + /* + * List of pfns to set access for + * Used only with XENMEM_access_op_set_access_multi + */ + XEN_GUEST_HANDLE(const_uint64) pfn_list; + /* + * Corresponding list of access settings for pfn_list + * Used only with XENMEM_access_op_set_access_multi + */ + XEN_GUEST_HANDLE(const_uint8) access_list; +}; +typedef struct xen_mem_access_op xen_mem_access_op_t; +DEFINE_XEN_GUEST_HANDLE(xen_mem_access_op_t); + +#define XENMEM_sharing_op 22 +#define XENMEM_sharing_op_nominate_gfn 0 +#define XENMEM_sharing_op_nominate_gref 1 +#define XENMEM_sharing_op_share 2 +#define XENMEM_sharing_op_debug_gfn 3 +#define XENMEM_sharing_op_debug_mfn 4 +#define XENMEM_sharing_op_debug_gref 5 +#define XENMEM_sharing_op_add_physmap 6 +#define XENMEM_sharing_op_audit 7 +#define XENMEM_sharing_op_range_share 8 +#define XENMEM_sharing_op_fork 9 +#define XENMEM_sharing_op_fork_reset 10 + +#define XENMEM_SHARING_OP_S_HANDLE_INVALID (-10) +#define XENMEM_SHARING_OP_C_HANDLE_INVALID (-9) + +/* The following allows sharing of grant refs. This is useful + * for sharing utilities sitting as "filters" in IO backends + * (e.g. memshr + blktap(2)). The IO backend is only exposed + * to grant references, and this allows sharing of the grefs */ +#define XENMEM_SHARING_OP_FIELD_IS_GREF_FLAG (xen_mk_ullong(1) << 62) + +#define XENMEM_SHARING_OP_FIELD_MAKE_GREF(field, val) \ + (field) = (XENMEM_SHARING_OP_FIELD_IS_GREF_FLAG | val) +#define XENMEM_SHARING_OP_FIELD_IS_GREF(field) \ + ((field) & XENMEM_SHARING_OP_FIELD_IS_GREF_FLAG) +#define XENMEM_SHARING_OP_FIELD_GET_GREF(field) \ + ((field) & (~XENMEM_SHARING_OP_FIELD_IS_GREF_FLAG)) + +struct xen_mem_sharing_op { + uint8_t op; /* XENMEM_sharing_op_* */ + domid_t domain; + + union { + struct mem_sharing_op_nominate { /* OP_NOMINATE_xxx */ + union { + uint64_aligned_t gfn; /* IN: gfn to nominate */ + uint32_t grant_ref; /* IN: grant ref to nominate */ + } u; + uint64_aligned_t handle; /* OUT: the handle */ + } nominate; + struct mem_sharing_op_share { /* OP_SHARE/ADD_PHYSMAP */ + uint64_aligned_t source_gfn; /* IN: the gfn of the source page */ + uint64_aligned_t source_handle; /* IN: handle to the source page */ + uint64_aligned_t client_gfn; /* IN: the client gfn */ + uint64_aligned_t client_handle; /* IN: handle to the client page */ + domid_t client_domain; /* IN: the client domain id */ + } share; + struct mem_sharing_op_range { /* OP_RANGE_SHARE */ + uint64_aligned_t first_gfn; /* IN: the first gfn */ + uint64_aligned_t last_gfn; /* IN: the last gfn */ + uint64_aligned_t opaque; /* Must be set to 0 */ + domid_t client_domain; /* IN: the client domain id */ + uint16_t _pad[3]; /* Must be set to 0 */ + } range; + struct mem_sharing_op_debug { /* OP_DEBUG_xxx */ + union { + uint64_aligned_t gfn; /* IN: gfn to debug */ + uint64_aligned_t mfn; /* IN: mfn to debug */ + uint32_t gref; /* IN: gref to debug */ + } u; + } debug; + struct mem_sharing_op_fork { /* OP_FORK */ + domid_t parent_domain; /* IN: parent's domain id */ +/* Only makes sense for short-lived forks */ +#define XENMEM_FORK_WITH_IOMMU_ALLOWED (1u << 0) +/* Only makes sense for short-lived forks */ +#define XENMEM_FORK_BLOCK_INTERRUPTS (1u << 1) + uint16_t flags; /* IN: optional settings */ + uint32_t pad; /* Must be set to 0 */ + } fork; + } u; +}; +typedef struct xen_mem_sharing_op xen_mem_sharing_op_t; +DEFINE_XEN_GUEST_HANDLE(xen_mem_sharing_op_t); + +/* + * Attempt to stake a claim for a domain on a quantity of pages + * of system RAM, but _not_ assign specific pageframes. Only + * arithmetic is performed so the hypercall is very fast and need + * not be preemptible, thus sidestepping time-of-check-time-of-use + * races for memory allocation. Returns 0 if the hypervisor page + * allocator has atomically and successfully claimed the requested + * number of pages, else non-zero. + * + * Any domain may have only one active claim. When sufficient memory + * has been allocated to resolve the claim, the claim silently expires. + * Claiming zero pages effectively resets any outstanding claim and + * is always successful. + * + * Note that a valid claim may be staked even after memory has been + * allocated for a domain. In this case, the claim is not incremental, + * i.e. if the domain's total page count is 3, and a claim is staked + * for 10, only 7 additional pages are claimed. + * + * Caller must be privileged or the hypercall fails. + */ +#define XENMEM_claim_pages 24 + +/* + * XENMEM_claim_pages flags - the are no flags at this time. + * The zero value is appropriate. + */ + +/* + * With some legacy devices, certain guest-physical addresses cannot safely + * be used for other purposes, e.g. to map guest RAM. This hypercall + * enumerates those regions so the toolstack can avoid using them. + */ +#define XENMEM_reserved_device_memory_map 27 +struct xen_reserved_device_memory { + xen_pfn_t start_pfn; + xen_ulong_t nr_pages; +}; +typedef struct xen_reserved_device_memory xen_reserved_device_memory_t; +DEFINE_XEN_GUEST_HANDLE(xen_reserved_device_memory_t); + +struct xen_reserved_device_memory_map { +#define XENMEM_RDM_ALL 1 /* Request all regions (ignore dev union). */ + /* IN */ + uint32_t flags; + /* + * IN/OUT + * + * Gets set to the required number of entries when too low, + * signaled by error code -ERANGE. + */ + unsigned int nr_entries; + /* OUT */ + XEN_GUEST_HANDLE(xen_reserved_device_memory_t) buffer; + /* IN */ + union { + physdev_pci_device_t pci; + } dev; +}; +typedef struct xen_reserved_device_memory_map xen_reserved_device_memory_map_t; +DEFINE_XEN_GUEST_HANDLE(xen_reserved_device_memory_map_t); + +#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ + +/* + * Get the pages for a particular guest resource, so that they can be + * mapped directly by a tools domain. + */ +#define XENMEM_acquire_resource 28 +struct xen_mem_acquire_resource { + /* IN - The domain whose resource is to be mapped */ + domid_t domid; + /* IN - the type of resource */ + uint16_t type; + +#define XENMEM_resource_ioreq_server 0 +#define XENMEM_resource_grant_table 1 +#define XENMEM_resource_vmtrace_buf 2 + + /* + * IN - a type-specific resource identifier, which must be zero + * unless stated otherwise. + * + * type == XENMEM_resource_ioreq_server -> id == ioreq server id + * type == XENMEM_resource_grant_table -> id defined below + */ + uint32_t id; + +#define XENMEM_resource_grant_table_id_shared 0 +#define XENMEM_resource_grant_table_id_status 1 + + /* + * IN/OUT + * + * As an IN parameter number of frames of the resource to be mapped. + * This value may be updated over the course of the operation. + * + * When frame_list is NULL and nr_frames is 0, this is interpreted as a + * request for the size of the resource, which shall be returned in the + * nr_frames field. + * + * The size of a resource will never be zero, but a nonzero result doesn't + * guarantee that a subsequent mapping request will be successful. There + * are further type/id specific constraints which may change between the + * two calls. + */ + uint32_t nr_frames; + uint32_t pad; + /* + * IN - the index of the initial frame to be mapped. This parameter + * is ignored if nr_frames is 0. This value may be updated + * over the course of the operation. + */ + uint64_t frame; + +#define XENMEM_resource_ioreq_server_frame_bufioreq 0 +#define XENMEM_resource_ioreq_server_frame_ioreq(n) (1 + (n)) + + /* + * IN/OUT - If the tools domain is PV then, upon return, frame_list + * will be populated with the MFNs of the resource. + * If the tools domain is HVM then it is expected that, on + * entry, frame_list will be populated with a list of GFNs + * that will be mapped to the MFNs of the resource. + * If -EIO is returned then the frame_list has only been + * partially mapped and it is up to the caller to unmap all + * the GFNs. + * This parameter may be NULL if nr_frames is 0. This + * value may be updated over the course of the operation. + */ + XEN_GUEST_HANDLE(xen_pfn_t) frame_list; +}; +typedef struct xen_mem_acquire_resource xen_mem_acquire_resource_t; +DEFINE_XEN_GUEST_HANDLE(xen_mem_acquire_resource_t); + +/* + * XENMEM_get_vnumainfo used by guest to get + * vNUMA topology from hypervisor. + */ +#define XENMEM_get_vnumainfo 26 + +/* vNUMA node memory ranges */ +struct xen_vmemrange { + uint64_t start, end; + unsigned int flags; + unsigned int nid; +}; +typedef struct xen_vmemrange xen_vmemrange_t; +DEFINE_XEN_GUEST_HANDLE(xen_vmemrange_t); + +/* + * vNUMA topology specifies vNUMA node number, distance table, + * memory ranges and vcpu mapping provided for guests. + * XENMEM_get_vnumainfo hypercall expects to see from guest + * nr_vnodes, nr_vmemranges and nr_vcpus to indicate available memory. + * After filling guests structures, nr_vnodes, nr_vmemranges and nr_vcpus + * copied back to guest. Domain returns expected values of nr_vnodes, + * nr_vmemranges and nr_vcpus to guest if the values where incorrect. + */ +struct xen_vnuma_topology_info { + /* IN */ + domid_t domid; + uint16_t pad; + /* IN/OUT */ + unsigned int nr_vnodes; + unsigned int nr_vcpus; + unsigned int nr_vmemranges; + /* OUT */ + union { + XEN_GUEST_HANDLE(uint) h; + uint64_t pad; + } vdistance; + union { + XEN_GUEST_HANDLE(uint) h; + uint64_t pad; + } vcpu_to_vnode; + union { + XEN_GUEST_HANDLE(xen_vmemrange_t) h; + uint64_t pad; + } vmemrange; +}; +typedef struct xen_vnuma_topology_info xen_vnuma_topology_info_t; +DEFINE_XEN_GUEST_HANDLE(xen_vnuma_topology_info_t); + +/* Next available subop number is 29 */ + +#endif /* __XEN_PUBLIC_MEMORY_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/physdev.h b/include/hw/xen/interface/physdev.h new file mode 100644 index 0000000000..d271766ad0 --- /dev/null +++ b/include/hw/xen/interface/physdev.h @@ -0,0 +1,383 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2006, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_PHYSDEV_H__ +#define __XEN_PUBLIC_PHYSDEV_H__ + +#include "xen.h" + +/* + * Prototype for this hypercall is: + * int physdev_op(int cmd, void *args) + * @cmd == PHYSDEVOP_??? (physdev operation). + * @args == Operation-specific extra arguments (NULL if none). + */ + +/* + * Notify end-of-interrupt (EOI) for the specified IRQ. + * @arg == pointer to physdev_eoi structure. + */ +#define PHYSDEVOP_eoi 12 +struct physdev_eoi { + /* IN */ + uint32_t irq; +}; +typedef struct physdev_eoi physdev_eoi_t; +DEFINE_XEN_GUEST_HANDLE(physdev_eoi_t); + +/* + * Register a shared page for the hypervisor to indicate whether the guest + * must issue PHYSDEVOP_eoi. The semantics of PHYSDEVOP_eoi change slightly + * once the guest used this function in that the associated event channel + * will automatically get unmasked. The page registered is used as a bit + * array indexed by Xen's PIRQ value. + */ +#define PHYSDEVOP_pirq_eoi_gmfn_v1 17 +/* + * Register a shared page for the hypervisor to indicate whether the + * guest must issue PHYSDEVOP_eoi. This hypercall is very similar to + * PHYSDEVOP_pirq_eoi_gmfn_v1 but it doesn't change the semantics of + * PHYSDEVOP_eoi. The page registered is used as a bit array indexed by + * Xen's PIRQ value. + */ +#define PHYSDEVOP_pirq_eoi_gmfn_v2 28 +struct physdev_pirq_eoi_gmfn { + /* IN */ + xen_pfn_t gmfn; +}; +typedef struct physdev_pirq_eoi_gmfn physdev_pirq_eoi_gmfn_t; +DEFINE_XEN_GUEST_HANDLE(physdev_pirq_eoi_gmfn_t); + +/* + * Query the status of an IRQ line. + * @arg == pointer to physdev_irq_status_query structure. + */ +#define PHYSDEVOP_irq_status_query 5 +struct physdev_irq_status_query { + /* IN */ + uint32_t irq; + /* OUT */ + uint32_t flags; /* XENIRQSTAT_* */ +}; +typedef struct physdev_irq_status_query physdev_irq_status_query_t; +DEFINE_XEN_GUEST_HANDLE(physdev_irq_status_query_t); + +/* Need to call PHYSDEVOP_eoi when the IRQ has been serviced? */ +#define _XENIRQSTAT_needs_eoi (0) +#define XENIRQSTAT_needs_eoi (1U<<_XENIRQSTAT_needs_eoi) + +/* IRQ shared by multiple guests? */ +#define _XENIRQSTAT_shared (1) +#define XENIRQSTAT_shared (1U<<_XENIRQSTAT_shared) + +/* + * Set the current VCPU's I/O privilege level. + * @arg == pointer to physdev_set_iopl structure. + */ +#define PHYSDEVOP_set_iopl 6 +struct physdev_set_iopl { + /* IN */ + uint32_t iopl; +}; +typedef struct physdev_set_iopl physdev_set_iopl_t; +DEFINE_XEN_GUEST_HANDLE(physdev_set_iopl_t); + +/* + * Set the current VCPU's I/O-port permissions bitmap. + * @arg == pointer to physdev_set_iobitmap structure. + */ +#define PHYSDEVOP_set_iobitmap 7 +struct physdev_set_iobitmap { + /* IN */ +#if __XEN_INTERFACE_VERSION__ >= 0x00030205 + XEN_GUEST_HANDLE(uint8) bitmap; +#else + uint8_t *bitmap; +#endif + uint32_t nr_ports; +}; +typedef struct physdev_set_iobitmap physdev_set_iobitmap_t; +DEFINE_XEN_GUEST_HANDLE(physdev_set_iobitmap_t); + +/* + * Read or write an IO-APIC register. + * @arg == pointer to physdev_apic structure. + */ +#define PHYSDEVOP_apic_read 8 +#define PHYSDEVOP_apic_write 9 +struct physdev_apic { + /* IN */ + unsigned long apic_physbase; + uint32_t reg; + /* IN or OUT */ + uint32_t value; +}; +typedef struct physdev_apic physdev_apic_t; +DEFINE_XEN_GUEST_HANDLE(physdev_apic_t); + +/* + * Allocate or free a physical upcall vector for the specified IRQ line. + * @arg == pointer to physdev_irq structure. + */ +#define PHYSDEVOP_alloc_irq_vector 10 +#define PHYSDEVOP_free_irq_vector 11 +struct physdev_irq { + /* IN */ + uint32_t irq; + /* IN or OUT */ + uint32_t vector; +}; +typedef struct physdev_irq physdev_irq_t; +DEFINE_XEN_GUEST_HANDLE(physdev_irq_t); + +#define MAP_PIRQ_TYPE_MSI 0x0 +#define MAP_PIRQ_TYPE_GSI 0x1 +#define MAP_PIRQ_TYPE_UNKNOWN 0x2 +#define MAP_PIRQ_TYPE_MSI_SEG 0x3 +#define MAP_PIRQ_TYPE_MULTI_MSI 0x4 + +#define PHYSDEVOP_map_pirq 13 +struct physdev_map_pirq { + domid_t domid; + /* IN */ + int type; + /* IN (ignored for ..._MULTI_MSI) */ + int index; + /* IN or OUT */ + int pirq; + /* IN - high 16 bits hold segment for ..._MSI_SEG and ..._MULTI_MSI */ + int bus; + /* IN */ + int devfn; + /* IN (also OUT for ..._MULTI_MSI) */ + int entry_nr; + /* IN */ + uint64_t table_base; +}; +typedef struct physdev_map_pirq physdev_map_pirq_t; +DEFINE_XEN_GUEST_HANDLE(physdev_map_pirq_t); + +#define PHYSDEVOP_unmap_pirq 14 +struct physdev_unmap_pirq { + domid_t domid; + /* IN */ + int pirq; +}; + +typedef struct physdev_unmap_pirq physdev_unmap_pirq_t; +DEFINE_XEN_GUEST_HANDLE(physdev_unmap_pirq_t); + +#define PHYSDEVOP_manage_pci_add 15 +#define PHYSDEVOP_manage_pci_remove 16 +struct physdev_manage_pci { + /* IN */ + uint8_t bus; + uint8_t devfn; +}; + +typedef struct physdev_manage_pci physdev_manage_pci_t; +DEFINE_XEN_GUEST_HANDLE(physdev_manage_pci_t); + +#define PHYSDEVOP_restore_msi 19 +struct physdev_restore_msi { + /* IN */ + uint8_t bus; + uint8_t devfn; +}; +typedef struct physdev_restore_msi physdev_restore_msi_t; +DEFINE_XEN_GUEST_HANDLE(physdev_restore_msi_t); + +#define PHYSDEVOP_manage_pci_add_ext 20 +struct physdev_manage_pci_ext { + /* IN */ + uint8_t bus; + uint8_t devfn; + unsigned is_extfn; + unsigned is_virtfn; + struct { + uint8_t bus; + uint8_t devfn; + } physfn; +}; + +typedef struct physdev_manage_pci_ext physdev_manage_pci_ext_t; +DEFINE_XEN_GUEST_HANDLE(physdev_manage_pci_ext_t); + +/* + * Argument to physdev_op_compat() hypercall. Superceded by new physdev_op() + * hypercall since 0x00030202. + */ +struct physdev_op { + uint32_t cmd; + union { + physdev_irq_status_query_t irq_status_query; + physdev_set_iopl_t set_iopl; + physdev_set_iobitmap_t set_iobitmap; + physdev_apic_t apic_op; + physdev_irq_t irq_op; + } u; +}; +typedef struct physdev_op physdev_op_t; +DEFINE_XEN_GUEST_HANDLE(physdev_op_t); + +#define PHYSDEVOP_setup_gsi 21 +struct physdev_setup_gsi { + int gsi; + /* IN */ + uint8_t triggering; + /* IN */ + uint8_t polarity; + /* IN */ +}; + +typedef struct physdev_setup_gsi physdev_setup_gsi_t; +DEFINE_XEN_GUEST_HANDLE(physdev_setup_gsi_t); + +/* leave PHYSDEVOP 22 free */ + +/* type is MAP_PIRQ_TYPE_GSI or MAP_PIRQ_TYPE_MSI + * the hypercall returns a free pirq */ +#define PHYSDEVOP_get_free_pirq 23 +struct physdev_get_free_pirq { + /* IN */ + int type; + /* OUT */ + uint32_t pirq; +}; + +typedef struct physdev_get_free_pirq physdev_get_free_pirq_t; +DEFINE_XEN_GUEST_HANDLE(physdev_get_free_pirq_t); + +#define XEN_PCI_MMCFG_RESERVED 0x1 + +#define PHYSDEVOP_pci_mmcfg_reserved 24 +struct physdev_pci_mmcfg_reserved { + uint64_t address; + uint16_t segment; + uint8_t start_bus; + uint8_t end_bus; + uint32_t flags; +}; +typedef struct physdev_pci_mmcfg_reserved physdev_pci_mmcfg_reserved_t; +DEFINE_XEN_GUEST_HANDLE(physdev_pci_mmcfg_reserved_t); + +#define XEN_PCI_DEV_EXTFN 0x1 +#define XEN_PCI_DEV_VIRTFN 0x2 +#define XEN_PCI_DEV_PXM 0x4 + +#define PHYSDEVOP_pci_device_add 25 +struct physdev_pci_device_add { + /* IN */ + uint16_t seg; + uint8_t bus; + uint8_t devfn; + uint32_t flags; + struct { + uint8_t bus; + uint8_t devfn; + } physfn; + /* + * Optional parameters array. + * First element ([0]) is PXM domain associated with the device (if + * XEN_PCI_DEV_PXM is set) + */ + uint32_t optarr[XEN_FLEX_ARRAY_DIM]; +}; +typedef struct physdev_pci_device_add physdev_pci_device_add_t; +DEFINE_XEN_GUEST_HANDLE(physdev_pci_device_add_t); + +#define PHYSDEVOP_pci_device_remove 26 +#define PHYSDEVOP_restore_msi_ext 27 +/* + * Dom0 should use these two to announce MMIO resources assigned to + * MSI-X capable devices won't (prepare) or may (release) change. + */ +#define PHYSDEVOP_prepare_msix 30 +#define PHYSDEVOP_release_msix 31 +struct physdev_pci_device { + /* IN */ + uint16_t seg; + uint8_t bus; + uint8_t devfn; +}; +typedef struct physdev_pci_device physdev_pci_device_t; +DEFINE_XEN_GUEST_HANDLE(physdev_pci_device_t); + +#define PHYSDEVOP_DBGP_RESET_PREPARE 1 +#define PHYSDEVOP_DBGP_RESET_DONE 2 + +#define PHYSDEVOP_DBGP_BUS_UNKNOWN 0 +#define PHYSDEVOP_DBGP_BUS_PCI 1 + +#define PHYSDEVOP_dbgp_op 29 +struct physdev_dbgp_op { + /* IN */ + uint8_t op; + uint8_t bus; + union { + physdev_pci_device_t pci; + } u; +}; +typedef struct physdev_dbgp_op physdev_dbgp_op_t; +DEFINE_XEN_GUEST_HANDLE(physdev_dbgp_op_t); + +/* + * Notify that some PIRQ-bound event channels have been unmasked. + * ** This command is obsolete since interface version 0x00030202 and is ** + * ** unsupported by newer versions of Xen. ** + */ +#define PHYSDEVOP_IRQ_UNMASK_NOTIFY 4 + +#if __XEN_INTERFACE_VERSION__ < 0x00040600 +/* + * These all-capitals physdev operation names are superceded by the new names + * (defined above) since interface version 0x00030202. The guard above was + * added post-4.5 only though and hence shouldn't check for 0x00030202. + */ +#define PHYSDEVOP_IRQ_STATUS_QUERY PHYSDEVOP_irq_status_query +#define PHYSDEVOP_SET_IOPL PHYSDEVOP_set_iopl +#define PHYSDEVOP_SET_IOBITMAP PHYSDEVOP_set_iobitmap +#define PHYSDEVOP_APIC_READ PHYSDEVOP_apic_read +#define PHYSDEVOP_APIC_WRITE PHYSDEVOP_apic_write +#define PHYSDEVOP_ASSIGN_VECTOR PHYSDEVOP_alloc_irq_vector +#define PHYSDEVOP_FREE_VECTOR PHYSDEVOP_free_irq_vector +#define PHYSDEVOP_IRQ_NEEDS_UNMASK_NOTIFY XENIRQSTAT_needs_eoi +#define PHYSDEVOP_IRQ_SHARED XENIRQSTAT_shared +#endif + +#if __XEN_INTERFACE_VERSION__ < 0x00040200 +#define PHYSDEVOP_pirq_eoi_gmfn PHYSDEVOP_pirq_eoi_gmfn_v1 +#else +#define PHYSDEVOP_pirq_eoi_gmfn PHYSDEVOP_pirq_eoi_gmfn_v2 +#endif + +#endif /* __XEN_PUBLIC_PHYSDEV_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/sched.h b/include/hw/xen/interface/sched.h new file mode 100644 index 0000000000..811bd87c82 --- /dev/null +++ b/include/hw/xen/interface/sched.h @@ -0,0 +1,202 @@ +/****************************************************************************** + * sched.h + * + * Scheduler state interactions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2005, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_SCHED_H__ +#define __XEN_PUBLIC_SCHED_H__ + +#include "event_channel.h" + +/* + * `incontents 150 sched Guest Scheduler Operations + * + * The SCHEDOP interface provides mechanisms for a guest to interact + * with the scheduler, including yield, blocking and shutting itself + * down. + */ + +/* + * The prototype for this hypercall is: + * ` long HYPERVISOR_sched_op(enum sched_op cmd, void *arg, ...) + * + * @cmd == SCHEDOP_??? (scheduler operation). + * @arg == Operation-specific extra argument(s), as described below. + * ... == Additional Operation-specific extra arguments, described below. + * + * Versions of Xen prior to 3.0.2 provided only the following legacy version + * of this hypercall, supporting only the commands yield, block and shutdown: + * long sched_op(int cmd, unsigned long arg) + * @cmd == SCHEDOP_??? (scheduler operation). + * @arg == 0 (SCHEDOP_yield and SCHEDOP_block) + * == SHUTDOWN_* code (SCHEDOP_shutdown) + * + * This legacy version is available to new guests as: + * ` long HYPERVISOR_sched_op_compat(enum sched_op cmd, unsigned long arg) + */ + +/* ` enum sched_op { // SCHEDOP_* => struct sched_* */ +/* + * Voluntarily yield the CPU. + * @arg == NULL. + */ +#define SCHEDOP_yield 0 + +/* + * Block execution of this VCPU until an event is received for processing. + * If called with event upcalls masked, this operation will atomically + * reenable event delivery and check for pending events before blocking the + * VCPU. This avoids a "wakeup waiting" race. + * @arg == NULL. + */ +#define SCHEDOP_block 1 + +/* + * Halt execution of this domain (all VCPUs) and notify the system controller. + * @arg == pointer to sched_shutdown_t structure. + * + * If the sched_shutdown_t reason is SHUTDOWN_suspend then + * x86 PV guests must also set RDX (EDX for 32-bit guests) to the MFN + * of the guest's start info page. RDX/EDX is the third hypercall + * argument. + * + * In addition, which reason is SHUTDOWN_suspend this hypercall + * returns 1 if suspend was cancelled or the domain was merely + * checkpointed, and 0 if it is resuming in a new domain. + */ +#define SCHEDOP_shutdown 2 + +/* + * Poll a set of event-channel ports. Return when one or more are pending. An + * optional timeout may be specified. + * @arg == pointer to sched_poll_t structure. + */ +#define SCHEDOP_poll 3 + +/* + * Declare a shutdown for another domain. The main use of this function is + * in interpreting shutdown requests and reasons for fully-virtualized + * domains. A para-virtualized domain may use SCHEDOP_shutdown directly. + * @arg == pointer to sched_remote_shutdown_t structure. + */ +#define SCHEDOP_remote_shutdown 4 + +/* + * Latch a shutdown code, so that when the domain later shuts down it + * reports this code to the control tools. + * @arg == sched_shutdown_t, as for SCHEDOP_shutdown. + */ +#define SCHEDOP_shutdown_code 5 + +/* + * Setup, poke and destroy a domain watchdog timer. + * @arg == pointer to sched_watchdog_t structure. + * With id == 0, setup a domain watchdog timer to cause domain shutdown + * after timeout, returns watchdog id. + * With id != 0 and timeout == 0, destroy domain watchdog timer. + * With id != 0 and timeout != 0, poke watchdog timer and set new timeout. + */ +#define SCHEDOP_watchdog 6 + +/* + * Override the current vcpu affinity by pinning it to one physical cpu or + * undo this override restoring the previous affinity. + * @arg == pointer to sched_pin_override_t structure. + * + * A negative pcpu value will undo a previous pin override and restore the + * previous cpu affinity. + * This call is allowed for the hardware domain only and requires the cpu + * to be part of the domain's cpupool. + */ +#define SCHEDOP_pin_override 7 +/* ` } */ + +struct sched_shutdown { + unsigned int reason; /* SHUTDOWN_* => enum sched_shutdown_reason */ +}; +typedef struct sched_shutdown sched_shutdown_t; +DEFINE_XEN_GUEST_HANDLE(sched_shutdown_t); + +struct sched_poll { + XEN_GUEST_HANDLE(evtchn_port_t) ports; + unsigned int nr_ports; + uint64_t timeout; +}; +typedef struct sched_poll sched_poll_t; +DEFINE_XEN_GUEST_HANDLE(sched_poll_t); + +struct sched_remote_shutdown { + domid_t domain_id; /* Remote domain ID */ + unsigned int reason; /* SHUTDOWN_* => enum sched_shutdown_reason */ +}; +typedef struct sched_remote_shutdown sched_remote_shutdown_t; +DEFINE_XEN_GUEST_HANDLE(sched_remote_shutdown_t); + +struct sched_watchdog { + uint32_t id; /* watchdog ID */ + uint32_t timeout; /* timeout */ +}; +typedef struct sched_watchdog sched_watchdog_t; +DEFINE_XEN_GUEST_HANDLE(sched_watchdog_t); + +struct sched_pin_override { + int32_t pcpu; +}; +typedef struct sched_pin_override sched_pin_override_t; +DEFINE_XEN_GUEST_HANDLE(sched_pin_override_t); + +/* + * Reason codes for SCHEDOP_shutdown. These may be interpreted by control + * software to determine the appropriate action. For the most part, Xen does + * not care about the shutdown code. + */ +/* ` enum sched_shutdown_reason { */ +#define SHUTDOWN_poweroff 0 /* Domain exited normally. Clean up and kill. */ +#define SHUTDOWN_reboot 1 /* Clean up, kill, and then restart. */ +#define SHUTDOWN_suspend 2 /* Clean up, save suspend info, kill. */ +#define SHUTDOWN_crash 3 /* Tell controller we've crashed. */ +#define SHUTDOWN_watchdog 4 /* Restart because watchdog time expired. */ + +/* + * Domain asked to perform 'soft reset' for it. The expected behavior is to + * reset internal Xen state for the domain returning it to the point where it + * was created but leaving the domain's memory contents and vCPU contexts + * intact. This will allow the domain to start over and set up all Xen specific + * interfaces again. + */ +#define SHUTDOWN_soft_reset 5 +#define SHUTDOWN_MAX 5 /* Maximum valid shutdown reason. */ +/* ` } */ + +#endif /* __XEN_PUBLIC_SCHED_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/trace.h b/include/hw/xen/interface/trace.h new file mode 100644 index 0000000000..d5fa4aea8d --- /dev/null +++ b/include/hw/xen/interface/trace.h @@ -0,0 +1,341 @@ +/****************************************************************************** + * include/public/trace.h + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Mark Williamson, (C) 2004 Intel Research Cambridge + * Copyright (C) 2005 Bin Ren + */ + +#ifndef __XEN_PUBLIC_TRACE_H__ +#define __XEN_PUBLIC_TRACE_H__ + +#define TRACE_EXTRA_MAX 7 +#define TRACE_EXTRA_SHIFT 28 + +/* Trace classes */ +#define TRC_CLS_SHIFT 16 +#define TRC_GEN 0x0001f000 /* General trace */ +#define TRC_SCHED 0x0002f000 /* Xen Scheduler trace */ +#define TRC_DOM0OP 0x0004f000 /* Xen DOM0 operation trace */ +#define TRC_HVM 0x0008f000 /* Xen HVM trace */ +#define TRC_MEM 0x0010f000 /* Xen memory trace */ +#define TRC_PV 0x0020f000 /* Xen PV traces */ +#define TRC_SHADOW 0x0040f000 /* Xen shadow tracing */ +#define TRC_HW 0x0080f000 /* Xen hardware-related traces */ +#define TRC_GUEST 0x0800f000 /* Guest-generated traces */ +#define TRC_ALL 0x0ffff000 +#define TRC_HD_TO_EVENT(x) ((x)&0x0fffffff) +#define TRC_HD_CYCLE_FLAG (1UL<<31) +#define TRC_HD_INCLUDES_CYCLE_COUNT(x) ( !!( (x) & TRC_HD_CYCLE_FLAG ) ) +#define TRC_HD_EXTRA(x) (((x)>>TRACE_EXTRA_SHIFT)&TRACE_EXTRA_MAX) + +/* Trace subclasses */ +#define TRC_SUBCLS_SHIFT 12 + +/* trace subclasses for SVM */ +#define TRC_HVM_ENTRYEXIT 0x00081000 /* VMENTRY and #VMEXIT */ +#define TRC_HVM_HANDLER 0x00082000 /* various HVM handlers */ +#define TRC_HVM_EMUL 0x00084000 /* emulated devices */ + +#define TRC_SCHED_MIN 0x00021000 /* Just runstate changes */ +#define TRC_SCHED_CLASS 0x00022000 /* Scheduler-specific */ +#define TRC_SCHED_VERBOSE 0x00028000 /* More inclusive scheduling */ + +/* + * The highest 3 bits of the last 12 bits of TRC_SCHED_CLASS above are + * reserved for encoding what scheduler produced the information. The + * actual event is encoded in the last 9 bits. + * + * This means we have 8 scheduling IDs available (which means at most 8 + * schedulers generating events) and, in each scheduler, up to 512 + * different events. + */ +#define TRC_SCHED_ID_BITS 3 +#define TRC_SCHED_ID_SHIFT (TRC_SUBCLS_SHIFT - TRC_SCHED_ID_BITS) +#define TRC_SCHED_ID_MASK (((1UL<cpu_offset[cpu]). + */ +struct t_info { + uint16_t tbuf_size; /* Size in pages of each trace buffer */ + uint16_t mfn_offset[]; /* Offset within t_info structure of the page list per cpu */ + /* MFN lists immediately after the header */ +}; + +#endif /* __XEN_PUBLIC_TRACE_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/vcpu.h b/include/hw/xen/interface/vcpu.h new file mode 100644 index 0000000000..3623af932f --- /dev/null +++ b/include/hw/xen/interface/vcpu.h @@ -0,0 +1,248 @@ +/****************************************************************************** + * vcpu.h + * + * VCPU initialisation, query, and hotplug. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2005, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_VCPU_H__ +#define __XEN_PUBLIC_VCPU_H__ + +#include "xen.h" + +/* + * Prototype for this hypercall is: + * long vcpu_op(int cmd, unsigned int vcpuid, void *extra_args) + * @cmd == VCPUOP_??? (VCPU operation). + * @vcpuid == VCPU to operate on. + * @extra_args == Operation-specific extra arguments (NULL if none). + */ + +/* + * Initialise a VCPU. Each VCPU can be initialised only once. A + * newly-initialised VCPU will not run until it is brought up by VCPUOP_up. + * + * @extra_arg == For PV or ARM guests this is a pointer to a vcpu_guest_context + * structure containing the initial state for the VCPU. For x86 + * HVM based guests this is a pointer to a vcpu_hvm_context + * structure. + */ +#define VCPUOP_initialise 0 + +/* + * Bring up a VCPU. This makes the VCPU runnable. This operation will fail + * if the VCPU has not been initialised (VCPUOP_initialise). + */ +#define VCPUOP_up 1 + +/* + * Bring down a VCPU (i.e., make it non-runnable). + * There are a few caveats that callers should observe: + * 1. This operation may return, and VCPU_is_up may return false, before the + * VCPU stops running (i.e., the command is asynchronous). It is a good + * idea to ensure that the VCPU has entered a non-critical loop before + * bringing it down. Alternatively, this operation is guaranteed + * synchronous if invoked by the VCPU itself. + * 2. After a VCPU is initialised, there is currently no way to drop all its + * references to domain memory. Even a VCPU that is down still holds + * memory references via its pagetable base pointer and GDT. It is good + * practise to move a VCPU onto an 'idle' or default page table, LDT and + * GDT before bringing it down. + */ +#define VCPUOP_down 2 + +/* Returns 1 if the given VCPU is up. */ +#define VCPUOP_is_up 3 + +/* + * Return information about the state and running time of a VCPU. + * @extra_arg == pointer to vcpu_runstate_info structure. + */ +#define VCPUOP_get_runstate_info 4 +struct vcpu_runstate_info { + /* VCPU's current state (RUNSTATE_*). */ + int state; + /* When was current state entered (system time, ns)? */ + uint64_t state_entry_time; + /* + * Update indicator set in state_entry_time: + * When activated via VMASST_TYPE_runstate_update_flag, set during + * updates in guest memory mapped copy of vcpu_runstate_info. + */ +#define XEN_RUNSTATE_UPDATE (xen_mk_ullong(1) << 63) + /* + * Time spent in each RUNSTATE_* (ns). The sum of these times is + * guaranteed not to drift from system time. + */ + uint64_t time[4]; +}; +typedef struct vcpu_runstate_info vcpu_runstate_info_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_runstate_info_t); + +/* VCPU is currently running on a physical CPU. */ +#define RUNSTATE_running 0 + +/* VCPU is runnable, but not currently scheduled on any physical CPU. */ +#define RUNSTATE_runnable 1 + +/* VCPU is blocked (a.k.a. idle). It is therefore not runnable. */ +#define RUNSTATE_blocked 2 + +/* + * VCPU is not runnable, but it is not blocked. + * This is a 'catch all' state for things like hotplug and pauses by the + * system administrator (or for critical sections in the hypervisor). + * RUNSTATE_blocked dominates this state (it is the preferred state). + */ +#define RUNSTATE_offline 3 + +/* + * Register a shared memory area from which the guest may obtain its own + * runstate information without needing to execute a hypercall. + * Notes: + * 1. The registered address may be virtual or physical or guest handle, + * depending on the platform. Virtual address or guest handle should be + * registered on x86 systems. + * 2. Only one shared area may be registered per VCPU. The shared area is + * updated by the hypervisor each time the VCPU is scheduled. Thus + * runstate.state will always be RUNSTATE_running and + * runstate.state_entry_time will indicate the system time at which the + * VCPU was last scheduled to run. + * @extra_arg == pointer to vcpu_register_runstate_memory_area structure. + */ +#define VCPUOP_register_runstate_memory_area 5 +struct vcpu_register_runstate_memory_area { + union { + XEN_GUEST_HANDLE(vcpu_runstate_info_t) h; + struct vcpu_runstate_info *v; + uint64_t p; + } addr; +}; +typedef struct vcpu_register_runstate_memory_area vcpu_register_runstate_memory_area_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_register_runstate_memory_area_t); + +/* + * Set or stop a VCPU's periodic timer. Every VCPU has one periodic timer + * which can be set via these commands. Periods smaller than one millisecond + * may not be supported. + */ +#define VCPUOP_set_periodic_timer 6 /* arg == vcpu_set_periodic_timer_t */ +#define VCPUOP_stop_periodic_timer 7 /* arg == NULL */ +struct vcpu_set_periodic_timer { + uint64_t period_ns; +}; +typedef struct vcpu_set_periodic_timer vcpu_set_periodic_timer_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_set_periodic_timer_t); + +/* + * Set or stop a VCPU's single-shot timer. Every VCPU has one single-shot + * timer which can be set via these commands. + */ +#define VCPUOP_set_singleshot_timer 8 /* arg == vcpu_set_singleshot_timer_t */ +#define VCPUOP_stop_singleshot_timer 9 /* arg == NULL */ +struct vcpu_set_singleshot_timer { + uint64_t timeout_abs_ns; /* Absolute system time value in nanoseconds. */ + uint32_t flags; /* VCPU_SSHOTTMR_??? */ +}; +typedef struct vcpu_set_singleshot_timer vcpu_set_singleshot_timer_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_set_singleshot_timer_t); + +/* Flags to VCPUOP_set_singleshot_timer. */ + /* Require the timeout to be in the future (return -ETIME if it's passed). */ +#define _VCPU_SSHOTTMR_future (0) +#define VCPU_SSHOTTMR_future (1U << _VCPU_SSHOTTMR_future) + +/* + * Register a memory location in the guest address space for the + * vcpu_info structure. This allows the guest to place the vcpu_info + * structure in a convenient place, such as in a per-cpu data area. + * The pointer need not be page aligned, but the structure must not + * cross a page boundary. + * + * This may be called only once per vcpu. + */ +#define VCPUOP_register_vcpu_info 10 /* arg == vcpu_register_vcpu_info_t */ +struct vcpu_register_vcpu_info { + uint64_t mfn; /* mfn of page to place vcpu_info */ + uint32_t offset; /* offset within page */ + uint32_t rsvd; /* unused */ +}; +typedef struct vcpu_register_vcpu_info vcpu_register_vcpu_info_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_register_vcpu_info_t); + +/* Send an NMI to the specified VCPU. @extra_arg == NULL. */ +#define VCPUOP_send_nmi 11 + +/* + * Get the physical ID information for a pinned vcpu's underlying physical + * processor. The physical ID informmation is architecture-specific. + * On x86: id[31:0]=apic_id, id[63:32]=acpi_id. + * This command returns -EINVAL if it is not a valid operation for this VCPU. + */ +#define VCPUOP_get_physid 12 /* arg == vcpu_get_physid_t */ +struct vcpu_get_physid { + uint64_t phys_id; +}; +typedef struct vcpu_get_physid vcpu_get_physid_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_get_physid_t); +#define xen_vcpu_physid_to_x86_apicid(physid) ((uint32_t)(physid)) +#define xen_vcpu_physid_to_x86_acpiid(physid) ((uint32_t)((physid) >> 32)) + +/* + * Register a memory location to get a secondary copy of the vcpu time + * parameters. The master copy still exists as part of the vcpu shared + * memory area, and this secondary copy is updated whenever the master copy + * is updated (and using the same versioning scheme for synchronisation). + * + * The intent is that this copy may be mapped (RO) into userspace so + * that usermode can compute system time using the time info and the + * tsc. Usermode will see an array of vcpu_time_info structures, one + * for each vcpu, and choose the right one by an existing mechanism + * which allows it to get the current vcpu number (such as via a + * segment limit). It can then apply the normal algorithm to compute + * system time from the tsc. + * + * @extra_arg == pointer to vcpu_register_time_info_memory_area structure. + */ +#define VCPUOP_register_vcpu_time_memory_area 13 +DEFINE_XEN_GUEST_HANDLE(vcpu_time_info_t); +struct vcpu_register_time_memory_area { + union { + XEN_GUEST_HANDLE(vcpu_time_info_t) h; + struct vcpu_time_info *v; + uint64_t p; + } addr; +}; +typedef struct vcpu_register_time_memory_area vcpu_register_time_memory_area_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_register_time_memory_area_t); + +#endif /* __XEN_PUBLIC_VCPU_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/version.h b/include/hw/xen/interface/version.h new file mode 100644 index 0000000000..17a81e23cd --- /dev/null +++ b/include/hw/xen/interface/version.h @@ -0,0 +1,113 @@ +/****************************************************************************** + * version.h + * + * Xen version, type, and compile information. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2005, Nguyen Anh Quynh + * Copyright (c) 2005, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_VERSION_H__ +#define __XEN_PUBLIC_VERSION_H__ + +#include "xen.h" + +/* NB. All ops return zero on success, except XENVER_{version,pagesize} + * XENVER_{version,pagesize,build_id} */ + +/* arg == NULL; returns major:minor (16:16). */ +#define XENVER_version 0 + +/* arg == xen_extraversion_t. */ +#define XENVER_extraversion 1 +typedef char xen_extraversion_t[16]; +#define XEN_EXTRAVERSION_LEN (sizeof(xen_extraversion_t)) + +/* arg == xen_compile_info_t. */ +#define XENVER_compile_info 2 +struct xen_compile_info { + char compiler[64]; + char compile_by[16]; + char compile_domain[32]; + char compile_date[32]; +}; +typedef struct xen_compile_info xen_compile_info_t; + +#define XENVER_capabilities 3 +typedef char xen_capabilities_info_t[1024]; +#define XEN_CAPABILITIES_INFO_LEN (sizeof(xen_capabilities_info_t)) + +#define XENVER_changeset 4 +typedef char xen_changeset_info_t[64]; +#define XEN_CHANGESET_INFO_LEN (sizeof(xen_changeset_info_t)) + +#define XENVER_platform_parameters 5 +struct xen_platform_parameters { + xen_ulong_t virt_start; +}; +typedef struct xen_platform_parameters xen_platform_parameters_t; + +#define XENVER_get_features 6 +struct xen_feature_info { + unsigned int submap_idx; /* IN: which 32-bit submap to return */ + uint32_t submap; /* OUT: 32-bit submap */ +}; +typedef struct xen_feature_info xen_feature_info_t; + +/* Declares the features reported by XENVER_get_features. */ +#include "features.h" + +/* arg == NULL; returns host memory page size. */ +#define XENVER_pagesize 7 + +/* arg == xen_domain_handle_t. + * + * The toolstack fills it out for guest consumption. It is intended to hold + * the UUID of the guest. + */ +#define XENVER_guest_handle 8 + +#define XENVER_commandline 9 +typedef char xen_commandline_t[1024]; + +/* + * Return value is the number of bytes written, or XEN_Exx on error. + * Calling with empty parameter returns the size of build_id. + */ +#define XENVER_build_id 10 +struct xen_build_id { + uint32_t len; /* IN: size of buf[]. */ + unsigned char buf[XEN_FLEX_ARRAY_DIM]; + /* OUT: Variable length buffer with build_id. */ +}; +typedef struct xen_build_id xen_build_id_t; + +#endif /* __XEN_PUBLIC_VERSION_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/xen-compat.h b/include/hw/xen/interface/xen-compat.h new file mode 100644 index 0000000000..e1c027a95c --- /dev/null +++ b/include/hw/xen/interface/xen-compat.h @@ -0,0 +1,46 @@ +/****************************************************************************** + * xen-compat.h + * + * Guest OS interface to Xen. Compatibility layer. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2006, Christian Limpach + */ + +#ifndef __XEN_PUBLIC_XEN_COMPAT_H__ +#define __XEN_PUBLIC_XEN_COMPAT_H__ + +#define __XEN_LATEST_INTERFACE_VERSION__ 0x00040e00 + +#if defined(__XEN__) || defined(__XEN_TOOLS__) +/* Xen is built with matching headers and implements the latest interface. */ +#define __XEN_INTERFACE_VERSION__ __XEN_LATEST_INTERFACE_VERSION__ +#elif !defined(__XEN_INTERFACE_VERSION__) +/* Guests which do not specify a version get the legacy interface. */ +#define __XEN_INTERFACE_VERSION__ 0x00000000 +#endif + +#if __XEN_INTERFACE_VERSION__ > __XEN_LATEST_INTERFACE_VERSION__ +#error "These header files do not support the requested interface version." +#endif + +#define COMPAT_FLEX_ARRAY_DIM XEN_FLEX_ARRAY_DIM + +#endif /* __XEN_PUBLIC_XEN_COMPAT_H__ */ diff --git a/include/hw/xen/interface/xen.h b/include/hw/xen/interface/xen.h new file mode 100644 index 0000000000..e373592c33 --- /dev/null +++ b/include/hw/xen/interface/xen.h @@ -0,0 +1,1049 @@ +/****************************************************************************** + * xen.h + * + * Guest OS interface to Xen. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2004, K A Fraser + */ + +#ifndef __XEN_PUBLIC_XEN_H__ +#define __XEN_PUBLIC_XEN_H__ + +#include "xen-compat.h" + +#if defined(__i386__) || defined(__x86_64__) +#include "arch-x86/xen.h" +#elif defined(__arm__) || defined (__aarch64__) +#include "arch-arm.h" +#else +#error "Unsupported architecture" +#endif + +#ifndef __ASSEMBLY__ +/* Guest handles for primitive C types. */ +DEFINE_XEN_GUEST_HANDLE(char); +__DEFINE_XEN_GUEST_HANDLE(uchar, unsigned char); +DEFINE_XEN_GUEST_HANDLE(int); +__DEFINE_XEN_GUEST_HANDLE(uint, unsigned int); +#if __XEN_INTERFACE_VERSION__ < 0x00040300 +DEFINE_XEN_GUEST_HANDLE(long); +__DEFINE_XEN_GUEST_HANDLE(ulong, unsigned long); +#endif +DEFINE_XEN_GUEST_HANDLE(void); + +DEFINE_XEN_GUEST_HANDLE(uint64_t); +DEFINE_XEN_GUEST_HANDLE(xen_pfn_t); +DEFINE_XEN_GUEST_HANDLE(xen_ulong_t); + +/* Define a variable length array (depends on compiler). */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define XEN_FLEX_ARRAY_DIM +#elif defined(__GNUC__) +#define XEN_FLEX_ARRAY_DIM 0 +#else +#define XEN_FLEX_ARRAY_DIM 1 /* variable size */ +#endif + +/* Turn a plain number into a C unsigned (long (long)) constant. */ +#define __xen_mk_uint(x) x ## U +#define __xen_mk_ulong(x) x ## UL +#ifndef __xen_mk_ullong +# define __xen_mk_ullong(x) x ## ULL +#endif +#define xen_mk_uint(x) __xen_mk_uint(x) +#define xen_mk_ulong(x) __xen_mk_ulong(x) +#define xen_mk_ullong(x) __xen_mk_ullong(x) + +#else + +/* In assembly code we cannot use C numeric constant suffixes. */ +#define xen_mk_uint(x) x +#define xen_mk_ulong(x) x +#define xen_mk_ullong(x) x + +#endif + +/* + * HYPERCALLS + */ + +/* `incontents 100 hcalls List of hypercalls + * ` enum hypercall_num { // __HYPERVISOR_* => HYPERVISOR_*() + */ + +#define __HYPERVISOR_set_trap_table 0 +#define __HYPERVISOR_mmu_update 1 +#define __HYPERVISOR_set_gdt 2 +#define __HYPERVISOR_stack_switch 3 +#define __HYPERVISOR_set_callbacks 4 +#define __HYPERVISOR_fpu_taskswitch 5 +#define __HYPERVISOR_sched_op_compat 6 /* compat since 0x00030101 */ +#define __HYPERVISOR_platform_op 7 +#define __HYPERVISOR_set_debugreg 8 +#define __HYPERVISOR_get_debugreg 9 +#define __HYPERVISOR_update_descriptor 10 +#define __HYPERVISOR_memory_op 12 +#define __HYPERVISOR_multicall 13 +#define __HYPERVISOR_update_va_mapping 14 +#define __HYPERVISOR_set_timer_op 15 +#define __HYPERVISOR_event_channel_op_compat 16 /* compat since 0x00030202 */ +#define __HYPERVISOR_xen_version 17 +#define __HYPERVISOR_console_io 18 +#define __HYPERVISOR_physdev_op_compat 19 /* compat since 0x00030202 */ +#define __HYPERVISOR_grant_table_op 20 +#define __HYPERVISOR_vm_assist 21 +#define __HYPERVISOR_update_va_mapping_otherdomain 22 +#define __HYPERVISOR_iret 23 /* x86 only */ +#define __HYPERVISOR_vcpu_op 24 +#define __HYPERVISOR_set_segment_base 25 /* x86/64 only */ +#define __HYPERVISOR_mmuext_op 26 +#define __HYPERVISOR_xsm_op 27 +#define __HYPERVISOR_nmi_op 28 +#define __HYPERVISOR_sched_op 29 +#define __HYPERVISOR_callback_op 30 +#define __HYPERVISOR_xenoprof_op 31 +#define __HYPERVISOR_event_channel_op 32 +#define __HYPERVISOR_physdev_op 33 +#define __HYPERVISOR_hvm_op 34 +#define __HYPERVISOR_sysctl 35 +#define __HYPERVISOR_domctl 36 +#define __HYPERVISOR_kexec_op 37 +#define __HYPERVISOR_tmem_op 38 +#define __HYPERVISOR_argo_op 39 +#define __HYPERVISOR_xenpmu_op 40 +#define __HYPERVISOR_dm_op 41 +#define __HYPERVISOR_hypfs_op 42 + +/* Architecture-specific hypercall definitions. */ +#define __HYPERVISOR_arch_0 48 +#define __HYPERVISOR_arch_1 49 +#define __HYPERVISOR_arch_2 50 +#define __HYPERVISOR_arch_3 51 +#define __HYPERVISOR_arch_4 52 +#define __HYPERVISOR_arch_5 53 +#define __HYPERVISOR_arch_6 54 +#define __HYPERVISOR_arch_7 55 + +/* ` } */ + +/* + * HYPERCALL COMPATIBILITY. + */ + +/* New sched_op hypercall introduced in 0x00030101. */ +#if __XEN_INTERFACE_VERSION__ < 0x00030101 +#undef __HYPERVISOR_sched_op +#define __HYPERVISOR_sched_op __HYPERVISOR_sched_op_compat +#endif + +/* New event-channel and physdev hypercalls introduced in 0x00030202. */ +#if __XEN_INTERFACE_VERSION__ < 0x00030202 +#undef __HYPERVISOR_event_channel_op +#define __HYPERVISOR_event_channel_op __HYPERVISOR_event_channel_op_compat +#undef __HYPERVISOR_physdev_op +#define __HYPERVISOR_physdev_op __HYPERVISOR_physdev_op_compat +#endif + +/* New platform_op hypercall introduced in 0x00030204. */ +#if __XEN_INTERFACE_VERSION__ < 0x00030204 +#define __HYPERVISOR_dom0_op __HYPERVISOR_platform_op +#endif + +/* + * VIRTUAL INTERRUPTS + * + * Virtual interrupts that a guest OS may receive from Xen. + * + * In the side comments, 'V.' denotes a per-VCPU VIRQ while 'G.' denotes a + * global VIRQ. The former can be bound once per VCPU and cannot be re-bound. + * The latter can be allocated only once per guest: they must initially be + * allocated to VCPU0 but can subsequently be re-bound. + */ +/* ` enum virq { */ +#define VIRQ_TIMER 0 /* V. Timebase update, and/or requested timeout. */ +#define VIRQ_DEBUG 1 /* V. Request guest to dump debug info. */ +#define VIRQ_CONSOLE 2 /* G. (DOM0) Bytes received on emergency console. */ +#define VIRQ_DOM_EXC 3 /* G. (DOM0) Exceptional event for some domain. */ +#define VIRQ_TBUF 4 /* G. (DOM0) Trace buffer has records available. */ +#define VIRQ_DEBUGGER 6 /* G. (DOM0) A domain has paused for debugging. */ +#define VIRQ_XENOPROF 7 /* V. XenOprofile interrupt: new sample available */ +#define VIRQ_CON_RING 8 /* G. (DOM0) Bytes received on console */ +#define VIRQ_PCPU_STATE 9 /* G. (DOM0) PCPU state changed */ +#define VIRQ_MEM_EVENT 10 /* G. (DOM0) A memory event has occurred */ +#define VIRQ_ARGO 11 /* G. Argo interdomain message notification */ +#define VIRQ_ENOMEM 12 /* G. (DOM0) Low on heap memory */ +#define VIRQ_XENPMU 13 /* V. PMC interrupt */ + +/* Architecture-specific VIRQ definitions. */ +#define VIRQ_ARCH_0 16 +#define VIRQ_ARCH_1 17 +#define VIRQ_ARCH_2 18 +#define VIRQ_ARCH_3 19 +#define VIRQ_ARCH_4 20 +#define VIRQ_ARCH_5 21 +#define VIRQ_ARCH_6 22 +#define VIRQ_ARCH_7 23 +/* ` } */ + +#define NR_VIRQS 24 + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_mmu_update(const struct mmu_update reqs[], + * ` unsigned count, unsigned *done_out, + * ` unsigned foreigndom) + * ` + * @reqs is an array of mmu_update_t structures ((ptr, val) pairs). + * @count is the length of the above array. + * @pdone is an output parameter indicating number of completed operations + * @foreigndom[15:0]: FD, the expected owner of data pages referenced in this + * hypercall invocation. Can be DOMID_SELF. + * @foreigndom[31:16]: PFD, the expected owner of pagetable pages referenced + * in this hypercall invocation. The value of this field + * (x) encodes the PFD as follows: + * x == 0 => PFD == DOMID_SELF + * x != 0 => PFD == x - 1 + * + * Sub-commands: ptr[1:0] specifies the appropriate MMU_* command. + * ------------- + * ptr[1:0] == MMU_NORMAL_PT_UPDATE: + * Updates an entry in a page table belonging to PFD. If updating an L1 table, + * and the new table entry is valid/present, the mapped frame must belong to + * FD. If attempting to map an I/O page then the caller assumes the privilege + * of the FD. + * FD == DOMID_IO: Permit /only/ I/O mappings, at the priv level of the caller. + * FD == DOMID_XEN: Map restricted areas of Xen's heap space. + * ptr[:2] -- Machine address of the page-table entry to modify. + * val -- Value to write. + * + * There also certain implicit requirements when using this hypercall. The + * pages that make up a pagetable must be mapped read-only in the guest. + * This prevents uncontrolled guest updates to the pagetable. Xen strictly + * enforces this, and will disallow any pagetable update which will end up + * mapping pagetable page RW, and will disallow using any writable page as a + * pagetable. In practice it means that when constructing a page table for a + * process, thread, etc, we MUST be very dilligient in following these rules: + * 1). Start with top-level page (PGD or in Xen language: L4). Fill out + * the entries. + * 2). Keep on going, filling out the upper (PUD or L3), and middle (PMD + * or L2). + * 3). Start filling out the PTE table (L1) with the PTE entries. Once + * done, make sure to set each of those entries to RO (so writeable bit + * is unset). Once that has been completed, set the PMD (L2) for this + * PTE table as RO. + * 4). When completed with all of the PMD (L2) entries, and all of them have + * been set to RO, make sure to set RO the PUD (L3). Do the same + * operation on PGD (L4) pagetable entries that have a PUD (L3) entry. + * 5). Now before you can use those pages (so setting the cr3), you MUST also + * pin them so that the hypervisor can verify the entries. This is done + * via the HYPERVISOR_mmuext_op(MMUEXT_PIN_L4_TABLE, guest physical frame + * number of the PGD (L4)). And this point the HYPERVISOR_mmuext_op( + * MMUEXT_NEW_BASEPTR, guest physical frame number of the PGD (L4)) can be + * issued. + * For 32-bit guests, the L4 is not used (as there is less pagetables), so + * instead use L3. + * At this point the pagetables can be modified using the MMU_NORMAL_PT_UPDATE + * hypercall. Also if so desired the OS can also try to write to the PTE + * and be trapped by the hypervisor (as the PTE entry is RO). + * + * To deallocate the pages, the operations are the reverse of the steps + * mentioned above. The argument is MMUEXT_UNPIN_TABLE for all levels and the + * pagetable MUST not be in use (meaning that the cr3 is not set to it). + * + * ptr[1:0] == MMU_MACHPHYS_UPDATE: + * Updates an entry in the machine->pseudo-physical mapping table. + * ptr[:2] -- Machine address within the frame whose mapping to modify. + * The frame must belong to the FD, if one is specified. + * val -- Value to write into the mapping entry. + * + * ptr[1:0] == MMU_PT_UPDATE_PRESERVE_AD: + * As MMU_NORMAL_PT_UPDATE above, but A/D bits currently in the PTE are ORed + * with those in @val. + * + * ptr[1:0] == MMU_PT_UPDATE_NO_TRANSLATE: + * As MMU_NORMAL_PT_UPDATE above, but @val is not translated though FD + * page tables. + * + * @val is usually the machine frame number along with some attributes. + * The attributes by default follow the architecture defined bits. Meaning that + * if this is a X86_64 machine and four page table layout is used, the layout + * of val is: + * - 63 if set means No execute (NX) + * - 46-13 the machine frame number + * - 12 available for guest + * - 11 available for guest + * - 10 available for guest + * - 9 available for guest + * - 8 global + * - 7 PAT (PSE is disabled, must use hypercall to make 4MB or 2MB pages) + * - 6 dirty + * - 5 accessed + * - 4 page cached disabled + * - 3 page write through + * - 2 userspace accessible + * - 1 writeable + * - 0 present + * + * The one bits that does not fit with the default layout is the PAGE_PSE + * also called PAGE_PAT). The MMUEXT_[UN]MARK_SUPER arguments to the + * HYPERVISOR_mmuext_op serve as mechanism to set a pagetable to be 4MB + * (or 2MB) instead of using the PAGE_PSE bit. + * + * The reason that the PAGE_PSE (bit 7) is not being utilized is due to Xen + * using it as the Page Attribute Table (PAT) bit - for details on it please + * refer to Intel SDM 10.12. The PAT allows to set the caching attributes of + * pages instead of using MTRRs. + * + * The PAT MSR is as follows (it is a 64-bit value, each entry is 8 bits): + * PAT4 PAT0 + * +-----+-----+----+----+----+-----+----+----+ + * | UC | UC- | WC | WB | UC | UC- | WC | WB | <= Linux + * +-----+-----+----+----+----+-----+----+----+ + * | UC | UC- | WT | WB | UC | UC- | WT | WB | <= BIOS (default when machine boots) + * +-----+-----+----+----+----+-----+----+----+ + * | rsv | rsv | WP | WC | UC | UC- | WT | WB | <= Xen + * +-----+-----+----+----+----+-----+----+----+ + * + * The lookup of this index table translates to looking up + * Bit 7, Bit 4, and Bit 3 of val entry: + * + * PAT/PSE (bit 7) ... PCD (bit 4) .. PWT (bit 3). + * + * If all bits are off, then we are using PAT0. If bit 3 turned on, + * then we are using PAT1, if bit 3 and bit 4, then PAT2.. + * + * As you can see, the Linux PAT1 translates to PAT4 under Xen. Which means + * that if a guest that follows Linux's PAT setup and would like to set Write + * Combined on pages it MUST use PAT4 entry. Meaning that Bit 7 (PAGE_PAT) is + * set. For example, under Linux it only uses PAT0, PAT1, and PAT2 for the + * caching as: + * + * WB = none (so PAT0) + * WC = PWT (bit 3 on) + * UC = PWT | PCD (bit 3 and 4 are on). + * + * To make it work with Xen, it needs to translate the WC bit as so: + * + * PWT (so bit 3 on) --> PAT (so bit 7 is on) and clear bit 3 + * + * And to translate back it would: + * + * PAT (bit 7 on) --> PWT (bit 3 on) and clear bit 7. + */ +#define MMU_NORMAL_PT_UPDATE 0 /* checked '*ptr = val'. ptr is MA. */ +#define MMU_MACHPHYS_UPDATE 1 /* ptr = MA of frame to modify entry for */ +#define MMU_PT_UPDATE_PRESERVE_AD 2 /* atomically: *ptr = val | (*ptr&(A|D)) */ +#define MMU_PT_UPDATE_NO_TRANSLATE 3 /* checked '*ptr = val'. ptr is MA. */ + /* val never translated. */ + +/* + * MMU EXTENDED OPERATIONS + * + * ` enum neg_errnoval + * ` HYPERVISOR_mmuext_op(mmuext_op_t uops[], + * ` unsigned int count, + * ` unsigned int *pdone, + * ` unsigned int foreigndom) + */ +/* HYPERVISOR_mmuext_op() accepts a list of mmuext_op structures. + * A foreigndom (FD) can be specified (or DOMID_SELF for none). + * Where the FD has some effect, it is described below. + * + * cmd: MMUEXT_(UN)PIN_*_TABLE + * mfn: Machine frame number to be (un)pinned as a p.t. page. + * The frame must belong to the FD, if one is specified. + * + * cmd: MMUEXT_NEW_BASEPTR + * mfn: Machine frame number of new page-table base to install in MMU. + * + * cmd: MMUEXT_NEW_USER_BASEPTR [x86/64 only] + * mfn: Machine frame number of new page-table base to install in MMU + * when in user space. + * + * cmd: MMUEXT_TLB_FLUSH_LOCAL + * No additional arguments. Flushes local TLB. + * + * cmd: MMUEXT_INVLPG_LOCAL + * linear_addr: Linear address to be flushed from the local TLB. + * + * cmd: MMUEXT_TLB_FLUSH_MULTI + * vcpumask: Pointer to bitmap of VCPUs to be flushed. + * + * cmd: MMUEXT_INVLPG_MULTI + * linear_addr: Linear address to be flushed. + * vcpumask: Pointer to bitmap of VCPUs to be flushed. + * + * cmd: MMUEXT_TLB_FLUSH_ALL + * No additional arguments. Flushes all VCPUs' TLBs. + * + * cmd: MMUEXT_INVLPG_ALL + * linear_addr: Linear address to be flushed from all VCPUs' TLBs. + * + * cmd: MMUEXT_FLUSH_CACHE + * No additional arguments. Writes back and flushes cache contents. + * + * cmd: MMUEXT_FLUSH_CACHE_GLOBAL + * No additional arguments. Writes back and flushes cache contents + * on all CPUs in the system. + * + * cmd: MMUEXT_SET_LDT + * linear_addr: Linear address of LDT base (NB. must be page-aligned). + * nr_ents: Number of entries in LDT. + * + * cmd: MMUEXT_CLEAR_PAGE + * mfn: Machine frame number to be cleared. + * + * cmd: MMUEXT_COPY_PAGE + * mfn: Machine frame number of the destination page. + * src_mfn: Machine frame number of the source page. + * + * cmd: MMUEXT_[UN]MARK_SUPER + * mfn: Machine frame number of head of superpage to be [un]marked. + */ +/* ` enum mmuext_cmd { */ +#define MMUEXT_PIN_L1_TABLE 0 +#define MMUEXT_PIN_L2_TABLE 1 +#define MMUEXT_PIN_L3_TABLE 2 +#define MMUEXT_PIN_L4_TABLE 3 +#define MMUEXT_UNPIN_TABLE 4 +#define MMUEXT_NEW_BASEPTR 5 +#define MMUEXT_TLB_FLUSH_LOCAL 6 +#define MMUEXT_INVLPG_LOCAL 7 +#define MMUEXT_TLB_FLUSH_MULTI 8 +#define MMUEXT_INVLPG_MULTI 9 +#define MMUEXT_TLB_FLUSH_ALL 10 +#define MMUEXT_INVLPG_ALL 11 +#define MMUEXT_FLUSH_CACHE 12 +#define MMUEXT_SET_LDT 13 +#define MMUEXT_NEW_USER_BASEPTR 15 +#define MMUEXT_CLEAR_PAGE 16 +#define MMUEXT_COPY_PAGE 17 +#define MMUEXT_FLUSH_CACHE_GLOBAL 18 +#define MMUEXT_MARK_SUPER 19 +#define MMUEXT_UNMARK_SUPER 20 +/* ` } */ + +#ifndef __ASSEMBLY__ +struct mmuext_op { + unsigned int cmd; /* => enum mmuext_cmd */ + union { + /* [UN]PIN_TABLE, NEW_BASEPTR, NEW_USER_BASEPTR + * CLEAR_PAGE, COPY_PAGE, [UN]MARK_SUPER */ + xen_pfn_t mfn; + /* INVLPG_LOCAL, INVLPG_ALL, SET_LDT */ + unsigned long linear_addr; + } arg1; + union { + /* SET_LDT */ + unsigned int nr_ents; + /* TLB_FLUSH_MULTI, INVLPG_MULTI */ +#if __XEN_INTERFACE_VERSION__ >= 0x00030205 + XEN_GUEST_HANDLE(const_void) vcpumask; +#else + const void *vcpumask; +#endif + /* COPY_PAGE */ + xen_pfn_t src_mfn; + } arg2; +}; +typedef struct mmuext_op mmuext_op_t; +DEFINE_XEN_GUEST_HANDLE(mmuext_op_t); +#endif + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_update_va_mapping(unsigned long va, u64 val, + * ` enum uvm_flags flags) + * ` + * ` enum neg_errnoval + * ` HYPERVISOR_update_va_mapping_otherdomain(unsigned long va, u64 val, + * ` enum uvm_flags flags, + * ` domid_t domid) + * ` + * ` @va: The virtual address whose mapping we want to change + * ` @val: The new page table entry, must contain a machine address + * ` @flags: Control TLB flushes + */ +/* These are passed as 'flags' to update_va_mapping. They can be ORed. */ +/* When specifying UVMF_MULTI, also OR in a pointer to a CPU bitmap. */ +/* UVMF_LOCAL is merely UVMF_MULTI with a NULL bitmap pointer. */ +/* ` enum uvm_flags { */ +#define UVMF_NONE (xen_mk_ulong(0)<<0) /* No flushing at all. */ +#define UVMF_TLB_FLUSH (xen_mk_ulong(1)<<0) /* Flush entire TLB(s). */ +#define UVMF_INVLPG (xen_mk_ulong(2)<<0) /* Flush only one entry. */ +#define UVMF_FLUSHTYPE_MASK (xen_mk_ulong(3)<<0) +#define UVMF_MULTI (xen_mk_ulong(0)<<2) /* Flush subset of TLBs. */ +#define UVMF_LOCAL (xen_mk_ulong(0)<<2) /* Flush local TLB. */ +#define UVMF_ALL (xen_mk_ulong(1)<<2) /* Flush all TLBs. */ +/* ` } */ + +/* + * ` int + * ` HYPERVISOR_console_io(unsigned int cmd, + * ` unsigned int count, + * ` char buffer[]); + * + * @cmd: Command (see below) + * @count: Size of the buffer to read/write + * @buffer: Pointer in the guest memory + * + * List of commands: + * + * * CONSOLEIO_write: Write the buffer to Xen console. + * For the hardware domain, all the characters in the buffer will + * be written. Characters will be printed directly to the console. + * For all the other domains, only the printable characters will be + * written. Characters may be buffered until a newline (i.e '\n') is + * found. + * @return 0 on success, otherwise return an error code. + * * CONSOLEIO_read: Attempts to read up to @count characters from Xen + * console. The maximum buffer size (i.e. @count) supported is 2GB. + * @return the number of characters read on success, otherwise return + * an error code. + */ +#define CONSOLEIO_write 0 +#define CONSOLEIO_read 1 + +/* + * Commands to HYPERVISOR_vm_assist(). + */ +#define VMASST_CMD_enable 0 +#define VMASST_CMD_disable 1 + +/* x86/32 guests: simulate full 4GB segment limits. */ +#define VMASST_TYPE_4gb_segments 0 + +/* x86/32 guests: trap (vector 15) whenever above vmassist is used. */ +#define VMASST_TYPE_4gb_segments_notify 1 + +/* + * x86 guests: support writes to bottom-level PTEs. + * NB1. Page-directory entries cannot be written. + * NB2. Guest must continue to remove all writable mappings of PTEs. + */ +#define VMASST_TYPE_writable_pagetables 2 + +/* x86/PAE guests: support PDPTs above 4GB. */ +#define VMASST_TYPE_pae_extended_cr3 3 + +/* + * x86 guests: Sane behaviour for virtual iopl + * - virtual iopl updated from do_iret() hypercalls. + * - virtual iopl reported in bounce frames. + * - guest kernels assumed to be level 0 for the purpose of iopl checks. + */ +#define VMASST_TYPE_architectural_iopl 4 + +/* + * All guests: activate update indicator in vcpu_runstate_info + * Enable setting the XEN_RUNSTATE_UPDATE flag in guest memory mapped + * vcpu_runstate_info during updates of the runstate information. + */ +#define VMASST_TYPE_runstate_update_flag 5 + +/* + * x86/64 guests: strictly hide M2P from user mode. + * This allows the guest to control respective hypervisor behavior: + * - when not set, L4 tables get created with the respective slot blank, + * and whenever the L4 table gets used as a kernel one the missing + * mapping gets inserted, + * - when set, L4 tables get created with the respective slot initialized + * as before, and whenever the L4 table gets used as a user one the + * mapping gets zapped. + */ +#define VMASST_TYPE_m2p_strict 32 + +#if __XEN_INTERFACE_VERSION__ < 0x00040600 +#define MAX_VMASST_TYPE 3 +#endif + +/* Domain ids >= DOMID_FIRST_RESERVED cannot be used for ordinary domains. */ +#define DOMID_FIRST_RESERVED xen_mk_uint(0x7FF0) + +/* DOMID_SELF is used in certain contexts to refer to oneself. */ +#define DOMID_SELF xen_mk_uint(0x7FF0) + +/* + * DOMID_IO is used to restrict page-table updates to mapping I/O memory. + * Although no Foreign Domain need be specified to map I/O pages, DOMID_IO + * is useful to ensure that no mappings to the OS's own heap are accidentally + * installed. (e.g., in Linux this could cause havoc as reference counts + * aren't adjusted on the I/O-mapping code path). + * This only makes sense as HYPERVISOR_mmu_update()'s and + * HYPERVISOR_update_va_mapping_otherdomain()'s "foreigndom" argument. For + * HYPERVISOR_mmu_update() context it can be specified by any calling domain, + * otherwise it's only permitted if the caller is privileged. + */ +#define DOMID_IO xen_mk_uint(0x7FF1) + +/* + * DOMID_XEN is used to allow privileged domains to map restricted parts of + * Xen's heap space (e.g., the machine_to_phys table). + * This only makes sense as + * - HYPERVISOR_mmu_update()'s, HYPERVISOR_mmuext_op()'s, or + * HYPERVISOR_update_va_mapping_otherdomain()'s "foreigndom" argument, + * - with XENMAPSPACE_gmfn_foreign, + * and is only permitted if the caller is privileged. + */ +#define DOMID_XEN xen_mk_uint(0x7FF2) + +/* + * DOMID_COW is used as the owner of sharable pages */ +#define DOMID_COW xen_mk_uint(0x7FF3) + +/* DOMID_INVALID is used to identify pages with unknown owner. */ +#define DOMID_INVALID xen_mk_uint(0x7FF4) + +/* Idle domain. */ +#define DOMID_IDLE xen_mk_uint(0x7FFF) + +/* Mask for valid domain id values */ +#define DOMID_MASK xen_mk_uint(0x7FFF) + +#ifndef __ASSEMBLY__ + +typedef uint16_t domid_t; + +/* + * Send an array of these to HYPERVISOR_mmu_update(). + * NB. The fields are natural pointer/address size for this architecture. + */ +struct mmu_update { + uint64_t ptr; /* Machine address of PTE. */ + uint64_t val; /* New contents of PTE. */ +}; +typedef struct mmu_update mmu_update_t; +DEFINE_XEN_GUEST_HANDLE(mmu_update_t); + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_multicall(multicall_entry_t call_list[], + * ` uint32_t nr_calls); + * + * NB. The fields are logically the natural register size for this + * architecture. In cases where xen_ulong_t is larger than this then + * any unused bits in the upper portion must be zero. + */ +struct multicall_entry { + xen_ulong_t op, result; + xen_ulong_t args[6]; +}; +typedef struct multicall_entry multicall_entry_t; +DEFINE_XEN_GUEST_HANDLE(multicall_entry_t); + +#if __XEN_INTERFACE_VERSION__ < 0x00040400 +/* + * Event channel endpoints per domain (when using the 2-level ABI): + * 1024 if a long is 32 bits; 4096 if a long is 64 bits. + */ +#define NR_EVENT_CHANNELS EVTCHN_2L_NR_CHANNELS +#endif + +struct vcpu_time_info { + /* + * Updates to the following values are preceded and followed by an + * increment of 'version'. The guest can therefore detect updates by + * looking for changes to 'version'. If the least-significant bit of + * the version number is set then an update is in progress and the guest + * must wait to read a consistent set of values. + * The correct way to interact with the version number is similar to + * Linux's seqlock: see the implementations of read_seqbegin/read_seqretry. + */ + uint32_t version; + uint32_t pad0; + uint64_t tsc_timestamp; /* TSC at last update of time vals. */ + uint64_t system_time; /* Time, in nanosecs, since boot. */ + /* + * Current system time: + * system_time + + * ((((tsc - tsc_timestamp) << tsc_shift) * tsc_to_system_mul) >> 32) + * CPU frequency (Hz): + * ((10^9 << 32) / tsc_to_system_mul) >> tsc_shift + */ + uint32_t tsc_to_system_mul; + int8_t tsc_shift; +#if __XEN_INTERFACE_VERSION__ > 0x040600 + uint8_t flags; + uint8_t pad1[2]; +#else + int8_t pad1[3]; +#endif +}; /* 32 bytes */ +typedef struct vcpu_time_info vcpu_time_info_t; + +#define XEN_PVCLOCK_TSC_STABLE_BIT (1 << 0) +#define XEN_PVCLOCK_GUEST_STOPPED (1 << 1) + +struct vcpu_info { + /* + * 'evtchn_upcall_pending' is written non-zero by Xen to indicate + * a pending notification for a particular VCPU. It is then cleared + * by the guest OS /before/ checking for pending work, thus avoiding + * a set-and-check race. Note that the mask is only accessed by Xen + * on the CPU that is currently hosting the VCPU. This means that the + * pending and mask flags can be updated by the guest without special + * synchronisation (i.e., no need for the x86 LOCK prefix). + * This may seem suboptimal because if the pending flag is set by + * a different CPU then an IPI may be scheduled even when the mask + * is set. However, note: + * 1. The task of 'interrupt holdoff' is covered by the per-event- + * channel mask bits. A 'noisy' event that is continually being + * triggered can be masked at source at this very precise + * granularity. + * 2. The main purpose of the per-VCPU mask is therefore to restrict + * reentrant execution: whether for concurrency control, or to + * prevent unbounded stack usage. Whatever the purpose, we expect + * that the mask will be asserted only for short periods at a time, + * and so the likelihood of a 'spurious' IPI is suitably small. + * The mask is read before making an event upcall to the guest: a + * non-zero mask therefore guarantees that the VCPU will not receive + * an upcall activation. The mask is cleared when the VCPU requests + * to block: this avoids wakeup-waiting races. + */ + uint8_t evtchn_upcall_pending; +#ifdef XEN_HAVE_PV_UPCALL_MASK + uint8_t evtchn_upcall_mask; +#else /* XEN_HAVE_PV_UPCALL_MASK */ + uint8_t pad0; +#endif /* XEN_HAVE_PV_UPCALL_MASK */ + xen_ulong_t evtchn_pending_sel; + struct arch_vcpu_info arch; + vcpu_time_info_t time; +}; /* 64 bytes (x86) */ +#ifndef __XEN__ +typedef struct vcpu_info vcpu_info_t; +#endif + +/* + * `incontents 200 startofday_shared Start-of-day shared data structure + * Xen/kernel shared data -- pointer provided in start_info. + * + * This structure is defined to be both smaller than a page, and the + * only data on the shared page, but may vary in actual size even within + * compatible Xen versions; guests should not rely on the size + * of this structure remaining constant. + */ +struct shared_info { + struct vcpu_info vcpu_info[XEN_LEGACY_MAX_VCPUS]; + + /* + * A domain can create "event channels" on which it can send and receive + * asynchronous event notifications. There are three classes of event that + * are delivered by this mechanism: + * 1. Bi-directional inter- and intra-domain connections. Domains must + * arrange out-of-band to set up a connection (usually by allocating + * an unbound 'listener' port and avertising that via a storage service + * such as xenstore). + * 2. Physical interrupts. A domain with suitable hardware-access + * privileges can bind an event-channel port to a physical interrupt + * source. + * 3. Virtual interrupts ('events'). A domain can bind an event-channel + * port to a virtual interrupt source, such as the virtual-timer + * device or the emergency console. + * + * Event channels are addressed by a "port index". Each channel is + * associated with two bits of information: + * 1. PENDING -- notifies the domain that there is a pending notification + * to be processed. This bit is cleared by the guest. + * 2. MASK -- if this bit is clear then a 0->1 transition of PENDING + * will cause an asynchronous upcall to be scheduled. This bit is only + * updated by the guest. It is read-only within Xen. If a channel + * becomes pending while the channel is masked then the 'edge' is lost + * (i.e., when the channel is unmasked, the guest must manually handle + * pending notifications as no upcall will be scheduled by Xen). + * + * To expedite scanning of pending notifications, any 0->1 pending + * transition on an unmasked channel causes a corresponding bit in a + * per-vcpu selector word to be set. Each bit in the selector covers a + * 'C long' in the PENDING bitfield array. + */ + xen_ulong_t evtchn_pending[sizeof(xen_ulong_t) * 8]; + xen_ulong_t evtchn_mask[sizeof(xen_ulong_t) * 8]; + + /* + * Wallclock time: updated by control software or RTC emulation. + * Guests should base their gettimeofday() syscall on this + * wallclock-base value. + * The values of wc_sec and wc_nsec are offsets from the Unix epoch + * adjusted by the domain's 'time offset' (in seconds) as set either + * by XEN_DOMCTL_settimeoffset, or adjusted via a guest write to the + * emulated RTC. + */ + uint32_t wc_version; /* Version counter: see vcpu_time_info_t. */ + uint32_t wc_sec; + uint32_t wc_nsec; +#if !defined(__i386__) + uint32_t wc_sec_hi; +# define xen_wc_sec_hi wc_sec_hi +#elif !defined(__XEN__) && !defined(__XEN_TOOLS__) +# define xen_wc_sec_hi arch.wc_sec_hi +#endif + + struct arch_shared_info arch; + +}; +#ifndef __XEN__ +typedef struct shared_info shared_info_t; +#endif + +/* + * `incontents 200 startofday Start-of-day memory layout + * + * 1. The domain is started within contiguous virtual-memory region. + * 2. The contiguous region ends on an aligned 4MB boundary. + * 3. This the order of bootstrap elements in the initial virtual region: + * a. relocated kernel image + * b. initial ram disk [mod_start, mod_len] + * (may be omitted) + * c. list of allocated page frames [mfn_list, nr_pages] + * (unless relocated due to XEN_ELFNOTE_INIT_P2M) + * d. start_info_t structure [register rSI (x86)] + * in case of dom0 this page contains the console info, too + * e. unless dom0: xenstore ring page + * f. unless dom0: console ring page + * g. bootstrap page tables [pt_base and CR3 (x86)] + * h. bootstrap stack [register ESP (x86)] + * 4. Bootstrap elements are packed together, but each is 4kB-aligned. + * 5. The list of page frames forms a contiguous 'pseudo-physical' memory + * layout for the domain. In particular, the bootstrap virtual-memory + * region is a 1:1 mapping to the first section of the pseudo-physical map. + * 6. All bootstrap elements are mapped read-writable for the guest OS. The + * only exception is the bootstrap page table, which is mapped read-only. + * 7. There is guaranteed to be at least 512kB padding after the final + * bootstrap element. If necessary, the bootstrap virtual region is + * extended by an extra 4MB to ensure this. + * + * Note: Prior to 25833:bb85bbccb1c9. ("x86/32-on-64 adjust Dom0 initial page + * table layout") a bug caused the pt_base (3.g above) and cr3 to not point + * to the start of the guest page tables (it was offset by two pages). + * This only manifested itself on 32-on-64 dom0 kernels and not 32-on-64 domU + * or 64-bit kernels of any colour. The page tables for a 32-on-64 dom0 got + * allocated in the order: 'first L1','first L2', 'first L3', so the offset + * to the page table base is by two pages back. The initial domain if it is + * 32-bit and runs under a 64-bit hypervisor should _NOT_ use two of the + * pages preceding pt_base and mark them as reserved/unused. + */ +#ifdef XEN_HAVE_PV_GUEST_ENTRY +struct start_info { + /* THE FOLLOWING ARE FILLED IN BOTH ON INITIAL BOOT AND ON RESUME. */ + char magic[32]; /* "xen--". */ + unsigned long nr_pages; /* Total pages allocated to this domain. */ + unsigned long shared_info; /* MACHINE address of shared info struct. */ + uint32_t flags; /* SIF_xxx flags. */ + xen_pfn_t store_mfn; /* MACHINE page number of shared page. */ + uint32_t store_evtchn; /* Event channel for store communication. */ + union { + struct { + xen_pfn_t mfn; /* MACHINE page number of console page. */ + uint32_t evtchn; /* Event channel for console page. */ + } domU; + struct { + uint32_t info_off; /* Offset of console_info struct. */ + uint32_t info_size; /* Size of console_info struct from start.*/ + } dom0; + } console; + /* THE FOLLOWING ARE ONLY FILLED IN ON INITIAL BOOT (NOT RESUME). */ + unsigned long pt_base; /* VIRTUAL address of page directory. */ + unsigned long nr_pt_frames; /* Number of bootstrap p.t. frames. */ + unsigned long mfn_list; /* VIRTUAL address of page-frame list. */ + unsigned long mod_start; /* VIRTUAL address of pre-loaded module */ + /* (PFN of pre-loaded module if */ + /* SIF_MOD_START_PFN set in flags). */ + unsigned long mod_len; /* Size (bytes) of pre-loaded module. */ +#define MAX_GUEST_CMDLINE 1024 + int8_t cmd_line[MAX_GUEST_CMDLINE]; + /* The pfn range here covers both page table and p->m table frames. */ + unsigned long first_p2m_pfn;/* 1st pfn forming initial P->M table. */ + unsigned long nr_p2m_frames;/* # of pfns forming initial P->M table. */ +}; +typedef struct start_info start_info_t; + +/* New console union for dom0 introduced in 0x00030203. */ +#if __XEN_INTERFACE_VERSION__ < 0x00030203 +#define console_mfn console.domU.mfn +#define console_evtchn console.domU.evtchn +#endif +#endif /* XEN_HAVE_PV_GUEST_ENTRY */ + +/* These flags are passed in the 'flags' field of start_info_t. */ +#define SIF_PRIVILEGED (1<<0) /* Is the domain privileged? */ +#define SIF_INITDOMAIN (1<<1) /* Is this the initial control domain? */ +#define SIF_MULTIBOOT_MOD (1<<2) /* Is mod_start a multiboot module? */ +#define SIF_MOD_START_PFN (1<<3) /* Is mod_start a PFN? */ +#define SIF_VIRT_P2M_4TOOLS (1<<4) /* Do Xen tools understand a virt. mapped */ + /* P->M making the 3 level tree obsolete? */ +#define SIF_PM_MASK (0xFF<<8) /* reserve 1 byte for xen-pm options */ + +/* + * A multiboot module is a package containing modules very similar to a + * multiboot module array. The only differences are: + * - the array of module descriptors is by convention simply at the beginning + * of the multiboot module, + * - addresses in the module descriptors are based on the beginning of the + * multiboot module, + * - the number of modules is determined by a termination descriptor that has + * mod_start == 0. + * + * This permits to both build it statically and reference it in a configuration + * file, and let the PV guest easily rebase the addresses to virtual addresses + * and at the same time count the number of modules. + */ +struct xen_multiboot_mod_list +{ + /* Address of first byte of the module */ + uint32_t mod_start; + /* Address of last byte of the module (inclusive) */ + uint32_t mod_end; + /* Address of zero-terminated command line */ + uint32_t cmdline; + /* Unused, must be zero */ + uint32_t pad; +}; +/* + * `incontents 200 startofday_dom0_console Dom0_console + * + * The console structure in start_info.console.dom0 + * + * This structure includes a variety of information required to + * have a working VGA/VESA console. + */ +typedef struct dom0_vga_console_info { + uint8_t video_type; /* DOM0_VGA_CONSOLE_??? */ +#define XEN_VGATYPE_TEXT_MODE_3 0x03 +#define XEN_VGATYPE_VESA_LFB 0x23 +#define XEN_VGATYPE_EFI_LFB 0x70 + + union { + struct { + /* Font height, in pixels. */ + uint16_t font_height; + /* Cursor location (column, row). */ + uint16_t cursor_x, cursor_y; + /* Number of rows and columns (dimensions in characters). */ + uint16_t rows, columns; + } text_mode_3; + + struct { + /* Width and height, in pixels. */ + uint16_t width, height; + /* Bytes per scan line. */ + uint16_t bytes_per_line; + /* Bits per pixel. */ + uint16_t bits_per_pixel; + /* LFB physical address, and size (in units of 64kB). */ + uint32_t lfb_base; + uint32_t lfb_size; + /* RGB mask offsets and sizes, as defined by VBE 1.2+ */ + uint8_t red_pos, red_size; + uint8_t green_pos, green_size; + uint8_t blue_pos, blue_size; + uint8_t rsvd_pos, rsvd_size; +#if __XEN_INTERFACE_VERSION__ >= 0x00030206 + /* VESA capabilities (offset 0xa, VESA command 0x4f00). */ + uint32_t gbl_caps; + /* Mode attributes (offset 0x0, VESA command 0x4f01). */ + uint16_t mode_attrs; + uint16_t pad; +#endif +#if __XEN_INTERFACE_VERSION__ >= 0x00040d00 + /* high 32 bits of lfb_base */ + uint32_t ext_lfb_base; +#endif + } vesa_lfb; + } u; +} dom0_vga_console_info_t; +#define xen_vga_console_info dom0_vga_console_info +#define xen_vga_console_info_t dom0_vga_console_info_t + +typedef uint8_t xen_domain_handle_t[16]; + +__DEFINE_XEN_GUEST_HANDLE(uint8, uint8_t); +__DEFINE_XEN_GUEST_HANDLE(uint16, uint16_t); +__DEFINE_XEN_GUEST_HANDLE(uint32, uint32_t); +__DEFINE_XEN_GUEST_HANDLE(uint64, uint64_t); + +typedef struct { + uint8_t a[16]; +} xen_uuid_t; + +/* + * XEN_DEFINE_UUID(0x00112233, 0x4455, 0x6677, 0x8899, + * 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff) + * will construct UUID 00112233-4455-6677-8899-aabbccddeeff presented as + * {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + * 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + * + * NB: This is compatible with Linux kernel and with libuuid, but it is not + * compatible with Microsoft, as they use mixed-endian encoding (some + * components are little-endian, some are big-endian). + */ +#define XEN_DEFINE_UUID_(a, b, c, d, e1, e2, e3, e4, e5, e6) \ + {{((a) >> 24) & 0xFF, ((a) >> 16) & 0xFF, \ + ((a) >> 8) & 0xFF, ((a) >> 0) & 0xFF, \ + ((b) >> 8) & 0xFF, ((b) >> 0) & 0xFF, \ + ((c) >> 8) & 0xFF, ((c) >> 0) & 0xFF, \ + ((d) >> 8) & 0xFF, ((d) >> 0) & 0xFF, \ + e1, e2, e3, e4, e5, e6}} + +#if defined(__STDC_VERSION__) ? __STDC_VERSION__ >= 199901L : defined(__GNUC__) +#define XEN_DEFINE_UUID(a, b, c, d, e1, e2, e3, e4, e5, e6) \ + ((xen_uuid_t)XEN_DEFINE_UUID_(a, b, c, d, e1, e2, e3, e4, e5, e6)) +#else +#define XEN_DEFINE_UUID(a, b, c, d, e1, e2, e3, e4, e5, e6) \ + XEN_DEFINE_UUID_(a, b, c, d, e1, e2, e3, e4, e5, e6) +#endif /* __STDC_VERSION__ / __GNUC__ */ + +#endif /* !__ASSEMBLY__ */ + +/* Default definitions for macros used by domctl/sysctl. */ +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +#ifndef int64_aligned_t +#define int64_aligned_t int64_t +#endif +#ifndef uint64_aligned_t +#define uint64_aligned_t uint64_t +#endif +#ifndef XEN_GUEST_HANDLE_64 +#define XEN_GUEST_HANDLE_64(name) XEN_GUEST_HANDLE(name) +#endif + +#ifndef __ASSEMBLY__ +struct xenctl_bitmap { + XEN_GUEST_HANDLE_64(uint8) bitmap; + uint32_t nr_bits; +}; +typedef struct xenctl_bitmap xenctl_bitmap_t; +#endif + +#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ + +#endif /* __XEN_PUBLIC_XEN_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h index afdf9c436a..4d412fd4b2 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -1,12 +1,22 @@ -#ifndef QEMU_HW_XEN_H -#define QEMU_HW_XEN_H - /* * public xen header * stuff needed outside xen-*.c, i.e. interfaces to qemu. * must not depend on any xen headers being present in * /usr/include/xen, so it can be included unconditionally. */ +#ifndef QEMU_HW_XEN_H +#define QEMU_HW_XEN_H + +/* + * As a temporary measure while the headers are being untangled, define + * __XEN_TOOLS__ here before any Xen headers are included. Otherwise, if + * the Xen toolstack library headers are later included, they will find + * some of the "internal" definitions missing and the build will fail. In + * later commits, we'll end up with a rule that the native libraries have + * to be included first, which will ensure that the libraries get the + * version of Xen libraries that they expect. + */ +#define __XEN_TOOLS__ 1 #include "exec/cpu-common.h" From 820c1aba519bd072ac71c754733f6c86d8b4309f Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 6 Dec 2022 09:03:48 +0000 Subject: [PATCH 127/185] xen: add CONFIG_XEN_BUS and CONFIG_XEN_EMU options for Xen emulation The XEN_EMU option will cover core Xen support in target/, which exists only for x86 with KVM today but could theoretically also be implemented on Arm/Aarch64 and with TCG or other accelerators (if anyone wants to run the gauntlet of struct layout compatibility, errno mapping, and the rest of that fui). It will also cover the support for architecture-independent grant table and event channel support which will be added in hw/i386/kvm/ (on the basis that the non-KVM support is very theoretical and making it not use KVM directly seems like gratuitous overengineering at this point). The XEN_BUS option is for the xenfv platform support, which will now be used both by XEN_EMU and by real Xen. The XEN option remains dependent on the Xen runtime libraries, and covers support for real Xen. Some code which currently resides under CONFIG_XEN will be moving to CONFIG_XEN_BUS over time as the direct dependencies on Xen runtime libraries are eliminated. The Xen PCI platform device will also reside under CONFIG_XEN_BUS. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/Kconfig | 1 + hw/i386/Kconfig | 5 +++++ hw/xen/Kconfig | 3 +++ meson.build | 1 + 4 files changed, 10 insertions(+) create mode 100644 hw/xen/Kconfig diff --git a/hw/Kconfig b/hw/Kconfig index 38233bbb0f..ba62ff6417 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -41,6 +41,7 @@ source tpm/Kconfig source usb/Kconfig source virtio/Kconfig source vfio/Kconfig +source xen/Kconfig source watchdog/Kconfig # arch Kconfig diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 9fbfe748b5..d40802d83f 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -136,3 +136,8 @@ config VMPORT config VMMOUSE bool depends on VMPORT + +config XEN_EMU + bool + default y + depends on KVM && (I386 || X86_64) diff --git a/hw/xen/Kconfig b/hw/xen/Kconfig new file mode 100644 index 0000000000..3467efb986 --- /dev/null +++ b/hw/xen/Kconfig @@ -0,0 +1,3 @@ +config XEN_BUS + bool + default y if (XEN || XEN_EMU) diff --git a/meson.build b/meson.build index 77d2ae87e4..71a14e6b3d 100644 --- a/meson.build +++ b/meson.build @@ -3881,6 +3881,7 @@ if have_system if xen.found() summary_info += {'xen ctrl version': xen.version()} endif + summary_info += {'Xen emulation': config_all.has_key('CONFIG_XEN_EMU')} endif summary_info += {'TCG support': config_all.has_key('CONFIG_TCG')} if config_all.has_key('CONFIG_TCG') From faa4e80071f5114f8113263b5303ac18e7df0a9d Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 12 Dec 2022 22:32:54 +0000 Subject: [PATCH 128/185] xen: Add XEN_DISABLED mode and make it default Also set XEN_ATTACH mode in xen_init() to reflect the truth; not that anyone ever cared before. It was *only* ever checked in xen_init_pv() before. Suggested-by: Paolo Bonzini Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- accel/xen/xen-all.c | 2 ++ include/hw/xen/xen.h | 5 +++-- softmmu/globals.c | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index c1b697a8bd..e85e4aeba5 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -171,6 +171,8 @@ static int xen_init(MachineState *ms) * opt out of system RAM being allocated by generic code */ mc->default_ram_id = NULL; + + xen_mode = XEN_ATTACH; return 0; } diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h index 4d412fd4b2..03983939f9 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -22,8 +22,9 @@ /* xen-machine.c */ enum xen_mode { - XEN_EMULATE = 0, // xen emulation, using xenner (default) - XEN_ATTACH // attach to xen domain created by libxl + XEN_DISABLED = 0, /* xen support disabled (default) */ + XEN_ATTACH, /* attach to xen domain created by libxl */ + XEN_EMULATE, /* emulate Xen within QEMU */ }; extern uint32_t xen_domid; diff --git a/softmmu/globals.c b/softmmu/globals.c index 527edbefdd..0a4405614e 100644 --- a/softmmu/globals.c +++ b/softmmu/globals.c @@ -63,5 +63,5 @@ QemuUUID qemu_uuid; bool qemu_uuid_set; uint32_t xen_domid; -enum xen_mode xen_mode = XEN_EMULATE; +enum xen_mode xen_mode = XEN_DISABLED; bool xen_domid_restrict; From 61491cf4410423b2d3162143c43b9aeb4ccf7f26 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 3 Dec 2022 09:51:13 -0800 Subject: [PATCH 129/185] i386/kvm: Add xen-version KVM accelerator property and init KVM Xen support This just initializes the basic Xen support in KVM for now. Only permitted on TYPE_PC_MACHINE because that's where the sysbus devices for Xen heap overlay, event channel, grant tables and other stuff will exist. There's no point having the basic hypercall support if nothing else works. Provide sysemu/kvm_xen.h and a kvm_xen_get_caps() which will be used later by support devices. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- accel/kvm/kvm-all.c | 1 + include/sysemu/kvm_int.h | 2 ++ include/sysemu/kvm_xen.h | 20 +++++++++++++ target/i386/kvm/kvm.c | 59 +++++++++++++++++++++++++++++++++++++ target/i386/kvm/meson.build | 2 ++ target/i386/kvm/xen-emu.c | 58 ++++++++++++++++++++++++++++++++++++ target/i386/kvm/xen-emu.h | 19 ++++++++++++ 7 files changed, 161 insertions(+) create mode 100644 include/sysemu/kvm_xen.h create mode 100644 target/i386/kvm/xen-emu.c create mode 100644 target/i386/kvm/xen-emu.h diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 86f7523833..511d3eb9a0 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -3703,6 +3703,7 @@ static void kvm_accel_instance_init(Object *obj) s->kvm_dirty_ring_size = 0; s->notify_vmexit = NOTIFY_VMEXIT_OPTION_RUN; s->notify_window = 0; + s->xen_version = 0; } /** diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h index 60b520a13e..7f945bc763 100644 --- a/include/sysemu/kvm_int.h +++ b/include/sysemu/kvm_int.h @@ -118,6 +118,8 @@ struct KVMState struct KVMDirtyRingReaper reaper; NotifyVmexitOption notify_vmexit; uint32_t notify_window; + uint32_t xen_version; + uint32_t xen_caps; }; void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml, diff --git a/include/sysemu/kvm_xen.h b/include/sysemu/kvm_xen.h new file mode 100644 index 0000000000..296533f2d5 --- /dev/null +++ b/include/sysemu/kvm_xen.h @@ -0,0 +1,20 @@ +/* + * Xen HVM emulation support in KVM + * + * Copyright © 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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_SYSEMU_KVM_XEN_H +#define QEMU_SYSEMU_KVM_XEN_H + +uint32_t kvm_xen_get_caps(void); + +#define kvm_xen_has_cap(cap) (!!(kvm_xen_get_caps() & \ + KVM_XEN_HVM_CONFIG_ ## cap)) + +#endif /* QEMU_SYSEMU_KVM_XEN_H */ diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index d18bd2f3e8..da7342630c 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -31,6 +31,7 @@ #include "sysemu/runstate.h" #include "kvm_i386.h" #include "sev.h" +#include "xen-emu.h" #include "hyperv.h" #include "hyperv-proto.h" @@ -42,6 +43,7 @@ #include "qemu/error-report.h" #include "qemu/memalign.h" #include "hw/i386/x86.h" +#include "hw/i386/pc.h" #include "hw/i386/apic.h" #include "hw/i386/apic_internal.h" #include "hw/i386/apic-msidef.h" @@ -49,6 +51,8 @@ #include "hw/i386/x86-iommu.h" #include "hw/i386/e820_memory_layout.h" +#include "hw/xen/xen.h" + #include "hw/pci/pci.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" @@ -2529,6 +2533,22 @@ int kvm_arch_init(MachineState *ms, KVMState *s) } } + if (s->xen_version) { +#ifdef CONFIG_XEN_EMU + if (!object_dynamic_cast(OBJECT(ms), TYPE_PC_MACHINE)) { + error_report("kvm: Xen support only available in PC machine"); + return -ENOTSUP; + } + ret = kvm_xen_init(s); + if (ret < 0) { + return ret; + } +#else + error_report("kvm: Xen support not enabled in qemu"); + return -ENOTSUP; +#endif + } + ret = kvm_get_supported_msrs(s); if (ret < 0) { return ret; @@ -5719,6 +5739,36 @@ static void kvm_arch_set_notify_window(Object *obj, Visitor *v, s->notify_window = value; } +static void kvm_arch_get_xen_version(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + uint32_t value = s->xen_version; + + visit_type_uint32(v, name, &value, errp); +} + +static void kvm_arch_set_xen_version(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + Error *error = NULL; + uint32_t value; + + visit_type_uint32(v, name, &value, &error); + if (error) { + error_propagate(errp, error); + return; + } + + s->xen_version = value; + if (value && xen_mode == XEN_DISABLED) { + xen_mode = XEN_EMULATE; + } +} + void kvm_arch_accel_class_init(ObjectClass *oc) { object_class_property_add_enum(oc, "notify-vmexit", "NotifyVMexitOption", @@ -5735,6 +5785,15 @@ void kvm_arch_accel_class_init(ObjectClass *oc) object_class_property_set_description(oc, "notify-window", "Clock cycles without an event window " "after which a notification VM exit occurs"); + + object_class_property_add(oc, "xen-version", "uint32", + kvm_arch_get_xen_version, + kvm_arch_set_xen_version, + NULL, NULL); + object_class_property_set_description(oc, "xen-version", + "Xen version to be emulated " + "(in XENVER_version form " + "e.g. 0x4000a for 4.10)"); } void kvm_set_max_apic_id(uint32_t max_apic_id) diff --git a/target/i386/kvm/meson.build b/target/i386/kvm/meson.build index 736df8b72e..322272091b 100644 --- a/target/i386/kvm/meson.build +++ b/target/i386/kvm/meson.build @@ -7,6 +7,8 @@ i386_softmmu_kvm_ss.add(files( 'kvm-cpu.c', )) +i386_softmmu_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files('xen-emu.c')) + i386_softmmu_kvm_ss.add(when: 'CONFIG_SEV', if_false: files('sev-stub.c')) i386_softmmu_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c'), if_false: files('hyperv-stub.c')) diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c new file mode 100644 index 0000000000..b556d903aa --- /dev/null +++ b/target/i386/kvm/xen-emu.c @@ -0,0 +1,58 @@ +/* + * Xen HVM emulation support in KVM + * + * Copyright © 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "sysemu/kvm_int.h" +#include "sysemu/kvm_xen.h" +#include "kvm/kvm_i386.h" +#include "xen-emu.h" + +int kvm_xen_init(KVMState *s) +{ + const int required_caps = KVM_XEN_HVM_CONFIG_HYPERCALL_MSR | + KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL | KVM_XEN_HVM_CONFIG_SHARED_INFO; + struct kvm_xen_hvm_config cfg = { + .msr = XEN_HYPERCALL_MSR, + .flags = KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL, + }; + int xen_caps, ret; + + xen_caps = kvm_check_extension(s, KVM_CAP_XEN_HVM); + if (required_caps & ~xen_caps) { + error_report("kvm: Xen HVM guest support not present or insufficient"); + return -ENOSYS; + } + + if (xen_caps & KVM_XEN_HVM_CONFIG_EVTCHN_SEND) { + struct kvm_xen_hvm_attr ha = { + .type = KVM_XEN_ATTR_TYPE_XEN_VERSION, + .u.xen_version = s->xen_version, + }; + (void)kvm_vm_ioctl(s, KVM_XEN_HVM_SET_ATTR, &ha); + + cfg.flags |= KVM_XEN_HVM_CONFIG_EVTCHN_SEND; + } + + ret = kvm_vm_ioctl(s, KVM_XEN_HVM_CONFIG, &cfg); + if (ret < 0) { + error_report("kvm: Failed to enable Xen HVM support: %s", + strerror(-ret)); + return ret; + } + + s->xen_caps = xen_caps; + return 0; +} + +uint32_t kvm_xen_get_caps(void) +{ + return kvm_state->xen_caps; +} diff --git a/target/i386/kvm/xen-emu.h b/target/i386/kvm/xen-emu.h new file mode 100644 index 0000000000..4f31bd96cb --- /dev/null +++ b/target/i386/kvm/xen-emu.h @@ -0,0 +1,19 @@ +/* + * Xen HVM emulation support in KVM + * + * Copyright © 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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_I386_KVM_XEN_EMU_H +#define QEMU_I386_KVM_XEN_EMU_H + +#define XEN_HYPERCALL_MSR 0x40000000 + +int kvm_xen_init(KVMState *s); + +#endif /* QEMU_I386_KVM_XEN_EMU_H */ From f66b8a83c5723e5337d522ba2b87bf8c7e306445 Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Tue, 6 Dec 2022 10:48:53 +0000 Subject: [PATCH 130/185] i386/kvm: handle Xen HVM cpuid leaves Introduce support for emulating CPUID for Xen HVM guests. It doesn't make sense to advertise the KVM leaves to a Xen guest, so do Xen unconditionally when the xen-version machine property is set. Signed-off-by: Joao Martins [dwmw2: Obtain xen_version from KVM property, make it automatic] Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/cpu.c | 1 + target/i386/cpu.h | 2 + target/i386/kvm/kvm.c | 77 ++++++++++++++++++++++++++++++++++++++- target/i386/kvm/xen-emu.c | 4 +- target/i386/kvm/xen-emu.h | 13 ++++++- 5 files changed, 91 insertions(+), 6 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 4bad3d41d3..a0fa04e079 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7200,6 +7200,7 @@ static Property x86_cpu_properties[] = { * own cache information (see x86_cpu_load_def()). */ DEFINE_PROP_BOOL("legacy-cache", X86CPU, legacy_cache, true), + DEFINE_PROP_BOOL("xen-vapic", X86CPU, xen_vapic, false), /* * From "Requirements for Implementing the Microsoft diff --git a/target/i386/cpu.h b/target/i386/cpu.h index ea650e68a3..5069adfbe7 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1975,6 +1975,8 @@ struct ArchCPU { int32_t thread_id; int32_t hv_max_vps; + + bool xen_vapic; }; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index da7342630c..aac43174e8 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -22,6 +22,7 @@ #include #include "standard-headers/asm-x86/kvm_para.h" +#include "hw/xen/interface/arch-x86/cpuid.h" #include "cpu.h" #include "host-cpu.h" @@ -1819,7 +1820,77 @@ int kvm_arch_init_vcpu(CPUState *cs) has_msr_hv_hypercall = true; } - if (cpu->expose_kvm) { + if (cs->kvm_state->xen_version) { +#ifdef CONFIG_XEN_EMU + struct kvm_cpuid_entry2 *xen_max_leaf; + + memcpy(signature, "XenVMMXenVMM", 12); + + xen_max_leaf = c = &cpuid_data.entries[cpuid_i++]; + c->function = kvm_base + XEN_CPUID_SIGNATURE; + c->eax = kvm_base + XEN_CPUID_TIME; + c->ebx = signature[0]; + c->ecx = signature[1]; + c->edx = signature[2]; + + c = &cpuid_data.entries[cpuid_i++]; + c->function = kvm_base + XEN_CPUID_VENDOR; + c->eax = cs->kvm_state->xen_version; + c->ebx = 0; + c->ecx = 0; + c->edx = 0; + + c = &cpuid_data.entries[cpuid_i++]; + c->function = kvm_base + XEN_CPUID_HVM_MSR; + /* Number of hypercall-transfer pages */ + c->eax = 1; + /* Hypercall MSR base address */ + if (hyperv_enabled(cpu)) { + c->ebx = XEN_HYPERCALL_MSR_HYPERV; + kvm_xen_init(cs->kvm_state, c->ebx); + } else { + c->ebx = XEN_HYPERCALL_MSR; + } + c->ecx = 0; + c->edx = 0; + + c = &cpuid_data.entries[cpuid_i++]; + c->function = kvm_base + XEN_CPUID_TIME; + c->eax = ((!!tsc_is_stable_and_known(env) << 1) | + (!!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_RDTSCP) << 2)); + /* default=0 (emulate if necessary) */ + c->ebx = 0; + /* guest tsc frequency */ + c->ecx = env->user_tsc_khz; + /* guest tsc incarnation (migration count) */ + c->edx = 0; + + c = &cpuid_data.entries[cpuid_i++]; + c->function = kvm_base + XEN_CPUID_HVM; + xen_max_leaf->eax = kvm_base + XEN_CPUID_HVM; + if (cs->kvm_state->xen_version >= XEN_VERSION(4, 5)) { + c->function = kvm_base + XEN_CPUID_HVM; + + if (cpu->xen_vapic) { + c->eax |= XEN_HVM_CPUID_APIC_ACCESS_VIRT; + c->eax |= XEN_HVM_CPUID_X2APIC_VIRT; + } + + c->eax |= XEN_HVM_CPUID_IOMMU_MAPPINGS; + + if (cs->kvm_state->xen_version >= XEN_VERSION(4, 6)) { + c->eax |= XEN_HVM_CPUID_VCPU_ID_PRESENT; + c->ebx = cs->cpu_index; + } + } + + kvm_base += 0x100; +#else /* CONFIG_XEN_EMU */ + /* This should never happen as kvm_arch_init() would have died first. */ + fprintf(stderr, "Cannot enable Xen CPUID without Xen support\n"); + abort(); +#endif + } else if (cpu->expose_kvm) { memcpy(signature, "KVMKVMKVM\0\0\0", 12); c = &cpuid_data.entries[cpuid_i++]; c->function = KVM_CPUID_SIGNATURE | kvm_base; @@ -2539,7 +2610,9 @@ int kvm_arch_init(MachineState *ms, KVMState *s) error_report("kvm: Xen support only available in PC machine"); return -ENOTSUP; } - ret = kvm_xen_init(s); + /* hyperv_enabled() doesn't work yet. */ + uint32_t msr = XEN_HYPERCALL_MSR; + ret = kvm_xen_init(s, msr); if (ret < 0) { return ret; } diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index b556d903aa..34d5bc1bc9 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -15,12 +15,12 @@ #include "kvm/kvm_i386.h" #include "xen-emu.h" -int kvm_xen_init(KVMState *s) +int kvm_xen_init(KVMState *s, uint32_t hypercall_msr) { const int required_caps = KVM_XEN_HVM_CONFIG_HYPERCALL_MSR | KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL | KVM_XEN_HVM_CONFIG_SHARED_INFO; struct kvm_xen_hvm_config cfg = { - .msr = XEN_HYPERCALL_MSR, + .msr = hypercall_msr, .flags = KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL, }; int xen_caps, ret; diff --git a/target/i386/kvm/xen-emu.h b/target/i386/kvm/xen-emu.h index 4f31bd96cb..2101df0182 100644 --- a/target/i386/kvm/xen-emu.h +++ b/target/i386/kvm/xen-emu.h @@ -12,8 +12,17 @@ #ifndef QEMU_I386_KVM_XEN_EMU_H #define QEMU_I386_KVM_XEN_EMU_H -#define XEN_HYPERCALL_MSR 0x40000000 +#define XEN_HYPERCALL_MSR 0x40000000 +#define XEN_HYPERCALL_MSR_HYPERV 0x40000200 -int kvm_xen_init(KVMState *s); +#define XEN_CPUID_SIGNATURE 0 +#define XEN_CPUID_VENDOR 1 +#define XEN_CPUID_HVM_MSR 2 +#define XEN_CPUID_TIME 3 +#define XEN_CPUID_HVM 4 + +#define XEN_VERSION(maj, min) ((maj) << 16 | (min)) + +int kvm_xen_init(KVMState *s, uint32_t hypercall_msr); #endif /* QEMU_I386_KVM_XEN_EMU_H */ From 5e691a955a06dc9a45d9ab62769a12e6e8d18cee Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 16 Dec 2022 11:05:29 +0000 Subject: [PATCH 131/185] i386/kvm: Set Xen vCPU ID in KVM There are (at least) three different vCPU ID number spaces. One is the internal KVM vCPU index, based purely on which vCPU was chronologically created in the kernel first. If userspace threads are all spawned and create their KVM vCPUs in essentially random order, then the KVM indices are basically random too. The second number space is the APIC ID space, which is consistent and useful for referencing vCPUs. MSIs will specify the target vCPU using the APIC ID, for example, and the KVM Xen APIs also take an APIC ID from userspace whenever a vCPU needs to be specified (as opposed to just using the appropriate vCPU fd). The third number space is not normally relevant to the kernel, and is the ACPI/MADT/Xen CPU number which corresponds to cs->cpu_index. But Xen timer hypercalls use it, and Xen timer hypercalls *really* want to be accelerated in the kernel rather than handled in userspace, so the kernel needs to be told. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/kvm/kvm.c | 5 +++++ target/i386/kvm/xen-emu.c | 28 ++++++++++++++++++++++++++++ target/i386/kvm/xen-emu.h | 1 + 3 files changed, 34 insertions(+) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index aac43174e8..edcf8a2a65 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -1884,6 +1884,11 @@ int kvm_arch_init_vcpu(CPUState *cs) } } + r = kvm_xen_init_vcpu(cs); + if (r) { + return r; + } + kvm_base += 0x100; #else /* CONFIG_XEN_EMU */ /* This should never happen as kvm_arch_init() would have died first. */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 34d5bc1bc9..4883b95d9d 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -52,6 +52,34 @@ int kvm_xen_init(KVMState *s, uint32_t hypercall_msr) return 0; } +int kvm_xen_init_vcpu(CPUState *cs) +{ + int err; + + /* + * The kernel needs to know the Xen/ACPI vCPU ID because that's + * what the guest uses in hypercalls such as timers. It doesn't + * match the APIC ID which is generally used for talking to the + * kernel about vCPUs. And if vCPU threads race with creating + * their KVM vCPUs out of order, it doesn't necessarily match + * with the kernel's internal vCPU indices either. + */ + if (kvm_xen_has_cap(EVTCHN_SEND)) { + struct kvm_xen_vcpu_attr va = { + .type = KVM_XEN_VCPU_ATTR_TYPE_VCPU_ID, + .u.vcpu_id = cs->cpu_index, + }; + err = kvm_vcpu_ioctl(cs, KVM_XEN_VCPU_SET_ATTR, &va); + if (err) { + error_report("kvm: Failed to set Xen vCPU ID attribute: %s", + strerror(-err)); + return err; + } + } + + return 0; +} + uint32_t kvm_xen_get_caps(void) { return kvm_state->xen_caps; diff --git a/target/i386/kvm/xen-emu.h b/target/i386/kvm/xen-emu.h index 2101df0182..d62f1d8ed8 100644 --- a/target/i386/kvm/xen-emu.h +++ b/target/i386/kvm/xen-emu.h @@ -24,5 +24,6 @@ #define XEN_VERSION(maj, min) ((maj) << 16 | (min)) int kvm_xen_init(KVMState *s, uint32_t hypercall_msr); +int kvm_xen_init_vcpu(CPUState *cs); #endif /* QEMU_I386_KVM_XEN_EMU_H */ From 3bb1ebac6cc372d2eaa12a5e576b2d28f3a29269 Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Fri, 18 Jan 2019 14:29:52 -0500 Subject: [PATCH 132/185] xen-platform: exclude vfio-pci from the PCI platform unplug Such that PCI passthrough devices work for Xen emulated guests. Signed-off-by: Joao Martins Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/xen/xen_platform.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index 3795a203d4..8a61697053 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -108,12 +108,25 @@ static void log_writeb(PCIXenPlatformState *s, char val) #define _UNPLUG_NVME_DISKS 3 #define UNPLUG_NVME_DISKS (1u << _UNPLUG_NVME_DISKS) +static bool pci_device_is_passthrough(PCIDevice *d) +{ + if (!strcmp(d->name, "xen-pci-passthrough")) { + return true; + } + + if (xen_mode == XEN_EMULATE && !strcmp(d->name, "vfio-pci")) { + return true; + } + + return false; +} + static void unplug_nic(PCIBus *b, PCIDevice *d, void *o) { /* We have to ignore passthrough devices */ if (pci_get_word(d->config + PCI_CLASS_DEVICE) == PCI_CLASS_NETWORK_ETHERNET - && strcmp(d->name, "xen-pci-passthrough") != 0) { + && !pci_device_is_passthrough(d)) { object_unparent(OBJECT(d)); } } @@ -186,9 +199,8 @@ static void unplug_disks(PCIBus *b, PCIDevice *d, void *opaque) !(flags & UNPLUG_IDE_SCSI_DISKS); /* We have to ignore passthrough devices */ - if (!strcmp(d->name, "xen-pci-passthrough")) { + if (pci_device_is_passthrough(d)) return; - } switch (pci_get_word(d->config + PCI_CLASS_DEVICE)) { case PCI_CLASS_STORAGE_IDE: From bb346fae3827b0d7d241e0304069e18cd8155d05 Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Tue, 19 Jun 2018 06:44:46 -0400 Subject: [PATCH 133/185] xen-platform: allow its creation with XEN_EMULATE mode The only thing we need to fix to make this build is the PIO hack which sets the BIOS memory areas to R/W v.s. R/O. Theoretically we could hook that up to the PAM registers on the emulated PIIX, but in practice nobody cares, so just leave it doing nothing. Now it builds without actual Xen, move it to CONFIG_XEN_BUS to include it in the KVM-only builds. Signed-off-by: Joao Martins Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/xen/meson.build | 5 ++++- hw/i386/xen/xen_platform.c | 39 +++++++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/hw/i386/xen/meson.build b/hw/i386/xen/meson.build index be84130300..2e64a34e16 100644 --- a/hw/i386/xen/meson.build +++ b/hw/i386/xen/meson.build @@ -2,6 +2,9 @@ i386_ss.add(when: 'CONFIG_XEN', if_true: files( 'xen-hvm.c', 'xen-mapcache.c', 'xen_apic.c', - 'xen_platform.c', 'xen_pvdevice.c', )) + +i386_ss.add(when: 'CONFIG_XEN_BUS', if_true: files( + 'xen_platform.c', +)) diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index 8a61697053..539f7da374 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -27,9 +27,9 @@ #include "qapi/error.h" #include "hw/ide/pci.h" #include "hw/pci/pci.h" -#include "hw/xen/xen_common.h" #include "migration/vmstate.h" -#include "hw/xen/xen-legacy-backend.h" +#include "hw/xen/xen.h" +#include "net/net.h" #include "trace.h" #include "sysemu/xen.h" #include "sysemu/block-backend.h" @@ -37,6 +37,11 @@ #include "qemu/module.h" #include "qom/object.h" +#ifdef CONFIG_XEN +#include "hw/xen/xen_common.h" +#include "hw/xen/xen-legacy-backend.h" +#endif + //#define DEBUG_PLATFORM #ifdef DEBUG_PLATFORM @@ -279,18 +284,26 @@ static void platform_fixed_ioport_writeb(void *opaque, uint32_t addr, uint32_t v PCIXenPlatformState *s = opaque; switch (addr) { - case 0: /* Platform flags */ { - hvmmem_type_t mem_type = (val & PFFLAG_ROM_LOCK) ? - HVMMEM_ram_ro : HVMMEM_ram_rw; - if (xen_set_mem_type(xen_domid, mem_type, 0xc0, 0x40)) { - DPRINTF("unable to change ro/rw state of ROM memory area!\n"); - } else { + case 0: /* Platform flags */ + if (xen_mode == XEN_EMULATE) { + /* XX: Use i440gx/q35 PAM setup to do this? */ s->flags = val & PFFLAG_ROM_LOCK; - DPRINTF("changed ro/rw state of ROM memory area. now is %s state.\n", - (mem_type == HVMMEM_ram_ro ? "ro":"rw")); +#ifdef CONFIG_XEN + } else { + hvmmem_type_t mem_type = (val & PFFLAG_ROM_LOCK) ? + HVMMEM_ram_ro : HVMMEM_ram_rw; + + if (xen_set_mem_type(xen_domid, mem_type, 0xc0, 0x40)) { + DPRINTF("unable to change ro/rw state of ROM memory area!\n"); + } else { + s->flags = val & PFFLAG_ROM_LOCK; + DPRINTF("changed ro/rw state of ROM memory area. now is %s state.\n", + (mem_type == HVMMEM_ram_ro ? "ro" : "rw")); + } +#endif } break; - } + case 2: log_writeb(s, val); break; @@ -508,8 +521,8 @@ static void xen_platform_realize(PCIDevice *dev, Error **errp) uint8_t *pci_conf; /* Device will crash on reset if xen is not initialized */ - if (!xen_enabled()) { - error_setg(errp, "xen-platform device requires the Xen accelerator"); + if (xen_mode == XEN_DISABLED) { + error_setg(errp, "xen-platform device requires a Xen guest"); return; } From 55a3f666b4ca06012e1fd2bc298c4658b28773ea Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Wed, 13 Jun 2018 10:14:31 -0400 Subject: [PATCH 134/185] i386/xen: handle guest hypercalls This means handling the new exit reason for Xen but still crashing on purpose. As we implement each of the hypercalls we will then return the right return code. Signed-off-by: Joao Martins [dwmw2: Add CPL to hypercall tracing, disallow hypercalls from CPL > 0] Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/kvm/kvm.c | 5 ++++ target/i386/kvm/trace-events | 3 +++ target/i386/kvm/xen-emu.c | 44 ++++++++++++++++++++++++++++++++++++ target/i386/kvm/xen-emu.h | 1 + 4 files changed, 53 insertions(+) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index edcf8a2a65..f43e5531bf 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5493,6 +5493,11 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) assert(run->msr.reason == KVM_MSR_EXIT_REASON_FILTER); ret = kvm_handle_wrmsr(cpu, run); break; +#ifdef CONFIG_XEN_EMU + case KVM_EXIT_XEN: + ret = kvm_xen_handle_exit(cpu, &run->xen); + break; +#endif default: fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); ret = -1; diff --git a/target/i386/kvm/trace-events b/target/i386/kvm/trace-events index 7c369db1e1..cd6f842b1f 100644 --- a/target/i386/kvm/trace-events +++ b/target/i386/kvm/trace-events @@ -5,3 +5,6 @@ kvm_x86_fixup_msi_error(uint32_t gsi) "VT-d failed to remap interrupt for GSI %" kvm_x86_add_msi_route(int virq) "Adding route entry for virq %d" kvm_x86_remove_msi_route(int virq) "Removing route entry for virq %d" kvm_x86_update_msi_routes(int num) "Updated %d MSI routes" + +# xen-emu.c +kvm_xen_hypercall(int cpu, uint8_t cpl, uint64_t input, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t ret) "xen_hypercall: cpu %d cpl %d input %" PRIu64 " a0 0x%" PRIx64 " a1 0x%" PRIx64 " a2 0x%" PRIx64" ret 0x%" PRIx64 diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 4883b95d9d..476f464ee2 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -10,10 +10,12 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "sysemu/kvm_int.h" #include "sysemu/kvm_xen.h" #include "kvm/kvm_i386.h" #include "xen-emu.h" +#include "trace.h" int kvm_xen_init(KVMState *s, uint32_t hypercall_msr) { @@ -84,3 +86,45 @@ uint32_t kvm_xen_get_caps(void) { return kvm_state->xen_caps; } + +static bool do_kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) +{ + uint16_t code = exit->u.hcall.input; + + if (exit->u.hcall.cpl > 0) { + exit->u.hcall.result = -EPERM; + return true; + } + + switch (code) { + default: + return false; + } +} + +int kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) +{ + if (exit->type != KVM_EXIT_XEN_HCALL) { + return -1; + } + + if (!do_kvm_xen_handle_exit(cpu, exit)) { + /* + * Some hypercalls will be deliberately "implemented" by returning + * -ENOSYS. This case is for hypercalls which are unexpected. + */ + exit->u.hcall.result = -ENOSYS; + qemu_log_mask(LOG_UNIMP, "Unimplemented Xen hypercall %" + PRId64 " (0x%" PRIx64 " 0x%" PRIx64 " 0x%" PRIx64 ")\n", + (uint64_t)exit->u.hcall.input, + (uint64_t)exit->u.hcall.params[0], + (uint64_t)exit->u.hcall.params[1], + (uint64_t)exit->u.hcall.params[2]); + } + + trace_kvm_xen_hypercall(CPU(cpu)->cpu_index, exit->u.hcall.cpl, + exit->u.hcall.input, exit->u.hcall.params[0], + exit->u.hcall.params[1], exit->u.hcall.params[2], + exit->u.hcall.result); + return 0; +} diff --git a/target/i386/kvm/xen-emu.h b/target/i386/kvm/xen-emu.h index d62f1d8ed8..21faf6bf38 100644 --- a/target/i386/kvm/xen-emu.h +++ b/target/i386/kvm/xen-emu.h @@ -25,5 +25,6 @@ int kvm_xen_init(KVMState *s, uint32_t hypercall_msr); int kvm_xen_init_vcpu(CPUState *cs); +int kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit); #endif /* QEMU_I386_KVM_XEN_EMU_H */ From bedcc1392481e2528d9b470a9b21512a923a3b75 Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Thu, 14 Jun 2018 08:29:45 -0400 Subject: [PATCH 135/185] i386/xen: implement HYPERVISOR_xen_version This is just meant to serve as an example on how we can implement hypercalls. xen_version specifically since Qemu does all kind of feature controllability. So handling that here seems appropriate. Signed-off-by: Joao Martins [dwmw2: Implement kvm_gva_rw() safely] Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/kvm/xen-emu.c | 86 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 476f464ee2..56b80a7880 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -14,9 +14,55 @@ #include "sysemu/kvm_int.h" #include "sysemu/kvm_xen.h" #include "kvm/kvm_i386.h" +#include "exec/address-spaces.h" #include "xen-emu.h" #include "trace.h" +#include "hw/xen/interface/version.h" + +static int kvm_gva_rw(CPUState *cs, uint64_t gva, void *_buf, size_t sz, + bool is_write) +{ + uint8_t *buf = (uint8_t *)_buf; + int ret; + + while (sz) { + struct kvm_translation tr = { + .linear_address = gva, + }; + + size_t len = TARGET_PAGE_SIZE - (tr.linear_address & ~TARGET_PAGE_MASK); + if (len > sz) { + len = sz; + } + + ret = kvm_vcpu_ioctl(cs, KVM_TRANSLATE, &tr); + if (ret || !tr.valid || (is_write && !tr.writeable)) { + return -EFAULT; + } + + cpu_physical_memory_rw(tr.physical_address, buf, len, is_write); + + buf += len; + sz -= len; + gva += len; + } + + return 0; +} + +static inline int kvm_copy_from_gva(CPUState *cs, uint64_t gva, void *buf, + size_t sz) +{ + return kvm_gva_rw(cs, gva, buf, sz, false); +} + +static inline int kvm_copy_to_gva(CPUState *cs, uint64_t gva, void *buf, + size_t sz) +{ + return kvm_gva_rw(cs, gva, buf, sz, true); +} + int kvm_xen_init(KVMState *s, uint32_t hypercall_msr) { const int required_caps = KVM_XEN_HVM_CONFIG_HYPERCALL_MSR | @@ -87,6 +133,43 @@ uint32_t kvm_xen_get_caps(void) return kvm_state->xen_caps; } +static bool kvm_xen_hcall_xen_version(struct kvm_xen_exit *exit, X86CPU *cpu, + int cmd, uint64_t arg) +{ + int err = 0; + + switch (cmd) { + case XENVER_get_features: { + struct xen_feature_info fi; + + /* No need for 32/64 compat handling */ + qemu_build_assert(sizeof(fi) == 8); + + err = kvm_copy_from_gva(CPU(cpu), arg, &fi, sizeof(fi)); + if (err) { + break; + } + + fi.submap = 0; + if (fi.submap_idx == 0) { + fi.submap |= 1 << XENFEAT_writable_page_tables | + 1 << XENFEAT_writable_descriptor_tables | + 1 << XENFEAT_auto_translated_physmap | + 1 << XENFEAT_supervisor_mode_kernel; + } + + err = kvm_copy_to_gva(CPU(cpu), arg, &fi, sizeof(fi)); + break; + } + + default: + return false; + } + + exit->u.hcall.result = err; + return true; +} + static bool do_kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) { uint16_t code = exit->u.hcall.input; @@ -97,6 +180,9 @@ static bool do_kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) } switch (code) { + case __HYPERVISOR_xen_version: + return kvm_xen_hcall_xen_version(exit, cpu, exit->u.hcall.params[0], + exit->u.hcall.params[1]); default: return false; } From 79b7067dc6acec07d93407dd870268a5cd68924d Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Fri, 20 Jul 2018 15:19:05 -0400 Subject: [PATCH 136/185] i386/xen: implement HYPERVISOR_sched_op, SCHEDOP_shutdown It allows to shutdown itself via hypercall with any of the 3 reasons: 1) self-reboot 2) shutdown 3) crash Implementing SCHEDOP_shutdown sub op let us handle crashes gracefully rather than leading to triple faults if it remains unimplemented. In addition, the SHUTDOWN_soft_reset reason is used for kexec, to reset Xen shared pages and other enlightenments and leave a clean slate for the new kernel without the hypervisor helpfully writing information at unexpected addresses. Signed-off-by: Joao Martins [dwmw2: Ditch sched_op_compat which was never available for HVM guests, Add SCHEDOP_soft_reset] Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- include/sysemu/kvm_xen.h | 1 + target/i386/kvm/trace-events | 1 + target/i386/kvm/xen-emu.c | 75 ++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) diff --git a/include/sysemu/kvm_xen.h b/include/sysemu/kvm_xen.h index 296533f2d5..5dffcc0542 100644 --- a/include/sysemu/kvm_xen.h +++ b/include/sysemu/kvm_xen.h @@ -12,6 +12,7 @@ #ifndef QEMU_SYSEMU_KVM_XEN_H #define QEMU_SYSEMU_KVM_XEN_H +int kvm_xen_soft_reset(void); uint32_t kvm_xen_get_caps(void); #define kvm_xen_has_cap(cap) (!!(kvm_xen_get_caps() & \ diff --git a/target/i386/kvm/trace-events b/target/i386/kvm/trace-events index cd6f842b1f..bb732e1da8 100644 --- a/target/i386/kvm/trace-events +++ b/target/i386/kvm/trace-events @@ -8,3 +8,4 @@ kvm_x86_update_msi_routes(int num) "Updated %d MSI routes" # xen-emu.c kvm_xen_hypercall(int cpu, uint8_t cpl, uint64_t input, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t ret) "xen_hypercall: cpu %d cpl %d input %" PRIu64 " a0 0x%" PRIx64 " a1 0x%" PRIx64 " a2 0x%" PRIx64" ret 0x%" PRIx64 +kvm_xen_soft_reset(void) "" diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 56b80a7880..4ed833656f 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -11,14 +11,17 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "qemu/main-loop.h" #include "sysemu/kvm_int.h" #include "sysemu/kvm_xen.h" #include "kvm/kvm_i386.h" #include "exec/address-spaces.h" #include "xen-emu.h" #include "trace.h" +#include "sysemu/runstate.h" #include "hw/xen/interface/version.h" +#include "hw/xen/interface/sched.h" static int kvm_gva_rw(CPUState *cs, uint64_t gva, void *_buf, size_t sz, bool is_write) @@ -170,6 +173,75 @@ static bool kvm_xen_hcall_xen_version(struct kvm_xen_exit *exit, X86CPU *cpu, return true; } +int kvm_xen_soft_reset(void) +{ + assert(qemu_mutex_iothread_locked()); + + trace_kvm_xen_soft_reset(); + + /* Nothing to reset... yet. */ + return 0; +} + +static int schedop_shutdown(CPUState *cs, uint64_t arg) +{ + struct sched_shutdown shutdown; + int ret = 0; + + /* No need for 32/64 compat handling */ + qemu_build_assert(sizeof(shutdown) == 4); + + if (kvm_copy_from_gva(cs, arg, &shutdown, sizeof(shutdown))) { + return -EFAULT; + } + + switch (shutdown.reason) { + case SHUTDOWN_crash: + cpu_dump_state(cs, stderr, CPU_DUMP_CODE); + qemu_system_guest_panicked(NULL); + break; + + case SHUTDOWN_reboot: + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + break; + + case SHUTDOWN_poweroff: + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + break; + + case SHUTDOWN_soft_reset: + qemu_mutex_lock_iothread(); + ret = kvm_xen_soft_reset(); + qemu_mutex_unlock_iothread(); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static bool kvm_xen_hcall_sched_op(struct kvm_xen_exit *exit, X86CPU *cpu, + int cmd, uint64_t arg) +{ + CPUState *cs = CPU(cpu); + int err = -ENOSYS; + + switch (cmd) { + case SCHEDOP_shutdown: + err = schedop_shutdown(cs, arg); + break; + + default: + return false; + } + + exit->u.hcall.result = err; + return true; +} + static bool do_kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) { uint16_t code = exit->u.hcall.input; @@ -180,6 +252,9 @@ static bool do_kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) } switch (code) { + case __HYPERVISOR_sched_op: + return kvm_xen_hcall_sched_op(exit, cpu, exit->u.hcall.params[0], + exit->u.hcall.params[1]); case __HYPERVISOR_xen_version: return kvm_xen_hcall_xen_version(exit, cpu, exit->u.hcall.params[0], exit->u.hcall.params[1]); From c789b9ef5f80ac471a270bf65cd22024594671a5 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 14 Dec 2022 21:50:41 +0000 Subject: [PATCH 137/185] i386/xen: Implement SCHEDOP_poll and SCHEDOP_yield They both do the same thing and just call sched_yield. This is enough to stop the Linux guest panicking when running on a host kernel which doesn't intercept SCHEDOP_poll and lets it reach userspace. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/kvm/xen-emu.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 4ed833656f..ebea27caf6 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -234,6 +234,19 @@ static bool kvm_xen_hcall_sched_op(struct kvm_xen_exit *exit, X86CPU *cpu, err = schedop_shutdown(cs, arg); break; + case SCHEDOP_poll: + /* + * Linux will panic if this doesn't work. Just yield; it's not + * worth overthinking it because with event channel handling + * in KVM, the kernel will intercept this and it will never + * reach QEMU anyway. The semantics of the hypercall explicltly + * permit spurious wakeups. + */ + case SCHEDOP_yield: + sched_yield(); + err = 0; + break; + default: return false; } From d40ddd529004709350202114fc1c2fc99127647d Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 7 Dec 2022 09:19:31 +0000 Subject: [PATCH 138/185] hw/xen: Add xen_overlay device for emulating shared xenheap pages For the shared info page and for grant tables, Xen shares its own pages from the "Xen heap" to the guest. The guest requests that a given page from a certain address space (XENMAPSPACE_shared_info, etc.) be mapped to a given GPA using the XENMEM_add_to_physmap hypercall. To support that in qemu when *emulating* Xen, create a memory region (migratable) and allow it to be mapped as an overlay when requested. Xen theoretically allows the same page to be mapped multiple times into the guest, but that's hard to track and reinstate over migration, so we automatically *unmap* any previous mapping when creating a new one. This approach has been used in production with.... a non-trivial number of guests expecting true Xen, without any problems yet being noticed. This adds just the shared info page for now. The grant tables will be a larger region, and will need to be overlaid one page at a time. I think that means I need to create separate aliases for each page of the overall grant_frames region, so that they can be mapped individually. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/meson.build | 1 + hw/i386/kvm/xen_overlay.c | 210 ++++++++++++++++++++++++++++++++++++++ hw/i386/kvm/xen_overlay.h | 20 ++++ include/sysemu/kvm_xen.h | 7 ++ 4 files changed, 238 insertions(+) create mode 100644 hw/i386/kvm/xen_overlay.c create mode 100644 hw/i386/kvm/xen_overlay.h diff --git a/hw/i386/kvm/meson.build b/hw/i386/kvm/meson.build index 95467f1ded..6165cbf019 100644 --- a/hw/i386/kvm/meson.build +++ b/hw/i386/kvm/meson.build @@ -4,5 +4,6 @@ i386_kvm_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c')) i386_kvm_ss.add(when: 'CONFIG_I8254', if_true: files('i8254.c')) i386_kvm_ss.add(when: 'CONFIG_I8259', if_true: files('i8259.c')) i386_kvm_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic.c')) +i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files('xen_overlay.c')) i386_ss.add_all(when: 'CONFIG_KVM', if_true: i386_kvm_ss) diff --git a/hw/i386/kvm/xen_overlay.c b/hw/i386/kvm/xen_overlay.c new file mode 100644 index 0000000000..a2441e2b4e --- /dev/null +++ b/hw/i386/kvm/xen_overlay.c @@ -0,0 +1,210 @@ +/* + * QEMU Xen emulation: Shared/overlay pages support + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/host-utils.h" +#include "qemu/module.h" +#include "qemu/main-loop.h" +#include "qapi/error.h" +#include "qom/object.h" +#include "exec/target_page.h" +#include "exec/address-spaces.h" +#include "migration/vmstate.h" + +#include "hw/sysbus.h" +#include "hw/xen/xen.h" +#include "xen_overlay.h" + +#include "sysemu/kvm.h" +#include "sysemu/kvm_xen.h" +#include + +#include "hw/xen/interface/memory.h" + + +#define TYPE_XEN_OVERLAY "xen-overlay" +OBJECT_DECLARE_SIMPLE_TYPE(XenOverlayState, XEN_OVERLAY) + +#define XEN_PAGE_SHIFT 12 +#define XEN_PAGE_SIZE (1ULL << XEN_PAGE_SHIFT) + +struct XenOverlayState { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + MemoryRegion shinfo_mem; + void *shinfo_ptr; + uint64_t shinfo_gpa; +}; + +struct XenOverlayState *xen_overlay_singleton; + +static void xen_overlay_do_map_page(MemoryRegion *page, uint64_t gpa) +{ + /* + * Xen allows guests to map the same page as many times as it likes + * into guest physical frames. We don't, because it would be hard + * to track and restore them all. One mapping of each page is + * perfectly sufficient for all known guests... and we've tested + * that theory on a few now in other implementations. dwmw2. + */ + if (memory_region_is_mapped(page)) { + if (gpa == INVALID_GPA) { + memory_region_del_subregion(get_system_memory(), page); + } else { + /* Just move it */ + memory_region_set_address(page, gpa); + } + } else if (gpa != INVALID_GPA) { + memory_region_add_subregion_overlap(get_system_memory(), gpa, page, 0); + } +} + +/* KVM is the only existing back end for now. Let's not overengineer it yet. */ +static int xen_overlay_set_be_shinfo(uint64_t gfn) +{ + struct kvm_xen_hvm_attr xa = { + .type = KVM_XEN_ATTR_TYPE_SHARED_INFO, + .u.shared_info.gfn = gfn, + }; + + return kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &xa); +} + + +static void xen_overlay_realize(DeviceState *dev, Error **errp) +{ + XenOverlayState *s = XEN_OVERLAY(dev); + + if (xen_mode != XEN_EMULATE) { + error_setg(errp, "Xen overlay page support is for Xen emulation"); + return; + } + + memory_region_init_ram(&s->shinfo_mem, OBJECT(dev), "xen:shared_info", + XEN_PAGE_SIZE, &error_abort); + memory_region_set_enabled(&s->shinfo_mem, true); + + s->shinfo_ptr = memory_region_get_ram_ptr(&s->shinfo_mem); + s->shinfo_gpa = INVALID_GPA; + memset(s->shinfo_ptr, 0, XEN_PAGE_SIZE); +} + +static int xen_overlay_post_load(void *opaque, int version_id) +{ + XenOverlayState *s = opaque; + + if (s->shinfo_gpa != INVALID_GPA) { + xen_overlay_do_map_page(&s->shinfo_mem, s->shinfo_gpa); + xen_overlay_set_be_shinfo(s->shinfo_gpa >> XEN_PAGE_SHIFT); + } + + return 0; +} + +static bool xen_overlay_is_needed(void *opaque) +{ + return xen_mode == XEN_EMULATE; +} + +static const VMStateDescription xen_overlay_vmstate = { + .name = "xen_overlay", + .version_id = 1, + .minimum_version_id = 1, + .needed = xen_overlay_is_needed, + .post_load = xen_overlay_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT64(shinfo_gpa, XenOverlayState), + VMSTATE_END_OF_LIST() + } +}; + +static void xen_overlay_reset(DeviceState *dev) +{ + kvm_xen_soft_reset(); +} + +static void xen_overlay_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = xen_overlay_reset; + dc->realize = xen_overlay_realize; + dc->vmsd = &xen_overlay_vmstate; +} + +static const TypeInfo xen_overlay_info = { + .name = TYPE_XEN_OVERLAY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XenOverlayState), + .class_init = xen_overlay_class_init, +}; + +void xen_overlay_create(void) +{ + xen_overlay_singleton = XEN_OVERLAY(sysbus_create_simple(TYPE_XEN_OVERLAY, + -1, NULL)); + + /* If xen_domid wasn't explicitly set, at least make sure it isn't zero. */ + if (xen_domid == DOMID_QEMU) { + xen_domid = 1; + }; +} + +static void xen_overlay_register_types(void) +{ + type_register_static(&xen_overlay_info); +} + +type_init(xen_overlay_register_types) + +int xen_overlay_map_shinfo_page(uint64_t gpa) +{ + XenOverlayState *s = xen_overlay_singleton; + int ret; + + if (!s) { + return -ENOENT; + } + + assert(qemu_mutex_iothread_locked()); + + if (s->shinfo_gpa) { + /* If removing shinfo page, turn the kernel magic off first */ + ret = xen_overlay_set_be_shinfo(INVALID_GFN); + if (ret) { + return ret; + } + } + + xen_overlay_do_map_page(&s->shinfo_mem, gpa); + if (gpa != INVALID_GPA) { + ret = xen_overlay_set_be_shinfo(gpa >> XEN_PAGE_SHIFT); + if (ret) { + return ret; + } + } + s->shinfo_gpa = gpa; + + return 0; +} + +void *xen_overlay_get_shinfo_ptr(void) +{ + XenOverlayState *s = xen_overlay_singleton; + + if (!s) { + return NULL; + } + + return s->shinfo_ptr; +} diff --git a/hw/i386/kvm/xen_overlay.h b/hw/i386/kvm/xen_overlay.h new file mode 100644 index 0000000000..00cff05bb0 --- /dev/null +++ b/hw/i386/kvm/xen_overlay.h @@ -0,0 +1,20 @@ +/* + * QEMU Xen emulation: Shared/overlay pages support + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * 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_XEN_OVERLAY_H +#define QEMU_XEN_OVERLAY_H + +void xen_overlay_create(void); + +int xen_overlay_map_shinfo_page(uint64_t gpa); +void *xen_overlay_get_shinfo_ptr(void); + +#endif /* QEMU_XEN_OVERLAY_H */ diff --git a/include/sysemu/kvm_xen.h b/include/sysemu/kvm_xen.h index 5dffcc0542..cec21c8fab 100644 --- a/include/sysemu/kvm_xen.h +++ b/include/sysemu/kvm_xen.h @@ -12,6 +12,13 @@ #ifndef QEMU_SYSEMU_KVM_XEN_H #define QEMU_SYSEMU_KVM_XEN_H +/* The KVM API uses these to indicate "no GPA" or "no GFN" */ +#define INVALID_GPA UINT64_MAX +#define INVALID_GFN UINT64_MAX + +/* QEMU plays the rôle of dom0 for "interdomain" communication. */ +#define DOMID_QEMU 0 + int kvm_xen_soft_reset(void); uint32_t kvm_xen_get_caps(void); From 7058fa02200e70bfecda10fd58c2555674d802ed Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Tue, 24 Jan 2023 10:59:49 +0000 Subject: [PATCH 139/185] xen: Permit --xen-domid argument when accel is KVM Signed-off-by: Paul Durrant Signed-off-by: David Wooodhouse --- softmmu/vl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/softmmu/vl.c b/softmmu/vl.c index cebe8d9452..3340f63c37 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -3360,7 +3360,7 @@ void qemu_init(int argc, char **argv) has_defaults = 0; break; case QEMU_OPTION_xen_domid: - if (!(accel_find("xen"))) { + if (!(accel_find("xen")) && !(accel_find("kvm"))) { error_report("Option not supported for this target"); exit(1); } From e21be724eaf5dfdf1ac3595e0c808b775ec262f2 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 12 Dec 2022 23:40:45 +0000 Subject: [PATCH 140/185] i386/xen: add pc_machine_kvm_type to initialize XEN_EMULATE mode The xen_overlay device (and later similar devices for event channels and grant tables) need to be instantiated. Do this from a kvm_type method on the PC machine derivatives, since KVM is only way to support Xen emulation for now. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/pc.c | 11 +++++++++++ include/hw/i386/pc.h | 3 +++ 2 files changed, 14 insertions(+) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 992951c107..a316a01b15 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -90,6 +90,7 @@ #include "hw/virtio/virtio-iommu.h" #include "hw/virtio/virtio-pmem-pci.h" #include "hw/virtio/virtio-mem-pci.h" +#include "hw/i386/kvm/xen_overlay.h" #include "hw/mem/memory-device.h" #include "sysemu/replay.h" #include "target/i386/cpu.h" @@ -1846,6 +1847,16 @@ static void pc_machine_initfn(Object *obj) cxl_machine_init(obj, &pcms->cxl_devices_state); } +int pc_machine_kvm_type(MachineState *machine, const char *kvm_type) +{ +#ifdef CONFIG_XEN_EMU + if (xen_mode == XEN_EMULATE) { + xen_overlay_create(); + } +#endif + return 0; +} + static void pc_machine_reset(MachineState *machine, ShutdownCause reason) { CPUState *cs; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 66e3d059ef..467311007e 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -291,12 +291,15 @@ extern const size_t pc_compat_1_5_len; extern GlobalProperty pc_compat_1_4[]; extern const size_t pc_compat_1_4_len; +int pc_machine_kvm_type(MachineState *machine, const char *vm_type); + #define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \ static void pc_machine_##suffix##_class_init(ObjectClass *oc, void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ optsfn(mc); \ mc->init = initfn; \ + mc->kvm_type = pc_machine_kvm_type; \ } \ static const TypeInfo pc_machine_type_##suffix = { \ .name = namestr TYPE_MACHINE_SUFFIX, \ From 110a0ea59f263b6e382ee22c70c31c2364d11eb0 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 12 Dec 2022 14:03:41 +0000 Subject: [PATCH 141/185] i386/xen: manage and save/restore Xen guest long_mode setting Xen will "latch" the guest's 32-bit or 64-bit ("long mode") setting when the guest writes the MSR to fill in the hypercall page, or when the guest sets the event channel callback in HVM_PARAM_CALLBACK_IRQ. KVM handles the former and sets the kernel's long_mode flag accordingly. The latter will be handled in userspace. Keep them in sync by noticing when a hypercall is made in a mode that doesn't match qemu's idea of the guest mode, and resyncing from the kernel. Do that same sync right before serialization too, in case the guest has set the hypercall page but hasn't yet made a system call. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_overlay.c | 62 +++++++++++++++++++++++++++++++++++++++ hw/i386/kvm/xen_overlay.h | 4 +++ target/i386/kvm/xen-emu.c | 12 ++++++++ 3 files changed, 78 insertions(+) diff --git a/hw/i386/kvm/xen_overlay.c b/hw/i386/kvm/xen_overlay.c index a2441e2b4e..8685d87959 100644 --- a/hw/i386/kvm/xen_overlay.c +++ b/hw/i386/kvm/xen_overlay.c @@ -44,6 +44,7 @@ struct XenOverlayState { MemoryRegion shinfo_mem; void *shinfo_ptr; uint64_t shinfo_gpa; + bool long_mode; }; struct XenOverlayState *xen_overlay_singleton; @@ -96,9 +97,21 @@ static void xen_overlay_realize(DeviceState *dev, Error **errp) s->shinfo_ptr = memory_region_get_ram_ptr(&s->shinfo_mem); s->shinfo_gpa = INVALID_GPA; + s->long_mode = false; memset(s->shinfo_ptr, 0, XEN_PAGE_SIZE); } +static int xen_overlay_pre_save(void *opaque) +{ + /* + * Fetch the kernel's idea of long_mode to avoid the race condition + * where the guest has set the hypercall page up in 64-bit mode but + * not yet made a hypercall by the time migration happens, so qemu + * hasn't yet noticed. + */ + return xen_sync_long_mode(); +} + static int xen_overlay_post_load(void *opaque, int version_id) { XenOverlayState *s = opaque; @@ -107,6 +120,9 @@ static int xen_overlay_post_load(void *opaque, int version_id) xen_overlay_do_map_page(&s->shinfo_mem, s->shinfo_gpa); xen_overlay_set_be_shinfo(s->shinfo_gpa >> XEN_PAGE_SHIFT); } + if (s->long_mode) { + xen_set_long_mode(true); + } return 0; } @@ -121,9 +137,11 @@ static const VMStateDescription xen_overlay_vmstate = { .version_id = 1, .minimum_version_id = 1, .needed = xen_overlay_is_needed, + .pre_save = xen_overlay_pre_save, .post_load = xen_overlay_post_load, .fields = (VMStateField[]) { VMSTATE_UINT64(shinfo_gpa, XenOverlayState), + VMSTATE_BOOL(long_mode, XenOverlayState), VMSTATE_END_OF_LIST() } }; @@ -208,3 +226,47 @@ void *xen_overlay_get_shinfo_ptr(void) return s->shinfo_ptr; } + +int xen_sync_long_mode(void) +{ + int ret; + struct kvm_xen_hvm_attr xa = { + .type = KVM_XEN_ATTR_TYPE_LONG_MODE, + }; + + if (!xen_overlay_singleton) { + return -ENOENT; + } + + ret = kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_GET_ATTR, &xa); + if (!ret) { + xen_overlay_singleton->long_mode = xa.u.long_mode; + } + + return ret; +} + +int xen_set_long_mode(bool long_mode) +{ + int ret; + struct kvm_xen_hvm_attr xa = { + .type = KVM_XEN_ATTR_TYPE_LONG_MODE, + .u.long_mode = long_mode, + }; + + if (!xen_overlay_singleton) { + return -ENOENT; + } + + ret = kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &xa); + if (!ret) { + xen_overlay_singleton->long_mode = xa.u.long_mode; + } + + return ret; +} + +bool xen_is_long_mode(void) +{ + return xen_overlay_singleton && xen_overlay_singleton->long_mode; +} diff --git a/hw/i386/kvm/xen_overlay.h b/hw/i386/kvm/xen_overlay.h index 00cff05bb0..5c46a0b036 100644 --- a/hw/i386/kvm/xen_overlay.h +++ b/hw/i386/kvm/xen_overlay.h @@ -17,4 +17,8 @@ void xen_overlay_create(void); int xen_overlay_map_shinfo_page(uint64_t gpa); void *xen_overlay_get_shinfo_ptr(void); +int xen_sync_long_mode(void); +int xen_set_long_mode(bool long_mode); +bool xen_is_long_mode(void); + #endif /* QEMU_XEN_OVERLAY_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index ebea27caf6..be6d85f2cb 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -20,6 +20,8 @@ #include "trace.h" #include "sysemu/runstate.h" +#include "hw/i386/kvm/xen_overlay.h" + #include "hw/xen/interface/version.h" #include "hw/xen/interface/sched.h" @@ -282,6 +284,16 @@ int kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) return -1; } + /* + * The kernel latches the guest 32/64 mode when the MSR is used to fill + * the hypercall page. So if we see a hypercall in a mode that doesn't + * match our own idea of the guest mode, fetch the kernel's idea of the + * "long mode" to remain in sync. + */ + if (exit->u.hcall.longmode != xen_is_long_mode()) { + xen_sync_long_mode(); + } + if (!do_kvm_xen_handle_exit(cpu, exit)) { /* * Some hypercalls will be deliberately "implemented" by returning From fb0fd2ce38400e158f3eb483b0f1c553d3551445 Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Mon, 18 Jun 2018 12:17:42 -0400 Subject: [PATCH 142/185] i386/xen: implement HYPERVISOR_memory_op Specifically XENMEM_add_to_physmap with space XENMAPSPACE_shared_info to allow the guest to set its shared_info page. Signed-off-by: Joao Martins [dwmw2: Use the xen_overlay device, add compat support] Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/kvm/trace-events | 1 + target/i386/kvm/xen-compat.h | 27 ++++++++ target/i386/kvm/xen-emu.c | 116 ++++++++++++++++++++++++++++++++++- 3 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 target/i386/kvm/xen-compat.h diff --git a/target/i386/kvm/trace-events b/target/i386/kvm/trace-events index bb732e1da8..8e9f269f56 100644 --- a/target/i386/kvm/trace-events +++ b/target/i386/kvm/trace-events @@ -9,3 +9,4 @@ kvm_x86_update_msi_routes(int num) "Updated %d MSI routes" # xen-emu.c kvm_xen_hypercall(int cpu, uint8_t cpl, uint64_t input, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t ret) "xen_hypercall: cpu %d cpl %d input %" PRIu64 " a0 0x%" PRIx64 " a1 0x%" PRIx64 " a2 0x%" PRIx64" ret 0x%" PRIx64 kvm_xen_soft_reset(void) "" +kvm_xen_set_shared_info(uint64_t gfn) "shared info at gfn 0x%" PRIx64 diff --git a/target/i386/kvm/xen-compat.h b/target/i386/kvm/xen-compat.h new file mode 100644 index 0000000000..2d852e2a28 --- /dev/null +++ b/target/i386/kvm/xen-compat.h @@ -0,0 +1,27 @@ +/* + * Xen HVM emulation support in KVM + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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_I386_KVM_XEN_COMPAT_H +#define QEMU_I386_KVM_XEN_COMPAT_H + +#include "hw/xen/interface/memory.h" + +typedef uint32_t compat_pfn_t; +typedef uint32_t compat_ulong_t; + +struct compat_xen_add_to_physmap { + domid_t domid; + uint16_t size; + unsigned int space; + compat_ulong_t idx; + compat_pfn_t gpfn; +}; + +#endif /* QEMU_I386_XEN_COMPAT_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index be6d85f2cb..5d79827128 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/main-loop.h" +#include "hw/xen/xen.h" #include "sysemu/kvm_int.h" #include "sysemu/kvm_xen.h" #include "kvm/kvm_i386.h" @@ -24,6 +25,15 @@ #include "hw/xen/interface/version.h" #include "hw/xen/interface/sched.h" +#include "hw/xen/interface/memory.h" + +#include "xen-compat.h" + +#ifdef TARGET_X86_64 +#define hypercall_compat32(longmode) (!(longmode)) +#else +#define hypercall_compat32(longmode) (false) +#endif static int kvm_gva_rw(CPUState *cs, uint64_t gva, void *_buf, size_t sz, bool is_write) @@ -175,13 +185,114 @@ static bool kvm_xen_hcall_xen_version(struct kvm_xen_exit *exit, X86CPU *cpu, return true; } +static int xen_set_shared_info(uint64_t gfn) +{ + uint64_t gpa = gfn << TARGET_PAGE_BITS; + int err; + + QEMU_IOTHREAD_LOCK_GUARD(); + + /* + * The xen_overlay device tells KVM about it too, since it had to + * do that on migration load anyway (unless we're going to jump + * through lots of hoops to maintain the fiction that this isn't + * KVM-specific. + */ + err = xen_overlay_map_shinfo_page(gpa); + if (err) { + return err; + } + + trace_kvm_xen_set_shared_info(gfn); + + return err; +} + +static int add_to_physmap_one(uint32_t space, uint64_t idx, uint64_t gfn) +{ + switch (space) { + case XENMAPSPACE_shared_info: + if (idx > 0) { + return -EINVAL; + } + return xen_set_shared_info(gfn); + + case XENMAPSPACE_grant_table: + case XENMAPSPACE_gmfn: + case XENMAPSPACE_gmfn_range: + return -ENOTSUP; + + case XENMAPSPACE_gmfn_foreign: + case XENMAPSPACE_dev_mmio: + return -EPERM; + + default: + return -EINVAL; + } +} + +static int do_add_to_physmap(struct kvm_xen_exit *exit, X86CPU *cpu, + uint64_t arg) +{ + struct xen_add_to_physmap xatp; + CPUState *cs = CPU(cpu); + + if (hypercall_compat32(exit->u.hcall.longmode)) { + struct compat_xen_add_to_physmap xatp32; + + qemu_build_assert(sizeof(struct compat_xen_add_to_physmap) == 16); + if (kvm_copy_from_gva(cs, arg, &xatp32, sizeof(xatp32))) { + return -EFAULT; + } + xatp.domid = xatp32.domid; + xatp.size = xatp32.size; + xatp.space = xatp32.space; + xatp.idx = xatp32.idx; + xatp.gpfn = xatp32.gpfn; + } else { + if (kvm_copy_from_gva(cs, arg, &xatp, sizeof(xatp))) { + return -EFAULT; + } + } + + if (xatp.domid != DOMID_SELF && xatp.domid != xen_domid) { + return -ESRCH; + } + + return add_to_physmap_one(xatp.space, xatp.idx, xatp.gpfn); +} + +static bool kvm_xen_hcall_memory_op(struct kvm_xen_exit *exit, X86CPU *cpu, + int cmd, uint64_t arg) +{ + int err; + + switch (cmd) { + case XENMEM_add_to_physmap: + err = do_add_to_physmap(exit, cpu, arg); + break; + + default: + return false; + } + + exit->u.hcall.result = err; + return true; +} + int kvm_xen_soft_reset(void) { + int err; + assert(qemu_mutex_iothread_locked()); trace_kvm_xen_soft_reset(); - /* Nothing to reset... yet. */ + err = xen_overlay_map_shinfo_page(INVALID_GFN); + if (err) { + return err; + } + return 0; } @@ -270,6 +381,9 @@ static bool do_kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) case __HYPERVISOR_sched_op: return kvm_xen_hcall_sched_op(exit, cpu, exit->u.hcall.params[0], exit->u.hcall.params[1]); + case __HYPERVISOR_memory_op: + return kvm_xen_hcall_memory_op(exit, cpu, exit->u.hcall.params[0], + exit->u.hcall.params[1]); case __HYPERVISOR_xen_version: return kvm_xen_hcall_xen_version(exit, cpu, exit->u.hcall.params[0], exit->u.hcall.params[1]); From 782a79601e9d41e840727f9f8270e416dcd47243 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 15 Dec 2022 10:39:30 +0000 Subject: [PATCH 143/185] i386/xen: implement XENMEM_add_to_physmap_batch Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/kvm/xen-compat.h | 24 +++++++++++++ target/i386/kvm/xen-emu.c | 69 ++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/target/i386/kvm/xen-compat.h b/target/i386/kvm/xen-compat.h index 2d852e2a28..448336de92 100644 --- a/target/i386/kvm/xen-compat.h +++ b/target/i386/kvm/xen-compat.h @@ -15,6 +15,20 @@ typedef uint32_t compat_pfn_t; typedef uint32_t compat_ulong_t; +typedef uint32_t compat_ptr_t; + +#define __DEFINE_COMPAT_HANDLE(name, type) \ + typedef struct { \ + compat_ptr_t c; \ + type *_[0] __attribute__((packed)); \ + } __compat_handle_ ## name; \ + +#define DEFINE_COMPAT_HANDLE(name) __DEFINE_COMPAT_HANDLE(name, name) +#define COMPAT_HANDLE(name) __compat_handle_ ## name + +DEFINE_COMPAT_HANDLE(compat_pfn_t); +DEFINE_COMPAT_HANDLE(compat_ulong_t); +DEFINE_COMPAT_HANDLE(int); struct compat_xen_add_to_physmap { domid_t domid; @@ -24,4 +38,14 @@ struct compat_xen_add_to_physmap { compat_pfn_t gpfn; }; +struct compat_xen_add_to_physmap_batch { + domid_t domid; + uint16_t space; + uint16_t size; + uint16_t extra; + COMPAT_HANDLE(compat_ulong_t) idxs; + COMPAT_HANDLE(compat_pfn_t) gpfns; + COMPAT_HANDLE(int) errs; +}; + #endif /* QEMU_I386_XEN_COMPAT_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 5d79827128..2b235e7b27 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -262,6 +262,71 @@ static int do_add_to_physmap(struct kvm_xen_exit *exit, X86CPU *cpu, return add_to_physmap_one(xatp.space, xatp.idx, xatp.gpfn); } +static int do_add_to_physmap_batch(struct kvm_xen_exit *exit, X86CPU *cpu, + uint64_t arg) +{ + struct xen_add_to_physmap_batch xatpb; + unsigned long idxs_gva, gpfns_gva, errs_gva; + CPUState *cs = CPU(cpu); + size_t op_sz; + + if (hypercall_compat32(exit->u.hcall.longmode)) { + struct compat_xen_add_to_physmap_batch xatpb32; + + qemu_build_assert(sizeof(struct compat_xen_add_to_physmap_batch) == 20); + if (kvm_copy_from_gva(cs, arg, &xatpb32, sizeof(xatpb32))) { + return -EFAULT; + } + xatpb.domid = xatpb32.domid; + xatpb.space = xatpb32.space; + xatpb.size = xatpb32.size; + + idxs_gva = xatpb32.idxs.c; + gpfns_gva = xatpb32.gpfns.c; + errs_gva = xatpb32.errs.c; + op_sz = sizeof(uint32_t); + } else { + if (kvm_copy_from_gva(cs, arg, &xatpb, sizeof(xatpb))) { + return -EFAULT; + } + op_sz = sizeof(unsigned long); + idxs_gva = (unsigned long)xatpb.idxs.p; + gpfns_gva = (unsigned long)xatpb.gpfns.p; + errs_gva = (unsigned long)xatpb.errs.p; + } + + if (xatpb.domid != DOMID_SELF && xatpb.domid != xen_domid) { + return -ESRCH; + } + + /* Explicitly invalid for the batch op. Not that we implement it anyway. */ + if (xatpb.space == XENMAPSPACE_gmfn_range) { + return -EINVAL; + } + + while (xatpb.size--) { + unsigned long idx = 0; + unsigned long gpfn = 0; + int err; + + /* For 32-bit compat this only copies the low 32 bits of each */ + if (kvm_copy_from_gva(cs, idxs_gva, &idx, op_sz) || + kvm_copy_from_gva(cs, gpfns_gva, &gpfn, op_sz)) { + return -EFAULT; + } + idxs_gva += op_sz; + gpfns_gva += op_sz; + + err = add_to_physmap_one(xatpb.space, idx, gpfn); + + if (kvm_copy_to_gva(cs, errs_gva, &err, sizeof(err))) { + return -EFAULT; + } + errs_gva += sizeof(err); + } + return 0; +} + static bool kvm_xen_hcall_memory_op(struct kvm_xen_exit *exit, X86CPU *cpu, int cmd, uint64_t arg) { @@ -272,6 +337,10 @@ static bool kvm_xen_hcall_memory_op(struct kvm_xen_exit *exit, X86CPU *cpu, err = do_add_to_physmap(exit, cpu, arg); break; + case XENMEM_add_to_physmap_batch: + err = do_add_to_physmap_batch(exit, cpu, arg); + break; + default: return false; } From 671bfdcd47739a103ce8633e48d07a74d0d3cbd9 Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Mon, 18 Jun 2018 12:21:57 -0400 Subject: [PATCH 144/185] i386/xen: implement HYPERVISOR_hvm_op This is when guest queries for support for HVMOP_pagetable_dying. Signed-off-by: Joao Martins Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/kvm/xen-emu.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 2b235e7b27..4002b1b797 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -26,6 +26,7 @@ #include "hw/xen/interface/version.h" #include "hw/xen/interface/sched.h" #include "hw/xen/interface/memory.h" +#include "hw/xen/interface/hvm/hvm_op.h" #include "xen-compat.h" @@ -349,6 +350,19 @@ static bool kvm_xen_hcall_memory_op(struct kvm_xen_exit *exit, X86CPU *cpu, return true; } +static bool kvm_xen_hcall_hvm_op(struct kvm_xen_exit *exit, X86CPU *cpu, + int cmd, uint64_t arg) +{ + switch (cmd) { + case HVMOP_pagetable_dying: + exit->u.hcall.result = -ENOSYS; + return true; + + default: + return false; + } +} + int kvm_xen_soft_reset(void) { int err; @@ -450,6 +464,9 @@ static bool do_kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) case __HYPERVISOR_sched_op: return kvm_xen_hcall_sched_op(exit, cpu, exit->u.hcall.params[0], exit->u.hcall.params[1]); + case __HYPERVISOR_hvm_op: + return kvm_xen_hcall_hvm_op(exit, cpu, exit->u.hcall.params[0], + exit->u.hcall.params[1]); case __HYPERVISOR_memory_op: return kvm_xen_hcall_memory_op(exit, cpu, exit->u.hcall.params[0], exit->u.hcall.params[1]); From d70bd6a485d54ff60a6dd708c51b5c0ba679056f Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Mon, 18 Jun 2018 12:26:44 -0400 Subject: [PATCH 145/185] i386/xen: implement HYPERVISOR_vcpu_op This is simply when guest tries to register a vcpu_info and since vcpu_info placement is optional in the minimum ABI therefore we can just fail with -ENOSYS Signed-off-by: Joao Martins Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/kvm/xen-emu.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 4002b1b797..e5ae0a9a38 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -27,6 +27,7 @@ #include "hw/xen/interface/sched.h" #include "hw/xen/interface/memory.h" #include "hw/xen/interface/hvm/hvm_op.h" +#include "hw/xen/interface/vcpu.h" #include "xen-compat.h" @@ -363,6 +364,25 @@ static bool kvm_xen_hcall_hvm_op(struct kvm_xen_exit *exit, X86CPU *cpu, } } +static bool kvm_xen_hcall_vcpu_op(struct kvm_xen_exit *exit, X86CPU *cpu, + int cmd, int vcpu_id, uint64_t arg) +{ + int err; + + switch (cmd) { + case VCPUOP_register_vcpu_info: + /* no vcpu info placement for now */ + err = -ENOSYS; + break; + + default: + return false; + } + + exit->u.hcall.result = err; + return true; +} + int kvm_xen_soft_reset(void) { int err; @@ -464,6 +484,11 @@ static bool do_kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) case __HYPERVISOR_sched_op: return kvm_xen_hcall_sched_op(exit, cpu, exit->u.hcall.params[0], exit->u.hcall.params[1]); + case __HYPERVISOR_vcpu_op: + return kvm_xen_hcall_vcpu_op(exit, cpu, + exit->u.hcall.params[0], + exit->u.hcall.params[1], + exit->u.hcall.params[2]); case __HYPERVISOR_hvm_op: return kvm_xen_hcall_hvm_op(exit, cpu, exit->u.hcall.params[0], exit->u.hcall.params[1]); From c345104cd1d17e1e801b99a216fa3654cdcbce35 Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Fri, 29 Jun 2018 10:54:50 -0400 Subject: [PATCH 146/185] i386/xen: handle VCPUOP_register_vcpu_info Handle the hypercall to set a per vcpu info, and also wire up the default vcpu_info in the shared_info page for the first 32 vCPUs. To avoid deadlock within KVM a vCPU thread must set its *own* vcpu_info rather than it being set from the context in which the hypercall is invoked. Add the vcpu_info (and default) GPA to the vmstate_x86_cpu for migration, and restore it in kvm_arch_put_registers() appropriately. Signed-off-by: Joao Martins Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/cpu.h | 2 + target/i386/kvm/kvm.c | 17 ++++ target/i386/kvm/trace-events | 1 + target/i386/kvm/xen-emu.c | 153 ++++++++++++++++++++++++++++++++++- target/i386/kvm/xen-emu.h | 2 + target/i386/machine.c | 19 +++++ 6 files changed, 191 insertions(+), 3 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 5069adfbe7..9070efdc51 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1799,6 +1799,8 @@ typedef struct CPUArchState { #endif #if defined(CONFIG_KVM) struct kvm_nested_state *nested_state; + uint64_t xen_vcpu_info_gpa; + uint64_t xen_vcpu_info_default_gpa; #endif #if defined(CONFIG_HVF) HVFX86LazyFlags hvf_lflags; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index f43e5531bf..5a144ec0de 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -4750,6 +4750,15 @@ int kvm_arch_put_registers(CPUState *cpu, int level) kvm_arch_set_tsc_khz(cpu); } +#ifdef CONFIG_XEN_EMU + if (xen_mode == XEN_EMULATE && level == KVM_PUT_FULL_STATE) { + ret = kvm_put_xen_state(cpu); + if (ret < 0) { + return ret; + } + } +#endif + ret = kvm_getput_regs(x86_cpu, 1); if (ret < 0) { return ret; @@ -4849,6 +4858,14 @@ int kvm_arch_get_registers(CPUState *cs) if (ret < 0) { goto out; } +#ifdef CONFIG_XEN_EMU + if (xen_mode == XEN_EMULATE) { + ret = kvm_get_xen_state(cs); + if (ret < 0) { + goto out; + } + } +#endif ret = 0; out: cpu_sync_bndcs_hflags(&cpu->env); diff --git a/target/i386/kvm/trace-events b/target/i386/kvm/trace-events index 8e9f269f56..a840e0333d 100644 --- a/target/i386/kvm/trace-events +++ b/target/i386/kvm/trace-events @@ -10,3 +10,4 @@ kvm_x86_update_msi_routes(int num) "Updated %d MSI routes" kvm_xen_hypercall(int cpu, uint8_t cpl, uint64_t input, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t ret) "xen_hypercall: cpu %d cpl %d input %" PRIu64 " a0 0x%" PRIx64 " a1 0x%" PRIx64 " a2 0x%" PRIx64" ret 0x%" PRIx64 kvm_xen_soft_reset(void) "" kvm_xen_set_shared_info(uint64_t gfn) "shared info at gfn 0x%" PRIx64 +kvm_xen_set_vcpu_attr(int cpu, int type, uint64_t gpa) "vcpu attr cpu %d type %d gpa 0x%" PRIx64 diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index e5ae0a9a38..30b4789da3 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -119,6 +119,8 @@ int kvm_xen_init(KVMState *s, uint32_t hypercall_msr) int kvm_xen_init_vcpu(CPUState *cs) { + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; int err; /* @@ -142,6 +144,9 @@ int kvm_xen_init_vcpu(CPUState *cs) } } + env->xen_vcpu_info_gpa = INVALID_GPA; + env->xen_vcpu_info_default_gpa = INVALID_GPA; + return 0; } @@ -187,10 +192,58 @@ static bool kvm_xen_hcall_xen_version(struct kvm_xen_exit *exit, X86CPU *cpu, return true; } +static int kvm_xen_set_vcpu_attr(CPUState *cs, uint16_t type, uint64_t gpa) +{ + struct kvm_xen_vcpu_attr xhsi; + + xhsi.type = type; + xhsi.u.gpa = gpa; + + trace_kvm_xen_set_vcpu_attr(cs->cpu_index, type, gpa); + + return kvm_vcpu_ioctl(cs, KVM_XEN_VCPU_SET_ATTR, &xhsi); +} + +static void do_set_vcpu_info_default_gpa(CPUState *cs, run_on_cpu_data data) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + env->xen_vcpu_info_default_gpa = data.host_ulong; + + /* Changing the default does nothing if a vcpu_info was explicitly set. */ + if (env->xen_vcpu_info_gpa == INVALID_GPA) { + kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, + env->xen_vcpu_info_default_gpa); + } +} + +static void do_set_vcpu_info_gpa(CPUState *cs, run_on_cpu_data data) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + env->xen_vcpu_info_gpa = data.host_ulong; + + kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, + env->xen_vcpu_info_gpa); +} + +static void do_vcpu_soft_reset(CPUState *cs, run_on_cpu_data data) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + env->xen_vcpu_info_gpa = INVALID_GPA; + env->xen_vcpu_info_default_gpa = INVALID_GPA; + + kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, INVALID_GPA); +} + static int xen_set_shared_info(uint64_t gfn) { uint64_t gpa = gfn << TARGET_PAGE_BITS; - int err; + int i, err; QEMU_IOTHREAD_LOCK_GUARD(); @@ -207,6 +260,15 @@ static int xen_set_shared_info(uint64_t gfn) trace_kvm_xen_set_shared_info(gfn); + for (i = 0; i < XEN_LEGACY_MAX_VCPUS; i++) { + CPUState *cpu = qemu_get_cpu(i); + if (cpu) { + async_run_on_cpu(cpu, do_set_vcpu_info_default_gpa, + RUN_ON_CPU_HOST_ULONG(gpa)); + } + gpa += sizeof(vcpu_info_t); + } + return err; } @@ -364,15 +426,43 @@ static bool kvm_xen_hcall_hvm_op(struct kvm_xen_exit *exit, X86CPU *cpu, } } +static int vcpuop_register_vcpu_info(CPUState *cs, CPUState *target, + uint64_t arg) +{ + struct vcpu_register_vcpu_info rvi; + uint64_t gpa; + + /* No need for 32/64 compat handling */ + qemu_build_assert(sizeof(rvi) == 16); + qemu_build_assert(sizeof(struct vcpu_info) == 64); + + if (!target) { + return -ENOENT; + } + + if (kvm_copy_from_gva(cs, arg, &rvi, sizeof(rvi))) { + return -EFAULT; + } + + if (rvi.offset > TARGET_PAGE_SIZE - sizeof(struct vcpu_info)) { + return -EINVAL; + } + + gpa = ((rvi.mfn << TARGET_PAGE_BITS) + rvi.offset); + async_run_on_cpu(target, do_set_vcpu_info_gpa, RUN_ON_CPU_HOST_ULONG(gpa)); + return 0; +} + static bool kvm_xen_hcall_vcpu_op(struct kvm_xen_exit *exit, X86CPU *cpu, int cmd, int vcpu_id, uint64_t arg) { + CPUState *dest = qemu_get_cpu(vcpu_id); + CPUState *cs = CPU(cpu); int err; switch (cmd) { case VCPUOP_register_vcpu_info: - /* no vcpu info placement for now */ - err = -ENOSYS; + err = vcpuop_register_vcpu_info(cs, dest, arg); break; default: @@ -385,12 +475,17 @@ static bool kvm_xen_hcall_vcpu_op(struct kvm_xen_exit *exit, X86CPU *cpu, int kvm_xen_soft_reset(void) { + CPUState *cpu; int err; assert(qemu_mutex_iothread_locked()); trace_kvm_xen_soft_reset(); + CPU_FOREACH(cpu) { + async_run_on_cpu(cpu, do_vcpu_soft_reset, RUN_ON_CPU_NULL); + } + err = xen_overlay_map_shinfo_page(INVALID_GFN); if (err) { return err; @@ -539,3 +634,55 @@ int kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) exit->u.hcall.result); return 0; } + +int kvm_put_xen_state(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + uint64_t gpa; + int ret; + + gpa = env->xen_vcpu_info_gpa; + if (gpa == INVALID_GPA) { + gpa = env->xen_vcpu_info_default_gpa; + } + + if (gpa != INVALID_GPA) { + ret = kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, gpa); + if (ret < 0) { + return ret; + } + } + + return 0; +} + +int kvm_get_xen_state(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + uint64_t gpa; + + /* + * The kernel does not mark vcpu_info as dirty when it delivers interrupts + * to it. It's up to userspace to *assume* that any page shared thus is + * always considered dirty. The shared_info page is different since it's + * an overlay and migrated separately anyway. + */ + gpa = env->xen_vcpu_info_gpa; + if (gpa == INVALID_GPA) { + gpa = env->xen_vcpu_info_default_gpa; + } + if (gpa != INVALID_GPA) { + MemoryRegionSection mrs = memory_region_find(get_system_memory(), + gpa, + sizeof(struct vcpu_info)); + if (mrs.mr && + !int128_lt(mrs.size, int128_make64(sizeof(struct vcpu_info)))) { + memory_region_set_dirty(mrs.mr, mrs.offset_within_region, + sizeof(struct vcpu_info)); + } + } + + return 0; +} diff --git a/target/i386/kvm/xen-emu.h b/target/i386/kvm/xen-emu.h index 21faf6bf38..452605699a 100644 --- a/target/i386/kvm/xen-emu.h +++ b/target/i386/kvm/xen-emu.h @@ -26,5 +26,7 @@ int kvm_xen_init(KVMState *s, uint32_t hypercall_msr); int kvm_xen_init_vcpu(CPUState *cs); int kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit); +int kvm_put_xen_state(CPUState *cs); +int kvm_get_xen_state(CPUState *cs); #endif /* QEMU_I386_KVM_XEN_EMU_H */ diff --git a/target/i386/machine.c b/target/i386/machine.c index 310b125235..1215e616c8 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -6,8 +6,10 @@ #include "kvm/hyperv.h" #include "hw/i386/x86.h" #include "kvm/kvm_i386.h" +#include "hw/xen/xen.h" #include "sysemu/kvm.h" +#include "sysemu/kvm_xen.h" #include "sysemu/tcg.h" #include "qemu/error-report.h" @@ -1257,6 +1259,22 @@ static const VMStateDescription vmstate_nested_state = { } }; +static bool xen_vcpu_needed(void *opaque) +{ + return (xen_mode == XEN_EMULATE); +} + +static const VMStateDescription vmstate_xen_vcpu = { + .name = "cpu/xen_vcpu", + .version_id = 1, + .minimum_version_id = 1, + .needed = xen_vcpu_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.xen_vcpu_info_gpa, X86CPU), + VMSTATE_UINT64(env.xen_vcpu_info_default_gpa, X86CPU), + VMSTATE_END_OF_LIST() + } +}; #endif static bool mcg_ext_ctl_needed(void *opaque) @@ -1716,6 +1734,7 @@ const VMStateDescription vmstate_x86_cpu = { #endif #ifdef CONFIG_KVM &vmstate_nested_state, + &vmstate_xen_vcpu, #endif &vmstate_msr_tsx_ctrl, &vmstate_msr_intel_sgx, From f068930277347667125a10e9d8612a585d84738d Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Mon, 23 Jul 2018 11:24:36 -0400 Subject: [PATCH 147/185] i386/xen: handle VCPUOP_register_vcpu_time_info In order to support Linux vdso in Xen. Signed-off-by: Joao Martins Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/cpu.h | 1 + target/i386/kvm/xen-emu.c | 102 +++++++++++++++++++++++++++++++++----- target/i386/machine.c | 1 + 3 files changed, 91 insertions(+), 13 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 9070efdc51..7c39d917e8 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1801,6 +1801,7 @@ typedef struct CPUArchState { struct kvm_nested_state *nested_state; uint64_t xen_vcpu_info_gpa; uint64_t xen_vcpu_info_default_gpa; + uint64_t xen_vcpu_time_info_gpa; #endif #if defined(CONFIG_HVF) HVFX86LazyFlags hvf_lflags; diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 30b4789da3..04832522f5 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -37,28 +37,41 @@ #define hypercall_compat32(longmode) (false) #endif -static int kvm_gva_rw(CPUState *cs, uint64_t gva, void *_buf, size_t sz, - bool is_write) +static bool kvm_gva_to_gpa(CPUState *cs, uint64_t gva, uint64_t *gpa, + size_t *len, bool is_write) { - uint8_t *buf = (uint8_t *)_buf; - int ret; - - while (sz) { struct kvm_translation tr = { .linear_address = gva, }; - size_t len = TARGET_PAGE_SIZE - (tr.linear_address & ~TARGET_PAGE_MASK); + if (len) { + *len = TARGET_PAGE_SIZE - (gva & ~TARGET_PAGE_MASK); + } + + if (kvm_vcpu_ioctl(cs, KVM_TRANSLATE, &tr) || !tr.valid || + (is_write && !tr.writeable)) { + return false; + } + *gpa = tr.physical_address; + return true; +} + +static int kvm_gva_rw(CPUState *cs, uint64_t gva, void *_buf, size_t sz, + bool is_write) +{ + uint8_t *buf = (uint8_t *)_buf; + uint64_t gpa; + size_t len; + + while (sz) { + if (!kvm_gva_to_gpa(cs, gva, &gpa, &len, is_write)) { + return -EFAULT; + } if (len > sz) { len = sz; } - ret = kvm_vcpu_ioctl(cs, KVM_TRANSLATE, &tr); - if (ret || !tr.valid || (is_write && !tr.writeable)) { - return -EFAULT; - } - - cpu_physical_memory_rw(tr.physical_address, buf, len, is_write); + cpu_physical_memory_rw(gpa, buf, len, is_write); buf += len; sz -= len; @@ -146,6 +159,7 @@ int kvm_xen_init_vcpu(CPUState *cs) env->xen_vcpu_info_gpa = INVALID_GPA; env->xen_vcpu_info_default_gpa = INVALID_GPA; + env->xen_vcpu_time_info_gpa = INVALID_GPA; return 0; } @@ -229,6 +243,17 @@ static void do_set_vcpu_info_gpa(CPUState *cs, run_on_cpu_data data) env->xen_vcpu_info_gpa); } +static void do_set_vcpu_time_info_gpa(CPUState *cs, run_on_cpu_data data) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + env->xen_vcpu_time_info_gpa = data.host_ulong; + + kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO, + env->xen_vcpu_time_info_gpa); +} + static void do_vcpu_soft_reset(CPUState *cs, run_on_cpu_data data) { X86CPU *cpu = X86_CPU(cs); @@ -236,8 +261,11 @@ static void do_vcpu_soft_reset(CPUState *cs, run_on_cpu_data data) env->xen_vcpu_info_gpa = INVALID_GPA; env->xen_vcpu_info_default_gpa = INVALID_GPA; + env->xen_vcpu_time_info_gpa = INVALID_GPA; kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, INVALID_GPA); + kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO, + INVALID_GPA); } static int xen_set_shared_info(uint64_t gfn) @@ -453,6 +481,42 @@ static int vcpuop_register_vcpu_info(CPUState *cs, CPUState *target, return 0; } +static int vcpuop_register_vcpu_time_info(CPUState *cs, CPUState *target, + uint64_t arg) +{ + struct vcpu_register_time_memory_area tma; + uint64_t gpa; + size_t len; + + /* No need for 32/64 compat handling */ + qemu_build_assert(sizeof(tma) == 8); + qemu_build_assert(sizeof(struct vcpu_time_info) == 32); + + if (!target) { + return -ENOENT; + } + + if (kvm_copy_from_gva(cs, arg, &tma, sizeof(tma))) { + return -EFAULT; + } + + /* + * Xen actually uses the GVA and does the translation through the guest + * page tables each time. But Linux/KVM uses the GPA, on the assumption + * that guests only ever use *global* addresses (kernel virtual addresses) + * for it. If Linux is changed to redo the GVA→GPA translation each time, + * it will offer a new vCPU attribute for that, and we'll use it instead. + */ + if (!kvm_gva_to_gpa(cs, tma.addr.p, &gpa, &len, false) || + len < sizeof(struct vcpu_time_info)) { + return -EFAULT; + } + + async_run_on_cpu(target, do_set_vcpu_time_info_gpa, + RUN_ON_CPU_HOST_ULONG(gpa)); + return 0; +} + static bool kvm_xen_hcall_vcpu_op(struct kvm_xen_exit *exit, X86CPU *cpu, int cmd, int vcpu_id, uint64_t arg) { @@ -461,6 +525,9 @@ static bool kvm_xen_hcall_vcpu_op(struct kvm_xen_exit *exit, X86CPU *cpu, int err; switch (cmd) { + case VCPUOP_register_vcpu_time_memory_area: + err = vcpuop_register_vcpu_time_info(cs, dest, arg); + break; case VCPUOP_register_vcpu_info: err = vcpuop_register_vcpu_info(cs, dest, arg); break; @@ -654,6 +721,15 @@ int kvm_put_xen_state(CPUState *cs) } } + gpa = env->xen_vcpu_time_info_gpa; + if (gpa != INVALID_GPA) { + ret = kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO, + gpa); + if (ret < 0) { + return ret; + } + } + return 0; } diff --git a/target/i386/machine.c b/target/i386/machine.c index 1215e616c8..eb657907ca 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -1272,6 +1272,7 @@ static const VMStateDescription vmstate_xen_vcpu = { .fields = (VMStateField[]) { VMSTATE_UINT64(env.xen_vcpu_info_gpa, X86CPU), VMSTATE_UINT64(env.xen_vcpu_info_default_gpa, X86CPU), + VMSTATE_UINT64(env.xen_vcpu_time_info_gpa, X86CPU), VMSTATE_END_OF_LIST() } }; From 5092db87e4dd97ba66b2afe3c69a82507244134d Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Tue, 24 Jul 2018 12:46:00 -0400 Subject: [PATCH 148/185] i386/xen: handle VCPUOP_register_runstate_memory_area Allow guest to setup the vcpu runstates which is used as steal clock. Signed-off-by: Joao Martins Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/cpu.h | 1 + target/i386/kvm/xen-emu.c | 57 +++++++++++++++++++++++++++++++++++++++ target/i386/machine.c | 1 + 3 files changed, 59 insertions(+) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 7c39d917e8..5613df6d75 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1802,6 +1802,7 @@ typedef struct CPUArchState { uint64_t xen_vcpu_info_gpa; uint64_t xen_vcpu_info_default_gpa; uint64_t xen_vcpu_time_info_gpa; + uint64_t xen_vcpu_runstate_gpa; #endif #if defined(CONFIG_HVF) HVFX86LazyFlags hvf_lflags; diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 04832522f5..c0a8e4c34e 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -160,6 +160,7 @@ int kvm_xen_init_vcpu(CPUState *cs) env->xen_vcpu_info_gpa = INVALID_GPA; env->xen_vcpu_info_default_gpa = INVALID_GPA; env->xen_vcpu_time_info_gpa = INVALID_GPA; + env->xen_vcpu_runstate_gpa = INVALID_GPA; return 0; } @@ -254,6 +255,17 @@ static void do_set_vcpu_time_info_gpa(CPUState *cs, run_on_cpu_data data) env->xen_vcpu_time_info_gpa); } +static void do_set_vcpu_runstate_gpa(CPUState *cs, run_on_cpu_data data) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + env->xen_vcpu_runstate_gpa = data.host_ulong; + + kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR, + env->xen_vcpu_runstate_gpa); +} + static void do_vcpu_soft_reset(CPUState *cs, run_on_cpu_data data) { X86CPU *cpu = X86_CPU(cs); @@ -262,10 +274,14 @@ static void do_vcpu_soft_reset(CPUState *cs, run_on_cpu_data data) env->xen_vcpu_info_gpa = INVALID_GPA; env->xen_vcpu_info_default_gpa = INVALID_GPA; env->xen_vcpu_time_info_gpa = INVALID_GPA; + env->xen_vcpu_runstate_gpa = INVALID_GPA; kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, INVALID_GPA); kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO, INVALID_GPA); + kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR, + INVALID_GPA); + } static int xen_set_shared_info(uint64_t gfn) @@ -517,6 +533,35 @@ static int vcpuop_register_vcpu_time_info(CPUState *cs, CPUState *target, return 0; } +static int vcpuop_register_runstate_info(CPUState *cs, CPUState *target, + uint64_t arg) +{ + struct vcpu_register_runstate_memory_area rma; + uint64_t gpa; + size_t len; + + /* No need for 32/64 compat handling */ + qemu_build_assert(sizeof(rma) == 8); + /* The runstate area actually does change size, but Linux copes. */ + + if (!target) { + return -ENOENT; + } + + if (kvm_copy_from_gva(cs, arg, &rma, sizeof(rma))) { + return -EFAULT; + } + + /* As with vcpu_time_info, Xen actually uses the GVA but KVM doesn't. */ + if (!kvm_gva_to_gpa(cs, rma.addr.p, &gpa, &len, false)) { + return -EFAULT; + } + + async_run_on_cpu(target, do_set_vcpu_runstate_gpa, + RUN_ON_CPU_HOST_ULONG(gpa)); + return 0; +} + static bool kvm_xen_hcall_vcpu_op(struct kvm_xen_exit *exit, X86CPU *cpu, int cmd, int vcpu_id, uint64_t arg) { @@ -525,6 +570,9 @@ static bool kvm_xen_hcall_vcpu_op(struct kvm_xen_exit *exit, X86CPU *cpu, int err; switch (cmd) { + case VCPUOP_register_runstate_memory_area: + err = vcpuop_register_runstate_info(cs, dest, arg); + break; case VCPUOP_register_vcpu_time_memory_area: err = vcpuop_register_vcpu_time_info(cs, dest, arg); break; @@ -730,6 +778,15 @@ int kvm_put_xen_state(CPUState *cs) } } + gpa = env->xen_vcpu_runstate_gpa; + if (gpa != INVALID_GPA) { + ret = kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR, + gpa); + if (ret < 0) { + return ret; + } + } + return 0; } diff --git a/target/i386/machine.c b/target/i386/machine.c index eb657907ca..3f3d436aaa 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -1273,6 +1273,7 @@ static const VMStateDescription vmstate_xen_vcpu = { VMSTATE_UINT64(env.xen_vcpu_info_gpa, X86CPU), VMSTATE_UINT64(env.xen_vcpu_info_default_gpa, X86CPU), VMSTATE_UINT64(env.xen_vcpu_time_info_gpa, X86CPU), + VMSTATE_UINT64(env.xen_vcpu_runstate_gpa, X86CPU), VMSTATE_END_OF_LIST() } }; From 3b06f29b2497da8362ee25b729a727cb8463fd62 Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Thu, 28 Jun 2018 12:36:19 -0400 Subject: [PATCH 149/185] i386/xen: implement HYPERVISOR_event_channel_op Signed-off-by: Joao Martins [dwmw2: Ditch event_channel_op_compat which was never available to HVM guests] Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/kvm/xen-emu.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index c0a8e4c34e..ac143c05a4 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -28,6 +28,7 @@ #include "hw/xen/interface/memory.h" #include "hw/xen/interface/hvm/hvm_op.h" #include "hw/xen/interface/vcpu.h" +#include "hw/xen/interface/event_channel.h" #include "xen-compat.h" @@ -588,6 +589,27 @@ static bool kvm_xen_hcall_vcpu_op(struct kvm_xen_exit *exit, X86CPU *cpu, return true; } +static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, + int cmd, uint64_t arg) +{ + int err = -ENOSYS; + + switch (cmd) { + case EVTCHNOP_init_control: + case EVTCHNOP_expand_array: + case EVTCHNOP_set_priority: + /* We do not support FIFO channels at this point */ + err = -ENOSYS; + break; + + default: + return false; + } + + exit->u.hcall.result = err; + return true; +} + int kvm_xen_soft_reset(void) { CPUState *cpu; @@ -694,6 +716,9 @@ static bool do_kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) case __HYPERVISOR_sched_op: return kvm_xen_hcall_sched_op(exit, cpu, exit->u.hcall.params[0], exit->u.hcall.params[1]); + case __HYPERVISOR_event_channel_op: + return kvm_xen_hcall_evtchn_op(exit, exit->u.hcall.params[0], + exit->u.hcall.params[1]); case __HYPERVISOR_vcpu_op: return kvm_xen_hcall_vcpu_op(exit, cpu, exit->u.hcall.params[0], From 105b47fdf2d0ed18d73cebb055f4cbc1c88a8b30 Mon Sep 17 00:00:00 2001 From: Ankur Arora Date: Tue, 6 Dec 2022 11:14:07 +0000 Subject: [PATCH 150/185] i386/xen: implement HVMOP_set_evtchn_upcall_vector The HVMOP_set_evtchn_upcall_vector hypercall sets the per-vCPU upcall vector, to be delivered to the local APIC just like an MSI (with an EOI). This takes precedence over the system-wide delivery method set by the HVMOP_set_param hypercall with HVM_PARAM_CALLBACK_IRQ. It's used by Windows and Xen (PV shim) guests but normally not by Linux. Signed-off-by: Ankur Arora Signed-off-by: Joao Martins [dwmw2: Rework for upstream kernel changes and split from HVMOP_set_param] Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/cpu.h | 1 + target/i386/kvm/trace-events | 1 + target/i386/kvm/xen-emu.c | 84 ++++++++++++++++++++++++++++++++++-- target/i386/machine.c | 1 + 4 files changed, 84 insertions(+), 3 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 5613df6d75..e882c4e251 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1803,6 +1803,7 @@ typedef struct CPUArchState { uint64_t xen_vcpu_info_default_gpa; uint64_t xen_vcpu_time_info_gpa; uint64_t xen_vcpu_runstate_gpa; + uint8_t xen_vcpu_callback_vector; #endif #if defined(CONFIG_HVF) HVFX86LazyFlags hvf_lflags; diff --git a/target/i386/kvm/trace-events b/target/i386/kvm/trace-events index a840e0333d..b365a8e8e2 100644 --- a/target/i386/kvm/trace-events +++ b/target/i386/kvm/trace-events @@ -11,3 +11,4 @@ kvm_xen_hypercall(int cpu, uint8_t cpl, uint64_t input, uint64_t a0, uint64_t a1 kvm_xen_soft_reset(void) "" kvm_xen_set_shared_info(uint64_t gfn) "shared info at gfn 0x%" PRIx64 kvm_xen_set_vcpu_attr(int cpu, int type, uint64_t gpa) "vcpu attr cpu %d type %d gpa 0x%" PRIx64 +kvm_xen_set_vcpu_callback(int cpu, int vector) "callback vcpu %d vector %d" diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index ac143c05a4..e9a4422d93 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -27,6 +27,7 @@ #include "hw/xen/interface/sched.h" #include "hw/xen/interface/memory.h" #include "hw/xen/interface/hvm/hvm_op.h" +#include "hw/xen/interface/hvm/params.h" #include "hw/xen/interface/vcpu.h" #include "hw/xen/interface/event_channel.h" @@ -193,7 +194,8 @@ static bool kvm_xen_hcall_xen_version(struct kvm_xen_exit *exit, X86CPU *cpu, fi.submap |= 1 << XENFEAT_writable_page_tables | 1 << XENFEAT_writable_descriptor_tables | 1 << XENFEAT_auto_translated_physmap | - 1 << XENFEAT_supervisor_mode_kernel; + 1 << XENFEAT_supervisor_mode_kernel | + 1 << XENFEAT_hvm_callback_vector; } err = kvm_copy_to_gva(CPU(cpu), arg, &fi, sizeof(fi)); @@ -220,6 +222,31 @@ static int kvm_xen_set_vcpu_attr(CPUState *cs, uint16_t type, uint64_t gpa) return kvm_vcpu_ioctl(cs, KVM_XEN_VCPU_SET_ATTR, &xhsi); } +static int kvm_xen_set_vcpu_callback_vector(CPUState *cs) +{ + uint8_t vector = X86_CPU(cs)->env.xen_vcpu_callback_vector; + struct kvm_xen_vcpu_attr xva; + + xva.type = KVM_XEN_VCPU_ATTR_TYPE_UPCALL_VECTOR; + xva.u.vector = vector; + + trace_kvm_xen_set_vcpu_callback(cs->cpu_index, vector); + + return kvm_vcpu_ioctl(cs, KVM_XEN_HVM_SET_ATTR, &xva); +} + +static void do_set_vcpu_callback_vector(CPUState *cs, run_on_cpu_data data) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + env->xen_vcpu_callback_vector = data.host_int; + + if (kvm_xen_has_cap(EVTCHN_SEND)) { + kvm_xen_set_vcpu_callback_vector(cs); + } +} + static void do_set_vcpu_info_default_gpa(CPUState *cs, run_on_cpu_data data) { X86CPU *cpu = X86_CPU(cs); @@ -276,12 +303,16 @@ static void do_vcpu_soft_reset(CPUState *cs, run_on_cpu_data data) env->xen_vcpu_info_default_gpa = INVALID_GPA; env->xen_vcpu_time_info_gpa = INVALID_GPA; env->xen_vcpu_runstate_gpa = INVALID_GPA; + env->xen_vcpu_callback_vector = 0; kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, INVALID_GPA); kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO, INVALID_GPA); kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR, INVALID_GPA); + if (kvm_xen_has_cap(EVTCHN_SEND)) { + kvm_xen_set_vcpu_callback_vector(cs); + } } @@ -458,17 +489,53 @@ static bool kvm_xen_hcall_memory_op(struct kvm_xen_exit *exit, X86CPU *cpu, return true; } +static int kvm_xen_hcall_evtchn_upcall_vector(struct kvm_xen_exit *exit, + X86CPU *cpu, uint64_t arg) +{ + struct xen_hvm_evtchn_upcall_vector up; + CPUState *target_cs; + + /* No need for 32/64 compat handling */ + qemu_build_assert(sizeof(up) == 8); + + if (kvm_copy_from_gva(CPU(cpu), arg, &up, sizeof(up))) { + return -EFAULT; + } + + if (up.vector < 0x10) { + return -EINVAL; + } + + target_cs = qemu_get_cpu(up.vcpu); + if (!target_cs) { + return -EINVAL; + } + + async_run_on_cpu(target_cs, do_set_vcpu_callback_vector, + RUN_ON_CPU_HOST_INT(up.vector)); + return 0; +} + static bool kvm_xen_hcall_hvm_op(struct kvm_xen_exit *exit, X86CPU *cpu, int cmd, uint64_t arg) { + int ret = -ENOSYS; switch (cmd) { + case HVMOP_set_evtchn_upcall_vector: + ret = kvm_xen_hcall_evtchn_upcall_vector(exit, cpu, + exit->u.hcall.params[0]); + break; + case HVMOP_pagetable_dying: - exit->u.hcall.result = -ENOSYS; - return true; + ret = -ENOSYS; + break; default: return false; } + + exit->u.hcall.result = ret; + return true; } static int vcpuop_register_vcpu_info(CPUState *cs, CPUState *target, @@ -812,6 +879,17 @@ int kvm_put_xen_state(CPUState *cs) } } + if (!kvm_xen_has_cap(EVTCHN_SEND)) { + return 0; + } + + if (env->xen_vcpu_callback_vector) { + ret = kvm_xen_set_vcpu_callback_vector(cs); + if (ret < 0) { + return ret; + } + } + return 0; } diff --git a/target/i386/machine.c b/target/i386/machine.c index 3f3d436aaa..a4874eda90 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -1274,6 +1274,7 @@ static const VMStateDescription vmstate_xen_vcpu = { VMSTATE_UINT64(env.xen_vcpu_info_default_gpa, X86CPU), VMSTATE_UINT64(env.xen_vcpu_time_info_gpa, X86CPU), VMSTATE_UINT64(env.xen_vcpu_runstate_gpa, X86CPU), + VMSTATE_UINT8(env.xen_vcpu_callback_vector, X86CPU), VMSTATE_END_OF_LIST() } }; From 5dbcd01a8dfa4601dd3ce2cab2c4aa3654f1114d Mon Sep 17 00:00:00 2001 From: Ankur Arora Date: Tue, 6 Dec 2022 11:14:07 +0000 Subject: [PATCH 151/185] i386/xen: implement HVMOP_set_param This is the hook for adding the HVM_PARAM_CALLBACK_IRQ parameter in a subsequent commit. Signed-off-by: Ankur Arora Signed-off-by: Joao Martins [dwmw2: Split out from another commit] Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/kvm/xen-emu.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index e9a4422d93..ce858ac63c 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -489,6 +489,36 @@ static bool kvm_xen_hcall_memory_op(struct kvm_xen_exit *exit, X86CPU *cpu, return true; } +static bool handle_set_param(struct kvm_xen_exit *exit, X86CPU *cpu, + uint64_t arg) +{ + CPUState *cs = CPU(cpu); + struct xen_hvm_param hp; + int err = 0; + + /* No need for 32/64 compat handling */ + qemu_build_assert(sizeof(hp) == 16); + + if (kvm_copy_from_gva(cs, arg, &hp, sizeof(hp))) { + err = -EFAULT; + goto out; + } + + if (hp.domid != DOMID_SELF && hp.domid != xen_domid) { + err = -ESRCH; + goto out; + } + + switch (hp.index) { + default: + return false; + } + +out: + exit->u.hcall.result = err; + return true; +} + static int kvm_xen_hcall_evtchn_upcall_vector(struct kvm_xen_exit *exit, X86CPU *cpu, uint64_t arg) { @@ -530,6 +560,9 @@ static bool kvm_xen_hcall_hvm_op(struct kvm_xen_exit *exit, X86CPU *cpu, ret = -ENOSYS; break; + case HVMOP_set_param: + return handle_set_param(exit, cpu, arg); + default: return false; } From 91cce756179195f0725f551a5b2922ae2abbef9f Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 16 Dec 2022 14:02:29 +0000 Subject: [PATCH 152/185] hw/xen: Add xen_evtchn device for event channel emulation Include basic support for setting HVM_PARAM_CALLBACK_IRQ to the global vector method HVM_PARAM_CALLBACK_TYPE_VECTOR, which is handled in-kernel by raising the vector whenever the vCPU's vcpu_info->evtchn_upcall_pending flag is set. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/meson.build | 5 +- hw/i386/kvm/xen_evtchn.c | 155 ++++++++++++++++++++++++++++++++++++++ hw/i386/kvm/xen_evtchn.h | 18 +++++ hw/i386/pc.c | 2 + target/i386/kvm/xen-emu.c | 15 ++++ 5 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 hw/i386/kvm/xen_evtchn.c create mode 100644 hw/i386/kvm/xen_evtchn.h diff --git a/hw/i386/kvm/meson.build b/hw/i386/kvm/meson.build index 6165cbf019..cab64df339 100644 --- a/hw/i386/kvm/meson.build +++ b/hw/i386/kvm/meson.build @@ -4,6 +4,9 @@ i386_kvm_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c')) i386_kvm_ss.add(when: 'CONFIG_I8254', if_true: files('i8254.c')) i386_kvm_ss.add(when: 'CONFIG_I8259', if_true: files('i8259.c')) i386_kvm_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic.c')) -i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files('xen_overlay.c')) +i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files( + 'xen_overlay.c', + 'xen_evtchn.c', + )) i386_ss.add_all(when: 'CONFIG_KVM', if_true: i386_kvm_ss) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c new file mode 100644 index 0000000000..9d6f4076ad --- /dev/null +++ b/hw/i386/kvm/xen_evtchn.c @@ -0,0 +1,155 @@ +/* + * QEMU Xen emulation: Event channel support + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/host-utils.h" +#include "qemu/module.h" +#include "qemu/main-loop.h" +#include "qapi/error.h" +#include "qom/object.h" +#include "exec/target_page.h" +#include "exec/address-spaces.h" +#include "migration/vmstate.h" + +#include "hw/sysbus.h" +#include "hw/xen/xen.h" +#include "xen_evtchn.h" + +#include "sysemu/kvm.h" +#include "sysemu/kvm_xen.h" +#include + +#include "hw/xen/interface/memory.h" +#include "hw/xen/interface/hvm/params.h" + +#define TYPE_XEN_EVTCHN "xen-evtchn" +OBJECT_DECLARE_SIMPLE_TYPE(XenEvtchnState, XEN_EVTCHN) + +struct XenEvtchnState { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + uint64_t callback_param; + bool evtchn_in_kernel; + + QemuMutex port_lock; +}; + +struct XenEvtchnState *xen_evtchn_singleton; + +/* Top bits of callback_param are the type (HVM_PARAM_CALLBACK_TYPE_xxx) */ +#define CALLBACK_VIA_TYPE_SHIFT 56 + +static int xen_evtchn_post_load(void *opaque, int version_id) +{ + XenEvtchnState *s = opaque; + + if (s->callback_param) { + xen_evtchn_set_callback_param(s->callback_param); + } + + return 0; +} + +static bool xen_evtchn_is_needed(void *opaque) +{ + return xen_mode == XEN_EMULATE; +} + +static const VMStateDescription xen_evtchn_vmstate = { + .name = "xen_evtchn", + .version_id = 1, + .minimum_version_id = 1, + .needed = xen_evtchn_is_needed, + .post_load = xen_evtchn_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT64(callback_param, XenEvtchnState), + VMSTATE_END_OF_LIST() + } +}; + +static void xen_evtchn_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &xen_evtchn_vmstate; +} + +static const TypeInfo xen_evtchn_info = { + .name = TYPE_XEN_EVTCHN, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XenEvtchnState), + .class_init = xen_evtchn_class_init, +}; + +void xen_evtchn_create(void) +{ + XenEvtchnState *s = XEN_EVTCHN(sysbus_create_simple(TYPE_XEN_EVTCHN, + -1, NULL)); + xen_evtchn_singleton = s; + + qemu_mutex_init(&s->port_lock); +} + +static void xen_evtchn_register_types(void) +{ + type_register_static(&xen_evtchn_info); +} + +type_init(xen_evtchn_register_types) + +int xen_evtchn_set_callback_param(uint64_t param) +{ + XenEvtchnState *s = xen_evtchn_singleton; + struct kvm_xen_hvm_attr xa = { + .type = KVM_XEN_ATTR_TYPE_UPCALL_VECTOR, + .u.vector = 0, + }; + bool in_kernel = false; + int ret; + + if (!s) { + return -ENOTSUP; + } + + qemu_mutex_lock(&s->port_lock); + + switch (param >> CALLBACK_VIA_TYPE_SHIFT) { + case HVM_PARAM_CALLBACK_TYPE_VECTOR: { + xa.u.vector = (uint8_t)param, + + ret = kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &xa); + if (!ret && kvm_xen_has_cap(EVTCHN_SEND)) { + in_kernel = true; + } + break; + } + default: + /* Xen doesn't return error even if you set something bogus */ + ret = 0; + break; + } + + if (!ret) { + /* If vector delivery was turned *off* then tell the kernel */ + if ((s->callback_param >> CALLBACK_VIA_TYPE_SHIFT) == + HVM_PARAM_CALLBACK_TYPE_VECTOR && !xa.u.vector) { + kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &xa); + } + s->callback_param = param; + s->evtchn_in_kernel = in_kernel; + } + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h new file mode 100644 index 0000000000..c9b7f9d11f --- /dev/null +++ b/hw/i386/kvm/xen_evtchn.h @@ -0,0 +1,18 @@ +/* + * QEMU Xen emulation: Event channel support + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * 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_XEN_EVTCHN_H +#define QEMU_XEN_EVTCHN_H + +void xen_evtchn_create(void); +int xen_evtchn_set_callback_param(uint64_t param); + +#endif /* QEMU_XEN_EVTCHN_H */ diff --git a/hw/i386/pc.c b/hw/i386/pc.c index a316a01b15..f38e3f1be8 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -91,6 +91,7 @@ #include "hw/virtio/virtio-pmem-pci.h" #include "hw/virtio/virtio-mem-pci.h" #include "hw/i386/kvm/xen_overlay.h" +#include "hw/i386/kvm/xen_evtchn.h" #include "hw/mem/memory-device.h" #include "sysemu/replay.h" #include "target/i386/cpu.h" @@ -1852,6 +1853,7 @@ int pc_machine_kvm_type(MachineState *machine, const char *kvm_type) #ifdef CONFIG_XEN_EMU if (xen_mode == XEN_EMULATE) { xen_overlay_create(); + xen_evtchn_create(); } #endif return 0; diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index ce858ac63c..435c51f625 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -22,6 +22,7 @@ #include "sysemu/runstate.h" #include "hw/i386/kvm/xen_overlay.h" +#include "hw/i386/kvm/xen_evtchn.h" #include "hw/xen/interface/version.h" #include "hw/xen/interface/sched.h" @@ -510,6 +511,10 @@ static bool handle_set_param(struct kvm_xen_exit *exit, X86CPU *cpu, } switch (hp.index) { + case HVM_PARAM_CALLBACK_IRQ: + err = xen_evtchn_set_callback_param(hp.value); + xen_set_long_mode(exit->u.hcall.longmode); + break; default: return false; } @@ -719,6 +724,16 @@ int kvm_xen_soft_reset(void) trace_kvm_xen_soft_reset(); + /* + * Zero is the reset/startup state for HVM_PARAM_CALLBACK_IRQ. Strictly, + * it maps to HVM_PARAM_CALLBACK_TYPE_GSI with GSI#0, but Xen refuses to + * to deliver to the timer interrupt and treats that as 'disabled'. + */ + err = xen_evtchn_set_callback_param(0); + if (err) { + return err; + } + CPU_FOREACH(cpu) { async_run_on_cpu(cpu, do_vcpu_soft_reset, RUN_ON_CPU_NULL); } From 27d4075dd88a3c558fdc2da13b95915c1b6c66c9 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 16 Dec 2022 14:32:25 +0000 Subject: [PATCH 153/185] i386/xen: Add support for Xen event channel delivery to vCPU The kvm_xen_inject_vcpu_callback_vector() function will either deliver the per-vCPU local APIC vector (as an MSI), or just kick the vCPU out of the kernel to trigger KVM's automatic delivery of the global vector. Support for asserting the GSI/PCI_INTX callbacks will come later. Also add kvm_xen_get_vcpu_info_hva() which returns the vcpu_info of a given vCPU. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- include/sysemu/kvm_xen.h | 2 + target/i386/cpu.h | 2 + target/i386/kvm/xen-emu.c | 94 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 92 insertions(+), 6 deletions(-) diff --git a/include/sysemu/kvm_xen.h b/include/sysemu/kvm_xen.h index cec21c8fab..644c7d889c 100644 --- a/include/sysemu/kvm_xen.h +++ b/include/sysemu/kvm_xen.h @@ -21,6 +21,8 @@ int kvm_xen_soft_reset(void); uint32_t kvm_xen_get_caps(void); +void *kvm_xen_get_vcpu_info_hva(uint32_t vcpu_id); +void kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type); #define kvm_xen_has_cap(cap) (!!(kvm_xen_get_caps() & \ KVM_XEN_HVM_CONFIG_ ## cap)) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index e882c4e251..1c7603221d 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1799,6 +1799,8 @@ typedef struct CPUArchState { #endif #if defined(CONFIG_KVM) struct kvm_nested_state *nested_state; + MemoryRegion *xen_vcpu_info_mr; + void *xen_vcpu_info_hva; uint64_t xen_vcpu_info_gpa; uint64_t xen_vcpu_info_default_gpa; uint64_t xen_vcpu_time_info_gpa; diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 435c51f625..1b319e8bad 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -21,6 +21,8 @@ #include "trace.h" #include "sysemu/runstate.h" +#include "hw/pci/msi.h" +#include "hw/i386/apic-msidef.h" #include "hw/i386/kvm/xen_overlay.h" #include "hw/i386/kvm/xen_evtchn.h" @@ -248,6 +250,43 @@ static void do_set_vcpu_callback_vector(CPUState *cs, run_on_cpu_data data) } } +static int set_vcpu_info(CPUState *cs, uint64_t gpa) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + MemoryRegionSection mrs = { .mr = NULL }; + void *vcpu_info_hva = NULL; + int ret; + + ret = kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, gpa); + if (ret || gpa == INVALID_GPA) { + goto out; + } + + mrs = memory_region_find(get_system_memory(), gpa, + sizeof(struct vcpu_info)); + if (mrs.mr && mrs.mr->ram_block && + !int128_lt(mrs.size, int128_make64(sizeof(struct vcpu_info)))) { + vcpu_info_hva = qemu_map_ram_ptr(mrs.mr->ram_block, + mrs.offset_within_region); + } + if (!vcpu_info_hva) { + if (mrs.mr) { + memory_region_unref(mrs.mr); + mrs.mr = NULL; + } + ret = -EINVAL; + } + + out: + if (env->xen_vcpu_info_mr) { + memory_region_unref(env->xen_vcpu_info_mr); + } + env->xen_vcpu_info_hva = vcpu_info_hva; + env->xen_vcpu_info_mr = mrs.mr; + return ret; +} + static void do_set_vcpu_info_default_gpa(CPUState *cs, run_on_cpu_data data) { X86CPU *cpu = X86_CPU(cs); @@ -257,8 +296,7 @@ static void do_set_vcpu_info_default_gpa(CPUState *cs, run_on_cpu_data data) /* Changing the default does nothing if a vcpu_info was explicitly set. */ if (env->xen_vcpu_info_gpa == INVALID_GPA) { - kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, - env->xen_vcpu_info_default_gpa); + set_vcpu_info(cs, env->xen_vcpu_info_default_gpa); } } @@ -269,8 +307,52 @@ static void do_set_vcpu_info_gpa(CPUState *cs, run_on_cpu_data data) env->xen_vcpu_info_gpa = data.host_ulong; - kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, - env->xen_vcpu_info_gpa); + set_vcpu_info(cs, env->xen_vcpu_info_gpa); +} + +void *kvm_xen_get_vcpu_info_hva(uint32_t vcpu_id) +{ + CPUState *cs = qemu_get_cpu(vcpu_id); + if (!cs) { + return NULL; + } + + return X86_CPU(cs)->env.xen_vcpu_info_hva; +} + +void kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type) +{ + CPUState *cs = qemu_get_cpu(vcpu_id); + uint8_t vector; + + if (!cs) { + return; + } + + vector = X86_CPU(cs)->env.xen_vcpu_callback_vector; + if (vector) { + /* + * The per-vCPU callback vector injected via lapic. Just + * deliver it as an MSI. + */ + MSIMessage msg = { + .address = APIC_DEFAULT_ADDRESS | X86_CPU(cs)->apic_id, + .data = vector | (1UL << MSI_DATA_LEVEL_SHIFT), + }; + kvm_irqchip_send_msi(kvm_state, msg); + return; + } + + switch (type) { + case HVM_PARAM_CALLBACK_TYPE_VECTOR: + /* + * If the evtchn_upcall_pending field in the vcpu_info is set, then + * KVM will automatically deliver the vector on entering the vCPU + * so all we have to do is kick it out. + */ + qemu_cpu_kick(cs); + break; + } } static void do_set_vcpu_time_info_gpa(CPUState *cs, run_on_cpu_data data) @@ -306,7 +388,7 @@ static void do_vcpu_soft_reset(CPUState *cs, run_on_cpu_data data) env->xen_vcpu_runstate_gpa = INVALID_GPA; env->xen_vcpu_callback_vector = 0; - kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, INVALID_GPA); + set_vcpu_info(cs, INVALID_GPA); kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO, INVALID_GPA); kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR, @@ -903,7 +985,7 @@ int kvm_put_xen_state(CPUState *cs) } if (gpa != INVALID_GPA) { - ret = kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, gpa); + ret = set_vcpu_info(cs, gpa); if (ret < 0) { return ret; } From 4858ba2065c8bddf7f1c056eda3a5357588bdd50 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 13 Dec 2022 13:29:46 +0000 Subject: [PATCH 154/185] hw/xen: Implement EVTCHNOP_status This adds the basic structure for maintaining the port table and reporting the status of ports therein. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 104 ++++++++++++++++++++++++++++++++++++++ hw/i386/kvm/xen_evtchn.h | 3 ++ target/i386/kvm/xen-emu.c | 20 +++++++- 3 files changed, 125 insertions(+), 2 deletions(-) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index 9d6f4076ad..8bed33890f 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -22,6 +22,7 @@ #include "hw/sysbus.h" #include "hw/xen/xen.h" #include "xen_evtchn.h" +#include "xen_overlay.h" #include "sysemu/kvm.h" #include "sysemu/kvm_xen.h" @@ -33,6 +34,22 @@ #define TYPE_XEN_EVTCHN "xen-evtchn" OBJECT_DECLARE_SIMPLE_TYPE(XenEvtchnState, XEN_EVTCHN) +typedef struct XenEvtchnPort { + uint32_t vcpu; /* Xen/ACPI vcpu_id */ + uint16_t type; /* EVTCHNSTAT_xxxx */ + uint16_t type_val; /* pirq# / virq# / remote port according to type */ +} XenEvtchnPort; + +#define COMPAT_EVTCHN_2L_NR_CHANNELS 1024 + +/* + * For unbound/interdomain ports there are only two possible remote + * domains; self and QEMU. Use a single high bit in type_val for that, + * and the low bits for the remote port number (or 0 for unbound). + */ +#define PORT_INFO_TYPEVAL_REMOTE_QEMU 0x8000 +#define PORT_INFO_TYPEVAL_REMOTE_PORT_MASK 0x7FFF + struct XenEvtchnState { /*< private >*/ SysBusDevice busdev; @@ -42,6 +59,8 @@ struct XenEvtchnState { bool evtchn_in_kernel; QemuMutex port_lock; + uint32_t nr_ports; + XenEvtchnPort port_table[EVTCHN_2L_NR_CHANNELS]; }; struct XenEvtchnState *xen_evtchn_singleton; @@ -65,6 +84,18 @@ static bool xen_evtchn_is_needed(void *opaque) return xen_mode == XEN_EMULATE; } +static const VMStateDescription xen_evtchn_port_vmstate = { + .name = "xen_evtchn_port", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(vcpu, XenEvtchnPort), + VMSTATE_UINT16(type, XenEvtchnPort), + VMSTATE_UINT16(type_val, XenEvtchnPort), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription xen_evtchn_vmstate = { .name = "xen_evtchn", .version_id = 1, @@ -73,6 +104,9 @@ static const VMStateDescription xen_evtchn_vmstate = { .post_load = xen_evtchn_post_load, .fields = (VMStateField[]) { VMSTATE_UINT64(callback_param, XenEvtchnState), + VMSTATE_UINT32(nr_ports, XenEvtchnState), + VMSTATE_STRUCT_VARRAY_UINT32(port_table, XenEvtchnState, nr_ports, 1, + xen_evtchn_port_vmstate, XenEvtchnPort), VMSTATE_END_OF_LIST() } }; @@ -153,3 +187,73 @@ int xen_evtchn_set_callback_param(uint64_t param) return ret; } + +static bool valid_port(evtchn_port_t port) +{ + if (!port) { + return false; + } + + if (xen_is_long_mode()) { + return port < EVTCHN_2L_NR_CHANNELS; + } else { + return port < COMPAT_EVTCHN_2L_NR_CHANNELS; + } +} + +int xen_evtchn_status_op(struct evtchn_status *status) +{ + XenEvtchnState *s = xen_evtchn_singleton; + XenEvtchnPort *p; + + if (!s) { + return -ENOTSUP; + } + + if (status->dom != DOMID_SELF && status->dom != xen_domid) { + return -ESRCH; + } + + if (!valid_port(status->port)) { + return -EINVAL; + } + + qemu_mutex_lock(&s->port_lock); + + p = &s->port_table[status->port]; + + status->status = p->type; + status->vcpu = p->vcpu; + + switch (p->type) { + case EVTCHNSTAT_unbound: + if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) { + status->u.unbound.dom = DOMID_QEMU; + } else { + status->u.unbound.dom = xen_domid; + } + break; + + case EVTCHNSTAT_interdomain: + if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) { + status->u.interdomain.dom = DOMID_QEMU; + } else { + status->u.interdomain.dom = xen_domid; + } + + status->u.interdomain.port = p->type_val & + PORT_INFO_TYPEVAL_REMOTE_PORT_MASK; + break; + + case EVTCHNSTAT_pirq: + status->u.pirq = p->type_val; + break; + + case EVTCHNSTAT_virq: + status->u.virq = p->type_val; + break; + } + + qemu_mutex_unlock(&s->port_lock); + return 0; +} diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index c9b7f9d11f..76467636ee 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -15,4 +15,7 @@ void xen_evtchn_create(void); int xen_evtchn_set_callback_param(uint64_t param); +struct evtchn_status; +int xen_evtchn_status_op(struct evtchn_status *status); + #endif /* QEMU_XEN_EVTCHN_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 1b319e8bad..45fac5ea03 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -776,9 +776,10 @@ static bool kvm_xen_hcall_vcpu_op(struct kvm_xen_exit *exit, X86CPU *cpu, return true; } -static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, +static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, X86CPU *cpu, int cmd, uint64_t arg) { + CPUState *cs = CPU(cpu); int err = -ENOSYS; switch (cmd) { @@ -789,6 +790,21 @@ static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, err = -ENOSYS; break; + case EVTCHNOP_status: { + struct evtchn_status status; + + qemu_build_assert(sizeof(status) == 24); + if (kvm_copy_from_gva(cs, arg, &status, sizeof(status))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_status_op(&status); + if (!err && kvm_copy_to_gva(cs, arg, &status, sizeof(status))) { + err = -EFAULT; + } + break; + } default: return false; } @@ -914,7 +930,7 @@ static bool do_kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) return kvm_xen_hcall_sched_op(exit, cpu, exit->u.hcall.params[0], exit->u.hcall.params[1]); case __HYPERVISOR_event_channel_op: - return kvm_xen_hcall_evtchn_op(exit, exit->u.hcall.params[0], + return kvm_xen_hcall_evtchn_op(exit, cpu, exit->u.hcall.params[0], exit->u.hcall.params[1]); case __HYPERVISOR_vcpu_op: return kvm_xen_hcall_vcpu_op(exit, cpu, From 83eb5811342b4e48544ab79dd92a0bb1a2a2cbfb Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 13 Dec 2022 13:57:44 +0000 Subject: [PATCH 155/185] hw/xen: Implement EVTCHNOP_close It calls an internal close_port() helper which will also be used from EVTCHNOP_reset and will actually do the work to disconnect/unbind a port once any of that is actually implemented in the first place. That in turn calls a free_port() internal function which will be in error paths after allocation. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 123 ++++++++++++++++++++++++++++++++++++++ hw/i386/kvm/xen_evtchn.h | 2 + target/i386/kvm/xen-emu.c | 12 ++++ 3 files changed, 137 insertions(+) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index 8bed33890f..c57d36b492 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -21,6 +21,7 @@ #include "hw/sysbus.h" #include "hw/xen/xen.h" + #include "xen_evtchn.h" #include "xen_overlay.h" @@ -40,6 +41,41 @@ typedef struct XenEvtchnPort { uint16_t type_val; /* pirq# / virq# / remote port according to type */ } XenEvtchnPort; +/* 32-bit compatibility definitions, also used natively in 32-bit build */ +struct compat_arch_vcpu_info { + unsigned int cr2; + unsigned int pad[5]; +}; + +struct compat_vcpu_info { + uint8_t evtchn_upcall_pending; + uint8_t evtchn_upcall_mask; + uint16_t pad; + uint32_t evtchn_pending_sel; + struct compat_arch_vcpu_info arch; + struct vcpu_time_info time; +}; /* 64 bytes (x86) */ + +struct compat_arch_shared_info { + unsigned int max_pfn; + unsigned int pfn_to_mfn_frame_list_list; + unsigned int nmi_reason; + unsigned int p2m_cr3; + unsigned int p2m_vaddr; + unsigned int p2m_generation; + uint32_t wc_sec_hi; +}; + +struct compat_shared_info { + struct compat_vcpu_info vcpu_info[XEN_LEGACY_MAX_VCPUS]; + uint32_t evtchn_pending[32]; + uint32_t evtchn_mask[32]; + uint32_t wc_version; /* Version counter: see vcpu_time_info_t. */ + uint32_t wc_sec; + uint32_t wc_nsec; + struct compat_arch_shared_info arch; +}; + #define COMPAT_EVTCHN_2L_NR_CHANNELS 1024 /* @@ -257,3 +293,90 @@ int xen_evtchn_status_op(struct evtchn_status *status) qemu_mutex_unlock(&s->port_lock); return 0; } + +static int clear_port_pending(XenEvtchnState *s, evtchn_port_t port) +{ + void *p = xen_overlay_get_shinfo_ptr(); + + if (!p) { + return -ENOTSUP; + } + + if (xen_is_long_mode()) { + struct shared_info *shinfo = p; + const int bits_per_word = BITS_PER_BYTE * sizeof(shinfo->evtchn_pending[0]); + typeof(shinfo->evtchn_pending[0]) mask; + int idx = port / bits_per_word; + int offset = port % bits_per_word; + + mask = 1UL << offset; + + qatomic_fetch_and(&shinfo->evtchn_pending[idx], ~mask); + } else { + struct compat_shared_info *shinfo = p; + const int bits_per_word = BITS_PER_BYTE * sizeof(shinfo->evtchn_pending[0]); + typeof(shinfo->evtchn_pending[0]) mask; + int idx = port / bits_per_word; + int offset = port % bits_per_word; + + mask = 1UL << offset; + + qatomic_fetch_and(&shinfo->evtchn_pending[idx], ~mask); + } + return 0; +} + +static void free_port(XenEvtchnState *s, evtchn_port_t port) +{ + s->port_table[port].type = EVTCHNSTAT_closed; + s->port_table[port].type_val = 0; + s->port_table[port].vcpu = 0; + + if (s->nr_ports == port + 1) { + do { + s->nr_ports--; + } while (s->nr_ports && + s->port_table[s->nr_ports - 1].type == EVTCHNSTAT_closed); + } + + /* Clear pending event to avoid unexpected behavior on re-bind. */ + clear_port_pending(s, port); +} + +static int close_port(XenEvtchnState *s, evtchn_port_t port) +{ + XenEvtchnPort *p = &s->port_table[port]; + + switch (p->type) { + case EVTCHNSTAT_closed: + return -ENOENT; + + default: + break; + } + + free_port(s, port); + return 0; +} + +int xen_evtchn_close_op(struct evtchn_close *close) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (!valid_port(close->port)) { + return -EINVAL; + } + + qemu_mutex_lock(&s->port_lock); + + ret = close_port(s, close->port); + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index 76467636ee..cb3924941a 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -16,6 +16,8 @@ void xen_evtchn_create(void); int xen_evtchn_set_callback_param(uint64_t param); struct evtchn_status; +struct evtchn_close; int xen_evtchn_status_op(struct evtchn_status *status); +int xen_evtchn_close_op(struct evtchn_close *close); #endif /* QEMU_XEN_EVTCHN_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 45fac5ea03..fab7d6b6a9 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -805,6 +805,18 @@ static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, X86CPU *cpu, } break; } + case EVTCHNOP_close: { + struct evtchn_close close; + + qemu_build_assert(sizeof(close) == 4); + if (kvm_copy_from_gva(cs, arg, &close, sizeof(close))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_close_op(&close); + break; + } default: return false; } From 190cc3c0edd60876150959ee9f242363e40d168e Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 13 Dec 2022 17:20:46 +0000 Subject: [PATCH 156/185] hw/xen: Implement EVTCHNOP_unmask This finally comes with a mechanism for actually injecting events into the guest vCPU, with all the atomic-test-and-set that's involved in setting the bit in the shinfo, then the index in the vcpu_info, and injecting either the lapic vector as MSI, or letting KVM inject the bare vector. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 175 ++++++++++++++++++++++++++++++++++++++ hw/i386/kvm/xen_evtchn.h | 2 + target/i386/kvm/xen-emu.c | 12 +++ 3 files changed, 189 insertions(+) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index c57d36b492..54707f1f9f 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -224,6 +224,13 @@ int xen_evtchn_set_callback_param(uint64_t param) return ret; } +static void inject_callback(XenEvtchnState *s, uint32_t vcpu) +{ + int type = s->callback_param >> CALLBACK_VIA_TYPE_SHIFT; + + kvm_xen_inject_vcpu_callback_vector(vcpu, type); +} + static bool valid_port(evtchn_port_t port) { if (!port) { @@ -294,6 +301,152 @@ int xen_evtchn_status_op(struct evtchn_status *status) return 0; } +/* + * Never thought I'd hear myself say this, but C++ templates would be + * kind of nice here. + * + * template static int do_unmask_port(T *shinfo, ...); + */ +static int do_unmask_port_lm(XenEvtchnState *s, evtchn_port_t port, + bool do_unmask, struct shared_info *shinfo, + struct vcpu_info *vcpu_info) +{ + const int bits_per_word = BITS_PER_BYTE * sizeof(shinfo->evtchn_pending[0]); + typeof(shinfo->evtchn_pending[0]) mask; + int idx = port / bits_per_word; + int offset = port % bits_per_word; + + mask = 1UL << offset; + + if (idx >= bits_per_word) { + return -EINVAL; + } + + if (do_unmask) { + /* + * If this is a true unmask operation, clear the mask bit. If + * it was already unmasked, we have nothing further to do. + */ + if (!((qatomic_fetch_and(&shinfo->evtchn_mask[idx], ~mask) & mask))) { + return 0; + } + } else { + /* + * This is a pseudo-unmask for affinity changes. We don't + * change the mask bit, and if it's *masked* we have nothing + * else to do. + */ + if (qatomic_fetch_or(&shinfo->evtchn_mask[idx], 0) & mask) { + return 0; + } + } + + /* If the event was not pending, we're done. */ + if (!(qatomic_fetch_or(&shinfo->evtchn_pending[idx], 0) & mask)) { + return 0; + } + + /* Now on to the vcpu_info evtchn_pending_sel index... */ + mask = 1UL << idx; + + /* If a port in this word was already pending for this vCPU, all done. */ + if (qatomic_fetch_or(&vcpu_info->evtchn_pending_sel, mask) & mask) { + return 0; + } + + /* Set evtchn_upcall_pending for this vCPU */ + if (qatomic_fetch_or(&vcpu_info->evtchn_upcall_pending, 1)) { + return 0; + } + + inject_callback(s, s->port_table[port].vcpu); + + return 0; +} + +static int do_unmask_port_compat(XenEvtchnState *s, evtchn_port_t port, + bool do_unmask, + struct compat_shared_info *shinfo, + struct compat_vcpu_info *vcpu_info) +{ + const int bits_per_word = BITS_PER_BYTE * sizeof(shinfo->evtchn_pending[0]); + typeof(shinfo->evtchn_pending[0]) mask; + int idx = port / bits_per_word; + int offset = port % bits_per_word; + + mask = 1UL << offset; + + if (idx >= bits_per_word) { + return -EINVAL; + } + + if (do_unmask) { + /* + * If this is a true unmask operation, clear the mask bit. If + * it was already unmasked, we have nothing further to do. + */ + if (!((qatomic_fetch_and(&shinfo->evtchn_mask[idx], ~mask) & mask))) { + return 0; + } + } else { + /* + * This is a pseudo-unmask for affinity changes. We don't + * change the mask bit, and if it's *masked* we have nothing + * else to do. + */ + if (qatomic_fetch_or(&shinfo->evtchn_mask[idx], 0) & mask) { + return 0; + } + } + + /* If the event was not pending, we're done. */ + if (!(qatomic_fetch_or(&shinfo->evtchn_pending[idx], 0) & mask)) { + return 0; + } + + /* Now on to the vcpu_info evtchn_pending_sel index... */ + mask = 1UL << idx; + + /* If a port in this word was already pending for this vCPU, all done. */ + if (qatomic_fetch_or(&vcpu_info->evtchn_pending_sel, mask) & mask) { + return 0; + } + + /* Set evtchn_upcall_pending for this vCPU */ + if (qatomic_fetch_or(&vcpu_info->evtchn_upcall_pending, 1)) { + return 0; + } + + inject_callback(s, s->port_table[port].vcpu); + + return 0; +} + +static int unmask_port(XenEvtchnState *s, evtchn_port_t port, bool do_unmask) +{ + void *vcpu_info, *shinfo; + + if (s->port_table[port].type == EVTCHNSTAT_closed) { + return -EINVAL; + } + + shinfo = xen_overlay_get_shinfo_ptr(); + if (!shinfo) { + return -ENOTSUP; + } + + vcpu_info = kvm_xen_get_vcpu_info_hva(s->port_table[port].vcpu); + if (!vcpu_info) { + return -EINVAL; + } + + if (xen_is_long_mode()) { + return do_unmask_port_lm(s, port, do_unmask, shinfo, vcpu_info); + } else { + return do_unmask_port_compat(s, port, do_unmask, shinfo, vcpu_info); + } +} + static int clear_port_pending(XenEvtchnState *s, evtchn_port_t port) { void *p = xen_overlay_get_shinfo_ptr(); @@ -380,3 +533,25 @@ int xen_evtchn_close_op(struct evtchn_close *close) return ret; } + +int xen_evtchn_unmask_op(struct evtchn_unmask *unmask) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (!valid_port(unmask->port)) { + return -EINVAL; + } + + qemu_mutex_lock(&s->port_lock); + + ret = unmask_port(s, unmask->port, true); + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index cb3924941a..69c6b0d743 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -17,7 +17,9 @@ int xen_evtchn_set_callback_param(uint64_t param); struct evtchn_status; struct evtchn_close; +struct evtchn_unmask; int xen_evtchn_status_op(struct evtchn_status *status); int xen_evtchn_close_op(struct evtchn_close *close); +int xen_evtchn_unmask_op(struct evtchn_unmask *unmask); #endif /* QEMU_XEN_EVTCHN_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index fab7d6b6a9..884e1c9c11 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -817,6 +817,18 @@ static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, X86CPU *cpu, err = xen_evtchn_close_op(&close); break; } + case EVTCHNOP_unmask: { + struct evtchn_unmask unmask; + + qemu_build_assert(sizeof(unmask) == 4); + if (kvm_copy_from_gva(cs, arg, &unmask, sizeof(unmask))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_unmask_op(&unmask); + break; + } default: return false; } From c723d4c15e224e9d058384eecd30559580836c7a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 13 Dec 2022 22:40:56 +0000 Subject: [PATCH 157/185] hw/xen: Implement EVTCHNOP_bind_virq Add the array of virq ports to each vCPU so that we can deliver timers, debug ports, etc. Global virqs are allocated against vCPU 0 initially, but can be migrated to other vCPUs (when we implement that). The kernel needs to know about VIRQ_TIMER in order to accelerate timers, so tell it via KVM_XEN_VCPU_ATTR_TYPE_TIMER. Also save/restore the value of the singleshot timer across migration, as the kernel will handle the hypercalls automatically now. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 85 ++++++++++++++++++++++++++++++++++++ hw/i386/kvm/xen_evtchn.h | 2 + include/sysemu/kvm_xen.h | 1 + target/i386/cpu.h | 4 ++ target/i386/kvm/xen-emu.c | 91 +++++++++++++++++++++++++++++++++++++++ target/i386/machine.c | 2 + 6 files changed, 185 insertions(+) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index 54707f1f9f..a3202d39ab 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -244,6 +244,11 @@ static bool valid_port(evtchn_port_t port) } } +static bool valid_vcpu(uint32_t vcpu) +{ + return !!qemu_get_cpu(vcpu); +} + int xen_evtchn_status_op(struct evtchn_status *status) { XenEvtchnState *s = xen_evtchn_singleton; @@ -496,6 +501,43 @@ static void free_port(XenEvtchnState *s, evtchn_port_t port) clear_port_pending(s, port); } +static int allocate_port(XenEvtchnState *s, uint32_t vcpu, uint16_t type, + uint16_t val, evtchn_port_t *port) +{ + evtchn_port_t p = 1; + + for (p = 1; valid_port(p); p++) { + if (s->port_table[p].type == EVTCHNSTAT_closed) { + s->port_table[p].vcpu = vcpu; + s->port_table[p].type = type; + s->port_table[p].type_val = val; + + *port = p; + + if (s->nr_ports < p + 1) { + s->nr_ports = p + 1; + } + + return 0; + } + } + return -ENOSPC; +} + +static bool virq_is_global(uint32_t virq) +{ + switch (virq) { + case VIRQ_TIMER: + case VIRQ_DEBUG: + case VIRQ_XENOPROF: + case VIRQ_XENPMU: + return false; + + default: + return true; + } +} + static int close_port(XenEvtchnState *s, evtchn_port_t port) { XenEvtchnPort *p = &s->port_table[port]; @@ -504,6 +546,11 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port) case EVTCHNSTAT_closed: return -ENOENT; + case EVTCHNSTAT_virq: + kvm_xen_set_vcpu_virq(virq_is_global(p->type_val) ? 0 : p->vcpu, + p->type_val, 0); + break; + default: break; } @@ -555,3 +602,41 @@ int xen_evtchn_unmask_op(struct evtchn_unmask *unmask) return ret; } + +int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (virq->virq >= NR_VIRQS) { + return -EINVAL; + } + + /* Global VIRQ must be allocated on vCPU0 first */ + if (virq_is_global(virq->virq) && virq->vcpu != 0) { + return -EINVAL; + } + + if (!valid_vcpu(virq->vcpu)) { + return -ENOENT; + } + + qemu_mutex_lock(&s->port_lock); + + ret = allocate_port(s, virq->vcpu, EVTCHNSTAT_virq, virq->virq, + &virq->port); + if (!ret) { + ret = kvm_xen_set_vcpu_virq(virq->vcpu, virq->virq, virq->port); + if (ret) { + free_port(s, virq->port); + } + } + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index 69c6b0d743..0ea13dda3a 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -18,8 +18,10 @@ int xen_evtchn_set_callback_param(uint64_t param); struct evtchn_status; struct evtchn_close; struct evtchn_unmask; +struct evtchn_bind_virq; int xen_evtchn_status_op(struct evtchn_status *status); int xen_evtchn_close_op(struct evtchn_close *close); int xen_evtchn_unmask_op(struct evtchn_unmask *unmask); +int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq); #endif /* QEMU_XEN_EVTCHN_H */ diff --git a/include/sysemu/kvm_xen.h b/include/sysemu/kvm_xen.h index 644c7d889c..fbb7414eb7 100644 --- a/include/sysemu/kvm_xen.h +++ b/include/sysemu/kvm_xen.h @@ -23,6 +23,7 @@ int kvm_xen_soft_reset(void); uint32_t kvm_xen_get_caps(void); void *kvm_xen_get_vcpu_info_hva(uint32_t vcpu_id); void kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type); +int kvm_xen_set_vcpu_virq(uint32_t vcpu_id, uint16_t virq, uint16_t port); #define kvm_xen_has_cap(cap) (!!(kvm_xen_get_caps() & \ KVM_XEN_HVM_CONFIG_ ## cap)) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 1c7603221d..4b70257db5 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -27,6 +27,8 @@ #include "qapi/qapi-types-common.h" #include "qemu/cpu-float.h" +#define XEN_NR_VIRQS 24 + /* The x86 has a strong memory model with some store-after-load re-ordering */ #define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) @@ -1806,6 +1808,8 @@ typedef struct CPUArchState { uint64_t xen_vcpu_time_info_gpa; uint64_t xen_vcpu_runstate_gpa; uint8_t xen_vcpu_callback_vector; + uint16_t xen_virq[XEN_NR_VIRQS]; + uint64_t xen_singleshot_timer_ns; #endif #if defined(CONFIG_HVF) HVFX86LazyFlags hvf_lflags; diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 884e1c9c11..e0417f3d13 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -355,6 +355,53 @@ void kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type) } } +static int kvm_xen_set_vcpu_timer(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + struct kvm_xen_vcpu_attr va = { + .type = KVM_XEN_VCPU_ATTR_TYPE_TIMER, + .u.timer.port = env->xen_virq[VIRQ_TIMER], + .u.timer.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL, + .u.timer.expires_ns = env->xen_singleshot_timer_ns, + }; + + return kvm_vcpu_ioctl(cs, KVM_XEN_VCPU_SET_ATTR, &va); +} + +static void do_set_vcpu_timer_virq(CPUState *cs, run_on_cpu_data data) +{ + kvm_xen_set_vcpu_timer(cs); +} + +int kvm_xen_set_vcpu_virq(uint32_t vcpu_id, uint16_t virq, uint16_t port) +{ + CPUState *cs = qemu_get_cpu(vcpu_id); + + if (!cs) { + return -ENOENT; + } + + /* cpu.h doesn't include the actual Xen header. */ + qemu_build_assert(NR_VIRQS == XEN_NR_VIRQS); + + if (virq >= NR_VIRQS) { + return -EINVAL; + } + + if (port && X86_CPU(cs)->env.xen_virq[virq]) { + return -EEXIST; + } + + X86_CPU(cs)->env.xen_virq[virq] = port; + if (virq == VIRQ_TIMER && kvm_xen_has_cap(EVTCHN_SEND)) { + async_run_on_cpu(cs, do_set_vcpu_timer_virq, + RUN_ON_CPU_HOST_INT(port)); + } + return 0; +} + static void do_set_vcpu_time_info_gpa(CPUState *cs, run_on_cpu_data data) { X86CPU *cpu = X86_CPU(cs); @@ -387,6 +434,8 @@ static void do_vcpu_soft_reset(CPUState *cs, run_on_cpu_data data) env->xen_vcpu_time_info_gpa = INVALID_GPA; env->xen_vcpu_runstate_gpa = INVALID_GPA; env->xen_vcpu_callback_vector = 0; + env->xen_singleshot_timer_ns = 0; + memset(env->xen_virq, 0, sizeof(env->xen_virq)); set_vcpu_info(cs, INVALID_GPA); kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO, @@ -395,6 +444,7 @@ static void do_vcpu_soft_reset(CPUState *cs, run_on_cpu_data data) INVALID_GPA); if (kvm_xen_has_cap(EVTCHN_SEND)) { kvm_xen_set_vcpu_callback_vector(cs); + kvm_xen_set_vcpu_timer(cs); } } @@ -829,6 +879,21 @@ static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, X86CPU *cpu, err = xen_evtchn_unmask_op(&unmask); break; } + case EVTCHNOP_bind_virq: { + struct evtchn_bind_virq virq; + + qemu_build_assert(sizeof(virq) == 12); + if (kvm_copy_from_gva(cs, arg, &virq, sizeof(virq))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_bind_virq_op(&virq); + if (!err && kvm_copy_to_gva(cs, arg, &virq, sizeof(virq))) { + err = -EFAULT; + } + break; + } default: return false; } @@ -1060,6 +1125,12 @@ int kvm_put_xen_state(CPUState *cs) } } + if (env->xen_virq[VIRQ_TIMER]) { + ret = kvm_xen_set_vcpu_timer(cs); + if (ret < 0) { + return ret; + } + } return 0; } @@ -1068,6 +1139,7 @@ int kvm_get_xen_state(CPUState *cs) X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; uint64_t gpa; + int ret; /* * The kernel does not mark vcpu_info as dirty when it delivers interrupts @@ -1090,5 +1162,24 @@ int kvm_get_xen_state(CPUState *cs) } } + if (!kvm_xen_has_cap(EVTCHN_SEND)) { + return 0; + } + + /* + * If the kernel is accelerating timers, read out the current value of the + * singleshot timer deadline. + */ + if (env->xen_virq[VIRQ_TIMER]) { + struct kvm_xen_vcpu_attr va = { + .type = KVM_XEN_VCPU_ATTR_TYPE_TIMER, + }; + ret = kvm_vcpu_ioctl(cs, KVM_XEN_VCPU_GET_ATTR, &va); + if (ret < 0) { + return ret; + } + env->xen_singleshot_timer_ns = va.u.timer.expires_ns; + } + return 0; } diff --git a/target/i386/machine.c b/target/i386/machine.c index a4874eda90..603a1077e3 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -1275,6 +1275,8 @@ static const VMStateDescription vmstate_xen_vcpu = { VMSTATE_UINT64(env.xen_vcpu_time_info_gpa, X86CPU), VMSTATE_UINT64(env.xen_vcpu_runstate_gpa, X86CPU), VMSTATE_UINT8(env.xen_vcpu_callback_vector, X86CPU), + VMSTATE_UINT16_ARRAY(env.xen_virq, X86CPU, XEN_NR_VIRQS), + VMSTATE_UINT64(env.xen_singleshot_timer_ns, X86CPU), VMSTATE_END_OF_LIST() } }; From f5417856d25f1cf4932ac77a687979af8427f313 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 13 Dec 2022 23:12:59 +0000 Subject: [PATCH 158/185] hw/xen: Implement EVTCHNOP_bind_ipi Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 69 +++++++++++++++++++++++++++++++++++++++ hw/i386/kvm/xen_evtchn.h | 2 ++ target/i386/kvm/xen-emu.c | 15 +++++++++ 3 files changed, 86 insertions(+) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index a3202d39ab..eea80dc0f5 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -13,6 +13,7 @@ #include "qemu/host-utils.h" #include "qemu/module.h" #include "qemu/main-loop.h" +#include "qemu/log.h" #include "qapi/error.h" #include "qom/object.h" #include "exec/target_page.h" @@ -231,6 +232,43 @@ static void inject_callback(XenEvtchnState *s, uint32_t vcpu) kvm_xen_inject_vcpu_callback_vector(vcpu, type); } +static void deassign_kernel_port(evtchn_port_t port) +{ + struct kvm_xen_hvm_attr ha; + int ret; + + ha.type = KVM_XEN_ATTR_TYPE_EVTCHN; + ha.u.evtchn.send_port = port; + ha.u.evtchn.flags = KVM_XEN_EVTCHN_DEASSIGN; + + ret = kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &ha); + if (ret) { + qemu_log_mask(LOG_GUEST_ERROR, "Failed to unbind kernel port %d: %s\n", + port, strerror(ret)); + } +} + +static int assign_kernel_port(uint16_t type, evtchn_port_t port, + uint32_t vcpu_id) +{ + CPUState *cpu = qemu_get_cpu(vcpu_id); + struct kvm_xen_hvm_attr ha; + + if (!cpu) { + return -ENOENT; + } + + ha.type = KVM_XEN_ATTR_TYPE_EVTCHN; + ha.u.evtchn.send_port = port; + ha.u.evtchn.type = type; + ha.u.evtchn.flags = 0; + ha.u.evtchn.deliver.port.port = port; + ha.u.evtchn.deliver.port.vcpu = kvm_arch_vcpu_id(cpu); + ha.u.evtchn.deliver.port.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL; + + return kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &ha); +} + static bool valid_port(evtchn_port_t port) { if (!port) { @@ -551,6 +589,12 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port) p->type_val, 0); break; + case EVTCHNSTAT_ipi: + if (s->evtchn_in_kernel) { + deassign_kernel_port(port); + } + break; + default: break; } @@ -640,3 +684,28 @@ int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq) return ret; } + +int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (!valid_vcpu(ipi->vcpu)) { + return -ENOENT; + } + + qemu_mutex_lock(&s->port_lock); + + ret = allocate_port(s, ipi->vcpu, EVTCHNSTAT_ipi, 0, &ipi->port); + if (!ret && s->evtchn_in_kernel) { + assign_kernel_port(EVTCHNSTAT_ipi, ipi->port, ipi->vcpu); + } + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index 0ea13dda3a..107f420848 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -19,9 +19,11 @@ struct evtchn_status; struct evtchn_close; struct evtchn_unmask; struct evtchn_bind_virq; +struct evtchn_bind_ipi; int xen_evtchn_status_op(struct evtchn_status *status); int xen_evtchn_close_op(struct evtchn_close *close); int xen_evtchn_unmask_op(struct evtchn_unmask *unmask); int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq); +int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi); #endif /* QEMU_XEN_EVTCHN_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index e0417f3d13..b8b439dd31 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -894,6 +894,21 @@ static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, X86CPU *cpu, } break; } + case EVTCHNOP_bind_ipi: { + struct evtchn_bind_ipi ipi; + + qemu_build_assert(sizeof(ipi) == 8); + if (kvm_copy_from_gva(cs, arg, &ipi, sizeof(ipi))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_bind_ipi_op(&ipi); + if (!err && kvm_copy_to_gva(cs, arg, &ipi, sizeof(ipi))) { + err = -EFAULT; + } + break; + } default: return false; } From cf7679abddd05712c5cfbe5516328586eded22c3 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 14 Dec 2022 00:11:07 +0000 Subject: [PATCH 159/185] hw/xen: Implement EVTCHNOP_send Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 180 ++++++++++++++++++++++++++++++++++++++ hw/i386/kvm/xen_evtchn.h | 2 + target/i386/kvm/xen-emu.c | 12 +++ 3 files changed, 194 insertions(+) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index eea80dc0f5..ce7a969234 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -490,6 +490,133 @@ static int unmask_port(XenEvtchnState *s, evtchn_port_t port, bool do_unmask) } } +static int do_set_port_lm(XenEvtchnState *s, evtchn_port_t port, + struct shared_info *shinfo, + struct vcpu_info *vcpu_info) +{ + const int bits_per_word = BITS_PER_BYTE * sizeof(shinfo->evtchn_pending[0]); + typeof(shinfo->evtchn_pending[0]) mask; + int idx = port / bits_per_word; + int offset = port % bits_per_word; + + mask = 1UL << offset; + + if (idx >= bits_per_word) { + return -EINVAL; + } + + /* Update the pending bit itself. If it was already set, we're done. */ + if (qatomic_fetch_or(&shinfo->evtchn_pending[idx], mask) & mask) { + return 0; + } + + /* Check if it's masked. */ + if (qatomic_fetch_or(&shinfo->evtchn_mask[idx], 0) & mask) { + return 0; + } + + /* Now on to the vcpu_info evtchn_pending_sel index... */ + mask = 1UL << idx; + + /* If a port in this word was already pending for this vCPU, all done. */ + if (qatomic_fetch_or(&vcpu_info->evtchn_pending_sel, mask) & mask) { + return 0; + } + + /* Set evtchn_upcall_pending for this vCPU */ + if (qatomic_fetch_or(&vcpu_info->evtchn_upcall_pending, 1)) { + return 0; + } + + inject_callback(s, s->port_table[port].vcpu); + + return 0; +} + +static int do_set_port_compat(XenEvtchnState *s, evtchn_port_t port, + struct compat_shared_info *shinfo, + struct compat_vcpu_info *vcpu_info) +{ + const int bits_per_word = BITS_PER_BYTE * sizeof(shinfo->evtchn_pending[0]); + typeof(shinfo->evtchn_pending[0]) mask; + int idx = port / bits_per_word; + int offset = port % bits_per_word; + + mask = 1UL << offset; + + if (idx >= bits_per_word) { + return -EINVAL; + } + + /* Update the pending bit itself. If it was already set, we're done. */ + if (qatomic_fetch_or(&shinfo->evtchn_pending[idx], mask) & mask) { + return 0; + } + + /* Check if it's masked. */ + if (qatomic_fetch_or(&shinfo->evtchn_mask[idx], 0) & mask) { + return 0; + } + + /* Now on to the vcpu_info evtchn_pending_sel index... */ + mask = 1UL << idx; + + /* If a port in this word was already pending for this vCPU, all done. */ + if (qatomic_fetch_or(&vcpu_info->evtchn_pending_sel, mask) & mask) { + return 0; + } + + /* Set evtchn_upcall_pending for this vCPU */ + if (qatomic_fetch_or(&vcpu_info->evtchn_upcall_pending, 1)) { + return 0; + } + + inject_callback(s, s->port_table[port].vcpu); + + return 0; +} + +static int set_port_pending(XenEvtchnState *s, evtchn_port_t port) +{ + void *vcpu_info, *shinfo; + + if (s->port_table[port].type == EVTCHNSTAT_closed) { + return -EINVAL; + } + + if (s->evtchn_in_kernel) { + XenEvtchnPort *p = &s->port_table[port]; + CPUState *cpu = qemu_get_cpu(p->vcpu); + struct kvm_irq_routing_xen_evtchn evt; + + if (!cpu) { + return 0; + } + + evt.port = port; + evt.vcpu = kvm_arch_vcpu_id(cpu); + evt.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL; + + return kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_EVTCHN_SEND, &evt); + } + + shinfo = xen_overlay_get_shinfo_ptr(); + if (!shinfo) { + return -ENOTSUP; + } + + vcpu_info = kvm_xen_get_vcpu_info_hva(s->port_table[port].vcpu); + if (!vcpu_info) { + return -EINVAL; + } + + if (xen_is_long_mode()) { + return do_set_port_lm(s, port, shinfo, vcpu_info); + } else { + return do_set_port_compat(s, port, shinfo, vcpu_info); + } +} + static int clear_port_pending(XenEvtchnState *s, evtchn_port_t port) { void *p = xen_overlay_get_shinfo_ptr(); @@ -709,3 +836,56 @@ int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi) return ret; } + +int xen_evtchn_send_op(struct evtchn_send *send) +{ + XenEvtchnState *s = xen_evtchn_singleton; + XenEvtchnPort *p; + int ret = 0; + + if (!s) { + return -ENOTSUP; + } + + if (!valid_port(send->port)) { + return -EINVAL; + } + + qemu_mutex_lock(&s->port_lock); + + p = &s->port_table[send->port]; + + switch (p->type) { + case EVTCHNSTAT_interdomain: + if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) { + /* + * This is an event from the guest to qemu itself, which is + * serving as the driver domain. Not yet implemented; it will + * be hooked up to the qemu implementation of xenstore, + * console, PV net/block drivers etc. + */ + ret = -ENOSYS; + } else { + /* Loopback interdomain ports; just a complex IPI */ + set_port_pending(s, p->type_val); + } + break; + + case EVTCHNSTAT_ipi: + set_port_pending(s, send->port); + break; + + case EVTCHNSTAT_unbound: + /* Xen will silently drop these */ + break; + + default: + ret = -EINVAL; + break; + } + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} + diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index 107f420848..500fdbe8b8 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -20,10 +20,12 @@ struct evtchn_close; struct evtchn_unmask; struct evtchn_bind_virq; struct evtchn_bind_ipi; +struct evtchn_send; int xen_evtchn_status_op(struct evtchn_status *status); int xen_evtchn_close_op(struct evtchn_close *close); int xen_evtchn_unmask_op(struct evtchn_unmask *unmask); int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq); int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi); +int xen_evtchn_send_op(struct evtchn_send *send); #endif /* QEMU_XEN_EVTCHN_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index b8b439dd31..19a7f16ebe 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -909,6 +909,18 @@ static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, X86CPU *cpu, } break; } + case EVTCHNOP_send: { + struct evtchn_send send; + + qemu_build_assert(sizeof(send) == 4); + if (kvm_copy_from_gva(cs, arg, &send, sizeof(send))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_send_op(&send); + break; + } default: return false; } From e1db61b87b0e479bcacdf0c17656c8ee563cc5ba Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 14 Dec 2022 16:39:48 +0000 Subject: [PATCH 160/185] hw/xen: Implement EVTCHNOP_alloc_unbound Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 32 ++++++++++++++++++++++++++++++++ hw/i386/kvm/xen_evtchn.h | 2 ++ target/i386/kvm/xen-emu.c | 15 +++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index ce7a969234..df25bf0806 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -837,6 +837,38 @@ int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi) return ret; } +int xen_evtchn_alloc_unbound_op(struct evtchn_alloc_unbound *alloc) +{ + XenEvtchnState *s = xen_evtchn_singleton; + uint16_t type_val; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (alloc->dom != DOMID_SELF && alloc->dom != xen_domid) { + return -ESRCH; + } + + if (alloc->remote_dom == DOMID_QEMU) { + type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU; + } else if (alloc->remote_dom == DOMID_SELF || + alloc->remote_dom == xen_domid) { + type_val = 0; + } else { + return -EPERM; + } + + qemu_mutex_lock(&s->port_lock); + + ret = allocate_port(s, 0, EVTCHNSTAT_unbound, type_val, &alloc->port); + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} + int xen_evtchn_send_op(struct evtchn_send *send) { XenEvtchnState *s = xen_evtchn_singleton; diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index 500fdbe8b8..fc080138e3 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -21,11 +21,13 @@ struct evtchn_unmask; struct evtchn_bind_virq; struct evtchn_bind_ipi; struct evtchn_send; +struct evtchn_alloc_unbound; int xen_evtchn_status_op(struct evtchn_status *status); int xen_evtchn_close_op(struct evtchn_close *close); int xen_evtchn_unmask_op(struct evtchn_unmask *unmask); int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq); int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi); int xen_evtchn_send_op(struct evtchn_send *send); +int xen_evtchn_alloc_unbound_op(struct evtchn_alloc_unbound *alloc); #endif /* QEMU_XEN_EVTCHN_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 19a7f16ebe..ac04387a6a 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -921,6 +921,21 @@ static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, X86CPU *cpu, err = xen_evtchn_send_op(&send); break; } + case EVTCHNOP_alloc_unbound: { + struct evtchn_alloc_unbound alloc; + + qemu_build_assert(sizeof(alloc) == 8); + if (kvm_copy_from_gva(cs, arg, &alloc, sizeof(alloc))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_alloc_unbound_op(&alloc); + if (!err && kvm_copy_to_gva(cs, arg, &alloc, sizeof(alloc))) { + err = -EFAULT; + } + break; + } default: return false; } From 8432788104b6b4843738edbc5cf084b2a3687ee9 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 14 Dec 2022 17:26:32 +0000 Subject: [PATCH 161/185] hw/xen: Implement EVTCHNOP_bind_interdomain Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 78 +++++++++++++++++++++++++++++++++++++++ hw/i386/kvm/xen_evtchn.h | 2 + target/i386/kvm/xen-emu.c | 16 ++++++++ 3 files changed, 96 insertions(+) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index df25bf0806..e2e36d94f6 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -722,6 +722,23 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port) } break; + case EVTCHNSTAT_interdomain: + if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) { + /* Not yet implemented. This can't happen! */ + } else { + /* Loopback interdomain */ + XenEvtchnPort *rp = &s->port_table[p->type_val]; + if (!valid_port(p->type_val) || rp->type_val != port || + rp->type != EVTCHNSTAT_interdomain) { + error_report("Inconsistent state for interdomain unbind"); + } else { + /* Set the other end back to unbound */ + rp->type = EVTCHNSTAT_unbound; + rp->type_val = 0; + } + } + break; + default: break; } @@ -837,6 +854,67 @@ int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi) return ret; } +int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain) +{ + XenEvtchnState *s = xen_evtchn_singleton; + uint16_t type_val; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (interdomain->remote_dom == DOMID_QEMU) { + type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU; + } else if (interdomain->remote_dom == DOMID_SELF || + interdomain->remote_dom == xen_domid) { + type_val = 0; + } else { + return -ESRCH; + } + + if (!valid_port(interdomain->remote_port)) { + return -EINVAL; + } + + qemu_mutex_lock(&s->port_lock); + + /* The newly allocated port starts out as unbound */ + ret = allocate_port(s, 0, EVTCHNSTAT_unbound, type_val, + &interdomain->local_port); + if (ret) { + goto out; + } + + if (interdomain->remote_dom == DOMID_QEMU) { + /* We haven't hooked up QEMU's PV drivers to this yet */ + ret = -ENOSYS; + } else { + /* Loopback */ + XenEvtchnPort *rp = &s->port_table[interdomain->remote_port]; + XenEvtchnPort *lp = &s->port_table[interdomain->local_port]; + + if (rp->type == EVTCHNSTAT_unbound && rp->type_val == 0) { + /* It's a match! */ + rp->type = EVTCHNSTAT_interdomain; + rp->type_val = interdomain->local_port; + + lp->type = EVTCHNSTAT_interdomain; + lp->type_val = interdomain->remote_port; + } else { + ret = -EINVAL; + } + } + + if (ret) { + free_port(s, interdomain->local_port); + } + out: + qemu_mutex_unlock(&s->port_lock); + + return ret; + +} int xen_evtchn_alloc_unbound_op(struct evtchn_alloc_unbound *alloc) { XenEvtchnState *s = xen_evtchn_singleton; diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index fc080138e3..1ebc7580eb 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -22,6 +22,7 @@ struct evtchn_bind_virq; struct evtchn_bind_ipi; struct evtchn_send; struct evtchn_alloc_unbound; +struct evtchn_bind_interdomain; int xen_evtchn_status_op(struct evtchn_status *status); int xen_evtchn_close_op(struct evtchn_close *close); int xen_evtchn_unmask_op(struct evtchn_unmask *unmask); @@ -29,5 +30,6 @@ int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq); int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi); int xen_evtchn_send_op(struct evtchn_send *send); int xen_evtchn_alloc_unbound_op(struct evtchn_alloc_unbound *alloc); +int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain); #endif /* QEMU_XEN_EVTCHN_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index ac04387a6a..1aff6b1042 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -936,6 +936,22 @@ static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, X86CPU *cpu, } break; } + case EVTCHNOP_bind_interdomain: { + struct evtchn_bind_interdomain interdomain; + + qemu_build_assert(sizeof(interdomain) == 12); + if (kvm_copy_from_gva(cs, arg, &interdomain, sizeof(interdomain))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_bind_interdomain_op(&interdomain); + if (!err && + kvm_copy_to_gva(cs, arg, &interdomain, sizeof(interdomain))) { + err = -EFAULT; + } + break; + } default: return false; } From 306670461bb19e7af42b3d68d007d7690efd2334 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 14 Dec 2022 19:27:38 +0000 Subject: [PATCH 162/185] hw/xen: Implement EVTCHNOP_bind_vcpu Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 40 +++++++++++++++++++++++++++++++++++++++ hw/i386/kvm/xen_evtchn.h | 2 ++ target/i386/kvm/xen-emu.c | 12 ++++++++++++ 3 files changed, 54 insertions(+) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index e2e36d94f6..cbaf4f535a 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -791,6 +791,46 @@ int xen_evtchn_unmask_op(struct evtchn_unmask *unmask) return ret; } +int xen_evtchn_bind_vcpu_op(struct evtchn_bind_vcpu *vcpu) +{ + XenEvtchnState *s = xen_evtchn_singleton; + XenEvtchnPort *p; + int ret = -EINVAL; + + if (!s) { + return -ENOTSUP; + } + + if (!valid_port(vcpu->port)) { + return -EINVAL; + } + + if (!valid_vcpu(vcpu->vcpu)) { + return -ENOENT; + } + + qemu_mutex_lock(&s->port_lock); + + p = &s->port_table[vcpu->port]; + + if (p->type == EVTCHNSTAT_interdomain || + p->type == EVTCHNSTAT_unbound || + p->type == EVTCHNSTAT_pirq || + (p->type == EVTCHNSTAT_virq && virq_is_global(p->type_val))) { + /* + * unmask_port() with do_unmask==false will just raise the event + * on the new vCPU if the port was already pending. + */ + p->vcpu = vcpu->vcpu; + unmask_port(s, vcpu->port, false); + ret = 0; + } + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} + int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq) { XenEvtchnState *s = xen_evtchn_singleton; diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index 1ebc7580eb..486b031c82 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -23,6 +23,7 @@ struct evtchn_bind_ipi; struct evtchn_send; struct evtchn_alloc_unbound; struct evtchn_bind_interdomain; +struct evtchn_bind_vcpu; int xen_evtchn_status_op(struct evtchn_status *status); int xen_evtchn_close_op(struct evtchn_close *close); int xen_evtchn_unmask_op(struct evtchn_unmask *unmask); @@ -31,5 +32,6 @@ int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi); int xen_evtchn_send_op(struct evtchn_send *send); int xen_evtchn_alloc_unbound_op(struct evtchn_alloc_unbound *alloc); int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain); +int xen_evtchn_bind_vcpu_op(struct evtchn_bind_vcpu *vcpu); #endif /* QEMU_XEN_EVTCHN_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 1aff6b1042..8dc0d320f5 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -952,6 +952,18 @@ static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, X86CPU *cpu, } break; } + case EVTCHNOP_bind_vcpu: { + struct evtchn_bind_vcpu vcpu; + + qemu_build_assert(sizeof(vcpu) == 8); + if (kvm_copy_from_gva(cs, arg, &vcpu, sizeof(vcpu))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_bind_vcpu_op(&vcpu); + break; + } default: return false; } From a15b10978fe62411936e577288f28184a41754a9 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 14 Dec 2022 19:36:15 +0000 Subject: [PATCH 163/185] hw/xen: Implement EVTCHNOP_reset Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 30 ++++++++++++++++++++++++++++++ hw/i386/kvm/xen_evtchn.h | 3 +++ target/i386/kvm/xen-emu.c | 17 +++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index cbaf4f535a..c804a315e0 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "qemu/host-utils.h" #include "qemu/module.h" +#include "qemu/lockable.h" #include "qemu/main-loop.h" #include "qemu/log.h" #include "qapi/error.h" @@ -747,6 +748,35 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port) return 0; } +int xen_evtchn_soft_reset(void) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int i; + + if (!s) { + return -ENOTSUP; + } + + assert(qemu_mutex_iothread_locked()); + + QEMU_LOCK_GUARD(&s->port_lock); + + for (i = 0; i < s->nr_ports; i++) { + close_port(s, i); + } + + return 0; +} + +int xen_evtchn_reset_op(struct evtchn_reset *reset) +{ + if (reset->dom != DOMID_SELF && reset->dom != xen_domid) { + return -ESRCH; + } + + return xen_evtchn_soft_reset(); +} + int xen_evtchn_close_op(struct evtchn_close *close) { XenEvtchnState *s = xen_evtchn_singleton; diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index 486b031c82..5d3e03553f 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -13,6 +13,7 @@ #define QEMU_XEN_EVTCHN_H void xen_evtchn_create(void); +int xen_evtchn_soft_reset(void); int xen_evtchn_set_callback_param(uint64_t param); struct evtchn_status; @@ -24,6 +25,7 @@ struct evtchn_send; struct evtchn_alloc_unbound; struct evtchn_bind_interdomain; struct evtchn_bind_vcpu; +struct evtchn_reset; int xen_evtchn_status_op(struct evtchn_status *status); int xen_evtchn_close_op(struct evtchn_close *close); int xen_evtchn_unmask_op(struct evtchn_unmask *unmask); @@ -33,5 +35,6 @@ int xen_evtchn_send_op(struct evtchn_send *send); int xen_evtchn_alloc_unbound_op(struct evtchn_alloc_unbound *alloc); int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain); int xen_evtchn_bind_vcpu_op(struct evtchn_bind_vcpu *vcpu); +int xen_evtchn_reset_op(struct evtchn_reset *reset); #endif /* QEMU_XEN_EVTCHN_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 8dc0d320f5..821629f077 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -964,6 +964,18 @@ static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, X86CPU *cpu, err = xen_evtchn_bind_vcpu_op(&vcpu); break; } + case EVTCHNOP_reset: { + struct evtchn_reset reset; + + qemu_build_assert(sizeof(reset) == 2); + if (kvm_copy_from_gva(cs, arg, &reset, sizeof(reset))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_reset_op(&reset); + break; + } default: return false; } @@ -981,6 +993,11 @@ int kvm_xen_soft_reset(void) trace_kvm_xen_soft_reset(); + err = xen_evtchn_soft_reset(); + if (err) { + return err; + } + /* * Zero is the reset/startup state for HVM_PARAM_CALLBACK_IRQ. Strictly, * it maps to HVM_PARAM_CALLBACK_TYPE_GSI with GSI#0, but Xen refuses to From 507cb64d6e66d672bfddb275fe746241e0ed8db2 Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Tue, 21 Aug 2018 12:16:19 -0400 Subject: [PATCH 164/185] i386/xen: add monitor commands to test event injection Specifically add listing, injection of event channels. Signed-off-by: Joao Martins Signed-off-by: David Woodhouse Acked-by: Dr. David Alan Gilbert Reviewed-by: Paul Durrant --- hmp-commands.hx | 29 ++++++++ hw/i386/kvm/meson.build | 7 ++ hw/i386/kvm/xen-stubs.c | 28 ++++++++ hw/i386/kvm/xen_evtchn.c | 137 +++++++++++++++++++++++++++++++++++++ include/monitor/hmp.h | 2 + qapi/misc-target.json | 116 +++++++++++++++++++++++++++++++ tests/qtest/qmp-cmd-test.c | 1 + 7 files changed, 320 insertions(+) create mode 100644 hw/i386/kvm/xen-stubs.c diff --git a/hmp-commands.hx b/hmp-commands.hx index fbb5daf09b..b87c250e23 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1815,3 +1815,32 @@ SRST Dump the FDT in dtb format to *filename*. ERST #endif + +#if defined(CONFIG_XEN_EMU) + { + .name = "xen-event-inject", + .args_type = "port:i", + .params = "port", + .help = "inject event channel", + .cmd = hmp_xen_event_inject, + }, + +SRST +``xen-event-inject`` *port* + Notify guest via event channel on port *port*. +ERST + + + { + .name = "xen-event-list", + .args_type = "", + .params = "", + .help = "list event channel state", + .cmd = hmp_xen_event_list, + }, + +SRST +``xen-event-list`` + List event channels in the guest +ERST +#endif diff --git a/hw/i386/kvm/meson.build b/hw/i386/kvm/meson.build index cab64df339..325c901496 100644 --- a/hw/i386/kvm/meson.build +++ b/hw/i386/kvm/meson.build @@ -10,3 +10,10 @@ i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files( )) i386_ss.add_all(when: 'CONFIG_KVM', if_true: i386_kvm_ss) + +xen_stubs_ss = ss.source_set() +xen_stubs_ss.add(when: 'CONFIG_XEN_EMU', if_false: files( + 'xen-stubs.c', +)) + +specific_ss.add_all(when: 'CONFIG_SOFTMMU', if_true: xen_stubs_ss) diff --git a/hw/i386/kvm/xen-stubs.c b/hw/i386/kvm/xen-stubs.c new file mode 100644 index 0000000000..720590aedd --- /dev/null +++ b/hw/i386/kvm/xen-stubs.c @@ -0,0 +1,28 @@ +/* + * QEMU Xen emulation: QMP stubs + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "qapi/error.h" +#include "qapi/qapi-commands-misc-target.h" + +#ifdef TARGET_I386 +EvtchnInfoList *qmp_xen_event_list(Error **errp) +{ + error_setg(errp, "Xen event channel emulation not enabled"); + return NULL; +} + +void qmp_xen_event_inject(uint32_t port, Error **errp) +{ + error_setg(errp, "Xen event channel emulation not enabled"); +} +#endif diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index c804a315e0..e937de7a93 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -15,7 +15,11 @@ #include "qemu/lockable.h" #include "qemu/main-loop.h" #include "qemu/log.h" +#include "monitor/monitor.h" +#include "monitor/hmp.h" #include "qapi/error.h" +#include "qapi/qapi-commands-misc-target.h" +#include "qapi/qmp/qdict.h" #include "qom/object.h" #include "exec/target_page.h" #include "exec/address-spaces.h" @@ -1069,3 +1073,136 @@ int xen_evtchn_send_op(struct evtchn_send *send) return ret; } +EvtchnInfoList *qmp_xen_event_list(Error **errp) +{ + XenEvtchnState *s = xen_evtchn_singleton; + EvtchnInfoList *head = NULL, **tail = &head; + void *shinfo, *pending, *mask; + int i; + + if (!s) { + error_setg(errp, "Xen event channel emulation not enabled"); + return NULL; + } + + shinfo = xen_overlay_get_shinfo_ptr(); + if (!shinfo) { + error_setg(errp, "Xen shared info page not allocated"); + return NULL; + } + + if (xen_is_long_mode()) { + pending = shinfo + offsetof(struct shared_info, evtchn_pending); + mask = shinfo + offsetof(struct shared_info, evtchn_mask); + } else { + pending = shinfo + offsetof(struct compat_shared_info, evtchn_pending); + mask = shinfo + offsetof(struct compat_shared_info, evtchn_mask); + } + + QEMU_LOCK_GUARD(&s->port_lock); + + for (i = 0; i < s->nr_ports; i++) { + XenEvtchnPort *p = &s->port_table[i]; + EvtchnInfo *info; + + if (p->type == EVTCHNSTAT_closed) { + continue; + } + + info = g_new0(EvtchnInfo, 1); + + info->port = i; + qemu_build_assert(EVTCHN_PORT_TYPE_CLOSED == EVTCHNSTAT_closed); + qemu_build_assert(EVTCHN_PORT_TYPE_UNBOUND == EVTCHNSTAT_unbound); + qemu_build_assert(EVTCHN_PORT_TYPE_INTERDOMAIN == EVTCHNSTAT_interdomain); + qemu_build_assert(EVTCHN_PORT_TYPE_PIRQ == EVTCHNSTAT_pirq); + qemu_build_assert(EVTCHN_PORT_TYPE_VIRQ == EVTCHNSTAT_virq); + qemu_build_assert(EVTCHN_PORT_TYPE_IPI == EVTCHNSTAT_ipi); + + info->type = p->type; + if (p->type == EVTCHNSTAT_interdomain) { + info->remote_domain = g_strdup((p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) ? + "qemu" : "loopback"); + info->target = p->type_val & PORT_INFO_TYPEVAL_REMOTE_PORT_MASK; + } else { + info->target = p->type_val; + } + info->vcpu = p->vcpu; + info->pending = test_bit(i, pending); + info->masked = test_bit(i, mask); + + QAPI_LIST_APPEND(tail, info); + } + + return head; +} + +void qmp_xen_event_inject(uint32_t port, Error **errp) +{ + XenEvtchnState *s = xen_evtchn_singleton; + + if (!s) { + error_setg(errp, "Xen event channel emulation not enabled"); + return; + } + + if (!valid_port(port)) { + error_setg(errp, "Invalid port %u", port); + } + + QEMU_LOCK_GUARD(&s->port_lock); + + if (set_port_pending(s, port)) { + error_setg(errp, "Failed to set port %u", port); + return; + } +} + +void hmp_xen_event_list(Monitor *mon, const QDict *qdict) +{ + EvtchnInfoList *iter, *info_list; + Error *err = NULL; + + info_list = qmp_xen_event_list(&err); + if (err) { + hmp_handle_error(mon, err); + return; + } + + for (iter = info_list; iter; iter = iter->next) { + EvtchnInfo *info = iter->value; + + monitor_printf(mon, "port %4u: vcpu: %d %s", info->port, info->vcpu, + EvtchnPortType_str(info->type)); + if (info->type != EVTCHN_PORT_TYPE_IPI) { + monitor_printf(mon, "("); + if (info->remote_domain) { + monitor_printf(mon, "%s:", info->remote_domain); + } + monitor_printf(mon, "%d)", info->target); + } + if (info->pending) { + monitor_printf(mon, " PENDING"); + } + if (info->masked) { + monitor_printf(mon, " MASKED"); + } + monitor_printf(mon, "\n"); + } + + qapi_free_EvtchnInfoList(info_list); +} + +void hmp_xen_event_inject(Monitor *mon, const QDict *qdict) +{ + int port = qdict_get_int(qdict, "port"); + Error *err = NULL; + + qmp_xen_event_inject(port, &err); + if (err) { + hmp_handle_error(mon, err); + } else { + monitor_printf(mon, "Delivered port %d\n", port); + } +} + diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index 2220f14fc9..efae6b06bc 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -114,6 +114,8 @@ void hmp_virtio_status(Monitor *mon, const QDict *qdict); void hmp_virtio_queue_status(Monitor *mon, const QDict *qdict); void hmp_vhost_queue_status(Monitor *mon, const QDict *qdict); void hmp_virtio_queue_element(Monitor *mon, const QDict *qdict); +void hmp_xen_event_inject(Monitor *mon, const QDict *qdict); +void hmp_xen_event_list(Monitor *mon, const QDict *qdict); void object_add_completion(ReadLineState *rs, int nb_args, const char *str); void object_del_completion(ReadLineState *rs, int nb_args, const char *str); void device_add_completion(ReadLineState *rs, int nb_args, const char *str); diff --git a/qapi/misc-target.json b/qapi/misc-target.json index 5b6a8e9185..de91054523 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -380,3 +380,119 @@ # ## { 'command': 'query-sgx-capabilities', 'returns': 'SGXInfo', 'if': 'TARGET_I386' } + + +## +# @EvtchnPortType: +# +# An enumeration of Xen event channel port types. +# +# @closed: The port is unused. +# +# @unbound: The port is allocated and ready to be bound. +# +# @interdomain: The port is connected as an interdomain interrupt. +# +# @pirq: The port is bound to a physical IRQ (PIRQ). +# +# @virq: The port is bound to a virtual IRQ (VIRQ). +# +# @ipi: The post is an inter-processor interrupt (IPI). +# +# Since: 8.0 +## +{ 'enum': 'EvtchnPortType', + 'data': ['closed', 'unbound', 'interdomain', 'pirq', 'virq', 'ipi'], + 'if': 'TARGET_I386' } + +## +# @EvtchnInfo: +# +# Information about a Xen event channel port +# +# @port: the port number +# +# @vcpu: target vCPU for this port +# +# @type: the port type +# +# @remote-domain: remote domain for interdomain ports +# +# @target: remote port ID, or virq/pirq number +# +# @pending: port is currently active pending delivery +# +# @masked: port is masked +# +# Since: 8.0 +## +{ 'struct': 'EvtchnInfo', + 'data': {'port': 'uint16', + 'vcpu': 'uint32', + 'type': 'EvtchnPortType', + 'remote-domain': 'str', + 'target': 'uint16', + 'pending': 'bool', + 'masked': 'bool'}, + 'if': 'TARGET_I386' } + + +## +# @xen-event-list: +# +# Query the Xen event channels opened by the guest. +# +# Returns: list of open event channel ports. +# +# Since: 8.0 +# +# Example: +# +# -> { "execute": "xen-event-list" } +# <- { "return": [ +# { +# "pending": false, +# "port": 1, +# "vcpu": 1, +# "remote-domain": "qemu", +# "masked": false, +# "type": "interdomain", +# "target": 1 +# }, +# { +# "pending": false, +# "port": 2, +# "vcpu": 0, +# "remote-domain": "", +# "masked": false, +# "type": "virq", +# "target": 0 +# } +# ] +# } +# +## +{ 'command': 'xen-event-list', + 'returns': ['EvtchnInfo'], + 'if': 'TARGET_I386' } + +## +# @xen-event-inject: +# +# Inject a Xen event channel port (interrupt) to the guest. +# +# @port: The port number +# +# Returns: - Nothing on success. +# +# Since: 8.0 +# +# Example: +# +# -> { "execute": "xen-event-inject", "arguments": { "port": 1 } } +# <- { "return": { } } +# +## +{ 'command': 'xen-event-inject', + 'data': { 'port': 'uint32' }, + 'if': 'TARGET_I386' } diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index 98caf6fef6..a58de48d2a 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -54,6 +54,7 @@ static int query_error_class(const char *cmd) /* Only valid with accel=tcg */ { "x-query-jit", ERROR_CLASS_GENERIC_ERROR }, { "x-query-opcount", ERROR_CLASS_GENERIC_ERROR }, + { "xen-event-list", ERROR_CLASS_GENERIC_ERROR }, { NULL, -1 } }; int i; From ddf0fd9ae1fd1ff95489763b37a483adb3cd5907 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 15 Dec 2022 20:35:24 +0000 Subject: [PATCH 165/185] hw/xen: Support HVM_PARAM_CALLBACK_TYPE_GSI callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GSI callback (and later PCI_INTX) is a level triggered interrupt. It is asserted when an event channel is delivered to vCPU0, and is supposed to be cleared when the vcpu_info->evtchn_upcall_pending field for vCPU0 is cleared again. Thankfully, Xen does *not* assert the GSI if the guest sets its own evtchn_upcall_pending field; we only need to assert the GSI when we have delivered an event for ourselves. So that's the easy part, kind of. There's a slight complexity in that we need to hold the BQL before we can call qemu_set_irq(), and we definitely can't do that while holding our own port_lock (because we'll need to take that from the qemu-side functions that the PV backend drivers will call). So if we end up wanting to set the IRQ in a context where we *don't* already hold the BQL, defer to a BH. However, we *do* need to poll for the evtchn_upcall_pending flag being cleared. In an ideal world we would poll that when the EOI happens on the PIC/IOAPIC. That's how it works in the kernel with the VFIO eventfd pairs — one is used to trigger the interrupt, and the other works in the other direction to 'resample' on EOI, and trigger the first eventfd again if the line is still active. However, QEMU doesn't seem to do that. Even VFIO level interrupts seem to be supported by temporarily unmapping the device's BARs from the guest when an interrupt happens, then trapping *all* MMIO to the device and sending the 'resample' event on *every* MMIO access until the IRQ is cleared! Maybe in future we'll plumb the 'resample' concept through QEMU's irq framework but for now we'll do what Xen itself does: just check the flag on every vmexit if the upcall GSI is known to be asserted. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 97 +++++++++++++++++++++++++++++++++++++++ hw/i386/kvm/xen_evtchn.h | 4 ++ hw/i386/pc.c | 6 +++ include/sysemu/kvm_xen.h | 1 + target/i386/cpu.h | 1 + target/i386/kvm/kvm.c | 11 +++++ target/i386/kvm/xen-emu.c | 40 ++++++++++++++++ target/i386/kvm/xen-emu.h | 1 + 8 files changed, 161 insertions(+) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index e937de7a93..6b0bdba65d 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -27,6 +27,8 @@ #include "hw/sysbus.h" #include "hw/xen/xen.h" +#include "hw/i386/x86.h" +#include "hw/irq.h" #include "xen_evtchn.h" #include "xen_overlay.h" @@ -100,9 +102,12 @@ struct XenEvtchnState { uint64_t callback_param; bool evtchn_in_kernel; + QEMUBH *gsi_bh; + QemuMutex port_lock; uint32_t nr_ports; XenEvtchnPort port_table[EVTCHN_2L_NR_CHANNELS]; + qemu_irq gsis[IOAPIC_NUM_PINS]; }; struct XenEvtchnState *xen_evtchn_singleton; @@ -167,13 +172,42 @@ static const TypeInfo xen_evtchn_info = { .class_init = xen_evtchn_class_init, }; +static void gsi_assert_bh(void *opaque) +{ + struct vcpu_info *vi = kvm_xen_get_vcpu_info_hva(0); + if (vi) { + xen_evtchn_set_callback_level(!!vi->evtchn_upcall_pending); + } +} + void xen_evtchn_create(void) { XenEvtchnState *s = XEN_EVTCHN(sysbus_create_simple(TYPE_XEN_EVTCHN, -1, NULL)); + int i; + xen_evtchn_singleton = s; qemu_mutex_init(&s->port_lock); + s->gsi_bh = aio_bh_new(qemu_get_aio_context(), gsi_assert_bh, s); + + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->gsis[i]); + } +} + +void xen_evtchn_connect_gsis(qemu_irq *system_gsis) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int i; + + if (!s) { + return; + } + + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(s), i, system_gsis[i]); + } } static void xen_evtchn_register_types(void) @@ -183,6 +217,64 @@ static void xen_evtchn_register_types(void) type_init(xen_evtchn_register_types) +void xen_evtchn_set_callback_level(int level) +{ + XenEvtchnState *s = xen_evtchn_singleton; + uint32_t param; + + if (!s) { + return; + } + + /* + * We get to this function in a number of ways: + * + * • From I/O context, via PV backend drivers sending a notification to + * the guest. + * + * • From guest vCPU context, via loopback interdomain event channels + * (or theoretically even IPIs but guests don't use those with GSI + * delivery because that's pointless. We don't want a malicious guest + * to be able to trigger a deadlock though, so we can't rule it out.) + * + * • From guest vCPU context when the HVM_PARAM_CALLBACK_IRQ is being + * configured. + * + * • From guest vCPU context in the KVM exit handler, if the upcall + * pending flag has been cleared and the GSI needs to be deasserted. + * + * • Maybe in future, in an interrupt ack/eoi notifier when the GSI has + * been acked in the irqchip. + * + * Whichever context we come from if we aren't already holding the BQL + * then e can't take it now, as we may already hold s->port_lock. So + * trigger the BH to set the IRQ for us instead of doing it immediately. + * + * In the HVM_PARAM_CALLBACK_IRQ and KVM exit handler cases, the caller + * will deliberately take the BQL because they want the change to take + * effect immediately. That just leaves interdomain loopback as the case + * which uses the BH. + */ + if (!qemu_mutex_iothread_locked()) { + qemu_bh_schedule(s->gsi_bh); + return; + } + + param = (uint32_t)s->callback_param; + + switch (s->callback_param >> CALLBACK_VIA_TYPE_SHIFT) { + case HVM_PARAM_CALLBACK_TYPE_GSI: + if (param < IOAPIC_NUM_PINS) { + qemu_set_irq(s->gsis[param], level); + if (level) { + /* Ensure the vCPU polls for deassertion */ + kvm_xen_set_callback_asserted(); + } + } + break; + } +} + int xen_evtchn_set_callback_param(uint64_t param) { XenEvtchnState *s = xen_evtchn_singleton; @@ -209,6 +301,11 @@ int xen_evtchn_set_callback_param(uint64_t param) } break; } + + case HVM_PARAM_CALLBACK_TYPE_GSI: + ret = 0; + break; + default: /* Xen doesn't return error even if you set something bogus */ ret = 0; diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index 5d3e03553f..b03c3108bc 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -12,9 +12,13 @@ #ifndef QEMU_XEN_EVTCHN_H #define QEMU_XEN_EVTCHN_H +#include "hw/sysbus.h" + void xen_evtchn_create(void); int xen_evtchn_soft_reset(void); int xen_evtchn_set_callback_param(uint64_t param); +void xen_evtchn_connect_gsis(qemu_irq *system_gsis); +void xen_evtchn_set_callback_level(int level); struct evtchn_status; struct evtchn_close; diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f38e3f1be8..71d3b8bec2 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1310,6 +1310,12 @@ void pc_basic_device_init(struct PCMachineState *pcms, } *rtc_state = ISA_DEVICE(mc146818_rtc_init(isa_bus, 2000, rtc_irq)); +#ifdef CONFIG_XEN_EMU + if (xen_mode == XEN_EMULATE) { + xen_evtchn_connect_gsis(gsi); + } +#endif + qemu_register_boot_set(pc_boot_set, *rtc_state); if (!xen_enabled() && diff --git a/include/sysemu/kvm_xen.h b/include/sysemu/kvm_xen.h index fbb7414eb7..2b20030281 100644 --- a/include/sysemu/kvm_xen.h +++ b/include/sysemu/kvm_xen.h @@ -23,6 +23,7 @@ int kvm_xen_soft_reset(void); uint32_t kvm_xen_get_caps(void); void *kvm_xen_get_vcpu_info_hva(uint32_t vcpu_id); void kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type); +void kvm_xen_set_callback_asserted(void); int kvm_xen_set_vcpu_virq(uint32_t vcpu_id, uint16_t virq, uint16_t port); #define kvm_xen_has_cap(cap) (!!(kvm_xen_get_caps() & \ diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 4b70257db5..7227a8ec08 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1808,6 +1808,7 @@ typedef struct CPUArchState { uint64_t xen_vcpu_time_info_gpa; uint64_t xen_vcpu_runstate_gpa; uint8_t xen_vcpu_callback_vector; + bool xen_callback_asserted; uint16_t xen_virq[XEN_NR_VIRQS]; uint64_t xen_singleshot_timer_ns; #endif diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 5a144ec0de..3c3795506d 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -4990,6 +4990,17 @@ MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run) kvm_rate_limit_on_bus_lock(); } + /* + * If the callback is asserted as a GSI (or PCI INTx) then check if + * vcpu_info->evtchn_upcall_pending has been cleared, and deassert + * the callback IRQ if so. Ideally we could hook into the PIC/IOAPIC + * EOI and only resample then, exactly how the VFIO eventfd pairs + * are designed to work for level triggered interrupts. + */ + if (x86_cpu->env.xen_callback_asserted) { + kvm_xen_maybe_deassert_callback(cpu); + } + /* We need to protect the apic state against concurrent accesses from * different threads in case the userspace irqchip is used. */ if (!kvm_irqchip_in_kernel()) { diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 821629f077..b52617df54 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -320,6 +320,39 @@ void *kvm_xen_get_vcpu_info_hva(uint32_t vcpu_id) return X86_CPU(cs)->env.xen_vcpu_info_hva; } +void kvm_xen_maybe_deassert_callback(CPUState *cs) +{ + CPUX86State *env = &X86_CPU(cs)->env; + struct vcpu_info *vi = env->xen_vcpu_info_hva; + if (!vi) { + return; + } + + /* If the evtchn_upcall_pending flag is cleared, turn the GSI off. */ + if (!vi->evtchn_upcall_pending) { + qemu_mutex_lock_iothread(); + /* + * Check again now we have the lock, because it may have been + * asserted in the interim. And we don't want to take the lock + * every time because this is a fast path. + */ + if (!vi->evtchn_upcall_pending) { + X86_CPU(cs)->env.xen_callback_asserted = false; + xen_evtchn_set_callback_level(0); + } + qemu_mutex_unlock_iothread(); + } +} + +void kvm_xen_set_callback_asserted(void) +{ + CPUState *cs = qemu_get_cpu(0); + + if (cs) { + X86_CPU(cs)->env.xen_callback_asserted = true; + } +} + void kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type) { CPUState *cs = qemu_get_cpu(vcpu_id); @@ -352,6 +385,13 @@ void kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type) */ qemu_cpu_kick(cs); break; + + case HVM_PARAM_CALLBACK_TYPE_GSI: + case HVM_PARAM_CALLBACK_TYPE_PCI_INTX: + if (vcpu_id == 0) { + xen_evtchn_set_callback_level(1); + } + break; } } diff --git a/target/i386/kvm/xen-emu.h b/target/i386/kvm/xen-emu.h index 452605699a..fe85e0b195 100644 --- a/target/i386/kvm/xen-emu.h +++ b/target/i386/kvm/xen-emu.h @@ -28,5 +28,6 @@ int kvm_xen_init_vcpu(CPUState *cs); int kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit); int kvm_put_xen_state(CPUState *cs); int kvm_get_xen_state(CPUState *cs); +void kvm_xen_maybe_deassert_callback(CPUState *cs); #endif /* QEMU_I386_KVM_XEN_EMU_H */ From 2aff696b10d16ef09dc5a2c953ceccbf6d38f744 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 16 Dec 2022 00:03:21 +0000 Subject: [PATCH 166/185] hw/xen: Support HVM_PARAM_CALLBACK_TYPE_PCI_INTX callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guest is permitted to specify an arbitrary domain/bus/device/function and INTX pin from which the callback IRQ shall appear to have come. In QEMU we can only easily do this for devices that actually exist, and even that requires us "knowing" that it's a PCMachine in order to find the PCI root bus — although that's OK really because it's always true. We also don't get to get notified of INTX routing changes, because we can't do that as a passive observer; if we try to register a notifier it will overwrite any existing notifier callback on the device. But in practice, guests using PCI_INTX will only ever use pin A on the Xen platform device, and won't swizzle the INTX routing after they set it up. So this is just fine. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 80 ++++++++++++++++++++++++++++++++------- target/i386/kvm/xen-emu.c | 34 +++++++++++++++++ 2 files changed, 100 insertions(+), 14 deletions(-) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index 6b0bdba65d..f39b751ff9 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -28,6 +28,8 @@ #include "hw/sysbus.h" #include "hw/xen/xen.h" #include "hw/i386/x86.h" +#include "hw/i386/pc.h" +#include "hw/pci/pci.h" #include "hw/irq.h" #include "xen_evtchn.h" @@ -101,6 +103,7 @@ struct XenEvtchnState { uint64_t callback_param; bool evtchn_in_kernel; + uint32_t callback_gsi; QEMUBH *gsi_bh; @@ -217,11 +220,41 @@ static void xen_evtchn_register_types(void) type_init(xen_evtchn_register_types) +static int set_callback_pci_intx(XenEvtchnState *s, uint64_t param) +{ + PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); + uint8_t pin = param & 3; + uint8_t devfn = (param >> 8) & 0xff; + uint16_t bus = (param >> 16) & 0xffff; + uint16_t domain = (param >> 32) & 0xffff; + PCIDevice *pdev; + PCIINTxRoute r; + + if (domain || !pcms) { + return 0; + } + + pdev = pci_find_device(pcms->bus, bus, devfn); + if (!pdev) { + return 0; + } + + r = pci_device_route_intx_to_irq(pdev, pin); + if (r.mode != PCI_INTX_ENABLED) { + return 0; + } + + /* + * Hm, can we be notified of INTX routing changes? Not without + * *owning* the device and being allowed to overwrite its own + * ->intx_routing_notifier, AFAICT. So let's not. + */ + return r.irq; +} + void xen_evtchn_set_callback_level(int level) { XenEvtchnState *s = xen_evtchn_singleton; - uint32_t param; - if (!s) { return; } @@ -260,18 +293,12 @@ void xen_evtchn_set_callback_level(int level) return; } - param = (uint32_t)s->callback_param; - - switch (s->callback_param >> CALLBACK_VIA_TYPE_SHIFT) { - case HVM_PARAM_CALLBACK_TYPE_GSI: - if (param < IOAPIC_NUM_PINS) { - qemu_set_irq(s->gsis[param], level); - if (level) { - /* Ensure the vCPU polls for deassertion */ - kvm_xen_set_callback_asserted(); - } + if (s->callback_gsi && s->callback_gsi < IOAPIC_NUM_PINS) { + qemu_set_irq(s->gsis[s->callback_gsi], level); + if (level) { + /* Ensure the vCPU polls for deassertion */ + kvm_xen_set_callback_asserted(); } - break; } } @@ -283,15 +310,22 @@ int xen_evtchn_set_callback_param(uint64_t param) .u.vector = 0, }; bool in_kernel = false; + uint32_t gsi = 0; + int type = param >> CALLBACK_VIA_TYPE_SHIFT; int ret; if (!s) { return -ENOTSUP; } + /* + * We need the BQL because set_callback_pci_intx() may call into PCI code, + * and because we may need to manipulate the old and new GSI levels. + */ + assert(qemu_mutex_iothread_locked()); qemu_mutex_lock(&s->port_lock); - switch (param >> CALLBACK_VIA_TYPE_SHIFT) { + switch (type) { case HVM_PARAM_CALLBACK_TYPE_VECTOR: { xa.u.vector = (uint8_t)param, @@ -299,10 +333,17 @@ int xen_evtchn_set_callback_param(uint64_t param) if (!ret && kvm_xen_has_cap(EVTCHN_SEND)) { in_kernel = true; } + gsi = 0; break; } + case HVM_PARAM_CALLBACK_TYPE_PCI_INTX: + gsi = set_callback_pci_intx(s, param); + ret = gsi ? 0 : -EINVAL; + break; + case HVM_PARAM_CALLBACK_TYPE_GSI: + gsi = (uint32_t)param; ret = 0; break; @@ -320,6 +361,17 @@ int xen_evtchn_set_callback_param(uint64_t param) } s->callback_param = param; s->evtchn_in_kernel = in_kernel; + + if (gsi != s->callback_gsi) { + struct vcpu_info *vi = kvm_xen_get_vcpu_info_hva(0); + + xen_evtchn_set_callback_level(0); + s->callback_gsi = gsi; + + if (gsi && vi && vi->evtchn_upcall_pending) { + kvm_xen_inject_vcpu_callback_vector(0, type); + } + } } qemu_mutex_unlock(&s->port_lock); diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index b52617df54..9e22c9fa02 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -131,6 +131,38 @@ int kvm_xen_init(KVMState *s, uint32_t hypercall_msr) return ret; } + /* If called a second time, don't repeat the rest of the setup. */ + if (s->xen_caps) { + return 0; + } + + /* + * Event channel delivery via GSI/PCI_INTX needs to poll the vcpu_info + * of vCPU0 to deassert the IRQ when ->evtchn_upcall_pending is cleared. + * + * In the kernel, there's a notifier hook on the PIC/IOAPIC which allows + * such things to be polled at precisely the right time. We *could* do + * it nicely in the kernel: check vcpu_info[0]->evtchn_upcall_pending at + * the moment the IRQ is acked, and see if it should be reasserted. + * + * But the in-kernel irqchip is deprecated, so we're unlikely to add + * that support in the kernel. Insist on using the split irqchip mode + * instead. + * + * This leaves us polling for the level going low in QEMU, which lacks + * the appropriate hooks in its PIC/IOAPIC code. Even VFIO is sending a + * spurious 'ack' to an INTX IRQ every time there's any MMIO access to + * the device (for which it has to unmap the device and trap access, for + * some period after an IRQ!!). In the Xen case, we do it on exit from + * KVM_RUN, if the flag is set to say that the GSI is currently asserted. + * Which is kind of icky, but less so than the VFIO one. I may fix them + * both later... + */ + if (!kvm_kernel_irqchip_split()) { + error_report("kvm: Xen support requires kernel-irqchip=split"); + return -EINVAL; + } + s->xen_caps = xen_caps; return 0; } @@ -684,7 +716,9 @@ static bool handle_set_param(struct kvm_xen_exit *exit, X86CPU *cpu, switch (hp.index) { case HVM_PARAM_CALLBACK_IRQ: + qemu_mutex_lock_iothread(); err = xen_evtchn_set_callback_param(hp.value); + qemu_mutex_unlock_iothread(); xen_set_long_mode(exit->u.hcall.longmode); break; default: From 6f43f2ee49568f067b15c31372bb8116e1cb0182 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 16 Dec 2022 16:27:00 +0000 Subject: [PATCH 167/185] kvm/i386: Add xen-gnttab-max-frames property Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- accel/kvm/kvm-all.c | 1 + include/sysemu/kvm_int.h | 1 + include/sysemu/kvm_xen.h | 1 + target/i386/kvm/kvm.c | 34 ++++++++++++++++++++++++++++++++++ target/i386/kvm/xen-emu.c | 6 ++++++ 5 files changed, 43 insertions(+) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 511d3eb9a0..3d8e400bbf 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -3704,6 +3704,7 @@ static void kvm_accel_instance_init(Object *obj) s->notify_vmexit = NOTIFY_VMEXIT_OPTION_RUN; s->notify_window = 0; s->xen_version = 0; + s->xen_gnttab_max_frames = 64; } /** diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h index 7f945bc763..39ce4d36f6 100644 --- a/include/sysemu/kvm_int.h +++ b/include/sysemu/kvm_int.h @@ -120,6 +120,7 @@ struct KVMState uint32_t notify_window; uint32_t xen_version; uint32_t xen_caps; + uint16_t xen_gnttab_max_frames; }; void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml, diff --git a/include/sysemu/kvm_xen.h b/include/sysemu/kvm_xen.h index 2b20030281..49afa0eb9e 100644 --- a/include/sysemu/kvm_xen.h +++ b/include/sysemu/kvm_xen.h @@ -25,6 +25,7 @@ void *kvm_xen_get_vcpu_info_hva(uint32_t vcpu_id); void kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type); void kvm_xen_set_callback_asserted(void); int kvm_xen_set_vcpu_virq(uint32_t vcpu_id, uint16_t virq, uint16_t port); +uint16_t kvm_xen_get_gnttab_max_frames(void); #define kvm_xen_has_cap(cap) (!!(kvm_xen_get_caps() & \ KVM_XEN_HVM_CONFIG_ ## cap)) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 3c3795506d..a73c49aabb 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5880,6 +5880,33 @@ static void kvm_arch_set_xen_version(Object *obj, Visitor *v, } } +static void kvm_arch_get_xen_gnttab_max_frames(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + uint16_t value = s->xen_gnttab_max_frames; + + visit_type_uint16(v, name, &value, errp); +} + +static void kvm_arch_set_xen_gnttab_max_frames(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + Error *error = NULL; + uint16_t value; + + visit_type_uint16(v, name, &value, &error); + if (error) { + error_propagate(errp, error); + return; + } + + s->xen_gnttab_max_frames = value; +} + void kvm_arch_accel_class_init(ObjectClass *oc) { object_class_property_add_enum(oc, "notify-vmexit", "NotifyVMexitOption", @@ -5905,6 +5932,13 @@ void kvm_arch_accel_class_init(ObjectClass *oc) "Xen version to be emulated " "(in XENVER_version form " "e.g. 0x4000a for 4.10)"); + + object_class_property_add(oc, "xen-gnttab-max-frames", "uint16", + kvm_arch_get_xen_gnttab_max_frames, + kvm_arch_set_xen_gnttab_max_frames, + NULL, NULL); + object_class_property_set_description(oc, "xen-gnttab-max-frames", + "Maximum number of grant table frames"); } void kvm_set_max_apic_id(uint32_t max_apic_id) diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 9e22c9fa02..46be631726 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -1238,6 +1238,12 @@ int kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) return 0; } +uint16_t kvm_xen_get_gnttab_max_frames(void) +{ + KVMState *s = KVM_STATE(current_accel()); + return s->xen_gnttab_max_frames; +} + int kvm_put_xen_state(CPUState *cs) { X86CPU *cpu = X86_CPU(cs); From a28b0fc0345b1ddc12109de807e6d4ce566c1914 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 16 Dec 2022 15:50:26 +0000 Subject: [PATCH 168/185] hw/xen: Add xen_gnttab device for grant table emulation Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/meson.build | 1 + hw/i386/kvm/xen_gnttab.c | 111 ++++++++++++++++++++++++++++++++++++++ hw/i386/kvm/xen_gnttab.h | 18 +++++++ hw/i386/pc.c | 2 + target/i386/kvm/xen-emu.c | 3 ++ 5 files changed, 135 insertions(+) create mode 100644 hw/i386/kvm/xen_gnttab.c create mode 100644 hw/i386/kvm/xen_gnttab.h diff --git a/hw/i386/kvm/meson.build b/hw/i386/kvm/meson.build index 325c901496..067ff525ee 100644 --- a/hw/i386/kvm/meson.build +++ b/hw/i386/kvm/meson.build @@ -7,6 +7,7 @@ i386_kvm_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic.c')) i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files( 'xen_overlay.c', 'xen_evtchn.c', + 'xen_gnttab.c', )) i386_ss.add_all(when: 'CONFIG_KVM', if_true: i386_kvm_ss) diff --git a/hw/i386/kvm/xen_gnttab.c b/hw/i386/kvm/xen_gnttab.c new file mode 100644 index 0000000000..ef8857e50c --- /dev/null +++ b/hw/i386/kvm/xen_gnttab.c @@ -0,0 +1,111 @@ +/* + * QEMU Xen emulation: Grant table support + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/host-utils.h" +#include "qemu/module.h" +#include "qemu/lockable.h" +#include "qemu/main-loop.h" +#include "qapi/error.h" +#include "qom/object.h" +#include "exec/target_page.h" +#include "exec/address-spaces.h" +#include "migration/vmstate.h" + +#include "hw/sysbus.h" +#include "hw/xen/xen.h" +#include "xen_overlay.h" +#include "xen_gnttab.h" + +#include "sysemu/kvm.h" +#include "sysemu/kvm_xen.h" + +#include "hw/xen/interface/memory.h" +#include "hw/xen/interface/grant_table.h" + +#define TYPE_XEN_GNTTAB "xen-gnttab" +OBJECT_DECLARE_SIMPLE_TYPE(XenGnttabState, XEN_GNTTAB) + +#define XEN_PAGE_SHIFT 12 +#define XEN_PAGE_SIZE (1ULL << XEN_PAGE_SHIFT) + +struct XenGnttabState { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + uint32_t nr_frames; + uint32_t max_frames; +}; + +struct XenGnttabState *xen_gnttab_singleton; + +static void xen_gnttab_realize(DeviceState *dev, Error **errp) +{ + XenGnttabState *s = XEN_GNTTAB(dev); + + if (xen_mode != XEN_EMULATE) { + error_setg(errp, "Xen grant table support is for Xen emulation"); + return; + } + s->nr_frames = 0; + s->max_frames = kvm_xen_get_gnttab_max_frames(); +} + +static bool xen_gnttab_is_needed(void *opaque) +{ + return xen_mode == XEN_EMULATE; +} + +static const VMStateDescription xen_gnttab_vmstate = { + .name = "xen_gnttab", + .version_id = 1, + .minimum_version_id = 1, + .needed = xen_gnttab_is_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT32(nr_frames, XenGnttabState), + VMSTATE_END_OF_LIST() + } +}; + +static void xen_gnttab_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = xen_gnttab_realize; + dc->vmsd = &xen_gnttab_vmstate; +} + +static const TypeInfo xen_gnttab_info = { + .name = TYPE_XEN_GNTTAB, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XenGnttabState), + .class_init = xen_gnttab_class_init, +}; + +void xen_gnttab_create(void) +{ + xen_gnttab_singleton = XEN_GNTTAB(sysbus_create_simple(TYPE_XEN_GNTTAB, + -1, NULL)); +} + +static void xen_gnttab_register_types(void) +{ + type_register_static(&xen_gnttab_info); +} + +type_init(xen_gnttab_register_types) + +int xen_gnttab_map_page(uint64_t idx, uint64_t gfn) +{ + return -ENOSYS; +} + diff --git a/hw/i386/kvm/xen_gnttab.h b/hw/i386/kvm/xen_gnttab.h new file mode 100644 index 0000000000..a7caa94c83 --- /dev/null +++ b/hw/i386/kvm/xen_gnttab.h @@ -0,0 +1,18 @@ +/* + * QEMU Xen emulation: Grant table support + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * 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_XEN_GNTTAB_H +#define QEMU_XEN_GNTTAB_H + +void xen_gnttab_create(void); +int xen_gnttab_map_page(uint64_t idx, uint64_t gfn); + +#endif /* QEMU_XEN_GNTTAB_H */ diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 71d3b8bec2..1cf112e542 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -92,6 +92,7 @@ #include "hw/virtio/virtio-mem-pci.h" #include "hw/i386/kvm/xen_overlay.h" #include "hw/i386/kvm/xen_evtchn.h" +#include "hw/i386/kvm/xen_gnttab.h" #include "hw/mem/memory-device.h" #include "sysemu/replay.h" #include "target/i386/cpu.h" @@ -1860,6 +1861,7 @@ int pc_machine_kvm_type(MachineState *machine, const char *kvm_type) if (xen_mode == XEN_EMULATE) { xen_overlay_create(); xen_evtchn_create(); + xen_gnttab_create(); } #endif return 0; diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 46be631726..6f9b7a5cb9 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -25,6 +25,7 @@ #include "hw/i386/apic-msidef.h" #include "hw/i386/kvm/xen_overlay.h" #include "hw/i386/kvm/xen_evtchn.h" +#include "hw/i386/kvm/xen_gnttab.h" #include "hw/xen/interface/version.h" #include "hw/xen/interface/sched.h" @@ -563,6 +564,8 @@ static int add_to_physmap_one(uint32_t space, uint64_t idx, uint64_t gfn) return xen_set_shared_info(gfn); case XENMAPSPACE_grant_table: + return xen_gnttab_map_page(idx, gfn); + case XENMAPSPACE_gmfn: case XENMAPSPACE_gmfn_range: return -ENOTSUP; From e33cb789afd3aedbe181b78fd75d04a5c48f7cba Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 16 Dec 2022 18:33:49 +0000 Subject: [PATCH 169/185] hw/xen: Support mapping grant frames Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_gnttab.c | 73 ++++++++++++++++++++++++++++++++++++++- hw/i386/kvm/xen_overlay.c | 2 +- hw/i386/kvm/xen_overlay.h | 2 ++ 3 files changed, 75 insertions(+), 2 deletions(-) diff --git a/hw/i386/kvm/xen_gnttab.c b/hw/i386/kvm/xen_gnttab.c index ef8857e50c..72e87aea6a 100644 --- a/hw/i386/kvm/xen_gnttab.c +++ b/hw/i386/kvm/xen_gnttab.c @@ -37,13 +37,26 @@ OBJECT_DECLARE_SIMPLE_TYPE(XenGnttabState, XEN_GNTTAB) #define XEN_PAGE_SHIFT 12 #define XEN_PAGE_SIZE (1ULL << XEN_PAGE_SHIFT) +#define ENTRIES_PER_FRAME_V1 (XEN_PAGE_SIZE / sizeof(grant_entry_v1_t)) + struct XenGnttabState { /*< private >*/ SysBusDevice busdev; /*< public >*/ + QemuMutex gnt_lock; + uint32_t nr_frames; uint32_t max_frames; + + union { + grant_entry_v1_t *v1; + /* Theoretically, v2 support could be added here. */ + } entries; + + MemoryRegion gnt_frames; + MemoryRegion *gnt_aliases; + uint64_t *gnt_frame_gpas; }; struct XenGnttabState *xen_gnttab_singleton; @@ -51,6 +64,7 @@ struct XenGnttabState *xen_gnttab_singleton; static void xen_gnttab_realize(DeviceState *dev, Error **errp) { XenGnttabState *s = XEN_GNTTAB(dev); + int i; if (xen_mode != XEN_EMULATE) { error_setg(errp, "Xen grant table support is for Xen emulation"); @@ -58,6 +72,38 @@ static void xen_gnttab_realize(DeviceState *dev, Error **errp) } s->nr_frames = 0; s->max_frames = kvm_xen_get_gnttab_max_frames(); + memory_region_init_ram(&s->gnt_frames, OBJECT(dev), "xen:grant_table", + XEN_PAGE_SIZE * s->max_frames, &error_abort); + memory_region_set_enabled(&s->gnt_frames, true); + s->entries.v1 = memory_region_get_ram_ptr(&s->gnt_frames); + memset(s->entries.v1, 0, XEN_PAGE_SIZE * s->max_frames); + + /* Create individual page-sizes aliases for overlays */ + s->gnt_aliases = (void *)g_new0(MemoryRegion, s->max_frames); + s->gnt_frame_gpas = (void *)g_new(uint64_t, s->max_frames); + for (i = 0; i < s->max_frames; i++) { + memory_region_init_alias(&s->gnt_aliases[i], OBJECT(dev), + NULL, &s->gnt_frames, + i * XEN_PAGE_SIZE, XEN_PAGE_SIZE); + s->gnt_frame_gpas[i] = INVALID_GPA; + } + + qemu_mutex_init(&s->gnt_lock); + + xen_gnttab_singleton = s; +} + +static int xen_gnttab_post_load(void *opaque, int version_id) +{ + XenGnttabState *s = XEN_GNTTAB(opaque); + uint32_t i; + + for (i = 0; i < s->nr_frames; i++) { + if (s->gnt_frame_gpas[i] != INVALID_GPA) { + xen_overlay_do_map_page(&s->gnt_aliases[i], s->gnt_frame_gpas[i]); + } + } + return 0; } static bool xen_gnttab_is_needed(void *opaque) @@ -70,8 +116,11 @@ static const VMStateDescription xen_gnttab_vmstate = { .version_id = 1, .minimum_version_id = 1, .needed = xen_gnttab_is_needed, + .post_load = xen_gnttab_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32(nr_frames, XenGnttabState), + VMSTATE_VARRAY_UINT32(gnt_frame_gpas, XenGnttabState, nr_frames, 0, + vmstate_info_uint64, uint64_t), VMSTATE_END_OF_LIST() } }; @@ -106,6 +155,28 @@ type_init(xen_gnttab_register_types) int xen_gnttab_map_page(uint64_t idx, uint64_t gfn) { - return -ENOSYS; + XenGnttabState *s = xen_gnttab_singleton; + uint64_t gpa = gfn << XEN_PAGE_SHIFT; + + if (!s) { + return -ENOTSUP; + } + + if (idx >= s->max_frames) { + return -EINVAL; + } + + QEMU_IOTHREAD_LOCK_GUARD(); + QEMU_LOCK_GUARD(&s->gnt_lock); + + xen_overlay_do_map_page(&s->gnt_aliases[idx], gpa); + + s->gnt_frame_gpas[idx] = gpa; + + if (s->nr_frames <= idx) { + s->nr_frames = idx + 1; + } + + return 0; } diff --git a/hw/i386/kvm/xen_overlay.c b/hw/i386/kvm/xen_overlay.c index 8685d87959..39fda1b72c 100644 --- a/hw/i386/kvm/xen_overlay.c +++ b/hw/i386/kvm/xen_overlay.c @@ -49,7 +49,7 @@ struct XenOverlayState { struct XenOverlayState *xen_overlay_singleton; -static void xen_overlay_do_map_page(MemoryRegion *page, uint64_t gpa) +void xen_overlay_do_map_page(MemoryRegion *page, uint64_t gpa) { /* * Xen allows guests to map the same page as many times as it likes diff --git a/hw/i386/kvm/xen_overlay.h b/hw/i386/kvm/xen_overlay.h index 5c46a0b036..75ecb6b359 100644 --- a/hw/i386/kvm/xen_overlay.h +++ b/hw/i386/kvm/xen_overlay.h @@ -21,4 +21,6 @@ int xen_sync_long_mode(void); int xen_set_long_mode(bool long_mode); bool xen_is_long_mode(void); +void xen_overlay_do_map_page(MemoryRegion *page, uint64_t gpa); + #endif /* QEMU_XEN_OVERLAY_H */ From 28b7ae94a21d37f38f7b68a9f40fa521144898a6 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 16 Dec 2022 23:40:57 +0000 Subject: [PATCH 170/185] i386/xen: Implement HYPERVISOR_grant_table_op and GNTTABOP_[gs]et_verson Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_gnttab.c | 31 ++++++++++++++++++++ hw/i386/kvm/xen_gnttab.h | 5 ++++ target/i386/kvm/xen-emu.c | 60 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/hw/i386/kvm/xen_gnttab.c b/hw/i386/kvm/xen_gnttab.c index 72e87aea6a..b54a94e2bd 100644 --- a/hw/i386/kvm/xen_gnttab.c +++ b/hw/i386/kvm/xen_gnttab.c @@ -180,3 +180,34 @@ int xen_gnttab_map_page(uint64_t idx, uint64_t gfn) return 0; } +int xen_gnttab_set_version_op(struct gnttab_set_version *set) +{ + int ret; + + switch (set->version) { + case 1: + ret = 0; + break; + + case 2: + /* Behave as before set_version was introduced. */ + ret = -ENOSYS; + break; + + default: + ret = -EINVAL; + } + + set->version = 1; + return ret; +} + +int xen_gnttab_get_version_op(struct gnttab_get_version *get) +{ + if (get->dom != DOMID_SELF && get->dom != xen_domid) { + return -ESRCH; + } + + get->version = 1; + return 0; +} diff --git a/hw/i386/kvm/xen_gnttab.h b/hw/i386/kvm/xen_gnttab.h index a7caa94c83..79579677ba 100644 --- a/hw/i386/kvm/xen_gnttab.h +++ b/hw/i386/kvm/xen_gnttab.h @@ -15,4 +15,9 @@ void xen_gnttab_create(void); int xen_gnttab_map_page(uint64_t idx, uint64_t gfn); +struct gnttab_set_version; +struct gnttab_get_version; +int xen_gnttab_set_version_op(struct gnttab_set_version *set); +int xen_gnttab_get_version_op(struct gnttab_get_version *get); + #endif /* QEMU_XEN_GNTTAB_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 6f9b7a5cb9..d49b6117f1 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -34,6 +34,7 @@ #include "hw/xen/interface/hvm/params.h" #include "hw/xen/interface/vcpu.h" #include "hw/xen/interface/event_channel.h" +#include "hw/xen/interface/grant_table.h" #include "xen-compat.h" @@ -1169,6 +1170,61 @@ static bool kvm_xen_hcall_sched_op(struct kvm_xen_exit *exit, X86CPU *cpu, return true; } +static bool kvm_xen_hcall_gnttab_op(struct kvm_xen_exit *exit, X86CPU *cpu, + int cmd, uint64_t arg, int count) +{ + CPUState *cs = CPU(cpu); + int err; + + switch (cmd) { + case GNTTABOP_set_version: { + struct gnttab_set_version set; + + qemu_build_assert(sizeof(set) == 4); + if (kvm_copy_from_gva(cs, arg, &set, sizeof(set))) { + err = -EFAULT; + break; + } + + err = xen_gnttab_set_version_op(&set); + if (!err && kvm_copy_to_gva(cs, arg, &set, sizeof(set))) { + err = -EFAULT; + } + break; + } + case GNTTABOP_get_version: { + struct gnttab_get_version get; + + qemu_build_assert(sizeof(get) == 8); + if (kvm_copy_from_gva(cs, arg, &get, sizeof(get))) { + err = -EFAULT; + break; + } + + err = xen_gnttab_get_version_op(&get); + if (!err && kvm_copy_to_gva(cs, arg, &get, sizeof(get))) { + err = -EFAULT; + } + break; + } + case GNTTABOP_query_size: + case GNTTABOP_setup_table: + case GNTTABOP_copy: + case GNTTABOP_map_grant_ref: + case GNTTABOP_unmap_grant_ref: + case GNTTABOP_swap_grant_ref: + return false; + + default: + /* Xen explicitly returns -ENOSYS to HVM guests for all others */ + err = -ENOSYS; + break; + } + + exit->u.hcall.result = err; + return true; +} + static bool do_kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) { uint16_t code = exit->u.hcall.input; @@ -1179,6 +1235,10 @@ static bool do_kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) } switch (code) { + case __HYPERVISOR_grant_table_op: + return kvm_xen_hcall_gnttab_op(exit, cpu, exit->u.hcall.params[0], + exit->u.hcall.params[1], + exit->u.hcall.params[2]); case __HYPERVISOR_sched_op: return kvm_xen_hcall_sched_op(exit, cpu, exit->u.hcall.params[0], exit->u.hcall.params[1]); From b46f9745b1b5c8dd6ea1bd1361531f966c404f8c Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 16 Dec 2022 23:49:48 +0000 Subject: [PATCH 171/185] hw/xen: Implement GNTTABOP_query_size Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_gnttab.c | 19 +++++++++++++++++++ hw/i386/kvm/xen_gnttab.h | 2 ++ target/i386/kvm/xen-emu.c | 16 +++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/hw/i386/kvm/xen_gnttab.c b/hw/i386/kvm/xen_gnttab.c index b54a94e2bd..1e691ded32 100644 --- a/hw/i386/kvm/xen_gnttab.c +++ b/hw/i386/kvm/xen_gnttab.c @@ -211,3 +211,22 @@ int xen_gnttab_get_version_op(struct gnttab_get_version *get) get->version = 1; return 0; } + +int xen_gnttab_query_size_op(struct gnttab_query_size *size) +{ + XenGnttabState *s = xen_gnttab_singleton; + + if (!s) { + return -ENOTSUP; + } + + if (size->dom != DOMID_SELF && size->dom != xen_domid) { + size->status = GNTST_bad_domain; + return 0; + } + + size->status = GNTST_okay; + size->nr_frames = s->nr_frames; + size->max_nr_frames = s->max_frames; + return 0; +} diff --git a/hw/i386/kvm/xen_gnttab.h b/hw/i386/kvm/xen_gnttab.h index 79579677ba..3bdbe96191 100644 --- a/hw/i386/kvm/xen_gnttab.h +++ b/hw/i386/kvm/xen_gnttab.h @@ -17,7 +17,9 @@ int xen_gnttab_map_page(uint64_t idx, uint64_t gfn); struct gnttab_set_version; struct gnttab_get_version; +struct gnttab_query_size; int xen_gnttab_set_version_op(struct gnttab_set_version *set); int xen_gnttab_get_version_op(struct gnttab_get_version *get); +int xen_gnttab_query_size_op(struct gnttab_query_size *size); #endif /* QEMU_XEN_GNTTAB_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index d49b6117f1..3b46cab1da 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -1207,7 +1207,21 @@ static bool kvm_xen_hcall_gnttab_op(struct kvm_xen_exit *exit, X86CPU *cpu, } break; } - case GNTTABOP_query_size: + case GNTTABOP_query_size: { + struct gnttab_query_size size; + + qemu_build_assert(sizeof(size) == 16); + if (kvm_copy_from_gva(cs, arg, &size, sizeof(size))) { + err = -EFAULT; + break; + } + + err = xen_gnttab_query_size_op(&size); + if (!err && kvm_copy_to_gva(cs, arg, &size, sizeof(size))) { + err = -EFAULT; + } + break; + } case GNTTABOP_setup_table: case GNTTABOP_copy: case GNTTABOP_map_grant_ref: From b746a77926f6e84bdb35a38a9ee956ac12693757 Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Mon, 17 Sep 2018 07:04:54 -0400 Subject: [PATCH 172/185] i386/xen: handle PV timer hypercalls Introduce support for one shot and periodic mode of Xen PV timers, whereby timer interrupts come through a special virq event channel with deadlines being set through: 1) set_timer_op hypercall (only oneshot) 2) vcpu_op hypercall for {set,stop}_{singleshot,periodic}_timer hypercalls Signed-off-by: Joao Martins Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 31 +++++ hw/i386/kvm/xen_evtchn.h | 2 + target/i386/cpu.h | 5 + target/i386/kvm/xen-emu.c | 271 +++++++++++++++++++++++++++++++++++++- target/i386/machine.c | 1 + 5 files changed, 308 insertions(+), 2 deletions(-) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index f39b751ff9..71ed480189 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -1222,6 +1222,37 @@ int xen_evtchn_send_op(struct evtchn_send *send) return ret; } +int xen_evtchn_set_port(uint16_t port) +{ + XenEvtchnState *s = xen_evtchn_singleton; + XenEvtchnPort *p; + int ret = -EINVAL; + + if (!s) { + return -ENOTSUP; + } + + if (!valid_port(port)) { + return -EINVAL; + } + + qemu_mutex_lock(&s->port_lock); + + p = &s->port_table[port]; + + /* QEMU has no business sending to anything but these */ + if (p->type == EVTCHNSTAT_virq || + (p->type == EVTCHNSTAT_interdomain && + (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU))) { + set_port_pending(s, port); + ret = 0; + } + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} + EvtchnInfoList *qmp_xen_event_list(Error **errp) { XenEvtchnState *s = xen_evtchn_singleton; diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index b03c3108bc..24611478b8 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -20,6 +20,8 @@ int xen_evtchn_set_callback_param(uint64_t param); void xen_evtchn_connect_gsis(qemu_irq *system_gsis); void xen_evtchn_set_callback_level(int level); +int xen_evtchn_set_port(uint16_t port); + struct evtchn_status; struct evtchn_close; struct evtchn_unmask; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 7227a8ec08..d243e290d3 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -26,6 +26,7 @@ #include "exec/cpu-defs.h" #include "qapi/qapi-types-common.h" #include "qemu/cpu-float.h" +#include "qemu/timer.h" #define XEN_NR_VIRQS 24 @@ -1811,6 +1812,10 @@ typedef struct CPUArchState { bool xen_callback_asserted; uint16_t xen_virq[XEN_NR_VIRQS]; uint64_t xen_singleshot_timer_ns; + QEMUTimer *xen_singleshot_timer; + uint64_t xen_periodic_timer_period; + QEMUTimer *xen_periodic_timer; + QemuMutex xen_timers_lock; #endif #if defined(CONFIG_HVF) HVFX86LazyFlags hvf_lflags; diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 3b46cab1da..c210ff9d91 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -38,6 +38,9 @@ #include "xen-compat.h" +static void xen_vcpu_singleshot_timer_event(void *opaque); +static void xen_vcpu_periodic_timer_event(void *opaque); + #ifdef TARGET_X86_64 #define hypercall_compat32(longmode) (!(longmode)) #else @@ -201,6 +204,23 @@ int kvm_xen_init_vcpu(CPUState *cs) env->xen_vcpu_time_info_gpa = INVALID_GPA; env->xen_vcpu_runstate_gpa = INVALID_GPA; + qemu_mutex_init(&env->xen_timers_lock); + env->xen_singleshot_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + xen_vcpu_singleshot_timer_event, + cpu); + if (!env->xen_singleshot_timer) { + return -ENOMEM; + } + env->xen_singleshot_timer->opaque = cs; + + env->xen_periodic_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + xen_vcpu_periodic_timer_event, + cpu); + if (!env->xen_periodic_timer) { + return -ENOMEM; + } + env->xen_periodic_timer->opaque = cs; + return 0; } @@ -232,7 +252,8 @@ static bool kvm_xen_hcall_xen_version(struct kvm_xen_exit *exit, X86CPU *cpu, 1 << XENFEAT_writable_descriptor_tables | 1 << XENFEAT_auto_translated_physmap | 1 << XENFEAT_supervisor_mode_kernel | - 1 << XENFEAT_hvm_callback_vector; + 1 << XENFEAT_hvm_callback_vector | + 1 << XENFEAT_hvm_safe_pvclock; } err = kvm_copy_to_gva(CPU(cpu), arg, &fi, sizeof(fi)); @@ -878,13 +899,208 @@ static int vcpuop_register_runstate_info(CPUState *cs, CPUState *target, return 0; } +static uint64_t kvm_get_current_ns(void) +{ + struct kvm_clock_data data; + int ret; + + ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data); + if (ret < 0) { + fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret)); + abort(); + } + + return data.clock; +} + +static void xen_vcpu_singleshot_timer_event(void *opaque) +{ + CPUState *cpu = opaque; + CPUX86State *env = &X86_CPU(cpu)->env; + uint16_t port = env->xen_virq[VIRQ_TIMER]; + + if (likely(port)) { + xen_evtchn_set_port(port); + } + + qemu_mutex_lock(&env->xen_timers_lock); + env->xen_singleshot_timer_ns = 0; + qemu_mutex_unlock(&env->xen_timers_lock); +} + +static void xen_vcpu_periodic_timer_event(void *opaque) +{ + CPUState *cpu = opaque; + CPUX86State *env = &X86_CPU(cpu)->env; + uint16_t port = env->xen_virq[VIRQ_TIMER]; + int64_t qemu_now; + + if (likely(port)) { + xen_evtchn_set_port(port); + } + + qemu_mutex_lock(&env->xen_timers_lock); + + qemu_now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + timer_mod_ns(env->xen_periodic_timer, + qemu_now + env->xen_periodic_timer_period); + + qemu_mutex_unlock(&env->xen_timers_lock); +} + +static int do_set_periodic_timer(CPUState *target, uint64_t period_ns) +{ + CPUX86State *tenv = &X86_CPU(target)->env; + int64_t qemu_now; + + timer_del(tenv->xen_periodic_timer); + + qemu_mutex_lock(&tenv->xen_timers_lock); + + qemu_now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + timer_mod_ns(tenv->xen_periodic_timer, qemu_now + period_ns); + tenv->xen_periodic_timer_period = period_ns; + + qemu_mutex_unlock(&tenv->xen_timers_lock); + return 0; +} + +#define MILLISECS(_ms) ((int64_t)((_ms) * 1000000ULL)) +#define MICROSECS(_us) ((int64_t)((_us) * 1000ULL)) +#define STIME_MAX ((time_t)((int64_t)~0ull >> 1)) +/* Chosen so (NOW() + delta) wont overflow without an uptime of 200 years */ +#define STIME_DELTA_MAX ((int64_t)((uint64_t)~0ull >> 2)) + +static int vcpuop_set_periodic_timer(CPUState *cs, CPUState *target, + uint64_t arg) +{ + struct vcpu_set_periodic_timer spt; + + qemu_build_assert(sizeof(spt) == 8); + if (kvm_copy_from_gva(cs, arg, &spt, sizeof(spt))) { + return -EFAULT; + } + + if (spt.period_ns < MILLISECS(1) || spt.period_ns > STIME_DELTA_MAX) { + return -EINVAL; + } + + return do_set_periodic_timer(target, spt.period_ns); +} + +static int vcpuop_stop_periodic_timer(CPUState *target) +{ + CPUX86State *tenv = &X86_CPU(target)->env; + + qemu_mutex_lock(&tenv->xen_timers_lock); + + timer_del(tenv->xen_periodic_timer); + tenv->xen_periodic_timer_period = 0; + + qemu_mutex_unlock(&tenv->xen_timers_lock); + return 0; +} + +static int do_set_singleshot_timer(CPUState *cs, uint64_t timeout_abs, + bool future, bool linux_wa) +{ + CPUX86State *env = &X86_CPU(cs)->env; + int64_t now = kvm_get_current_ns(); + int64_t qemu_now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t delta = timeout_abs - now; + + if (future && timeout_abs < now) { + return -ETIME; + } + + if (linux_wa && unlikely((int64_t)timeout_abs < 0 || + (delta > 0 && (uint32_t)(delta >> 50) != 0))) { + /* + * Xen has a 'Linux workaround' in do_set_timer_op() which checks + * for negative absolute timeout values (caused by integer + * overflow), and for values about 13 days in the future (2^50ns) + * which would be caused by jiffies overflow. For those cases, it + * sets the timeout 100ms in the future (not *too* soon, since if + * a guest really did set a long timeout on purpose we don't want + * to keep churning CPU time by waking it up). + */ + delta = (100 * SCALE_MS); + timeout_abs = now + delta; + } + + qemu_mutex_lock(&env->xen_timers_lock); + + timer_mod_ns(env->xen_singleshot_timer, qemu_now + delta); + env->xen_singleshot_timer_ns = now + delta; + + qemu_mutex_unlock(&env->xen_timers_lock); + return 0; +} + +static int vcpuop_set_singleshot_timer(CPUState *cs, uint64_t arg) +{ + struct vcpu_set_singleshot_timer sst = { 0 }; + + /* + * The struct is a uint64_t followed by a uint32_t. On 32-bit that + * makes it 12 bytes. On 64-bit it gets padded to 16. The parts + * that get used are identical, and there's four bytes of padding + * unused at the end. For true Xen compatibility we should attempt + * to copy the full 16 bytes from 64-bit guests, and return -EFAULT + * if we can't get the padding too. But that's daft. Just copy what + * we need. + */ + qemu_build_assert(offsetof(struct vcpu_set_singleshot_timer, flags) == 8); + qemu_build_assert(sizeof(sst) >= 12); + + if (kvm_copy_from_gva(cs, arg, &sst, 12)) { + return -EFAULT; + } + + return do_set_singleshot_timer(cs, sst.timeout_abs_ns, + !!(sst.flags & VCPU_SSHOTTMR_future), + false); +} + +static int vcpuop_stop_singleshot_timer(CPUState *cs) +{ + CPUX86State *env = &X86_CPU(cs)->env; + + qemu_mutex_lock(&env->xen_timers_lock); + + timer_del(env->xen_singleshot_timer); + env->xen_singleshot_timer_ns = 0; + + qemu_mutex_unlock(&env->xen_timers_lock); + return 0; +} + +static bool kvm_xen_hcall_set_timer_op(struct kvm_xen_exit *exit, X86CPU *cpu, + uint64_t timeout) +{ + int err; + + if (unlikely(timeout == 0)) { + err = vcpuop_stop_singleshot_timer(CPU(cpu)); + } else { + err = do_set_singleshot_timer(CPU(cpu), timeout, false, true); + } + exit->u.hcall.result = err; + return true; +} + static bool kvm_xen_hcall_vcpu_op(struct kvm_xen_exit *exit, X86CPU *cpu, int cmd, int vcpu_id, uint64_t arg) { - CPUState *dest = qemu_get_cpu(vcpu_id); CPUState *cs = CPU(cpu); + CPUState *dest = cs->cpu_index == vcpu_id ? cs : qemu_get_cpu(vcpu_id); int err; + if (!dest) { + err = -ENOENT; + goto out; + } + switch (cmd) { case VCPUOP_register_runstate_memory_area: err = vcpuop_register_runstate_info(cs, dest, arg); @@ -895,11 +1111,34 @@ static bool kvm_xen_hcall_vcpu_op(struct kvm_xen_exit *exit, X86CPU *cpu, case VCPUOP_register_vcpu_info: err = vcpuop_register_vcpu_info(cs, dest, arg); break; + case VCPUOP_set_singleshot_timer: { + if (cs->cpu_index == vcpu_id) { + err = vcpuop_set_singleshot_timer(dest, arg); + } else { + err = -EINVAL; + } + break; + } + case VCPUOP_stop_singleshot_timer: + if (cs->cpu_index == vcpu_id) { + err = vcpuop_stop_singleshot_timer(dest); + } else { + err = -EINVAL; + } + break; + case VCPUOP_set_periodic_timer: { + err = vcpuop_set_periodic_timer(cs, dest, arg); + break; + } + case VCPUOP_stop_periodic_timer: + err = vcpuop_stop_periodic_timer(dest); + break; default: return false; } + out: exit->u.hcall.result = err; return true; } @@ -1249,6 +1488,16 @@ static bool do_kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) } switch (code) { + case __HYPERVISOR_set_timer_op: + if (exit->u.hcall.longmode) { + return kvm_xen_hcall_set_timer_op(exit, cpu, + exit->u.hcall.params[0]); + } else { + /* In 32-bit mode, the 64-bit timer value is in two args. */ + uint64_t val = ((uint64_t)exit->u.hcall.params[1]) << 32 | + (uint32_t)exit->u.hcall.params[0]; + return kvm_xen_hcall_set_timer_op(exit, cpu, val); + } case __HYPERVISOR_grant_table_op: return kvm_xen_hcall_gnttab_op(exit, cpu, exit->u.hcall.params[0], exit->u.hcall.params[1], @@ -1358,7 +1607,25 @@ int kvm_put_xen_state(CPUState *cs) } } + if (env->xen_periodic_timer_period) { + ret = do_set_periodic_timer(cs, env->xen_periodic_timer_period); + if (ret < 0) { + return ret; + } + } + if (!kvm_xen_has_cap(EVTCHN_SEND)) { + /* + * If the kernel has EVTCHN_SEND support then it handles timers too, + * so the timer will be restored by kvm_xen_set_vcpu_timer() below. + */ + if (env->xen_singleshot_timer_ns) { + ret = do_set_singleshot_timer(cs, env->xen_singleshot_timer_ns, + false, false); + if (ret < 0) { + return ret; + } + } return 0; } diff --git a/target/i386/machine.c b/target/i386/machine.c index 603a1077e3..c7ac8084b2 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -1277,6 +1277,7 @@ static const VMStateDescription vmstate_xen_vcpu = { VMSTATE_UINT8(env.xen_vcpu_callback_vector, X86CPU), VMSTATE_UINT16_ARRAY(env.xen_virq, X86CPU, XEN_NR_VIRQS), VMSTATE_UINT64(env.xen_singleshot_timer_ns, X86CPU), + VMSTATE_UINT64(env.xen_periodic_timer_period, X86CPU), VMSTATE_END_OF_LIST() } }; From 8b57d5c523f8f1a650188ffa58939e1c5aaa6254 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 27 Dec 2022 19:02:23 +0000 Subject: [PATCH 173/185] i386/xen: Reserve Xen special pages for console, xenstore rings Xen has eight frames at 0xfeff8000 for this; we only really need two for now and KVM puts the identity map at 0xfeffc000, so limit ourselves to four. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- include/sysemu/kvm_xen.h | 9 +++++++++ target/i386/kvm/xen-emu.c | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/include/sysemu/kvm_xen.h b/include/sysemu/kvm_xen.h index 49afa0eb9e..b2aafaf7ab 100644 --- a/include/sysemu/kvm_xen.h +++ b/include/sysemu/kvm_xen.h @@ -30,4 +30,13 @@ uint16_t kvm_xen_get_gnttab_max_frames(void); #define kvm_xen_has_cap(cap) (!!(kvm_xen_get_caps() & \ KVM_XEN_HVM_CONFIG_ ## cap)) +#define XEN_SPECIAL_AREA_ADDR 0xfeff8000UL +#define XEN_SPECIAL_AREA_SIZE 0x4000UL + +#define XEN_SPECIALPAGE_CONSOLE 0 +#define XEN_SPECIALPAGE_XENSTORE 1 + +#define XEN_SPECIAL_PFN(x) ((XEN_SPECIAL_AREA_ADDR >> TARGET_PAGE_BITS) + \ + XEN_SPECIALPAGE_##x) + #endif /* QEMU_SYSEMU_KVM_XEN_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index c210ff9d91..75bcf7b630 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -23,6 +23,7 @@ #include "hw/pci/msi.h" #include "hw/i386/apic-msidef.h" +#include "hw/i386/e820_memory_layout.h" #include "hw/i386/kvm/xen_overlay.h" #include "hw/i386/kvm/xen_evtchn.h" #include "hw/i386/kvm/xen_gnttab.h" @@ -169,6 +170,15 @@ int kvm_xen_init(KVMState *s, uint32_t hypercall_msr) } s->xen_caps = xen_caps; + + /* Tell fw_cfg to notify the BIOS to reserve the range. */ + ret = e820_add_entry(XEN_SPECIAL_AREA_ADDR, XEN_SPECIAL_AREA_SIZE, + E820_RESERVED); + if (ret < 0) { + fprintf(stderr, "e820_add_entry() table is full\n"); + return ret; + } + return 0; } From c6623cc3e785b806c6175c3e85a0ee4d6db4f7d4 Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Fri, 28 Sep 2018 13:17:47 -0400 Subject: [PATCH 174/185] i386/xen: handle HVMOP_get_param Which is used to fetch xenstore PFN and port to be used by the guest. This is preallocated by the toolstack when guest will just read those and use it straight away. Signed-off-by: Joao Martins Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- target/i386/kvm/xen-emu.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 75bcf7b630..d2c88ef0d9 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -765,6 +765,42 @@ out: return true; } +static bool handle_get_param(struct kvm_xen_exit *exit, X86CPU *cpu, + uint64_t arg) +{ + CPUState *cs = CPU(cpu); + struct xen_hvm_param hp; + int err = 0; + + /* No need for 32/64 compat handling */ + qemu_build_assert(sizeof(hp) == 16); + + if (kvm_copy_from_gva(cs, arg, &hp, sizeof(hp))) { + err = -EFAULT; + goto out; + } + + if (hp.domid != DOMID_SELF && hp.domid != xen_domid) { + err = -ESRCH; + goto out; + } + + switch (hp.index) { + case HVM_PARAM_STORE_PFN: + hp.value = XEN_SPECIAL_PFN(XENSTORE); + break; + default: + return false; + } + + if (kvm_copy_to_gva(cs, arg, &hp, sizeof(hp))) { + err = -EFAULT; + } +out: + exit->u.hcall.result = err; + return true; +} + static int kvm_xen_hcall_evtchn_upcall_vector(struct kvm_xen_exit *exit, X86CPU *cpu, uint64_t arg) { @@ -809,6 +845,9 @@ static bool kvm_xen_hcall_hvm_op(struct kvm_xen_exit *exit, X86CPU *cpu, case HVMOP_set_param: return handle_set_param(exit, cpu, arg); + case HVMOP_get_param: + return handle_get_param(exit, cpu, arg); + default: return false; } From 794fba23a53ac714589f84f868202d0cfcb41cd2 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 27 Dec 2022 21:20:07 +0000 Subject: [PATCH 175/185] hw/xen: Add backend implementation of interdomain event channel support The provides the QEMU side of interdomain event channels, allowing events to be sent to/from the guest. The API mirrors libxenevtchn, and in time both this and the real Xen one will be available through ops structures so that the PV backend drivers can use the correct one as appropriate. For now, this implementation can be used directly by our XenStore which will be for emulated mode only. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 340 ++++++++++++++++++++++++++++++++++++++- hw/i386/kvm/xen_evtchn.h | 19 +++ 2 files changed, 352 insertions(+), 7 deletions(-) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index 71ed480189..601fbeee72 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -38,6 +38,7 @@ #include "sysemu/kvm.h" #include "sysemu/kvm_xen.h" #include +#include #include "hw/xen/interface/memory.h" #include "hw/xen/interface/hvm/params.h" @@ -88,6 +89,13 @@ struct compat_shared_info { #define COMPAT_EVTCHN_2L_NR_CHANNELS 1024 +/* Local private implementation of struct xenevtchn_handle */ +struct xenevtchn_handle { + evtchn_port_t be_port; + evtchn_port_t guest_port; /* Or zero for unbound */ + int fd; +}; + /* * For unbound/interdomain ports there are only two possible remote * domains; self and QEMU. Use a single high bit in type_val for that, @@ -111,6 +119,8 @@ struct XenEvtchnState { uint32_t nr_ports; XenEvtchnPort port_table[EVTCHN_2L_NR_CHANNELS]; qemu_irq gsis[IOAPIC_NUM_PINS]; + + struct xenevtchn_handle *be_handles[EVTCHN_2L_NR_CHANNELS]; }; struct XenEvtchnState *xen_evtchn_singleton; @@ -118,6 +128,18 @@ struct XenEvtchnState *xen_evtchn_singleton; /* Top bits of callback_param are the type (HVM_PARAM_CALLBACK_TYPE_xxx) */ #define CALLBACK_VIA_TYPE_SHIFT 56 +static void unbind_backend_ports(XenEvtchnState *s); + +static int xen_evtchn_pre_load(void *opaque) +{ + XenEvtchnState *s = opaque; + + /* Unbind all the backend-side ports; they need to rebind */ + unbind_backend_ports(s); + + return 0; +} + static int xen_evtchn_post_load(void *opaque, int version_id) { XenEvtchnState *s = opaque; @@ -151,6 +173,7 @@ static const VMStateDescription xen_evtchn_vmstate = { .version_id = 1, .minimum_version_id = 1, .needed = xen_evtchn_is_needed, + .pre_load = xen_evtchn_pre_load, .post_load = xen_evtchn_post_load, .fields = (VMStateField[]) { VMSTATE_UINT64(callback_param, XenEvtchnState), @@ -423,6 +446,20 @@ static int assign_kernel_port(uint16_t type, evtchn_port_t port, return kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &ha); } +static int assign_kernel_eventfd(uint16_t type, evtchn_port_t port, int fd) +{ + struct kvm_xen_hvm_attr ha; + + ha.type = KVM_XEN_ATTR_TYPE_EVTCHN; + ha.u.evtchn.send_port = port; + ha.u.evtchn.type = type; + ha.u.evtchn.flags = 0; + ha.u.evtchn.deliver.eventfd.port = 0; + ha.u.evtchn.deliver.eventfd.fd = fd; + + return kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &ha); +} + static bool valid_port(evtchn_port_t port) { if (!port) { @@ -441,6 +478,32 @@ static bool valid_vcpu(uint32_t vcpu) return !!qemu_get_cpu(vcpu); } +static void unbind_backend_ports(XenEvtchnState *s) +{ + XenEvtchnPort *p; + int i; + + for (i = 1; i < s->nr_ports; i++) { + p = &s->port_table[i]; + if (p->type == EVTCHNSTAT_interdomain && + (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU)) { + evtchn_port_t be_port = p->type_val & PORT_INFO_TYPEVAL_REMOTE_PORT_MASK; + + if (s->be_handles[be_port]) { + /* This part will be overwritten on the load anyway. */ + p->type = EVTCHNSTAT_unbound; + p->type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU; + + /* Leave the backend port open and unbound too. */ + if (kvm_xen_has_cap(EVTCHN_SEND)) { + deassign_kernel_port(i); + } + s->be_handles[be_port]->guest_port = 0; + } + } + } +} + int xen_evtchn_status_op(struct evtchn_status *status) { XenEvtchnState *s = xen_evtchn_singleton; @@ -878,7 +941,14 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port) case EVTCHNSTAT_interdomain: if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) { - /* Not yet implemented. This can't happen! */ + uint16_t be_port = p->type_val & ~PORT_INFO_TYPEVAL_REMOTE_QEMU; + struct xenevtchn_handle *xc = s->be_handles[be_port]; + if (xc) { + if (kvm_xen_has_cap(EVTCHN_SEND)) { + deassign_kernel_port(port); + } + xc->guest_port = 0; + } } else { /* Loopback interdomain */ XenEvtchnPort *rp = &s->port_table[p->type_val]; @@ -1110,8 +1180,27 @@ int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain) } if (interdomain->remote_dom == DOMID_QEMU) { - /* We haven't hooked up QEMU's PV drivers to this yet */ - ret = -ENOSYS; + struct xenevtchn_handle *xc = s->be_handles[interdomain->remote_port]; + XenEvtchnPort *lp = &s->port_table[interdomain->local_port]; + + if (!xc) { + ret = -ENOENT; + goto out_free_port; + } + + if (xc->guest_port) { + ret = -EBUSY; + goto out_free_port; + } + + assert(xc->be_port == interdomain->remote_port); + xc->guest_port = interdomain->local_port; + if (kvm_xen_has_cap(EVTCHN_SEND)) { + assign_kernel_eventfd(lp->type, xc->guest_port, xc->fd); + } + lp->type = EVTCHNSTAT_interdomain; + lp->type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU | interdomain->remote_port; + ret = 0; } else { /* Loopback */ XenEvtchnPort *rp = &s->port_table[interdomain->remote_port]; @@ -1129,6 +1218,7 @@ int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain) } } + out_free_port: if (ret) { free_port(s, interdomain->local_port); } @@ -1193,11 +1283,16 @@ int xen_evtchn_send_op(struct evtchn_send *send) if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) { /* * This is an event from the guest to qemu itself, which is - * serving as the driver domain. Not yet implemented; it will - * be hooked up to the qemu implementation of xenstore, - * console, PV net/block drivers etc. + * serving as the driver domain. */ - ret = -ENOSYS; + uint16_t be_port = p->type_val & ~PORT_INFO_TYPEVAL_REMOTE_QEMU; + struct xenevtchn_handle *xc = s->be_handles[be_port]; + if (xc) { + eventfd_write(xc->fd, 1); + ret = 0; + } else { + ret = -ENOENT; + } } else { /* Loopback interdomain ports; just a complex IPI */ set_port_pending(s, p->type_val); @@ -1253,6 +1348,237 @@ int xen_evtchn_set_port(uint16_t port) return ret; } +struct xenevtchn_handle *xen_be_evtchn_open(void) +{ + struct xenevtchn_handle *xc = g_new0(struct xenevtchn_handle, 1); + + xc->fd = eventfd(0, EFD_CLOEXEC); + if (xc->fd < 0) { + free(xc); + return NULL; + } + + return xc; +} + +static int find_be_port(XenEvtchnState *s, struct xenevtchn_handle *xc) +{ + int i; + + for (i = 1; i < EVTCHN_2L_NR_CHANNELS; i++) { + if (!s->be_handles[i]) { + s->be_handles[i] = xc; + xc->be_port = i; + return i; + } + } + return 0; +} + +int xen_be_evtchn_bind_interdomain(struct xenevtchn_handle *xc, uint32_t domid, + evtchn_port_t guest_port) +{ + XenEvtchnState *s = xen_evtchn_singleton; + XenEvtchnPort *gp; + uint16_t be_port = 0; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (!xc) { + return -EFAULT; + } + + if (domid != xen_domid) { + return -ESRCH; + } + + if (!valid_port(guest_port)) { + return -EINVAL; + } + + qemu_mutex_lock(&s->port_lock); + + /* The guest has to have an unbound port waiting for us to bind */ + gp = &s->port_table[guest_port]; + + switch (gp->type) { + case EVTCHNSTAT_interdomain: + /* Allow rebinding after migration, preserve port # if possible */ + be_port = gp->type_val & ~PORT_INFO_TYPEVAL_REMOTE_QEMU; + assert(be_port != 0); + if (!s->be_handles[be_port]) { + s->be_handles[be_port] = xc; + xc->guest_port = guest_port; + ret = xc->be_port = be_port; + if (kvm_xen_has_cap(EVTCHN_SEND)) { + assign_kernel_eventfd(gp->type, guest_port, xc->fd); + } + break; + } + /* fall through */ + + case EVTCHNSTAT_unbound: + be_port = find_be_port(s, xc); + if (!be_port) { + ret = -ENOSPC; + goto out; + } + + gp->type = EVTCHNSTAT_interdomain; + gp->type_val = be_port | PORT_INFO_TYPEVAL_REMOTE_QEMU; + xc->guest_port = guest_port; + if (kvm_xen_has_cap(EVTCHN_SEND)) { + assign_kernel_eventfd(gp->type, guest_port, xc->fd); + } + ret = be_port; + break; + + default: + ret = -EINVAL; + break; + } + + out: + qemu_mutex_unlock(&s->port_lock); + + return ret; +} + +int xen_be_evtchn_unbind(struct xenevtchn_handle *xc, evtchn_port_t port) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (!xc) { + return -EFAULT; + } + + qemu_mutex_lock(&s->port_lock); + + if (port && port != xc->be_port) { + ret = -EINVAL; + goto out; + } + + if (xc->guest_port) { + XenEvtchnPort *gp = &s->port_table[xc->guest_port]; + + /* This should never *not* be true */ + if (gp->type == EVTCHNSTAT_interdomain) { + gp->type = EVTCHNSTAT_unbound; + gp->type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU; + } + + if (kvm_xen_has_cap(EVTCHN_SEND)) { + deassign_kernel_port(xc->guest_port); + } + xc->guest_port = 0; + } + + s->be_handles[xc->be_port] = NULL; + xc->be_port = 0; + ret = 0; + out: + qemu_mutex_unlock(&s->port_lock); + return ret; +} + +int xen_be_evtchn_close(struct xenevtchn_handle *xc) +{ + if (!xc) { + return -EFAULT; + } + + xen_be_evtchn_unbind(xc, 0); + + close(xc->fd); + free(xc); + return 0; +} + +int xen_be_evtchn_fd(struct xenevtchn_handle *xc) +{ + if (!xc) { + return -1; + } + return xc->fd; +} + +int xen_be_evtchn_notify(struct xenevtchn_handle *xc, evtchn_port_t port) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (!xc) { + return -EFAULT; + } + + qemu_mutex_lock(&s->port_lock); + + if (xc->guest_port) { + set_port_pending(s, xc->guest_port); + ret = 0; + } else { + ret = -ENOTCONN; + } + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} + +int xen_be_evtchn_pending(struct xenevtchn_handle *xc) +{ + uint64_t val; + + if (!xc) { + return -EFAULT; + } + + if (!xc->be_port) { + return 0; + } + + if (eventfd_read(xc->fd, &val)) { + return -errno; + } + + return val ? xc->be_port : 0; +} + +int xen_be_evtchn_unmask(struct xenevtchn_handle *xc, evtchn_port_t port) +{ + if (!xc) { + return -EFAULT; + } + + if (xc->be_port != port) { + return -EINVAL; + } + + /* + * We don't actually do anything to unmask it; the event was already + * consumed in xen_be_evtchn_pending(). + */ + return 0; +} + +int xen_be_evtchn_get_guest_port(struct xenevtchn_handle *xc) +{ + return xc->guest_port; +} + EvtchnInfoList *qmp_xen_event_list(Error **errp) { XenEvtchnState *s = xen_evtchn_singleton; diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index 24611478b8..5a71ffb753 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -14,6 +14,8 @@ #include "hw/sysbus.h" +typedef uint32_t evtchn_port_t; + void xen_evtchn_create(void); int xen_evtchn_soft_reset(void); int xen_evtchn_set_callback_param(uint64_t param); @@ -22,6 +24,23 @@ void xen_evtchn_set_callback_level(int level); int xen_evtchn_set_port(uint16_t port); +/* + * These functions mirror the libxenevtchn library API, providing the QEMU + * backend side of "interdomain" event channels. + */ +struct xenevtchn_handle; +struct xenevtchn_handle *xen_be_evtchn_open(void); +int xen_be_evtchn_bind_interdomain(struct xenevtchn_handle *xc, uint32_t domid, + evtchn_port_t guest_port); +int xen_be_evtchn_unbind(struct xenevtchn_handle *xc, evtchn_port_t port); +int xen_be_evtchn_close(struct xenevtchn_handle *xc); +int xen_be_evtchn_fd(struct xenevtchn_handle *xc); +int xen_be_evtchn_notify(struct xenevtchn_handle *xc, evtchn_port_t port); +int xen_be_evtchn_unmask(struct xenevtchn_handle *xc, evtchn_port_t port); +int xen_be_evtchn_pending(struct xenevtchn_handle *xc); +/* Apart from this which is a local addition */ +int xen_be_evtchn_get_guest_port(struct xenevtchn_handle *xc); + struct evtchn_status; struct evtchn_close; struct evtchn_unmask; From c08f5d0e53b00f101c6aab7b5c7eabe22bab1962 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 23 Dec 2022 17:39:23 +0000 Subject: [PATCH 176/185] hw/xen: Add xen_xenstore device for xenstore emulation Just the basic shell, with the event channel hookup. It only dumps the buffer for now; a real ring implmentation will come in a subsequent patch. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/meson.build | 1 + hw/i386/kvm/xen_evtchn.c | 1 + hw/i386/kvm/xen_xenstore.c | 252 +++++++++++++++++++++++++++++++++++++ hw/i386/kvm/xen_xenstore.h | 20 +++ hw/i386/pc.c | 2 + target/i386/kvm/xen-emu.c | 12 ++ 6 files changed, 288 insertions(+) create mode 100644 hw/i386/kvm/xen_xenstore.c create mode 100644 hw/i386/kvm/xen_xenstore.h diff --git a/hw/i386/kvm/meson.build b/hw/i386/kvm/meson.build index 067ff525ee..82dd6ae7c6 100644 --- a/hw/i386/kvm/meson.build +++ b/hw/i386/kvm/meson.build @@ -8,6 +8,7 @@ i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files( 'xen_overlay.c', 'xen_evtchn.c', 'xen_gnttab.c', + 'xen_xenstore.c', )) i386_ss.add_all(when: 'CONFIG_KVM', if_true: i386_kvm_ss) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index 601fbeee72..6c396270b7 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -34,6 +34,7 @@ #include "xen_evtchn.h" #include "xen_overlay.h" +#include "xen_xenstore.h" #include "sysemu/kvm.h" #include "sysemu/kvm_xen.h" diff --git a/hw/i386/kvm/xen_xenstore.c b/hw/i386/kvm/xen_xenstore.c new file mode 100644 index 0000000000..e8abddae57 --- /dev/null +++ b/hw/i386/kvm/xen_xenstore.c @@ -0,0 +1,252 @@ +/* + * QEMU Xen emulation: Shared/overlay pages support + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "qemu/host-utils.h" +#include "qemu/module.h" +#include "qemu/main-loop.h" +#include "qemu/cutils.h" +#include "qapi/error.h" +#include "qom/object.h" +#include "migration/vmstate.h" + +#include "hw/sysbus.h" +#include "hw/xen/xen.h" +#include "xen_overlay.h" +#include "xen_evtchn.h" +#include "xen_xenstore.h" + +#include "sysemu/kvm.h" +#include "sysemu/kvm_xen.h" + +#include "hw/xen/interface/io/xs_wire.h" +#include "hw/xen/interface/event_channel.h" + +#define TYPE_XEN_XENSTORE "xen-xenstore" +OBJECT_DECLARE_SIMPLE_TYPE(XenXenstoreState, XEN_XENSTORE) + +#define XEN_PAGE_SHIFT 12 +#define XEN_PAGE_SIZE (1ULL << XEN_PAGE_SHIFT) + +#define ENTRIES_PER_FRAME_V1 (XEN_PAGE_SIZE / sizeof(grant_entry_v1_t)) +#define ENTRIES_PER_FRAME_V2 (XEN_PAGE_SIZE / sizeof(grant_entry_v2_t)) + +#define XENSTORE_HEADER_SIZE ((unsigned int)sizeof(struct xsd_sockmsg)) + +struct XenXenstoreState { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + MemoryRegion xenstore_page; + struct xenstore_domain_interface *xs; + uint8_t req_data[XENSTORE_HEADER_SIZE + XENSTORE_PAYLOAD_MAX]; + uint8_t rsp_data[XENSTORE_HEADER_SIZE + XENSTORE_PAYLOAD_MAX]; + uint32_t req_offset; + uint32_t rsp_offset; + bool rsp_pending; + bool fatal_error; + + evtchn_port_t guest_port; + evtchn_port_t be_port; + struct xenevtchn_handle *eh; +}; + +struct XenXenstoreState *xen_xenstore_singleton; + +static void xen_xenstore_event(void *opaque); + +static void xen_xenstore_realize(DeviceState *dev, Error **errp) +{ + XenXenstoreState *s = XEN_XENSTORE(dev); + + if (xen_mode != XEN_EMULATE) { + error_setg(errp, "Xen xenstore support is for Xen emulation"); + return; + } + memory_region_init_ram(&s->xenstore_page, OBJECT(dev), "xen:xenstore_page", + XEN_PAGE_SIZE, &error_abort); + memory_region_set_enabled(&s->xenstore_page, true); + s->xs = memory_region_get_ram_ptr(&s->xenstore_page); + memset(s->xs, 0, XEN_PAGE_SIZE); + + /* We can't map it this early as KVM isn't ready */ + xen_xenstore_singleton = s; + + s->eh = xen_be_evtchn_open(); + if (!s->eh) { + error_setg(errp, "Xenstore evtchn port init failed"); + return; + } + aio_set_fd_handler(qemu_get_aio_context(), xen_be_evtchn_fd(s->eh), true, + xen_xenstore_event, NULL, NULL, NULL, s); +} + +static bool xen_xenstore_is_needed(void *opaque) +{ + return xen_mode == XEN_EMULATE; +} + +static int xen_xenstore_pre_save(void *opaque) +{ + XenXenstoreState *s = opaque; + + if (s->eh) { + s->guest_port = xen_be_evtchn_get_guest_port(s->eh); + } + return 0; +} + +static int xen_xenstore_post_load(void *opaque, int ver) +{ + XenXenstoreState *s = opaque; + + /* + * As qemu/dom0, rebind to the guest's port. The Windows drivers may + * unbind the XenStore evtchn and rebind to it, having obtained the + * "remote" port through EVTCHNOP_status. In the case that migration + * occurs while it's unbound, the "remote" port needs to be the same + * as before so that the guest can find it, but should remain unbound. + */ + if (s->guest_port) { + int be_port = xen_be_evtchn_bind_interdomain(s->eh, xen_domid, + s->guest_port); + if (be_port < 0) { + return be_port; + } + s->be_port = be_port; + } + return 0; +} + +static const VMStateDescription xen_xenstore_vmstate = { + .name = "xen_xenstore", + .version_id = 1, + .minimum_version_id = 1, + .needed = xen_xenstore_is_needed, + .pre_save = xen_xenstore_pre_save, + .post_load = xen_xenstore_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(req_data, XenXenstoreState, + sizeof_field(XenXenstoreState, req_data)), + VMSTATE_UINT8_ARRAY(rsp_data, XenXenstoreState, + sizeof_field(XenXenstoreState, rsp_data)), + VMSTATE_UINT32(req_offset, XenXenstoreState), + VMSTATE_UINT32(rsp_offset, XenXenstoreState), + VMSTATE_BOOL(rsp_pending, XenXenstoreState), + VMSTATE_UINT32(guest_port, XenXenstoreState), + VMSTATE_BOOL(fatal_error, XenXenstoreState), + VMSTATE_END_OF_LIST() + } +}; + +static void xen_xenstore_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = xen_xenstore_realize; + dc->vmsd = &xen_xenstore_vmstate; +} + +static const TypeInfo xen_xenstore_info = { + .name = TYPE_XEN_XENSTORE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XenXenstoreState), + .class_init = xen_xenstore_class_init, +}; + +void xen_xenstore_create(void) +{ + DeviceState *dev = sysbus_create_simple(TYPE_XEN_XENSTORE, -1, NULL); + + xen_xenstore_singleton = XEN_XENSTORE(dev); + + /* + * Defer the init (xen_xenstore_reset()) until KVM is set up and the + * overlay page can be mapped. + */ +} + +static void xen_xenstore_register_types(void) +{ + type_register_static(&xen_xenstore_info); +} + +type_init(xen_xenstore_register_types) + +uint16_t xen_xenstore_get_port(void) +{ + XenXenstoreState *s = xen_xenstore_singleton; + if (!s) { + return 0; + } + return s->guest_port; +} + +static void xen_xenstore_event(void *opaque) +{ + XenXenstoreState *s = opaque; + evtchn_port_t port = xen_be_evtchn_pending(s->eh); + if (port != s->be_port) { + return; + } + printf("xenstore event\n"); + /* We know this is a no-op. */ + xen_be_evtchn_unmask(s->eh, port); + qemu_hexdump(stdout, "", s->xs, sizeof(*s->xs)); + xen_be_evtchn_notify(s->eh, s->be_port); +} + +static void alloc_guest_port(XenXenstoreState *s) +{ + struct evtchn_alloc_unbound alloc = { + .dom = DOMID_SELF, + .remote_dom = DOMID_QEMU, + }; + + if (!xen_evtchn_alloc_unbound_op(&alloc)) { + s->guest_port = alloc.port; + } +} + +int xen_xenstore_reset(void) +{ + XenXenstoreState *s = xen_xenstore_singleton; + int err; + + if (!s) { + return -ENOTSUP; + } + + s->req_offset = s->rsp_offset = 0; + s->rsp_pending = false; + + if (!memory_region_is_mapped(&s->xenstore_page)) { + uint64_t gpa = XEN_SPECIAL_PFN(XENSTORE) << TARGET_PAGE_BITS; + xen_overlay_do_map_page(&s->xenstore_page, gpa); + } + + alloc_guest_port(s); + + /* + * As qemu/dom0, bind to the guest's port. For incoming migration, this + * will be unbound as the guest's evtchn table is overwritten. We then + * rebind to the correct guest port in xen_xenstore_post_load(). + */ + err = xen_be_evtchn_bind_interdomain(s->eh, xen_domid, s->guest_port); + if (err < 0) { + return err; + } + s->be_port = err; + + return 0; +} diff --git a/hw/i386/kvm/xen_xenstore.h b/hw/i386/kvm/xen_xenstore.h new file mode 100644 index 0000000000..8c3768e075 --- /dev/null +++ b/hw/i386/kvm/xen_xenstore.h @@ -0,0 +1,20 @@ +/* + * QEMU Xen emulation: Xenstore emulation + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * 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_XEN_XENSTORE_H +#define QEMU_XEN_XENSTORE_H + +void xen_xenstore_create(void); +int xen_xenstore_reset(void); + +uint16_t xen_xenstore_get_port(void); + +#endif /* QEMU_XEN_XENSTORE_H */ diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 1cf112e542..f4a08cc23f 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -93,6 +93,7 @@ #include "hw/i386/kvm/xen_overlay.h" #include "hw/i386/kvm/xen_evtchn.h" #include "hw/i386/kvm/xen_gnttab.h" +#include "hw/i386/kvm/xen_xenstore.h" #include "hw/mem/memory-device.h" #include "sysemu/replay.h" #include "target/i386/cpu.h" @@ -1862,6 +1863,7 @@ int pc_machine_kvm_type(MachineState *machine, const char *kvm_type) xen_overlay_create(); xen_evtchn_create(); xen_gnttab_create(); + xen_xenstore_create(); } #endif return 0; diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index d2c88ef0d9..50965b56f3 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -27,6 +27,7 @@ #include "hw/i386/kvm/xen_overlay.h" #include "hw/i386/kvm/xen_evtchn.h" #include "hw/i386/kvm/xen_gnttab.h" +#include "hw/i386/kvm/xen_xenstore.h" #include "hw/xen/interface/version.h" #include "hw/xen/interface/sched.h" @@ -179,6 +180,9 @@ int kvm_xen_init(KVMState *s, uint32_t hypercall_msr) return ret; } + /* The page couldn't be overlaid until KVM was initialized */ + xen_xenstore_reset(); + return 0; } @@ -789,6 +793,9 @@ static bool handle_get_param(struct kvm_xen_exit *exit, X86CPU *cpu, case HVM_PARAM_STORE_PFN: hp.value = XEN_SPECIAL_PFN(XENSTORE); break; + case HVM_PARAM_STORE_EVTCHN: + hp.value = xen_xenstore_get_port(); + break; default: return false; } @@ -1383,6 +1390,11 @@ int kvm_xen_soft_reset(void) return err; } + err = xen_xenstore_reset(); + if (err) { + return err; + } + return 0; } From f3341e7b91548c38d484285307c23b8f9ce73307 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 28 Dec 2022 10:06:49 +0000 Subject: [PATCH 177/185] hw/xen: Add basic ring handling to xenstore Extract requests, return ENOSYS to all of them. This is enough to allow older Linux guests to boot, as they need *something* back but it doesn't matter much what. A full implementation of a single-tentant internal XenStore copy-on-write tree with transactions and watches is waiting in the wings to be sent in a subsequent round of patches along with hooking up the actual PV disk back end in qemu, but this is enough to get guests booting for now. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_xenstore.c | 254 ++++++++++++++++++++++++++++++++++++- 1 file changed, 251 insertions(+), 3 deletions(-) diff --git a/hw/i386/kvm/xen_xenstore.c b/hw/i386/kvm/xen_xenstore.c index e8abddae57..14193ef3f9 100644 --- a/hw/i386/kvm/xen_xenstore.c +++ b/hw/i386/kvm/xen_xenstore.c @@ -192,18 +192,266 @@ uint16_t xen_xenstore_get_port(void) return s->guest_port; } +static bool req_pending(XenXenstoreState *s) +{ + struct xsd_sockmsg *req = (struct xsd_sockmsg *)s->req_data; + + return s->req_offset == XENSTORE_HEADER_SIZE + req->len; +} + +static void reset_req(XenXenstoreState *s) +{ + memset(s->req_data, 0, sizeof(s->req_data)); + s->req_offset = 0; +} + +static void reset_rsp(XenXenstoreState *s) +{ + s->rsp_pending = false; + + memset(s->rsp_data, 0, sizeof(s->rsp_data)); + s->rsp_offset = 0; +} + +static void process_req(XenXenstoreState *s) +{ + struct xsd_sockmsg *req = (struct xsd_sockmsg *)s->req_data; + struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data; + const char enosys[] = "ENOSYS"; + + assert(req_pending(s)); + assert(!s->rsp_pending); + + rsp->type = XS_ERROR; + rsp->req_id = req->req_id; + rsp->tx_id = req->tx_id; + rsp->len = sizeof(enosys); + memcpy((void *)&rsp[1], enosys, sizeof(enosys)); + + s->rsp_pending = true; + reset_req(s); +} + +static unsigned int copy_from_ring(XenXenstoreState *s, uint8_t *ptr, + unsigned int len) +{ + if (!len) { + return 0; + } + + XENSTORE_RING_IDX prod = qatomic_read(&s->xs->req_prod); + XENSTORE_RING_IDX cons = qatomic_read(&s->xs->req_cons); + unsigned int copied = 0; + + /* Ensure the ring contents don't cross the req_prod access. */ + smp_rmb(); + + while (len) { + unsigned int avail = prod - cons; + unsigned int offset = MASK_XENSTORE_IDX(cons); + unsigned int copylen = avail; + + if (avail > XENSTORE_RING_SIZE) { + error_report("XenStore ring handling error"); + s->fatal_error = true; + break; + } else if (avail == 0) { + break; + } + + if (copylen > len) { + copylen = len; + } + if (copylen > XENSTORE_RING_SIZE - offset) { + copylen = XENSTORE_RING_SIZE - offset; + } + + memcpy(ptr, &s->xs->req[offset], copylen); + copied += copylen; + + ptr += copylen; + len -= copylen; + + cons += copylen; + } + + /* + * Not sure this ever mattered except on Alpha, but this barrier + * is to ensure that the update to req_cons is globally visible + * only after we have consumed all the data from the ring, and we + * don't end up seeing data written to the ring *after* the other + * end sees the update and writes more to the ring. Xen's own + * xenstored has the same barrier here (although with no comment + * at all, obviously, because it's Xen code). + */ + smp_mb(); + + qatomic_set(&s->xs->req_cons, cons); + + return copied; +} + +static unsigned int copy_to_ring(XenXenstoreState *s, uint8_t *ptr, + unsigned int len) +{ + if (!len) { + return 0; + } + + XENSTORE_RING_IDX cons = qatomic_read(&s->xs->rsp_cons); + XENSTORE_RING_IDX prod = qatomic_read(&s->xs->rsp_prod); + unsigned int copied = 0; + + /* + * This matches the barrier in copy_to_ring() (or the guest's + * equivalent) betweem writing the data to the ring and updating + * rsp_prod. It protects against the pathological case (which + * again I think never happened except on Alpha) where our + * subsequent writes to the ring could *cross* the read of + * rsp_cons and the guest could see the new data when it was + * intending to read the old. + */ + smp_mb(); + + while (len) { + unsigned int avail = cons + XENSTORE_RING_SIZE - prod; + unsigned int offset = MASK_XENSTORE_IDX(prod); + unsigned int copylen = len; + + if (avail > XENSTORE_RING_SIZE) { + error_report("XenStore ring handling error"); + s->fatal_error = true; + break; + } else if (avail == 0) { + break; + } + + if (copylen > avail) { + copylen = avail; + } + if (copylen > XENSTORE_RING_SIZE - offset) { + copylen = XENSTORE_RING_SIZE - offset; + } + + + memcpy(&s->xs->rsp[offset], ptr, copylen); + copied += copylen; + + ptr += copylen; + len -= copylen; + + prod += copylen; + } + + /* Ensure the ring contents are seen before rsp_prod update. */ + smp_wmb(); + + qatomic_set(&s->xs->rsp_prod, prod); + + return copied; +} + +static unsigned int get_req(XenXenstoreState *s) +{ + unsigned int copied = 0; + + if (s->fatal_error) { + return 0; + } + + assert(!req_pending(s)); + + if (s->req_offset < XENSTORE_HEADER_SIZE) { + void *ptr = s->req_data + s->req_offset; + unsigned int len = XENSTORE_HEADER_SIZE; + unsigned int copylen = copy_from_ring(s, ptr, len); + + copied += copylen; + s->req_offset += copylen; + } + + if (s->req_offset >= XENSTORE_HEADER_SIZE) { + struct xsd_sockmsg *req = (struct xsd_sockmsg *)s->req_data; + + if (req->len > (uint32_t)XENSTORE_PAYLOAD_MAX) { + error_report("Illegal XenStore request"); + s->fatal_error = true; + return 0; + } + + void *ptr = s->req_data + s->req_offset; + unsigned int len = XENSTORE_HEADER_SIZE + req->len - s->req_offset; + unsigned int copylen = copy_from_ring(s, ptr, len); + + copied += copylen; + s->req_offset += copylen; + } + + return copied; +} + +static unsigned int put_rsp(XenXenstoreState *s) +{ + if (s->fatal_error) { + return 0; + } + + assert(s->rsp_pending); + + struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data; + assert(s->rsp_offset < XENSTORE_HEADER_SIZE + rsp->len); + + void *ptr = s->rsp_data + s->rsp_offset; + unsigned int len = XENSTORE_HEADER_SIZE + rsp->len - s->rsp_offset; + unsigned int copylen = copy_to_ring(s, ptr, len); + + s->rsp_offset += copylen; + + /* Have we produced a complete response? */ + if (s->rsp_offset == XENSTORE_HEADER_SIZE + rsp->len) { + reset_rsp(s); + } + + return copylen; +} + static void xen_xenstore_event(void *opaque) { XenXenstoreState *s = opaque; evtchn_port_t port = xen_be_evtchn_pending(s->eh); + unsigned int copied_to, copied_from; + bool processed, notify = false; + if (port != s->be_port) { return; } - printf("xenstore event\n"); + /* We know this is a no-op. */ xen_be_evtchn_unmask(s->eh, port); - qemu_hexdump(stdout, "", s->xs, sizeof(*s->xs)); - xen_be_evtchn_notify(s->eh, s->be_port); + + do { + copied_to = copied_from = 0; + processed = false; + + if (s->rsp_pending) { + copied_to = put_rsp(s); + } + + if (!req_pending(s)) { + copied_from = get_req(s); + } + + if (req_pending(s) && !s->rsp_pending) { + process_req(s); + processed = true; + } + + notify |= copied_to || copied_from; + } while (copied_to || copied_from || processed); + + if (notify) { + xen_be_evtchn_notify(s->eh, s->be_port); + } } static void alloc_guest_port(XenXenstoreState *s) From bdfdb74882b6cff4cc7f6f0f91cd3b34ee8e2216 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 17 Jan 2023 13:51:22 +0000 Subject: [PATCH 178/185] hw/xen: Automatically add xen-platform PCI device for emulated Xen guests It isn't strictly mandatory but Linux guests at least will only map their grant tables over the dummy BAR that it provides, and don't have sufficient wit to map them in any other unused part of their guest address space. So include it by default for minimal surprise factor. As I come to document "how to run a Xen guest in QEMU", this means one fewer thing to tell the user about, according to the mantra of "if it needs documenting, fix it first, then document what remains". Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/pc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f4a08cc23f..fd17ce7a94 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1315,6 +1315,9 @@ void pc_basic_device_init(struct PCMachineState *pcms, #ifdef CONFIG_XEN_EMU if (xen_mode == XEN_EMULATE) { xen_evtchn_connect_gsis(gsi); + if (pcms->bus) { + pci_create_simple(pcms->bus, -1, "xen-platform"); + } } #endif From 799c23548f0d1a7a49b8c5bbc6b9b86e736296c0 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 13 Jan 2023 00:24:40 +0000 Subject: [PATCH 179/185] i386/xen: Implement HYPERVISOR_physdev_op Just hook up the basic hypercalls to stubs in xen_evtchn.c for now. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 25 ++++++++ hw/i386/kvm/xen_evtchn.h | 11 ++++ target/i386/kvm/xen-compat.h | 19 ++++++ target/i386/kvm/xen-emu.c | 118 +++++++++++++++++++++++++++++++++++ 4 files changed, 173 insertions(+) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index 6c396270b7..b95ee9ba91 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -1349,6 +1349,31 @@ int xen_evtchn_set_port(uint16_t port) return ret; } +int xen_physdev_map_pirq(struct physdev_map_pirq *map) +{ + return -ENOTSUP; +} + +int xen_physdev_unmap_pirq(struct physdev_unmap_pirq *unmap) +{ + return -ENOTSUP; +} + +int xen_physdev_eoi_pirq(struct physdev_eoi *eoi) +{ + return -ENOTSUP; +} + +int xen_physdev_query_pirq(struct physdev_irq_status_query *query) +{ + return -ENOTSUP; +} + +int xen_physdev_get_free_pirq(struct physdev_get_free_pirq *get) +{ + return -ENOTSUP; +} + struct xenevtchn_handle *xen_be_evtchn_open(void) { struct xenevtchn_handle *xc = g_new0(struct xenevtchn_handle, 1); diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index 5a71ffb753..352c875976 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -62,4 +62,15 @@ int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain); int xen_evtchn_bind_vcpu_op(struct evtchn_bind_vcpu *vcpu); int xen_evtchn_reset_op(struct evtchn_reset *reset); +struct physdev_map_pirq; +struct physdev_unmap_pirq; +struct physdev_eoi; +struct physdev_irq_status_query; +struct physdev_get_free_pirq; +int xen_physdev_map_pirq(struct physdev_map_pirq *map); +int xen_physdev_unmap_pirq(struct physdev_unmap_pirq *unmap); +int xen_physdev_eoi_pirq(struct physdev_eoi *eoi); +int xen_physdev_query_pirq(struct physdev_irq_status_query *query); +int xen_physdev_get_free_pirq(struct physdev_get_free_pirq *get); + #endif /* QEMU_XEN_EVTCHN_H */ diff --git a/target/i386/kvm/xen-compat.h b/target/i386/kvm/xen-compat.h index 448336de92..7f30180cc2 100644 --- a/target/i386/kvm/xen-compat.h +++ b/target/i386/kvm/xen-compat.h @@ -48,4 +48,23 @@ struct compat_xen_add_to_physmap_batch { COMPAT_HANDLE(int) errs; }; +struct compat_physdev_map_pirq { + domid_t domid; + uint16_t pad; + /* IN */ + int type; + /* IN (ignored for ..._MULTI_MSI) */ + int index; + /* IN or OUT */ + int pirq; + /* IN - high 16 bits hold segment for ..._MSI_SEG and ..._MULTI_MSI */ + int bus; + /* IN */ + int devfn; + /* IN (also OUT for ..._MULTI_MSI) */ + int entry_nr; + /* IN */ + uint64_t table_base; +} __attribute__((packed)); + #endif /* QEMU_I386_XEN_COMPAT_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 50965b56f3..fe15696cc9 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -1539,6 +1539,121 @@ static bool kvm_xen_hcall_gnttab_op(struct kvm_xen_exit *exit, X86CPU *cpu, return true; } +static bool kvm_xen_hcall_physdev_op(struct kvm_xen_exit *exit, X86CPU *cpu, + int cmd, uint64_t arg) +{ + CPUState *cs = CPU(cpu); + int err; + + switch (cmd) { + case PHYSDEVOP_map_pirq: { + struct physdev_map_pirq map; + + if (hypercall_compat32(exit->u.hcall.longmode)) { + struct compat_physdev_map_pirq *map32 = (void *)↦ + + if (kvm_copy_from_gva(cs, arg, map32, sizeof(*map32))) { + return -EFAULT; + } + + /* + * The only thing that's different is the alignment of the + * uint64_t table_base at the end, which gets padding to make + * it 64-bit aligned in the 64-bit version. + */ + qemu_build_assert(sizeof(*map32) == 36); + qemu_build_assert(offsetof(struct physdev_map_pirq, entry_nr) == + offsetof(struct compat_physdev_map_pirq, entry_nr)); + memmove(&map.table_base, &map32->table_base, sizeof(map.table_base)); + } else { + if (kvm_copy_from_gva(cs, arg, &map, sizeof(map))) { + err = -EFAULT; + break; + } + } + err = xen_physdev_map_pirq(&map); + /* + * Since table_base is an IN parameter and won't be changed, just + * copy the size of the compat structure back to the guest. + */ + if (!err && kvm_copy_to_gva(cs, arg, &map, + sizeof(struct compat_physdev_map_pirq))) { + err = -EFAULT; + } + break; + } + case PHYSDEVOP_unmap_pirq: { + struct physdev_unmap_pirq unmap; + + qemu_build_assert(sizeof(unmap) == 8); + if (kvm_copy_from_gva(cs, arg, &unmap, sizeof(unmap))) { + err = -EFAULT; + break; + } + + err = xen_physdev_unmap_pirq(&unmap); + if (!err && kvm_copy_to_gva(cs, arg, &unmap, sizeof(unmap))) { + err = -EFAULT; + } + break; + } + case PHYSDEVOP_eoi: { + struct physdev_eoi eoi; + + qemu_build_assert(sizeof(eoi) == 4); + if (kvm_copy_from_gva(cs, arg, &eoi, sizeof(eoi))) { + err = -EFAULT; + break; + } + + err = xen_physdev_eoi_pirq(&eoi); + if (!err && kvm_copy_to_gva(cs, arg, &eoi, sizeof(eoi))) { + err = -EFAULT; + } + break; + } + case PHYSDEVOP_irq_status_query: { + struct physdev_irq_status_query query; + + qemu_build_assert(sizeof(query) == 8); + if (kvm_copy_from_gva(cs, arg, &query, sizeof(query))) { + err = -EFAULT; + break; + } + + err = xen_physdev_query_pirq(&query); + if (!err && kvm_copy_to_gva(cs, arg, &query, sizeof(query))) { + err = -EFAULT; + } + break; + } + case PHYSDEVOP_get_free_pirq: { + struct physdev_get_free_pirq get; + + qemu_build_assert(sizeof(get) == 8); + if (kvm_copy_from_gva(cs, arg, &get, sizeof(get))) { + err = -EFAULT; + break; + } + + err = xen_physdev_get_free_pirq(&get); + if (!err && kvm_copy_to_gva(cs, arg, &get, sizeof(get))) { + err = -EFAULT; + } + break; + } + case PHYSDEVOP_pirq_eoi_gmfn_v2: /* FreeBSD 13 makes this hypercall */ + err = -ENOSYS; + break; + + default: + return false; + } + + exit->u.hcall.result = err; + return true; +} + static bool do_kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) { uint16_t code = exit->u.hcall.input; @@ -1580,6 +1695,9 @@ static bool do_kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) case __HYPERVISOR_memory_op: return kvm_xen_hcall_memory_op(exit, cpu, exit->u.hcall.params[0], exit->u.hcall.params[1]); + case __HYPERVISOR_physdev_op: + return kvm_xen_hcall_physdev_op(exit, cpu, exit->u.hcall.params[0], + exit->u.hcall.params[1]); case __HYPERVISOR_xen_version: return kvm_xen_hcall_xen_version(exit, cpu, exit->u.hcall.params[0], exit->u.hcall.params[1]); From aa98ee38a5e77d1e40987c694abd0c5bc5d6c811 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 13 Jan 2023 19:51:32 +0000 Subject: [PATCH 180/185] hw/xen: Implement emulated PIRQ hypercall support This wires up the basic infrastructure but the actual interrupts aren't there yet, so don't advertise it to the guest. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/trace-events | 4 + hw/i386/kvm/trace.h | 1 + hw/i386/kvm/xen_evtchn.c | 306 +++++++++++++++++++++++++++++++++++++- hw/i386/kvm/xen_evtchn.h | 2 + meson.build | 1 + target/i386/kvm/xen-emu.c | 15 ++ 6 files changed, 324 insertions(+), 5 deletions(-) create mode 100644 hw/i386/kvm/trace-events create mode 100644 hw/i386/kvm/trace.h diff --git a/hw/i386/kvm/trace-events b/hw/i386/kvm/trace-events new file mode 100644 index 0000000000..04e60c5bb8 --- /dev/null +++ b/hw/i386/kvm/trace-events @@ -0,0 +1,4 @@ +kvm_xen_map_pirq(int pirq, int gsi) "pirq %d gsi %d" +kvm_xen_unmap_pirq(int pirq, int gsi) "pirq %d gsi %d" +kvm_xen_get_free_pirq(int pirq, int type) "pirq %d type %d" +kvm_xen_bind_pirq(int pirq, int port) "pirq %d port %d" diff --git a/hw/i386/kvm/trace.h b/hw/i386/kvm/trace.h new file mode 100644 index 0000000000..e55d0812fd --- /dev/null +++ b/hw/i386/kvm/trace.h @@ -0,0 +1 @@ +#include "trace/trace-hw_i386_kvm.h" diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index b95ee9ba91..59eea63272 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -24,6 +24,7 @@ #include "exec/target_page.h" #include "exec/address-spaces.h" #include "migration/vmstate.h" +#include "trace.h" #include "hw/sysbus.h" #include "hw/xen/xen.h" @@ -105,6 +106,21 @@ struct xenevtchn_handle { #define PORT_INFO_TYPEVAL_REMOTE_QEMU 0x8000 #define PORT_INFO_TYPEVAL_REMOTE_PORT_MASK 0x7FFF +/* + * These 'emuirq' values are used by Xen in the LM stream... and yes, I am + * insane enough to think about guest-transparent live migration from actual + * Xen to QEMU, and ensuring that we can convert/consume the stream. + */ +#define IRQ_UNBOUND -1 +#define IRQ_PT -2 +#define IRQ_MSI_EMU -3 + + +struct pirq_info { + int gsi; + uint16_t port; +}; + struct XenEvtchnState { /*< private >*/ SysBusDevice busdev; @@ -122,8 +138,25 @@ struct XenEvtchnState { qemu_irq gsis[IOAPIC_NUM_PINS]; struct xenevtchn_handle *be_handles[EVTCHN_2L_NR_CHANNELS]; + + uint32_t nr_pirqs; + + /* Bitmap of allocated PIRQs (serialized) */ + uint16_t nr_pirq_inuse_words; + uint64_t *pirq_inuse_bitmap; + + /* GSI → PIRQ mapping (serialized) */ + uint16_t gsi_pirq[IOAPIC_NUM_PINS]; + + /* Per-PIRQ information (rebuilt on migration) */ + struct pirq_info *pirq; }; +#define pirq_inuse_word(s, pirq) (s->pirq_inuse_bitmap[((pirq) / 64)]) +#define pirq_inuse_bit(pirq) (1ULL << ((pirq) & 63)) + +#define pirq_inuse(s, pirq) (pirq_inuse_word(s, pirq) & pirq_inuse_bit(pirq)) + struct XenEvtchnState *xen_evtchn_singleton; /* Top bits of callback_param are the type (HVM_PARAM_CALLBACK_TYPE_xxx) */ @@ -138,17 +171,45 @@ static int xen_evtchn_pre_load(void *opaque) /* Unbind all the backend-side ports; they need to rebind */ unbind_backend_ports(s); + /* It'll be leaked otherwise. */ + g_free(s->pirq_inuse_bitmap); + s->pirq_inuse_bitmap = NULL; + return 0; } static int xen_evtchn_post_load(void *opaque, int version_id) { XenEvtchnState *s = opaque; + uint32_t i; if (s->callback_param) { xen_evtchn_set_callback_param(s->callback_param); } + /* Rebuild s->pirq[].port mapping */ + for (i = 0; i < s->nr_ports; i++) { + XenEvtchnPort *p = &s->port_table[i]; + + if (p->type == EVTCHNSTAT_pirq) { + assert(p->type_val); + assert(p->type_val < s->nr_pirqs); + + /* + * Set the gsi to IRQ_UNBOUND; it may be changed to an actual + * GSI# below, or to IRQ_MSI_EMU when the MSI table snooping + * catches up with it. + */ + s->pirq[p->type_val].gsi = IRQ_UNBOUND; + s->pirq[p->type_val].port = i; + } + } + /* Rebuild s->pirq[].gsi mapping */ + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + if (s->gsi_pirq[i]) { + s->pirq[s->gsi_pirq[i]].gsi = i; + } + } return 0; } @@ -181,6 +242,10 @@ static const VMStateDescription xen_evtchn_vmstate = { VMSTATE_UINT32(nr_ports, XenEvtchnState), VMSTATE_STRUCT_VARRAY_UINT32(port_table, XenEvtchnState, nr_ports, 1, xen_evtchn_port_vmstate, XenEvtchnPort), + VMSTATE_UINT16_ARRAY(gsi_pirq, XenEvtchnState, IOAPIC_NUM_PINS), + VMSTATE_VARRAY_UINT16_ALLOC(pirq_inuse_bitmap, XenEvtchnState, + nr_pirq_inuse_words, 0, + vmstate_info_uint64, uint64_t), VMSTATE_END_OF_LIST() } }; @@ -221,6 +286,23 @@ void xen_evtchn_create(void) for (i = 0; i < IOAPIC_NUM_PINS; i++) { sysbus_init_irq(SYS_BUS_DEVICE(s), &s->gsis[i]); } + + /* + * We could parameterise the number of PIRQs available if needed, + * but for now limit it to 256. The Xen scheme for encoding PIRQ# + * into an MSI message is not compatible with 32-bit MSI, as it + * puts the high bits of the PIRQ# into the high bits of the MSI + * message address, instead of using the Extended Destination ID + * in address bits 4-11 which perhaps would have been a better + * choice. So to keep life simple, just stick with 256 as the + * default, which conveniently doesn't need to set anything + * outside the low 32 bits of the address. + */ + s->nr_pirqs = 256; + + s->nr_pirq_inuse_words = DIV_ROUND_UP(s->nr_pirqs, 64); + s->pirq_inuse_bitmap = g_new0(uint64_t, s->nr_pirq_inuse_words); + s->pirq = g_new0(struct pirq_info, s->nr_pirqs); } void xen_evtchn_connect_gsis(qemu_irq *system_gsis) @@ -929,6 +1011,10 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port) case EVTCHNSTAT_closed: return -ENOENT; + case EVTCHNSTAT_pirq: + s->pirq[p->type_val].port = 0; + break; + case EVTCHNSTAT_virq: kvm_xen_set_vcpu_virq(virq_is_global(p->type_val) ? 0 : p->vcpu, p->type_val, 0); @@ -1123,6 +1209,37 @@ int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq) return ret; } +int xen_evtchn_bind_pirq_op(struct evtchn_bind_pirq *pirq) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (pirq->pirq >= s->nr_pirqs) { + return -EINVAL; + } + + QEMU_LOCK_GUARD(&s->port_lock); + + if (s->pirq[pirq->pirq].port) { + return -EBUSY; + } + + ret = allocate_port(s, 0, EVTCHNSTAT_pirq, pirq->pirq, + &pirq->port); + if (ret) { + return ret; + } + + s->pirq[pirq->pirq].port = pirq->port; + trace_kvm_xen_bind_pirq(pirq->pirq, pirq->port); + + return ret; +} + int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi) { XenEvtchnState *s = xen_evtchn_singleton; @@ -1349,29 +1466,208 @@ int xen_evtchn_set_port(uint16_t port) return ret; } +static int allocate_pirq(XenEvtchnState *s, int type, int gsi) +{ + uint16_t pirq; + + /* + * Preserve the allocation strategy that Xen has. It looks like + * we *never* give out PIRQ 0-15, we give out 16-nr_irqs_gsi only + * to GSIs (counting up from 16), and then we count backwards from + * the top for MSIs or when the GSI space is exhausted. + */ + if (type == MAP_PIRQ_TYPE_GSI) { + for (pirq = 16 ; pirq < IOAPIC_NUM_PINS; pirq++) { + if (pirq_inuse(s, pirq)) { + continue; + } + + /* Found it */ + goto found; + } + } + for (pirq = s->nr_pirqs - 1; pirq >= IOAPIC_NUM_PINS; pirq--) { + /* Skip whole words at a time when they're full */ + if (pirq_inuse_word(s, pirq) == UINT64_MAX) { + pirq &= ~63ULL; + continue; + } + if (pirq_inuse(s, pirq)) { + continue; + } + + goto found; + } + return -ENOSPC; + + found: + pirq_inuse_word(s, pirq) |= pirq_inuse_bit(pirq); + if (gsi >= 0) { + assert(gsi <= IOAPIC_NUM_PINS); + s->gsi_pirq[gsi] = pirq; + } + s->pirq[pirq].gsi = gsi; + return pirq; +} + int xen_physdev_map_pirq(struct physdev_map_pirq *map) { - return -ENOTSUP; + XenEvtchnState *s = xen_evtchn_singleton; + int pirq = map->pirq; + int gsi = map->index; + + if (!s) { + return -ENOTSUP; + } + + QEMU_LOCK_GUARD(&s->port_lock); + + if (map->domid != DOMID_SELF && map->domid != xen_domid) { + return -EPERM; + } + if (map->type != MAP_PIRQ_TYPE_GSI) { + return -EINVAL; + } + if (gsi < 0 || gsi >= IOAPIC_NUM_PINS) { + return -EINVAL; + } + + if (pirq < 0) { + pirq = allocate_pirq(s, map->type, gsi); + if (pirq < 0) { + return pirq; + } + map->pirq = pirq; + } else if (pirq > s->nr_pirqs) { + return -EINVAL; + } else { + /* + * User specified a valid-looking PIRQ#. Allow it if it is + * allocated and not yet bound, or if it is unallocated + */ + if (pirq_inuse(s, pirq)) { + if (s->pirq[pirq].gsi != IRQ_UNBOUND) { + return -EBUSY; + } + } else { + /* If it was unused, mark it used now. */ + pirq_inuse_word(s, pirq) |= pirq_inuse_bit(pirq); + } + /* Set the mapping in both directions. */ + s->pirq[pirq].gsi = gsi; + s->gsi_pirq[gsi] = pirq; + } + + trace_kvm_xen_map_pirq(pirq, gsi); + return 0; } int xen_physdev_unmap_pirq(struct physdev_unmap_pirq *unmap) { - return -ENOTSUP; + XenEvtchnState *s = xen_evtchn_singleton; + int pirq = unmap->pirq; + int gsi; + + if (!s) { + return -ENOTSUP; + } + + if (unmap->domid != DOMID_SELF && unmap->domid != xen_domid) { + return -EPERM; + } + if (pirq < 0 || pirq >= s->nr_pirqs) { + return -EINVAL; + } + + QEMU_LOCK_GUARD(&s->port_lock); + + if (!pirq_inuse(s, pirq)) { + return -ENOENT; + } + + gsi = s->pirq[pirq].gsi; + + /* We can only unmap GSI PIRQs */ + if (gsi < 0) { + return -EINVAL; + } + + s->gsi_pirq[gsi] = 0; + s->pirq[pirq].gsi = IRQ_UNBOUND; /* Doesn't actually matter because: */ + pirq_inuse_word(s, pirq) &= ~pirq_inuse_bit(pirq); + + trace_kvm_xen_unmap_pirq(pirq, gsi); + return 0; } int xen_physdev_eoi_pirq(struct physdev_eoi *eoi) { - return -ENOTSUP; + XenEvtchnState *s = xen_evtchn_singleton; + int pirq = eoi->irq; + int gsi; + + if (!s) { + return -ENOTSUP; + } + + QEMU_LOCK_GUARD(&s->port_lock); + + if (!pirq_inuse(s, pirq)) { + return -ENOENT; + } + + gsi = s->pirq[pirq].gsi; + if (gsi < 0) { + return -EINVAL; + } + + /* XX: Reassert a level IRQ if needed */ + return 0; } int xen_physdev_query_pirq(struct physdev_irq_status_query *query) { - return -ENOTSUP; + XenEvtchnState *s = xen_evtchn_singleton; + int pirq = query->irq; + + if (!s) { + return -ENOTSUP; + } + + QEMU_LOCK_GUARD(&s->port_lock); + + if (!pirq_inuse(s, pirq)) { + return -ENOENT; + } + + if (s->pirq[pirq].gsi >= 0) { + query->flags = XENIRQSTAT_needs_eoi; + } else { + query->flags = 0; + } + + return 0; } int xen_physdev_get_free_pirq(struct physdev_get_free_pirq *get) { - return -ENOTSUP; + XenEvtchnState *s = xen_evtchn_singleton; + int pirq; + + if (!s) { + return -ENOTSUP; + } + + QEMU_LOCK_GUARD(&s->port_lock); + + pirq = allocate_pirq(s, get->type, IRQ_UNBOUND); + if (pirq < 0) { + return pirq; + } + + get->pirq = pirq; + trace_kvm_xen_get_free_pirq(pirq, get->type); + return 0; } struct xenevtchn_handle *xen_be_evtchn_open(void) diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index 352c875976..a7383f760c 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -45,6 +45,7 @@ struct evtchn_status; struct evtchn_close; struct evtchn_unmask; struct evtchn_bind_virq; +struct evtchn_bind_pirq; struct evtchn_bind_ipi; struct evtchn_send; struct evtchn_alloc_unbound; @@ -55,6 +56,7 @@ int xen_evtchn_status_op(struct evtchn_status *status); int xen_evtchn_close_op(struct evtchn_close *close); int xen_evtchn_unmask_op(struct evtchn_unmask *unmask); int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq); +int xen_evtchn_bind_pirq_op(struct evtchn_bind_pirq *pirq); int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi); int xen_evtchn_send_op(struct evtchn_send *send); int xen_evtchn_alloc_unbound_op(struct evtchn_alloc_unbound *alloc); diff --git a/meson.build b/meson.build index 71a14e6b3d..e533d6c95b 100644 --- a/meson.build +++ b/meson.build @@ -2982,6 +2982,7 @@ if have_system 'hw/i2c', 'hw/i386', 'hw/i386/xen', + 'hw/i386/kvm', 'hw/ide', 'hw/input', 'hw/intc', diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index fe15696cc9..0e81e5b6b1 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -1267,6 +1267,21 @@ static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, X86CPU *cpu, } break; } + case EVTCHNOP_bind_pirq: { + struct evtchn_bind_pirq pirq; + + qemu_build_assert(sizeof(pirq) == 12); + if (kvm_copy_from_gva(cs, arg, &pirq, sizeof(pirq))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_bind_pirq_op(&pirq); + if (!err && kvm_copy_to_gva(cs, arg, &pirq, sizeof(pirq))) { + err = -EFAULT; + } + break; + } case EVTCHNOP_bind_ipi: { struct evtchn_bind_ipi ipi; From 4f81baa33ed645fc17a9908236630b8154502ae5 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 13 Jan 2023 20:41:19 +0000 Subject: [PATCH 181/185] hw/xen: Support GSI mapping to PIRQ If I advertise XENFEAT_hvm_pirqs then a guest now boots successfully as long as I tell it 'pci=nomsi'. [root@localhost ~]# cat /proc/interrupts CPU0 0: 52 IO-APIC 2-edge timer 1: 16 xen-pirq 1-ioapic-edge i8042 4: 1534 xen-pirq 4-ioapic-edge ttyS0 8: 1 xen-pirq 8-ioapic-edge rtc0 9: 0 xen-pirq 9-ioapic-level acpi 11: 5648 xen-pirq 11-ioapic-level ahci[0000:00:04.0] 12: 257 xen-pirq 12-ioapic-edge i8042 ... Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 58 +++++++++++++++++++++++++++++++++++++++- hw/i386/kvm/xen_evtchn.h | 2 ++ hw/i386/x86.c | 16 +++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index 59eea63272..f2c4b43871 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -148,6 +148,9 @@ struct XenEvtchnState { /* GSI → PIRQ mapping (serialized) */ uint16_t gsi_pirq[IOAPIC_NUM_PINS]; + /* Per-GSI assertion state (serialized) */ + uint32_t pirq_gsi_set; + /* Per-PIRQ information (rebuilt on migration) */ struct pirq_info *pirq; }; @@ -246,6 +249,7 @@ static const VMStateDescription xen_evtchn_vmstate = { VMSTATE_VARRAY_UINT16_ALLOC(pirq_inuse_bitmap, XenEvtchnState, nr_pirq_inuse_words, 0, vmstate_info_uint64, uint64_t), + VMSTATE_UINT32(pirq_gsi_set, XenEvtchnState), VMSTATE_END_OF_LIST() } }; @@ -1510,6 +1514,51 @@ static int allocate_pirq(XenEvtchnState *s, int type, int gsi) return pirq; } +bool xen_evtchn_set_gsi(int gsi, int level) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int pirq; + + assert(qemu_mutex_iothread_locked()); + + if (!s || gsi < 0 || gsi > IOAPIC_NUM_PINS) { + return false; + } + + /* + * Check that that it *isn't* the event channel GSI, and thus + * that we are not recursing and it's safe to take s->port_lock. + * + * Locking aside, it's perfectly sane to bail out early for that + * special case, as it would make no sense for the event channel + * GSI to be routed back to event channels, when the delivery + * method is to raise the GSI... that recursion wouldn't *just* + * be a locking issue. + */ + if (gsi && gsi == s->callback_gsi) { + return false; + } + + QEMU_LOCK_GUARD(&s->port_lock); + + pirq = s->gsi_pirq[gsi]; + if (!pirq) { + return false; + } + + if (level) { + int port = s->pirq[pirq].port; + + s->pirq_gsi_set |= (1U << gsi); + if (port) { + set_port_pending(s, port); + } + } else { + s->pirq_gsi_set &= ~(1U << gsi); + } + return true; +} + int xen_physdev_map_pirq(struct physdev_map_pirq *map) { XenEvtchnState *s = xen_evtchn_singleton; @@ -1621,7 +1670,14 @@ int xen_physdev_eoi_pirq(struct physdev_eoi *eoi) return -EINVAL; } - /* XX: Reassert a level IRQ if needed */ + /* Reassert a level IRQ if needed */ + if (s->pirq_gsi_set & (1U << gsi)) { + int port = s->pirq[pirq].port; + if (port) { + set_port_pending(s, port); + } + } + return 0; } diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index a7383f760c..95400b7fbf 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -24,6 +24,8 @@ void xen_evtchn_set_callback_level(int level); int xen_evtchn_set_port(uint16_t port); +bool xen_evtchn_set_gsi(int gsi, int level); + /* * These functions mirror the libxenevtchn library API, providing the QEMU * backend side of "interdomain" event channels. diff --git a/hw/i386/x86.c b/hw/i386/x86.c index c44846f47b..a56b10b2fb 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -61,6 +61,11 @@ #include CONFIG_DEVICES #include "kvm/kvm_i386.h" +#ifdef CONFIG_XEN_EMU +#include "hw/xen/xen.h" +#include "hw/i386/kvm/xen_evtchn.h" +#endif + /* Physical Address of PVH entry point read from kernel ELF NOTE */ static size_t pvh_start_addr; @@ -610,6 +615,17 @@ void gsi_handler(void *opaque, int n, int level) } /* fall through */ case ISA_NUM_IRQS ... IOAPIC_NUM_PINS - 1: +#ifdef CONFIG_XEN_EMU + /* + * Xen delivers the GSI to the Legacy PIC (not that Legacy PIC + * routing actually works properly under Xen). And then to + * *either* the PIRQ handling or the I/OAPIC depending on + * whether the former wants it. + */ + if (xen_mode == XEN_EMULATE && xen_evtchn_set_gsi(n, level)) { + break; + } +#endif qemu_set_irq(s->ioapic_irq[n], level); break; case IO_APIC_SECONDARY_IRQBASE From 6096cf7877bc6ee84e6b3b44dfe144bc8b549724 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 13 Jan 2023 23:35:46 +0000 Subject: [PATCH 182/185] hw/xen: Support MSI mapping to PIRQ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The way that Xen handles MSI PIRQs is kind of awful. There is a special MSI message which targets a PIRQ. The vector in the low bits of data must be zero. The low 8 bits of the PIRQ# are in the destination ID field, the extended destination ID field is unused, and instead the high bits of the PIRQ# are in the high 32 bits of the address. Using the high bits of the address means that we can't intercept and translate these messages in kvm_send_msi(), because they won't be caught by the APIC — addresses like 0x1000fee46000 aren't in the APIC's range. So we catch them in pci_msi_trigger() instead, and deliver the event channel directly. That isn't even the worst part. The worst part is that Xen snoops on writes to devices' MSI vectors while they are *masked*. When a MSI message is written which looks like it targets a PIRQ, it remembers the device and vector for later. When the guest makes a hypercall to bind that PIRQ# (snooped from a marked MSI vector) to an event channel port, Xen *unmasks* that MSI vector on the device. Xen guests using PIRQ delivery of MSI don't ever actually unmask the MSI for themselves. Now that this is working we can finally enable XENFEAT_hvm_pirqs and let the guest use it all. Tested with passthrough igb and emulated e1000e + AHCI. CPU0 CPU1 0: 65 0 IO-APIC 2-edge timer 1: 0 14 xen-pirq 1-ioapic-edge i8042 4: 0 846 xen-pirq 4-ioapic-edge ttyS0 8: 1 0 xen-pirq 8-ioapic-edge rtc0 9: 0 0 xen-pirq 9-ioapic-level acpi 12: 257 0 xen-pirq 12-ioapic-edge i8042 24: 9600 0 xen-percpu -virq timer0 25: 2758 0 xen-percpu -ipi resched0 26: 0 0 xen-percpu -ipi callfunc0 27: 0 0 xen-percpu -virq debug0 28: 1526 0 xen-percpu -ipi callfuncsingle0 29: 0 0 xen-percpu -ipi spinlock0 30: 0 8608 xen-percpu -virq timer1 31: 0 874 xen-percpu -ipi resched1 32: 0 0 xen-percpu -ipi callfunc1 33: 0 0 xen-percpu -virq debug1 34: 0 1617 xen-percpu -ipi callfuncsingle1 35: 0 0 xen-percpu -ipi spinlock1 36: 8 0 xen-dyn -event xenbus 37: 0 6046 xen-pirq -msi ahci[0000:00:03.0] 38: 1 0 xen-pirq -msi-x ens4 39: 0 73 xen-pirq -msi-x ens4-rx-0 40: 14 0 xen-pirq -msi-x ens4-rx-1 41: 0 32 xen-pirq -msi-x ens4-tx-0 42: 47 0 xen-pirq -msi-x ens4-tx-1 Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/trace-events | 1 + hw/i386/kvm/xen-stubs.c | 16 +++ hw/i386/kvm/xen_evtchn.c | 262 ++++++++++++++++++++++++++++++++++++- hw/i386/kvm/xen_evtchn.h | 8 ++ hw/pci/msi.c | 11 ++ hw/pci/msix.c | 9 ++ hw/pci/pci.c | 17 +++ include/hw/pci/msi.h | 1 + target/i386/kvm/kvm.c | 19 ++- target/i386/kvm/kvm_i386.h | 2 + target/i386/kvm/xen-emu.c | 3 +- 11 files changed, 339 insertions(+), 10 deletions(-) diff --git a/hw/i386/kvm/trace-events b/hw/i386/kvm/trace-events index 04e60c5bb8..b83c3eb965 100644 --- a/hw/i386/kvm/trace-events +++ b/hw/i386/kvm/trace-events @@ -2,3 +2,4 @@ kvm_xen_map_pirq(int pirq, int gsi) "pirq %d gsi %d" kvm_xen_unmap_pirq(int pirq, int gsi) "pirq %d gsi %d" kvm_xen_get_free_pirq(int pirq, int type) "pirq %d type %d" kvm_xen_bind_pirq(int pirq, int port) "pirq %d port %d" +kvm_xen_unmask_pirq(int pirq, char *dev, int vector) "pirq %d dev %s vector %d" diff --git a/hw/i386/kvm/xen-stubs.c b/hw/i386/kvm/xen-stubs.c index 720590aedd..ae406e0b02 100644 --- a/hw/i386/kvm/xen-stubs.c +++ b/hw/i386/kvm/xen-stubs.c @@ -14,6 +14,22 @@ #include "qapi/error.h" #include "qapi/qapi-commands-misc-target.h" +#include "xen_evtchn.h" + +void xen_evtchn_snoop_msi(PCIDevice *dev, bool is_msix, unsigned int vector, + uint64_t addr, uint32_t data, bool is_masked) +{ +} + +void xen_evtchn_remove_pci_device(PCIDevice *dev) +{ +} + +bool xen_evtchn_deliver_pirq_msi(uint64_t address, uint32_t data) +{ + return false; +} + #ifdef TARGET_I386 EvtchnInfoList *qmp_xen_event_list(Error **errp) { diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index f2c4b43871..69c0204d4f 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -31,6 +31,8 @@ #include "hw/i386/x86.h" #include "hw/i386/pc.h" #include "hw/pci/pci.h" +#include "hw/pci/msi.h" +#include "hw/pci/msix.h" #include "hw/irq.h" #include "xen_evtchn.h" @@ -45,6 +47,9 @@ #include "hw/xen/interface/memory.h" #include "hw/xen/interface/hvm/params.h" +/* XX: For kvm_update_msi_routes_all() */ +#include "target/i386/kvm/kvm_i386.h" + #define TYPE_XEN_EVTCHN "xen-evtchn" OBJECT_DECLARE_SIMPLE_TYPE(XenEvtchnState, XEN_EVTCHN) @@ -119,6 +124,11 @@ struct xenevtchn_handle { struct pirq_info { int gsi; uint16_t port; + PCIDevice *dev; + int vector; + bool is_msix; + bool is_masked; + bool is_translated; }; struct XenEvtchnState { @@ -151,7 +161,7 @@ struct XenEvtchnState { /* Per-GSI assertion state (serialized) */ uint32_t pirq_gsi_set; - /* Per-PIRQ information (rebuilt on migration) */ + /* Per-PIRQ information (rebuilt on migration, protected by BQL) */ struct pirq_info *pirq; }; @@ -1007,16 +1017,23 @@ static bool virq_is_global(uint32_t virq) } } -static int close_port(XenEvtchnState *s, evtchn_port_t port) +static int close_port(XenEvtchnState *s, evtchn_port_t port, + bool *flush_kvm_routes) { XenEvtchnPort *p = &s->port_table[port]; + /* Because it *might* be a PIRQ port */ + assert(qemu_mutex_iothread_locked()); + switch (p->type) { case EVTCHNSTAT_closed: return -ENOENT; case EVTCHNSTAT_pirq: s->pirq[p->type_val].port = 0; + if (s->pirq[p->type_val].is_translated) { + *flush_kvm_routes = true; + } break; case EVTCHNSTAT_virq: @@ -1065,6 +1082,7 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port) int xen_evtchn_soft_reset(void) { XenEvtchnState *s = xen_evtchn_singleton; + bool flush_kvm_routes; int i; if (!s) { @@ -1073,10 +1091,16 @@ int xen_evtchn_soft_reset(void) assert(qemu_mutex_iothread_locked()); - QEMU_LOCK_GUARD(&s->port_lock); + qemu_mutex_lock(&s->port_lock); for (i = 0; i < s->nr_ports; i++) { - close_port(s, i); + close_port(s, i, &flush_kvm_routes); + } + + qemu_mutex_unlock(&s->port_lock); + + if (flush_kvm_routes) { + kvm_update_msi_routes_all(NULL, true, 0, 0); } return 0; @@ -1094,6 +1118,7 @@ int xen_evtchn_reset_op(struct evtchn_reset *reset) int xen_evtchn_close_op(struct evtchn_close *close) { XenEvtchnState *s = xen_evtchn_singleton; + bool flush_kvm_routes = false; int ret; if (!s) { @@ -1104,12 +1129,17 @@ int xen_evtchn_close_op(struct evtchn_close *close) return -EINVAL; } + QEMU_IOTHREAD_LOCK_GUARD(); qemu_mutex_lock(&s->port_lock); - ret = close_port(s, close->port); + ret = close_port(s, close->port, &flush_kvm_routes); qemu_mutex_unlock(&s->port_lock); + if (flush_kvm_routes) { + kvm_update_msi_routes_all(NULL, true, 0, 0); + } + return ret; } @@ -1226,21 +1256,54 @@ int xen_evtchn_bind_pirq_op(struct evtchn_bind_pirq *pirq) return -EINVAL; } - QEMU_LOCK_GUARD(&s->port_lock); + QEMU_IOTHREAD_LOCK_GUARD(); if (s->pirq[pirq->pirq].port) { return -EBUSY; } + qemu_mutex_lock(&s->port_lock); + ret = allocate_port(s, 0, EVTCHNSTAT_pirq, pirq->pirq, &pirq->port); if (ret) { + qemu_mutex_unlock(&s->port_lock); return ret; } s->pirq[pirq->pirq].port = pirq->port; trace_kvm_xen_bind_pirq(pirq->pirq, pirq->port); + qemu_mutex_unlock(&s->port_lock); + + /* + * Need to do the unmask outside port_lock because it may call + * back into the MSI translate function. + */ + if (s->pirq[pirq->pirq].gsi == IRQ_MSI_EMU) { + if (s->pirq[pirq->pirq].is_masked) { + PCIDevice *dev = s->pirq[pirq->pirq].dev; + int vector = s->pirq[pirq->pirq].vector; + char *dev_path = qdev_get_dev_path(DEVICE(dev)); + + trace_kvm_xen_unmask_pirq(pirq->pirq, dev_path, vector); + g_free(dev_path); + + if (s->pirq[pirq->pirq].is_msix) { + msix_set_mask(dev, vector, false); + } else { + msi_set_mask(dev, vector, false, NULL); + } + } else if (s->pirq[pirq->pirq].is_translated) { + /* + * If KVM had attempted to translate this one before, make it try + * again. If we unmasked, then the notifier on the MSI(-X) vector + * will already have had the same effect. + */ + kvm_update_msi_routes_all(NULL, true, 0, 0); + } + } + return ret; } @@ -1559,6 +1622,179 @@ bool xen_evtchn_set_gsi(int gsi, int level) return true; } +static uint32_t msi_pirq_target(uint64_t addr, uint32_t data) +{ + /* The vector (in low 8 bits of data) must be zero */ + if (data & 0xff) { + return 0; + } + + uint32_t pirq = (addr & 0xff000) >> 12; + pirq |= (addr >> 32) & 0xffffff00; + + return pirq; +} + +static void do_remove_pci_vector(XenEvtchnState *s, PCIDevice *dev, int vector, + int except_pirq) +{ + uint32_t pirq; + + for (pirq = 0; pirq < s->nr_pirqs; pirq++) { + /* + * We could be cleverer here, but it isn't really a fast path, and + * this trivial optimisation is enough to let us skip the big gap + * in the middle a bit quicker (in terms of both loop iterations, + * and cache lines). + */ + if (!(pirq & 63) && !(pirq_inuse_word(s, pirq))) { + pirq += 64; + continue; + } + if (except_pirq && pirq == except_pirq) { + continue; + } + if (s->pirq[pirq].dev != dev) { + continue; + } + if (vector != -1 && s->pirq[pirq].vector != vector) { + continue; + } + + /* It could theoretically be bound to a port already, but that is OK. */ + s->pirq[pirq].dev = dev; + s->pirq[pirq].gsi = IRQ_UNBOUND; + s->pirq[pirq].is_msix = false; + s->pirq[pirq].vector = 0; + s->pirq[pirq].is_masked = false; + s->pirq[pirq].is_translated = false; + } +} + +void xen_evtchn_remove_pci_device(PCIDevice *dev) +{ + XenEvtchnState *s = xen_evtchn_singleton; + + if (!s) { + return; + } + + QEMU_LOCK_GUARD(&s->port_lock); + do_remove_pci_vector(s, dev, -1, 0); +} + +void xen_evtchn_snoop_msi(PCIDevice *dev, bool is_msix, unsigned int vector, + uint64_t addr, uint32_t data, bool is_masked) +{ + XenEvtchnState *s = xen_evtchn_singleton; + uint32_t pirq; + + if (!s) { + return; + } + + assert(qemu_mutex_iothread_locked()); + + pirq = msi_pirq_target(addr, data); + + /* + * The PIRQ# must be sane, and there must be an allocated PIRQ in + * IRQ_UNBOUND or IRQ_MSI_EMU state to match it. + */ + if (!pirq || pirq >= s->nr_pirqs || !pirq_inuse(s, pirq) || + (s->pirq[pirq].gsi != IRQ_UNBOUND && + s->pirq[pirq].gsi != IRQ_MSI_EMU)) { + pirq = 0; + } + + if (pirq) { + s->pirq[pirq].dev = dev; + s->pirq[pirq].gsi = IRQ_MSI_EMU; + s->pirq[pirq].is_msix = is_msix; + s->pirq[pirq].vector = vector; + s->pirq[pirq].is_masked = is_masked; + } + + /* Remove any (other) entries for this {device, vector} */ + do_remove_pci_vector(s, dev, vector, pirq); +} + +int xen_evtchn_translate_pirq_msi(struct kvm_irq_routing_entry *route, + uint64_t address, uint32_t data) +{ + XenEvtchnState *s = xen_evtchn_singleton; + uint32_t pirq, port; + CPUState *cpu; + + if (!s) { + return 1; /* Not a PIRQ */ + } + + assert(qemu_mutex_iothread_locked()); + + pirq = msi_pirq_target(address, data); + if (!pirq || pirq >= s->nr_pirqs) { + return 1; /* Not a PIRQ */ + } + + if (!kvm_xen_has_cap(EVTCHN_2LEVEL)) { + return -ENOTSUP; + } + + if (s->pirq[pirq].gsi != IRQ_MSI_EMU) { + return -EINVAL; + } + + /* Remember that KVM tried to translate this. It might need to try again. */ + s->pirq[pirq].is_translated = true; + + QEMU_LOCK_GUARD(&s->port_lock); + + port = s->pirq[pirq].port; + if (!valid_port(port)) { + return -EINVAL; + } + + cpu = qemu_get_cpu(s->port_table[port].vcpu); + if (!cpu) { + return -EINVAL; + } + + route->type = KVM_IRQ_ROUTING_XEN_EVTCHN; + route->u.xen_evtchn.port = port; + route->u.xen_evtchn.vcpu = kvm_arch_vcpu_id(cpu); + route->u.xen_evtchn.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL; + + return 0; /* Handled */ +} + +bool xen_evtchn_deliver_pirq_msi(uint64_t address, uint32_t data) +{ + XenEvtchnState *s = xen_evtchn_singleton; + uint32_t pirq, port; + + if (!s) { + return false; + } + + assert(qemu_mutex_iothread_locked()); + + pirq = msi_pirq_target(address, data); + if (!pirq || pirq >= s->nr_pirqs) { + return false; + } + + QEMU_LOCK_GUARD(&s->port_lock); + + port = s->pirq[pirq].port; + if (!valid_port(port)) { + return false; + } + + set_port_pending(s, port); + return true; +} + int xen_physdev_map_pirq(struct physdev_map_pirq *map) { XenEvtchnState *s = xen_evtchn_singleton; @@ -1569,6 +1805,7 @@ int xen_physdev_map_pirq(struct physdev_map_pirq *map) return -ENOTSUP; } + QEMU_IOTHREAD_LOCK_GUARD(); QEMU_LOCK_GUARD(&s->port_lock); if (map->domid != DOMID_SELF && map->domid != xen_domid) { @@ -1628,9 +1865,11 @@ int xen_physdev_unmap_pirq(struct physdev_unmap_pirq *unmap) return -EINVAL; } - QEMU_LOCK_GUARD(&s->port_lock); + QEMU_IOTHREAD_LOCK_GUARD(); + qemu_mutex_lock(&s->port_lock); if (!pirq_inuse(s, pirq)) { + qemu_mutex_unlock(&s->port_lock); return -ENOENT; } @@ -1638,6 +1877,7 @@ int xen_physdev_unmap_pirq(struct physdev_unmap_pirq *unmap) /* We can only unmap GSI PIRQs */ if (gsi < 0) { + qemu_mutex_unlock(&s->port_lock); return -EINVAL; } @@ -1646,6 +1886,12 @@ int xen_physdev_unmap_pirq(struct physdev_unmap_pirq *unmap) pirq_inuse_word(s, pirq) &= ~pirq_inuse_bit(pirq); trace_kvm_xen_unmap_pirq(pirq, gsi); + qemu_mutex_unlock(&s->port_lock); + + if (gsi == IRQ_MSI_EMU) { + kvm_update_msi_routes_all(NULL, true, 0, 0); + } + return 0; } @@ -1659,6 +1905,7 @@ int xen_physdev_eoi_pirq(struct physdev_eoi *eoi) return -ENOTSUP; } + QEMU_IOTHREAD_LOCK_GUARD(); QEMU_LOCK_GUARD(&s->port_lock); if (!pirq_inuse(s, pirq)) { @@ -1690,6 +1937,7 @@ int xen_physdev_query_pirq(struct physdev_irq_status_query *query) return -ENOTSUP; } + QEMU_IOTHREAD_LOCK_GUARD(); QEMU_LOCK_GUARD(&s->port_lock); if (!pirq_inuse(s, pirq)) { diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index 95400b7fbf..bfb67ac2bc 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -25,6 +25,14 @@ void xen_evtchn_set_callback_level(int level); int xen_evtchn_set_port(uint16_t port); bool xen_evtchn_set_gsi(int gsi, int level); +void xen_evtchn_snoop_msi(PCIDevice *dev, bool is_msix, unsigned int vector, + uint64_t addr, uint32_t data, bool is_masked); +void xen_evtchn_remove_pci_device(PCIDevice *dev); +struct kvm_irq_routing_entry; +int xen_evtchn_translate_pirq_msi(struct kvm_irq_routing_entry *route, + uint64_t address, uint32_t data); +bool xen_evtchn_deliver_pirq_msi(uint64_t address, uint32_t data); + /* * These functions mirror the libxenevtchn library API, providing the QEMU diff --git a/hw/pci/msi.c b/hw/pci/msi.c index 1cadf150bc..041b0bdbec 100644 --- a/hw/pci/msi.c +++ b/hw/pci/msi.c @@ -24,6 +24,8 @@ #include "qemu/range.h" #include "qapi/error.h" +#include "hw/i386/kvm/xen_evtchn.h" + /* PCI_MSI_ADDRESS_LO */ #define PCI_MSI_ADDRESS_LO_MASK (~0x3) @@ -414,6 +416,15 @@ void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len) fprintf(stderr, "\n"); #endif + if (xen_mode == XEN_EMULATE) { + for (vector = 0; vector < msi_nr_vectors(flags); vector++) { + MSIMessage msg = msi_prepare_message(dev, vector); + + xen_evtchn_snoop_msi(dev, false, vector, msg.address, msg.data, + msi_is_masked(dev, vector)); + } + } + if (!(flags & PCI_MSI_FLAGS_ENABLE)) { return; } diff --git a/hw/pci/msix.c b/hw/pci/msix.c index 9e70fcd6fa..ab8869d9d0 100644 --- a/hw/pci/msix.c +++ b/hw/pci/msix.c @@ -26,6 +26,8 @@ #include "qapi/error.h" #include "trace.h" +#include "hw/i386/kvm/xen_evtchn.h" + /* MSI enable bit and maskall bit are in byte 1 in FLAGS register */ #define MSIX_CONTROL_OFFSET (PCI_MSIX_FLAGS + 1) #define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8) @@ -124,6 +126,13 @@ static void msix_handle_mask_update(PCIDevice *dev, int vector, bool was_masked) { bool is_masked = msix_is_masked(dev, vector); + if (xen_mode == XEN_EMULATE) { + MSIMessage msg = msix_prepare_message(dev, vector); + + xen_evtchn_snoop_msi(dev, true, vector, msg.address, msg.data, + is_masked); + } + if (is_masked == was_masked) { return; } diff --git a/hw/pci/pci.c b/hw/pci/pci.c index bad8e63db3..10c980b9f5 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -49,6 +49,9 @@ #include "qemu/cutils.h" #include "pci-internal.h" +#include "hw/xen/xen.h" +#include "hw/i386/kvm/xen_evtchn.h" + //#define DEBUG_PCI #ifdef DEBUG_PCI # define PCI_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) @@ -319,6 +322,17 @@ static void pci_msi_trigger(PCIDevice *dev, MSIMessage msg) { MemTxAttrs attrs = {}; + /* + * Xen uses the high bits of the address to contain some of the bits + * of the PIRQ#. Therefore we can't just send the write cycle and + * trust that it's caught by the APIC at 0xfee00000 because the + * target of the write might be e.g. 0x0x1000fee46000 for PIRQ#4166. + * So we intercept the delivery here instead of in kvm_send_msi(). + */ + if (xen_mode == XEN_EMULATE && + xen_evtchn_deliver_pirq_msi(msg.address, msg.data)) { + return; + } attrs.requester_id = pci_requester_id(dev); address_space_stl_le(&dev->bus_master_as, msg.address, msg.data, attrs, NULL); @@ -988,6 +1002,9 @@ static void do_pci_unregister_device(PCIDevice *pci_dev) pci_get_bus(pci_dev)->devices[pci_dev->devfn] = NULL; pci_config_free(pci_dev); + if (xen_mode == XEN_EMULATE) { + xen_evtchn_remove_pci_device(pci_dev); + } if (memory_region_is_mapped(&pci_dev->bus_master_enable_region)) { memory_region_del_subregion(&pci_dev->bus_master_container_region, &pci_dev->bus_master_enable_region); diff --git a/include/hw/pci/msi.h b/include/hw/pci/msi.h index ee8ee469a6..abcfd13925 100644 --- a/include/hw/pci/msi.h +++ b/include/hw/pci/msi.h @@ -33,6 +33,7 @@ extern bool msi_nonbroken; void msi_set_message(PCIDevice *dev, MSIMessage msg); MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector); bool msi_enabled(const PCIDevice *dev); +void msi_set_enabled(PCIDevice *dev); int msi_init(struct PCIDevice *dev, uint8_t offset, unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask, Error **errp); diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index a73c49aabb..d390137f02 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -44,6 +44,7 @@ #include "qemu/error-report.h" #include "qemu/memalign.h" #include "hw/i386/x86.h" +#include "hw/i386/kvm/xen_evtchn.h" #include "hw/i386/pc.h" #include "hw/i386/apic.h" #include "hw/i386/apic_internal.h" @@ -5654,6 +5655,20 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, } } +#ifdef CONFIG_XEN_EMU + if (xen_mode == XEN_EMULATE) { + int handled = xen_evtchn_translate_pirq_msi(route, address, data); + + /* + * If it was a PIRQ and successfully routed (handled == 0) or it was + * an error (handled < 0), return. If it wasn't a PIRQ, keep going. + */ + if (handled <= 0) { + return handled; + } + } +#endif + address = kvm_swizzle_msi_ext_dest_id(address); route->u.msi.address_hi = address >> VTD_MSI_ADDR_HI_SHIFT; route->u.msi.address_lo = address & VTD_MSI_ADDR_LO_MASK; @@ -5673,8 +5688,8 @@ struct MSIRouteEntry { static QLIST_HEAD(, MSIRouteEntry) msi_route_list = \ QLIST_HEAD_INITIALIZER(msi_route_list); -static void kvm_update_msi_routes_all(void *private, bool global, - uint32_t index, uint32_t mask) +void kvm_update_msi_routes_all(void *private, bool global, + uint32_t index, uint32_t mask) { int cnt = 0, vector; MSIRouteEntry *entry; diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index 6a5c24e3dc..e24753abfe 100644 --- a/target/i386/kvm/kvm_i386.h +++ b/target/i386/kvm/kvm_i386.h @@ -51,6 +51,8 @@ bool kvm_hv_vpindex_settable(void); bool kvm_hyperv_expand_features(X86CPU *cpu, Error **errp); uint64_t kvm_swizzle_msi_ext_dest_id(uint64_t address); +void kvm_update_msi_routes_all(void *private, bool global, + uint32_t index, uint32_t mask); bool kvm_enable_sgx_provisioning(KVMState *s); void kvm_request_xsave_components(X86CPU *cpu, uint64_t mask); diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 0e81e5b6b1..96a9082196 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -267,7 +267,8 @@ static bool kvm_xen_hcall_xen_version(struct kvm_xen_exit *exit, X86CPU *cpu, 1 << XENFEAT_auto_translated_physmap | 1 << XENFEAT_supervisor_mode_kernel | 1 << XENFEAT_hvm_callback_vector | - 1 << XENFEAT_hvm_safe_pvclock; + 1 << XENFEAT_hvm_safe_pvclock | + 1 << XENFEAT_hvm_pirqs; } err = kvm_copy_to_gva(CPU(cpu), arg, &fi, sizeof(fi)); From e16aff4cc2ef285d1346abcff1a1752614fa9c60 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 18 Jan 2023 14:36:23 +0000 Subject: [PATCH 183/185] kvm/i386: Add xen-evtchn-max-pirq property The default number of PIRQs is set to 256 to avoid issues with 32-bit MSI devices. Allow it to be increased if the user desires. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- accel/kvm/kvm-all.c | 1 + hw/i386/kvm/xen_evtchn.c | 21 +++++++++++---------- include/sysemu/kvm_int.h | 1 + include/sysemu/kvm_xen.h | 1 + target/i386/kvm/kvm.c | 34 ++++++++++++++++++++++++++++++++++ target/i386/kvm/xen-emu.c | 6 ++++++ 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 3d8e400bbf..f2a6ea6a68 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -3705,6 +3705,7 @@ static void kvm_accel_instance_init(Object *obj) s->notify_window = 0; s->xen_version = 0; s->xen_gnttab_max_frames = 64; + s->xen_evtchn_max_pirq = 256; } /** diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index 69c0204d4f..886fbf6b3b 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -302,17 +302,18 @@ void xen_evtchn_create(void) } /* - * We could parameterise the number of PIRQs available if needed, - * but for now limit it to 256. The Xen scheme for encoding PIRQ# - * into an MSI message is not compatible with 32-bit MSI, as it - * puts the high bits of the PIRQ# into the high bits of the MSI - * message address, instead of using the Extended Destination ID - * in address bits 4-11 which perhaps would have been a better - * choice. So to keep life simple, just stick with 256 as the - * default, which conveniently doesn't need to set anything - * outside the low 32 bits of the address. + * The Xen scheme for encoding PIRQ# into an MSI message is not + * compatible with 32-bit MSI, as it puts the high bits of the + * PIRQ# into the high bits of the MSI message address, instead of + * using the Extended Destination ID in address bits 4-11 which + * perhaps would have been a better choice. + * + * To keep life simple, kvm_accel_instance_init() initialises the + * default to 256. which conveniently doesn't need to set anything + * outside the low 32 bits of the address. It can be increased by + * setting the xen-evtchn-max-pirq property. */ - s->nr_pirqs = 256; + s->nr_pirqs = kvm_xen_get_evtchn_max_pirq(); s->nr_pirq_inuse_words = DIV_ROUND_UP(s->nr_pirqs, 64); s->pirq_inuse_bitmap = g_new0(uint64_t, s->nr_pirq_inuse_words); diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h index 39ce4d36f6..a641c974ea 100644 --- a/include/sysemu/kvm_int.h +++ b/include/sysemu/kvm_int.h @@ -121,6 +121,7 @@ struct KVMState uint32_t xen_version; uint32_t xen_caps; uint16_t xen_gnttab_max_frames; + uint16_t xen_evtchn_max_pirq; }; void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml, diff --git a/include/sysemu/kvm_xen.h b/include/sysemu/kvm_xen.h index b2aafaf7ab..595abfbe40 100644 --- a/include/sysemu/kvm_xen.h +++ b/include/sysemu/kvm_xen.h @@ -26,6 +26,7 @@ void kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type); void kvm_xen_set_callback_asserted(void); int kvm_xen_set_vcpu_virq(uint32_t vcpu_id, uint16_t virq, uint16_t port); uint16_t kvm_xen_get_gnttab_max_frames(void); +uint16_t kvm_xen_get_evtchn_max_pirq(void); #define kvm_xen_has_cap(cap) (!!(kvm_xen_get_caps() & \ KVM_XEN_HVM_CONFIG_ ## cap)) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index d390137f02..1aef54f87e 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5922,6 +5922,33 @@ static void kvm_arch_set_xen_gnttab_max_frames(Object *obj, Visitor *v, s->xen_gnttab_max_frames = value; } +static void kvm_arch_get_xen_evtchn_max_pirq(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + uint16_t value = s->xen_evtchn_max_pirq; + + visit_type_uint16(v, name, &value, errp); +} + +static void kvm_arch_set_xen_evtchn_max_pirq(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + Error *error = NULL; + uint16_t value; + + visit_type_uint16(v, name, &value, &error); + if (error) { + error_propagate(errp, error); + return; + } + + s->xen_evtchn_max_pirq = value; +} + void kvm_arch_accel_class_init(ObjectClass *oc) { object_class_property_add_enum(oc, "notify-vmexit", "NotifyVMexitOption", @@ -5954,6 +5981,13 @@ void kvm_arch_accel_class_init(ObjectClass *oc) NULL, NULL); object_class_property_set_description(oc, "xen-gnttab-max-frames", "Maximum number of grant table frames"); + + object_class_property_add(oc, "xen-evtchn-max-pirq", "uint16", + kvm_arch_get_xen_evtchn_max_pirq, + kvm_arch_set_xen_evtchn_max_pirq, + NULL, NULL); + object_class_property_set_description(oc, "xen-evtchn-max-pirq", + "Maximum number of Xen PIRQs"); } void kvm_set_max_apic_id(uint32_t max_apic_id) diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index 96a9082196..bad3131d08 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -1765,6 +1765,12 @@ uint16_t kvm_xen_get_gnttab_max_frames(void) return s->xen_gnttab_max_frames; } +uint16_t kvm_xen_get_evtchn_max_pirq(void) +{ + KVMState *s = KVM_STATE(current_accel()); + return s->xen_evtchn_max_pirq; +} + int kvm_put_xen_state(CPUState *cs) { X86CPU *cpu = X86_CPU(cs); From c1eaa6d0df6ed9e021f751d0be6eb321551a9bea Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 17 Jan 2023 14:13:32 +0000 Subject: [PATCH 184/185] i386/xen: Document Xen HVM emulation Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- docs/system/i386/xen.rst | 76 +++++++++++++++++++++++++++++++++++++ docs/system/target-i386.rst | 1 + 2 files changed, 77 insertions(+) create mode 100644 docs/system/i386/xen.rst diff --git a/docs/system/i386/xen.rst b/docs/system/i386/xen.rst new file mode 100644 index 0000000000..a00523b492 --- /dev/null +++ b/docs/system/i386/xen.rst @@ -0,0 +1,76 @@ +Xen HVM guest support +===================== + + +Description +----------- + +KVM has support for hosting Xen guests, intercepting Xen hypercalls and event +channel (Xen PV interrupt) delivery. This allows guests which expect to be +run under Xen to be hosted in QEMU under Linux/KVM instead. + +Setup +----- + +Xen mode is enabled by setting the ``xen-version`` property of the KVM +accelerator, for example for Xen 4.10: + +.. parsed-literal:: + + |qemu_system| --accel kvm,xen-version=0x4000a + +Additionally, virtual APIC support can be advertised to the guest through the +``xen-vapic`` CPU flag: + +.. parsed-literal:: + + |qemu_system| --accel kvm,xen-version=0x4000a --cpu host,+xen_vapic + +When Xen support is enabled, QEMU changes hypervisor identification (CPUID +0x40000000..0x4000000A) to Xen. The KVM identification and features are not +advertised to a Xen guest. If Hyper-V is also enabled, the Xen identification +moves to leaves 0x40000100..0x4000010A. + +The Xen platform device is enabled automatically for a Xen guest. This allows +a guest to unplug all emulated devices, in order to use Xen PV block and network +drivers instead. Note that until the Xen PV device back ends are enabled to work +with Xen mode in QEMU, that is unlikely to cause significant joy. Linux guests +can be dissuaded from this by adding 'xen_emul_unplug=never' on their command +line, and it can also be noted that AHCI disk controllers are exempt from being +unplugged, as are passthrough VFIO PCI devices. + +Properties +---------- + +The following properties exist on the KVM accelerator object: + +``xen-version`` + This property contains the Xen version in ``XENVER_version`` form, with the + major version in the top 16 bits and the minor version in the low 16 bits. + Setting this property enables the Xen guest support. + +``xen-evtchn-max-pirq`` + Xen PIRQs represent an emulated physical interrupt, either GSI or MSI, which + can be routed to an event channel instead of to the emulated I/O or local + APIC. By default, QEMU permits only 256 PIRQs because this allows maximum + compatibility with 32-bit MSI where the higher bits of the PIRQ# would need + to be in the upper 64 bits of the MSI message. For guests with large numbers + of PCI devices (and none which are limited to 32-bit addressing) it may be + desirable to increase this value. + +``xen-gnttab-max-frames`` + Xen grant tables are the means by which a Xen guest grants access to its + memory for PV back ends (disk, network, etc.). Since QEMU only supports v1 + grant tables which are 8 bytes in size, each page (each frame) of the grant + table can reference 512 pages of guest memory. The default number of frames + is 64, allowing for 32768 pages of guest memory to be accessed by PV backends + through simultaneous grants. For guests with large numbers of PV devices and + high throughput, it may be desirable to increase this value. + +OS requirements +--------------- + +The minimal Xen support in the KVM accelerator requires the host to be running +Linux v5.12 or newer. Later versions add optimisations: Linux v5.17 added +acceleration of interrupt delivery via the Xen PIRQ mechanism, and Linux v5.19 +accelerated Xen PV timers and inter-processor interrupts (IPIs). diff --git a/docs/system/target-i386.rst b/docs/system/target-i386.rst index e64c013077..77c2f3b979 100644 --- a/docs/system/target-i386.rst +++ b/docs/system/target-i386.rst @@ -27,6 +27,7 @@ Architectural features i386/cpu i386/hyperv + i386/xen i386/kvm-pv i386/sgx i386/amd-memory-encryption From 79807f3e6bf1186c684312d4e7fb426b2643bade Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 15 Feb 2023 16:05:26 +0100 Subject: [PATCH 185/185] hw/xen: Subsume xen_be_register_common() into xen_be_init() Every caller of xen_be_init() checks and exits on error, then calls xen_be_register_common(). Just make xen_be_init() abort for itself and return void, and register the common devices too. Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/xen/xen-hvm.c | 8 +---- hw/xen/xen-legacy-backend.c | 56 ++++++++++++----------------- hw/xenpv/xen_machine_pv.c | 6 +--- include/hw/xen/xen-legacy-backend.h | 3 +- 4 files changed, 25 insertions(+), 48 deletions(-) diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index b9a6f7f538..e5a1dd19f4 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -1502,13 +1502,7 @@ void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) device_listener_register(&state->device_listener); xen_bus_init(); - - /* Initialize backend core & drivers */ - if (xen_be_init() != 0) { - error_report("xen backend core setup failed"); - goto err; - } - xen_be_register_common(); + xen_be_init(); QLIST_INIT(&xen_physmap); xen_read_physmap(state); diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c index 085fd31ef7..afba71f6eb 100644 --- a/hw/xen/xen-legacy-backend.c +++ b/hw/xen/xen-legacy-backend.c @@ -676,21 +676,30 @@ void xenstore_update_fe(char *watch, struct XenLegacyDevice *xendev) } /* -------------------------------------------------------------------- */ -int xen_be_init(void) +static void xen_set_dynamic_sysbus(void) +{ + Object *machine = qdev_get_machine(); + ObjectClass *oc = object_get_class(machine); + MachineClass *mc = MACHINE_CLASS(oc); + + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_XENSYSDEV); +} + +void xen_be_init(void) { xengnttab_handle *gnttabdev; xenstore = xs_daemon_open(); if (!xenstore) { xen_pv_printf(NULL, 0, "can't connect to xenstored\n"); - return -1; + exit(1); } qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL); if (xen_xc == NULL || xen_fmem == NULL) { - /* Check if xen_init() have been called */ - goto err; + xen_pv_printf(NULL, 0, "Xen operations not set up\n"); + exit(1); } gnttabdev = xengnttab_open(NULL, 0); @@ -706,23 +715,16 @@ int xen_be_init(void) xen_sysbus = qbus_new(TYPE_XENSYSBUS, xen_sysdev, "xen-sysbus"); qbus_set_bus_hotplug_handler(xen_sysbus); - return 0; + xen_set_dynamic_sysbus(); -err: - qemu_set_fd_handler(xs_fileno(xenstore), NULL, NULL, NULL); - xs_daemon_close(xenstore); - xenstore = NULL; - - return -1; -} - -static void xen_set_dynamic_sysbus(void) -{ - Object *machine = qdev_get_machine(); - ObjectClass *oc = object_get_class(machine); - MachineClass *mc = MACHINE_CLASS(oc); - - machine_class_allow_dynamic_sysbus_dev(mc, TYPE_XENSYSDEV); + xen_be_register("console", &xen_console_ops); + xen_be_register("vkbd", &xen_kbdmouse_ops); +#ifdef CONFIG_VIRTFS + xen_be_register("9pfs", &xen_9pfs_ops); +#endif +#ifdef CONFIG_USB_LIBUSB + xen_be_register("qusb", &xen_usb_ops); +#endif } int xen_be_register(const char *type, struct XenDevOps *ops) @@ -744,20 +746,6 @@ int xen_be_register(const char *type, struct XenDevOps *ops) return xenstore_scan(type, xen_domid, ops); } -void xen_be_register_common(void) -{ - xen_set_dynamic_sysbus(); - - xen_be_register("console", &xen_console_ops); - xen_be_register("vkbd", &xen_kbdmouse_ops); -#ifdef CONFIG_VIRTFS - xen_be_register("9pfs", &xen_9pfs_ops); -#endif -#ifdef CONFIG_USB_LIBUSB - xen_be_register("qusb", &xen_usb_ops); -#endif -} - int xen_be_bind_evtchn(struct XenLegacyDevice *xendev) { if (xendev->local_port != -1) { diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c index 20c9611d71..2e759d0619 100644 --- a/hw/xenpv/xen_machine_pv.c +++ b/hw/xenpv/xen_machine_pv.c @@ -36,10 +36,7 @@ static void xen_init_pv(MachineState *machine) int i; /* Initialize backend core & drivers */ - if (xen_be_init() != 0) { - error_report("%s: xen backend core setup failed", __func__); - exit(1); - } + xen_be_init(); switch (xen_mode) { case XEN_ATTACH: @@ -55,7 +52,6 @@ static void xen_init_pv(MachineState *machine) break; } - xen_be_register_common(); xen_be_register("vfb", &xen_framebuffer_ops); xen_be_register("qnic", &xen_netdev_ops); diff --git a/include/hw/xen/xen-legacy-backend.h b/include/hw/xen/xen-legacy-backend.h index be281e1f38..e31cd3a068 100644 --- a/include/hw/xen/xen-legacy-backend.h +++ b/include/hw/xen/xen-legacy-backend.h @@ -42,8 +42,7 @@ int xenstore_read_fe_uint64(struct XenLegacyDevice *xendev, const char *node, void xen_be_check_state(struct XenLegacyDevice *xendev); /* xen backend driver bits */ -int xen_be_init(void); -void xen_be_register_common(void); +void xen_be_init(void); int xen_be_register(const char *type, struct XenDevOps *ops); int xen_be_set_state(struct XenLegacyDevice *xendev, enum xenbus_state state); int xen_be_bind_evtchn(struct XenLegacyDevice *xendev);