Compare commits

..
63 Commits
Author SHA1 Message Date
Michael Tokarev ff3419cbac Update version for 10.0.2 release
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-28 10:55:26 +03:00
Michael Tokarev 6f7a53e58e Revert "Drop support for Python 3.8"
This reverts commit 3d5b2f81fb.

It should not be in 10.0.x branch, since we do support python 3.8
in 10.0.

Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-28 10:49:31 +03:00
Michael Tokarev fd0b1a5b13 Update version for 10.0.1 release
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-26 10:33:49 +03:00
Thomas Huth 3d5b2f81fb Drop support for Python 3.8
Python 3.8 went "end of life" in October 2024 and Fedora 42 dropped
this version already, so the "python" CI job is currently failing.
Thus it's time to drop support for this Python version in QEMU, too.

While we're at it, also look for "python3.13" in the configure script.

Message-ID: <20250425120710.879518-1-thuth@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Thomas Huth <thuth@redhat.com>
(cherry picked from commit d64db833d6)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-25 13:37:53 +03:00
Helge Deller 6966e0ba1d target/hppa: Fix FPE exceptions
Implement FP exception register #1 (lower 32-bits of 64-bit fr[0]).
A proper implementation is necessary to allow the Linux kernel in
system mode and the qemu linux-user to send proper si_code values
on SIGFPE signal.

Always set the T-bit on taken exception, and merge over- and underflow
in system mode to just set overflow bit to mimic the behaviour I tested
on a physical machine.

The test program below can be used to verify correct behaviour. Note
that behaviour on SIGFPE may vary on different platforms. The program
should always detect the correct signal, but it may or may not be able
to sucessfully continue afterwards.

 #define _GNU_SOURCE
 #include <signal.h>
 #include <stdio.h>
 #include <fenv.h>
 #include <float.h>

 static void fpe_func(int sig, siginfo_t *i, void *v) {
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGFPE);
    sigprocmask(SIG_UNBLOCK, &set, NULL);
    printf("GOT signal %d with si_code %ld\n", sig, i->si_code);
 }

 int main(int argc, char *argv[]) {
    struct sigaction action = {
        .sa_sigaction = fpe_func,
        .sa_flags = SA_RESTART|SA_SIGINFO };
    sigaction(SIGFPE, &action, 0);
    feenableexcept(FE_OVERFLOW | FE_UNDERFLOW);
    double x = DBL_MIN;
    return printf("%lf\n", argc > 1
        ? 1.7976931348623158E308*1.7976931348623158E308
        : x / 10);
 }

Signed-off-by: Helge Deller <deller@gmx.de>
(cherry picked from commit ebd394948d)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-25 11:20:04 +03:00
Helge Deller 5f119c69ff linux-user/hppa: Send proper si_code on SIGFPE exception
Improve the linux-user emulation to send the correct si_code depending
on overflow (TARGET_FPE_FLTOVF), underflow (TARGET_FPE_FLTUND), ...
Note that the hardware stores the relevant flags in FP exception
register #1, which is actually the lower 32-bits of the 64-bit fr[0]
register in qemu.

Signed-off-by: Helge Deller <deller@gmx.de>
(cherry picked from commit b4b49cf39d)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-25 11:19:57 +03:00
Helge Deller 679450c926 target/hppa: Copy instruction code into fr1 on FPU assist fault
The hardware stores the instruction code in the lower bits of the FP
exception register #1 on FP assist traps.
This fixes the FP exception handler on Linux, as the Linux kernel uses
the value to decide on the correct signal which should be pushed into
userspace (see decode_fpu() in Linux kernel).

Signed-off-by: Helge Deller <deller@gmx.de>
(cherry picked from commit 923976dfe3)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-25 11:19:51 +03:00
Peter Xu 8a48520c17 migration: Allow caps to be set when preempt or multifd cap enabled
With commit 82137e6c8c ("migration: enforce multifd and postcopy preempt to
be set before incoming"), and if postcopy preempt / multifd is enabled, one
cannot setup any capability because these checks would always fail.

(qemu) migrate_set_capability xbzrle off
Error: Postcopy preempt must be set before incoming starts

To fix it, check existing cap and only raise an error if the specific cap
changed.

Fixes: 82137e6c8c ("migration: enforce multifd and postcopy preempt to be set before incoming")
Reviewed-by: Dr. David Alan Gilbert <dave@treblig.org>
Reviewed-by: Juraj Marcin <jmarcin@redhat.com>
Signed-off-by: Peter Xu <peterx@redhat.com>
(cherry picked from commit 17bec9235b)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-22 23:13:03 +03:00
Maciej S. Szmigiero 7484d61bdb migration/multifd: Don't send device state packets with zerocopy flag
If zerocopy is enabled for multifd then QIO_CHANNEL_WRITE_FLAG_ZERO_COPY
flag is forced into all multifd channel write calls via p->write_flags
that was setup in multifd_nocomp_send_setup().

However, device state packets aren't compatible with zerocopy - the data
buffer isn't getting kept pinned until multifd channel flush.

Make sure to mask that QIO_CHANNEL_WRITE_FLAG_ZERO_COPY flag in a multifd
send thread if the data being sent is device state.

Fixes: 0525b91a0b ("migration/multifd: Device state transfer support - send side")
Signed-off-by: Maciej S. Szmigiero <maciej.szmigiero@oracle.com>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
Link: https://lore.kernel.org/r/3bd5f48578e29f3a78f41b1e4fbea3d4b2d9b136.1747403393.git.maciej.szmigiero@oracle.com
Signed-off-by: Peter Xu <peterx@redhat.com>
(cherry picked from commit 6be7696129)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-22 23:11:57 +03:00
Zhao Liu 5e8ef1da89 qapi/misc-target: Fix the doc to distinguish query-sgx and query-sgx-capabilities
There're 2 QMP commands: query-sgx and query-sgx-capabilities, but
their outputs are very similar and the documentation lacks clear
differentiation.

From the codes, query-sgx is used to gather guest's SGX capabilities
(including SGX related CPUIDs and EPC sections' size, in SGXInfo), and
if guest doesn't have SGX, then QEMU will report the error message.

On the other hand, query-sgx-capabilities is used to gather host's SGX
capabilities (descripted by SGXInfo as well). And if host doesn't
support SGX, then QEMU will also report the error message.

Considering that SGXInfo is already documented and both these 2 commands
have enough error messages (for the exception case in their codes).

Therefore the QAPI documentation for these two commands only needs to
emphasize that one of them applies to the guest and the other to the
host.

Fix their documentation to reflect this difference.

Reported-by: Markus Armbruster <armbru@redhat.com>
Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
Acked-by: Markus Armbruster <armbru@redhat.com>
Link: https://lore.kernel.org/r/20250513143131.2008078-3-zhao1.liu@intel.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 7f2131c35c)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-22 16:09:32 +03:00
Rakesh Jeyasingh ef1655962f hw/pci-host: Remove unused pci_host_data_be_ops
pci_host_data_be_ops became unused after endianness fixes

Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Rakesh Jeyasingh <rakeshjb010@gmail.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Tested-by: Thomas Huth <thuth@redhat.com>
Link: https://lore.kernel.org/r/20250429170354.150581-3-rakeshjb010@gmail.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 560375cff3)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-22 16:09:32 +03:00
Rakesh Jeyasingh cdf3fb0617 hw/pci-host/gt64120: Fix endianness handling
The GT-64120 PCI controller requires special handling where:
1. Host bridge(bus 0 ,device 0) must never be byte-swapped
2. Other devices follow MByteSwap bit in GT_PCI0_CMD

The previous implementation incorrectly  swapped all accesses, breaking
host bridge detection (lspci -d 11ab:4620).

Changes made:
1. Removed gt64120_update_pci_cfgdata_mapping() and moved data_mem initialization
  to gt64120_realize() for cleaner setup
2. Implemented custom read/write handlers that:
   - Preserve host bridge accesses (extract32(config_reg,11,13)==0)
   - apply swapping only for non-bridge devices in big-endian mode

Fixes: 145e2198 ("hw/mips/gt64xxx_pci: Endian-swap using PCI_HOST_BRIDGE MemoryRegionOps")
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2826

Signed-off-by: Rakesh Jeyasingh <rakeshjb010@gmail.com>
Tested-by: Thomas Huth <thuth@redhat.com>
Link: https://lore.kernel.org/r/20250429170354.150581-2-rakeshjb010@gmail.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit e5894fd6f4)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-22 14:33:00 +03:00
Xiaoyao Li 3e59fccb03 i386/hvf: Make CPUID_HT supported
Since Commit c6bd2dd634 ("i386/cpu: Set up CPUID_HT in
x86_cpu_expand_features() instead of cpu_x86_cpuid()"), CPUID_HT will be
set in env->features[] in x86_cpu_expand_features() when vcpus >= 2.

Later in x86_cpu_filter_features() it will check against the HVF
supported bits. It will trigger the warning like

    qemu-system-x86_64: warning: host doesn't support requested feature: CPUID.01H:EDX.ht [bit 28]

Add CPUID_HT to HVF supported CPUID bits to fix it.

Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
Link: https://lore.kernel.org/r/20250514031652.838763-3-xiaoyao.li@intel.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 7a48612306)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-22 14:27:23 +03:00
Xiaoyao Li 2f6357ca96 i386/tcg: Make CPUID_HT and CPUID_EXT3_CMP_LEG supported
Since commit c6bd2dd634 ("i386/cpu: Set up CPUID_HT in
x86_cpu_expand_features() instead of cpu_x86_cpuid()") and
commit 99a637a86f ("i386/cpu: Set and track CPUID_EXT3_CMP_LEG in
env->features[FEAT_8000_0001_ECX]"), it gets warnings when booting the
VM with vcpus >= 2 and with tcg:

  qemu-system-x86_64: warning: TCG doesn't support requested feature: CPUID.01H:EDX.ht [bit 28]
  qemu-system-x86_64: warning: TCG doesn't support requested feature: CPUID.80000001H:ECX.cmp-legacy [bit 1]

This is because, after the two commits, CPUID_HT and CPUID_EXT3_CMP_LEG
are set in env->features[] when vcpus >=2 (in x86_cpu_expand_features())
later in x86_cpu_filter_features() it will check against the TCG supported
bits. However, current TCG doesn't mark the two bits as supported, hence
the warnings.

Fix it by adding the two bits to the supported bits of TCG since multiple
vcpus are supported by TCG.

Fixes: c6bd2dd634 ("i386/cpu: Set up CPUID_HT in x86_cpu_expand_features() instead of cpu_x86_cpuid()")
Fixes: 99a637a86f ("i386/cpu: Set and track CPUID_EXT3_CMP_LEG in env->features[FEAT_8000_0001_ECX]")
Reported-by: Ewan Hai <ewanhai-oc@zhaoxin.com>
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
Link: https://lore.kernel.org/r/20250514031652.838763-2-xiaoyao.li@intel.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 5979f50fa9)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-22 14:27:12 +03:00
Daniel Henrique Barboza 9ac56fb385 target/riscv/kvm: do not read unavailable CSRs
[1] reports that commit 4db19d5b21 broke a KVM guest running kernel 6.6.
This happens because the kernel does not know 'senvcfg', making it
unable to boot because QEMU is reading/wriiting it without any checks.

After converting the CSRs to do "automated" get/put reg procedures in
the previous patch we can now scan for availability. Two functions are
created:

- kvm_riscv_read_csr_cfg_legacy() will check if the CSR exists by brute
  forcing KVM_GET_ONE_REG in each one of them, interpreting an EINVAL
  return as indication that the CSR isn't available. This will be use in
  absence of KVM_GET_REG_LIST;

- kvm_riscv_read_csr_cfg() will use the existing result of get_reg_list
  to check if the CSRs ids are present.

kvm_riscv_init_multiext_cfg() is now kvm_riscv_init_cfg() to reflect that
the function is also dealing with CSRs.

[1] https://lore.kernel.org/qemu-riscv/CABJz62OfUDHYkQ0T3rGHStQprf1c7_E0qBLbLKhfv=+jb0SYAw@mail.gmail.com/

Fixes: 4db19d5b21 ("target/riscv/kvm: add missing KVM CSRs")
Reported-by: Andrea Bolognani <abologna@redhat.com>
Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
Acked-by: Alistair Francis <alistair.francis@wdc.com>
Message-ID: <20250429124421.223883-7-dbarboza@ventanamicro.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit f396c217a5)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 10:04:19 +03:00
Daniel Henrique Barboza 6ba14ba513 target/riscv/kvm: add kvm_csr_cfgs[]
At this moment we're not checking if the host has support for any
specific CSR before doing get/put regs. This will cause problems if the
host KVM doesn't support it (see [1] as an example).

We'll use the same approach done with the CPU extensions: read all known
KVM CSRs during init() to check for availability, then read/write them
if they are present. This will be made by either using get-reglist or by
directly reading the CSRs.

For now we'll just convert the CSRs to use a kvm_csr_cfg[] array,
reusing the same KVMCPUConfig abstraction we use for extensions, and use
the array in (get|put)_csr_regs() instead of manually listing them. A
lot of boilerplate will be added but at least we'll automate the get/put
procedure for CSRs, i.e. adding a new CSR in the future will be a matter
of adding it in kvm_csr_regs[] and everything else will be taken care
of.

Despite all the code changes no behavioral change is made.

[1] https://lore.kernel.org/qemu-riscv/CABJz62OfUDHYkQ0T3rGHStQprf1c7_E0qBLbLKhfv=+jb0SYAw@mail.gmail.com/

Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
Acked-by: Alistair Francis <alistair.francis@wdc.com>
Message-ID: <20250429124421.223883-6-dbarboza@ventanamicro.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit d3b6f1742c)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 10:03:56 +03:00
Daniel Henrique Barboza 90f7e23cb7 target/riscv/kvm: turn kvm_riscv_reg_id_ulong() into a macro
We need the reg_id_ulong() helper to be a macro to be able to create a
static array of KVMCPUConfig that will hold CSR information.

Despite the amount of changes all of them are tedious/trivial:

- replace instances of "kvm_riscv_reg_id_ulong" with
  "KVM_RISCV_REG_ID_ULONG";

- RISCV_CORE_REG(), RISCV_CSR_REG(), RISCV_CONFIG_REG() and
  RISCV_VECTOR_CSR_REG() only receives one 'name' arg. Remove unneeded
  'env' variables when applicable.

Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-ID: <20250429124421.223883-5-dbarboza@ventanamicro.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit 11766e1761)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 10:03:44 +03:00
Daniel Henrique Barboza ea8eb871ab target/riscv/kvm: turn u32/u64 reg functions into macros
This change is motivated by a future change w.r.t CSRs management. We
want to handle them the same way as KVM extensions, i.e. a static array
with KVMCPUConfig objs that will be read/write during init and so on.
But to do that properly we must be able to declare a static array that
hold KVM regs.

C does not allow to init static arrays and use functions as
initializers, e.g. we can't do:

.kvm_reg_id = kvm_riscv_reg_id_ulong(...)

When instantiating the array. We can do that with macros though, so our
goal is turn kvm_riscv_reg_ulong() in a macro. It is cleaner to turn
every other reg_id_*() function in macros, and ulong will end up using
the macros for u32 and u64, so we'll start with them.

Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-ID: <20250429124421.223883-4-dbarboza@ventanamicro.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit b609610349)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 10:03:27 +03:00
Daniel Henrique Barboza 04b855727f target/riscv/kvm: fix leak in kvm_riscv_init_multiext_cfg()
'reglist' is being g-malloc'ed but never freed.

Reported-by: Andrew Jones <ajones@ventanamicro.com>
Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-ID: <20250429124421.223883-3-dbarboza@ventanamicro.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit 906af6de94)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 10:01:56 +03:00
Daniel Henrique Barboza c500814317 target/riscv/kvm: minor fixes/tweaks
Remove an unused 'KVMScratchCPU' pointer argument in
kvm_riscv_check_sbi_dbcn_support().

Put kvm_riscv_reset_regs_csr() after kvm_riscv_put_regs_csr(). This will
make a future patch diff easier to read, when changes in
kvm_riscv_reset_regs_csr() and kvm_riscv_get_regs_csr() will be made.

Fixes: a6b53378f5 ("target/riscv/kvm: implement SBI debug console (DBCN) calls")
Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-ID: <20250429124421.223883-2-dbarboza@ventanamicro.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit 73f81da0a3)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 10:01:41 +03:00
Anton Blanchard 95c0e6e065 target/riscv: Fix vslidedown with rvv_ta_all_1s
vslidedown always zeroes elements past vl, where it should use the
tail policy.

Signed-off-by: Anton Blanchard <antonb@tenstorrent.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-ID: <20250414213006.3509058-1-antonb@tenstorrent.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit 2669b696e2)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 09:59:28 +03:00
Max Chou 7b21781562 target/riscv: Fix the rvv reserved encoding of unmasked instructions
According to the v spec, the encodings of vcomoress.vm and vector
mask-register logical instructions with vm=0 are reserved.

Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Signed-off-by: Max Chou <max.chou@sifive.com>
Message-ID: <20250408103938.3623486-11-max.chou@sifive.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit 8539a1244b)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 09:59:14 +03:00
Max Chou 43a4f232fa target/riscv: rvv: Apply vext_check_input_eew to vector indexed load/store instructions
Handle the overlap of source registers with different EEWs.

Co-authored-by: Anton Blanchard <antonb@tenstorrent.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Signed-off-by: Max Chou <max.chou@sifive.com>
Message-ID: <20250408103938.3623486-10-max.chou@sifive.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit db21c3eb05)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 09:59:09 +03:00
Max Chou be6e117a83 target/riscv: rvv: Apply vext_check_input_eew to vector narrow/widen instructions
Handle the overlap of source registers with different EEWs.
The vd of vector widening mul-add instructions is one of the input
operands.

Co-authored-by: Anton Blanchard <antonb@tenstorrent.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Signed-off-by: Max Chou <max.chou@sifive.com>
Message-ID: <20250408103938.3623486-9-max.chou@sifive.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit 1f090a229f)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 09:59:03 +03:00
Max Chou faaeaa955c target/riscv: rvv: Apply vext_check_input_eew to vector integer extension instructions(OPMVV)
Handle the overlap of source registers with different EEWs.

Co-authored-by: Anton Blanchard <antonb@tenstorrent.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Signed-off-by: Max Chou <max.chou@sifive.com>
Message-ID: <20250408103938.3623486-8-max.chou@sifive.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit 411eefd56a)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 09:58:56 +03:00
Max Chou 19d107468e target/riscv: rvv: Apply vext_check_input_eew to vector slide instructions(OPIVI/OPIVX)
Handle the overlap of source registers with different EEWs.

Co-authored-by: Anton Blanchard <antonb@tenstorrent.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Signed-off-by: Max Chou <max.chou@sifive.com>
Message-ID: <20250408103938.3623486-7-max.chou@sifive.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit b5480a693e)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 09:58:08 +03:00
Max Chou 2a0eb3c2a1 target/riscv: rvv: Apply vext_check_input_eew to OPIVV/OPFVV(vext_check_sss) instructions
Handle the overlap of source registers with different EEWs.

Co-authored-by: Anton Blanchard <antonb@tenstorrent.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Signed-off-by: Max Chou <max.chou@sifive.com>
Message-ID: <20250408103938.3623486-6-max.chou@sifive.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit fda68acb77)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 09:57:23 +03:00
Max Chou 0bfd8dd14b target/riscv: rvv: Apply vext_check_input_eew to OPIVI/OPIVX/OPFVF(vext_check_ss) instructions
Handle the overlap of source registers with different EEWs.

Co-authored-by: Anton Blanchard <antonb@tenstorrent.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Signed-off-by: Max Chou <max.chou@sifive.com>
Message-ID: <20250408103938.3623486-5-max.chou@sifive.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit fbeaf35838)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 09:57:07 +03:00
Max Chou bc502d4de9 target/riscv: rvv: Apply vext_check_input_eew to vrgather instructions to check mismatched input EEWs encoding constraint
According to the v spec, a vector register cannot be used to provide source
operands with more than one EEW for a single instruction.
The vs1 EEW of vrgatherei16.vv is 16.

Co-authored-by: Anton Blanchard <antonb@tenstorrent.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Signed-off-by: Max Chou <max.chou@sifive.com>
Message-ID: <20250408103938.3623486-4-max.chou@sifive.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit 629c2a8dd7)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 09:56:55 +03:00
Anton Blanchard 027ea4a2f5 target/riscv: rvv: Add CHECK arg to GEN_OPFVF_WIDEN_TRANS
Signed-off-by: Anton Blanchard <antonb@tenstorrent.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Reviewed-by: Max Chou <max.chou@sifive.com>
Signed-off-by: Max Chou <max.chou@sifive.com>
Message-ID: <20250408103938.3623486-3-max.chou@sifive.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit b0450a101d)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 09:56:46 +03:00
Anton Blanchard 336fed6bb8 target/riscv: rvv: Source vector registers cannot overlap mask register
Add the relevant ISA paragraphs explaining why source (and destination)
registers cannot overlap the mask register.

Signed-off-by: Anton Blanchard <antonb@tenstorrent.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Reviewed-by: Max Chou <max.chou@sifive.com>
Signed-off-by: Max Chou <max.chou@sifive.com>
Message-ID: <20250408103938.3623486-2-max.chou@sifive.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit 3e8d1e4a62)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 09:56:38 +03:00
Icenowy 70dbbc2811 common-user/host/riscv: use tail pseudoinstruction for calling tail
The j pseudoinstruction maps to a JAL instruction, which can only handle
a jump to somewhere with a signed 20-bit destination. In case of static
linking and LTO'ing this easily leads to "relocation truncated to fit"
error.

Switch to use tail pseudoinstruction, which is the standard way to
tail-call a function in medium code model (emits AUIPC+JALR).

Signed-off-by: Icenowy Zheng <uwu@icenowy.me>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-ID: <20250417072206.364008-1-uwu@icenowy.me>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit 22b448ccc6)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 09:56:01 +03:00
Ziqiao Kong 156321d18e target/riscv: fix endless translation loop on big endian systems
On big endian systems, pte and updated_pte hold big endian host data
while pte_pa points to little endian target data. This means the branch
at cpu_helper.c:1669 will be always satisfied and restart translation,
causing an endless translation loop.

The correctness of this patch can be deduced by:

old_pte will hold value either from cpu_to_le32/64(pte) or
cpu_to_le32/64(updated_pte), both of wich is litte endian. After that,
an in-place conversion by le32/64_to_cpu(old_pte) ensures that old_pte
now is in native endian, same with pte. Therefore, the endianness of the
both side of if (old_pte != pte) is correct.

Signed-off-by: Ziqiao Kong <ziqiaokong@gmail.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-ID: <20250415080254.3667878-2-ziqiaokong@gmail.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit ad63158bdb)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 09:55:46 +03:00
Paolo Bonzini b76d4a5657 hw/riscv: Fix type conflict of GLib function pointers
qtest_set_command_cb passed to g_once should match GThreadFunc,
which it does not.  But using g_once is actually unnecessary,
because the function is called by riscv_harts_realize() under
the Big QEMU Lock.

Reported-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Message-ID: <20250410161722.595634-1-pbonzini@redhat.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit 56cde18d04)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 09:55:35 +03:00
Loïc Lefort bc15a8db4f target/riscv: pmp: fix checks on writes to pmpcfg in Smepmp MML mode
With Machine Mode Lockdown (mseccfg.MML) set and RLB not set, checks on pmpcfg
writes would match the wrong cases of Smepmp truth table.

The existing code allows writes for the following cases:
- L=1, X=0: cases 8, 10, 12, 14
- L=0, RWX!=WX: cases 0-2, 4-6
This leaves cases 3, 7, 9, 11, 13, 15 for which writes are ignored.

From the Smepmp specification: "Adding a rule with executable privileges that
either is M-mode-only or a locked Shared-Region is not possible (...)" This
description matches cases 9-11, 13 of the truth table.

This commit implements an explicit check for these cases by using
pmp_get_epmp_operation to convert between PMP configuration and Smepmp truth
table cases.

Signed-off-by: Loïc Lefort <loic@rivosinc.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Reviewed-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com>
Message-ID: <20250313193011.720075-4-loic@rivosinc.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit 19cf1a7d9e)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 09:55:13 +03:00
Loïc Lefort 504dcda206 target/riscv: pmp: move Smepmp operation conversion into a function
Signed-off-by: Loïc Lefort <loic@rivosinc.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com>
Message-ID: <20250313193011.720075-3-loic@rivosinc.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit 915b203745)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 09:54:34 +03:00
Loïc Lefort f82dd2837c target/riscv: pmp: don't allow RLB to bypass rule privileges
When Smepmp is supported, mseccfg.RLB allows bypassing locks when writing CSRs
but should not affect interpretation of actual PMP rules.

This is not the case with the current implementation where pmp_hart_has_privs
calls pmp_is_locked which implements mseccfg.RLB bypass.

This commit implements the correct behavior by removing mseccfg.RLB bypass from
pmp_is_locked.

RLB bypass when writing CSRs is implemented by adding a new pmp_is_readonly
function that calls pmp_is_locked and check mseccfg.RLB. pmp_write_cfg and
pmpaddr_csr_write are changed to use this new function.

Signed-off-by: Loïc Lefort <loic@rivosinc.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Reviewed-by: LIU Zhiwei  <zhiwei_liu@linux.alibaba.com>
Message-ID: <20250313193011.720075-2-loic@rivosinc.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Cc: qemu-stable@nongnu.org
(cherry picked from commit 4541d205f0)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-20 09:54:12 +03:00
Klaus Jensen 15143814cd hw/nvme: fix nvme hotplugging
Commit cd59f50ab0 caused a regression on nvme hotplugging for devices
with an implicit nvm subsystem.

The nvme-subsys device was incorrectly left with being marked as
non-hotpluggable. Fix this.

Cc: qemu-stable@nongnu.org
Reported-by: Stéphane Graber <stgraber@stgraber.org>
Tested-by: Stéphane Graber <stgraber@stgraber.org>
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2950
Fixes: cd59f50ab0 ("hw/nvme: always initialize a subsystem")
Reviewed-by: Keith Busch <kbusch@kernel.org>
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
(cherry picked from commit 0b1c23a582)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-16 01:04:12 +03:00
Akihiko Odaki 40558266b1 virtio: Call set_features during reset
virtio-net expects set_features() will be called when the feature set
used by the guest changes to update the number of virtqueues but it is
not called during reset, which will clear all features, leaving the
queues added for VIRTIO_NET_F_MQ or VIRTIO_NET_F_RSS. Not only these
extra queues are visible to the guest, they will cause segmentation
fault during migration.

Call set_features() during reset to remove those queues for virtio-net
as we call set_status(). It will also prevent similar bugs for
virtio-net and other devices in the future.

Fixes: f9d6dbf0bf ("virtio-net: remove virtio queues if the guest doesn't support multiqueue")
Buglink: https://issues.redhat.com/browse/RHEL-73842
Cc: qemu-stable@nongnu.org
Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com>
Message-Id: <20250421-reset-v2-1-e4c1ead88ea1@daynix.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit 0caed25cd1)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-16 01:00:59 +03:00
Fabiano Rosas 8efe15927b s390x: Fix leak in machine_set_loadparm
ASAN spotted a leaking string in machine_set_loadparm():

Direct leak of 9 byte(s) in 1 object(s) allocated from:
    #0 0x560ffb5bb379 in malloc ../projects/compiler-rt/lib/asan/asan_malloc_linux.cpp:69:3
    #1 0x7f1aca926518 in g_malloc ../glib/gmem.c:106
    #2 0x7f1aca94113e in g_strdup ../glib/gstrfuncs.c:364
    #3 0x560ffc8afbf9 in qobject_input_type_str ../qapi/qobject-input-visitor.c:542:12
    #4 0x560ffc8a80ff in visit_type_str ../qapi/qapi-visit-core.c:349:10
    #5 0x560ffbe6053a in machine_set_loadparm ../hw/s390x/s390-virtio-ccw.c:802:10
    #6 0x560ffc0c5e52 in object_property_set ../qom/object.c:1450:5
    #7 0x560ffc0d4175 in object_property_set_qobject ../qom/qom-qobject.c:28:10
    #8 0x560ffc0c6004 in object_property_set_str ../qom/object.c:1458:15
    #9 0x560ffbe2ae60 in update_machine_ipl_properties ../hw/s390x/ipl.c:569:9
    #10 0x560ffbe2aa65 in s390_ipl_update_diag308 ../hw/s390x/ipl.c:594:5
    #11 0x560ffbdee132 in handle_diag_308 ../target/s390x/diag.c:147:9
    #12 0x560ffbebb956 in helper_diag ../target/s390x/tcg/misc_helper.c:137:9
    #13 0x7f1a3c51c730  (/memfd:tcg-jit (deleted)+0x39730)

Cc: qemu-stable@nongnu.org
Signed-off-by: Fabiano Rosas <farosas@suse.de>
Message-ID: <20250509174938.25935-1-farosas@suse.de>
Fixes: 1fd396e322 ("s390x: Register TYPE_S390_CCW_MACHINE properties as class properties")
Reviewed-by: Thomas Huth <thuth@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Thomas Huth <thuth@redhat.com>
(cherry picked from commit bdf12f2a56)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-16 00:54:49 +03:00
Christian Schoenebeck a0f326228a 9pfs: fix FD leak and reduce latency of v9fs_reclaim_fd()
This patch fixes two different bugs in v9fs_reclaim_fd():

1. Reduce latency:

This function calls v9fs_co_close() and v9fs_co_closedir() in a loop. Each
one of the calls adds two thread hops (between main thread and a fs driver
background thread). Each thread hop adds latency, which sums up in
function's loop to a significant duration.

Reduce overall latency by open coding what v9fs_co_close() and
v9fs_co_closedir() do, executing those and the loop itself altogether in
only one background thread block, hence reducing the total amount of
thread hops to only two.

2. Fix file descriptor leak:

The existing code called v9fs_co_close() and v9fs_co_closedir() to close
file descriptors. Both functions check right at the beginning if the 9p
request was cancelled:

    if (v9fs_request_cancelled(pdu)) {
        return -EINTR;
    }

So if client sent a 'Tflush' message, v9fs_co_close() / v9fs_co_closedir()
returned without having closed the file descriptor and v9fs_reclaim_fd()
subsequently freed the FID without its file descriptor being closed, hence
leaking those file descriptors.

This 2nd bug is fixed by this patch as well by open coding v9fs_co_close()
and v9fs_co_closedir() inside of v9fs_reclaim_fd() and not performing the
v9fs_request_cancelled(pdu) check there.

Fixes: 7a46274529 ('hw/9pfs: Add file descriptor reclaim support')
Fixes: bccacf6c79 ('hw/9pfs: Implement TFLUSH operation')
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
Reviewed-by: Greg Kurz <groug@kaod.org>
Message-Id: <5747469d3f039c53147e850b456943a1d4b5485c.1741339452.git.qemu_oss@crudebyte.com>
(cherry picked from commit 89f7b4da76)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-12 12:27:03 +03:00
Christian Schoenebeck 5081dc508d 9pfs: fix concurrent v9fs_reclaim_fd() calls
Even though this function is serialized to be always called from main
thread, v9fs_reclaim_fd() is dispatching the coroutine to a worker thread
in between via its v9fs_co_*() calls, hence leading to the situation where
v9fs_reclaim_fd() is effectively executed multiple times simultaniously,
which renders its LRU algorithm useless and causes high latency.

Fix this by adding a simple boolean variable to ensure this function is
only called once at a time. No synchronization needed for this boolean
variable as this function is only entered and returned on main thread.

Fixes: 7a46274529 ('hw/9pfs: Add file descriptor reclaim support')
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
Reviewed-by: Greg Kurz <groug@kaod.org>
Message-Id: <5c622067efd66dd4ee5eca740dcf263f41db20b2.1741339452.git.qemu_oss@crudebyte.com>
(cherry picked from commit 61da38db70)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-12 12:27:03 +03:00
Bernhard Beschow 9340920c3f hw/i2c/imx: Always set interrupt status bit if interrupt condition occurs
According to the i.MX 8M Plus reference manual, the status flag
I2C_I2SR[IIF] continues to be set when an interrupt condition
occurs even when I2C interrupts are disabled (I2C_I2CR[IIEN] is
clear). However, the device model only sets the flag when I2C
interrupts are enabled which causes U-Boot to loop forever. Fix
the device model by always setting the flag and let I2C_I2CR[IIEN]
guard I2C interrupts only.

Also remove the comment in the code since it merely stated the
obvious and would be outdated now.

Cc: qemu-stable@nongnu.org
Fixes: 20d0f9cf6a ("i.MX: Add I2C controller emulator")
Signed-off-by: Bernhard Beschow <shentey@gmail.com>
Acked-by: Corey Minyard <cminyard@mvista.com>
Message-ID: <20250507124040.425773-1-shentey@gmail.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
(cherry picked from commit 54e54e594b)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-11 09:16:33 +03:00
Edgar E. Iglesias bfa3f55f0f xen: mapcache: Split mapcache_grants by ro and rw
Today, we don't track write-abiliy in the cache, if a user
requests a readable mapping followed by a writeable mapping
on the same page, the second lookup will incorrectly hit
the readable entry.

Split mapcache_grants by ro and rw access. Grants will now
have separate ways in the cache depending on writeability.

Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com>
(cherry picked from commit 88fb705600)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-11 09:15:20 +03:00
Aleksandr Partanen a03d7d6e32 xen: mapcache: Fix finding matching entry
If we have request without lock and hit unlocked or invalid
entry during the search, we remap it immediately,
even if we have matching entry in next entries in bucket.
This leads to duplication of mappings of the same size,
and to possibility of selecting the wrong element
during invalidation and underflow it's entry->lock counter

Signed-off-by: Aleksandr Partanen <alex.pentagrid@gmail.com>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
Reviewed-by: Edgar E. Iglesias <edgar.iglesias@amd.com>
Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com>
(cherry picked from commit a4b20f737c)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-11 09:15:20 +03:00
Paolo Bonzini 827be9d37a target/i386: do not block singlestep for STI
STI will trigger a singlestep exception even if it has inhibit-IRQ
behavior.  Do not suppress single-step for all IRQ-inhibiting
instructions, instead special case MOV SS and POP SS.

Cc: qemu-stable@nongnu.org
Fixes: f0f0136abb ("target/i386: no single-step exception after MOV or POP SS", 2024-05-25)
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 1e94ddc685)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-08 12:38:50 +03:00
Paolo Bonzini f60033d379 target/i386: do not trigger IRQ shadow for LSS
Because LSS need not trigger an IRQ shadow, gen_movl_seg can't just use
the destination register to decide whether to inhibit IRQs.  Add an
argument.

Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit e54ef98c8a)
(back-ported to 10.0)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-08 12:36:23 +03:00
Bernhard Beschow 01a9f1a6c5 hw/gpio/imx_gpio: Fix interpretation of GDIR polarity
According to the i.MX 8M Plus reference manual, a GPIO pin is
configured as an output when the corresponding bit in the GDIR
register is set.  The function imx_gpio_set_int_line() is intended to
be a no-op if the pin is configured as an output, returning early in
such cases.  However, it inverts the condition.  Fix this by
returning early when the bit is set.

cc: qemu-stable@nongnu.org
Fixes: f442728097 ("i.MX: Add GPIO device")
Signed-off-by: Bernhard Beschow <shentey@gmail.com>
Message-id: 20250501183445.2389-4-shentey@gmail.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit eba837a31b)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-08 12:18:57 +03:00
Peter Maydell be7b08eb9f docs: Don't define duplicate label in qemu-block-drivers.rst.inc
Sphinx requires that labels within documents are unique across the
whole manual.  This is because the "create a hyperlink" directive
specifies only the name of the label, not a filename+label.  Some
Sphinx versions will warn about duplicate labels, but even if there
is no warning there is still an ambiguity and no guarantee that the
hyperlink will be created to the right target.

For QEMU this is awkward, because we have various .rst.inc fragments
which we include into multiple .rst files.  If you define a label in
the .rst.inc file then it will be a duplicate label.  We have mostly
worked around this by not putting labels into those .rst.inc files,
or by adding "insert a label" functionality into the hxtool extension
(see commit 1eeb432a95 "doc/sphinx/hxtool.py: add optional label
argument to SRST directive").

Unfortunately in commit 7f6314427e ("docs/devel: add a codebase
section") we accidentally added a duplicate label, because not all
Sphinx versions warn about the mistake.

In this case the link was only from the developer docs codebase
summary, so as the simplest fix for the stable branch, we drop
the link entirely.

Cc: qemu-stable@nongnu.org
Fixes: 1eeb432a95 "doc/sphinx/hxtool.py: add optional label argument to SRST directive"
Reported-by: Dario Faggioli <dfaggioli@suse.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Acked-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Message-id: 20250501093126.716667-1-peter.maydell@linaro.org
(cherry picked from commit 82707dd4f0)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-08 10:53:58 +03:00
Peter Maydell ac32612b93 target/arm: Don't assert() for ISB/SB inside IT block
If the guest code has an ISB or SB insn inside an IT block, we
generate incorrect code which trips a TCG assertion:

qemu-system-arm: ../tcg/tcg-op.c:3343: void tcg_gen_goto_tb(unsigned int): Assertion `(tcg_ctx->goto_tb_issue_mask & (1 << idx)) == 0' failed.

This is because we call gen_goto_tb(dc, 1, ...) twice:

 brcond_i32 ZF,$0x0,ne,$L1
 add_i32 pc,pc,$0x4
 goto_tb $0x1
 exit_tb $0x73d948001b81
 set_label $L1
 add_i32 pc,pc,$0x4
 goto_tb $0x1
 exit_tb $0x73d948001b81

Both calls are in arm_tr_tb_stop(), one for the
DISAS_NEXT/DISAS_TOO_MANY handling, and one for the dc->condjump
condition-failed codepath.  The DISAS_NEXT handling doesn't have this
problem because arm_post_translate_insn() does the handling of "emit
the label for the condition-failed conditional execution" and so
arm_tr_tb_stop() doesn't have dc->condjump set.  But for
DISAS_TOO_MANY we don't do that.

Fix the bug by making arm_post_translate_insn() handle the
DISAS_TOO_MANY case.  This only affects the SB and ISB insns when
used in Thumb mode inside an IT block: only these insns specifically
set is_jmp to TOO_MANY, and their A32 encodings are unconditional.

For the major TOO_MANY case (breaking the TB because it would cross a
page boundary) we do that check and set is_jmp to TOO_MANY only after
the call to arm_post_translate_insn(); so arm_post_translate_insn()
sees is_jmp == DISAS_NEXT, and  we emit the correct code for that
situation.

With this fix we generate the somewhat more sensible set of TCG ops:
 brcond_i32 ZF,$0x0,ne,$L1
 set_label $L1
 add_i32 pc,pc,$0x4
 goto_tb $0x1
 exit_tb $0x7c5434001b81

(NB: the TCG optimizer doesn't optimize out the jump-to-next, but
we can't really avoid emitting it because we don't know at the
point we're emitting the handling for the condexec check whether
this insn is going to happen to be a nop for us or not.)

Cc: qemu-stable@nongnu.org
Fixes: https://gitlab.com/qemu-project/qemu/-/issues/2942
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20250501125544.727038-1-peter.maydell@linaro.org
(cherry picked from commit 8ed7c0b648)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-08 10:22:21 +03:00
Tim Lee 07f034c7f4 hw/arm/npcm8xx_boards: Correct valid_cpu_types setting of NPCM8XX SoC
NPCM8XX SoC is the successor of the NPCM7XX. It features quad-core
Cortex-A35 (Armv8, 64-bit) CPUs and some additional peripherals.
Correct the `valid_cpu_types` setting to match the NPCM8XX SoC.

Cc: qemu-stable@nongnu.org
Fixes: 7e70eb3cad ("hw/arm: Add NPCM845 Evaluation board")
Signed-off-by: Tim Lee <timlee660101@gmail.com>
Message-id: 20250428022934.3081139-1-timlee660101@gmail.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 97cdd1b0a7)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-08 10:20:54 +03:00
Richard Henderson 86b846f902 accel/tcg: Don't use TARGET_LONG_BITS in decode_sleb128
When we changed decode_sleb128 from target_long to
int64_t, we failed to adjust the shift limit.

Cc: qemu-stable@nongnu.org
Fixes: c9ad8d27ca ("tcg: Widen gen_insn_data to uint64_t")
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
(cherry picked from commit 9401f91b9b)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-05-07 16:42:10 +03:00
Akihiko Odaki 8a9a719314 meson: Use osdep_prefix for strchrnul()
macOS SDK may have the symbol of strchrnul(), but it is actually
available only on macOS 15.4 or later and that fact is codified in
string.h. Include the header file using osdep_prefix to check if the
function is available on the deployment target.

Cc: qemu-stable@nongnu.org
Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Message-ID: <20250424-buildsys-v1-4-97655e3b25d7@daynix.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
(cherry picked from commit a5b30be534)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-04-29 10:07:56 +03:00
Akihiko Odaki e1ccfea380 meson: Share common C source prefixes
gnu_source_prefix defines _GNU_SOURCE for compiler object functions.
The definition is universally available in the code base.

docs/devel/style.rst also says that the "qemu/osdep.h" header is
always included, so files included in the file is also universally
available in the code base.

Rename gnu_source_prefix to osdep_prefix, and add #include directives
that are referred by the users of gnu_source_prefix and contained in
qemu/osdep.h to safely de-duplicate #include directives.

Cc: qemu-stable@nongnu.org
Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Message-ID: <20250424-buildsys-v1-3-97655e3b25d7@daynix.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
(cherry picked from commit 797150d69d)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-04-29 10:07:19 +03:00
Akihiko Odaki 1604055e4e meson: Remove CONFIG_STATX and CONFIG_STATX_MNT_ID
CONFIG_STATX and CONFIG_STATX_MNT_ID are not used since commit
e0dc2631ec ("virtiofsd: Remove source").

Cc: qemu-stable@nongnu.org
Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Message-ID: <20250424-buildsys-v1-2-97655e3b25d7@daynix.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
(cherry picked from commit 6804b89fb5)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-04-29 10:07:03 +03:00
Akihiko Odaki 7bff88a745 meson: Use has_header_symbol() to check getcpu()
The use of gnu_source_prefix in the detection of getcpu() was
ineffective because the header file that declares getcpu() when
_GNU_SOURCE is defined was not included. Pass sched.h to
has_header_symbol() so that the existence of the declaration will be
properly checked.

Cc: qemu-stable@nongnu.org
Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Message-ID: <20250424-buildsys-v1-1-97655e3b25d7@daynix.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
(cherry picked from commit 563cd698df)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-04-29 10:04:24 +03:00
Hauke Mehrtens e60bbdad93 target/mips: Fix MIPS16e translation
Fix a wrong conversion to gen_op_addr_addi(). The framesize should be
added like it was done before.

This bug broke booting OpenWrt MIPS32 BE malta Linux system images
generated by OpenWrt.

Cc: qemu-stable@nongnu.org
Fixes: d0b24b7f50 ("target/mips: Use gen_op_addr_addi() when possible")
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Message-ID: <20250412194003.181411-1-hauke@hauke-m.de>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
(cherry picked from commit d4a785ba30)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-04-29 10:03:15 +03:00
Peter Maydell dbbb444d72 hw/core/cpu: gdb_arch_name string should not be freed
The documentation for the CPUClass::gdb_arch_name method claims that
the returned string should be freed with g_free().  This is not
correct: in commit a650683871 we changed this method to
instead return a simple constant string, but forgot to update
the documentation.

Make the documentation match the new semantics.

Fixes: a650683871 ("hw/core/cpu: Return static value with gdb_arch_name()")
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Message-ID: <20250317142819.900029-2-peter.maydell@linaro.org>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
(cherry picked from commit 56a9f0d4c4)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-04-29 10:02:32 +03:00
Philippe Mathieu-Daudé 94da90b8c7 hw/core: Get default_cpu_type calling machine_class_default_cpu_type()
Since commit 62b4a227a3 the default cpu type can come from the
valid_cpu_types[] array. Call the machine_class_default_cpu_type()
instead of accessing MachineClass::default_cpu_type field.

Cc: qemu-stable@nongnu.org
Fixes: 62b4a227a3 ("hw/core: Add machine_class_default_cpu_type()")
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Message-Id: <20250422084114.39499-1-philmd@linaro.org>
(cherry picked from commit d5f241834b)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-04-29 10:01:21 +03:00
Richard Henderson 2da497fd4f target/avr: Improve decode of LDS, STS
The comment about not being able to define a field with
zero bits is out of date since 94597b6146
("decodetree: Allow !function with no input bits").

This fixes the missing load of imm in the disassembler.

Cc: qemu-stable@nongnu.org
Fixes: 9d8caa67a2 ("target/avr: Add support for disassembling via option '-d in_asm'")
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
(cherry picked from commit 6b661b7ed7)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-04-24 10:30:44 +03:00
Paolo Bonzini 00a5dc2899 target/i386/hvf: fix lflags_to_rflags
Clear the flags before adding in the ones computed from lflags.

Cc: Wei Liu <liuwe@linux.microsoft.com>
Cc: qemu-stable@nongnu.org
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 94a159f3dc)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-04-24 10:27:39 +03:00
Maciej S. Szmigiero 86ffc25d35 target/i386: Reset parked vCPUs together with the online ones
Commit 3f2a05b31e ("target/i386: Reset TSCs of parked vCPUs too on VM
reset") introduced a way to reset TSCs of parked vCPUs during VM reset to
prevent them getting desynchronized with the online vCPUs and therefore
causing the KVM PV clock to lose PVCLOCK_TSC_STABLE_BIT.

The way this was done was by registering a parked vCPU-specific QEMU reset
callback via qemu_register_reset().

However, it turns out that on particularly device-rich VMs QEMU reset
callbacks can take a long time to execute (which isn't surprising,
considering that they involve resetting all of VM devices).

In particular, their total runtime can exceed the 1-second TSC
synchronization window introduced in KVM commit 5d3cb0f6a8e3 ("KVM:
Improve TSC offset matching").
Since the TSCs of online vCPUs are only reset from "synchronize_post_reset"
AccelOps handler (which runs after all qemu_register_reset() handlers) this
essentially makes that fix ineffective on these VMs.

The easiest way to guarantee that these parked vCPUs are reset at the same
time as the online ones (regardless how long it takes for VM devices to
reset) is to piggyback on post-reset vCPU synchronization handler for one
of online vCPUs - as there is no generic post-reset AccelOps handler that
isn't per-vCPU.

The first online vCPU was selected for that since it is easily available
under "first_cpu" define.
This does not create an ordering issue since the order of vCPU TSC resets
does not matter.

Fixes: 3f2a05b31e ("target/i386: Reset TSCs of parked vCPUs too on VM reset")
Signed-off-by: Maciej S. Szmigiero <maciej.szmigiero@oracle.com>
Link: https://lore.kernel.org/r/e8b85a5915f79aa177ca49eccf0e9b534470c1cd.1743099810.git.maciej.szmigiero@oracle.com
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit f6b5f71f04)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-04-24 10:27:02 +03:00
Ewan Hai 3af56fc07f target/i386: Fix model number of Zhaoxin YongFeng vCPU template
The model number was mistakenly set to 0x0b (11) in commit ff04bc1ac4.
The correct value is 0x5b. This mistake occurred because the extended
model bits in cpuid[eax=0x1].eax were overlooked, and only the base
model was used.

Using the wrong model number can affect guest behavior. One known issue
is that vPMU (which relies on the model number) may fail to operate
correctly.

This patch corrects the model field by introducing a new vCPU version.

Fixes: ff04bc1ac4 ("target/i386: Introduce Zhaoxin Yongfeng CPU model")
Signed-off-by: Ewan Hai <ewanhai-oc@zhaoxin.com>
Link: https://lore.kernel.org/r/20250414075342.411626-1-ewanhai-oc@zhaoxin.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 280712b787)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2025-04-24 10:25:09 +03:00
4111 changed files with 89791 additions and 183974 deletions
+1
View File
@@ -11,3 +11,4 @@
prep-perpatch-check-cmd = scripts/checkpatch.pl -q --terse --no-summary --mailback -
searchmask = https://lore.kernel.org/qemu-devel/?x=m&t=1&q=%s
linkmask = https://lore.kernel.org/qemu-devel/%s
linktrailermask = Message-ID: <%s>
+1 -1
View File
@@ -55,7 +55,7 @@ indent_size = 4
emacs_mode = perl
# but user kernel "style" for imported scripts
[scripts/{get_maintainer.pl,checkpatch.pl}]
[scripts/{kernel-doc,get_maintainer.pl,checkpatch.pl}]
indent_style = tab
indent_size = 8
emacs_mode = perl
+6 -2
View File
@@ -69,6 +69,10 @@ variables:
- if: '$QEMU_CI != "1" && $QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM'
when: never
# Avocado jobs don't run in forks unless $QEMU_CI_AVOCADO_TESTING is set
- if: '$QEMU_JOB_AVOCADO && $QEMU_CI_AVOCADO_TESTING != "1" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM'
when: never
#############################################################
# Stage 2: fine tune execution of jobs in specific scenarios
@@ -97,8 +101,8 @@ variables:
when: manual
allow_failure: true
# Functional jobs can be manually started in forks
- if: '$QEMU_JOB_FUNCTIONAL && $QEMU_CI_FUNCTIONAL != "1" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM'
# Avocado jobs can be manually start in forks if $QEMU_CI_AVOCADO_TESTING is unset
- if: '$QEMU_JOB_AVOCADO && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM'
when: manual
allow_failure: true
+18 -42
View File
@@ -24,7 +24,6 @@
- ccache --zero-stats
- section_start configure "Running configure"
- ../configure --enable-werror --disable-docs --enable-fdt=system
--disable-debug-info
${TARGETS:+--target-list="$TARGETS"}
$CONFIGURE_ARGS ||
{ cat config.log meson-logs/meson-log.txt && exit 1; }
@@ -77,30 +76,26 @@
fi
- section_end buildenv
- section_start test "Running tests"
# doctests need all the compilation artifacts
- $MAKE NINJA=":" MTESTARGS="--no-suite doc" $MAKE_CHECK_ARGS
- $MAKE NINJA=":" $MAKE_CHECK_ARGS
- section_end test
.native_test_job_template:
extends: .common_test_job_template
before_script:
# Prevent logs from the build job that run earlier
# from being duplicated in the test job artifacts
- rm -f build/meson-logs/*
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
when: always
expire_in: 7 days
paths:
- build/meson-logs
- build/meson-logs/testlog.txt
reports:
junit: build/meson-logs/*.junit.xml
junit: build/meson-logs/testlog.junit.xml
.functional_test_job_template:
extends: .common_test_job_template
cache:
key: "${CI_JOB_NAME}-cache"
paths:
- ${CI_PROJECT_DIR}/avocado-cache
- ${CI_PROJECT_DIR}/functional-cache
policy: pull-push
artifacts:
@@ -108,45 +103,26 @@
when: always
expire_in: 7 days
paths:
- build/meson-logs
- build/tests/results/latest/results.xml
- build/tests/results/latest/test-results
- build/tests/functional/*/*/*.log
reports:
junit: build/meson-logs/*.junit.xml
junit: build/tests/results/latest/results.xml
before_script:
- mkdir -p ~/.config/avocado
- echo "[datadir.paths]" > ~/.config/avocado/avocado.conf
- echo "cache_dirs = ['${CI_PROJECT_DIR}/avocado-cache']"
>> ~/.config/avocado/avocado.conf
- echo -e '[job.output.testlogs]\nstatuses = ["FAIL", "INTERRUPT"]'
>> ~/.config/avocado/avocado.conf
- if [ -d ${CI_PROJECT_DIR}/avocado-cache ]; then
du -chs ${CI_PROJECT_DIR}/*-cache ;
fi
- export AVOCADO_ALLOW_UNTRUSTED_CODE=1
- export QEMU_TEST_ALLOW_UNTRUSTED_CODE=1
- export QEMU_TEST_CACHE_DIR=${CI_PROJECT_DIR}/functional-cache
# Prevent logs from the build job that run earlier
# from being duplicated in the test job artifacts
- rm -f build/meson-logs/*
after_script:
- cd build
- du -chs ${CI_PROJECT_DIR}/*-cache
variables:
QEMU_JOB_FUNCTIONAL: 1
.wasm_build_job_template:
extends: .base_job_template
stage: build
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG
before_script:
- source scripts/ci/gitlab-ci-section
- section_start setup "Pre-script setup"
- JOBS=$(expr $(nproc) + 1)
- section_end setup
script:
- du -sh .git
- mkdir build
- cd build
- section_start configure "Running configure"
- emconfigure ../configure --disable-docs
${TARGETS:+--target-list="$TARGETS"}
$CONFIGURE_ARGS ||
{ cat config.log meson-logs/meson-log.txt && exit 1; }
- if test -n "$LD_JOBS";
then
pyvenv/bin/meson configure . -Dbackend_max_links="$LD_JOBS" ;
fi || exit 1;
- section_end configure
- section_start build "Building QEMU"
- emmake make -j"$JOBS"
- section_end build
QEMU_JOB_AVOCADO: 1
+76 -70
View File
@@ -29,14 +29,15 @@ functional-system-alpine:
artifacts: true
variables:
IMAGE: alpine
MAKE_CHECK_ARGS: check-functional
MAKE_CHECK_ARGS: check-avocado check-functional
AVOCADO_TAGS: arch:avr arch:loongarch64 arch:mips64 arch:mipsel
build-system-ubuntu:
extends:
- .native_build_job_template
- .native_build_artifact_template
needs:
- job: amd64-ubuntu2204-container
job: amd64-ubuntu2204-container
variables:
IMAGE: ubuntu2204
CONFIGURE_ARGS: --enable-docs --enable-rust
@@ -59,14 +60,15 @@ functional-system-ubuntu:
artifacts: true
variables:
IMAGE: ubuntu2204
MAKE_CHECK_ARGS: check-functional
MAKE_CHECK_ARGS: check-avocado check-functional
AVOCADO_TAGS: arch:alpha arch:microblazeel arch:mips64el
build-system-debian:
extends:
- .native_build_job_template
- .native_build_artifact_template
needs:
- job: amd64-debian-container
job: amd64-debian-container
variables:
IMAGE: debian
CONFIGURE_ARGS: --with-coroutine=sigaltstack --enable-rust
@@ -90,7 +92,8 @@ functional-system-debian:
artifacts: true
variables:
IMAGE: debian
MAKE_CHECK_ARGS: check-functional
MAKE_CHECK_ARGS: check-avocado check-functional
AVOCADO_TAGS: arch:arm arch:i386 arch:riscv64 arch:sh4 arch:sparc arch:xtensa
crash-test-debian:
extends: .native_test_job_template
@@ -109,25 +112,30 @@ build-system-fedora:
- .native_build_job_template
- .native_build_artifact_template
needs:
- job: amd64-fedora-container
job: amd64-fedora-container
variables:
IMAGE: fedora
CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs --enable-crypto-afalg --enable-rust
TARGETS: microblaze-softmmu mips-softmmu
xtensa-softmmu m68k-softmmu riscv32-softmmu ppc-softmmu sparc64-softmmu
MAKE_CHECK_ARGS: check-build check-doc
MAKE_CHECK_ARGS: check-build
build-system-fedora-rust-nightly:
extends:
- .native_build_job_template
- .native_build_artifact_template
needs:
- job: amd64-fedora-rust-nightly-container
job: amd64-fedora-rust-nightly-container
variables:
IMAGE: fedora-rust-nightly
CONFIGURE_ARGS: --disable-docs --enable-rust --enable-strict-rust-lints
TARGETS: aarch64-softmmu
MAKE_CHECK_ARGS: check-build check-doc
MAKE_CHECK_ARGS: check-build
after_script:
- source scripts/ci/gitlab-ci-section
- section_start test "Running Rust doctests"
- cd build
- pyvenv/bin/meson devenv -w ../rust ${CARGO-cargo} test --doc -p qemu_api
allow_failure: true
@@ -147,7 +155,9 @@ functional-system-fedora:
artifacts: true
variables:
IMAGE: fedora
MAKE_CHECK_ARGS: check-functional
MAKE_CHECK_ARGS: check-avocado check-functional
AVOCADO_TAGS: arch:microblaze arch:mips arch:xtensa arch:m68k
arch:riscv32 arch:ppc arch:sparc64
crash-test-fedora:
extends: .native_test_job_template
@@ -167,7 +177,7 @@ build-system-centos:
- .native_build_job_template
- .native_build_artifact_template
needs:
- job: amd64-centos9-container
job: amd64-centos9-container
variables:
IMAGE: centos9
CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-vfio-user-server
@@ -183,13 +193,14 @@ build-previous-qemu:
when: on_success
expire_in: 2 days
paths:
- build-previous/qemu-bundle
- build-previous/qemu-system-aarch64
- build-previous/qemu-system-x86_64
- build-previous/tests/qtest/migration-test
- build-previous/scripts
- build-previous
exclude:
- build-previous/**/*.p
- build-previous/**/*.a.p
- build-previous/**/*.c.o
- build-previous/**/*.c.o.d
needs:
- job: amd64-opensuse-leap-container
job: amd64-opensuse-leap-container
variables:
IMAGE: opensuse-leap
TARGETS: x86_64-softmmu aarch64-softmmu
@@ -197,11 +208,6 @@ build-previous-qemu:
GIT_FETCH_EXTRA_FLAGS: --prune --quiet
before_script:
- source scripts/ci/gitlab-ci-section
# Skip if this series contains the release bump commit. During the
# release process there might be a window of commits when the
# version tag is not yet present in the remote and git fetch would
# fail.
- if grep -q "\.0$" VERSION; then exit 0; fi
- export QEMU_PREV_VERSION="$(sed 's/\([0-9.]*\)\.[0-9]*/v\1.0/' VERSION)"
- git remote add upstream https://gitlab.com/qemu-project/qemu
- git fetch upstream refs/tags/$QEMU_PREV_VERSION:refs/tags/$QEMU_PREV_VERSION
@@ -222,13 +228,18 @@ build-previous-qemu:
IMAGE: opensuse-leap
MAKE_CHECK_ARGS: check-build
script:
# Skip for round release numbers, this job is only relevant for
# testing a development tree.
- if grep -q "\.0$" VERSION; then exit 0; fi
# Use the migration-tests from the older QEMU tree. This avoids
# testing an old QEMU against new features/tests that it is not
# compatible with.
- cd build-previous
# Don't allow python-based tests to run. The
# vmstate-checker-script test has a race that causes it to fail
# sometimes. It cannot be fixed it because this job runs the test
# from the old QEMU version. The test will be removed on master,
# but this job will only see the change in the next release.
#
# TODO: remove this line after 9.2 release
- unset PYTHON
# old to new
- QTEST_QEMU_BINARY_SRC=./qemu-system-${TARGET}
QTEST_QEMU_BINARY=../build/qemu-system-${TARGET} ./tests/qtest/migration-test
@@ -267,14 +278,16 @@ functional-system-centos:
artifacts: true
variables:
IMAGE: centos9
MAKE_CHECK_ARGS: check-functional
MAKE_CHECK_ARGS: check-avocado check-functional
AVOCADO_TAGS: arch:ppc64 arch:or1k arch:s390x arch:x86_64 arch:rx
arch:sh4
build-system-opensuse:
extends:
- .native_build_job_template
- .native_build_artifact_template
needs:
- job: amd64-opensuse-leap-container
job: amd64-opensuse-leap-container
variables:
IMAGE: opensuse-leap
TARGETS: s390x-softmmu x86_64-softmmu aarch64-softmmu
@@ -296,7 +309,8 @@ functional-system-opensuse:
artifacts: true
variables:
IMAGE: opensuse-leap
MAKE_CHECK_ARGS: check-functional
MAKE_CHECK_ARGS: check-avocado check-functional
AVOCADO_TAGS: arch:s390x arch:x86_64 arch:aarch64
#
# Flaky tests. We don't run these by default and they are allow fail
@@ -308,7 +322,7 @@ build-system-flaky:
- .native_build_job_template
- .native_build_artifact_template
needs:
- job: amd64-debian-container
job: amd64-debian-container
variables:
IMAGE: debian
QEMU_JOB_OPTIONAL: 1
@@ -324,9 +338,10 @@ functional-system-flaky:
allow_failure: true
variables:
IMAGE: debian
MAKE_CHECK_ARGS: check-functional
MAKE_CHECK_ARGS: check-avocado check-functional
QEMU_JOB_OPTIONAL: 1
QEMU_TEST_FLAKY_TESTS: 1
AVOCADO_TAGS: flaky
# This jobs explicitly disable TCG (--disable-tcg), KVM is detected by
# the configure script. The container doesn't contain Xen headers so
@@ -338,7 +353,7 @@ functional-system-flaky:
build-tcg-disabled:
extends: .native_build_job_template
needs:
- job: amd64-centos9-container
job: amd64-centos9-container
variables:
IMAGE: centos9
script:
@@ -364,7 +379,7 @@ build-tcg-disabled:
build-user:
extends: .native_build_job_template
needs:
- job: amd64-debian-user-cross-container
job: amd64-debian-user-cross-container
variables:
IMAGE: debian-all-test-cross
CONFIGURE_ARGS: --disable-tools --disable-system
@@ -374,7 +389,7 @@ build-user:
build-user-static:
extends: .native_build_job_template
needs:
- job: amd64-debian-user-cross-container
job: amd64-debian-user-cross-container
variables:
IMAGE: debian-all-test-cross
CONFIGURE_ARGS: --disable-tools --disable-system --static
@@ -385,7 +400,7 @@ build-user-static:
build-legacy:
extends: .native_build_job_template
needs:
- job: amd64-debian-legacy-cross-container
job: amd64-debian-legacy-cross-container
variables:
IMAGE: debian-legacy-test-cross
TARGETS: alpha-linux-user alpha-softmmu sh4-linux-user
@@ -395,7 +410,7 @@ build-legacy:
build-user-hexagon:
extends: .native_build_job_template
needs:
- job: hexagon-cross-container
job: hexagon-cross-container
variables:
IMAGE: debian-hexagon-cross
TARGETS: hexagon-linux-user
@@ -408,7 +423,7 @@ build-user-hexagon:
build-some-softmmu:
extends: .native_build_job_template
needs:
- job: amd64-debian-user-cross-container
job: amd64-debian-user-cross-container
variables:
IMAGE: debian-all-test-cross
CONFIGURE_ARGS: --disable-tools --enable-debug
@@ -419,7 +434,7 @@ build-some-softmmu:
build-loongarch64:
extends: .native_build_job_template
needs:
- job: loongarch-debian-cross-container
job: loongarch-debian-cross-container
variables:
IMAGE: debian-loongarch-cross
CONFIGURE_ARGS: --disable-tools --enable-debug
@@ -430,7 +445,7 @@ build-loongarch64:
build-tricore-softmmu:
extends: .native_build_job_template
needs:
- job: tricore-debian-cross-container
job: tricore-debian-cross-container
variables:
IMAGE: debian-tricore-cross
CONFIGURE_ARGS: --disable-tools --disable-fdt --enable-debug
@@ -440,7 +455,7 @@ build-tricore-softmmu:
clang-system:
extends: .native_build_job_template
needs:
- job: amd64-fedora-container
job: amd64-fedora-container
variables:
IMAGE: fedora
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-ubsan
@@ -451,7 +466,7 @@ clang-system:
clang-user:
extends: .native_build_job_template
needs:
- job: amd64-debian-user-cross-container
job: amd64-debian-user-cross-container
timeout: 70m
variables:
IMAGE: debian-all-test-cross
@@ -467,8 +482,8 @@ clang-user:
# Since slirp callbacks are used in QEMU Timers, we cannot use libslirp with
# CFI builds, and thus have to disable it here.
#
# Split in three sets of build/check/functional to limit the execution time
# of each job
# Split in three sets of build/check/avocado to limit the execution time of each
# job
build-cfi-aarch64:
extends:
- .native_build_job_template
@@ -479,7 +494,7 @@ build-cfi-aarch64:
LD_JOBS: 1
AR: llvm-ar
IMAGE: fedora
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug
--enable-safe-stack --disable-slirp
TARGETS: aarch64-softmmu
MAKE_CHECK_ARGS: check-build
@@ -505,7 +520,7 @@ functional-cfi-aarch64:
artifacts: true
variables:
IMAGE: fedora
MAKE_CHECK_ARGS: check-functional
MAKE_CHECK_ARGS: check-avocado check-functional
build-cfi-ppc64-s390x:
extends:
@@ -517,7 +532,7 @@ build-cfi-ppc64-s390x:
LD_JOBS: 1
AR: llvm-ar
IMAGE: fedora
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug
--enable-safe-stack --disable-slirp
TARGETS: ppc64-softmmu s390x-softmmu
MAKE_CHECK_ARGS: check-build
@@ -543,7 +558,7 @@ functional-cfi-ppc64-s390x:
artifacts: true
variables:
IMAGE: fedora
MAKE_CHECK_ARGS: check-functional
MAKE_CHECK_ARGS: check-avocado check-functional
build-cfi-x86_64:
extends:
@@ -555,7 +570,7 @@ build-cfi-x86_64:
LD_JOBS: 1
AR: llvm-ar
IMAGE: fedora
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug
--enable-safe-stack --disable-slirp
TARGETS: x86_64-softmmu
MAKE_CHECK_ARGS: check-build
@@ -577,12 +592,12 @@ functional-cfi-x86_64:
artifacts: true
variables:
IMAGE: fedora
MAKE_CHECK_ARGS: check-functional
MAKE_CHECK_ARGS: check-avocado check-functional
tsan-build:
extends: .native_build_job_template
needs:
- job: amd64-ubuntu2204-container
job: amd64-ubuntu2204-container
variables:
IMAGE: ubuntu2204
CONFIGURE_ARGS: --enable-tsan --cc=clang --cxx=clang++
@@ -596,7 +611,7 @@ tsan-build:
gcov:
extends: .native_build_job_template
needs:
- job: amd64-ubuntu2204-container
job: amd64-ubuntu2204-container
timeout: 80m
variables:
IMAGE: ubuntu2204
@@ -613,9 +628,9 @@ gcov:
when: always
expire_in: 2 days
paths:
- build/meson-logs
- build/meson-logs/testlog.txt
reports:
junit: build/meson-logs/*.junit.xml
junit: build/meson-logs/testlog.junit.xml
coverage_report:
coverage_format: cobertura
path: build/coverage.xml
@@ -623,7 +638,7 @@ gcov:
build-oss-fuzz:
extends: .native_build_job_template
needs:
- job: amd64-fedora-container
job: amd64-fedora-container
variables:
IMAGE: fedora
script:
@@ -645,7 +660,7 @@ build-oss-fuzz:
build-tci:
extends: .native_build_job_template
needs:
- job: amd64-debian-user-cross-container
job: amd64-debian-user-cross-container
variables:
IMAGE: debian-all-test-cross
script:
@@ -670,7 +685,7 @@ build-tci:
build-without-defaults:
extends: .native_build_job_template
needs:
- job: amd64-centos9-container
job: amd64-centos9-container
variables:
IMAGE: centos9
CONFIGURE_ARGS:
@@ -688,7 +703,7 @@ build-libvhost-user:
stage: build
image: $CI_REGISTRY_IMAGE/qemu/fedora:$QEMU_CI_CONTAINER_TAG
needs:
- job: amd64-fedora-container
job: amd64-fedora-container
script:
- mkdir subprojects/libvhost-user/build
- cd subprojects/libvhost-user/build
@@ -702,9 +717,9 @@ build-tools-and-docs-debian:
- .native_build_job_template
- .native_build_artifact_template
needs:
- job: amd64-debian-container
# when running on 'master' we use pre-existing container
optional: true
job: amd64-debian-container
# when running on 'master' we use pre-existing container
optional: true
variables:
IMAGE: debian
MAKE_CHECK_ARGS: check-unit ctags TAGS cscope
@@ -736,7 +751,7 @@ pages:
- make gtags
# We unset variables to work around a bug in some htags versions
# which causes it to fail when the environment is large
- CI_COMMIT_MESSAGE= CI_COMMIT_TAG_MESSAGE= CI_COMMIT_DESCRIPTION= htags
- CI_COMMIT_MESSAGE= CI_COMMIT_TAG_MESSAGE= htags
-anT --tree-view=filetree -m qemu_init
-t "Welcome to the QEMU sourcecode"
- mv HTML public/src
@@ -759,7 +774,7 @@ coverity:
- job: amd64-fedora-container
optional: true
before_script:
- dnf install -y curl wget file
- dnf install -y curl wget
script:
# would be nice to cancel the job if over quota (https://gitlab.com/gitlab-org/gitlab/-/issues/256089)
# for example:
@@ -786,12 +801,3 @@ coverity:
when: never
# Always manual on forks even if $QEMU_CI == "2"
- when: manual
build-wasm:
extends: .wasm_build_job_template
timeout: 2h
needs:
- job: wasm-emsdk-cross-container
variables:
IMAGE: emsdk-wasm32-cross
CONFIGURE_ARGS: --static --disable-tools --enable-debug --enable-tcg-interpreter
+12 -17
View File
@@ -8,10 +8,8 @@
# SPDX-License-Identifier: GPL-2.0-or-later
from os import access, R_OK, path
from sys import exit
from sys import argv, exit
import json
import argparse
from pathlib import Path
from collections import Counter
@@ -30,7 +28,7 @@ def extract_build_units(cc_path):
return build_units
def analyse_units(build_units, top_n):
def analyse_units(build_units):
"""
Analyse the build units and report stats and the top 10 rebuilds
"""
@@ -44,7 +42,7 @@ def analyse_units(build_units, top_n):
reverse=True)
print("Most rebuilt units:")
for unit, count in sorted_build_units[:top_n]:
for unit, count in sorted_build_units[:20]:
print(f" {unit} built {count} times")
print("Least rebuilt units:")
@@ -53,19 +51,16 @@ def analyse_units(build_units, top_n):
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="analyse number of build units in compile_commands.json")
parser.add_argument("cc_path", type=Path, default=None,
help="Path to compile_commands.json")
parser.add_argument("-n", type=int, default=20,
help="Dump the top <n> entries")
if len(argv) != 2:
script_name = path.basename(argv[0])
print(f"Usage: {script_name} <path_to_compile_commands.json>")
exit(1)
args = parser.parse_args()
if path.isfile(args.cc_path) and access(args.cc_path, R_OK):
units = extract_build_units(args.cc_path)
analyse_units(units, args.n)
cc_path = argv[1]
if path.isfile(cc_path) and access(cc_path, R_OK):
units = extract_build_units(cc_path)
analyse_units(units)
exit(0)
else:
print(f"{args.cc_path} doesn't exist or isn't readable")
print(f"{cc_path} doesn't exist or isn't readable")
exit(1)
+1 -1
View File
@@ -42,7 +42,7 @@ x64-freebsd-14-build:
CIRRUS_VM_RAM: 8G
UPDATE_COMMAND: pkg update; pkg upgrade -y
INSTALL_COMMAND: pkg install -y
CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblaze-softmmu,mips64el-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4eb-softmmu,xtensa-softmmu --enable-rust
CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblaze-softmmu,mips64el-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4eb-softmmu,xtensa-softmmu
TEST_TARGETS: check
aarch64-macos-build:
+9 -5
View File
@@ -52,6 +52,12 @@ mips64el-debian-cross-container:
variables:
NAME: debian-mips64el-cross
mipsel-debian-cross-container:
extends: .container_job_template
stage: containers
variables:
NAME: debian-mipsel-cross
ppc64el-debian-cross-container:
extends: .container_job_template
stage: containers
@@ -61,8 +67,11 @@ ppc64el-debian-cross-container:
riscv64-debian-cross-container:
extends: .container_job_template
stage: containers
# as we are currently based on 'sid/unstable' we may break so...
allow_failure: true
variables:
NAME: debian-riscv64-cross
QEMU_JOB_OPTIONAL: 1
s390x-debian-cross-container:
extends: .container_job_template
@@ -85,8 +94,3 @@ win64-fedora-cross-container:
extends: .container_job_template
variables:
NAME: fedora-win64-cross
wasm-emsdk-cross-container:
extends: .container_job_template
variables:
NAME: emsdk-wasm32-cross
+2 -2
View File
@@ -128,6 +128,6 @@
when: always
expire_in: 7 days
paths:
- build/meson-logs
- build/meson-logs/testlog.txt
reports:
junit: build/meson-logs/*.junit.xml
junit: build/meson-logs/testlog.junit.xml
+40 -21
View File
@@ -4,28 +4,28 @@ include:
cross-armhf-user:
extends: .cross_user_build_job
needs:
- job: armhf-debian-cross-container
job: armhf-debian-cross-container
variables:
IMAGE: debian-armhf-cross
cross-arm64-system:
extends: .cross_system_build_job
needs:
- job: arm64-debian-cross-container
job: arm64-debian-cross-container
variables:
IMAGE: debian-arm64-cross
cross-arm64-user:
extends: .cross_user_build_job
needs:
- job: arm64-debian-cross-container
job: arm64-debian-cross-container
variables:
IMAGE: debian-arm64-cross
cross-arm64-kvm-only:
extends: .cross_accel_build_job
needs:
- job: arm64-debian-cross-container
job: arm64-debian-cross-container
variables:
IMAGE: debian-arm64-cross
EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-features
@@ -35,7 +35,7 @@ cross-i686-system:
- .cross_system_build_job
- .cross_test_artifacts
needs:
- job: i686-debian-cross-container
job: i686-debian-cross-container
variables:
IMAGE: debian-i686-cross
EXTRA_CONFIGURE_OPTS: --disable-kvm
@@ -46,7 +46,7 @@ cross-i686-user:
- .cross_user_build_job
- .cross_test_artifacts
needs:
- job: i686-debian-cross-container
job: i686-debian-cross-container
variables:
IMAGE: debian-i686-cross
MAKE_CHECK_ARGS: check
@@ -57,7 +57,7 @@ cross-i686-tci:
- .cross_test_artifacts
timeout: 60m
needs:
- job: i686-debian-cross-container
job: i686-debian-cross-container
variables:
IMAGE: debian-i686-cross
ACCEL: tcg-interpreter
@@ -68,74 +68,93 @@ cross-i686-tci:
# would otherwise be using a parallelism of 9.
MAKE_CHECK_ARGS: check check-tcg -j2
cross-mipsel-system:
extends: .cross_system_build_job
needs:
job: mipsel-debian-cross-container
variables:
IMAGE: debian-mipsel-cross
cross-mipsel-user:
extends: .cross_user_build_job
needs:
job: mipsel-debian-cross-container
variables:
IMAGE: debian-mipsel-cross
cross-mips64el-system:
extends: .cross_system_build_job
needs:
- job: mips64el-debian-cross-container
job: mips64el-debian-cross-container
variables:
IMAGE: debian-mips64el-cross
cross-mips64el-user:
extends: .cross_user_build_job
needs:
- job: mips64el-debian-cross-container
job: mips64el-debian-cross-container
variables:
IMAGE: debian-mips64el-cross
cross-ppc64el-system:
extends: .cross_system_build_job
needs:
- job: ppc64el-debian-cross-container
job: ppc64el-debian-cross-container
variables:
IMAGE: debian-ppc64el-cross
cross-ppc64el-user:
extends: .cross_user_build_job
needs:
- job: ppc64el-debian-cross-container
job: ppc64el-debian-cross-container
variables:
IMAGE: debian-ppc64el-cross
cross-ppc64el-kvm-only:
extends: .cross_accel_build_job
needs:
- job: ppc64el-debian-cross-container
job: ppc64el-debian-cross-container
variables:
IMAGE: debian-ppc64el-cross
EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-devices
# The riscv64 cross-builds currently use a 'sid' container to get
# compilers and libraries. Until something more stable is found we
# allow_failure so as not to block CI.
cross-riscv64-system:
extends: .cross_system_build_job
allow_failure: true
needs:
- job: riscv64-debian-cross-container
job: riscv64-debian-cross-container
variables:
IMAGE: debian-riscv64-cross
cross-riscv64-user:
extends: .cross_user_build_job
allow_failure: true
needs:
- job: riscv64-debian-cross-container
job: riscv64-debian-cross-container
variables:
IMAGE: debian-riscv64-cross
cross-s390x-system:
extends: .cross_system_build_job
needs:
- job: s390x-debian-cross-container
job: s390x-debian-cross-container
variables:
IMAGE: debian-s390x-cross
cross-s390x-user:
extends: .cross_user_build_job
needs:
- job: s390x-debian-cross-container
job: s390x-debian-cross-container
variables:
IMAGE: debian-s390x-cross
cross-s390x-kvm-only:
extends: .cross_accel_build_job
needs:
- job: s390x-debian-cross-container
job: s390x-debian-cross-container
variables:
IMAGE: debian-s390x-cross
EXTRA_CONFIGURE_OPTS: --disable-tcg --enable-trace-backends=ftrace
@@ -143,7 +162,7 @@ cross-s390x-kvm-only:
cross-mips64el-kvm-only:
extends: .cross_accel_build_job
needs:
- job: mips64el-debian-cross-container
job: mips64el-debian-cross-container
variables:
IMAGE: debian-mips64el-cross
EXTRA_CONFIGURE_OPTS: --disable-tcg --target-list=mips64el-softmmu
@@ -151,7 +170,7 @@ cross-mips64el-kvm-only:
cross-win64-system:
extends: .cross_system_build_job
needs:
- job: win64-fedora-cross-container
job: win64-fedora-cross-container
variables:
IMAGE: fedora-win64-cross
EXTRA_CONFIGURE_OPTS: --enable-fdt=internal --disable-plugins
@@ -167,7 +186,7 @@ cross-win64-system:
cross-amd64-xen-only:
extends: .cross_accel_build_job
needs:
- job: amd64-debian-cross-container
job: amd64-debian-cross-container
variables:
IMAGE: debian-amd64-cross
ACCEL: xen
@@ -176,7 +195,7 @@ cross-amd64-xen-only:
cross-arm64-xen-only:
extends: .cross_accel_build_job
needs:
- job: arm64-debian-cross-container
job: arm64-debian-cross-container
variables:
IMAGE: debian-arm64-cross
ACCEL: xen
+4 -3
View File
@@ -26,8 +26,9 @@
- build/build.ninja
- build/meson-logs
reports:
junit: build/meson-logs/*.junit.xml
junit: build/meson-logs/testlog.junit.xml
include:
- local: '/.gitlab-ci.d/custom-runners/ubuntu-24.04-s390x.yml'
- local: '/.gitlab-ci.d/custom-runners/ubuntu-24.04-aarch64.yml'
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml'
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml'
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml'
@@ -0,0 +1,25 @@
# All ubuntu-22.04 jobs should run successfully in an environment
# setup by the scripts/ci/setup/ubuntu/build-environment.yml task
# "Install basic packages to build QEMU on Ubuntu 22.04"
ubuntu-22.04-aarch32-all:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- aarch32
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$AARCH32_RUNNER_AVAILABLE"
when: manual
allow_failure: true
script:
- mkdir build
- cd build
- ../configure --cross-prefix=arm-linux-gnueabihf-
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc --ignore=40`
- make --output-sync -j`nproc --ignore=40` check
@@ -0,0 +1,151 @@
# All ubuntu-22.04 jobs should run successfully in an environment
# setup by the scripts/ci/setup/ubuntu/build-environment.yml task
# "Install basic packages to build QEMU on Ubuntu 22.04"
ubuntu-22.04-aarch64-all-linux-static:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$AARCH64_RUNNER_AVAILABLE"
script:
- mkdir build
- cd build
# Disable -static-pie due to build error with system libc:
# https://bugs.launchpad.net/ubuntu/+source/glibc/+bug/1987438
- ../configure --enable-debug --static --disable-system --disable-pie
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc --ignore=40`
- make check-tcg
- make --output-sync -j`nproc --ignore=40` check
ubuntu-22.04-aarch64-all:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$AARCH64_RUNNER_AVAILABLE"
when: manual
allow_failure: true
script:
- mkdir build
- cd build
- ../configure
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc --ignore=40`
- make --output-sync -j`nproc --ignore=40` check
ubuntu-22.04-aarch64-without-defaults:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$AARCH64_RUNNER_AVAILABLE"
when: manual
allow_failure: true
script:
- mkdir build
- cd build
- ../configure --disable-user --without-default-devices --without-default-features
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc --ignore=40`
- make --output-sync -j`nproc --ignore=40` check
ubuntu-22.04-aarch64-alldbg:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$AARCH64_RUNNER_AVAILABLE"
script:
- mkdir build
- cd build
- ../configure --enable-debug
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make clean
- make --output-sync -j`nproc --ignore=40`
- make --output-sync -j`nproc --ignore=40` check
ubuntu-22.04-aarch64-clang:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$AARCH64_RUNNER_AVAILABLE"
when: manual
allow_failure: true
script:
- mkdir build
- cd build
- ../configure --disable-libssh --cc=clang --cxx=clang++ --enable-ubsan
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc --ignore=40`
- make --output-sync -j`nproc --ignore=40` check
ubuntu-22.04-aarch64-tci:
needs: []
stage: build
tags:
- ubuntu_22.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$AARCH64_RUNNER_AVAILABLE"
when: manual
allow_failure: true
script:
- mkdir build
- cd build
- ../configure --enable-tcg-interpreter
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc --ignore=40`
ubuntu-22.04-aarch64-notcg:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_22.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$AARCH64_RUNNER_AVAILABLE"
when: manual
allow_failure: true
script:
- mkdir build
- cd build
- ../configure --disable-tcg --with-devices-aarch64=minimal
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc --ignore=40`
- make --output-sync -j`nproc --ignore=40` check
@@ -1,13 +1,13 @@
# All ubuntu-24.04 jobs should run successfully in an environment
# All ubuntu-22.04 jobs should run successfully in an environment
# setup by the scripts/ci/setup/ubuntu/build-environment.yml task
# "Install basic packages to build QEMU on Ubuntu 24.04"
# "Install basic packages to build QEMU on Ubuntu 22.04"
ubuntu-24.04-s390x-all-linux:
ubuntu-22.04-s390x-all-linux:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_24.04
- ubuntu_22.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
@@ -21,19 +21,17 @@ ubuntu-24.04-s390x-all-linux:
- make --output-sync check-tcg
- make --output-sync -j`nproc` check
ubuntu-24.04-s390x-all-system:
ubuntu-22.04-s390x-all-system:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_24.04
- ubuntu_22.04
- s390x
timeout: 75m
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
allow_failure: true
- if: "$S390X_RUNNER_AVAILABLE"
allow_failure: true
script:
- mkdir build
- cd build
@@ -42,12 +40,12 @@ ubuntu-24.04-s390x-all-system:
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check
ubuntu-24.04-s390x-alldbg:
ubuntu-22.04-s390x-alldbg:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_24.04
- ubuntu_22.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
@@ -65,12 +63,12 @@ ubuntu-24.04-s390x-alldbg:
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check
ubuntu-24.04-s390x-clang:
ubuntu-22.04-s390x-clang:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_24.04
- ubuntu_22.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
@@ -87,11 +85,11 @@ ubuntu-24.04-s390x-clang:
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check
ubuntu-24.04-s390x-tci:
ubuntu-22.04-s390x-tci:
needs: []
stage: build
tags:
- ubuntu_24.04
- ubuntu_22.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
@@ -107,12 +105,12 @@ ubuntu-24.04-s390x-tci:
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc`
ubuntu-24.04-s390x-notcg:
ubuntu-22.04-s390x-notcg:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_24.04
- ubuntu_22.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
@@ -1,111 +0,0 @@
# All ubuntu-24.04 jobs should run successfully in an environment
# setup by the scripts/ci/setup/ubuntu/build-environment.yml task
# "Install basic packages to build QEMU on Ubuntu 24.04"
.ubuntu_aarch64_template:
extends: .custom_runner_template
needs: []
stage: build
tags:
- ubuntu_24.04
- aarch64
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$AARCH64_RUNNER_AVAILABLE"
before_script:
- source scripts/ci/gitlab-ci-section
- section_start setup "Pre-script setup"
- JOBS=$(expr $(nproc) - 4)
- section_end setup
script:
- mkdir build
- cd build
- section_start configure "Running configure"
- ../configure $CONFIGURE_ARGS ||
{ cat config.log meson-logs/meson-log.txt && exit 1; }
- section_end configure
- section_start build "Building QEMU"
- make --output-sync -j"$JOBS"
- section_end build
- section_start test "Running tests"
- if test -n "$MAKE_CHECK_ARGS";
then
make -j"$JOBS" $MAKE_CHECK_ARGS ;
fi
- section_end test
ubuntu-24.04-aarch64-all-linux-static:
extends: .ubuntu_aarch64_template
variables:
# Disable -static-pie due to build error with system libc:
# https://bugs.launchpad.net/ubuntu/+source/glibc/+bug/1987438
CONFIGURE_ARGS: --enable-debug --static --disable-system --disable-pie
MAKE_CHECK_ARGS: check-tcg
ubuntu-24.04-aarch64-all:
extends: .ubuntu_aarch64_template
variables:
MAKE_CHECK_ARGS: check
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$AARCH64_RUNNER_AVAILABLE"
when: manual
allow_failure: true
ubuntu-24.04-aarch64-without-defaults:
extends: .ubuntu_aarch64_template
variables:
CONFIGURE_ARGS: --disable-user --without-default-devices --without-default-features
MAKE_CHECK_ARGS: check
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$AARCH64_RUNNER_AVAILABLE"
when: manual
allow_failure: true
ubuntu-24.04-aarch64-alldbg:
extends: .ubuntu_aarch64_template
variables:
CONFIGURE_ARGS: --enable-debug
MAKE_CHECK_ARGS: check-tcg
ubuntu-24.04-aarch64-clang:
extends: .ubuntu_aarch64_template
variables:
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-ubsan
MAKE_CHECK_ARGS: check
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$AARCH64_RUNNER_AVAILABLE"
when: manual
allow_failure: true
ubuntu-24.04-aarch64-tci:
extends: .ubuntu_aarch64_template
variables:
CONFIGURE_ARGS: --enable-tcg-interpreter
MAKE_CHECK_ARGS: check
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
allow_failure: true
- if: "$AARCH64_RUNNER_AVAILABLE"
when: manual
allow_failure: true
ubuntu-24.04-aarch64-notcg:
extends: .ubuntu_aarch64_template
variables:
CONFIGURE_ARGS: --disable-tcg --with-devices-aarch64=minimal
MAKE_CHECK_ARGS: check
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
- if: "$AARCH64_RUNNER_AVAILABLE"
when: manual
+3 -3
View File
@@ -32,7 +32,7 @@ check-python-minreqs:
variables:
GIT_DEPTH: 1
needs:
- job: python-container
job: python-container
check-python-tox:
extends: .base_job_template
@@ -45,7 +45,7 @@ check-python-tox:
QEMU_TOX_EXTRA_ARGS: --skip-missing-interpreters=false
QEMU_JOB_OPTIONAL: 1
needs:
- job: python-container
job: python-container
check-rust-tools-nightly:
extends: .base_job_template
@@ -76,7 +76,7 @@ check-build-units:
stage: build
image: $CI_REGISTRY_IMAGE/qemu/debian:$QEMU_CI_CONTAINER_TAG
needs:
- job: amd64-debian-container
job: amd64-debian-container
before_script:
- source scripts/ci/gitlab-ci-section
- section_start setup "Install Tools"
+4 -8
View File
@@ -24,10 +24,9 @@ msys2-64bit:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
expire_in: 7 days
paths:
- build/meson-logs
- build/cache-log.txt
- build/meson-logs/testlog.txt
reports:
junit: build/meson-logs/*.junit.xml
junit: "build/meson-logs/testlog.junit.xml"
before_script:
- Write-Output "Acquiring msys2.exe installer at $(Get-Date -Format u)"
- If ( !(Test-Path -Path msys64\var\cache ) ) {
@@ -78,7 +77,7 @@ msys2-64bit:
git grep make sed
mingw-w64-x86_64-binutils
mingw-w64-x86_64-ccache
mingw-w64-x86_64-curl-winssl
mingw-w64-x86_64-curl
mingw-w64-x86_64-gcc
mingw-w64-x86_64-glib2
mingw-w64-x86_64-libnfs
@@ -88,14 +87,13 @@ msys2-64bit:
mingw-w64-x86_64-pkgconf
mingw-w64-x86_64-python
mingw-w64-x86_64-zstd"
- .\msys64\usr\bin\bash -lc "pacman -Sc --noconfirm"
- Write-Output "Running build at $(Get-Date -Format u)"
- $env:JOBS = $(.\msys64\usr\bin\bash -lc nproc)
- $env:CHERE_INVOKING = 'yes' # Preserve the current working directory
- $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink
- $env:CCACHE_BASEDIR = "$env:CI_PROJECT_DIR"
- $env:CCACHE_DIR = "$env:CCACHE_BASEDIR/ccache"
- $env:CCACHE_MAXSIZE = "180M"
- $env:CCACHE_MAXSIZE = "500M"
- $env:CCACHE_DEPEND = 1 # cache misses are too expensive with preprocessor mode
- $env:CC = "ccache gcc"
- mkdir build
@@ -104,7 +102,5 @@ msys2-64bit:
- ..\msys64\usr\bin\bash -lc "../configure $CONFIGURE_ARGS"
- ..\msys64\usr\bin\bash -lc "make -j$env:JOBS"
- ..\msys64\usr\bin\bash -lc "make check MTESTARGS='$TEST_ARGS' || { cat meson-logs/testlog.txt; exit 1; } ;"
- ..\msys64\usr\bin\bash -lc "ls -lR /var/cache > cache-log.txt"
- ..\msys64\usr\bin\bash -lc "du -sh ."
- ..\msys64\usr\bin\bash -lc "ccache --show-stats"
- Write-Output "Finished build at $(Get-Date -Format u)"
-2
View File
@@ -15,7 +15,6 @@
url = https://gitlab.com/qemu-project/qemu-palcode.git
[submodule "roms/u-boot"]
path = roms/u-boot
# upstream is https://github.com/u-boot/u-boot
url = https://gitlab.com/qemu-project/u-boot.git
[submodule "roms/skiboot"]
path = roms/skiboot
@@ -28,7 +27,6 @@
url = https://gitlab.com/qemu-project/seabios-hppa.git
[submodule "roms/u-boot-sam460ex"]
path = roms/u-boot-sam460ex
# upstream is https://github.com/zbalaton/u-boot-sam460ex
url = https://gitlab.com/qemu-project/u-boot-sam460ex.git
[submodule "roms/edk2"]
path = roms/edk2
+8 -8
View File
@@ -4,48 +4,48 @@
# See https://github.com/stefanha/git-publish for more information
#
[gitpublishprofile "default"]
base = origin/master
base = master
to = qemu-devel@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "rfc"]
base = origin/master
base = master
prefix = RFC PATCH
to = qemu-devel@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "stable"]
base = origin/master
base = master
to = qemu-devel@nongnu.org
cc = qemu-stable@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "trivial"]
base = origin/master
base = master
to = qemu-devel@nongnu.org
cc = qemu-trivial@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "block"]
base = origin/master
base = master
to = qemu-devel@nongnu.org
cc = qemu-block@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "arm"]
base = origin/master
base = master
to = qemu-devel@nongnu.org
cc = qemu-arm@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "s390"]
base = origin/master
base = master
to = qemu-devel@nongnu.org
cc = qemu-s390@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "ppc"]
base = origin/master
base = master
to = qemu-devel@nongnu.org
cc = qemu-ppc@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
+1 -4
View File
@@ -67,14 +67,12 @@ Andrey Drobyshev <andrey.drobyshev@virtuozzo.com> Andrey Drobyshev via <qemu-blo
BALATON Zoltan <balaton@eik.bme.hu> BALATON Zoltan via <qemu-ppc@nongnu.org>
# Next, replace old addresses by a more recent one.
Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> <akihiko.odaki@daynix.com>
Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> <akihiko.odaki@gmail.com>
Akihiko Odaki <akihiko.odaki@daynix.com> <akihiko.odaki@gmail.com>
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <aleksandar.markovic@mips.com>
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <aleksandar.markovic@imgtec.com>
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <amarkovic@wavecomp.com>
Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <arikalo@wavecomp.com>
Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> <aleksandar.rikalo@rt-rk.com>
Alex Williamson <alex@shazbot.org> <alex.williamson@redhat.com>
Alexander Graf <agraf@csgraf.de> <agraf@suse.de>
Ani Sinha <anisinha@redhat.com> <ani@anisinha.ca>
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
@@ -137,7 +135,6 @@ Chen Gang <gang.chen.5i5j@gmail.com>
Chen Gang <gang.chen@sunrus.com.cn>
Chen Wei-Ren <chenwj@iis.sinica.edu.tw>
Christophe Lyon <christophe.lyon@st.com>
Clément Mathieu--Drif <clement.mathieu--drif@eviden.com>
Collin L. Walling <walling@linux.ibm.com>
Daniel P. Berrangé <berrange@redhat.com>
Eduardo Otubo <otubo@redhat.com>
+2
View File
@@ -21,3 +21,5 @@ python:
install:
- requirements: docs/requirements.txt
# We want all the document formats
formats: all
+35
View File
@@ -79,6 +79,41 @@ after_script:
jobs:
include:
- name: "[aarch64] GCC check-tcg"
arch: arm64
addons:
apt_packages:
- libaio-dev
- libattr1-dev
- libbrlapi-dev
- libcacard-dev
- libcap-ng-dev
- libfdt-dev
- libgcrypt20-dev
- libgnutls28-dev
- libgtk-3-dev
- libiscsi-dev
- liblttng-ust-dev
- libncurses5-dev
- libnfs-dev
- libpixman-1-dev
- libpng-dev
- librados-dev
- libsdl2-dev
- libseccomp-dev
- liburcu-dev
- libusb-1.0-0-dev
- libvdeplug-dev
- libvte-2.91-dev
- ninja-build
- python3-tomli
# Tests dependencies
- genisoimage
env:
- TEST_CMD="make check check-tcg V=1"
- CONFIG="--disable-containers --enable-fdt=system
--target-list=${MAIN_SYSTEM_TARGETS} --cxx=/bin/false"
- name: "[ppc64] Clang check-tcg"
arch: ppc64le
compiler: clang
+3 -2
View File
@@ -2,7 +2,7 @@
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
<https://fsf.org/>
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -304,7 +304,8 @@ the "copyright" line and a pointer to where the full notice is found.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, see <https://www.gnu.org/licenses/>.
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
+3 -2
View File
@@ -2,7 +2,7 @@
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
<https://fsf.org/>
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -484,7 +484,8 @@ convey the exclusion of warranty; and each file should have at least the
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 <https://www.gnu.org/licenses/>.
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
+166 -360
View File
File diff suppressed because it is too large Load Diff
-1
View File
@@ -227,7 +227,6 @@ distclean: clean recurse-distclean
rm -Rf .sdk qemu-bundle
find-src-path = find "$(SRC_PATH)" -path "$(SRC_PATH)/meson" -prune -o \
-path "$(SRC_PATH)/.pc" -prune -o \
-type l -prune -o \( -name "*.[chsS]" -o -name "*.[ch].inc" \)
.PHONY: ctags
+1 -1
View File
@@ -1 +1 @@
10.1.50
10.0.2
-3
View File
@@ -13,9 +13,6 @@ config TCG
config KVM
bool
config MSHV
bool
config XEN
bool
select FSDEV_9P if VIRTFS
-144
View File
@@ -1,144 +0,0 @@
/*
* QEMU accel class, components common to system emulation and user mode
*
* Copyright (c) 2003-2008 Fabrice Bellard
* Copyright (c) 2014 Red Hat Inc.
*
* SPDX-License-Identifier: MIT
*/
#include "qemu/osdep.h"
#include "qemu/accel.h"
#include "qemu/target-info.h"
#include "accel/accel-ops.h"
#include "accel/accel-cpu.h"
#include "accel/accel-cpu-ops.h"
#include "accel-internal.h"
/* Lookup AccelClass from opt_name. Returns NULL if not found */
AccelClass *accel_find(const char *opt_name)
{
char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name);
AccelClass *ac = ACCEL_CLASS(module_object_class_by_name(class_name));
g_free(class_name);
return ac;
}
/* Return the name of the current accelerator */
const char *current_accel_name(void)
{
AccelClass *ac = ACCEL_GET_CLASS(current_accel());
return ac->name;
}
static void accel_init_cpu_int_aux(ObjectClass *klass, void *opaque)
{
CPUClass *cc = CPU_CLASS(klass);
AccelCPUClass *accel_cpu = opaque;
/*
* The first callback allows accel-cpu to run initializations
* for the CPU, customizing CPU behavior according to the accelerator.
*
* The second one allows the CPU to customize the accel-cpu
* behavior according to the CPU.
*
* The second is currently only used by TCG, to specialize the
* TCGCPUOps depending on the CPU type.
*/
cc->accel_cpu = accel_cpu;
if (accel_cpu->cpu_class_init) {
accel_cpu->cpu_class_init(cc);
}
if (cc->init_accel_cpu) {
cc->init_accel_cpu(accel_cpu, cc);
}
}
/* initialize the arch-specific accel CpuClass interfaces */
static void accel_init_cpu_interfaces(AccelClass *ac)
{
const char *ac_name; /* AccelClass name */
char *acc_name; /* AccelCPUClass name */
ObjectClass *acc; /* AccelCPUClass */
const char *cpu_resolving_type = target_cpu_type();
ac_name = object_class_get_name(OBJECT_CLASS(ac));
g_assert(ac_name != NULL);
acc_name = g_strdup_printf("%s-%s", ac_name, cpu_resolving_type);
acc = object_class_by_name(acc_name);
g_free(acc_name);
if (acc) {
object_class_foreach(accel_init_cpu_int_aux,
cpu_resolving_type, false, acc);
}
}
void accel_init_interfaces(AccelClass *ac)
{
accel_init_ops_interfaces(ac);
accel_init_cpu_interfaces(ac);
}
void accel_cpu_instance_init(CPUState *cpu)
{
if (cpu->cc->accel_cpu && cpu->cc->accel_cpu->cpu_instance_init) {
cpu->cc->accel_cpu->cpu_instance_init(cpu);
}
}
bool accel_cpu_common_realize(CPUState *cpu, Error **errp)
{
AccelState *accel = current_accel();
AccelClass *acc = ACCEL_GET_CLASS(accel);
/* target specific realization */
if (cpu->cc->accel_cpu
&& cpu->cc->accel_cpu->cpu_target_realize
&& !cpu->cc->accel_cpu->cpu_target_realize(cpu, errp)) {
return false;
}
/* generic realization */
if (acc->cpu_common_realize && !acc->cpu_common_realize(cpu, errp)) {
return false;
}
return true;
}
void accel_cpu_common_unrealize(CPUState *cpu)
{
AccelState *accel = current_accel();
AccelClass *acc = ACCEL_GET_CLASS(accel);
/* generic unrealization */
if (acc->cpu_common_unrealize) {
acc->cpu_common_unrealize(cpu);
}
}
int accel_supported_gdbstub_sstep_flags(void)
{
AccelState *accel = current_accel();
AccelClass *acc = ACCEL_GET_CLASS(accel);
if (acc->gdbstub_supported_sstep_flags) {
return acc->gdbstub_supported_sstep_flags(accel);
}
return 0;
}
static const TypeInfo accel_types[] = {
{
.name = TYPE_ACCEL,
.parent = TYPE_OBJECT,
.class_size = sizeof(AccelClass),
.instance_size = sizeof(AccelState),
.abstract = true,
},
};
DEFINE_TYPES(accel_types)
-106
View File
@@ -1,106 +0,0 @@
/*
* Accelerated irqchip abstraction
*
* Copyright Microsoft, Corp. 2025
*
* Authors: Ziqiao Zhou <ziqiaozhou@microsoft.com>
* Magnus Kulke <magnuskulke@microsoft.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "hw/pci/msi.h"
#include "system/kvm.h"
#include "system/mshv.h"
#include "system/accel-irq.h"
int accel_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
return mshv_irqchip_add_msi_route(vector, dev);
}
#endif
if (kvm_enabled()) {
return kvm_irqchip_add_msi_route(c, vector, dev);
}
return -ENOSYS;
}
int accel_irqchip_update_msi_route(int vector, MSIMessage msg, PCIDevice *dev)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
return mshv_irqchip_update_msi_route(vector, msg, dev);
}
#endif
if (kvm_enabled()) {
return kvm_irqchip_update_msi_route(kvm_state, vector, msg, dev);
}
return -ENOSYS;
}
void accel_irqchip_commit_route_changes(KVMRouteChange *c)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
mshv_irqchip_commit_routes();
}
#endif
if (kvm_enabled()) {
kvm_irqchip_commit_route_changes(c);
}
}
void accel_irqchip_commit_routes(void)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
mshv_irqchip_commit_routes();
}
#endif
if (kvm_enabled()) {
kvm_irqchip_commit_routes(kvm_state);
}
}
void accel_irqchip_release_virq(int virq)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
mshv_irqchip_release_virq(virq);
}
#endif
if (kvm_enabled()) {
kvm_irqchip_release_virq(kvm_state, virq);
}
}
int accel_irqchip_add_irqfd_notifier_gsi(EventNotifier *n, EventNotifier *rn,
int virq)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
return mshv_irqchip_add_irqfd_notifier_gsi(n, rn, virq);
}
#endif
if (kvm_enabled()) {
return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, rn, virq);
}
return -ENOSYS;
}
int accel_irqchip_remove_irqfd_notifier_gsi(EventNotifier *n, int virq)
{
#ifdef CONFIG_MSHV_IS_POSSIBLE
if (mshv_msi_via_irqfd_enabled()) {
return mshv_irqchip_remove_irqfd_notifier_gsi(n, virq);
}
#endif
if (kvm_enabled()) {
return kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, virq);
}
return -ENOSYS;
}
-35
View File
@@ -1,35 +0,0 @@
/*
* QMP commands related to accelerators
*
* Copyright (c) Linaro
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "qemu/accel.h"
#include "qapi/type-helpers.h"
#include "qapi/qapi-commands-accelerator.h"
#include "accel/accel-ops.h"
#include "accel/accel-cpu-ops.h"
#include "hw/core/cpu.h"
HumanReadableText *qmp_x_accel_stats(Error **errp)
{
AccelState *accel = current_accel();
AccelClass *acc = ACCEL_GET_CLASS(accel);
g_autoptr(GString) buf = g_string_new("");
if (acc->get_stats) {
acc->get_stats(accel, buf);
}
if (acc->ops->get_vcpu_stats) {
CPUState *cpu;
CPU_FOREACH(cpu) {
acc->ops->get_vcpu_stats(cpu, buf);
}
}
return human_readable_text_from_str(buf);
}
+6 -26
View File
@@ -25,15 +25,11 @@
#include "qemu/osdep.h"
#include "qemu/accel.h"
#include "qapi/qapi-commands-accelerator.h"
#include "monitor/monitor.h"
#include "hw/boards.h"
#include "hw/core/cpu.h"
#include "accel/accel-ops.h"
#include "accel/accel-cpu-ops.h"
#include "system/accel-ops.h"
#include "system/cpus.h"
#include "qemu/error-report.h"
#include "accel-internal.h"
#include "accel-system.h"
int accel_init_machine(AccelState *accel, MachineState *ms)
{
@@ -41,7 +37,7 @@ int accel_init_machine(AccelState *accel, MachineState *ms)
int ret;
ms->accelerator = accel;
*(acc->allowed) = true;
ret = acc->init_machine(accel, ms);
ret = acc->init_machine(ms);
if (ret < 0) {
ms->accelerator = NULL;
*(acc->allowed) = false;
@@ -62,21 +58,12 @@ void accel_setup_post(MachineState *ms)
AccelState *accel = ms->accelerator;
AccelClass *acc = ACCEL_GET_CLASS(accel);
if (acc->setup_post) {
acc->setup_post(accel);
}
}
void accel_pre_resume(MachineState *ms, bool step_pending)
{
AccelState *accel = ms->accelerator;
AccelClass *acc = ACCEL_GET_CLASS(accel);
if (acc->pre_resume_vm) {
acc->pre_resume_vm(accel, step_pending);
acc->setup_post(ms, accel);
}
}
/* initialize the arch-independent accel operation interfaces */
void accel_init_ops_interfaces(AccelClass *ac)
void accel_system_init_ops_interfaces(AccelClass *ac)
{
const char *ac_name;
char *ops_name;
@@ -98,24 +85,17 @@ void accel_init_ops_interfaces(AccelClass *ac)
* non-NULL create_vcpu_thread operation.
*/
ops = ACCEL_OPS_CLASS(oc);
ac->ops = ops;
if (ops->ops_init) {
ops->ops_init(ac);
ops->ops_init(ops);
}
cpus_register_accel(ops);
}
static void accel_ops_class_init(ObjectClass *oc, const void *data)
{
monitor_register_hmp_info_hrt("accel", qmp_x_accel_stats);
}
static const TypeInfo accel_ops_type_info = {
.name = TYPE_ACCEL_OPS,
.parent = TYPE_OBJECT,
.abstract = true,
.class_size = sizeof(AccelOpsClass),
.class_init = accel_ops_class_init,
};
static void accel_system_register_types(void)
@@ -1,5 +1,5 @@
/*
* QEMU accel internal functions
* QEMU System Emulation accel internal functions
*
* Copyright 2021 SUSE LLC
*
@@ -7,11 +7,9 @@
* See the COPYING file in the top-level directory.
*/
#ifndef ACCEL_INTERNAL_H
#define ACCEL_INTERNAL_H
#ifndef ACCEL_SYSTEM_H
#define ACCEL_SYSTEM_H
#include "qemu/accel.h"
void accel_init_ops_interfaces(AccelClass *ac);
void accel_system_init_ops_interfaces(AccelClass *ac);
#endif /* ACCEL_SYSTEM_H */
+134
View File
@@ -24,8 +24,141 @@
*/
#include "qemu/osdep.h"
#include "qemu/accel.h"
#include "cpu.h"
#include "accel/accel-cpu-target.h"
#ifndef CONFIG_USER_ONLY
#include "accel-system.h"
#endif /* !CONFIG_USER_ONLY */
static const TypeInfo accel_type = {
.name = TYPE_ACCEL,
.parent = TYPE_OBJECT,
.class_size = sizeof(AccelClass),
.instance_size = sizeof(AccelState),
.abstract = true,
};
/* Lookup AccelClass from opt_name. Returns NULL if not found */
AccelClass *accel_find(const char *opt_name)
{
char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name);
AccelClass *ac = ACCEL_CLASS(module_object_class_by_name(class_name));
g_free(class_name);
return ac;
}
/* Return the name of the current accelerator */
const char *current_accel_name(void)
{
AccelClass *ac = ACCEL_GET_CLASS(current_accel());
return ac->name;
}
static void accel_init_cpu_int_aux(ObjectClass *klass, void *opaque)
{
CPUClass *cc = CPU_CLASS(klass);
AccelCPUClass *accel_cpu = opaque;
/*
* The first callback allows accel-cpu to run initializations
* for the CPU, customizing CPU behavior according to the accelerator.
*
* The second one allows the CPU to customize the accel-cpu
* behavior according to the CPU.
*
* The second is currently only used by TCG, to specialize the
* TCGCPUOps depending on the CPU type.
*/
cc->accel_cpu = accel_cpu;
if (accel_cpu->cpu_class_init) {
accel_cpu->cpu_class_init(cc);
}
if (cc->init_accel_cpu) {
cc->init_accel_cpu(accel_cpu, cc);
}
}
/* initialize the arch-specific accel CpuClass interfaces */
static void accel_init_cpu_interfaces(AccelClass *ac)
{
const char *ac_name; /* AccelClass name */
char *acc_name; /* AccelCPUClass name */
ObjectClass *acc; /* AccelCPUClass */
ac_name = object_class_get_name(OBJECT_CLASS(ac));
g_assert(ac_name != NULL);
acc_name = g_strdup_printf("%s-%s", ac_name, CPU_RESOLVING_TYPE);
acc = object_class_by_name(acc_name);
g_free(acc_name);
if (acc) {
object_class_foreach(accel_init_cpu_int_aux,
CPU_RESOLVING_TYPE, false, acc);
}
}
void accel_init_interfaces(AccelClass *ac)
{
#ifndef CONFIG_USER_ONLY
accel_system_init_ops_interfaces(ac);
#endif /* !CONFIG_USER_ONLY */
accel_init_cpu_interfaces(ac);
}
void accel_cpu_instance_init(CPUState *cpu)
{
if (cpu->cc->accel_cpu && cpu->cc->accel_cpu->cpu_instance_init) {
cpu->cc->accel_cpu->cpu_instance_init(cpu);
}
}
bool accel_cpu_common_realize(CPUState *cpu, Error **errp)
{
AccelState *accel = current_accel();
AccelClass *acc = ACCEL_GET_CLASS(accel);
/* target specific realization */
if (cpu->cc->accel_cpu
&& cpu->cc->accel_cpu->cpu_target_realize
&& !cpu->cc->accel_cpu->cpu_target_realize(cpu, errp)) {
return false;
}
/* generic realization */
if (acc->cpu_common_realize && !acc->cpu_common_realize(cpu, errp)) {
return false;
}
return true;
}
void accel_cpu_common_unrealize(CPUState *cpu)
{
AccelState *accel = current_accel();
AccelClass *acc = ACCEL_GET_CLASS(accel);
/* generic unrealization */
if (acc->cpu_common_unrealize) {
acc->cpu_common_unrealize(cpu);
}
}
int accel_supported_gdbstub_sstep_flags(void)
{
AccelState *accel = current_accel();
AccelClass *acc = ACCEL_GET_CLASS(accel);
if (acc->gdbstub_supported_sstep_flags) {
return acc->gdbstub_supported_sstep_flags();
}
return 0;
}
static const TypeInfo accel_cpu_type = {
.name = TYPE_ACCEL_CPU,
.parent = TYPE_OBJECT,
@@ -35,6 +168,7 @@ static const TypeInfo accel_cpu_type = {
static void register_accel_types(void)
{
type_register_static(&accel_type);
type_register_static(&accel_cpu_type);
}
-6
View File
@@ -9,12 +9,6 @@
#include "qemu/osdep.h"
#include "qemu/accel.h"
#include "accel-internal.h"
void accel_init_ops_interfaces(AccelClass *ac)
{
/* nothing */
}
AccelState *current_accel(void)
{
+1 -2
View File
@@ -17,7 +17,6 @@
#include "qemu/guest-random.h"
#include "qemu/main-loop.h"
#include "hw/core/cpu.h"
#include "accel/dummy-cpus.h"
static void *dummy_cpu_thread_fn(void *arg)
{
@@ -43,7 +42,6 @@ static void *dummy_cpu_thread_fn(void *arg)
qemu_guest_random_seed_thread_part2(cpu->random_seed);
do {
qemu_process_cpu_events(cpu);
bql_unlock();
#ifndef _WIN32
do {
@@ -58,6 +56,7 @@ static void *dummy_cpu_thread_fn(void *arg)
qemu_sem_wait(&cpu->sem);
#endif
bql_lock();
qemu_wait_io_event(cpu);
} while (!cpu->unplug);
bql_unlock();
-14
View File
@@ -1,14 +0,0 @@
/*
* Dummy cpu thread code
*
* Copyright IBM, Corp. 2011
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef ACCEL_DUMMY_CPUS_H
#define ACCEL_DUMMY_CPUS_H
void dummy_start_vcpu_thread(CPUState *cpu);
#endif
+270 -67
View File
@@ -48,17 +48,18 @@
*/
#include "qemu/osdep.h"
#include "qemu/guest-random.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
#include "qemu/queue.h"
#include "exec/address-spaces.h"
#include "exec/exec-all.h"
#include "gdbstub/enums.h"
#include "exec/cpu-common.h"
#include "hw/core/cpu.h"
#include "accel/accel-cpu-ops.h"
#include "hw/boards.h"
#include "system/accel-ops.h"
#include "system/cpus.h"
#include "system/hvf.h"
#include "system/hvf_int.h"
#include <mach/mach_time.h>
#include "system/runstate.h"
#include "qemu/guest-random.h"
HVFState *hvf_state;
@@ -78,17 +79,138 @@ hvf_slot *hvf_find_overlap_slot(uint64_t start, uint64_t size)
return NULL;
}
struct mac_slot {
int present;
uint64_t size;
uint64_t gpa_start;
uint64_t gva;
};
struct mac_slot mac_slots[32];
static int do_hvf_set_memory(hvf_slot *slot, hv_memory_flags_t flags)
{
struct mac_slot *macslot;
hv_return_t ret;
macslot = &mac_slots[slot->slot_id];
if (macslot->present) {
if (macslot->size != slot->size) {
macslot->present = 0;
ret = hv_vm_unmap(macslot->gpa_start, macslot->size);
assert_hvf_ok(ret);
}
}
if (!slot->size) {
return 0;
}
macslot->present = 1;
macslot->gpa_start = slot->start;
macslot->size = slot->size;
ret = hv_vm_map(slot->mem, slot->start, slot->size, flags);
assert_hvf_ok(ret);
return 0;
}
static void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
{
hvf_slot *mem;
MemoryRegion *area = section->mr;
bool writable = !area->readonly && !area->rom_device;
hv_memory_flags_t flags;
uint64_t page_size = qemu_real_host_page_size();
if (!memory_region_is_ram(area)) {
if (writable) {
return;
} else if (!memory_region_is_romd(area)) {
/*
* If the memory device is not in romd_mode, then we actually want
* to remove the hvf memory slot so all accesses will trap.
*/
add = false;
}
}
if (!QEMU_IS_ALIGNED(int128_get64(section->size), page_size) ||
!QEMU_IS_ALIGNED(section->offset_within_address_space, page_size)) {
/* Not page aligned, so we can not map as RAM */
add = false;
}
mem = hvf_find_overlap_slot(
section->offset_within_address_space,
int128_get64(section->size));
if (mem && add) {
if (mem->size == int128_get64(section->size) &&
mem->start == section->offset_within_address_space &&
mem->mem == (memory_region_get_ram_ptr(area) +
section->offset_within_region)) {
return; /* Same region was attempted to register, go away. */
}
}
/* Region needs to be reset. set the size to 0 and remap it. */
if (mem) {
mem->size = 0;
if (do_hvf_set_memory(mem, 0)) {
error_report("Failed to reset overlapping slot");
abort();
}
}
if (!add) {
return;
}
if (area->readonly ||
(!memory_region_is_ram(area) && memory_region_is_romd(area))) {
flags = HV_MEMORY_READ | HV_MEMORY_EXEC;
} else {
flags = HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC;
}
/* Now make a new slot. */
int x;
for (x = 0; x < hvf_state->num_slots; ++x) {
mem = &hvf_state->slots[x];
if (!mem->size) {
break;
}
}
if (x == hvf_state->num_slots) {
error_report("No free slots");
abort();
}
mem->size = int128_get64(section->size);
mem->mem = memory_region_get_ram_ptr(area) + section->offset_within_region;
mem->start = section->offset_within_address_space;
mem->region = area;
if (do_hvf_set_memory(mem, flags)) {
error_report("Error registering new memory slot");
abort();
}
}
static void do_hvf_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
{
if (!cpu->vcpu_dirty) {
hvf_arch_get_registers(cpu);
cpu->vcpu_dirty = true;
if (!cpu->accel->dirty) {
hvf_get_registers(cpu);
cpu->accel->dirty = true;
}
}
static void hvf_cpu_synchronize_state(CPUState *cpu)
{
if (!cpu->vcpu_dirty) {
if (!cpu->accel->dirty) {
run_on_cpu(cpu, do_hvf_cpu_synchronize_state, RUN_ON_CPU_NULL);
}
}
@@ -97,7 +219,7 @@ static void do_hvf_cpu_synchronize_set_dirty(CPUState *cpu,
run_on_cpu_data arg)
{
/* QEMU state is the reference, push it to HVF now and on next entry */
cpu->vcpu_dirty = true;
cpu->accel->dirty = true;
}
static void hvf_cpu_synchronize_post_reset(CPUState *cpu)
@@ -115,16 +237,146 @@ static void hvf_cpu_synchronize_pre_loadvm(CPUState *cpu)
run_on_cpu(cpu, do_hvf_cpu_synchronize_set_dirty, RUN_ON_CPU_NULL);
}
static void hvf_set_dirty_tracking(MemoryRegionSection *section, bool on)
{
hvf_slot *slot;
slot = hvf_find_overlap_slot(
section->offset_within_address_space,
int128_get64(section->size));
/* protect region against writes; begin tracking it */
if (on) {
slot->flags |= HVF_SLOT_LOG;
hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size,
HV_MEMORY_READ | HV_MEMORY_EXEC);
/* stop tracking region*/
} else {
slot->flags &= ~HVF_SLOT_LOG;
hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size,
HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC);
}
}
static void hvf_log_start(MemoryListener *listener,
MemoryRegionSection *section, int old, int new)
{
if (old != 0) {
return;
}
hvf_set_dirty_tracking(section, 1);
}
static void hvf_log_stop(MemoryListener *listener,
MemoryRegionSection *section, int old, int new)
{
if (new != 0) {
return;
}
hvf_set_dirty_tracking(section, 0);
}
static void hvf_log_sync(MemoryListener *listener,
MemoryRegionSection *section)
{
/*
* sync of dirty pages is handled elsewhere; just make sure we keep
* tracking the region.
*/
hvf_set_dirty_tracking(section, 1);
}
static void hvf_region_add(MemoryListener *listener,
MemoryRegionSection *section)
{
hvf_set_phys_mem(section, true);
}
static void hvf_region_del(MemoryListener *listener,
MemoryRegionSection *section)
{
hvf_set_phys_mem(section, false);
}
static MemoryListener hvf_memory_listener = {
.name = "hvf",
.priority = MEMORY_LISTENER_PRIORITY_ACCEL,
.region_add = hvf_region_add,
.region_del = hvf_region_del,
.log_start = hvf_log_start,
.log_stop = hvf_log_stop,
.log_sync = hvf_log_sync,
};
static void dummy_signal(int sig)
{
}
static void do_hvf_get_vcpu_exec_time(CPUState *cpu, run_on_cpu_data arg)
bool hvf_allowed;
static int hvf_accel_init(MachineState *ms)
{
int r = hv_vcpu_get_exec_time(cpu->accel->fd, arg.host_ptr);
assert_hvf_ok(r);
int x;
hv_return_t ret;
HVFState *s;
int pa_range = 36;
MachineClass *mc = MACHINE_GET_CLASS(ms);
if (mc->hvf_get_physical_address_range) {
pa_range = mc->hvf_get_physical_address_range(ms);
if (pa_range < 0) {
return -EINVAL;
}
}
ret = hvf_arch_vm_create(ms, (uint32_t)pa_range);
assert_hvf_ok(ret);
s = g_new0(HVFState, 1);
s->num_slots = ARRAY_SIZE(s->slots);
for (x = 0; x < s->num_slots; ++x) {
s->slots[x].size = 0;
s->slots[x].slot_id = x;
}
QTAILQ_INIT(&s->hvf_sw_breakpoints);
hvf_state = s;
memory_listener_register(&hvf_memory_listener, &address_space_memory);
return hvf_arch_init();
}
static inline int hvf_gdbstub_sstep_flags(void)
{
return SSTEP_ENABLE | SSTEP_NOIRQ;
}
static void hvf_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "HVF";
ac->init_machine = hvf_accel_init;
ac->allowed = &hvf_allowed;
ac->gdbstub_supported_sstep_flags = hvf_gdbstub_sstep_flags;
}
static const TypeInfo hvf_accel_type = {
.name = TYPE_HVF_ACCEL,
.parent = TYPE_ACCEL,
.class_init = hvf_accel_class_init,
};
static void hvf_type_init(void)
{
type_register_static(&hvf_accel_type);
}
type_init(hvf_type_init);
static void hvf_vcpu_destroy(CPUState *cpu)
{
hv_return_t ret = hv_vcpu_destroy(cpu->accel->fd);
@@ -157,8 +409,8 @@ static int hvf_init_vcpu(CPUState *cpu)
#else
r = hv_vcpu_create(&cpu->accel->fd, HV_VCPU_DEFAULT);
#endif
cpu->accel->dirty = true;
assert_hvf_ok(r);
cpu->vcpu_dirty = true;
cpu->accel->guest_debug_enabled = false;
@@ -192,13 +444,13 @@ static void *hvf_cpu_thread_fn(void *arg)
qemu_guest_random_seed_thread_part2(cpu->random_seed);
do {
qemu_process_cpu_events(cpu);
if (cpu_can_run(cpu)) {
r = hvf_arch_vcpu_exec(cpu);
r = hvf_vcpu_exec(cpu);
if (r == EXCP_DEBUG) {
cpu_handle_guest_debug(cpu);
}
}
qemu_wait_io_event(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
hvf_vcpu_destroy(cpu);
@@ -224,34 +476,6 @@ static void hvf_start_vcpu_thread(CPUState *cpu)
cpu, QEMU_THREAD_JOINABLE);
}
struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, vaddr pc)
{
struct hvf_sw_breakpoint *bp;
QTAILQ_FOREACH(bp, &hvf_state->hvf_sw_breakpoints, entry) {
if (bp->pc == pc) {
return bp;
}
}
return NULL;
}
int hvf_sw_breakpoints_active(CPUState *cpu)
{
return !QTAILQ_EMPTY(&hvf_state->hvf_sw_breakpoints);
}
static void do_hvf_update_guest_debug(CPUState *cpu, run_on_cpu_data arg)
{
hvf_arch_update_guest_debug(cpu);
}
int hvf_update_guest_debug(CPUState *cpu)
{
run_on_cpu(cpu, do_hvf_update_guest_debug, RUN_ON_CPU_NULL);
return 0;
}
static int hvf_insert_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len)
{
struct hvf_sw_breakpoint *bp;
@@ -354,28 +578,12 @@ static void hvf_remove_all_breakpoints(CPUState *cpu)
}
}
static void hvf_get_vcpu_stats(CPUState *cpu, GString *buf)
{
uint64_t time_mach; /* units of mach_absolute_time() */
run_on_cpu(cpu, do_hvf_get_vcpu_exec_time, RUN_ON_CPU_HOST_PTR(&time_mach));
mach_timebase_info_data_t timebase;
mach_timebase_info(&timebase);
uint64_t time_ns = time_mach * timebase.numer / timebase.denom;
g_string_append_printf(buf, "HVF cumulative execution time: %llu.%.3llus\n",
time_ns / 1000000000,
(time_ns % 1000000000) / 1000000);
}
static void hvf_accel_ops_class_init(ObjectClass *oc, const void *data)
static void hvf_accel_ops_class_init(ObjectClass *oc, void *data)
{
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
ops->create_vcpu_thread = hvf_start_vcpu_thread;
ops->kick_vcpu_thread = hvf_kick_vcpu_thread;
ops->handle_interrupt = generic_handle_interrupt;
ops->synchronize_post_reset = hvf_cpu_synchronize_post_reset;
ops->synchronize_post_init = hvf_cpu_synchronize_post_init;
@@ -387,10 +595,7 @@ static void hvf_accel_ops_class_init(ObjectClass *oc, const void *data)
ops->remove_all_breakpoints = hvf_remove_all_breakpoints;
ops->update_guest_debug = hvf_update_guest_debug;
ops->supports_guest_debug = hvf_arch_supports_guest_debug;
ops->get_vcpu_stats = hvf_get_vcpu_stats;
};
static const TypeInfo hvf_accel_ops_type = {
.name = ACCEL_OPS_NAME("hvf"),
@@ -398,10 +603,8 @@ static const TypeInfo hvf_accel_ops_type = {
.class_init = hvf_accel_ops_class_init,
.abstract = true,
};
static void hvf_accel_ops_register_types(void)
{
type_register_static(&hvf_accel_ops_type);
}
type_init(hvf_accel_ops_register_types);
+14 -263
View File
@@ -10,25 +10,8 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "accel/accel-ops.h"
#include "system/address-spaces.h"
#include "system/memory.h"
#include "system/hvf.h"
#include "system/hvf_int.h"
#include "hw/core/cpu.h"
#include "hw/boards.h"
#include "trace.h"
bool hvf_allowed;
struct mac_slot {
int present;
uint64_t size;
uint64_t gpa_start;
uint64_t gva;
};
struct mac_slot mac_slots[32];
const char *hvf_return_string(hv_return_t ret)
{
@@ -58,257 +41,25 @@ void assert_hvf_ok_impl(hv_return_t ret, const char *file, unsigned int line,
abort();
}
static int do_hvf_set_memory(hvf_slot *slot, hv_memory_flags_t flags)
struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, vaddr pc)
{
struct mac_slot *macslot;
hv_return_t ret;
struct hvf_sw_breakpoint *bp;
macslot = &mac_slots[slot->slot_id];
if (macslot->present) {
if (macslot->size != slot->size) {
macslot->present = 0;
trace_hvf_vm_unmap(macslot->gpa_start, macslot->size);
ret = hv_vm_unmap(macslot->gpa_start, macslot->size);
assert_hvf_ok(ret);
QTAILQ_FOREACH(bp, &hvf_state->hvf_sw_breakpoints, entry) {
if (bp->pc == pc) {
return bp;
}
}
return NULL;
}
if (!slot->size) {
return 0;
}
int hvf_sw_breakpoints_active(CPUState *cpu)
{
return !QTAILQ_EMPTY(&hvf_state->hvf_sw_breakpoints);
}
macslot->present = 1;
macslot->gpa_start = slot->start;
macslot->size = slot->size;
trace_hvf_vm_map(slot->start, slot->size, slot->mem, flags,
flags & HV_MEMORY_READ ? 'R' : '-',
flags & HV_MEMORY_WRITE ? 'W' : '-',
flags & HV_MEMORY_EXEC ? 'X' : '-');
ret = hv_vm_map(slot->mem, slot->start, slot->size, flags);
assert_hvf_ok(ret);
int hvf_update_guest_debug(CPUState *cpu)
{
hvf_arch_update_guest_debug(cpu);
return 0;
}
static void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
{
hvf_slot *mem;
MemoryRegion *area = section->mr;
bool writable = !area->readonly && !area->rom_device;
hv_memory_flags_t flags;
uint64_t page_size = qemu_real_host_page_size();
if (!memory_region_is_ram(area)) {
if (writable) {
return;
} else if (!memory_region_is_romd(area)) {
/*
* If the memory device is not in romd_mode, then we actually want
* to remove the hvf memory slot so all accesses will trap.
*/
add = false;
}
}
if (!QEMU_IS_ALIGNED(int128_get64(section->size), page_size) ||
!QEMU_IS_ALIGNED(section->offset_within_address_space, page_size)) {
/* Not page aligned, so we can not map as RAM */
add = false;
}
mem = hvf_find_overlap_slot(
section->offset_within_address_space,
int128_get64(section->size));
if (mem && add) {
if (mem->size == int128_get64(section->size) &&
mem->start == section->offset_within_address_space &&
mem->mem == (memory_region_get_ram_ptr(area) +
section->offset_within_region)) {
return; /* Same region was attempted to register, go away. */
}
}
/* Region needs to be reset. set the size to 0 and remap it. */
if (mem) {
mem->size = 0;
if (do_hvf_set_memory(mem, 0)) {
error_report("Failed to reset overlapping slot");
abort();
}
}
if (!add) {
return;
}
if (area->readonly ||
(!memory_region_is_ram(area) && memory_region_is_romd(area))) {
flags = HV_MEMORY_READ | HV_MEMORY_EXEC;
} else {
flags = HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC;
}
/* Now make a new slot. */
int x;
for (x = 0; x < hvf_state->num_slots; ++x) {
mem = &hvf_state->slots[x];
if (!mem->size) {
break;
}
}
if (x == hvf_state->num_slots) {
error_report("No free slots");
abort();
}
mem->size = int128_get64(section->size);
mem->mem = memory_region_get_ram_ptr(area) + section->offset_within_region;
mem->start = section->offset_within_address_space;
mem->region = area;
if (do_hvf_set_memory(mem, flags)) {
error_report("Error registering new memory slot");
abort();
}
}
static void hvf_set_dirty_tracking(MemoryRegionSection *section, bool on)
{
hvf_slot *slot;
slot = hvf_find_overlap_slot(
section->offset_within_address_space,
int128_get64(section->size));
/* protect region against writes; begin tracking it */
if (on) {
slot->flags |= HVF_SLOT_LOG;
hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size,
HV_MEMORY_READ | HV_MEMORY_EXEC);
/* stop tracking region*/
} else {
slot->flags &= ~HVF_SLOT_LOG;
hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size,
HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC);
}
}
static void hvf_log_start(MemoryListener *listener,
MemoryRegionSection *section, int old, int new)
{
if (old != 0) {
return;
}
hvf_set_dirty_tracking(section, 1);
}
static void hvf_log_stop(MemoryListener *listener,
MemoryRegionSection *section, int old, int new)
{
if (new != 0) {
return;
}
hvf_set_dirty_tracking(section, 0);
}
static void hvf_log_sync(MemoryListener *listener,
MemoryRegionSection *section)
{
/*
* sync of dirty pages is handled elsewhere; just make sure we keep
* tracking the region.
*/
hvf_set_dirty_tracking(section, 1);
}
static void hvf_region_add(MemoryListener *listener,
MemoryRegionSection *section)
{
hvf_set_phys_mem(section, true);
}
static void hvf_region_del(MemoryListener *listener,
MemoryRegionSection *section)
{
hvf_set_phys_mem(section, false);
}
static MemoryListener hvf_memory_listener = {
.name = "hvf",
.priority = MEMORY_LISTENER_PRIORITY_ACCEL,
.region_add = hvf_region_add,
.region_del = hvf_region_del,
.log_start = hvf_log_start,
.log_stop = hvf_log_stop,
.log_sync = hvf_log_sync,
};
static int hvf_accel_init(AccelState *as, MachineState *ms)
{
int x;
hv_return_t ret;
HVFState *s = HVF_STATE(as);
int pa_range = 36;
MachineClass *mc = MACHINE_GET_CLASS(ms);
if (mc->hvf_get_physical_address_range) {
pa_range = mc->hvf_get_physical_address_range(ms);
if (pa_range < 0) {
return -EINVAL;
}
}
ret = hvf_arch_vm_create(ms, (uint32_t)pa_range);
if (ret == HV_DENIED) {
error_report("Could not access HVF. Is the executable signed"
" with com.apple.security.hypervisor entitlement?");
exit(1);
}
assert_hvf_ok(ret);
s->num_slots = ARRAY_SIZE(s->slots);
for (x = 0; x < s->num_slots; ++x) {
s->slots[x].size = 0;
s->slots[x].slot_id = x;
}
QTAILQ_INIT(&s->hvf_sw_breakpoints);
hvf_state = s;
memory_listener_register(&hvf_memory_listener, &address_space_memory);
return hvf_arch_init();
}
static int hvf_gdbstub_sstep_flags(AccelState *as)
{
return SSTEP_ENABLE | SSTEP_NOIRQ;
}
static void hvf_accel_class_init(ObjectClass *oc, const void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "HVF";
ac->init_machine = hvf_accel_init;
ac->allowed = &hvf_allowed;
ac->gdbstub_supported_sstep_flags = hvf_gdbstub_sstep_flags;
}
static const TypeInfo hvf_accel_type = {
.name = TYPE_HVF_ACCEL,
.parent = TYPE_ACCEL,
.instance_size = sizeof(HVFState),
.class_init = hvf_accel_class_init,
};
static void hvf_type_init(void)
{
type_register_static(&hvf_accel_type);
}
type_init(hvf_type_init);
-7
View File
@@ -1,7 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# See docs/devel/tracing.rst for syntax documentation.
# hvf-accel-ops.c
hvf_vm_map(uint64_t paddr, uint64_t size, void *vaddr, uint8_t flags, const char r, const char w, const char e) "paddr:0x%016"PRIx64" size:0x%08"PRIx64" vaddr:%p flags:0x%02x/%c%c%c"
hvf_vm_unmap(uint64_t paddr, uint64_t size) "paddr:0x%016"PRIx64" size:0x%08"PRIx64
-2
View File
@@ -1,2 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "trace/trace-accel_hvf.h"
+3 -5
View File
@@ -16,7 +16,7 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
#include "accel/accel-cpu-ops.h"
#include "system/accel-ops.h"
#include "system/kvm.h"
#include "system/kvm_int.h"
#include "system/runstate.h"
@@ -47,14 +47,13 @@ static void *kvm_vcpu_thread_fn(void *arg)
qemu_guest_random_seed_thread_part2(cpu->random_seed);
do {
qemu_process_cpu_events(cpu);
if (cpu_can_run(cpu)) {
r = kvm_cpu_exec(cpu);
if (r == EXCP_DEBUG) {
cpu_handle_guest_debug(cpu);
}
}
qemu_wait_io_event(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
kvm_destroy_vcpu(cpu);
@@ -91,7 +90,7 @@ static int kvm_update_guest_debug_ops(CPUState *cpu)
}
#endif
static void kvm_accel_ops_class_init(ObjectClass *oc, const void *data)
static void kvm_accel_ops_class_init(ObjectClass *oc, void *data)
{
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
@@ -102,7 +101,6 @@ static void kvm_accel_ops_class_init(ObjectClass *oc, const void *data)
ops->synchronize_post_init = kvm_cpu_synchronize_post_init;
ops->synchronize_state = kvm_cpu_synchronize_state;
ops->synchronize_pre_loadvm = kvm_cpu_synchronize_pre_loadvm;
ops->handle_interrupt = generic_handle_interrupt;
#ifdef TARGET_KVM_HAVE_GUEST_DEBUG
ops->update_guest_debug = kvm_update_guest_debug_ops;
+87 -137
View File
@@ -32,13 +32,9 @@
#include "system/runstate.h"
#include "system/cpus.h"
#include "system/accel-blocker.h"
#include "system/physmem.h"
#include "system/ramblock.h"
#include "accel/accel-ops.h"
#include "qemu/bswap.h"
#include "exec/tswap.h"
#include "exec/target_page.h"
#include "system/memory.h"
#include "exec/memory.h"
#include "exec/ram_addr.h"
#include "qemu/event_notifier.h"
#include "qemu/main-loop.h"
#include "trace.h"
@@ -61,11 +57,6 @@
#include <sys/eventfd.h>
#endif
#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
# define KVM_HAVE_MCE_INJECTION 1
#endif
/* KVM uses PAGE_SIZE in its definition of KVM_COALESCED_MMIO_MAX. We
* need to use the real host PAGE_SIZE, as that's what KVM will use.
*/
@@ -102,7 +93,6 @@ bool kvm_allowed;
bool kvm_readonly_mem_allowed;
bool kvm_vm_attributes_allowed;
bool kvm_msi_use_devid;
bool kvm_pre_fault_memory_supported;
static bool kvm_has_guest_debug;
static int kvm_sstep_flags;
static bool kvm_immediate_exit;
@@ -360,7 +350,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram,
static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new)
{
KVMState *s = kvm_state;
struct kvm_userspace_memory_region2 mem = {};
struct kvm_userspace_memory_region2 mem;
int ret;
mem.slot = slot->slot | (kml->as_id << 16);
@@ -416,7 +406,7 @@ err:
return ret;
}
static void kvm_park_vcpu(CPUState *cpu)
void kvm_park_vcpu(CPUState *cpu)
{
struct KVMParkedVcpu *vcpu;
@@ -428,7 +418,7 @@ static void kvm_park_vcpu(CPUState *cpu)
QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node);
}
static int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id)
int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id)
{
struct KVMParkedVcpu *cpu;
int kvm_fd = -ENOENT;
@@ -456,13 +446,7 @@ static void kvm_reset_parked_vcpus(KVMState *s)
}
}
/**
* kvm_create_vcpu - Gets a parked KVM vCPU or creates a KVM vCPU
* @cpu: QOM CPUState object for which KVM vCPU has to be fetched/created.
*
* @returns: 0 when success, errno (<0) when failed.
*/
static int kvm_create_vcpu(CPUState *cpu)
int kvm_create_vcpu(CPUState *cpu)
{
unsigned long vcpu_id = kvm_arch_vcpu_id(cpu);
KVMState *s = kvm_state;
@@ -481,9 +465,7 @@ static int kvm_create_vcpu(CPUState *cpu)
cpu->kvm_fd = kvm_fd;
cpu->kvm_state = s;
if (!s->guest_state_protected) {
cpu->vcpu_dirty = true;
}
cpu->vcpu_dirty = true;
cpu->dirty_pages = 0;
cpu->throttle_us_per_full = 0;
@@ -524,24 +506,16 @@ static int do_kvm_destroy_vcpu(CPUState *cpu)
goto err;
}
/* If I am the CPU that created coalesced_mmio_ring, then discard it */
if (s->coalesced_mmio_ring ==
(void *)cpu->kvm_run + s->coalesced_mmio * PAGE_SIZE) {
s->coalesced_mmio_ring = NULL;
}
ret = munmap(cpu->kvm_run, mmap_size);
if (ret < 0) {
goto err;
}
cpu->kvm_run = NULL;
if (cpu->kvm_dirty_gfns) {
ret = munmap(cpu->kvm_dirty_gfns, s->kvm_dirty_ring_bytes);
if (ret < 0) {
goto err;
}
cpu->kvm_dirty_gfns = NULL;
}
kvm_park_vcpu(cpu);
@@ -565,11 +539,6 @@ int kvm_init_vcpu(CPUState *cpu, Error **errp)
trace_kvm_init_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu));
ret = kvm_arch_pre_create_vcpu(cpu, errp);
if (ret < 0) {
goto err;
}
ret = kvm_create_vcpu(cpu);
if (ret < 0) {
error_setg_errno(errp, -ret,
@@ -625,31 +594,6 @@ err:
return ret;
}
void kvm_close(void)
{
CPUState *cpu;
if (!kvm_state || kvm_state->fd == -1) {
return;
}
CPU_FOREACH(cpu) {
cpu_remove_sync(cpu);
close(cpu->kvm_fd);
cpu->kvm_fd = -1;
close(cpu->kvm_vcpu_stats_fd);
cpu->kvm_vcpu_stats_fd = -1;
}
if (kvm_state && kvm_state->fd != -1) {
close(kvm_state->vmfd);
kvm_state->vmfd = -1;
close(kvm_state->fd);
kvm_state->fd = -1;
}
kvm_state = NULL;
}
/*
* dirty pages logging control
*/
@@ -759,7 +703,7 @@ static void kvm_slot_sync_dirty_pages(KVMSlot *slot)
ram_addr_t start = slot->ram_start_offset;
ram_addr_t pages = slot->memory_size / qemu_real_host_page_size();
physical_memory_set_dirty_lebitmap(slot->dirty_bmap, start, pages);
cpu_physical_memory_set_dirty_lebitmap(slot->dirty_bmap, start, pages);
}
static void kvm_slot_reset_dirty_pages(KVMSlot *slot)
@@ -1369,22 +1313,21 @@ bool kvm_hwpoisoned_mem(void)
static uint32_t adjust_ioeventfd_endianness(uint32_t val, uint32_t size)
{
if (target_needs_bswap()) {
/*
* The kernel expects ioeventfd values in HOST_BIG_ENDIAN
* endianness, but the memory core hands them in target endianness.
* For example, PPC is always treated as big-endian even if running
* on KVM and on PPC64LE. Correct here, swapping back.
*/
switch (size) {
case 2:
val = bswap16(val);
break;
case 4:
val = bswap32(val);
break;
}
#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN
/* The kernel expects ioeventfd values in HOST_BIG_ENDIAN
* endianness, but the memory core hands them in target endianness.
* For example, PPC is always treated as big-endian even if running
* on KVM and on PPC64LE. Correct here.
*/
switch (size) {
case 2:
val = bswap16(val);
break;
case 4:
val = bswap32(val);
break;
}
#endif
return val;
}
@@ -1598,8 +1541,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
mem->ram = ram;
mem->flags = kvm_mem_flags(mr);
mem->guest_memfd = mr->ram_block->guest_memfd;
mem->guest_memfd_offset = mem->guest_memfd >= 0 ?
(uint8_t*)ram - mr->ram_block->host : 0;
mem->guest_memfd_offset = (uint8_t*)ram - mr->ram_block->host;
kvm_slot_init_dirty_bitmap(mem);
err = kvm_set_user_memory_region(kml, mem, true);
@@ -2477,7 +2419,7 @@ static int kvm_recommended_vcpus(KVMState *s)
static int kvm_max_vcpus(KVMState *s)
{
int ret = kvm_vm_check_extension(s, KVM_CAP_MAX_VCPUS);
int ret = kvm_check_extension(s, KVM_CAP_MAX_VCPUS);
return (ret) ? ret : kvm_recommended_vcpus(s);
}
@@ -2507,10 +2449,13 @@ uint32_t kvm_dirty_ring_size(void)
return kvm_state->kvm_dirty_ring_size;
}
static int do_kvm_create_vm(KVMState *s, int type)
static int do_kvm_create_vm(MachineState *ms, int type)
{
KVMState *s;
int ret;
s = KVM_STATE(ms->accelerator);
do {
ret = kvm_ioctl(s, KVM_CREATE_VM, type);
} while (ret == -EINTR);
@@ -2607,11 +2552,12 @@ static int kvm_setup_dirty_ring(KVMState *s)
return 0;
}
static int kvm_init(AccelState *as, MachineState *ms)
static int kvm_init(MachineState *ms)
{
MachineClass *mc = MACHINE_GET_CLASS(ms);
static const char upgrade_note[] =
"Please upgrade to at least kernel 4.5.\n";
"Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n"
"(see http://sourceforge.net/projects/kvm).\n";
const struct {
const char *name;
int num;
@@ -2621,13 +2567,15 @@ static int kvm_init(AccelState *as, MachineState *ms)
{ /* end of list */ }
}, *nc = num_cpus;
int soft_vcpus_limit, hard_vcpus_limit;
KVMState *s = KVM_STATE(as);
KVMState *s;
const KVMCapabilityInfo *missing_cap;
int ret;
int type;
qemu_mutex_init(&kml_slots_lock);
s = KVM_STATE(ms->accelerator);
/*
* On systems where the kernel can support different base page
* sizes, host page size may be different from TARGET_PAGE_SIZE,
@@ -2679,7 +2627,7 @@ static int kvm_init(AccelState *as, MachineState *ms)
goto err;
}
ret = do_kvm_create_vm(s, type);
ret = do_kvm_create_vm(ms, type);
if (ret < 0) {
goto err;
}
@@ -2780,10 +2728,9 @@ static int kvm_init(AccelState *as, MachineState *ms)
kvm_supported_memory_attributes = kvm_vm_check_extension(s, KVM_CAP_MEMORY_ATTRIBUTES);
kvm_guest_memfd_supported =
kvm_vm_check_extension(s, KVM_CAP_GUEST_MEMFD) &&
kvm_vm_check_extension(s, KVM_CAP_USER_MEMORY2) &&
kvm_check_extension(s, KVM_CAP_GUEST_MEMFD) &&
kvm_check_extension(s, KVM_CAP_USER_MEMORY2) &&
(kvm_supported_memory_attributes & KVM_MEMORY_ATTRIBUTE_PRIVATE);
kvm_pre_fault_memory_supported = kvm_vm_check_extension(s, KVM_CAP_PRE_FAULT_MEMORY);
if (s->kernel_irqchip_split == ON_OFF_AUTO_AUTO) {
s->kernel_irqchip_split = mc->default_kernel_irqchip_split ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
@@ -2938,32 +2885,22 @@ void kvm_cpu_synchronize_state(CPUState *cpu)
}
}
static bool kvm_cpu_synchronize_put(CPUState *cpu, KvmPutState state,
const char *desc)
{
Error *err = NULL;
int ret = kvm_arch_put_registers(cpu, state, &err);
if (ret) {
if (err) {
error_reportf_err(err, "Restoring resisters %s: ", desc);
} else {
error_report("Failed to put registers %s: %s", desc,
strerror(-ret));
}
return false;
}
cpu->vcpu_dirty = false;
return true;
}
static void do_kvm_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg)
{
if (!kvm_cpu_synchronize_put(cpu, KVM_PUT_RESET_STATE, "after reset")) {
Error *err = NULL;
int ret = kvm_arch_put_registers(cpu, KVM_PUT_RESET_STATE, &err);
if (ret) {
if (err) {
error_reportf_err(err, "Restoring resisters after reset: ");
} else {
error_report("Failed to put registers after reset: %s",
strerror(-ret));
}
cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
vm_stop(RUN_STATE_INTERNAL_ERROR);
}
cpu->vcpu_dirty = false;
}
void kvm_cpu_synchronize_post_reset(CPUState *cpu)
@@ -2977,9 +2914,19 @@ void kvm_cpu_synchronize_post_reset(CPUState *cpu)
static void do_kvm_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg)
{
if (!kvm_cpu_synchronize_put(cpu, KVM_PUT_FULL_STATE, "after init")) {
Error *err = NULL;
int ret = kvm_arch_put_registers(cpu, KVM_PUT_FULL_STATE, &err);
if (ret) {
if (err) {
error_reportf_err(err, "Putting registers after init: ");
} else {
error_report("Failed to put registers after init: %s",
strerror(-ret));
}
exit(1);
}
cpu->vcpu_dirty = false;
}
void kvm_cpu_synchronize_post_init(CPUState *cpu)
@@ -3033,6 +2980,10 @@ static void kvm_eat_signals(CPUState *cpu)
if (kvm_immediate_exit) {
qatomic_set(&cpu->kvm_run->immediate_exit, 0);
/* Write kvm_run->immediate_exit before the cpu->exit_request
* write in kvm_cpu_exec.
*/
smp_wmb();
return;
}
@@ -3124,15 +3075,6 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
addr = memory_region_get_ram_ptr(mr) + section.offset_within_region;
rb = qemu_ram_block_from_host(addr, false, &offset);
ret = ram_block_attributes_state_change(RAM_BLOCK_ATTRIBUTES(mr->rdm),
offset, size, to_private);
if (ret) {
error_report("Failed to notify the listener the state change of "
"(0x%"HWADDR_PRIx" + 0x%"HWADDR_PRIx") to %s",
start, size, to_private ? "private" : "shared");
goto out_unref;
}
if (to_private) {
if (rb->page_size != qemu_real_host_page_size()) {
/*
@@ -3159,6 +3101,7 @@ int kvm_cpu_exec(CPUState *cpu)
trace_kvm_cpu_exec();
if (kvm_arch_process_async_events(cpu)) {
qatomic_set(&cpu->exit_request, 0);
return EXCP_HLT;
}
@@ -3169,16 +3112,24 @@ int kvm_cpu_exec(CPUState *cpu)
MemTxAttrs attrs;
if (cpu->vcpu_dirty) {
if (!kvm_cpu_synchronize_put(cpu, KVM_PUT_RUNTIME_STATE,
"at runtime")) {
Error *err = NULL;
ret = kvm_arch_put_registers(cpu, KVM_PUT_RUNTIME_STATE, &err);
if (ret) {
if (err) {
error_reportf_err(err, "Putting registers after init: ");
} else {
error_report("Failed to put registers after init: %s",
strerror(-ret));
}
ret = -1;
break;
}
cpu->vcpu_dirty = false;
}
kvm_arch_pre_run(cpu, run);
/* Corresponding store-release is in cpu_exit. */
if (qatomic_load_acquire(&cpu->exit_request)) {
if (qatomic_read(&cpu->exit_request)) {
trace_kvm_interrupt_exit_request();
/*
* KVM requires us to reenter the kernel after IO exits to complete
@@ -3188,14 +3139,12 @@ int kvm_cpu_exec(CPUState *cpu)
kvm_cpu_kick_self();
}
run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0);
/*
* After writing cpu->exit_request, cpu_exit() sends a signal that writes
* kvm->run->immediate_exit. The signal is already happening after the
* write to cpu->exit_request so, if KVM read kvm->run->immediate_exit
* as true, cpu->exit_request will always read as true.
/* Read cpu->exit_request before KVM_RUN reads run->immediate_exit.
* Matching barrier in kvm_eat_signals.
*/
smp_rmb();
run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0);
attrs = kvm_arch_post_run(cpu, run);
@@ -3339,6 +3288,7 @@ int kvm_cpu_exec(CPUState *cpu)
vm_stop(RUN_STATE_INTERNAL_ERROR);
}
qatomic_set(&cpu->exit_request, 0);
return ret;
}
@@ -3723,7 +3673,7 @@ int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr)
have_sigbus_pending = true;
pending_sigbus_addr = addr;
pending_sigbus_code = code;
qatomic_set(&cpu->exit_request, true);
qatomic_set(&cpu->exit_request, 1);
return 0;
#else
return 1;
@@ -3810,10 +3760,10 @@ int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target)
return r;
}
static bool kvm_accel_has_memory(AccelState *accel, AddressSpace *as,
static bool kvm_accel_has_memory(MachineState *ms, AddressSpace *as,
hwaddr start_addr, hwaddr size)
{
KVMState *kvm = KVM_STATE(accel);
KVMState *kvm = KVM_STATE(ms->accelerator);
int i;
for (i = 0; i < kvm->nr_as; ++i) {
@@ -4004,12 +3954,12 @@ static void kvm_accel_instance_init(Object *obj)
* Returns: SSTEP_* flags that KVM supports for guest debug. The
* support is probed during kvm_init()
*/
static int kvm_gdbstub_sstep_flags(AccelState *as)
static int kvm_gdbstub_sstep_flags(void)
{
return kvm_sstep_flags;
}
static void kvm_accel_class_init(ObjectClass *oc, const void *data)
static void kvm_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "KVM";
+1 -3
View File
@@ -1,6 +1,5 @@
common_ss.add(files('accel-common.c'))
specific_ss.add(files('accel-target.c'))
system_ss.add(files('accel-system.c', 'accel-blocker.c', 'accel-qmp.c', 'accel-irq.c'))
system_ss.add(files('accel-system.c', 'accel-blocker.c'))
user_ss.add(files('accel-user.c'))
subdir('tcg')
@@ -10,7 +9,6 @@ if have_system
subdir('kvm')
subdir('xen')
subdir('stubs')
subdir('mshv')
endif
# qtest
-399
View File
@@ -1,399 +0,0 @@
/*
* QEMU MSHV support
*
* Copyright Microsoft, Corp. 2025
*
* Authors: Ziqiao Zhou <ziqiaozhou@microsoft.com>
* Magnus Kulke <magnuskulke@microsoft.com>
* Stanislav Kinsburskii <skinsburskii@microsoft.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "linux/mshv.h"
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "hw/hyperv/hvhdk_mini.h"
#include "hw/hyperv/hvgdk_mini.h"
#include "hw/intc/ioapic.h"
#include "hw/pci/msi.h"
#include "system/mshv.h"
#include "system/mshv_int.h"
#include "trace.h"
#include <stdint.h>
#include <sys/ioctl.h>
#define MSHV_IRQFD_RESAMPLE_FLAG (1 << MSHV_IRQFD_BIT_RESAMPLE)
#define MSHV_IRQFD_BIT_DEASSIGN_FLAG (1 << MSHV_IRQFD_BIT_DEASSIGN)
static MshvMsiControl *msi_control;
static QemuMutex msi_control_mutex;
void mshv_init_msicontrol(void)
{
qemu_mutex_init(&msi_control_mutex);
msi_control = g_new0(MshvMsiControl, 1);
msi_control->gsi_routes = g_hash_table_new(g_direct_hash, g_direct_equal);
msi_control->updated = false;
}
static int set_msi_routing(uint32_t gsi, uint64_t addr, uint32_t data)
{
struct mshv_user_irq_entry *entry;
uint32_t high_addr = addr >> 32;
uint32_t low_addr = addr & 0xFFFFFFFF;
GHashTable *gsi_routes;
trace_mshv_set_msi_routing(gsi, addr, data);
if (gsi >= MSHV_MAX_MSI_ROUTES) {
error_report("gsi >= MSHV_MAX_MSI_ROUTES");
return -1;
}
assert(msi_control);
WITH_QEMU_LOCK_GUARD(&msi_control_mutex) {
gsi_routes = msi_control->gsi_routes;
entry = g_hash_table_lookup(gsi_routes, GINT_TO_POINTER(gsi));
if (entry
&& entry->address_hi == high_addr
&& entry->address_lo == low_addr
&& entry->data == data)
{
/* nothing to update */
return 0;
}
/* free old entry */
g_free(entry);
/* create new entry */
entry = g_new0(struct mshv_user_irq_entry, 1);
entry->gsi = gsi;
entry->address_hi = high_addr;
entry->address_lo = low_addr;
entry->data = data;
g_hash_table_insert(gsi_routes, GINT_TO_POINTER(gsi), entry);
msi_control->updated = true;
}
return 0;
}
static int add_msi_routing(uint64_t addr, uint32_t data)
{
struct mshv_user_irq_entry *route_entry;
uint32_t high_addr = addr >> 32;
uint32_t low_addr = addr & 0xFFFFFFFF;
int gsi;
GHashTable *gsi_routes;
trace_mshv_add_msi_routing(addr, data);
assert(msi_control);
WITH_QEMU_LOCK_GUARD(&msi_control_mutex) {
/* find an empty slot */
gsi = 0;
gsi_routes = msi_control->gsi_routes;
while (gsi < MSHV_MAX_MSI_ROUTES) {
route_entry = g_hash_table_lookup(gsi_routes, GINT_TO_POINTER(gsi));
if (!route_entry) {
break;
}
gsi++;
}
if (gsi >= MSHV_MAX_MSI_ROUTES) {
error_report("No empty gsi slot available");
return -1;
}
/* create new entry */
route_entry = g_new0(struct mshv_user_irq_entry, 1);
route_entry->gsi = gsi;
route_entry->address_hi = high_addr;
route_entry->address_lo = low_addr;
route_entry->data = data;
g_hash_table_insert(gsi_routes, GINT_TO_POINTER(gsi), route_entry);
msi_control->updated = true;
}
return gsi;
}
static int commit_msi_routing_table(int vm_fd)
{
guint len;
int i, ret;
size_t table_size;
struct mshv_user_irq_table *table;
GHashTableIter iter;
gpointer key, value;
assert(msi_control);
WITH_QEMU_LOCK_GUARD(&msi_control_mutex) {
if (!msi_control->updated) {
/* nothing to update */
return 0;
}
/* Calculate the size of the table */
len = g_hash_table_size(msi_control->gsi_routes);
table_size = sizeof(struct mshv_user_irq_table)
+ len * sizeof(struct mshv_user_irq_entry);
table = g_malloc0(table_size);
g_hash_table_iter_init(&iter, msi_control->gsi_routes);
i = 0;
while (g_hash_table_iter_next(&iter, &key, &value)) {
struct mshv_user_irq_entry *entry = value;
table->entries[i] = *entry;
i++;
}
table->nr = i;
trace_mshv_commit_msi_routing_table(vm_fd, len);
ret = ioctl(vm_fd, MSHV_SET_MSI_ROUTING, table);
g_free(table);
if (ret < 0) {
error_report("Failed to commit msi routing table");
return -1;
}
msi_control->updated = false;
}
return 0;
}
static int remove_msi_routing(uint32_t gsi)
{
struct mshv_user_irq_entry *route_entry;
GHashTable *gsi_routes;
trace_mshv_remove_msi_routing(gsi);
if (gsi >= MSHV_MAX_MSI_ROUTES) {
error_report("Invalid GSI: %u", gsi);
return -1;
}
assert(msi_control);
WITH_QEMU_LOCK_GUARD(&msi_control_mutex) {
gsi_routes = msi_control->gsi_routes;
route_entry = g_hash_table_lookup(gsi_routes, GINT_TO_POINTER(gsi));
if (route_entry) {
g_hash_table_remove(gsi_routes, GINT_TO_POINTER(gsi));
g_free(route_entry);
msi_control->updated = true;
}
}
return 0;
}
/* Pass an eventfd which is to be used for injecting interrupts from userland */
static int irqfd(int vm_fd, int fd, int resample_fd, uint32_t gsi,
uint32_t flags)
{
int ret;
struct mshv_user_irqfd arg = {
.fd = fd,
.resamplefd = resample_fd,
.gsi = gsi,
.flags = flags,
};
ret = ioctl(vm_fd, MSHV_IRQFD, &arg);
if (ret < 0) {
error_report("Failed to set irqfd: gsi=%u, fd=%d", gsi, fd);
return -1;
}
return ret;
}
static int register_irqfd(int vm_fd, int event_fd, uint32_t gsi)
{
int ret;
trace_mshv_register_irqfd(vm_fd, event_fd, gsi);
ret = irqfd(vm_fd, event_fd, 0, gsi, 0);
if (ret < 0) {
error_report("Failed to register irqfd: gsi=%u", gsi);
return -1;
}
return 0;
}
static int register_irqfd_with_resample(int vm_fd, int event_fd,
int resample_fd, uint32_t gsi)
{
int ret;
uint32_t flags = MSHV_IRQFD_RESAMPLE_FLAG;
ret = irqfd(vm_fd, event_fd, resample_fd, gsi, flags);
if (ret < 0) {
error_report("Failed to register irqfd with resample: gsi=%u", gsi);
return -errno;
}
return 0;
}
static int unregister_irqfd(int vm_fd, int event_fd, uint32_t gsi)
{
int ret;
uint32_t flags = MSHV_IRQFD_BIT_DEASSIGN_FLAG;
ret = irqfd(vm_fd, event_fd, 0, gsi, flags);
if (ret < 0) {
error_report("Failed to unregister irqfd: gsi=%u", gsi);
return -errno;
}
return 0;
}
static int irqchip_update_irqfd_notifier_gsi(const EventNotifier *event,
const EventNotifier *resample,
int virq, bool add)
{
int fd = event_notifier_get_fd(event);
int rfd = resample ? event_notifier_get_fd(resample) : -1;
int vm_fd = mshv_state->vm;
trace_mshv_irqchip_update_irqfd_notifier_gsi(fd, rfd, virq, add);
if (!add) {
return unregister_irqfd(vm_fd, fd, virq);
}
if (rfd > 0) {
return register_irqfd_with_resample(vm_fd, fd, rfd, virq);
}
return register_irqfd(vm_fd, fd, virq);
}
int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev)
{
MSIMessage msg = { 0, 0 };
int virq = 0;
if (pci_available && dev) {
msg = pci_get_msi_message(dev, vector);
virq = add_msi_routing(msg.address, le32_to_cpu(msg.data));
}
return virq;
}
void mshv_irqchip_release_virq(int virq)
{
remove_msi_routing(virq);
}
int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev)
{
int ret;
ret = set_msi_routing(virq, msg.address, le32_to_cpu(msg.data));
if (ret < 0) {
error_report("Failed to set msi routing");
return -1;
}
return 0;
}
int mshv_request_interrupt(MshvState *mshv_state, uint32_t interrupt_type, uint32_t vector,
uint32_t vp_index, bool logical_dest_mode,
bool level_triggered)
{
int ret;
int vm_fd = mshv_state->vm;
if (vector == 0) {
warn_report("Ignoring request for interrupt vector 0");
return 0;
}
union hv_interrupt_control control = {
.interrupt_type = interrupt_type,
.level_triggered = level_triggered,
.logical_dest_mode = logical_dest_mode,
.rsvd = 0,
};
struct hv_input_assert_virtual_interrupt arg = {0};
arg.control = control;
arg.dest_addr = (uint64_t)vp_index;
arg.vector = vector;
struct mshv_root_hvcall args = {0};
args.code = HVCALL_ASSERT_VIRTUAL_INTERRUPT;
args.in_sz = sizeof(arg);
args.in_ptr = (uint64_t)&arg;
ret = mshv_hvcall(vm_fd, &args);
if (ret < 0) {
error_report("Failed to request interrupt");
return -errno;
}
return 0;
}
void mshv_irqchip_commit_routes(void)
{
int ret;
int vm_fd = mshv_state->vm;
ret = commit_msi_routing_table(vm_fd);
if (ret < 0) {
error_report("Failed to commit msi routing table");
abort();
}
}
int mshv_irqchip_add_irqfd_notifier_gsi(const EventNotifier *event,
const EventNotifier *resample,
int virq)
{
return irqchip_update_irqfd_notifier_gsi(event, resample, virq, true);
}
int mshv_irqchip_remove_irqfd_notifier_gsi(const EventNotifier *event,
int virq)
{
return irqchip_update_irqfd_notifier_gsi(event, NULL, virq, false);
}
int mshv_reserve_ioapic_msi_routes(int vm_fd)
{
int ret, gsi;
/*
* Reserve GSI 0-23 for IOAPIC pins, to avoid conflicts of legacy
* peripherals with MSI-X devices
*/
for (gsi = 0; gsi < IOAPIC_NUM_PINS; gsi++) {
ret = add_msi_routing(0, 0);
if (ret < 0) {
error_report("Failed to reserve GSI %d", gsi);
return -1;
}
}
ret = commit_msi_routing_table(vm_fd);
if (ret < 0) {
error_report("Failed to commit reserved IOAPIC MSI routes");
return -1;
}
return 0;
}
-563
View File
@@ -1,563 +0,0 @@
/*
* QEMU MSHV support
*
* Copyright Microsoft, Corp. 2025
*
* Authors:
* Magnus Kulke <magnuskulke@microsoft.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
*/
#include "qemu/osdep.h"
#include "qemu/lockable.h"
#include "qemu/error-report.h"
#include "qemu/rcu.h"
#include "linux/mshv.h"
#include "system/address-spaces.h"
#include "system/mshv.h"
#include "system/mshv_int.h"
#include "exec/memattrs.h"
#include <sys/ioctl.h>
#include "trace.h"
typedef struct SlotsRCUReclaim {
struct rcu_head rcu;
GList *old_head;
MshvMemorySlot *removed_slot;
} SlotsRCUReclaim;
static void rcu_reclaim_slotlist(struct rcu_head *rcu)
{
SlotsRCUReclaim *r = container_of(rcu, SlotsRCUReclaim, rcu);
g_list_free(r->old_head);
g_free(r->removed_slot);
g_free(r);
}
static void publish_slots(GList *new_head, GList *old_head,
MshvMemorySlot *removed_slot)
{
MshvMemorySlotManager *manager = &mshv_state->msm;
assert(manager);
qatomic_store_release(&manager->slots, new_head);
SlotsRCUReclaim *r = g_new(SlotsRCUReclaim, 1);
r->old_head = old_head;
r->removed_slot = removed_slot;
call_rcu1(&r->rcu, rcu_reclaim_slotlist);
}
/* Needs to be called with mshv_state->msm.mutex held */
static int remove_slot(MshvMemorySlot *slot)
{
GList *old_head, *new_head;
MshvMemorySlotManager *manager = &mshv_state->msm;
assert(manager);
old_head = qatomic_load_acquire(&manager->slots);
if (!g_list_find(old_head, slot)) {
error_report("slot requested for removal not found");
return -1;
}
new_head = g_list_copy(old_head);
new_head = g_list_remove(new_head, slot);
manager->n_slots--;
publish_slots(new_head, old_head, slot);
return 0;
}
/* Needs to be called with mshv_state->msm.mutex held */
static MshvMemorySlot *append_slot(uint64_t gpa, uint64_t userspace_addr,
uint64_t size, bool readonly)
{
GList *old_head, *new_head;
MshvMemorySlot *slot;
MshvMemorySlotManager *manager = &mshv_state->msm;
assert(manager);
old_head = qatomic_load_acquire(&manager->slots);
if (manager->n_slots >= MSHV_MAX_MEM_SLOTS) {
error_report("no free memory slots available");
return NULL;
}
slot = g_new0(MshvMemorySlot, 1);
slot->guest_phys_addr = gpa;
slot->userspace_addr = userspace_addr;
slot->memory_size = size;
slot->readonly = readonly;
new_head = g_list_copy(old_head);
new_head = g_list_append(new_head, slot);
manager->n_slots++;
publish_slots(new_head, old_head, NULL);
return slot;
}
static int slot_overlaps(const MshvMemorySlot *slot1,
const MshvMemorySlot *slot2)
{
uint64_t start_1 = slot1->userspace_addr,
start_2 = slot2->userspace_addr;
size_t len_1 = slot1->memory_size,
len_2 = slot2->memory_size;
if (slot1 == slot2) {
return -1;
}
return ranges_overlap(start_1, len_1, start_2, len_2) ? 0 : -1;
}
static bool is_mapped(MshvMemorySlot *slot)
{
/* Subsequent reads of mapped field see a fully-initialized slot */
return qatomic_load_acquire(&slot->mapped);
}
/*
* Find slot that is:
* - overlapping in userspace
* - currently mapped in the guest
*
* Needs to be called with mshv_state->msm.mutex or RCU read lock held.
*/
static MshvMemorySlot *find_overlap_mem_slot(GList *head, MshvMemorySlot *slot)
{
GList *found;
MshvMemorySlot *overlap_slot;
found = g_list_find_custom(head, slot, (GCompareFunc) slot_overlaps);
if (!found) {
return NULL;
}
overlap_slot = found->data;
if (!overlap_slot || !is_mapped(overlap_slot)) {
return NULL;
}
return overlap_slot;
}
static int set_guest_memory(int vm_fd,
const struct mshv_user_mem_region *region)
{
int ret;
ret = ioctl(vm_fd, MSHV_SET_GUEST_MEMORY, region);
if (ret < 0) {
error_report("failed to set guest memory: %s", strerror(errno));
return -1;
}
return 0;
}
static int map_or_unmap(int vm_fd, const MshvMemorySlot *slot, bool map)
{
struct mshv_user_mem_region region = {0};
region.guest_pfn = slot->guest_phys_addr >> MSHV_PAGE_SHIFT;
region.size = slot->memory_size;
region.userspace_addr = slot->userspace_addr;
if (!map) {
region.flags |= (1 << MSHV_SET_MEM_BIT_UNMAP);
trace_mshv_unmap_memory(slot->userspace_addr, slot->guest_phys_addr,
slot->memory_size);
return set_guest_memory(vm_fd, &region);
}
region.flags = BIT(MSHV_SET_MEM_BIT_EXECUTABLE);
if (!slot->readonly) {
region.flags |= BIT(MSHV_SET_MEM_BIT_WRITABLE);
}
trace_mshv_map_memory(slot->userspace_addr, slot->guest_phys_addr,
slot->memory_size);
return set_guest_memory(vm_fd, &region);
}
static int slot_matches_region(const MshvMemorySlot *slot1,
const MshvMemorySlot *slot2)
{
return (slot1->guest_phys_addr == slot2->guest_phys_addr &&
slot1->userspace_addr == slot2->userspace_addr &&
slot1->memory_size == slot2->memory_size) ? 0 : -1;
}
/* Needs to be called with mshv_state->msm.mutex held */
static MshvMemorySlot *find_mem_slot_by_region(uint64_t gpa, uint64_t size,
uint64_t userspace_addr)
{
MshvMemorySlot ref_slot = {
.guest_phys_addr = gpa,
.userspace_addr = userspace_addr,
.memory_size = size,
};
GList *found;
MshvMemorySlotManager *manager = &mshv_state->msm;
assert(manager);
found = g_list_find_custom(manager->slots, &ref_slot,
(GCompareFunc) slot_matches_region);
return found ? found->data : NULL;
}
static int slot_covers_gpa(const MshvMemorySlot *slot, uint64_t *gpa_p)
{
uint64_t gpa_offset, gpa = *gpa_p;
gpa_offset = gpa - slot->guest_phys_addr;
return (slot->guest_phys_addr <= gpa && gpa_offset < slot->memory_size)
? 0 : -1;
}
/* Needs to be called with mshv_state->msm.mutex or RCU read lock held */
static MshvMemorySlot *find_mem_slot_by_gpa(GList *head, uint64_t gpa)
{
GList *found;
MshvMemorySlot *slot;
trace_mshv_find_slot_by_gpa(gpa);
found = g_list_find_custom(head, &gpa, (GCompareFunc) slot_covers_gpa);
if (found) {
slot = found->data;
trace_mshv_found_slot(slot->userspace_addr, slot->guest_phys_addr,
slot->memory_size);
return slot;
}
return NULL;
}
/* Needs to be called with mshv_state->msm.mutex held */
static void set_mapped(MshvMemorySlot *slot, bool mapped)
{
/* prior writes to mapped field becomes visible before readers see slot */
qatomic_store_release(&slot->mapped, mapped);
}
MshvRemapResult mshv_remap_overlap_region(int vm_fd, uint64_t gpa)
{
MshvMemorySlot *gpa_slot, *overlap_slot;
GList *head;
int ret;
MshvMemorySlotManager *manager = &mshv_state->msm;
/* fast path, called often by unmapped_gpa vm exit */
WITH_RCU_READ_LOCK_GUARD() {
assert(manager);
head = qatomic_load_acquire(&manager->slots);
/* return early if no slot is found */
gpa_slot = find_mem_slot_by_gpa(head, gpa);
if (gpa_slot == NULL) {
return MshvRemapNoMapping;
}
/* return early if no overlapping slot is found */
overlap_slot = find_overlap_mem_slot(head, gpa_slot);
if (overlap_slot == NULL) {
return MshvRemapNoOverlap;
}
}
/*
* We'll modify the mapping list, so we need to upgrade to mutex and
* recheck.
*/
assert(manager);
QEMU_LOCK_GUARD(&manager->mutex);
/* return early if no slot is found */
gpa_slot = find_mem_slot_by_gpa(manager->slots, gpa);
if (gpa_slot == NULL) {
return MshvRemapNoMapping;
}
/* return early if no overlapping slot is found */
overlap_slot = find_overlap_mem_slot(manager->slots, gpa_slot);
if (overlap_slot == NULL) {
return MshvRemapNoOverlap;
}
/* unmap overlapping slot */
ret = map_or_unmap(vm_fd, overlap_slot, false);
if (ret < 0) {
error_report("failed to unmap overlap region");
abort();
}
set_mapped(overlap_slot, false);
warn_report("mapped out userspace_addr=0x%016lx gpa=0x%010lx size=0x%lx",
overlap_slot->userspace_addr,
overlap_slot->guest_phys_addr,
overlap_slot->memory_size);
/* map region for gpa */
ret = map_or_unmap(vm_fd, gpa_slot, true);
if (ret < 0) {
error_report("failed to map new region");
abort();
}
set_mapped(gpa_slot, true);
warn_report("mapped in userspace_addr=0x%016lx gpa=0x%010lx size=0x%lx",
gpa_slot->userspace_addr, gpa_slot->guest_phys_addr,
gpa_slot->memory_size);
return MshvRemapOk;
}
static int handle_unmapped_mmio_region_read(uint64_t gpa, uint64_t size,
uint8_t *data)
{
warn_report("read from unmapped mmio region gpa=0x%lx size=%lu", gpa, size);
if (size == 0 || size > 8) {
error_report("invalid size %lu for reading from unmapped mmio region",
size);
return -1;
}
memset(data, 0xFF, size);
return 0;
}
int mshv_guest_mem_read(uint64_t gpa, uint8_t *data, uintptr_t size,
bool is_secure_mode, bool instruction_fetch)
{
int ret;
MemTxAttrs memattr = { .secure = is_secure_mode };
if (instruction_fetch) {
trace_mshv_insn_fetch(gpa, size);
} else {
trace_mshv_mem_read(gpa, size);
}
ret = address_space_rw(&address_space_memory, gpa, memattr, (void *)data,
size, false);
if (ret == MEMTX_OK) {
return 0;
}
if (ret == MEMTX_DECODE_ERROR) {
return handle_unmapped_mmio_region_read(gpa, size, data);
}
error_report("failed to read guest memory at 0x%lx", gpa);
return -1;
}
int mshv_guest_mem_write(uint64_t gpa, const uint8_t *data, uintptr_t size,
bool is_secure_mode)
{
int ret;
MemTxAttrs memattr = { .secure = is_secure_mode };
trace_mshv_mem_write(gpa, size);
ret = address_space_rw(&address_space_memory, gpa, memattr, (void *)data,
size, true);
if (ret == MEMTX_OK) {
return 0;
}
if (ret == MEMTX_DECODE_ERROR) {
warn_report("write to unmapped mmio region gpa=0x%lx size=%lu", gpa,
size);
return 0;
}
error_report("Failed to write guest memory");
return -1;
}
static int tracked_unmap(int vm_fd, uint64_t gpa, uint64_t size,
uint64_t userspace_addr)
{
int ret;
MshvMemorySlot *slot;
MshvMemorySlotManager *manager = &mshv_state->msm;
assert(manager);
QEMU_LOCK_GUARD(&manager->mutex);
slot = find_mem_slot_by_region(gpa, size, userspace_addr);
if (!slot) {
trace_mshv_skip_unset_mem(userspace_addr, gpa, size);
/* no work to do */
return 0;
}
if (!is_mapped(slot)) {
/* remove slot, no need to unmap */
return remove_slot(slot);
}
ret = map_or_unmap(vm_fd, slot, false);
if (ret < 0) {
error_report("failed to unmap memory region");
return ret;
}
return remove_slot(slot);
}
static int tracked_map(int vm_fd, uint64_t gpa, uint64_t size, bool readonly,
uint64_t userspace_addr)
{
MshvMemorySlot *slot, *overlap_slot;
int ret;
MshvMemorySlotManager *manager = &mshv_state->msm;
assert(manager);
QEMU_LOCK_GUARD(&manager->mutex);
slot = find_mem_slot_by_region(gpa, size, userspace_addr);
if (slot) {
error_report("memory region already mapped at gpa=0x%lx, "
"userspace_addr=0x%lx, size=0x%lx",
slot->guest_phys_addr, slot->userspace_addr,
slot->memory_size);
return -1;
}
slot = append_slot(gpa, userspace_addr, size, readonly);
overlap_slot = find_overlap_mem_slot(manager->slots, slot);
if (overlap_slot) {
trace_mshv_remap_attempt(slot->userspace_addr,
slot->guest_phys_addr,
slot->memory_size);
warn_report("attempt to map region [0x%lx-0x%lx], while "
"[0x%lx-0x%lx] is already mapped in the guest",
userspace_addr, userspace_addr + size - 1,
overlap_slot->userspace_addr,
overlap_slot->userspace_addr +
overlap_slot->memory_size - 1);
/* do not register mem slot in hv, but record for later swap-in */
set_mapped(slot, false);
return 0;
}
ret = map_or_unmap(vm_fd, slot, true);
if (ret < 0) {
error_report("failed to map memory region");
return -1;
}
set_mapped(slot, true);
return 0;
}
static int set_memory(uint64_t gpa, uint64_t size, bool readonly,
uint64_t userspace_addr, bool add)
{
int vm_fd = mshv_state->vm;
if (add) {
return tracked_map(vm_fd, gpa, size, readonly, userspace_addr);
}
return tracked_unmap(vm_fd, gpa, size, userspace_addr);
}
/*
* Calculate and align the start address and the size of the section.
* Return the size. If the size is 0, the aligned section is empty.
*/
static hwaddr align_section(MemoryRegionSection *section, hwaddr *start)
{
hwaddr size = int128_get64(section->size);
hwaddr delta, aligned;
/*
* works in page size chunks, but the function may be called
* with sub-page size and unaligned start address. Pad the start
* address to next and truncate size to previous page boundary.
*/
aligned = ROUND_UP(section->offset_within_address_space,
qemu_real_host_page_size());
delta = aligned - section->offset_within_address_space;
*start = aligned;
if (delta > size) {
return 0;
}
return (size - delta) & qemu_real_host_page_mask();
}
void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section,
bool add)
{
int ret = 0;
MemoryRegion *area = section->mr;
bool writable = !area->readonly && !area->rom_device;
hwaddr start_addr, mr_offset, size;
void *ram;
size = align_section(section, &start_addr);
trace_mshv_set_phys_mem(add, section->mr->name, start_addr);
size = align_section(section, &start_addr);
trace_mshv_set_phys_mem(add, section->mr->name, start_addr);
/*
* If the memory device is a writable non-ram area, we do not
* want to map it into the guest memory. If it is not a ROM device,
* we want to remove mshv memory mapping, so accesses will trap.
*/
if (!memory_region_is_ram(area)) {
if (writable) {
return;
} else if (!area->romd_mode) {
add = false;
}
}
if (!size) {
return;
}
mr_offset = section->offset_within_region + start_addr -
section->offset_within_address_space;
ram = memory_region_get_ram_ptr(area) + mr_offset;
ret = set_memory(start_addr, size, !writable, (uint64_t)ram, add);
if (ret < 0) {
error_report("failed to set memory region");
abort();
}
}
void mshv_init_memory_slot_manager(MshvState *mshv_state)
{
MshvMemorySlotManager *manager;
assert(mshv_state);
manager = &mshv_state->msm;
manager->n_slots = 0;
manager->slots = NULL;
qemu_mutex_init(&manager->mutex);
}
-9
View File
@@ -1,9 +0,0 @@
mshv_ss = ss.source_set()
mshv_ss.add(if_true: files(
'irq.c',
'mem.c',
'msr.c',
'mshv-all.c'
))
specific_ss.add_all(when: 'CONFIG_MSHV', if_true: mshv_ss)
-730
View File
@@ -1,730 +0,0 @@
/*
* QEMU MSHV support
*
* Copyright Microsoft, Corp. 2025
*
* Authors:
* Ziqiao Zhou <ziqiaozhou@microsoft.com>
* Magnus Kulke <magnuskulke@microsoft.com>
* Jinank Jain <jinankjain@microsoft.com>
* Wei Liu <liuwe@microsoft.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/event_notifier.h"
#include "qemu/module.h"
#include "qemu/main-loop.h"
#include "hw/boards.h"
#include "hw/hyperv/hvhdk.h"
#include "hw/hyperv/hvhdk_mini.h"
#include "hw/hyperv/hvgdk.h"
#include "hw/hyperv/hvgdk_mini.h"
#include "linux/mshv.h"
#include "qemu/accel.h"
#include "qemu/guest-random.h"
#include "accel/accel-ops.h"
#include "accel/accel-cpu-ops.h"
#include "system/cpus.h"
#include "system/runstate.h"
#include "system/accel-blocker.h"
#include "system/address-spaces.h"
#include "system/mshv.h"
#include "system/mshv_int.h"
#include "system/reset.h"
#include "trace.h"
#include <err.h>
#include <stdint.h>
#include <sys/ioctl.h>
#define TYPE_MSHV_ACCEL ACCEL_CLASS_NAME("mshv")
DECLARE_INSTANCE_CHECKER(MshvState, MSHV_STATE, TYPE_MSHV_ACCEL)
bool mshv_allowed;
MshvState *mshv_state;
static int init_mshv(int *mshv_fd)
{
int fd = open("/dev/mshv", O_RDWR | O_CLOEXEC);
if (fd < 0) {
error_report("Failed to open /dev/mshv: %s", strerror(errno));
return -1;
}
*mshv_fd = fd;
return 0;
}
/* freeze 1 to pause, 0 to resume */
static int set_time_freeze(int vm_fd, int freeze)
{
int ret;
struct hv_input_set_partition_property in = {0};
in.property_code = HV_PARTITION_PROPERTY_TIME_FREEZE;
in.property_value = freeze;
struct mshv_root_hvcall args = {0};
args.code = HVCALL_SET_PARTITION_PROPERTY;
args.in_sz = sizeof(in);
args.in_ptr = (uint64_t)&in;
ret = mshv_hvcall(vm_fd, &args);
if (ret < 0) {
error_report("Failed to set time freeze");
return -1;
}
return 0;
}
static int pause_vm(int vm_fd)
{
int ret;
ret = set_time_freeze(vm_fd, 1);
if (ret < 0) {
error_report("Failed to pause partition: %s", strerror(errno));
return -1;
}
return 0;
}
static int resume_vm(int vm_fd)
{
int ret;
ret = set_time_freeze(vm_fd, 0);
if (ret < 0) {
error_report("Failed to resume partition: %s", strerror(errno));
return -1;
}
return 0;
}
static int create_partition(int mshv_fd, int *vm_fd)
{
int ret;
struct mshv_create_partition args = {0};
/* Initialize pt_flags with the desired features */
uint64_t pt_flags = (1ULL << MSHV_PT_BIT_LAPIC) |
(1ULL << MSHV_PT_BIT_X2APIC) |
(1ULL << MSHV_PT_BIT_GPA_SUPER_PAGES);
/* Set default isolation type */
uint64_t pt_isolation = MSHV_PT_ISOLATION_NONE;
args.pt_flags = pt_flags;
args.pt_isolation = pt_isolation;
ret = ioctl(mshv_fd, MSHV_CREATE_PARTITION, &args);
if (ret < 0) {
error_report("Failed to create partition: %s", strerror(errno));
return -1;
}
*vm_fd = ret;
return 0;
}
static int set_synthetic_proc_features(int vm_fd)
{
int ret;
struct hv_input_set_partition_property in = {0};
union hv_partition_synthetic_processor_features features = {0};
/* Access the bitfield and set the desired features */
features.hypervisor_present = 1;
features.hv1 = 1;
features.access_partition_reference_counter = 1;
features.access_synic_regs = 1;
features.access_synthetic_timer_regs = 1;
features.access_partition_reference_tsc = 1;
features.access_frequency_regs = 1;
features.access_intr_ctrl_regs = 1;
features.access_vp_index = 1;
features.access_hypercall_regs = 1;
features.tb_flush_hypercalls = 1;
features.synthetic_cluster_ipi = 1;
features.direct_synthetic_timers = 1;
mshv_arch_amend_proc_features(&features);
in.property_code = HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES;
in.property_value = features.as_uint64[0];
struct mshv_root_hvcall args = {0};
args.code = HVCALL_SET_PARTITION_PROPERTY;
args.in_sz = sizeof(in);
args.in_ptr = (uint64_t)&in;
trace_mshv_hvcall_args("synthetic_proc_features", args.code, args.in_sz);
ret = mshv_hvcall(vm_fd, &args);
if (ret < 0) {
error_report("Failed to set synthethic proc features");
return -errno;
}
return 0;
}
static int initialize_vm(int vm_fd)
{
int ret = ioctl(vm_fd, MSHV_INITIALIZE_PARTITION);
if (ret < 0) {
error_report("Failed to initialize partition: %s", strerror(errno));
return -1;
}
return 0;
}
static int create_vm(int mshv_fd, int *vm_fd)
{
int ret = create_partition(mshv_fd, vm_fd);
if (ret < 0) {
return -1;
}
ret = set_synthetic_proc_features(*vm_fd);
if (ret < 0) {
return -1;
}
ret = initialize_vm(*vm_fd);
if (ret < 0) {
return -1;
}
ret = mshv_reserve_ioapic_msi_routes(*vm_fd);
if (ret < 0) {
return -1;
}
ret = mshv_arch_post_init_vm(*vm_fd);
if (ret < 0) {
return -1;
}
/* Always create a frozen partition */
pause_vm(*vm_fd);
return 0;
}
static void mem_region_add(MemoryListener *listener,
MemoryRegionSection *section)
{
MshvMemoryListener *mml;
mml = container_of(listener, MshvMemoryListener, listener);
memory_region_ref(section->mr);
mshv_set_phys_mem(mml, section, true);
}
static void mem_region_del(MemoryListener *listener,
MemoryRegionSection *section)
{
MshvMemoryListener *mml;
mml = container_of(listener, MshvMemoryListener, listener);
mshv_set_phys_mem(mml, section, false);
memory_region_unref(section->mr);
}
typedef enum {
DATAMATCH_NONE,
DATAMATCH_U32,
DATAMATCH_U64,
} DatamatchTag;
typedef struct {
DatamatchTag tag;
union {
uint32_t u32;
uint64_t u64;
} value;
} Datamatch;
/* flags: determine whether to de/assign */
static int ioeventfd(int vm_fd, int event_fd, uint64_t addr, Datamatch dm,
uint32_t flags)
{
struct mshv_user_ioeventfd args = {0};
args.fd = event_fd;
args.addr = addr;
args.flags = flags;
if (dm.tag == DATAMATCH_NONE) {
args.datamatch = 0;
} else {
flags |= BIT(MSHV_IOEVENTFD_BIT_DATAMATCH);
args.flags = flags;
if (dm.tag == DATAMATCH_U64) {
args.len = sizeof(uint64_t);
args.datamatch = dm.value.u64;
} else {
args.len = sizeof(uint32_t);
args.datamatch = dm.value.u32;
}
}
return ioctl(vm_fd, MSHV_IOEVENTFD, &args);
}
static int unregister_ioevent(int vm_fd, int event_fd, uint64_t mmio_addr)
{
uint32_t flags = 0;
Datamatch dm = {0};
flags |= BIT(MSHV_IOEVENTFD_BIT_DEASSIGN);
dm.tag = DATAMATCH_NONE;
return ioeventfd(vm_fd, event_fd, mmio_addr, dm, flags);
}
static int register_ioevent(int vm_fd, int event_fd, uint64_t mmio_addr,
uint64_t val, bool is_64bit, bool is_datamatch)
{
uint32_t flags = 0;
Datamatch dm = {0};
if (!is_datamatch) {
dm.tag = DATAMATCH_NONE;
} else if (is_64bit) {
dm.tag = DATAMATCH_U64;
dm.value.u64 = val;
} else {
dm.tag = DATAMATCH_U32;
dm.value.u32 = val;
}
return ioeventfd(vm_fd, event_fd, mmio_addr, dm, flags);
}
static void mem_ioeventfd_add(MemoryListener *listener,
MemoryRegionSection *section,
bool match_data, uint64_t data,
EventNotifier *e)
{
int fd = event_notifier_get_fd(e);
int ret;
bool is_64 = int128_get64(section->size) == 8;
uint64_t addr = section->offset_within_address_space;
trace_mshv_mem_ioeventfd_add(addr, int128_get64(section->size), data);
ret = register_ioevent(mshv_state->vm, fd, addr, data, is_64, match_data);
if (ret < 0) {
error_report("Failed to register ioeventfd: %s (%d)", strerror(-ret),
-ret);
abort();
}
}
static void mem_ioeventfd_del(MemoryListener *listener,
MemoryRegionSection *section,
bool match_data, uint64_t data,
EventNotifier *e)
{
int fd = event_notifier_get_fd(e);
int ret;
uint64_t addr = section->offset_within_address_space;
trace_mshv_mem_ioeventfd_del(section->offset_within_address_space,
int128_get64(section->size), data);
ret = unregister_ioevent(mshv_state->vm, fd, addr);
if (ret < 0) {
error_report("Failed to unregister ioeventfd: %s (%d)", strerror(-ret),
-ret);
abort();
}
}
static MemoryListener mshv_memory_listener = {
.name = "mshv",
.priority = MEMORY_LISTENER_PRIORITY_ACCEL,
.region_add = mem_region_add,
.region_del = mem_region_del,
.eventfd_add = mem_ioeventfd_add,
.eventfd_del = mem_ioeventfd_del,
};
static MemoryListener mshv_io_listener = {
.name = "mshv", .priority = MEMORY_LISTENER_PRIORITY_DEV_BACKEND,
/* MSHV does not support PIO eventfd */
};
static void register_mshv_memory_listener(MshvState *s, MshvMemoryListener *mml,
AddressSpace *as, int as_id,
const char *name)
{
int i;
mml->listener = mshv_memory_listener;
mml->listener.name = name;
memory_listener_register(&mml->listener, as);
for (i = 0; i < s->nr_as; ++i) {
if (!s->as[i].as) {
s->as[i].as = as;
s->as[i].ml = mml;
break;
}
}
}
int mshv_hvcall(int fd, const struct mshv_root_hvcall *args)
{
int ret = 0;
ret = ioctl(fd, MSHV_ROOT_HVCALL, args);
if (ret < 0) {
error_report("Failed to perform hvcall: %s", strerror(errno));
return -1;
}
return ret;
}
static int mshv_init_vcpu(CPUState *cpu)
{
int vm_fd = mshv_state->vm;
uint8_t vp_index = cpu->cpu_index;
int ret;
cpu->accel = g_new0(AccelCPUState, 1);
mshv_arch_init_vcpu(cpu);
ret = mshv_create_vcpu(vm_fd, vp_index, &cpu->accel->cpufd);
if (ret < 0) {
return -1;
}
cpu->accel->dirty = true;
return 0;
}
static int mshv_init(AccelState *as, MachineState *ms)
{
MshvState *s;
int mshv_fd, vm_fd, ret;
if (mshv_state) {
warn_report("MSHV accelerator already initialized");
return 0;
}
s = MSHV_STATE(as);
accel_blocker_init();
s->vm = 0;
ret = init_mshv(&mshv_fd);
if (ret < 0) {
return -1;
}
mshv_init_mmio_emu();
mshv_init_msicontrol();
mshv_init_memory_slot_manager(s);
ret = create_vm(mshv_fd, &vm_fd);
if (ret < 0) {
close(mshv_fd);
return -1;
}
ret = resume_vm(vm_fd);
if (ret < 0) {
close(mshv_fd);
close(vm_fd);
return -1;
}
s->vm = vm_fd;
s->fd = mshv_fd;
s->nr_as = 1;
s->as = g_new0(MshvAddressSpace, s->nr_as);
mshv_state = s;
register_mshv_memory_listener(s, &s->memory_listener, &address_space_memory,
0, "mshv-memory");
memory_listener_register(&mshv_io_listener, &address_space_io);
return 0;
}
static int mshv_destroy_vcpu(CPUState *cpu)
{
int cpu_fd = mshv_vcpufd(cpu);
int vm_fd = mshv_state->vm;
mshv_remove_vcpu(vm_fd, cpu_fd);
mshv_vcpufd(cpu) = 0;
mshv_arch_destroy_vcpu(cpu);
g_clear_pointer(&cpu->accel, g_free);
return 0;
}
static int mshv_cpu_exec(CPUState *cpu)
{
hv_message mshv_msg;
enum MshvVmExit exit_reason;
int ret = 0;
bql_unlock();
cpu_exec_start(cpu);
do {
if (cpu->accel->dirty) {
ret = mshv_arch_put_registers(cpu);
if (ret) {
error_report("Failed to put registers after init: %s",
strerror(-ret));
ret = -1;
break;
}
cpu->accel->dirty = false;
}
ret = mshv_run_vcpu(mshv_state->vm, cpu, &mshv_msg, &exit_reason);
if (ret < 0) {
error_report("Failed to run on vcpu %d", cpu->cpu_index);
abort();
}
switch (exit_reason) {
case MshvVmExitIgnore:
break;
default:
ret = EXCP_INTERRUPT;
break;
}
} while (ret == 0);
cpu_exec_end(cpu);
bql_lock();
if (ret < 0) {
cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
vm_stop(RUN_STATE_INTERNAL_ERROR);
}
return ret;
}
/*
* The signal handler is triggered when QEMU's main thread receives a SIG_IPI
* (SIGUSR1). This signal causes the current CPU thread to be kicked, forcing a
* VM exit on the CPU. The VM exit generates an exit reason that breaks the loop
* (see mshv_cpu_exec). If the exit is due to a Ctrl+A+x command, the system
* will shut down. For other cases, the system will continue running.
*/
static void sa_ipi_handler(int sig)
{
/* TODO: call IOCTL to set_immediate_exit, once implemented. */
qemu_cpu_kick_self();
}
static void init_signal(CPUState *cpu)
{
/* init cpu signals */
struct sigaction sigact;
sigset_t set;
memset(&sigact, 0, sizeof(sigact));
sigact.sa_handler = sa_ipi_handler;
sigaction(SIG_IPI, &sigact, NULL);
pthread_sigmask(SIG_BLOCK, NULL, &set);
sigdelset(&set, SIG_IPI);
pthread_sigmask(SIG_SETMASK, &set, NULL);
}
static void *mshv_vcpu_thread(void *arg)
{
CPUState *cpu = arg;
int ret;
rcu_register_thread();
bql_lock();
qemu_thread_get_self(cpu->thread);
cpu->thread_id = qemu_get_thread_id();
current_cpu = cpu;
ret = mshv_init_vcpu(cpu);
if (ret < 0) {
error_report("Failed to init vcpu %d", cpu->cpu_index);
goto cleanup;
}
init_signal(cpu);
/* signal CPU creation */
cpu_thread_signal_created(cpu);
qemu_guest_random_seed_thread_part2(cpu->random_seed);
do {
qemu_process_cpu_events(cpu);
if (cpu_can_run(cpu)) {
mshv_cpu_exec(cpu);
}
} while (!cpu->unplug || cpu_can_run(cpu));
mshv_destroy_vcpu(cpu);
cleanup:
cpu_thread_signal_destroyed(cpu);
bql_unlock();
rcu_unregister_thread();
return NULL;
}
static void mshv_start_vcpu_thread(CPUState *cpu)
{
char thread_name[VCPU_THREAD_NAME_SIZE];
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/MSHV",
cpu->cpu_index);
cpu->thread = g_malloc0(sizeof(QemuThread));
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
qemu_cond_init(cpu->halt_cond);
trace_mshv_start_vcpu_thread(thread_name, cpu->cpu_index);
qemu_thread_create(cpu->thread, thread_name, mshv_vcpu_thread, cpu,
QEMU_THREAD_JOINABLE);
}
static void do_mshv_cpu_synchronize_post_init(CPUState *cpu,
run_on_cpu_data arg)
{
int ret = mshv_arch_put_registers(cpu);
if (ret < 0) {
error_report("Failed to put registers after init: %s", strerror(-ret));
abort();
}
cpu->accel->dirty = false;
}
static void mshv_cpu_synchronize_post_init(CPUState *cpu)
{
run_on_cpu(cpu, do_mshv_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
}
static void mshv_cpu_synchronize_post_reset(CPUState *cpu)
{
int ret = mshv_arch_put_registers(cpu);
if (ret) {
error_report("Failed to put registers after reset: %s",
strerror(-ret));
cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
vm_stop(RUN_STATE_INTERNAL_ERROR);
}
cpu->accel->dirty = false;
}
static void do_mshv_cpu_synchronize_pre_loadvm(CPUState *cpu,
run_on_cpu_data arg)
{
cpu->accel->dirty = true;
}
static void mshv_cpu_synchronize_pre_loadvm(CPUState *cpu)
{
run_on_cpu(cpu, do_mshv_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL);
}
static void do_mshv_cpu_synchronize(CPUState *cpu, run_on_cpu_data arg)
{
if (!cpu->accel->dirty) {
int ret = mshv_load_regs(cpu);
if (ret < 0) {
error_report("Failed to load registers for vcpu %d",
cpu->cpu_index);
cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
vm_stop(RUN_STATE_INTERNAL_ERROR);
}
cpu->accel->dirty = true;
}
}
static void mshv_cpu_synchronize(CPUState *cpu)
{
if (!cpu->accel->dirty) {
run_on_cpu(cpu, do_mshv_cpu_synchronize, RUN_ON_CPU_NULL);
}
}
static bool mshv_cpus_are_resettable(void)
{
return false;
}
static void mshv_accel_class_init(ObjectClass *oc, const void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "MSHV";
ac->init_machine = mshv_init;
ac->allowed = &mshv_allowed;
}
static void mshv_accel_instance_init(Object *obj)
{
MshvState *s = MSHV_STATE(obj);
s->vm = 0;
}
static const TypeInfo mshv_accel_type = {
.name = TYPE_MSHV_ACCEL,
.parent = TYPE_ACCEL,
.instance_init = mshv_accel_instance_init,
.class_init = mshv_accel_class_init,
.instance_size = sizeof(MshvState),
};
static void mshv_accel_ops_class_init(ObjectClass *oc, const void *data)
{
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
ops->create_vcpu_thread = mshv_start_vcpu_thread;
ops->synchronize_post_init = mshv_cpu_synchronize_post_init;
ops->synchronize_post_reset = mshv_cpu_synchronize_post_reset;
ops->synchronize_state = mshv_cpu_synchronize;
ops->synchronize_pre_loadvm = mshv_cpu_synchronize_pre_loadvm;
ops->cpus_are_resettable = mshv_cpus_are_resettable;
ops->handle_interrupt = generic_handle_interrupt;
}
static const TypeInfo mshv_accel_ops_type = {
.name = ACCEL_OPS_NAME("mshv"),
.parent = TYPE_ACCEL_OPS,
.class_init = mshv_accel_ops_class_init,
.abstract = true,
};
static void mshv_type_init(void)
{
type_register_static(&mshv_accel_type);
type_register_static(&mshv_accel_ops_type);
}
type_init(mshv_type_init);
-375
View File
@@ -1,375 +0,0 @@
/*
* QEMU MSHV support
*
* Copyright Microsoft, Corp. 2025
*
* Authors: Magnus Kulke <magnuskulke@microsoft.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "system/mshv.h"
#include "system/mshv_int.h"
#include "hw/hyperv/hvgdk_mini.h"
#include "linux/mshv.h"
#include "qemu/error-report.h"
static uint32_t supported_msrs[64] = {
IA32_MSR_TSC,
IA32_MSR_EFER,
IA32_MSR_KERNEL_GS_BASE,
IA32_MSR_APIC_BASE,
IA32_MSR_PAT,
IA32_MSR_SYSENTER_CS,
IA32_MSR_SYSENTER_ESP,
IA32_MSR_SYSENTER_EIP,
IA32_MSR_STAR,
IA32_MSR_LSTAR,
IA32_MSR_CSTAR,
IA32_MSR_SFMASK,
IA32_MSR_MTRR_DEF_TYPE,
IA32_MSR_MTRR_PHYSBASE0,
IA32_MSR_MTRR_PHYSMASK0,
IA32_MSR_MTRR_PHYSBASE1,
IA32_MSR_MTRR_PHYSMASK1,
IA32_MSR_MTRR_PHYSBASE2,
IA32_MSR_MTRR_PHYSMASK2,
IA32_MSR_MTRR_PHYSBASE3,
IA32_MSR_MTRR_PHYSMASK3,
IA32_MSR_MTRR_PHYSBASE4,
IA32_MSR_MTRR_PHYSMASK4,
IA32_MSR_MTRR_PHYSBASE5,
IA32_MSR_MTRR_PHYSMASK5,
IA32_MSR_MTRR_PHYSBASE6,
IA32_MSR_MTRR_PHYSMASK6,
IA32_MSR_MTRR_PHYSBASE7,
IA32_MSR_MTRR_PHYSMASK7,
IA32_MSR_MTRR_FIX64K_00000,
IA32_MSR_MTRR_FIX16K_80000,
IA32_MSR_MTRR_FIX16K_A0000,
IA32_MSR_MTRR_FIX4K_C0000,
IA32_MSR_MTRR_FIX4K_C8000,
IA32_MSR_MTRR_FIX4K_D0000,
IA32_MSR_MTRR_FIX4K_D8000,
IA32_MSR_MTRR_FIX4K_E0000,
IA32_MSR_MTRR_FIX4K_E8000,
IA32_MSR_MTRR_FIX4K_F0000,
IA32_MSR_MTRR_FIX4K_F8000,
IA32_MSR_TSC_AUX,
IA32_MSR_DEBUG_CTL,
HV_X64_MSR_GUEST_OS_ID,
HV_X64_MSR_SINT0,
HV_X64_MSR_SINT1,
HV_X64_MSR_SINT2,
HV_X64_MSR_SINT3,
HV_X64_MSR_SINT4,
HV_X64_MSR_SINT5,
HV_X64_MSR_SINT6,
HV_X64_MSR_SINT7,
HV_X64_MSR_SINT8,
HV_X64_MSR_SINT9,
HV_X64_MSR_SINT10,
HV_X64_MSR_SINT11,
HV_X64_MSR_SINT12,
HV_X64_MSR_SINT13,
HV_X64_MSR_SINT14,
HV_X64_MSR_SINT15,
HV_X64_MSR_SCONTROL,
HV_X64_MSR_SIEFP,
HV_X64_MSR_SIMP,
HV_X64_MSR_REFERENCE_TSC,
HV_X64_MSR_EOM,
};
static const size_t msr_count = ARRAY_SIZE(supported_msrs);
static int compare_msr_index(const void *a, const void *b)
{
return *(uint32_t *)a - *(uint32_t *)b;
}
__attribute__((constructor))
static void init_sorted_msr_map(void)
{
qsort(supported_msrs, msr_count, sizeof(uint32_t), compare_msr_index);
}
static int mshv_is_supported_msr(uint32_t msr)
{
return bsearch(&msr, supported_msrs, msr_count, sizeof(uint32_t),
compare_msr_index) != NULL;
}
static int mshv_msr_to_hv_reg_name(uint32_t msr, uint32_t *hv_reg)
{
switch (msr) {
case IA32_MSR_TSC:
*hv_reg = HV_X64_REGISTER_TSC;
return 0;
case IA32_MSR_EFER:
*hv_reg = HV_X64_REGISTER_EFER;
return 0;
case IA32_MSR_KERNEL_GS_BASE:
*hv_reg = HV_X64_REGISTER_KERNEL_GS_BASE;
return 0;
case IA32_MSR_APIC_BASE:
*hv_reg = HV_X64_REGISTER_APIC_BASE;
return 0;
case IA32_MSR_PAT:
*hv_reg = HV_X64_REGISTER_PAT;
return 0;
case IA32_MSR_SYSENTER_CS:
*hv_reg = HV_X64_REGISTER_SYSENTER_CS;
return 0;
case IA32_MSR_SYSENTER_ESP:
*hv_reg = HV_X64_REGISTER_SYSENTER_ESP;
return 0;
case IA32_MSR_SYSENTER_EIP:
*hv_reg = HV_X64_REGISTER_SYSENTER_EIP;
return 0;
case IA32_MSR_STAR:
*hv_reg = HV_X64_REGISTER_STAR;
return 0;
case IA32_MSR_LSTAR:
*hv_reg = HV_X64_REGISTER_LSTAR;
return 0;
case IA32_MSR_CSTAR:
*hv_reg = HV_X64_REGISTER_CSTAR;
return 0;
case IA32_MSR_SFMASK:
*hv_reg = HV_X64_REGISTER_SFMASK;
return 0;
case IA32_MSR_MTRR_CAP:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_CAP;
return 0;
case IA32_MSR_MTRR_DEF_TYPE:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_DEF_TYPE;
return 0;
case IA32_MSR_MTRR_PHYSBASE0:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE0;
return 0;
case IA32_MSR_MTRR_PHYSMASK0:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK0;
return 0;
case IA32_MSR_MTRR_PHYSBASE1:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE1;
return 0;
case IA32_MSR_MTRR_PHYSMASK1:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK1;
return 0;
case IA32_MSR_MTRR_PHYSBASE2:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE2;
return 0;
case IA32_MSR_MTRR_PHYSMASK2:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK2;
return 0;
case IA32_MSR_MTRR_PHYSBASE3:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE3;
return 0;
case IA32_MSR_MTRR_PHYSMASK3:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK3;
return 0;
case IA32_MSR_MTRR_PHYSBASE4:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE4;
return 0;
case IA32_MSR_MTRR_PHYSMASK4:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK4;
return 0;
case IA32_MSR_MTRR_PHYSBASE5:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE5;
return 0;
case IA32_MSR_MTRR_PHYSMASK5:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK5;
return 0;
case IA32_MSR_MTRR_PHYSBASE6:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE6;
return 0;
case IA32_MSR_MTRR_PHYSMASK6:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK6;
return 0;
case IA32_MSR_MTRR_PHYSBASE7:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE7;
return 0;
case IA32_MSR_MTRR_PHYSMASK7:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK7;
return 0;
case IA32_MSR_MTRR_FIX64K_00000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX64K00000;
return 0;
case IA32_MSR_MTRR_FIX16K_80000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX16K80000;
return 0;
case IA32_MSR_MTRR_FIX16K_A0000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX16KA0000;
return 0;
case IA32_MSR_MTRR_FIX4K_C0000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KC0000;
return 0;
case IA32_MSR_MTRR_FIX4K_C8000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KC8000;
return 0;
case IA32_MSR_MTRR_FIX4K_D0000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KD0000;
return 0;
case IA32_MSR_MTRR_FIX4K_D8000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KD8000;
return 0;
case IA32_MSR_MTRR_FIX4K_E0000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KE0000;
return 0;
case IA32_MSR_MTRR_FIX4K_E8000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KE8000;
return 0;
case IA32_MSR_MTRR_FIX4K_F0000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KF0000;
return 0;
case IA32_MSR_MTRR_FIX4K_F8000:
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KF8000;
return 0;
case IA32_MSR_TSC_AUX:
*hv_reg = HV_X64_REGISTER_TSC_AUX;
return 0;
case IA32_MSR_BNDCFGS:
*hv_reg = HV_X64_REGISTER_BNDCFGS;
return 0;
case IA32_MSR_DEBUG_CTL:
*hv_reg = HV_X64_REGISTER_DEBUG_CTL;
return 0;
case IA32_MSR_TSC_ADJUST:
*hv_reg = HV_X64_REGISTER_TSC_ADJUST;
return 0;
case IA32_MSR_SPEC_CTRL:
*hv_reg = HV_X64_REGISTER_SPEC_CTRL;
return 0;
case HV_X64_MSR_GUEST_OS_ID:
*hv_reg = HV_REGISTER_GUEST_OS_ID;
return 0;
case HV_X64_MSR_SINT0:
*hv_reg = HV_REGISTER_SINT0;
return 0;
case HV_X64_MSR_SINT1:
*hv_reg = HV_REGISTER_SINT1;
return 0;
case HV_X64_MSR_SINT2:
*hv_reg = HV_REGISTER_SINT2;
return 0;
case HV_X64_MSR_SINT3:
*hv_reg = HV_REGISTER_SINT3;
return 0;
case HV_X64_MSR_SINT4:
*hv_reg = HV_REGISTER_SINT4;
return 0;
case HV_X64_MSR_SINT5:
*hv_reg = HV_REGISTER_SINT5;
return 0;
case HV_X64_MSR_SINT6:
*hv_reg = HV_REGISTER_SINT6;
return 0;
case HV_X64_MSR_SINT7:
*hv_reg = HV_REGISTER_SINT7;
return 0;
case HV_X64_MSR_SINT8:
*hv_reg = HV_REGISTER_SINT8;
return 0;
case HV_X64_MSR_SINT9:
*hv_reg = HV_REGISTER_SINT9;
return 0;
case HV_X64_MSR_SINT10:
*hv_reg = HV_REGISTER_SINT10;
return 0;
case HV_X64_MSR_SINT11:
*hv_reg = HV_REGISTER_SINT11;
return 0;
case HV_X64_MSR_SINT12:
*hv_reg = HV_REGISTER_SINT12;
return 0;
case HV_X64_MSR_SINT13:
*hv_reg = HV_REGISTER_SINT13;
return 0;
case HV_X64_MSR_SINT14:
*hv_reg = HV_REGISTER_SINT14;
return 0;
case HV_X64_MSR_SINT15:
*hv_reg = HV_REGISTER_SINT15;
return 0;
case IA32_MSR_MISC_ENABLE:
*hv_reg = HV_X64_REGISTER_MSR_IA32_MISC_ENABLE;
return 0;
case HV_X64_MSR_SCONTROL:
*hv_reg = HV_REGISTER_SCONTROL;
return 0;
case HV_X64_MSR_SIEFP:
*hv_reg = HV_REGISTER_SIEFP;
return 0;
case HV_X64_MSR_SIMP:
*hv_reg = HV_REGISTER_SIMP;
return 0;
case HV_X64_MSR_REFERENCE_TSC:
*hv_reg = HV_REGISTER_REFERENCE_TSC;
return 0;
case HV_X64_MSR_EOM:
*hv_reg = HV_REGISTER_EOM;
return 0;
default:
error_report("failed to map MSR %u to HV register name", msr);
return -1;
}
}
static int set_msrs(const CPUState *cpu, GList *msrs)
{
size_t n_msrs;
GList *entries;
MshvMsrEntry *entry;
enum hv_register_name name;
struct hv_register_assoc *assoc;
int ret;
size_t i = 0;
n_msrs = g_list_length(msrs);
hv_register_assoc *assocs = g_new0(hv_register_assoc, n_msrs);
entries = msrs;
for (const GList *elem = entries; elem != NULL; elem = elem->next) {
entry = elem->data;
ret = mshv_msr_to_hv_reg_name(entry->index, &name);
if (ret < 0) {
g_free(assocs);
return ret;
}
assoc = &assocs[i];
assoc->name = name;
/* the union has been initialized to 0 */
assoc->value.reg64 = entry->data;
i++;
}
ret = mshv_set_generic_regs(cpu, assocs, n_msrs);
g_free(assocs);
if (ret < 0) {
error_report("failed to set msrs");
return -1;
}
return 0;
}
int mshv_configure_msr(const CPUState *cpu, const MshvMsrEntry *msrs,
size_t n_msrs)
{
GList *valid_msrs = NULL;
uint32_t msr_index;
int ret;
for (size_t i = 0; i < n_msrs; i++) {
msr_index = msrs[i].index;
/* check whether index of msrs is in SUPPORTED_MSRS */
if (mshv_is_supported_msr(msr_index)) {
valid_msrs = g_list_append(valid_msrs, (void *) &msrs[i]);
}
}
ret = set_msrs(cpu, valid_msrs);
g_list_free(valid_msrs);
return ret;
}
-33
View File
@@ -1,33 +0,0 @@
# Authors: Ziqiao Zhou <ziqiaozhou@microsoft.com>
# Magnus Kulke <magnuskulke@microsoft.com>
#
# SPDX-License-Identifier: GPL-2.0-or-later
mshv_start_vcpu_thread(const char* thread, uint32_t cpu) "thread=%s cpu_index=%d"
mshv_set_memory(bool add, uint64_t gpa, uint64_t size, uint64_t user_addr, bool readonly, int ret) "add=%d gpa=0x%" PRIx64 " size=0x%" PRIx64 " user=0x%" PRIx64 " readonly=%d result=%d"
mshv_mem_ioeventfd_add(uint64_t addr, uint32_t size, uint32_t data) "addr=0x%" PRIx64 " size=%d data=0x%x"
mshv_mem_ioeventfd_del(uint64_t addr, uint32_t size, uint32_t data) "addr=0x%" PRIx64 " size=%d data=0x%x"
mshv_hvcall_args(const char* hvcall, uint16_t code, uint16_t in_sz) "built args for '%s' code: %d in_sz: %d"
mshv_handle_interrupt(uint32_t cpu, int mask) "cpu_index=%d mask=0x%x"
mshv_set_msi_routing(uint32_t gsi, uint64_t addr, uint32_t data) "gsi=%d addr=0x%" PRIx64 " data=0x%x"
mshv_remove_msi_routing(uint32_t gsi) "gsi=%d"
mshv_add_msi_routing(uint64_t addr, uint32_t data) "addr=0x%" PRIx64 " data=0x%x"
mshv_commit_msi_routing_table(int vm_fd, int len) "vm_fd=%d table_size=%d"
mshv_register_irqfd(int vm_fd, int event_fd, uint32_t gsi) "vm_fd=%d event_fd=%d gsi=%d"
mshv_irqchip_update_irqfd_notifier_gsi(int event_fd, int resample_fd, int virq, bool add) "event_fd=%d resample_fd=%d virq=%d add=%d"
mshv_insn_fetch(uint64_t addr, size_t size) "gpa=0x%" PRIx64 " size=%zu"
mshv_mem_write(uint64_t addr, size_t size) "\tgpa=0x%" PRIx64 " size=%zu"
mshv_mem_read(uint64_t addr, size_t size) "\tgpa=0x%" PRIx64 " size=%zu"
mshv_map_memory(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64
mshv_unmap_memory(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64
mshv_set_phys_mem(bool add, const char *name, uint64_t gpa) "\tadd=%d name=%s gpa=0x%010" PRIx64
mshv_handle_mmio(uint64_t gva, uint64_t gpa, uint64_t size, uint8_t access_type) "\tgva=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%" PRIx64 " access_type=%d"
mshv_found_slot(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64
mshv_skip_unset_mem(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64
mshv_remap_attempt(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64
mshv_find_slot_by_gpa(uint64_t gpa) "\tgpa=0x%010" PRIx64
-14
View File
@@ -1,14 +0,0 @@
/*
* QEMU MSHV support
*
* Copyright Microsoft, Corp. 2025
*
* Authors:
* Ziqiao Zhou <ziqiaozhou@microsoft.com>
* Magnus Kulke <magnuskulke@microsoft.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
*/
#include "trace/trace-accel_mshv.h"
+4 -7
View File
@@ -18,14 +18,12 @@
#include "qemu/option.h"
#include "qemu/config-file.h"
#include "qemu/accel.h"
#include "accel/accel-ops.h"
#include "accel/accel-cpu-ops.h"
#include "system/accel-ops.h"
#include "system/qtest.h"
#include "system/cpus.h"
#include "qemu/guest-random.h"
#include "qemu/main-loop.h"
#include "hw/core/cpu.h"
#include "accel/dummy-cpus.h"
static int64_t qtest_clock_counter;
@@ -39,12 +37,12 @@ static void qtest_set_virtual_clock(int64_t count)
qatomic_set_i64(&qtest_clock_counter, count);
}
static int qtest_init_accel(AccelState *as, MachineState *ms)
static int qtest_init_accel(MachineState *ms)
{
return 0;
}
static void qtest_accel_class_init(ObjectClass *oc, const void *data)
static void qtest_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "QTest";
@@ -61,14 +59,13 @@ static const TypeInfo qtest_accel_type = {
};
module_obj(TYPE_QTEST_ACCEL);
static void qtest_accel_ops_class_init(ObjectClass *oc, const void *data)
static void qtest_accel_ops_class_init(ObjectClass *oc, void *data)
{
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
ops->create_vcpu_thread = dummy_start_vcpu_thread;
ops->get_virtual_clock = qtest_get_virtual_clock;
ops->set_virtual_clock = qtest_set_virtual_clock;
ops->handle_interrupt = generic_handle_interrupt;
};
static const TypeInfo qtest_accel_ops_type = {
-12
View File
@@ -1,12 +0,0 @@
/*
* HVF stubs for QEMU
*
* Copyright (c) Linaro
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "system/hvf.h"
bool hvf_allowed;
+9
View File
@@ -29,6 +29,10 @@ void kvm_flush_coalesced_mmio_buffer(void)
{
}
void kvm_cpu_synchronize_state(CPUState *cpu)
{
}
bool kvm_has_sync_mmu(void)
{
return false;
@@ -101,6 +105,11 @@ unsigned int kvm_get_free_memslots(void)
return 0;
}
void kvm_init_cpu_signals(CPUState *cpu)
{
abort();
}
bool kvm_arm_supports_user_irq(void)
{
return false;
-4
View File
@@ -2,9 +2,5 @@ system_stubs_ss = ss.source_set()
system_stubs_ss.add(when: 'CONFIG_XEN', if_false: files('xen-stub.c'))
system_stubs_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c'))
system_stubs_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c'))
system_stubs_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c'))
system_stubs_ss.add(when: 'CONFIG_NVMM', if_false: files('nvmm-stub.c'))
system_stubs_ss.add(when: 'CONFIG_WHPX', if_false: files('whpx-stub.c'))
system_stubs_ss.add(when: 'CONFIG_MSHV', if_false: files('mshv-stub.c'))
specific_ss.add_all(when: ['CONFIG_SYSTEM_ONLY'], if_true: system_stubs_ss)
-44
View File
@@ -1,44 +0,0 @@
/*
* QEMU MSHV stub
*
* Copyright Red Hat, Inc. 2025
*
* Author: Paolo Bonzini <pbonzini@redhat.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "hw/pci/msi.h"
#include "system/mshv.h"
bool mshv_allowed;
int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev)
{
return -ENOSYS;
}
void mshv_irqchip_release_virq(int virq)
{
}
int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev)
{
return -ENOSYS;
}
void mshv_irqchip_commit_routes(void)
{
}
int mshv_irqchip_add_irqfd_notifier_gsi(const EventNotifier *n,
const EventNotifier *rn, int virq)
{
return -ENOSYS;
}
int mshv_irqchip_remove_irqfd_notifier_gsi(const EventNotifier *n, int virq)
{
return -ENOSYS;
}
-12
View File
@@ -1,12 +0,0 @@
/*
* NVMM stubs for QEMU
*
* Copyright (c) Linaro
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "system/nvmm.h"
bool nvmm_allowed;
+7 -1
View File
@@ -11,9 +11,15 @@
*/
#include "qemu/osdep.h"
#include "exec/cpu-common.h"
#include "exec/tb-flush.h"
#include "exec/exec-all.h"
G_NORETURN void cpu_loop_exit(CPUState *cpu)
{
g_assert_not_reached();
}
G_NORETURN void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc)
{
g_assert_not_reached();
}
-12
View File
@@ -1,12 +0,0 @@
/*
* WHPX stubs for QEMU
*
* Copyright (c) Linaro
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "system/whpx.h"
bool whpx_allowed;
-9
View File
@@ -122,14 +122,5 @@ GEN_ATOMIC_HELPERS(umax_fetch)
GEN_ATOMIC_HELPERS(xchg)
#if HAVE_CMPXCHG128
ATOMIC_HELPER(xchgo_be, Int128)
ATOMIC_HELPER(xchgo_le, Int128)
ATOMIC_HELPER(fetch_ando_be, Int128)
ATOMIC_HELPER(fetch_ando_le, Int128)
ATOMIC_HELPER(fetch_oro_be, Int128)
ATOMIC_HELPER(fetch_oro_le, Int128)
#endif
#undef ATOMIC_HELPER
#undef GEN_ATOMIC_HELPERS
+12 -84
View File
@@ -77,7 +77,7 @@
# define END _le
#endif
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr,
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, abi_ptr addr,
ABI_TYPE cmpv, ABI_TYPE newv,
MemOpIdx oi, uintptr_t retaddr)
{
@@ -100,18 +100,15 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr,
return ret;
}
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
#if DATA_SIZE < 16
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, abi_ptr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
DATA_SIZE, retaddr);
DATA_TYPE ret;
#if DATA_SIZE == 16
ret = atomic16_xchg(haddr, val);
#else
ret = qatomic_xchg__nocheck(haddr, val);
#endif
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret),
@@ -122,41 +119,8 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
return ret;
}
#if DATA_SIZE == 16
ABI_TYPE ATOMIC_NAME(fetch_and)(CPUArchState *env, vaddr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
DATA_SIZE, retaddr);
DATA_TYPE ret = atomic16_fetch_and(haddr, val);
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret),
VALUE_HIGH(ret),
VALUE_LOW(val),
VALUE_HIGH(val),
oi);
return ret;
}
ABI_TYPE ATOMIC_NAME(fetch_or)(CPUArchState *env, vaddr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
DATA_SIZE, retaddr);
DATA_TYPE ret = atomic16_fetch_or(haddr, val);
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret),
VALUE_HIGH(ret),
VALUE_LOW(val),
VALUE_HIGH(val),
oi);
return ret;
}
#else
#define GEN_ATOMIC_HELPER(X) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, abi_ptr addr, \
ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
{ \
DATA_TYPE *haddr, ret; \
@@ -192,7 +156,7 @@ GEN_ATOMIC_HELPER(xor_fetch)
* of CF_PARALLEL's value, we'll trace just a read and a write.
*/
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, abi_ptr addr, \
ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \
{ \
XDATA_TYPE *haddr, cmp, old, new, val = xval; \
@@ -224,7 +188,7 @@ GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
#undef GEN_ATOMIC_HELPER_FN
#endif /* DATA SIZE == 16 */
#endif /* DATA SIZE < 16 */
#undef END
@@ -238,7 +202,7 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
# define END _be
#endif
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr,
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, abi_ptr addr,
ABI_TYPE cmpv, ABI_TYPE newv,
MemOpIdx oi, uintptr_t retaddr)
{
@@ -261,18 +225,15 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr,
return BSWAP(ret);
}
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
#if DATA_SIZE < 16
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, abi_ptr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
DATA_SIZE, retaddr);
ABI_TYPE ret;
#if DATA_SIZE == 16
ret = atomic16_xchg(haddr, BSWAP(val));
#else
ret = qatomic_xchg__nocheck(haddr, BSWAP(val));
#endif
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret),
@@ -283,41 +244,8 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
return BSWAP(ret);
}
#if DATA_SIZE == 16
ABI_TYPE ATOMIC_NAME(fetch_and)(CPUArchState *env, vaddr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
DATA_SIZE, retaddr);
DATA_TYPE ret = atomic16_fetch_and(haddr, BSWAP(val));
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret),
VALUE_HIGH(ret),
VALUE_LOW(val),
VALUE_HIGH(val),
oi);
return BSWAP(ret);
}
ABI_TYPE ATOMIC_NAME(fetch_or)(CPUArchState *env, vaddr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
DATA_SIZE, retaddr);
DATA_TYPE ret = atomic16_fetch_or(haddr, BSWAP(val));
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret),
VALUE_HIGH(ret),
VALUE_LOW(val),
VALUE_HIGH(val),
oi);
return BSWAP(ret);
}
#else
#define GEN_ATOMIC_HELPER(X) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, abi_ptr addr, \
ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
{ \
DATA_TYPE *haddr, ret; \
@@ -350,7 +278,7 @@ GEN_ATOMIC_HELPER(xor_fetch)
* of CF_PARALLEL's value, we'll trace just a read and a write.
*/
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, abi_ptr addr, \
ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \
{ \
XDATA_TYPE *haddr, ldo, ldn, old, new, val = xval; \
@@ -389,7 +317,7 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
#undef ADD
#undef GEN_ATOMIC_HELPER_FN
#endif /* DATA_SIZE == 16 */
#endif /* DATA_SIZE < 16 */
#undef END
#endif /* DATA_SIZE > 1 */
-41
View File
@@ -1,41 +0,0 @@
/*
* Internal memory barrier helpers for QEMU (target agnostic)
*
* Copyright (c) 2003 Fabrice Bellard
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef ACCEL_TCG_BACKEND_LDST_H
#define ACCEL_TCG_BACKEND_LDST_H
#include "tcg-target-mo.h"
/**
* tcg_req_mo:
* @guest_mo: Guest default memory order
* @type: TCGBar
*
* Filter @type to the barrier that is required for the guest
* memory ordering vs the host memory ordering. A non-zero
* result indicates that some barrier is required.
*/
#define tcg_req_mo(guest_mo, type) \
((type) & guest_mo & ~TCG_TARGET_DEFAULT_MO)
/**
* cpu_req_mo:
* @cpu: CPUState
* @type: TCGBar
*
* If tcg_req_mo indicates a barrier for @type is required
* for the guest memory model, issue a host memory barrier.
*/
#define cpu_req_mo(cpu, type) \
do { \
if (tcg_req_mo(cpu->cc->tcg_ops->guest_default_memory_order, type)) { \
smp_mb(); \
} \
} while (0)
#endif
+122 -111
View File
@@ -23,29 +23,27 @@
#include "qapi/type-helpers.h"
#include "hw/core/cpu.h"
#include "accel/tcg/cpu-ops.h"
#include "accel/tcg/helper-retaddr.h"
#include "trace.h"
#include "disas/disas.h"
#include "exec/cpu-common.h"
#include "exec/cpu-interrupt.h"
#include "exec/page-protection.h"
#include "exec/mmap-lock.h"
#include "exec/translation-block.h"
#include "tcg/tcg.h"
#include "qemu/atomic.h"
#include "qemu/rcu.h"
#include "exec/log.h"
#include "qemu/main-loop.h"
#include "exec/icount.h"
#include "exec/cpu-all.h"
#include "system/cpu-timers.h"
#include "exec/replay-core.h"
#include "system/tcg.h"
#include "exec/helper-proto-common.h"
#include "tcg-accel-ops.h"
#include "tb-jmp-cache.h"
#include "tb-hash.h"
#include "tb-context.h"
#include "tb-internal.h"
#include "internal-common.h"
#include "internal-target.h"
/* -icount align implementation. */
@@ -150,9 +148,12 @@ static void init_delay_params(SyncClocks *sc, const CPUState *cpu)
#endif /* CONFIG USER ONLY */
struct tb_desc {
TCGTBCPUState s;
vaddr pc;
uint64_t cs_base;
CPUArchState *env;
tb_page_addr_t page_addr0;
uint32_t flags;
uint32_t cflags;
};
static bool tb_lookup_cmp(const void *p, const void *d)
@@ -160,11 +161,11 @@ static bool tb_lookup_cmp(const void *p, const void *d)
const TranslationBlock *tb = p;
const struct tb_desc *desc = d;
if ((tb_cflags(tb) & CF_PCREL || tb->pc == desc->s.pc) &&
if ((tb_cflags(tb) & CF_PCREL || tb->pc == desc->pc) &&
tb_page_addr0(tb) == desc->page_addr0 &&
tb->cs_base == desc->s.cs_base &&
tb->flags == desc->s.flags &&
tb_cflags(tb) == desc->s.cflags) {
tb->cs_base == desc->cs_base &&
tb->flags == desc->flags &&
tb_cflags(tb) == desc->cflags) {
/* check next page if needed */
tb_page_addr_t tb_phys_page1 = tb_page_addr1(tb);
if (tb_phys_page1 == -1) {
@@ -182,7 +183,7 @@ static bool tb_lookup_cmp(const void *p, const void *d)
* is different for the new TB. Therefore any exception raised
* here by the faulting lookup is not premature.
*/
virt_page1 = TARGET_PAGE_ALIGN(desc->s.pc);
virt_page1 = TARGET_PAGE_ALIGN(desc->pc);
phys_page1 = get_page_addr_code(desc->env, virt_page1);
if (tb_phys_page1 == phys_page1) {
return true;
@@ -192,21 +193,26 @@ static bool tb_lookup_cmp(const void *p, const void *d)
return false;
}
static TranslationBlock *tb_htable_lookup(CPUState *cpu, TCGTBCPUState s)
static TranslationBlock *tb_htable_lookup(CPUState *cpu, vaddr pc,
uint64_t cs_base, uint32_t flags,
uint32_t cflags)
{
tb_page_addr_t phys_pc;
struct tb_desc desc;
uint32_t h;
desc.s = s;
desc.env = cpu_env(cpu);
phys_pc = get_page_addr_code(desc.env, s.pc);
desc.cs_base = cs_base;
desc.flags = flags;
desc.cflags = cflags;
desc.pc = pc;
phys_pc = get_page_addr_code(desc.env, pc);
if (phys_pc == -1) {
return NULL;
}
desc.page_addr0 = phys_pc;
h = tb_hash_func(phys_pc, (s.cflags & CF_PCREL ? 0 : s.pc),
s.flags, s.cs_base, s.cflags);
h = tb_hash_func(phys_pc, (cflags & CF_PCREL ? 0 : pc),
flags, cs_base, cflags);
return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp);
}
@@ -224,33 +230,35 @@ static TranslationBlock *tb_htable_lookup(CPUState *cpu, TCGTBCPUState s)
*
* Returns: an existing translation block or NULL.
*/
static inline TranslationBlock *tb_lookup(CPUState *cpu, TCGTBCPUState s)
static inline TranslationBlock *tb_lookup(CPUState *cpu, vaddr pc,
uint64_t cs_base, uint32_t flags,
uint32_t cflags)
{
TranslationBlock *tb;
CPUJumpCache *jc;
uint32_t hash;
/* we should never be trying to look up an INVALID tb */
tcg_debug_assert(!(s.cflags & CF_INVALID));
tcg_debug_assert(!(cflags & CF_INVALID));
hash = tb_jmp_cache_hash_func(s.pc);
hash = tb_jmp_cache_hash_func(pc);
jc = cpu->tb_jmp_cache;
tb = qatomic_read(&jc->array[hash].tb);
if (likely(tb &&
jc->array[hash].pc == s.pc &&
tb->cs_base == s.cs_base &&
tb->flags == s.flags &&
tb_cflags(tb) == s.cflags)) {
jc->array[hash].pc == pc &&
tb->cs_base == cs_base &&
tb->flags == flags &&
tb_cflags(tb) == cflags)) {
goto hit;
}
tb = tb_htable_lookup(cpu, s);
tb = tb_htable_lookup(cpu, pc, cs_base, flags, cflags);
if (tb == NULL) {
return NULL;
}
jc->array[hash].pc = s.pc;
jc->array[hash].pc = pc;
qatomic_set(&jc->array[hash].tb, tb);
hit:
@@ -258,7 +266,7 @@ hit:
* As long as tb is not NULL, the contents are consistent. Therefore,
* the virtual PC has to match for non-CF_PCREL translations.
*/
assert((tb_cflags(tb) & CF_PCREL) || tb->pc == s.pc);
assert((tb_cflags(tb) & CF_PCREL) || tb->pc == pc);
return tb;
}
@@ -275,11 +283,14 @@ static void log_cpu_exec(vaddr pc, CPUState *cpu,
if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) {
FILE *logfile = qemu_log_trylock();
if (logfile) {
int flags = CPU_DUMP_CCOP;
int flags = 0;
if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) {
flags |= CPU_DUMP_FPU;
}
#if defined(TARGET_I386)
flags |= CPU_DUMP_CCOP;
#endif
if (qemu_loglevel_mask(CPU_LOG_TB_VPU)) {
flags |= CPU_DUMP_VPU;
}
@@ -375,6 +386,9 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env)
{
CPUState *cpu = env_cpu(env);
TranslationBlock *tb;
vaddr pc;
uint64_t cs_base;
uint32_t flags, cflags;
/*
* By definition we've just finished a TB, so I/O is OK.
@@ -384,21 +398,20 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env)
* The next TB, if we chain to it, will clear the flag again.
*/
cpu->neg.can_do_io = true;
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
TCGTBCPUState s = cpu->cc->tcg_ops->get_tb_cpu_state(cpu);
s.cflags = curr_cflags(cpu);
if (check_for_breakpoints(cpu, s.pc, &s.cflags)) {
cflags = curr_cflags(cpu);
if (check_for_breakpoints(cpu, pc, &cflags)) {
cpu_loop_exit(cpu);
}
tb = tb_lookup(cpu, s);
tb = tb_lookup(cpu, pc, cs_base, flags, cflags);
if (tb == NULL) {
return tcg_code_gen_epilogue;
}
if (qemu_loglevel_mask(CPU_LOG_TB_CPU | CPU_LOG_EXEC)) {
log_cpu_exec(s.pc, cpu, tb);
log_cpu_exec(pc, cpu, tb);
}
return tb->tc.ptr;
@@ -548,7 +561,11 @@ static void cpu_exec_longjmp_cleanup(CPUState *cpu)
void cpu_exec_step_atomic(CPUState *cpu)
{
CPUArchState *env = cpu_env(cpu);
TranslationBlock *tb;
vaddr pc;
uint64_t cs_base;
uint32_t flags, cflags;
int tb_exit;
if (sigsetjmp(cpu->jmp_env, 0) == 0) {
@@ -557,13 +574,13 @@ void cpu_exec_step_atomic(CPUState *cpu)
g_assert(!cpu->running);
cpu->running = true;
TCGTBCPUState s = cpu->cc->tcg_ops->get_tb_cpu_state(cpu);
s.cflags = curr_cflags(cpu);
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
cflags = curr_cflags(cpu);
/* Execute in a serial context. */
s.cflags &= ~CF_PARALLEL;
cflags &= ~CF_PARALLEL;
/* After 1 insn, return and release the exclusive lock. */
s.cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR | 1;
cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR | 1;
/*
* No need to check_for_breakpoints here.
* We only arrive in cpu_exec_step_atomic after beginning execution
@@ -571,16 +588,16 @@ void cpu_exec_step_atomic(CPUState *cpu)
* Any breakpoint for this insn will have been recognized earlier.
*/
tb = tb_lookup(cpu, s);
tb = tb_lookup(cpu, pc, cs_base, flags, cflags);
if (tb == NULL) {
mmap_lock();
tb = tb_gen_code(cpu, s);
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
mmap_unlock();
}
cpu_exec_enter(cpu);
/* execute the generated code */
trace_exec_tb(tb, s.pc);
trace_exec_tb(tb, pc);
cpu_tb_exec(cpu, tb, &tb_exit);
cpu_exec_exit(cpu);
} else {
@@ -648,6 +665,7 @@ static inline void tb_add_jump(TranslationBlock *tb, int n,
out_unlock_next:
qemu_spin_unlock(&tb_next->jmp_lock);
return;
}
static inline bool cpu_handle_halt(CPUState *cpu)
@@ -713,10 +731,10 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
* If user mode only, we simulate a fake exception which will be
* handled outside the cpu execution loop.
*/
#if defined(TARGET_I386)
const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
if (tcg_ops->fake_user_interrupt) {
tcg_ops->fake_user_interrupt(cpu);
}
tcg_ops->fake_user_interrupt(cpu);
#endif /* TARGET_I386 */
*ret = cpu->exception_index;
cpu->exception_index = -1;
return true;
@@ -749,22 +767,6 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
return false;
}
void tcg_kick_vcpu_thread(CPUState *cpu)
{
#ifndef CONFIG_USER_ONLY
/*
* Ensure cpu_exec will see the reason why the exit request was set.
* FIXME: this is not always needed. Other accelerators instead
* read interrupt_request and set exit_request on demand from the
* CPU thread; see kvm_arch_pre_run() for example.
*/
qatomic_store_release(&cpu->exit_request, true);
#endif
/* Ensure cpu_exec will see the exit request after TCG has exited. */
qatomic_store_release(&cpu->neg.icount_decr.u16.high, -1);
}
static inline bool icount_exit_request(CPUState *cpu)
{
if (!icount_enabled()) {
@@ -791,53 +793,61 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
/* Clear the interrupt flag now since we're processing
* cpu->interrupt_request and cpu->exit_request.
* Ensure zeroing happens before reading cpu->exit_request or
* cpu->interrupt_request (see also store-release in
* tcg_kick_vcpu_thread())
* cpu->interrupt_request (see also smp_wmb in cpu_exit())
*/
qatomic_set_mb(&cpu->neg.icount_decr.u16.high, 0);
#ifdef CONFIG_USER_ONLY
assert(!cpu_test_interrupt(cpu, ~0));
#else
if (unlikely(cpu_test_interrupt(cpu, ~0))) {
if (unlikely(qatomic_read(&cpu->interrupt_request))) {
int interrupt_request;
bql_lock();
if (cpu_test_interrupt(cpu, CPU_INTERRUPT_DEBUG)) {
cpu_reset_interrupt(cpu, CPU_INTERRUPT_DEBUG);
interrupt_request = cpu->interrupt_request;
if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) {
/* Mask out external interrupts for this step. */
interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK;
}
if (interrupt_request & CPU_INTERRUPT_DEBUG) {
cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
cpu->exception_index = EXCP_DEBUG;
bql_unlock();
return true;
}
#if !defined(CONFIG_USER_ONLY)
if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) {
/* Do nothing */
} else if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HALT)) {
} else if (interrupt_request & CPU_INTERRUPT_HALT) {
replay_interrupt();
cpu_reset_interrupt(cpu, CPU_INTERRUPT_HALT);
cpu->interrupt_request &= ~CPU_INTERRUPT_HALT;
cpu->halted = 1;
cpu->exception_index = EXCP_HLT;
bql_unlock();
return true;
} else {
}
#if defined(TARGET_I386)
else if (interrupt_request & CPU_INTERRUPT_INIT) {
X86CPU *x86_cpu = X86_CPU(cpu);
CPUArchState *env = &x86_cpu->env;
replay_interrupt();
cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0);
do_cpu_init(x86_cpu);
cpu->exception_index = EXCP_HALTED;
bql_unlock();
return true;
}
#else
else if (interrupt_request & CPU_INTERRUPT_RESET) {
replay_interrupt();
cpu_reset(cpu);
bql_unlock();
return true;
}
#endif /* !TARGET_I386 */
/* The target hook has 3 exit conditions:
False when the interrupt isn't processed,
True when it is, and we should restart on a new TB,
and via longjmp via cpu_loop_exit. */
else {
const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
int interrupt_request = cpu->interrupt_request;
if (cpu_test_interrupt(cpu, CPU_INTERRUPT_RESET)) {
replay_interrupt();
tcg_ops->cpu_exec_reset(cpu);
bql_unlock();
return true;
}
if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) {
/* Mask out external interrupts for this step. */
interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK;
}
/*
* The target hook has 3 exit conditions:
* False when the interrupt isn't processed,
* True when it is, and we should restart on a new TB,
* and via longjmp via cpu_loop_exit.
*/
if (tcg_ops->cpu_exec_interrupt(cpu, interrupt_request)) {
if (!tcg_ops->need_replay_interrupt ||
tcg_ops->need_replay_interrupt(interrupt_request)) {
@@ -856,9 +866,13 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
cpu->exception_index = -1;
*last_tb = NULL;
}
/* The target hook may have updated the 'cpu->interrupt_request';
* reload the 'interrupt_request' value */
interrupt_request = cpu->interrupt_request;
}
if (cpu_test_interrupt(cpu, CPU_INTERRUPT_EXITTB)) {
cpu_reset_interrupt(cpu, CPU_INTERRUPT_EXITTB);
#endif /* !CONFIG_USER_ONLY */
if (interrupt_request & CPU_INTERRUPT_EXITTB) {
cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB;
/* ensure that no TB jump will be modified as
the program flow was changed */
*last_tb = NULL;
@@ -867,13 +881,10 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
/* If we exit via cpu_loop_exit/longjmp it is reset in cpu_exec */
bql_unlock();
}
#endif /* !CONFIG_USER_ONLY */
/*
* Finally, check if we need to exit to the main loop.
* The corresponding store-release is in cpu_exit.
*/
if (unlikely(qatomic_load_acquire(&cpu->exit_request)) || icount_exit_request(cpu)) {
/* Finally, check if we need to exit to the main loop. */
if (unlikely(qatomic_read(&cpu->exit_request)) || icount_exit_request(cpu)) {
qatomic_set(&cpu->exit_request, 0);
if (cpu->exception_index == -1) {
cpu->exception_index = EXCP_INTERRUPT;
}
@@ -943,8 +954,11 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc)
while (!cpu_handle_interrupt(cpu, &last_tb)) {
TranslationBlock *tb;
TCGTBCPUState s = cpu->cc->tcg_ops->get_tb_cpu_state(cpu);
s.cflags = cpu->cflags_next_tb;
vaddr pc;
uint64_t cs_base;
uint32_t flags, cflags;
cpu_get_tb_cpu_state(cpu_env(cpu), &pc, &cs_base, &flags);
/*
* When requested, use an exact setting for cflags for the next
@@ -953,32 +967,33 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc)
* have CF_INVALID set, -1 is a convenient invalid value that
* does not require tcg headers for cpu_common_reset.
*/
if (s.cflags == -1) {
s.cflags = curr_cflags(cpu);
cflags = cpu->cflags_next_tb;
if (cflags == -1) {
cflags = curr_cflags(cpu);
} else {
cpu->cflags_next_tb = -1;
}
if (check_for_breakpoints(cpu, s.pc, &s.cflags)) {
if (check_for_breakpoints(cpu, pc, &cflags)) {
break;
}
tb = tb_lookup(cpu, s);
tb = tb_lookup(cpu, pc, cs_base, flags, cflags);
if (tb == NULL) {
CPUJumpCache *jc;
uint32_t h;
mmap_lock();
tb = tb_gen_code(cpu, s);
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
mmap_unlock();
/*
* We add the TB in the virtual pc hash table
* for the fast lookup
*/
h = tb_jmp_cache_hash_func(s.pc);
h = tb_jmp_cache_hash_func(pc);
jc = cpu->tb_jmp_cache;
jc->array[h].pc = s.pc;
jc->array[h].pc = pc;
qatomic_set(&jc->array[h].tb, tb);
}
@@ -998,7 +1013,7 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc)
tb_add_jump(last_tb, tb_exit, tb);
}
cpu_loop_exec_tb(cpu, tb, s.pc, &last_tb, &tb_exit);
cpu_loop_exec_tb(cpu, tb, pc, &last_tb, &tb_exit);
/* Try to align the host and virtual clocks
if the guest is in advance */
@@ -1057,12 +1072,8 @@ bool tcg_exec_realizefn(CPUState *cpu, Error **errp)
#ifndef CONFIG_USER_ONLY
assert(tcg_ops->cpu_exec_halt);
assert(tcg_ops->cpu_exec_interrupt);
assert(tcg_ops->cpu_exec_reset);
assert(tcg_ops->pointer_wrap);
#endif /* !CONFIG_USER_ONLY */
assert(tcg_ops->translate_code);
assert(tcg_ops->get_tb_cpu_state);
assert(tcg_ops->mmu_index);
tcg_ops->initialize();
tcg_target_initialized = true;
}
+134 -125
View File
@@ -19,18 +19,15 @@
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
#include "qemu/target-info.h"
#include "accel/tcg/cpu-ops.h"
#include "accel/tcg/iommu.h"
#include "accel/tcg/probe.h"
#include "exec/exec-all.h"
#include "exec/page-protection.h"
#include "system/memory.h"
#include "system/physmem.h"
#include "accel/tcg/cpu-ldst-common.h"
#include "accel/tcg/cpu-mmu-index.h"
#include "exec/memory.h"
#include "exec/cpu_ldst.h"
#include "exec/cputlb.h"
#include "exec/tb-flush.h"
#include "system/ram_addr.h"
#include "exec/memory-internal.h"
#include "exec/ram_addr.h"
#include "exec/mmu-access-type.h"
#include "exec/tlb-common.h"
#include "exec/vaddr.h"
@@ -38,21 +35,18 @@
#include "qemu/error-report.h"
#include "exec/log.h"
#include "exec/helper-proto-common.h"
#include "exec/tlb-flags.h"
#include "qemu/atomic.h"
#include "qemu/atomic128.h"
#include "tb-internal.h"
#include "trace.h"
#include "tb-hash.h"
#include "tb-internal.h"
#include "tlb-bounds.h"
#include "internal-common.h"
#include "internal-target.h"
#ifdef CONFIG_PLUGIN
#include "qemu/plugin-memory.h"
#endif
#include "tcg/tcg-ldst.h"
#include "backend-ldst.h"
/* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */
/* #define DEBUG_TLB */
@@ -90,6 +84,9 @@
*/
QEMU_BUILD_BUG_ON(sizeof(vaddr) > sizeof(run_on_cpu_data));
/* We currently can't handle more than 16 bits in the MMUIDX bitmask.
*/
QEMU_BUILD_BUG_ON(NB_MMU_MODES > 16);
#define ALL_MMUIDX_BITS ((1 << NB_MMU_MODES) - 1)
static inline size_t tlb_n_entries(CPUTLBDescFast *fast)
@@ -127,7 +124,7 @@ static inline uint64_t tlb_addr_write(const CPUTLBEntry *entry)
static inline uintptr_t tlb_index(CPUState *cpu, uintptr_t mmu_idx,
vaddr addr)
{
uintptr_t size_mask = cpu_tlb_fast(cpu, mmu_idx)->mask >> CPU_TLB_ENTRY_BITS;
uintptr_t size_mask = cpu->neg.tlb.f[mmu_idx].mask >> CPU_TLB_ENTRY_BITS;
return (addr >> TARGET_PAGE_BITS) & size_mask;
}
@@ -136,7 +133,7 @@ static inline uintptr_t tlb_index(CPUState *cpu, uintptr_t mmu_idx,
static inline CPUTLBEntry *tlb_entry(CPUState *cpu, uintptr_t mmu_idx,
vaddr addr)
{
return &cpu_tlb_fast(cpu, mmu_idx)->table[tlb_index(cpu, mmu_idx, addr)];
return &cpu->neg.tlb.f[mmu_idx].table[tlb_index(cpu, mmu_idx, addr)];
}
static void tlb_window_reset(CPUTLBDesc *desc, int64_t ns,
@@ -290,7 +287,7 @@ static void tlb_flush_one_mmuidx_locked(CPUState *cpu, int mmu_idx,
int64_t now)
{
CPUTLBDesc *desc = &cpu->neg.tlb.d[mmu_idx];
CPUTLBDescFast *fast = cpu_tlb_fast(cpu, mmu_idx);
CPUTLBDescFast *fast = &cpu->neg.tlb.f[mmu_idx];
tlb_mmu_resize_locked(desc, fast, now);
tlb_mmu_flush_locked(desc, fast);
@@ -329,7 +326,7 @@ void tlb_init(CPUState *cpu)
cpu->neg.tlb.c.dirty = 0;
for (i = 0; i < NB_MMU_MODES; i++) {
tlb_mmu_init(&cpu->neg.tlb.d[i], cpu_tlb_fast(cpu, i), now);
tlb_mmu_init(&cpu->neg.tlb.d[i], &cpu->neg.tlb.f[i], now);
}
}
@@ -340,7 +337,7 @@ void tlb_destroy(CPUState *cpu)
qemu_spin_destroy(&cpu->neg.tlb.c.lock);
for (i = 0; i < NB_MMU_MODES; i++) {
CPUTLBDesc *desc = &cpu->neg.tlb.d[i];
CPUTLBDescFast *fast = cpu_tlb_fast(cpu, i);
CPUTLBDescFast *fast = &cpu->neg.tlb.f[i];
g_free(fast->table);
g_free(desc->fulltlb);
@@ -368,8 +365,8 @@ static void flush_all_helper(CPUState *src, run_on_cpu_func fn,
static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data)
{
MMUIdxMap asked = data.host_int;
MMUIdxMap all_dirty, work, to_clean;
uint16_t asked = data.host_int;
uint16_t all_dirty, work, to_clean;
int64_t now = get_clock_realtime();
assert_cpu_is_self(cpu);
@@ -406,7 +403,7 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data)
}
}
void tlb_flush_by_mmuidx(CPUState *cpu, MMUIdxMap idxmap)
void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap)
{
tlb_debug("mmu_idx: 0x%" PRIx16 "\n", idxmap);
@@ -420,7 +417,7 @@ void tlb_flush(CPUState *cpu)
tlb_flush_by_mmuidx(cpu, ALL_MMUIDX_BITS);
}
void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, MMUIdxMap idxmap)
void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, uint16_t idxmap)
{
const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work;
@@ -529,7 +526,7 @@ static void tlb_flush_page_locked(CPUState *cpu, int midx, vaddr page)
*/
static void tlb_flush_page_by_mmuidx_async_0(CPUState *cpu,
vaddr addr,
MMUIdxMap idxmap)
uint16_t idxmap)
{
int mmu_idx;
@@ -568,14 +565,14 @@ static void tlb_flush_page_by_mmuidx_async_1(CPUState *cpu,
{
vaddr addr_and_idxmap = data.target_ptr;
vaddr addr = addr_and_idxmap & TARGET_PAGE_MASK;
MMUIdxMap idxmap = addr_and_idxmap & ~TARGET_PAGE_MASK;
uint16_t idxmap = addr_and_idxmap & ~TARGET_PAGE_MASK;
tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap);
}
typedef struct {
vaddr addr;
MMUIdxMap idxmap;
uint16_t idxmap;
} TLBFlushPageByMMUIdxData;
/**
@@ -597,7 +594,7 @@ static void tlb_flush_page_by_mmuidx_async_2(CPUState *cpu,
g_free(d);
}
void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, MMUIdxMap idxmap)
void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, uint16_t idxmap)
{
tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%" PRIx16 "\n", addr, idxmap);
@@ -616,7 +613,7 @@ void tlb_flush_page(CPUState *cpu, vaddr addr)
void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
vaddr addr,
MMUIdxMap idxmap)
uint16_t idxmap)
{
tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%"PRIx16"\n", addr, idxmap);
@@ -665,7 +662,7 @@ static void tlb_flush_range_locked(CPUState *cpu, int midx,
unsigned bits)
{
CPUTLBDesc *d = &cpu->neg.tlb.d[midx];
CPUTLBDescFast *f = cpu_tlb_fast(cpu, midx);
CPUTLBDescFast *f = &cpu->neg.tlb.f[midx];
vaddr mask = MAKE_64BIT_MASK(0, bits);
/*
@@ -713,8 +710,8 @@ static void tlb_flush_range_locked(CPUState *cpu, int midx,
typedef struct {
vaddr addr;
vaddr len;
MMUIdxMap idxmap;
unsigned bits;
uint16_t idxmap;
uint16_t bits;
} TLBFlushRangeData;
static void tlb_flush_range_by_mmuidx_async_0(CPUState *cpu,
@@ -764,26 +761,26 @@ static void tlb_flush_range_by_mmuidx_async_1(CPUState *cpu,
}
void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr,
vaddr len, MMUIdxMap idxmap,
vaddr len, uint16_t idxmap,
unsigned bits)
{
TLBFlushRangeData d;
assert_cpu_is_self(cpu);
/* If no page bits are significant, this devolves to tlb_flush. */
if (bits < TARGET_PAGE_BITS) {
tlb_flush_by_mmuidx(cpu, idxmap);
return;
}
/*
* If all bits are significant, and len is small,
* this devolves to tlb_flush_page.
*/
if (len <= TARGET_PAGE_SIZE && bits >= target_long_bits()) {
if (bits >= TARGET_LONG_BITS && len <= TARGET_PAGE_SIZE) {
tlb_flush_page_by_mmuidx(cpu, addr, idxmap);
return;
}
/* If no page bits are significant, this devolves to tlb_flush. */
if (bits < TARGET_PAGE_BITS) {
tlb_flush_by_mmuidx(cpu, idxmap);
return;
}
/* This should already be page aligned */
d.addr = addr & TARGET_PAGE_MASK;
@@ -795,7 +792,7 @@ void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr,
}
void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr,
MMUIdxMap idxmap, unsigned bits)
uint16_t idxmap, unsigned bits)
{
tlb_flush_range_by_mmuidx(cpu, addr, TARGET_PAGE_SIZE, idxmap, bits);
}
@@ -803,25 +800,25 @@ void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr,
void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
vaddr addr,
vaddr len,
MMUIdxMap idxmap,
uint16_t idxmap,
unsigned bits)
{
TLBFlushRangeData d, *p;
CPUState *dst_cpu;
/* If no page bits are significant, this devolves to tlb_flush. */
if (bits < TARGET_PAGE_BITS) {
tlb_flush_by_mmuidx_all_cpus_synced(src_cpu, idxmap);
return;
}
/*
* If all bits are significant, and len is small,
* this devolves to tlb_flush_page.
*/
if (len <= TARGET_PAGE_SIZE && bits >= target_long_bits()) {
if (bits >= TARGET_LONG_BITS && len <= TARGET_PAGE_SIZE) {
tlb_flush_page_by_mmuidx_all_cpus_synced(src_cpu, addr, idxmap);
return;
}
/* If no page bits are significant, this devolves to tlb_flush. */
if (bits < TARGET_PAGE_BITS) {
tlb_flush_by_mmuidx_all_cpus_synced(src_cpu, idxmap);
return;
}
/* This should already be page aligned */
d.addr = addr & TARGET_PAGE_MASK;
@@ -845,7 +842,7 @@ void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
vaddr addr,
MMUIdxMap idxmap,
uint16_t idxmap,
unsigned bits)
{
tlb_flush_range_by_mmuidx_all_cpus_synced(src_cpu, addr, TARGET_PAGE_SIZE,
@@ -856,7 +853,7 @@ void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
can be detected */
void tlb_protect_code(ram_addr_t ram_addr)
{
physical_memory_test_and_clear_dirty(ram_addr & TARGET_PAGE_MASK,
cpu_physical_memory_test_and_clear_dirty(ram_addr & TARGET_PAGE_MASK,
TARGET_PAGE_SIZE,
DIRTY_MEMORY_CODE);
}
@@ -865,7 +862,7 @@ void tlb_protect_code(ram_addr_t ram_addr)
tested for self modifying code */
void tlb_unprotect_code(ram_addr_t ram_addr)
{
physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE);
cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE);
}
@@ -885,17 +882,18 @@ void tlb_unprotect_code(ram_addr_t ram_addr)
*
* Called with tlb_c.lock held.
*/
static void tlb_reset_dirty_range_locked(CPUTLBEntryFull *full, CPUTLBEntry *ent,
static void tlb_reset_dirty_range_locked(CPUTLBEntry *tlb_entry,
uintptr_t start, uintptr_t length)
{
const uintptr_t addr = ent->addr_write;
int flags = addr | full->slow_flags[MMU_DATA_STORE];
uintptr_t addr = tlb_entry->addr_write;
flags &= TLB_INVALID_MASK | TLB_MMIO | TLB_DISCARD_WRITE | TLB_NOTDIRTY;
if (flags == 0) {
uintptr_t host = (addr & TARGET_PAGE_MASK) + ent->addend;
if ((host - start) < length) {
qatomic_set(&ent->addr_write, addr | TLB_NOTDIRTY);
if ((addr & (TLB_INVALID_MASK | TLB_MMIO |
TLB_DISCARD_WRITE | TLB_NOTDIRTY)) == 0) {
addr &= TARGET_PAGE_MASK;
addr += tlb_entry->addend;
if ((addr - start) < length) {
qatomic_set(&tlb_entry->addr_write,
tlb_entry->addr_write | TLB_NOTDIRTY);
}
}
}
@@ -914,25 +912,23 @@ static inline void copy_tlb_helper_locked(CPUTLBEntry *d, const CPUTLBEntry *s)
* We must take tlb_c.lock to avoid racing with another vCPU update. The only
* thing actually updated is the target TLB entry ->addr_write flags.
*/
void tlb_reset_dirty(CPUState *cpu, uintptr_t start, uintptr_t length)
void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length)
{
int mmu_idx;
qemu_spin_lock(&cpu->neg.tlb.c.lock);
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
CPUTLBDesc *desc = &cpu->neg.tlb.d[mmu_idx];
CPUTLBDescFast *fast = cpu_tlb_fast(cpu, mmu_idx);
unsigned int n = tlb_n_entries(fast);
unsigned int i;
unsigned int n = tlb_n_entries(&cpu->neg.tlb.f[mmu_idx]);
for (i = 0; i < n; i++) {
tlb_reset_dirty_range_locked(&desc->fulltlb[i], &fast->table[i],
start, length);
tlb_reset_dirty_range_locked(&cpu->neg.tlb.f[mmu_idx].table[i],
start1, length);
}
for (i = 0; i < CPU_VTLB_SIZE; i++) {
tlb_reset_dirty_range_locked(&desc->vfulltlb[i], &desc->vtable[i],
start, length);
tlb_reset_dirty_range_locked(&cpu->neg.tlb.d[mmu_idx].vtable[i],
start1, length);
}
}
qemu_spin_unlock(&cpu->neg.tlb.c.lock);
@@ -1083,7 +1079,7 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx,
if (prot & PAGE_WRITE) {
if (section->readonly) {
write_flags |= TLB_DISCARD_WRITE;
} else if (physical_memory_is_clean(iotlb)) {
} else if (cpu_physical_memory_is_clean(iotlb)) {
write_flags |= TLB_NOTDIRTY;
}
}
@@ -1314,7 +1310,7 @@ static bool victim_tlb_hit(CPUState *cpu, size_t mmu_idx, size_t index,
if (cmp == page) {
/* Found entry in victim tlb, swap tlb and iotlb. */
CPUTLBEntry tmptlb, *tlb = &cpu_tlb_fast(cpu, mmu_idx)->table[index];
CPUTLBEntry tmptlb, *tlb = &cpu->neg.tlb.f[mmu_idx].table[index];
qemu_spin_lock(&cpu->neg.tlb.c.lock);
copy_tlb_helper_locked(&tmptlb, tlb);
@@ -1339,18 +1335,18 @@ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size,
trace_memory_notdirty_write_access(mem_vaddr, ram_addr, size);
if (!physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) {
tb_invalidate_phys_range_fast(cpu, ram_addr, size, retaddr);
if (!cpu_physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) {
tb_invalidate_phys_range_fast(ram_addr, size, retaddr);
}
/*
* Set both VGA and migration bits for simplicity and to remove
* the notdirty callback faster.
*/
physical_memory_set_dirty_range(ram_addr, size, DIRTY_CLIENTS_NOCODE);
cpu_physical_memory_set_dirty_range(ram_addr, size, DIRTY_CLIENTS_NOCODE);
/* We remove the notdirty callback only if the code has been flushed. */
if (!physical_memory_is_clean(ram_addr)) {
if (!cpu_physical_memory_is_clean(ram_addr)) {
trace_memory_notdirty_set_dirty(mem_vaddr);
tlb_set_dirty(cpu, mem_vaddr);
}
@@ -1668,7 +1664,18 @@ static bool mmu_lookup1(CPUState *cpu, MMULookupPageData *data, MemOp memop,
if (likely(!maybe_resized)) {
/* Alignment has not been checked by tlb_fill_align. */
int a_bits = memop_tlb_alignment_bits(memop, flags & TLB_CHECK_ALIGNED);
int a_bits = memop_alignment_bits(memop);
/*
* This alignment check differs from the one above, in that this is
* based on the atomicity of the operation. The intended use case is
* the ARM memory type field of each PTE, where access to pages with
* Device memory type require alignment.
*/
if (unlikely(flags & TLB_CHECK_ALIGNED)) {
int at_bits = memop_atomicity_bits(memop);
a_bits = MAX(a_bits, at_bits);
}
if (unlikely(addr & ((1 << a_bits) - 1))) {
cpu_unaligned_access(cpu, addr, access_type, mmu_idx, ra);
}
@@ -1731,7 +1738,6 @@ static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi,
uintptr_t ra, MMUAccessType type, MMULookupLocals *l)
{
bool crosspage;
vaddr last;
int flags;
l->memop = get_memop(oi);
@@ -1741,15 +1747,13 @@ static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi,
l->page[0].addr = addr;
l->page[0].size = memop_size(l->memop);
l->page[1].addr = 0;
l->page[1].addr = (addr + l->page[0].size - 1) & TARGET_PAGE_MASK;
l->page[1].size = 0;
crosspage = (addr ^ l->page[1].addr) & TARGET_PAGE_MASK;
/* Lookup and recognize exceptions from the first page. */
mmu_lookup1(cpu, &l->page[0], l->memop, l->mmu_idx, type, ra);
last = addr + l->page[0].size - 1;
crosspage = (addr ^ last) & TARGET_PAGE_MASK;
if (likely(!crosspage)) {
mmu_lookup1(cpu, &l->page[0], l->memop, l->mmu_idx, type, ra);
flags = l->page[0].flags;
if (unlikely(flags & (TLB_WATCHPOINT | TLB_NOTDIRTY))) {
mmu_watch_or_dirty(cpu, &l->page[0], type, ra);
@@ -1759,18 +1763,15 @@ static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi,
}
} else {
/* Finish compute of page crossing. */
vaddr addr1 = last & TARGET_PAGE_MASK;
int size0 = addr1 - addr;
int size0 = l->page[1].addr - addr;
l->page[1].size = l->page[0].size - size0;
l->page[0].size = size0;
l->page[1].addr = cpu->cc->tcg_ops->pointer_wrap(cpu, l->mmu_idx,
addr1, addr);
/*
* Lookup and recognize exceptions from the second page.
* If the lookup potentially resized the table, refresh the
* first CPUTLBEntryFull pointer.
* Lookup both pages, recognizing exceptions from either. If the
* second lookup potentially resized, refresh first CPUTLBEntryFull.
*/
mmu_lookup1(cpu, &l->page[0], l->memop, l->mmu_idx, type, ra);
if (mmu_lookup1(cpu, &l->page[1], 0, l->mmu_idx, type, ra)) {
uintptr_t index = tlb_index(cpu, l->mmu_idx, addr);
l->page[0].full = &cpu->neg.tlb.d[l->mmu_idx].fulltlb[index];
@@ -1864,12 +1865,8 @@ static void *atomic_mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi,
goto stop_the_world;
}
/* Finish collecting tlb flags for both read and write. */
full = &cpu->neg.tlb.d[mmu_idx].fulltlb[index];
/* Collect tlb flags for read. */
tlb_addr |= tlbe->addr_read;
tlb_addr &= TLB_FLAGS_MASK & ~TLB_FORCE_SLOW;
tlb_addr |= full->slow_flags[MMU_DATA_STORE];
tlb_addr |= full->slow_flags[MMU_DATA_LOAD];
/* Notice an IO access or a needs-MMU-lookup access */
if (unlikely(tlb_addr & (TLB_MMIO | TLB_DISCARD_WRITE))) {
@@ -1879,12 +1876,13 @@ static void *atomic_mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi,
}
hostaddr = (void *)((uintptr_t)addr + tlbe->addend);
full = &cpu->neg.tlb.d[mmu_idx].fulltlb[index];
if (unlikely(tlb_addr & TLB_NOTDIRTY)) {
notdirty_write(cpu, addr, size, full, retaddr);
}
if (unlikely(tlb_addr & TLB_WATCHPOINT)) {
if (unlikely(tlb_addr & TLB_FORCE_SLOW)) {
int wp_flags = 0;
if (full->slow_flags[MMU_DATA_STORE] & TLB_WATCHPOINT) {
@@ -1893,8 +1891,10 @@ static void *atomic_mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi,
if (full->slow_flags[MMU_DATA_LOAD] & TLB_WATCHPOINT) {
wp_flags |= BP_MEM_READ;
}
cpu_check_watchpoint(cpu, addr, size,
full->attrs, wp_flags, retaddr);
if (wp_flags) {
cpu_check_watchpoint(cpu, addr, size,
full->attrs, wp_flags, retaddr);
}
}
return hostaddr;
@@ -2321,7 +2321,7 @@ static uint8_t do_ld1_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi,
MMULookupLocals l;
bool crosspage;
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
crosspage = mmu_lookup(cpu, addr, oi, ra, access_type, &l);
tcg_debug_assert(!crosspage);
@@ -2336,7 +2336,7 @@ static uint16_t do_ld2_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi,
uint16_t ret;
uint8_t a, b;
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
crosspage = mmu_lookup(cpu, addr, oi, ra, access_type, &l);
if (likely(!crosspage)) {
return do_ld_2(cpu, &l.page[0], l.mmu_idx, access_type, l.memop, ra);
@@ -2360,7 +2360,7 @@ static uint32_t do_ld4_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi,
bool crosspage;
uint32_t ret;
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
crosspage = mmu_lookup(cpu, addr, oi, ra, access_type, &l);
if (likely(!crosspage)) {
return do_ld_4(cpu, &l.page[0], l.mmu_idx, access_type, l.memop, ra);
@@ -2381,7 +2381,7 @@ static uint64_t do_ld8_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi,
bool crosspage;
uint64_t ret;
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
crosspage = mmu_lookup(cpu, addr, oi, ra, access_type, &l);
if (likely(!crosspage)) {
return do_ld_8(cpu, &l.page[0], l.mmu_idx, access_type, l.memop, ra);
@@ -2404,7 +2404,7 @@ static Int128 do_ld16_mmu(CPUState *cpu, vaddr addr,
Int128 ret;
int first;
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_LOAD, &l);
if (likely(!crosspage)) {
if (unlikely(l.page[0].flags & TLB_MMIO)) {
@@ -2732,7 +2732,7 @@ static void do_st1_mmu(CPUState *cpu, vaddr addr, uint8_t val,
MMULookupLocals l;
bool crosspage;
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l);
tcg_debug_assert(!crosspage);
@@ -2746,7 +2746,7 @@ static void do_st2_mmu(CPUState *cpu, vaddr addr, uint16_t val,
bool crosspage;
uint8_t a, b;
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l);
if (likely(!crosspage)) {
do_st_2(cpu, &l.page[0], val, l.mmu_idx, l.memop, ra);
@@ -2768,7 +2768,7 @@ static void do_st4_mmu(CPUState *cpu, vaddr addr, uint32_t val,
MMULookupLocals l;
bool crosspage;
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l);
if (likely(!crosspage)) {
do_st_4(cpu, &l.page[0], val, l.mmu_idx, l.memop, ra);
@@ -2789,7 +2789,7 @@ static void do_st8_mmu(CPUState *cpu, vaddr addr, uint64_t val,
MMULookupLocals l;
bool crosspage;
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l);
if (likely(!crosspage)) {
do_st_8(cpu, &l.page[0], val, l.mmu_idx, l.memop, ra);
@@ -2812,7 +2812,7 @@ static void do_st16_mmu(CPUState *cpu, vaddr addr, Int128 val,
uint64_t a, b;
int first;
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l);
if (likely(!crosspage)) {
if (unlikely(l.page[0].flags & TLB_MMIO)) {
@@ -2897,45 +2897,54 @@ static void do_st16_mmu(CPUState *cpu, vaddr addr, Int128 val,
/* Code access functions. */
uint8_t cpu_ldb_code_mmu(CPUArchState *env, vaddr addr,
uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr addr)
{
CPUState *cs = env_cpu(env);
MemOpIdx oi = make_memop_idx(MO_UB, cpu_mmu_index(cs, true));
return do_ld1_mmu(cs, addr, oi, 0, MMU_INST_FETCH);
}
uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr addr)
{
CPUState *cs = env_cpu(env);
MemOpIdx oi = make_memop_idx(MO_TEUW, cpu_mmu_index(cs, true));
return do_ld2_mmu(cs, addr, oi, 0, MMU_INST_FETCH);
}
uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr addr)
{
CPUState *cs = env_cpu(env);
MemOpIdx oi = make_memop_idx(MO_TEUL, cpu_mmu_index(cs, true));
return do_ld4_mmu(cs, addr, oi, 0, MMU_INST_FETCH);
}
uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr)
{
CPUState *cs = env_cpu(env);
MemOpIdx oi = make_memop_idx(MO_TEUQ, cpu_mmu_index(cs, true));
return do_ld8_mmu(cs, addr, oi, 0, MMU_INST_FETCH);
}
uint8_t cpu_ldb_code_mmu(CPUArchState *env, abi_ptr addr,
MemOpIdx oi, uintptr_t retaddr)
{
return do_ld1_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH);
}
uint16_t cpu_ldw_code_mmu(CPUArchState *env, vaddr addr,
uint16_t cpu_ldw_code_mmu(CPUArchState *env, abi_ptr addr,
MemOpIdx oi, uintptr_t retaddr)
{
return do_ld2_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH);
}
uint32_t cpu_ldl_code_mmu(CPUArchState *env, vaddr addr,
uint32_t cpu_ldl_code_mmu(CPUArchState *env, abi_ptr addr,
MemOpIdx oi, uintptr_t retaddr)
{
return do_ld4_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH);
}
uint64_t cpu_ldq_code_mmu(CPUArchState *env, vaddr addr,
uint64_t cpu_ldq_code_mmu(CPUArchState *env, abi_ptr addr,
MemOpIdx oi, uintptr_t retaddr)
{
return do_ld8_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH);
}
/*
* Common pointer_wrap implementations.
*/
/*
* To be used for strict alignment targets.
* Because no accesses are unaligned, no accesses wrap either.
*/
vaddr cpu_pointer_wrap_notreached(CPUState *cs, int idx, vaddr res, vaddr base)
{
g_assert_not_reached();
}
/* To be used for strict 32-bit targets. */
vaddr cpu_pointer_wrap_uint32(CPUState *cs, int idx, vaddr res, vaddr base)
{
return (uint32_t)res;
}
+1 -1
View File
@@ -35,7 +35,7 @@
#include "system/replay.h"
#include "system/runstate.h"
#include "hw/core/cpu.h"
#include "exec/icount.h"
#include "system/cpu-timers.h"
#include "system/cpu-timers-internal.h"
/*
+3 -70
View File
@@ -11,8 +11,6 @@
#include "exec/cpu-common.h"
#include "exec/translation-block.h"
#include "exec/mmap-lock.h"
#include "accel/tcg/tb-cpu-state.h"
extern int64_t max_delay;
extern int64_t max_advance;
@@ -47,7 +45,9 @@ static inline bool cpu_plugin_mem_cbs_enabled(const CPUState *cpu)
#endif
}
TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s);
TranslationBlock *tb_gen_code(CPUState *cpu, vaddr pc,
uint64_t cs_base, uint32_t flags,
int cflags);
void page_init(void);
void tb_htable_init(void);
void tb_reset_jump(TranslationBlock *tb, int n);
@@ -74,71 +74,4 @@ uint32_t curr_cflags(CPUState *cpu);
void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr);
/**
* get_page_addr_code_hostp()
* @env: CPUArchState
* @addr: guest virtual address of guest code
*
* See get_page_addr_code() (full-system version) for documentation on the
* return value.
*
* Sets *@hostp (when @hostp is non-NULL) as follows.
* If the return value is -1, sets *@hostp to NULL. Otherwise, sets *@hostp
* to the host address where @addr's content is kept.
*
* Note: this function can trigger an exception.
*/
tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr,
void **hostp);
/**
* get_page_addr_code()
* @env: CPUArchState
* @addr: guest virtual address of guest code
*
* If we cannot translate and execute from the entire RAM page, or if
* the region is not backed by RAM, returns -1. Otherwise, returns the
* ram_addr_t corresponding to the guest code at @addr.
*
* Note: this function can trigger an exception.
*/
static inline tb_page_addr_t get_page_addr_code(CPUArchState *env,
vaddr addr)
{
return get_page_addr_code_hostp(env, addr, NULL);
}
/*
* Access to the various translations structures need to be serialised
* via locks for consistency. In user-mode emulation access to the
* memory related structures are protected with mmap_lock.
* In !user-mode we use per-page locks.
*/
#ifdef CONFIG_USER_ONLY
#define assert_memory_lock() tcg_debug_assert(have_mmap_lock())
#else
#define assert_memory_lock()
#endif
#if defined(CONFIG_SOFTMMU) && defined(CONFIG_DEBUG_TCG)
void assert_no_pages_locked(void);
#else
static inline void assert_no_pages_locked(void) { }
#endif
#ifdef CONFIG_USER_ONLY
static inline void page_table_config_init(void) { }
#else
void page_table_config_init(void);
#endif
#ifndef CONFIG_USER_ONLY
G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr);
#endif /* CONFIG_USER_ONLY */
void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr);
void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr);
void tcg_get_stats(AccelState *accel, GString *buf);
#endif
+79
View File
@@ -0,0 +1,79 @@
/*
* Internal execution defines for qemu (target specific)
*
* Copyright (c) 2003 Fabrice Bellard
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef ACCEL_TCG_INTERNAL_TARGET_H
#define ACCEL_TCG_INTERNAL_TARGET_H
#include "exec/exec-all.h"
#include "exec/translation-block.h"
#include "tb-internal.h"
#include "tcg-target-mo.h"
/*
* Access to the various translations structures need to be serialised
* via locks for consistency. In user-mode emulation access to the
* memory related structures are protected with mmap_lock.
* In !user-mode we use per-page locks.
*/
#ifdef CONFIG_USER_ONLY
#define assert_memory_lock() tcg_debug_assert(have_mmap_lock())
#else
#define assert_memory_lock()
#endif
#if defined(CONFIG_SOFTMMU) && defined(CONFIG_DEBUG_TCG)
void assert_no_pages_locked(void);
#else
static inline void assert_no_pages_locked(void) { }
#endif
#ifdef CONFIG_USER_ONLY
static inline void page_table_config_init(void) { }
#else
void page_table_config_init(void);
#endif
#ifndef CONFIG_USER_ONLY
G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr);
#endif /* CONFIG_USER_ONLY */
/**
* tcg_req_mo:
* @type: TCGBar
*
* Filter @type to the barrier that is required for the guest
* memory ordering vs the host memory ordering. A non-zero
* result indicates that some barrier is required.
*
* If TCG_GUEST_DEFAULT_MO is not defined, assume that the
* guest requires strict ordering.
*
* This is a macro so that it's constant even without optimization.
*/
#ifdef TCG_GUEST_DEFAULT_MO
# define tcg_req_mo(type) \
((type) & TCG_GUEST_DEFAULT_MO & ~TCG_TARGET_DEFAULT_MO)
#else
# define tcg_req_mo(type) ((type) & ~TCG_TARGET_DEFAULT_MO)
#endif
/**
* cpu_req_mo:
* @type: TCGBar
*
* If tcg_req_mo indicates a barrier for @type is required
* for the guest memory model, issue a host memory barrier.
*/
#define cpu_req_mo(type) \
do { \
if (tcg_req_mo(type)) { \
smp_mb(); \
} \
} while (0)
#endif /* ACCEL_TCG_INTERNAL_H */
+327 -12
View File
@@ -123,7 +123,7 @@ void helper_st_i128(CPUArchState *env, uint64_t addr, Int128 val, MemOpIdx oi)
* Load helpers for cpu_ldst.h
*/
static void plugin_load_cb(CPUArchState *env, vaddr addr,
static void plugin_load_cb(CPUArchState *env, abi_ptr addr,
uint64_t value_low,
uint64_t value_high,
MemOpIdx oi)
@@ -135,7 +135,7 @@ static void plugin_load_cb(CPUArchState *env, vaddr addr,
}
}
uint8_t cpu_ldb_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra)
uint8_t cpu_ldb_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra)
{
uint8_t ret;
@@ -145,7 +145,7 @@ uint8_t cpu_ldb_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra)
return ret;
}
uint16_t cpu_ldw_mmu(CPUArchState *env, vaddr addr,
uint16_t cpu_ldw_mmu(CPUArchState *env, abi_ptr addr,
MemOpIdx oi, uintptr_t ra)
{
uint16_t ret;
@@ -156,7 +156,7 @@ uint16_t cpu_ldw_mmu(CPUArchState *env, vaddr addr,
return ret;
}
uint32_t cpu_ldl_mmu(CPUArchState *env, vaddr addr,
uint32_t cpu_ldl_mmu(CPUArchState *env, abi_ptr addr,
MemOpIdx oi, uintptr_t ra)
{
uint32_t ret;
@@ -167,7 +167,7 @@ uint32_t cpu_ldl_mmu(CPUArchState *env, vaddr addr,
return ret;
}
uint64_t cpu_ldq_mmu(CPUArchState *env, vaddr addr,
uint64_t cpu_ldq_mmu(CPUArchState *env, abi_ptr addr,
MemOpIdx oi, uintptr_t ra)
{
uint64_t ret;
@@ -178,7 +178,7 @@ uint64_t cpu_ldq_mmu(CPUArchState *env, vaddr addr,
return ret;
}
Int128 cpu_ld16_mmu(CPUArchState *env, vaddr addr,
Int128 cpu_ld16_mmu(CPUArchState *env, abi_ptr addr,
MemOpIdx oi, uintptr_t ra)
{
Int128 ret;
@@ -193,7 +193,7 @@ Int128 cpu_ld16_mmu(CPUArchState *env, vaddr addr,
* Store helpers for cpu_ldst.h
*/
static void plugin_store_cb(CPUArchState *env, vaddr addr,
static void plugin_store_cb(CPUArchState *env, abi_ptr addr,
uint64_t value_low,
uint64_t value_high,
MemOpIdx oi)
@@ -205,14 +205,14 @@ static void plugin_store_cb(CPUArchState *env, vaddr addr,
}
}
void cpu_stb_mmu(CPUArchState *env, vaddr addr, uint8_t val,
void cpu_stb_mmu(CPUArchState *env, abi_ptr addr, uint8_t val,
MemOpIdx oi, uintptr_t retaddr)
{
helper_stb_mmu(env, addr, val, oi, retaddr);
plugin_store_cb(env, addr, val, 0, oi);
}
void cpu_stw_mmu(CPUArchState *env, vaddr addr, uint16_t val,
void cpu_stw_mmu(CPUArchState *env, abi_ptr addr, uint16_t val,
MemOpIdx oi, uintptr_t retaddr)
{
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16);
@@ -220,7 +220,7 @@ void cpu_stw_mmu(CPUArchState *env, vaddr addr, uint16_t val,
plugin_store_cb(env, addr, val, 0, oi);
}
void cpu_stl_mmu(CPUArchState *env, vaddr addr, uint32_t val,
void cpu_stl_mmu(CPUArchState *env, abi_ptr addr, uint32_t val,
MemOpIdx oi, uintptr_t retaddr)
{
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32);
@@ -228,7 +228,7 @@ void cpu_stl_mmu(CPUArchState *env, vaddr addr, uint32_t val,
plugin_store_cb(env, addr, val, 0, oi);
}
void cpu_stq_mmu(CPUArchState *env, vaddr addr, uint64_t val,
void cpu_stq_mmu(CPUArchState *env, abi_ptr addr, uint64_t val,
MemOpIdx oi, uintptr_t retaddr)
{
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64);
@@ -236,10 +236,325 @@ void cpu_stq_mmu(CPUArchState *env, vaddr addr, uint64_t val,
plugin_store_cb(env, addr, val, 0, oi);
}
void cpu_st16_mmu(CPUArchState *env, vaddr addr, Int128 val,
void cpu_st16_mmu(CPUArchState *env, abi_ptr addr, Int128 val,
MemOpIdx oi, uintptr_t retaddr)
{
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128);
do_st16_mmu(env_cpu(env), addr, val, oi, retaddr);
plugin_store_cb(env, addr, int128_getlo(val), int128_gethi(val), oi);
}
/*
* Wrappers of the above
*/
uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx);
return cpu_ldb_mmu(env, addr, oi, ra);
}
int cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
return (int8_t)cpu_ldub_mmuidx_ra(env, addr, mmu_idx, ra);
}
uint32_t cpu_lduw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_BEUW | MO_UNALN, mmu_idx);
return cpu_ldw_mmu(env, addr, oi, ra);
}
int cpu_ldsw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
return (int16_t)cpu_lduw_be_mmuidx_ra(env, addr, mmu_idx, ra);
}
uint32_t cpu_ldl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_BEUL | MO_UNALN, mmu_idx);
return cpu_ldl_mmu(env, addr, oi, ra);
}
uint64_t cpu_ldq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_BEUQ | MO_UNALN, mmu_idx);
return cpu_ldq_mmu(env, addr, oi, ra);
}
uint32_t cpu_lduw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_LEUW | MO_UNALN, mmu_idx);
return cpu_ldw_mmu(env, addr, oi, ra);
}
int cpu_ldsw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
return (int16_t)cpu_lduw_le_mmuidx_ra(env, addr, mmu_idx, ra);
}
uint32_t cpu_ldl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_LEUL | MO_UNALN, mmu_idx);
return cpu_ldl_mmu(env, addr, oi, ra);
}
uint64_t cpu_ldq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_LEUQ | MO_UNALN, mmu_idx);
return cpu_ldq_mmu(env, addr, oi, ra);
}
void cpu_stb_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx);
cpu_stb_mmu(env, addr, val, oi, ra);
}
void cpu_stw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_BEUW | MO_UNALN, mmu_idx);
cpu_stw_mmu(env, addr, val, oi, ra);
}
void cpu_stl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_BEUL | MO_UNALN, mmu_idx);
cpu_stl_mmu(env, addr, val, oi, ra);
}
void cpu_stq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_BEUQ | MO_UNALN, mmu_idx);
cpu_stq_mmu(env, addr, val, oi, ra);
}
void cpu_stw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_LEUW | MO_UNALN, mmu_idx);
cpu_stw_mmu(env, addr, val, oi, ra);
}
void cpu_stl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_LEUL | MO_UNALN, mmu_idx);
cpu_stl_mmu(env, addr, val, oi, ra);
}
void cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val,
int mmu_idx, uintptr_t ra)
{
MemOpIdx oi = make_memop_idx(MO_LEUQ | MO_UNALN, mmu_idx);
cpu_stq_mmu(env, addr, val, oi, ra);
}
/*--------------------------*/
uint32_t cpu_ldub_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
int mmu_index = cpu_mmu_index(env_cpu(env), false);
return cpu_ldub_mmuidx_ra(env, addr, mmu_index, ra);
}
int cpu_ldsb_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
return (int8_t)cpu_ldub_data_ra(env, addr, ra);
}
uint32_t cpu_lduw_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
int mmu_index = cpu_mmu_index(env_cpu(env), false);
return cpu_lduw_be_mmuidx_ra(env, addr, mmu_index, ra);
}
int cpu_ldsw_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
return (int16_t)cpu_lduw_be_data_ra(env, addr, ra);
}
uint32_t cpu_ldl_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
int mmu_index = cpu_mmu_index(env_cpu(env), false);
return cpu_ldl_be_mmuidx_ra(env, addr, mmu_index, ra);
}
uint64_t cpu_ldq_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
int mmu_index = cpu_mmu_index(env_cpu(env), false);
return cpu_ldq_be_mmuidx_ra(env, addr, mmu_index, ra);
}
uint32_t cpu_lduw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
int mmu_index = cpu_mmu_index(env_cpu(env), false);
return cpu_lduw_le_mmuidx_ra(env, addr, mmu_index, ra);
}
int cpu_ldsw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
return (int16_t)cpu_lduw_le_data_ra(env, addr, ra);
}
uint32_t cpu_ldl_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
int mmu_index = cpu_mmu_index(env_cpu(env), false);
return cpu_ldl_le_mmuidx_ra(env, addr, mmu_index, ra);
}
uint64_t cpu_ldq_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
{
int mmu_index = cpu_mmu_index(env_cpu(env), false);
return cpu_ldq_le_mmuidx_ra(env, addr, mmu_index, ra);
}
void cpu_stb_data_ra(CPUArchState *env, abi_ptr addr,
uint32_t val, uintptr_t ra)
{
int mmu_index = cpu_mmu_index(env_cpu(env), false);
cpu_stb_mmuidx_ra(env, addr, val, mmu_index, ra);
}
void cpu_stw_be_data_ra(CPUArchState *env, abi_ptr addr,
uint32_t val, uintptr_t ra)
{
int mmu_index = cpu_mmu_index(env_cpu(env), false);
cpu_stw_be_mmuidx_ra(env, addr, val, mmu_index, ra);
}
void cpu_stl_be_data_ra(CPUArchState *env, abi_ptr addr,
uint32_t val, uintptr_t ra)
{
int mmu_index = cpu_mmu_index(env_cpu(env), false);
cpu_stl_be_mmuidx_ra(env, addr, val, mmu_index, ra);
}
void cpu_stq_be_data_ra(CPUArchState *env, abi_ptr addr,
uint64_t val, uintptr_t ra)
{
int mmu_index = cpu_mmu_index(env_cpu(env), false);
cpu_stq_be_mmuidx_ra(env, addr, val, mmu_index, ra);
}
void cpu_stw_le_data_ra(CPUArchState *env, abi_ptr addr,
uint32_t val, uintptr_t ra)
{
int mmu_index = cpu_mmu_index(env_cpu(env), false);
cpu_stw_le_mmuidx_ra(env, addr, val, mmu_index, ra);
}
void cpu_stl_le_data_ra(CPUArchState *env, abi_ptr addr,
uint32_t val, uintptr_t ra)
{
int mmu_index = cpu_mmu_index(env_cpu(env), false);
cpu_stl_le_mmuidx_ra(env, addr, val, mmu_index, ra);
}
void cpu_stq_le_data_ra(CPUArchState *env, abi_ptr addr,
uint64_t val, uintptr_t ra)
{
int mmu_index = cpu_mmu_index(env_cpu(env), false);
cpu_stq_le_mmuidx_ra(env, addr, val, mmu_index, ra);
}
/*--------------------------*/
uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr addr)
{
return cpu_ldub_data_ra(env, addr, 0);
}
int cpu_ldsb_data(CPUArchState *env, abi_ptr addr)
{
return (int8_t)cpu_ldub_data(env, addr);
}
uint32_t cpu_lduw_be_data(CPUArchState *env, abi_ptr addr)
{
return cpu_lduw_be_data_ra(env, addr, 0);
}
int cpu_ldsw_be_data(CPUArchState *env, abi_ptr addr)
{
return (int16_t)cpu_lduw_be_data(env, addr);
}
uint32_t cpu_ldl_be_data(CPUArchState *env, abi_ptr addr)
{
return cpu_ldl_be_data_ra(env, addr, 0);
}
uint64_t cpu_ldq_be_data(CPUArchState *env, abi_ptr addr)
{
return cpu_ldq_be_data_ra(env, addr, 0);
}
uint32_t cpu_lduw_le_data(CPUArchState *env, abi_ptr addr)
{
return cpu_lduw_le_data_ra(env, addr, 0);
}
int cpu_ldsw_le_data(CPUArchState *env, abi_ptr addr)
{
return (int16_t)cpu_lduw_le_data(env, addr);
}
uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr addr)
{
return cpu_ldl_le_data_ra(env, addr, 0);
}
uint64_t cpu_ldq_le_data(CPUArchState *env, abi_ptr addr)
{
return cpu_ldq_le_data_ra(env, addr, 0);
}
void cpu_stb_data(CPUArchState *env, abi_ptr addr, uint32_t val)
{
cpu_stb_data_ra(env, addr, val, 0);
}
void cpu_stw_be_data(CPUArchState *env, abi_ptr addr, uint32_t val)
{
cpu_stw_be_data_ra(env, addr, val, 0);
}
void cpu_stl_be_data(CPUArchState *env, abi_ptr addr, uint32_t val)
{
cpu_stl_be_data_ra(env, addr, val, 0);
}
void cpu_stq_be_data(CPUArchState *env, abi_ptr addr, uint64_t val)
{
cpu_stq_be_data_ra(env, addr, val, 0);
}
void cpu_stw_le_data(CPUArchState *env, abi_ptr addr, uint32_t val)
{
cpu_stw_le_data_ra(env, addr, val, 0);
}
void cpu_stl_le_data(CPUArchState *env, abi_ptr addr, uint32_t val)
{
cpu_stl_le_data_ra(env, addr, val, 0);
}
void cpu_stq_le_data(CPUArchState *env, abi_ptr addr, uint64_t val)
{
cpu_stq_le_data_ra(env, addr, val, 0);
}
+13 -19
View File
@@ -1,34 +1,28 @@
if not have_tcg
subdir_done()
endif
tcg_ss = ss.source_set()
tcg_ss.add(files(
'cpu-exec.c',
common_ss.add(when: 'CONFIG_TCG', if_true: files(
'cpu-exec-common.c',
'tcg-runtime.c',
'tcg-runtime-gvec.c',
'tb-maint.c',
))
tcg_specific_ss = ss.source_set()
tcg_specific_ss.add(files(
'tcg-all.c',
'tcg-stats.c',
'cpu-exec.c',
'tb-maint.c',
'translate-all.c',
'translator.c',
))
tcg_specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c'))
tcg_specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_false: files('user-exec-stub.c'))
if get_option('plugins')
tcg_ss.add(files('plugin-gen.c'))
tcg_specific_ss.add(files('plugin-gen.c'))
endif
specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_specific_ss)
user_ss.add_all(tcg_ss)
system_ss.add_all(tcg_ss)
user_ss.add(files(
'user-exec.c',
'user-exec-stub.c',
specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files(
'cputlb.c',
))
system_ss.add(files(
'cputlb.c',
system_ss.add(when: ['CONFIG_TCG'], if_true: files(
'icount-common.c',
'monitor.c',
'tcg-accel-ops.c',
+207 -1
View File
@@ -7,13 +7,196 @@
*/
#include "qemu/osdep.h"
#include "qemu/accel.h"
#include "qemu/qht.h"
#include "qapi/error.h"
#include "qapi/type-helpers.h"
#include "qapi/qapi-commands-machine.h"
#include "monitor/monitor.h"
#include "system/cpu-timers.h"
#include "system/tcg.h"
#include "tcg/tcg.h"
#include "internal-common.h"
#include "tb-context.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");
}
}
static void dump_accel_info(GString *buf)
{
AccelState *accel = current_accel();
bool one_insn_per_tb = object_property_get_bool(OBJECT(accel),
"one-insn-per-tb",
&error_fatal);
g_string_append_printf(buf, "Accelerator settings:\n");
g_string_append_printf(buf, "one-insn-per-tb: %s\n\n",
one_insn_per_tb ? "on" : "off");
}
static void print_qht_statistics(struct qht_stats hst, GString *buf)
{
uint32_t hgram_opts;
size_t hgram_bins;
char *hgram;
if (!hst.head_buckets) {
return;
}
g_string_append_printf(buf, "TB hash buckets %zu/%zu "
"(%0.2f%% head buckets used)\n",
hst.used_head_buckets, hst.head_buckets,
(double)hst.used_head_buckets /
hst.head_buckets * 100);
hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS;
hgram_opts |= QDIST_PR_100X | QDIST_PR_PERCENT;
if (qdist_xmax(&hst.occupancy) - qdist_xmin(&hst.occupancy) == 1) {
hgram_opts |= QDIST_PR_NODECIMAL;
}
hgram = qdist_pr(&hst.occupancy, 10, hgram_opts);
g_string_append_printf(buf, "TB hash occupancy %0.2f%% avg chain occ. "
"Histogram: %s\n",
qdist_avg(&hst.occupancy) * 100, hgram);
g_free(hgram);
hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS;
hgram_bins = qdist_xmax(&hst.chain) - qdist_xmin(&hst.chain);
if (hgram_bins > 10) {
hgram_bins = 10;
} else {
hgram_bins = 0;
hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE;
}
hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts);
g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. "
"Histogram: %s\n",
qdist_avg(&hst.chain), hgram);
g_free(hgram);
}
struct tb_tree_stats {
size_t nb_tbs;
size_t host_size;
size_t target_size;
size_t max_target_size;
size_t direct_jmp_count;
size_t direct_jmp2_count;
size_t cross_page;
};
static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data)
{
const TranslationBlock *tb = value;
struct tb_tree_stats *tst = data;
tst->nb_tbs++;
tst->host_size += tb->tc.size;
tst->target_size += tb->size;
if (tb->size > tst->max_target_size) {
tst->max_target_size = tb->size;
}
if (tb->page_addr[1] != -1) {
tst->cross_page++;
}
if (tb->jmp_reset_offset[0] != TB_JMP_OFFSET_INVALID) {
tst->direct_jmp_count++;
if (tb->jmp_reset_offset[1] != TB_JMP_OFFSET_INVALID) {
tst->direct_jmp2_count++;
}
}
return false;
}
static void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide)
{
CPUState *cpu;
size_t full = 0, part = 0, elide = 0;
CPU_FOREACH(cpu) {
full += qatomic_read(&cpu->neg.tlb.c.full_flush_count);
part += qatomic_read(&cpu->neg.tlb.c.part_flush_count);
elide += qatomic_read(&cpu->neg.tlb.c.elide_flush_count);
}
*pfull = full;
*ppart = part;
*pelide = elide;
}
static void tcg_dump_info(GString *buf)
{
g_string_append_printf(buf, "[TCG profiler not compiled]\n");
}
static void dump_exec_info(GString *buf)
{
struct tb_tree_stats tst = {};
struct qht_stats hst;
size_t nb_tbs, flush_full, flush_part, flush_elide;
tcg_tb_foreach(tb_tree_stats_iter, &tst);
nb_tbs = tst.nb_tbs;
/* XXX: avoid using doubles ? */
g_string_append_printf(buf, "Translation buffer state:\n");
/*
* Report total code size including the padding and TB structs;
* otherwise users might think "-accel tcg,tb-size" is not honoured.
* For avg host size we use the precise numbers from tb_tree_stats though.
*/
g_string_append_printf(buf, "gen code size %zu/%zu\n",
tcg_code_size(), tcg_code_capacity());
g_string_append_printf(buf, "TB count %zu\n", nb_tbs);
g_string_append_printf(buf, "TB avg target size %zu max=%zu bytes\n",
nb_tbs ? tst.target_size / nb_tbs : 0,
tst.max_target_size);
g_string_append_printf(buf, "TB avg host size %zu bytes "
"(expansion ratio: %0.1f)\n",
nb_tbs ? tst.host_size / nb_tbs : 0,
tst.target_size ?
(double)tst.host_size / tst.target_size : 0);
g_string_append_printf(buf, "cross page TB count %zu (%zu%%)\n",
tst.cross_page,
nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0);
g_string_append_printf(buf, "direct jump count %zu (%zu%%) "
"(2 jumps=%zu %zu%%)\n",
tst.direct_jmp_count,
nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0,
tst.direct_jmp2_count,
nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0);
qht_statistics_init(&tb_ctx.htable, &hst);
print_qht_statistics(hst, buf);
qht_statistics_destroy(&hst);
g_string_append_printf(buf, "\nStatistics:\n");
g_string_append_printf(buf, "TB flush count %u\n",
qatomic_read(&tb_ctx.tb_flush_count));
g_string_append_printf(buf, "TB invalidate count %u\n",
qatomic_read(&tb_ctx.tb_phys_invalidate_count));
tlb_flush_counts(&flush_full, &flush_part, &flush_elide);
g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full);
g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part);
g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide);
tcg_dump_info(buf);
}
HumanReadableText *qmp_x_query_jit(Error **errp)
{
@@ -24,7 +207,29 @@ HumanReadableText *qmp_x_query_jit(Error **errp)
return NULL;
}
tcg_dump_stats(buf);
dump_accel_info(buf);
dump_exec_info(buf);
dump_drift_info(buf);
return human_readable_text_from_str(buf);
}
static void tcg_dump_op_count(GString *buf)
{
g_string_append_printf(buf, "[TCG profiler not compiled]\n");
}
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);
}
@@ -32,6 +237,7 @@ HumanReadableText *qmp_x_query_jit(Error **errp)
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);
+10 -37
View File
@@ -22,12 +22,13 @@
#include "qemu/osdep.h"
#include "qemu/plugin.h"
#include "qemu/log.h"
#include "cpu.h"
#include "tcg/tcg.h"
#include "tcg/tcg-temp-internal.h"
#include "tcg/tcg-op-common.h"
#include "tcg/tcg-op.h"
#include "exec/exec-all.h"
#include "exec/plugin-gen.h"
#include "exec/translator.h"
#include "exec/translation-block.h"
enum plugin_gen_from {
PLUGIN_GEN_FROM_TB,
@@ -88,13 +89,15 @@ static void gen_enable_mem_helper(struct qemu_plugin_tb *ptb,
qemu_plugin_add_dyn_cb_arr(arr);
tcg_gen_st_ptr(tcg_constant_ptr((intptr_t)arr), tcg_env,
offsetof(CPUState, neg.plugin_mem_cbs) - sizeof(CPUState));
offsetof(CPUState, neg.plugin_mem_cbs) -
offsetof(ArchCPU, env));
}
static void gen_disable_mem_helper(void)
{
tcg_gen_st_ptr(tcg_constant_ptr(0), tcg_env,
offsetof(CPUState, neg.plugin_mem_cbs) - sizeof(CPUState));
offsetof(CPUState, neg.plugin_mem_cbs) -
offsetof(ArchCPU, env));
}
static TCGv_i32 gen_cpu_index(void)
@@ -102,35 +105,25 @@ static TCGv_i32 gen_cpu_index(void)
/*
* Optimize when we run with a single vcpu. All values using cpu_index,
* including scoreboard index, will be optimized out.
* User-mode flushes all TBs when setting this flag.
* In system-mode, all vcpus are created before generating code.
* User-mode calls tb_flush when setting this flag. In system-mode, all
* vcpus are created before generating code.
*/
if (!tcg_cflags_has(current_cpu, CF_PARALLEL)) {
return tcg_constant_i32(current_cpu->cpu_index);
}
TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
tcg_gen_ld_i32(cpu_index, tcg_env,
offsetof(CPUState, cpu_index) - sizeof(CPUState));
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
return cpu_index;
}
static void gen_udata_cb(struct qemu_plugin_regular_cb *cb)
{
TCGv_i32 cpu_index = gen_cpu_index();
enum qemu_plugin_cb_flags cb_flags =
tcg_call_to_qemu_plugin_cb_flags(cb->info->flags);
TCGv_i32 flags = tcg_constant_i32(cb_flags);
TCGv_i32 clear_flags = tcg_constant_i32(QEMU_PLUGIN_CB_NO_REGS);
tcg_gen_st_i32(flags, tcg_env,
offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState));
tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
tcgv_i32_temp(cpu_index),
tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
tcg_gen_st_i32(clear_flags, tcg_env,
offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState));
tcg_temp_free_i32(cpu_index);
tcg_temp_free_i32(flags);
tcg_temp_free_i32(clear_flags);
}
static TCGv_ptr gen_plugin_u64_ptr(qemu_plugin_u64 entry)
@@ -183,20 +176,10 @@ static void gen_udata_cond_cb(struct qemu_plugin_conditional_cb *cb)
tcg_gen_ld_i64(val, ptr, 0);
tcg_gen_brcondi_i64(cond, val, cb->imm, after_cb);
TCGv_i32 cpu_index = gen_cpu_index();
enum qemu_plugin_cb_flags cb_flags =
tcg_call_to_qemu_plugin_cb_flags(cb->info->flags);
TCGv_i32 flags = tcg_constant_i32(cb_flags);
TCGv_i32 clear_flags = tcg_constant_i32(QEMU_PLUGIN_CB_NO_REGS);
tcg_gen_st_i32(flags, tcg_env,
offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState));
tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
tcgv_i32_temp(cpu_index),
tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
tcg_gen_st_i32(clear_flags, tcg_env,
offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState));
tcg_temp_free_i32(cpu_index);
tcg_temp_free_i32(flags);
tcg_temp_free_i32(clear_flags);
gen_set_label(after_cb);
tcg_temp_free_i64(val);
@@ -230,22 +213,12 @@ static void gen_mem_cb(struct qemu_plugin_regular_cb *cb,
qemu_plugin_meminfo_t meminfo, TCGv_i64 addr)
{
TCGv_i32 cpu_index = gen_cpu_index();
enum qemu_plugin_cb_flags cb_flags =
tcg_call_to_qemu_plugin_cb_flags(cb->info->flags);
TCGv_i32 flags = tcg_constant_i32(cb_flags);
TCGv_i32 clear_flags = tcg_constant_i32(QEMU_PLUGIN_CB_NO_REGS);
tcg_gen_st_i32(flags, tcg_env,
offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState));
tcg_gen_call4(cb->f.vcpu_mem, cb->info, NULL,
tcgv_i32_temp(cpu_index),
tcgv_i32_temp(tcg_constant_i32(meminfo)),
tcgv_i64_temp(addr),
tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
tcg_gen_st_i32(clear_flags, tcg_env,
offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState));
tcg_temp_free_i32(cpu_index);
tcg_temp_free_i32(flags);
tcg_temp_free_i32(clear_flags);
}
static void inject_cb(struct qemu_plugin_dyn_cb *cb)
+2 -2
View File
@@ -20,8 +20,8 @@
#ifndef EXEC_TB_HASH_H
#define EXEC_TB_HASH_H
#include "exec/vaddr.h"
#include "exec/target_page.h"
#include "exec/cpu-defs.h"
#include "exec/exec-all.h"
#include "exec/translation-block.h"
#include "qemu/xxhash.h"
#include "tb-jmp-cache.h"
+40 -6
View File
@@ -9,6 +9,8 @@
#ifndef ACCEL_TCG_TB_INTERNAL_TARGET_H
#define ACCEL_TCG_TB_INTERNAL_TARGET_H
#include "exec/cpu-all.h"
#include "exec/exec-all.h"
#include "exec/translation-block.h"
/*
@@ -22,34 +24,66 @@
*/
#define GETPC_ADJ 2
void tb_lock_page0(tb_page_addr_t);
#ifdef CONFIG_SOFTMMU
#define CPU_TLB_DYN_MIN_BITS 6
#define CPU_TLB_DYN_DEFAULT_BITS 8
# if HOST_LONG_BITS == 32
/* Make sure we do not require a double-word shift for the TLB load */
# define CPU_TLB_DYN_MAX_BITS (32 - TARGET_PAGE_BITS)
# else /* HOST_LONG_BITS == 64 */
/*
* Assuming TARGET_PAGE_BITS==12, with 2**22 entries we can cover 2**(22+12) ==
* 2**34 == 16G of address space. This is roughly what one would expect a
* TLB to cover in a modern (as of 2018) x86_64 CPU. For instance, Intel
* Skylake's Level-2 STLB has 16 1G entries.
* Also, make sure we do not size the TLB past the guest's address space.
*/
# ifdef TARGET_PAGE_BITS_VARY
# define CPU_TLB_DYN_MAX_BITS \
MIN(22, TARGET_VIRT_ADDR_SPACE_BITS - TARGET_PAGE_BITS)
# else
# define CPU_TLB_DYN_MAX_BITS \
MIN_CONST(22, TARGET_VIRT_ADDR_SPACE_BITS - TARGET_PAGE_BITS)
# endif
# endif
#endif /* CONFIG_SOFTMMU */
#ifdef CONFIG_USER_ONLY
#include "user/page-protection.h"
/*
* For user-only, page_protect sets the page read-only.
* Since most execution is already on read-only pages, and we'd need to
* account for other TBs on the same page, defer undoing any page protection
* until we receive the write fault.
*/
static inline void tb_lock_page0(tb_page_addr_t p0)
{
page_protect(p0);
}
static inline void tb_lock_page1(tb_page_addr_t p0, tb_page_addr_t p1)
{
tb_lock_page0(p1);
page_protect(p1);
}
static inline void tb_unlock_page1(tb_page_addr_t p0, tb_page_addr_t p1) { }
static inline void tb_unlock_pages(TranslationBlock *tb) { }
#else
void tb_lock_page0(tb_page_addr_t);
void tb_lock_page1(tb_page_addr_t, tb_page_addr_t);
void tb_unlock_page1(tb_page_addr_t, tb_page_addr_t);
void tb_unlock_pages(TranslationBlock *);
#endif
#ifdef CONFIG_SOFTMMU
void tb_invalidate_phys_range_fast(CPUState *cpu, ram_addr_t ram_addr,
unsigned size, uintptr_t retaddr);
void tb_invalidate_phys_range_fast(ram_addr_t ram_addr,
unsigned size,
uintptr_t retaddr);
#endif /* CONFIG_SOFTMMU */
bool tb_invalidate_phys_page_unwind(CPUState *cpu, tb_page_addr_t addr,
uintptr_t pc);
bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc);
#endif
+84 -79
View File
@@ -22,11 +22,9 @@
#include "qemu/qtree.h"
#include "exec/cputlb.h"
#include "exec/log.h"
#include "exec/exec-all.h"
#include "exec/page-protection.h"
#include "exec/mmap-lock.h"
#include "exec/tb-flush.h"
#include "exec/target_page.h"
#include "accel/tcg/cpu-ops.h"
#include "tb-internal.h"
#include "system/tcg.h"
#include "tcg/tcg.h"
@@ -34,13 +32,11 @@
#include "tb-context.h"
#include "tb-internal.h"
#include "internal-common.h"
#include "internal-target.h"
#ifdef CONFIG_USER_ONLY
#include "user/page-protection.h"
#define runstate_is_running() true
#else
#include "system/runstate.h"
#endif
#include "trace.h"
/* List iterators for lists of tagged pointers in TranslationBlock. */
#define TB_FOR_EACH_TAGGED(head, tb, n, field) \
@@ -91,10 +87,7 @@ static IntervalTreeRoot tb_root;
static void tb_remove_all(void)
{
/*
* Only called from tb_flush__exclusive_or_serial, where we have already
* asserted that we're in an exclusive state.
*/
assert_memory_lock();
memset(&tb_root, 0, sizeof(tb_root));
}
@@ -163,7 +156,11 @@ static PageForEachNext foreach_tb_next(PageForEachNext tb,
/*
* In system mode we want L1_MAP to be based on ram offsets.
*/
#define L1_MAP_ADDR_SPACE_BITS HOST_LONG_BITS
#if HOST_LONG_BITS < TARGET_PHYS_ADDR_SPACE_BITS
# define L1_MAP_ADDR_SPACE_BITS HOST_LONG_BITS
#else
# define L1_MAP_ADDR_SPACE_BITS TARGET_PHYS_ADDR_SPACE_BITS
#endif
/* Size of the L2 (and L3, etc) page tables. */
#define V_L2_BITS 10
@@ -762,20 +759,17 @@ static void tb_remove(TranslationBlock *tb)
}
#endif /* CONFIG_USER_ONLY */
/*
* Flush all the translation blocks.
* Must be called from a context in which no cpus are running,
* e.g. start_exclusive() or vm_stop().
*/
void tb_flush__exclusive_or_serial(void)
/* flush all the translation blocks */
static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
{
CPUState *cpu;
bool did_flush = false;
trace_tb_flush();
assert(tcg_enabled());
/* Note that cpu_in_serial_context checks cpu_in_exclusive_context. */
assert(!runstate_is_running() ||
(current_cpu && cpu_in_serial_context(current_cpu)));
mmap_lock();
/* If it is already been done on request of another CPU, just retry. */
if (tb_ctx.tb_flush_count != tb_flush_count.host_int) {
goto done;
}
did_flush = true;
CPU_FOREACH(cpu) {
tcg_flush_jmp_cache(cpu);
@@ -787,23 +781,25 @@ void tb_flush__exclusive_or_serial(void)
tcg_region_reset_all();
/* XXX: flush processor icache at this point if cache flush is expensive */
qatomic_inc(&tb_ctx.tb_flush_count);
qemu_plugin_flush_cb();
}
static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
{
/* If it is already been done on request of another CPU, just retry. */
if (tb_ctx.tb_flush_count == tb_flush_count.host_int) {
tb_flush__exclusive_or_serial();
done:
mmap_unlock();
if (did_flush) {
qemu_plugin_flush_cb();
}
}
void queue_tb_flush(CPUState *cs)
void tb_flush(CPUState *cpu)
{
if (tcg_enabled()) {
unsigned tb_flush_count = qatomic_read(&tb_ctx.tb_flush_count);
async_safe_run_on_cpu(cs, do_tb_flush,
RUN_ON_CPU_HOST_INT(tb_flush_count));
if (cpu_in_serial_context(cpu)) {
do_tb_flush(cpu, RUN_ON_CPU_HOST_INT(tb_flush_count));
} else {
async_safe_run_on_cpu(cpu, do_tb_flush,
RUN_ON_CPU_HOST_INT(tb_flush_count));
}
}
}
@@ -843,14 +839,6 @@ static inline void tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig)
* We first acquired the lock, and since the destination pointer matches,
* we know for sure that @orig is in the jmp list.
*/
if (dest == orig) {
/*
* In the case of a TB that links to itself, removing the entry
* from the list means that it won't be present later during
* tb_jmp_unlink -- unlink now.
*/
tb_reset_jump(orig, n_orig);
}
pprev = &dest->jmp_list_head;
TB_FOR_EACH_JMP(dest, tb, n) {
if (tb == orig && n == n_orig) {
@@ -1021,8 +1009,7 @@ TranslationBlock *tb_link_page(TranslationBlock *tb)
* Called with mmap_lock held for user-mode emulation.
* NOTE: this function must not be called while a TB is running.
*/
void tb_invalidate_phys_range(CPUState *cpu, tb_page_addr_t start,
tb_page_addr_t last)
void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last)
{
TranslationBlock *tb;
PageForEachNext n;
@@ -1045,16 +1032,17 @@ static void tb_invalidate_phys_page(tb_page_addr_t addr)
start = addr & TARGET_PAGE_MASK;
last = addr | ~TARGET_PAGE_MASK;
tb_invalidate_phys_range(NULL, start, last);
tb_invalidate_phys_range(start, last);
}
/*
* Called with mmap_lock held. If pc is not 0 then it indicates the
* host PC of the faulting store instruction that caused this invalidate.
* Returns true if the caller needs to abort execution of the current TB.
* Returns true if the caller needs to abort execution of the current
* TB (because it was modified by this store and the guest CPU has
* precise-SMC semantics).
*/
bool tb_invalidate_phys_page_unwind(CPUState *cpu, tb_page_addr_t addr,
uintptr_t pc)
bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc)
{
TranslationBlock *current_tb;
bool current_tb_modified;
@@ -1066,7 +1054,10 @@ bool tb_invalidate_phys_page_unwind(CPUState *cpu, tb_page_addr_t addr,
* Without precise smc semantics, or when outside of a TB,
* we can skip to invalidate.
*/
if (!pc || !cpu || !cpu->cc->tcg_ops->precise_smc) {
#ifndef TARGET_HAS_PRECISE_SMC
pc = 0;
#endif
if (!pc) {
tb_invalidate_phys_page(addr);
return false;
}
@@ -1089,14 +1080,15 @@ bool tb_invalidate_phys_page_unwind(CPUState *cpu, tb_page_addr_t addr,
* the CPU state.
*/
current_tb_modified = true;
cpu_restore_state_from_tb(cpu, current_tb, pc);
cpu_restore_state_from_tb(current_cpu, current_tb, pc);
}
tb_phys_invalidate__locked(tb);
}
if (current_tb_modified) {
/* Force execution of one insn next time. */
cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu);
CPUState *cpu = current_cpu;
cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(current_cpu);
return true;
}
return false;
@@ -1105,28 +1097,23 @@ bool tb_invalidate_phys_page_unwind(CPUState *cpu, tb_page_addr_t addr,
/*
* @p must be non-NULL.
* Call with all @pages locked.
* (@cpu, @retaddr) may be (NULL, 0) outside of a cpu context,
* in which case precise_smc need not be detected.
*/
static void
tb_invalidate_phys_page_range__locked(CPUState *cpu,
struct page_collection *pages,
tb_invalidate_phys_page_range__locked(struct page_collection *pages,
PageDesc *p, tb_page_addr_t start,
tb_page_addr_t last,
uintptr_t retaddr)
{
TranslationBlock *tb;
PageForEachNext n;
#ifdef TARGET_HAS_PRECISE_SMC
bool current_tb_modified = false;
TranslationBlock *current_tb = NULL;
TranslationBlock *current_tb = retaddr ? tcg_tb_lookup(retaddr) : NULL;
#endif /* TARGET_HAS_PRECISE_SMC */
/* Range may not cross a page. */
tcg_debug_assert(((start ^ last) & TARGET_PAGE_MASK) == 0);
if (retaddr && cpu && cpu->cc->tcg_ops->precise_smc) {
current_tb = tcg_tb_lookup(retaddr);
}
/*
* We remove all the TBs in the range [start, last].
* XXX: see if in some cases it could be faster to invalidate all the code
@@ -1144,7 +1131,8 @@ tb_invalidate_phys_page_range__locked(CPUState *cpu,
tb_last = tb_start + (tb_last & ~TARGET_PAGE_MASK);
}
if (!(tb_last < start || tb_start > last)) {
if (unlikely(current_tb == tb) &&
#ifdef TARGET_HAS_PRECISE_SMC
if (current_tb == tb &&
(tb_cflags(current_tb) & CF_COUNT_MASK) != 1) {
/*
* If we are modifying the current TB, we must stop
@@ -1154,8 +1142,9 @@ tb_invalidate_phys_page_range__locked(CPUState *cpu,
* restore the CPU state.
*/
current_tb_modified = true;
cpu_restore_state_from_tb(cpu, current_tb, retaddr);
cpu_restore_state_from_tb(current_cpu, current_tb, retaddr);
}
#endif /* TARGET_HAS_PRECISE_SMC */
tb_phys_invalidate__locked(tb);
}
}
@@ -1165,12 +1154,15 @@ tb_invalidate_phys_page_range__locked(CPUState *cpu,
tlb_unprotect_code(start);
}
if (unlikely(current_tb_modified)) {
#ifdef TARGET_HAS_PRECISE_SMC
if (current_tb_modified) {
page_collection_unlock(pages);
/* Force execution of one insn next time. */
cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu);
cpu_loop_exit_noexc(cpu);
current_cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(current_cpu);
mmap_unlock();
cpu_loop_exit_noexc(current_cpu);
}
#endif
}
/*
@@ -1180,8 +1172,7 @@ tb_invalidate_phys_page_range__locked(CPUState *cpu,
* access: the virtual CPU will exit the current TB if code is modified inside
* this TB.
*/
void tb_invalidate_phys_range(CPUState *cpu, tb_page_addr_t start,
tb_page_addr_t last)
void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last)
{
struct page_collection *pages;
tb_page_addr_t index, index_last;
@@ -1200,30 +1191,44 @@ void tb_invalidate_phys_range(CPUState *cpu, tb_page_addr_t start,
page_start = index << TARGET_PAGE_BITS;
page_last = page_start | ~TARGET_PAGE_MASK;
page_last = MIN(page_last, last);
tb_invalidate_phys_page_range__locked(cpu, pages, pd,
tb_invalidate_phys_page_range__locked(pages, pd,
page_start, page_last, 0);
}
page_collection_unlock(pages);
}
/*
* Call with all @pages in the range [@start, @start + len[ locked.
*/
static void tb_invalidate_phys_page_fast__locked(struct page_collection *pages,
tb_page_addr_t start,
unsigned len, uintptr_t ra)
{
PageDesc *p;
p = page_find(start >> TARGET_PAGE_BITS);
if (!p) {
return;
}
assert_page_locked(p);
tb_invalidate_phys_page_range__locked(pages, p, start, start + len - 1, ra);
}
/*
* len must be <= 8 and start must be a multiple of len.
* Called via softmmu_template.h when code areas are written to with
* iothread mutex not held.
*/
void tb_invalidate_phys_range_fast(CPUState *cpu, ram_addr_t start,
unsigned len, uintptr_t ra)
void tb_invalidate_phys_range_fast(ram_addr_t ram_addr,
unsigned size,
uintptr_t retaddr)
{
PageDesc *p = page_find(start >> TARGET_PAGE_BITS);
struct page_collection *pages;
if (p) {
ram_addr_t last = start + len - 1;
struct page_collection *pages = page_collection_lock(start, last);
tb_invalidate_phys_page_range__locked(cpu, pages, p,
start, last, ra);
page_collection_unlock(pages);
}
pages = page_collection_lock(ram_addr, ram_addr + size - 1);
tb_invalidate_phys_page_fast__locked(pages, ram_addr, size, retaddr);
page_collection_unlock(pages);
}
#endif /* CONFIG_USER_ONLY */
+1 -1
View File
@@ -25,7 +25,7 @@
#include "qemu/osdep.h"
#include "system/replay.h"
#include "exec/icount.h"
#include "system/cpu-timers.h"
#include "qemu/main-loop.h"
#include "qemu/guest-random.h"
#include "hw/core/cpu.h"
+12 -3
View File
@@ -26,7 +26,7 @@
#include "qemu/osdep.h"
#include "system/tcg.h"
#include "system/replay.h"
#include "exec/icount.h"
#include "system/cpu-timers.h"
#include "qemu/main-loop.h"
#include "qemu/notify.h"
#include "qemu/guest-random.h"
@@ -84,9 +84,10 @@ static void *mttcg_cpu_thread_fn(void *arg)
cpu_thread_signal_created(cpu);
qemu_guest_random_seed_thread_part2(cpu->random_seed);
do {
qemu_process_cpu_events(cpu);
/* process any pending work */
cpu->exit_request = 1;
do {
if (cpu_can_run(cpu)) {
int r;
bql_unlock();
@@ -111,6 +112,9 @@ static void *mttcg_cpu_thread_fn(void *arg)
break;
}
}
qatomic_set_mb(&cpu->exit_request, 0);
qemu_wait_io_event(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
tcg_cpu_destroy(cpu);
@@ -120,6 +124,11 @@ static void *mttcg_cpu_thread_fn(void *arg)
return NULL;
}
void mttcg_kick_vcpu_thread(CPUState *cpu)
{
cpu_exit(cpu);
}
void mttcg_start_vcpu_thread(CPUState *cpu)
{
char thread_name[VCPU_THREAD_NAME_SIZE];
+3
View File
@@ -10,6 +10,9 @@
#ifndef TCG_ACCEL_OPS_MTTCG_H
#define TCG_ACCEL_OPS_MTTCG_H
/* kick MTTCG vCPU thread */
void mttcg_kick_vcpu_thread(CPUState *cpu);
/* start an mttcg vCPU thread */
void mttcg_start_vcpu_thread(CPUState *cpu);
+25 -34
View File
@@ -27,7 +27,7 @@
#include "qemu/lockable.h"
#include "system/tcg.h"
#include "system/replay.h"
#include "exec/icount.h"
#include "system/cpu-timers.h"
#include "qemu/main-loop.h"
#include "qemu/notify.h"
#include "qemu/guest-random.h"
@@ -43,7 +43,7 @@ void rr_kick_vcpu_thread(CPUState *unused)
CPUState *cpu;
CPU_FOREACH(cpu) {
tcg_kick_vcpu_thread(cpu);
cpu_exit(cpu);
};
}
@@ -117,7 +117,7 @@ static void rr_wait_io_event(void)
rr_start_kick_timer();
CPU_FOREACH(cpu) {
qemu_process_cpu_events_common(cpu);
qemu_wait_io_event_common(cpu);
}
}
@@ -197,13 +197,13 @@ static void *rr_cpu_thread_fn(void *arg)
qemu_guest_random_seed_thread_part2(cpu->random_seed);
/* wait for initial kick-off after machine start */
while (cpu_is_stopped(first_cpu)) {
while (first_cpu->stopped) {
qemu_cond_wait_bql(first_cpu->halt_cond);
/* process any pending work */
CPU_FOREACH(cpu) {
current_cpu = cpu;
qemu_process_cpu_events_common(cpu);
qemu_wait_io_event_common(cpu);
}
}
@@ -211,30 +211,13 @@ static void *rr_cpu_thread_fn(void *arg)
cpu = first_cpu;
/* process any pending work */
cpu->exit_request = 1;
while (1) {
/* Only used for icount_enabled() */
int64_t cpu_budget = 0;
if (cpu) {
/*
* This could even reset exit_request for all CPUs, but in practice
* races between CPU exits and changes to "cpu" are so rare that
* there's no advantage in doing so.
*/
qatomic_set(&cpu->exit_request, false);
}
if (icount_enabled() && all_cpu_threads_idle()) {
/*
* When all cpus are sleeping (e.g in WFI), to avoid a deadlock
* in the main_loop, wake it up in order to start the warp timer.
*/
qemu_notify_event();
}
rr_wait_io_event();
rr_deal_with_unplugged_cpus();
bql_unlock();
replay_mutex_lock();
bql_lock();
@@ -259,17 +242,10 @@ static void *rr_cpu_thread_fn(void *arg)
cpu = first_cpu;
}
while (cpu && cpu_work_list_empty(cpu)) {
/*
* Store rr_current_cpu before evaluating cpu->exit_request.
* Pairs with rr_kick_next_cpu().
*/
while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) {
/* Store rr_current_cpu before evaluating cpu_can_run(). */
qatomic_set_mb(&rr_current_cpu, cpu);
/* Pairs with store-release in cpu_exit. */
if (qatomic_load_acquire(&cpu->exit_request)) {
break;
}
current_cpu = cpu;
qemu_clock_enable(QEMU_CLOCK_VIRTUAL,
@@ -309,6 +285,21 @@ static void *rr_cpu_thread_fn(void *arg)
/* Does not need a memory barrier because a spurious wakeup is okay. */
qatomic_set(&rr_current_cpu, NULL);
if (cpu && cpu->exit_request) {
qatomic_set_mb(&cpu->exit_request, 0);
}
if (icount_enabled() && all_cpu_threads_idle()) {
/*
* When all cpus are sleeping (e.g in WFI), to avoid a deadlock
* in the main_loop, wake it up in order to start the warp timer.
*/
qemu_notify_event();
}
rr_wait_io_event();
rr_deal_with_unplugged_cpus();
}
g_assert_not_reached();
+8 -11
View File
@@ -26,11 +26,10 @@
*/
#include "qemu/osdep.h"
#include "accel/accel-ops.h"
#include "accel/accel-cpu-ops.h"
#include "system/accel-ops.h"
#include "system/tcg.h"
#include "system/replay.h"
#include "exec/icount.h"
#include "system/cpu-timers.h"
#include "qemu/main-loop.h"
#include "qemu/guest-random.h"
#include "qemu/timer.h"
@@ -38,7 +37,6 @@
#include "exec/hwaddr.h"
#include "exec/tb-flush.h"
#include "exec/translation-block.h"
#include "exec/watchpoint.h"
#include "gdbstub/enums.h"
#include "hw/core/cpu.h"
@@ -81,7 +79,6 @@ int tcg_cpu_exec(CPUState *cpu)
cpu_exec_start(cpu);
ret = cpu_exec(cpu);
cpu_exec_end(cpu);
return ret;
}
@@ -95,7 +92,9 @@ static void tcg_cpu_reset_hold(CPUState *cpu)
/* mask must never be zero, except for A20 change call */
void tcg_handle_interrupt(CPUState *cpu, int mask)
{
cpu_set_interrupt(cpu, mask);
g_assert(bql_locked());
cpu->interrupt_request |= mask;
/*
* If called from iothread context, wake the target cpu in
@@ -198,13 +197,11 @@ static inline void tcg_remove_all_breakpoints(CPUState *cpu)
cpu_watchpoint_remove_all(cpu, BP_GDB);
}
static void tcg_accel_ops_init(AccelClass *ac)
static void tcg_accel_ops_init(AccelOpsClass *ops)
{
AccelOpsClass *ops = ac->ops;
if (qemu_tcg_mttcg_enabled()) {
ops->create_vcpu_thread = mttcg_start_vcpu_thread;
ops->kick_vcpu_thread = tcg_kick_vcpu_thread;
ops->kick_vcpu_thread = mttcg_kick_vcpu_thread;
ops->handle_interrupt = tcg_handle_interrupt;
} else {
ops->create_vcpu_thread = rr_start_vcpu_thread;
@@ -226,7 +223,7 @@ static void tcg_accel_ops_init(AccelClass *ac)
ops->remove_all_breakpoints = tcg_remove_all_breakpoints;
}
static void tcg_accel_ops_class_init(ObjectClass *oc, const void *data)
static void tcg_accel_ops_class_init(ObjectClass *oc, void *data)
{
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
-1
View File
@@ -18,6 +18,5 @@ void tcg_cpu_destroy(CPUState *cpu);
int tcg_cpu_exec(CPUState *cpu);
void tcg_handle_interrupt(CPUState *cpu, int mask);
void tcg_cpu_init_cflags(CPUState *cpu, bool parallel);
void tcg_kick_vcpu_thread(CPUState *cpu);
#endif /* TCG_ACCEL_OPS_H */
+51 -82
View File
@@ -26,31 +26,27 @@
#include "qemu/osdep.h"
#include "system/tcg.h"
#include "exec/replay-core.h"
#include "exec/icount.h"
#include "system/cpu-timers.h"
#include "tcg/startup.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/accel.h"
#include "qemu/atomic.h"
#include "qapi/qapi-types-common.h"
#include "qapi/qapi-builtin-visit.h"
#include "qemu/units.h"
#include "qemu/target-info.h"
#ifndef CONFIG_USER_ONLY
#if defined(CONFIG_USER_ONLY)
#include "hw/qdev-core.h"
#else
#include "hw/boards.h"
#include "exec/tb-flush.h"
#include "system/runstate.h"
#endif
#include "accel/accel-ops.h"
#include "accel/accel-cpu-ops.h"
#include "accel/tcg/cpu-ops.h"
#include "internal-common.h"
#include "cpu-param.h"
struct TCGState {
AccelState parent_obj;
OnOffAuto mttcg_enabled;
bool mttcg_enabled;
bool one_insn_per_tb;
int splitwx_enabled;
unsigned long tb_size;
@@ -62,18 +58,40 @@ typedef struct TCGState TCGState;
DECLARE_INSTANCE_CHECKER(TCGState, TCG_STATE,
TYPE_TCG_ACCEL)
#ifndef CONFIG_USER_ONLY
bool qemu_tcg_mttcg_enabled(void)
/*
* We default to false if we know other options have been enabled
* which are currently incompatible with MTTCG. Otherwise when each
* guest (target) has been updated to support:
* - atomic instructions
* - memory ordering primitives (barriers)
* they can set the appropriate CONFIG flags in ${target}-softmmu.mak
*
* Once a guest architecture has been converted to the new primitives
* there is one remaining limitation to check:
* - The guest can't be oversized (e.g. 64 bit guest on 32 bit host)
*/
static bool default_mttcg_enabled(void)
{
TCGState *s = TCG_STATE(current_accel());
return s->mttcg_enabled == ON_OFF_AUTO_ON;
if (icount_enabled()) {
return false;
}
#ifdef TARGET_SUPPORTS_MTTCG
# ifndef TCG_GUEST_DEFAULT_MO
# error "TARGET_SUPPORTS_MTTCG without TCG_GUEST_DEFAULT_MO"
# endif
return true;
#else
return false;
#endif
}
#endif /* !CONFIG_USER_ONLY */
static void tcg_accel_instance_init(Object *obj)
{
TCGState *s = TCG_STATE(obj);
s->mttcg_enabled = default_mttcg_enabled();
/* If debugging enabled, default "auto on", otherwise off. */
#if defined(CONFIG_DEBUG_TCG) && !defined(CONFIG_USER_ONLY)
s->splitwx_enabled = -1;
@@ -82,76 +100,24 @@ static void tcg_accel_instance_init(Object *obj)
#endif
}
bool mttcg_enabled;
bool one_insn_per_tb;
#ifndef CONFIG_USER_ONLY
static void tcg_vm_change_state(void *opaque, bool running, RunState state)
static int tcg_init_machine(MachineState *ms)
{
if (state == RUN_STATE_RESTORE_VM) {
/*
* loadvm will update the content of RAM, bypassing the usual
* mechanisms that ensure we flush TBs for writes to memory
* we've translated code from, so we must flush all TBs.
*
* vm_stop() has just stopped all cpus, so we are exclusive.
*/
assert(!running);
tb_flush__exclusive_or_serial();
}
}
#endif
static int tcg_init_machine(AccelState *as, MachineState *ms)
{
TCGState *s = TCG_STATE(as);
unsigned max_threads = 1;
#ifndef CONFIG_USER_ONLY
CPUClass *cc = CPU_CLASS(object_class_by_name(target_cpu_type()));
bool mttcg_supported = cc->tcg_ops->mttcg_supported;
switch (s->mttcg_enabled) {
case ON_OFF_AUTO_AUTO:
/*
* We default to false if we know other options have been enabled
* which are currently incompatible with MTTCG. Otherwise when each
* guest (target) has been updated to support:
* - atomic instructions
* - memory ordering primitives (barriers)
* they can set the appropriate CONFIG flags in ${target}-softmmu.mak
*
* Once a guest architecture has been converted to the new primitives
* there is one remaining limitation to check:
* - The guest can't be oversized (e.g. 64 bit guest on 32 bit host)
*/
if (mttcg_supported && !icount_enabled()) {
s->mttcg_enabled = ON_OFF_AUTO_ON;
max_threads = ms->smp.max_cpus;
} else {
s->mttcg_enabled = ON_OFF_AUTO_OFF;
}
break;
case ON_OFF_AUTO_ON:
if (!mttcg_supported) {
warn_report("Guest not yet converted to MTTCG - "
"you may get unexpected results");
}
max_threads = ms->smp.max_cpus;
break;
case ON_OFF_AUTO_OFF:
break;
default:
g_assert_not_reached();
}
qemu_add_vm_change_state_handler(tcg_vm_change_state, NULL);
TCGState *s = TCG_STATE(current_accel());
#ifdef CONFIG_USER_ONLY
unsigned max_cpus = 1;
#else
unsigned max_cpus = ms->smp.max_cpus;
#endif
tcg_allowed = true;
mttcg_enabled = s->mttcg_enabled;
page_init();
tb_htable_init();
tcg_init(s->tb_size * MiB, s->splitwx_enabled, max_threads);
tcg_init(s->tb_size * MiB, s->splitwx_enabled, max_cpus);
#if defined(CONFIG_SOFTMMU)
/*
@@ -172,7 +138,7 @@ static char *tcg_get_thread(Object *obj, Error **errp)
{
TCGState *s = TCG_STATE(obj);
return g_strdup(s->mttcg_enabled == ON_OFF_AUTO_ON ? "multi" : "single");
return g_strdup(s->mttcg_enabled ? "multi" : "single");
}
static void tcg_set_thread(Object *obj, const char *value, Error **errp)
@@ -183,10 +149,14 @@ static void tcg_set_thread(Object *obj, const char *value, Error **errp)
if (icount_enabled()) {
error_setg(errp, "No MTTCG when icount is enabled");
} else {
s->mttcg_enabled = ON_OFF_AUTO_ON;
#ifndef TARGET_SUPPORTS_MTTCG
warn_report("Guest not yet converted to MTTCG - "
"you may get unexpected results");
#endif
s->mttcg_enabled = true;
}
} else if (strcmp(value, "single") == 0) {
s->mttcg_enabled = ON_OFF_AUTO_OFF;
s->mttcg_enabled = false;
} else {
error_setg(errp, "Invalid 'thread' setting %s", value);
}
@@ -242,7 +212,7 @@ static void tcg_set_one_insn_per_tb(Object *obj, bool value, Error **errp)
qatomic_set(&one_insn_per_tb, value);
}
static int tcg_gdbstub_supported_sstep_flags(AccelState *as)
static int tcg_gdbstub_supported_sstep_flags(void)
{
/*
* In replay mode all events will come from the log and can't be
@@ -257,14 +227,13 @@ static int tcg_gdbstub_supported_sstep_flags(AccelState *as)
}
}
static void tcg_accel_class_init(ObjectClass *oc, const void *data)
static void tcg_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "tcg";
ac->init_machine = tcg_init_machine;
ac->cpu_common_realize = tcg_exec_realizefn;
ac->cpu_common_unrealize = tcg_exec_unrealizefn;
ac->get_stats = tcg_get_stats;
ac->allowed = &tcg_allowed;
ac->gdbstub_supported_sstep_flags = tcg_gdbstub_supported_sstep_flags;
-12
View File
@@ -63,18 +63,6 @@ DEF_HELPER_FLAGS_5(atomic_cmpxchgo_be, TCG_CALL_NO_WG,
i128, env, i64, i128, i128, i32)
DEF_HELPER_FLAGS_5(atomic_cmpxchgo_le, TCG_CALL_NO_WG,
i128, env, i64, i128, i128, i32)
DEF_HELPER_FLAGS_4(atomic_xchgo_be, TCG_CALL_NO_WG,
i128, env, i64, i128, i32)
DEF_HELPER_FLAGS_4(atomic_xchgo_le, TCG_CALL_NO_WG,
i128, env, i64, i128, i32)
DEF_HELPER_FLAGS_4(atomic_fetch_ando_be, TCG_CALL_NO_WG,
i128, env, i64, i128, i32)
DEF_HELPER_FLAGS_4(atomic_fetch_ando_le, TCG_CALL_NO_WG,
i128, env, i64, i128, i32)
DEF_HELPER_FLAGS_4(atomic_fetch_oro_be, TCG_CALL_NO_WG,
i128, env, i64, i128, i32)
DEF_HELPER_FLAGS_4(atomic_fetch_oro_le, TCG_CALL_NO_WG,
i128, env, i64, i128, i32)
#endif
DEF_HELPER_FLAGS_5(nonatomic_cmpxchgo, TCG_CALL_NO_WG,
-219
View File
@@ -1,219 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* QEMU TCG statistics
*
* Copyright (c) 2003-2005 Fabrice Bellard
*/
#include "qemu/osdep.h"
#include "qemu/accel.h"
#include "qemu/qht.h"
#include "qapi/error.h"
#include "system/cpu-timers.h"
#include "exec/icount.h"
#include "hw/core/cpu.h"
#include "tcg/tcg.h"
#include "internal-common.h"
#include "tb-context.h"
#include <math.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");
}
}
static void dump_accel_info(AccelState *accel, GString *buf)
{
bool one_insn_per_tb = object_property_get_bool(OBJECT(accel),
"one-insn-per-tb",
&error_fatal);
g_string_append_printf(buf, "Accelerator settings:\n");
g_string_append_printf(buf, "one-insn-per-tb: %s\n\n",
one_insn_per_tb ? "on" : "off");
}
static void print_qht_statistics(struct qht_stats hst, GString *buf)
{
uint32_t hgram_opts;
size_t hgram_bins;
char *hgram;
double avg;
if (!hst.head_buckets) {
return;
}
g_string_append_printf(buf, "TB hash buckets %zu/%zu "
"(%0.2f%% head buckets used)\n",
hst.used_head_buckets, hst.head_buckets,
(double)hst.used_head_buckets /
hst.head_buckets * 100);
hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS;
hgram_opts |= QDIST_PR_100X | QDIST_PR_PERCENT;
if (qdist_xmax(&hst.occupancy) - qdist_xmin(&hst.occupancy) == 1) {
hgram_opts |= QDIST_PR_NODECIMAL;
}
hgram = qdist_pr(&hst.occupancy, 10, hgram_opts);
avg = qdist_avg(&hst.occupancy);
if (!isnan(avg)) {
g_string_append_printf(buf, "TB hash occupancy "
"%0.2f%% avg chain occ. "
"Histogram: %s\n",
avg * 100, hgram);
}
g_free(hgram);
hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS;
hgram_bins = qdist_xmax(&hst.chain) - qdist_xmin(&hst.chain);
if (hgram_bins > 10) {
hgram_bins = 10;
} else {
hgram_bins = 0;
hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE;
}
hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts);
avg = qdist_avg(&hst.chain);
if (!isnan(avg)) {
g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. "
"Histogram: %s\n",
avg, hgram);
}
g_free(hgram);
}
struct tb_tree_stats {
size_t nb_tbs;
size_t host_size;
size_t target_size;
size_t max_target_size;
size_t direct_jmp_count;
size_t direct_jmp2_count;
size_t cross_page;
};
static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data)
{
const TranslationBlock *tb = value;
struct tb_tree_stats *tst = data;
tst->nb_tbs++;
tst->host_size += tb->tc.size;
tst->target_size += tb->size;
if (tb->size > tst->max_target_size) {
tst->max_target_size = tb->size;
}
#ifndef CONFIG_USER_ONLY
if (tb->page_addr[1] != -1) {
tst->cross_page++;
}
#endif
if (tb->jmp_reset_offset[0] != TB_JMP_OFFSET_INVALID) {
tst->direct_jmp_count++;
if (tb->jmp_reset_offset[1] != TB_JMP_OFFSET_INVALID) {
tst->direct_jmp2_count++;
}
}
return false;
}
static void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide)
{
CPUState *cpu;
size_t full = 0, part = 0, elide = 0;
CPU_FOREACH(cpu) {
full += qatomic_read(&cpu->neg.tlb.c.full_flush_count);
part += qatomic_read(&cpu->neg.tlb.c.part_flush_count);
elide += qatomic_read(&cpu->neg.tlb.c.elide_flush_count);
}
*pfull = full;
*ppart = part;
*pelide = elide;
}
static void tcg_dump_flush_info(GString *buf)
{
size_t flush_full, flush_part, flush_elide;
g_string_append_printf(buf, "TB flush count %u\n",
qatomic_read(&tb_ctx.tb_flush_count));
g_string_append_printf(buf, "TB invalidate count %u\n",
qatomic_read(&tb_ctx.tb_phys_invalidate_count));
tlb_flush_counts(&flush_full, &flush_part, &flush_elide);
g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full);
g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part);
g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide);
}
static void dump_exec_info(GString *buf)
{
struct tb_tree_stats tst = {};
struct qht_stats hst;
size_t nb_tbs;
tcg_tb_foreach(tb_tree_stats_iter, &tst);
nb_tbs = tst.nb_tbs;
/* XXX: avoid using doubles ? */
g_string_append_printf(buf, "Translation buffer state:\n");
/*
* Report total code size including the padding and TB structs;
* otherwise users might think "-accel tcg,tb-size" is not honoured.
* For avg host size we use the precise numbers from tb_tree_stats though.
*/
g_string_append_printf(buf, "gen code size %zu/%zu\n",
tcg_code_size(), tcg_code_capacity());
g_string_append_printf(buf, "TB count %zu\n", nb_tbs);
g_string_append_printf(buf, "TB avg target size %zu max=%zu bytes\n",
nb_tbs ? tst.target_size / nb_tbs : 0,
tst.max_target_size);
g_string_append_printf(buf, "TB avg host size %zu bytes "
"(expansion ratio: %0.1f)\n",
nb_tbs ? tst.host_size / nb_tbs : 0,
tst.target_size ?
(double)tst.host_size / tst.target_size : 0);
g_string_append_printf(buf, "cross page TB count %zu (%zu%%)\n",
tst.cross_page,
nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0);
g_string_append_printf(buf, "direct jump count %zu (%zu%%) "
"(2 jumps=%zu %zu%%)\n",
tst.direct_jmp_count,
nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0,
tst.direct_jmp2_count,
nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0);
qht_statistics_init(&tb_ctx.htable, &hst);
print_qht_statistics(hst, buf);
qht_statistics_destroy(&hst);
g_string_append_printf(buf, "\nStatistics:\n");
tcg_dump_flush_info(buf);
}
void tcg_get_stats(AccelState *accel, GString *buf)
{
dump_accel_info(accel, buf);
dump_exec_info(buf);
dump_drift_info(buf);
}
void tcg_dump_stats(GString *buf)
{
tcg_get_stats(current_accel(), buf);
}
-13
View File
@@ -1,13 +0,0 @@
/*
* softmmu size bounds
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef ACCEL_TCG_TLB_BOUNDS_H
#define ACCEL_TCG_TLB_BOUNDS_H
#define CPU_TLB_DYN_MIN_BITS 6
#define CPU_TLB_DYN_MAX_BITS (32 - TARGET_PAGE_BITS)
#define CPU_TLB_DYN_DEFAULT_BITS 8
#endif /* ACCEL_TCG_TLB_BOUNDS_H */
-4
View File
@@ -12,7 +12,6 @@ memory_notdirty_set_dirty(uint64_t vaddr) "0x%" PRIx64
# translate-all.c
translate_block(void *tb, uintptr_t pc, const void *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p"
tb_gen_code_buffer_overflow(const char *reason) "reason: %s"
# ldst_atomicity
load_atom2_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR""
@@ -25,6 +24,3 @@ store_atom2_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIx
store_atom4_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR""
store_atom8_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR""
store_atom16_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR""
# tb-maint.c
tb_flush(void) ""
+76 -38
View File
@@ -21,20 +21,49 @@
#include "trace.h"
#include "disas/disas.h"
#include "exec/exec-all.h"
#include "tcg/tcg.h"
#include "exec/mmap-lock.h"
#if defined(CONFIG_USER_ONLY)
#include "qemu.h"
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#include <sys/param.h>
#if __FreeBSD_version >= 700104
#define HAVE_KINFO_GETVMMAP
#define sigqueue sigqueue_freebsd /* avoid redefinition */
#include <sys/proc.h>
#include <machine/profile.h>
#define _KERNEL
#include <sys/user.h>
#undef _KERNEL
#undef sigqueue
#include <libutil.h>
#endif
#endif
#else
#include "exec/ram_addr.h"
#endif
#include "exec/cputlb.h"
#include "exec/page-protection.h"
#include "tb-internal.h"
#include "exec/translator.h"
#include "exec/tb-flush.h"
#include "qemu/bitmap.h"
#include "qemu/qemu-print.h"
#include "qemu/main-loop.h"
#include "qemu/cacheinfo.h"
#include "qemu/target-info.h"
#include "qemu/timer.h"
#include "exec/log.h"
#include "exec/icount.h"
#include "system/cpu-timers.h"
#include "system/tcg.h"
#include "qapi/error.h"
#include "accel/tcg/cpu-ops.h"
#include "tb-jmp-cache.h"
#include "tb-hash.h"
#include "tb-context.h"
#include "tb-internal.h"
#include "internal-common.h"
#include "internal-target.h"
#include "tcg/perf.h"
#include "tcg/insn-start-words.h"
@@ -88,7 +117,7 @@ static int64_t decode_sleb128(const uint8_t **pp)
/* Encode the data collected about the instructions while compiling TB.
Place the data at BLOCK, and return the number of bytes consumed.
The logical table consists of INSN_START_WORDS uint64_t's,
The logical table consists of TARGET_INSN_START_WORDS target_ulong's,
which come from the target's insn_start data, followed by a uintptr_t
which comes from the host pc of the end of the code implementing the insn.
@@ -108,13 +137,13 @@ static int encode_search(TranslationBlock *tb, uint8_t *block)
for (i = 0, n = tb->icount; i < n; ++i) {
uint64_t prev, curr;
for (j = 0; j < INSN_START_WORDS; ++j) {
for (j = 0; j < TARGET_INSN_START_WORDS; ++j) {
if (i == 0) {
prev = (!(tb_cflags(tb) & CF_PCREL) && j == 0 ? tb->pc : 0);
} else {
prev = insn_data[(i - 1) * INSN_START_WORDS + j];
prev = insn_data[(i - 1) * TARGET_INSN_START_WORDS + j];
}
curr = insn_data[i * INSN_START_WORDS + j];
curr = insn_data[i * TARGET_INSN_START_WORDS + j];
p = encode_sleb128(p, curr - prev);
}
prev = (i == 0 ? 0 : insn_end_off[i - 1]);
@@ -146,7 +175,7 @@ static int cpu_unwind_data_from_tb(TranslationBlock *tb, uintptr_t host_pc,
return -1;
}
memset(data, 0, sizeof(uint64_t) * INSN_START_WORDS);
memset(data, 0, sizeof(uint64_t) * TARGET_INSN_START_WORDS);
if (!(tb_cflags(tb) & CF_PCREL)) {
data[0] = tb->pc;
}
@@ -156,7 +185,7 @@ static int cpu_unwind_data_from_tb(TranslationBlock *tb, uintptr_t host_pc,
* at which the end of the insn exceeds host_pc.
*/
for (i = 0; i < num_insns; ++i) {
for (j = 0; j < INSN_START_WORDS; ++j) {
for (j = 0; j < TARGET_INSN_START_WORDS; ++j) {
data[j] += decode_sleb128(&p);
}
iter_pc += decode_sleb128(&p);
@@ -174,7 +203,7 @@ static int cpu_unwind_data_from_tb(TranslationBlock *tb, uintptr_t host_pc,
void cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,
uintptr_t host_pc)
{
uint64_t data[INSN_START_WORDS];
uint64_t data[TARGET_INSN_START_WORDS];
int insns_left = cpu_unwind_data_from_tb(tb, host_pc, data);
if (insns_left < 0) {
@@ -258,7 +287,9 @@ static int setjmp_gen_code(CPUArchState *env, TranslationBlock *tb,
}
/* Called with mmap_lock held for user mode emulation. */
TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s)
TranslationBlock *tb_gen_code(CPUState *cpu,
vaddr pc, uint64_t cs_base,
uint32_t flags, int cflags)
{
CPUArchState *env = cpu_env(cpu);
TranslationBlock *tb, *existing_tb;
@@ -271,14 +302,14 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s)
assert_memory_lock();
qemu_thread_jit_write();
phys_pc = get_page_addr_code_hostp(env, s.pc, &host_pc);
phys_pc = get_page_addr_code_hostp(env, pc, &host_pc);
if (phys_pc == -1) {
/* Generate a one-shot TB with 1 insn in it */
s.cflags = (s.cflags & ~CF_COUNT_MASK) | 1;
cflags = (cflags & ~CF_COUNT_MASK) | 1;
}
max_insns = s.cflags & CF_COUNT_MASK;
max_insns = cflags & CF_COUNT_MASK;
if (max_insns == 0) {
max_insns = TCG_MAX_INSNS;
}
@@ -289,12 +320,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s)
tb = tcg_tb_alloc(tcg_ctx);
if (unlikely(!tb)) {
/* flush must be done */
if (cpu_in_serial_context(cpu)) {
trace_tb_gen_code_buffer_overflow("tcg_tb_alloc");
tb_flush__exclusive_or_serial();
goto buffer_overflow;
}
queue_tb_flush(cpu);
tb_flush(cpu);
mmap_unlock();
/* Make the execution loop process the flush as soon as possible. */
cpu->exception_index = EXCP_INTERRUPT;
@@ -303,12 +329,12 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s)
gen_code_buf = tcg_ctx->code_gen_ptr;
tb->tc.ptr = tcg_splitwx_to_rx(gen_code_buf);
if (!(s.cflags & CF_PCREL)) {
tb->pc = s.pc;
if (!(cflags & CF_PCREL)) {
tb->pc = pc;
}
tb->cs_base = s.cs_base;
tb->flags = s.flags;
tb->cflags = s.cflags;
tb->cs_base = cs_base;
tb->flags = flags;
tb->cflags = cflags;
tb_set_page_addr0(tb, phys_pc);
tb_set_page_addr1(tb, -1);
if (phys_pc != -1) {
@@ -316,17 +342,26 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s)
}
tcg_ctx->gen_tb = tb;
tcg_ctx->addr_type = target_long_bits() == 32 ? TCG_TYPE_I32 : TCG_TYPE_I64;
tcg_ctx->guest_mo = cpu->cc->tcg_ops->guest_default_memory_order;
tcg_ctx->addr_type = TARGET_LONG_BITS == 32 ? TCG_TYPE_I32 : TCG_TYPE_I64;
#ifdef CONFIG_SOFTMMU
tcg_ctx->page_bits = TARGET_PAGE_BITS;
tcg_ctx->page_mask = TARGET_PAGE_MASK;
tcg_ctx->tlb_dyn_max_bits = CPU_TLB_DYN_MAX_BITS;
#endif
tcg_ctx->insn_start_words = TARGET_INSN_START_WORDS;
#ifdef TCG_GUEST_DEFAULT_MO
tcg_ctx->guest_mo = TCG_GUEST_DEFAULT_MO;
#else
tcg_ctx->guest_mo = TCG_MO_ALL;
#endif
restart_translate:
trace_translate_block(tb, s.pc, tb->tc.ptr);
trace_translate_block(tb, pc, tb->tc.ptr);
gen_code_size = setjmp_gen_code(env, tb, s.pc, host_pc, &max_insns, &ti);
gen_code_size = setjmp_gen_code(env, tb, pc, host_pc, &max_insns, &ti);
if (unlikely(gen_code_size < 0)) {
switch (gen_code_size) {
case -1:
trace_tb_gen_code_buffer_overflow("setjmp_gen_code");
/*
* Overflow of code_gen_buffer, or the current slice of it.
*
@@ -391,7 +426,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s)
search_size = encode_search(tb, (void *)gen_code_buf + gen_code_size);
if (unlikely(search_size < 0)) {
trace_tb_gen_code_buffer_overflow("encode_search");
tb_unlock_pages(tb);
goto buffer_overflow;
}
@@ -401,10 +435,10 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s)
* For CF_PCREL, attribute all executions of the generated code
* to its first mapping.
*/
perf_report_code(s.pc, tb, tcg_splitwx_to_rx(gen_code_buf));
perf_report_code(pc, tb, tcg_splitwx_to_rx(gen_code_buf));
if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM) &&
qemu_log_in_addr_range(s.pc)) {
qemu_log_in_addr_range(pc)) {
FILE *logfile = qemu_log_trylock();
if (logfile) {
int code_size, data_size;
@@ -426,7 +460,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s)
fprintf(logfile, "OUT: [size=%d]\n", gen_code_size);
fprintf(logfile,
" -- guest addr 0x%016" PRIx64 " + tb prologue\n",
tcg_ctx->gen_insn_data[insn * INSN_START_WORDS]);
tcg_ctx->gen_insn_data[insn * TARGET_INSN_START_WORDS]);
chunk_start = tcg_ctx->gen_insn_end_off[insn];
disas(logfile, tb->tc.ptr, chunk_start);
@@ -439,7 +473,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s)
size_t chunk_end = tcg_ctx->gen_insn_end_off[insn];
if (chunk_end > chunk_start) {
fprintf(logfile, " -- guest addr 0x%016" PRIx64 "\n",
tcg_ctx->gen_insn_data[insn * INSN_START_WORDS]);
tcg_ctx->gen_insn_data[insn * TARGET_INSN_START_WORDS]);
disas(logfile, tb->tc.ptr + chunk_start,
chunk_end - chunk_start);
chunk_start = chunk_end;
@@ -557,11 +591,15 @@ void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr)
/* The exception probably happened in a helper. The CPU state should
have been saved before calling it. Fetch the PC from there. */
CPUArchState *env = cpu_env(cpu);
TCGTBCPUState s = cpu->cc->tcg_ops->get_tb_cpu_state(cpu);
tb_page_addr_t addr = get_page_addr_code(env, s.pc);
vaddr pc;
uint64_t cs_base;
tb_page_addr_t addr;
uint32_t flags;
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
addr = get_page_addr_code(env, pc);
if (addr != -1) {
tb_invalidate_phys_range(cpu, addr, addr);
tb_invalidate_phys_range(addr, addr);
}
}
}
+47 -88
View File
@@ -8,16 +8,16 @@
*/
#include "qemu/osdep.h"
#include "qemu/bswap.h"
#include "qemu/log.h"
#include "qemu/error-report.h"
#include "accel/tcg/cpu-ldst-common.h"
#include "accel/tcg/cpu-mmu-index.h"
#include "exec/target_page.h"
#include "exec/exec-all.h"
#include "exec/translator.h"
#include "exec/cpu_ldst.h"
#include "exec/plugin-gen.h"
#include "exec/cpu_ldst.h"
#include "exec/tswap.h"
#include "tcg/tcg-op-common.h"
#include "internal-common.h"
#include "internal-target.h"
#include "disas/disas.h"
#include "tb-internal.h"
@@ -25,7 +25,8 @@ static void set_can_do_io(DisasContextBase *db, bool val)
{
QEMU_BUILD_BUG_ON(sizeof_field(CPUState, neg.can_do_io) != 1);
tcg_gen_st8_i32(tcg_constant_i32(val), tcg_env,
offsetof(CPUState, neg.can_do_io) - sizeof(CPUState));
offsetof(ArchCPU, parent_obj.neg.can_do_io) -
offsetof(ArchCPU, env));
}
bool translator_io_start(DisasContextBase *db)
@@ -48,8 +49,8 @@ static TCGOp *gen_tb_start(DisasContextBase *db, uint32_t cflags)
if ((cflags & CF_USE_ICOUNT) || !(cflags & CF_NOIRQ)) {
count = tcg_temp_new_i32();
tcg_gen_ld_i32(count, tcg_env,
offsetof(CPUState, neg.icount_decr.u32) -
sizeof(CPUState));
offsetof(ArchCPU, parent_obj.neg.icount_decr.u32)
- offsetof(ArchCPU, env));
}
if (cflags & CF_USE_ICOUNT) {
@@ -78,8 +79,8 @@ static TCGOp *gen_tb_start(DisasContextBase *db, uint32_t cflags)
if (cflags & CF_USE_ICOUNT) {
tcg_gen_st16_i32(count, tcg_env,
offsetof(CPUState, neg.icount_decr.u16.low) -
sizeof(CPUState));
offsetof(ArchCPU, parent_obj.neg.icount_decr.u16.low)
- offsetof(ArchCPU, env));
}
return icount_start_insn;
@@ -141,7 +142,6 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
db->host_addr[1] = NULL;
db->record_start = 0;
db->record_len = 0;
db->code_mmuidx = cpu_mmu_index(cpu, true);
ops->init_disas_context(db, cpu);
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
@@ -265,14 +265,12 @@ static bool translator_ld(CPUArchState *env, DisasContextBase *db,
if (likely(((base ^ last) & TARGET_PAGE_MASK) == 0)) {
/* Entire read is from the first page. */
goto do_read;
memcpy(dest, host + (pc - base), len);
return true;
}
if (unlikely(((base ^ pc) & TARGET_PAGE_MASK) == 0)) {
/*
* Read begins on the first page and extends to the second.
* The unaligned read is never atomic.
*/
/* Read begins on the first page and extends to the second. */
size_t len0 = -(pc | TARGET_PAGE_MASK);
memcpy(dest, host + (pc - base), len0);
pc += len0;
@@ -331,39 +329,7 @@ static bool translator_ld(CPUArchState *env, DisasContextBase *db,
host = db->host_addr[1];
}
do_read:
/*
* Assume aligned reads should be atomic, if possible.
* We're not in a position to jump out with EXCP_ATOMIC.
*/
host += pc - base;
switch (len) {
case 2:
if (QEMU_IS_ALIGNED(pc, 2)) {
uint16_t t = qatomic_read((uint16_t *)host);
stw_he_p(dest, t);
return true;
}
break;
case 4:
if (QEMU_IS_ALIGNED(pc, 4)) {
uint32_t t = qatomic_read((uint32_t *)host);
stl_he_p(dest, t);
return true;
}
break;
#ifdef CONFIG_ATOMIC64
case 8:
if (QEMU_IS_ALIGNED(pc, 8)) {
uint64_t t = qatomic_read__nocheck((uint64_t *)host);
stq_he_p(dest, t);
return true;
}
break;
#endif
}
/* Unaligned or partial read from the second page is not atomic. */
memcpy(dest, host, len);
memcpy(dest, host + (pc - base), len);
return true;
}
@@ -457,62 +423,55 @@ bool translator_st(const DisasContextBase *db, void *dest,
uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, vaddr pc)
{
uint8_t val;
uint8_t raw;
if (!translator_ld(env, db, &val, pc, sizeof(val))) {
MemOpIdx oi = make_memop_idx(MO_UB, db->code_mmuidx);
val = cpu_ldb_code_mmu(env, pc, oi, 0);
record_save(db, pc, &val, sizeof(val));
if (!translator_ld(env, db, &raw, pc, sizeof(raw))) {
raw = cpu_ldub_code(env, pc);
record_save(db, pc, &raw, sizeof(raw));
}
return val;
return raw;
}
uint16_t translator_lduw_end(CPUArchState *env, DisasContextBase *db,
vaddr pc, MemOp endian)
uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, vaddr pc)
{
uint16_t val;
uint16_t raw, tgt;
if (!translator_ld(env, db, &val, pc, sizeof(val))) {
MemOpIdx oi = make_memop_idx(MO_UW, db->code_mmuidx);
val = cpu_ldw_code_mmu(env, pc, oi, 0);
record_save(db, pc, &val, sizeof(val));
if (translator_ld(env, db, &raw, pc, sizeof(raw))) {
tgt = tswap16(raw);
} else {
tgt = cpu_lduw_code(env, pc);
raw = tswap16(tgt);
record_save(db, pc, &raw, sizeof(raw));
}
if (endian & MO_BSWAP) {
val = bswap16(val);
}
return val;
return tgt;
}
uint32_t translator_ldl_end(CPUArchState *env, DisasContextBase *db,
vaddr pc, MemOp endian)
uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, vaddr pc)
{
uint32_t val;
uint32_t raw, tgt;
if (!translator_ld(env, db, &val, pc, sizeof(val))) {
MemOpIdx oi = make_memop_idx(MO_UL, db->code_mmuidx);
val = cpu_ldl_code_mmu(env, pc, oi, 0);
record_save(db, pc, &val, sizeof(val));
if (translator_ld(env, db, &raw, pc, sizeof(raw))) {
tgt = tswap32(raw);
} else {
tgt = cpu_ldl_code(env, pc);
raw = tswap32(tgt);
record_save(db, pc, &raw, sizeof(raw));
}
if (endian & MO_BSWAP) {
val = bswap32(val);
}
return val;
return tgt;
}
uint64_t translator_ldq_end(CPUArchState *env, DisasContextBase *db,
vaddr pc, MemOp endian)
uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc)
{
uint64_t val;
uint64_t raw, tgt;
if (!translator_ld(env, db, &val, pc, sizeof(val))) {
MemOpIdx oi = make_memop_idx(MO_UQ, db->code_mmuidx);
val = cpu_ldq_code_mmu(env, pc, oi, 0);
record_save(db, pc, &val, sizeof(val));
if (translator_ld(env, db, &raw, pc, sizeof(raw))) {
tgt = tswap64(raw);
} else {
tgt = cpu_ldq_code(env, pc);
raw = tswap64(tgt);
record_save(db, pc, &raw, sizeof(raw));
}
if (endian & MO_BSWAP) {
val = bswap64(val);
}
return val;
return tgt;
}
void translator_fake_ld(DisasContextBase *db, const void *data, size_t len)
+240 -140
View File
@@ -20,42 +20,34 @@
#include "accel/tcg/cpu-ops.h"
#include "disas/disas.h"
#include "exec/vaddr.h"
#include "exec/tlb-flags.h"
#include "exec/exec-all.h"
#include "tcg/tcg.h"
#include "qemu/bitops.h"
#include "qemu/rcu.h"
#include "accel/tcg/cpu-ldst-common.h"
#include "accel/tcg/helper-retaddr.h"
#include "accel/tcg/probe.h"
#include "exec/cpu_ldst.h"
#include "user/cpu_loop.h"
#include "user/guest-host.h"
#include "qemu/main-loop.h"
#include "user/page-protection.h"
#include "exec/page-protection.h"
#include "exec/helper-proto-common.h"
#include "exec/helper-proto.h"
#include "qemu/atomic128.h"
#include "qemu/bswap.h"
#include "qemu/int128.h"
#include "trace.h"
#include "tcg/tcg-ldst.h"
#include "tcg-accel-ops.h"
#include "backend-ldst.h"
#include "internal-common.h"
#include "internal-target.h"
#include "tb-internal.h"
__thread uintptr_t helper_retaddr;
//#define DEBUG_SIGNAL
void qemu_cpu_kick(CPUState *cpu)
void cpu_interrupt(CPUState *cpu, int mask)
{
tcg_kick_vcpu_thread(cpu);
}
void qemu_process_cpu_events(CPUState *cpu)
{
qatomic_set(&cpu->exit_request, false);
process_queued_cpu_work(cpu);
g_assert(bql_locked());
cpu->interrupt_request |= mask;
qatomic_set(&cpu->neg.icount_decr.u16.high, -1);
}
/*
@@ -131,9 +123,9 @@ MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write)
* guest, we'd end up in an infinite loop of retrying the faulting access.
*/
bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set,
uintptr_t host_pc, vaddr guest_addr)
uintptr_t host_pc, abi_ptr guest_addr)
{
switch (page_unprotect(cpu, guest_addr, host_pc)) {
switch (page_unprotect(guest_addr, host_pc)) {
case 0:
/*
* Fault not caused by a page marked unwritable to protect
@@ -167,7 +159,7 @@ typedef struct PageFlagsNode {
static IntervalTreeRoot pageflags_root;
static PageFlagsNode *pageflags_find(vaddr start, vaddr last)
static PageFlagsNode *pageflags_find(target_ulong start, target_ulong last)
{
IntervalTreeNode *n;
@@ -175,7 +167,8 @@ static PageFlagsNode *pageflags_find(vaddr start, vaddr last)
return n ? container_of(n, PageFlagsNode, itree) : NULL;
}
static PageFlagsNode *pageflags_next(PageFlagsNode *p, vaddr start, vaddr last)
static PageFlagsNode *pageflags_next(PageFlagsNode *p, target_ulong start,
target_ulong last)
{
IntervalTreeNode *n;
@@ -204,22 +197,13 @@ int walk_memory_regions(void *priv, walk_memory_regions_fn fn)
return rc;
}
static int dump_region(void *opaque, vaddr start, vaddr end, int prot)
static int dump_region(void *priv, target_ulong start,
target_ulong end, unsigned long prot)
{
FILE *f = opaque;
uint64_t mask;
int width;
FILE *f = (FILE *)priv;
if (guest_addr_max <= UINT32_MAX) {
mask = UINT32_MAX, width = 8;
} else {
mask = UINT64_MAX, width = 16;
}
fprintf(f, "%0*" PRIx64 "-%0*" PRIx64 " %0*" PRIx64 " %c%c%c\n",
width, start & mask,
width, end & mask,
width, (end - start) & mask,
fprintf(f, TARGET_FMT_lx"-"TARGET_FMT_lx" "TARGET_FMT_lx" %c%c%c\n",
start, end, end - start,
((prot & PAGE_READ) ? 'r' : '-'),
((prot & PAGE_WRITE) ? 'w' : '-'),
((prot & PAGE_EXEC) ? 'x' : '-'));
@@ -229,14 +213,14 @@ static int dump_region(void *opaque, vaddr start, vaddr end, int prot)
/* dump memory mappings */
void page_dump(FILE *f)
{
int width = guest_addr_max <= UINT32_MAX ? 8 : 16;
const int length = sizeof(target_ulong) * 2;
fprintf(f, "%-*s %-*s %-*s %s\n",
width, "start", width, "end", width, "size", "prot");
length, "start", length, "end", length, "size", "prot");
walk_memory_regions(f, dump_region);
}
int page_get_flags(vaddr address)
int page_get_flags(target_ulong address)
{
PageFlagsNode *p = pageflags_find(address, address);
@@ -259,7 +243,7 @@ int page_get_flags(vaddr address)
}
/* A subroutine of page_set_flags: insert a new node for [start,last]. */
static void pageflags_create(vaddr start, vaddr last, int flags)
static void pageflags_create(target_ulong start, target_ulong last, int flags)
{
PageFlagsNode *p = g_new(PageFlagsNode, 1);
@@ -269,11 +253,54 @@ static void pageflags_create(vaddr start, vaddr last, int flags)
interval_tree_insert(&p->itree, &pageflags_root);
}
/* A subroutine of page_set_flags: remove everything in [start,last]. */
static bool pageflags_unset(target_ulong start, target_ulong last)
{
bool inval_tb = false;
while (true) {
PageFlagsNode *p = pageflags_find(start, last);
target_ulong p_last;
if (!p) {
break;
}
if (p->flags & PAGE_EXEC) {
inval_tb = true;
}
interval_tree_remove(&p->itree, &pageflags_root);
p_last = p->itree.last;
if (p->itree.start < start) {
/* Truncate the node from the end, or split out the middle. */
p->itree.last = start - 1;
interval_tree_insert(&p->itree, &pageflags_root);
if (last < p_last) {
pageflags_create(last + 1, p_last, p->flags);
break;
}
} else if (p_last <= last) {
/* Range completely covers node -- remove it. */
g_free_rcu(p, rcu);
} else {
/* Truncate the node from the start. */
p->itree.start = last + 1;
interval_tree_insert(&p->itree, &pageflags_root);
break;
}
}
return inval_tb;
}
/*
* A subroutine of page_set_flags: nothing overlaps [start,last],
* but check adjacent mappings and maybe merge into a single range.
*/
static void pageflags_create_merge(vaddr start, vaddr last, int flags)
static void pageflags_create_merge(target_ulong start, target_ulong last,
int flags)
{
PageFlagsNode *next = NULL, *prev = NULL;
@@ -314,19 +341,28 @@ static void pageflags_create_merge(vaddr start, vaddr last, int flags)
}
}
/*
* Allow the target to decide if PAGE_TARGET_[12] may be reset.
* By default, they are not kept.
*/
#ifndef PAGE_TARGET_STICKY
#define PAGE_TARGET_STICKY 0
#endif
#define PAGE_STICKY (PAGE_ANON | PAGE_PASSTHROUGH | PAGE_TARGET_STICKY)
/* A subroutine of page_set_flags: add flags to [start,last]. */
static bool pageflags_set_clear(vaddr start, vaddr last,
static bool pageflags_set_clear(target_ulong start, target_ulong last,
int set_flags, int clear_flags)
{
PageFlagsNode *p;
vaddr p_start, p_last;
target_ulong p_start, p_last;
int p_flags, merge_flags;
bool inval_tb = false;
restart:
p = pageflags_find(start, last);
if (!p) {
if (set_flags & PAGE_VALID) {
if (set_flags) {
pageflags_create_merge(start, last, set_flags);
}
goto done;
@@ -340,12 +376,11 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
/*
* Need to flush if an overlapping executable region
* removes exec, adds write, or is a new mapping.
* removes exec, or adds write.
*/
if ((p_flags & PAGE_EXEC)
&& (!(merge_flags & PAGE_EXEC)
|| (merge_flags & ~p_flags & PAGE_WRITE)
|| (clear_flags & PAGE_VALID))) {
|| (merge_flags & ~p_flags & PAGE_WRITE))) {
inval_tb = true;
}
@@ -354,7 +389,7 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
* attempting to merge with adjacent regions.
*/
if (start == p_start && last == p_last) {
if (merge_flags & PAGE_VALID) {
if (merge_flags) {
p->flags = merge_flags;
} else {
interval_tree_remove(&p->itree, &pageflags_root);
@@ -374,12 +409,12 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
interval_tree_insert(&p->itree, &pageflags_root);
if (last < p_last) {
if (merge_flags & PAGE_VALID) {
if (merge_flags) {
pageflags_create(start, last, merge_flags);
}
pageflags_create(last + 1, p_last, p_flags);
} else {
if (merge_flags & PAGE_VALID) {
if (merge_flags) {
pageflags_create(start, p_last, merge_flags);
}
if (p_last < last) {
@@ -388,18 +423,18 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
}
}
} else {
if (start < p_start && (set_flags & PAGE_VALID)) {
if (start < p_start && set_flags) {
pageflags_create(start, p_start - 1, set_flags);
}
if (last < p_last) {
interval_tree_remove(&p->itree, &pageflags_root);
p->itree.start = last + 1;
interval_tree_insert(&p->itree, &pageflags_root);
if (merge_flags & PAGE_VALID) {
if (merge_flags) {
pageflags_create(start, last, merge_flags);
}
} else {
if (merge_flags & PAGE_VALID) {
if (merge_flags) {
p->flags = merge_flags;
} else {
interval_tree_remove(&p->itree, &pageflags_root);
@@ -447,7 +482,7 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
g_free_rcu(p, rcu);
goto restart;
}
if (set_flags & PAGE_VALID) {
if (set_flags) {
pageflags_create(start, last, set_flags);
}
@@ -455,43 +490,49 @@ static bool pageflags_set_clear(vaddr start, vaddr last,
return inval_tb;
}
void page_set_flags(vaddr start, vaddr last, int set_flags, int clear_flags)
void page_set_flags(target_ulong start, target_ulong last, int flags)
{
/*
* This function should never be called with addresses outside the
* guest address space. If this assert fires, it probably indicates
* a missing call to h2g_valid.
*/
bool reset = false;
bool inval_tb = false;
/* This function should never be called with addresses outside the
guest address space. If this assert fires, it probably indicates
a missing call to h2g_valid. */
assert(start <= last);
assert(last <= guest_addr_max);
assert(last <= GUEST_ADDR_MAX);
/* Only set PAGE_ANON with new mappings. */
assert(!(flags & PAGE_ANON) || (flags & PAGE_RESET));
assert_memory_lock();
start &= TARGET_PAGE_MASK;
last |= ~TARGET_PAGE_MASK;
if (set_flags & PAGE_WRITE) {
set_flags |= PAGE_WRITE_ORG;
}
if (clear_flags & PAGE_WRITE) {
clear_flags |= PAGE_WRITE_ORG;
}
if (clear_flags & PAGE_VALID) {
page_reset_target_data(start, last);
clear_flags = -1;
if (!(flags & PAGE_VALID)) {
flags = 0;
} else {
/* Only set PAGE_ANON with new mappings. */
assert(!(set_flags & PAGE_ANON));
reset = flags & PAGE_RESET;
flags &= ~PAGE_RESET;
if (flags & PAGE_WRITE) {
flags |= PAGE_WRITE_ORG;
}
}
if (pageflags_set_clear(start, last, set_flags, clear_flags)) {
tb_invalidate_phys_range(NULL, start, last);
if (!flags || reset) {
page_reset_target_data(start, last);
inval_tb |= pageflags_unset(start, last);
}
if (flags) {
inval_tb |= pageflags_set_clear(start, last, flags,
~(reset ? 0 : PAGE_STICKY));
}
if (inval_tb) {
tb_invalidate_phys_range(start, last);
}
}
bool page_check_range(vaddr start, vaddr len, int flags)
bool page_check_range(target_ulong start, target_ulong len, int flags)
{
vaddr last;
target_ulong last;
int locked; /* tri-state: =0: unlocked, +1: global, -1: local */
bool ret;
@@ -540,7 +581,7 @@ bool page_check_range(vaddr start, vaddr len, int flags)
break;
}
/* Asking about writable, but has been protected: undo. */
if (!page_unprotect(NULL, start, 0)) {
if (!page_unprotect(start, 0)) {
ret = false;
break;
}
@@ -567,19 +608,20 @@ bool page_check_range(vaddr start, vaddr len, int flags)
return ret;
}
bool page_check_range_empty(vaddr start, vaddr last)
bool page_check_range_empty(target_ulong start, target_ulong last)
{
assert(last >= start);
assert_memory_lock();
return pageflags_find(start, last) == NULL;
}
vaddr page_find_range_empty(vaddr min, vaddr max, vaddr len, vaddr align)
target_ulong page_find_range_empty(target_ulong min, target_ulong max,
target_ulong len, target_ulong align)
{
vaddr len_m1, align_m1;
target_ulong len_m1, align_m1;
assert(min <= max);
assert(max <= guest_addr_max);
assert(max <= GUEST_ADDR_MAX);
assert(len != 0);
assert(is_power_of_2(align));
assert_memory_lock();
@@ -614,10 +656,10 @@ vaddr page_find_range_empty(vaddr min, vaddr max, vaddr len, vaddr align)
}
}
void tb_lock_page0(tb_page_addr_t address)
void page_protect(tb_page_addr_t address)
{
PageFlagsNode *p;
vaddr start, last;
target_ulong start, last;
int host_page_size = qemu_real_host_page_size();
int prot;
@@ -659,13 +701,11 @@ void tb_lock_page0(tb_page_addr_t address)
* immediately exited. (We can only return 2 if the 'pc' argument is
* non-zero.)
*/
int page_unprotect(CPUState *cpu, tb_page_addr_t address, uintptr_t pc)
int page_unprotect(tb_page_addr_t address, uintptr_t pc)
{
PageFlagsNode *p;
bool current_tb_invalidated;
assert((cpu == NULL) == (pc == 0));
/*
* Technically this isn't safe inside a signal handler. However we
* know this only ever happens in a synchronous SEGV handler, so in
@@ -688,15 +728,15 @@ int page_unprotect(CPUState *cpu, tb_page_addr_t address, uintptr_t pc)
* this thread raced with another one which got here first and
* set the page to PAGE_WRITE and did the TB invalidate for us.
*/
if (pc && cpu->cc->tcg_ops->precise_smc) {
TranslationBlock *current_tb = tcg_tb_lookup(pc);
if (current_tb) {
current_tb_invalidated = tb_cflags(current_tb) & CF_INVALID;
}
#ifdef TARGET_HAS_PRECISE_SMC
TranslationBlock *current_tb = tcg_tb_lookup(pc);
if (current_tb) {
current_tb_invalidated = tb_cflags(current_tb) & CF_INVALID;
}
#endif
} else {
int host_page_size = qemu_real_host_page_size();
vaddr start, len, i;
target_ulong start, len, i;
int prot;
if (host_page_size <= TARGET_PAGE_SIZE) {
@@ -704,15 +744,14 @@ int page_unprotect(CPUState *cpu, tb_page_addr_t address, uintptr_t pc)
len = TARGET_PAGE_SIZE;
prot = p->flags | PAGE_WRITE;
pageflags_set_clear(start, start + len - 1, PAGE_WRITE, 0);
current_tb_invalidated =
tb_invalidate_phys_page_unwind(cpu, start, pc);
current_tb_invalidated = tb_invalidate_phys_page_unwind(start, pc);
} else {
start = address & -host_page_size;
len = host_page_size;
prot = 0;
for (i = 0; i < len; i += TARGET_PAGE_SIZE) {
vaddr addr = start + i;
target_ulong addr = start + i;
p = pageflags_find(addr, addr);
if (p) {
@@ -728,7 +767,7 @@ int page_unprotect(CPUState *cpu, tb_page_addr_t address, uintptr_t pc)
* the corresponding translated code.
*/
current_tb_invalidated |=
tb_invalidate_phys_page_unwind(cpu, addr, pc);
tb_invalidate_phys_page_unwind(addr, pc);
}
}
if (prot & PAGE_EXEC) {
@@ -808,12 +847,6 @@ void *probe_access(CPUArchState *env, vaddr addr, int size,
return size ? g2h(env_cpu(env), addr) : NULL;
}
void *tlb_vaddr_to_host(CPUArchState *env, vaddr addr,
MMUAccessType access_type, int mmu_idx)
{
return g2h(env_cpu(env), addr);
}
tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr,
void **hostp)
{
@@ -828,6 +861,7 @@ tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr,
return addr;
}
#ifdef TARGET_PAGE_DATA_SIZE
/*
* Allocate chunks of target data together. For the only current user,
* if we allocate one hunk per page, we have overhead of 40/128 or 40%.
@@ -843,16 +877,10 @@ typedef struct TargetPageDataNode {
} TargetPageDataNode;
static IntervalTreeRoot targetdata_root;
static size_t target_page_data_size;
void page_reset_target_data(vaddr start, vaddr last)
void page_reset_target_data(target_ulong start, target_ulong last)
{
IntervalTreeNode *n, *next;
size_t size = target_page_data_size;
if (likely(size == 0)) {
return;
}
assert_memory_lock();
@@ -864,7 +892,7 @@ void page_reset_target_data(vaddr start, vaddr last)
n != NULL;
n = next,
next = next ? interval_tree_iter_next(n, start, last) : NULL) {
vaddr n_start, n_last, p_ofs, p_len;
target_ulong n_start, n_last, p_ofs, p_len;
TargetPageDataNode *t = container_of(n, TargetPageDataNode, itree);
if (n->start >= start && n->last <= last) {
@@ -883,21 +911,16 @@ void page_reset_target_data(vaddr start, vaddr last)
n_last = MIN(last, n->last);
p_len = (n_last + 1 - n_start) >> TARGET_PAGE_BITS;
memset(t->data + p_ofs * size, 0, p_len * size);
memset(t->data + p_ofs * TARGET_PAGE_DATA_SIZE, 0,
p_len * TARGET_PAGE_DATA_SIZE);
}
}
void *page_get_target_data(vaddr address, size_t size)
void *page_get_target_data(target_ulong address)
{
IntervalTreeNode *n;
TargetPageDataNode *t;
vaddr page, region, p_ofs;
/* Remember the size from the first call, and it should be constant. */
if (unlikely(target_page_data_size != size)) {
assert(target_page_data_size == 0);
target_page_data_size = size;
}
target_ulong page, region, p_ofs;
page = address & TARGET_PAGE_MASK;
region = address & TBD_MASK;
@@ -913,7 +936,8 @@ void *page_get_target_data(vaddr address, size_t size)
mmap_lock();
n = interval_tree_iter_first(&targetdata_root, page, page);
if (!n) {
t = g_malloc0(sizeof(TargetPageDataNode) + TPD_PAGES * size);
t = g_malloc0(sizeof(TargetPageDataNode)
+ TPD_PAGES * TARGET_PAGE_DATA_SIZE);
n = &t->itree;
n->start = region;
n->last = region | ~TBD_MASK;
@@ -924,8 +948,11 @@ void *page_get_target_data(vaddr address, size_t size)
t = container_of(n, TargetPageDataNode, itree);
p_ofs = (page - region) >> TARGET_PAGE_BITS;
return t->data + p_ofs * size;
return t->data + p_ofs * TARGET_PAGE_DATA_SIZE;
}
#else
void page_reset_target_data(target_ulong start, target_ulong last) { }
#endif /* TARGET_PAGE_DATA_SIZE */
/* The system-mode versions of these helpers are in cputlb.c. */
@@ -987,7 +1014,7 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr,
* be under mmap_lock() in order to prevent the creation of
* another TranslationBlock in between.
*/
tb_invalidate_phys_range(NULL, addr, addr + l - 1);
tb_invalidate_phys_range(addr, addr + l - 1);
written = pwrite(fd, buf, l,
(off_t)(uintptr_t)g2h_untagged(addr));
if (written != l) {
@@ -1032,7 +1059,7 @@ static uint8_t do_ld1_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi,
void *haddr;
uint8_t ret;
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
haddr = cpu_mmu_lookup(cpu, addr, get_memop(oi), ra, access_type);
ret = ldub_p(haddr);
clear_helper_retaddr();
@@ -1046,7 +1073,7 @@ static uint16_t do_ld2_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi,
uint16_t ret;
MemOp mop = get_memop(oi);
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
haddr = cpu_mmu_lookup(cpu, addr, mop, ra, access_type);
ret = load_atom_2(cpu, ra, haddr, mop);
clear_helper_retaddr();
@@ -1064,7 +1091,7 @@ static uint32_t do_ld4_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi,
uint32_t ret;
MemOp mop = get_memop(oi);
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
haddr = cpu_mmu_lookup(cpu, addr, mop, ra, access_type);
ret = load_atom_4(cpu, ra, haddr, mop);
clear_helper_retaddr();
@@ -1082,7 +1109,7 @@ static uint64_t do_ld8_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi,
uint64_t ret;
MemOp mop = get_memop(oi);
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
haddr = cpu_mmu_lookup(cpu, addr, mop, ra, access_type);
ret = load_atom_8(cpu, ra, haddr, mop);
clear_helper_retaddr();
@@ -1093,7 +1120,7 @@ static uint64_t do_ld8_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi,
return ret;
}
static Int128 do_ld16_mmu(CPUState *cpu, vaddr addr,
static Int128 do_ld16_mmu(CPUState *cpu, abi_ptr addr,
MemOpIdx oi, uintptr_t ra)
{
void *haddr;
@@ -1101,7 +1128,7 @@ static Int128 do_ld16_mmu(CPUState *cpu, vaddr addr,
MemOp mop = get_memop(oi);
tcg_debug_assert((mop & MO_SIZE) == MO_128);
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_LOAD);
ret = load_atom_16(cpu, ra, haddr, mop);
clear_helper_retaddr();
@@ -1117,7 +1144,7 @@ static void do_st1_mmu(CPUState *cpu, vaddr addr, uint8_t val,
{
void *haddr;
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
haddr = cpu_mmu_lookup(cpu, addr, get_memop(oi), ra, MMU_DATA_STORE);
stb_p(haddr, val);
clear_helper_retaddr();
@@ -1129,7 +1156,7 @@ static void do_st2_mmu(CPUState *cpu, vaddr addr, uint16_t val,
void *haddr;
MemOp mop = get_memop(oi);
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_STORE);
if (mop & MO_BSWAP) {
@@ -1145,7 +1172,7 @@ static void do_st4_mmu(CPUState *cpu, vaddr addr, uint32_t val,
void *haddr;
MemOp mop = get_memop(oi);
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_STORE);
if (mop & MO_BSWAP) {
@@ -1161,7 +1188,7 @@ static void do_st8_mmu(CPUState *cpu, vaddr addr, uint64_t val,
void *haddr;
MemOp mop = get_memop(oi);
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_STORE);
if (mop & MO_BSWAP) {
@@ -1177,7 +1204,7 @@ static void do_st16_mmu(CPUState *cpu, vaddr addr, Int128 val,
void *haddr;
MemOpIdx mop = get_memop(oi);
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_STORE);
if (mop & MO_BSWAP) {
@@ -1187,28 +1214,101 @@ static void do_st16_mmu(CPUState *cpu, vaddr addr, Int128 val,
clear_helper_retaddr();
}
uint8_t cpu_ldb_code_mmu(CPUArchState *env, vaddr addr,
uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr ptr)
{
uint32_t ret;
set_helper_retaddr(1);
ret = ldub_p(g2h_untagged(ptr));
clear_helper_retaddr();
return ret;
}
uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr ptr)
{
uint32_t ret;
set_helper_retaddr(1);
ret = lduw_p(g2h_untagged(ptr));
clear_helper_retaddr();
return ret;
}
uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr ptr)
{
uint32_t ret;
set_helper_retaddr(1);
ret = ldl_p(g2h_untagged(ptr));
clear_helper_retaddr();
return ret;
}
uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr ptr)
{
uint64_t ret;
set_helper_retaddr(1);
ret = ldq_p(g2h_untagged(ptr));
clear_helper_retaddr();
return ret;
}
uint8_t cpu_ldb_code_mmu(CPUArchState *env, abi_ptr addr,
MemOpIdx oi, uintptr_t ra)
{
return do_ld1_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH);
void *haddr;
uint8_t ret;
haddr = cpu_mmu_lookup(env_cpu(env), addr, oi, ra, MMU_INST_FETCH);
ret = ldub_p(haddr);
clear_helper_retaddr();
return ret;
}
uint16_t cpu_ldw_code_mmu(CPUArchState *env, vaddr addr,
uint16_t cpu_ldw_code_mmu(CPUArchState *env, abi_ptr addr,
MemOpIdx oi, uintptr_t ra)
{
return do_ld2_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH);
void *haddr;
uint16_t ret;
haddr = cpu_mmu_lookup(env_cpu(env), addr, oi, ra, MMU_INST_FETCH);
ret = lduw_p(haddr);
clear_helper_retaddr();
if (get_memop(oi) & MO_BSWAP) {
ret = bswap16(ret);
}
return ret;
}
uint32_t cpu_ldl_code_mmu(CPUArchState *env, vaddr addr,
uint32_t cpu_ldl_code_mmu(CPUArchState *env, abi_ptr addr,
MemOpIdx oi, uintptr_t ra)
{
return do_ld4_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH);
void *haddr;
uint32_t ret;
haddr = cpu_mmu_lookup(env_cpu(env), addr, oi, ra, MMU_INST_FETCH);
ret = ldl_p(haddr);
clear_helper_retaddr();
if (get_memop(oi) & MO_BSWAP) {
ret = bswap32(ret);
}
return ret;
}
uint64_t cpu_ldq_code_mmu(CPUArchState *env, vaddr addr,
uint64_t cpu_ldq_code_mmu(CPUArchState *env, abi_ptr addr,
MemOpIdx oi, uintptr_t ra)
{
return do_ld8_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH);
void *haddr;
uint64_t ret;
haddr = cpu_mmu_lookup(env_cpu(env), addr, oi, ra, MMU_DATA_LOAD);
ret = ldq_p(haddr);
clear_helper_retaddr();
if (get_memop(oi) & MO_BSWAP) {
ret = bswap64(ret);
}
return ret;
}
#include "ldst_common.c.inc"
+3
View File
@@ -124,14 +124,17 @@ void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len,
}
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_NOIRQ | curr_cflags(cpu);
mmap_unlock();
cpu_loop_exit_noexc(cpu);
}
} else {
+5 -8
View File
@@ -18,9 +18,7 @@
#include "hw/xen/xen_igd.h"
#include "chardev/char.h"
#include "qemu/accel.h"
#include "accel/dummy-cpus.h"
#include "accel/accel-ops.h"
#include "accel/accel-cpu-ops.h"
#include "system/accel-ops.h"
#include "system/cpus.h"
#include "system/xen.h"
#include "system/runstate.h"
@@ -65,7 +63,7 @@ static void xen_set_igd_gfx_passthru(Object *obj, bool value, Error **errp)
xen_igd_gfx_pt_set(value, errp);
}
static void xen_setup_post(AccelState *as)
static void xen_setup_post(MachineState *ms, AccelState *accel)
{
int rc;
@@ -78,7 +76,7 @@ static void xen_setup_post(AccelState *as)
}
}
static int xen_init(AccelState *as, MachineState *ms)
static int xen_init(MachineState *ms)
{
MachineClass *mc = MACHINE_GET_CLASS(ms);
@@ -118,7 +116,7 @@ static int xen_init(AccelState *as, MachineState *ms)
return 0;
}
static void xen_accel_class_init(ObjectClass *oc, const void *data)
static void xen_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
static GlobalProperty compat[] = {
@@ -149,12 +147,11 @@ static const TypeInfo xen_accel_type = {
.class_init = xen_accel_class_init,
};
static void xen_accel_ops_class_init(ObjectClass *oc, const void *data)
static void xen_accel_ops_class_init(ObjectClass *oc, void *data)
{
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
ops->create_vcpu_thread = dummy_start_vcpu_thread;
ops->handle_interrupt = generic_handle_interrupt;
}
static const TypeInfo xen_accel_ops_type = {
+30 -9
View File
@@ -26,7 +26,7 @@
#include <alsa/asoundlib.h>
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/audio.h"
#include "audio.h"
#include "trace.h"
#pragma GCC diagnostic ignored "-Waddress"
@@ -41,7 +41,7 @@ struct pollhlp {
struct pollfd *pfds;
int count;
int mask;
AudioBackend *s;
AudioState *s;
};
typedef struct ALSAVoiceOut {
@@ -264,7 +264,7 @@ static int alsa_poll_in (HWVoiceIn *hw)
return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLIN);
}
static snd_pcm_format_t aud_to_alsafmt(AudioFormat fmt, bool big_endian)
static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
{
switch (fmt) {
case AUDIO_FORMAT_S8:
@@ -274,19 +274,39 @@ static snd_pcm_format_t aud_to_alsafmt(AudioFormat fmt, bool big_endian)
return SND_PCM_FORMAT_U8;
case AUDIO_FORMAT_S16:
return big_endian ? SND_PCM_FORMAT_S16_BE : SND_PCM_FORMAT_S16_LE;
if (endianness) {
return SND_PCM_FORMAT_S16_BE;
} else {
return SND_PCM_FORMAT_S16_LE;
}
case AUDIO_FORMAT_U16:
return big_endian ? SND_PCM_FORMAT_U16_BE : SND_PCM_FORMAT_U16_LE;
if (endianness) {
return SND_PCM_FORMAT_U16_BE;
} else {
return SND_PCM_FORMAT_U16_LE;
}
case AUDIO_FORMAT_S32:
return big_endian ? SND_PCM_FORMAT_S32_BE : SND_PCM_FORMAT_S32_LE;
if (endianness) {
return SND_PCM_FORMAT_S32_BE;
} else {
return SND_PCM_FORMAT_S32_LE;
}
case AUDIO_FORMAT_U32:
return big_endian ? SND_PCM_FORMAT_U32_BE : SND_PCM_FORMAT_U32_LE;
if (endianness) {
return SND_PCM_FORMAT_U32_BE;
} else {
return SND_PCM_FORMAT_U32_LE;
}
case AUDIO_FORMAT_F32:
return big_endian ? SND_PCM_FORMAT_FLOAT_BE : SND_PCM_FORMAT_FLOAT_LE;
if (endianness) {
return SND_PCM_FORMAT_FLOAT_BE;
} else {
return SND_PCM_FORMAT_FLOAT_LE;
}
default:
dolog ("Internal logic error: Bad audio format %d\n", fmt);
@@ -879,7 +899,7 @@ static void alsa_enable_in(HWVoiceIn *hw, bool enable)
static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo)
{
if (!apdo->has_try_poll) {
apdo->try_poll = false;
apdo->try_poll = true;
apdo->has_try_poll = true;
}
}
@@ -936,6 +956,7 @@ static struct audio_pcm_ops alsa_pcm_ops = {
static struct audio_driver alsa_audio_driver = {
.name = "alsa",
.descr = "ALSA http://www.alsa-project.org",
.init = alsa_audio_init,
.fini = alsa_audio_fini,
.pcm_ops = &alsa_pcm_ops,
+2 -9
View File
@@ -23,12 +23,11 @@
*/
#include "qemu/osdep.h"
#include "audio_int.h"
#include "audio/audio.h"
#include "monitor/hmp.h"
#include "monitor/monitor.h"
#include "qapi/error.h"
#include "qobject/qdict.h"
#include "qemu/error-report.h"
static QLIST_HEAD (capture_list_head, CaptureState) capture_head;
@@ -37,8 +36,6 @@ void hmp_info_capture(Monitor *mon, const QDict *qdict)
int i;
CaptureState *s;
warn_report_once("'info capture' is deprecated since v10.2, to be removed");
for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
monitor_printf(mon, "[%d]: ", i);
s->ops.info (s->opaque);
@@ -51,8 +48,6 @@ void hmp_stopcapture(Monitor *mon, const QDict *qdict)
int n = qdict_get_int(qdict, "n");
CaptureState *s;
warn_report_once("'stopcapture' is deprecated since v10.2, to be removed");
for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
if (i == n) {
s->ops.destroy (s->opaque);
@@ -72,9 +67,7 @@ void hmp_wavcapture(Monitor *mon, const QDict *qdict)
const char *audiodev = qdict_get_str(qdict, "audiodev");
CaptureState *s;
Error *local_err = NULL;
AudioBackend *as = audio_be_by_name(audiodev, &local_err);
warn_report_once("'wavcapture' is deprecated since v10.2, to be removed");
AudioState *as = audio_state_by_name(audiodev, &local_err);
if (!as) {
error_report_err(local_err);
+159 -179
View File
@@ -23,8 +23,9 @@
*/
#include "qemu/osdep.h"
#include "qemu/audio.h"
#include "audio.h"
#include "migration/vmstate.h"
#include "monitor/monitor.h"
#include "qemu/timer.h"
#include "qapi/error.h"
#include "qapi/clone-visitor.h"
@@ -32,6 +33,7 @@
#include "qapi/qapi-visit-audio.h"
#include "qapi/qapi-commands-audio.h"
#include "qobject/qdict.h"
#include "qemu/cutils.h"
#include "qemu/error-report.h"
#include "qemu/log.h"
#include "qemu/module.h"
@@ -39,6 +41,7 @@
#include "system/system.h"
#include "system/replay.h"
#include "system/runstate.h"
#include "ui/qemu-spice.h"
#include "trace.h"
#define AUDIO_CAP "audio"
@@ -99,7 +102,9 @@ static audio_driver *audio_driver_lookup(const char *name)
return NULL;
}
static AudioBackend *default_audio_be;
static QTAILQ_HEAD(AudioStateHead, AudioState) audio_states =
QTAILQ_HEAD_INITIALIZER(audio_states);
static AudioState *default_audio_state;
const struct mixeng_volume nominal_volume = {
.mute = 0,
@@ -274,7 +279,7 @@ static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *a
&& info->is_signed == is_signed
&& info->is_float == is_float
&& info->bits == bits
&& info->swap_endianness == (as->endianness != HOST_BIG_ENDIAN);
&& info->swap_endianness == (as->endianness != AUDIO_HOST_ENDIANNESS);
}
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
@@ -320,7 +325,7 @@ void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
info->nchannels = as->nchannels;
info->bytes_per_frame = as->nchannels * mul;
info->bytes_per_second = info->freq * info->bytes_per_frame;
info->swap_endianness = (as->endianness != HOST_BIG_ENDIAN);
info->swap_endianness = (as->endianness != AUDIO_HOST_ENDIANNESS);
}
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
@@ -380,7 +385,7 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
/*
* Capture
*/
static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioBackend *s,
static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioState *s,
struct audsettings *as)
{
CaptureVoiceOut *cap;
@@ -405,7 +410,7 @@ static void audio_notify_capture (CaptureVoiceOut *cap, audcnotification_e cmd)
}
}
static void audio_capture_maybe_changed(CaptureVoiceOut *cap, bool enabled)
static void audio_capture_maybe_changed (CaptureVoiceOut *cap, int enabled)
{
if (cap->hw.enabled != enabled) {
audcnotification_e cmd;
@@ -419,11 +424,11 @@ static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap)
{
HWVoiceOut *hw = &cap->hw;
SWVoiceOut *sw;
bool enabled = false;
int enabled = 0;
for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
if (sw->active) {
enabled = true;
enabled = 1;
break;
}
}
@@ -460,7 +465,7 @@ static void audio_detach_capture (HWVoiceOut *hw)
static int audio_attach_capture (HWVoiceOut *hw)
{
AudioBackend *s = hw->s;
AudioState *s = hw->s;
CaptureVoiceOut *cap;
audio_detach_capture (hw);
@@ -475,7 +480,7 @@ static int audio_attach_capture (HWVoiceOut *hw)
sw = &sc->sw;
sw->hw = hw_cap;
sw->info = hw->info;
sw->empty = true;
sw->empty = 1;
sw->active = hw->enabled;
sw->vol = nominal_volume;
sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq);
@@ -798,7 +803,7 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
/*
* Timer
*/
static int audio_is_timer_needed(AudioBackend *s)
static int audio_is_timer_needed(AudioState *s)
{
HWVoiceIn *hwi = NULL;
HWVoiceOut *hwo = NULL;
@@ -816,7 +821,7 @@ static int audio_is_timer_needed(AudioBackend *s)
return 0;
}
static void audio_reset_timer(AudioBackend *s)
static void audio_reset_timer (AudioState *s)
{
if (audio_is_timer_needed(s)) {
timer_mod_anticipate_ns(s->ts,
@@ -838,7 +843,7 @@ static void audio_reset_timer(AudioBackend *s)
static void audio_timer (void *opaque)
{
int64_t now, diff;
AudioBackend *s = opaque;
AudioState *s = opaque;
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
diff = now - s->timer_last;
@@ -900,18 +905,10 @@ size_t AUD_read(SWVoiceIn *sw, void *buf, size_t size)
int AUD_get_buffer_size_out(SWVoiceOut *sw)
{
if (!sw) {
return 0;
}
if (audio_get_pdo_out(sw->s->dev)->mixing_engine) {
return sw->resample_buf.size * sw->info.bytes_per_frame;
}
return sw->hw->samples * sw->hw->info.bytes_per_frame;
}
void AUD_set_active_out(SWVoiceOut *sw, bool on)
void AUD_set_active_out (SWVoiceOut *sw, int on)
{
HWVoiceOut *hw;
@@ -921,14 +918,14 @@ void AUD_set_active_out(SWVoiceOut *sw, bool on)
hw = sw->hw;
if (sw->active != on) {
AudioBackend *s = sw->s;
AudioState *s = sw->s;
SWVoiceOut *temp_sw;
SWVoiceCap *sc;
if (on) {
hw->pending_disable = 0;
if (!hw->enabled) {
hw->enabled = true;
hw->enabled = 1;
if (s->vm_running) {
if (hw->pcm_ops->enable_out) {
hw->pcm_ops->enable_out(hw, true);
@@ -959,7 +956,7 @@ void AUD_set_active_out(SWVoiceOut *sw, bool on)
}
}
void AUD_set_active_in(SWVoiceIn *sw, bool on)
void AUD_set_active_in (SWVoiceIn *sw, int on)
{
HWVoiceIn *hw;
@@ -969,12 +966,12 @@ void AUD_set_active_in(SWVoiceIn *sw, bool on)
hw = sw->hw;
if (sw->active != on) {
AudioBackend *s = sw->s;
AudioState *s = sw->s;
SWVoiceIn *temp_sw;
if (on) {
if (!hw->enabled) {
hw->enabled = true;
hw->enabled = 1;
if (s->vm_running) {
if (hw->pcm_ops->enable_in) {
hw->pcm_ops->enable_in(hw, true);
@@ -993,7 +990,7 @@ void AUD_set_active_in(SWVoiceIn *sw, bool on)
}
if (nb_active == 1) {
hw->enabled = false;
hw->enabled = 0;
if (hw->pcm_ops->enable_in) {
hw->pcm_ops->enable_in(hw, false);
}
@@ -1137,7 +1134,7 @@ static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live)
return clipped;
}
static void audio_run_out(AudioBackend *s)
static void audio_run_out (AudioState *s)
{
HWVoiceOut *hw = NULL;
SWVoiceOut *sw;
@@ -1152,8 +1149,8 @@ static void audio_run_out(AudioBackend *s)
sw = hw->sw_head.lh_first;
if (hw->pending_disable) {
hw->enabled = false;
hw->pending_disable = false;
hw->enabled = 0;
hw->pending_disable = 0;
if (hw->pcm_ops->enable_out) {
hw->pcm_ops->enable_out(hw, false);
}
@@ -1206,13 +1203,13 @@ static void audio_run_out(AudioBackend *s)
#ifdef DEBUG_OUT
dolog ("Disabling voice\n");
#endif
hw->enabled = false;
hw->pending_disable = false;
hw->enabled = 0;
hw->pending_disable = 0;
if (hw->pcm_ops->enable_out) {
hw->pcm_ops->enable_out(hw, false);
}
for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
sc->sw.active = false;
sc->sw.active = 0;
audio_recalc_and_notify_capture (sc->cap);
}
continue;
@@ -1257,7 +1254,7 @@ static void audio_run_out(AudioBackend *s)
sw->total_hw_samples_mixed -= played;
if (!sw->total_hw_samples_mixed) {
sw->empty = true;
sw->empty = 1;
}
}
}
@@ -1291,7 +1288,7 @@ static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)
return conv;
}
static void audio_run_in(AudioBackend *s)
static void audio_run_in (AudioState *s)
{
HWVoiceIn *hw = NULL;
@@ -1339,7 +1336,7 @@ static void audio_run_in(AudioBackend *s)
}
}
static void audio_run_capture(AudioBackend *s)
static void audio_run_capture (AudioState *s)
{
CaptureVoiceOut *cap;
@@ -1386,7 +1383,7 @@ static void audio_run_capture(AudioBackend *s)
}
}
void audio_run(AudioBackend *s, const char *msg)
void audio_run(AudioState *s, const char *msg)
{
audio_run_out(s);
audio_run_in(s);
@@ -1559,40 +1556,41 @@ size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size)
return total;
}
static bool audio_driver_init(AudioBackend *s, struct audio_driver *drv,
Audiodev *dev, Error **errp)
static int audio_driver_init(AudioState *s, struct audio_driver *drv,
Audiodev *dev, Error **errp)
{
s->drv_opaque = drv->init(dev, errp);
if (!s->drv_opaque) {
return false;
}
Error *local_err = NULL;
if (!drv->pcm_ops->get_buffer_in) {
drv->pcm_ops->get_buffer_in = audio_generic_get_buffer_in;
drv->pcm_ops->put_buffer_in = audio_generic_put_buffer_in;
}
if (!drv->pcm_ops->get_buffer_out) {
drv->pcm_ops->get_buffer_out = audio_generic_get_buffer_out;
drv->pcm_ops->put_buffer_out = audio_generic_put_buffer_out;
}
s->drv_opaque = drv->init(dev, &local_err);
audio_init_nb_voices_out(s, drv, 1);
audio_init_nb_voices_in(s, drv, 0);
s->drv = drv;
if (s->drv_opaque) {
if (!drv->pcm_ops->get_buffer_in) {
drv->pcm_ops->get_buffer_in = audio_generic_get_buffer_in;
drv->pcm_ops->put_buffer_in = audio_generic_put_buffer_in;
}
if (!drv->pcm_ops->get_buffer_out) {
drv->pcm_ops->get_buffer_out = audio_generic_get_buffer_out;
drv->pcm_ops->put_buffer_out = audio_generic_put_buffer_out;
}
if (dev->timer_period <= 0) {
s->period_ticks = 1;
audio_init_nb_voices_out(s, drv, 1);
audio_init_nb_voices_in(s, drv, 0);
s->drv = drv;
return 0;
} else {
s->period_ticks = dev->timer_period * (int64_t)SCALE_US;
if (local_err) {
error_propagate(errp, local_err);
} else {
error_setg(errp, "Could not init `%s' audio driver", drv->name);
}
return -1;
}
return true;
}
static void audio_vm_change_state_handler (void *opaque, bool running,
RunState state)
{
AudioBackend *s = opaque;
AudioState *s = opaque;
HWVoiceOut *hwo = NULL;
HWVoiceIn *hwi = NULL;
@@ -1611,26 +1609,8 @@ static void audio_vm_change_state_handler (void *opaque, bool running,
audio_reset_timer (s);
}
static const VMStateDescription vmstate_audio;
static void audio_be_init(Object *obj)
static void free_audio_state(AudioState *s)
{
AudioBackend *s = AUDIO_BACKEND(obj);
QLIST_INIT(&s->hw_head_out);
QLIST_INIT(&s->hw_head_in);
QLIST_INIT(&s->cap_head);
s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);
s->vmse = qemu_add_vm_change_state_handler(audio_vm_change_state_handler, s);
assert(s->vmse != NULL);
vmstate_register_any(NULL, &vmstate_audio, s);
}
static void audio_be_finalize(Object *obj)
{
AudioBackend *s = AUDIO_BACKEND(obj);
HWVoiceOut *hwo, *hwon;
HWVoiceIn *hwi, *hwin;
@@ -1676,24 +1656,17 @@ static void audio_be_finalize(Object *obj)
s->ts = NULL;
}
if (s->vmse) {
qemu_del_vm_change_state_handler(s->vmse);
s->vmse = NULL;
}
vmstate_unregister(NULL, &vmstate_audio, s);
}
static Object *get_audiodevs_root(void)
{
return object_get_container("audiodevs");
g_free(s);
}
void audio_cleanup(void)
{
default_audio_be = NULL;
object_unparent(get_audiodevs_root());
default_audio_state = NULL;
while (!QTAILQ_EMPTY(&audio_states)) {
AudioState *s = QTAILQ_FIRST(&audio_states);
QTAILQ_REMOVE(&audio_states, s, list);
free_audio_state(s);
}
}
static bool vmstate_audio_needed(void *opaque)
@@ -1731,7 +1704,7 @@ void audio_create_default_audiodevs(void)
visit_type_Audiodev(v, NULL, &dev, &error_fatal);
visit_free(v);
audio_add_default_audiodev(dev, &error_abort);
audio_define_default(dev, &error_abort);
}
}
}
@@ -1742,14 +1715,26 @@ void audio_create_default_audiodevs(void)
* if dev == NULL => legacy implicit initialization, return the already created
* state or create a new one
*/
static AudioBackend *audio_init(Audiodev *dev, Error **errp)
static AudioState *audio_init(Audiodev *dev, Error **errp)
{
static bool atexit_registered;
int done = 0;
const char *drvname;
AudioBackend *s;
VMChangeStateEntry *vmse;
AudioState *s;
struct audio_driver *driver;
s = AUDIO_BACKEND(object_new(TYPE_AUDIO_BACKEND));
s = g_new0(AudioState, 1);
QLIST_INIT (&s->hw_head_out);
QLIST_INIT (&s->hw_head_in);
QLIST_INIT (&s->cap_head);
if (!atexit_registered) {
atexit(audio_cleanup);
atexit_registered = true;
}
s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);
if (dev) {
/* -audiodev option */
@@ -1757,7 +1742,7 @@ static AudioBackend *audio_init(Audiodev *dev, Error **errp)
drvname = AudiodevDriver_str(dev->driver);
driver = audio_driver_lookup(drvname);
if (driver) {
done = audio_driver_init(s, driver, dev, errp);
done = !audio_driver_init(s, driver, dev, errp);
} else {
error_setg(errp, "Unknown audio driver `%s'", drvname);
}
@@ -1765,7 +1750,7 @@ static AudioBackend *audio_init(Audiodev *dev, Error **errp)
goto out;
}
} else {
assert(!default_audio_be);
assert(!default_audio_state);
for (;;) {
AudiodevListEntry *e = QSIMPLEQ_FIRST(&default_audiodevs);
if (!e) {
@@ -1777,7 +1762,7 @@ static AudioBackend *audio_init(Audiodev *dev, Error **errp)
g_free(e);
drvname = AudiodevDriver_str(dev->driver);
driver = audio_driver_lookup(drvname);
if (audio_driver_init(s, driver, dev, NULL)) {
if (!audio_driver_init(s, driver, dev, NULL)) {
break;
}
qapi_free_Audiodev(dev);
@@ -1785,22 +1770,33 @@ static AudioBackend *audio_init(Audiodev *dev, Error **errp)
}
}
if (!object_property_try_add_child(get_audiodevs_root(), dev->id, OBJECT(s), errp)) {
goto out;
if (dev->timer_period <= 0) {
s->period_ticks = 1;
} else {
s->period_ticks = dev->timer_period * (int64_t)SCALE_US;
}
object_unref(s);
vmse = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
if (!vmse) {
dolog ("warning: Could not register change state handler\n"
"(Audio can continue looping even after stopping the VM)\n");
}
QTAILQ_INSERT_TAIL(&audio_states, s, list);
QLIST_INIT (&s->card_head);
vmstate_register_any(NULL, &vmstate_audio, s);
return s;
out:
object_unref(s);
free_audio_state(s);
return NULL;
}
AudioBackend *audio_get_default_audio_be(Error **errp)
AudioState *audio_get_default_audio_state(Error **errp)
{
if (!default_audio_be) {
default_audio_be = audio_init(NULL, errp);
if (!default_audio_be) {
if (!default_audio_state) {
default_audio_state = audio_init(NULL, errp);
if (!default_audio_state) {
if (!QSIMPLEQ_EMPTY(&audiodevs)) {
error_append_hint(errp, "Perhaps you wanted to use -audio or set audiodev=%s?\n",
QSIMPLEQ_FIRST(&audiodevs)->dev->id);
@@ -1808,27 +1804,35 @@ AudioBackend *audio_get_default_audio_be(Error **errp)
}
}
return default_audio_be;
return default_audio_state;
}
bool AUD_backend_check(AudioBackend **be, Error **errp)
bool AUD_register_card (const char *name, QEMUSoundCard *card, Error **errp)
{
assert(be != NULL);
if (!*be) {
*be = audio_get_default_audio_be(errp);
if (!*be) {
if (!card->state) {
card->state = audio_get_default_audio_state(errp);
if (!card->state) {
return false;
}
}
card->name = g_strdup (name);
memset (&card->entries, 0, sizeof (card->entries));
QLIST_INSERT_HEAD(&card->state->card_head, card, entries);
return true;
}
void AUD_remove_card (QEMUSoundCard *card)
{
QLIST_REMOVE (card, entries);
g_free (card->name);
}
static struct audio_pcm_ops capture_pcm_ops;
CaptureVoiceOut *AUD_add_capture(
AudioBackend *s,
AudioState *s,
struct audsettings *as,
struct audio_capture_ops *ops,
void *cb_opaque
@@ -1880,8 +1884,7 @@ CaptureVoiceOut *AUD_add_capture(
cap->buf = g_malloc0_n(hw->mix_buf.size, hw->info.bytes_per_frame);
if (hw->info.is_float) {
hw->clip = mixeng_clip_float[hw->info.nchannels == 2]
[hw->info.swap_endianness];
hw->clip = mixeng_clip_float[hw->info.nchannels == 2];
} else {
hw->clip = mixeng_clip
[hw->info.nchannels == 2]
@@ -1940,7 +1943,13 @@ void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque)
}
}
void AUD_set_volume_out(SWVoiceOut *sw, Volume *vol)
void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol)
{
Volume vol = { .mute = mute, .channels = 2, .vol = { lvol, rvol } };
audio_set_volume_out(sw, &vol);
}
void audio_set_volume_out(SWVoiceOut *sw, Volume *vol)
{
if (sw) {
HWVoiceOut *hw = sw->hw;
@@ -1956,7 +1965,13 @@ void AUD_set_volume_out(SWVoiceOut *sw, Volume *vol)
}
}
void AUD_set_volume_in(SWVoiceIn *sw, Volume *vol)
void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
{
Volume vol = { .mute = mute, .channels = 2, .vol = { lvol, rvol } };
audio_set_volume_in(sw, &vol);
}
void audio_set_volume_in(SWVoiceIn *sw, Volume *vol)
{
if (sw) {
HWVoiceIn *hw = sw->hw;
@@ -2118,10 +2133,10 @@ void audio_parse_option(const char *opt)
visit_type_Audiodev(v, NULL, &dev, &error_fatal);
visit_free(v);
audio_add_audiodev(dev);
audio_define(dev);
}
void audio_add_audiodev(Audiodev *dev)
void audio_define(Audiodev *dev)
{
AudiodevListEntry *e;
@@ -2132,7 +2147,7 @@ void audio_add_audiodev(Audiodev *dev)
QSIMPLEQ_INSERT_TAIL(&audiodevs, e, next);
}
void audio_add_default_audiodev(Audiodev *dev, Error **errp)
void audio_define_default(Audiodev *dev, Error **errp)
{
AudiodevListEntry *e;
@@ -2158,7 +2173,7 @@ audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo)
.freq = pdo->frequency,
.nchannels = pdo->channels,
.fmt = pdo->format,
.endianness = HOST_BIG_ENDIAN,
.endianness = AUDIO_HOST_ENDIANNESS,
};
}
@@ -2211,40 +2226,24 @@ int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
audioformat_bytes_per_sample(as->fmt);
}
AudioBackend *audio_be_by_name(const char *name, Error **errp)
AudioState *audio_state_by_name(const char *name, Error **errp)
{
Object *obj = object_resolve_path_component(get_audiodevs_root(), name);
if (!obj) {
error_setg(errp, "audiodev '%s' not found", name);
return NULL;
} else {
return AUDIO_BACKEND(obj);
AudioState *s;
QTAILQ_FOREACH(s, &audio_states, list) {
assert(s->dev);
if (strcmp(name, s->dev->id) == 0) {
return s;
}
}
error_setg(errp, "audiodev '%s' not found", name);
return NULL;
}
#ifdef CONFIG_GIO
bool audio_be_set_dbus_server(AudioBackend *be,
GDBusObjectManagerServer *server,
bool p2p,
Error **errp)
const char *audio_get_id(QEMUSoundCard *card)
{
assert(be != NULL);
if (!be->drv->set_dbus_server) {
error_setg(errp, "Audiodev '%s' is not compatible with DBus", be->dev->id);
return false;
}
return be->drv->set_dbus_server(be, server, p2p, errp);
}
#endif
const char *audio_be_get_id(AudioBackend *be)
{
if (be) {
assert(be->dev);
return be->dev->id;
if (card->state) {
assert(card->state->dev);
return card->state->dev->id;
} else {
return "";
}
@@ -2275,19 +2274,17 @@ size_t audio_rate_peek_bytes(RateCtl *rate, struct audio_pcm_info *info)
ticks = now - rate->start_ticks;
bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND);
frames = (bytes - rate->bytes_sent) / info->bytes_per_frame;
rate->peeked_frames = frames;
if (frames < 0 || frames > 65536) {
AUD_log(NULL, "Resetting rate control (%" PRId64 " frames)\n", frames);
audio_rate_start(rate);
frames = 0;
}
return frames < 0 ? 0 : frames * info->bytes_per_frame;
return frames * info->bytes_per_frame;
}
void audio_rate_add_bytes(RateCtl *rate, size_t bytes_used)
{
if (rate->peeked_frames < 0 || rate->peeked_frames > 65536) {
AUD_log(NULL, "Resetting rate control (%" PRId64 " frames)\n",
rate->peeked_frames);
audio_rate_start(rate);
}
rate->bytes_sent += bytes_used;
}
@@ -2312,20 +2309,3 @@ AudiodevList *qmp_query_audiodevs(Error **errp)
}
return ret;
}
static const TypeInfo audio_be_info = {
.name = TYPE_AUDIO_BACKEND,
.parent = TYPE_OBJECT,
.instance_size = sizeof(AudioBackend),
.instance_init = audio_be_init,
.instance_finalize = audio_be_finalize,
.abstract = false, /* TODO: subclass drivers and make it abstract */
.class_size = sizeof(AudioBackendClass),
};
static void register_types(void)
{
type_register_static(&audio_be_info);
}
type_init(register_types);
+185
View File
@@ -0,0 +1,185 @@
/*
* QEMU Audio subsystem header
*
* Copyright (c) 2003-2005 Vassili Karpov (malc)
*
* 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.
*/
#ifndef QEMU_AUDIO_H
#define QEMU_AUDIO_H
#include "qemu/queue.h"
#include "qapi/qapi-types-audio.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
typedef void (*audio_callback_fn) (void *opaque, int avail);
#if HOST_BIG_ENDIAN
#define AUDIO_HOST_ENDIANNESS 1
#else
#define AUDIO_HOST_ENDIANNESS 0
#endif
typedef struct audsettings {
int freq;
int nchannels;
AudioFormat fmt;
int endianness;
} audsettings;
audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo);
int audioformat_bytes_per_sample(AudioFormat fmt);
int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
typedef enum {
AUD_CNOTIFY_ENABLE,
AUD_CNOTIFY_DISABLE
} audcnotification_e;
struct audio_capture_ops {
void (*notify) (void *opaque, audcnotification_e cmd);
void (*capture) (void *opaque, const void *buf, int size);
void (*destroy) (void *opaque);
};
struct capture_ops {
void (*info) (void *opaque);
void (*destroy) (void *opaque);
};
typedef struct CaptureState {
void *opaque;
struct capture_ops ops;
QLIST_ENTRY (CaptureState) entries;
} CaptureState;
typedef struct SWVoiceOut SWVoiceOut;
typedef struct CaptureVoiceOut CaptureVoiceOut;
typedef struct SWVoiceIn SWVoiceIn;
typedef struct AudioState AudioState;
typedef struct QEMUSoundCard {
char *name;
AudioState *state;
QLIST_ENTRY (QEMUSoundCard) entries;
} QEMUSoundCard;
typedef struct QEMUAudioTimeStamp {
uint64_t old_ts;
} QEMUAudioTimeStamp;
void AUD_vlog (const char *cap, const char *fmt, va_list ap) G_GNUC_PRINTF(2, 0);
void AUD_log (const char *cap, const char *fmt, ...) G_GNUC_PRINTF(2, 3);
bool AUD_register_card (const char *name, QEMUSoundCard *card, Error **errp);
void AUD_remove_card (QEMUSoundCard *card);
CaptureVoiceOut *AUD_add_capture(
AudioState *s,
struct audsettings *as,
struct audio_capture_ops *ops,
void *opaque
);
void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque);
SWVoiceOut *AUD_open_out (
QEMUSoundCard *card,
SWVoiceOut *sw,
const char *name,
void *callback_opaque,
audio_callback_fn callback_fn,
struct audsettings *settings
);
void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw);
size_t AUD_write (SWVoiceOut *sw, void *pcm_buf, size_t size);
int AUD_get_buffer_size_out (SWVoiceOut *sw);
void AUD_set_active_out (SWVoiceOut *sw, int on);
int AUD_is_active_out (SWVoiceOut *sw);
void AUD_init_time_stamp_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
uint64_t AUD_get_elapsed_usec_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol);
void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol);
#define AUDIO_MAX_CHANNELS 16
typedef struct Volume {
bool mute;
int channels;
uint8_t vol[AUDIO_MAX_CHANNELS];
} Volume;
void audio_set_volume_out(SWVoiceOut *sw, Volume *vol);
void audio_set_volume_in(SWVoiceIn *sw, Volume *vol);
SWVoiceIn *AUD_open_in (
QEMUSoundCard *card,
SWVoiceIn *sw,
const char *name,
void *callback_opaque,
audio_callback_fn callback_fn,
struct audsettings *settings
);
void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw);
size_t AUD_read (SWVoiceIn *sw, void *pcm_buf, size_t size);
void AUD_set_active_in (SWVoiceIn *sw, int on);
int AUD_is_active_in (SWVoiceIn *sw);
void AUD_init_time_stamp_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts);
uint64_t AUD_get_elapsed_usec_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts);
static inline void *advance (void *p, int incr)
{
uint8_t *d = p;
return (d + incr);
}
int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
int freq, int bits, int nchannels);
void audio_cleanup(void);
void audio_sample_to_uint64(const void *samples, int pos,
uint64_t *left, uint64_t *right);
void audio_sample_from_uint64(void *samples, int pos,
uint64_t left, uint64_t right);
void audio_define(Audiodev *audio);
void audio_define_default(Audiodev *dev, Error **errp);
void audio_parse_option(const char *opt);
void audio_create_default_audiodevs(void);
void audio_init_audiodevs(void);
void audio_help(void);
AudioState *audio_state_by_name(const char *name, Error **errp);
AudioState *audio_get_default_audio_state(Error **errp);
const char *audio_get_id(QEMUSoundCard *card);
#define DEFINE_AUDIO_PROPERTIES(_s, _f) \
DEFINE_PROP_AUDIODEV("audiodev", _s, _f)
#endif /* QEMU_AUDIO_H */
+21 -47
View File
@@ -29,20 +29,12 @@
#define FLOAT_MIXENG
/* #define RECIPROCAL */
#endif
#include "qemu/audio.h"
#include "qemu/audio-capture.h"
#include "mixeng.h"
#ifdef CONFIG_GIO
#include <gio/gio.h>
#endif
void G_GNUC_PRINTF(2, 0)
AUD_vlog(const char *cap, const char *fmt, va_list ap);
void G_GNUC_PRINTF(2, 3)
AUD_log(const char *cap, const char *fmt, ...);
struct audio_pcm_ops;
struct audio_callback {
@@ -61,7 +53,7 @@ struct audio_pcm_info {
int swap_endianness;
};
typedef struct AudioBackend AudioBackend;
typedef struct AudioState AudioState;
typedef struct SWVoiceCap SWVoiceCap;
typedef struct STSampleBuffer {
@@ -70,10 +62,10 @@ typedef struct STSampleBuffer {
} STSampleBuffer;
typedef struct HWVoiceOut {
AudioBackend *s;
bool enabled;
AudioState *s;
int enabled;
int poll_mode;
bool pending_disable;
int pending_disable;
struct audio_pcm_info info;
f_sample *clip;
@@ -91,8 +83,8 @@ typedef struct HWVoiceOut {
} HWVoiceOut;
typedef struct HWVoiceIn {
AudioBackend *s;
bool enabled;
AudioState *s;
int enabled;
int poll_mode;
struct audio_pcm_info info;
@@ -112,14 +104,15 @@ typedef struct HWVoiceIn {
} HWVoiceIn;
struct SWVoiceOut {
AudioBackend *s;
QEMUSoundCard *card;
AudioState *s;
struct audio_pcm_info info;
t_sample *conv;
STSampleBuffer resample_buf;
void *rate;
size_t total_hw_samples_mixed;
bool active;
bool empty;
int active;
int empty;
HWVoiceOut *hw;
char *name;
struct mixeng_volume vol;
@@ -128,8 +121,9 @@ struct SWVoiceOut {
};
struct SWVoiceIn {
AudioBackend *s;
bool active;
QEMUSoundCard *card;
AudioState *s;
int active;
struct audio_pcm_info info;
void *rate;
size_t total_hw_samples_acquired;
@@ -145,13 +139,11 @@ struct SWVoiceIn {
typedef struct audio_driver audio_driver;
struct audio_driver {
const char *name;
const char *descr;
void *(*init) (Audiodev *, Error **);
void (*fini) (void *);
#ifdef CONFIG_GIO
bool (*set_dbus_server)(AudioBackend *be,
GDBusObjectManagerServer *manager,
bool p2p,
Error **errp);
void (*set_dbus_server) (AudioState *s, GDBusObjectManagerServer *manager, bool p2p);
#endif
struct audio_pcm_ops *pcm_ops;
int max_voices_out;
@@ -195,23 +187,6 @@ struct audio_pcm_ops {
void (*volume_in)(HWVoiceIn *hw, Volume *vol);
};
audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo);
int audioformat_bytes_per_sample(AudioFormat fmt);
int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
static inline void *advance(void *p, size_t incr)
{
return (uint8_t *)p + incr;
}
int wav_start_capture(AudioBackend *state, CaptureState *s, const char *path,
int freq, int bits, int nchannels);
void audio_generic_run_buffer_in(HWVoiceIn *hw);
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
@@ -241,14 +216,13 @@ struct SWVoiceCap {
QLIST_ENTRY (SWVoiceCap) entries;
};
typedef struct AudioBackend {
Object parent;
typedef struct AudioState {
struct audio_driver *drv;
Audiodev *dev;
void *drv_opaque;
QEMUTimer *ts;
QLIST_HEAD (card_listhead, QEMUSoundCard) card_head;
QLIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in;
QLIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out;
QLIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head;
@@ -259,8 +233,9 @@ typedef struct AudioBackend {
bool timer_running;
uint64_t timer_last;
VMChangeStateEntry *vmse;
} AudioBackend;
QTAILQ_ENTRY(AudioState) list;
} AudioState;
extern const struct mixeng_volume nominal_volume;
@@ -273,14 +248,13 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
int audio_bug (const char *funcname, int cond);
void audio_run(AudioBackend *s, const char *msg);
void audio_run(AudioState *s, const char *msg);
const char *audio_application_name(void);
typedef struct RateCtl {
int64_t start_ticks;
int64_t bytes_sent;
int64_t peeked_frames;
} RateCtl;
void audio_rate_start(RateCtl *rate);
+27 -29
View File
@@ -36,7 +36,7 @@
#define HWBUF hw->conv_buf
#endif
static void glue(audio_init_nb_voices_, TYPE)(AudioBackend *s,
static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
struct audio_driver *drv, int min_voices)
{
int max_voices = glue (drv->max_voices_, TYPE);
@@ -166,19 +166,17 @@ static int glue (audio_pcm_sw_init_, TYPE) (
audio_pcm_init_info (&sw->info, as);
sw->hw = hw;
sw->active = false;
sw->active = 0;
#ifdef DAC
sw->total_hw_samples_mixed = 0;
sw->empty = true;
sw->empty = 1;
#endif
if (sw->info.is_float) {
#ifdef DAC
sw->conv = mixeng_conv_float[sw->info.nchannels == 2]
[sw->info.swap_endianness];
sw->conv = mixeng_conv_float[sw->info.nchannels == 2];
#else
sw->clip = mixeng_clip_float[sw->info.nchannels == 2]
[sw->info.swap_endianness];
sw->clip = mixeng_clip_float[sw->info.nchannels == 2];
#endif
} else {
#ifdef DAC
@@ -221,7 +219,7 @@ static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
{
HW *hw = *hwp;
AudioBackend *s = hw->s;
AudioState *s = hw->s;
if (!hw->sw_head.lh_first) {
#ifdef DAC
@@ -236,12 +234,12 @@ static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
}
}
static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioBackend *s, HW *hw)
static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioState *s, HW *hw)
{
return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first;
}
static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioBackend *s, HW *hw)
static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw)
{
while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
if (hw->enabled) {
@@ -251,7 +249,7 @@ static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioBackend *s, HW *hw)
return NULL;
}
static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioBackend *s, HW *hw,
static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw,
struct audsettings *as)
{
while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
@@ -262,7 +260,7 @@ static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioBackend *s, HW *hw,
return NULL;
}
static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioBackend *s,
static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
struct audsettings *as)
{
HW *hw;
@@ -305,11 +303,9 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioBackend *s,
if (hw->info.is_float) {
#ifdef DAC
hw->clip = mixeng_clip_float[hw->info.nchannels == 2]
[hw->info.swap_endianness];
hw->clip = mixeng_clip_float[hw->info.nchannels == 2];
#else
hw->conv = mixeng_conv_float[hw->info.nchannels == 2]
[hw->info.swap_endianness];
hw->conv = mixeng_conv_float[hw->info.nchannels == 2];
#endif
} else {
#ifdef DAC
@@ -398,7 +394,7 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
abort();
}
static HW *glue(audio_pcm_hw_add_, TYPE)(AudioBackend *s, struct audsettings *as)
static HW *glue(audio_pcm_hw_add_, TYPE)(AudioState *s, struct audsettings *as)
{
HW *hw;
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
@@ -424,7 +420,7 @@ static HW *glue(audio_pcm_hw_add_, TYPE)(AudioBackend *s, struct audsettings *as
}
static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
AudioBackend *s,
AudioState *s,
const char *sw_name,
struct audsettings *as
)
@@ -473,11 +469,11 @@ static void glue (audio_close_, TYPE) (SW *sw)
g_free (sw);
}
void glue(AUD_close_, TYPE)(AudioBackend *be, SW *sw)
void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw)
{
if (sw) {
if (audio_bug(__func__, !be)) {
dolog("backend=%p\n", be);
if (audio_bug(__func__, !card)) {
dolog ("card=%p\n", card);
return;
}
@@ -486,7 +482,7 @@ void glue(AUD_close_, TYPE)(AudioBackend *be, SW *sw)
}
SW *glue (AUD_open_, TYPE) (
AudioBackend *be,
QEMUSoundCard *card,
SW *sw,
const char *name,
void *callback_opaque ,
@@ -494,15 +490,16 @@ SW *glue (AUD_open_, TYPE) (
struct audsettings *as
)
{
AudioBackend *s = be;
AudioState *s;
AudiodevPerDirectionOptions *pdo;
if (audio_bug(__func__, !be || !name || !callback_fn || !as)) {
dolog("backend=%p name=%p callback_fn=%p as=%p\n",
be, name, callback_fn, as);
if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
dolog ("card=%p name=%p callback_fn=%p as=%p\n",
card, name, callback_fn, as);
goto fail;
}
s = card->state;
pdo = glue(audio_get_pdo_, TYPE)(s->dev);
ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
@@ -523,7 +520,7 @@ SW *glue (AUD_open_, TYPE) (
}
if (!pdo->fixed_settings && sw) {
glue(AUD_close_, TYPE)(be, sw);
glue (AUD_close_, TYPE) (card, sw);
sw = NULL;
}
@@ -547,6 +544,7 @@ SW *glue (AUD_open_, TYPE) (
}
}
sw->card = card;
sw->vol = nominal_volume;
sw->callback.fn = callback_fn;
sw->callback.opaque = callback_opaque;
@@ -560,11 +558,11 @@ SW *glue (AUD_open_, TYPE) (
return sw;
fail:
glue(AUD_close_, TYPE)(be, sw);
glue (AUD_close_, TYPE) (card, sw);
return NULL;
}
bool glue(AUD_is_active_, TYPE)(SW *sw)
int glue (AUD_is_active_, TYPE) (SW *sw)
{
return sw ? sw->active : 0;
}
+1 -1
View File
@@ -7,7 +7,7 @@
#include <mmreg.h>
#include <mmsystem.h>
#include "qemu/audio.h"
#include "audio.h"
#include "audio_int.h"
#include "audio_win_int.h"
+2 -1
View File
@@ -28,7 +28,7 @@
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/audio.h"
#include "audio.h"
#define AUDIO_CAP "coreaudio"
#include "audio_int.h"
@@ -664,6 +664,7 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
static struct audio_driver coreaudio_audio_driver = {
.name = "coreaudio",
.descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
.init = coreaudio_audio_init,
.fini = coreaudio_audio_fini,
.pcm_ops = &coreaudio_pcm_ops,
+9 -11
View File
@@ -24,7 +24,9 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qemu/host-utils.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "qemu/dbus.h"
#ifdef G_OS_UNIX
@@ -35,7 +37,7 @@
#include "ui/dbus-display1.h"
#define AUDIO_CAP "dbus"
#include "qemu/audio.h"
#include "audio.h"
#include "audio_int.h"
#include "trace.h"
@@ -458,7 +460,7 @@ listener_in_vanished_cb(GDBusConnection *connection,
}
static gboolean
dbus_audio_register_listener(AudioBackend *s,
dbus_audio_register_listener(AudioState *s,
GDBusMethodInvocation *invocation,
#ifdef G_OS_UNIX
GUnixFDList *fd_list,
@@ -615,7 +617,7 @@ dbus_audio_register_listener(AudioBackend *s,
}
static gboolean
dbus_audio_register_out_listener(AudioBackend *s,
dbus_audio_register_out_listener(AudioState *s,
GDBusMethodInvocation *invocation,
#ifdef G_OS_UNIX
GUnixFDList *fd_list,
@@ -631,7 +633,7 @@ dbus_audio_register_out_listener(AudioBackend *s,
}
static gboolean
dbus_audio_register_in_listener(AudioBackend *s,
dbus_audio_register_in_listener(AudioState *s,
GDBusMethodInvocation *invocation,
#ifdef G_OS_UNIX
GUnixFDList *fd_list,
@@ -645,11 +647,8 @@ dbus_audio_register_in_listener(AudioBackend *s,
arg_listener, false);
}
static bool
dbus_audio_set_server(AudioBackend *s,
GDBusObjectManagerServer *server,
bool p2p,
Error **errp)
static void
dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server, bool p2p)
{
DBusAudio *da = s->drv_opaque;
@@ -672,8 +671,6 @@ dbus_audio_set_server(AudioBackend *s,
g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(da->audio),
G_DBUS_INTERFACE_SKELETON(da->iface));
g_dbus_object_manager_server_export(da->server, da->audio);
return true;
}
static struct audio_pcm_ops dbus_pcm_ops = {
@@ -695,6 +692,7 @@ static struct audio_pcm_ops dbus_pcm_ops = {
static struct audio_driver dbus_audio_driver = {
.name = "dbus",
.descr = "Timer based audio exposed with DBus interface",
.init = dbus_audio_init,
.fini = dbus_audio_fini,
.set_dbus_server = dbus_audio_set_server,
+124 -91
View File
@@ -27,12 +27,12 @@
*/
#include "qemu/osdep.h"
#include "qemu/audio.h"
#include "audio.h"
#define AUDIO_CAP "dsound"
#include "audio_int.h"
#include "qemu/host-utils.h"
#include "qemu/module.h"
#include "qapi/error.h"
#include <windows.h>
#include <mmsystem.h>
@@ -64,154 +64,162 @@ typedef struct {
dsound *s;
} DSoundVoiceIn;
static const char *dserror(HRESULT hr)
static void dsound_log_hresult (HRESULT hr)
{
const char *str = "BUG";
switch (hr) {
case DS_OK:
return "The method succeeded";
str = "The method succeeded";
break;
#ifdef DS_NO_VIRTUALIZATION
case DS_NO_VIRTUALIZATION:
return "The buffer was created, but another 3D algorithm was substituted";
str = "The buffer was created, but another 3D algorithm was substituted";
break;
#endif
#ifdef DS_INCOMPLETE
case DS_INCOMPLETE:
return "The method succeeded, but not all the optional effects were obtained";
str = "The method succeeded, but not all the optional effects were obtained";
break;
#endif
#ifdef DSERR_ACCESSDENIED
case DSERR_ACCESSDENIED:
return "The request failed because access was denied";
str = "The request failed because access was denied";
break;
#endif
#ifdef DSERR_ALLOCATED
case DSERR_ALLOCATED:
return "The request failed because resources, "
"such as a priority level, were already in use "
"by another caller";
str = "The request failed because resources, "
"such as a priority level, were already in use "
"by another caller";
break;
#endif
#ifdef DSERR_ALREADYINITIALIZED
case DSERR_ALREADYINITIALIZED:
return "The object is already initialized";
str = "The object is already initialized";
break;
#endif
#ifdef DSERR_BADFORMAT
case DSERR_BADFORMAT:
return "The specified wave format is not supported";
str = "The specified wave format is not supported";
break;
#endif
#ifdef DSERR_BADSENDBUFFERGUID
case DSERR_BADSENDBUFFERGUID:
return "The GUID specified in an audiopath file "
"does not match a valid mix-in buffer";
str = "The GUID specified in an audiopath file "
"does not match a valid mix-in buffer";
break;
#endif
#ifdef DSERR_BUFFERLOST
case DSERR_BUFFERLOST:
return "The buffer memory has been lost and must be restored";
str = "The buffer memory has been lost and must be restored";
break;
#endif
#ifdef DSERR_BUFFERTOOSMALL
case DSERR_BUFFERTOOSMALL:
return "The buffer size is not great enough to "
"enable effects processing";
str = "The buffer size is not great enough to "
"enable effects processing";
break;
#endif
#ifdef DSERR_CONTROLUNAVAIL
case DSERR_CONTROLUNAVAIL:
return "The buffer control (volume, pan, and so on) "
"requested by the caller is not available. "
"Controls must be specified when the buffer is created, "
"using the dwFlags member of DSBUFFERDESC";
str = "The buffer control (volume, pan, and so on) "
"requested by the caller is not available. "
"Controls must be specified when the buffer is created, "
"using the dwFlags member of DSBUFFERDESC";
break;
#endif
#ifdef DSERR_DS8_REQUIRED
case DSERR_DS8_REQUIRED:
return "A DirectSound object of class CLSID_DirectSound8 or later "
"is required for the requested functionality. "
"For more information, see IDirectSound8 Interface";
str = "A DirectSound object of class CLSID_DirectSound8 or later "
"is required for the requested functionality. "
"For more information, see IDirectSound8 Interface";
break;
#endif
#ifdef DSERR_FXUNAVAILABLE
case DSERR_FXUNAVAILABLE:
return "The effects requested could not be found on the system, "
"or they are in the wrong order or in the wrong location; "
"for example, an effect expected in hardware "
"was found in software";
str = "The effects requested could not be found on the system, "
"or they are in the wrong order or in the wrong location; "
"for example, an effect expected in hardware "
"was found in software";
break;
#endif
#ifdef DSERR_GENERIC
case DSERR_GENERIC:
return "An undetermined error occurred inside the DirectSound subsystem";
str = "An undetermined error occurred inside the DirectSound subsystem";
break;
#endif
#ifdef DSERR_INVALIDCALL
case DSERR_INVALIDCALL:
return "This function is not valid for the current state of this object";
str = "This function is not valid for the current state of this object";
break;
#endif
#ifdef DSERR_INVALIDPARAM
case DSERR_INVALIDPARAM:
return "An invalid parameter was passed to the returning function";
str = "An invalid parameter was passed to the returning function";
break;
#endif
#ifdef DSERR_NOAGGREGATION
case DSERR_NOAGGREGATION:
return "The object does not support aggregation";
str = "The object does not support aggregation";
break;
#endif
#ifdef DSERR_NODRIVER
case DSERR_NODRIVER:
return "No sound driver is available for use, "
"or the given GUID is not a valid DirectSound device ID";
str = "No sound driver is available for use, "
"or the given GUID is not a valid DirectSound device ID";
break;
#endif
#ifdef DSERR_NOINTERFACE
case DSERR_NOINTERFACE:
return "The requested COM interface is not available";
str = "The requested COM interface is not available";
break;
#endif
#ifdef DSERR_OBJECTNOTFOUND
case DSERR_OBJECTNOTFOUND:
return "The requested object was not found";
str = "The requested object was not found";
break;
#endif
#ifdef DSERR_OTHERAPPHASPRIO
case DSERR_OTHERAPPHASPRIO:
return "Another application has a higher priority level, "
str = "Another application has a higher priority level, "
"preventing this call from succeeding";
break;
#endif
#ifdef DSERR_OUTOFMEMORY
case DSERR_OUTOFMEMORY:
return "The DirectSound subsystem could not allocate "
str = "The DirectSound subsystem could not allocate "
"sufficient memory to complete the caller's request";
break;
#endif
#ifdef DSERR_PRIOLEVELNEEDED
case DSERR_PRIOLEVELNEEDED:
return "A cooperative level of DSSCL_PRIORITY or higher is required";
str = "A cooperative level of DSSCL_PRIORITY or higher is required";
break;
#endif
#ifdef DSERR_SENDLOOP
case DSERR_SENDLOOP:
return "A circular loop of send effects was detected";
str = "A circular loop of send effects was detected";
break;
#endif
#ifdef DSERR_UNINITIALIZED
case DSERR_UNINITIALIZED:
return "The Initialize method has not been called "
"or has not been called successfully "
"before other methods were called";
str = "The Initialize method has not been called "
"or has not been called successfully "
"before other methods were called";
break;
#endif
#ifdef DSERR_UNSUPPORTED
case DSERR_UNSUPPORTED:
return "The function called is not supported at this time";
str = "The function called is not supported at this time";
break;
#endif
default:
return NULL;
AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT 0x%lx)\n", hr);
return;
}
}
static void dserror_set(Error **errp, HRESULT hr, const char *msg)
{
const char *str = dserror(hr);
if (str) {
error_setg(errp, "%s: %s", msg, str);
} else {
error_setg(errp, "%s: Unknown (HRESULT: 0x%lx)", msg, hr);
}
}
static void dsound_log_hresult(HRESULT hr)
{
const char *str = dserror(hr);
if (str) {
AUD_log (AUDIO_CAP, "Reason: %s\n", str);
} else {
AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT: 0x%lx)\n", hr);
}
AUD_log (AUDIO_CAP, "Reason: %s\n", str);
}
static void G_GNUC_PRINTF (2, 3) dsound_logerr (
@@ -351,6 +359,27 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
dsound_unlock_out (dsb, p1, p2, blen1, blen2);
}
static int dsound_set_cooperative_level(dsound *s)
{
HRESULT hr;
HWND hwnd;
hwnd = GetDesktopWindow();
hr = IDirectSound_SetCooperativeLevel (
s->dsound,
hwnd,
DSSCL_PRIORITY
);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not set cooperative level for window %p\n",
hwnd);
return -1;
}
return 0;
}
static void dsound_enable_out(HWVoiceOut *hw, bool enable)
{
HRESULT hr;
@@ -592,6 +621,7 @@ static void dsound_audio_fini (void *opaque)
static void *dsound_audio_init(Audiodev *dev, Error **errp)
{
int err;
HRESULT hr;
dsound *s = g_new0(dsound, 1);
AudiodevDsoundOptions *dso;
@@ -607,8 +637,8 @@ static void *dsound_audio_init(Audiodev *dev, Error **errp)
hr = CoInitialize (NULL);
if (FAILED (hr)) {
dserror_set(errp, hr, "Could not initialize COM");
dsound_audio_fini(s);
dsound_logerr (hr, "Could not initialize COM\n");
g_free(s);
return NULL;
}
@@ -620,15 +650,20 @@ static void *dsound_audio_init(Audiodev *dev, Error **errp)
(void **) &s->dsound
);
if (FAILED (hr)) {
dserror_set(errp, hr, "Could not create DirectSound instance");
dsound_audio_fini(s);
dsound_logerr (hr, "Could not create DirectSound instance\n");
g_free(s);
return NULL;
}
hr = IDirectSound_Initialize (s->dsound, NULL);
if (FAILED (hr)) {
dserror_set(errp, hr, "Could not initialize DirectSound");
dsound_audio_fini(s);
dsound_logerr (hr, "Could not initialize DirectSound\n");
hr = IDirectSound_Release (s->dsound);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not release DirectSound\n");
}
g_free(s);
return NULL;
}
@@ -640,26 +675,23 @@ static void *dsound_audio_init(Audiodev *dev, Error **errp)
(void **) &s->dsound_capture
);
if (FAILED (hr)) {
dserror_set(errp, hr, "Could not create DirectSoundCapture instance");
dsound_audio_fini(s);
return NULL;
dsound_logerr (hr, "Could not create DirectSoundCapture instance\n");
} else {
hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
hr = IDirectSoundCapture_Release (s->dsound_capture);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not release DirectSoundCapture\n");
}
s->dsound_capture = NULL;
}
}
hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
if (FAILED(hr)) {
dserror_set(errp, hr, "Could not initialize DirectSoundCapture");
dsound_audio_fini(s);
return NULL;
}
hr = IDirectSound_SetCooperativeLevel (
s->dsound,
GetDesktopWindow(),
DSSCL_PRIORITY
);
if (FAILED(hr)) {
dserror_set(errp, hr, "Could not set cooperative level");
dsound_audio_fini(s);
err = dsound_set_cooperative_level(s);
if (err) {
dsound_audio_fini (s);
return NULL;
}
@@ -685,6 +717,7 @@ static struct audio_pcm_ops dsound_pcm_ops = {
static struct audio_driver dsound_audio_driver = {
.name = "dsound",
.descr = "DirectSound http://wikipedia.org/wiki/DirectSound",
.init = dsound_audio_init,
.fini = dsound_audio_fini,
.pcm_ops = &dsound_pcm_ops,

Some files were not shown because too many files have changed in this diff Show More